Skip to content
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

Enhance Olympia to add support for multiple load/store pipelines-LFX Mentorship Enhancement to Olympia: Ojus Chugh #202

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 102 additions & 91 deletions core/LSU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,111 +6,120 @@
#include "OlympiaAllocators.hpp"

namespace olympia
const char LSU::name[] = "lsu";

////////////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////////

LSU::LSU(sparta::TreeNode* node, const LSUParameterSet* p) :
sparta::Unit(node),
ldst_inst_queue_size_(p->ldst_inst_queue_size),
replay_buffer_size_(p->replay_buffer_size),
replay_issue_delay_(p->replay_issue_delay),
num_pipelines_(p->num_pipelines),
address_calculation_stage_(0),
mmu_lookup_stage_(address_calculation_stage_ + p->mmu_lookup_stage_length),
cache_lookup_stage_(mmu_lookup_stage_ + p->cache_lookup_stage_length),
cache_read_stage_(cache_lookup_stage_ + 1), // Get data from the cache in the cycle after cache lookup
complete_stage_(cache_read_stage_ + p->cache_read_stage_length), // Complete stage is after the cache read stage
allow_speculative_load_exec_(p->allow_speculative_load_exec),
load_store_info_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node))
->load_store_info_allocator),
memory_access_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node))
->memory_access_allocator)
{
const char LSU::name[] = "lsu";

////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////

LSU::LSU(sparta::TreeNode* node, const LSUParameterSet* p) :
sparta::Unit(node),
ldst_inst_queue_("lsu_inst_queue", p->ldst_inst_queue_size, getClock()),
ldst_inst_queue_size_(p->ldst_inst_queue_size),
replay_buffer_("replay_buffer", p->replay_buffer_size, getClock()),
replay_buffer_size_(p->replay_buffer_size),
replay_issue_delay_(p->replay_issue_delay),
ready_queue_(),
load_store_info_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node))
->load_store_info_allocator),
memory_access_allocator_(sparta::notNull(OlympiaAllocators::getOlympiaAllocators(node))
->memory_access_allocator),
address_calculation_stage_(0),
mmu_lookup_stage_(address_calculation_stage_ + p->mmu_lookup_stage_length),
cache_lookup_stage_(mmu_lookup_stage_ + p->cache_lookup_stage_length),
cache_read_stage_(cache_lookup_stage_
+ 1), // Get data from the cache in the cycle after cache lookup
complete_stage_(
cache_read_stage_
+ 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)
{
sparta_assert(p->mmu_lookup_stage_length > 0,
"MMU lookup stage should atleast be one cycle");
sparta_assert(p->cache_read_stage_length > 0,
"Cache read stage should atleast be one cycle");
sparta_assert(p->cache_lookup_stage_length > 0,
"Cache lookup stage should atleast be one cycle");

// Pipeline collection config
ldst_pipeline_.enableCollection(node);
ldst_inst_queue_.enableCollection(node);
replay_buffer_.enableCollection(node);

// Startup handler for sending initial credits
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(LSU, sendInitialCredits_));
// Validate the stages' lengths to ensure they are at least one cycle
sparta_assert(p->mmu_lookup_stage_length > 0,
"MMU lookup stage should at least be one cycle");
sparta_assert(p->cache_read_stage_length > 0,
"Cache read stage should at least be one cycle");
sparta_assert(p->cache_lookup_stage_length > 0,
"Cache lookup stage should at least be one cycle");

// Initialize vectors for multiple pipelines, buffers, and ready queues
ldst_pipelines_.resize(num_pipelines_);
ldst_inst_queues_.resize(num_pipelines_);
replay_buffers_.resize(num_pipelines_);
ready_queues_.resize(num_pipelines_);

