2 * Copyright (c) 2017 Jason Lowe-Power
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.
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.
28 * Authors: Jason Lowe-Power
31 #include "learning_gem5/part2/simple_cache.hh"
33 #include "base/random.hh"
34 #include "debug/SimpleCache.hh"
35 #include "sim/system.hh"
37 SimpleCache::SimpleCache(SimpleCacheParams
*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)
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);
55 SimpleCache::getMasterPort(const std::string
& if_name
, PortID idx
)
57 panic_if(idx
!= InvalidPortID
, "This object doesn't support vector ports");
59 // This is the name from the Python SimObject declaration in SimpleCache.py
60 if (if_name
== "mem_side") {
63 // pass it along to our super class
64 return MemObject::getMasterPort(if_name
, idx
);
69 SimpleCache::getSlavePort(const std::string
& if_name
, PortID idx
)
71 // This is the name from the Python SimObject declaration (SimpleMemobj.py)
72 if (if_name
== "cpu_side" && idx
< cpuPorts
.size()) {
73 // We should have already created all of the ports in the constructor
76 // pass it along to our super class
77 return MemObject::getSlavePort(if_name
, idx
);
82 SimpleCache::CPUSidePort::sendPacket(PacketPtr pkt
)
84 // Note: This flow control is very simple since the cache is blocking.
86 panic_if(blockedPacket
!= nullptr, "Should never try to send if blocked!");
88 // If we can't send the packet across the port, store it for later.
89 DPRINTF(SimpleCache
, "Sending %s to CPU\n", pkt
->print());
90 if (!sendTimingResp(pkt
)) {
91 DPRINTF(SimpleCache
, "failed!\n");
97 SimpleCache::CPUSidePort::getAddrRanges() const
99 return owner
->getAddrRanges();
103 SimpleCache::CPUSidePort::trySendRetry()
105 if (needRetry
&& blockedPacket
== nullptr) {
106 // Only send a retry if the port is now completely free
108 DPRINTF(SimpleCache
, "Sending retry req.\n");
114 SimpleCache::CPUSidePort::recvFunctional(PacketPtr pkt
)
116 // Just forward to the cache.
117 return owner
->handleFunctional(pkt
);
121 SimpleCache::CPUSidePort::recvTimingReq(PacketPtr pkt
)
123 DPRINTF(SimpleCache
, "Got request %s\n", pkt
->print());
125 if (blockedPacket
|| needRetry
) {
126 // The cache may not be able to send a reply if this is blocked
127 DPRINTF(SimpleCache
, "Request blocked\n");
131 // Just forward to the cache.
132 if (!owner
->handleRequest(pkt
, id
)) {
133 DPRINTF(SimpleCache
, "Request failed\n");
138 DPRINTF(SimpleCache
, "Request succeeded\n");
144 SimpleCache::CPUSidePort::recvRespRetry()
146 // We should have a blocked packet if this function is called.
147 assert(blockedPacket
!= nullptr);
149 // Grab the blocked packet.
150 PacketPtr pkt
= blockedPacket
;
151 blockedPacket
= nullptr;
153 DPRINTF(SimpleCache
, "Retrying response pkt %s\n", pkt
->print());
154 // Try to resend it. It's possible that it fails again.
157 // We may now be able to accept new packets
162 SimpleCache::MemSidePort::sendPacket(PacketPtr pkt
)
164 // Note: This flow control is very simple since the cache is blocking.
166 panic_if(blockedPacket
!= nullptr, "Should never try to send if blocked!");
168 // If we can't send the packet across the port, store it for later.
169 if (!sendTimingReq(pkt
)) {
175 SimpleCache::MemSidePort::recvTimingResp(PacketPtr pkt
)
177 // Just forward to the cache.
178 return owner
->handleResponse(pkt
);
182 SimpleCache::MemSidePort::recvReqRetry()
184 // We should have a blocked packet if this function is called.
185 assert(blockedPacket
!= nullptr);
187 // Grab the blocked packet.
188 PacketPtr pkt
= blockedPacket
;
189 blockedPacket
= nullptr;
191 // Try to resend it. It's possible that it fails again.
196 SimpleCache::MemSidePort::recvRangeChange()
198 owner
->sendRangeChange();
202 SimpleCache::handleRequest(PacketPtr pkt
, int port_id
)
205 // There is currently an outstanding request so we can't respond. Stall
209 DPRINTF(SimpleCache
, "Got request for addr %#x\n", pkt
->getAddr());
211 // This cache is now blocked waiting for the response to this packet.
214 // Store the port for when we get the response
215 assert(waitingPortId
== -1);
216 waitingPortId
= port_id
;
218 // Schedule an event after cache access latency to actually access
219 schedule(new EventFunctionWrapper([this, pkt
]{ accessTiming(pkt
); },
220 name() + ".accessEvent", true),
227 SimpleCache::handleResponse(PacketPtr pkt
)
230 DPRINTF(SimpleCache
, "Got response for addr %#x\n", pkt
->getAddr());
232 // For now assume that inserts are off of the critical path and don't count
233 // for any added latency.
236 missLatency
.sample(curTick() - missTime
);
238 // If we had to upgrade the request packet to a full cache line, now we
239 // can use that packet to construct the response.
240 if (originalPacket
!= nullptr) {
241 DPRINTF(SimpleCache
, "Copying data from new packet to old\n");
242 // We had to upgrade a previous packet. We can functionally deal with
243 // the cache access now. It better be a hit.
244 bool hit M5_VAR_USED
= accessFunctional(originalPacket
);
245 panic_if(!hit
, "Should always hit after inserting");
246 originalPacket
->makeResponse();
247 delete pkt
; // We may need to delay this, I'm not sure.
248 pkt
= originalPacket
;
249 originalPacket
= nullptr;
250 } // else, pkt contains the data it needs
257 void SimpleCache::sendResponse(PacketPtr pkt
)
260 DPRINTF(SimpleCache
, "Sending resp for addr %#x\n", pkt
->getAddr());
262 int port
= waitingPortId
;
264 // The packet is now done. We're about to put it in the port, no need for
265 // this object to continue to stall.
266 // We need to free the resource before sending the packet in case the CPU
267 // tries to send another request immediately (e.g., in the same callchain).
271 // Simply forward to the memory port
272 cpuPorts
[port
].sendPacket(pkt
);
274 // For each of the cpu ports, if it needs to send a retry, it should do it
275 // now since this memory object may be unblocked now.
276 for (auto& port
: cpuPorts
) {
282 SimpleCache::handleFunctional(PacketPtr pkt
)
284 if (accessFunctional(pkt
)) {
287 memPort
.sendFunctional(pkt
);
292 SimpleCache::accessTiming(PacketPtr pkt
)
294 bool hit
= accessFunctional(pkt
);
296 DPRINTF(SimpleCache
, "%s for packet: %s\n", hit
? "Hit" : "Miss",
300 // Respond to the CPU side
301 hits
++; // update stats
302 DDUMP(SimpleCache
, pkt
->getConstPtr
<uint8_t>(), pkt
->getSize());
306 misses
++; // update stats
307 missTime
= curTick();
308 // Forward to the memory side.
309 // We can't directly forward the packet unless it is exactly the size
310 // of the cache line, and aligned. Check for that here.
311 Addr addr
= pkt
->getAddr();
312 Addr block_addr
= pkt
->getBlockAddr(blockSize
);
313 unsigned size
= pkt
->getSize();
314 if (addr
== block_addr
&& size
== blockSize
) {
315 // Aligned and block size. We can just forward.
316 DPRINTF(SimpleCache
, "forwarding packet\n");
317 memPort
.sendPacket(pkt
);
319 DPRINTF(SimpleCache
, "Upgrading packet to block size\n");
320 panic_if(addr
- block_addr
+ size
> blockSize
,
321 "Cannot handle accesses that span multiple cache lines");
322 // Unaligned access to one cache block
323 assert(pkt
->needsResponse());
325 if (pkt
->isWrite() || pkt
->isRead()) {
326 // Read the data from memory to write into the block.
327 // We'll write the data in the cache (i.e., a writeback cache)
328 cmd
= MemCmd::ReadReq
;
330 panic("Unknown packet type in upgrade size");
333 // Create a new packet that is blockSize
334 PacketPtr new_pkt
= new Packet(pkt
->req
, cmd
, blockSize
);
337 // Should now be block aligned
338 assert(new_pkt
->getAddr() == new_pkt
->getBlockAddr(blockSize
));
340 // Save the old packet
341 originalPacket
= pkt
;
343 DPRINTF(SimpleCache
, "forwarding packet\n");
344 memPort
.sendPacket(new_pkt
);
350 SimpleCache::accessFunctional(PacketPtr pkt
)
352 Addr block_addr
= pkt
->getBlockAddr(blockSize
);
353 auto it
= cacheStore
.find(block_addr
);
354 if (it
!= cacheStore
.end()) {
355 if (pkt
->isWrite()) {
356 // Write the data into the block in the cache
357 pkt
->writeDataToBlock(it
->second
, blockSize
);
358 } else if (pkt
->isRead()) {
359 // Read the data out of the cache block into the packet
360 pkt
->setDataFromBlock(it
->second
, blockSize
);
362 panic("Unknown packet type!");
370 SimpleCache::insert(PacketPtr pkt
)
372 // The packet should be aligned.
373 assert(pkt
->getAddr() == pkt
->getBlockAddr(blockSize
));
374 // The address should not be in the cache
375 assert(cacheStore
.find(pkt
->getAddr()) == cacheStore
.end());
376 // The pkt should be a response
377 assert(pkt
->isResponse());
379 if (cacheStore
.size() >= capacity
) {
380 // Select random thing to evict. This is a little convoluted since we
381 // are using a std::unordered_map. See http://bit.ly/2hrnLP2
382 int bucket
, bucket_size
;
384 bucket
= random_mt
.random(0, (int)cacheStore
.bucket_count() - 1);
385 } while ( (bucket_size
= cacheStore
.bucket_size(bucket
)) == 0 );
386 auto block
= std::next(cacheStore
.begin(bucket
),
387 random_mt
.random(0, bucket_size
- 1));
389 DPRINTF(SimpleCache
, "Removing addr %#x\n", block
->first
);
391 // Write back the data.
392 // Create a new request-packet pair
393 RequestPtr req
= std::make_shared
<Request
>(
394 block
->first
, blockSize
, 0, 0);
396 PacketPtr new_pkt
= new Packet(req
, MemCmd::WritebackDirty
, blockSize
);
397 new_pkt
->dataDynamic(block
->second
); // This will be deleted later
399 DPRINTF(SimpleCache
, "Writing packet back %s\n", pkt
->print());
400 // Send the write to memory
401 memPort
.sendPacket(new_pkt
);
404 cacheStore
.erase(block
->first
);
407 DPRINTF(SimpleCache
, "Inserting %s\n", pkt
->print());
408 DDUMP(SimpleCache
, pkt
->getConstPtr
<uint8_t>(), blockSize
);
410 // Allocate space for the cache block data
411 uint8_t *data
= new uint8_t[blockSize
];
413 // Insert the data and address into the cache store
414 cacheStore
[pkt
->getAddr()] = data
;
416 // Write the data into the cache
417 pkt
->writeDataToBlock(data
, blockSize
);
421 SimpleCache::getAddrRanges() const
423 DPRINTF(SimpleCache
, "Sending new ranges\n");
424 // Just use the same ranges as whatever is on the memory side.
425 return memPort
.getAddrRanges();
429 SimpleCache::sendRangeChange() const
431 for (auto& port
: cpuPorts
) {
432 port
.sendRangeChange();
437 SimpleCache::regStats()
439 // If you don't do this you get errors about uninitialized stats.
440 MemObject::regStats();
442 hits
.name(name() + ".hits")
443 .desc("Number of hits")
446 misses
.name(name() + ".misses")
447 .desc("Number of misses")
450 missLatency
.name(name() + ".missLatency")
451 .desc("Ticks for misses to the cache")
452 .init(16) // number of buckets
455 hitRatio
.name(name() + ".hitRatio")
456 .desc("The ratio of hits to the total accesses to the cache")
459 hitRatio
= hits
/ (hits
+ misses
);
465 SimpleCacheParams::create()
467 return new SimpleCache(this);