Skip to content

Commit

Permalink
Refactored Vector Uop Generation (#179)
Browse files Browse the repository at this point in the history
This PR lands the groundwork for expanding vector uop generation to
support different types of vector instructions.

---------

Co-authored-by: Kathlene Magnus <[email protected]>
  • Loading branch information
kathlenemagnus and kathlenemagnus-mips authored Jul 16, 2024
1 parent 2e27aba commit 2578a7d
Show file tree
Hide file tree
Showing 16 changed files with 462 additions and 274 deletions.
1 change: 1 addition & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_library(core
SimpleBranchPred.cpp
Fetch.cpp
Decode.cpp
VectorUopGenerator.cpp
Rename.cpp
Dispatch.cpp
Dispatcher.cpp
Expand Down
6 changes: 5 additions & 1 deletion core/CPUFactories.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "Core.hpp"
#include "Fetch.hpp"
#include "Decode.hpp"
#include "VectorUopGenerator.hpp"
#include "Rename.hpp"
#include "Dispatch.hpp"
#include "Execute.hpp"
Expand Down Expand Up @@ -47,6 +48,10 @@ namespace olympia{
sparta::ResourceFactory<olympia::Decode,
olympia::Decode::DecodeParameterSet> decode_rf;

//! \brief Resource Factory to build a VectorUopGenerator
sparta::ResourceFactory<olympia::VectorUopGenerator,
olympia::VectorUopGenerator::VectorUopGeneratorParameterSet> vec_uop_gen_rf;

//! \brief Resource Factory to build a Rename Unit
RenameFactory rename_rf;

Expand All @@ -56,7 +61,6 @@ namespace olympia{
//! \brief Resource Factory to build a Execute Unit
ExecuteFactory execute_rf;


//! \brief Resource Factory to build a MMU Unit
sparta::ResourceFactory<olympia::DCache,
olympia::DCache::CacheParameterSet> dcache_rf;
Expand Down
8 changes: 8 additions & 0 deletions core/CPUTopology.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ olympia::CoreTopologySimple::CoreTopologySimple(){
sparta::TreeNode::GROUP_IDX_NONE,
&factories->decode_rf
},
{
"vec_uop_gen",
"cpu.core*.decode",
"Vector Uop Generator",
sparta::TreeNode::GROUP_NAME_NONE,
sparta::TreeNode::GROUP_IDX_NONE,
&factories->vec_uop_gen_rf
},
{
"rename",
"cpu.core*",
Expand Down
331 changes: 157 additions & 174 deletions core/Decode.cpp

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions core/Decode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace olympia
{

class VectorUopGenerator;
/**
* @file Decode.h
* @brief Decode instructions from Fetch and send them on
Expand Down Expand Up @@ -153,6 +153,9 @@ namespace olympia
InstQueue fetch_queue_;
InstQueue uop_queue_;

// Vector uop generator
VectorUopGenerator * vec_uop_gen_ = nullptr;

// Port listening to the fetch queue appends - Note the 1 cycle delay
sparta::DataInPort<InstGroupPtr> fetch_queue_write_in_{&unit_port_set_,
"in_fetch_queue_write", 1};
Expand Down Expand Up @@ -331,14 +334,18 @@ namespace olympia
const std::vector<std::string> fusion_group_definitions_;

Inst::VCSRs VCSRs_;

MavisType* mavis_facade_;

uint32_t mavis_vsetvl_uid_;
uint32_t mavis_vsetivli_uid_;
uint32_t mavis_vsetvli_uid_;

bool waiting_on_vset_;

// Helper method to update VCSRs
void updateVcsrs_(const InstPtr &);

//////////////////////////////////////////////////////////////////////
// Decoder callbacks
void sendInitialCredits_();
Expand Down
44 changes: 37 additions & 7 deletions core/Inst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ namespace olympia
// Vector CSRs
struct VCSRs
{
uint32_t vl = 16; // vector length
uint32_t sew = 8; // set element width
uint32_t lmul = 1; // effective length
uint32_t vl = 128;
bool vta = false; // vector tail agnostic, false = undisturbed, true = agnostic
bool vta = false; // vector tail agnostic, false = undisturbed, true = agnostic

uint32_t vlmax_formula() { return (VLEN / sew) * lmul; }

void setVCSRs(uint32_t input_vl, uint32_t input_sew, uint32_t input_lmul,
void setVCSRs(uint32_t input_vl,
uint32_t input_sew,
uint32_t input_lmul,
uint32_t input_vta)
{
vl = input_vl;
Expand Down Expand Up @@ -247,10 +249,18 @@ namespace olympia
void setTargetVAddr(sparta::memory::addr_t target_vaddr) { target_vaddr_ = target_vaddr; }

// Set lmul from vset (vsetivli, vsetvli)
void setLMUL(uint32_t lmul) { VCSRs_.lmul = lmul; }
void setLMUL(uint32_t lmul)
{
VCSRs_.lmul = lmul;
VCSRs_.vlmax = VCSRs_.vlmax_formula();
}

// Set sew from vset (vsetivli, vsetvli)
void setSEW(uint32_t sew) { VCSRs_.sew = sew; }
void setSEW(uint32_t sew)
{
VCSRs_.sew = sew;
VCSRs_.vlmax = VCSRs_.vlmax_formula();
}

// Set VL from vset (vsetivli, vsetvli)
void setVL(uint32_t vl) { VCSRs_.vl = vl; }
Expand All @@ -262,11 +272,13 @@ namespace olympia

void setTail(bool has_tail) { has_tail_ = has_tail; }

void setVCSRs(const VCSRs & inputVCSRs)
void setVCSRs(const VCSRs * inputVCSRs)
{
VCSRs_.setVCSRs(inputVCSRs.vl, inputVCSRs.sew, inputVCSRs.lmul, inputVCSRs.vta);
VCSRs_.setVCSRs(inputVCSRs->vl, inputVCSRs->sew, inputVCSRs->lmul, inputVCSRs->vta);
}

const VCSRs * getVCSRs() const { return &VCSRs_; }

void setUOpParent(sparta::SpartaWeakPointer<olympia::Inst> & uop_parent)
{
uop_parent_ = uop_parent;
Expand Down Expand Up @@ -324,6 +336,24 @@ namespace olympia

const OpInfoList & getDestOpInfoList() const { return opcode_info_->getDestOpInfoList(); }

bool hasZeroRegSource() const
{
return std::any_of(getSourceOpInfoList().begin(), getSourceOpInfoList().end(),
[](const mavis::OperandInfo::Element & elem)
{
return elem.field_value == 0;
});
}

bool hasZeroRegDest() const
{
return std::any_of(getDestOpInfoList().begin(), getDestOpInfoList().end(),
[](const mavis::OperandInfo::Element & elem)
{
return elem.field_value == 0;
});
}

// Static instruction information
bool isStoreInst() const { return is_store_; }

Expand Down
83 changes: 28 additions & 55 deletions core/IssueQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,70 +97,43 @@ namespace olympia

void IssueQueue::handleOperandIssueCheck_(const InstPtr & ex_inst)
{
// FIXME: Now every source operand should be ready
auto reg_file = core_types::RegFile::RF_INTEGER;
const auto srcs = ex_inst->getRenameData().getSourceList();

if(srcs.size() > 1 && srcs[0].rf != srcs[1].rf && ex_inst->isVector()){
// we have a vector-scalar operation, 1 vector src and 1 scalar src
// need to check both
uint32_t ready = 0;
for(auto src: srcs){
reg_file = src.rf;
const auto & src_bits = ex_inst->getSrcRegisterBitMask(reg_file);
if (scoreboard_views_[reg_file]->isSet(src_bits))
{
ready++;
}
else
{
// temporary fix for clearCallbacks not working
scoreboard_views_[reg_file]->registerReadyCallback(
src_bits, ex_inst->getUniqueID(),
[this, ex_inst](const sparta::Scoreboard::RegisterBitMask &)
{ this->handleOperandIssueCheck_(ex_inst); });
ILOG("Instruction NOT ready: " << ex_inst
<< " Bits needed:" << sparta::printBitSet(src_bits) << " rf: " << reg_file);
// we break to prevent multiple callbacks from being sent out
break;
}
}
// we wait till the final callback comes back and checks in the case where both RF are ready at the same time
if(ready == srcs.size()){
// all register file types are ready
ILOG("Sending to issue queue " << ex_inst);
// will insert based on if in_order_issue_ is set
// if it is, will be first in first out, if not it'll be by age, so by UniqueID (UID)
ready_queue_.insert(ex_inst);
ev_issue_ready_inst_.schedule(sparta::Clock::Cycle(0));
}
}
else{
if (srcs.size() > 0)
{
reg_file = srcs[0].rf;
}
uint32_t ready = 0;
for(const auto & src : srcs)
{
// vector-scalar operations have 1 vector src and 1 scalar src that
// need to be checked, so can't assume the register files are the
// same for every source
auto reg_file = src.rf;
const auto & src_bits = ex_inst->getSrcRegisterBitMask(reg_file);
if (scoreboard_views_[reg_file]->isSet(src_bits))
{
// Insert at the end if we are doing in order issue or if the scheduler is
// empty
ILOG("Sending to issue queue " << ex_inst);
// will insert based on if in_order_issue_ is set
// if it is, will be first in first out, if not it'll be by age, so by UniqueID (UID)
ready_queue_.insert(ex_inst);
ev_issue_ready_inst_.schedule(sparta::Clock::Cycle(0));
ready++;
}
else
{
scoreboard_views_[reg_file]->registerReadyCallback(
src_bits, ex_inst->getUniqueID(),
[this, ex_inst](const sparta::Scoreboard::RegisterBitMask &)
{ this->handleOperandIssueCheck_(ex_inst); });
// temporary fix for clearCallbacks not working
scoreboard_views_[reg_file]->registerReadyCallback(src_bits, ex_inst->getUniqueID(),
[this, ex_inst](const sparta::Scoreboard::RegisterBitMask &)
{ this->handleOperandIssueCheck_(ex_inst); });
ILOG("Instruction NOT ready: " << ex_inst
<< " Bits needed:" << sparta::printBitSet(src_bits) << " rf: " << reg_file);
<< " Bits needed:" << sparta::printBitSet(src_bits)
<< " rf: " << reg_file);
// we break to prevent multiple callbacks from being sent out
break;
}
}

// we wait till the final callback comes back and checks in the case where both RF are ready at the same time
if(ready == srcs.size())
{
// all register file types are ready
ILOG("Sending to issue queue " << ex_inst);
// will insert based on if in_order_issue_ is set
// if it is, will be first in first out, if not it'll be by age, so by UniqueID (UID)
ready_queue_.insert(ex_inst);
ev_issue_ready_inst_.schedule(sparta::Clock::Cycle(0));
}
}

void IssueQueue::readyExeUnit_(const uint32_t & readyExe)
Expand Down Expand Up @@ -279,4 +252,4 @@ namespace olympia
}
sparta_assert(false, "Attempt to complete instruction no longer exiting in issue queue!");
}
} // namespace olympia
} // namespace olympia
1 change: 1 addition & 0 deletions core/ROB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ namespace olympia
ILOG("retiring " << ex_inst);

retire_event_.collect(*ex_inst_ptr);
last_inst_retired_ = ex_inst_ptr;

// Use the program ID to verify that the program order has been maintained.
sparta_assert(ex_inst.getProgramID() == expected_program_id_,
Expand Down
7 changes: 7 additions & 0 deletions core/ROB.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ namespace olympia
// For correlation activities
sparta::pevents::PeventCollector<InstPEventPairs> retire_event_{"RETIRE", getContainer(), getClock()};

// Last inst retired for testing
InstPtr last_inst_retired_ = nullptr;

// A nice checker to make sure forward progress is being made
// Note that in the ROB constructor, this event is set as non-continuing
sparta::Clock::Cycle last_retirement_ = 0; // Last retirement cycle for checking stalled retire
Expand All @@ -136,5 +139,9 @@ namespace olympia

void retireSysInst_(InstPtr & );

// Friend class used in retire testing
friend class ROBTester;
};

class ROBTester;
}
22 changes: 14 additions & 8 deletions core/Rename.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,18 +205,24 @@ namespace olympia
sparta_assert(oldest_inst->getUniqueID() == inst_ptr->getUniqueID(),
"ROB and rename inst_queue out of sync");
}

inst_queue_.pop_front();

// pop all UOps from inst_queue_ to relaign ROB and rename inst_queue
if (inst_ptr->hasUOps())
{
// pop all UOps from inst_queue_ to relaign ROB and rename inst_queue
for (uint32_t i = 0; i < inst_ptr->getLMUL(); i++)
while (inst_queue_.empty() == false)
{
inst_queue_.pop_front();
if (inst_ptr->getUOpID() == inst_queue_.front()->getUOpID())
{
inst_queue_.pop_front();
}
else
{
break;
}
}
}
else
{
inst_queue_.pop_front();
}
}
else
{
Expand Down Expand Up @@ -567,4 +573,4 @@ namespace olympia
ev_schedule_rename_.schedule(1);
}
}
} // namespace olympia
} // namespace olympia
Loading

0 comments on commit 2578a7d

Please sign in to comment.