// Configure each pipeline with corresponding buffers and stages
for (uint32_t i = 0; i < num_pipelines_; ++i) {
// Initialize instruction queue for each pipeline
ldst_inst_queues_[i] = sparta::Buffer<LoadStoreInstInfoPtr>("lsu_inst_queue_" + std::to_string(i), ldst_inst_queue_size_, getClock());

// Initialize replay buffer for speculative execution
replay_buffers_[i] = sparta::Buffer<LoadStoreInstInfoPtr>("replay_buffer_" + std::to_string(i), replay_buffer_size_, getClock());

// Initialize priority queue for ready instructions
ready_queues_[i] = sparta::PriorityQueue<LoadStoreInstInfoPtr>("ready_queue_" + std::to_string(i), getClock());

// Initialize pipeline for load/store instructions
ldst_pipelines_[i] = sparta::Pipeline<LoadStoreInstInfoPtr>("LoadStorePipeline_" + std::to_string(i), (complete_stage_ + 1), getClock());

// Enable collection for the pipeline and buffers for monitoring and debugging
ldst_pipelines_[i].enableCollection(node);
ldst_inst_queues_[i].enableCollection(node);
replay_buffers_[i].enableCollection(node);

// Register handlers for each stage in the pipeline
ldst_pipelines_[i].registerHandlerAtStage(
address_calculation_stage_, CREATE_SPARTA_HANDLER(LSU, handleAddressCalculation_));

// Port config
in_lsu_insts_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getInstsFromDispatch_, InstPtr));
ldst_pipelines_[i].registerHandlerAtStage(
mmu_lookup_stage_, CREATE_SPARTA_HANDLER(LSU, handleMMULookupReq_));

in_rob_retire_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromROB_, InstPtr));
ldst_pipelines_[i].registerHandlerAtStage(
cache_lookup_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_));

in_reorder_flush_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleFlush_, FlushManager::FlushingCriteria));
ldst_pipelines_[i].registerHandlerAtStage(
cache_read_stage_, CREATE_SPARTA_HANDLER(LSU, handleCacheRead_));

in_mmu_lookup_req_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleMMUReadyReq_, MemoryAccessInfoPtr));
ldst_pipelines_[i].registerHandlerAtStage(
complete_stage_, CREATE_SPARTA_HANDLER(LSU, completeInst_));
}

in_mmu_lookup_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromMMU_, MemoryAccessInfoPtr));
// Startup event to send initial credits for the LSU
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(LSU, sendInitialCredits_));

in_cache_lookup_req_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleCacheReadyReq_, MemoryAccessInfoPtr));
// Configure ports and register handlers for various events
in_lsu_insts_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getInstsFromDispatch_, InstPtr));

in_cache_lookup_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromCache_, MemoryAccessInfoPtr));
in_rob_retire_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromROB_, InstPtr));

// Allow the pipeline to create events and schedule work
ldst_pipeline_.performOwnUpdates();
in_reorder_flush_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleFlush_, FlushManager::FlushingCriteria));

// There can be situations where NOTHING is going on in the
// simulator but forward progression of the pipeline elements.
// In this case, the internal event for the LS pipeline will
// be the only event keeping simulation alive. Sparta
// supports identifying non-essential events (by calling
// setContinuing to false on any event).
ldst_pipeline_.setContinuing(true);
in_mmu_lookup_req_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleMMUReadyReq_, MemoryAccessInfoPtr));

ldst_pipeline_.registerHandlerAtStage(
address_calculation_stage_, CREATE_SPARTA_HANDLER(LSU, handleAddressCalculation_));
in_mmu_lookup_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromMMU_, MemoryAccessInfoPtr));

ldst_pipeline_.registerHandlerAtStage(mmu_lookup_stage_,
CREATE_SPARTA_HANDLER(LSU, handleMMULookupReq_));
in_cache_lookup_req_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, handleCacheReadyReq_, MemoryAccessInfoPtr));

ldst_pipeline_.registerHandlerAtStage(cache_lookup_stage_,
CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_));
in_cache_lookup_ack_.registerConsumerHandler(
CREATE_SPARTA_HANDLER_WITH_DATA(LSU, getAckFromCache_, MemoryAccessInfoPtr));

ldst_pipeline_.registerHandlerAtStage(cache_read_stage_,
CREATE_SPARTA_HANDLER(LSU, handleCacheRead_));
// Allow each pipeline to perform its own updates and set them to continue
for (auto& pipeline : ldst_pipelines_) {
pipeline.performOwnUpdates();
pipeline.setContinuing(true);
}

ldst_pipeline_.registerHandlerAtStage(complete_stage_,
CREATE_SPARTA_HANDLER(LSU, completeInst_));
// Register for notification if the simulation is stopped prematurely due to the ROB hitting the retire limit
node->getParent()->registerForNotification<bool, LSU, &LSU::onROBTerminate_>(
this, "rob_stopped_notif_channel", false /* ROB may not be constructed yet */);

