gem5 v24.0.0.0
Loading...
Searching...
No Matches
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
36namespace gem5
37{
38
39SimpleCache::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
56Port &
57SimpleCache::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") {
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
73void
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
94void
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
105void
107{
108 // Just forward to the cache.
109 return owner->handleFunctional(pkt);
110}
111
112bool
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
135void
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
153void
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
166bool
168{
169 // Just forward to the cache.
170 return owner->handleResponse(pkt);
171}
172
173void
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
187void
189{
190 owner->sendRangeChange();
191}
192
193bool
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),
214
215 return true;
216}
217
218bool
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
273void
275{
276 if (accessFunctional(pkt)) {
277 pkt->makeResponse();
278 } else {
280 }
281}
282
283void
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
341bool
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
361void
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
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
420void
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:204
#define DPRINTF(x,...)
Definition trace.hh:210
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.
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
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: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 - Pranith Kumar Copyright (c) 2020 Inria 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 Tue Jun 18 2024 16:24:04 for gem5 by doxygen 1.11.0