mem-ruby: int to Cycle converter
[gem5.git] / src / mem / ruby / system / Sequencer.cc
1 /*
2 * Copyright (c) 2019-2020 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
15 * Copyright (c) 2013 Advanced Micro Devices, Inc.
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions are
20 * met: redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer;
22 * redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution;
25 * neither the name of the copyright holders nor the names of its
26 * contributors may be used to endorse or promote products derived from
27 * this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 */
41
42 #include "mem/ruby/system/Sequencer.hh"
43
44 #include "arch/x86/ldstflags.hh"
45 #include "base/logging.hh"
46 #include "base/str.hh"
47 #include "cpu/testers/rubytest/RubyTester.hh"
48 #include "debug/LLSC.hh"
49 #include "debug/MemoryAccess.hh"
50 #include "debug/ProtocolTrace.hh"
51 #include "debug/RubySequencer.hh"
52 #include "debug/RubyStats.hh"
53 #include "mem/packet.hh"
54 #include "mem/ruby/profiler/Profiler.hh"
55 #include "mem/ruby/protocol/PrefetchBit.hh"
56 #include "mem/ruby/protocol/RubyAccessMode.hh"
57 #include "mem/ruby/slicc_interface/RubyRequest.hh"
58 #include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
59 #include "mem/ruby/system/RubySystem.hh"
60 #include "sim/system.hh"
61
62 using namespace std;
63
64 Sequencer *
65 RubySequencerParams::create()
66 {
67 return new Sequencer(this);
68 }
69
70 Sequencer::Sequencer(const Params *p)
71 : RubyPort(p), m_IncompleteTimes(MachineType_NUM),
72 deadlockCheckEvent([this]{ wakeup(); }, "Sequencer deadlock check")
73 {
74 m_outstanding_count = 0;
75
76 m_instCache_ptr = p->icache;
77 m_dataCache_ptr = p->dcache;
78 m_max_outstanding_requests = p->max_outstanding_requests;
79 m_deadlock_threshold = p->deadlock_threshold;
80
81 m_coreId = p->coreid; // for tracking the two CorePair sequencers
82 assert(m_max_outstanding_requests > 0);
83 assert(m_deadlock_threshold > 0);
84 assert(m_instCache_ptr != NULL);
85 assert(m_dataCache_ptr != NULL);
86
87 m_runningGarnetStandalone = p->garnet_standalone;
88 }
89
90 Sequencer::~Sequencer()
91 {
92 }
93
94 void
95 Sequencer::llscLoadLinked(const Addr claddr)
96 {
97 AbstractCacheEntry *line = m_dataCache_ptr->lookup(claddr);
98 if (line) {
99 line->setLocked(m_version);
100 DPRINTF(LLSC, "LLSC Monitor - inserting load linked - "
101 "addr=0x%lx - cpu=%u\n", claddr, m_version);
102 }
103 }
104
105 void
106 Sequencer::llscClearMonitor(const Addr claddr)
107 {
108 AbstractCacheEntry *line = m_dataCache_ptr->lookup(claddr);
109 if (line && line->isLocked(m_version)) {
110 line->clearLocked();
111 DPRINTF(LLSC, "LLSC Monitor - clearing due to store - "
112 "addr=0x%lx - cpu=%u\n", claddr, m_version);
113 }
114 }
115
116 bool
117 Sequencer::llscStoreConditional(const Addr claddr)
118 {
119 AbstractCacheEntry *line = m_dataCache_ptr->lookup(claddr);
120 if (!line)
121 return false;
122
123 DPRINTF(LLSC, "LLSC Monitor - clearing due to "
124 "store conditional - "
125 "addr=0x%lx - cpu=%u\n",
126 claddr, m_version);
127
128 if (line->isLocked(m_version)) {
129 line->clearLocked();
130 return true;
131 } else {
132 line->clearLocked();
133 return false;
134 }
135 }
136
137 bool
138 Sequencer::llscCheckMonitor(const Addr address)
139 {
140 const Addr claddr = makeLineAddress(address);
141 AbstractCacheEntry *line = m_dataCache_ptr->lookup(claddr);
142 if (!line)
143 return false;
144
145 if (line->isLocked(m_version)) {
146 return true;
147 } else {
148 return false;
149 }
150 }
151
152 void
153 Sequencer::llscClearLocalMonitor()
154 {
155 m_dataCache_ptr->clearLockedAll(m_version);
156 }
157
158 void
159 Sequencer::wakeup()
160 {
161 assert(drainState() != DrainState::Draining);
162
163 // Check for deadlock of any of the requests
164 Cycles current_time = curCycle();
165
166 // Check across all outstanding requests
167 int total_outstanding = 0;
168
169 for (const auto &table_entry : m_RequestTable) {
170 for (const auto &seq_req : table_entry.second) {
171 if (current_time - seq_req.issue_time < m_deadlock_threshold)
172 continue;
173
174 panic("Possible Deadlock detected. Aborting!\n version: %d "
175 "request.paddr: 0x%x m_readRequestTable: %d current time: "
176 "%u issue_time: %d difference: %d\n", m_version,
177 seq_req.pkt->getAddr(), table_entry.second.size(),
178 current_time * clockPeriod(), seq_req.issue_time
179 * clockPeriod(), (current_time * clockPeriod())
180 - (seq_req.issue_time * clockPeriod()));
181 }
182 total_outstanding += table_entry.second.size();
183 }
184
185 assert(m_outstanding_count == total_outstanding);
186
187 if (m_outstanding_count > 0) {
188 // If there are still outstanding requests, keep checking
189 schedule(deadlockCheckEvent, clockEdge(m_deadlock_threshold));
190 }
191 }
192
193 int
194 Sequencer::functionalWrite(Packet *func_pkt)
195 {
196 int num_written = RubyPort::functionalWrite(func_pkt);
197
198 for (const auto &table_entry : m_RequestTable) {
199 for (const auto& seq_req : table_entry.second) {
200 if (seq_req.functionalWrite(func_pkt))
201 ++num_written;
202 }
203 }
204
205 return num_written;
206 }
207
208 void Sequencer::resetStats()
209 {
210 m_outstandReqHist.reset();
211 m_latencyHist.reset();
212 m_hitLatencyHist.reset();
213 m_missLatencyHist.reset();
214 for (int i = 0; i < RubyRequestType_NUM; i++) {
215 m_typeLatencyHist[i]->reset();
216 m_hitTypeLatencyHist[i]->reset();
217 m_missTypeLatencyHist[i]->reset();
218 for (int j = 0; j < MachineType_NUM; j++) {
219 m_hitTypeMachLatencyHist[i][j]->reset();
220 m_missTypeMachLatencyHist[i][j]->reset();
221 }
222 }
223
224 for (int i = 0; i < MachineType_NUM; i++) {
225 m_missMachLatencyHist[i]->reset();
226 m_hitMachLatencyHist[i]->reset();
227
228 m_IssueToInitialDelayHist[i]->reset();
229 m_InitialToForwardDelayHist[i]->reset();
230 m_ForwardToFirstResponseDelayHist[i]->reset();
231 m_FirstResponseToCompletionDelayHist[i]->reset();
232
233 m_IncompleteTimes[i] = 0;
234 }
235 }
236
237 // Insert the request in the request table. Return RequestStatus_Aliased
238 // if the entry was already present.
239 RequestStatus
240 Sequencer::insertRequest(PacketPtr pkt, RubyRequestType primary_type,
241 RubyRequestType secondary_type)
242 {
243 // See if we should schedule a deadlock check
244 if (!deadlockCheckEvent.scheduled() &&
245 drainState() != DrainState::Draining) {
246 schedule(deadlockCheckEvent, clockEdge(m_deadlock_threshold));
247 }
248
249 Addr line_addr = makeLineAddress(pkt->getAddr());
250 // Check if there is any outstanding request for the same cache line.
251 auto &seq_req_list = m_RequestTable[line_addr];
252 // Create a default entry
253 seq_req_list.emplace_back(pkt, primary_type,
254 secondary_type, curCycle());
255 m_outstanding_count++;
256
257 if (seq_req_list.size() > 1) {
258 return RequestStatus_Aliased;
259 }
260
261 m_outstandReqHist.sample(m_outstanding_count);
262
263 return RequestStatus_Ready;
264 }
265
266 void
267 Sequencer::markRemoved()
268 {
269 m_outstanding_count--;
270 }
271
272 void
273 Sequencer::recordMissLatency(SequencerRequest* srequest, bool llscSuccess,
274 const MachineType respondingMach,
275 bool isExternalHit, Cycles initialRequestTime,
276 Cycles forwardRequestTime,
277 Cycles firstResponseTime)
278 {
279 RubyRequestType type = srequest->m_type;
280 Cycles issued_time = srequest->issue_time;
281 Cycles completion_time = curCycle();
282
283 assert(curCycle() >= issued_time);
284 Cycles total_lat = completion_time - issued_time;
285
286 if (initialRequestTime < issued_time) {
287 // if the request was combined in the protocol with an earlier request
288 // for the same address, it is possible that it will return an
289 // initialRequestTime corresponding the earlier request. Since Cycles
290 // is unsigned, we can't let this request get profiled below.
291
292 total_lat = Cycles(0);
293 }
294
295 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %s %d cycles\n",
296 curTick(), m_version, "Seq", llscSuccess ? "Done" : "SC_Failed",
297 "", "", printAddress(srequest->pkt->getAddr()), total_lat);
298
299 m_latencyHist.sample(total_lat);
300 m_typeLatencyHist[type]->sample(total_lat);
301
302 if (isExternalHit) {
303 m_missLatencyHist.sample(total_lat);
304 m_missTypeLatencyHist[type]->sample(total_lat);
305
306 if (respondingMach != MachineType_NUM) {
307 m_missMachLatencyHist[respondingMach]->sample(total_lat);
308 m_missTypeMachLatencyHist[type][respondingMach]->sample(total_lat);
309
310 if ((issued_time <= initialRequestTime) &&
311 (initialRequestTime <= forwardRequestTime) &&
312 (forwardRequestTime <= firstResponseTime) &&
313 (firstResponseTime <= completion_time)) {
314
315 m_IssueToInitialDelayHist[respondingMach]->sample(
316 initialRequestTime - issued_time);
317 m_InitialToForwardDelayHist[respondingMach]->sample(
318 forwardRequestTime - initialRequestTime);
319 m_ForwardToFirstResponseDelayHist[respondingMach]->sample(
320 firstResponseTime - forwardRequestTime);
321 m_FirstResponseToCompletionDelayHist[respondingMach]->sample(
322 completion_time - firstResponseTime);
323 } else {
324 m_IncompleteTimes[respondingMach]++;
325 }
326 }
327 } else {
328 m_hitLatencyHist.sample(total_lat);
329 m_hitTypeLatencyHist[type]->sample(total_lat);
330
331 if (respondingMach != MachineType_NUM) {
332 m_hitMachLatencyHist[respondingMach]->sample(total_lat);
333 m_hitTypeMachLatencyHist[type][respondingMach]->sample(total_lat);
334 }
335 }
336 }
337
338 void
339 Sequencer::writeCallbackScFail(Addr address, DataBlock& data)
340 {
341 llscClearMonitor(address);
342 writeCallback(address, data);
343 }
344
345 void
346 Sequencer::writeCallback(Addr address, DataBlock& data,
347 const bool externalHit, const MachineType mach,
348 const Cycles initialRequestTime,
349 const Cycles forwardRequestTime,
350 const Cycles firstResponseTime)
351 {
352 //
353 // Free the whole list as we assume we have had the exclusive access
354 // to this cache line when response for the write comes back
355 //
356 assert(address == makeLineAddress(address));
357 assert(m_RequestTable.find(address) != m_RequestTable.end());
358 auto &seq_req_list = m_RequestTable[address];
359
360 // Perform hitCallback on every cpu request made to this cache block while
361 // ruby request was outstanding. Since only 1 ruby request was made,
362 // profile the ruby latency once.
363 bool ruby_request = true;
364 int aliased_stores = 0;
365 int aliased_loads = 0;
366 while (!seq_req_list.empty()) {
367 SequencerRequest &seq_req = seq_req_list.front();
368 if (ruby_request) {
369 assert(seq_req.m_type != RubyRequestType_LD);
370 assert(seq_req.m_type != RubyRequestType_Load_Linked);
371 assert(seq_req.m_type != RubyRequestType_IFETCH);
372 }
373
374 // handle write request
375 if ((seq_req.m_type != RubyRequestType_LD) &&
376 (seq_req.m_type != RubyRequestType_Load_Linked) &&
377 (seq_req.m_type != RubyRequestType_IFETCH)) {
378 // LL/SC support (tested with ARMv8)
379 bool success = true;
380
381 if (seq_req.m_type != RubyRequestType_Store_Conditional) {
382 // Regular stores to addresses being monitored
383 // will fail (remove) the monitor entry.
384 llscClearMonitor(address);
385 } else {
386 // Store conditionals must first check the monitor
387 // if they will succeed or not
388 success = llscStoreConditional(address);
389 seq_req.pkt->req->setExtraData(success ? 1 : 0);
390 }
391
392 // Handle SLICC block_on behavior for Locked_RMW accesses. NOTE: the
393 // address variable here is assumed to be a line address, so when
394 // blocking buffers, must check line addresses.
395 if (seq_req.m_type == RubyRequestType_Locked_RMW_Read) {
396 // blockOnQueue blocks all first-level cache controller queues
397 // waiting on memory accesses for the specified address that go
398 // to the specified queue. In this case, a Locked_RMW_Write must
399 // go to the mandatory_q before unblocking the first-level
400 // controller. This will block standard loads, stores, ifetches,
401 // etc.
402 m_controller->blockOnQueue(address, m_mandatory_q_ptr);
403 } else if (seq_req.m_type == RubyRequestType_Locked_RMW_Write) {
404 m_controller->unblock(address);
405 }
406
407 if (ruby_request) {
408 recordMissLatency(&seq_req, success, mach, externalHit,
409 initialRequestTime, forwardRequestTime,
410 firstResponseTime);
411 } else {
412 aliased_stores++;
413 }
414 markRemoved();
415 ruby_request = false;
416 hitCallback(&seq_req, data, success, mach, externalHit,
417 initialRequestTime, forwardRequestTime,
418 firstResponseTime);
419 } else {
420 // handle read request
421 assert(!ruby_request);
422 markRemoved();
423 ruby_request = false;
424 aliased_loads++;
425 hitCallback(&seq_req, data, true, mach, externalHit,
426 initialRequestTime, forwardRequestTime,
427 firstResponseTime);
428 }
429 seq_req_list.pop_front();
430 }
431
432 // free all outstanding requests corresponding to this address
433 if (seq_req_list.empty()) {
434 m_RequestTable.erase(address);
435 }
436 }
437
438 void
439 Sequencer::readCallback(Addr address, DataBlock& data,
440 bool externalHit, const MachineType mach,
441 Cycles initialRequestTime,
442 Cycles forwardRequestTime,
443 Cycles firstResponseTime)
444 {
445 //
446 // Free up read requests until we hit the first Write request
447 // or end of the corresponding list.
448 //
449 assert(address == makeLineAddress(address));
450 assert(m_RequestTable.find(address) != m_RequestTable.end());
451 auto &seq_req_list = m_RequestTable[address];
452
453 // Perform hitCallback on every cpu request made to this cache block while
454 // ruby request was outstanding. Since only 1 ruby request was made,
455 // profile the ruby latency once.
456 bool ruby_request = true;
457 int aliased_loads = 0;
458 while (!seq_req_list.empty()) {
459 SequencerRequest &seq_req = seq_req_list.front();
460 if (ruby_request) {
461 assert((seq_req.m_type == RubyRequestType_LD) ||
462 (seq_req.m_type == RubyRequestType_Load_Linked) ||
463 (seq_req.m_type == RubyRequestType_IFETCH));
464 } else {
465 aliased_loads++;
466 }
467 if ((seq_req.m_type != RubyRequestType_LD) &&
468 (seq_req.m_type != RubyRequestType_Load_Linked) &&
469 (seq_req.m_type != RubyRequestType_IFETCH)) {
470 // Write request: reissue request to the cache hierarchy
471 issueRequest(seq_req.pkt, seq_req.m_second_type);
472 break;
473 }
474 if (ruby_request) {
475 recordMissLatency(&seq_req, true, mach, externalHit,
476 initialRequestTime, forwardRequestTime,
477 firstResponseTime);
478 }
479 markRemoved();
480 ruby_request = false;
481 hitCallback(&seq_req, data, true, mach, externalHit,
482 initialRequestTime, forwardRequestTime,
483 firstResponseTime);
484 seq_req_list.pop_front();
485 }
486
487 // free all outstanding requests corresponding to this address
488 if (seq_req_list.empty()) {
489 m_RequestTable.erase(address);
490 }
491 }
492
493 void
494 Sequencer::hitCallback(SequencerRequest* srequest, DataBlock& data,
495 bool llscSuccess,
496 const MachineType mach, const bool externalHit,
497 const Cycles initialRequestTime,
498 const Cycles forwardRequestTime,
499 const Cycles firstResponseTime)
500 {
501 warn_once("Replacement policy updates recently became the responsibility "
502 "of SLICC state machines. Make sure to setMRU() near callbacks "
503 "in .sm files!");
504
505 PacketPtr pkt = srequest->pkt;
506 Addr request_address(pkt->getAddr());
507 RubyRequestType type = srequest->m_type;
508
509 // Load-linked handling
510 if (type == RubyRequestType_Load_Linked) {
511 Addr line_addr = makeLineAddress(request_address);
512 llscLoadLinked(line_addr);
513 }
514
515 // update the data unless it is a non-data-carrying flush
516 if (RubySystem::getWarmupEnabled()) {
517 data.setData(pkt->getConstPtr<uint8_t>(),
518 getOffset(request_address), pkt->getSize());
519 } else if (!pkt->isFlush()) {
520 if ((type == RubyRequestType_LD) ||
521 (type == RubyRequestType_IFETCH) ||
522 (type == RubyRequestType_RMW_Read) ||
523 (type == RubyRequestType_Locked_RMW_Read) ||
524 (type == RubyRequestType_Load_Linked)) {
525 pkt->setData(
526 data.getData(getOffset(request_address), pkt->getSize()));
527 DPRINTF(RubySequencer, "read data %s\n", data);
528 } else if (pkt->req->isSwap()) {
529 std::vector<uint8_t> overwrite_val(pkt->getSize());
530 pkt->writeData(&overwrite_val[0]);
531 pkt->setData(
532 data.getData(getOffset(request_address), pkt->getSize()));
533 data.setData(&overwrite_val[0],
534 getOffset(request_address), pkt->getSize());
535 DPRINTF(RubySequencer, "swap data %s\n", data);
536 } else if (type != RubyRequestType_Store_Conditional || llscSuccess) {
537 // Types of stores set the actual data here, apart from
538 // failed Store Conditional requests
539 data.setData(pkt->getConstPtr<uint8_t>(),
540 getOffset(request_address), pkt->getSize());
541 DPRINTF(RubySequencer, "set data %s\n", data);
542 }
543 }
544
545 // If using the RubyTester, update the RubyTester sender state's
546 // subBlock with the recieved data. The tester will later access
547 // this state.
548 if (m_usingRubyTester) {
549 DPRINTF(RubySequencer, "hitCallback %s 0x%x using RubyTester\n",
550 pkt->cmdString(), pkt->getAddr());
551 RubyTester::SenderState* testerSenderState =
552 pkt->findNextSenderState<RubyTester::SenderState>();
553 assert(testerSenderState);
554 testerSenderState->subBlock.mergeFrom(data);
555 }
556
557 RubySystem *rs = m_ruby_system;
558 if (RubySystem::getWarmupEnabled()) {
559 assert(pkt->req);
560 delete pkt;
561 rs->m_cache_recorder->enqueueNextFetchRequest();
562 } else if (RubySystem::getCooldownEnabled()) {
563 delete pkt;
564 rs->m_cache_recorder->enqueueNextFlushRequest();
565 } else {
566 ruby_hit_callback(pkt);
567 testDrainComplete();
568 }
569 }
570
571 bool
572 Sequencer::empty() const
573 {
574 return m_RequestTable.empty();
575 }
576
577 RequestStatus
578 Sequencer::makeRequest(PacketPtr pkt)
579 {
580 // HTM abort signals must be allowed to reach the Sequencer
581 // the same cycle they are issued. They cannot be retried.
582 if ((m_outstanding_count >= m_max_outstanding_requests) &&
583 !pkt->req->isHTMAbort()) {
584 return RequestStatus_BufferFull;
585 }
586
587 RubyRequestType primary_type = RubyRequestType_NULL;
588 RubyRequestType secondary_type = RubyRequestType_NULL;
589
590 if (pkt->isLLSC()) {
591 // LL/SC instructions need to be handled carefully by the cache
592 // coherence protocol to ensure they follow the proper semantics. In
593 // particular, by identifying the operations as atomic, the protocol
594 // should understand that migratory sharing optimizations should not
595 // be performed (i.e. a load between the LL and SC should not steal
596 // away exclusive permission).
597 //
598 // The following logic works correctly with the semantics
599 // of armV8 LDEX/STEX instructions.
600
601 if (pkt->isWrite()) {
602 DPRINTF(RubySequencer, "Issuing SC\n");
603 primary_type = RubyRequestType_Store_Conditional;
604 #if defined (PROTOCOL_MESI_Three_Level) || defined (PROTOCOL_MESI_Three_Level_HTM)
605 secondary_type = RubyRequestType_Store_Conditional;
606 #else
607 secondary_type = RubyRequestType_ST;
608 #endif
609 } else {
610 DPRINTF(RubySequencer, "Issuing LL\n");
611 assert(pkt->isRead());
612 primary_type = RubyRequestType_Load_Linked;
613 secondary_type = RubyRequestType_LD;
614 }
615 } else if (pkt->req->isLockedRMW()) {
616 //
617 // x86 locked instructions are translated to store cache coherence
618 // requests because these requests should always be treated as read
619 // exclusive operations and should leverage any migratory sharing
620 // optimization built into the protocol.
621 //
622 if (pkt->isWrite()) {
623 DPRINTF(RubySequencer, "Issuing Locked RMW Write\n");
624 primary_type = RubyRequestType_Locked_RMW_Write;
625 } else {
626 DPRINTF(RubySequencer, "Issuing Locked RMW Read\n");
627 assert(pkt->isRead());
628 primary_type = RubyRequestType_Locked_RMW_Read;
629 }
630 secondary_type = RubyRequestType_ST;
631 } else {
632 //
633 // To support SwapReq, we need to check isWrite() first: a SwapReq
634 // should always be treated like a write, but since a SwapReq implies
635 // both isWrite() and isRead() are true, check isWrite() first here.
636 //
637 if (pkt->isWrite()) {
638 //
639 // Note: M5 packets do not differentiate ST from RMW_Write
640 //
641 primary_type = secondary_type = RubyRequestType_ST;
642 } else if (pkt->isRead()) {
643 // hardware transactional memory commands
644 if (pkt->req->isHTMCmd()) {
645 primary_type = secondary_type = htmCmdToRubyRequestType(pkt);
646 } else if (pkt->req->isInstFetch()) {
647 primary_type = secondary_type = RubyRequestType_IFETCH;
648 } else {
649 bool storeCheck = false;
650 // only X86 need the store check
651 if (system->getArch() == Arch::X86ISA) {
652 uint32_t flags = pkt->req->getFlags();
653 storeCheck = flags &
654 (X86ISA::StoreCheck << X86ISA::FlagShift);
655 }
656 if (storeCheck) {
657 primary_type = RubyRequestType_RMW_Read;
658 secondary_type = RubyRequestType_ST;
659 } else {
660 primary_type = secondary_type = RubyRequestType_LD;
661 }
662 }
663 } else if (pkt->isFlush()) {
664 primary_type = secondary_type = RubyRequestType_FLUSH;
665 } else {
666 panic("Unsupported ruby packet type\n");
667 }
668 }
669
670 // Check if the line is blocked for a Locked_RMW
671 if (m_controller->isBlocked(makeLineAddress(pkt->getAddr())) &&
672 (primary_type != RubyRequestType_Locked_RMW_Write)) {
673 // Return that this request's cache line address aliases with
674 // a prior request that locked the cache line. The request cannot
675 // proceed until the cache line is unlocked by a Locked_RMW_Write
676 return RequestStatus_Aliased;
677 }
678
679 RequestStatus status = insertRequest(pkt, primary_type, secondary_type);
680
681 // It is OK to receive RequestStatus_Aliased, it can be considered Issued
682 if (status != RequestStatus_Ready && status != RequestStatus_Aliased)
683 return status;
684 // non-aliased with any existing request in the request table, just issue
685 // to the cache
686 if (status != RequestStatus_Aliased)
687 issueRequest(pkt, secondary_type);
688
689 // TODO: issue hardware prefetches here
690 return RequestStatus_Issued;
691 }
692
693 void
694 Sequencer::issueRequest(PacketPtr pkt, RubyRequestType secondary_type)
695 {
696 assert(pkt != NULL);
697 ContextID proc_id = pkt->req->hasContextId() ?
698 pkt->req->contextId() : InvalidContextID;
699
700 ContextID core_id = coreId();
701
702 // If valid, copy the pc to the ruby request
703 Addr pc = 0;
704 if (pkt->req->hasPC()) {
705 pc = pkt->req->getPC();
706 }
707
708 // check if the packet has data as for example prefetch and flush
709 // requests do not
710 std::shared_ptr<RubyRequest> msg =
711 std::make_shared<RubyRequest>(clockEdge(), pkt->getAddr(),
712 pkt->isFlush() ?
713 nullptr : pkt->getPtr<uint8_t>(),
714 pkt->getSize(), pc, secondary_type,
715 RubyAccessMode_Supervisor, pkt,
716 PrefetchBit_No, proc_id, core_id);
717
718 DPRINTFR(ProtocolTrace, "%15s %3s %10s%20s %6s>%-6s %#x %s\n",
719 curTick(), m_version, "Seq", "Begin", "", "",
720 printAddress(msg->getPhysicalAddress()),
721 RubyRequestType_to_string(secondary_type));
722
723 // hardware transactional memory
724 // If the request originates in a transaction,
725 // then mark the Ruby message as such.
726 if (pkt->isHtmTransactional()) {
727 msg->m_htmFromTransaction = true;
728 msg->m_htmTransactionUid = pkt->getHtmTransactionUid();
729 }
730
731 Tick latency = cyclesToTicks(
732 m_controller->mandatoryQueueLatency(secondary_type));
733 assert(latency > 0);
734
735 assert(m_mandatory_q_ptr != NULL);
736 m_mandatory_q_ptr->enqueue(msg, clockEdge(), latency);
737 }
738
739 template <class KEY, class VALUE>
740 std::ostream &
741 operator<<(ostream &out, const std::unordered_map<KEY, VALUE> &map)
742 {
743 for (const auto &table_entry : map) {
744 out << "[ " << table_entry.first << " =";
745 for (const auto &seq_req : table_entry.second) {
746 out << " " << RubyRequestType_to_string(seq_req.m_second_type);
747 }
748 }
749 out << " ]";
750
751 return out;
752 }
753
754 void
755 Sequencer::print(ostream& out) const
756 {
757 out << "[Sequencer: " << m_version
758 << ", outstanding requests: " << m_outstanding_count
759 << ", request table: " << m_RequestTable
760 << "]";
761 }
762
763 void
764 Sequencer::recordRequestType(SequencerRequestType requestType) {
765 DPRINTF(RubyStats, "Recorded statistic: %s\n",
766 SequencerRequestType_to_string(requestType));
767 }
768
769 void
770 Sequencer::evictionCallback(Addr address)
771 {
772 llscClearMonitor(address);
773 ruby_eviction_callback(address);
774 }
775
776 void
777 Sequencer::regStats()
778 {
779 RubyPort::regStats();
780
781 // These statistical variables are not for display.
782 // The profiler will collate these across different
783 // sequencers and display those collated statistics.
784 m_outstandReqHist.init(10);
785 m_latencyHist.init(10);
786 m_hitLatencyHist.init(10);
787 m_missLatencyHist.init(10);
788
789 for (int i = 0; i < RubyRequestType_NUM; i++) {
790 m_typeLatencyHist.push_back(new Stats::Histogram());
791 m_typeLatencyHist[i]->init(10);
792
793 m_hitTypeLatencyHist.push_back(new Stats::Histogram());
794 m_hitTypeLatencyHist[i]->init(10);
795
796 m_missTypeLatencyHist.push_back(new Stats::Histogram());
797 m_missTypeLatencyHist[i]->init(10);
798 }
799
800 for (int i = 0; i < MachineType_NUM; i++) {
801 m_hitMachLatencyHist.push_back(new Stats::Histogram());
802 m_hitMachLatencyHist[i]->init(10);
803
804 m_missMachLatencyHist.push_back(new Stats::Histogram());
805 m_missMachLatencyHist[i]->init(10);
806
807 m_IssueToInitialDelayHist.push_back(new Stats::Histogram());
808 m_IssueToInitialDelayHist[i]->init(10);
809
810 m_InitialToForwardDelayHist.push_back(new Stats::Histogram());
811 m_InitialToForwardDelayHist[i]->init(10);
812
813 m_ForwardToFirstResponseDelayHist.push_back(new Stats::Histogram());
814 m_ForwardToFirstResponseDelayHist[i]->init(10);
815
816 m_FirstResponseToCompletionDelayHist.push_back(new Stats::Histogram());
817 m_FirstResponseToCompletionDelayHist[i]->init(10);
818 }
819
820 for (int i = 0; i < RubyRequestType_NUM; i++) {
821 m_hitTypeMachLatencyHist.push_back(std::vector<Stats::Histogram *>());
822 m_missTypeMachLatencyHist.push_back(std::vector<Stats::Histogram *>());
823
824 for (int j = 0; j < MachineType_NUM; j++) {
825 m_hitTypeMachLatencyHist[i].push_back(new Stats::Histogram());
826 m_hitTypeMachLatencyHist[i][j]->init(10);
827
828 m_missTypeMachLatencyHist[i].push_back(new Stats::Histogram());
829 m_missTypeMachLatencyHist[i][j]->init(10);
830 }
831 }
832 }