-
Notifications
You must be signed in to change notification settings - Fork 74
Multiple LSU pipeline support #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 28 commits
8538bdc
22dda9f
9037295
8819e27
14a3a70
dc379a1
0eb3a6a
5404063
5cf7454
622a0d2
5bdeefa
60d5fba
0b94a35
662f00c
0d52a0e
a27fa71
d7df290
8084f56
d6c92b5
b4b60d3
575256d
67bb0cc
b3980ec
020b493
8034e14
e949057
b01d7e2
7db7fcc
423ba58
69a3e41
82ce2e7
411ef1c
4994499
711f66a
287b102
4d1270a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,8 @@ namespace olympia | |
replay_buffer_("replay_buffer", p->replay_buffer_size, getClock()), | ||
replay_buffer_size_(p->replay_buffer_size), | ||
replay_issue_delay_(p->replay_issue_delay), | ||
store_buffer_("store_buffer", p->ldst_inst_queue_size, getClock()), // Add this line | ||
store_buffer_size_(p->ldst_inst_queue_size), | ||
ready_queue_(), | ||
load_store_info_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node)) | ||
->load_store_info_allocator), | ||
|
@@ -35,7 +37,8 @@ namespace olympia | |
+ p->cache_read_stage_length), // Complete stage is after the cache read stage | ||
ldst_pipeline_("LoadStorePipeline", (complete_stage_ + 1), | ||
getClock()), // complete_stage_ + 1 is number of stages | ||
allow_speculative_load_exec_(p->allow_speculative_load_exec) | ||
allow_speculative_load_exec_(p->allow_speculative_load_exec), | ||
allow_data_forwarding_(p->allow_data_forwarding) | ||
{ | ||
sparta_assert(p->mmu_lookup_stage_length > 0, | ||
"MMU lookup stage should atleast be one cycle"); | ||
|
@@ -48,6 +51,7 @@ namespace olympia | |
ldst_pipeline_.enableCollection(node); | ||
ldst_inst_queue_.enableCollection(node); | ||
replay_buffer_.enableCollection(node); | ||
store_buffer_.enableCollection(node); | ||
|
||
// Startup handler for sending initial credits | ||
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(LSU, sendInitialCredits_)); | ||
|
@@ -177,6 +181,12 @@ namespace olympia | |
{ | ||
ILOG("New instruction added to the ldst queue " << inst_ptr); | ||
allocateInstToIssueQueue_(inst_ptr); | ||
// allocate to Store buffer | ||
if (inst_ptr->isStoreInst()) | ||
{ | ||
allocateInstToStoreBuffer_(inst_ptr); | ||
} | ||
|
||
handleOperandIssueCheck_(inst_ptr); | ||
lsu_insts_dispatched_++; | ||
} | ||
|
@@ -265,7 +275,19 @@ namespace olympia | |
sparta_assert(inst_ptr->getStatus() == Inst::Status::RETIRED, | ||
"Get ROB Ack, but the store inst hasn't retired yet!"); | ||
|
||
++stores_retired_; | ||
if (inst_ptr->isStoreInst()) | ||
{ | ||
auto oldest_store = getOldestStore_(); | ||
sparta_assert(oldest_store && oldest_store->getInstPtr()->getUniqueID() == inst_ptr->getUniqueID(), | ||
"Attempting to retire store out of order! Expected: " | ||
<< (oldest_store ? oldest_store->getInstPtr()->getUniqueID() : 0) | ||
<< " Got: " << inst_ptr->getUniqueID()); | ||
|
||
// Remove from store buffer -> don't actually need to send cache request | ||
store_buffer_.erase(store_buffer_.begin());; | ||
++stores_retired_; | ||
} | ||
|
||
|
||
updateIssuePriorityAfterStoreInstRetire_(inst_ptr); | ||
if (isReadyToIssueInsts_()) | ||
|
@@ -447,7 +469,7 @@ namespace olympia | |
{ | ||
updateInstReplayReady_(load_store_info_ptr); | ||
} | ||
// There might not be a wake up because the cache cannot handle nay more instruction | ||
// There might not be a wake up because the cache cannot handle any more instruction | ||
// Change to nack wakeup when implemented | ||
if (!load_store_info_ptr->isInReadyQueue()) | ||
{ | ||
|
@@ -468,7 +490,7 @@ namespace olympia | |
// If have passed translation and the instruction is a store, | ||
// then it's good to be retired (i.e. mark it completed). | ||
// Stores typically do not cause a flush after a successful | ||
// translation. We now wait for the Retire block to "retire" | ||
// translation. We now wait for the Retire block to "retire" | ||
// it, meaning it's good to go to the cache | ||
if (inst_ptr->isStoreInst() && (inst_ptr->getStatus() == Inst::Status::SCHEDULED)) | ||
{ | ||
|
@@ -483,21 +505,36 @@ namespace olympia | |
return; | ||
} | ||
|
||
// Loads dont perform a cache lookup if there are older stores present in the load store | ||
// queue | ||
if (!inst_ptr->isStoreInst() && olderStoresExists_(inst_ptr) | ||
// Loads don't perform a cache lookup if there are older stores haven't issued in the load store queue | ||
if (!inst_ptr->isStoreInst() && !allOlderStoresIssued_(inst_ptr) | ||
&& allow_speculative_load_exec_) | ||
{ | ||
ILOG("Dropping speculative load " << inst_ptr); | ||
load_store_info_ptr->setState(LoadStoreInstInfo::IssueState::READY); | ||
ldst_pipeline_.invalidateStage(cache_lookup_stage_); | ||
// TODO: double check whether "allow_speculative_load_exec_" means not allow | ||
if (allow_speculative_load_exec_) | ||
{ | ||
updateInstReplayReady_(load_store_info_ptr); | ||
} | ||
return; | ||
} | ||
|
||
// Add store forwarding check here for loads | ||
if (!inst_ptr->isStoreInst() && allow_data_forwarding_) | ||
{ | ||
const uint64_t load_addr = inst_ptr->getTargetVAddr(); | ||
auto forwarding_store = findYoungestMatchingStore_(load_addr); | ||
|
||
if (forwarding_store) | ||
{ | ||
ILOG("Found forwarding store for load " << inst_ptr); | ||
mem_access_info_ptr->setDataReady(true); | ||
mem_access_info_ptr->setCacheState(MemoryAccessInfo::CacheState::HIT); | ||
return; | ||
} | ||
} | ||
|
||
const bool is_already_hit = | ||
(mem_access_info_ptr->getCacheState() == MemoryAccessInfo::CacheState::HIT); | ||
const bool is_unretired_store = | ||
|
@@ -790,6 +827,7 @@ namespace olympia | |
flushIssueQueue_(criteria); | ||
flushReplayBuffer_(criteria); | ||
flushReadyQueue_(criteria); | ||
flushStoreBuffer_(criteria); | ||
|
||
// Cancel replay events | ||
auto flush = [&criteria](const LoadStoreInstInfoPtr & ldst_info_ptr) -> bool | ||
|
@@ -894,6 +932,36 @@ namespace olympia | |
ILOG("Append new load/store instruction to issue queue!"); | ||
} | ||
|
||
void LSU::allocateInstToStoreBuffer_(const InstPtr & inst_ptr) | ||
{ | ||
const auto & store_info_ptr = createLoadStoreInst_(inst_ptr); | ||
|
||
sparta_assert(store_buffer_.size() < ldst_inst_queue_size_, | ||
"Appending store buffer causes overflows!"); | ||
|
||
store_buffer_.push_back(store_info_ptr); | ||
ILOG("Store added to store buffer: " << inst_ptr); | ||
} | ||
|
||
LoadStoreInstInfoPtr LSU::findYoungestMatchingStore_(const uint64_t addr) const | ||
{ | ||
LoadStoreInstInfoPtr matching_store = nullptr; | ||
|
||
auto it = std::find_if(store_buffer_.rbegin(), store_buffer_.rend(), | ||
[addr](const auto& store) { | ||
return store->getInstPtr()->getTargetVAddr() == addr; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually incorrect. You're missing the size. If the store is only 1 byte and the load is expecting 4... boom. The way this should be done is with a proper store queue or combining buffer that can mask which bytes overlap. If the overlap is partial, it's a miss. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @klingaard, is there any attribute I can use for the load/store inst's size? Attached is my idea of modifying this function.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You will have to add the method to
Your implementation almost works. 😄 What if I have two stores that can supply the data to one load? block-beta
columns 1
block:ID
StoreA ["Store A of 2 bytes"]
StoreB ["Store B of 2 bytes"]
end
space
LoadC ["Load C of 4 bytes"]
ID --> LoadC
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @klingaard , I added a new function called tryStoreToLoadForwarding. Basically is adding a mask so that we make sure only all the bits needed by load are covered by store, then we mark could forward. Also added a paragraph under LSU.adoc to explain how it works However, there is a build error, BUILD_OLYMPIA=2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like either a syntax issue or you're missing an update: https://github.com/riscv-software-src/riscv-perf-model/actions/runs/15093255238/job/42424580055?pr=216#step:8:2035 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! Everything should be fixed now. :) |
||
}); | ||
return (it != store_buffer_.rend()) ? *it : nullptr; | ||
} | ||
|
||
LoadStoreInstInfoPtr LSU::getOldestStore_() const | ||
{ | ||
if(store_buffer_.empty()) { | ||
return nullptr; | ||
} | ||
return store_buffer_.read(0); | ||
} | ||
|
||
bool LSU::allOlderStoresIssued_(const InstPtr & inst_ptr) | ||
{ | ||
for (const auto & ldst_info_ptr : ldst_inst_queue_) | ||
|
@@ -1368,4 +1436,20 @@ namespace olympia | |
} | ||
} | ||
|
||
void LSU::flushStoreBuffer_(const FlushCriteria & criteria) | ||
{ | ||
auto sb_iter = store_buffer_.begin(); | ||
while(sb_iter != store_buffer_.end()) { | ||
auto inst_ptr = (*sb_iter)->getInstPtr(); | ||
if(criteria.includedInFlush(inst_ptr)) { | ||
auto delete_iter = sb_iter++; | ||
// store buffer didn't return an iterator | ||
store_buffer_.erase(delete_iter); | ||
ILOG("Flushed store from store buffer: " << inst_ptr); | ||
} else { | ||
++sb_iter; | ||
} | ||
} | ||
} | ||
|
||
} // namespace olympia |
Uh oh!
There was an error while loading. Please reload this page.