The Sparta Modeling Framework
Loading...
Searching...
No Matches
LSU.cpp
1
3#include "LSU.hpp"
4
5namespace core_example
6{
7 const char LSU::name[] = "lsu";
8
10 // Constructor
12
14 sparta::Unit(node),
15 memory_access_allocator(50, 30), // 50 and 30 are arbitrary numbers here. It can be tuned to an exact value.
16 load_store_info_allocator(50, 30),
17 ldst_inst_queue_("lsu_inst_queue", p->ldst_inst_queue_size, getClock()),
18 ldst_inst_queue_size_(p->ldst_inst_queue_size),
19
20 tlb_always_hit_(p->tlb_always_hit),
21 dl1_always_hit_(p->dl1_always_hit),
22
23 issue_latency_(p->issue_latency),
24 mmu_latency_(p->mmu_latency),
25 cache_latency_(p->cache_latency),
26 complete_latency_(p->complete_latency)
27 {
28 // Pipeline collection config
29 ldst_pipeline_.enableCollection(node);
30 ldst_inst_queue_.enableCollection(node);
31
32
33 // Startup handler for sending initiatl credits
34 sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(LSU, sendInitialCredits_));
35
36
37 // Port config
38 in_lsu_insts_.registerConsumerHandler
39 (CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getInstsFromDispatch_, ExampleInstPtr));
40
41 in_biu_ack_.registerConsumerHandler
43
44 in_rob_retire_ack_.registerConsumerHandler
46
47 in_reorder_flush_.registerConsumerHandler
48 (CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleFlush_, FlushManager::FlushingCriteria));
49
50
51 // Pipeline events config
52 ldst_pipeline_.performOwnUpdates();
53 ldst_pipeline_.registerHandlerAtStage(static_cast<uint32_t>(PipelineStage::MMU_LOOKUP),
54 CREATE_SPARTA_HANDLER(LSU, handleMMULookupReq_));
55
56 ldst_pipeline_.registerHandlerAtStage(static_cast<uint32_t>(PipelineStage::CACHE_LOOKUP),
57 CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_));
58
59 ldst_pipeline_.registerHandlerAtStage(static_cast<uint32_t>(PipelineStage::COMPLETE),
60 CREATE_SPARTA_HANDLER(LSU, completeInst_));
61
62
63 // Event precedence setup
64 uev_cache_drive_biu_port_ >> uev_mmu_drive_biu_port_;
65
66 // NOTE:
67 // To resolve the race condition when:
68 // Both cache and MMU try to drive the single BIU port at the same cycle
69 // Here we give cache the higher priority
70
71 // DL1 cache config
72 const uint64_t dl1_line_size = p->dl1_line_size;
73 const uint64_t dl1_size_kb = p->dl1_size_kb;
74 const uint64_t dl1_associativity = p->dl1_associativity;
75 std::unique_ptr<sparta::cache::ReplacementIF> repl(new sparta::cache::TreePLRUReplacement
76 (dl1_associativity));
77 dl1_cache_.reset(new SimpleDL1( getContainer(), dl1_size_kb, dl1_line_size, *repl ));
78
80 info_logger_ << "LSU construct: #" << node->getGroupIdx();
81 }
82 }
83
84
86 // Callbacks
88
89 // Send initial credits (ldst_inst_queue_size_) to Dispatch Unit
90 void LSU::sendInitialCredits_()
91 {
92 out_lsu_credits_.send(ldst_inst_queue_size_);
93
94
96 info_logger_ << "LSU initial credits for Dispatch Unit: " << ldst_inst_queue_size_;
97 }
98 }
99
100 // Receive new load/store instruction from Dispatch Unit
101 void LSU::getInstsFromDispatch_(const ExampleInstPtr & inst_ptr)
102 {
103 // Create load/store memory access info
104 MemoryAccessInfoPtr mem_info_ptr = sparta::allocate_sparta_shared_pointer<MemoryAccessInfo>(memory_access_allocator,
105 inst_ptr);
106
107 // Create load/store instruction issue info
108 LoadStoreInstInfoPtr inst_info_ptr = sparta::allocate_sparta_shared_pointer<LoadStoreInstInfo>(load_store_info_allocator,
109 mem_info_ptr);
110
111 // Append to instruction issue queue
112 appendIssueQueue_(inst_info_ptr);
113
114 // Update issue priority & Schedule an instruction issue event
115 updateIssuePriorityAfterNewDispatch_(inst_ptr);
116 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
117
118 // NOTE:
119 // IssuePriority should always be updated before a new issue event is scheduled.
120 // This guarantees that whenever a new instruction issue event is scheduled:
121 // (1)Instruction issue queue already has "something READY";
122 // (2)Instruction issue arbitration is guaranteed to be sucessful.
123
124
125 // Update instruction status
126 inst_ptr->setStatus(ExampleInst::Status::SCHEDULED);
127
128 // NOTE:
129 // It is a bug if instruction status is updated as SCHEDULED in the issueInst_()
130 // The reason is: when issueInst_() is called, it could be scheduled for
131 // either a new issue event, or a re-issue event
132 // however, we can ONLY update instruction status as SCHEDULED for a new issue event
133
134
136 info_logger_ << "Another issue event scheduled";
137 }
138 }
139
140 // Receive MSS access acknowledge from Bus Interface Unit
141 void LSU::getAckFromBIU_(const ExampleInstPtr & inst_ptr)
142 {
143 if (inst_ptr == mmu_pending_inst_ptr_) {
144 rehandleMMULookupReq_(inst_ptr);
145 }
146 else if (inst_ptr == cache_pending_inst_ptr_) {
147 rehandleCacheLookupReq_(inst_ptr);
148 }
149 else {
150 sparta_assert(false, "Unexpected BIU Ack event occurs!");
151 }
152 }
153
154 // Receive update from ROB whenever store instructions retire
155 void LSU::getAckFromROB_(const ExampleInstPtr & inst_ptr)
156 {
157 sparta_assert(inst_ptr->getStatus() == ExampleInst::Status::RETIRED,
158 "Get ROB Ack, but the store inst hasn't retired yet!");
159
160 updateIssuePriorityAfterStoreInstRetire_(inst_ptr);
161 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
162
163
165 info_logger_ << "Get Ack from ROB! Retired store instruction: " << inst_ptr;
166 }
167 }
168
169 // Issue/Re-issue ready instructions in the issue queue
170 void LSU::issueInst_()
171 {
172 // Instruction issue arbitration
173 const LoadStoreInstInfoPtr & win_ptr = arbitrateInstIssue_();
174 // NOTE:
175 // win_ptr should always point to an instruction ready to be issued
176 // Otherwise assertion error should already be fired in arbitrateInstIssue_()
177
178 // Append load/store pipe
179 ldst_pipeline_.append(win_ptr->getMemoryAccessInfoPtr());
180
181 // Update instruction issue info
182 win_ptr->setState(LoadStoreInstInfo::IssueState::ISSUED);
183 win_ptr->setPriority(LoadStoreInstInfo::IssuePriority::LOWEST);
184
185 // Schedule another instruction issue event if possible
186 if (isReadyToIssueInsts_()) {
187 uev_issue_inst_.schedule(sparta::Clock::Cycle(1));
188 }
189
190
192 info_logger_ << "Issue/Re-issue Instruction: " << win_ptr->getInstPtr();
193 }
194 }
195
196 // Handle MMU access request
197 void LSU::handleMMULookupReq_()
198 {
199 const auto stage_id = static_cast<uint32_t>(PipelineStage::MMU_LOOKUP);
200
201 // Check if flushing event occurred just now
202 if (!ldst_pipeline_.isValid(stage_id)) {
203 return;
204 }
205
206
207 const MemoryAccessInfoPtr & mem_access_info_ptr = ldst_pipeline_[stage_id];
208 bool isAlreadyHIT = (mem_access_info_ptr->getMMUState() == MemoryAccessInfo::MMUState::HIT);
209 bool MMUBypass = isAlreadyHIT;
210
211 if (MMUBypass) {
212
214 info_logger_ << "MMU Lookup is skipped (TLB is already hit)!";
215 }
216
217 return;
218 }
219
220 // Access TLB, and check TLB hit or miss
221 bool TLB_HIT = MMULookup_(mem_access_info_ptr);
222
223 if (TLB_HIT) {
224 // Update memory access info
225 mem_access_info_ptr->setMMUState(MemoryAccessInfo::MMUState::HIT);
226 // Update physical address status
227 mem_access_info_ptr->setPhyAddrStatus(true);
228 }
229 else {
230 // Update memory access info
231 mem_access_info_ptr->setMMUState(MemoryAccessInfo::MMUState::MISS);
232
233 if (mmu_busy_ == false) {
234 // MMU is busy, no more TLB MISS can be handled, RESET is required on finish
235 mmu_busy_ = true;
236 // Keep record of the current TLB MISS instruction
237 mmu_pending_inst_ptr_ = mem_access_info_ptr->getInstPtr();
238
239 // NOTE:
240 // mmu_busy_ RESET could be done:
241 // as early as port-driven event for this miss finish, and
242 // as late as TLB reload event for this miss finish.
243
244 // Schedule port-driven event in BIU
245 uev_mmu_drive_biu_port_.schedule(sparta::Clock::Cycle(0));
246
247 // NOTE:
248 // The race between simultaneous MMU and cache requests is resolved by
249 // specifying precedence between these two competing events
250
251
253 info_logger_ << "MMU is trying to drive BIU request port!";
254 }
255 }
256 else {
257
260 << "MMU miss cannot be served right now due to another outstanding one!";
261 }
262 }
263
264 // NEW: Invalidate pipeline stage
265 ldst_pipeline_.invalidateStage(static_cast<uint32_t>(PipelineStage::MMU_LOOKUP));
266 }
267 }
268
269 // Drive BIU request port from MMU
270 void LSU::driveBIUPortFromMMU_()
271 {
272 bool succeed = false;
273
274 // Check if DataOutPort is available
275 if (!out_biu_req_.isDriven()) {
276 sparta_assert(mmu_pending_inst_ptr_ != nullptr,
277 "Attempt to drive BIU port when no outstanding TLB miss exists!");
278
279 // Port is available, drive port immediately, and send request out
280 out_biu_req_.send(mmu_pending_inst_ptr_);
281
282 succeed = true;
283 }
284 else {
285 // Port is being driven by another source, wait for one cycle and check again
286 uev_mmu_drive_biu_port_.schedule(sparta::Clock::Cycle(1));
287 }
288
289
291 if (succeed) {
292 info_logger_ << "MMU is driving the BIU request port!";
293 }
294 else {
295 info_logger_ << "MMU is waiting to drive the BIU request port!";
296 }
297 }
298 }
299
300 // Handle cache access request
301 void LSU::handleCacheLookupReq_()
302 {
303 const auto stage_id = static_cast<uint32_t>(PipelineStage::CACHE_LOOKUP);
304
305 // Check if flushing event occurred just now
306 if (!ldst_pipeline_.isValid(stage_id)) {
307 return;
308 }
309
310
311 const MemoryAccessInfoPtr & mem_access_info_ptr = ldst_pipeline_[stage_id];
312 const ExampleInstPtr & inst_ptr = mem_access_info_ptr->getInstPtr();
313
314 const bool phyAddrIsReady =
315 mem_access_info_ptr->getPhyAddrStatus();
316 const bool isAlreadyHIT =
317 (mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT);
318 const bool isUnretiredStore =
319 inst_ptr->isStoreInst() && (inst_ptr->getStatus() != ExampleInst::Status::RETIRED);
320 const bool cacheBypass = isAlreadyHIT || !phyAddrIsReady || isUnretiredStore;
321
322 if (cacheBypass) {
323
325 if (isAlreadyHIT) {
326 info_logger_ << "Cache Lookup is skipped (Cache already hit)!";
327 }
328 else if (!phyAddrIsReady) {
329 info_logger_ << "Cache Lookup is skipped (Physical address not ready)!";
330 }
331 else if (isUnretiredStore) {
332 info_logger_ << "Cache Lookup is skipped (Un-retired store instruction)!";
333 }
334 else {
335 sparta_assert(false, "Cache access is bypassed without a valid reason!");
336 }
337 }
338
339 return;
340 }
341
342 // Access cache, and check cache hit or miss
343 const bool CACHE_HIT = cacheLookup_(mem_access_info_ptr);
344
345 if (CACHE_HIT) {
346 // Update memory access info
347 mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::HIT);
348 }
349 else {
350 // Update memory access info
351 mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::MISS);
352
353 if (cache_busy_ == false) {
354 // Cache is now busy, no more CACHE MISS can be handled, RESET required on finish
355 cache_busy_ = true;
356 // Keep record of the current CACHE MISS instruction
357 cache_pending_inst_ptr_ = mem_access_info_ptr->getInstPtr();
358
359 // NOTE:
360 // cache_busy_ RESET could be done:
361 // as early as port-driven event for this miss finish, and
362 // as late as cache reload event for this miss finish.
363
364 // Schedule port-driven event in BIU
365 uev_cache_drive_biu_port_.schedule(sparta::Clock::Cycle(0));
366
367 // NOTE:
368 // The race between simultaneous MMU and cache requests is resolved by
369 // specifying precedence between these two competing events
370
371
373 info_logger_ << "Cache is trying to drive BIU request port!";
374 }
375 }
376 else {
377
379 info_logger_ << "Cache miss cannot be served right now due to another outstanding one!";
380 }
381 }
382
383 // NEW: Invalidate pipeline stage
384 ldst_pipeline_.invalidateStage(static_cast<uint32_t>(PipelineStage::CACHE_LOOKUP));
385 }
386 }
387
388 // Drive BIU request port from cache
389 void LSU::driveBIUPortFromCache_()
390 {
391 bool succeed = false;
392
393 // Check if DataOutPort is available
394 if (!out_biu_req_.isDriven()) {
395 sparta_assert(cache_pending_inst_ptr_ != nullptr,
396 "Attempt to drive BIU port when no outstanding cache miss exists!");
397
398 // Port is available, drive the port immediately, and send request out
399 out_biu_req_.send(cache_pending_inst_ptr_);
400
401 succeed = true;
402 }
403 else {
404 // Port is being driven by another source, wait for one cycle and check again
405 uev_cache_drive_biu_port_.schedule(sparta::Clock::Cycle(1));
406 }
407
408
410 if (succeed) {
411 info_logger_ << "Cache is driving the BIU request port!";
412 }
413 else {
414 info_logger_ << "Cache is waiting to drive the BIU request port!";
415 }
416 }
417 }
418
419 // Retire load/store instruction
420 void LSU::completeInst_()
421 {
422 const auto stage_id = static_cast<uint32_t>(PipelineStage::COMPLETE);
423
424 // Check if flushing event occurred just now
425 if (!ldst_pipeline_.isValid(stage_id)) {
426 return;
427 }
428
429
430 const MemoryAccessInfoPtr & mem_access_info_ptr = ldst_pipeline_[stage_id];
431 const ExampleInstPtr & inst_ptr = mem_access_info_ptr->getInstPtr();
432 bool isStoreInst = inst_ptr->isStoreInst();
433
434 // Complete load instruction
435 if (!isStoreInst) {
436 sparta_assert(mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT,
437 "Load instruction cannot complete when cache is still a miss!");
438
439 // Update instruction status
440 inst_ptr->setStatus(ExampleInst::Status::COMPLETED);
441
442 // Remove completed instruction from issue queue
443 popIssueQueue_(inst_ptr);
444
445 // Update instruction issue queue credits to Dispatch Unit
446 out_lsu_credits_.send(1, 0);
447
448
450 info_logger_ << "Complete Load Instruction: "
451 << inst_ptr->getMnemonic()
452 << " uid(" << inst_ptr->getUniqueID() << ")";
453 }
454
455 return;
456 }
457
458
459 // Complete store instruction
460 if (inst_ptr->getStatus() != ExampleInst::Status::RETIRED) {
461
462 sparta_assert(mem_access_info_ptr->getMMUState() == MemoryAccessInfo::MMUState::HIT,
463 "Store instruction cannot complete when TLB is still a miss!");
464
465 // Update instruction status
466 inst_ptr->setStatus(ExampleInst::Status::COMPLETED);
467
468
470 info_logger_ << "Complete Store Instruction: "
471 << inst_ptr->getMnemonic()
472 << " uid(" << inst_ptr->getUniqueID() << ")";
473 }
474 }
475 // Finish store operation
476 else {
477 sparta_assert(mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT,
478 "Store inst cannot finish when cache is still a miss!");
479
480 // Remove store instruction from issue queue
481 popIssueQueue_(inst_ptr);
482
483 // Update instruction issue queue credits to Dispatch Unit
484 out_lsu_credits_.send(1, 0);
485
486
488 info_logger_ << "Store operation is done!";
489 }
490 }
491
492
493 // NOTE:
494 // Checking whether an instruction is ready to complete could be non-trivial
495 // Right now we simply assume:
496 // (1)Load inst is ready to complete as long as both MMU and cache access finish
497 // (2)Store inst is ready to complete as long as MMU (address translation) is done
498 }
499
500 // Handle instruction flush in LSU
501 void LSU::handleFlush_(const FlushCriteria & criteria)
502 {
504 info_logger_ << "Start Flushing!";
505 }
506
507 // Flush criteria setup
508 auto flush = [criteria] (const uint64_t & id) -> bool {
509 return id >= static_cast<uint64_t>(criteria);
510 };
511
512 // Flush load/store pipeline entry
513 flushLSPipeline_(flush);
514
515 // Mark flushed flag for unfinished speculative MMU access
516 if (mmu_busy_ && flush(mmu_pending_inst_ptr_->getUniqueID())) {
517 mmu_pending_inst_flushed = true;
518 }
519
520 // Mark flushed flag for unfinished speculative cache access
521 if (cache_busy_ && flush(cache_pending_inst_ptr_->getUniqueID())) {
522 cache_pending_inst_flushed_ = true;
523 }
524
525 // Flush instruction issue queue
526 flushIssueQueue_(flush);
527
528 // Cancel issue event already scheduled if no ready-to-issue inst left after flush
529 if (!isReadyToIssueInsts_()) {
530 uev_issue_inst_.cancel();
531 }
532
533 // NOTE:
534 // Flush is handled at Flush phase (inbetween PortUpdate phase and Tick phase).
535 // This also guarantees that whenever an instruction issue event happens,
536 // instruction issue arbitration should always succeed, even when flush happens.
537 // Otherwise, assertion error is fired inside arbitrateInstIssue_()
538 }
539
540
542 // Regular Function/Subroutine Call
544
545 // Append new load/store instruction into issue queue
546 void LSU::appendIssueQueue_(const LoadStoreInstInfoPtr & inst_info_ptr)
547 {
548 sparta_assert(ldst_inst_queue_.size() <= ldst_inst_queue_size_,
549 "Appending issue queue causes overflows!");
550
551 // Always append newly dispatched instructions to the back of issue queue
552 ldst_inst_queue_.push_back(inst_info_ptr);
553
554
556 info_logger_ << "Append new load/store instruction to issue queue!";
557 }
558 }
559
560 // Pop completed load/store instruction out of issue queue
561 void LSU::popIssueQueue_(const ExampleInstPtr & inst_ptr)
562 {
563 // Look for the instruction to be completed, and remove it from issue queue
564 for (auto iter = ldst_inst_queue_.begin(); iter != ldst_inst_queue_.end(); iter++) {
565 if ((*iter)->getInstPtr() == inst_ptr) {
566 ldst_inst_queue_.erase(iter);
567
568 return;
569 }
570 }
571
572 sparta_assert(false, "Attempt to complete instruction no longer exiting in issue queue!");
573 }
574
575 // Arbitrate instruction issue from ldst_inst_queue
576 const LSU::LoadStoreInstInfoPtr & LSU::arbitrateInstIssue_()
577 {
578 sparta_assert(ldst_inst_queue_.size() > 0, "Arbitration fails: issue is empty!");
579
580 // Initialization of winner
581 auto win_ptr_iter = ldst_inst_queue_.begin();
582
583 // Select the ready instruction with highest issue priority
584 for (auto iter = ldst_inst_queue_.begin(); iter != ldst_inst_queue_.end(); iter++) {
585 // Skip not ready-to-issue instruction
586 if (!(*iter)->isReady()) {
587 continue;
588 }
589
590 // Pick winner
591 if (!(*win_ptr_iter)->isReady() || (*iter)->winArb(*win_ptr_iter)) {
592 win_ptr_iter = iter;
593 }
594 // NOTE:
595 // If the inst pointed to by (*win_ptr_iter) is not ready (possible @initialization),
596 // Re-assign it pointing to the ready-to-issue instruction pointed by (*iter).
597 // Otherwise, both (*win_ptr_iter) and (*iter) point to ready-to-issue instructions,
598 // Pick the one with higher issue priority.
599 }
600
601 sparta_assert((*win_ptr_iter)->isReady(), "Arbitration fails: no instruction is ready!");
602
603 return *win_ptr_iter;
604 }
605
606 // Check for ready to issue instructions
607 bool LSU::isReadyToIssueInsts_() const
608 {
609 bool isReady = false;
610
611 // Check if there is at least one ready-to-issue instruction in issue queue
612 for (auto const &inst_info_ptr : ldst_inst_queue_) {
613 if (inst_info_ptr->isReady()) {
614 isReady = true;
615
616 break;
617 }
618 }
619
620
622 if (isReady) {
623 info_logger_ << "At least one more instruction is ready to be issued!";
624 }
625 else {
626 info_logger_ << "No more instruction is ready to be issued!";
627 }
628 }
629
630 return isReady;
631 }
632
633
634 // Access MMU/TLB
635 bool LSU::MMULookup_(const MemoryAccessInfoPtr & mem_access_info_ptr)
636 {
637 const ExampleInstPtr & inst_ptr = mem_access_info_ptr->getInstPtr();
638 uint64_t vaddr = inst_ptr->getVAdr();
639
640 bool tlb_hit = false;
641
642 // C++ comma operator: assign tlb_hit first, then evaluate it. Just For Fun
643 if (tlb_hit = tlb_always_hit_, tlb_hit) {
644 }
645 else {
646 auto tlb_entry = tlb_cache_->peekLine(vaddr);
647 tlb_hit = (tlb_entry != nullptr) && tlb_entry->isValid();
648
649 // Update MRU replacement state if TLB HIT
650 if (tlb_hit) {
651 tlb_cache_->touch(*tlb_entry);
652 }
653 }
654
655
657 if (tlb_always_hit_) {
658 info_logger_ << "TLB HIT all the time: vaddr=0x" << std::hex << vaddr;
659 }
660 else if (tlb_hit) {
661 info_logger_ << "TLB HIT: vaddr=0x" << std::hex << vaddr;
662 }
663 else {
664 info_logger_ << "TLB MISS: vaddr=0x" << std::hex << vaddr;
665 }
666 }
667
668 return tlb_hit;
669 }
670
671 // Re-handle outstanding MMU access request
672 void LSU::rehandleMMULookupReq_(const ExampleInstPtr & inst_ptr)
673 {
674 // MMU is no longer busy any more
675 mmu_busy_ = false;
676 mmu_pending_inst_ptr_.reset();
677
678 // NOTE:
679 // MMU may not have to wait until MSS Ack comes back
680 // MMU could be ready to service new TLB MISS once previous request has been sent
681 // However, that means MMU has to keep record of a list of pending instructions
682
683 // Check if this MMU miss Ack is for an already flushed instruction
684 if (mmu_pending_inst_flushed) {
685 mmu_pending_inst_flushed = false;
686
687
689 info_logger_ << "BIU Ack for a flushed MMU miss is received!";
690 }
691
692 // Schedule an instruction (re-)issue event
693 // Note: some younger load/store instruction(s) might have been blocked by
694 // this outstanding miss
695 updateIssuePriorityAfterTLBReload_(inst_ptr, true);
696 if (isReadyToIssueInsts_()) {
697 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
698 }
699 return;
700 }
701
702
704 info_logger_ << "BIU Ack for an outstanding MMU miss is received!";
705 }
706
707 // Reload TLB entry
708 reloadTLB_(inst_ptr->getVAdr());
709
710 // Update issue priority & Schedule an instruction (re-)issue event
711 updateIssuePriorityAfterTLBReload_(inst_ptr);
712 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
713
714
716 info_logger_ << "MMU rehandling event is scheduled!";
717 }
718 }
719
720 // Reload TLB entry
721 void LSU::reloadTLB_(uint64_t vaddr)
722 {
723 auto tlb_entry = &tlb_cache_->getLineForReplacementWithInvalidCheck(vaddr);
724 tlb_cache_->allocateWithMRUUpdate(*tlb_entry, vaddr);
725
726
728 info_logger_ << "TLB reload complete!";
729 }
730 }
731
732 // Access Cache
733 bool LSU::cacheLookup_(const MemoryAccessInfoPtr & mem_access_info_ptr)
734 {
735 const ExampleInstPtr & inst_ptr = mem_access_info_ptr->getInstPtr();
736 uint64_t phyAddr = inst_ptr->getRAdr();
737
738 bool cache_hit = false;
739
740 if (dl1_always_hit_) {
741 cache_hit = true;
742 }
743 else {
744 auto cache_line = dl1_cache_->peekLine(phyAddr);
745 cache_hit = (cache_line != nullptr) && cache_line->isValid();
746
747 // Update MRU replacement state if Cache HIT
748 if (cache_hit) {
749 dl1_cache_->touchMRU(*cache_line);
750 }
751 }
752
753
755 if (dl1_always_hit_) {
756 info_logger_ << "DL1 Cache HIT all the time: phyAddr=0x" << std::hex << phyAddr;
757 }
758 else if (cache_hit) {
759 info_logger_ << "DL1 Cache HIT: phyAddr=0x" << std::hex << phyAddr;
760 }
761 else {
762 info_logger_ << "DL1 Cache MISS: phyAddr=0x" << std::hex << phyAddr;
763 }
764 }
765
766 return cache_hit;
767 }
768
769 // Re-handle outstanding cache access request
770 void LSU::rehandleCacheLookupReq_(const ExampleInstPtr & inst_ptr)
771 {
772 // Cache is no longer busy any more
773 cache_busy_ = false;
774 cache_pending_inst_ptr_.reset();
775
776 // NOTE:
777 // Depending on cache is blocking or not,
778 // It may not have to wait until MMS Ack returns.
779 // However, that means cache has to keep record of a list of pending instructions
780
781 // Check if this cache miss Ack is for an already flushed instruction
782 if (cache_pending_inst_flushed_) {
783 cache_pending_inst_flushed_ = false;
784
786 info_logger_ << "BIU Ack for a flushed cache miss is received!";
787 }
788
789 // Schedule an instruction (re-)issue event
790 // Note: some younger load/store instruction(s) might have been blocked by
791 // this outstanding miss
792 updateIssuePriorityAfterCacheReload_(inst_ptr, true);
793 if (isReadyToIssueInsts_()) {
794 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
795 }
796
797 return;
798 }
799
800
802 info_logger_ << "BIU Ack for an outstanding cache miss is received!";
803 }
804
805 // Reload cache line
806 reloadCache_(inst_ptr->getRAdr());
807
808 // Update issue priority & Schedule an instruction (re-)issue event
809 updateIssuePriorityAfterCacheReload_(inst_ptr);
810 uev_issue_inst_.schedule(sparta::Clock::Cycle(0));
811
812
814 info_logger_ << "Cache rehandling event is scheduled!";
815 }
816 }
817
818 // Reload cache line
819 void LSU::reloadCache_(uint64_t phyAddr)
820 {
821 auto dl1_cache_line = &dl1_cache_->getLineForReplacementWithInvalidCheck(phyAddr);
822 dl1_cache_->allocateWithMRUUpdate(*dl1_cache_line, phyAddr);
823
824
826 info_logger_ << "Cache reload complete!";
827 }
828 }
829
830 // Update issue priority when newly dispatched instruction comes in
831 void LSU::updateIssuePriorityAfterNewDispatch_(const ExampleInstPtr & inst_ptr)
832 {
833 for (auto &inst_info_ptr : ldst_inst_queue_) {
834 if (inst_info_ptr->getInstPtr() == inst_ptr) {
835
836 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
837 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::NEW_DISP);
838
839 return;
840 }
841 }
842
843 sparta_assert(false,
844 "Attempt to update issue priority for instruction not yet in the issue queue!");
845 }
846
847 // Update issue priority after cache reload
848 void LSU::updateIssuePriorityAfterTLBReload_(const ExampleInstPtr & inst_ptr,
849 const bool is_flushed_inst)
850 {
851 bool is_found = false;
852
853 for (auto &inst_info_ptr : ldst_inst_queue_) {
854 const MemoryAccessInfoPtr & mem_info_ptr = inst_info_ptr->getMemoryAccessInfoPtr();
855
856 if (mem_info_ptr->getMMUState() == MemoryAccessInfo::MMUState::MISS) {
857 // Re-activate all TLB-miss-pending instructions in the issue queue
858 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
859 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::MMU_PENDING);
860
861 // NOTE:
862 // We may not have to re-activate all of the pending MMU miss instruction here
863 // However, re-activation must be scheduled somewhere else
864
865 if (inst_info_ptr->getInstPtr() == inst_ptr) {
866 // Update issue priority for this outstanding TLB miss
867 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
868 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::MMU_RELOAD);
869
870 // NOTE:
871 // The priority should be set in such a way that
872 // the outstanding miss is always re-issued earlier than other pending miss
873 // Here we have MMU_RELOAD > MMU_PENDING
874
875 is_found = true;
876 }
877 }
878 }
879
880 sparta_assert(is_flushed_inst || is_found,
881 "Attempt to rehandle TLB lookup for instruction not yet in the issue queue!");
882 }
883
884 // Update issue priority after cache reload
885 void LSU::updateIssuePriorityAfterCacheReload_(const ExampleInstPtr & inst_ptr,
886 const bool is_flushed_inst)
887 {
888 bool is_found = false;
889
890 for (auto &inst_info_ptr : ldst_inst_queue_) {
891 const MemoryAccessInfoPtr & mem_info_ptr = inst_info_ptr->getMemoryAccessInfoPtr();
892
893 if (mem_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::MISS) {
894 // Re-activate all cache-miss-pending instructions in the issue queue
895 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
896 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::CACHE_PENDING);
897
898 // NOTE:
899 // We may not have to re-activate all of the pending cache miss instruction here
900 // However, re-activation must be scheduled somewhere else
901
902 if (inst_info_ptr->getInstPtr() == inst_ptr) {
903 // Update issue priority for this outstanding cache miss
904 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
905 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::CACHE_RELOAD);
906
907 // NOTE:
908 // The priority should be set in such a way that
909 // the outstanding miss is always re-issued earlier than other pending miss
910 // Here we have CACHE_RELOAD > CACHE_PENDING > MMU_RELOAD
911
912 is_found = true;
913 }
914 }
915 }
916
917 sparta_assert(is_flushed_inst || is_found,
918 "Attempt to rehandle cache lookup for instruction not yet in the issue queue!");
919 }
920
921 // Update issue priority after store instruction retires
922 void LSU::updateIssuePriorityAfterStoreInstRetire_(const ExampleInstPtr & inst_ptr)
923 {
924 for (auto &inst_info_ptr : ldst_inst_queue_) {
925 if (inst_info_ptr->getInstPtr() == inst_ptr) {
926
927 inst_info_ptr->setState(LoadStoreInstInfo::IssueState::READY);
928 inst_info_ptr->setPriority(LoadStoreInstInfo::IssuePriority::CACHE_PENDING);
929
930 return;
931 }
932 }
933
934 sparta_assert(false,
935 "Attempt to update issue priority for instruction not yet in the issue queue!");
936
937 }
938
939 // Flush instruction issue queue
940 template<typename Comp>
941 void LSU::flushIssueQueue_(const Comp & flush)
942 {
943 uint32_t credits_to_send = 0;
944
945 auto iter = ldst_inst_queue_.begin();
946 while (iter != ldst_inst_queue_.end()) {
947 auto inst_id = (*iter)->getInstPtr()->getUniqueID();
948
949 auto delete_iter = iter++;
950
951 if (flush(inst_id)) {
952 ldst_inst_queue_.erase(delete_iter);
953
954 // NOTE:
955 // We cannot increment iter after erase because it's already invalidated by then
956
957 ++credits_to_send;
958
959
961 info_logger_ << "Flush Instruction ID: " << inst_id;
962 }
963 }
964 }
965
966 if (credits_to_send > 0) {
967 out_lsu_credits_.send(credits_to_send);
968
969
971 info_logger_ << "Flush " << credits_to_send << " instructions in issue queue!";
972 }
973 }
974 }
975
976 // Flush load/store pipe
977 template<typename Comp>
978 void LSU::flushLSPipeline_(const Comp & flush)
979 {
980 uint32_t stage_id = 0;
981 for (auto iter = ldst_pipeline_.begin(); iter != ldst_pipeline_.end(); iter++, stage_id++) {
982 // If the pipe stage is already invalid, no need to flush
983 if (!iter.isValid()) {
984 continue;
985 }
986
987 auto inst_id = (*iter)->getInstPtr()->getUniqueID();
988 if (flush(inst_id)) {
989 ldst_pipeline_.flushStage(iter);
990
991
993 info_logger_ << "Flush Pipeline Stage[" << stage_id
994 << "], Instruction ID: " << inst_id;
995 }
996 }
997 }
998 }
999
1000} // namespace core_example
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
#define CREATE_SPARTA_HANDLER_WITH_DATA(clname, meth, dataT)
#define CREATE_SPARTA_HANDLER(clname, meth)
Parameters for LSU model.
Definition LSU.hpp:38
LSU(sparta::TreeNode *node, const LSUParameterSet *p)
Constructor for LSU.
Definition LSU.cpp:13
static const char name[]
name of this resource.
Definition LSU.hpp:83
void erase(uint32_t idx)
erase a position in the Buffer immediately.
Definition Buffer.hpp:623
size_type size() const
Return the number of valid entries.
Definition Buffer.hpp:497
iterator push_back(const value_type &dat)
Append data to the end of Buffer, and return a BufferIterator.
Definition Buffer.hpp:520
iterator begin()
Get the iterator pointing to the beginning of Buffer.
Definition Buffer.hpp:743
void enableCollection(TreeNode *parent)
Request that this queue begin collecting its contents for pipeline collection.
Definition Buffer.hpp:733
iterator end()
Returns an iterator referring to past-the-end element in the Buffer container.
Definition Buffer.hpp:755
void send(const DataT &dat, sparta::Clock::Cycle rel_time=0)
Send data to bound receivers.
Definition DataPort.hpp:145
void registerConsumerHandler(const SpartaHandler &handler)
Register a handler to this Port (must be Direction::IN) to handle data arrival.
Definition Port.hpp:337
bool observed() const noexcept
Is this NotificationSourceBase being observed at this node or an ancestor of any distance.
bool isValid(const uint32_t &stage_id) const
Indicate the validity of a specific pipeline stage.
Definition Pipeline.hpp:836
void registerHandlerAtStage(const uint32_t &id, const SpartaHandler &handler)
Register event handler for a designated pipeline stage.
Definition Pipeline.hpp:293
void append(const DataT &item)
Append data to the beginning of the pipeline.
Definition Pipeline.hpp:622
void performOwnUpdates()
Ask pipeline to perform its own update.
Definition Pipeline.hpp:865
void enableCollection(TreeNode *parent)
Enable pipeline collection.
Definition Pipeline.hpp:903
void flushStage(const uint32_t &flush_stage_id)
Flush a specific stage of the pipeline using stage id.
Definition Pipeline.hpp:743
void invalidateStage(const uint32_t &stage_id)
Invalidate a specific stage of the pipeline.
Definition Pipeline.hpp:681
TreeNode * getContainer()
Gets the TreeNode (container) for this resource (if any)
void cancel()
Cancel all the times that this Scheduleable was placed on the Scheduler.
void reset(PointerT *p=nullptr)
Reset this shared pointer.
StartupEvent is a simple class for scheduling a starting event on the Scheduler. It does not support ...
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
group_idx_type getGroupIdx() const
Gets the group index of this node.
void schedule()
Schedule this event with its pre-set delay using the pre-set Clock.
log::MessageSource info_logger_
Default info logger.
Definition Unit.hpp:161
Macros for handling exponential backoff.