gem5  v22.1.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 
30 
31 #include "base/compiler.hh"
32 #include "base/random.hh"
33 #include "debug/SimpleCache.hh"
34 #include "sim/system.hh"
35 
36 namespace gem5
37 {
38 
39 SimpleCache::SimpleCache(const SimpleCacheParams &params) :
40  ClockedObject(params),
41  latency(params.latency),
42  blockSize(params.system->cacheLineSize()),
43  capacity(params.size / blockSize),
44  memPort(params.name + ".mem_side", this),
45  blocked(false), originalPacket(nullptr), waitingPortId(-1), stats(this)
46 {
47  // Since the CPU side ports are a vector of ports, create an instance of
48  // the CPUSidePort for each connection. This member of params is
49  // automatically created depending on the name of the vector port and
50  // holds the number of connections to this port name
51  for (int i = 0; i < params.port_cpu_side_connection_count; ++i) {
52  cpuPorts.emplace_back(name() + csprintf(".cpu_side[%d]", i), i, this);
53  }
54 }
55 
56 Port &
57 SimpleCache::getPort(const std::string &if_name, PortID idx)
58 {
59  // This is the name from the Python SimObject declaration in SimpleCache.py
60  if (if_name == "mem_side") {
61  panic_if(idx != InvalidPortID,
62  "Mem side of simple cache not a vector port");
63  return memPort;
64  } else if (if_name == "cpu_side" && idx < cpuPorts.size()) {
65  // We should have already created all of the ports in the constructor
66  return cpuPorts[idx];
67  } else {
68  // pass it along to our super class
69  return ClockedObject::getPort(if_name, idx);
70  }
71 }
72 
73 void
75 {
76  // Note: This flow control is very simple since the cache is blocking.
77 
78  panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
79 
80  // If we can't send the packet across the port, store it for later.
81  DPRINTF(SimpleCache, "Sending %s to CPU\n", pkt->print());
82  if (!sendTimingResp(pkt)) {
83  DPRINTF(SimpleCache, "failed!\n");
84  blockedPacket = pkt;
85  }
86 }
87 
90 {
91  return owner->getAddrRanges();
92 }
93 
94 void
96 {
97  if (needRetry && blockedPacket == nullptr) {
98  // Only send a retry if the port is now completely free
99  needRetry = false;
100  DPRINTF(SimpleCache, "Sending retry req.\n");
101  sendRetryReq();
102  }
103 }
104 
105 void
107 {
108  // Just forward to the cache.
109  return owner->handleFunctional(pkt);
110 }
111 
112 bool
114 {
115  DPRINTF(SimpleCache, "Got request %s\n", pkt->print());
116 
117  if (blockedPacket || needRetry) {
118  // The cache may not be able to send a reply if this is blocked
119  DPRINTF(SimpleCache, "Request blocked\n");
120  needRetry = true;
121  return false;
122  }
123  // Just forward to the cache.
124  if (!owner->handleRequest(pkt, id)) {
125  DPRINTF(SimpleCache, "Request failed\n");
126  // stalling
127  needRetry = true;
128  return false;
129  } else {
130  DPRINTF(SimpleCache, "Request succeeded\n");
131  return true;
132  }
133 }
134 
135 void
137 {
138  // We should have a blocked packet if this function is called.
139  assert(blockedPacket != nullptr);
140 
141  // Grab the blocked packet.
142  PacketPtr pkt = blockedPacket;
143  blockedPacket = nullptr;
144 
145  DPRINTF(SimpleCache, "Retrying response pkt %s\n", pkt->print());
146  // Try to resend it. It's possible that it fails again.
147  sendPacket(pkt);
148 
149  // We may now be able to accept new packets
150  trySendRetry();
151 }
152 
153 void
155 {
156  // Note: This flow control is very simple since the cache is blocking.
157 
158  panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
159 
160  // If we can't send the packet across the port, store it for later.
161  if (!sendTimingReq(pkt)) {
162  blockedPacket = pkt;
163  }
164 }
165 
166 bool
168 {
169  // Just forward to the cache.
170  return owner->handleResponse(pkt);
171 }
172 
173 void
175 {
176  // We should have a blocked packet if this function is called.
177  assert(blockedPacket != nullptr);
178 
179  // Grab the blocked packet.
180  PacketPtr pkt = blockedPacket;
181  blockedPacket = nullptr;
182 
183  // Try to resend it. It's possible that it fails again.
184  sendPacket(pkt);
185 }
186 
187 void
189 {
190  owner->sendRangeChange();
191 }
192 
193 bool
195 {
196  if (blocked) {
197  // There is currently an outstanding request so we can't respond. Stall
198  return false;
199  }
200 
201  DPRINTF(SimpleCache, "Got request for addr %#x\n", pkt->getAddr());
202 
203  // This cache is now blocked waiting for the response to this packet.
204  blocked = true;
205 
206  // Store the port for when we get the response
207  assert(waitingPortId == -1);
208  waitingPortId = port_id;
209 
210  // Schedule an event after cache access latency to actually access
211  schedule(new EventFunctionWrapper([this, pkt]{ accessTiming(pkt); },
212  name() + ".accessEvent", true),
213  clockEdge(latency));
214 
215  return true;
216 }
217 
218 bool
220 {
221  assert(blocked);
222  DPRINTF(SimpleCache, "Got response for addr %#x\n", pkt->getAddr());
223 
224  // For now assume that inserts are off of the critical path and don't count
225  // for any added latency.
226  insert(pkt);
227 
229 
230  // If we had to upgrade the request packet to a full cache line, now we
231  // can use that packet to construct the response.
232  if (originalPacket != nullptr) {
233  DPRINTF(SimpleCache, "Copying data from new packet to old\n");
234  // We had to upgrade a previous packet. We can functionally deal with
235  // the cache access now. It better be a hit.
236  [[maybe_unused]] bool hit = accessFunctional(originalPacket);
237  panic_if(!hit, "Should always hit after inserting");
239  delete pkt; // We may need to delay this, I'm not sure.
240  pkt = originalPacket;
241  originalPacket = nullptr;
242  } // else, pkt contains the data it needs
243 
244  sendResponse(pkt);
245 
246  return true;
247 }
248 
250 {
251  assert(blocked);
252  DPRINTF(SimpleCache, "Sending resp for addr %#x\n", pkt->getAddr());
253 
254  int port = waitingPortId;
255 
256  // The packet is now done. We're about to put it in the port, no need for
257  // this object to continue to stall.
258  // We need to free the resource before sending the packet in case the CPU
259  // tries to send another request immediately (e.g., in the same callchain).
260  blocked = false;
261  waitingPortId = -1;
262 
263  // Simply forward to the memory port
264  cpuPorts[port].sendPacket(pkt);
265 
266  // For each of the cpu ports, if it needs to send a retry, it should do it
267  // now since this memory object may be unblocked now.
268  for (auto& port : cpuPorts) {
269  port.trySendRetry();
270  }
271 }
272 
273 void
275 {
276  if (accessFunctional(pkt)) {
277  pkt->makeResponse();
278  } else {
279  memPort.sendFunctional(pkt);
280  }
281 }
282 
283 void
285 {
286  bool hit = accessFunctional(pkt);
287 
288  DPRINTF(SimpleCache, "%s for packet: %s\n", hit ? "Hit" : "Miss",
289  pkt->print());
290 
291  if (hit) {
292  // Respond to the CPU side
293  stats.hits++; // update stats
294  DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), pkt->getSize());
295  pkt->makeResponse();
296  sendResponse(pkt);
297  } else {
298  stats.misses++; // update stats
299  missTime = curTick();
300  // Forward to the memory side.
301  // We can't directly forward the packet unless it is exactly the size
302  // of the cache line, and aligned. Check for that here.
303  Addr addr = pkt->getAddr();
304  Addr block_addr = pkt->getBlockAddr(blockSize);
305  unsigned size = pkt->getSize();
306  if (addr == block_addr && size == blockSize) {
307  // Aligned and block size. We can just forward.
308  DPRINTF(SimpleCache, "forwarding packet\n");
309  memPort.sendPacket(pkt);
310  } else {
311  DPRINTF(SimpleCache, "Upgrading packet to block size\n");
312  panic_if(addr - block_addr + size > blockSize,
313  "Cannot handle accesses that span multiple cache lines");
314  // Unaligned access to one cache block
315  assert(pkt->needsResponse());
316  MemCmd cmd;
317  if (pkt->isWrite() || pkt->isRead()) {
318  // Read the data from memory to write into the block.
319  // We'll write the data in the cache (i.e., a writeback cache)
320  cmd = MemCmd::ReadReq;
321  } else {
322  panic("Unknown packet type in upgrade size");
323  }
324 
325  // Create a new packet that is blockSize
326  PacketPtr new_pkt = new Packet(pkt->req, cmd, blockSize);
327  new_pkt->allocate();
328 
329  // Should now be block aligned
330  assert(new_pkt->getAddr() == new_pkt->getBlockAddr(blockSize));
331 
332  // Save the old packet
333  originalPacket = pkt;
334 
335  DPRINTF(SimpleCache, "forwarding packet\n");
336  memPort.sendPacket(new_pkt);
337  }
338  }
339 }
340 
341 bool
343 {
344  Addr block_addr = pkt->getBlockAddr(blockSize);
345  auto it = cacheStore.find(block_addr);
346  if (it != cacheStore.end()) {
347  if (pkt->isWrite()) {
348  // Write the data into the block in the cache
349  pkt->writeDataToBlock(it->second, blockSize);
350  } else if (pkt->isRead()) {
351  // Read the data out of the cache block into the packet
352  pkt->setDataFromBlock(it->second, blockSize);
353  } else {
354  panic("Unknown packet type!");
355  }
356  return true;
357  }
358  return false;
359 }
360 
361 void
363 {
364  // The packet should be aligned.
365  assert(pkt->getAddr() == pkt->getBlockAddr(blockSize));
366  // The address should not be in the cache
367  assert(cacheStore.find(pkt->getAddr()) == cacheStore.end());
368  // The pkt should be a response
369  assert(pkt->isResponse());
370 
371  if (cacheStore.size() >= capacity) {
372  // Select random thing to evict. This is a little convoluted since we
373  // are using a std::unordered_map. See http://bit.ly/2hrnLP2
374  int bucket, bucket_size;
375  do {
376  bucket = random_mt.random(0, (int)cacheStore.bucket_count() - 1);
377  } while ( (bucket_size = cacheStore.bucket_size(bucket)) == 0 );
378  auto block = std::next(cacheStore.begin(bucket),
379  random_mt.random(0, bucket_size - 1));
380 
381  DPRINTF(SimpleCache, "Removing addr %#x\n", block->first);
382 
383  // Write back the data.
384  // Create a new request-packet pair
385  RequestPtr req = std::make_shared<Request>(
386  block->first, blockSize, 0, 0);
387 
388  PacketPtr new_pkt = new Packet(req, MemCmd::WritebackDirty, blockSize);
389  new_pkt->dataDynamic(block->second); // This will be deleted later
390 
391  DPRINTF(SimpleCache, "Writing packet back %s\n", pkt->print());
392  // Send the write to memory
393  memPort.sendPacket(new_pkt);
394 
395  // Delete this entry
396  cacheStore.erase(block->first);
397  }
398 
399  DPRINTF(SimpleCache, "Inserting %s\n", pkt->print());
400  DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), blockSize);
401 
402  // Allocate space for the cache block data
403  uint8_t *data = new uint8_t[blockSize];
404 
405  // Insert the data and address into the cache store
406  cacheStore[pkt->getAddr()] = data;
407 
408  // Write the data into the cache
410 }
411 
414 {
415  DPRINTF(SimpleCache, "Sending new ranges\n");
416  // Just use the same ranges as whatever is on the memory side.
417  return memPort.getAddrRanges();
418 }
419 
420 void
422 {
423  for (auto& port : cpuPorts) {
424  port.sendRangeChange();
425  }
426 }
427 
429  : statistics::Group(parent),
430  ADD_STAT(hits, statistics::units::Count::get(), "Number of hits"),
431  ADD_STAT(misses, statistics::units::Count::get(), "Number of misses"),
432  ADD_STAT(missLatency, statistics::units::Tick::get(),
433  "Ticks for misses to the cache"),
434  ADD_STAT(hitRatio, statistics::units::Ratio::get(),
435  "The ratio of hits to the total accesses to the cache",
436  hits / (hits + misses))
437 {
438  missLatency.init(16); // number of buckets
439 }
440 
441 } // namespace gem5
#define DDUMP(x, data, count)
DPRINTF is a debugging trace facility that allows one to selectively enable tracing statements.
Definition: trace.hh:180
#define DPRINTF(x,...)
Definition: trace.hh:186
const char data[]
The ClockedObject class extends the SimObject with a clock and accessor functions to relate ticks to ...
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...
@ WritebackDirty
Definition: packet.hh:92
virtual std::string name() const
Definition: named.hh:47
A Packet is used to encapsulate a transfer between two objects in the memory system (e....
Definition: packet.hh:294
bool isRead() const
Definition: packet.hh:592
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:1333
Addr getAddr() const
Definition: packet.hh:805
bool isResponse() const
Definition: packet.hh:597
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition: packet.cc:368
bool needsResponse() const
Definition: packet.hh:607
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:1059
bool isWrite() const
Definition: packet.hh:593
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:1299
Addr getBlockAddr(unsigned int blk_size) const
Definition: packet.hh:829
RequestPtr req
A pointer to the original request.
Definition: packet.hh:376
unsigned getSize() const
Definition: packet.hh:815
void dataDynamic(T *p)
Set the data pointer to a value that should have delete [] called on it.
Definition: packet.hh:1200
const T * getConstPtr() const
Definition: packet.hh:1221
void allocate()
Allocate memory for the packet.
Definition: packet.hh:1354
Ports are used to interface objects to each other.
Definition: port.hh:62
AddrRangeList getAddrRanges() const
Get the address ranges of the connected responder port.
Definition: port.cc:152
void sendFunctional(PacketPtr pkt) const
Send a functional request packet, where the data is instantly updated everywhere in the memory system...
Definition: port.hh:485
bool sendTimingResp(PacketPtr pkt)
Attempt to send a timing response to the request port by calling its corresponding receive function.
Definition: port.hh:370
bool recvTimingReq(PacketPtr pkt) override
Receive a timing request from the request port.
void sendPacket(PacketPtr pkt)
Send a packet across this port.
Definition: simple_cache.cc:74
PacketPtr blockedPacket
If we tried to send a packet and it was blocked, store it here.
Definition: simple_cache.hh:70
AddrRangeList getAddrRanges() const override
Get a list of the non-overlapping address ranges the owner is responsible for.
Definition: simple_cache.cc:89
void recvFunctional(PacketPtr pkt) override
Receive a functional request packet from the request port.
void trySendRetry()
Send a retry to the peer port only if it is needed.
Definition: simple_cache.cc:95
void recvRespRetry() override
Called by the request port if sendTimingResp was called on this response port (causing recvTimingResp...
void sendPacket(PacketPtr pkt)
Send a packet across this port.
bool recvTimingResp(PacketPtr pkt) override
Receive a timing response from the response port.
void recvRangeChange() override
Called to receive an address range change from the peer response port.
void recvReqRetry() override
Called by the response port if sendTimingReq was called on this request port (causing recvTimingReq t...
A very simple cache object.
Definition: simple_cache.hh:50
const unsigned capacity
Number of blocks in the cache (size of cache / block size)
void handleFunctional(PacketPtr pkt)
Handle a packet functionally.
PacketPtr originalPacket
Packet that we are currently handling.
bool blocked
True if this cache is currently blocked waiting for a response.
Port & getPort(const std::string &if_name, PortID idx=InvalidPortID) override
Get a port with a given name and index.
Definition: simple_cache.cc:57
bool handleResponse(PacketPtr pkt)
Handle the respone from the memory side.
const unsigned blockSize
The block size for the cache.
void sendRangeChange() const
Tell the CPU side to ask for our memory ranges.
SimpleCache(const SimpleCacheParams &params)
constructor
Definition: simple_cache.cc:39
void insert(PacketPtr pkt)
Insert a block into the cache.
gem5::SimpleCache::SimpleCacheStats stats
AddrRangeList getAddrRanges() const
Return the address ranges this cache is responsible for.
bool handleRequest(PacketPtr pkt, int port_id)
Handle the request from the CPU side.
MemSidePort memPort
Instantiation of the memory-side port.
Tick missTime
For tracking the miss latency.
std::unordered_map< Addr, uint8_t * > cacheStore
An incredibly simple cache storage. Maps block addresses to data.
std::vector< CPUSidePort > cpuPorts
Instantiation of the CPU-side port.
void accessTiming(PacketPtr pkt)
Access the cache for a timing access.
void sendResponse(PacketPtr pkt)
Send the packet to the CPU side.
bool accessFunctional(PacketPtr pkt)
This is where we actually update / read from the cache.
const Cycles latency
Latency to check the cache. Number of cycles for both hit and miss.
int waitingPortId
The port to send the response when we recieve it back.
void sample(const U &v, int n=1)
Add a value to the distribtion n times.
Definition: statistics.hh:1328
Statistics container.
Definition: group.hh:94
Histogram & init(size_type size)
Set the parameters of this histogram.
Definition: statistics.hh:2154
#define ADD_STAT(n,...)
Convenience macro to add a stat to a statistics group.
Definition: group.hh:75
Random random_mt
Definition: random.cc:99
std::enable_if_t< std::is_integral_v< T >, T > random()
Use the SFINAE idiom to choose an implementation based on whether the type is integral or floating po...
Definition: random.hh:90
void schedule(Event &event, Tick when)
Definition: eventq.hh:1019
#define panic(...)
This implements a cprintf based panic() function.
Definition: logging.hh:178
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:204
const Params & params() const
Definition: sim_object.hh:176
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
Bitfield< 7 > i
Definition: misc_types.hh:67
Bitfield< 15 > system
Definition: misc.hh:1004
Bitfield< 3 > addr
Definition: types.hh:84
ProbePointArg< PacketInfo > Packet
Packet probe point.
Definition: mem.hh:109
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
std::shared_ptr< Request > RequestPtr
Definition: request.hh:92
const PortID InvalidPortID
Definition: types.hh:246
Tick curTick()
The universal simulation clock.
Definition: cur_tick.hh:46
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
int16_t PortID
Port index/ID type, and a symbolic name for an invalid port id.
Definition: types.hh:245
uint64_t Tick
Tick count type.
Definition: types.hh:58
std::string csprintf(const char *format, const Args &...args)
Definition: cprintf.hh:161
SimpleCacheStats(statistics::Group *parent)
statistics::Histogram missLatency
const std::string & name()
Definition: trace.cc:49

Generated on Wed Dec 21 2022 10:22:36 for gem5 by doxygen 1.9.1