Implement loading and executing simulations through GUI
It was time to dump the old command-line interface for the simulator.
The single cycle simulator can now be initialized and run/reset through the GUI.
MODIFY
src/instructionmodel.cpp
src/instructionmodel.cpp
#include "instructionmodel.h"
#include "parser.h"
+#include "runner.h"
#include <QHeaderView>
@@ -10,18 +11,14 @@ InstructionModel::InstructionModel(const StagePCS& pcsptr, Parser* parser, QObje
setHeaderData(1, Qt::Horizontal, "Stage");
setHeaderData(2, Qt::Horizontal, "Instruction");
*/
+ m_memory = Runner::getRunner()->getMemoryPtr();
}
-void InstructionModel::setMemory(memory* mem, int textSize) {
+void InstructionModel::update() {
+ // Called when changes to the memory has been made
+ // assumes that only instructions are present in the memory when called!
beginResetModel();
- m_memory = mem;
- m_textSize = textSize;
- endResetModel();
-}
-
-void InstructionModel::setTextSize(int textSize) {
- beginResetModel();
- m_textSize = textSize;
+ m_textSize = m_memory->size();
endResetModel();
}

MODIFY
src/instructionmodel.h
src/instructionmodel.h
@@ -17,8 +17,7 @@ public:
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
- void setMemory(memory* mem, int textSize = 0);
- void setTextSize(int textSize);
+ void update();
private:
const StagePCS& m_pcsptr;

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -58,6 +58,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
m_ui->consoles->hide();
};
});
+ connect(this, &MainWindow::update, m_ui->processortab, &ProcessorTab::update);
}
MainWindow::~MainWindow() {
@@ -105,27 +106,12 @@ void MainWindow::on_actionLoadAssemblyFile_triggered() {
auto filename = QFileDialog::getOpenFileName(this, "Open assembly file", "", "Assembly file (*.s *.as *.asm)");
}
-void MainWindow::loadBinaryFile(QString fileName) {
- // ... load file
- QString output = "";
- QFile file(fileName);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QByteArray read = file.readAll();
- auto length = read.length();
- QDataStream in(&read, QIODevice::ReadOnly);
- char buffer[4];
- for (int i = 0; i < length; i += 4) {
- in.readRawData(buffer, 4);
- for (int j = 0; j < 4; j++) {
- output.append(QString().setNum((uint8_t)buffer[j], 2).rightJustified(8, '0'));
- }
- output.append("\n");
- }
-
+void MainWindow::loadBinaryFile(QString filename) {
+ const QString& output = Parser::getParser()->loadBinaryFile(filename);
m_ui->programfiletab->setBinaryText(output);
- file.close();
- }
+ emit update();
}
+
void MainWindow::loadAssemblyFile(QString fileName) {
// ... load file
QFile file(fileName);

MODIFY
src/mainwindow.h
src/mainwindow.h
@@ -29,6 +29,9 @@ private slots:
void on_actionOpen_documentation_triggered();
+signals:
+ void update();
+
private:
Ui::MainWindow* m_ui;
void setupExamples();

MODIFY
src/parser.cpp
src/parser.cpp
#include "parser.h"
+#include "defines.h"
+#include "runner.h"
#include <assert.h>
#include <iostream>
@@ -15,7 +17,7 @@ Parser::Parser() {
m_decodeJInstr = generateWordParser(vector<int>{5, 8, 1, 10, 1});
}
-bool Parser::init(char* filename) {
+bool Parser::initBinaryFile(char* filename) {
// Open binary file
const string fname = string(filename);
m_fileStream = ifstream(fname.c_str(), ios::binary);
@@ -36,16 +38,52 @@ bool Parser::init(char* filename) {
Parser::~Parser() {}
-void Parser::parseFile(memory* memoryPtr) {
+void Parser::parseFile() {
// Parse the file in 8-bit segments and write to memory map
+ memory* memPtr = Runner::getRunner()->getMemoryPtr();
int pc = 0;
while (m_fileIter != istreambuf_iterator<char>()) {
- (*memoryPtr)[pc] = *m_fileIter;
+ (*memPtr)[pc] = *m_fileIter;
pc++;
m_fileIter++;
}
}
+const QString& Parser::loadBinaryFile(QString fileName) {
+ // Loads a binary file and converts it to a text string, as well as puts the binary information into the runner
+ // memorys text segment
+
+ // Reset the runner
+ Runner::getRunner()->reset();
+
+ QString output = "";
+ memory* memPtr = Runner::getRunner()->getMemoryPtr();
+ QFile file(fileName);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QByteArray read = file.readAll();
+ auto length = read.length();
+ QDataStream in(&read, QIODevice::ReadOnly);
+ char buffer[4];
+ int pc = 0;
+ for (int i = 0; i < length; i += 4) {
+ in.readRawData(buffer, 4);
+ for (int j = 0; j < 4; j++) {
+ output.append(QString().setNum((uint8_t)buffer[j], 2).rightJustified(8, '0'));
+ (*memPtr)[pc] = buffer[j];
+ pc++;
+ }
+ output.append("\n");
+ }
+ m_binaryRepr = output;
+ file.close();
+ }
+
+ // Update the runner
+ Runner::getRunner()->update();
+
+ return m_binaryRepr;
+}
+
decode_functor Parser::generateWordParser(std::vector<int> bitFields) {
// Generates functors that can decode a binary number based on the input
// vector which is supplied upon generation

MODIFY
src/parser.h
src/parser.h
@@ -28,8 +28,9 @@ public:
std::vector<uint32_t> decodeRInstr(uint32_t instr) const { return m_decodeRInstr(instr); }
std::vector<uint32_t> decodeBInstr(uint32_t instr) const { return m_decodeBInstr(instr); }
- bool init(char* filename);
- void parseFile(memory* memoryPtr);
+ const QString& loadBinaryFile(QString fileName);
+ bool initBinaryFile(char* filename);
+ void parseFile();
private:
Parser();
@@ -37,6 +38,8 @@ private:
ifstream m_fileStream;
istreambuf_iterator<char> m_fileIter;
int m_fileSize;
+ QString m_binaryRepr; // binary representation of the currently loaded binary file in the runner,, for displaying
+ // the binary values in the binary view
// Instruction decode lambda functions; runtime generated
decode_functor generateWordParser(std::vector<int> bitFields);

MODIFY
src/processortab.cpp
src/processortab.cpp
@@ -20,6 +20,12 @@ ProcessorTab::ProcessorTab(QWidget* parent) : QWidget(parent), m_ui(new Ui::Proc
connect(m_ui->reset, &QPushButton::clicked, [=] { m_ui->start->setChecked(false); });
}
+void ProcessorTab::update() {
+ // Invoked when changes to binary simulation file has been made
+ m_instrModel->update();
+ m_ui->registerContainer->update();
+}
+
void ProcessorTab::initRegWidget() {
// Setup register widget
m_ui->registerContainer->setRegPtr(Runner::getRunner()->getRegPtr());
@@ -30,11 +36,11 @@ void ProcessorTab::initInstructionView() {
// Setup instruction view
m_instrModel = new InstructionModel(Runner::getRunner()->getStagePCS(), Parser::getParser());
m_ui->instructionView->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_instrModel->setMemory(Runner::getRunner()->getMemoryPtr(), Runner::getRunner()->getTextSize());
m_ui->instructionView->setModel(m_instrModel);
m_ui->instructionView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
m_ui->instructionView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
m_ui->instructionView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+ m_instrModel->update();
}
ProcessorTab::~ProcessorTab() {
@@ -48,3 +54,15 @@ void ProcessorTab::on_expandView_clicked() {
void ProcessorTab::on_displayValues_toggled(bool checked) {
m_ui->pipelineWidget->displayAllValues(checked);
}
+
+void ProcessorTab::on_run_clicked() {
+ Runner::getRunner()->exec();
+ m_ui->instructionView->update();
+ m_ui->registerContainer->update();
+}
+
+void ProcessorTab::on_reset_clicked() {
+ Runner::getRunner()->restart();
+ m_ui->instructionView->update();
+ m_ui->registerContainer->update();
+}

MODIFY
src/processortab.h
src/processortab.h
@@ -21,11 +21,19 @@ public:
void initRegWidget();
void initInstructionView();
+public slots:
+ void update();
+
private slots:
+
void on_expandView_clicked();
void on_displayValues_toggled(bool checked);
+ void on_run_clicked();
+
+ void on_reset_clicked();
+
private:
Ui::ProcessorTab* m_ui;
InstructionModel* m_instrModel;

MODIFY
src/registercontainerwidget.cpp
src/registercontainerwidget.cpp
@@ -17,6 +17,12 @@ RegisterContainerWidget::RegisterContainerWidget(QWidget* parent)
m_ui->displayType->setCurrentIndex(displayTypeN::Hex);
}
+void RegisterContainerWidget::update() {
+ for (auto& reg : m_registers) {
+ reg->setText();
+ }
+}
+
void RegisterContainerWidget::init() {
// Initialize register descriptions
QStringList descriptions = QStringList() << "Hard-Wired zero"
@@ -67,5 +73,6 @@ void RegisterContainerWidget::init() {
connect(m_ui->displayType, QOverload<const QString&>::of(&QComboBox::currentTextChanged),
[=] { reg->setDisplayType(qvariant_cast<displayTypeN>(m_ui->displayType->currentData())); });
m_ui->registerLayout->addWidget(reg);
+ m_registers.append(reg);
}
}

MODIFY
src/registercontainerwidget.h
src/registercontainerwidget.h
@@ -7,6 +7,7 @@ namespace Ui {
class RegisterContainerWidget;
}
+class RegisterWidget;
class RegisterContainerWidget : public QWidget {
Q_OBJECT
public:
@@ -15,10 +16,14 @@ public:
void setRegPtr(std::vector<uint32_t>* regPtr) { m_regPtr = regPtr; }
void init();
+public slots:
+ void update();
+
private:
Ui::RegisterContainerWidget* m_ui;
std::vector<uint32_t>* m_regPtr;
+ QList<RegisterWidget*> m_registers;
};
#endif // REGISTERCONTAINERWIDGET_H

MODIFY
src/runner.cpp
src/runner.cpp
@@ -11,17 +11,7 @@ Runner::Runner(Parser* parser) {
// temporary allocation method - get filesize from parser, and allocate same
// amount of memory
m_reg = std::vector<uint32_t>(32, 0);
-
- // Set default register values
- m_reg[0] = 0;
- m_reg[2] = m_stackStart;
- m_reg[3] = m_dataStart;
-
- // file parsing
- m_parser->parseFile(&m_memory);
- // Assuming the map was empty when parsing the program into memory, the size of the memory will now equate to the
- // number of instructions in the text segment
- m_textSize = m_memory.size();
+ reset();
}
Runner::~Runner() {}
@@ -433,3 +423,37 @@ void Runner::handleError(instrState /*err*/) const {
// handle error and print program counter + current instruction
throw "Error!";
}
+
+void Runner::reset() {
+ // Restarts and clears memory of the simulator
+ restart();
+ m_memory.clear();
+ m_textSize = 0;
+}
+
+void Runner::restart() {
+ // Resets the runner to its initial state, but keeps the text segment of the memory
+ // Set default register values
+ for (auto& reg : m_reg) {
+ reg = 0;
+ }
+ for (auto it = m_memory.begin(); it != m_memory.end();) {
+ // Use standard associative-container erasing. If erased, set iterator to iterator after the erased value
+ // (default return value from .erase), else, increment iterator;
+ if (it->first > m_textSize) {
+ it = m_memory.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ m_reg[0] = 0;
+ m_reg[2] = m_stackStart;
+ m_reg[3] = m_dataStart;
+ m_pc = 0;
+}
+
+void Runner::update() {
+ // Must be called whenever external changes to the memory has been envoked
+ m_textSize = m_memory.size();
+ restart();
+}

MODIFY
src/runner.h
src/runner.h
@@ -26,6 +26,10 @@ public:
int getTextSize() const { return m_textSize; }
const StagePCS& getStagePCS() const { return m_stagePCS; }
+ void reset();
+ void restart();
+ void update();
+
private:
Runner(Parser* parser);
~Runner();
@@ -51,7 +55,7 @@ private:
void memWrite(uint32_t address, uint32_t value, int size);
uint32_t memRead(uint32_t address);
- int m_textSize = 0;
+ uint32_t m_textSize = 0;
Parser* m_parser;
bool m_running = false; // flag for disabling UI update signals when running simulator

Implement loading and executing simulations through GUI

It was time to dump the old command-line interface for the simulator.
The single cycle simulator can now be initialized and run/reset through the GUI.