Skip to content

Commit

Permalink
Add SYS execute pipe (#170)
Browse files Browse the repository at this point in the history
This PR is a followup to
#162.

It is also discussed in:
#164
 
This PR changes the way SYS instructions are executed. Previously, SYS
instructions did not go through an execution pipe but instead were
dispatched to the ROB without delay, and the ROB handled the scoreboard
with respect to the destination register. This PR requires the SYS
instructions to pass through an execution pipe, thereby obviating any
special scoreboard code in the ROB.  With the exception of CSR read
instructions, the ROB will flush all SYS instructions.

This feature can be tested by running example_json.json workload, which
is part of the make regress suite.  Also, the sys execute pipe was added
to 3 config files big_core, medium_core and small_core.

How was this tested:
make regress passes 83/83.
 

---------

Co-authored-by: Anthony Hung <[email protected]>
  • Loading branch information
ah-condor and Anthony Hung authored Jun 5, 2024
1 parent a0f965f commit c1f2a23
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 118 deletions.
4 changes: 2 additions & 2 deletions arches/big_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ top.cpu.core0.extension.core_extensions:
# targets of: "int" and "div"
pipelines:
[
["int"], # exe0
["sys"], # exe0
["int", "div"], # exe1
["int", "mul"], # exe2
["int", "mul", "i2f", "cmov"], # exe3
Expand Down Expand Up @@ -68,4 +68,4 @@ top.cpu.core0.rename.scoreboards:
["iq1", 1, 1, 1, 1, 1, 1],
["iq2", 1, 1, 1, 1, 1, 1],
["iq3", 1, 1, 1, 1, 1, 1],
["iq4", 1, 1, 1, 1, 1, 1]]
["iq4", 1, 1, 1, 1, 1, 1]]
4 changes: 2 additions & 2 deletions arches/medium_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ top.cpu.core0.extension.core_extensions:
# targets of: "int" and "div"
pipelines:
[
["int", "mul", "i2f", "cmov"], # exe0
["sys", "int", "mul", "i2f", "cmov"], # exe0
["int", "div"], # exe1
["int"], # exe2
["float", "faddsub", "fmac"], # exe3
Expand Down Expand Up @@ -60,4 +60,4 @@ top.cpu.core0.rename.scoreboards:
["iq0", 1, 1, 1, 1, 1],
["iq1", 1, 1, 1, 1, 1],
["iq2", 1, 1, 1, 1, 1],
["iq3", 1, 1, 1, 1, 1]]
["iq3", 1, 1, 1, 1, 1]]
4 changes: 2 additions & 2 deletions arches/small_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ top.cpu.core0.extension.core_extensions:
# targets of: "int" and "div"
pipelines:
[
["int", "mul", "i2f", "cmov", "div"], # exe0
["sys", "int", "mul", "i2f", "cmov", "div"], # exe0
["float", "faddsub", "fmac", "f2i"], # exe1
["br"] # exe2
]
Expand Down Expand Up @@ -50,4 +50,4 @@ top.cpu.core0.rename.scoreboards:
["lsu", 1, 1, 1, 1],
["iq0", 1, 1, 1, 1],
["iq1", 1, 1, 1, 1],
["iq2", 1, 1, 1, 1]]
["iq2", 1, 1, 1, 1]]
124 changes: 57 additions & 67 deletions core/Dispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,89 +223,79 @@ namespace olympia
sparta_assert(target_pipe != InstArchInfo::TargetPipe::UNKNOWN,
"Have an instruction that doesn't know where to go: " << ex_inst);

