diff options
68 files changed, 1869 insertions, 896 deletions
@@ -7,3 +7,5 @@ # Common test directory test_dir +# Build directory +build @@ -4,7 +4,7 @@ MIPS CPU simulator for education purposes. Dependencies ------------ -* Qt 5 (version 4 is not tested but it might work) +* Qt 5 (qtmips\_gui can be compiled with Qt4 but not qtmips\_cli) * elfutils (libelf works too but you might have problems with it) Compilation @@ -43,3 +43,7 @@ being so. * Privileged instructions and all features dependent on it * Coprocessors (so no floating point unit nor any other type) * Peripherals +* Memory access stall (stalling execution because of cache miss would be pretty + annoying for users so difference between cache and memory is just in collected + statistics) +* Interrupts and exceptions (if exception occurs then machine execution is halted) diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..aafe93b --- /dev/null +++ b/build.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +mkdir -p build + +ROOT="$(dirname "$(readlink -f "$0")")" + +cd build +/usr/lib/qt5/bin/qmake "$ROOT" + +make sub-qtmips_cli sub-qtmips_gui diff --git a/qtmips_cli/main.cpp b/qtmips_cli/main.cpp index 780f782..6240414 100644 --- a/qtmips_cli/main.cpp +++ b/qtmips_cli/main.cpp @@ -1,12 +1,50 @@ #include <QCoreApplication> +#include <QCommandLineParser> #include <iostream> -#include "machineapp.h" +#include "tracer.h" -#include "instructions/arithmetic.h" +void create_parser(QCommandLineParser &p) { + p.setApplicationDescription("QtMips CLI machine simulator"); + p.addHelpOption(); + p.addVersionOption(); -int main(int argc, char *argv[]) -{ - MachineApp app(argc, argv); + p.addPositionalArgument("FILE", "Input ELF executable file"); +} + +void configure_machine(QCommandLineParser &p, MachineConfig &cc) { + QStringList pa = p.positionalArguments(); + if (pa.size() != 1) { + std::cerr << "Single ELF file has to be specified" << std::endl; + exit(1); + } + cc.set_elf(pa[0]); + + // TODO +} + +void configure_tracer(QCommandLineParser &p, Tracer &tr) { + // TODO + tr.reg_pc(); +} + +int main(int argc, char *argv[]) { + QCoreApplication app(argc, argv); + app.setApplicationName("qtmips_cli"); + app.setApplicationVersion("0.1"); + + QCommandLineParser p; + create_parser(p); + p.process(app); + + MachineConfig cc; + configure_machine(p, cc); + QtMipsMachine machine(cc); + + app.connect(&machine, SIGNAL(program_exit()), &app, SLOT(quit())); + + Tracer tr(&machine); + configure_tracer(p, tr); + machine.play(); // Run machine return app.exec(); } diff --git a/qtmips_cli/qtmips_cli.pro b/qtmips_cli/qtmips_cli.pro index a541451..088a015 100644 --- a/qtmips_cli/qtmips_cli.pro +++ b/qtmips_cli/qtmips_cli.pro @@ -12,11 +12,13 @@ LIBS += -L$$OUT_PWD/../qtmips_machine/ -lqtmips_machine INCLUDEPATH += $$PWD/../qtmips_machine DEPENDPATH += $$PWD/../qtmips_machine QMAKE_CXXFLAGS += -std=c++0x +QMAKE_CXXFLAGS += -ggdb DEFINES += QT_DEPRECATED_WARNINGS -SOURCES += main.cpp \ - machineapp.cpp +SOURCES += \ + main.cpp \ + tracer.cpp HEADERS += \ - machineapp.h + tracer.h diff --git a/qtmips_cli/tracer.cpp b/qtmips_cli/tracer.cpp index 63d00d1..5cbf339 100644 --- a/qtmips_cli/tracer.cpp +++ b/qtmips_cli/tracer.cpp @@ -1,6 +1,16 @@ #include "tracer.h" +#include <iostream> -Tracer::Tracer() -{ +using namespace std; +Tracer::Tracer(QtMipsMachine *machine) { + this->machine = machine; +} + +void Tracer::reg_pc() { + connect(machine->registers(), SIGNAL(pc_update(std::uint32_t)), this, SLOT(regs_pc_update(std::uint32_t))); +} + +void Tracer::regs_pc_update(std::uint32_t val) { + cout << "PC:" << hex << val << endl; } diff --git a/qtmips_cli/tracer.h b/qtmips_cli/tracer.h index 5c99970..7055cd8 100644 --- a/qtmips_cli/tracer.h +++ b/qtmips_cli/tracer.h @@ -2,11 +2,21 @@ #define TRACER_H #include <QObject> +#include "qtmipsmachine.h" -class Tracer -{ +class Tracer : public QObject { + Q_OBJECT public: - Tracer(); + Tracer(QtMipsMachine *machine); + + // Trace registers + void reg_pc(); + +private slots: + void regs_pc_update(std::uint32_t val); + +private: + QtMipsMachine *machine; }; -#endif // TRACER_H
\ No newline at end of file +#endif // TRACER_H diff --git a/qtmips_gui/CacheContent.ui b/qtmips_gui/CacheContent.ui index ac2f263..372dee1 100644 --- a/qtmips_gui/CacheContent.ui +++ b/qtmips_gui/CacheContent.ui @@ -1,7 +1,7 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>DockWidget</class> - <widget class="QDockWidget" name="DockWidget"> + <class>CacheContent</class> + <widget class="QDockWidget" name="CacheContent"> <property name="geometry"> <rect> <x>0</x> @@ -11,8 +11,20 @@ </rect> </property> <property name="windowTitle"> - <string>DockWidget</string> + <string>Cache content</string> </property> - <widget class="QWidget" name="dockWidgetContents"/> + <widget class="QWidget" name="dockWidgetContents"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>TODO</string> + </property> + </widget> + </item> + </layout> + </widget> </widget> + <resources/> + <connections/> </ui> diff --git a/qtmips_gui/CacheStatistics.ui b/qtmips_gui/CacheStatistics.ui index ac2f263..5bb8b72 100644 --- a/qtmips_gui/CacheStatistics.ui +++ b/qtmips_gui/CacheStatistics.ui @@ -1,7 +1,7 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>DockWidget</class> - <widget class="QDockWidget" name="DockWidget"> + <class>CacheStatistics</class> + <widget class="QDockWidget" name="CacheStatistics"> <property name="geometry"> <rect> <x>0</x> @@ -11,8 +11,10 @@ </rect> </property> <property name="windowTitle"> - <string>DockWidget</string> + <string>Cache statistics</string> </property> <widget class="QWidget" name="dockWidgetContents"/> </widget> + <resources/> + <connections/> </ui> diff --git a/qtmips_gui/MainWindow.ui b/qtmips_gui/MainWindow.ui index 0f69613..56f5319 100644 --- a/qtmips_gui/MainWindow.ui +++ b/qtmips_gui/MainWindow.ui @@ -6,20 +6,47 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>957</width> + <height>651</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> - <widget class="QWidget" name="centralWidget"/> + <property name="dockNestingEnabled"> + <bool>false</bool> + </property> + <property name="unifiedTitleAndToolBarOnMac"> + <bool>true</bool> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="widget" native="true"/> + </item> + </layout> + </widget> <widget class="QMenuBar" name="menuBar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>400</width> + <width>957</width> <height>20</height> </rect> </property> @@ -27,7 +54,8 @@ <property name="title"> <string>File</string> </property> - <addaction name="actionLoad_binary"/> + <addaction name="actionNew"/> + <addaction name="actionReload"/> <addaction name="separator"/> <addaction name="actionExit"/> </widget> @@ -85,16 +113,39 @@ <attribute name="toolBarBreak"> <bool>false</bool> </attribute> + <addaction name="actionNew"/> + <addaction name="actionReload"/> + <addaction name="separator"/> + <addaction name="actionRun"/> + <addaction name="actionPause"/> + <addaction name="actionStep"/> </widget> - <action name="actionLoad_binary"> + <action name="actionNew"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/document-import.png</normaloff>:/icons/document-import.png</iconset> + </property> <property name="text"> - <string>Load binary...</string> + <string>New simulation...</string> + </property> + <property name="shortcut"> + <string>Ctrl+O, Ctrl+N</string> </property> </action> <action name="actionExit"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/application-exit.png</normaloff>:/icons/application-exit.png</iconset> + </property> <property name="text"> <string>Exit</string> </property> + <property name="toolTip"> + <string>Exit program</string> + </property> + <property name="shortcut"> + <string>Ctrl+Q</string> + </property> </action> <action name="actionRestart"> <property name="text"> @@ -102,19 +153,40 @@ </property> </action> <action name="actionRun"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset> + </property> <property name="text"> <string>Run</string> </property> + <property name="shortcut"> + <string>Ctrl+R</string> + </property> </action> <action name="actionStep"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/next.png</normaloff>:/icons/next.png</iconset> + </property> <property name="text"> <string>Step</string> </property> + <property name="shortcut"> + <string>Ctrl+S</string> + </property> </action> <action name="actionPause"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/pause.png</normaloff>:/icons/pause.png</iconset> + </property> <property name="text"> <string>Pause</string> </property> + <property name="shortcut"> + <string>Ctrl+P</string> + </property> </action> <action name="action1_ips"> <property name="text"> @@ -184,8 +256,19 @@ <string>Assembler</string> </property> </action> + <action name="actionReload"> + <property name="icon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/reload.png</normaloff>:/icons/reload.png</iconset> + </property> + <property name="text"> + <string>Reload existing</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> - <resources/> + <resources> + <include location="icons.qrc"/> + </resources> <connections/> </ui> diff --git a/qtmips_gui/NewDialog.ui b/qtmips_gui/NewDialog.ui index 18d31ab..e39edbf 100644 --- a/qtmips_gui/NewDialog.ui +++ b/qtmips_gui/NewDialog.ui @@ -1,71 +1,299 @@ -<ui version="4.0" > - <author></author> - <comment></comment> - <exportmacro></exportmacro> - <class>Dialog</class> - <widget class="QDialog" name="Dialog" > - <property name="geometry" > +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NewDialog</class> + <widget class="QDialog" name="NewDialog"> + <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>558</width> + <height>306</height> </rect> </property> - <property name="windowTitle" > + <property name="windowTitle"> <string>Dialog</string> </property> - <widget class="QDialogButtonBox" name="buttonBox" > - <property name="geometry" > - <rect> - <x>30</x> - <y>240</y> - <width>341</width> - <height>32</height> - </rect> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> </property> - <property name="orientation" > - <enum>Qt::Horizontal</enum> + <property name="topMargin"> + <number>0</number> </property> - <property name="standardButtons" > - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <property name="rightMargin"> + <number>0</number> </property> - </widget> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>2</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Basic</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Preset</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QRadioButton" name="preset_no_pipeline"> + <property name="text"> + <string>No pipeline no cache</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="preset_pipelined"> + <property name="text"> + <string>Pipelined and cache</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="preset_custom"> + <property name="text"> + <string>Custom</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Line" name="line_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Elf executable: </string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="elf_file"/> + </item> + <item> + <widget class="QPushButton" name="pushButton_browse"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_core"> + <attribute name="title"> + <string>Core</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="QCheckBox" name="pipelined"> + <property name="text"> + <string>Pipelined</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="hazard"> + <property name="text"> + <string>Hazard unit</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="flush_jump"> + <property name="text"> + <string>Flush pipeline on jump</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="prediction"> + <property name="title"> + <string>Jump prediction</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QRadioButton" name="prediction_static"> + <property name="text"> + <string>Static</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="prediction_one_dynamic"> + <property name="text"> + <string>One level dynamic</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_memory"> + <attribute name="title"> + <string>Memory</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="mem_protec_write"> + <property name="text"> + <string>Program memory write protection</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="mem_protec_exec"> + <property name="text"> + <string>Data memory executable protection</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="cache"> + <property name="title"> + <string>Cache (for both program and data)</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <item> + <widget class="QRadioButton" name="cache_associative"> + <property name="text"> + <string>Associative</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_load"> + <property name="text"> + <string>Load machine</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButton_cancel"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> </widget> - <pixmapfunction></pixmapfunction> <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>Dialog</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>Dialog</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> + <connections/> </ui> diff --git a/qtmips_gui/cachecontent.cpp b/qtmips_gui/cachecontent.cpp index c651b7b..1dd4693 100644 --- a/qtmips_gui/cachecontent.cpp +++ b/qtmips_gui/cachecontent.cpp @@ -1,6 +1,10 @@ -#include "cache_content.h" +#include "cachecontent.h" -cache_content::cache_content() -{ +CacheContentDock::CacheContentDock(QWidget *parent) : QDockWidget(parent) { + ui = new Ui::CacheContent(); + ui->setupUi(this); +} +CacheContentDock::~CacheContentDock() { + delete ui; } diff --git a/qtmips_gui/cachecontent.h b/qtmips_gui/cachecontent.h index 09b8734..3b028b5 100644 --- a/qtmips_gui/cachecontent.h +++ b/qtmips_gui/cachecontent.h @@ -1,11 +1,16 @@ -#ifndef CACHE_CONTENT_H -#define CACHE_CONTENT_H +#ifndef CACHECONTENT_H +#define CACHECONTENT_H +#include <QDockWidget> +#include "ui_CacheContent.h" -class cache_content -{ +class CacheContentDock : public QDockWidget { public: - cache_content(); + CacheContentDock(QWidget *parent); + ~CacheContentDock(); + +private: + Ui::CacheContent *ui; }; -#endif // CACHE_CONTENT_H
\ No newline at end of file +#endif // CACHECONTENT_H diff --git a/qtmips_gui/cachestatistics.cpp b/qtmips_gui/cachestatistics.cpp index ade92c6..dd78a8c 100644 --- a/qtmips_gui/cachestatistics.cpp +++ b/qtmips_gui/cachestatistics.cpp @@ -1,6 +1,10 @@ #include "cachestatistics.h" -CacheStatistics::CacheStatistics() -{ +CacheStatisticsDock::CacheStatisticsDock(QWidget *parent) : QDockWidget(parent) { + ui = new Ui::CacheStatistics(); + ui->setupUi(this); +} +CacheStatisticsDock::~CacheStatisticsDock() { + delete ui; } diff --git a/qtmips_gui/cachestatistics.h b/qtmips_gui/cachestatistics.h index 4fa77a6..a92d125 100644 --- a/qtmips_gui/cachestatistics.h +++ b/qtmips_gui/cachestatistics.h @@ -1,11 +1,18 @@ #ifndef CACHESTATISTICS_H #define CACHESTATISTICS_H +#include <QDockWidget> +#include "ui_CacheStatistics.h" -class CacheStatistics -{ +class CacheStatisticsDock : public QDockWidget { + Q_OBJECT public: - CacheStatistics(); + CacheStatisticsDock(QWidget *parent); + ~CacheStatisticsDock(); + +private: + Ui::CacheStatistics *ui; + }; -#endif // CACHESTATISTICS_H
\ No newline at end of file +#endif // CACHESTATISTICS_H diff --git a/qtmips_gui/coreview.cpp b/qtmips_gui/coreview.cpp index de8a637..4d23bd8 100644 --- a/qtmips_gui/coreview.cpp +++ b/qtmips_gui/coreview.cpp @@ -1,6 +1,35 @@ #include "coreview.h" -CoreView::CoreView() -{ +CoreView::CoreView(QWidget *parent) : QGraphicsView(parent) { } + +/* +CoreViewBlock::CoreViewBlock() { +} + +CoreViewLine::CoreViewLine() { + +} + +CoreViewLine::CoreViewLine(struct point start, struct point end, QList<struct point> axis) { + +} + +CoreViewLine::~CoreViewLine() { + +} + +void CoreViewLine::set_start(struct point p) { + +} + +void CoreViewLine::set_end(struct point p) { + +} + +void CoreViewLine::set_axis(QList<struct point> axs) { + +} + +*/ diff --git a/qtmips_gui/coreview.h b/qtmips_gui/coreview.h index 6e7407f..a2772fb 100644 --- a/qtmips_gui/coreview.h +++ b/qtmips_gui/coreview.h @@ -1,11 +1,46 @@ #ifndef COREVIEW_H #define COREVIEW_H +#include <QGraphicsView> +#include <QGraphicsItem> +#include <QList> +#include "machineconfig.h" -class CoreView -{ +class CoreView : public QGraphicsView { + Q_OBJECT public: - CoreView(); + CoreView(QWidget *parent); + +private: + +}; + +/* +class CoreViewBlock : public QGraphicsItem { + Q_OBJECT +public: + CoreViewBlock(); +}; + +class CoreViewLine : public QGraphicsItem { + Q_OBJECT +public: + struct point { + int x1, y1, x2, y2; + }; + + CoreViewLine(); + CoreViewLine(struct point start, struct point end, QList<struct point> axis); + ~CoreViewLine(); + + void set_start(struct point); + void set_end(struct point); + void set_axis(QList<struct point>); + +protected: + struct point start, end; + QList<struct point> axis; }; +*/ -#endif // COREVIEW_H
\ No newline at end of file +#endif // COREVIEW_H diff --git a/qtmips_gui/icons.qrc b/qtmips_gui/icons.qrc index 90f4a83..08542ee 100644 --- a/qtmips_gui/icons.qrc +++ b/qtmips_gui/icons.qrc @@ -1,2 +1,14 @@ -<!DOCTYPE RCC> -<RCC version="1.0"/> +<RCC> + <qresource prefix="/"> + <file>icons/application-exit.png</file> + <file>icons/reload.png</file> + <file>icons/document-import.png</file> + <file>icons/finish.png</file> + <file>icons/forward.png</file> + <file>icons/next.png</file> + <file>icons/pause.png</file> + <file>icons/refresh.png</file> + <file>icons/play.png</file> + <file>icons/stop.png</file> + </qresource> +</RCC> diff --git a/qtmips_gui/icons/application-exit.png b/qtmips_gui/icons/application-exit.png Binary files differindex 2d0cd61..79ee79c 100644 --- a/qtmips_gui/icons/application-exit.png +++ b/qtmips_gui/icons/application-exit.png diff --git a/qtmips_gui/main.cpp b/qtmips_gui/main.cpp index b48f94e..1c03f3f 100644 --- a/qtmips_gui/main.cpp +++ b/qtmips_gui/main.cpp @@ -1,11 +1,14 @@ -#include "mainwindow.h" #include <QApplication> +#include <QCommandLineParser> +#include "mainwindow.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + app.setApplicationName("qtmips_gui"); + app.setApplicationVersion("0.1"); -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); MainWindow w; - w.show(); + w.start(); - return a.exec(); + return app.exec(); } diff --git a/qtmips_gui/mainwindow.cpp b/qtmips_gui/mainwindow.cpp index 49d64fc..ea48736 100644 --- a/qtmips_gui/mainwindow.cpp +++ b/qtmips_gui/mainwindow.cpp @@ -1,14 +1,93 @@ #include "mainwindow.h" -#include "ui_mainwindow.h" -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { + settings = new QSettings("CTU", "QtMips"); + coreview = nullptr; + + ui = new Ui::MainWindow(); ui->setupUi(this); + setWindowTitle("QtMips"); + + // Create/prepare other widgets + ndialog = new NewDialog(this, settings); + cache_content = new CacheContentDock(this); + cache_content->hide(); + cache_statictics = new CacheStatisticsDock(this); + cache_statictics->hide(); + registers = new RegistersDock(this); + registers->hide(); + + // Connect signals from menu + QObject::connect(ui->actionExit, SIGNAL(triggered(bool)), this, SLOT(close())); + QObject::connect(ui->actionNew, SIGNAL(triggered(bool)), this, SLOT(new_machine())); + QObject::connect(ui->actionCache, SIGNAL(triggered(bool)), this, SLOT(show_cache_content())); + QObject::connect(ui->actionCache_statistics, SIGNAL(triggered(bool)), this, SLOT(show_cache_statictics())); + QObject::connect(ui->actionRegisters, SIGNAL(triggered(bool)), this, SLOT(show_registers())); + + // Restore application state from settings + restoreState(settings->value("windowState").toByteArray()); + restoreGeometry(settings->value("windowGeometry").toByteArray()); } -MainWindow::~MainWindow() -{ +MainWindow::~MainWindow() { + settings->sync(); + delete settings; + if (coreview != nullptr) + delete coreview; + delete ndialog; + delete cache_content; + delete cache_statictics; + delete registers; delete ui; } + +void MainWindow::start() { + this->show(); + ndialog->show(); +} + +void MainWindow::create_core(MachineConfig *config) { + // Create machine + machine = new QtMipsMachine(config); + // Create machine view + coreview = new CoreView(this); + this->setCentralWidget(coreview); + // TODO connect signals +} + +void MainWindow::new_machine() { + ndialog->show(); +} + +void MainWindow::show_cache_content() { + show_dockwidget(cache_content); +} + +void MainWindow::show_cache_statictics() { + show_dockwidget(cache_statictics); +} + +void MainWindow::show_registers() { + show_dockwidget(registers); +} + +bool MainWindow::configured() { + return (machine != nullptr); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + settings->setValue("windowGeometry", saveGeometry()); + settings->setValue("windowState", saveState()); + settings->sync(); + QMainWindow::closeEvent(event); +} + +void MainWindow::show_dockwidget(QDockWidget *dw) { + if (dw->isHidden()) { + dw->show(); + addDockWidget(Qt::RightDockWidgetArea, dw); + } else { + dw->raise(); + dw->setFocus(); + } +} diff --git a/qtmips_gui/mainwindow.h b/qtmips_gui/mainwindow.h index a3948a9..bba1cb7 100644 --- a/qtmips_gui/mainwindow.h +++ b/qtmips_gui/mainwindow.h @@ -2,21 +2,55 @@ #define MAINWINDOW_H #include <QMainWindow> +#include <QSettings> +#include "ui_MainWindow.h" +#include "newdialog.h" +#include "coreview.h" +#include "cachecontent.h" +#include "cachestatistics.h" +#include "registersdock.h" -namespace Ui { -class MainWindow; -} +#include "qtmipsmachine.h" +#include "machineconfig.h" -class MainWindow : public QMainWindow -{ +class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); + void start(); + void create_core(MachineConfig *config); + +public slots: + void new_machine(); + + void show_cache_content(); + void show_cache_statictics(); + void show_registers(); + + bool configured(); + +protected: + void closeEvent(QCloseEvent *event); + private: Ui::MainWindow *ui; + + NewDialog *ndialog; + + CoreView *coreview; + + CacheContentDock *cache_content; + CacheStatisticsDock *cache_statictics; + RegistersDock *registers; + + QSettings *settings; + + QtMipsMachine *machine; // Current simulated machine + + void show_dockwidget(QDockWidget *w); }; #endif // MAINWINDOW_H diff --git a/qtmips_gui/newdialog.cpp b/qtmips_gui/newdialog.cpp index b2d24a8..31f4bb8 100644 --- a/qtmips_gui/newdialog.cpp +++ b/qtmips_gui/newdialog.cpp @@ -1,6 +1,155 @@ #include "newdialog.h" +#include "mainwindow.h" -newdialog::newdialog() -{ +NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { + ui = new Ui::NewDialog(); + ui->setupUi(this); + setWindowTitle("New machine"); + this->settings = settings; + + QObject::connect(ui->pushButton_load, SIGNAL(clicked(bool)), this, SLOT(create())); + QObject::connect(ui->pushButton_cancel, SIGNAL(clicked(bool)), this, SLOT(cancel())); + // Signals on Base tab + QObject::connect(ui->preset_no_pipeline, SIGNAL(toggled(bool)), this, SLOT(preset(bool))); + QObject::connect(ui->preset_pipelined, SIGNAL(toggled(bool)), this, SLOT(preset(bool))); + QObject::connect(ui->pushButton_browse, SIGNAL(clicked(bool)), this, SLOT(browse_elf())); +#define CUSTOM_PRESET(UI) QObject::connect(UI, SIGNAL(clicked(bool)), this, SLOT(set_custom_preset())) + // Signals on Core tab + CUSTOM_PRESET(ui->pipelined); + CUSTOM_PRESET(ui->hazard); + CUSTOM_PRESET(ui->flush_jump); + CUSTOM_PRESET(ui->prediction); + CUSTOM_PRESET(ui->prediction_static); + CUSTOM_PRESET(ui->prediction_one_dynamic); + // Signals on Memory tab + CUSTOM_PRESET(ui->mem_protec_write); + CUSTOM_PRESET(ui->mem_protec_exec); + CUSTOM_PRESET(ui->cache); + CUSTOM_PRESET(ui->cache_associative); +#undef CUSTOM_PRESET + + // Load setting after signals are configured so that we can have correct settings + load_settings(); +} + +NewDialog::~NewDialog() { + delete ui; + // Settings is freed by parent + delete elf_dialog; +} + +void NewDialog::cancel() { + this->close(); +} + +void NewDialog::create() { + MainWindow *prnt = (MainWindow*)parent(); + + MachineConfig *mc = new MachineConfig(); + mc->set_elf(ui->elf_file->text()); + mc->set_pipelined(ui->pipelined->isChecked()); + // TODO other settings + + try { + prnt->create_core(mc); + } catch (const QtMipsExceptionInput &e) { + QMessageBox msg(this); + msg.setText(e.msg(false)); + msg.setIcon(QMessageBox::Critical); + msg.setToolTip("Please check that ELF executable realy exists and is in correct format."); + msg.setDetailedText(e.msg(true)); + msg.setWindowTitle("Error while initializing new machine"); + msg.exec(); + goto cleanup; + } + + store_settings(); // Save to settings + this->close(); + +cleanup: + delete mc; +} + +void NewDialog::browse_elf() { + QFileDialog elf_dialog(this); + elf_dialog.setFileMode(QFileDialog::ExistingFile); + if (elf_dialog.exec()) + ui->elf_file->setText(elf_dialog.selectedFiles()[0]); +} + +void NewDialog::preset(bool value) { + if (value) { + bool pip = ui->preset_pipelined->isChecked(); + // Core settings + ui->pipelined->setChecked(pip); + ui->hazard->setChecked(pip); + ui->flush_jump->setChecked(pip); + ui->prediction->setChecked(pip); + ui->prediction_one_dynamic->setChecked(pip); + // Memory settings + ui->mem_protec_write->setChecked(true); + ui->mem_protec_exec->setChecked(true); + ui->cache->setChecked(pip); + ui->cache_associative->setChecked(true); + } // Else custom so do no changes +} + +void NewDialog::set_custom_preset() { + ui->preset_custom->setChecked(true); +} + +void NewDialog::closeEvent(QCloseEvent *) { + load_settings(); // Reset from settings + // Close main window if not already configured + MainWindow *prnt = (MainWindow*)parent(); + if (!prnt->configured()) + prnt->close(); +} + +#define LOAD_BUTTON(NAME, DEF) ui->NAME->setChecked(settings->value(#NAME, DEF).toBool()) +#define LOAD_LINE(NAME, DEF) ui->NAME->setText(settings->value(#NAME, DEF).toString()) + +#define STORE_BUTTON(NAME) settings->setValue(#NAME, ui->NAME->isChecked()) +#define STORE_LINE(NAME) settings->setValue(#NAME, ui->NAME->text()) + +void NewDialog::load_settings() { + // Core tab + LOAD_BUTTON(pipelined, false); + LOAD_BUTTON(hazard, false); + LOAD_BUTTON(flush_jump, false); + LOAD_BUTTON(prediction, false); + LOAD_BUTTON(prediction_static, true); + LOAD_BUTTON(prediction_one_dynamic, false); + // Memory tab + LOAD_BUTTON(mem_protec_write, true); + LOAD_BUTTON(mem_protec_exec, true); + LOAD_BUTTON(cache, false); + LOAD_BUTTON(cache_associative, true); + // Base tab + // We are doing this last so presets can reset previous configuration to somethin valid + LOAD_BUTTON(preset_no_pipeline, true); + LOAD_BUTTON(preset_pipelined, false); + LOAD_BUTTON(preset_custom, false); + LOAD_LINE(elf_file, ""); +} + +void NewDialog::store_settings() { + // Core tab + STORE_BUTTON(pipelined); + STORE_BUTTON(hazard); + STORE_BUTTON(flush_jump); + STORE_BUTTON(prediction); + STORE_BUTTON(prediction_static); + STORE_BUTTON(prediction_one_dynamic); + // Memory tab + STORE_BUTTON(mem_protec_write); + STORE_BUTTON(mem_protec_exec); + STORE_BUTTON(cache); + STORE_BUTTON(cache_associative); + // Base tab + STORE_BUTTON(preset_no_pipeline); + STORE_BUTTON(preset_pipelined); + STORE_BUTTON(preset_custom); + STORE_LINE(elf_file); } diff --git a/qtmips_gui/newdialog.h b/qtmips_gui/newdialog.h index d37c9c0..d9e850e 100644 --- a/qtmips_gui/newdialog.h +++ b/qtmips_gui/newdialog.h @@ -1,11 +1,36 @@ #ifndef NEWDIALOG_H #define NEWDIALOG_H +#include <QDialog> +#include <QSettings> +#include <QFileDialog> +#include <QMessageBox> +#include "ui_NewDialog.h" +#include "qtmipsexception.h" -class newdialog -{ +class NewDialog : public QDialog { + Q_OBJECT public: - newdialog(); + NewDialog(QWidget *parent, QSettings *settings); + ~NewDialog(); + +public slots: + void cancel(); + void create(); + void browse_elf(); + void preset(bool); + void set_custom_preset(); + +protected: + void closeEvent(QCloseEvent *); + +private: + Ui::NewDialog *ui; + QSettings *settings; + QFileDialog *elf_dialog; + + void load_settings(); + void store_settings(); }; -#endif // NEWDIALOG_H
\ No newline at end of file +#endif // NEWDIALOG_H diff --git a/qtmips_gui/qtmips_gui.pro b/qtmips_gui/qtmips_gui.pro index 7dfb53c..ee2bd6e 100644 --- a/qtmips_gui/qtmips_gui.pro +++ b/qtmips_gui/qtmips_gui.pro @@ -14,10 +14,27 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + newdialog.cpp \ + coreview.cpp \ + registersdock.cpp \ + cachestatistics.cpp \ + cachecontent.cpp HEADERS += \ - mainwindow.h + mainwindow.h \ + newdialog.h \ + coreview.h \ + registersdock.h \ + cachestatistics.h \ + cachecontent.h FORMS += \ - mainwindow.ui + NewDialog.ui \ + MainWindow.ui \ + CacheContent.ui \ + registersdock.ui \ + CacheStatistics.ui + +RESOURCES += \ + icons.qrc diff --git a/qtmips_gui/registersdock.cpp b/qtmips_gui/registersdock.cpp index bf82725..e8a5907 100644 --- a/qtmips_gui/registersdock.cpp +++ b/qtmips_gui/registersdock.cpp @@ -1,6 +1,10 @@ -#include "registers.h" +#include "registersdock.h" -Registers::Registers() -{ +RegistersDock::RegistersDock(QWidget *parent) : QDockWidget(parent) { + ui = new Ui::RegistersDock(); + ui->setupUi(this); +} +RegistersDock::~RegistersDock() { + delete ui; } diff --git a/qtmips_gui/registersdock.h b/qtmips_gui/registersdock.h index 0fb60eb..338edc3 100644 --- a/qtmips_gui/registersdock.h +++ b/qtmips_gui/registersdock.h @@ -1,11 +1,17 @@ -#ifndef REGISTERS_H -#define REGISTERS_H +#ifndef REGISTERSDOCK_H +#define REGISTERSDOCK_H +#include <QDockWidget> +#include "ui_registersdock.h" -class Registers -{ +class RegistersDock : public QDockWidget { public: - Registers(); + RegistersDock(QWidget *parent); + ~RegistersDock(); + +private: + Ui::RegistersDock *ui; + }; -#endif // REGISTERS_H
\ No newline at end of file +#endif // REGISTERSDOCK_H diff --git a/qtmips_gui/registersdock.ui b/qtmips_gui/registersdock.ui index ac2f263..9b51505 100644 --- a/qtmips_gui/registersdock.ui +++ b/qtmips_gui/registersdock.ui @@ -1,7 +1,7 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>DockWidget</class> - <widget class="QDockWidget" name="DockWidget"> + <class>RegistersDock</class> + <widget class="QDockWidget" name="RegistersDock"> <property name="geometry"> <rect> <x>0</x> @@ -11,8 +11,16 @@ </rect> </property> <property name="windowTitle"> - <string>DockWidget</string> + <string>Registers</string> </property> - <widget class="QWidget" name="dockWidgetContents"/> + <widget class="QWidget" name="dockWidgetContents"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTableWidget" name="tableWidget"/> + </item> + </layout> + </widget> </widget> + <resources/> + <connections/> </ui> diff --git a/qtmips_machine/alu.cpp b/qtmips_machine/alu.cpp index 5c74a5d..85bc804 100644 --- a/qtmips_machine/alu.cpp +++ b/qtmips_machine/alu.cpp @@ -1,6 +1,53 @@ #include "alu.h" +#include "qtmipsexception.h" -Alu::Alu() -{ +std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) { + switch(operation) { + case ALU_OP_SLL: + return t << sa; + case ALU_OP_SRL: + return t >> sa; + case ALU_OP_SRA: + // TODO is this correct implementation? (Shouldn't we be masking top most bit?) + return (t >> sa) | (t & 0x80000000); + case ALU_OP_SLLV: + return t << s; + case ALU_OP_SRLV: + return t >> s; + case ALU_OP_SRAV: + // TODO is this correct implementation? (Shouldn't we be masking top most bit?) + return (t >> s) | (t & 0x80000000); + case ALU_OP_ADD: + if (s > (0xFFFFFFFF - t)) + throw QTMIPS_EXCEPTION(Overflow, "ADD operation overflow/underflow", QString::number(s) + QString(" + ") + QString::number(t)); + // Intentional falltrough + case ALU_OP_ADDU: + return s + t; + case ALU_OP_SUB: + if (s < t) + throw QTMIPS_EXCEPTION(Overflow, "SUB operation overflow/underflow", QString::number(s) + QString(" - ") + QString::number(t)); + // Intentional falltrough + case ALU_OP_SUBU: + return s - t; + case ALU_OP_AND: + return s & t; + case ALU_OP_OR: + return s | t; + case ALU_OP_XOR: + return s ^ t; + case ALU_OP_NOR: + return ~(s | t); + case ALU_OP_SLTU: + // TODO is this correct implementation? (this is two's complement signed representation so do we care?) + // Intentional falltrough + case ALU_OP_SLT: + return (s < t) ? 1 : 0; + default: + throw QTMIPS_EXCEPTION(UnsupportedAluOperation, "Unknown ALU operation", QString::number(operation, 16)); + } +} +QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa) { + // TODO + return QString(""); } diff --git a/qtmips_machine/alu.h b/qtmips_machine/alu.h index e913b0c..974b462 100644 --- a/qtmips_machine/alu.h +++ b/qtmips_machine/alu.h @@ -1,11 +1,39 @@ #ifndef ALU_H #define ALU_H +#include <cstdint> +#include <QString> -class Alu -{ -public: - Alu(); +// TODO Any other operations? We seems to be missing a lot of them. +enum AluOp : std::uint8_t { + ALU_OP_SLL = 0, + ALU_OP_SRL = 2, + ALU_OP_SRA, + ALU_OP_SLLV, + ALU_OP_SRLV = 6, + ALU_OP_SRAV, + ALU_OP_ADD = 32, + ALU_OP_ADDU, + ALU_OP_SUB, + ALU_OP_SUBU, + ALU_OP_AND, + ALU_OP_OR, + ALU_OP_XOR, + ALU_OP_NOR, + ALU_OP_SLT = 42, + ALU_OP_SLTU, + ALU_OP_LAST = 64 // First impossible operation (just to be sure that we don't overflow) }; -#endif // ALU_H
\ No newline at end of file +// Do ALU operation. +// operation: This is function field from instruction or shifted opcode for immediate instructions +// s: Loaded from rs. Also calles as source. +// t: Loaded from rt or immediate field from instruction it self. Also called as target. +// sa: This is value directly from instruction it self (sa section) used for shift operations +// Returned value is commonly saved to rt/rd or any other way passed trough core +std::uint32_t alu_operate(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa); + +// Returns string representation of ALU instruction (internally used by Instruction::to_str) +QString alu_str(enum AluOp operation, std::uint32_t s, std::uint32_t t, std::uint8_t sa); + +#endif // ALU_H diff --git a/qtmips_machine/core.cpp b/qtmips_machine/core.cpp index a6c92b5..1edcc97 100644 --- a/qtmips_machine/core.cpp +++ b/qtmips_machine/core.cpp @@ -1,5 +1,164 @@ #include "core.h" +#include "programloader.h" -Core::Core() { + struct DecodeMap { + bool supported, mem2reg, memwrite, alubimm, regd, regwrite, branch; +}; + +// This is temporally operation place holder +#define NOPE { .supported = false } + +// This is map from opcode to signals. +static const struct DecodeMap dmap[] = { + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = true, .branch = false }, // Alu operations and more + // TODO These are just copies of first one + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = false, .regwrite = false, .branch = true }, // Branch on alu operations + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // J + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // JAL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BEQ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BNE + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BLEZ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // BGTZ + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ADDIU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SLTIU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ANDI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // ORI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // XORI + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LUI + NOPE, // 16 + NOPE, // 17 + NOPE, // 18 + NOPE, // 19 + NOPE, // 20 + NOPE, // 21 + NOPE, // 22 + NOPE, // 23 + NOPE, // 24 + NOPE, // 25 + NOPE, // 26 + NOPE, // 27 + NOPE, // 28 + NOPE, // 29 + NOPE, // 30 + NOPE, // 31 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LB + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LH + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LW + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LBU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LHU + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // LWR + NOPE, // 39 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SB + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SH + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SWL + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SW + NOPE, // 44 + NOPE, // 45 + { .supported = true, .mem2reg = false, .memwrite = false, .alubimm = false, .regd = true, .regwrite = true, .branch = false }, // SWR + NOPE, // 47 + NOPE, // 48 + NOPE, // 49 + NOPE, // 50 + NOPE, // 51 + NOPE, // 52 + NOPE, // 53 + NOPE, // 54 + NOPE, // 55 + NOPE, // 56 + NOPE, // 57 + NOPE, // 58 + NOPE, // 59 + NOPE, // 60 + NOPE, // 61 + NOPE, // 62 + NOPE // 63 +}; + +Core::Core(Registers *regs, MemoryAccess *mem) { + this->regs = regs; + this->mem = mem; +} + +struct Core::dtFetch Core::fetch() { + // TODO signals + Instruction inst(mem->read_word(regs->read_pc())); + regs->pc_inc(); + return { + .inst = inst + }; +} + +struct Core::dtDecode Core::decode(struct dtFetch dt) { + struct DecodeMap dec = dmap[dt.inst.opcode()]; + if (!dec.supported) + // TODO message + throw QTMIPS_EXCEPTION(UnsupportedInstruction, "", ""); + enum AluOp d_alu = ALU_OP_SLL; // TODO decode for real + return { + .mem2reg = dec.mem2reg, + .memwrite = dec.memwrite, + .alubimm = dec.alubimm, + .regd = dec.regd, + .regwrite = dec.regwrite, + .branch = dec.branch, + .aluop = d_alu, + .val_rs = regs->read_gp(dt.inst.rs()), + .val_rt = regs->read_gp(dt.inst.rt()), + .val_sa = dt.inst.shamt(), + .val_immediate = dt.inst.immediate(), + }; + // TODO on jump there should be delay slot. Does processor addes it or compiler. And do we care? +} + +struct Core::dtExecute Core::execute(struct dtDecode dt) { + // TODO signals + return { + .mem2reg = dt.mem2reg, + .val = alu_operate(dt.aluop, dt.val_rs, dt.val_rt, dt.val_sa) + }; +} + +struct Core::dtMemory Core::memory(struct dtExecute dt) { + // TODO signals + return { + .mem2reg = dt.mem2reg, + .val = dt.val, + }; +} + +void Core::writeback(struct dtMemory dt) { + if (dt.mem2reg) { + + } +} + +CoreSingle::CoreSingle(Registers *regs, MemoryAccess *mem) : \ + Core(regs, mem) { + // Nothing to do +} + +void CoreSingle::step() { + struct dtFetch f = fetch(); + struct dtDecode d = decode(f); + struct dtExecute e = execute(d); + struct dtMemory m = memory(e); + writeback(m); +} + +CorePipelined::CorePipelined(Registers *regs, MemoryAccess *mem) : \ + Core(regs, mem) { + // Nothing to do +} + +void CorePipelined::step() { + // TODO implement pipelined + struct dtFetch f = fetch(); + struct dtDecode d = decode(f); + struct dtExecute e = execute(d); + struct dtMemory m =memory(e); + writeback(m); } diff --git a/qtmips_machine/core.h b/qtmips_machine/core.h index 2fd0a40..d4523c3 100644 --- a/qtmips_machine/core.h +++ b/qtmips_machine/core.h @@ -2,20 +2,72 @@ #define CORE_H #include <QObject> -#include "instruction.h" +#include "qtmipsexception.h" #include "registers.h" #include "memory.h" -#include "programloader.h" -#include "programmemory.h" +#include "instruction.h" +#include "alu.h" class Core : public QObject { Q_OBJECT public: - Core(); + Core(Registers *regs, MemoryAccess *mem); + + virtual void step() = 0; // Do single step signals: -public slots: +protected: + Registers *regs; + MemoryAccess *mem; + + struct dtFetch { + Instruction inst; // Loaded instruction + }; + struct dtDecode { + bool mem2reg; // Write memory output to register (instead alu output) + bool memwrite; // If memory should write input + bool alubimm; // If b value to alu is immediate value (rt used otherwise) + bool regd; // If rd is used (otherwise rt is used for write target) + bool regwrite; // If output should be written back to register (which one depends on regd) + bool branch; // If this is branch instruction + enum AluOp aluop; // Decoded ALU operation + std::uint32_t val_rs; // Value from register rs + std::uint32_t val_rt; // Value from register rt + std::uint8_t val_sa; // Value of sa in instruction it self + std::uint16_t val_immediate; // Value of immediate in instruction it self + }; + struct dtExecute { + bool mem2reg; + std::uint32_t val; + // TODO + }; + struct dtMemory { + bool mem2reg; + // TODO + std::uint32_t val; + }; + + struct dtFetch fetch(); + struct dtDecode decode(struct dtFetch); + struct dtExecute execute(struct dtDecode); + struct dtMemory memory(struct dtExecute); + void writeback(struct dtMemory); + +}; + +class CoreSingle : public Core { +public: + CoreSingle(Registers *regs, MemoryAccess *mem); + + void step(); +}; + +class CorePipelined : public Core { +public: + CorePipelined(Registers *regs, MemoryAccess *mem); + + void step(); }; #endif // CORE_H diff --git a/qtmips_machine/instruction.cpp b/qtmips_machine/instruction.cpp index 1771afb..8591d93 100644 --- a/qtmips_machine/instruction.cpp +++ b/qtmips_machine/instruction.cpp @@ -1,102 +1,106 @@ #include "instruction.h" #include "qtmipsexception.h" -Instruction::Instruction() { - this->st = IS_FETCH; +struct InstructionMap { + const char *name; +}; + +#define IM_UNKNOWN {"UNKNOWN"} +const struct InstructionMap instruction_map[] = { + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + {"ADDI"}, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN, + IM_UNKNOWN +}; + +Instruction::Instruction(std::uint32_t inst) { + this->dt = inst; +} + +Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= rs << 21; + this->dt |= rt << 16; + this->dt |= rd << 11; + this->dt |= shamt << 6; + this->dt |= funct; } -void Instruction::decode(Registers *regs) { - if (this->st != IS_FETCH) - // TODO other exception - throw std::exception(); - this->st = IS_DECODE; +Instruction::Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= rs << 21; + this->dt |= rt << 16; + this->dt |= immediate; } -void Instruction::execute() { - if (this->st != IS_DECODE) - // TODO other exception - throw std::exception(); - this->st = IS_EXECUTE; +Instruction::Instruction(std::uint8_t opcode, std::uint32_t address) { + this->dt = 0; + this->dt |= opcode << 26; + this->dt |= address; } -void Instruction::memory(Memory *mem) { - if (this->st != IS_EXECUTE) - // TODO other exception - throw std::exception(); - this->st = IS_MEMORY; +QString Instruction::to_str() { + if (this->opcode() >= sizeof(instruction_map)) + return QString("UNKNOWN"); + return QString(instruction_map[this->opcode()].name); } -void Instruction::write_back(Registers *regs) { - if (this->st != IS_MEMORY) - // TODO other exception - throw std::exception(); - this->st = IS_WRITE_BACK; +#define MASK(LEN,OFF) ((this->dt >> (OFF)) & ((1 << (LEN)) - 1)) + +std::uint8_t Instruction::opcode() const { + return (std::uint8_t) MASK(6, 26); } -enum InstructionState Instruction::state() { - return this->st; +std::uint8_t Instruction::rs() const { + return (std::uint8_t) MASK(5, 21); } -bool Instruction::running() { - return this->st > IS_FETCH && this->st < IS_WRITE_BACK; +std::uint8_t Instruction::rt() const { + return (std::uint8_t) MASK(5, 16); } -bool Instruction::done() { - return this->st >= IS_WRITE_BACK; +std::uint8_t Instruction::rd() const { + return (std::uint8_t) MASK(5, 11); } -InstructionR::InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa) : Instruction() { - this->rs = rs; - this->rd = rd; - this->rt = rt; - this->sa = sa; +std::uint8_t Instruction::shamt() const { + return (std::uint8_t) MASK(5, 6); + } -// TODO for registers output as register ($0)! - -QVector<QString> InstructionR::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->rs, 10); - // Target register - str << QString::number((unsigned)this->rt, 10); - // Destination register - str << QString::number((unsigned)this->rd, 10); - // Shift amount - str << QString::number((unsigned)this->sa, 10); - return str; +std::uint8_t Instruction::funct() const { + return (std::uint8_t) MASK(6, 0); } -InstructionI::InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate) : Instruction() { - this->rs = rs; - this->rt = rt; - this->immediate = immediate; +std::uint16_t Instruction::immediate() const { + return (std::uint16_t) MASK(16, 0); } -QVector<QString> InstructionI::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->rs, 10); - // Target register - str << QString::number((unsigned)this->rt, 10); - // Immediate value - str << QString::number((unsigned)this->immediate, 16); - return str; +std::uint32_t Instruction::address() const { + return (std::uint32_t) MASK(26, 0); } -InstructionJ::InstructionJ(std::uint32_t address) : Instruction() { - this->address = address; +std::uint32_t Instruction::data() const { + return this->dt; } -QVector<QString> InstructionJ::to_strs() { - QVector<QString> str; - // Instruction name - str << "unknown"; // unknown instruction, should be replaced by child - // Source register - str << QString::number((unsigned)this->address, 16); - return str; +bool Instruction::operator ==(const Instruction &c) const { + return (this->data() == c.data()); } diff --git a/qtmips_machine/instruction.h b/qtmips_machine/instruction.h index 8c5ede9..3b76fba 100644 --- a/qtmips_machine/instruction.h +++ b/qtmips_machine/instruction.h @@ -2,64 +2,30 @@ #define INSTRUCTION_H #include <qstring.h> -#include <qvector.h> -#include "registers.h" -#include "memory.h" - -enum InstructionState { - IS_FETCH, - IS_DECODE, - IS_EXECUTE, - IS_MEMORY, - IS_WRITE_BACK, -}; class Instruction { public: - Instruction(); + Instruction(std::uint32_t inst); + Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd, std::uint8_t shamt, std::uint8_t funct); // Type R + Instruction(std::uint8_t opcode, std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); // Type I + Instruction(std::uint8_t opcode, std::uint32_t address); // Type J - // TODO return some info for forwarding, stall, flush - virtual void decode(Registers *regs); // Read and prepare instructions - virtual void execute(); // ALU operations - virtual void memory(Memory *mem); // Read or write to memory - virtual void write_back(Registers *regs); // Write results to registers + QString to_str(); - enum InstructionState state(); - bool running(); - bool done(); + std::uint8_t opcode() const; + std::uint8_t rs() const; + std::uint8_t rt() const; + std::uint8_t rd() const; + std::uint8_t shamt() const; + std::uint8_t funct() const; + std::uint16_t immediate() const; + std::uint32_t address() const; + std::uint32_t data() const; - virtual QVector<QString> to_strs() = 0; // Returns all fields of instructions in string + bool operator ==(const Instruction &c) const; private: - enum InstructionState st; -}; - -class InstructionR : public Instruction { -public: - InstructionR(std::uint8_t rs, std::uint8_t rd, std::uint8_t rt, std::uint8_t sa); - - QVector<QString> to_strs(); -protected: - std::uint8_t rs, rd, rt, sa; -}; - -class InstructionI : public Instruction { -public: - InstructionI(std::uint8_t rs, std::uint8_t rt, std::uint16_t immediate); - - QVector<QString> to_strs(); -protected: - std::uint8_t rs, rt; - std::uint16_t immediate; -}; - -class InstructionJ : public Instruction { -public: - InstructionJ(std::uint32_t address); - - QVector<QString> to_strs(); -protected: - std::uint32_t address; + std::uint32_t dt; }; #endif // INSTRUCTION_H diff --git a/qtmips_machine/instructions/arithmetic.cpp b/qtmips_machine/instructions/arithmetic.cpp deleted file mode 100644 index f3cad86..0000000 --- a/qtmips_machine/instructions/arithmetic.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "instructions/arithmetic.h" - -InstructionArithmetic::InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt) - : InstructionR(rs, rd, rt, 0) { - this->type = type; -} - -void InstructionArithmetic::decode(Registers *regs) { - Instruction::decode(regs); - this->rs_d = regs->read_gp(this->rs); - this->rd_d = regs->read_gp(this->rd); -} - -void InstructionArithmetic::execute() { - Instruction::execute(); - switch (this->type) { - case IAT_ADD: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->rd_d); - break; - case IAT_ADDU: - this->rt_d = this->rs_d + this->rd_d; - break; - case IAT_SUB: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->rd_d); - break; - case IAT_SUBU: - this->rt_d = this->rs_d - this->rd_d; - break; - case IAT_AND: - this->rt_d = this->rs_d & this->rd_d; - break; - case IAT_OR: - this->rt_d = this->rs_d | this->rd_d; - break; - case IAT_XOR: - this->rt_d = this->rs_d ^ this->rd_d; - break; - case IAT_NOR: - // TODO - break; - case IAT_SLT: - // TODO - break; - case IAT_SLTU: - // TODO - break; - } -} - -void InstructionArithmetic::memory(Memory *mem) { - Instruction::memory(mem); - // pass -} - -void InstructionArithmetic::write_back(Registers *regs) { - Instruction::write_back(regs); - regs->write_gp(this->rt, this->rt_d); -} - -QVector<QString> InstructionArithmetic::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 4); // Drop sa field - switch (this->type) { - case IAT_ADD: - str[0] = "add"; - break; - case IAT_ADDU: - str[0] = "addu"; - break; - case IAT_SUB: - str[0] = "sub"; - break; - case IAT_SUBU: - str[0] = "subu"; - break; - case IAT_AND: - str[0] = "and"; - break; - case IAT_OR: - str[0] = "or"; - break; - case IAT_XOR: - str[0] = "xor"; - break; - case IAT_NOR: - str[0] = "nor"; - break; - case IAT_SLT: - str[0] = "slt"; - break; - case IAT_SLTU: - str[0] = "sltu"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionArithmeticImmediate::InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value) - : InstructionI(rs, rt, value) { - this->type = type; -} - -void InstructionArithmeticImmediate::decode(Registers *regs) { - Instruction::decode(regs); - this->rs_d = regs->read_gp(this->rs); -} - -void InstructionArithmeticImmediate::execute() { - Instruction::execute(); - switch (this->type) { - case IAT_ADDI: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d + (std::int32_t)this->immediate); - break; - case IAT_ADDIU: - this->rt_d = this->rs_d + this->immediate; - break; - case IAT_ANDI: - this->rt_d = (std::uint32_t)((std::int32_t)this->rs_d - (std::int32_t)this->immediate); - break; - case IAT_ORI: - this->rt_d = this->rs_d - this->immediate; - break; - case IAT_XORI: - this->rt_d = this->rs_d & this->immediate; - break; - } -} - -void InstructionArithmeticImmediate::memory(Memory *mem) { - Instruction::memory(mem); - // pass -} - -void InstructionArithmeticImmediate::write_back(Registers *regs) { - Instruction::write_back(regs); - regs->write_gp(this->rt, this->rt_d); -} - -QVector<QString> InstructionArithmeticImmediate::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case IAT_ADDI: - str[0] = "addi"; - break; - case IAT_ADDIU: - str[0] = "addiu"; - break; - case IAT_ANDI: - str[0] = "andi"; - break; - case IAT_ORI: - str[0] = "ori"; - break; - case IAT_XORI: - str[0] = "xori"; - break; - case IAT_SLTI: - str[0] = "slti"; - break; - case IAT_SLTIU: - str[0] = "sltiu"; - break; - case IAT_LUI: - str[0] = "lui"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/arithmetic.h b/qtmips_machine/instructions/arithmetic.h deleted file mode 100644 index 185ed95..0000000 --- a/qtmips_machine/instructions/arithmetic.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef ARITHMETIC_H -#define ARITHMETIC_H - -#include "instruction.h" - -enum InstructionArithmeticT { - IAT_ADD, // Add - IAT_ADDU, // Add unsigned - IAT_SUB, // Subtract - IAT_SUBU, // Subtract unsigned - IAT_AND, - IAT_OR, - IAT_XOR, - IAT_NOR, - IAT_SLT, // set on less than - IAT_SLTU, // set on less than unsigned -}; - -class InstructionArithmetic : public InstructionR { -public: - InstructionArithmetic(enum InstructionArithmeticT type, std::uint8_t rs, std::uint8_t rd, std::uint8_t rt); - - void decode(Registers *regs); - void execute(); - void memory(Memory *mem); - void write_back(Registers *regs); - - QVector<QString> to_strs(); -private: - enum InstructionArithmeticT type; - std::uint32_t rs_d, rd_d, rt_d; -}; - -enum InstructionArithmeticImmediateT { - IAT_ADDI, - IAT_ADDIU, - IAT_ANDI, - IAT_ORI, - IAT_XORI, - IAT_SLTI, - IAT_SLTIU, - IAT_LUI -}; - -class InstructionArithmeticImmediate : public InstructionI { -public: - InstructionArithmeticImmediate(enum InstructionArithmeticImmediateT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t value); - - void decode(Registers *regs); - void execute(); - void memory(Memory *mem); - void write_back(Registers *regs); - - QVector<QString> to_strs(); -private: - enum InstructionArithmeticImmediateT type; - std::uint32_t rs_d, rt_d; -}; - -#endif // ARITHMETIC_H diff --git a/qtmips_machine/instructions/jumpbranch.cpp b/qtmips_machine/instructions/jumpbranch.cpp deleted file mode 100644 index 6579c2b..0000000 --- a/qtmips_machine/instructions/jumpbranch.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "jumpbranch.h" - -InstructionJump::InstructionJump(bool link, std::uint32_t address) - : InstructionJ(address) { - this->link = link; -} - -QVector<QString> InstructionJump::to_strs() { - QVector<QString> str = this->InstructionJ::to_strs(); - if (link) - str[0] = "j"; - else - str[0] = "jal"; - return str; -} - -InstructionJumpRegister::InstructionJumpRegister(bool link, std::uint8_t rs) - : InstructionR(rs, 0, 0, 0) { - this->link = link; -} - -QVector<QString> InstructionJumpRegister::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 2, str.end()); // Drop every field after rs - if (link) - str[0] = "j"; - else - str[0] = "jal"; - return str; -} diff --git a/qtmips_machine/instructions/jumpbranch.h b/qtmips_machine/instructions/jumpbranch.h deleted file mode 100644 index 762ad95..0000000 --- a/qtmips_machine/instructions/jumpbranch.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef JUMPBRANCH_H -#define JUMPBRANCH_H - -#include "instruction.h" - -class InstructionJump : InstructionJ { -public: - InstructionJump(bool link, std::uint32_t address); - QVector<QString> to_strs(); -private: - bool link; -}; - -class InstructionJumpRegister : InstructionR { -public: - InstructionJumpRegister(bool link, std::uint8_t rs); - QVector<QString> to_strs(); -private: - bool link; -}; - -enum InstructionBranchT { - -}; - -class InstructionBranch : InstructionI { -public: - InstructionBranch(); - QVector<QString> to_strs(); -private: - // TODO -}; - -#endif // JUMPBRANCH_H diff --git a/qtmips_machine/instructions/loadstore.cpp b/qtmips_machine/instructions/loadstore.cpp deleted file mode 100644 index 27c6402..0000000 --- a/qtmips_machine/instructions/loadstore.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "loadstore.h" - -InstructionLoad::InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset) - : InstructionI(rs, rt, offset) { - this->type = type; -} - -QVector<QString> InstructionLoad::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case ILST_B: - str[0] = "lb"; - break; - case ILST_HW: - str[0] = "lh"; - break; - case ILST_WL: - str[0] = "lwl"; - break; - case ILST_W: - str[0] = "lw"; - break; - case ILST_BU: - str[0] = "lbu"; - break; - case ILST_HU: - str[0] = "lhu"; - break; - case ILST_WR: - str[0] = "lwr"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionStore::InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset) - : InstructionI(rs, rt, offset) { - this->type = type; -} - -QVector<QString> InstructionStore::to_strs() { - QVector<QString> str = this->InstructionI::to_strs(); - switch (this->type) { - case ILST_B: - str[0] = "sb"; - break; - case ILST_HW: - str[0] = "sh"; - break; - case ILST_WL: - str[0] = "swl"; - break; - case ILST_W: - str[0] = "sw"; - break; - case ILST_WR: - str[0] = "swr"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/loadstore.h b/qtmips_machine/instructions/loadstore.h deleted file mode 100644 index 6f028fd..0000000 --- a/qtmips_machine/instructions/loadstore.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef LOADSTORE_H -#define LOADSTORE_H - -#include "instruction.h" - -enum InstructionLoadStoreT { - ILST_B, // Byte - ILST_HW, // Half word - ILST_WL, // Word left - ILST_W, // Word - ILST_BU, // Byte unsigned - ILST_HU, // Half word unsigned - ILST_WR // Word right -}; - -class InstructionLoad : public InstructionI { -public: - InstructionLoad(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset); - QVector<QString> to_strs(); -private: - enum InstructionLoadStoreT type; -}; - -class InstructionStore : public InstructionI { -public: - InstructionStore(enum InstructionLoadStoreT type, std::uint8_t rs, std::uint8_t rt, std::uint16_t offset); - QVector<QString> to_strs(); -private: - enum InstructionLoadStoreT type; -}; - -#endif // LOADSTORE_H diff --git a/qtmips_machine/instructions/nop.cpp b/qtmips_machine/instructions/nop.cpp deleted file mode 100644 index 7623dff..0000000 --- a/qtmips_machine/instructions/nop.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "nop.h" - -QVector<QString> InstructionNop::to_strs() { - QVector<QString> str; - str << QString("nop"); - return str; -} diff --git a/qtmips_machine/instructions/nop.h b/qtmips_machine/instructions/nop.h deleted file mode 100644 index 5c019fd..0000000 --- a/qtmips_machine/instructions/nop.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NOP_H -#define NOP_H - -#include "instruction.h" - -class InstructionNop : public Instruction { -public: - QVector<QString> to_strs(); -}; - -#endif // NOP_H diff --git a/qtmips_machine/instructions/shift.cpp b/qtmips_machine/instructions/shift.cpp deleted file mode 100644 index 34bc1c9..0000000 --- a/qtmips_machine/instructions/shift.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "shift.h" - -InstructionShift::InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa) - : InstructionR(0, rt, rd, sa) { - this->type = type; -} - -QVector<QString> InstructionShift::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 1); // Drop rs field - switch (this->type) { - case IST_LL: - str[0] = "sll"; - break; - case IST_RL: - str[0] = "srl"; - break; - case IST_RA: - str[0] = "sra"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} - -InstructionShiftVariable::InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd) - : InstructionR(rs, rt, rd, 0) { - this->type = type; -} - -QVector<QString> InstructionShiftVariable::to_strs() { - QVector<QString> str = this->InstructionR::to_strs(); - str.erase(str.begin() + 4); // Drop sa field - switch (this->type) { - case IST_LL: - str[0] = "sllv"; - break; - case IST_RL: - str[0] = "srlv"; - break; - case IST_RA: - str[0] = "srav"; - break; - default: - // TODO different exception - throw std::exception(); - } - return str; -} diff --git a/qtmips_machine/instructions/shift.h b/qtmips_machine/instructions/shift.h deleted file mode 100644 index 69e2e1e..0000000 --- a/qtmips_machine/instructions/shift.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SHIFT_H -#define SHIFT_H - -#include "instruction.h" - -enum InstructionShiftT { - IST_LL, // Left logical - IST_RL, // Right logical - IST_RA // Right arithmetic -}; - -class InstructionShift : public InstructionR { -public: - InstructionShift(enum InstructionShiftT type, std::uint8_t rt, std::uint8_t rd, std::uint8_t sa); - QVector<QString> to_strs(); -private: - enum InstructionShiftT type; -}; - -class InstructionShiftVariable : public InstructionR { -public: - InstructionShiftVariable(enum InstructionShiftT type, std::uint8_t rs, std::uint8_t rt, std::uint8_t rd); - QVector<QString> to_strs(); -private: - enum InstructionShiftT type; -}; - -#endif // SHIFT_H diff --git a/qtmips_machine/machineconfig.cpp b/qtmips_machine/machineconfig.cpp index 8562ab9..f23140f 100644 --- a/qtmips_machine/machineconfig.cpp +++ b/qtmips_machine/machineconfig.cpp @@ -1,6 +1,45 @@ #include "machineconfig.h" -MachineConfig::MachineConfig() -{ +MachineConfig::MachineConfig() { + pipeline = false; + jumppred = false; +} + +MachineConfig::MachineConfig(MachineConfig *cc) { + pipeline = cc->pipelined(); + jumppred = cc->jump_prediction(); +} + +void MachineConfig::set_pipelined(bool v) { + pipeline = v; +} + +void MachineConfig::set_jump_prediction(bool v) { + jumppred = v; + if (jumppred) + pipeline = true; +} + +void MachineConfig::set_cache(enum CacheType cc) { + cache_type = cc; +} + +void MachineConfig::set_elf(QString path) { + elf_path = path; +} + +bool MachineConfig::pipelined() const { + return pipeline; +} + +bool MachineConfig::jump_prediction() const { + return jumppred; +} + +enum MachineConfig::CacheType MachineConfig::cache() const { + return cache_type; +} +QString MachineConfig::elf() const { + return elf_path; } diff --git a/qtmips_machine/machineconfig.h b/qtmips_machine/machineconfig.h index 58c2fc2..352e62b 100644 --- a/qtmips_machine/machineconfig.h +++ b/qtmips_machine/machineconfig.h @@ -1,11 +1,40 @@ #ifndef MACHINECONFIG_H #define MACHINECONFIG_H +#include <QString> -class MachineConfig -{ +class MachineConfig { public: MachineConfig(); + MachineConfig(MachineConfig *cc); + + enum CacheType { + CCT_NONE, + CCT_ASSOCIATIVE, + // TODO + }; + + // Configure if CPU is pipelined + // In default disabled. + void set_pipelined(bool); + // Configure if we want to do jump prediction + // In default disabled. When enabled it also automatically enables pipelining + void set_jump_prediction(bool); + // Configure cache type + // In default CCT_NONE is used. + void set_cache(enum CacheType); + // Set path to source elf file. This has to be set before core is initialized. + void set_elf(QString path); + + bool pipelined() const; + bool jump_prediction() const; + enum CacheType cache() const; + QString elf() const; + +private: + bool pipeline, jumppred; + enum CacheType cache_type; + QString elf_path; }; -#endif // MACHINECONFIG_H
\ No newline at end of file +#endif // MACHINECONFIG_H diff --git a/qtmips_machine/programloader.cpp b/qtmips_machine/programloader.cpp index 6eefa6c..f49fa3c 100644 --- a/qtmips_machine/programloader.cpp +++ b/qtmips_machine/programloader.cpp @@ -2,12 +2,11 @@ #include <exception> #include <unistd.h> #include <fcntl.h> -#include <iostream> #include <errno.h> #include <cstring> #include "qtmipsexception.h" -ProgramLoader::ProgramLoader(char *file) { +ProgramLoader::ProgramLoader(const char *file) { // Initialize elf library if (elf_version(EV_CURRENT) == EV_NONE) throw QTMIPS_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1)); @@ -53,29 +52,35 @@ ProgramLoader::ProgramLoader(char *file) { // TODO instead of direct access should we be using sections and elf_data? And if so how to link program header and section? } +ProgramLoader::ProgramLoader(QString file) : ProgramLoader(file.toStdString().c_str()) { } + ProgramLoader::~ProgramLoader() { // Close elf - elf_end(this->elf); + // TODO fix (this results to segfault, there is probably somethig passed to it on stack or something) + //elf_end(this->elf); // Close file close(this->fd); } -size_t ProgramLoader::get_nsec() { - return this->map.size(); -} - -std::uint32_t ProgramLoader::get_address(size_t sec) { - SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section"); - return this->phdrs[this->map[sec]].p_vaddr; +void ProgramLoader::to_memory(Memory *mem) { + // Load program to memory (just dump it byte by byte) + for (int i = 0; i < this->map.size(); i++) { + std::uint32_t base_address = this->phdrs[this->map[i]].p_vaddr; + char *f = elf_rawfile(this->elf, NULL); + size_t phdrs_i = this->map[i]; + for (unsigned y = 0; y < this->phdrs[phdrs_i].p_filesz; y++) { + mem->write_byte(base_address + y, (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + y]); + } + } } -QVector<std::uint8_t> ProgramLoader::get_data(size_t sec) { - SANITY_ASSERT(sec > this->get_nsec(), "Requesting too big section"); - QVector<std::uint8_t> d; - char *f = elf_rawfile(this->elf, NULL); - size_t phdrs_i = this->map[sec]; - for (unsigned i = 0; i < this->phdrs[phdrs_i].p_filesz; i++) { - d << (std::uint8_t) f[this->phdrs[phdrs_i].p_offset + i]; +std::uint32_t ProgramLoader::end() { + std::uint32_t last = 0; + // Go trough all sections and found out last one + for (int i = 0; i < this->map.size(); i++) { + Elf32_Phdr *phdr = &(this->phdrs[this->map[i]]); + if ((phdr->p_vaddr + phdr->p_filesz) > last) + last = phdr->p_vaddr + phdr->p_filesz; } - return d; + return last + 0x10; // We add offset so we are sure that also pipeline is empty } diff --git a/qtmips_machine/programloader.h b/qtmips_machine/programloader.h index 4d722d2..7da241c 100644 --- a/qtmips_machine/programloader.h +++ b/qtmips_machine/programloader.h @@ -6,16 +6,18 @@ #include <gelf.h> #include <cstdint> #include <qvector.h> +#include <qstring.h> +#include "memory.h" class ProgramLoader { public: - ProgramLoader(char *file); + ProgramLoader(const char *file); + ProgramLoader(QString file); ~ProgramLoader(); - size_t get_nsec(); // Returns number of loadable sections - std::uint32_t get_address(size_t sec); // Get target address for given section - QVector<std::uint8_t> get_data(size_t sec); // Returns bytes of given section + void to_memory(Memory *mem); // Writes all loaded sections to memory + std::uint32_t end(); // Return address after which there is no more code for sure private: int fd; Elf *elf; diff --git a/qtmips_machine/programmemory.h b/qtmips_machine/programmemory.h deleted file mode 100644 index 14187c5..0000000 --- a/qtmips_machine/programmemory.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef PROGRAMMEMORY_H -#define PROGRAMMEMORY_H - -#include <vector> -#include "programloader.h" -#include "memory.h" -#include "instruction.h" - -class ProgramMemory { -public: - ProgramMemory(MemoryAccess *memory); - - void load(ProgramLoader *l); - Instruction *at(std::uint32_t address); // return instruction isntance for given address - -private: - MemoryAccess *memory; - Instruction *decode_r(std::uint32_t dt); - Instruction *decode_j(std::uint32_t dt, std::uint8_t opcode); - Instruction *decode_i(std::uint32_t dt, std::uint8_t opcode); -}; - -#endif // PROGRAMMEMORY_H diff --git a/qtmips_machine/qtmips_machine.pro b/qtmips_machine/qtmips_machine.pro index 213727d..a1edcaa 100644 --- a/qtmips_machine/qtmips_machine.pro +++ b/qtmips_machine/qtmips_machine.pro @@ -7,6 +7,7 @@ TEMPLATE = lib LIBS += -lelf QMAKE_CXXFLAGS += -std=c++0x +QMAKE_CXXFLAGS += -ggdb DEFINES += QTMIPS_MACHINE_LIBRARY DEFINES += QT_DEPRECATED_WARNINGS @@ -19,13 +20,9 @@ SOURCES += \ instruction.cpp \ registers.cpp \ programloader.cpp \ - programmemory.cpp \ - instructions/arithmetic.cpp \ - instructions/loadstore.cpp \ - instructions/shift.cpp \ - instructions/nop.cpp \ - instructions/jumpbranch.cpp \ - cache.cpp + cache.cpp \ + alu.cpp \ + machineconfig.cpp HEADERS += \ qtmipsmachine.h \ @@ -41,4 +38,6 @@ HEADERS += \ instructions/shift.h \ instructions/nop.h \ instructions/jumpbranch.h \ - cache.h + cache.h \ + alu.h \ + machineconfig.h diff --git a/qtmips_machine/qtmipsexception.cpp b/qtmips_machine/qtmipsexception.cpp index 4cec6b0..02193cb 100644 --- a/qtmips_machine/qtmipsexception.cpp +++ b/qtmips_machine/qtmipsexception.cpp @@ -19,7 +19,7 @@ const char *QtMipsException::what() const throw() { QString QtMipsException::msg(bool pos) const { QString message; if (pos) - message += QString("(") + QString(this->file) + QString(":") + QString(this->line) + QString(") "); + message += QString("(") + QString(this->file) + QString(":") + QString::number(this->line) + QString(") "); message += this->reason; if (!this->ext.isEmpty()) { message += QString(": "); @@ -44,6 +44,16 @@ QtMipsExceptionUnsupportedInstruction::QtMipsExceptionUnsupportedInstruction(QTM return; } +QtMipsExceptionUnsupportedAluOperation::QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON) + : QtMipsExceptionRuntime(reason, ext, file, line) { + return; +} + +QtMipsExceptionOverflow::QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON) + : QtMipsExceptionRuntime(reason, ext, file, line) { + return; +} + QtMipsExceptionUnalignedJump::QtMipsExceptionUnalignedJump(QTMIPS_ARGS_COMMON) : QtMipsExceptionRuntime(reason, ext, file, line) { return; diff --git a/qtmips_machine/qtmipsexception.h b/qtmips_machine/qtmipsexception.h index 81895d6..b81f748 100644 --- a/qtmips_machine/qtmipsexception.h +++ b/qtmips_machine/qtmipsexception.h @@ -37,6 +37,20 @@ public: QtMipsExceptionUnsupportedInstruction(QTMIPS_ARGS_COMMON); }; +// Decoded ALU operation is not supported +// This is basically same exception as QtMipsExceptionUnsupportedInstruction but it is emmited from ALU when executed and not before that. +class QtMipsExceptionUnsupportedAluOperation : public QtMipsExceptionRuntime { +public: + QtMipsExceptionUnsupportedAluOperation(QTMIPS_ARGS_COMMON); +}; + +// Integer operation resulted to overflow (or underflow as we are working with unsigned values) +// This is for sure caused by program it self. +class QtMipsExceptionOverflow : public QtMipsExceptionRuntime { +public: + QtMipsExceptionOverflow(QTMIPS_ARGS_COMMON); +}; + // Instruction is jumping to unaligned address (ADDR%4!=0) // This can be caused by bug or by user program as it can be jumping relative to register // This shouldn't be happening with non-register jumps as those should be verified by compiler diff --git a/qtmips_machine/qtmipsmachine.cpp b/qtmips_machine/qtmipsmachine.cpp index 0fc207c..3d5ce98 100644 --- a/qtmips_machine/qtmipsmachine.cpp +++ b/qtmips_machine/qtmipsmachine.cpp @@ -1,5 +1,70 @@ #include "qtmipsmachine.h" +#include "programloader.h" -QtMipsMachine::QtMipsMachine(char *file) { - this->loader = new ProgramLoader(file); +QtMipsMachine::QtMipsMachine(const MachineConfig &cc) { + ProgramLoader program(cc.elf()); + + regs = new Registers(); + mem = new Memory(); + + program.to_memory(mem); + program_end = program.end(); + + MemoryAccess *coremem; + switch (cc.cache()) { + case MachineConfig::CCT_NONE: + cch = nullptr; + coremem = mem; + break; + case MachineConfig::CCT_ASSOCIATIVE: + // TODO + coremem = mem; + //coremem = cch = new CacheAssociative(); + break; + } + + // TODO pipelined + cr = new CoreSingle(regs, coremem); + + run_speed = 1; + run_t = new QTimer(this); + connect(run_t, SIGNAL(timeout()), this, SLOT(step())); +} + +void QtMipsMachine::set_speed(unsigned val) { + run_speed = val; +} + +const Registers *QtMipsMachine::registers() { + return regs; +} + +const Memory *QtMipsMachine::memory() { + return mem; +} + +const Cache *QtMipsMachine::cache() { + return cch; +} + +const Core *QtMipsMachine::core() { + return cr; +} + +void QtMipsMachine::play() { + run_t->start(run_speed); +} + +void QtMipsMachine::pause() { + run_t->stop(); +} + +void QtMipsMachine::step() { + cr->step(); + if (regs->read_pc() >= program_end) + emit program_exit(); +} + +void QtMipsMachine::restart() { + // TODO } diff --git a/qtmips_machine/qtmipsmachine.h b/qtmips_machine/qtmipsmachine.h index af981b3..829e571 100644 --- a/qtmips_machine/qtmipsmachine.h +++ b/qtmips_machine/qtmipsmachine.h @@ -2,24 +2,47 @@ #define QTMIPSMACHINE_H #include <QObject> - +#include <QTimer> +#include <cstdint> #include "qtmipsexception.h" -#include "programloader.h" +#include "machineconfig.h" +#include "registers.h" +#include "memory.h" #include "core.h" -// TODO piplined core +#include "cache.h" -class QtMipsMachine : QObject { +class QtMipsMachine : public QObject { Q_OBJECT public: - QtMipsMachine(char *file); + QtMipsMachine(const MachineConfig &cc); + + void set_speed(unsigned); + const Registers *registers(); + const Memory *memory(); + const Cache *cache(); + const Core *core(); + +public slots: // TODO handle speed void play(); void pause(); void step(); void restart(); + +signals: + void program_exit(); + private: - ProgramLoader *loader; + Registers *regs; + Memory *mem; + Cache *cch; + Core *cr; + + unsigned run_speed; + QTimer *run_t; + + std::uint32_t program_end; }; #endif // QTMIPSMACHINE_H diff --git a/qtmips_machine/registers.cpp b/qtmips_machine/registers.cpp index 837f403..fa984fb 100644 --- a/qtmips_machine/registers.cpp +++ b/qtmips_machine/registers.cpp @@ -14,12 +14,21 @@ Registers::Registers() { this->hi = this->lo = 0; } -std::uint32_t Registers::read_pc() { +Registers::Registers(const Registers *orig) : Registers() { + this->pc = orig->read_pc(); + for (int i = 0; i < 31; i++) + this->gp[i] = orig->read_gp(i); + this->lo = orig->read_hi_lo(false); + this->hi = orig->read_hi_lo(true); +} + +std::uint32_t Registers::read_pc() const { return this->pc; } std::uint32_t Registers::pc_inc() { this->pc += 4; + emit pc_update(this->pc); return this->pc; } @@ -27,6 +36,7 @@ std::uint32_t Registers::pc_jmp(std::int32_t offset) { if (offset % 4) throw QTMIPS_EXCEPTION(UnalignedJump, "Trying to jump by unaligned offset", QString::number(offset, 16)); this->pc += offset; + emit pc_update(this->pc); return this->pc; } @@ -36,7 +46,7 @@ void Registers::pc_abs_jmp(std::uint32_t address) { this->pc = address; } -std::uint32_t Registers::read_gp(std::uint8_t i) { +std::uint32_t Registers::read_gp(std::uint8_t i) const { SANITY_ASSERT(i < 32, QString("Trying to read from register ") + QString(i)); if (!i) // $0 always reads as 0 return 0; @@ -50,7 +60,7 @@ void Registers::write_gp(std::uint8_t i, std::uint32_t value) { this->gp[i - 1] = value; } -std::uint32_t Registers::read_hi_lo(bool hi) { +std::uint32_t Registers::read_hi_lo(bool hi) const { if (hi) return this->hi; else @@ -63,3 +73,16 @@ void Registers::write_hi_lo(bool hi, std::uint32_t value) { else this->lo = value; } + +bool Registers::operator ==(const Registers &c) const { + if (read_pc() != c.read_pc()) + return false; + for (int i = 0; i < 31; i++) + if (read_gp(i) != c.read_gp(i)) + return false; + if (read_hi_lo(false) != c.read_hi_lo(false)) + return false; + if (read_hi_lo(true) != c.read_hi_lo(true)) + return false; + return true; +} diff --git a/qtmips_machine/registers.h b/qtmips_machine/registers.h index a550f4a..905a212 100644 --- a/qtmips_machine/registers.h +++ b/qtmips_machine/registers.h @@ -8,18 +8,22 @@ class Registers : public QObject { Q_OBJECT public: Registers(); + Registers(const Registers*); - std::uint32_t read_pc(); // Return current value of program counter + std::uint32_t read_pc() const; // Return current value of program counter std::uint32_t pc_inc(); // Increment program counter by four bytes std::uint32_t pc_jmp(std::int32_t offset); // Relative jump from current location in program counter void pc_abs_jmp(std::uint32_t address); // Absolute jump in program counter (write to pc) - std::uint32_t read_gp(std::uint8_t i); // Read general-purpose register + std::uint32_t read_gp(std::uint8_t i) const; // Read general-purpose register void write_gp(std::uint8_t i, std::uint32_t value); // Write general-purpose register - std::uint32_t read_hi_lo(bool hi); // true - read HI / false - read LO + std::uint32_t read_hi_lo(bool hi) const; // true - read HI / false - read LO void write_hi_lo(bool hi, std::uint32_t value); + bool operator ==(const Registers &c) const; + signals: + void pc_update(std::uint32_t val); // TODO signals private: diff --git a/qtmips_machine/tests/testalu.cpp b/qtmips_machine/tests/testalu.cpp index e69de29..37accdf 100644 --- a/qtmips_machine/tests/testalu.cpp +++ b/qtmips_machine/tests/testalu.cpp @@ -0,0 +1,91 @@ +#include "tst_machine.h" +#include "alu.h" +#include "qtmipsexception.h" + +void MachineTests::alu_data() { + QTest::addColumn<std::uint8_t>("op"); + QTest::addColumn<std::uint32_t>("s"); + QTest::addColumn<std::uint32_t>("t"); + QTest::addColumn<std::uint8_t>("sa"); + QTest::addColumn<std::uint32_t>("res"); + + // TODO SLL-SRAV + QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)90; + QTest::newRow("ADDU") << (std::uint8_t)ALU_OP_ADDU \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)90; + QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ + << (std::uint32_t)66 \ + << (std::uint32_t)24 \ + << (std::uint8_t)0 \ + << (std::uint32_t)42; + QTest::newRow("SUBU") << (std::uint8_t)ALU_OP_SUBU \ + << (std::uint32_t)24 \ + << (std::uint32_t)66 \ + << (std::uint8_t)0 \ + << (std::uint32_t)-42; + QTest::newRow("AND") << (std::uint8_t)ALU_OP_AND \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0x201; + QTest::newRow("OR") << (std::uint8_t)ALU_OP_OR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xE83; + QTest::newRow("XOR") << (std::uint8_t)ALU_OP_XOR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xC82; + QTest::newRow("NOR") << (std::uint8_t)ALU_OP_NOR \ + << (std::uint32_t)0xA81 \ + << (std::uint32_t)0x603 \ + << (std::uint8_t)0 \ + << (std::uint32_t)0xFFFFF17C; + // TODO SLT-SLTU +} + +void MachineTests::alu() { + QFETCH(std::uint8_t, op); + QFETCH(std::uint32_t, s); + QFETCH(std::uint32_t, t); + QFETCH(std::uint8_t, sa); + QFETCH(std::uint32_t, res); + + QCOMPARE(alu_operate((enum AluOp)op, s , t, sa), res); +} + +void MachineTests::alu_except_data() { + QTest::addColumn<std::uint8_t>("op"); + QTest::addColumn<std::uint32_t>("s"); + QTest::addColumn<std::uint32_t>("t"); + // Note no sa as shift unstruction has no exceptions + + QTest::newRow("ADD") << (std::uint8_t)ALU_OP_ADD \ + << (std::uint32_t)0x8fffffff \ + << (std::uint32_t)0x90000000; + QTest::newRow("SUB") << (std::uint8_t)ALU_OP_SUB \ + << (std::uint32_t)3 \ + << (std::uint32_t)4; + // Just test that we can throw unsupported ALU operation + QTest::newRow("?") << (std::uint8_t)ALU_OP_LAST \ + << (std::uint32_t)0 \ + << (std::uint32_t)0; +} + +void MachineTests::alu_except() { + QFETCH(std::uint8_t, op); + QFETCH(std::uint32_t, s); + QFETCH(std::uint32_t, t); + + // Only runtime exception is expected as any other exception is a bug + QVERIFY_EXCEPTION_THROWN(alu_operate((enum AluOp)op, s , t, 0), QtMipsExceptionRuntime); +} diff --git a/qtmips_machine/tests/testcore.cpp b/qtmips_machine/tests/testcore.cpp index e69de29..bbf8086 100644 --- a/qtmips_machine/tests/testcore.cpp +++ b/qtmips_machine/tests/testcore.cpp @@ -0,0 +1,35 @@ +#include "tst_machine.h" +#include "core.h" + +void MachineTests::core_regs_data() { + /* + QTest::addColumn<Instruction>("i"); + QTest::addColumn<Registers>("init"); + QTest::addColumn<Registers>("res"); + + // Test arithmetic instructions + { + Registers regs_init(); + regs_init.write_gp(24, 12); + regs_init.write_gp(25, 24); + Registers regs_res(®s_init); + regs_res.write_gp(26, 36); + QTest::newRow("ADD") << Instruction(0, 24, 25, 26, 0, 32) \ + << regs_init \ + << regs_res; + } + */ + // TODO test other operations +} + +void MachineTests::core_regs() { + +} + +void MachineTests::core_mem_data() { + +} + +void MachineTests::core_mem() { + +} diff --git a/qtmips_machine/tests/testinstruction.cpp b/qtmips_machine/tests/testinstruction.cpp index e69de29..4efedac 100644 --- a/qtmips_machine/tests/testinstruction.cpp +++ b/qtmips_machine/tests/testinstruction.cpp @@ -0,0 +1,25 @@ +#include "tst_machine.h" +#include "instruction.h" + +// Test that we are correctly encoding instructions in constructor +void MachineTests::instruction() { + QCOMPARE(Instruction(0x00), Instruction(0,0)); + QCOMPARE(Instruction(0x4000002), Instruction(1, 2)); + // QCOMPARE(Instruction(0x4000002), Instruction(1, 2, 3, 4)); + // TODO other combinations +} + +// Test that we are correctly decoding instruction fields +void MachineTests::instruction_access() { + Instruction i(0xffffffff); + + QCOMPARE(i.data(), (std::uint32_t) 0xffffffff); + QCOMPARE(i.opcode(), (std::uint8_t) 0x3f); + QCOMPARE(i.rs(), (std::uint8_t) 0x1f); + QCOMPARE(i.rt(), (std::uint8_t) 0x1f); + QCOMPARE(i.rd(), (std::uint8_t) 0x1f); + QCOMPARE(i.shamt(), (std::uint8_t) 0x1f); + QCOMPARE(i.funct(), (std::uint8_t) 0x3f); + QCOMPARE(i.immediate(), (std::uint16_t) 0xffff); + QCOMPARE(i.address(), (std::uint32_t) 0x3ffffff); +} diff --git a/qtmips_machine/tests/testmemory.cpp b/qtmips_machine/tests/testmemory.cpp index eac7dd6..e450231 100644 --- a/qtmips_machine/tests/testmemory.cpp +++ b/qtmips_machine/tests/testmemory.cpp @@ -13,21 +13,21 @@ void MachineTests::memory_data() { void MachineTests::memory() { Memory m; - QFETCH(std::uint32_t, address); + QFETCH(std::uint32_t, address); - // Uninitialize memory should read as zero - QCOMPARE(m.read_byte(address), (std::uint8_t)0); - QCOMPARE(m.read_hword(address), (std::uint16_t)0); - QCOMPARE(m.read_word(address), (std::uint32_t)0); - // Just a byte - m.write_byte(address, 0x42); - QCOMPARE(m.read_byte(address), (std::uint8_t)0x42); - // Half word - m.write_hword(address, 0x4243); - QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243); - // Word - m.write_word(address, 0x42434445); - QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445); + // Uninitialize memory should read as zero + QCOMPARE(m.read_byte(address), (std::uint8_t)0); + QCOMPARE(m.read_hword(address), (std::uint16_t)0); + QCOMPARE(m.read_word(address), (std::uint32_t)0); + // Just a byte + m.write_byte(address, 0x42); + QCOMPARE(m.read_byte(address), (std::uint8_t)0x42); + // Half word + m.write_hword(address, 0x4243); + QCOMPARE(m.read_hword(address), (std::uint16_t)0x4243); + // Word + m.write_word(address, 0x42434445); + QCOMPARE(m.read_word(address), (std::uint32_t)0x42434445); } void MachineTests::memory_section_data() { @@ -40,24 +40,24 @@ void MachineTests::memory_section_data() { } void MachineTests::memory_section() { - Memory m; + Memory m; - QFETCH(std::uint32_t, address); + QFETCH(std::uint32_t, address); - // First section shouldn't exists - QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); - // Create section - MemorySection *s = m.get_section(address, true); - QVERIFY(s != nullptr); + // First section shouldn't exists + QCOMPARE(m.get_section(address, false), (MemorySection*)nullptr); + // Create section + MemorySection *s = m.get_section(address, true); + QVERIFY(s != nullptr); - // Write some data to memory - m.write_byte(address, 0x42); - // Read it trough section (mask bits outside of the memory section) - QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42); - // Write some other data trough section - s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66); - // Read trough memory - QCOMPARE(m.read_byte(address), (std::uint8_t)0x66); + // Write some data to memory + m.write_byte(address, 0x42); + // Read it trough section (mask bits outside of the memory section) + QCOMPARE(s->read_byte(address & ((1 << MEMORY_SECTION_BITS) - 1)), (std::uint8_t)0x42); + // Write some other data trough section + s->write_byte(address & ((1 << MEMORY_SECTION_BITS) - 1), 0x66); + // Read trough memory + QCOMPARE(m.read_byte(address), (std::uint8_t)0x66); } void MachineTests::memory_endian() { diff --git a/qtmips_machine/tests/testprogramloader.cpp b/qtmips_machine/tests/testprogramloader.cpp index e69de29..7ff1c54 100644 --- a/qtmips_machine/tests/testprogramloader.cpp +++ b/qtmips_machine/tests/testprogramloader.cpp @@ -0,0 +1,20 @@ +#include <iostream> +#include "tst_machine.h" +#include "programloader.h" +#include "instruction.h" + +// This is common program start (initial value of program counter) +#define PC_INIT 0x80020000 + +void MachineTests::program_loader() { + ProgramLoader pl("data"); + Memory m; + pl.to_memory(&m); + + // addi $1, $0, 6 + QCOMPARE(Instruction(m.read_word(PC_INIT)), Instruction(8, 0, 1, 6)); + // j 80020000 + // TODO wtf to je relativni skok asi tady + //QCOMPARE(Instruction(m.read_word(PC_INIT + 4)), Instruction(2, PC_INIT)); + // TODO add some more code to data and do more compares (for example more sections) +} diff --git a/qtmips_machine/tests/tests.pro b/qtmips_machine/tests/tests.pro index 287d1e8..ffe75b7 100644 --- a/qtmips_machine/tests/tests.pro +++ b/qtmips_machine/tests/tests.pro @@ -18,8 +18,10 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += tst_machine.cpp \ testmemory.cpp \ testregisters.cpp \ - testprogrammemory.cpp \ - testinstruction.cpp + testprogramloader.cpp \ + testinstruction.cpp \ + testalu.cpp \ + testcore.cpp HEADERS += tst_machine.h diff --git a/qtmips_machine/tests/tst_machine.h b/qtmips_machine/tests/tst_machine.h index c05e8ad..da8082a 100644 --- a/qtmips_machine/tests/tst_machine.h +++ b/qtmips_machine/tests/tst_machine.h @@ -17,14 +17,21 @@ private Q_SLOTS: void memory_section(); void memory_section_data(); void memory_endian(); - // ProgramMemory - void program_memory(); - void program_memory_data(); + // Program loader + void program_loader(); // Instruction - void instruction_arithmetic(); - void instruction_arithmetic_data(); - void instruction_arithmetic_immediate(); - void instruction_arithmetic_immediate_data(); + void instruction(); + void instruction_access(); + // Alu + void alu(); + void alu_data(); + void alu_except(); + void alu_except_data(); + // Core + void core_regs(); + void core_regs_data(); + void core_mem(); + void core_mem_data(); }; #endif // TST_MACHINE_H diff --git a/tests/machine-unit-tests/Makefile b/tests/machine-unit-tests/Makefile new file mode 100644 index 0000000..18f7932 --- /dev/null +++ b/tests/machine-unit-tests/Makefile @@ -0,0 +1,5 @@ +include ../test.mk + +SRC_data = data.S + +$(eval $(call MIPS_ELF,data)) diff --git a/tests/machine-unit-tests/data.S b/tests/machine-unit-tests/data.S new file mode 100644 index 0000000..d61d81d --- /dev/null +++ b/tests/machine-unit-tests/data.S @@ -0,0 +1,6 @@ +.text +.globl _start + +_start: + addi $1, $0, 6 + j _start diff --git a/tests/machine-unit-tests/test.sh b/tests/machine-unit-tests/test.sh index 2f06965..8378eff 100755 --- a/tests/machine-unit-tests/test.sh +++ b/tests/machine-unit-tests/test.sh @@ -9,5 +9,7 @@ qtmips_make sub-qtmips_machine-tests # Build test data mips_make_test +cd "$TEST_DIR" + # Run unit tests qtmips_run qtmips_machine/tests/tst_machine || echo_fail "Test $TEST_NAME failed!" diff --git a/tests/t-registers/Makefile b/tests/t-registers/Makefile new file mode 100644 index 0000000..b8278d1 --- /dev/null +++ b/tests/t-registers/Makefile @@ -0,0 +1,5 @@ +MAKEFLAGS += --no-builtin-rules + +SRC = registers.S + +include ../test.mk diff --git a/tests/test.sh b/tests/test.sh index aad0f5a..34b42e3 100644 --- a/tests/test.sh +++ b/tests/test.sh @@ -66,10 +66,11 @@ TEST_DIR="$TEST_DIR_ROOT/$TEST_NAME" qtmips_make() { mkdir -p "$BUILD_DIR" - pushd "$BUILD_DIR" >/dev/null + local ORIG="$(pwd)" + cd "$BUILD_DIR" /usr/lib64/qt5/bin/qmake "$PROJECT_ROOT" || echo_fail "QtMips qmake failed!" make "$@" || echo_fail "QtMips build failed! (target: $@)" - popd >/dev/null + cd "$ORIG" } qtmips_run() { |