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 31#include "learning_gem5/part2/simple_cache.hh" 32 33#include "base/random.hh" 34#include "debug/SimpleCache.hh" 35#include "sim/system.hh" 36 37SimpleCache::SimpleCache(SimpleCacheParams *params) : 38 MemObject(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
| 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 31#include "learning_gem5/part2/simple_cache.hh" 32 33#include "base/random.hh" 34#include "debug/SimpleCache.hh" 35#include "sim/system.hh" 36 37SimpleCache::SimpleCache(SimpleCacheParams *params) : 38 MemObject(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
|
78 } 79} 80 81void 82SimpleCache::CPUSidePort::sendPacket(PacketPtr pkt) 83{ 84 // Note: This flow control is very simple since the cache is blocking. 85 86 panic_if(blockedPacket != nullptr, "Should never try to send if blocked!"); 87 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"); 92 blockedPacket = pkt; 93 } 94} 95 96AddrRangeList 97SimpleCache::CPUSidePort::getAddrRanges() const 98{ 99 return owner->getAddrRanges(); 100} 101 102void 103SimpleCache::CPUSidePort::trySendRetry() 104{ 105 if (needRetry && blockedPacket == nullptr) { 106 // Only send a retry if the port is now completely free 107 needRetry = false; 108 DPRINTF(SimpleCache, "Sending retry req.\n"); 109 sendRetryReq(); 110 } 111} 112 113void 114SimpleCache::CPUSidePort::recvFunctional(PacketPtr pkt) 115{ 116 // Just forward to the cache. 117 return owner->handleFunctional(pkt); 118} 119 120bool 121SimpleCache::CPUSidePort::recvTimingReq(PacketPtr pkt) 122{ 123 DPRINTF(SimpleCache, "Got request %s\n", pkt->print()); 124 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"); 128 needRetry = true; 129 return false; 130 } 131 // Just forward to the cache. 132 if (!owner->handleRequest(pkt, id)) { 133 DPRINTF(SimpleCache, "Request failed\n"); 134 // stalling 135 needRetry = true; 136 return false; 137 } else { 138 DPRINTF(SimpleCache, "Request succeeded\n"); 139 return true; 140 } 141} 142 143void 144SimpleCache::CPUSidePort::recvRespRetry() 145{ 146 // We should have a blocked packet if this function is called. 147 assert(blockedPacket != nullptr); 148 149 // Grab the blocked packet. 150 PacketPtr pkt = blockedPacket; 151 blockedPacket = nullptr; 152 153 DPRINTF(SimpleCache, "Retrying response pkt %s\n", pkt->print()); 154 // Try to resend it. It's possible that it fails again. 155 sendPacket(pkt); 156 157 // We may now be able to accept new packets 158 trySendRetry(); 159} 160 161void 162SimpleCache::MemSidePort::sendPacket(PacketPtr pkt) 163{ 164 // Note: This flow control is very simple since the cache is blocking. 165 166 panic_if(blockedPacket != nullptr, "Should never try to send if blocked!"); 167 168 // If we can't send the packet across the port, store it for later. 169 if (!sendTimingReq(pkt)) { 170 blockedPacket = pkt; 171 } 172} 173 174bool 175SimpleCache::MemSidePort::recvTimingResp(PacketPtr pkt) 176{ 177 // Just forward to the cache. 178 return owner->handleResponse(pkt); 179} 180 181void 182SimpleCache::MemSidePort::recvReqRetry() 183{ 184 // We should have a blocked packet if this function is called. 185 assert(blockedPacket != nullptr); 186 187 // Grab the blocked packet. 188 PacketPtr pkt = blockedPacket; 189 blockedPacket = nullptr; 190 191 // Try to resend it. It's possible that it fails again. 192 sendPacket(pkt); 193} 194 195void 196SimpleCache::MemSidePort::recvRangeChange() 197{ 198 owner->sendRangeChange(); 199} 200 201bool 202SimpleCache::handleRequest(PacketPtr pkt, int port_id) 203{ 204 if (blocked) { 205 // There is currently an outstanding request so we can't respond. Stall 206 return false; 207 } 208 209 DPRINTF(SimpleCache, "Got request for addr %#x\n", pkt->getAddr()); 210 211 // This cache is now blocked waiting for the response to this packet. 212 blocked = true; 213 214 // Store the port for when we get the response 215 assert(waitingPortId == -1); 216 waitingPortId = port_id; 217 218 // Schedule an event after cache access latency to actually access 219 schedule(new EventFunctionWrapper([this, pkt]{ accessTiming(pkt); }, 220 name() + ".accessEvent", true), 221 clockEdge(latency)); 222 223 return true; 224} 225 226bool 227SimpleCache::handleResponse(PacketPtr pkt) 228{ 229 assert(blocked); 230 DPRINTF(SimpleCache, "Got response for addr %#x\n", pkt->getAddr()); 231 232 // For now assume that inserts are off of the critical path and don't count 233 // for any added latency. 234 insert(pkt); 235 236 missLatency.sample(curTick() - missTime); 237 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 251 252 sendResponse(pkt); 253 254 return true; 255} 256 257void SimpleCache::sendResponse(PacketPtr pkt) 258{ 259 assert(blocked); 260 DPRINTF(SimpleCache, "Sending resp for addr %#x\n", pkt->getAddr()); 261 262 int port = waitingPortId; 263 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). 268 blocked = false; 269 waitingPortId = -1; 270 271 // Simply forward to the memory port 272 cpuPorts[port].sendPacket(pkt); 273 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) { 277 port.trySendRetry(); 278 } 279} 280 281void 282SimpleCache::handleFunctional(PacketPtr pkt) 283{ 284 if (accessFunctional(pkt)) { 285 pkt->makeResponse(); 286 } else { 287 memPort.sendFunctional(pkt); 288 } 289} 290 291void 292SimpleCache::accessTiming(PacketPtr pkt) 293{ 294 bool hit = accessFunctional(pkt); 295 296 DPRINTF(SimpleCache, "%s for packet: %s\n", hit ? "Hit" : "Miss", 297 pkt->print()); 298 299 if (hit) { 300 // Respond to the CPU side 301 hits++; // update stats 302 DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), pkt->getSize()); 303 pkt->makeResponse(); 304 sendResponse(pkt); 305 } else { 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); 318 } else { 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()); 324 MemCmd cmd; 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; 329 } else { 330 panic("Unknown packet type in upgrade size"); 331 } 332 333 // Create a new packet that is blockSize 334 PacketPtr new_pkt = new Packet(pkt->req, cmd, blockSize); 335 new_pkt->allocate(); 336 337 // Should now be block aligned 338 assert(new_pkt->getAddr() == new_pkt->getBlockAddr(blockSize)); 339 340 // Save the old packet 341 originalPacket = pkt; 342 343 DPRINTF(SimpleCache, "forwarding packet\n"); 344 memPort.sendPacket(new_pkt); 345 } 346 } 347} 348 349bool 350SimpleCache::accessFunctional(PacketPtr pkt) 351{ 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); 361 } else { 362 panic("Unknown packet type!"); 363 } 364 return true; 365 } 366 return false; 367} 368 369void 370SimpleCache::insert(PacketPtr pkt) 371{ 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()); 378 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; 383 do { 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)); 388 389 DPRINTF(SimpleCache, "Removing addr %#x\n", block->first); 390 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); 395 396 PacketPtr new_pkt = new Packet(req, MemCmd::WritebackDirty, blockSize); 397 new_pkt->dataDynamic(block->second); // This will be deleted later 398 399 DPRINTF(SimpleCache, "Writing packet back %s\n", pkt->print()); 400 // Send the write to memory 401 memPort.sendPacket(new_pkt); 402 403 // Delete this entry 404 cacheStore.erase(block->first); 405 } 406 407 DPRINTF(SimpleCache, "Inserting %s\n", pkt->print()); 408 DDUMP(SimpleCache, pkt->getConstPtr<uint8_t>(), blockSize); 409 410 // Allocate space for the cache block data 411 uint8_t *data = new uint8_t[blockSize]; 412 413 // Insert the data and address into the cache store 414 cacheStore[pkt->getAddr()] = data; 415 416 // Write the data into the cache 417 pkt->writeDataToBlock(data, blockSize); 418} 419 420AddrRangeList 421SimpleCache::getAddrRanges() const 422{ 423 DPRINTF(SimpleCache, "Sending new ranges\n"); 424 // Just use the same ranges as whatever is on the memory side. 425 return memPort.getAddrRanges(); 426} 427 428void 429SimpleCache::sendRangeChange() const 430{ 431 for (auto& port : cpuPorts) { 432 port.sendRangeChange(); 433 } 434} 435 436void 437SimpleCache::regStats() 438{ 439 // If you don't do this you get errors about uninitialized stats. 440 MemObject::regStats(); 441 442 hits.name(name() + ".hits") 443 .desc("Number of hits") 444 ; 445 446 misses.name(name() + ".misses") 447 .desc("Number of misses") 448 ; 449 450 missLatency.name(name() + ".missLatency") 451 .desc("Ticks for misses to the cache") 452 .init(16) // number of buckets 453 ; 454 455 hitRatio.name(name() + ".hitRatio") 456 .desc("The ratio of hits to the total accesses to the cache") 457 ; 458 459 hitRatio = hits / (hits + misses); 460 461} 462 463 464SimpleCache* 465SimpleCacheParams::create() 466{ 467 return new SimpleCache(this); 468}
| 68 } 69} 70 71void 72SimpleCache::CPUSidePort::sendPacket(PacketPtr pkt) 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 86AddrRangeList 87SimpleCache::CPUSidePort::getAddrRanges() const 88{ 89 return owner->getAddrRanges(); 90} 91 92void 93SimpleCache::CPUSidePort::trySendRetry() 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 103void 104SimpleCache::CPUSidePort::recvFunctional(PacketPtr pkt) 105{ 106 // Just forward to the cache. 107 return owner->handleFunctional(pkt); 108} 109 110bool 111SimpleCache::CPUSidePort::recvTimingReq(PacketPtr pkt) 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 133void 134SimpleCache::CPUSidePort::recvRespRetry() 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 151void 152SimpleCache::MemSidePort::sendPacket(PacketPtr pkt) 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 164bool 165SimpleCache::MemSidePort::recvTimingResp(PacketPtr pkt) 166{ 167 // Just forward to the cache. 168 return owner->handleResponse(pkt); 169} 170 171void 172SimpleCache::MemSidePort::recvReqRetry() 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 185void 186SimpleCache::MemSidePort::recvRangeChange() 187{ 188 owner->sendRangeChange(); 189} 190 191bool 192SimpleCache::handleRequest(PacketPtr pkt, int port_id) 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 216bool 217SimpleCache::handleResponse(PacketPtr pkt) 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 226 missLatency.sample(curTick() - missTime); 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. 234 bool hit M5_VAR_USED = accessFunctional(originalPacket); 235 panic_if(!hit, "Should always hit after inserting"); 236 originalPacket->makeResponse(); 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 247void SimpleCache::sendResponse(PacketPtr pkt) 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 271void 272SimpleCache::handleFunctional(PacketPtr pkt) 273{ 274 if (accessFunctional(pkt)) { 275 pkt->makeResponse(); 276 } else { 277 memPort.sendFunctional(pkt); 278 } 279} 280 281void 282SimpleCache::accessTiming(PacketPtr pkt) 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 339bool 340SimpleCache::accessFunctional(PacketPtr pkt) 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 359void 360SimpleCache::insert(PacketPtr pkt) 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 410AddrRangeList 411SimpleCache::getAddrRanges() const 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 418void 419SimpleCache::sendRangeChange() const 420{ 421 for (auto& port : cpuPorts) { 422 port.sendRangeChange(); 423 } 424} 425 426void 427SimpleCache::regStats() 428{ 429 // If you don't do this you get errors about uninitialized stats. 430 MemObject::regStats(); 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 454SimpleCache* 455SimpleCacheParams::create() 456{ 457 return new SimpleCache(this); 458}
|