Execute Pipeline::run() in separate thread
With this, programs with long execution times no longer block the GUI. Furthermore this fixes the issue of being able to abort infinite loops.
MODIFY
Ripes.pro
Ripes.pro
@@ -43,7 +43,9 @@ SOURCES += src/main.cpp \
src/pipelinetable.cpp \
src/pipelinetablemodel.cpp \
src/rwjumpwidget.cpp \
- src/rwjumpmodel.cpp
+ src/rwjumpmodel.cpp \
+ src/rundialog.cpp \
+ src/binutils.cpp
HEADERS += \
src/parser.h \
@@ -87,7 +89,8 @@ HEADERS += \
src/rwjumpwidget.h \
src/rwjumpmodel.h \
src/lexerutilities.h \
- src/graphics/descriptions.h
+ src/graphics/descriptions.h \
+ src/rundialog.h
FORMS += \
src/mainwindow.ui \
@@ -102,7 +105,8 @@ FORMS += \
src/registercontainerwidget.ui \
src/aboutwidget.ui \
src/pipelinetable.ui \
- src/rwjumpwidget.ui
+ src/rwjumpwidget.ui \
+ src/rundialog.ui
# External

ADD
src/binutils.cpp
+#include "binutils.h"
+
+uint32_t accBVec(const std::vector<bool>& v) {
+ static long here;
+ here++;
+ uint32_t r = 0;
+ for (uint32_t i = 0; i < v.size(); i++) {
+ r |= v.at(i) << i;
+ }
+ return r;
+}
+
+void buildVec(std::vector<bool>& v, uint32_t n) {
+ for (uint32_t i = 0; i < v.size(); i++) {
+ v[i] = n & 0b1;
+ n >>= 1;
+ }
+}

MODIFY
src/binutils.h
src/binutils.h
@@ -33,17 +33,5 @@ constexpr uint32_t bitcount(int n) {
return count;
}
-inline uint32_t accBVec(std::vector<bool> v) {
- uint32_t r = 0;
- for (uint32_t i = 0; i < v.size(); i++) {
- r |= v[i] << i;
- }
- return r;
-}
-
-inline void buildVec(std::vector<bool>& v, uint32_t n) {
- for (uint32_t i = 0; i < v.size(); i++) {
- v[i] = n & 0b1;
- n >>= 1;
- }
-}
+uint32_t accBVec(const std::vector<bool>& v);
+void buildVec(std::vector<bool>& v, uint32_t n);

