Skip to content

Commit

Permalink
Raise exception in LSU when ROB is drained (#108)
Browse files Browse the repository at this point in the history
This PR adds a notification from ROB to the entire simulation that the ROB
is about to or is expecting simulation to correctly terminate.  If a unit listens
for that notification, it can use that information to determine if simulation 
ended improperly, meaning, it might still have work to do, but didn't expect
simulation to stop.  An example of how this can happen: a unit is NOT 
scheduling an event to do work (a bug) because of a missed path to "wake"
it up.  

If the sparta::Scheduler ends simulation because it has no events to 
run, the unit will not receive the end-of-simulation notification from the ROB
and can throw an exception in this case.

---------

Signed-off-by: Suraj Shirvankar <[email protected]>
Co-authored-by: Knute Lingaard <[email protected]>
  • Loading branch information
h0lyalg0rithm and klingaard authored Dec 21, 2023
1 parent f4088b8 commit e3e1412
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 11 deletions.
1 change: 0 additions & 1 deletion core/Fetch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ namespace olympia
// Number of instructions to fetch
const uint32_t num_insts_to_fetch_;


// For traces with system instructions, skip them
const bool skip_nonuser_mode_;

Expand Down
10 changes: 8 additions & 2 deletions core/Inst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,18 @@ namespace olympia
return inst_arch_info_->getTargetPipe();
}

// ROB handling -- mark this instruction as the oldest in the machine
void setOldest(bool oldest, sparta::Scheduleable * rob_retire_event) {
ev_retire_ = rob_retire_event;
is_oldest_ = oldest;
}

bool isMarkedOldest() const { return is_oldest_; }

// Instruction trace/JSON generation -- mark instruction as
// last in trace/JSON file.
void setLast() { is_last_ = true; }
bool getLast() const { return is_last_; }

// Set the instructions unique ID. This ID in constantly
// incremented and does not repeat. The same instruction in a
// trace can have different unique IDs (due to flushing)
Expand Down Expand Up @@ -217,7 +222,8 @@ namespace olympia

sparta::memory::addr_t inst_pc_ = 0; // Instruction's PC
sparta::memory::addr_t target_vaddr_ = 0; // Instruction's Target PC (for branches, loads/stores)
bool is_oldest_ = false;
bool is_oldest_ = false;
bool is_last_ = false; // Is last intruction of trace
uint64_t unique_id_ = 0; // Supplied by Fetch
uint64_t program_id_ = 0; // Supplied by a trace Reader or execution backend
bool is_speculative_ = false; // Is this instruction soon to be flushed?
Expand Down
10 changes: 7 additions & 3 deletions core/InstGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ namespace olympia
}

++curr_inst_index_;
if (inst != nullptr) {
inst->setUniqueID(++unique_id_);
inst->setProgramID(unique_id_);
inst->setUniqueID(++unique_id_);
inst->setProgramID(unique_id_);
if(isDone()) {
inst->setLast();
}
return inst;

Expand Down Expand Up @@ -176,6 +177,9 @@ namespace olympia
//inst->setVAddrVector(std::move(addrs));
}
++next_it_;
if(isDone()) {
inst->setLast();
}
return inst;
}
catch(std::exception & excpt) {
Expand Down
25 changes: 25 additions & 0 deletions core/LSU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ namespace olympia
ldst_pipeline_.registerHandlerAtStage(cache_lookup_stage_,
CREATE_SPARTA_HANDLER(LSU, handleCacheLookupReq_));

node->getParent()->registerForNotification<bool, LSU, &LSU::onRobDrained_>(
this, "rob_notif_channel", false /* ROB maybe not be constructed yet */);

ldst_pipeline_.registerHandlerAtStage(cache_read_stage_,
CREATE_SPARTA_HANDLER(LSU, handleCacheRead_));

Expand All @@ -108,6 +111,8 @@ namespace olympia
ILOG("LSU construct: #" << node->getGroupIdx());
}

void LSU::onRobDrained_(const bool & val) { retire_done_ = val; }

LSU::~LSU()
{
DLOG(getContainer()->getLocation() << ": " << load_store_info_allocator_.getNumAllocated()
Expand All @@ -116,6 +121,17 @@ namespace olympia
<< " MemoryAccessInfo objects allocated/created");
}

void LSU::onStartingTeardown_()
{
// If ROB has not stopped the simulation &
// the ldst has entries to process we should fail
if ((false == retire_done_) && (false == ldst_inst_queue_.empty()))
{
dumpDebugContent_(std::cerr);
sparta_assert(false, "Issue queue has pending instructions");
}
}

////////////////////////////////////////////////////////////////////////////////
// Callbacks
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -761,6 +777,15 @@ namespace olympia
// Otherwise, assertion error is fired inside arbitrateInstIssue_()
}

void LSU::dumpDebugContent_(std::ostream & output) const
{
output << "LSU Contents" << std::endl;
for (const auto & entry : ldst_inst_queue_)
{
output << '\t' << entry << std::endl;
}
}