if (SPARTA_EXPECT_FALSE(target_pipe == InstArchInfo::TargetPipe::SYS))
// Get the dispatchers used to dispatch the target.
// Find a ready-to-go dispatcher
sparta_assert(static_cast<size_t>(target_pipe) < InstArchInfo::N_TARGET_PIPES,
"Target pipe: " << target_pipe
<< " not found in dispatchers, make sure pipe is "
"defined and has an assigned dispatcher");
auto & dispatchers = dispatchers_[static_cast<size_t>(target_pipe)];
sparta_assert(dispatchers.size() > 0,
"Pipe Target: "
<< target_pipe
<< " doesn't have any execution unit that can handle that target "
"pipe. Did you define it in the yaml properly?");
// so we have a map here that checks for which valid dispatchers for that
// instruction target pipe map needs to be: "int": [exe0, exe1, exe2]
if (target_pipe != InstArchInfo::TargetPipe::LSU)
{
ex_inst.setStatus(Inst::Status::COMPLETED);
// Indicate that this instruction was dispatched
// -- it goes right to the ROB
dispatched = true;
uint32_t max_credits = 0;
olympia::Dispatcher* best_dispatcher = nullptr;
// find the dispatcher with the most amount of credits, i.e the issue queue with
// the least amount of entries
for (auto & dispatcher_iq : dispatchers)
{
if (dispatcher_iq->canAccept() && dispatcher_iq->getCredits() > max_credits)
{
best_dispatcher = dispatcher_iq.get();
max_credits = dispatcher_iq->getCredits();
}
}
if (best_dispatcher != nullptr)
{
best_dispatcher->acceptInst(ex_inst_ptr);
++unit_distribution_[target_pipe];
++unit_distribution_context_.context(target_pipe);
++weighted_unit_distribution_context_.context(target_pipe);

ex_inst_ptr->setStatus(Inst::Status::DISPATCHED);
ILOG("Sending instruction: " << ex_inst_ptr << " to "
<< best_dispatcher->getName()
<< " of target type: " << target_pipe);
dispatched = true;
}
}
else
{
// Get the dispatchers used to dispatch the target.
// Find a ready-to-go dispatcher
sparta_assert(static_cast<size_t>(target_pipe) < InstArchInfo::N_TARGET_PIPES,
"Target pipe: " << target_pipe
<< " not found in dispatchers, make sure pipe is "
"defined and has an assigned dispatcher");
auto & dispatchers = dispatchers_[static_cast<size_t>(target_pipe)];
sparta_assert(dispatchers.size() > 0,
"Pipe Target: "
<< target_pipe
<< " doesn't have any execution unit that can handle that target "
"pipe. Did you define it in the yaml properly?");
// so we have a map here that checks for which valid dispatchers for that
// instruction target pipe map needs to be: "int": [exe0, exe1, exe2]
if (target_pipe != InstArchInfo::TargetPipe::LSU)
for (auto & disp : dispatchers)
{
uint32_t max_credits = 0;
olympia::Dispatcher* best_dispatcher = nullptr;
// find the dispatcher with the most amount of credits, i.e the issue queue with
// the least amount of entries
for (auto & dispatcher_iq : dispatchers)
{
if (dispatcher_iq->canAccept() && dispatcher_iq->getCredits() > max_credits)
{
best_dispatcher = dispatcher_iq.get();
max_credits = dispatcher_iq->getCredits();
}
}
if (best_dispatcher != nullptr)
if (disp->canAccept())
{
best_dispatcher->acceptInst(ex_inst_ptr);
disp->acceptInst(ex_inst_ptr);
++unit_distribution_[target_pipe];
++unit_distribution_context_.context(target_pipe);
++weighted_unit_distribution_context_.context(target_pipe);
++(unit_distribution_context_.context(target_pipe));
++(weighted_unit_distribution_context_.context(target_pipe));

ex_inst_ptr->setStatus(Inst::Status::DISPATCHED);
ILOG("Sending instruction: " << ex_inst_ptr << " to "
<< best_dispatcher->getName()
<< " of target type: " << target_pipe);
<< disp->getName());
dispatched = true;

break;
}
}
else
{
for (auto & disp : dispatchers)
else
{
if (disp->canAccept())
{
disp->acceptInst(ex_inst_ptr);
++unit_distribution_[target_pipe];
++(unit_distribution_context_.context(target_pipe));
++(weighted_unit_distribution_context_.context(target_pipe));

ex_inst_ptr->setStatus(Inst::Status::DISPATCHED);
ILOG("Sending instruction: " << ex_inst_ptr << " to "
<< disp->getName());
dispatched = true;

break;
}
else
{
ILOG(disp->getName() << " cannot accept inst: " << ex_inst_ptr);
blocking_dispatcher_ = target_pipe;
}
ILOG(disp->getName() << " cannot accept inst: " << ex_inst_ptr);
blocking_dispatcher_ = target_pipe;
}
}
if (false == dispatched)
{
current_stall_ = static_cast<StallReason>(target_pipe);
keep_dispatching = false;
}
}

if (false == dispatched)
{
current_stall_ = static_cast<StallReason>(target_pipe);
keep_dispatching = false;
}

