// Simulator for the RISC-V[ECTOR] mini-ISA
// Copyright (C) 2025 Siddarth Suresh
// Copyright (C) 2025 bdunahu
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#include "gui.h"
#include "./ui_gui.h"
#include "dynamicwaysentry.h"
#include "messages.h"
GUI::GUI(QWidget *parent) : QMainWindow(parent), ui(new Ui::GUI)
{
ui->setupUi(this);
this->status_label = new QLabel("", this);
this->set_status(get_waiting);
QLabel *risc_vector =
new QLabel("RISC V[ECTOR], CS535 UMASS AMHERST", this);
status_label->setMinimumWidth(1200);
ui->statusBar->addWidget(status_label);
ui->statusBar->addPermanentWidget(risc_vector);
worker = new Worker();
worker->moveToThread(&workerThread);
// display clock cycles and PC
connect(worker, &Worker::clock_cycles, this, &GUI::on_worker_refresh_gui);
connect(worker, &Worker::if_info, this, &GUI::onWorkerFetchInfo);
connect(worker, &Worker::id_info, this, &GUI::onWorkerDecodeInfo);
connect(worker, &Worker::ex_info, this, &GUI::onWorkerExecuteInfo);
connect(worker, &Worker::mm_info, this, &GUI::onWorkerMemoryInfo);
connect(worker, &Worker::wb_info, this, &GUI::onWorkerWriteBackInfo);
// Display dram
connect(worker, &Worker::dram_storage, this, &GUI::onWorkerShowDram);
// Display cache
connect(worker, &Worker::cache_storage, this, &GUI::onWorkerShowCache);
// Display registers
connect(
worker, &Worker::register_storage, this, &GUI::onWorkerShowRegisters);
// Configure pipeline
connect(
this, &GUI::sendConfigure, worker, &Worker::configure,
Qt::QueuedConnection);
// Advance controller by some steps
connect(
this, &GUI::sendRunSteps, worker, &Worker::runSteps,
Qt::QueuedConnection);
// Update the step button with step amount
connect(ui->step_slider, &QSlider::valueChanged, this, [=](int index) {
int value = step_values[index];
ui->step_btn->setText(QString("Step %1").arg(value));
});
// Proper cleanup when worker finishes
connect(worker, &Worker::finished, this, &GUI::onWorkerFinished);
connect(worker, &Worker::finished, &workerThread, &QThread::quit);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
workerThread.start(); // Start the worker thread
}
GUI::~GUI()
{
workerThread.quit();
workerThread.wait(); // Ensure proper cleanup
delete ui;
}
void displayArrayHTML(QTextEdit *textEdit, const std::array &data)
{
textEdit->setReadOnly(false);
QString tableText = "";
tableText += "";
int index = 0;
for (int value : data) {
tableText += QString(""
"%1 %2"
" | ")
.arg(QString::asprintf("%04X", value))
.arg(index);
index++;
}
tableText += "
";
tableText += "
";
textEdit->setHtml(tableText);
textEdit->setReadOnly(true);
}
void displayTableHTML(
QTextEdit *textEdit,
const std::vector> &data)
{
textEdit->setReadOnly(false);
QString tableText = "";
int index = 0;
for (const auto &row : data) {
tableText += "";
for (signed int value : row) {
tableText += QString(""
"%1 %2"
" | ")
.arg(QString::asprintf("%04X", value))
.arg(index);
index++;
}
tableText += "
";
}
tableText += "
";
textEdit->setHtml(tableText);
textEdit->setReadOnly(true);
}
void GUI::on_worker_refresh_gui(int cycles, int pc)
{
ui->p_counter->set_value(pc);
ui->cycle_counter->set_value(cycles);
}
void GUI::onWorkerFetchInfo(const std::vector info)
{
if (!info.empty()) {
ui->fetch_squashed->setText(QString::number(info[0]));
ui->fetch_bits->set_value(info[1]);
} else {
ui->fetch_squashed->clear();
ui->fetch_bits->clear();
}
}
void GUI::onWorkerDecodeInfo(const std::vector info)
{
if (!info.empty()) {
ui->decode_squashed->setText(QString::number(info[0]));
ui->decode_bits->set_value(info[1]);
} else {
ui->decode_squashed->clear();
ui->decode_bits->clear();
}
}
void GUI::onWorkerExecuteInfo(const std::vector info)
{
if (!info.empty()) {
ui->execute_mnemonic->setText(mnemonicToString((Mnemonic)info[0]));
ui->execute_squashed->setText(QString::number(info[1]));
ui->execute_s1->set_value(info[2]);
ui->execute_s2->set_value(info[3]);
ui->execute_s3->set_value(info[4]);
} else {
ui->execute_mnemonic->clear();
ui->execute_squashed->clear();
ui->execute_s1->clear();
ui->execute_s2->clear();
ui->execute_s3->clear();
}
}
void GUI::onWorkerMemoryInfo(const std::vector info)
{
if (!info.empty()) {
ui->memory_mnemonic->setText(mnemonicToString((Mnemonic)info[0]));
ui->memory_squashed->setText(QString::number(info[1]));
ui->memory_s1->set_value(info[2]);
ui->memory_s2->set_value(info[3]);
ui->memory_s3->set_value(info[4]);
} else {
ui->memory_mnemonic->clear();
ui->memory_squashed->clear();
ui->memory_s1->clear();
ui->memory_s2->clear();
ui->memory_s3->clear();
}
}
void GUI::onWorkerWriteBackInfo(const std::vector info)
{
if (!info.empty()) {
ui->write_mnemonic->setText(mnemonicToString((Mnemonic)info[0]));
ui->write_s1->set_value(info[2]);
ui->write_s2->set_value(info[3]);
ui->write_s3->set_value(info[4]);
} else {
ui->write_mnemonic->clear();
ui->write_s1->clear();
ui->write_s2->clear();
ui->write_s3->clear();
}
}
void GUI::onWorkerShowDram(
const std::vector> data)
{
displayTableHTML(ui->dram_table, data);
}
void GUI::onWorkerShowCache(
const std::vector> data)
{
displayTableHTML(ui->cache_table, data);
}
void GUI::onWorkerShowRegisters(const std::array &data)
{
displayArrayHTML(ui->register_table, data);
}
void GUI::onWorkerFinished() { qDebug() << "Worker has finished processing."; }
void GUI::on_upload_intructions_btn_clicked()
{
qDebug() << "Upload intructions button clicked.";
// why register_table?
QString filePath = QFileDialog::getOpenFileName(
ui->register_table, "Open Binary File", QDir::homePath(),
"Binary Files (*.bin *.rv);;All Files (*.*)");
QFile file(filePath);
if (filePath.isEmpty() || !file.open(QIODevice::ReadOnly)) {
this->set_status(get_no_instructions);
return;
}
this->p.clear();
while (!file.atEnd()) {
char bytes[4];
if (file.read(bytes, 4) == 4) {
uint32_t word = (static_cast(bytes[0]) << 24) |
(static_cast(bytes[1]) << 16) |
(static_cast(bytes[2]) << 8) |
(static_cast(bytes[3]));
this->p.push_back(static_cast(word));
}
}
if (this->p.empty())
this->set_status(get_no_instructions);
else
this->set_status(get_load_file);
file.close();
}
void GUI::on_upload_program_state_btn_clicked()
{
// TODO:Upload and set program state ( have to decide how to use this)
qDebug() << "upload program state button is clicked.";
}
void GUI::on_enable_pipeline_checkbox_checkStateChanged(
const Qt::CheckState &arg1)
{
if (arg1 == Qt::CheckState::Checked) {
qDebug() << "enable pipeline checkbox checked.";
this->is_pipelined = true;
} else {
qDebug() << "enable pipeline checkbox unchecked.";
this->is_pipelined = false;
}
}
void GUI::on_step_btn_clicked()
{
qDebug() << "Run step button clicked.";
// try to configure first
if (!this->ready)
this->on_config_clicked();
// try again
if (!this->ready)
return;
this->set_status(get_running);
int steps = step_values[ui->step_slider->value()];
emit sendRunSteps(steps);
this->set_status(get_waiting);
}
void GUI::on_save_program_state_btn_clicked()
{
// TODO: save program state
qDebug() << "save program state button is clicked.";
}
void GUI::on_config_clicked()
{
std::vector ways;
QStringList entries;
signed int i;
DynamicWaysEntry *dwe = ui->cache_way_selector;
for (const QString &s : dwe->get_entries()) {
if (s.isEmpty())
continue;
i = dwe->parse_valid_way(s);
if (i != -1) {
ways.push_back((unsigned int)i);
} else {
this->set_status(get_bad_cache);
return;
}
}
if (this->p.empty()) {
this->set_status(get_no_instructions);
return;
}
this->ready = true;
// say something snarky
if (!is_pipelined)
this->set_status(get_no_pipeline);
else if (ways.size() == 0)
this->set_status(get_no_cache);
else
this->set_status(get_initialize);
emit sendConfigure(ways, this->p, is_pipelined);
}
void GUI::set_status(const std::function &func)
{
this->status_label->setText(
"COMPUTER SAYS: \"" + QString::fromStdString(func()) + "\"");
}