void LSU::replayReady_(const LoadStoreInstInfoPtr & replay_inst_ptr)
{
ILOG("Replay inst ready " << replay_inst_ptr);
Expand Down
10 changes: 7 additions & 3 deletions core/LSU.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,18 @@ namespace olympia
// Type Name/Alias Declaration
////////////////////////////////////////////////////////////////////////////////

bool retire_done_ = false;
using LoadStoreInstInfoPtr = sparta::SpartaSharedPointer<LoadStoreInstInfo>;
using LoadStoreInstIterator = sparta::Buffer<LoadStoreInstInfoPtr>::const_iterator;

using FlushCriteria = FlushManager::FlushingCriteria;

using LoadStoreInstIterator = sparta::Buffer<LoadStoreInstInfoPtr>::const_iterator;
void onRobDrained_(const bool & val);

private:
using ScoreboardViews =
std::array<std::unique_ptr<sparta::ScoreboardView>, core_types::N_REGFILES>;

ScoreboardViews scoreboard_views_;
////////////////////////////////////////////////////////////////////////////////
// Input Ports
Expand Down Expand Up @@ -190,7 +194,6 @@ namespace olympia
////////////////////////////////////////////////////////////////////////////////
// Callbacks
////////////////////////////////////////////////////////////////////////////////

// Send initial credits (ldst_inst_queue_size_) to Dispatch Unit
void sendInitialCredits_();

Expand Down Expand Up @@ -229,6 +232,7 @@ namespace olympia
// Handle instruction flush in LSU
void handleFlush_(const FlushCriteria &);

void dumpDebugContent_(std::ostream & output) const override final;
// Instructions in the replay ready to issue
void replayReady_(const LoadStoreInstInfoPtr &);

Expand Down Expand Up @@ -319,7 +323,7 @@ namespace olympia

// When simulation is ending (error or not), this function
// will be called
void onStartingTeardown_() override {}
void onStartingTeardown_() override;

bool olderStoresExists_(const InstPtr & inst_ptr);

Expand Down
19 changes: 17 additions & 2 deletions core/ROB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ namespace olympia
registerConsumerHandler(CREATE_SPARTA_HANDLER_WITH_DATA(ROB, handleFlush_,
FlushManager::FlushingCriteria));

// This event is ALWAYS scheduled, but it should not keep
// simulation continuing on.
// Do not allow this event to keep simulation alive
ev_ensure_forward_progress_.setContinuing(false);

// Notify other components when ROB stops the simulation
rob_drained_notif_source_.reset(new sparta::NotificationSource<bool>(
this->getContainer(),
"rob_notif_channel",
"Notification channel for rob",
"rob_notif_channel"
));
// Send initial credits to anyone that cares. Probably Dispatch.
sparta::StartupEvent(node, CREATE_SPARTA_HANDLER(ROB, sendInitialCredits_));
}
Expand Down Expand Up @@ -133,6 +139,7 @@ namespace olympia
// Will be true if the user provides a -i option
if (SPARTA_EXPECT_FALSE((num_retired_ == num_insts_to_retire_))) {
rob_stopped_simulation_ = true;
rob_drained_notif_source_->postNotification(true);
getScheduler()->stopRunning();
break;
}
Expand All @@ -151,6 +158,14 @@ namespace olympia
break;
}

// Check to see if this is the last instruction of the
// trace
if(ex_inst.getLast()) {
rob_stopped_simulation_ = true;
rob_drained_notif_source_->postNotification(true);
// No need to stop the scheduler -- let simulation
// drain normally. Also, don't need to check forward progress
}
}
else {
break;
Expand Down
2 changes: 2 additions & 0 deletions core/ROB.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ namespace olympia
sparta::Event<> ev_ensure_forward_progress_{&unit_event_set_, "forward_progress_check",
CREATE_SPARTA_HANDLER(ROB, checkForwardProgress_)};

std::unique_ptr<sparta::NotificationSource<bool>> rob_drained_notif_source_;

void sendInitialCredits_();
void retireEvent_();
void robAppended_(const InstGroup &);
Expand Down
10 changes: 10 additions & 0 deletions test/core/rename/Rename_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ class olympia::LSUTester
// while the ADD instruction is running, the STORE instruction should NOT issue
EXPECT_TRUE(lsu.lsu_insts_issued_ == 0);
}

void clear_entries(olympia::LSU &lsu){
auto iter = lsu.ldst_inst_queue_.begin();
while(iter != lsu.ldst_inst_queue_.end()){
auto x(iter++);
lsu.ldst_inst_queue_.erase(x);
}
}
};

//
Expand Down Expand Up @@ -444,6 +452,7 @@ void runTest(int argc, char **argv)
cls.runSimulator(&sim, 6);
executepipe_tester.test_dependent_integer_first_instruction(*my_executepipe);
lsu_tester.test_dependent_lsu_instruction(*my_lsu);
lsu_tester.clear_entries(*my_lsu);
}
else if(input_file == "raw_float_lsu.json"){
// testing RAW dependency for data operand
Expand All @@ -457,6 +466,7 @@ void runTest(int argc, char **argv)
cls.runSimulator(&sim, 6);
executepipe_tester.test_dependent_integer_first_instruction(*my_executepipe);
lsu_tester.test_dependent_lsu_instruction(*my_lsu);
lsu_tester.clear_entries(*my_lsu);
}
else if(input_file == "amoadd.json"){
sparta::Scheduler sched;
Expand Down

0 comments on commit e3e1412

Please sign in to comment.