// 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("") .arg(QString::asprintf("%04X", value)) .arg(index); index++; } tableText += ""; tableText += "
" "%1 %2" "
"; 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("") .arg(QString::asprintf("%04X", value)) .arg(index); index++; } tableText += ""; } tableText += "
" "%1 %2" "
"; 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()) + "\""); }