Reimplement InstructionModel to interface with the VSRTL processor
MODIFY
src/instructionmodel.cpp
src/instructionmodel.cpp
#include <QHeaderView>
-InstructionModel::InstructionModel(const StagePCS& pcsptr, const StagePCS& pcsptrPre, Parser* parser, QObject* parent)
- : m_pcsptr(pcsptr), m_pcsptrPre(pcsptrPre), m_parserPtr(parser), QAbstractTableModel(parent) {
- m_pipelinePtr = Pipeline::getPipeline();
- m_memory = m_pipelinePtr->getRuntimeMemoryPtr();
+namespace {
+uint32_t indexToAddress(const QModelIndex& index) {
+ return index.row() * 4;
+}
+} // namespace
+
+InstructionModel::InstructionModel(ProcessorHandler& handler, QObject* parent)
+ : QAbstractTableModel(parent), m_handler(handler) {
+ for (int i = 0; i < m_handler.getProcessor()->stageCount(); i++) {
+ m_stageNames << m_handler.getProcessor()->stageName(i);
+ m_stageInfos[m_stageNames.last()];
+ }
+}
+
+int InstructionModel::columnCount(const QModelIndex&) const {
+ return NColumns;
+}
+
+int InstructionModel::rowCount(const QModelIndex&) const {
+ // Each instruction is 4 bytes and each row represents an instruction within the .text segment
+ return Pipeline::getPipeline()->getTextSize() / 4;
}
-void InstructionModel::update() {
- // Called when changes to the memory has been made
- // assumes that only instructions are present in the memory when called!
+void InstructionModel::processorWasClocked() {
+ // Reload model
beginResetModel();
- m_textSize = Pipeline::getPipeline()->getTextSize();
+ gatherStageInfo();
endResetModel();
}
-int InstructionModel::rowCount(const QModelIndex&) const {
- return m_textSize / 4;
+void InstructionModel::gatherStageInfo() {
+ bool firstStageChanged = false;
+ for (int i = 0; i < m_stageNames.length(); i++) {
+ if (i == 0) {
+ if (m_stageInfos[m_stageNames[i]].pc != m_handler.getProcessor()->stageInfo(i).pc) {
+ firstStageChanged = true;
+ }
+ }
+ m_stageInfos[m_stageNames[i]] = m_handler.getProcessor()->stageInfo(i);
+ if (firstStageChanged) {
+ emit firstStageInstrChanged(m_stageInfos[m_stageNames[0]].pc);
+ firstStageChanged = false;
+ }
+ }
}
-int InstructionModel::columnCount(const QModelIndex&) const {
- return 4;
+bool InstructionModel::setData(const QModelIndex& index, const QVariant& value, int role) {
+ const uint32_t addr = indexToAddress(index);
+ if ((index.column() == Column::Breakpoint) && role == Qt::CheckStateRole) {
+ if (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked) {
+ m_handler.setBreakpoint(addr, !m_handler.hasBreakpoint(addr));
+ return true;
+ }
+ }
+ return false;
}
-namespace {
-#define VALIDATE(stage) \
- if (!m_pcsptr.stage.initialized) { \
- emit textChanged(Stage::stage, ""); \
- } else if (m_pcsptr.stage.invalidReason == 1) { \
- emit textChanged(Stage::stage, "nop (flush)", QColor(Qt::red)); \
- } else if (m_pcsptr.stage.invalidReason == 2) { \
- emit textChanged(Stage::stage, "nop (stall)", QColor(Qt::red)); \
- } else if (m_pcsptr.stage.invalidReason == 3) { \
- emit textChanged(Stage::stage, ""); /*EOF*/ \
+QVariant InstructionModel::headerData(int section, Qt::Orientation orientation, int role) const {
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch (section) {
+ case Column::Breakpoint:
+ return "BP";
+ case Column::PC:
+ return "PC";
+ case Column::Stage:
+ return "Stage";
+ case Column::Instruction:
+ return "Instruction";
+ default:
+ return QVariant();
+ }
+ }
+ return QVariant();
+}
+
+QVariant InstructionModel::BPData(uint32_t addr) const {
+ return m_handler.hasBreakpoint(addr);
+}
+QVariant InstructionModel::PCData(uint32_t addr) const {
+ return addr;
+}
+QVariant InstructionModel::stageData(uint32_t addr) const {
+ for (const auto& si : m_stageInfos) {
+ if ((si.second.pc == addr) && si.second.pc_valid) {
+ return si.first;
}
}
+ return QVariant();
+}
+
+QVariant InstructionModel::instructionData(uint32_t addr) const {
+ return m_handler.parseInstrAt(addr);
+}
QVariant InstructionModel::data(const QModelIndex& index, int role) const {
if (!index.isValid()) {
return QVariant();
}
- int row = index.row();
+ const uint32_t addr = indexToAddress(index);
switch (index.column()) {
- case 0: {
- // Breakpoints. Return true if breakpoint is found
+ case Column::Breakpoint: {
if (role == Qt::CheckStateRole) {
- if (m_pipelinePtr->m_breakpoints.find(row * 4) != m_pipelinePtr->m_breakpoints.end()) {
- return true;
- } else {
- return false;
- }
+ return BPData(addr);
}
break;
}
- case 1: {
- if (role == Qt::DisplayRole)
- return row * 4;
- break;
- }
- case 2: {
+ case Column::PC: {
if (role == Qt::DisplayRole) {
- // check if instruction is in any pipeline stage, and whether the given PC for the pipeline
- // stage is valid. Furthermore, because of branching, an instruction can be in multiple stages
- // at once - therefore, we build up a return string by checking all stage program counters
- QStringList retStrings;
- uint32_t byteIndex = row * 4;
- uint32_t maxInstr = m_textSize - 4;
- if (byteIndex == m_pcsptr.EX.pc && m_pcsptr.EX.initialized && m_pcsptr.EX.isValid()) {
- emit textChanged(Stage::EX, m_parserPtr->getInstructionString(row * 4));
- if (byteIndex == maxInstr) {
- emit textChanged(Stage::ID, "");
- }
- retStrings << "EX";
- }
- if (byteIndex == m_pcsptr.ID.pc && m_pcsptr.ID.initialized && m_pcsptr.ID.isValid()) {
- emit textChanged(Stage::ID, m_parserPtr->getInstructionString(row * 4));
- if (byteIndex == maxInstr) {
- emit textChanged(Stage::IF, "");
- }
- retStrings << "ID";
- }
- if (byteIndex == m_pcsptr.IF.pc && m_pcsptr.IF.initialized && m_pcsptr.IF.isValid()) {
- emit textChanged(Stage::IF, m_parserPtr->getInstructionString(row * 4));
- emit currentIFRow(row); // for moving view to IF position
- retStrings << "IF";
- }
- if (byteIndex == m_pcsptr.MEM.pc && m_pcsptr.MEM.initialized && m_pcsptr.MEM.isValid()) {
- emit textChanged(Stage::MEM, m_parserPtr->getInstructionString(row * 4));
- if (byteIndex == maxInstr) {
- emit textChanged(Stage::EX, "");
- }
- retStrings << "MEM";
- }
- if (byteIndex == m_pcsptr.WB.pc && m_pcsptr.WB.initialized && m_pcsptr.WB.isValid()) {
- emit textChanged(Stage::WB, m_parserPtr->getInstructionString(row * 4));
- if (byteIndex == maxInstr) {
- emit textChanged(Stage::MEM, "");
- }
- retStrings << "WB";
- }
- if (retStrings.isEmpty()) {
- // Clear invalid PC values for each stage (used when resetting the program)
- VALIDATE(IF);
- VALIDATE(ID);
- VALIDATE(EX);
- VALIDATE(MEM);
- VALIDATE(WB);
- return QVariant();
- } else {
- // Join strings by "/"
- return retStrings.join("/");
- }
+ return PCData(addr);
}
break;
}
- case 3:
+ case Column::Stage: {
if (role == Qt::DisplayRole) {
- return m_parserPtr->getInstructionString(row * 4);
+ return stageData(addr);
}
- default:
- return QVariant();
- }
- return QVariant();
+ break;
}
-
-bool InstructionModel::setData(const QModelIndex& index, const QVariant& value, int role) {
- if (index.column() == 0 && role == Qt::CheckStateRole) {
- if ((Qt::CheckState)value.toInt() == Qt::Checked) {
- if (m_pipelinePtr->m_breakpoints.find(index.row() * 4) != m_pipelinePtr->m_breakpoints.end()) {
- // Breakpoint is already set - remove breakpoint
- m_pipelinePtr->m_breakpoints.erase(index.row() * 4);
- } else {
- // Set breakpoint
- m_pipelinePtr->m_breakpoints.insert(index.row() * 4);
+ case Column::Instruction: {
+ if (role == Qt::DisplayRole) {
+ return instructionData(addr);
}
+ break;
}
- return true;
}
- return false;
+ return QVariant();
}
Qt::ItemFlags InstructionModel::flags(const QModelIndex& index) const {
- auto def = Qt::ItemIsEnabled;
- if (index.column() == 0)
+ const auto def = Qt::ItemIsEnabled;
+ if (index.column() == Column::Breakpoint)
return Qt::ItemIsUserCheckable | def;
return def;
}
-
-QVariant InstructionModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
- switch (section) {
- case 0:
- return "BP";
- case 1:
- return "PC";
- case 2:
- return "Stage";
- case 3:
- return "Instruction";
- default:
- return QVariant();
- }
- }
-
- return QVariant();
-}

MODIFY
src/instructionmodel.h
src/instructionmodel.h
#include <set>
#include "defines.h"
#include "mainmemory.h"
+#include "processorhandler.h"
#include <QAbstractTableModel>
class Parser;
class Pipeline;
+
class InstructionModel : public QAbstractTableModel {
Q_OBJECT
public:
- InstructionModel(const StagePCS& pcsptr, const StagePCS& pcsptrPre, Parser* parser = nullptr,
- QObject* parent = nullptr);
+ enum Column { Breakpoint = 0, PC = 1, Stage = 2, Instruction = 3, NColumns };
+ InstructionModel(ProcessorHandler& handler, QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -25,23 +27,29 @@ public:
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
- void update();
+public slots:
+ void processorWasClocked();
- int m_currentIFrow = 0;
+signals:
+ /**
+ * @brief firstStageInstrChanged
+ * Emitted whenever the PC of the first stage, changed. Argument is the address of the instruction now present in
+ * the first stage.
+ */
+ void firstStageInstrChanged(uint32_t) const;
private:
- const StagePCS& m_pcsptr;
- const StagePCS& m_pcsptrPre;
- MainMemory* m_memory;
- Pipeline* m_pipelinePtr;
- const Parser* m_parserPtr;
- int m_textSize = 0; // text segment, in bytes
+ void gatherStageInfo();
-signals:
- void textChanged(Stage stage, QString text, QColor col = QColor()) const;
- void currentIFRow(int) const;
+ QVariant BPData(uint32_t addr) const;
+ QVariant PCData(uint32_t addr) const;
+ QVariant stageData(uint32_t addr) const;
+ QVariant instructionData(uint32_t addr) const;
-public slots:
+ QStringList m_stageNames;
+ std::map<QString, Ripes::StageInfo> m_stageInfos;
+
+ ProcessorHandler& m_handler;
};
#endif // INSTRUCTIONMODEL_H

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -60,7 +60,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
// Setup tab pointers
m_processorTab->initRegWidget();
- m_processorTab->initInstructionView();
m_memoryTab->initMemoryTab();
setupMenus();

MODIFY
src/processorhandler.cpp
src/processorhandler.cpp
#include "processorhandler.h"
+#include "parser.h"
#include "processorregistry.h"
#include "program.h"
@@ -13,6 +14,7 @@ ProcessorHandler::ProcessorHandler() {
void ProcessorHandler::loadProgram(const Program& p) {
auto& mem = m_currentProcessor->getMemory();
+ m_program = p;
mem.clearInitializationMemories();
mem.addInitializationMemory(p.text.first, p.text.second->data(), p.text.second->length());
for (const auto& seg : p.others) {
@@ -25,7 +27,19 @@ void ProcessorHandler::loadProgram(const Program& p) {
emit reqProcessorReset();
}
-void ProcessorHandler::selectProcessor(ProcessorID id) {
+void ProcessorHandler::setBreakpoint(const uint32_t address, bool enabled) {
+ if (enabled) {
+ m_breakpoints.insert(address);
+ } else if (m_breakpoints.count(address)) {
+ m_breakpoints.erase(address);
+ }
+}
+
+bool ProcessorHandler::hasBreakpoint(const uint32_t address) const {
+ return m_breakpoints.count(address);
+}
+
+void ProcessorHandler::selectProcessor(const ProcessorID id) {
m_currentProcessorID = id;
m_currentProcessor = ProcessorRegistry::constructProcessor(m_currentProcessorID);
m_currentProcessor->handleSysCall.Connect(this, &ProcessorHandler::handleSysCall);
@@ -33,6 +47,14 @@ void ProcessorHandler::selectProcessor(ProcessorID id) {
emit reqReloadProgram();
}
+int ProcessorHandler::getCurrentProgramSize() const {
+ return m_program.text.second->size();
+}
+
+QString ProcessorHandler::parseInstrAt(const uint32_t addr) const {
+ return Parser::getParser()->genStringRepr(m_currentProcessor->getMemory().readMem(addr), addr);
+}
+
void ProcessorHandler::handleSysCall() {
const unsigned int arg = m_currentProcessor->getRegister(10);
switch (arg) {

MODIFY
src/processorhandler.h
src/processorhandler.h
@@ -15,6 +15,8 @@ class ProcessorHandler : public QObject {
public:
ProcessorHandler();
+ /// @todo: this should return a const ptr - ONLY the processorhandler is allowed to modify the state of the
+ /// processor
Ripes::RipesProcessor* getProcessor() { return m_currentProcessor.get(); }
ProcessorID currentID() const { return m_currentProcessorID; }
@@ -23,7 +25,7 @@ public:
* Constructs the processor identified by @param id, and performs all necessary initialization through the
* RipesProcessor interface.
*/
- void selectProcessor(ProcessorID id);
+ void selectProcessor(const ProcessorID id);
/**
* @brief checkValidExecutionRange
@@ -33,6 +35,21 @@ public:
*/
void checkValidExecutionRange() const;
+ /**
+ * @brief getCurrentProgramSize
+ * @return size (in bytes) of the currently loaded .text segment
+ */
+ int getCurrentProgramSize() const;
+
+ /**
+ * @brief parseInstrAt
+ * @return string representation of the instruction at @param addr
+ */
+ QString parseInstrAt(const uint32_t address) const;
+
+ void setBreakpoint(const uint32_t address, bool enabled);
+ bool hasBreakpoint(const uint32_t address) const;
+
signals:
/**
* @brief reqProcessorReset
@@ -79,4 +96,6 @@ private:
* in the future, this range should be expanded to also include the .data or other relevant program sections.
*/
std::pair<uint32_t, uint32_t> m_validExecutionRange;
+ std::set<uint32_t> m_breakpoints;
+ Program m_program;
};

MODIFY
src/processors/ripesprocessor.h
src/processors/ripesprocessor.h
@@ -13,9 +13,10 @@ enum SysCall { None = 0, PrintInt = 1, PrintChar = 2, PrintStr = 4, Exit = 10 };
enum class SupportedISA { RISCV };
struct StageInfo {
- QString stageName;
- unsigned int pc;
- bool pc_valid;
+ StageInfo() {}
+ StageInfo(uint32_t _pc, bool _valid) : pc(_pc), pc_valid(_valid) {}
+ unsigned int pc = 0;
+ bool pc_valid = false;
};
class RipesProcessor : public vsrtl::core::Design {
@@ -34,13 +35,6 @@ public:
*/
virtual unsigned int stageCount() const = 0;
- /**
- * @brief breakpointBreaksStage
- * If a breakpoint has been set for address A, the processor will be stopped when A has reached the specified stage
- * @return stage to break in
- */
- virtual unsigned int breakpointBreaksStage() const = 0;
-
/**
* @brief pcForStage
* @param stageIndex
@@ -48,6 +42,12 @@ public:
*/
virtual unsigned int pcForStage(unsigned int stageIndex) const = 0;
+ /**
+ * @brief stageName
+ * @return name of stage identified by @param stageIndex
+ */
+ virtual QString stageName(unsigned int stageIndex) const = 0;
+
/**
* @brief nextPcForStage
* @param stageIndex

MODIFY
src/processortab.cpp
src/processortab.cpp
@@ -23,6 +23,7 @@ ProcessorTab::ProcessorTab(ProcessorHandler& handler, QToolBar* toolbar, QWidget
// Load the default processor
m_vsrtlWidget->setDesign(m_handler.getProcessor());
+ updateInstructionModel();
setupSimulatorActions();
@@ -146,6 +147,38 @@ void ProcessorTab::processorSelection() {
m_vsrtlWidget->clearDesign();
m_handler.selectProcessor(diag.selectedID);
m_vsrtlWidget->setDesign(m_handler.getProcessor());
+ updateInstructionModel();
+ update();
+ }
+}
+
+void ProcessorTab::updateInstructionModel() {
+ auto* oldModel = m_instrModel;
+ m_instrModel = new InstructionModel(m_handler, this);
+
+ // Update the instruction view according to the newly created model
+ m_ui->instructionView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_ui->instructionView->setModel(m_instrModel);
+
+ // Only the instruction column should stretch
+ m_ui->instructionView->horizontalHeader()->setMinimumSectionSize(1);
+ m_ui->instructionView->horizontalHeader()->setSectionResizeMode(InstructionModel::Breakpoint,
+ QHeaderView::ResizeToContents);
+ m_ui->instructionView->horizontalHeader()->setSectionResizeMode(InstructionModel::PC,
+ QHeaderView::ResizeToContents);
+ m_ui->instructionView->horizontalHeader()->setSectionResizeMode(InstructionModel::Stage,
+ QHeaderView::ResizeToContents);
+ m_ui->instructionView->horizontalHeader()->setSectionResizeMode(InstructionModel::Instruction,
+ QHeaderView::Stretch);
+
+ connect(this, &ProcessorTab::update, m_instrModel, &InstructionModel::processorWasClocked);
+
+ // Make the instruction view follow the instruction which is currently present in the first stage of the processor
+ connect(m_instrModel, &InstructionModel::firstStageInstrChanged, this,
+ &ProcessorTab::setInstructionViewCenterAddr);
+
+ if (oldModel) {
+ delete oldModel;
}
}
@@ -166,25 +199,6 @@ void ProcessorTab::updateMetrics() {
m_ui->nInstrExecuted->setText(QString::number(Pipeline::getPipeline()->getInstructionsExecuted()));
}
-void ProcessorTab::initInstructionView() {
- // Setup instruction view
- m_instrModel = new InstructionModel(Pipeline::getPipeline()->getStagePCS(),
- Pipeline::getPipeline()->getStagePCSPre(), Parser::getParser());
- m_ui->instructionView->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_ui->instructionView->setModel(m_instrModel);
- m_ui->instructionView->horizontalHeader()->setMinimumSectionSize(1);
- m_ui->instructionView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
- m_ui->instructionView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
- m_ui->instructionView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
- m_ui->instructionView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
- connect(this, &ProcessorTab::update, m_ui->instructionView, QOverload<>::of(&QWidget::update));
- connect(this, &ProcessorTab::update, m_instrModel, &InstructionModel::update);
- connect(m_instrModel, &InstructionModel::currentIFRow, this, &ProcessorTab::setCurrentInstruction);
- // Connect instruction model text changes to the pipeline widget (changing instruction names displayed above each
- // stage)
- connect(m_instrModel, &InstructionModel::textChanged, tmp_pipelineWidget, &PipelineWidget::stageTextChanged);
-}
-
ProcessorTab::~ProcessorTab() {
delete m_ui;
}
@@ -231,14 +245,13 @@ void ProcessorTab::reset() {
emit appendToLog("\n");
}
-void ProcessorTab::setCurrentInstruction(int row) {
- // model emits signal with current IF instruction row
- auto instructionView = m_ui->instructionView;
- auto rect = instructionView->rect();
- int indexTop = instructionView->indexAt(rect.topLeft()).row();
- int indexBot = instructionView->indexAt(rect.bottomLeft()).row();
-
- int nItems = indexBot - indexTop;
+void ProcessorTab::setInstructionViewCenterAddr(uint32_t address) {
+ const auto row = address / 4;
+ const auto instructionView = m_ui->instructionView;
+ const auto rect = instructionView->rect();
+ const int indexTop = instructionView->indexAt(rect.topLeft()).row();
+ const int indexBot = instructionView->indexAt(rect.bottomLeft()).row();
+ const int nItems = indexBot - indexTop;
// move scrollbar if if is not visible
if (row <= indexTop || row >= indexBot) {
@@ -250,6 +263,7 @@ void ProcessorTab::setCurrentInstruction(int row) {
void ProcessorTab::rewind() {
m_vsrtlWidget->rewind();
enableSimulatorControls();
+ emit update();
}
void ProcessorTab::clock() {

MODIFY
src/processortab.h
src/processortab.h
@@ -36,7 +36,6 @@ public:
~ProcessorTab() override;
void initRegWidget();
- void initInstructionView();
signals:
void update();
@@ -55,18 +54,19 @@ public slots:
private slots:
void expandView();
void clock();
- void setCurrentInstruction(int row);
+ void setInstructionViewCenterAddr(uint32_t address);
void showPipeliningTable();
void updateMetrics();
private:
void setupSimulatorActions();
void enableSimulatorControls();
+ void updateInstructionModel();
bool handleEcall(const std::pair<Pipeline::ECALL, int32_t>& ecallValue);
Ui::ProcessorTab* m_ui;
- InstructionModel* m_instrModel;
+ InstructionModel* m_instrModel = nullptr;
vsrtl::VSRTLWidget* m_vsrtlWidget = nullptr;
ProcessorHandler& m_handler;

Reimplement InstructionModel to interface with the VSRTL processor