if (dispatched)
{
insts_dispatched->emplace_back(ex_inst_ptr);
Expand Down
1 change: 1 addition & 0 deletions core/Inst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ namespace olympia
is_branch_(opcode_info_->isInstType(mavis::OpcodeInfo::InstructionTypes::BRANCH)),
is_condbranch_(opcode_info_->isInstType(mavis::OpcodeInfo::InstructionTypes::CONDITIONAL)),
is_call_(isCallInstruction(opcode_info)),
is_csr_(opcode_info->isInstType(mavis::OpcodeInfo::InstructionTypes::CSR)),
is_return_(isReturnInstruction(opcode_info)),
status_state_(Status::FETCHED)
{
Expand Down
3 changes: 3 additions & 0 deletions core/Inst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ namespace olympia

bool isCall() const { return is_call_; }

bool isCSR() const { return is_csr_; }

bool isReturn() const { return is_return_; }

// Rename information
Expand Down Expand Up @@ -369,6 +371,7 @@ namespace olympia
const bool is_branch_;
const bool is_condbranch_;
const bool is_call_;
const bool is_csr_;
const bool is_return_;

// Did this instruction mispredict?
Expand Down
51 changes: 11 additions & 40 deletions core/ROB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ namespace olympia

void ROB::sendInitialCredits_()
{
setupScoreboardView_() ;
out_reorder_buffer_credits_.send(reorder_buffer_.capacity());
ev_ensure_forward_progress_.schedule(retire_timeout_interval_);
}
Expand Down Expand Up @@ -250,54 +249,26 @@ namespace olympia
}
}

// sys instr doesn't have a pipe so we handle special stuff here
// sys gets flushed unless it is csr rd
void ROB::retireSysInst_(InstPtr &ex_inst)
{
auto reg_file = ex_inst->getRenameData().getDestination().rf;
if (reg_file == core_types::RegFile::RF_INVALID)
{ // this is the case if dst = x0
DLOG("retiring SYS instr ");
} else // this is needed or else destination register is
{ // forever reserved and not ready
const auto & dest_bits = ex_inst->getDestRegisterBitMask(reg_file);
scoreboard_views_[reg_file]->setReady(dest_bits);
DLOG("retiring SYS inst dest reg bits: " << sparta::printBitSet(dest_bits));
}

FlushManager::FlushingCriteria criteria(FlushManager::FlushCause::POST_SYNC, ex_inst);
auto srclist = ex_inst->getRenameData().getSourceList();
// if SYS instr is not a csr instruction flush
// if SYS instr is a csr but src1 is not x0, flush
// otherwise it is a csr read, therefore don't flush
if (ex_inst->isCSR())
{ // this is the case if csr instr with src != x0
DLOG("retiring SYS wr instr with src reg " << ex_inst->getMnemonic() );

FlushManager::FlushingCriteria criteria(FlushManager::FlushCause::POST_SYNC, ex_inst);
out_retire_flush_.send(criteria);
expect_flush_ = true;
++num_flushes_;
ILOG("Instigating flush due to SYS instruction... " << *ex_inst);

}

}

// for SYS instr which doesn't have an exe pipe
void ROB::setupScoreboardView_()
{
std::string iq_name = "iq0"; // default name

if (getContainer() != nullptr)
{
const auto exe_pipe_rename =
olympia::coreutils::getPipeTopology(getContainer()->getParent(), "exe_pipe_rename");
if (exe_pipe_rename.size() > 0)
iq_name = exe_pipe_rename[0][1]; // just grab the first issue queue
}

auto cpu_node = getContainer()->findAncestorByName("core.*");
if (cpu_node == nullptr)
{
cpu_node = getContainer()->getRoot();
}
const auto& rf = core_types::RF_INTEGER;

// alu0, alu1 name is based on exe names, point to issue_queue name instead
DLOG("setup sb view: " << iq_name );
scoreboard_views_[rf].reset(
new sparta::ScoreboardView(iq_name, core_types::regfile_names[rf],
cpu_node)); // name needs to come from issue_queue
}
}

5 changes: 0 additions & 5 deletions core/ROB.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,5 @@ namespace olympia

void retireSysInst_(InstPtr & );

void setupScoreboardView_() ;
// Scoreboards
using ScoreboardViews =
std::array<std::unique_ptr<sparta::ScoreboardView>, core_types::N_REGFILES>;
ScoreboardViews scoreboard_views_;
};
}
6 changes: 6 additions & 0 deletions traces/example_json.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
"rs2": 3,
"rd": 5
},
{
"mnemonic": "csrrw",
"rs1": 1,
"rd": 4,
"csr": 9
},
{
"mnemonic": "lw",
"rs1": 4,
Expand Down

0 comments on commit c1f2a23

Please sign in to comment.