Implement ecall for printing ints and strings to application output
MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -61,6 +61,10 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
connect(this, &MainWindow::update, m_ui->processortab, &ProcessorTab::restart);
connect(this, &MainWindow::updateMemoryTab, m_ui->memorytab, &MemoryTab::update);
connect(m_ui->stackedWidget, &QStackedWidget::currentChanged, m_ui->memorytab, &MemoryTab::update);
+
+ // Connect ECALL functionality to application output log
+ connect(m_ui->processortab, &ProcessorTab::appendToLog,
+ [this](QString string) { m_ui->applicationConsole->appendPlainText(string + '\n'); });
}
MainWindow::~MainWindow() {

MODIFY
src/parser.cpp
src/parser.cpp
@@ -49,6 +49,20 @@ void Parser::parseFile() {
}
}
+QString Parser::getStringAt(uint32_t address) const {
+ // Returns the null-terminated string starting at @address
+ MainMemory* memPtr = Pipeline::getPipeline()->getMemoryPtr();
+ QString string;
+
+ char read = memPtr->read(address) & 0xff;
+ while (read != 0) {
+ string.append(read);
+ address++;
+ read = memPtr->read(address) & 0xff;
+ }
+ return string;
+}
+
void Parser::clear() {
m_disassembledRepr.clear();
m_binaryRepr.clear();

MODIFY
src/parser.h
src/parser.h
@@ -42,6 +42,7 @@ public:
const QString& loadBinaryFile(QString fileName, bool disassembled = true);
const QString& getBinaryRepr() { return m_binaryRepr; }
const QString& getDisassembledRepr() { return m_disassembledRepr; }
+ QString getStringAt(uint32_t address) const;
QByteArray getFileByteArray() { return m_fileByteArray; }
bool initBinaryFile(char* filename);
void parseFile();

MODIFY
src/pipeline.cpp
src/pipeline.cpp
@@ -698,13 +698,28 @@ void Pipeline::handleEcall() {
if (a0_val == 10) {
// Start finishing sequence
m_finishing = true;
+ } else if (a0_val == 4) {
+ // Print string
+ m_ecallval = ECALL::print_string;
+ } else if (a0_val == 1) {
+ // Print string
+ m_ecallval = ECALL::print_int;
}
}
+
+ // Start finishing counter
if (m_finishing) {
m_finishingCnt++;
}
}
+std::pair<Pipeline::ECALL, uint32_t> Pipeline::checkEcall(bool reset) {
+ // Called from GUI after each clock cycle - resets the internal ECALL request
+ return {m_ecallval, m_reg.a1()};
+ if (reset)
+ m_ecallval = ECALL::none;
+}
+
int Pipeline::step() {
// Main processing function for the pipeline
// propagates the signals throgh the combinational logic and clocks the
@@ -751,6 +766,8 @@ int Pipeline::step() {
} else if (m_breakpoints.find((uint32_t)r_PC_IF) != m_breakpoints.end()) {
// Breakpoint set at current r_PC_IF value
return 1;
+ } else if (m_ecallval != ECALL::none) {
+ return 1; // GUI will automatically resume execution if in "running" mode
}
{ return 0; }
}

MODIFY
src/pipeline.h
src/pipeline.h
@@ -11,6 +11,7 @@ class Pipeline {
friend class RWJumpModel;
public:
+ enum class ECALL { none, exit, print_string, print_int };
Pipeline();
// Utility functions
void restart();
@@ -24,6 +25,7 @@ public:
MainMemory* getMemoryPtr() { return &m_memory; }
std::unordered_map<uint32_t, uint8_t>* getDataMemoryPtr() { return &m_dataMemory; }
std::vector<uint32_t>* getRegPtr() { return m_reg.getRegPtr(); }
+ std::pair<Pipeline::ECALL, uint32_t> checkEcall(bool reset = true);
const StagePCS& getStagePCS() const { return m_pcs; }
const StagePCS& getStagePCSPre() const { return m_pcsPre; }
int getTextSize() const { return m_textSize; }
@@ -49,6 +51,7 @@ private:
void aluCtrl();
void clock();
bool m_ready = false;
+ ECALL m_ecallval;
// Program counters for each stage
void setStagePCS();

MODIFY
src/pipelineobjects.h
src/pipelineobjects.h
@@ -387,7 +387,9 @@ public:
Signal<1>* regWrite);
Signal<32>* getOutput(int n = 1) { return n == 2 ? &m_readData2 : &m_readData1; }
- uint32_t a0() { return m_reg[10]; } // Direct accessor for a0 register (ECALL functionality)
+ // Direct accessors for a0 & a1 registers (ECALL functionality)
+ uint32_t a0() const { return m_reg[10]; }
+ uint32_t a1() const { return m_reg[11]; }
private:
// ReadRegister 1/2 is deduced from the input instruction signal

MODIFY
src/processortab.cpp
src/processortab.cpp
@@ -112,6 +112,10 @@ void ProcessorTab::on_run_clicked() {
m_ui->step->setEnabled(false);
m_ui->start->setEnabled(false);
m_ui->run->setEnabled(false);
+ } else if (pipeline->checkEcall(false).first != Pipeline::ECALL::none) {
+ // An ECALL has been invoked during continuous running. Handle ecall and continue to run
+ handleEcall();
+ on_run_clicked();
} else {
m_ui->table->setEnabled(true);
emit update();
@@ -148,6 +152,9 @@ void ProcessorTab::setCurrentInstruction(int row) {
void ProcessorTab::on_step_clicked() {
auto pipeline = Pipeline::getPipeline();
auto state = pipeline->step();
+
+ handleEcall();
+
emit update();
// Move instruction view
@@ -161,6 +168,25 @@ void ProcessorTab::on_step_clicked() {
}
}
+void ProcessorTab::handleEcall() {
+ // Check if ecall has been invoked
+ auto pipeline = Pipeline::getPipeline();
+ auto ecall_val = pipeline->checkEcall();
+ if (ecall_val.first != Pipeline::ECALL::none) {
+ switch (ecall_val.first) {
+ case Pipeline::ECALL::print_string: {
+ emit appendToLog(Parser::getParser()->getStringAt(ecall_val.second));
+ }
+ case Pipeline::ECALL::print_int: {
+ emit appendToLog(QString::number(ecall_val.second));
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
void ProcessorTab::on_zoomIn_clicked() {
m_ui->pipelineWidget->zoomIn();
}

MODIFY
src/processortab.h
src/processortab.h
#include <QWidget>
#include "defines.h"
+#include "pipeline.h"
#include "pipelinetable.h"
#include "pipelinetablemodel.h"
@@ -43,6 +44,8 @@ private slots:
void on_table_clicked();
private:
+ void handleEcall();
+
Ui::ProcessorTab* m_ui;
InstructionModel* m_instrModel;
@@ -50,6 +53,7 @@ private:
signals:
void update();
+ void appendToLog(QString string);
};
#endif // PROCESSORTAB_H

Implement ecall for printing ints and strings to application output