This is the rest of the code for part 2. See http://learning.gem5.org/book/part2/simplecache.html Change-Id: I5db099266a1196914656be3858fdd5fb4f8eab48 Signed-off-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-on: https://gem5-review.googlesource.com/5023 Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
340 lines
11 KiB
C++
340 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2017 Jason Lowe-Power
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Authors: Jason Lowe-Power
|
|
*/
|
|
|
|
#ifndef __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
|
|
#define __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
|
|
|
|
#include <unordered_map>
|
|
|
|
#include "mem/mem_object.hh"
|
|
#include "params/SimpleCache.hh"
|
|
|
|
/**
|
|
* A very simple cache object. Has a fully-associative data store with random
|
|
* replacement.
|
|
* This cache is fully blocking (not non-blocking). Only a single request can
|
|
* be outstanding at a time.
|
|
* This cache is a writeback cache.
|
|
*/
|
|
class SimpleCache : public MemObject
|
|
{
|
|
private:
|
|
|
|
/**
|
|
* Port on the CPU-side that receives requests.
|
|
* Mostly just forwards requests to the cache (owner)
|
|
*/
|
|
class CPUSidePort : public SlavePort
|
|
{
|
|
private:
|
|
/// Since this is a vector port, need to know what number this one is
|
|
int id;
|
|
|
|
/// The object that owns this object (SimpleCache)
|
|
SimpleCache *owner;
|
|
|
|
/// True if the port needs to send a retry req.
|
|
bool needRetry;
|
|
|
|
/// If we tried to send a packet and it was blocked, store it here
|
|
PacketPtr blockedPacket;
|
|
|
|
public:
|
|
/**
|
|
* Constructor. Just calls the superclass constructor.
|
|
*/
|
|
CPUSidePort(const std::string& name, int id, SimpleCache *owner) :
|
|
SlavePort(name, owner), id(id), owner(owner), needRetry(false),
|
|
blockedPacket(nullptr)
|
|
{ }
|
|
|
|
/**
|
|
* Send a packet across this port. This is called by the owner and
|
|
* all of the flow control is hanled in this function.
|
|
* This is a convenience function for the SimpleCache to send pkts.
|
|
*
|
|
* @param packet to send.
|
|
*/
|
|
void sendPacket(PacketPtr pkt);
|
|
|
|
/**
|
|
* Get a list of the non-overlapping address ranges the owner is
|
|
* responsible for. All slave ports must override this function
|
|
* and return a populated list with at least one item.
|
|
*
|
|
* @return a list of ranges responded to
|
|
*/
|
|
AddrRangeList getAddrRanges() const override;
|
|
|
|
/**
|
|
* Send a retry to the peer port only if it is needed. This is called
|
|
* from the SimpleCache whenever it is unblocked.
|
|
*/
|
|
void trySendRetry();
|
|
|
|
protected:
|
|
/**
|
|
* Receive an atomic request packet from the master port.
|
|
* No need to implement in this simple cache.
|
|
*/
|
|
Tick recvAtomic(PacketPtr pkt) override
|
|
{ panic("recvAtomic unimpl."); }
|
|
|
|
/**
|
|
* Receive a functional request packet from the master port.
|
|
* Performs a "debug" access updating/reading the data in place.
|
|
*
|
|
* @param packet the requestor sent.
|
|
*/
|
|
void recvFunctional(PacketPtr pkt) override;
|
|
|
|
/**
|
|
* Receive a timing request from the master port.
|
|
*
|
|
* @param the packet that the requestor sent
|
|
* @return whether this object can consume to packet. If false, we
|
|
* will call sendRetry() when we can try to receive this
|
|
* request again.
|
|
*/
|
|
bool recvTimingReq(PacketPtr pkt) override;
|
|
|
|
/**
|
|
* Called by the master port if sendTimingResp was called on this
|
|
* slave port (causing recvTimingResp to be called on the master
|
|
* port) and was unsuccesful.
|
|
*/
|
|
void recvRespRetry() override;
|
|
};
|
|
|
|
/**
|
|
* Port on the memory-side that receives responses.
|
|
* Mostly just forwards requests to the cache (owner)
|
|
*/
|
|
class MemSidePort : public MasterPort
|
|
{
|
|
private:
|
|
/// The object that owns this object (SimpleCache)
|
|
SimpleCache *owner;
|
|
|
|
/// If we tried to send a packet and it was blocked, store it here
|
|
PacketPtr blockedPacket;
|
|
|
|
public:
|
|
/**
|
|
* Constructor. Just calls the superclass constructor.
|
|
*/
|
|
MemSidePort(const std::string& name, SimpleCache *owner) :
|
|
MasterPort(name, owner), owner(owner), blockedPacket(nullptr)
|
|
{ }
|
|
|
|
/**
|
|
* Send a packet across this port. This is called by the owner and
|
|
* all of the flow control is hanled in this function.
|
|
* This is a convenience function for the SimpleCache to send pkts.
|
|
*
|
|
* @param packet to send.
|
|
*/
|
|
void sendPacket(PacketPtr pkt);
|
|
|
|
protected:
|
|
/**
|
|
* Receive a timing response from the slave port.
|
|
*/
|
|
bool recvTimingResp(PacketPtr pkt) override;
|
|
|
|
/**
|
|
* Called by the slave port if sendTimingReq was called on this
|
|
* master port (causing recvTimingReq to be called on the slave
|
|
* port) and was unsuccesful.
|
|
*/
|
|
void recvReqRetry() override;
|
|
|
|
/**
|
|
* Called to receive an address range change from the peer slave
|
|
* port. The default implementation ignores the change and does
|
|
* nothing. Override this function in a derived class if the owner
|
|
* needs to be aware of the address ranges, e.g. in an
|
|
* interconnect component like a bus.
|
|
*/
|
|
void recvRangeChange() override;
|
|
};
|
|
|
|
/**
|
|
* Handle the request from the CPU side. Called from the CPU port
|
|
* on a timing request.
|
|
*
|
|
* @param requesting packet
|
|
* @param id of the port to send the response
|
|
* @return true if we can handle the request this cycle, false if the
|
|
* requestor needs to retry later
|
|
*/
|
|
bool handleRequest(PacketPtr pkt, int port_id);
|
|
|
|
/**
|
|
* Handle the respone from the memory side. Called from the memory port
|
|
* on a timing response.
|
|
*
|
|
* @param responding packet
|
|
* @return true if we can handle the response this cycle, false if the
|
|
* responder needs to retry later
|
|
*/
|
|
bool handleResponse(PacketPtr pkt);
|
|
|
|
/**
|
|
* Send the packet to the CPU side.
|
|
* This function assumes the pkt is already a response packet and forwards
|
|
* it to the correct port. This function also unblocks this object and
|
|
* cleans up the whole request.
|
|
*
|
|
* @param the packet to send to the cpu side
|
|
*/
|
|
void sendResponse(PacketPtr pkt);
|
|
|
|
/**
|
|
* Handle a packet functionally. Update the data on a write and get the
|
|
* data on a read. Called from CPU port on a recv functional.
|
|
*
|
|
* @param packet to functionally handle
|
|
*/
|
|
void handleFunctional(PacketPtr pkt);
|
|
|
|
/**
|
|
* Access the cache for a timing access. This is called after the cache
|
|
* access latency has already elapsed.
|
|
*/
|
|
void accessTiming(PacketPtr pkt);
|
|
|
|
/**
|
|
* This is where we actually update / read from the cache. This function
|
|
* is executed on both timing and functional accesses.
|
|
*
|
|
* @return true if a hit, false otherwise
|
|
*/
|
|
bool accessFunctional(PacketPtr pkt);
|
|
|
|
/**
|
|
* Insert a block into the cache. If there is no room left in the cache,
|
|
* then this function evicts a random entry t make room for the new block.
|
|
*
|
|
* @param packet with the data (and address) to insert into the cache
|
|
*/
|
|
void insert(PacketPtr pkt);
|
|
|
|
/**
|
|
* Return the address ranges this cache is responsible for. Just use the
|
|
* same as the next upper level of the hierarchy.
|
|
*
|
|
* @return the address ranges this cache is responsible for
|
|
*/
|
|
AddrRangeList getAddrRanges() const;
|
|
|
|
/**
|
|
* Tell the CPU side to ask for our memory ranges.
|
|
*/
|
|
void sendRangeChange() const;
|
|
|
|
/// Latency to check the cache. Number of cycles for both hit and miss
|
|
const Cycles latency;
|
|
|
|
/// The block size for the cache
|
|
const unsigned blockSize;
|
|
|
|
/// Number of blocks in the cache (size of cache / block size)
|
|
const unsigned capacity;
|
|
|
|
/// Instantiation of the CPU-side port
|
|
std::vector<CPUSidePort> cpuPorts;
|
|
|
|
/// Instantiation of the memory-side port
|
|
MemSidePort memPort;
|
|
|
|
/// True if this cache is currently blocked waiting for a response.
|
|
bool blocked;
|
|
|
|
/// Packet that we are currently handling. Used for upgrading to larger
|
|
/// cache line sizes
|
|
PacketPtr originalPacket;
|
|
|
|
/// The port to send the response when we recieve it back
|
|
int waitingPortId;
|
|
|
|
/// For tracking the miss latency
|
|
Tick missTime;
|
|
|
|
/// An incredibly simple cache storage. Maps block addresses to data
|
|
std::unordered_map<Addr, uint8_t*> cacheStore;
|
|
|
|
/// Cache statistics
|
|
Stats::Scalar hits;
|
|
Stats::Scalar misses;
|
|
Stats::Histogram missLatency;
|
|
Stats::Formula hitRatio;
|
|
|
|
public:
|
|
|
|
/** constructor
|
|
*/
|
|
SimpleCache(SimpleCacheParams *params);
|
|
|
|
/**
|
|
* Get a master port with a given name and index. This is used at
|
|
* binding time and returns a reference to a protocol-agnostic
|
|
* base master port.
|
|
*
|
|
* @param if_name Port name
|
|
* @param idx Index in the case of a VectorPort
|
|
*
|
|
* @return A reference to the given port
|
|
*/
|
|
virtual BaseMasterPort& getMasterPort(const std::string& if_name,
|
|
PortID idx = InvalidPortID) override;
|
|
|
|
/**
|
|
* Get a slave port with a given name and index. This is used at
|
|
* binding time and returns a reference to a protocol-agnostic
|
|
* base master port.
|
|
*
|
|
* @param if_name Port name
|
|
* @param idx Index in the case of a VectorPort
|
|
*
|
|
* @return A reference to the given port
|
|
*/
|
|
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
|
|
PortID idx = InvalidPortID) override;
|
|
|
|
/**
|
|
* Register the stats
|
|
*/
|
|
void regStats() override;
|
|
};
|
|
|
|
|
|
#endif // __LEARNING_GEM5_SIMPLE_CACHE_SIMPLE_CACHE_HH__
|