From 2048c6604d80019cd565bcb4f61c09b994c7bf0b Mon Sep 17 00:00:00 2001 From: Morten Borup Petersen Date: Sat, 26 Mar 2022 12:29:29 -0400 Subject: [PATCH] Replace numStages with more elaborate processor structure info #203 numStages is only sensible for single issue processors. In this commit, we introduce the ProcessorStructure type, a 2d mapping suitable for communicating more information about the datapath of a processor. --- src/editor/codeeditor.cpp | 46 +-- src/instructionmodel.cpp | 60 ++-- src/instructionmodel.h | 4 +- src/pipelinediagrammodel.cpp | 10 +- src/pipelinediagrammodel.h | 2 +- src/processorregistry.cpp | 76 +++-- src/processorregistry.h | 2 +- src/processors/RISC-V/rv5s/rv5s.h | 29 +- src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h | 29 +- .../RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h | 29 +- src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h | 29 +- src/processors/RISC-V/rv6s_dual/rv6s_dual.h | 271 +++++++++--------- src/processors/RISC-V/rvss/rvss.h | 15 +- src/processors/interface/ripesprocessor.h | 33 ++- src/processortab.cpp | 62 ++-- src/processortab.h | 3 +- src/programviewer.cpp | 32 ++- test/tst_cosimulate.cpp | 8 +- test/tst_riscv.cpp | 8 +- 19 files changed, 416 insertions(+), 332 deletions(-) diff --git a/src/editor/codeeditor.cpp b/src/editor/codeeditor.cpp index d94cd0fcc..d1562e02c 100644 --- a/src/editor/codeeditor.cpp +++ b/src/editor/codeeditor.cpp @@ -442,30 +442,34 @@ void CodeEditor::updateHighlighting() { // Iterate over the processor stages and use the source mappings to determine // the source line which originated the instruction. - const unsigned stages = proc->stageCount(); + const unsigned stages = proc->structure().numStages(); auto colorGenerator = Colors::incrementalRedGenerator(stages); - for (unsigned sid = 0; sid < stages; sid++) { - const auto stageInfo = proc->stageInfo(sid); - QColor stageColor = colorGenerator(); - if (stageInfo.stage_valid) { - auto mappingIt = sourceMapping.find(stageInfo.pc); - if (mappingIt == sourceMapping.end()) { - // No source line registerred for this PC. - continue; - } - - for (auto sourceLine : mappingIt->second) { - // Find block - QTextBlock block = document()->findBlockByLineNumber(sourceLine); - if (!block.isValid()) + for (auto laneIt : proc->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex sid = StageIndex{laneIt.first, stageIdx}; + const auto stageInfo = proc->stageInfo(sid); + QColor stageColor = colorGenerator(); + if (stageInfo.stage_valid) { + auto mappingIt = sourceMapping.find(stageInfo.pc); + if (mappingIt == sourceMapping.end()) { + // No source line registerred for this PC. continue; - - // Record the stage name for the highlighted block for later painting - QString stageString = ProcessorHandler::getProcessor()->stageName(sid); - if (!stageInfo.namedState.isEmpty()) - stageString += " (" + stageInfo.namedState + ")"; - highlightBlock(block, stageColor, stageString); + } + + for (auto sourceLine : mappingIt->second) { + // Find block + QTextBlock block = document()->findBlockByLineNumber(sourceLine); + if (!block.isValid()) + continue; + + // Record the stage name for the highlighted block for later painting + QString stageString = + ProcessorHandler::getProcessor()->stageName(sid); + if (!stageInfo.namedState.isEmpty()) + stageString += " (" + stageInfo.namedState + ")"; + highlightBlock(block, stageColor, stageString); + } } } } diff --git a/src/instructionmodel.cpp b/src/instructionmodel.cpp index cf92b3ad8..6e113cc2a 100644 --- a/src/instructionmodel.cpp +++ b/src/instructionmodel.cpp @@ -27,10 +27,12 @@ int InstructionModel::addressToRow(AInt addr) const { InstructionModel::InstructionModel(QObject *parent) : QAbstractTableModel(parent) { - for (unsigned i = 0; i < ProcessorHandler::getProcessor()->stageCount(); - ++i) { - m_stageNames << ProcessorHandler::getProcessor()->stageName(i); - m_stageInfos[i]; + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex idx = {laneIt.first, stageIdx}; + m_stageNames[idx] = ProcessorHandler::getProcessor()->stageName(idx); + m_stageInfos[idx] = ProcessorHandler::getProcessor()->stageInfo(idx); + } } connect(ProcessorHandler::get(), &ProcessorHandler::procStateChangedNonRun, this, &InstructionModel::updateStageInfo); @@ -65,31 +67,33 @@ int InstructionModel::rowCount(const QModelIndex &) const { return m_rowCount; } void InstructionModel::updateStageInfo() { bool firstStageChanged = false; - for (unsigned i = 0; i < ProcessorHandler::getProcessor()->stageCount(); - ++i) { - if (static_cast(m_stageInfos.size()) > i) { - auto &oldStageInfo = m_stageInfos.at(i); - if (i == 0) { - if (oldStageInfo.pc != - ProcessorHandler::getProcessor()->stageInfo(i).pc) { - firstStageChanged = true; + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex idx = {laneIt.first, stageIdx}; + auto stageInfoIt = m_stageInfos.find(idx); + if (stageInfoIt != m_stageInfos.end()) { + auto &oldStageInfo = m_stageInfos.at(idx); + if (idx == StageIndex(0, 0)) { + if (oldStageInfo.pc != + ProcessorHandler::getProcessor()->stageInfo(idx).pc) { + firstStageChanged = true; + } + } + const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo(idx); + const AInt oldAddress = oldStageInfo.pc; + if (oldStageInfo != stageInfo) { + oldStageInfo = stageInfo; + const int oldRow = addressToRow(oldAddress); + const int newRow = addressToRow(stageInfo.pc); + const QModelIndex oldIdx = index(oldRow, Stage); + const QModelIndex newIdx = index(newRow, Stage); + emit dataChanged(oldIdx, oldIdx, {Qt::DisplayRole}); + emit dataChanged(newIdx, newIdx, {Qt::DisplayRole}); + } + if (firstStageChanged) { + emit firstStageInstrChanged(addressToRow(m_stageInfos.at({0, 0}).pc)); + firstStageChanged = false; } - } - const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo(i); - const AInt oldAddress = oldStageInfo.pc; - const bool stageInfoChanged = oldStageInfo != stageInfo; - oldStageInfo = stageInfo; - if (stageInfoChanged) { - const int oldRow = addressToRow(oldAddress); - const int newRow = addressToRow(stageInfo.pc); - const QModelIndex oldIdx = index(oldRow, Stage); - const QModelIndex newIdx = index(newRow, Stage); - emit dataChanged(oldIdx, oldIdx, {Qt::DisplayRole}); - emit dataChanged(newIdx, newIdx, {Qt::DisplayRole}); - } - if (firstStageChanged) { - emit firstStageInstrChanged(addressToRow(m_stageInfos.at(0).pc)); - firstStageChanged = false; } } } diff --git a/src/instructionmodel.h b/src/instructionmodel.h index efcb31f16..f9b235566 100644 --- a/src/instructionmodel.h +++ b/src/instructionmodel.h @@ -52,9 +52,9 @@ class InstructionModel : public QAbstractTableModel { void onProcessorReset(); std::shared_ptr m_program; - QStringList m_stageNames; + std::map m_stageNames; using StageID = unsigned; - std::map m_stageInfos; + std::map m_stageInfos; int m_rowCount = 0; }; } // namespace Ripes diff --git a/src/pipelinediagrammodel.cpp b/src/pipelinediagrammodel.cpp index 604a96638..af7e965f8 100644 --- a/src/pipelinediagrammodel.cpp +++ b/src/pipelinediagrammodel.cpp @@ -76,10 +76,12 @@ void PipelineDiagramModel::gatherStageInfo() { if (stageInfoForCycle == m_cycleStageInfos.end()) { return; } - for (unsigned i = 0; i < ProcessorHandler::getProcessor()->stageCount(); - ++i) { - stageInfoForCycle->second[i] = - ProcessorHandler::getProcessor()->stageInfo(i); + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex idx = {laneIt.first, stageIdx}; + stageInfoForCycle->second[idx] = + ProcessorHandler::getProcessor()->stageInfo(idx); + } } } diff --git a/src/pipelinediagrammodel.h b/src/pipelinediagrammodel.h index 5e1a3c744..1e17cf6a4 100644 --- a/src/pipelinediagrammodel.h +++ b/src/pipelinediagrammodel.h @@ -31,7 +31,7 @@ public slots: * @brief m_cycleStageInfos * map> */ - std::map> m_cycleStageInfos; + std::map> m_cycleStageInfos; /** * @brief m_atMaxCycles diff --git a/src/processorregistry.cpp b/src/processorregistry.cpp index 6e6b1ff91..b885430d0 100644 --- a/src/processorregistry.cpp +++ b/src/processorregistry.cpp @@ -19,10 +19,10 @@ ProcessorRegistry::ProcessorRegistry() { // RISC-V single cycle layouts = {{"Standard", ":/layouts/RISC-V/rvss/rv_ss_standard_layout.json", - {QPointF{0.5, 0}}}, + {{{0, 0}, QPointF{0.5, 0}}}}, {"Extended", ":/layouts/RISC-V/rvss/rv_ss_extended_layout.json", - {QPointF{0.5, 0}}}}; + {{{0, 0}, QPointF{0.5, 0}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_SS, "Single-cycle processor", @@ -35,12 +35,18 @@ ProcessorRegistry::ProcessorRegistry() { layouts = { {"Standard", ":/layouts/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz_standard_layout.json", - {QPointF{0.08, 0}, QPointF{0.3, 0}, QPointF{0.54, 0}, QPointF{0.73, 0}, - QPointF{0.88, 0}}}, + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.3, 0}}, + {{0, 2}, QPointF{0.54, 0}}, + {{0, 3}, QPointF{0.73, 0}}, + {{0, 4}, QPointF{0.88, 0}}}}, {"Extended", ":/layouts/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz_extended_layout.json", - {QPointF{0.08, 0.0}, QPointF{0.31, 0.0}, QPointF{0.56, 0.0}, - QPointF{0.76, 0.0}, QPointF{0.9, 0.0}}}}; + {{{0, 0}, QPointF{0.08, 0.0}}, + {{0, 1}, QPointF{0.31, 0.0}}, + {{0, 2}, QPointF{0.56, 0.0}}, + {{0, 3}, QPointF{0.76, 0.0}}, + {{0, 4}, QPointF{0.9, 0.0}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_5S_NO_FW_HZ, @@ -58,12 +64,18 @@ ProcessorRegistry::ProcessorRegistry() { // RISC-V 5-stage without hazard detection layouts = {{"Standard", ":/layouts/RISC-V/rv5s_no_hz/rv5s_no_hz_standard_layout.json", - {QPointF{0.08, 0}, QPointF{0.3, 0}, QPointF{0.53, 0}, - QPointF{0.75, 0}, QPointF{0.88, 0}}}, + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.3, 0}}, + {{0, 2}, QPointF{0.53, 0}}, + {{0, 3}, QPointF{0.75, 0}}, + {{0, 4}, QPointF{0.88, 0}}}}, {"Extended", ":/layouts/RISC-V/rv5s_no_hz/rv5s_no_hz_extended_layout.json", - {QPointF{0.08, 0}, QPointF{0.28, 0}, QPointF{0.53, 0}, - QPointF{0.78, 0}, QPointF{0.9, 0}}}}; + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.28, 0}}, + {{0, 2}, QPointF{0.53, 0}}, + {{0, 3}, QPointF{0.78, 0}}, + {{0, 4}, QPointF{0.9, 0}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_5S_NO_HZ, "5-stage processor w/o hazard detection", @@ -79,12 +91,18 @@ ProcessorRegistry::ProcessorRegistry() { // RISC-V 5-stage without forwarding unit layouts = {{"Standard", ":/layouts/RISC-V/rv5s_no_fw/rv5s_no_fw_standard_layout.json", - {QPointF{0.08, 0}, QPointF{0.3, 0}, QPointF{0.53, 0}, - QPointF{0.75, 0}, QPointF{0.88, 0}}}, + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.3, 0}}, + {{0, 2}, QPointF{0.53, 0}}, + {{0, 3}, QPointF{0.75, 0}}, + {{0, 4}, QPointF{0.88, 0}}}}, {"Extended", ":/layouts/RISC-V/rv5s_no_fw/rv5s_no_fw_extended_layout.json", - {QPointF{0.08, 0}, QPointF{0.28, 0}, QPointF{0.53, 0}, - QPointF{0.78, 0}, QPointF{0.9, 0}}}}; + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.28, 0}}, + {{0, 2}, QPointF{0.53, 0}}, + {{0, 3}, QPointF{0.78, 0}}, + {{0, 4}, QPointF{0.9, 0}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_5S_NO_FW, "5-Stage processor w/o forwarding unit", @@ -100,12 +118,18 @@ ProcessorRegistry::ProcessorRegistry() { // RISC-V 5-stage layouts = {{"Standard", ":/layouts/RISC-V/rv5s/rv5s_standard_layout.json", - {QPointF{0.08, 0}, QPointF{0.29, 0}, QPointF{0.55, 0}, - QPointF{0.75, 0}, QPointF{0.87, 0}}}, + {{{0, 0}, QPointF{0.08, 0}}, + {{0, 1}, QPointF{0.29, 0}}, + {{0, 2}, QPointF{0.55, 0}}, + {{0, 3}, QPointF{0.75, 0}}, + {{0, 4}, QPointF{0.87, 0}}}}, {"Extended", ":/layouts/RISC-V/rv5s/rv5s_extended_layout.json", - {QPointF{0.08, 0}, QPointF{0.28, 0}, QPointF{0.54, 0}, - QPointF{0.78, 0}, QPointF{0.9, 0}}}}; + {{{0, 1}, QPointF{0.08, 0}}, + {{0, 2}, QPointF{0.28, 0}}, + {{0, 3}, QPointF{0.54, 0}}, + {{0, 4}, QPointF{0.78, 0}}, + {{0, 5}, QPointF{0.9, 0}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_5S, "5-stage processor", @@ -121,10 +145,18 @@ ProcessorRegistry::ProcessorRegistry() { // RISC-V 6-stage dual issue layouts = {{"Extended", ":/layouts/RISC-V/rv6s_dual/rv6s_dual_extended_layout.json", - {{QPointF{0.06, 0}, QPointF{0.06, 1}, QPointF{0.22, 0}, - QPointF{0.22, 1}, QPointF{0.40, 0}, QPointF{0.40, 1}, - QPointF{0.59, 0}, QPointF{0.59, 1}, QPointF{0.80, 0}, - QPointF{0.80, 1}, QPointF{0.90, 0}, QPointF{0.90, 1}}}}}; + {{{{0, 0}, QPointF{0.06, 0}}, + {{1, 0}, QPointF{0.06, 1}}, + {{0, 1}, QPointF{0.22, 0}}, + {{1, 1}, QPointF{0.22, 1}}, + {{0, 2}, QPointF{0.40, 0}}, + {{1, 2}, QPointF{0.40, 1}}, + {{0, 3}, QPointF{0.59, 0}}, + {{1, 3}, QPointF{0.59, 1}}, + {{0, 4}, QPointF{0.80, 0}}, + {{1, 4}, QPointF{0.80, 1}}, + {{0, 5}, QPointF{0.90, 0}}, + {{1, 5}, QPointF{0.90, 1}}}}}}; defRegVals = {{2, 0x7ffffff0}, {3, 0x10000000}}; addProcessor(ProcInfo>( ProcessorID::RV32_6S_DUAL, "6-stage dual-issue processor", diff --git a/src/processorregistry.h b/src/processorregistry.h index 3b4c1d501..a7d88fae2 100644 --- a/src/processorregistry.h +++ b/src/processorregistry.h @@ -45,7 +45,7 @@ struct Layout { * labels can be "stacked" over one another. Must contain an entry for each * stage in the processor model. */ - std::vector stageLabelPositions; + std::map stageLabelPositions; bool operator==(const Layout &rhs) const { return this->name == rhs.name; } }; diff --git a/src/processors/RISC-V/rv5s/rv5s.h b/src/processors/RISC-V/rv5s/rv5s.h index 89cee3e11..f298c5641 100644 --- a/src/processors/RISC-V/rv5s/rv5s.h +++ b/src/processors/RISC-V/rv5s/rv5s.h @@ -333,10 +333,10 @@ class RV5S : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return STAGECOUNT; } - unsigned int getPcForStage(unsigned int idx) const override { + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return pc_reg->out.uValue(); case ID: return ifid_reg->pc_out.uValue(); case EX: return idex_reg->pc_out.uValue(); @@ -348,9 +348,9 @@ class RV5S : public RipesVSRTLProcessor { // clang-format on } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int idx) const override { + QString stageName(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return "IF"; case ID: return "ID"; case EX: return "EX"; @@ -361,14 +361,14 @@ class RV5S : public RipesVSRTLProcessor { Q_UNREACHABLE(); // clang-format on } - StageInfo stageInfo(unsigned int stage) const override { + StageInfo stageInfo(StageIndex stage) const override { bool stageValid = true; // Has the pipeline stage been filled? - stageValid &= stage <= m_cycleCount; + stageValid &= stage.index() <= m_cycleCount; // clang-format off // Has the stage been cleared? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= ifid_reg->valid_out.uValue(); break; case EX: stageValid &= idex_reg->valid_out.uValue(); break; case MEM: stageValid &= exmem_reg->valid_out.uValue(); break; @@ -377,7 +377,7 @@ class RV5S : public RipesVSRTLProcessor { } // Is the stage carrying a valid (executable) PC? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); break; case EX: stageValid &= isExecutableAddress(idex_reg->pc_out.uValue()); break; case MEM: stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()); break; @@ -386,14 +386,14 @@ class RV5S : public RipesVSRTLProcessor { } // Are we currently clearing the pipeline due to a syscall exit? if such, all stages before the EX stage are invalid - if(stage < EX){ + if(stage.index() < EX){ stageValid &= !ecallChecker->isSysCallExiting(); } // clang-format on // Gather stage state info StageInfo::State state = StageInfo ::State::None; - switch (stage) { + switch (stage.index()) { case IF: break; case ID: @@ -451,8 +451,8 @@ class RV5S : public RipesVSRTLProcessor { ecallChecker->setSysCallExiting(ecallChecker->isSysCallExiting() || (fr & FinalizeReason::exitSyscall)); } - const std::vector breakpointTriggeringStages() const override { - return {IF}; + const std::vector breakpointTriggeringStages() const override { + return {{0, IF}}; } MemoryAccess dataMemAccess() const override { @@ -469,7 +469,7 @@ class RV5S : public RipesVSRTLProcessor { // the pipeline bool allStagesInvalid = true; for (int stage = IF; stage < STAGECOUNT; stage++) { - allStagesInvalid &= !stageInfo(stage).stage_valid; + allStagesInvalid &= !stageInfo({0, stage}).stage_valid; if (!allStagesInvalid) break; } @@ -540,6 +540,7 @@ class RV5S : public RipesVSRTLProcessor { */ long long m_syscallExitCycle = -1; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 5}}; }; } // namespace core diff --git a/src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h b/src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h index 953e35b59..2fcb85870 100644 --- a/src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h +++ b/src/processors/RISC-V/rv5s_no_fw/rv5s_no_fw.h @@ -307,10 +307,10 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return STAGECOUNT; } - unsigned int getPcForStage(unsigned int idx) const override { + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return pc_reg->out.uValue(); case ID: return ifid_reg->pc_out.uValue(); case EX: return idex_reg->pc_out.uValue(); @@ -322,9 +322,9 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { // clang-format on } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int idx) const override { + QString stageName(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return "IF"; case ID: return "ID"; case EX: return "EX"; @@ -335,14 +335,14 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { Q_UNREACHABLE(); // clang-format on } - StageInfo stageInfo(unsigned int stage) const override { + StageInfo stageInfo(StageIndex stage) const override { bool stageValid = true; // Has the pipeline stage been filled? - stageValid &= stage <= m_cycleCount; + stageValid &= stage.index() <= m_cycleCount; // clang-format off // Has the stage been cleared? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= ifid_reg->valid_out.uValue(); break; case EX: stageValid &= idex_reg->valid_out.uValue(); break; case MEM: stageValid &= exmem_reg->valid_out.uValue(); break; @@ -351,7 +351,7 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { } // Is the stage carrying a valid (executable) PC? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); break; case EX: stageValid &= isExecutableAddress(idex_reg->pc_out.uValue()); break; case MEM: stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()); break; @@ -360,14 +360,14 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { } // Are we currently clearing the pipeline due to a syscall exit? if such, all stages before the EX stage are invalid - if(stage < EX){ + if(stage.index() < EX){ stageValid &= !ecallChecker->isSysCallExiting(); } // clang-format on // Gather stage state info StageInfo::State state = StageInfo ::State::None; - switch (stage) { + switch (stage.index()) { case IF: break; case ID: @@ -425,8 +425,8 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { ecallChecker->setSysCallExiting(ecallChecker->isSysCallExiting() || (fr & FinalizeReason::exitSyscall)); } - const std::vector breakpointTriggeringStages() const override { - return {IF}; + const std::vector breakpointTriggeringStages() const override { + return {{0, IF}}; } MemoryAccess dataMemAccess() const override { @@ -443,7 +443,7 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { // the pipeline bool allStagesInvalid = true; for (int stage = IF; stage < STAGECOUNT; stage++) { - allStagesInvalid &= !stageInfo(stage).stage_valid; + allStagesInvalid &= !stageInfo(StageIndex{0, stage}).stage_valid; if (!allStagesInvalid) break; } @@ -514,6 +514,7 @@ class RV5S_NO_FW : public RipesVSRTLProcessor { */ long long m_syscallExitCycle = -1; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 5}}; }; } // namespace core diff --git a/src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h b/src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h index aadadafc8..304a95f75 100644 --- a/src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h +++ b/src/processors/RISC-V/rv5s_no_fw_hz/rv5s_no_fw_hz.h @@ -264,10 +264,10 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return STAGECOUNT; } - unsigned int getPcForStage(unsigned int idx) const override { + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return pc_reg->out.uValue(); case ID: return ifid_reg->pc_out.uValue(); case EX: return idex_reg->pc_out.uValue(); @@ -279,9 +279,9 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { // clang-format on } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int idx) const override { + QString stageName(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return "IF"; case ID: return "ID"; case EX: return "EX"; @@ -292,14 +292,14 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { Q_UNREACHABLE(); // clang-format on } - StageInfo stageInfo(unsigned int stage) const override { + StageInfo stageInfo(StageIndex stage) const override { bool stageValid = true; // Has the pipeline stage been filled? - stageValid &= stage <= m_cycleCount; + stageValid &= stage.index() <= m_cycleCount; // clang-format off // Has the stage been cleared? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= ifid_reg->valid_out.uValue(); break; case EX: stageValid &= idex_reg->valid_out.uValue(); break; case MEM: stageValid &= exmem_reg->valid_out.uValue(); break; @@ -308,7 +308,7 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { } // Is the stage carrying a valid (executable) PC? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); break; case EX: stageValid &= isExecutableAddress(idex_reg->pc_out.uValue()); break; case MEM: stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()); break; @@ -317,14 +317,14 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { } // Are we currently clearing the pipeline due to a syscall exit? if such, all stages before the EX stage are invalid - if(stage < EX){ + if(stage.index() < EX){ stageValid &= !ecallChecker->isSysCallExiting(); } // clang-format on // Gather stage state info StageInfo::State state = StageInfo ::State::None; - switch (stage) { + switch (stage.index()) { case IF: break; case ID: @@ -376,8 +376,8 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { ecallChecker->setSysCallExiting(ecallChecker->isSysCallExiting() || (fr & FinalizeReason::exitSyscall)); } - const std::vector breakpointTriggeringStages() const override { - return {IF}; + const std::vector breakpointTriggeringStages() const override { + return {{0, IF}}; }; MemoryAccess dataMemAccess() const override { @@ -394,7 +394,7 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { // the pipeline bool allStagesInvalid = true; for (int stage = IF; stage < STAGECOUNT; stage++) { - allStagesInvalid &= !stageInfo(stage).stage_valid; + allStagesInvalid &= !stageInfo({0, stage}).stage_valid; if (!allStagesInvalid) break; } @@ -464,6 +464,7 @@ class RV5S_NO_FW_HZ : public RipesVSRTLProcessor { */ long long m_syscallExitCycle = -1; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 5}}; }; } // namespace core diff --git a/src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h b/src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h index 81d9f882e..afbf070e9 100644 --- a/src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h +++ b/src/processors/RISC-V/rv5s_no_hz/rv5s_no_hz.h @@ -299,10 +299,10 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return STAGECOUNT; } - unsigned int getPcForStage(unsigned int idx) const override { + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return pc_reg->out.uValue(); case ID: return ifid_reg->pc_out.uValue(); case EX: return idex_reg->pc_out.uValue(); @@ -314,9 +314,9 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { // clang-format on } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int idx) const override { + QString stageName(StageIndex idx) const override { // clang-format off - switch (idx) { + switch (idx.index()) { case IF: return "IF"; case ID: return "ID"; case EX: return "EX"; @@ -327,14 +327,14 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { Q_UNREACHABLE(); // clang-format on } - StageInfo stageInfo(unsigned int stage) const override { + StageInfo stageInfo(StageIndex stage) const override { bool stageValid = true; // Has the pipeline stage been filled? - stageValid &= stage <= m_cycleCount; + stageValid &= stage.index() <= m_cycleCount; // clang-format off // Has the stage been cleared? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= ifid_reg->valid_out.uValue(); break; case EX: stageValid &= idex_reg->valid_out.uValue(); break; case MEM: stageValid &= exmem_reg->valid_out.uValue(); break; @@ -343,7 +343,7 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { } // Is the stage carrying a valid (executable) PC? - switch(stage){ + switch(stage.index()){ case ID: stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); break; case EX: stageValid &= isExecutableAddress(idex_reg->pc_out.uValue()); break; case MEM: stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()); break; @@ -352,14 +352,14 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { } // Are we currently clearing the pipeline due to a syscall exit? if such, all stages before the EX stage are invalid - if(stage < EX){ + if(stage.index() < EX){ stageValid &= !ecallChecker->isSysCallExiting(); } // clang-format on // Gather stage state info StageInfo::State state = StageInfo ::State::None; - switch (stage) { + switch (stage.index()) { case IF: break; case ID: @@ -411,8 +411,8 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { ecallChecker->setSysCallExiting(ecallChecker->isSysCallExiting() || (fr & FinalizeReason::exitSyscall)); } - const std::vector breakpointTriggeringStages() const override { - return {IF}; + const std::vector breakpointTriggeringStages() const override { + return {{0, IF}}; }; MemoryAccess dataMemAccess() const override { @@ -429,7 +429,7 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { // the pipeline bool allStagesInvalid = true; for (int stage = IF; stage < STAGECOUNT; stage++) { - allStagesInvalid &= !stageInfo(stage).stage_valid; + allStagesInvalid &= !stageInfo({0, stage}).stage_valid; if (!allStagesInvalid) break; } @@ -499,6 +499,7 @@ class RV5S_NO_HZ : public RipesVSRTLProcessor { */ long long m_syscallExitCycle = -1; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 5}}; }; } // namespace core diff --git a/src/processors/RISC-V/rv6s_dual/rv6s_dual.h b/src/processors/RISC-V/rv6s_dual/rv6s_dual.h index 22e150b7b..e59cec8f3 100644 --- a/src/processors/RISC-V/rv6s_dual/rv6s_dual.h +++ b/src/processors/RISC-V/rv6s_dual/rv6s_dual.h @@ -47,21 +47,8 @@ class RV6S_DUAL : public RipesVSRTLProcessor { static constexpr unsigned XLEN = sizeof(XLEN_T) * CHAR_BIT; public: - enum Stage { - IF_1 = 0, - IF_2 = 1, - ID_1 = 2, - ID_2 = 3, - II_EXEC = 4, - II_DATA = 5, - EX_EXEC = 6, - EX_DATA = 7, - MEM_EXEC = 8, - MEM_DATA = 9, - WB_EXEC = 10, - WB_DATA = 11, - STAGECOUNT - }; + enum Lane { EXEC, DATA, LANECOUNT }; + enum Stage { IF, ID, II, EX, MEM, WB, STAGECOUNT }; RV6S_DUAL(const QStringList &extensions) : RipesVSRTLProcessor("6-Stage dual-issue RISC-V Processor") { m_enabledISA = std::make_shared()>>(extensions); @@ -637,151 +624,161 @@ class RV6S_DUAL : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return STAGECOUNT; } - unsigned int getPcForStage(unsigned int idx) const override { - // clang-format off - switch (idx) { - case IF_1: return pc_reg->out.uValue(); - case IF_2: return pc_reg->out.uValue() + pc_inc1->out.uValue(); - case ID_1: return ifid_reg->pc_out.uValue(); - case ID_2: return ifid_reg->pc4_out.uValue(); - case II_EXEC: return idii_reg->pc_exec_out.uValue(); - case II_DATA: return idii_reg->pc_data_out.uValue(); - case EX_EXEC: return iiex_reg->pc_out.uValue(); - case EX_DATA: return iiex_reg->pc_data_out.uValue(); - case MEM_EXEC: return exmem_reg->pc_out.uValue(); - case MEM_DATA: return exmem_reg->pc_data_out.uValue(); - case WB_EXEC: return memwb_reg->pc_out.uValue(); - case WB_DATA: return memwb_reg->pc_data_out.uValue(); - default: assert(false && "Processor does not contain stage"); - } - Q_UNREACHABLE(); - // clang-format on + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex idx) const override { + if (idx == StageIndex{EXEC, IF}) + return pc_reg->out.uValue(); + if (idx == StageIndex{DATA, IF}) + return pc_reg->out.uValue() + pc_inc1->out.uValue(); + if (idx == StageIndex{EXEC, ID}) + return ifid_reg->pc_out.uValue(); + if (idx == StageIndex{DATA, ID}) + return ifid_reg->pc4_out.uValue(); + if (idx == StageIndex{EXEC, II}) + return idii_reg->pc_exec_out.uValue(); + if (idx == StageIndex{DATA, II}) + return idii_reg->pc_data_out.uValue(); + if (idx == StageIndex{EXEC, EX}) + return iiex_reg->pc_out.uValue(); + if (idx == StageIndex{DATA, EX}) + return iiex_reg->pc_data_out.uValue(); + if (idx == StageIndex{EXEC, MEM}) + return exmem_reg->pc_out.uValue(); + if (idx == StageIndex{DATA, MEM}) + return exmem_reg->pc_data_out.uValue(); + if (idx == StageIndex{EXEC, WB}) + return memwb_reg->pc_out.uValue(); + if (idx == StageIndex{DATA, WB}) + return memwb_reg->pc_data_out.uValue(); + Q_UNREACHABLE(); } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int idx) const override { - // clang-format off - switch (idx) { - case IF_1: case IF_2: return "IF"; - case ID_1: case ID_2: return "ID"; - case II_EXEC: case II_DATA: return "II"; - case EX_EXEC: case EX_DATA: return "EX"; - case MEM_EXEC: case MEM_DATA: return "MEM"; - case WB_EXEC: case WB_DATA: return "WB"; - default: assert(false && "Processor does not contain stage"); - } - Q_UNREACHABLE(); - // clang-format on + QString stageName(StageIndex idx) const override { + if (idx == StageIndex{EXEC, IF} || idx == StageIndex{DATA, IF}) + return "IF"; + if (idx == StageIndex{EXEC, ID} || idx == StageIndex{DATA, ID}) + return "ID"; + if (idx == StageIndex{EXEC, II} || idx == StageIndex{DATA, II}) + return "II"; + if (idx == StageIndex{EXEC, EX} || idx == StageIndex{DATA, EX}) + return "EX"; + if (idx == StageIndex{EXEC, MEM} || idx == StageIndex{DATA, MEM}) + return "MEM"; + if (idx == StageIndex{EXEC, WB} || idx == StageIndex{DATA, WB}) + return "WB"; + Q_UNREACHABLE(); } - StageInfo stageInfo(unsigned int stage) const override { + StageInfo stageInfo(StageIndex stage) const override { bool stageValid = true; // Has the pipeline stage been filled? - stageValid &= (stage / 2) <= m_cycleCount; - - if (idii_reg->way_stall_out.uValue()) { + stageValid &= (stage.index() / 2) <= m_cycleCount; + + // Has the stage been cleared? + if (stage == StageIndex{EXEC, ID} || stage == StageIndex{DATA, ID}) + stageValid &= ifid_reg->valid_out.uValue(); + else if (stage == StageIndex{EXEC, II}) + stageValid &= + idii_reg->valid_out.uValue() && idii_reg->exec_valid_out.uValue(); + else if (stage == StageIndex{DATA, II}) + stageValid &= + idii_reg->valid_out.uValue() && idii_reg->data_valid_out.uValue(); + else if (stage == StageIndex{EXEC, EX}) + stageValid &= + iiex_reg->valid_out.uValue() && iiex_reg->exec_valid_out.uValue(); + else if (stage == StageIndex{DATA, EX}) + stageValid &= + iiex_reg->valid_out.uValue() && iiex_reg->data_valid_out.uValue(); + else if (stage == StageIndex{EXEC, MEM}) + stageValid &= + exmem_reg->valid_out.uValue() && exmem_reg->exec_valid_out.uValue(); + else if (stage == StageIndex{DATA, MEM}) + stageValid &= + exmem_reg->valid_out.uValue() && exmem_reg->data_valid_out.uValue(); + else if (stage == StageIndex{EXEC, WB}) + stageValid &= + memwb_reg->valid_out.uValue() && memwb_reg->exec_valid_out.uValue(); + else if (stage == StageIndex{DATA, WB}) + stageValid &= + memwb_reg->valid_out.uValue() && memwb_reg->data_valid_out.uValue(); + + // Is the stage carrying a valid (executable) PC? + /// @todo: also check for valid way (not cleared) + if (stage == StageIndex{EXEC, ID}) + stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); + else if (stage == StageIndex{DATA, ID}) + stageValid &= isExecutableAddress(ifid_reg->pc4_out.uValue()); + else if (stage == StageIndex{EXEC, II}) + stageValid &= isExecutableAddress(idii_reg->pc_out.uValue()); + else if (stage == StageIndex{DATA, II}) + stageValid &= isExecutableAddress(idii_reg->pc_data_out.uValue()); + else if (stage == StageIndex{EXEC, EX}) + stageValid &= isExecutableAddress(iiex_reg->pc_out.uValue()); + else if (stage == StageIndex{DATA, EX}) + stageValid &= isExecutableAddress(iiex_reg->pc_data_out.uValue()); + else if (stage == StageIndex{EXEC, MEM}) + stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()); + else if (stage == StageIndex{DATA, MEM}) + stageValid &= isExecutableAddress(exmem_reg->pc_data_out.uValue()); + else if (stage == StageIndex{EXEC, WB}) + stageValid &= isExecutableAddress(memwb_reg->pc_out.uValue()); + else if (stage == StageIndex{DATA, WB}) + stageValid &= isExecutableAddress(memwb_reg->pc_data_out.uValue()); + else if (stage == StageIndex{EXEC, IF} || stage == StageIndex{DATA, IF}) + stageValid &= isExecutableAddress(pc_reg->out.uValue()); + + // Are we currently clearing the pipeline due to a syscall exit? if such, + // all stages before the EX stage are invalid + if (stage.index() < EX) { + stageValid &= !ecallChecker->isSysCallExiting(); } - // clang-format off - // Has the stage been cleared? - switch(stage){ - case ID_1: case ID_2: stageValid &= ifid_reg->valid_out.uValue(); break; - case II_EXEC: stageValid &= idii_reg->valid_out.uValue() && idii_reg->exec_valid_out.uValue(); break; - case II_DATA: stageValid &= idii_reg->valid_out.uValue() && idii_reg->data_valid_out.uValue(); break; - case EX_EXEC: stageValid &= iiex_reg->valid_out.uValue() && iiex_reg->exec_valid_out.uValue(); break; - case EX_DATA: stageValid &= iiex_reg->valid_out.uValue() && iiex_reg->data_valid_out.uValue(); break; - case MEM_EXEC: stageValid &= exmem_reg->valid_out.uValue() && exmem_reg->exec_valid_out.uValue(); break; - case MEM_DATA: stageValid &= exmem_reg->valid_out.uValue() && exmem_reg->data_valid_out.uValue(); break; - case WB_EXEC: stageValid &= memwb_reg->valid_out.uValue() && memwb_reg->exec_valid_out.uValue(); break; - case WB_DATA: stageValid &= memwb_reg->valid_out.uValue() && memwb_reg->data_valid_out.uValue(); break; - default: case IF_1: case IF_2: break; - } - - - // Is the stage carrying a valid (executable) PC? - /// @todo: also check for valid way (not cleared) - switch(stage){ - case ID_1: stageValid &= isExecutableAddress(ifid_reg->pc_out.uValue()); break; - case ID_2: stageValid &= isExecutableAddress(ifid_reg->pc4_out.uValue()); break; - case II_EXEC: stageValid &= isExecutableAddress(idii_reg->pc_out.uValue()); break; - case II_DATA: stageValid &= isExecutableAddress(idii_reg->pc_data_out.uValue()); break; - case EX_EXEC: stageValid &= isExecutableAddress(iiex_reg->pc_out.uValue()) ; break; - case EX_DATA: stageValid &= isExecutableAddress(iiex_reg->pc_data_out.uValue()) ; break; - case MEM_EXEC: stageValid &= isExecutableAddress(exmem_reg->pc_out.uValue()) ; break; - case MEM_DATA: stageValid &= isExecutableAddress(exmem_reg->pc_data_out.uValue()) ; break; - case WB_EXEC: stageValid &= isExecutableAddress(memwb_reg->pc_out.uValue()) ; break; - case WB_DATA: stageValid &= isExecutableAddress(memwb_reg->pc_data_out.uValue()) ; break; - default: case IF_1: case IF_2: stageValid &= isExecutableAddress(pc_reg->out.uValue()); break; - } - - // Are we currently clearing the pipeline due to a syscall exit? if such, all stages before the EX stage are invalid - if(stage < EX_EXEC){ - stageValid &= !ecallChecker->isSysCallExiting(); - } - // clang-format on - // Gather stage state info StageInfo::State state = StageInfo ::State::None; - switch (stage) { - case IF_1: - case IF_2: - break; - case ID_1: - case ID_2: - if (m_cycleCount >= (ID_1 / 2)) { - if (idii_reg->way_stall_out.uValue() && stage == ID_1) { + if (stage == StageIndex(EXEC, ID) || stage == StageIndex(DATA, ID)) { + if (m_cycleCount >= ID) { + if (idii_reg->way_stall_out.uValue() && stage.lane() == EXEC) { state = StageInfo::State::WayHazard; } else if (ifid_reg->valid_out.uValue() == 0) { state = StageInfo::State::Flushed; } } - break; - case II_EXEC: - case II_DATA: - if (m_cycleCount >= (II_EXEC / 2)) { + } else if (stage == StageIndex(EXEC, II) || stage == StageIndex(DATA, II)) { + if (m_cycleCount >= II) { if (idii_reg->valid_out.uValue() == 0) { state = StageInfo::State::Flushed; - } else if (stage == II_EXEC ? !idii_reg->exec_valid_out.uValue() - : !idii_reg->data_valid_out.uValue()) { + } else if (stage.lane() == EXEC ? !idii_reg->exec_valid_out.uValue() + : !idii_reg->data_valid_out.uValue()) { state = StageInfo::State::Unused; } } - break; - case EX_EXEC: - case EX_DATA: { - if (m_cycleCount >= (EX_EXEC / 2)) { + } else if (stage == StageIndex(EXEC, EX) || stage == StageIndex(DATA, EX)) { + if (m_cycleCount >= EX) { if (iiex_reg->valid_out.uValue() == 0) { state = StageInfo::State::Flushed; - } else if (stage == EX_EXEC ? !iiex_reg->exec_valid_out.uValue() - : !iiex_reg->data_valid_out.uValue()) { + } else if (stage.lane() == EXEC ? !iiex_reg->exec_valid_out.uValue() + : !iiex_reg->data_valid_out.uValue()) { state = StageInfo::State::Unused; } } - break; - } - case MEM_DATA: - case MEM_EXEC: { - if (m_cycleCount >= (MEM_EXEC / 2)) { + } else if (stage == StageIndex(EXEC, MEM) || + stage == StageIndex(DATA, MEM)) { + if (m_cycleCount >= MEM) { if (exmem_reg->valid_out.uValue() == 0) { state = StageInfo::State::Flushed; - } else if (stage == MEM_EXEC ? !exmem_reg->exec_valid_out.uValue() - : !exmem_reg->data_valid_out.uValue()) { + } else if (stage.lane() == EXEC ? !exmem_reg->exec_valid_out.uValue() + : !exmem_reg->data_valid_out.uValue()) { state = StageInfo::State::Unused; } } - break; - } - case WB_DATA: - case WB_EXEC: { - if (m_cycleCount >= (WB_EXEC / 2)) { + } else if (stage == StageIndex(EXEC, WB) || stage == StageIndex(DATA, WB)) { + if (m_cycleCount >= WB) { if (memwb_reg->valid_out.uValue() == 0) { state = StageInfo::State::Flushed; - } else if (stage == WB_EXEC ? !memwb_reg->exec_valid_out.uValue() - : !memwb_reg->data_valid_out.uValue()) { + } else if (stage.lane() == EXEC ? !memwb_reg->exec_valid_out.uValue() + : !memwb_reg->data_valid_out.uValue()) { state = StageInfo::State::Unused; } } - break; - } } return StageInfo({getPcForStage(stage), stageValid, state}); @@ -808,9 +805,9 @@ class RV6S_DUAL : public RipesVSRTLProcessor { ecallChecker->setSysCallExiting(ecallChecker->isSysCallExiting() || (fr & FinalizeReason::exitSyscall)); } - const std::vector breakpointTriggeringStages() const override { - return {IF_1, IF_2}; - }; + const std::vector breakpointTriggeringStages() const override { + return {{DATA, IF}, {EXEC, IF}}; + } MemoryAccess dataMemAccess() const override { return memToAccessInfo(data_mem); @@ -825,10 +822,13 @@ class RV6S_DUAL : public RipesVSRTLProcessor { // The processor is finished when there are no more valid instructions in // the pipeline bool allStagesInvalid = true; - for (int stage = IF_1; stage < STAGECOUNT; stage++) { - allStagesInvalid &= !stageInfo(stage).stage_valid; - if (!allStagesInvalid) - break; + + for (int lane = 0; lane < LANECOUNT; lane++) { + for (int stage = IF; stage < STAGECOUNT; stage++) { + allStagesInvalid &= !stageInfo({lane, stage}).stage_valid; + if (!allStagesInvalid) + break; + } } return allStagesInvalid; } @@ -903,11 +903,12 @@ class RV6S_DUAL : public RipesVSRTLProcessor { /** * @brief m_syscallExitCycle * The variable will contain the cycle of which an exit system call was - * executed. From this, we may determine when we roll back an exit system call - * during rewinding. + * executed. From this, we may determine when we roll back an exit system + * call during rewinding. */ long long m_syscallExitCycle = -1; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 6}, {1, 6}}; }; } // namespace core diff --git a/src/processors/RISC-V/rvss/rvss.h b/src/processors/RISC-V/rvss/rvss.h index 36fad817d..39de30df6 100644 --- a/src/processors/RISC-V/rvss/rvss.h +++ b/src/processors/RISC-V/rvss/rvss.h @@ -160,13 +160,13 @@ class RVSS : public RipesVSRTLProcessor { SUBCOMPONENT(ecallChecker, EcallChecker); // Ripes interface compliance - unsigned int stageCount() const override { return 1; } - unsigned int getPcForStage(unsigned int) const override { + const ProcessorStructure &structure() const override { return m_structure; } + unsigned int getPcForStage(StageIndex) const override { return pc_reg->out.uValue(); } AInt nextFetchedAddress() const override { return pc_src->out.uValue(); } - QString stageName(unsigned int) const override { return "•"; } - StageInfo stageInfo(unsigned int) const override { + QString stageName(StageIndex) const override { return "•"; } + StageInfo stageInfo(StageIndex) const override { return StageInfo({pc_reg->out.uValue(), isExecutableAddress(pc_reg->out.uValue()), StageInfo::State::None}); @@ -189,10 +189,10 @@ class RVSS : public RipesVSRTLProcessor { } } bool finished() const override { - return m_finished || !stageInfo(0).stage_valid; + return m_finished || !stageInfo({0, 0}).stage_valid; } - const std::vector breakpointTriggeringStages() const override { - return {0}; + const std::vector breakpointTriggeringStages() const override { + return {{0, 0}}; } MemoryAccess dataMemAccess() const override { @@ -261,6 +261,7 @@ class RVSS : public RipesVSRTLProcessor { bool m_finishInNextCycle = false; bool m_finished = false; std::shared_ptr m_enabledISA; + ProcessorStructure m_structure = {{0, 1}}; }; } // namespace core diff --git a/src/processors/interface/ripesprocessor.h b/src/processors/interface/ripesprocessor.h index 7f1796bf8..d3466f453 100644 --- a/src/processors/interface/ripesprocessor.h +++ b/src/processors/interface/ripesprocessor.h @@ -42,6 +42,27 @@ struct MemoryAccess { unsigned bytes; }; +struct StageIndex : public std::pair { + using std::pair::pair; + unsigned lane() const {return this->first;} + unsigned index() const {return this->second;} +}; + + +/// Structural description of the processor model. Currently this is a map of {# of lanes, # of stages}. +/// @todo: add an iterator over all stages. +struct ProcessorStructure : public std::map { + using std::map::map; + + // Returns the total number of stages of this processor. + unsigned numStages() const { + unsigned count = 0; + for(auto it : *this) + count += it.second; + return count; + } +}; + /** * @brief The RipesProcessor class * Interface for all Ripes processors. This interface is intended to be @@ -108,20 +129,22 @@ class RipesProcessor { * @brief stageCount * @return number of stages for the processor */ - virtual unsigned int stageCount() const = 0; + virtual const ProcessorStructure& structure() const = 0; + + /** * @brief getPcForStage * @param stageIndex * @return Program counter currently present in stage @param stageIndex */ - virtual unsigned int getPcForStage(unsigned stageIndex) const = 0; + virtual unsigned int getPcForStage(StageIndex stageIndex) const = 0; /** * @brief stageName * @return name of stage identified by @param stageIndex */ - virtual QString stageName(unsigned stageIndex) const = 0; + virtual QString stageName(StageIndex stageIndex) const = 0; /** * @brief nextFetchedAddress @@ -136,14 +159,14 @@ class RipesProcessor { * @return Additional info related to the state of stage @param stageIndex in * the current cycle */ - virtual StageInfo stageInfo(unsigned stageIndex) const = 0; + virtual StageInfo stageInfo(StageIndex stageIndex) const = 0; /** * @brief breakpointTriggeringStages * @returns the stage indices for which a breakpoint is triggered when the * breakpoint PC address enters the stage. */ - virtual const std::vector breakpointTriggeringStages() const = 0; + virtual const std::vector breakpointTriggeringStages() const = 0; /** * @brief getMemory diff --git a/src/processortab.cpp b/src/processortab.cpp index 277239e26..5453d47ad 100644 --- a/src/processortab.cpp +++ b/src/processortab.cpp @@ -161,7 +161,7 @@ void ProcessorTab::loadLayout(const Layout &layout) { return; // Not a valid layout if (layout.stageLabelPositions.size() != - ProcessorHandler::getProcessor()->stageCount()) { + ProcessorHandler::getProcessor()->structure().numStages()) { Q_ASSERT(false && "A stage label position must be specified for each stage"); } @@ -185,13 +185,16 @@ void ProcessorTab::loadLayout(const Layout &layout) { tmpLayoutFile->remove(); // Adjust stage label positions - const auto &parent = m_stageInstructionLabels.at(0)->parentItem(); - for (unsigned i = 0; i < m_stageInstructionLabels.size(); ++i) { - auto &label = m_stageInstructionLabels.at(i); - QFontMetrics metrics(label->font()); - label->setPos(parent->boundingRect().width() * - layout.stageLabelPositions.at(i).x(), - metrics.height() * layout.stageLabelPositions.at(i).y()); + const auto &parent = m_stageInstructionLabels.at({0, 0})->parentItem(); + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex sid = {laneIt.first, stageIdx}; + auto &label = m_stageInstructionLabels.at(sid); + QFontMetrics metrics(label->font()); + label->setPos(parent->boundingRect().width() * + layout.stageLabelPositions.at(sid).x(), + metrics.height() * layout.stageLabelPositions.at(sid).y()); + } } } @@ -355,11 +358,13 @@ void ProcessorTab::loadProcessorToWidget(const Layout *layout) { auto *topLevelComponent = m_vsrtlWidget->getTopLevelComponent(); m_stageInstructionLabels.clear(); - const auto &proc = ProcessorHandler::getProcessor(); - for (unsigned i = 0; i < proc->stageCount(); ++i) { - auto *stagelabel = new vsrtl::Label(topLevelComponent, "-"); - stagelabel->setPointSize(14); - m_stageInstructionLabels[i] = stagelabel; + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex sid = {laneIt.first, stageIdx}; + auto *stagelabel = new vsrtl::Label(topLevelComponent, "-"); + stagelabel->setPointSize(14); + m_stageInstructionLabels[sid] = stagelabel; + } } if (layout != nullptr) { loadLayout(*layout); @@ -468,14 +473,16 @@ void ProcessorTab::enableSimulatorControls() { void ProcessorTab::updateInstructionLabels() { const auto &proc = ProcessorHandler::getProcessor(); - for (unsigned i = 0; i < proc->stageCount(); ++i) { - if (!m_stageInstructionLabels.count(i)) - continue; - const auto stageInfo = proc->stageInfo(i); - auto &instrLabel = m_stageInstructionLabels.at(i); - QString instrString; - if (stageInfo.state != StageInfo::State::None) { - /* clang-format off */ + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex sid = {laneIt.first, stageIdx}; + if (!m_stageInstructionLabels.count(sid)) + continue; + const auto stageInfo = proc->stageInfo(sid); + auto &instrLabel = m_stageInstructionLabels.at(sid); + QString instrString; + if (stageInfo.state != StageInfo::State::None) { + /* clang-format off */ switch (stageInfo.state) { case StageInfo::State::Flushed: instrString = "nop (flush)"; break; case StageInfo::State::Stalled: instrString = "nop (stall)"; break; @@ -483,13 +490,14 @@ void ProcessorTab::updateInstructionLabels() { case StageInfo::State::Unused: instrString = "nop (unused)"; break; case StageInfo::State::None: Q_UNREACHABLE(); } - /* clang-format on */ - instrLabel->forceDefaultTextColor(Qt::red); - } else if (stageInfo.stage_valid) { - instrString = ProcessorHandler::disassembleInstr(stageInfo.pc); - instrLabel->clearForcedDefaultTextColor(); + /* clang-format on */ + instrLabel->forceDefaultTextColor(Qt::red); + } else if (stageInfo.stage_valid) { + instrString = ProcessorHandler::disassembleInstr(stageInfo.pc); + instrLabel->clearForcedDefaultTextColor(); + } + instrLabel->setText(instrString); } - instrLabel->setText(instrString); } } diff --git a/src/processortab.h b/src/processortab.h index c7b59e0ab..e5b525b60 100644 --- a/src/processortab.h +++ b/src/processortab.h @@ -6,6 +6,7 @@ #include #include +#include "processors/interface/ripesprocessor.h" #include "ripes_types.h" #include "ripestab.h" @@ -70,7 +71,7 @@ private slots: vsrtl::VSRTLWidget *m_vsrtlWidget = nullptr; - std::map m_stageInstructionLabels; + std::map m_stageInstructionLabels; QTimer *m_statUpdateTimer; diff --git a/src/programviewer.cpp b/src/programviewer.cpp index 250382fbf..c7b644df2 100644 --- a/src/programviewer.cpp +++ b/src/programviewer.cpp @@ -86,7 +86,7 @@ void ProgramViewer::setCenterAddress(const AInt address) { } void ProgramViewer::updateCenterAddressFromProcessor() { - const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo(0); + const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo({0, 0}); setCenterAddress(stageInfo.pc); } @@ -101,21 +101,25 @@ void ProgramViewer::setFollowEnabled(bool enabled) { void ProgramViewer::updateHighlightedAddresses() { clearBlockHighlights(); - const unsigned stages = ProcessorHandler::getProcessor()->stageCount(); + const unsigned stages = + ProcessorHandler::getProcessor()->structure().numStages(); auto colorGenerator = Colors::incrementalRedGenerator(stages); - for (unsigned sid = 0; sid < stages; sid++) { - const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo(sid); - if (stageInfo.stage_valid) { - auto block = blockForAddress(stageInfo.pc); - if (!block.isValid()) - continue; - - // Record the stage name for the highlighted block for later painting - QString stageString = ProcessorHandler::getProcessor()->stageName(sid); - if (!stageInfo.namedState.isEmpty()) - stageString += " (" + stageInfo.namedState + ")"; - highlightBlock(block, colorGenerator(), stageString); + for (auto laneIt : ProcessorHandler::getProcessor()->structure()) { + for (unsigned stageIdx = 0; stageIdx < laneIt.second; stageIdx++) { + StageIndex sid = {laneIt.first, stageIdx}; + const auto stageInfo = ProcessorHandler::getProcessor()->stageInfo(sid); + if (stageInfo.stage_valid) { + auto block = blockForAddress(stageInfo.pc); + if (!block.isValid()) + continue; + + // Record the stage name for the highlighted block for later painting + QString stageString = ProcessorHandler::getProcessor()->stageName(sid); + if (!stageInfo.namedState.isEmpty()) + stageString += " (" + stageInfo.namedState + ")"; + highlightBlock(block, colorGenerator(), stageString); + } } } diff --git a/test/tst_cosimulate.cpp b/test/tst_cosimulate.cpp index 72389968f..694ea73c2 100644 --- a/test/tst_cosimulate.cpp +++ b/test/tst_cosimulate.cpp @@ -190,9 +190,9 @@ void tst_Cosimulate::executeSimulator(Trace &trace, const Trace *refTrace) { m_err = QString(); bool maxCyclesReached = false; unsigned cycles = 0; - trace.push_back( - TraceEntry{dumpRegs(), cycles, - ProcessorHandler::get()->getProcessor()->getPcForStage(0)}); + trace.push_back(TraceEntry{ + dumpRegs(), cycles, + ProcessorHandler::get()->getProcessor()->getPcForStage({0, 0})}); decltype(refTrace->begin()) cmpRegState; if (refTrace) { @@ -212,7 +212,7 @@ void tst_Cosimulate::executeSimulator(Trace &trace, const Trace *refTrace) { if (regNeq(regs, trace.rbegin()->regs)) { trace.push_back(TraceEntry{ dumpRegs(), cycles, - ProcessorHandler::get()->getProcessor()->getPcForStage(0)}); + ProcessorHandler::get()->getProcessor()->getPcForStage({0, 0})}); // Check whether change corresponds to expected change in comparison // trace. regChange might contain multiple register changes (for diff --git a/test/tst_riscv.cpp b/test/tst_riscv.cpp index dcd1af813..977b7ec07 100644 --- a/test/tst_riscv.cpp +++ b/test/tst_riscv.cpp @@ -110,10 +110,10 @@ bool tst_RISCV::skipTest(const QString &test) { QString tst_RISCV::dumpRegs() { QString str = "\n" + m_currentTest + "\nRegister dump:"; - str += - "\t PC:" + - QString::number(ProcessorHandler::getProcessor()->getPcForStage(0), 16) + - "\n"; + str += "\t PC:" + + QString::number( + ProcessorHandler::getProcessor()->getPcForStage({0, 0}), 16) + + "\n"; for (unsigned i = 0; i < ProcessorHandler::currentISA()->regCnt(); i++) { const auto value = ProcessorHandler::getProcessor()->getRegister(RegisterFileType::GPR, i);