gem5 v24.1.0.1
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 "debug/SimpleCache.hh"
33#include "sim/system.hh"
34
35namespace gem5
36{
37
38SimpleCache::SimpleCache(const SimpleCacheParams &params) :
39 ClockedObject(params),
40 latency(params.latency),
41 blockSize(params.system->cacheLineSize()),
42 capacity(params.size / blockSize),
43 memPort(params.name + ".mem_side", this),
44 blocked(false), originalPacket(nullptr), waitingPortId(-1), stats(this)
45{
46 // Since the CPU side ports are a vector of ports, create an instance of
47 // the CPUSidePort for each connection. This member of params is
48 // automatically created depending on the name of the vector port and
49 // holds the number of connections to this port name
50 for (int i = 0; i < params.port_cpu_side_connection_count; ++i) {
51 cpuPorts.emplace_back(name() + csprintf(".cpu_side[%d]", i), i, this);
52 }
53}
54
55Port &
56SimpleCache::getPort(const std::string &if_name, PortID idx)
57{
58 // This is the name from the Python SimObject declaration in SimpleCache.py
59 if (if_name == "mem_side") {
61 "Mem side of simple cache not a vector port");
62 return memPort;
63 } else if (if_name == "cpu_side" && idx < cpuPorts.size()) {
64 // We should have already created all of the ports in the constructor
65 return cpuPorts[idx];
66 } else {
67 // pass it along to our super class
68 return ClockedObject::getPort(if_name, idx);
69 }
70}
71
72void
74{
75 // Note: This flow control is very simple since the cache is blocking.
76
77 panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
78
79 // If we can't send the packet across the port, store it for later.
80 DPRINTF(SimpleCache, "Sending %s to CPU\n", pkt->print());
81 if (!sendTimingResp(pkt)) {
82 DPRINTF(SimpleCache, "failed!\n");
83 blockedPacket = pkt;
84 }
85}
86
89{
90 return owner->getAddrRanges();
91}
92
93void
95{
96 if (needRetry && blockedPacket == nullptr) {
97 // Only send a retry if the port is now completely free
98 needRetry = false;
99 DPRINTF(SimpleCache, "Sending retry req.\n");
100 sendRetryReq();
101 }
102}
103
104void
106{
107 // Just forward to the cache.
108 return owner->handleFunctional(pkt);
109}
110
111bool
113{
114 DPRINTF(SimpleCache, "Got request %s\n", pkt->print());
115
116 if (blockedPacket || needRetry) {
117 // The cache may not be able to send a reply if this is blocked
118 DPRINTF(SimpleCache, "Request blocked\n");
119 needRetry = true;
120 return false;
121 }
122 // Just forward to the cache.
123 if (!owner->handleRequest(pkt, id)) {
124 DPRINTF(SimpleCache, "Request failed\n");
125 // stalling
126 needRetry = true;
127 return false;
128 } else {
129 DPRINTF(SimpleCache, "Request succeeded\n");
130 return true;
131 }
132}
133
134void
136{
137 // We should have a blocked packet if this function is called.
138 assert(blockedPacket != nullptr);
139
140 // Grab the blocked packet.
141 PacketPtr pkt = blockedPacket;
142 blockedPacket = nullptr;
143
144 DPRINTF(SimpleCache, "Retrying response pkt %s\n", pkt->print());
145 // Try to resend it. It's possible that it fails again.
146 sendPacket(pkt);
147
148 // We may now be able to accept new packets
149 trySendRetry();
150}
151
152void
154{
155 // Note: This flow control is very simple since the cache is blocking.
156
157 panic_if(blockedPacket != nullptr, "Should never try to send if blocked!");
158
159 // If we can't send the packet across the port, store it for later.
160 if (!sendTimingReq(pkt)) {
161 blockedPacket = pkt;
162 }
163}
164
165bool
167{
168 // Just forward to the cache.
169 return owner->handleResponse(pkt);
170}
171
172void
174{
175 // We should have a blocked packet if this function is called.
176 assert(blockedPacket != nullptr);
177
178 // Grab the blocked packet.
179 PacketPtr pkt = blockedPacket;
180 blockedPacket = nullptr;
181
182 // Try to resend it. It's possible that it fails again.
183 sendPacket(pkt);
184}
185
186void
188{
189 owner->sendRangeChange();
190}
191
192bool
194{
195 if (blocked) {
196 // There is currently an outstanding request so we can't respond. Stall
197 return false;
198 }
199
200 DPRINTF(SimpleCache, "Got request for addr %#x\n", pkt->getAddr());
201
202 // This cache is now blocked waiting for the response to this packet.
203 blocked = true;
204
205 // Store the port for when we get the response
206 assert(waitingPortId == -1);
207 waitingPortId = port_id;
208
209 // Schedule an event after cache access latency to actually access
210 schedule(new EventFunctionWrapper([this, pkt]{ accessTiming(pkt); },
211 name() + ".accessEvent", true),
213
214 return true;
215}
216
217bool
219{
220 assert(blocked);
221 DPRINTF(SimpleCache, "Got response for addr %#x\n", pkt->getAddr());
222
223 // For now assume that inserts are off of the critical path and don't count
224 // for any added latency.
225 insert(pkt);
226
228
229 // If we had to upgrade the request packet to a full cache line, now we
230 // can use that packet to construct the response.
231 if (originalPacket != nullptr) {
232 DPRINTF(SimpleCache, "Copying data from new packet to old\n");
233 // We had to upgrade a previous packet. We can functionally deal with
234 // the cache access now. It better be a hit.
235 [[maybe_unused]] bool hit = accessFunctional(originalPacket);
236 panic_if(!hit, "Should always hit after inserting");
238 delete pkt; // We may need to delay this, I'm not sure.
239 pkt = originalPacket;
240 originalPacket = nullptr;
241 } // else, pkt contains the data it needs
242
243 sendResponse(pkt);
244
245 return true;
246}
247
249{
250 assert(blocked);
251 DPRINTF(SimpleCache, "Sending resp for addr %#x\n", pkt->getAddr());
252
253 int port = waitingPortId;
254
255 // The packet is now done. We're about to put it in the port, no need for
256 // this object to continue to stall.
257 // We need to free the resource before sending the packet in case the CPU
258 // tries to send another request immediately (e.g., in the same callchain).
259 blocked = false;
260 waitingPortId = -1;
261
262 // Simply forward to the memory port
263 cpuPorts[port].sendPacket(pkt);
264
265 // For each of the cpu ports, if it needs to send a retry, it should do it
266 // now since this memory object may be unblocked now.
267 for (auto& port : cpuPorts) {
268 port.trySendRetry();
269 }
270}
271
272void
274{
275 if (accessFunctional(pkt)) {
276 pkt->makeResponse();
277 } else {
279 }
280}
281
282void
284{
285 bool hit = accessFunctional(pkt);
286
287 DPRINTF(SimpleCache, "%s for packet: %s\n", hit ? "Hit" : "Miss",
288 pkt->print());
289
290 if (hit) {
291 // Respond to the CPU side
292 stats.hits++; // update stats
293 DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), pkt->getSize());
294 pkt->makeResponse();
295 sendResponse(pkt);
296 } else {
297 stats.misses++; // update stats
298 missTime = curTick();
299 // Forward to the memory side.
300 // We can't directly forward the packet unless it is exactly the size
301 // of the cache line, and aligned. Check for that here.
302 Addr addr = pkt->getAddr();
303 Addr block_addr = pkt->getBlockAddr(blockSize);
304 unsigned size = pkt->getSize();
305 if (addr == block_addr && size == blockSize) {
306 // Aligned and block size. We can just forward.
307 DPRINTF(SimpleCache, "forwarding packet\n");
308 memPort.sendPacket(pkt);
309 } else {
310 DPRINTF(SimpleCache, "Upgrading packet to block size\n");
311 panic_if(addr - block_addr + size > blockSize,
312 "Cannot handle accesses that span multiple cache lines");
313 // Unaligned access to one cache block
314 assert(pkt->needsResponse());
315 MemCmd cmd;
316 if (pkt->isWrite() || pkt->isRead()) {
317 // Read the data from memory to write into the block.
318 // We'll write the data in the cache (i.e., a writeback cache)
319 cmd = MemCmd::ReadReq;
320 } else {
321 panic("Unknown packet type in upgrade size");
322 }
323
324 // Create a new packet that is blockSize
325 PacketPtr new_pkt = new Packet(pkt->req, cmd, blockSize);
326 new_pkt->allocate();
327
328 // Should now be block aligned
329 assert(new_pkt->getAddr() == new_pkt->getBlockAddr(blockSize));
330
331 // Save the old packet
332 originalPacket = pkt;
333
334 DPRINTF(SimpleCache, "forwarding packet\n");
335 memPort.sendPacket(new_pkt);
336 }
337 }
338}
339
340bool
342{
343 Addr block_addr = pkt->getBlockAddr(blockSize);
344 auto it = cacheStore.find(block_addr);
345 if (it != cacheStore.end()) {
346 if (pkt->isWrite()) {
347 // Write the data into the block in the cache
348 pkt->writeDataToBlock(it->second, blockSize);
349 } else if (pkt->isRead()) {
350 // Read the data out of the cache block into the packet
351 pkt->setDataFromBlock(it->second, blockSize);
352 } else {
353 panic("Unknown packet type!");
354 }
355 return true;
356 }
357 return false;
358}
359
360void
362{
363 // The packet should be aligned.
364 assert(pkt->getAddr() == pkt->getBlockAddr(blockSize));
365 // The address should not be in the cache
366 assert(cacheStore.find(pkt->getAddr()) == cacheStore.end());
367 // The pkt should be a response
368 assert(pkt->isResponse());
369
370 if (cacheStore.size() >= capacity) {
371 // Select random thing to evict. This is a little convoluted since we
372 // are using a std::unordered_map. See http://bit.ly/2hrnLP2
373 int bucket, bucket_size;
374 do {
375 bucket = rng->random(0, (int)cacheStore.bucket_count() - 1);
376 } while ( (bucket_size = cacheStore.bucket_size(bucket)) == 0 );
377 auto block = std::next(cacheStore.begin(bucket),
378 rng->random(0, bucket_size - 1));
379
380 DPRINTF(SimpleCache, "Removing addr %#x\n", block->first);
381
382 // Write back the data.
383 // Create a new request-packet pair
384 RequestPtr req = std::make_shared<Request>(
385 block->first, blockSize, 0, 0);
386
388 new_pkt->dataDynamic(block->second); // This will be deleted later
389
390 DPRINTF(SimpleCache, "Writing packet back %s\n", pkt->print());
391 // Send the write to memory
392 memPort.sendPacket(new_pkt);
393
394 // Delete this entry
395 cacheStore.erase(block->first);
396 }
397
398 DPRINTF(SimpleCache, "Inserting %s\n", pkt->print());
399 DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), blockSize);
400
401 // Allocate space for the cache block data
402 uint8_t *data = new uint8_t[blockSize];
403
404 // Insert the data and address into the cache store
405 cacheStore[pkt->getAddr()] = data;
406
407 // Write the data into the cache
409}
410
413{
414 DPRINTF(SimpleCache, "Sending new ranges\n");
415 // Just use the same ranges as whatever is on the memory side.
416 return memPort.getAddrRanges();
417}
418
419void
421{
422 for (auto& port : cpuPorts) {
423 port.sendRangeChange();
424 }
425}
426
428 : statistics::Group(parent),
429 ADD_STAT(hits, statistics::units::Count::get(), "Number of hits"),
430 ADD_STAT(misses, statistics::units::Count::get(), "Number of misses"),
431 ADD_STAT(missLatency, statistics::units::Tick::get(),
432 "Ticks for misses to the cache"),
433 ADD_STAT(hitRatio, statistics::units::Ratio::get(),
434 "The ratio of hits to the total accesses to the cache",
435 hits / (hits + misses))
436{
437 missLatency.init(16); // number of buckets
438}
439
440} // namespace gem5
#define DDUMP(x, data, count)
DPRINTF is a debugging trace facility that allows one to selectively enable tracing statements.
Definition trace.hh:203
#define DPRINTF(x,...)
Definition trace.hh:209
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:93
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:295
bool isRead() const
Definition packet.hh:593
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:1346
Addr getAddr() const
Definition packet.hh:807
bool isResponse() const
Definition packet.hh:598
void print(std::ostream &o, int verbosity=0, const std::string &prefix="") const
Definition packet.cc:368
bool needsResponse() const
Definition packet.hh:608
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:1062
bool isWrite() const
Definition packet.hh:594
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:1312
Addr getBlockAddr(unsigned int blk_size) const
Definition packet.hh:831
RequestPtr req
A pointer to the original request.
Definition packet.hh:377
unsigned getSize() const
Definition packet.hh:817
const T * getConstPtr() const
Definition packet.hh:1234
void dataDynamic(T *p)
Set the data pointer to a value that should have delete [] called on it.
Definition packet.hh:1213
void allocate()
Allocate memory for the packet.
Definition packet.hh:1367
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:172
void sendFunctional(PacketPtr pkt) const
Send a functional request packet, where the data is instantly updated everywhere in the memory system...
Definition port.hh:579
bool sendTimingResp(PacketPtr pkt)
Attempt to send a timing response to the request port by calling its corresponding receive function.
Definition port.hh:454
bool recvTimingReq(PacketPtr pkt) override
Receive a timing request from the request port.
void sendPacket(PacketPtr pkt)
Send a packet across this port.
PacketPtr blockedPacket
If we tried to send a packet and it was blocked, store it here.
AddrRangeList getAddrRanges() const override
Get a list of the non-overlapping address ranges the owner is responsible for.
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.
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.
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.
Random::RandomPtr rng
bool blocked
True if this cache is currently blocked waiting for a response.
const Addr blockSize
The block size for the cache.
Port & getPort(const std::string &if_name, PortID idx=InvalidPortID) override
Get a port with a given name and index.
bool handleResponse(PacketPtr pkt)
Handle the respone from the memory side.
void sendRangeChange() const
Tell the CPU side to ask for our memory ranges.
SimpleCache(const SimpleCacheParams &params)
constructor
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.
Statistics container.
Definition group.hh:93
Histogram & init(size_type size)
Set the parameters of this histogram.
#define ADD_STAT(n,...)
Convenience macro to add a stat to a statistics group.
Definition group.hh:75
void schedule(Event &event, Tick when)
Definition eventq.hh:1012
#define panic(...)
This implements a cprintf based panic() function.
Definition logging.hh:188
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition logging.hh:214
const Params & params() const
virtual Port & getPort(const std::string &if_name, PortID idx=InvalidPortID)
Get a port with a given name and index.
Bitfield< 7 > i
Definition misc_types.hh:67
Bitfield< 15 > system
Definition misc.hh:1032
Bitfield< 3 > addr
Definition types.hh:84
Copyright (c) 2024 Arm Limited All rights reserved.
Definition binary32.hh:36
std::shared_ptr< Request > RequestPtr
Definition request.hh:94
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)
const std::string & name()
Definition trace.cc:48

Generated on Mon Jan 13 2025 04:28:37 for gem5 by doxygen 1.9.8