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