gem5  v19.0.0.0
simple_cache.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Jason Lowe-Power
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met: redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer;
9  * redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution;
12  * neither the name of the copyright holders nor the names of its
13  * contributors may be used to endorse or promote products derived from
14  * this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Authors: Jason Lowe-Power
29  */
30 
32 
33 #include "base/random.hh"
34 #include "debug/SimpleCache.hh"
35 #include "sim/system.hh"
36 
37 SimpleCache::SimpleCache(SimpleCacheParams *params) :
38  ClockedObject(params),
39  latency(params->latency),
40  blockSize(params->system->cacheLineSize()),
41  capacity(params->size / blockSize),
42  memPort(params->name + ".mem_side", this),
43  blocked(false), originalPacket(nullptr), waitingPortId(-1)
44 {
45  // Since the CPU side ports are a vector of ports, create an instance of
46  // the CPUSidePort for each connection. This member of params is
47  // automatically created depending on the name of the vector port and
48  // holds the number of connections to this port name
49  for (int i = 0; i < params->port_cpu_side_connection_count; ++i) {
50  cpuPorts.emplace_back(name() + csprintf(".cpu_side[%d]", i), i, this);
51  }
52 }
53 
54 Port &
55 SimpleCache::getPort(const std::string &if_name, PortID idx)
56 {
57  // This is the name from the Python SimObject declaration in SimpleCache.py
58  if (if_name == "mem_side") {
59  panic_if(idx != InvalidPortID,
60  "Mem side of simple cache not a vector port");
61  return memPort;
62  } else if (if_name == "cpu_side" && idx < cpuPorts.size()) {
63  // We should have already created all of the ports in the constructor
64  return cpuPorts[idx];
65  } else {
66  // pass it along to our super class
67  return ClockedObject::getPort(if_name, idx);
68  }
69 }
70 
71 void
73 {
74  // Note: This flow control is very simple since the cache is blocking.
75 
76  panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
77 
78  // If we can't send the packet across the port, store it for later.
79  DPRINTF(SimpleCache, "Sending %s to CPU\n", pkt->print());
80  if (!sendTimingResp(pkt)) {
81  DPRINTF(SimpleCache, "failed!\n");
82  blockedPacket = pkt;
83  }
84 }
85 
88 {
89  return owner->getAddrRanges();
90 }
91 
92 void
94 {
95  if (needRetry && blockedPacket == nullptr) {
96  // Only send a retry if the port is now completely free
97  needRetry = false;
98  DPRINTF(SimpleCache, "Sending retry req.\n");
99  sendRetryReq();
100  }
101 }
102 
103 void
105 {
106  // Just forward to the cache.
107  return owner->handleFunctional(pkt);
108 }
109 
110 bool
112 {
113  DPRINTF(SimpleCache, "Got request %s\n", pkt->print());
114 
115  if (blockedPacket || needRetry) {
116  // The cache may not be able to send a reply if this is blocked
117  DPRINTF(SimpleCache, "Request blocked\n");
118  needRetry = true;
119  return false;
120  }
121  // Just forward to the cache.
122  if (!owner->handleRequest(pkt, id)) {
123  DPRINTF(SimpleCache, "Request failed\n");
124  // stalling
125  needRetry = true;
126  return false;
127  } else {
128  DPRINTF(SimpleCache, "Request succeeded\n");
129  return true;
130  }
131 }
132 
133 void
135 {
136  // We should have a blocked packet if this function is called.
137  assert(blockedPacket != nullptr);
138 
139  // Grab the blocked packet.
140  PacketPtr pkt = blockedPacket;
141  blockedPacket = nullptr;
142 
143  DPRINTF(SimpleCache, "Retrying response pkt %s\n", pkt->print());
144  // Try to resend it. It's possible that it fails again.
145  sendPacket(pkt);
146 
147  // We may now be able to accept new packets
148  trySendRetry();
149 }
150 
151 void
153 {
154  // Note: This flow control is very simple since the cache is blocking.
155 
156  panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
157 
158  // If we can't send the packet across the port, store it for later.
159  if (!sendTimingReq(pkt)) {
160  blockedPacket = pkt;
161  }
162 }
163 
164 bool
166 {
167  // Just forward to the cache.
168  return owner->handleResponse(pkt);
169 }
170 
171 void
173 {
174  // We should have a blocked packet if this function is called.
175  assert(blockedPacket != nullptr);
176 
177  // Grab the blocked packet.
178  PacketPtr pkt = blockedPacket;
179  blockedPacket = nullptr;
180 
181  // Try to resend it. It's possible that it fails again.
182  sendPacket(pkt);
183 }
184 
185 void
187 {
188  owner->sendRangeChange();
189 }
190 
191 bool
193 {
194  if (blocked) {
195  // There is currently an outstanding request so we can't respond. Stall
196  return false;
197  }
198 
199  DPRINTF(SimpleCache, "Got request for addr %#x\n", pkt->getAddr());
200 
201  // This cache is now blocked waiting for the response to this packet.
202  blocked = true;
203 
204  // Store the port for when we get the response
205  assert(waitingPortId == -1);
206  waitingPortId = port_id;
207 
208  // Schedule an event after cache access latency to actually access
209  schedule(new EventFunctionWrapper([this, pkt]{ accessTiming(pkt); },
210  name() + ".accessEvent", true),
211  clockEdge(latency));
212 
213  return true;
214 }
215 
216 bool
218 {
219  assert(blocked);
220  DPRINTF(SimpleCache, "Got response for addr %#x\n", pkt->getAddr());
221 
222  // For now assume that inserts are off of the critical path and don't count
223  // for any added latency.
224  insert(pkt);
225 
227 
228  // If we had to upgrade the request packet to a full cache line, now we
229  // can use that packet to construct the response.
230  if (originalPacket != nullptr) {
231  DPRINTF(SimpleCache, "Copying data from new packet to old\n");
232  // We had to upgrade a previous packet. We can functionally deal with
233  // the cache access now. It better be a hit.
235  panic_if(!hit, "Should always hit after inserting");
237  delete pkt; // We may need to delay this, I'm not sure.
238  pkt = originalPacket;
239  originalPacket = nullptr;
240  } // else, pkt contains the data it needs
241 
242  sendResponse(pkt);
243 
244  return true;
245 }
246 
248 {
249  assert(blocked);
250  DPRINTF(SimpleCache, "Sending resp for addr %#x\n", pkt->getAddr());
251 
252  int port = waitingPortId;
253 
254  // The packet is now done. We're about to put it in the port, no need for
255  // this object to continue to stall.
256  // We need to free the resource before sending the packet in case the CPU
257  // tries to send another request immediately (e.g., in the same callchain).
258  blocked = false;
259  waitingPortId = -1;
260 
261  // Simply forward to the memory port
262  cpuPorts[port].sendPacket(pkt);
263 
264  // For each of the cpu ports, if it needs to send a retry, it should do it
265  // now since this memory object may be unblocked now.
266  for (auto& port : cpuPorts) {
267  port.trySendRetry();
268  }
269 }
270 
271 void
273 {
274  if (accessFunctional(pkt)) {
275  pkt->makeResponse();
276  } else {
277  memPort.sendFunctional(pkt);
278  }
279 }
280 
281 void
283 {
284  bool hit = accessFunctional(pkt);
285 
286  DPRINTF(SimpleCache, "%s for packet: %s\n", hit ? "Hit" : "Miss",
287  pkt->print());
288 
289  if (hit) {
290  // Respond to the CPU side
291  hits++; // update stats
292  DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), pkt->getSize());
293  pkt->makeResponse();
294  sendResponse(pkt);
295  } else {
296  misses++; // update stats
297  missTime = curTick();
298  // Forward to the memory side.
299  // We can't directly forward the packet unless it is exactly the size
300  // of the cache line, and aligned. Check for that here.
301  Addr addr = pkt->getAddr();
302  Addr block_addr = pkt->getBlockAddr(blockSize);
303  unsigned size = pkt->getSize();
304  if (addr == block_addr && size == blockSize) {
305  // Aligned and block size. We can just forward.
306  DPRINTF(SimpleCache, "forwarding packet\n");
307  memPort.sendPacket(pkt);
308  } else {
309  DPRINTF(SimpleCache, "Upgrading packet to block size\n");
310  panic_if(addr - block_addr + size > blockSize,
311  "Cannot handle accesses that span multiple cache lines");
312  // Unaligned access to one cache block
313  assert(pkt->needsResponse());
314  MemCmd cmd;
315  if (pkt->isWrite() || pkt->isRead()) {
316  // Read the data from memory to write into the block.
317  // We'll write the data in the cache (i.e., a writeback cache)
318  cmd = MemCmd::ReadReq;
319  } else {
320  panic("Unknown packet type in upgrade size");
321  }
322 
323  // Create a new packet that is blockSize
324  PacketPtr new_pkt = new Packet(pkt->req, cmd, blockSize);
325  new_pkt->allocate();
326 
327  // Should now be block aligned
328  assert(new_pkt->getAddr() == new_pkt->getBlockAddr(blockSize));
329 
330  // Save the old packet
331  originalPacket = pkt;
332 
333  DPRINTF(SimpleCache, "forwarding packet\n");
334  memPort.sendPacket(new_pkt);
335  }
336  }
337 }
338 
339 bool
341 {
342  Addr block_addr = pkt->getBlockAddr(blockSize);
343  auto it = cacheStore.find(block_addr);
344  if (it != cacheStore.end()) {
345  if (pkt->isWrite()) {
346  // Write the data into the block in the cache
347  pkt->writeDataToBlock(it->second, blockSize);
348  } else if (pkt->isRead()) {
349  // Read the data out of the cache block into the packet
350  pkt->setDataFromBlock(it->second, blockSize);
351  } else {
352  panic("Unknown packet type!");
353  }
354  return true;
355  }
356  return false;
357 }
358 
359 void
361 {
362  // The packet should be aligned.
363  assert(pkt->getAddr() == pkt->getBlockAddr(blockSize));
364  // The address should not be in the cache
365  assert(cacheStore.find(pkt->getAddr()) == cacheStore.end());
366  // The pkt should be a response
367  assert(pkt->isResponse());
368 
369  if (cacheStore.size() >= capacity) {
370  // Select random thing to evict. This is a little convoluted since we
371  // are using a std::unordered_map. See http://bit.ly/2hrnLP2
372  int bucket, bucket_size;
373  do {
374  bucket = random_mt.random(0, (int)cacheStore.bucket_count() - 1);
375  } while ( (bucket_size = cacheStore.bucket_size(bucket)) == 0 );
376  auto block = std::next(cacheStore.begin(bucket),
377  random_mt.random(0, bucket_size - 1));
378 
379  DPRINTF(SimpleCache, "Removing addr %#x\n", block->first);
380 
381  // Write back the data.
382  // Create a new request-packet pair
383  RequestPtr req = std::make_shared<Request>(
384  block->first, blockSize, 0, 0);
385 
386  PacketPtr new_pkt = new Packet(req, MemCmd::WritebackDirty, blockSize);
387  new_pkt->dataDynamic(block->second); // This will be deleted later
388 
389  DPRINTF(SimpleCache, "Writing packet back %s\n", pkt->print());
390  // Send the write to memory
391  memPort.sendPacket(new_pkt);
392 
393  // Delete this entry
394  cacheStore.erase(block->first);
395  }
396 
397  DPRINTF(SimpleCache, "Inserting %s\n", pkt->print());
398  DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), blockSize);
399 
400  // Allocate space for the cache block data
401  uint8_t *data = new uint8_t[blockSize];
402 
403  // Insert the data and address into the cache store
404  cacheStore[pkt->getAddr()] = data;
405 
406  // Write the data into the cache
407  pkt->writeDataToBlock(data, blockSize);
408 }
409 
412 {
413  DPRINTF(SimpleCache, "Sending new ranges\n");
414  // Just use the same ranges as whatever is on the memory side.
415  return memPort.getAddrRanges();
416 }
417 
418 void
420 {
421  for (auto& port : cpuPorts) {
422  port.sendRangeChange();
423  }
424 }
425 
426 void
428 {
429  // If you don't do this you get errors about uninitialized stats.
431 
432  hits.name(name() + ".hits")
433  .desc("Number of hits")
434  ;
435 
436  misses.name(name() + ".misses")
437  .desc("Number of misses")
438  ;
439 
440  missLatency.name(name() + ".missLatency")
441  .desc("Ticks for misses to the cache")
442  .init(16) // number of buckets
443  ;
444 
445  hitRatio.name(name() + ".hitRatio")
446  .desc("The ratio of hits to the total accesses to the cache")
447  ;
448 
449  hitRatio = hits / (hits + misses);
450 
451 }
452 
453 
455 SimpleCacheParams::create()
456 {
457  return new SimpleCache(this);
458 }
Port & getPort(const std::string &if_name, PortID idx=InvalidPortID) override
Get a port with a given name and index.
Definition: simple_cache.cc:55
#define panic(...)
This implements a cprintf based panic() function.
Definition: logging.hh:167
#define DPRINTF(x,...)
Definition: trace.hh:229
Stats::Scalar hits
Cache statistics.
Ports are used to interface objects to each other.
Definition: port.hh:60
void recvRangeChange() override
Called to receive an address range change from the peer slave port.
void sendPacket(PacketPtr pkt)
Send a packet across this port.
virtual Port & getPort(const std::string &if_name, PortID idx=InvalidPortID)
Get a port with a given name and index.
Definition: sim_object.cc:126
Definition: packet.hh:76
const std::string & name()
Definition: trace.cc:54
Bitfield< 7 > i
const PortID InvalidPortID
Definition: types.hh:238
A very simple cache object.
Definition: simple_cache.hh:48
void handleFunctional(PacketPtr pkt)
Handle a packet functionally.
int waitingPortId
The port to send the response when we recieve it back.
const unsigned blockSize
The block size for the cache.
SimpleCache(SimpleCacheParams *params)
constructor
Definition: simple_cache.cc:37
std::shared_ptr< Request > RequestPtr
Definition: request.hh:83
const unsigned capacity
Number of blocks in the cache (size of cache / block size)
ip6_addr_t addr
Definition: inet.hh:335
bool handleRequest(PacketPtr pkt, int port_id)
Handle the request from the CPU side.
#define DDUMP(x, data, count)
Definition: trace.hh:228
virtual void regStats()
Callback to set stat parameters.
Definition: group.cc:66
Histogram & init(size_type size)
Set the parameters of this histogram.
Definition: statistics.hh:2644
bool isWrite() const
Definition: packet.hh:529
Stats::Scalar misses
bool isRead() const
Definition: packet.hh:528
std::enable_if< std::is_integral< T >::value, T >::type random()
Use the SFINAE idiom to choose an implementation based on whether the type is integral or floating po...
Definition: random.hh:83
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:750
Stats::Formula hitRatio
RequestPtr req
A pointer to the original request.
Definition: packet.hh:327
AddrRangeList getAddrRanges() const
Return the address ranges this cache is responsible for.
unsigned getSize() const
Definition: packet.hh:736
void sendResponse(PacketPtr pkt)
Send the packet to the CPU side.
void accessTiming(PacketPtr pkt)
Access the cache for a timing access.
Tick curTick()
The current simulated tick.
Definition: core.hh:47
std::string csprintf(const char *format, const Args &...args)
Definition: cprintf.hh:162
bool needsResponse() const
Definition: packet.hh:542
void trySendRetry()
Send a retry to the peer port only if it is needed.
Definition: simple_cache.cc:93
bool handleResponse(PacketPtr pkt)
Handle the respone from the memory side.
std::unordered_map< Addr, uint8_t * > cacheStore
An incredibly simple cache storage. Maps block addresses to data.
void sendRangeChange() const
Tell the CPU side to ask for our memory ranges.
bool recvTimingReq(PacketPtr pkt) override
Receive a timing request from the master port.
bool isResponse() const
Definition: packet.hh:532
The ClockedObject class extends the SimObject with a clock and accessor functions to relate ticks to ...
void insert(PacketPtr pkt)
Insert a block into the cache.
void recvReqRetry() override
Called by the slave port if sendTimingReq was called on this master port (causing recvTimingReq to be...
void recvFunctional(PacketPtr pkt) override
Receive a functional request packet from the master port.
Addr getAddr() const
Definition: packet.hh:726
void regStats() override
Register the stats.
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:142
virtual const std::string name() const
Definition: sim_object.hh:120
A Packet is used to encapsulate a transfer between two objects in the memory system (e...
Definition: packet.hh:255
const Cycles latency
Latency to check the cache. Number of cycles for both hit and miss.
Tick clockEdge(Cycles cycles=Cycles(0)) const
Determine the tick when a cycle begins, by default the current one, but the argument also enables the...
void setDataFromBlock(const uint8_t *blk_data, int blkSize)
Copy data into the packet from the provided block pointer, which is aligned to the given block size...
Definition: packet.hh:1177
Bitfield< 15 > system
Definition: misc.hh:999
bool recvTimingResp(PacketPtr pkt) override
Receive a timing response from the slave port.
Tick missTime
For tracking the miss latency.
Derived & name(const std::string &name)
Set the name and marks this stat to print at the end of simulation.
Definition: statistics.hh:279
void makeResponse()
Take a request packet and modify it in place to be suitable for returning as a response to that reque...
Definition: packet.hh:937
AddrRangeList getAddrRanges() const
Get the address ranges of the connected slave port.
Definition: port.cc:93
MemSidePort memPort
Instantiation of the memory-side port.
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition: packet.cc:376
Random random_mt
Definition: random.cc:100
void writeDataToBlock(uint8_t *blk_data, int blkSize) const
Copy data from the packet to the provided block pointer, which is aligned to the given block size...
Definition: packet.hh:1211
void schedule(Event &event, Tick when)
Definition: eventq.hh:744
const T * getConstPtr() const
Definition: packet.hh:1099
PacketPtr originalPacket
Packet that we are currently handling.
std::vector< CPUSidePort > cpuPorts
Instantiation of the CPU-side port.
void sendFunctional(PacketPtr pkt) const
Send a functional request packet, where the data is instantly updated everywhere in the memory system...
Definition: port.hh:439
Derived & desc(const std::string &_desc)
Set the description and marks this stat to print at the end of simulation.
Definition: statistics.hh:312
int16_t PortID
Port index/ID type, and a symbolic name for an invalid port id.
Definition: types.hh:237
void sendPacket(PacketPtr pkt)
Send a packet across this port.
Definition: simple_cache.cc:72
AddrRangeList getAddrRanges() const override
Get a list of the non-overlapping address ranges the owner is responsible for.
Definition: simple_cache.cc:87
static const int NumArgumentRegs M5_VAR_USED
Definition: process.cc:84
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:185
Stats::Histogram missLatency
bool accessFunctional(PacketPtr pkt)
This is where we actually update / read from the cache.
const char data[]
void recvRespRetry() override
Called by the master port if sendTimingResp was called on this slave port (causing recvTimingResp to ...
bool blocked
True if this cache is currently blocked waiting for a response.
void sample(const U &v, int n=1)
Add a value to the distribtion n times.
Definition: statistics.hh:1899
void allocate()
Allocate memory for the packet.
Definition: packet.hh:1232
ProbePointArg< PacketInfo > Packet
Packet probe point.
Definition: mem.hh:104

Generated on Fri Feb 28 2020 16:27:01 for gem5 by doxygen 1.8.13