// Capture when the simulation is stopped prematurely by the ROB i.e. hitting retire limit
node->getParent()->registerForNotification<bool, LSU, &LSU::onROBTerminate_>(
this, "rob_stopped_notif_channel", false /* ROB maybe not be constructed yet */);
// Connect events for instruction issuing and arbitration
uev_append_ready_ >> uev_issue_inst_;

uev_append_ready_ >> uev_issue_inst_;
// NOTE:
// To resolve the race condition when:
// Both cache and MMU try to drive the single BIU port at the same cycle
// Here we give cache the higher priority
ILOG("LSU construct: #" << node->getGroupIdx());
}
// Log construction of LSU with the group index
ILOG("LSU construct: #" << node->getGroupIdx());
}

LSU::~LSU()
{
Expand Down Expand Up @@ -272,8 +281,9 @@ namespace olympia
}

// Issue/Re-issue ready instructions in the issue queue
void LSU::issueInst_()
{
void LSU::issueInst_(){
// Instruction issue arbitration for each pipeline
for (uint32_t i = 0; i < num_pipelines_; ++i) {
// Instruction issue arbitration
const LoadStoreInstInfoPtr win_ptr = arbitrateInstIssue_();
// NOTE:
Expand All @@ -284,7 +294,7 @@ namespace olympia
++lsu_insts_issued_;

// Append load/store pipe
ldst_pipeline_.append(win_ptr);
ldst_pipelines_[i].append(win_ptr);

// We append to replay queue to prevent ref count of the shared pointer to drop before
// calling pop below
Expand All @@ -308,6 +318,7 @@ namespace olympia
uev_issue_inst_.schedule(sparta::Clock::Cycle(1));
}
}
}

void LSU::handleAddressCalculation_()
{
Expand Down
19 changes: 12 additions & 7 deletions core/LSU.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#pragma once

#include "sparta/ports/PortSet.hpp"
Expand Down Expand Up @@ -51,6 +50,7 @@ namespace olympia
PARAMETER(uint32_t, replay_buffer_size, ldst_inst_queue_size, "Replay buffer size")
PARAMETER(uint32_t, replay_issue_delay, 3, "Replay Issue delay")
// LSU microarchitecture parameters
PARAMETER(uint32_t, num_pipelines, 2, "Number of load/store pipelines")
PARAMETER(
bool, allow_speculative_load_exec, true,
"Allow loads to proceed speculatively before all older store addresses are known")
Expand Down Expand Up @@ -87,6 +87,7 @@ namespace olympia
std::array<std::unique_ptr<sparta::ScoreboardView>, core_types::N_REGFILES>;

ScoreboardViews scoreboard_views_;

////////////////////////////////////////////////////////////////////////////////
// Input Ports
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -128,9 +129,17 @@ namespace olympia
// Internal States
////////////////////////////////////////////////////////////////////////////////

// Number of pipelines
const uint32_t num_pipelines_;

// Vectors for multiple pipelines
std::vector<sparta::Pipeline<LoadStoreInstInfoPtr>> ldst_pipelines_;
std::vector<sparta::Buffer<LoadStoreInstInfoPtr>> ldst_inst_queues_;
std::vector<sparta::Buffer<LoadStoreInstInfoPtr>> replay_buffers_;
std::vector<sparta::PriorityQueue<LoadStoreInstInfoPtr>> ready_queues_;

// Issue Queue
using LoadStoreIssueQueue = sparta::Buffer<LoadStoreInstInfoPtr>;
LoadStoreIssueQueue ldst_inst_queue_;
sparta::Buffer<LoadStoreInstInfoPtr> ldst_inst_queue_;
const uint32_t ldst_inst_queue_size_;

sparta::Buffer<LoadStoreInstInfoPtr> replay_buffer_;
Expand Down Expand Up @@ -163,10 +172,6 @@ namespace olympia
const int cache_read_stage_;
const int complete_stage_;

// Load/Store Pipeline
using LoadStorePipeline = sparta::Pipeline<LoadStoreInstInfoPtr>;
LoadStorePipeline ldst_pipeline_;

// LSU Microarchitecture parameters
const bool allow_speculative_load_exec_;

Expand Down
Loading