First cut at LL/SC support in caches (atomic mode only).
[gem5.git] / src / mem / cache / base_cache.cc
1 /*
2 * Copyright (c) 2003-2005 The Regents of The University of Michigan
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: Erik Hallnor
29 */
30
31 /**
32 * @file
33 * Definition of BaseCache functions.
34 */
35
36 #include "mem/cache/base_cache.hh"
37 #include "mem/cache/miss/mshr.hh"
38 #include "mem/packet_impl.hh"
39 #include "cpu/smt.hh"
40 #include "cpu/base.hh"
41
42 using namespace std;
43
44 BaseCache::CachePort::CachePort(const std::string &_name, BaseCache *_cache,
45 bool _isCpuSide)
46 : Port(_name), cache(_cache), isCpuSide(_isCpuSide)
47 {
48 blocked = false;
49 waitingOnRetry = false;
50 //Start ports at null if more than one is created we should panic
51 //cpuSidePort = NULL;
52 //memSidePort = NULL;
53 }
54
55 void
56 BaseCache::CachePort::recvStatusChange(Port::Status status)
57 {
58 cache->recvStatusChange(status, isCpuSide);
59 }
60
61 void
62 BaseCache::CachePort::getDeviceAddressRanges(AddrRangeList &resp,
63 AddrRangeList &snoop)
64 {
65 cache->getAddressRanges(resp, snoop, isCpuSide);
66 }
67
68 int
69 BaseCache::CachePort::deviceBlockSize()
70 {
71 return cache->getBlockSize();
72 }
73
74 bool
75 BaseCache::CachePort::recvTiming(Packet *pkt)
76 {
77 if (isCpuSide
78 && !pkt->req->isUncacheable()
79 && pkt->isInvalidate()
80 && !pkt->isRead() && !pkt->isWrite()) {
81 //Upgrade or Invalidate
82 //Look into what happens if two slave caches on bus
83 DPRINTF(Cache, "%s %x ? blk_addr: %x\n", pkt->cmdString(),
84 pkt->getAddr() & (((ULL(1))<<48)-1),
85 pkt->getAddr() & ~((Addr)cache->blkSize - 1));
86
87 assert(!(pkt->flags & SATISFIED));
88 pkt->flags |= SATISFIED;
89 //Invalidates/Upgrades need no response if they get the bus
90 return true;
91 }
92
93 if (pkt->isRequest() && blocked)
94 {
95 DPRINTF(Cache,"Scheduling a retry while blocked\n");
96 mustSendRetry = true;
97 return false;
98 }
99 return cache->doTimingAccess(pkt, this, isCpuSide);
100 }
101
102 Tick
103 BaseCache::CachePort::recvAtomic(Packet *pkt)
104 {
105 return cache->doAtomicAccess(pkt, isCpuSide);
106 }
107
108 void
109 BaseCache::CachePort::recvFunctional(Packet *pkt)
110 {
111 //Check storage here first
112 list<Packet *>::iterator i = drainList.begin();
113 list<Packet *>::iterator end = drainList.end();
114 for (; i != end; ++i) {
115 Packet * target = *i;
116 // If the target contains data, and it overlaps the
117 // probed request, need to update data
118 if (target->intersect(pkt)) {
119 uint8_t* pkt_data;
120 uint8_t* write_data;
121 int data_size;
122 if (target->getAddr() < pkt->getAddr()) {
123 int offset = pkt->getAddr() - target->getAddr();
124 pkt_data = pkt->getPtr<uint8_t>();
125 write_data = target->getPtr<uint8_t>() + offset;
126 data_size = target->getSize() - offset;
127 assert(data_size > 0);
128 if (data_size > pkt->getSize())
129 data_size = pkt->getSize();
130 } else {
131 int offset = target->getAddr() - pkt->getAddr();
132 pkt_data = pkt->getPtr<uint8_t>() + offset;
133 write_data = target->getPtr<uint8_t>();
134 data_size = pkt->getSize() - offset;
135 assert(data_size > pkt->getSize());
136 if (data_size > target->getSize())
137 data_size = target->getSize();
138 }
139
140 if (pkt->isWrite()) {
141 memcpy(pkt_data, write_data, data_size);
142 } else {
143 memcpy(write_data, pkt_data, data_size);
144 }
145 }
146 }
147 cache->doFunctionalAccess(pkt, isCpuSide);
148 }
149
150 void
151 BaseCache::CachePort::recvRetry()
152 {
153 Packet *pkt;
154 assert(waitingOnRetry);
155 if (!drainList.empty()) {
156 DPRINTF(CachePort, "%s attempting to send a retry for response\n", name());
157 //We have some responses to drain first
158 if (sendTiming(drainList.front())) {
159 DPRINTF(CachePort, "%s sucessful in sending a retry for response\n", name());
160 drainList.pop_front();
161 if (!drainList.empty() ||
162 !isCpuSide && cache->doMasterRequest() ||
163 isCpuSide && cache->doSlaveRequest()) {
164
165 DPRINTF(CachePort, "%s has more responses/requests\n", name());
166 BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(this);
167 reqCpu->schedule(curTick + 1);
168 }
169 waitingOnRetry = false;
170 }
171 }
172 else if (!isCpuSide)
173 {
174 DPRINTF(CachePort, "%s attempting to send a retry for MSHR\n", name());
175 if (!cache->doMasterRequest()) {
176 //This can happen if I am the owner of a block and see an upgrade
177 //while the block was in my WB Buffers. I just remove the
178 //wb and de-assert the masterRequest
179 waitingOnRetry = false;
180 return;
181 }
182 pkt = cache->getPacket();
183 MSHR* mshr = (MSHR*) pkt->senderState;
184 //Copy the packet, it may be modified/destroyed elsewhere
185 Packet * copyPkt = new Packet(*pkt);
186 copyPkt->dataStatic<uint8_t>(pkt->getPtr<uint8_t>());
187 mshr->pkt = copyPkt;
188
189 bool success = sendTiming(pkt);
190 DPRINTF(Cache, "Address %x was %s in sending the timing request\n",
191 pkt->getAddr(), success ? "succesful" : "unsuccesful");
192
193 waitingOnRetry = !success;
194 if (waitingOnRetry) {
195 DPRINTF(CachePort, "%s now waiting on a retry\n", name());
196 }
197
198 cache->sendResult(pkt, mshr, success);
199
200 if (success && cache->doMasterRequest())
201 {
202 DPRINTF(CachePort, "%s has more requests\n", name());
203 //Still more to issue, rerequest in 1 cycle
204 BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(this);
205 reqCpu->schedule(curTick + 1);
206 }
207 }
208 else
209 {
210 assert(cache->doSlaveRequest());
211 //pkt = cache->getCoherencePacket();
212 //We save the packet, no reordering on CSHRS
213 pkt = cache->getCoherencePacket();
214 MSHR* cshr = (MSHR*)pkt->senderState;
215 bool success = sendTiming(pkt);
216 cache->sendCoherenceResult(pkt, cshr, success);
217 waitingOnRetry = !success;
218 if (success && cache->doSlaveRequest())
219 {
220 DPRINTF(CachePort, "%s has more requests\n", name());
221 //Still more to issue, rerequest in 1 cycle
222 BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(this);
223 reqCpu->schedule(curTick + 1);
224 }
225 }
226 if (waitingOnRetry) DPRINTF(CachePort, "%s STILL Waiting on retry\n", name());
227 else DPRINTF(CachePort, "%s no longer waiting on retry\n", name());
228 return;
229 }
230 void
231 BaseCache::CachePort::setBlocked()
232 {
233 assert(!blocked);
234 DPRINTF(Cache, "Cache Blocking\n");
235 blocked = true;
236 //Clear the retry flag
237 mustSendRetry = false;
238 }
239
240 void
241 BaseCache::CachePort::clearBlocked()
242 {
243 assert(blocked);
244 DPRINTF(Cache, "Cache Unblocking\n");
245 blocked = false;
246 if (mustSendRetry)
247 {
248 DPRINTF(Cache, "Cache Sending Retry\n");
249 mustSendRetry = false;
250 sendRetry();
251 }
252 }
253
254 BaseCache::CacheEvent::CacheEvent(CachePort *_cachePort)
255 : Event(&mainEventQueue, CPU_Tick_Pri), cachePort(_cachePort)
256 {
257 this->setFlags(AutoDelete);
258 pkt = NULL;
259 }
260
261 BaseCache::CacheEvent::CacheEvent(CachePort *_cachePort, Packet *_pkt)
262 : Event(&mainEventQueue, CPU_Tick_Pri), cachePort(_cachePort), pkt(_pkt)
263 {
264 this->setFlags(AutoDelete);
265 }
266
267 void
268 BaseCache::CacheEvent::process()
269 {
270 if (!pkt)
271 {
272 if (cachePort->waitingOnRetry) return;
273 //We have some responses to drain first
274 if (!cachePort->drainList.empty()) {
275 DPRINTF(CachePort, "%s trying to drain a response\n", cachePort->name());
276 if (cachePort->sendTiming(cachePort->drainList.front())) {
277 DPRINTF(CachePort, "%s drains a response succesfully\n", cachePort->name());
278 cachePort->drainList.pop_front();
279 if (!cachePort->drainList.empty() ||
280 !cachePort->isCpuSide && cachePort->cache->doMasterRequest() ||
281 cachePort->isCpuSide && cachePort->cache->doSlaveRequest()) {
282
283 DPRINTF(CachePort, "%s still has outstanding bus reqs\n", cachePort->name());
284 this->schedule(curTick + 1);
285 }
286 }
287 else {
288 cachePort->waitingOnRetry = true;
289 DPRINTF(CachePort, "%s now waiting on a retry\n", cachePort->name());
290 }
291 }
292 else if (!cachePort->isCpuSide)
293 { //MSHR
294 DPRINTF(CachePort, "%s trying to send a MSHR request\n", cachePort->name());
295 if (!cachePort->cache->doMasterRequest()) {
296 //This can happen if I am the owner of a block and see an upgrade
297 //while the block was in my WB Buffers. I just remove the
298 //wb and de-assert the masterRequest
299 return;
300 }
301
302 pkt = cachePort->cache->getPacket();
303 MSHR* mshr = (MSHR*) pkt->senderState;
304 //Copy the packet, it may be modified/destroyed elsewhere
305 Packet * copyPkt = new Packet(*pkt);
306 copyPkt->dataStatic<uint8_t>(pkt->getPtr<uint8_t>());
307 mshr->pkt = copyPkt;
308
309 bool success = cachePort->sendTiming(pkt);
310 DPRINTF(Cache, "Address %x was %s in sending the timing request\n",
311 pkt->getAddr(), success ? "succesful" : "unsuccesful");
312
313 cachePort->waitingOnRetry = !success;
314 if (cachePort->waitingOnRetry) {
315 DPRINTF(CachePort, "%s now waiting on a retry\n", cachePort->name());
316 }
317
318 cachePort->cache->sendResult(pkt, mshr, success);
319 if (success && cachePort->cache->doMasterRequest())
320 {
321 DPRINTF(CachePort, "%s still more MSHR requests to send\n",
322 cachePort->name());
323 //Still more to issue, rerequest in 1 cycle
324 pkt = NULL;
325 this->schedule(curTick+1);
326 }
327 }
328 else
329 {
330 //CSHR
331 assert(cachePort->cache->doSlaveRequest());
332 pkt = cachePort->cache->getCoherencePacket();
333 MSHR* cshr = (MSHR*) pkt->senderState;
334 bool success = cachePort->sendTiming(pkt);
335 cachePort->cache->sendResult(pkt, cshr, success);
336 cachePort->waitingOnRetry = !success;
337 if (cachePort->waitingOnRetry)
338 DPRINTF(CachePort, "%s now waiting on a retry\n", cachePort->name());
339 if (success && cachePort->cache->doSlaveRequest())
340 {
341 DPRINTF(CachePort, "%s still more CSHR requests to send\n",
342 cachePort->name());
343 //Still more to issue, rerequest in 1 cycle
344 pkt = NULL;
345 this->schedule(curTick+1);
346 }
347 }
348 return;
349 }
350 //Response
351 //Know the packet to send
352 if (pkt->flags & NACKED_LINE)
353 pkt->result = Packet::Nacked;
354 else
355 pkt->result = Packet::Success;
356 pkt->makeTimingResponse();
357 DPRINTF(CachePort, "%s attempting to send a response\n", cachePort->name());
358 if (!cachePort->drainList.empty() || cachePort->waitingOnRetry) {
359 //Already have a list, just append
360 cachePort->drainList.push_back(pkt);
361 DPRINTF(CachePort, "%s appending response onto drain list\n", cachePort->name());
362 }
363 else if (!cachePort->sendTiming(pkt)) {
364 //It failed, save it to list of drain events
365 DPRINTF(CachePort, "%s now waiting for a retry\n", cachePort->name());
366 cachePort->drainList.push_back(pkt);
367 cachePort->waitingOnRetry = true;
368 }
369 }
370
371 const char *
372 BaseCache::CacheEvent::description()
373 {
374 return "timing event\n";
375 }
376
377 Port*
378 BaseCache::getPort(const std::string &if_name, int idx)
379 {
380 if (if_name == "")
381 {
382 if(cpuSidePort == NULL)
383 cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true);
384 return cpuSidePort;
385 }
386 else if (if_name == "functional")
387 {
388 if(cpuSidePort == NULL)
389 cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true);
390 return cpuSidePort;
391 }
392 else if (if_name == "cpu_side")
393 {
394 if(cpuSidePort == NULL)
395 cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true);
396 return cpuSidePort;
397 }
398 else if (if_name == "mem_side")
399 {
400 if (memSidePort != NULL)
401 panic("Already have a mem side for this cache\n");
402 memSidePort = new CachePort(name() + "-mem_side_port", this, false);
403 return memSidePort;
404 }
405 else panic("Port name %s unrecognized\n", if_name);
406 }
407
408 void
409 BaseCache::init()
410 {
411 if (!cpuSidePort || !memSidePort)
412 panic("Cache not hooked up on both sides\n");
413 cpuSidePort->sendStatusChange(Port::RangeChange);
414 }
415
416 void
417 BaseCache::regStats()
418 {
419 Request temp_req((Addr) NULL, 4, 0);
420 Packet::Command temp_cmd = Packet::ReadReq;
421 Packet temp_pkt(&temp_req, temp_cmd, 0); //@todo FIx command strings so this isn't neccessary
422 temp_pkt.allocate(); //Temp allocate, all need data
423
424 using namespace Stats;
425
426 // Hit statistics
427 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
428 Packet::Command cmd = (Packet::Command)access_idx;
429 const string &cstr = temp_pkt.cmdIdxToString(cmd);
430
431 hits[access_idx]
432 .init(maxThreadsPerCPU)
433 .name(name() + "." + cstr + "_hits")
434 .desc("number of " + cstr + " hits")
435 .flags(total | nozero | nonan)
436 ;
437 }
438
439 demandHits
440 .name(name() + ".demand_hits")
441 .desc("number of demand (read+write) hits")
442 .flags(total)
443 ;
444 demandHits = hits[Packet::ReadReq] + hits[Packet::WriteReq];
445
446 overallHits
447 .name(name() + ".overall_hits")
448 .desc("number of overall hits")
449 .flags(total)
450 ;
451 overallHits = demandHits + hits[Packet::SoftPFReq] + hits[Packet::HardPFReq]
452 + hits[Packet::Writeback];
453
454 // Miss statistics
455 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
456 Packet::Command cmd = (Packet::Command)access_idx;
457 const string &cstr = temp_pkt.cmdIdxToString(cmd);
458
459 misses[access_idx]
460 .init(maxThreadsPerCPU)
461 .name(name() + "." + cstr + "_misses")
462 .desc("number of " + cstr + " misses")
463 .flags(total | nozero | nonan)
464 ;
465 }
466
467 demandMisses
468 .name(name() + ".demand_misses")
469 .desc("number of demand (read+write) misses")
470 .flags(total)
471 ;
472 demandMisses = misses[Packet::ReadReq] + misses[Packet::WriteReq];
473
474 overallMisses
475 .name(name() + ".overall_misses")
476 .desc("number of overall misses")
477 .flags(total)
478 ;
479 overallMisses = demandMisses + misses[Packet::SoftPFReq] +
480 misses[Packet::HardPFReq] + misses[Packet::Writeback];
481
482 // Miss latency statistics
483 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
484 Packet::Command cmd = (Packet::Command)access_idx;
485 const string &cstr = temp_pkt.cmdIdxToString(cmd);
486
487 missLatency[access_idx]
488 .init(maxThreadsPerCPU)
489 .name(name() + "." + cstr + "_miss_latency")
490 .desc("number of " + cstr + " miss cycles")
491 .flags(total | nozero | nonan)
492 ;
493 }
494
495 demandMissLatency
496 .name(name() + ".demand_miss_latency")
497 .desc("number of demand (read+write) miss cycles")
498 .flags(total)
499 ;
500 demandMissLatency = missLatency[Packet::ReadReq] + missLatency[Packet::WriteReq];
501
502 overallMissLatency
503 .name(name() + ".overall_miss_latency")
504 .desc("number of overall miss cycles")
505 .flags(total)
506 ;
507 overallMissLatency = demandMissLatency + missLatency[Packet::SoftPFReq] +
508 missLatency[Packet::HardPFReq];
509
510 // access formulas
511 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
512 Packet::Command cmd = (Packet::Command)access_idx;
513 const string &cstr = temp_pkt.cmdIdxToString(cmd);
514
515 accesses[access_idx]
516 .name(name() + "." + cstr + "_accesses")
517 .desc("number of " + cstr + " accesses(hits+misses)")
518 .flags(total | nozero | nonan)
519 ;
520
521 accesses[access_idx] = hits[access_idx] + misses[access_idx];
522 }
523
524 demandAccesses
525 .name(name() + ".demand_accesses")
526 .desc("number of demand (read+write) accesses")
527 .flags(total)
528 ;
529 demandAccesses = demandHits + demandMisses;
530
531 overallAccesses
532 .name(name() + ".overall_accesses")
533 .desc("number of overall (read+write) accesses")
534 .flags(total)
535 ;
536 overallAccesses = overallHits + overallMisses;
537
538 // miss rate formulas
539 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
540 Packet::Command cmd = (Packet::Command)access_idx;
541 const string &cstr = temp_pkt.cmdIdxToString(cmd);
542
543 missRate[access_idx]
544 .name(name() + "." + cstr + "_miss_rate")
545 .desc("miss rate for " + cstr + " accesses")
546 .flags(total | nozero | nonan)
547 ;
548
549 missRate[access_idx] = misses[access_idx] / accesses[access_idx];
550 }
551
552 demandMissRate
553 .name(name() + ".demand_miss_rate")
554 .desc("miss rate for demand accesses")
555 .flags(total)
556 ;
557 demandMissRate = demandMisses / demandAccesses;
558
559 overallMissRate
560 .name(name() + ".overall_miss_rate")
561 .desc("miss rate for overall accesses")
562 .flags(total)
563 ;
564 overallMissRate = overallMisses / overallAccesses;
565
566 // miss latency formulas
567 for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) {
568 Packet::Command cmd = (Packet::Command)access_idx;
569 const string &cstr = temp_pkt.cmdIdxToString(cmd);
570
571 avgMissLatency[access_idx]
572 .name(name() + "." + cstr + "_avg_miss_latency")
573 .desc("average " + cstr + " miss latency")
574 .flags(total | nozero | nonan)
575 ;
576
577 avgMissLatency[access_idx] =
578 missLatency[access_idx] / misses[access_idx];
579 }
580
581 demandAvgMissLatency
582 .name(name() + ".demand_avg_miss_latency")
583 .desc("average overall miss latency")
584 .flags(total)
585 ;
586 demandAvgMissLatency = demandMissLatency / demandMisses;
587
588 overallAvgMissLatency
589 .name(name() + ".overall_avg_miss_latency")
590 .desc("average overall miss latency")
591 .flags(total)
592 ;
593 overallAvgMissLatency = overallMissLatency / overallMisses;
594
595 blocked_cycles.init(NUM_BLOCKED_CAUSES);
596 blocked_cycles
597 .name(name() + ".blocked_cycles")
598 .desc("number of cycles access was blocked")
599 .subname(Blocked_NoMSHRs, "no_mshrs")
600 .subname(Blocked_NoTargets, "no_targets")
601 ;
602
603
604 blocked_causes.init(NUM_BLOCKED_CAUSES);
605 blocked_causes
606 .name(name() + ".blocked")
607 .desc("number of cycles access was blocked")
608 .subname(Blocked_NoMSHRs, "no_mshrs")
609 .subname(Blocked_NoTargets, "no_targets")
610 ;
611
612 avg_blocked
613 .name(name() + ".avg_blocked_cycles")
614 .desc("average number of cycles each access was blocked")
615 .subname(Blocked_NoMSHRs, "no_mshrs")
616 .subname(Blocked_NoTargets, "no_targets")
617 ;
618
619 avg_blocked = blocked_cycles / blocked_causes;
620
621 fastWrites
622 .name(name() + ".fast_writes")
623 .desc("number of fast writes performed")
624 ;
625
626 cacheCopies
627 .name(name() + ".cache_copies")
628 .desc("number of cache copies performed")
629 ;
630
631 }