MODIFY
src/graphics/connection.cpp
src/graphics/connection.cpp
@@ -257,7 +257,7 @@ void Label::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWi
}
QRectF Label::boundingRect() const {
- if (m_signal != nullptr) {
+ if (m_signal != nullptr && !Pipeline::getPipeline()->isRunning()) {
auto text = QString("0x%1").arg(QString().setNum(m_signal->getValue(), 16));
QPointF pos = mapFromItem(m_source, *m_drawPos);
QFontMetrics metric = QFontMetrics(QFont());

MODIFY
src/graphics/pipelinewidget.cpp
src/graphics/pipelinewidget.cpp
@@ -479,11 +479,13 @@ PipelineWidget::PipelineWidget(QWidget* parent) : QGraphicsView(parent) {
}
void PipelineWidget::update() {
+ if(!m_pipelinePtr->isRunning()){
QWidget::update();
for (const auto& item : m_animatedItems) {
item->update();
}
}
+}
void PipelineWidget::setSignal(Graphics::Connection* conn, SignalBase* sig) {
conn->setSignal(sig);

MODIFY
src/graphics/shape.cpp
src/graphics/shape.cpp
#include "shape.h"
+#include "pipeline.h"
#include <QPainter>
@@ -171,6 +172,7 @@ QPainterPath Shape::drawALUPath(QRectF rect) const {
}
void Shape::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget*) {
+ if(!Pipeline::getPipeline()->isRunning()){
auto rect = boundingRect();
painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
@@ -287,6 +289,7 @@ void Shape::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/,
painter->drawEllipse(m_outputPoints[i], 5, 5);
}
}
+}
void Shape::setSignal(SignalPos pos, SignalBase* sig) {
switch (pos) {

MODIFY
src/pipeline.cpp
src/pipeline.cpp
@@ -772,9 +772,9 @@ void Pipeline::handleEcall() {
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;
+ return {m_ecallval, m_reg.a1()};
}
int Pipeline::step() {
@@ -846,10 +846,21 @@ void Pipeline::setStagePCS() {
m_pcs.IF = PCVAL(r_PC_IF, m_finishing | m_finished);
}
+void Pipeline::abort(){
+ m_abort = true; // async abort of run()
+}
+
int Pipeline::run() {
- while (!step())
+ m_running = true;
+ while (!step() && !m_abort)
;
+ m_running = false;
+ if(m_abort){
+ restart();
return 1;
+ } else {
+ return 0;
+ }
}
void Pipeline::reset() {
@@ -859,6 +870,7 @@ void Pipeline::reset() {
m_dataMemory.clear();
m_textSize = 0;
m_ready = false;
+ m_abort = false;
}
void Pipeline::update() {
@@ -879,6 +891,7 @@ void Pipeline::restart() {
m_pcsPre.reset();
m_finished = false;
m_finishing = false;
+ m_abort = false;
m_finishingCnt = 0;
m_pcsCycles.clear();
m_RVAccesses.clear();

MODIFY
src/pipeline.h
src/pipeline.h
@@ -19,6 +19,8 @@ public:
void update();
int step();
int run();
+ void abort();
+ bool isRunning() const {return m_running;}
// Pointers for GUI
std::set<uint32_t>* getBreakpoints() { return &m_breakpoints; }
@@ -51,6 +53,7 @@ private:
void aluCtrl();
void clock();
bool m_ready = false;
+ bool m_running;
ECALL m_ecallval;
// Program counters for each stage
@@ -60,6 +63,7 @@ private:
bool m_finishing =
false; // Set when ecall 10 is detected, and the pipeline is running its remaining instructions through
bool m_finished = false;
+ bool m_abort = false;
int m_finishingCnt = 0;
std::vector<StagePCS> m_pcsCycles; // list of PCs for each cycle
std::vector<RVAccess> m_RVAccesses; // List of all read/write accesses to memory

MODIFY
src/processortab.cpp
src/processortab.cpp
#include <QScrollBar>
#include "instructionmodel.h"
#include "pipelinewidget.h"
+#include "rundialog.h"
#include "ui_processortab.h"
#include <QFileDialog>
@@ -106,13 +107,14 @@ void ProcessorTab::on_displayValues_toggled(bool checked) {
void ProcessorTab::on_run_clicked() {
auto pipeline = Pipeline::getPipeline();
+ RunDialog dialog(this);
if (pipeline->isReady()) {
- if (pipeline->run() && pipeline->isFinished()) {
+ if (dialog.exec() && pipeline->isFinished()) {
emit update();
m_ui->step->setEnabled(false);
m_ui->start->setEnabled(false);
m_ui->run->setEnabled(false);
- } else if (pipeline->checkEcall(false).first != Pipeline::ECALL::none) {
+ } else if (pipeline->checkEcall(true).first != Pipeline::ECALL::none) {
// An ECALL has been invoked during continuous running. Handle ecall and continue to run
handleEcall();
on_run_clicked();
@@ -153,6 +155,7 @@ void ProcessorTab::on_step_clicked() {
auto pipeline = Pipeline::getPipeline();
auto state = pipeline->step();
+
handleEcall();
emit update();

ADD
src/rundialog.cpp
+#include "rundialog.h"
+#include "ui_rundialog.h"
+
+#include "pipeline.h"
+
+#include <QPushButton>
+#include <QtConcurrent/QtConcurrent>
+
+RunDialog::RunDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::RunDialog)
+{
+ ui->setupUi(this);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setVisible(false);
+ ui->buttonBox->button(QDialogButtonBox::Cancel)->setText("Abort");
+
+ setWindowTitle("Running...");
+
+ ui->label->setText(QString("Running for 0 seconds"));
+ m_timer.setInterval(1000);
+ connect(&m_timer, &QTimer::timeout, this, &RunDialog::updateText);
+ m_timer.start();
+}
+
+void RunDialog::updateText(){
+ runtime++;
+ ui->label->setText(QString("Running for %1 seconds").arg(runtime));
+ if(runtime > 15){
+ ui->extra->setText(QString("Your program may contain an infinite loop"));
+ }
+}
+QFuture<int> RunDialog::startSimulation(){
+ QFuture<int> future = QtConcurrent::run(Pipeline::getPipeline(), &Pipeline::run);
+ return future;
+}
+
+int RunDialog::exec(){
+ connect(&m_runWatcher, &QFutureWatcher<int>::finished,
+ this, &RunDialog::finished);
+
+ m_runWatcher.setFuture(startSimulation());
+ return QDialog::exec();
+}
+
+RunDialog::~RunDialog()
+{
+ delete ui;
+}
+
+void RunDialog::finished(){
+ m_timer.stop();
+ if(m_runWatcher.future().result() == 0){
+ accept();
+ } else {
+ reject();
+ }
+}
+
+void RunDialog::on_buttonBox_clicked(QAbstractButton* button) {
+ if (button == ui->buttonBox->button(QDialogButtonBox::Cancel)) {
+ // Disable the futureWatcher as to not call stateChanged twice
+ Pipeline::getPipeline()->abort();
+ }
+}

ADD
src/rundialog.h
+#ifndef RUNDIALOG_H
+#define RUNDIALOG_H
+
+#include <QAbstractButton>
+#include <QDialog>
+#include <QTimer>
+
+#include <QFutureWatcher>
+
+namespace Ui {
+class RunDialog;
+}
+
+class RunDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RunDialog(QWidget *parent = 0);
+ ~RunDialog();
+
+public slots:
+ int exec() override;
+
+private slots:
+ void on_buttonBox_clicked(QAbstractButton* button);
+ void finished();
+ void updateText();
+
+private:
+ int runtime = 0;
+ QFuture<int> startSimulation();
+
+ Ui::RunDialog *ui;
+
+ QFutureWatcher<int> m_runWatcher;
+ QTimer m_timer;
+};
+
+#endif // RUNDIALOG_H

ADD
src/rundialog.ui
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RunDialog</class>
+ <widget class="QDialog" name="RunDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>184</width>
+ <height>80</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Running...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="extra">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>RunDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>RunDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>

Execute Pipeline::run() in separate thread #12

With this, programs with long execution times no longer block the GUI. Furthermore this fixes the issue of being able to abort infinite loops.