diff --git a/.gitignore b/.gitignore index 40c0eede..d68f88f0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ compile_commands.json /src/project_info.h # Local settings template .dev-config.mk +/cmake-build-debug-wsl/ +/cmake-build-release-wsl/ diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 00000000..95508dff --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,641 @@ + + + + + + EnvironmentId + {44caa9ee-f1f6-427f-885b-a0ab99546cf1} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + Unchecked + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 6 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {0ebd45e9-4914-47c2-9de3-6dca8128b71a} + 1 + 0 + 10 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/tux/qtrvsim/build/Desktop-Debug + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/tux/qtrvsim/build/Desktop-Release + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/tux/qtrvsim/build/Desktop-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + /home/tux/qtrvsim/build/Desktop-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + /home/tux/qtrvsim/build/Desktop-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + alu_test + CMakeProjectManager.CMakeRunConfiguration.alu_test + alu_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + cache_test + CMakeProjectManager.CMakeRunConfiguration.cache_test + cache_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + gui + CMakeProjectManager.CMakeRunConfiguration.gui + gui + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + cli + CMakeProjectManager.CMakeRunConfiguration.cli + cli + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + core_test + CMakeProjectManager.CMakeRunConfiguration.core_test + core_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + instruction_test + CMakeProjectManager.CMakeRunConfiguration.instruction_test + instruction_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + memory_test + CMakeProjectManager.CMakeRunConfiguration.memory_test + memory_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + mulh64_test + CMakeProjectManager.CMakeRunConfiguration.mulh64_test + mulh64_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + program_loader_test + CMakeProjectManager.CMakeRunConfiguration.program_loader_test + program_loader_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + registers_test + CMakeProjectManager.CMakeRunConfiguration.registers_test + registers_test + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + svgscene-example + CMakeProjectManager.CMakeRunConfiguration.svgscene-example + svgscene-example + false + true + true + true + /home/tux/qtrvsim/build/Desktop-Release/target + + 11 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/src/assembler/simpleasm.cpp b/src/assembler/simpleasm.cpp index 7f6d38c8..93470ac0 100644 --- a/src/assembler/simpleasm.cpp +++ b/src/assembler/simpleasm.cpp @@ -69,10 +69,12 @@ void SimpleAsm::clear() { void SimpleAsm::setup( machine::FrontendMemory *mem, + machine::MachineConfig config, SymbolTableDb *symtab, machine::Address address, machine::Xlen xlen) { this->mem = mem; + this->config = config; this->symtab = symtab; this->address = address; this->symtab->setSymbol("XLEN", static_cast(xlen), sizeof(uint64_t)); @@ -286,7 +288,12 @@ bool SimpleAsm::process_line( if (error_ptr != nullptr) { *error_ptr = error; } return false; } - address = machine::Address(value); + qint64 offset = qint64(value); + qint64 base = 0; + if (config.get_vm_enabled() && config.get_vm_mode() == machine::MachineConfig::VM_SV32) { + base = config.get_va_base_addr(); + } + address = machine::Address(base + offset); return true; } if ((op == ".space") || (op == ".skip")) { diff --git a/src/assembler/simpleasm.h b/src/assembler/simpleasm.h index be134cd4..fd0f9d9c 100644 --- a/src/assembler/simpleasm.h +++ b/src/assembler/simpleasm.h @@ -53,6 +53,7 @@ class SimpleAsm : public QObject { void clear(); void setup( machine::FrontendMemory *mem, + machine::MachineConfig config, SymbolTableDb *symtab, machine::Address address, machine::Xlen xlen); @@ -79,6 +80,7 @@ class SimpleAsm : public QObject { private: QStringList include_stack; machine::FrontendMemory *mem {}; + machine::MachineConfig config {}; machine::RelocExpressionList reloc; }; diff --git a/src/cli/main.cpp b/src/cli/main.cpp index b56af019..bc28fef0 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -3,8 +3,8 @@ #include "common/logging.h" #include "common/logging_format_colors.h" #include "machine/machineconfig.h" -#include "os_emulation/ossyscall.h" #include "msgreport.h" +#include "os_emulation/ossyscall.h" #include "reporter.h" #include "tracer.h" @@ -83,9 +83,12 @@ void create_parser(QCommandLineParser &p) { p.addOption( { { "serial-out", "serout" }, "File connected to the serial port output.", "FNAME" }); p.addOption({ { "os-emulation", "osemu" }, "Operating system emulation." }); - p.addOption({ { "std-out", "stdout" }, "File connected to the syscall standard output.", "FNAME" }); - p.addOption({ { "os-fs-root", "osfsroot" }, "Emulated system root/prefix for opened files", "DIR" }); - p.addOption({ { "isa-variant", "isavariant" }, "Instruction set to emulate (default RV32IMA)", "STR" }); + p.addOption( + { { "std-out", "stdout" }, "File connected to the syscall standard output.", "FNAME" }); + p.addOption( + { { "os-fs-root", "osfsroot" }, "Emulated system root/prefix for opened files", "DIR" }); + p.addOption( + { { "isa-variant", "isavariant" }, "Instruction set to emulate (default RV32IMA)", "STR" }); p.addOption({ "cycle-limit", "Limit execution to specified maximum clock cycles", "NUMBER" }); } @@ -202,8 +205,7 @@ void configure_machine(QCommandLineParser &parser, MachineConfig &config) { int siz = parser.values("os-fs-root").size(); if (siz >= 1) { QString osemu_fs_root = parser.values("os-fs-root").at(siz - 1); - if (osemu_fs_root.length() > 0) - config.set_osemu_fs_root(osemu_fs_root); + if (osemu_fs_root.length() > 0) config.set_osemu_fs_root(osemu_fs_root); } siz = parser.values("isa-variant").size(); for (int i = 0; i < siz; i++) { @@ -211,10 +213,10 @@ void configure_machine(QCommandLineParser &parser, MachineConfig &config) { bool first = true; bool subtract = false; QString isa_str = parser.values("isa-variant").at(i).toUpper(); - if (isa_str.startsWith ("RV32")) { + if (isa_str.startsWith("RV32")) { config.set_simulated_xlen(machine::Xlen::_32); pos = 4; - } else if (isa_str.startsWith ("RV64")) { + } else if (isa_str.startsWith("RV64")) { config.set_simulated_xlen(machine::Xlen::_64); pos = 4; } @@ -228,10 +230,10 @@ void configure_machine(QCommandLineParser &parser, MachineConfig &config) { continue; } auto flag = machine::ConfigIsaWord::byChar(ch); - if (flag.isEmpty()) - continue; + if (flag.isEmpty()) continue; if (first) - config.modify_isa_word(~machine::ConfigIsaWord::empty(), machine::ConfigIsaWord::empty()); + config.modify_isa_word( + ~machine::ConfigIsaWord::empty(), machine::ConfigIsaWord::empty()); if (subtract) config.modify_isa_word(flag, machine::ConfigIsaWord::empty()); else @@ -253,7 +255,7 @@ void configure_tracer(QCommandLineParser &p, Tracer &tr) { if (p.isSet("trace-gp")) { tr.trace_regs_gp = true; } QStringList gps = p.values("trace-gp"); - for (const auto & gp : gps) { + for (const auto &gp : gps) { if (gp == "*") { tr.regs_to_trace.fill(true); } else { @@ -262,8 +264,7 @@ void configure_tracer(QCommandLineParser &p, Tracer &tr) { if (res && num <= machine::REGISTER_COUNT) { tr.regs_to_trace.at(num) = true; } else { - fprintf( - stderr, "Unknown register number given for trace-gp: %s\n", qPrintable(gp)); + fprintf(stderr, "Unknown register number given for trace-gp: %s\n", qPrintable(gp)); exit(EXIT_FAILURE); } } @@ -277,8 +278,7 @@ void configure_tracer(QCommandLineParser &p, Tracer &tr) { bool ok; tr.cycle_limit = clim.at(clim.size() - 1).toLong(&ok); if (!ok) { - fprintf( - stderr, "Cycle limit parse error\n"); + fprintf(stderr, "Cycle limit parse error\n"); exit(EXIT_FAILURE); } } @@ -296,7 +296,7 @@ void configure_reporter(QCommandLineParser &p, Reporter &r, const SymbolTable *s if (p.isSet("dump-cycles")) { r.enable_cycles_reporting(); } QStringList fail = p.values("fail-match"); - for (const auto & i : fail) { + for (const auto &i : fail) { for (int y = 0; y < i.length(); y++) { enum Reporter::FailReason reason; switch (tolower(i.toStdString()[y])) { @@ -413,8 +413,9 @@ void configure_osemu(QCommandLineParser &p, MachineConfig &config, Machine *mach exit(EXIT_FAILURE); } } - const static machine::ExceptionCause ecall_variats[] = {machine::EXCAUSE_ECALL_ANY, - machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S, machine::EXCAUSE_ECALL_U}; + const static machine::ExceptionCause ecall_variats[] + = { machine::EXCAUSE_ECALL_ANY, machine::EXCAUSE_ECALL_M, machine::EXCAUSE_ECALL_S, + machine::EXCAUSE_ECALL_U }; if (config.osemu_enable()) { auto *osemu_handler = new osemu::OsSyscallExceptionHandler( @@ -422,8 +423,8 @@ void configure_osemu(QCommandLineParser &p, MachineConfig &config, Machine *mach config.osemu_fs_root()); if (std_out) { machine::Machine::connect( - osemu_handler, &osemu::OsSyscallExceptionHandler::char_written, - std_out, QOverload::of(&CharIOHandler::writeByte)); + osemu_handler, &osemu::OsSyscallExceptionHandler::char_written, std_out, + QOverload::of(&CharIOHandler::writeByte)); } /*connect( osemu_handler, &osemu::OsSyscallExceptionHandler::rx_byte_pool, terminal, @@ -468,9 +469,7 @@ void load_ranges(Machine &machine, const QStringList &ranges) { Address addr = start; for (std::string line; getline(in, line);) { size_t end_pos = line.find_last_not_of(" \t\n"); - if (std::string::npos == end_pos) { - continue; - } + if (std::string::npos == end_pos) { continue; } size_t start_pos = line.find_first_not_of(" \t\n"); line = line.substr(0, end_pos + 1); @@ -498,11 +497,9 @@ bool assemble(Machine &machine, MsgReport &msgrep, const QString &filename) { SimpleAsm::connect(&assembler, &SimpleAsm::report_message, &msgrep, &MsgReport::report_message); - assembler.setup(mem, &symbol_table_db, 0x00000200_addr, machine.core()->get_xlen()); + assembler.setup(mem, TODO, &symbol_table_db, 0x00000200_addr, machine.core()->get_xlen()); - if (!assembler.process_file(filename)) { - return false; - } + if (!assembler.process_file(filename)) { return false; } return assembler.finish(); } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 636f1f72..5c7310c5 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -129,7 +129,9 @@ add_executable(gui ${gui_SOURCES} ${gui_HEADERS} ${gui_UI} - ${gui_RESOURCES}) + ${gui_RESOURCES} + dialogs/new/bigslider.h + dialogs/new/bigslider.cpp) target_include_directories(gui PUBLIC . windows/coreview) target_link_libraries(gui PRIVATE ${QtLib}::Core ${QtLib}::Widgets ${QtLib}::Gui diff --git a/src/gui/dialogs/new/NewDialog.ui b/src/gui/dialogs/new/NewDialog.ui index 76b0afad..0a13f421 100644 --- a/src/gui/dialogs/new/NewDialog.ui +++ b/src/gui/dialogs/new/NewDialog.ui @@ -66,6 +66,9 @@ + + 3 + Presets and ELF File @@ -263,6 +266,9 @@ + + true + Branch Predictor @@ -656,6 +662,213 @@ + + + Virtual Memory + + + + true + + + + 10 + 10 + 471 + 341 + + + + Virtual Memory + + + true + + + true + + + + + 360 + 40 + 91 + 22 + + + + 0x80000000 + + + + + + 210 + 40 + 141 + 21 + + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + + + + 20 + 40 + 191 + 20 + + + + Virtual Memory Base Address: + + + + + true + + + + 20 + 190 + 431 + 131 + + + + Translation Lookaside Buffer (TLB) + + + false + + + + + + Number of sets: + + + + + + + 1 + + + 1024 + + + + + + + Degree of associativity: + + + + + + + 1 + + + + + + + Replacement policy: + + + + + + + + Random + + + + + Least Recently Used (LRU) + + + + + Least Frequently Used (LFU) + + + + + Pseudo Least Recently Used (PLRU) + + + + + + + + + + 20 + 90 + 431 + 81 + + + + Supervisor Address Translation and Protection (SATP) + + + + + 60 + 50 + 99 + 21 + + + + Sv32 + + + true + + + + + + 60 + 30 + 99 + 21 + + + + Bare + + + + + + 10 + 30 + 51 + 21 + + + + MODE: + + + + + Memory Timings @@ -955,6 +1168,13 @@ + + + BigSlider + QSlider +
gui/dialogs/new/bigslider.h
+
+
diff --git a/src/gui/dialogs/new/NewDialogCache.ui b/src/gui/dialogs/new/NewDialogCache.ui index 52f1586e..0b1c0e58 100644 --- a/src/gui/dialogs/new/NewDialogCache.ui +++ b/src/gui/dialogs/new/NewDialogCache.ui @@ -16,6 +16,9 @@ + + true + Enable cache diff --git a/src/gui/dialogs/new/bigslider.cpp b/src/gui/dialogs/new/bigslider.cpp new file mode 100644 index 00000000..2d66319e --- /dev/null +++ b/src/gui/dialogs/new/bigslider.cpp @@ -0,0 +1,93 @@ +#include "bigslider.h" + +#include +#include +#include +#include + +BigSlider::BigSlider(QWidget *parent) : QSlider(Qt::Horizontal, parent) { + QSlider::setRange(0, 1); + int thickness = style()->pixelMetric(QStyle::PM_SliderThickness, nullptr, this); + setMinimumHeight(thickness * 2); +} + +qint64 BigSlider::value64() const { + return m_value; +} + +void BigSlider::setRange64(qint64 min, qint64 max) { + m_min = min; + m_max = max; + if (m_value < m_min) m_value = m_min; + if (m_value > m_max) m_value = m_max; + updateGeometry(); + update(); +} + +void BigSlider::setValue64(qint64 v) { + qint64 clamped = qBound(m_min, v, m_max); + clamped = m_min + ((clamped - m_min) / m_align) * m_align; + if (clamped == m_value) return; + m_value = clamped; + update(); + emit value64Changed(m_value); +} + +int BigSlider::span() const { + QStyleOptionSlider opt; + initStyleOption(&opt); + int handleLen = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this); + return width() - handleLen; +} + +void BigSlider::paintEvent(QPaintEvent *ev) { + int s = span(); + qint64 r = m_max - m_min; + int posPx = (r > 0) ? int((m_value - m_min) * s / r) : 0; + + QSlider::setRange(0, s); + QSlider::setValue(posPx); + + QSlider::paintEvent(ev); +} + +void BigSlider::mousePressEvent(QMouseEvent *ev) { + if (ev->button() == Qt::LeftButton) { + handleMousePos(ev->pos()); + ev->accept(); + } else { + QSlider::mousePressEvent(ev); + } +} + +void BigSlider::mouseMoveEvent(QMouseEvent *ev) { + if (ev->buttons() & Qt::LeftButton) { + handleMousePos(ev->pos()); + ev->accept(); + } else { + QSlider::mouseMoveEvent(ev); + } +} + +void BigSlider::handleMousePos(const QPoint &pt) { + QStyleOptionSlider opt; + initStyleOption(&opt); + int handleLen = style()->pixelMetric(QStyle::PM_SliderLength, &opt, this); + int s = span(); + + int x = pt.x() - handleLen / 2; + x = qBound(0, x, s); + + qint64 newVal = m_min + qint64(x) * (m_max - m_min) / qMax(1, s); + newVal = m_min + ((newVal - m_min) / m_align) * m_align; + setValue64(newVal); +} + +void BigSlider::setAlignment64(qint64 a) { + m_align = qMax(1, a); + setValue64(m_value); +} + +qint64 BigSlider::alignment64() const { + return m_align; +} \ No newline at end of file diff --git a/src/gui/dialogs/new/bigslider.h b/src/gui/dialogs/new/bigslider.h new file mode 100644 index 00000000..18bc99df --- /dev/null +++ b/src/gui/dialogs/new/bigslider.h @@ -0,0 +1,38 @@ +#ifndef BIGSLIDER_H +#define BIGSLIDER_H + +#include + +// Qt widget subclass exposing 64-bit slider range/value and custom painting, +// designed for selecting a base virtual memory address from 0X00000000 up to 0XFFFFF000. +class BigSlider : public QSlider { + Q_OBJECT + +public: + explicit BigSlider(QWidget* parent = nullptr); + + qint64 value64() const; + qint64 alignment64() const; + void setRange64(qint64 min, qint64 max); + void setValue64(qint64 v); + void setAlignment64(qint64 a); + + signals: + void value64Changed(qint64 newValue); + +protected: + void paintEvent(QPaintEvent* ev) override; + void mousePressEvent(QMouseEvent* ev) override; + void mouseMoveEvent(QMouseEvent* ev) override; + +private: + qint64 m_min = 0; + qint64 m_max = 0xFFFFFFFFULL; + qint64 m_value = 0; + qint64 m_align = 0x1000; + + int span() const; + void handleMousePos(const QPoint& pt); +}; + +#endif // BIGSLIDER_H diff --git a/src/gui/dialogs/new/newdialog.cpp b/src/gui/dialogs/new/newdialog.cpp index 5fd5bae2..2f0d38bd 100644 --- a/src/gui/dialogs/new/newdialog.cpp +++ b/src/gui/dialogs/new/newdialog.cpp @@ -33,79 +33,40 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { for (int i = 0; i < ui->config_pages->count(); ++i) { QString page_id = ui->config_pages->widget(i)->objectName(); QString page_name = ui->config_pages->widget(i)->accessibleName(); - config_pages_items.append(new QTreeWidgetItem(static_cast(nullptr), - QStringList{page_name, page_id})); + config_pages_items.append(new QTreeWidgetItem( + static_cast(nullptr), QStringList { page_name, page_id })); } ui->page_select_tree->insertTopLevelItems(0, config_pages_items); - connect( - ui->page_select_tree, &QTreeWidget::currentItemChanged, - this, &NewDialog::switch2page); + connect(ui->page_select_tree, &QTreeWidget::currentItemChanged, this, &NewDialog::switch2page); + connect(ui->pushButton_example, &QAbstractButton::clicked, this, &NewDialog::create_example); + connect(ui->pushButton_start_empty, &QAbstractButton::clicked, this, &NewDialog::create_empty); + connect(ui->pushButton_load, &QAbstractButton::clicked, this, &NewDialog::create); + connect(ui->pushButton_cancel, &QAbstractButton::clicked, this, &NewDialog::cancel); + connect(ui->pushButton_browse, &QAbstractButton::clicked, this, &NewDialog::browse_elf); + connect(ui->elf_file, &QLineEdit::textChanged, this, &NewDialog::elf_change); + connect(ui->preset_no_pipeline, &QAbstractButton::toggled, this, &NewDialog::set_preset); + connect(ui->preset_no_pipeline_cache, &QAbstractButton::toggled, this, &NewDialog::set_preset); + connect(ui->preset_pipelined_bare, &QAbstractButton::toggled, this, &NewDialog::set_preset); + connect(ui->preset_pipelined, &QAbstractButton::toggled, this, &NewDialog::set_preset); connect( - ui->pushButton_example, &QAbstractButton::clicked, this, - &NewDialog::create_example); - connect( - ui->pushButton_start_empty, &QAbstractButton::clicked, this, - &NewDialog::create_empty); - connect( - ui->pushButton_load, &QAbstractButton::clicked, this, - &NewDialog::create); - connect( - ui->pushButton_cancel, &QAbstractButton::clicked, this, - &NewDialog::cancel); - connect( - ui->pushButton_browse, &QAbstractButton::clicked, this, - &NewDialog::browse_elf); - connect( - ui->elf_file, &QLineEdit::textChanged, this, &NewDialog::elf_change); - connect( - ui->preset_no_pipeline, &QAbstractButton::toggled, this, - &NewDialog::set_preset); - connect( - ui->preset_no_pipeline_cache, &QAbstractButton::toggled, this, - &NewDialog::set_preset); - connect( - ui->preset_pipelined_bare, &QAbstractButton::toggled, this, - &NewDialog::set_preset); - connect( - ui->preset_pipelined, &QAbstractButton::toggled, this, - &NewDialog::set_preset); - connect( - ui->reset_at_compile, &QAbstractButton::clicked, this, - &NewDialog::reset_at_compile_change); + ui->reset_at_compile, &QAbstractButton::clicked, this, &NewDialog::reset_at_compile_change); + connect(ui->xlen_64bit, &QAbstractButton::clicked, this, &NewDialog::xlen_64bit_change); + connect(ui->isa_atomic, &QAbstractButton::clicked, this, &NewDialog::isa_atomic_change); + connect(ui->isa_multiply, &QAbstractButton::clicked, this, &NewDialog::isa_multiply_change); + connect(ui->pipelined, &QAbstractButton::clicked, this, &NewDialog::pipelined_change); + connect(ui->delay_slot, &QAbstractButton::clicked, this, &NewDialog::delay_slot_change); + connect(ui->hazard_unit, &QGroupBox::clicked, this, &NewDialog::hazard_unit_change); + connect(ui->hazard_stall, &QAbstractButton::clicked, this, &NewDialog::hazard_unit_change); connect( - ui->xlen_64bit, &QAbstractButton::clicked, this, - &NewDialog::xlen_64bit_change); - connect( - ui->isa_atomic, &QAbstractButton::clicked, this, - &NewDialog::isa_atomic_change); - connect( - ui->isa_multiply, &QAbstractButton::clicked, this, - &NewDialog::isa_multiply_change); - connect( - ui->pipelined, &QAbstractButton::clicked, this, - &NewDialog::pipelined_change); - connect( - ui->delay_slot, &QAbstractButton::clicked, this, - &NewDialog::delay_slot_change); - connect( - ui->hazard_unit, &QGroupBox::clicked, this, - &NewDialog::hazard_unit_change); - connect( - ui->hazard_stall, &QAbstractButton::clicked, this, - &NewDialog::hazard_unit_change); - connect( - ui->hazard_stall_forward, &QAbstractButton::clicked, this, - &NewDialog::hazard_unit_change); + ui->hazard_stall_forward, &QAbstractButton::clicked, this, &NewDialog::hazard_unit_change); connect( - ui->mem_protec_exec, &QAbstractButton::clicked, this, - &NewDialog::mem_protec_exec_change); + ui->mem_protec_exec, &QAbstractButton::clicked, this, &NewDialog::mem_protec_exec_change); connect( - ui->mem_protec_write, &QAbstractButton::clicked, this, - &NewDialog::mem_protec_write_change); + ui->mem_protec_write, &QAbstractButton::clicked, this, &NewDialog::mem_protec_write_change); connect( ui->mem_time_read, QOverload::of(&QSpinBox::valueChanged), this, &NewDialog::mem_time_read_change); @@ -113,8 +74,7 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { ui->mem_time_write, QOverload::of(&QSpinBox::valueChanged), this, &NewDialog::mem_time_write_change); connect( - ui->mem_enable_burst, &QAbstractButton::clicked, this, - &NewDialog::mem_enable_burst_change); + ui->mem_enable_burst, &QAbstractButton::clicked, this, &NewDialog::mem_enable_burst_change); connect( ui->mem_time_burst, QOverload::of(&QSpinBox::valueChanged), this, &NewDialog::mem_time_burst_change); @@ -122,9 +82,7 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { ui->mem_time_level2, QOverload::of(&QSpinBox::valueChanged), this, &NewDialog::mem_time_level2_change); - connect( - ui->osemu_enable, &QAbstractButton::clicked, this, - &NewDialog::osemu_enable_change); + connect(ui->osemu_enable, &QAbstractButton::clicked, this, &NewDialog::osemu_enable_change); connect( ui->osemu_known_syscall_stop, &QAbstractButton::clicked, this, &NewDialog::osemu_known_syscall_stop_change); @@ -158,9 +116,64 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { connect( ui->slider_bp_bht_bhr_bits, &QAbstractSlider::valueChanged, this, &NewDialog::bp_bht_bhr_bits_change); - connect(ui->slider_bp_bht_addr_bits, &QAbstractSlider::valueChanged, this, + connect( + ui->slider_bp_bht_addr_bits, &QAbstractSlider::valueChanged, this, &NewDialog::bp_bht_addr_bits_change); + // Virtual Memory + connect( + ui->group_vm, QOverload::of(&QGroupBox::toggled), this, + &NewDialog::vm_enabled_change); + connect(ui->radioButton_bare, &QRadioButton::clicked, this, &NewDialog::vm_mode_changed); + connect(ui->radioButton_sv32, &QRadioButton::clicked, this, &NewDialog::vm_mode_changed); + connect( + ui->tlb_number_of_sets, QOverload::of(&QSpinBox::valueChanged), this, + &NewDialog::tlb_num_sets_changed); + connect( + ui->tlb_degree_of_associativity, QOverload::of(&QSpinBox::valueChanged), this, + &NewDialog::tlb_assoc_changed); + connect( + ui->tlb_replacement_policy, QOverload::of(&QComboBox::activated), this, + &NewDialog::tlb_policy_changed); + + ui->horizontalSlider_va_base_addr->setRange64(0, 0xFFFFFFFFULL); + + auto updateVABaseAddrEdit = [this]() { + qint64 v = ui->horizontalSlider_va_base_addr->value64(); + QString hexText = QString("0x%1").arg(v, 8, 16, QChar('0')).toUpper(); + QSignalBlocker block(ui->lineEdit_va_base_addr); + ui->lineEdit_va_base_addr->setText(hexText); + }; + + connect( + ui->horizontalSlider_va_base_addr, &BigSlider::value64Changed, this, updateVABaseAddrEdit); + + connect( + ui->horizontalSlider_va_base_addr, &BigSlider::value64Changed, this, + &NewDialog::va_base_addr_changed); + + connect(ui->lineEdit_va_base_addr, &QLineEdit::editingFinished, this, [this]() { + QString txt = ui->lineEdit_va_base_addr->text().trimmed(); + if (txt.startsWith(QLatin1String("0x"), Qt::CaseInsensitive)) txt = txt.mid(2); + bool ok = false; + quint64 v = txt.toULongLong(&ok, 16); + if (!ok) { + qint64 cur = ui->horizontalSlider_va_base_addr->value64(); + ui->lineEdit_va_base_addr->setText( + QString("0x%1").arg(cur, 8, 16, QChar('0')).toUpper()); + return; + } + + quint64 vclamped = qBound( + ui->horizontalSlider_va_base_addr->value64() /*dummy*/, v, 0xFFFFFFFFULL); + + qint64 align = ui->horizontalSlider_va_base_addr->alignment64(); + vclamped = (vclamped / align) * align; + ui->horizontalSlider_va_base_addr->setValue64(static_cast(vclamped)); + }); + + updateVABaseAddrEdit(); + cache_handler_d = new NewDialogCacheHandler(this, ui_cache_d.data()); cache_handler_p = new NewDialogCacheHandler(this, ui_cache_p.data()); cache_handler_l2 = new NewDialogCacheHandler(this, ui_cache_l2.data()); @@ -177,8 +190,8 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { void NewDialog::switch2page(QTreeWidgetItem *current, QTreeWidgetItem *previous) { (void)previous; - QWidget *page = ui->config_pages->findChild(current->text(1), - Qt::FindDirectChildrenOnly); + QWidget *page + = ui->config_pages->findChild(current->text(1), Qt::FindDirectChildrenOnly); if (page != nullptr) { ui->config_pages->setCurrentWidget(page); ui->config_page_title->setText(current->text(0)); @@ -196,9 +209,7 @@ void NewDialog::closeEvent(QCloseEvent *) { load_settings(); // Reset from settings // Close the main window if not already configured auto *prnt = (MainWindow *)parent(); - if (!prnt->configured()) { - prnt->close(); - } + if (!prnt->configured()) { prnt->close(); } } void NewDialog::cancel() { @@ -285,7 +296,7 @@ void NewDialog::xlen_64bit_change(bool val) { } void NewDialog::isa_atomic_change(bool val) { - auto isa_mask = machine::ConfigIsaWord::byChar('A'); + auto isa_mask = machine::ConfigIsaWord::byChar('A'); if (val) config->modify_isa_word(isa_mask, isa_mask); else @@ -294,7 +305,7 @@ void NewDialog::isa_atomic_change(bool val) { } void NewDialog::isa_multiply_change(bool val) { - auto isa_mask = machine::ConfigIsaWord::byChar('M'); + auto isa_mask = machine::ConfigIsaWord::byChar('M'); if (val) config->modify_isa_word(isa_mask, isa_mask); else @@ -316,9 +327,8 @@ void NewDialog::delay_slot_change(bool val) { void NewDialog::hazard_unit_change() { if (ui->hazard_unit->isChecked()) { config->set_hazard_unit( - ui->hazard_stall->isChecked() - ? machine::MachineConfig::HU_STALL - : machine::MachineConfig::HU_STALL_FORWARD); + ui->hazard_stall->isChecked() ? machine::MachineConfig::HU_STALL + : machine::MachineConfig::HU_STALL_FORWARD); } else { config->set_hazard_unit(machine::MachineConfig::HU_NONE); } @@ -490,13 +500,11 @@ void NewDialog::bp_type_change() { } } break; - default: - break; + default: break; } bp_toggle_widgets(); - if (need_switch2custom) - switch2custom(); + if (need_switch2custom) switch2custom(); } void NewDialog::bp_enabled_change(bool v) { @@ -548,6 +556,43 @@ void NewDialog::bp_bht_addr_bits_change(int v) { bp_bht_bits_texts_update(); } +void NewDialog::vm_enabled_change(bool v) { + if (config->get_vm_enabled() != v) { + config->set_vm_enabled(v); + switch2custom(); + } +} + +void NewDialog::vm_mode_changed() { + if (!ui->group_vm->isChecked() || ui->radioButton_bare->isChecked()) { + config->set_vm_mode(machine::MachineConfig::VM_BARE); + } else { + config->set_vm_mode(machine::MachineConfig::VM_SV32); + } + switch2custom(); +} + +void NewDialog::va_base_addr_changed(qint64 new_base) { + auto v = static_cast(qBound(0, new_base, 0xFFFFFFFFULL)); + config->set_va_base_addr(v); + switch2custom(); +} + +void NewDialog::tlb_num_sets_changed(int v) { + config->set_tlb_num_sets(static_cast(v)); + switch2custom(); +} + +void NewDialog::tlb_assoc_changed(int v) { + config->set_tlb_associativity(static_cast(v)); + switch2custom(); +} + +void NewDialog::tlb_policy_changed(int idx) { + config->set_tlb_replacement_policy(static_cast(idx)); + switch2custom(); +} + void NewDialog::config_gui() { // Basic ui->elf_file->setText(config->elf()); @@ -607,6 +652,11 @@ void NewDialog::config_gui() { ui->text_bp_bht_entries_number->setText(QString::number(qPow(2, config->get_bp_bht_bits()))); bp_type_change(); + // Virtual + ui->group_vm->setChecked(config->get_vm_enabled()); + ui->radioButton_bare->setChecked(config->get_vm_mode() == machine::MachineConfig::VM_BARE); + ui->radioButton_sv32->setChecked(config->get_vm_mode() == machine::MachineConfig::VM_SV32); + // Memory ui->mem_protec_exec->setChecked(config->memory_execute_protection()); ui->mem_protec_write->setChecked(config->memory_write_protection()); @@ -660,15 +710,9 @@ void NewDialog::load_settings() { auto p = (enum machine::ConfigPresets)(preset - 1); config->preset(p); switch (p) { - case machine::CP_SINGLE: - ui->preset_no_pipeline->setChecked(true); - break; - case machine::CP_SINGLE_CACHE: - ui->preset_no_pipeline_cache->setChecked(true); - break; - case machine::CP_PIPE_NO_HAZARD: - ui->preset_pipelined_bare->setChecked(true); - break; + case machine::CP_SINGLE: ui->preset_no_pipeline->setChecked(true); break; + case machine::CP_SINGLE_CACHE: ui->preset_no_pipeline_cache->setChecked(true); break; + case machine::CP_PIPE_NO_HAZARD: ui->preset_pipelined_bare->setChecked(true); break; case machine::CP_PIPE: ui->preset_pipelined->setChecked(true); break; } } else { @@ -689,14 +733,11 @@ void NewDialog::store_settings() { } } -NewDialogCacheHandler::NewDialogCacheHandler(NewDialog *nd, - Ui::NewDialogCache *cui) : Super(nd) { +NewDialogCacheHandler::NewDialogCacheHandler(NewDialog *nd, Ui::NewDialogCache *cui) : Super(nd) { this->nd = nd; this->ui = cui; this->config = nullptr; - connect( - ui->enabled, &QGroupBox::clicked, this, - &NewDialogCacheHandler::enabled); + connect(ui->enabled, &QGroupBox::clicked, this, &NewDialogCacheHandler::enabled); connect( ui->number_of_sets, &QAbstractSpinBox::editingFinished, this, &NewDialogCacheHandler::numsets); @@ -748,8 +789,7 @@ void NewDialogCacheHandler::degreeassociativity() { } void NewDialogCacheHandler::replacement(int val) { - config->set_replacement_policy( - (enum machine::CacheConfig::ReplacementPolicy)val); + config->set_replacement_policy((enum machine::CacheConfig::ReplacementPolicy)val); nd->switch2custom(); } diff --git a/src/gui/dialogs/new/newdialog.h b/src/gui/dialogs/new/newdialog.h index c8c7175e..82917d3a 100644 --- a/src/gui/dialogs/new/newdialog.h +++ b/src/gui/dialogs/new/newdialog.h @@ -64,6 +64,14 @@ private slots: void bp_bht_bhr_bits_change(int); void bp_bht_addr_bits_change(int); + // Virtual Memory + void vm_mode_changed(); + void vm_enabled_change(bool); + void va_base_addr_changed(qint64 new_base); + void tlb_num_sets_changed(int); + void tlb_assoc_changed(int); + void tlb_policy_changed(int); + private: Box ui {}; Box ui_cache_p {}, ui_cache_d {}, ui_cache_l2 {}; diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index 7e36afdf..3f5dde3b 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -775,7 +775,7 @@ void MainWindow::compile_source() { return; } SymbolTableDb symtab(machine->symbol_table_rw(true)); - machine::FrontendMemory *mem = machine->memory_data_bus_rw(); + machine::FrontendMemory *mem = machine->data_frontend(); if (mem == nullptr) { showAsyncCriticalBox( this, "Simulator Error", tr("No physical addresspace to store program.")); @@ -793,7 +793,12 @@ void MainWindow::compile_source() { connect(&sasm, &SimpleAsm::report_message, this, &MainWindow::report_message); - sasm.setup(mem, &symtab, machine::Address(0x00000200), machine->core()->get_xlen()); + const uint32_t addr = (machine->config().get_vm_enabled() + && machine->config().get_vm_mode() == machine::MachineConfig::VM_SV32) + ? machine->config().get_va_base_addr() + : 0x00000200; + sasm.setup( + mem, machine->config(), &symtab, machine::Address(addr), machine->core()->get_xlen()); int ln = 1; for (QTextBlock block = content->begin(); block.isValid(); block = block.next(), ln++) { @@ -802,7 +807,15 @@ void MainWindow::compile_source() { } if (!sasm.finish()) { error_occured = true; } - if (error_occured) { show_messages(); } + if (error_occured) { + show_messages(); + } else { + program->setup(machine.data()); + machine::Address start_addr(addr); + program->fetch_inst_addr(start_addr); + show_program(); + program->request_update_all(); + } } void MainWindow::build_execute() { diff --git a/src/gui/windows/memory/memorydock.cpp b/src/gui/windows/memory/memorydock.cpp index 07fd4d69..afbcd220 100644 --- a/src/gui/windows/memory/memorydock.cpp +++ b/src/gui/windows/memory/memorydock.cpp @@ -4,6 +4,7 @@ #include "memorytableview.h" #include "ui/hexlineedit.h" +#include #include #include #include @@ -34,9 +35,33 @@ MemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) { auto *go_edit = new HexLineEdit(nullptr, 8, 16, "0x"); + vm_toggle_ = new QCheckBox(tr("Show virtual")); + auto *layout_top = new QHBoxLayout; layout_top->addWidget(cell_size); layout_top->addWidget(cached_access); + layout_top->addWidget(vm_toggle_); + connect(vm_toggle_, &QCheckBox::toggled, this, [this](bool nowShowingVirtual) { + auto *view = findChild(); + auto *model = findChild(); + if (!view || !model) return; + QModelIndex idx = view->currentIndex(); + int row = idx.isValid() ? idx.row() : 0; + machine::Address addr; + if (!model->get_row_address(addr, row)) return; + machine::Address newAddr = addr; + if (!nowShowingVirtual) { + newAddr = machinePtr->virtualToPhysical(addr); + } else { + auto const *tlb = dynamic_cast(machinePtr->data_frontend()); + machine::VirtualAddress va; + if (tlb && tlb->reverse_lookup(addr, va)) { + newAddr = machine::Address { va.get_raw() }; + } + } + model->setVirtualMode(nowShowingVirtual); + view->focus_address(newAddr); + }); auto *layout = new QVBoxLayout; layout->addLayout(layout_top); @@ -47,14 +72,17 @@ MemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) { setWidget(content); + connect(this, &MemoryDock::machine_setup, memory_model, &MemoryModel::setup); + connect(vm_toggle_, &QCheckBox::toggled, memory_model, &MemoryModel::setVirtualMode); connect( - this, &MemoryDock::machine_setup, memory_model, &MemoryModel::setup); + cell_size, QOverload::of(&QComboBox::currentIndexChanged), memory_content, + &MemoryTableView::set_cell_size); connect( - cell_size, QOverload::of(&QComboBox::currentIndexChanged), - memory_content, &MemoryTableView::set_cell_size); + memory_model, &QAbstractItemModel::layoutChanged, memory_content, + &MemoryTableView::recompute_columns); connect( - cached_access, QOverload::of(&QComboBox::currentIndexChanged), - memory_model, &MemoryModel::cached_access); + cached_access, QOverload::of(&QComboBox::currentIndexChanged), memory_model, + &MemoryModel::cached_access); connect( go_edit, &HexLineEdit::value_edit_finished, memory_content, [memory_content](uint32_t value) { @@ -62,17 +90,30 @@ MemoryDock::MemoryDock(QWidget *parent, QSettings *settings) : Super(parent) { }); connect( memory_content, &MemoryTableView::address_changed, go_edit, - [go_edit](machine::Address addr) { - go_edit->set_value(addr.get_raw()); - }); - connect( - this, &MemoryDock::focus_addr, memory_content, - &MemoryTableView::focus_address); + [go_edit](machine::Address addr) { go_edit->set_value(addr.get_raw()); }); + connect(this, &MemoryDock::focus_addr, memory_content, &MemoryTableView::focus_address); connect( memory_model, &MemoryModel::setup_done, memory_content, &MemoryTableView::recompute_columns); } void MemoryDock::setup(machine::Machine *machine) { + machinePtr = machine; emit machine_setup(machine); + + bool vm_on = machine->config().get_vm_enabled(); + vm_toggle_->setVisible(vm_on); + vm_toggle_->setChecked(vm_on); + + QTimer::singleShot(0, this, [this]() { + if (!machinePtr) return; + machine::Address pc = machinePtr->registers()->read_pc(); + if (machinePtr->config().get_vm_enabled() && vm_toggle_->isChecked()) { + this->focus_addr(pc); + } else if (machinePtr->config().get_vm_enabled()) { + this->focus_addr(machinePtr->virtualToPhysical(pc)); + } else { + this->focus_addr(pc); + } + }); } diff --git a/src/gui/windows/memory/memorydock.h b/src/gui/windows/memory/memorydock.h index 5b103be6..dc782e8a 100644 --- a/src/gui/windows/memory/memorydock.h +++ b/src/gui/windows/memory/memorydock.h @@ -4,6 +4,7 @@ #include "machine/machine.h" #include "machine/memory/address.h" +#include #include #include #include @@ -18,11 +19,14 @@ class MemoryDock : public QDockWidget { void setup(machine::Machine *machine); + signals: void machine_setup(machine::Machine *machine); void focus_addr(machine::Address); private: + QCheckBox *vm_toggle_; + machine::Machine *machinePtr; }; #endif // MEMORYDOCK_H diff --git a/src/gui/windows/memory/memorymodel.cpp b/src/gui/windows/memory/memorymodel.cpp index 3698e53c..244cfb36 100644 --- a/src/gui/windows/memory/memorymodel.cpp +++ b/src/gui/windows/memory/memorymodel.cpp @@ -43,7 +43,7 @@ QVariant MemoryModel::headerData(int section, Qt::Orientation orientation, int r if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { if (section == 0) { - return tr("Address"); + return isVirtualMode() ? tr("Virt Addr") : tr("Phys Addr"); } else { uint32_t addr = (section - 1) * cellSizeBytes(); QString ret = "+" + QString::number(addr, 10); @@ -59,7 +59,7 @@ QVariant MemoryModel::data(const QModelIndex &index, int role) const { QString s, t; machine::Address address; uint32_t data; - const machine::FrontendMemory *mem; + const machine::FrontendMemory *mem = nullptr; if (!get_row_address(address, index.row())) { return QString(""); } if (index.column() == 0) { t = QString::number(address.get_raw(), 16); @@ -67,11 +67,17 @@ QVariant MemoryModel::data(const QModelIndex &index, int role) const { return { QString("0x") + s + t }; } if (machine == nullptr) { return QString(""); } - mem = mem_access(); - if (mem == nullptr) { return QString(""); } - if ((access_through_cache > 0) && (machine->cache_data() != nullptr)) { - mem = machine->cache_data(); + if (machine->config().get_vm_enabled()) { + if (showVirtual) { + mem = machine->data_frontend(); + } else { + mem = mem_access_phys(); + } + } else { + mem = mem_access(); + if (access_through_cache > 0 && machine->cache_data()) { mem = machine->cache_data(); } } + if (!mem) { return QString(""); } address += cellSizeBytes() * (index.column() - 1); if (address < index0_offset) { return QString(""); } switch (cell_size) { @@ -198,6 +204,13 @@ void MemoryModel::cached_access(int cached) { update_all(); } +void MemoryModel::setVirtualMode(bool on) { + if (showVirtual == on) return; + showVirtual = on; + emit setup_done(); + layoutChanged(); +} + Qt::ItemFlags MemoryModel::flags(const QModelIndex &index) const { if (index.column() == 0) { return QAbstractTableModel::flags(index); @@ -207,26 +220,56 @@ Qt::ItemFlags MemoryModel::flags(const QModelIndex &index) const { } bool MemoryModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (role == Qt::EditRole) { - bool ok; - machine::Address address; - machine::FrontendMemory *mem; - uint32_t data = value.toString().toULong(&ok, 16); - if (!ok) { return false; } - if (!get_row_address(address, index.row())) { return false; } - if (index.column() == 0 || machine == nullptr) { return false; } + if (role != Qt::EditRole) return false; + + bool ok; + machine::Address address; + uint32_t data = value.toString().toULong(&ok, 16); + if (!ok) return false; + + if (!get_row_address(address, index.row()) || index.column() == 0 || machine == nullptr) { + return false; + } + + machine::FrontendMemory *mem = nullptr; + if (machine->config().get_vm_enabled()) { + if (showVirtual) { + mem = machine->data_frontend(); + } else { + mem = mem_access_phys_rw(); + } + } else { mem = mem_access_rw(); - if (mem == nullptr) { return false; } - if ((access_through_cache > 0) && (machine->cache_data_rw() != nullptr)) { + if (access_through_cache > 0 && machine->cache_data_rw()) { mem = machine->cache_data_rw(); } - address += cellSizeBytes() * (index.column() - 1); - switch (cell_size) { - case CELLSIZE_BYTE: mem->write_u8(address, data, ae::INTERNAL); break; - case CELLSIZE_HWORD: mem->write_u16(address, data, ae::INTERNAL); break; - default: - case CELLSIZE_WORD: mem->write_u32(address, data, ae::INTERNAL); break; - } + } + if (!mem) return false; + + // Compute the byte/halfword/word offset + address += cellSizeBytes() * (index.column() - 1); + switch (cell_size) { + case CELLSIZE_BYTE: mem->write_u8(address, data, ae::INTERNAL); break; + case CELLSIZE_HWORD: mem->write_u16(address, data, ae::INTERNAL); break; + default: // CELLSIZE_WORD + mem->write_u32(address, data, ae::INTERNAL); + break; } return true; } + +const machine::FrontendMemory *MemoryModel::mem_access_phys() const { + if (!machine) return nullptr; + if (access_through_cache > 0 && machine->cache_data()) { + return machine->cache_data(); + } else + return machine->memory_data_bus(); +} + +machine::FrontendMemory *MemoryModel::mem_access_phys_rw() const { + if (!machine) return nullptr; + if (access_through_cache > 0 && machine->cache_data_rw()) { + return machine->cache_data_rw(); + } else + return machine->memory_data_bus_rw(); +} diff --git a/src/gui/windows/memory/memorymodel.h b/src/gui/windows/memory/memorymodel.h index 331aaeb7..08bd53b8 100644 --- a/src/gui/windows/memory/memorymodel.h +++ b/src/gui/windows/memory/memorymodel.h @@ -69,12 +69,14 @@ class MemoryModel : public QAbstractTableModel { } return true; } + bool isVirtualMode() const { return showVirtual; } public slots: void setup(machine::Machine *machine); void set_cell_size(int index); void check_for_updates(); void cached_access(int cached); + void setVirtualMode(bool on); signals: void cell_size_changed(); @@ -83,6 +85,8 @@ public slots: private: [[nodiscard]] const machine::FrontendMemory *mem_access() const; [[nodiscard]] machine::FrontendMemory *mem_access_rw() const; + [[nodiscard]] const machine::FrontendMemory *mem_access_phys() const; + [[nodiscard]] machine::FrontendMemory *mem_access_phys_rw() const; enum MemoryCellSize cell_size; unsigned int cells_per_row; machine::Address index0_offset; @@ -91,6 +95,7 @@ public slots: uint32_t memory_change_counter; uint32_t cache_data_change_counter; int access_through_cache; + bool showVirtual = false; }; #endif // MEMORYMODEL_H diff --git a/src/gui/windows/program/programmodel.cpp b/src/gui/windows/program/programmodel.cpp index fbfa28b7..b1d959b5 100644 --- a/src/gui/windows/program/programmodel.cpp +++ b/src/gui/windows/program/programmodel.cpp @@ -1,5 +1,7 @@ #include "programmodel.h" +#include "machine/machine.h" + #include using ae = machine::AccessEffects; // For enum values, the type is obvious from context. @@ -18,14 +20,14 @@ ProgramModel::ProgramModel(QObject *parent) : Super(parent), data_font("Monospac const machine::FrontendMemory *ProgramModel::mem_access() const { if (machine == nullptr) { return nullptr; } - if (machine->memory_data_bus() != nullptr) { return machine->memory_data_bus(); } + return machine->instr_frontend(); throw std::logic_error("Use of backend memory in frontend."); // TODO // return machine->memory(); } machine::FrontendMemory *ProgramModel::mem_access_rw() const { if (machine == nullptr) { return nullptr; } - if (machine->memory_data_bus_rw() != nullptr) { return machine->memory_data_bus_rw(); } + return machine->instr_frontend(); throw std::logic_error("Use of backend memory in frontend."); // TODO // return machine->memory_rw(); } @@ -129,15 +131,19 @@ QVariant ProgramModel::data(const QModelIndex &index, int role) const { void ProgramModel::setup(machine::Machine *machine) { this->machine = machine; + if (machine && machine->config().get_vm_enabled() + && machine->config().get_vm_mode() == machine::MachineConfig::VM_SV32) { + index0_offset = machine::Address(machine->config().get_va_base_addr()); + } else { + index0_offset = machine::Address(0x00000200); + } for (auto &i : stage_addr) { i = machine::STAGEADDR_NONE; } if (machine != nullptr) { connect(machine, &machine::Machine::post_tick, this, &ProgramModel::check_for_updates); - } - if (mem_access() != nullptr) { connect( - mem_access(), &machine::FrontendMemory::external_change_notify, this, + machine->instr_frontend(), &machine::FrontendMemory::external_change_notify, this, &ProgramModel::check_for_updates); } emit update_all(); diff --git a/src/machine/CMakeLists.txt b/src/machine/CMakeLists.txt index f3cca553..747ee13d 100644 --- a/src/machine/CMakeLists.txt +++ b/src/machine/CMakeLists.txt @@ -21,7 +21,7 @@ set(machine_SOURCES memory/cache/cache.cpp memory/cache/cache_policy.cpp memory/frontend_memory.cpp - memory/memory_bus.cpp + memory/memory_bus.cpp programloader.cpp predictor.cpp registers.cpp @@ -68,7 +68,17 @@ set(machine_HEADERS utils.h execute/alu_op.h execute/mul_op.h - ) + memory/virtual/virtual_address.h + memory/tlb/tlb.h + memory/tlb/tlb.cpp + memory/virtual/page_table_walker.h + memory/virtual/page_table_walker.cpp + memory/virtual/sv32.h + memory/virtual/page_fault_handler.h + memory/virtual/page_fault_handler.cpp + memory/tlb/tlb_policy.h + memory/tlb/tlb_policy.cpp +) # Object library is preferred, because the library archive is never really # needed. This option skips the archive creation and links directly .o files. diff --git a/src/machine/core.cpp b/src/machine/core.cpp index c07cb797..719566ac 100644 --- a/src/machine/core.cpp +++ b/src/machine/core.cpp @@ -5,20 +5,18 @@ #include "utils.h" #include +#include +#include LOG_CATEGORY("machine.core"); using namespace machine; -static InstructionFlags unsupported_inst_flags_to_check(Xlen xlen, - ConfigIsaWord isa_word) { +static InstructionFlags unsupported_inst_flags_to_check(Xlen xlen, ConfigIsaWord isa_word) { unsigned flags_to_check = IMF_SUPPORTED; - if (xlen == Xlen::_32) - flags_to_check |= IMF_RV64; - if (!isa_word.contains('A')) - flags_to_check |= IMF_AMO; - if (!isa_word.contains('M')) - flags_to_check |= IMF_MUL; + if (xlen == Xlen::_32) flags_to_check |= IMF_RV64; + if (!isa_word.contains('A')) flags_to_check |= IMF_AMO; + if (!isa_word.contains('M')) flags_to_check |= IMF_MUL; return InstructionFlags(flags_to_check); } @@ -140,7 +138,7 @@ void Core::register_exception_handler(ExceptionCause excause, ExceptionHandler * bool Core::handle_exception( ExceptionCause excause, - const Instruction& inst, + const Instruction &inst, Address inst_addr, Address next_addr, Address jump_branch_pc, @@ -158,7 +156,8 @@ bool Core::handle_exception( control_state->update_exception_cause(excause); if (control_state->read_internal(CSR::Id::MTVEC) != 0 && !get_step_over_exception(excause)) { - control_state->exception_initiate(CSR::PrivilegeLevel::MACHINE, CSR::PrivilegeLevel::MACHINE); + control_state->exception_initiate( + CSR::PrivilegeLevel::MACHINE, CSR::PrivilegeLevel::MACHINE); regs->write_pc(control_state->exception_pc_address()); } } @@ -176,32 +175,32 @@ bool Core::handle_exception( } static int32_t amo32_operations(enum AccessControl memctl, int32_t a, int32_t b) { - switch(memctl) { + switch (memctl) { case AC_AMOSWAP32: return b; - case AC_AMOADD32: return a + b; - case AC_AMOXOR32: return a ^ b; - case AC_AMOAND32: return a & b; - case AC_AMOOR32: return a | b; - case AC_AMOMIN32: return a < b? a: b; - case AC_AMOMAX32: return a < b? b: a; - case AC_AMOMINU32: return (uint32_t)a < (uint32_t)b? a: b; - case AC_AMOMAXU32: return (uint32_t)a < (uint32_t)b? b: a; + case AC_AMOADD32: return a + b; + case AC_AMOXOR32: return a ^ b; + case AC_AMOAND32: return a & b; + case AC_AMOOR32: return a | b; + case AC_AMOMIN32: return a < b ? a : b; + case AC_AMOMAX32: return a < b ? b : a; + case AC_AMOMINU32: return (uint32_t)a < (uint32_t)b ? a : b; + case AC_AMOMAXU32: return (uint32_t)a < (uint32_t)b ? b : a; default: break; } return 0; } static int64_t amo64_operations(enum AccessControl memctl, int64_t a, int64_t b) { - switch(memctl) { + switch (memctl) { case AC_AMOSWAP64: return b; - case AC_AMOADD64: return a + b; - case AC_AMOXOR64: return a ^ b; - case AC_AMOAND64: return a & b; - case AC_AMOOR64: return a | b; - case AC_AMOMIN64: return a < b? a: b; - case AC_AMOMAX64: return a < b? b: a; - case AC_AMOMINU64: return (uint64_t)a < (uint64_t)b? a: b; - case AC_AMOMAXU64: return (uint64_t)a < (uint64_t)b? b: a; + case AC_AMOADD64: return a + b; + case AC_AMOXOR64: return a ^ b; + case AC_AMOAND64: return a & b; + case AC_AMOOR64: return a | b; + case AC_AMOMIN64: return a < b ? a : b; + case AC_AMOMAX64: return a < b ? b : a; + case AC_AMOMINU64: return (uint64_t)a < (uint64_t)b ? a : b; + case AC_AMOMAXU64: return (uint64_t)a < (uint64_t)b ? b : a; default: break; } return 0; @@ -252,8 +251,7 @@ enum ExceptionCause Core::memory_special( towrite_val = 1; } break; - case AC_FISRT_AMO_MODIFY32 ... AC_LAST_AMO_MODIFY32: - { + case AC_FISRT_AMO_MODIFY32 ... AC_LAST_AMO_MODIFY32: { if (!memread || !memwrite) { break; } int32_t fetched_value; fetched_value = (int32_t)(mem_data->read_u32(mem_addr)); @@ -262,8 +260,7 @@ enum ExceptionCause Core::memory_special( towrite_val = fetched_value; break; } - case AC_FISRT_AMO_MODIFY64 ... AC_LAST_AMO_MODIFY64: - { + case AC_FISRT_AMO_MODIFY64 ... AC_LAST_AMO_MODIFY64: { if (!memread || !memwrite) { break; } int64_t fetched_value; fetched_value = (int64_t)(mem_data->read_u64(mem_addr)); @@ -287,9 +284,7 @@ FetchState Core::fetch(PCInterstage pc, bool skip_break) { if (!skip_break && hw_breaks.contains(inst_addr)) { excause = EXCAUSE_HWBREAK; } - if (control_state != nullptr) { - control_state->increment_internal(CSR::Id::MCYCLE, 1); - } + if (control_state != nullptr) { control_state->increment_internal(CSR::Id::MCYCLE, 1); } if (control_state != nullptr && excause == EXCAUSE_NONE) { if (control_state->core_interrupt_request()) { excause = EXCAUSE_INT; } @@ -315,9 +310,7 @@ DecodeState Core::decode(const FetchInterstage &dt) { dt.inst.flags_alu_op_mem_ctl(flags, alu_op, mem_ctl); - if ((flags ^ check_inst_flags_val) & check_inst_flags_mask) { - excause = EXCAUSE_INSN_ILLEGAL; - } + if ((flags ^ check_inst_flags_val) & check_inst_flags_mask) { excause = EXCAUSE_INSN_ILLEGAL; } RegisterId num_rs = (flags & (IMF_ALU_REQ_RS | IMF_ALU_RS_ID)) ? dt.inst.rs() : 0; RegisterId num_rt = (flags & IMF_ALU_REQ_RT) ? dt.inst.rt() : 0; @@ -343,8 +336,7 @@ DecodeState Core::decode(const FetchInterstage &dt) { // TODO: EXCAUSE_ECALL_S, EXCAUSE_ECALL_U } } - if (flags & IMF_FORCE_W_OP) - w_operation = true; + if (flags & IMF_FORCE_W_OP) w_operation = true; return { DecodeInternalState { .alu_op_num = static_cast(alu_op.alu_op), @@ -366,8 +358,9 @@ DecodeState Core::decode(const FetchInterstage &dt) { .excause = excause, .ff_rs = FORWARD_NONE, .ff_rt = FORWARD_NONE, - .alu_component = (flags & IMF_AMO) ? AluComponent::PASS : - (flags & IMF_MUL) ? AluComponent::MUL : AluComponent::ALU, + .alu_component = (flags & IMF_AMO) ? AluComponent::PASS + : (flags & IMF_MUL) ? AluComponent::MUL + : AluComponent::ALU, .aluop = alu_op, .memctl = mem_ctl, .num_rs = num_rs, @@ -409,7 +402,8 @@ ExecuteState Core::execute(const DecodeInterstage &dt) { }(); const RegisterValue alu_val = [=] { if (excause != EXCAUSE_NONE) return RegisterValue(0); - return alu_combined_operate(dt.aluop, dt.alu_component, dt.w_operation, dt.alu_mod, alu_fst, alu_sec); + return alu_combined_operate( + dt.aluop, dt.alu_component, dt.w_operation, dt.alu_mod, alu_fst, alu_sec); }(); const Address branch_jal_target = dt.inst_addr + dt.immediate_val.as_i64(); @@ -501,12 +495,15 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { // Predictor update if (dt.branch_jal) { - // JAL Jump instruction (J-type (alternative to U-type with different immediate bit order)) - predictor->update(dt.inst, dt.inst_addr, dt.branch_jal_target, BranchType::JUMP, BranchResult::TAKEN); + // JAL Jump instruction (J-type (alternative to U-type with different immediate bit + // order)) + predictor->update( + dt.inst, dt.inst_addr, dt.branch_jal_target, BranchType::JUMP, BranchResult::TAKEN); } else if (dt.branch_jalr) { // JALR Jump register instruction (I-type) predictor->update( - dt.inst, dt.inst_addr, Address(get_xlen_from_reg(dt.alu_val)), BranchType::JUMP, BranchResult::TAKEN); + dt.inst, dt.inst_addr, Address(get_xlen_from_reg(dt.alu_val)), BranchType::JUMP, + BranchResult::TAKEN); } else if (dt.branch_bxx) { // BXX Conditional branch instruction (B-type (alternative to S-type with different // immediate bit order)) @@ -525,9 +522,11 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { if (dt.xret) { control_state->exception_return(CSR::PrivilegeLevel::MACHINE); if (this->xlen == Xlen::_32) - computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u32()); + computed_next_inst_addr + = Address(control_state->read_internal(CSR::Id::MEPC).as_u32()); else - computed_next_inst_addr = Address(control_state->read_internal(CSR::Id::MEPC).as_u64()); + computed_next_inst_addr + = Address(control_state->read_internal(CSR::Id::MEPC).as_u64()); csr_written = true; } } @@ -577,7 +576,7 @@ WritebackState Core::writeback(const MemoryInterstage &dt) { if (dt.regwrite) { regs->write_gp(dt.num_rd, dt.towrite_val); } return WritebackState { WritebackInternalState { - .inst = (dt.excause == EXCAUSE_NONE)? dt.inst: Instruction::NOP, + .inst = (dt.excause == EXCAUSE_NONE) ? dt.inst : Instruction::NOP, .inst_addr = dt.inst_addr, .value = dt.towrite_val, .num_rd = dt.num_rd, diff --git a/src/machine/csr/controlstate.h b/src/machine/csr/controlstate.h index 90918fd9..41729011 100644 --- a/src/machine/csr/controlstate.h +++ b/src/machine/csr/controlstate.h @@ -46,7 +46,13 @@ namespace machine { namespace CSR { // ... MCYCLE, MINSTRET, - _COUNT, + // Supervisor Trap Setup + // ... + // Supervisor Trap Handling + // ... + // Supervisor Protection and Translation + SATP, + _COUNT }; }; @@ -201,6 +207,13 @@ namespace machine { namespace CSR { static constexpr const RegisterFieldDesc *fields[] = { &SIE, &MIE, &SPIE, &MPIE, &SPP, &MPP, &UXL, &SXL}; static constexpr unsigned count = sizeof(fields) / sizeof(fields[0]); } + namespace satp { + static constexpr RegisterFieldDesc MODE = { "MODE", Id::SATP, {1, 31}, "Address translation mode" }; + static constexpr RegisterFieldDesc ASID = { "ASID", Id::SATP, {9, 22}, "Address-space ID" }; + static constexpr RegisterFieldDesc PPN = { "PPN", Id::SATP, {22, 0}, "Root page-table physical page number" }; + static constexpr const RegisterFieldDesc *fields[] = { &MODE, &ASID, &PPN }; + static constexpr unsigned count = sizeof(fields)/sizeof(fields[0]); + } } /** Definitions of supported CSR registers */ @@ -231,6 +244,8 @@ namespace machine { namespace CSR { [Id::MCYCLE] = { "mcycle", 0xB00_csr, "Machine cycle counter.", 0, (register_storage_t)0xffffffffffffffff, &ControlState::mcycle_wlrl_write_handler}, [Id::MINSTRET] = { "minstret", 0xB02_csr, "Machine instructions-retired counter."}, + + [Id::SATP] = { "satp", 0x180_csr, "Supervisor address translation and protection", 0, 0xffffffff, &ControlState::default_wlrl_write_handler, { Field::satp::fields, Field::satp::count } } } }; /** Lookup from CSR address (value used in instruction) to internal id (index in continuous diff --git a/src/machine/machine.cpp b/src/machine/machine.cpp index c50ae78e..43f05d75 100644 --- a/src/machine/machine.cpp +++ b/src/machine/machine.cpp @@ -1,11 +1,17 @@ #include "machine.h" +#include "memory/tlb/tlb.h" +#include "memory/virtual/page_fault_handler.h" #include "programloader.h" #include +#include +#include #include #include +LOG_CATEGORY("machine"); + using namespace machine; Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) @@ -13,33 +19,51 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) , stat(ST_READY) { regs = new Registers(); + controlst + = new CSR::ControlState(machine_config.get_simulated_xlen(), machine_config.get_isa_word()); + + uint32_t sv32_root_ppn = 0; if (load_executable) { ProgramLoader program(machine_config.elf()); this->machine_config.set_simulated_endian(program.get_endian()); mem_program_only = new Memory(machine_config.get_simulated_endian()); program.to_memory(mem_program_only); - if (program.get_architecture_type() == ARCH64) this->machine_config.set_simulated_xlen(Xlen::_64); else this->machine_config.set_simulated_xlen(Xlen::_32); - - if (load_symtab) { - symtab = program.get_symbol_table(); + if (load_symtab) { symtab = program.get_symbol_table(); } + program_end = program.end(); + if (machine_config.get_vm_enabled() + && machine_config.get_vm_mode() == MachineConfig::VM_SV32) { + uint32_t base_va = machine_config.get_va_base_addr(); + program_end = Address { program_end.get_raw() + base_va }; } - program_end = program.end(); if (program.get_executable_entry() != 0x0_addr) { - regs->write_pc(program.get_executable_entry()); + uint32_t base_va = 0; + if (machine_config.get_vm_enabled() + && machine_config.get_vm_mode() == MachineConfig::VM_SV32) { + base_va = machine_config.get_va_base_addr(); + } + regs->write_pc(Address { program.get_executable_entry().get_raw() + base_va }); } mem = new Memory(*mem_program_only); + + if (machine_config.get_vm_enabled() + && machine_config.get_vm_mode() == MachineConfig::VM_SV32) { + sv32_root_ppn = allocate_page(); + uint32_t asid = machine_config.get_vm_asid() & 0x1FFu; + uint32_t satp = (1u << 31) | (asid << 22) | sv32_root_ppn; + controlst->write_internal(CSR::Id::SATP, satp); + uint32_t base_va = machine_config.get_va_base_addr(); + map_elf_segments(program, mem, sv32_root_ppn, base_va); + } } else { mem = new Memory(machine_config.get_simulated_endian()); } - data_bus = new MemoryDataBus(machine_config.get_simulated_endian()); - data_bus->insert_device_to_range( - mem, 0x00000000_addr, 0xefffffff_addr, false); + data_bus->insert_device_to_range(mem, 0x00000000_addr, 0xefffffff_addr, false); setup_serial_port(); setup_perip_spi_led(); @@ -54,11 +78,8 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) bool access_enable_burst = machine_config.memory_access_enable_burst(); cch_level2 = new Cache( - data_bus, &machine_config.cache_level2(), - access_time_read, - access_time_write, - access_time_burst, - access_enable_burst); + data_bus, &machine_config.cache_level2(), access_time_read, access_time_write, + access_time_burst, access_enable_burst); if (machine_config.cache_level2().enabled()) { access_time_read = machine_config.memory_access_time_level2(); access_time_write = machine_config.memory_access_time_level2(); @@ -66,19 +87,47 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) access_enable_burst = true; } cch_program = new Cache( - cch_level2, &machine_config.cache_program(), - access_time_read, - access_time_write, - access_time_burst, - access_enable_burst); + cch_level2, &machine_config.cache_program(), access_time_read, access_time_write, + access_time_burst, access_enable_burst); cch_data = new Cache( - cch_level2, &machine_config.cache_data(), - access_time_read, - access_time_write, - access_time_burst, - access_enable_burst); + cch_level2, &machine_config.cache_data(), access_time_read, access_time_write, + access_time_burst, access_enable_burst); + + if (machine_config.get_vm_enabled() && machine_config.get_vm_mode() == MachineConfig::VM_SV32) { + uint32_t asid = machine_config.get_vm_asid() & 0x1FFu; + uint32_t satp = (1u << 31) | (asid << 22) | sv32_root_ppn; + + tlb_program.emplace(cch_program, PROGRAM, this); + tlb_data.emplace(cch_data, DATA, this); + + controlst->write_internal(CSR::Id::SATP, satp); + tlb_program->on_csr_write(CSR::Id::SATP, satp); + tlb_data->on_csr_write(CSR::Id::SATP, satp); + + auto *pfh_data = new PageFaultHandler(this, *tlb_data); + tlb_data->set_page_fault_handler(pfh_data); + auto *pfh_instr = new PageFaultHandler(this, *tlb_program); + tlb_program->set_page_fault_handler(pfh_instr); + + connect(&*tlb_data, &TLB::firstWrite, this, [this](VirtualAddress va) { + regs->write_pc(Address { va.get_raw() }); + }); + + } else if ( + machine_config.get_vm_enabled() && machine_config.get_vm_mode() == MachineConfig::VM_BARE) { + tlb_program.emplace(cch_program, PROGRAM, this); + tlb_data.emplace(cch_data, DATA, this); + controlst->write_internal(CSR::Id::SATP, 0); + tlb_program->on_csr_write(CSR::Id::SATP, 0); + tlb_data->on_csr_write(CSR::Id::SATP, 0); + LOG("Machine: running in bare mode (identity mapping via TLB/PTW)."); + } + + instr_if_ = tlb_program ? static_cast(&*tlb_program) + : static_cast(cch_program); + data_if_ = tlb_data ? static_cast(&*tlb_data) + : static_cast(cch_data); - controlst = new CSR::ControlState(machine_config.get_simulated_xlen(), machine_config.get_isa_word()); predictor = new BranchPredictor( machine_config.get_bp_enabled(), machine_config.get_bp_type(), machine_config.get_bp_init_state(), machine_config.get_bp_btb_bits(), @@ -86,11 +135,13 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) if (machine_config.pipelined()) { cr = new CorePipelined( - regs, predictor, cch_program, cch_data, controlst, - machine_config.get_simulated_xlen(), machine_config.get_isa_word(), machine_config.hazard_unit()); + regs, predictor, instr_if_, data_if_, controlst, machine_config.get_simulated_xlen(), + machine_config.get_isa_word(), machine_config.hazard_unit()); + } else { - cr = new CoreSingle(regs, predictor, cch_program, cch_data, controlst, - machine_config.get_simulated_xlen(), machine_config.get_isa_word()); + cr = new CoreSingle( + regs, predictor, instr_if_, data_if_, controlst, machine_config.get_simulated_xlen(), + machine_config.get_isa_word()); } connect( this, &Machine::set_interrupt_signal, controlst, &CSR::ControlState::set_interrupt_signal); @@ -101,10 +152,8 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) for (int i = 0; i < EXCAUSE_COUNT; i++) { if (i != EXCAUSE_INT && i != EXCAUSE_BREAK && i != EXCAUSE_HWBREAK) { - set_stop_on_exception( - (enum ExceptionCause)i, machine_config.osemu_exception_stop()); - set_step_over_exception( - (enum ExceptionCause)i, machine_config.osemu_exception_stop()); + set_stop_on_exception((enum ExceptionCause)i, machine_config.osemu_exception_stop()); + set_step_over_exception((enum ExceptionCause)i, machine_config.osemu_exception_stop()); } } @@ -113,16 +162,14 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) } void Machine::setup_lcd_display() { perip_lcd_display = new LcdDisplay(machine_config.get_simulated_endian()); - memory_bus_insert_range( - perip_lcd_display, 0xffe00000_addr, 0xffe4afff_addr, true); + memory_bus_insert_range(perip_lcd_display, 0xffe00000_addr, 0xffe4afff_addr, true); if (machine_config.get_simulated_xlen() == Xlen::_64) memory_bus_insert_range( perip_lcd_display, 0xffffffffffe00000_addr, 0xffffffffffe4afff_addr, false); } void Machine::setup_perip_spi_led() { perip_spi_led = new PeripSpiLed(machine_config.get_simulated_endian()); - memory_bus_insert_range( - perip_spi_led, 0xffffc100_addr, 0xffffc1ff_addr, true); + memory_bus_insert_range(perip_spi_led, 0xffffc100_addr, 0xffffc1ff_addr, true); if (machine_config.get_simulated_xlen() == Xlen::_64) memory_bus_insert_range( perip_spi_led, 0xffffffffffffc100_addr, 0xffffffffffffc1ff_addr, false); @@ -133,22 +180,19 @@ void Machine::setup_serial_port() { memory_bus_insert_range(ser_port, 0xffff0000_addr, 0xffff003f_addr, false); if (machine_config.get_simulated_xlen() == Xlen::_64) memory_bus_insert_range(ser_port, 0xffffffffffffc000_addr, 0xffffffffffffc03f_addr, false); - connect( - ser_port, &SerialPort::signal_interrupt, this, - &Machine::set_interrupt_signal); + connect(ser_port, &SerialPort::signal_interrupt, this, &Machine::set_interrupt_signal); } void Machine::setup_aclint_mtime() { aclint_mtimer = new aclint::AclintMtimer(machine_config.get_simulated_endian()); - memory_bus_insert_range(aclint_mtimer, - 0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET, - 0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1, - true); + memory_bus_insert_range( + aclint_mtimer, 0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET, + 0xfffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1, true); if (machine_config.get_simulated_xlen() == Xlen::_64) - memory_bus_insert_range(aclint_mtimer, - 0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET, - 0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1, - false); + memory_bus_insert_range( + aclint_mtimer, 0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET, + 0xfffffffffffd0000_addr + aclint::CLINT_MTIMER_OFFSET + aclint::CLINT_MTIMER_SIZE - 1, + false); connect( aclint_mtimer, &aclint::AclintMtimer::signal_interrupt, this, &Machine::set_interrupt_signal); @@ -156,34 +200,30 @@ void Machine::setup_aclint_mtime() { void Machine::setup_aclint_mswi() { aclint_mswi = new aclint::AclintMswi(machine_config.get_simulated_endian()); - memory_bus_insert_range(aclint_mswi, - 0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET, - 0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1, - true); + memory_bus_insert_range( + aclint_mswi, 0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET, + 0xfffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1, true); if (machine_config.get_simulated_xlen() == Xlen::_64) - memory_bus_insert_range(aclint_mswi, - 0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET, - 0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1, - false); + memory_bus_insert_range( + aclint_mswi, 0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET, + 0xfffffffffffd0000_addr + aclint::CLINT_MSWI_OFFSET + aclint::CLINT_MSWI_SIZE - 1, + false); connect( - aclint_mswi, &aclint::AclintMswi::signal_interrupt, this, - &Machine::set_interrupt_signal); + aclint_mswi, &aclint::AclintMswi::signal_interrupt, this, &Machine::set_interrupt_signal); } void Machine::setup_aclint_sswi() { aclint_sswi = new aclint::AclintSswi(machine_config.get_simulated_endian()); - memory_bus_insert_range(aclint_sswi, - 0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET, - 0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1, - true); + memory_bus_insert_range( + aclint_sswi, 0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET, + 0xfffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1, true); if (machine_config.get_simulated_xlen() == Xlen::_64) - memory_bus_insert_range(aclint_sswi, - 0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET, - 0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1, - false); + memory_bus_insert_range( + aclint_sswi, 0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET, + 0xfffffffffffd0000_addr + aclint::CLINT_SSWI_OFFSET + aclint::CLINT_SSWI_SIZE - 1, + false); connect( - aclint_sswi, &aclint::AclintSswi::signal_interrupt, this, - &Machine::set_interrupt_signal); + aclint_sswi, &aclint::AclintSswi::signal_interrupt, this, &Machine::set_interrupt_signal); } Machine::~Machine() { @@ -197,6 +237,8 @@ Machine::~Machine() { regs = nullptr; delete mem; mem = nullptr; + tlb_program.reset(); + tlb_data.reset(); delete cch_program; cch_program = nullptr; delete cch_data; @@ -259,15 +301,9 @@ Cache *Machine::cache_data_rw() { } void Machine::cache_sync() { - if (cch_program != nullptr) { - cch_program->sync(); - } - if (cch_data != nullptr) { - cch_data->sync(); - } - if (cch_level2 != nullptr) { - cch_level2->sync(); - } + if (cch_program != nullptr) { cch_program->sync(); } + if (cch_data != nullptr) { cch_data->sync(); } + if (cch_level2 != nullptr) { cch_level2->sync(); } } const MemoryDataBus *Machine::memory_data_bus() { @@ -291,9 +327,7 @@ LcdDisplay *Machine::peripheral_lcd_display() { } SymbolTable *Machine::symbol_table_rw(bool create) { - if (create && (symtab == nullptr)) { - symtab = new SymbolTable; - } + if (create && (symtab == nullptr)) { symtab = new SymbolTable; } return symtab; } @@ -307,9 +341,7 @@ void Machine::set_symbol( uint32_t size, unsigned char info, unsigned char other) { - if (symtab == nullptr) { - symtab = new SymbolTable; - } + if (symtab == nullptr) { symtab = new SymbolTable; } symtab->set_symbol(name, value, size, info, other); } @@ -339,10 +371,9 @@ bool Machine::exited() { // We don't allow to call control methods when machine exited or if it's busy // We rather silently fail. -#define CTL_GUARD \ - do { \ - if (exited() || stat == ST_BUSY) \ - return; \ +#define CTL_GUARD \ + do { \ + if (exited() || stat == ST_BUSY) return; \ } while (false) void Machine::play() { @@ -354,9 +385,7 @@ void Machine::play() { } void Machine::pause() { - if (stat != ST_BUSY) { - CTL_GUARD; - } + if (stat != ST_BUSY) { CTL_GUARD; } set_status(ST_READY); stop_core_clock(); emit play_paused(); @@ -449,17 +478,11 @@ void Machine::restart() { void Machine::set_status(enum Status st) { bool change = st != stat; stat = st; - if (change) { - emit status_change(st); - } + if (change) { emit status_change(st); } } -void Machine::register_exception_handler( - ExceptionCause excause, - ExceptionHandler *exhandler) { - if (cr != nullptr) { - cr->register_exception_handler(excause, exhandler); - } +void Machine::register_exception_handler(ExceptionCause excause, ExceptionHandler *exhandler) { + if (cr != nullptr) { cr->register_exception_handler(excause, exhandler); } } bool Machine::memory_bus_insert_range( @@ -467,63 +490,44 @@ bool Machine::memory_bus_insert_range( Address start_addr, Address last_addr, bool move_ownership) { - if (data_bus == nullptr) { - return false; - } - return data_bus->insert_device_to_range( - mem_acces, start_addr, last_addr, move_ownership); + if (data_bus == nullptr) { return false; } + return data_bus->insert_device_to_range(mem_acces, start_addr, last_addr, move_ownership); } void Machine::insert_hwbreak(Address address) { - if (cr != nullptr) { - cr->insert_hwbreak(address); - } + if (cr != nullptr) { cr->insert_hwbreak(address); } } void Machine::remove_hwbreak(Address address) { - if (cr != nullptr) { - cr->remove_hwbreak(address); - } + if (cr != nullptr) { cr->remove_hwbreak(address); } } bool Machine::is_hwbreak(Address address) { - if (cr != nullptr) { - return cr->is_hwbreak(address); - } + if (cr != nullptr) { return cr->is_hwbreak(address); } return false; } void Machine::set_stop_on_exception(enum ExceptionCause excause, bool value) { - if (cr != nullptr) { - cr->set_stop_on_exception(excause, value); - } + if (cr != nullptr) { cr->set_stop_on_exception(excause, value); } } bool Machine::get_stop_on_exception(enum ExceptionCause excause) const { - if (cr != nullptr) { - return cr->get_stop_on_exception(excause); - } + if (cr != nullptr) { return cr->get_stop_on_exception(excause); } return false; } void Machine::set_step_over_exception(enum ExceptionCause excause, bool value) { - if (cr != nullptr) { - cr->set_step_over_exception(excause, value); - } + if (cr != nullptr) { cr->set_step_over_exception(excause, value); } } bool Machine::get_step_over_exception(enum ExceptionCause excause) const { - if (cr != nullptr) { - return cr->get_step_over_exception(excause); - } + if (cr != nullptr) { return cr->get_step_over_exception(excause); } return false; } enum ExceptionCause Machine::get_exception_cause() const { uint32_t val; - if (controlst == nullptr) { - return EXCAUSE_NONE; - } + if (controlst == nullptr) { return EXCAUSE_NONE; } val = (controlst->read_internal(CSR::Id::MCAUSE).as_u64()); if (val & 0xffffffff80000000) { return EXCAUSE_INT; @@ -531,3 +535,71 @@ enum ExceptionCause Machine::get_exception_cause() const { return (ExceptionCause)val; } } + +void Machine::map_elf_segments( + ProgramLoader &program, + Memory *mem, + uint32_t root_ppn, + uint32_t base_va) { + constexpr unsigned PAGE_SHIFT = 12; + constexpr unsigned PAGE_SIZE = 1u << PAGE_SHIFT; + constexpr unsigned VPN1_SHIFT = PAGE_SHIFT + 10; + constexpr unsigned VPN0_SHIFT = PAGE_SHIFT; + constexpr unsigned VPN_MASK = (1u << 10) - 1; + + auto rd32 = [&](uint64_t a) { + uint32_t v; + mem->read(&v, Offset { a }, sizeof(v), { .type = ae::INTERNAL }); + return v; + }; + auto wr32 = [&](uint64_t a, uint32_t v) { + mem->write(Offset { a }, &v, sizeof(v), { .type = ae::INTERNAL }); + }; + + for (auto seg : program.get_load_segments()) { + uint32_t vbase = seg.vaddr + base_va; + uint32_t fsize = seg.size; + uint32_t perms = seg.flags; + uint32_t npages = (fsize + PAGE_SIZE - 1) / PAGE_SIZE; + + for (uint32_t i = 0; i < npages; i++) { + uint32_t va_page = vbase + i * PAGE_SIZE; + uint32_t vpn1 = (va_page >> VPN1_SHIFT) & VPN_MASK; + uint32_t vpn0 = (va_page >> VPN0_SHIFT) & VPN_MASK; + uint32_t current_ppn = root_ppn; + uint64_t pte_addr = 0; + + for (int lvl = 1; lvl >= 0; --lvl) { + uint32_t idx = (lvl == 1 ? vpn1 : vpn0); + pte_addr = (uint64_t(current_ppn) << PAGE_SHIFT) + idx * 4; + Sv32Pte pte = Sv32Pte::from_uint(rd32(pte_addr)); + + if (!pte.is_valid()) { + uint32_t new_ppn = allocate_page(); + Sv32Pte npte {}; + npte.v = 1; + npte.ppn = new_ppn; + wr32(pte_addr, npte.to_uint()); + current_ppn = new_ppn; + } else if (pte.is_leaf()) { + throw SIMULATOR_EXCEPTION( + PageFault, "map_elf_segments: unexpected leaf PTE", + QString::number(pte_addr, 16)); + } else { + current_ppn = pte.ppn; + } + } + + uint32_t phys_ppn = seg.vaddr >> PAGE_SHIFT; + Sv32Pte leaf {}; + leaf.v = 1; + leaf.r = (perms & PF_R) ? 1 : 0; + leaf.w = (perms & PF_W) ? 1 : 0; + leaf.x = (perms & PF_X) ? 1 : 0; + leaf.a = 1; + leaf.d = 1; + leaf.ppn = phys_ppn; + wr32(pte_addr, leaf.to_uint()); + } + } +} diff --git a/src/machine/machine.h b/src/machine/machine.h index fc5ba5f9..0ed7cc35 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -3,16 +3,18 @@ #include "core.h" #include "machineconfig.h" +#include "memory/backend/aclintmswi.h" +#include "memory/backend/aclintmtimer.h" +#include "memory/backend/aclintsswi.h" #include "memory/backend/lcddisplay.h" #include "memory/backend/peripheral.h" #include "memory/backend/peripspiled.h" #include "memory/backend/serialport.h" -#include "memory/backend/aclintmtimer.h" -#include "memory/backend/aclintmswi.h" -#include "memory/backend/aclintsswi.h" #include "memory/cache/cache.h" #include "memory/memory_bus.h" +#include "memory/tlb/tlb.h" #include "predictor.h" +#include "programloader.h" #include "registers.h" #include "simulator_exception.h" #include "symboltable.h" @@ -20,6 +22,13 @@ #include #include #include +#include +#include +#include + +namespace machine { + class PageTableManager; +} namespace machine { @@ -41,6 +50,8 @@ class Machine : public QObject { const Cache *cache_level2(); Cache *cache_data_rw(); void cache_sync(); + FrontendMemory *instr_frontend() { return instr_if_; } + FrontendMemory *data_frontend() { return data_if_; } const MemoryDataBus *memory_data_bus(); MemoryDataBus *memory_data_bus_rw(); SerialPort *serial_port(); @@ -85,6 +96,25 @@ class Machine : public QObject { bool get_step_over_exception(enum ExceptionCause excause) const; enum ExceptionCause get_exception_cause() const; + void map_elf_segments(ProgramLoader &program, Memory *mem, uint32_t root_ppn, uint32_t base_va); + + uint32_t allocate_page() { + uint32_t ppn = next_free_ppn++; + constexpr uint32_t PAGE_SIZE = 1u << PAGE_SHIFT; + uint64_t phys_base = uint64_t(ppn) << PAGE_SHIFT; + std::vector zeros(PAGE_SIZE, 0); + memory_rw()->write( Offset(phys_base), zeros.data(), PAGE_SIZE, { .type = ae::INTERNAL } ); + return ppn; + } + + Address virtualToPhysical(Address v) { + if (tlb_data) { + return tlb_data->translate_virtual_to_physical(v); + } else { + return v; + } + } + public slots: void play(); void pause(); @@ -128,6 +158,11 @@ private slots: aclint::AclintMtimer *aclint_mtimer = nullptr; aclint::AclintMswi *aclint_mswi = nullptr; aclint::AclintSswi *aclint_sswi = nullptr; + std::optional tlb_program; + std::optional tlb_data; + FrontendMemory *instr_if_; + FrontendMemory *data_if_; + uint32_t next_free_ppn = 0; Cache *cch_program = nullptr; Cache *cch_data = nullptr; Cache *cch_level2 = nullptr; diff --git a/src/machine/machineconfig.cpp b/src/machine/machineconfig.cpp index 0a4e74bf..27c51f43 100644 --- a/src/machine/machineconfig.cpp +++ b/src/machine/machineconfig.cpp @@ -1,8 +1,11 @@ #include "machineconfig.h" #include "common/endian.h" +#include "machine.h" #include +#include +#include #include using namespace machine; @@ -27,6 +30,11 @@ using namespace machine; #define DFC_BP_BTB_BITS 2 #define DFC_BP_BHR_BITS 0 #define DFC_BP_BHT_ADDR_BITS 2 +/// Default config of Virtual Memory +#define DFC_VM_ENABLED false +#define DFC_KERNEL_VIRT_BASE 0x80000000u +#define DFC_TLB_SETS 64 +#define DFC_TLB_ASSOC 4 ////////////////////////////////////////////////////////////////////////////// /// Default config of CacheConfig #define DFC_EN false @@ -186,6 +194,13 @@ MachineConfig::MachineConfig() { bp_bhr_bits = DFC_BP_BHR_BITS; bp_bht_addr_bits = DFC_BP_BHT_ADDR_BITS; bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; + + // Virtual Memory + vm_enabled = DFC_VM_ENABLED; + va_base_addr = DFC_KERNEL_VIRT_BASE; + tlb_num_sets = DFC_TLB_SETS; + tlb_associativity = DFC_TLB_ASSOC; + tlb_policy = TP_LRU; } MachineConfig::MachineConfig(const MachineConfig *config) { @@ -222,6 +237,14 @@ MachineConfig::MachineConfig(const MachineConfig *config) { bp_bhr_bits = config->get_bp_bhr_bits(); bp_bht_addr_bits = config->get_bp_bht_addr_bits(); bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; + + // Virtual Memory + vm_enabled = config->get_vm_enabled(); + vm_mode = config->get_vm_mode(); + va_base_addr = config->get_va_base_addr(); + tlb_num_sets = config->get_tlb_num_sets(); + tlb_associativity = config->get_tlb_associativity(); + tlb_policy = config->get_tlb_replacement_policy(); } #define N(STR) (prefix + QString(STR)) @@ -266,6 +289,13 @@ MachineConfig::MachineConfig(const QSettings *sts, const QString &prefix) { bp_bhr_bits = sts->value(N("BranchPredictor_BitsBHR"), DFC_BP_BHR_BITS).toUInt(); bp_bht_addr_bits = sts->value(N("BranchPredictor_BitsBHTAddr"), DFC_BP_BHT_ADDR_BITS).toUInt(); bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; + + // Virtual Memory + vm_enabled = sts->value(N("VMEnabled"), DFC_VM_ENABLED).toBool(); + va_base_addr = sts->value(N("KernelVirtBase"), DFC_KERNEL_VIRT_BASE).toUInt(); + tlb_num_sets = sts->value(N("TLB_NumSets"), 64).toUInt(); + tlb_associativity = sts->value(N("TLB_Associativity"), 4).toUInt(); + tlb_policy = TLBPolicy(sts->value(N("TLB_Policy"), (int)TP_LRU).toInt()); } void MachineConfig::store(QSettings *sts, const QString &prefix) { @@ -298,6 +328,13 @@ void MachineConfig::store(QSettings *sts, const QString &prefix) { sts->setValue(N("BranchPredictor_BitsBTB"), get_bp_btb_bits()); sts->setValue(N("BranchPredictor_BitsBHR"), get_bp_bhr_bits()); sts->setValue(N("BranchPredictor_BitsBHTAddr"), get_bp_bht_addr_bits()); + + // Virtual memory + sts->setValue(N("VMEnabled"), get_vm_enabled()); + sts->setValue(N("KernelVirtBase"), get_va_base_addr()); + sts->setValue(N("TLB_NumSets"), get_tlb_num_sets()); + sts->setValue(N("TLB_Associativity"), get_tlb_associativity()); + sts->setValue(N("TLB_Policy"), int(get_tlb_replacement_policy())); } #undef N @@ -629,12 +666,60 @@ uint8_t MachineConfig::get_bp_bht_bits() const { return bp_bht_bits; } +bool MachineConfig::get_vm_enabled() const { + return vm_enabled; +} + +void MachineConfig::set_vm_enabled(bool e) { + vm_enabled = e; +} + +void MachineConfig::set_vm_mode(VmMode m) { + vm_mode = m; +} +MachineConfig::VmMode MachineConfig::get_vm_mode() const { + return vm_mode; +} + +void MachineConfig::set_vm_asid(uint32_t a) { + vm_asid = a; +} +uint32_t MachineConfig::get_vm_asid() const { + return vm_asid; +} + +void MachineConfig::set_va_base_addr(uint32_t v) { + va_base_addr = v; +} +uint32_t MachineConfig::get_va_base_addr() const { + return va_base_addr; +} + +void MachineConfig::set_tlb_num_sets(unsigned v) { + tlb_num_sets = v > 0 ? v : 1; +} +void MachineConfig::set_tlb_associativity(unsigned v) { + tlb_associativity = v > 0 ? v : 1; +} +void MachineConfig::set_tlb_replacement_policy(MachineConfig::TLBPolicy p) { + tlb_policy = p; +} + +unsigned MachineConfig::get_tlb_num_sets() const { + return tlb_num_sets; +} +unsigned MachineConfig::get_tlb_associativity() const { + return tlb_associativity; +} +MachineConfig::TLBPolicy MachineConfig::get_tlb_replacement_policy() const { + return tlb_policy; +} + bool MachineConfig::operator==(const MachineConfig &c) const { #define CMP(GETTER) (GETTER)() == (c.GETTER)() return CMP(pipelined) && CMP(delay_slot) && CMP(hazard_unit) && CMP(get_simulated_xlen) - && CMP(get_isa_word) && CMP(get_bp_enabled) && CMP(get_bp_type) - && CMP(get_bp_init_state) && CMP(get_bp_btb_bits) - && CMP(get_bp_bhr_bits) && CMP(get_bp_bht_addr_bits) + && CMP(get_isa_word) && CMP(get_bp_enabled) && CMP(get_bp_type) && CMP(get_bp_init_state) + && CMP(get_bp_btb_bits) && CMP(get_bp_bhr_bits) && CMP(get_bp_bht_addr_bits) && CMP(memory_execute_protection) && CMP(memory_write_protection) && CMP(memory_access_time_read) && CMP(memory_access_time_write) && CMP(memory_access_time_burst) && CMP(memory_access_time_level2) diff --git a/src/machine/machineconfig.h b/src/machine/machineconfig.h index f331d1eb..733c3f3f 100644 --- a/src/machine/machineconfig.h +++ b/src/machine/machineconfig.h @@ -54,7 +54,6 @@ class CacheConfig { WP_THROUGH_ALLOC, // Write through WP_BACK // Write back }; - // If cache should be used or not void set_enabled(bool); void set_set_count(unsigned); // Number of sets @@ -93,6 +92,8 @@ class MachineConfig { enum HazardUnit { HU_NONE, HU_STALL, HU_STALL_FORWARD }; + enum VmMode { VM_BARE = 0, VM_SV32 = 1 }; + // Configure if CPU is pipelined // In default disabled. void set_pipelined(bool); @@ -176,6 +177,27 @@ class MachineConfig { uint8_t get_bp_bht_addr_bits() const; uint8_t get_bp_bht_bits() const; + // Virtual Memory + bool get_vm_enabled() const; + void set_vm_enabled(bool e); + + void set_vm_mode(VmMode m); + VmMode get_vm_mode() const; + + void set_vm_asid(uint32_t a); + uint32_t get_vm_asid() const; + + void set_va_base_addr(uint32_t v); + uint32_t get_va_base_addr() const; + + enum TLBPolicy { TP_RAND=0, TP_LRU, TP_LFU, TP_PLRU }; + void set_tlb_num_sets(unsigned); + void set_tlb_associativity(unsigned); + void set_tlb_replacement_policy(TLBPolicy); + unsigned get_tlb_num_sets() const; + unsigned get_tlb_associativity() const; + TLBPolicy get_tlb_replacement_policy() const; + CacheConfig *access_cache_program(); CacheConfig *access_cache_data(); CacheConfig *access_cache_level2(); @@ -207,6 +229,16 @@ class MachineConfig { uint8_t bp_bhr_bits; uint8_t bp_bht_addr_bits; uint8_t bp_bht_bits; // = bp_bhr_bits + bp_bht_addr_bits + + // Virtual Memory + bool vm_enabled; + uint32_t va_base_addr; + unsigned tlb_num_sets; + unsigned tlb_associativity; + TLBPolicy tlb_policy; + + VmMode vm_mode = VM_BARE; + uint32_t vm_asid = 0; }; } // namespace machine diff --git a/src/machine/memory/frontend_memory.cpp b/src/machine/memory/frontend_memory.cpp index a5e2205e..3cce115a 100644 --- a/src/machine/memory/frontend_memory.cpp +++ b/src/machine/memory/frontend_memory.cpp @@ -1,34 +1,26 @@ #include "memory/frontend_memory.h" #include "common/endian.h" +#include "tlb/tlb.h" + +#include +#include namespace machine { -bool FrontendMemory::write_u8( - Address address, - uint8_t value, - AccessEffects type) { +bool FrontendMemory::write_u8(Address address, uint8_t value, AccessEffects type) { return write_generic(address, value, type); } -bool FrontendMemory::write_u16( - Address address, - uint16_t value, - AccessEffects type) { +bool FrontendMemory::write_u16(Address address, uint16_t value, AccessEffects type) { return write_generic(address, value, type); } -bool FrontendMemory::write_u32( - Address address, - uint32_t value, - AccessEffects type) { +bool FrontendMemory::write_u32(Address address, uint32_t value, AccessEffects type) { return write_generic(address, value, type); } -bool FrontendMemory::write_u64( - Address address, - uint64_t value, - AccessEffects type) { +bool FrontendMemory::write_u64(Address address, uint64_t value, AccessEffects type) { return write_generic(address, value, type); } @@ -48,10 +40,7 @@ uint64_t FrontendMemory::read_u64(Address address, AccessEffects type) const { return read_generic(address, type); } -void FrontendMemory::write_ctl( - enum AccessControl ctl, - Address offset, - RegisterValue value) { +void FrontendMemory::write_ctl(enum AccessControl ctl, Address offset, RegisterValue value) { switch (ctl) { case AC_NONE: { break; @@ -84,8 +73,7 @@ void FrontendMemory::write_ctl( } } -RegisterValue -FrontendMemory::read_ctl(enum AccessControl ctl, Address address) const { +RegisterValue FrontendMemory::read_ctl(enum AccessControl ctl, Address address) const { switch (ctl) { case AC_NONE: return 0; case AC_I8: return (int8_t)read_u8(address); @@ -142,13 +130,17 @@ T FrontendMemory::read_generic(Address address, AccessEffects type) const { } template -bool FrontendMemory::write_generic( - Address address, - const T value, - AccessEffects type) { +bool FrontendMemory::write_generic(Address address, const T value, AccessEffects type) { // See example in read_generic for byteswap explanation. - const T swapped_value - = byteswap_if(value, this->simulated_machine_endian != NATIVE_ENDIAN); + const T swapped_value = byteswap_if(value, this->simulated_machine_endian != NATIVE_ENDIAN); + + if (auto tlb = dynamic_cast(this)) { + return tlb + ->write_vaddr( + VirtualAddress { address.get_raw() }, &swapped_value, sizeof(T), { .type = type }) + .changed; + } + return write(address, &swapped_value, sizeof(T), { .type = type }).changed; } FrontendMemory::FrontendMemory(Endian simulated_endian) diff --git a/src/machine/memory/frontend_memory.h b/src/machine/memory/frontend_memory.h index bab9a9b8..52654edb 100644 --- a/src/machine/memory/frontend_memory.h +++ b/src/machine/memory/frontend_memory.h @@ -7,6 +7,7 @@ #include "memory/memory_utils.h" #include "register_value.h" #include "simulator_exception.h" +#include "virtual/virtual_address.h" #include #include @@ -120,6 +121,22 @@ class FrontendMemory : public QObject { size_t size, ReadOptions options) const = 0; + virtual WriteResult write_vaddr( + VirtualAddress vaddr, + const void *source, + size_t size, + WriteOptions options) { + return write(Address{uint64_t(vaddr)}, source, size, options); + } + + virtual ReadResult read_vaddr( + void *destination, + VirtualAddress vaddr, + size_t size, + ReadOptions options) const { + return read(destination, Address{uint64_t(vaddr)}, size, options); + } + /** * Endian of the simulated CPU/memory system. * diff --git a/src/machine/memory/tlb/tlb.cpp b/src/machine/memory/tlb/tlb.cpp new file mode 100644 index 00000000..aef996d0 --- /dev/null +++ b/src/machine/memory/tlb/tlb.cpp @@ -0,0 +1,153 @@ +#include "tlb.h" + +#include "csr/controlstate.h" +#include "machine.h" +#include "simulator_exception.h" + +LOG_CATEGORY("machine.TLB"); + +namespace machine { + +static bool is_mmio_region(uint64_t virt) { + if (virt >= 0xFFFFC000u && virt <= 0xFFFFC1FFu) return true; + if (virt >= 0xFFE00000u && virt <= 0xFFE4AFFFu) return true; + if (virt >= 0xFFFD0000u && virt <= 0xFFFDBFFFu) return true; + return false; +} + +static Address bypass_mmio(Address vaddr) { + return vaddr; // VA == PA for devices +} + +TLB::TLB(FrontendMemory *memory, TLBType type_, Machine *mach) + : FrontendMemory(memory->simulated_machine_endian) + , mem(memory) + , ptw(memory) + , type(type_) + , machine(mach) { + auto &cfg = machine->config(); + num_sets_ = cfg.get_tlb_num_sets(); + associativity_ = cfg.get_tlb_associativity(); + auto pol = cfg.get_tlb_replacement_policy(); + repl_policy = make_tlb_policy(static_cast(pol), associativity_, num_sets_); + table.assign(num_sets_, std::vector(associativity_)); + const char *tag = (type == PROGRAM ? "I" : "D"); + LOG("TLB[%s] constructed; sets=%zu way=%zu", tag, num_sets_, associativity_); +} + +void TLB::on_csr_write(size_t internal_id, RegisterValue val) { + if (internal_id != CSR::Id::SATP) return; + current_satp_raw = static_cast(val.as_u64()); + for (size_t s = 0; s < num_sets_; s++) { + for (size_t w = 0; w < associativity_; w++) { + table[s][w].valid = false; + } + } + LOG("TLB: SATP changed → flushed all; new SATP=0x%08x", current_satp_raw); +} + +void TLB::flush_single(VirtualAddress va, uint16_t asid) { + uint64_t vpn = va.get_raw() >> 12; + size_t s = set_index(vpn); + for (size_t w = 0; w < associativity_; w++) { + auto &e = table[s][w]; + if (e.valid && e.vpn == vpn && e.asid == asid) { + e.valid = false; + const char *tag = (type == PROGRAM ? "I" : "D"); + LOG("TLB[%s]: flushed VA=0x%llx ASID=%u", tag, (unsigned long long)va.get_raw(), asid); + } + } +} + +Address TLB::translate_virtual_to_physical(Address vaddr) { + uint64_t virt = vaddr.get_raw(); + if (is_mmio_region(virt)) { return bypass_mmio(vaddr); } + + if (((current_satp_raw >> 31) & 1) == 0) { return vaddr; } + + VirtualAddress va { virt }; + uint16_t asid = (current_satp_raw >> 22) & 0x1FF; + uint64_t vpn = virt >> 12; + uint64_t off = virt & ((1ULL << PAGE_SHIFT) - 1); + size_t s = set_index(vpn); + const char *tag = (type == PROGRAM ? "I" : "D"); + + for (size_t w = 0; w < associativity_; w++) { + auto &e = table[s][w]; + if (e.valid && e.vpn == vpn && e.asid == asid) { + repl_policy->notify_access(s, w, /*valid=*/true); + uint64_t pbase = e.phys.get_raw() & ~((1ULL << PAGE_SHIFT) - 1); + return Address { pbase + off }; + } + } + + Address leaf_pa; + try { + leaf_pa = ptw.walk(va, current_satp_raw); + } catch (const SimulatorExceptionPageFault &pf) { + if (pf_handler) { + auto *rw_core = const_cast(machine->core()); + auto *rw_regs = const_cast(machine->registers()); + pf_handler->handle_exception( + rw_core, rw_regs, + (type == PROGRAM ? EXCAUSE_INSN_PAGE_FAULT : EXCAUSE_LOAD_PAGE_FAULT), + Address { 0 }, Address { 0 }, Address { 0 }, vaddr); + } + flush_single(va, asid); + leaf_pa = ptw.walk(va, current_satp_raw); + } + + uint64_t new_ppn = leaf_pa.get_raw() >> 12; + size_t victim = repl_policy->select_way(s); + + auto &ent = table[s][victim]; + ent.valid = true; + ent.asid = asid; + ent.vpn = vpn; + ent.phys = Address { new_ppn << PAGE_SHIFT }; + repl_policy->notify_access(s, victim, /*valid=*/true); + + LOG("TLB[%s]: mapped VA=0x%llx → PA=0x%llx (ASID=%u)", tag, (unsigned long long)virt, + (unsigned long long)(new_ppn << PAGE_SHIFT), asid); + + return Address { (new_ppn << PAGE_SHIFT) + off }; +} + +WriteResult TLB::translate_and_write(Address dst, const void *src, size_t sz, WriteOptions opts) { + if (first_instr_written) { + first_instr_written = false; + emit firstWrite(VirtualAddress { uint64_t(dst) }); // Update pc + } + Address pa = translate_virtual_to_physical(dst); + return mem->write(pa, src, sz, opts); +} + +ReadResult TLB::translate_and_read(void *dst, Address src, size_t sz, ReadOptions opts) { + Address pa = translate_virtual_to_physical(src); + return mem->read(dst, pa, sz, opts); +} + +WriteResult TLB::write_vaddr(VirtualAddress dst, const void *src, size_t sz, WriteOptions opts) { + return translate_and_write(Address { uint64_t(dst) }, src, sz, opts); +} + +ReadResult TLB::read_vaddr(void *dst, VirtualAddress src, size_t sz, ReadOptions opts) const { + return const_cast(this)->translate_and_read(dst, Address { uint64_t(src) }, sz, opts); +} + +bool TLB::reverse_lookup(Address paddr, VirtualAddress &out_va) const { + uint64_t ppn = paddr.get_raw() >> 12; + uint64_t offset = paddr.get_raw() & 0xFFF; + for (size_t s = 0; s < num_sets_; s++) { + for (size_t w = 0; w < associativity_; w++) { + auto &e = table[s][w]; + if (e.valid && (e.phys.get_raw() >> 12) == ppn) { + out_va = VirtualAddress { (e.vpn << 12) | offset }; + return true; + } + } + } + return false; +} + +} // namespace machine diff --git a/src/machine/memory/tlb/tlb.h b/src/machine/memory/tlb/tlb.h new file mode 100644 index 00000000..54074cf3 --- /dev/null +++ b/src/machine/memory/tlb/tlb.h @@ -0,0 +1,85 @@ +#ifndef TLB_H +#define TLB_H + +#include "common/logging.h" +#include "memory/frontend_memory.h" +#include "memory/virtual/page_fault_handler.h" +#include "memory/virtual/page_table_walker.h" +#include "tlb_policy.h" + +#include + +namespace machine { +enum TLBType { PROGRAM, DATA }; +class Machine; + +// Implements a set-associative Translation Lookaside Buffer (TLB) frontend over physical memory, +// handling virtual to physical translation, flush, and replacement policy. +class TLB : public FrontendMemory { + Q_OBJECT +public: + TLB(FrontendMemory *memory, TLBType type, Machine *mach); + + void on_csr_write(size_t internal_id, RegisterValue val); + void flush_single(VirtualAddress va, uint16_t asid); + + Address translate_virtual_to_physical(Address va); + + WriteResult write(Address dst, const void *src, size_t sz, WriteOptions opts) override { + return translate_and_write(dst, src, sz, opts); + } + ReadResult read(void *dst, Address src, size_t sz, ReadOptions opts) const override { + return const_cast(this)->translate_and_read(dst, src, sz, opts); + } + + WriteResult write_vaddr(VirtualAddress dst, const void *src, size_t sz, WriteOptions opts) override; + ReadResult read_vaddr(void *dst, VirtualAddress src, size_t sz, ReadOptions opts) const override; + + uint32_t get_change_counter() const override { + return mem->get_change_counter(); + } + + void set_replacement_policy(std::unique_ptr p) { + repl_policy = std::move(p); + } + + bool reverse_lookup(Address paddr, VirtualAddress &out_va) const; + + void set_page_fault_handler(PageFaultHandler *h) { pf_handler = h; } + +signals: + void firstWrite(VirtualAddress va); + +private: + struct Entry { + bool valid = false; + uint16_t asid = 0; + uint64_t vpn = 0; + Address phys = Address{0}; + uint32_t lru = 0; + }; + + FrontendMemory *mem; + PageTableWalker ptw; + TLBType type; + Machine *machine; + PageFaultHandler *pf_handler = nullptr; + uint32_t current_satp_raw = 0; + bool first_instr_written = true; + + size_t num_sets_; + size_t associativity_; + std::vector> table; + std::unique_ptr repl_policy; + + WriteResult translate_and_write(Address dst, const void *src, size_t sz, WriteOptions opts); + ReadResult translate_and_read(void *dst, Address src, size_t sz, ReadOptions opts); + + inline size_t set_index(uint64_t vpn) const { + return vpn & (num_sets_ - 1); + } +}; + +} + +#endif //TLB_H \ No newline at end of file diff --git a/src/machine/memory/tlb/tlb_policy.cpp b/src/machine/memory/tlb/tlb_policy.cpp new file mode 100644 index 00000000..93c7eb3f --- /dev/null +++ b/src/machine/memory/tlb/tlb_policy.cpp @@ -0,0 +1,108 @@ +#include "tlb_policy.h" + +#include +#include +#include +#include + +namespace machine { + +TLBPolicyRAND::TLBPolicyRAND(size_t assoc) : associativity(assoc) { + std::srand(1); +} +size_t TLBPolicyRAND::select_way(size_t) const { + return std::rand() % associativity; +} +void TLBPolicyRAND::notify_access(size_t, size_t, bool) { + /* no state */ +} + +TLBPolicyLRU::TLBPolicyLRU(size_t assoc, size_t sets) : associativity(assoc), set_count(sets) { + stats.resize(sets, std::vector(assoc)); + for (auto &row : stats) { + std::iota(row.begin(), row.end(), 0); + } +} +size_t TLBPolicyLRU::select_way(size_t set) const { + return stats[set][0]; +} +void TLBPolicyLRU::notify_access(size_t set, size_t way, bool valid) { + auto &row = stats[set]; + uint32_t next = way; + if (valid) { + for (int i = int(row.size()) - 1; i >= 0; --i) { + std::swap(row[i], next); + if (next == way) break; + } + } else { + for (unsigned int &i : row) { + std::swap(i, next); + if (next == way) break; + } + } +} + +TLBPolicyLFU::TLBPolicyLFU(size_t assoc, size_t sets) : associativity(assoc), set_count(sets) { + stats.assign(sets, std::vector(assoc, 0)); +} +size_t TLBPolicyLFU::select_way(size_t set) const { + const auto &row = stats[set]; + size_t idx = 0; + uint32_t minv = row[0]; + for (size_t i = 1; i < row.size(); ++i) { + if (row[i] == 0 || row[i] < minv) { + minv = row[i]; + idx = i; + if (minv == 0) break; + } + } + return idx; +} +void TLBPolicyLFU::notify_access(size_t set, size_t way, bool valid) { + if (valid) { + stats[set][way]++; + } else { + stats[set][way] = 0; + } +} + +TLBPolicyPLRU::TLBPolicyPLRU(size_t assoc, size_t sets) + : associativity(assoc) + , set_count(sets) + , c_log2(std::ceil(std::log2(float(assoc)))) { + size_t tree_size = (1u << c_log2) - 1; + tree.assign(sets, std::vector(tree_size, 0)); +} +size_t TLBPolicyPLRU::select_way(size_t set) const { + const auto &bits = tree[set]; + size_t idx = 0; + size_t node = 0; + for (size_t lvl = 0; lvl < c_log2; ++lvl) { + uint8_t b = bits[node]; + idx = (idx << 1) | b; + node = ((1u << (lvl + 1)) - 1) + idx; + } + return std::min(idx, associativity - 1); +} +void TLBPolicyPLRU::notify_access(size_t set, size_t way, bool) { + auto &bits = tree[set]; + size_t node = 0; + for (size_t lvl = 0; lvl < c_log2; ++lvl) { + uint8_t dir = (way >> (c_log2 - lvl - 1)) & 1; + bits[node] = dir ? 0 : 1; + node = ((1u << (lvl + 1)) - 1) + ((dir ? 1 : 0)); + } +} + +std::unique_ptr +make_tlb_policy(TLBPolicyKind kind, size_t associativity, size_t set_count) { + switch (kind) { + case TLBPolicyKind::RAND: return std::make_unique(associativity); + case TLBPolicyKind::LRU: return std::make_unique(associativity, set_count); + case TLBPolicyKind::LFU: return std::make_unique(associativity, set_count); + case TLBPolicyKind::PLRU: return std::make_unique(associativity, set_count); + } + return std::make_unique(associativity, set_count); +} + +} // namespace machine diff --git a/src/machine/memory/tlb/tlb_policy.h b/src/machine/memory/tlb/tlb_policy.h new file mode 100644 index 00000000..cf81d80c --- /dev/null +++ b/src/machine/memory/tlb/tlb_policy.h @@ -0,0 +1,71 @@ +#ifndef TLB_POLICY_H +#define TLB_POLICY_H + +#include +#include +#include +#include + +namespace machine { + +// Abstract TLB replacement policy interface & implementations (RAND, LRU, LFU, PLRU) for set-associative tables. +class TLBPolicy { +public: + virtual size_t select_way(size_t set) const = 0; + + virtual void notify_access(size_t set, size_t way, bool valid) = 0; + + virtual ~TLBPolicy() = default; +}; + +class TLBPolicyRAND final : public TLBPolicy { + size_t associativity; + +public: + explicit TLBPolicyRAND(size_t assoc); + size_t select_way(size_t set) const override; + void notify_access(size_t set, size_t way, bool valid) override; +}; + + +class TLBPolicyLRU final : public TLBPolicy { + size_t associativity; + size_t set_count; + std::vector> stats; +public: + TLBPolicyLRU(size_t assoc, size_t sets); + size_t select_way(size_t set) const override; + void notify_access(size_t set, size_t way, bool valid) override; +}; + +class TLBPolicyLFU final : public TLBPolicy { + size_t associativity; + size_t set_count; + std::vector> stats; +public: + TLBPolicyLFU(size_t assoc, size_t sets); + size_t select_way(size_t set) const override; + void notify_access(size_t set, size_t way, bool valid) override; +}; + +class TLBPolicyPLRU final : public TLBPolicy { + size_t associativity; + size_t set_count; + size_t c_log2; + std::vector> tree; +public: + TLBPolicyPLRU(size_t assoc, size_t sets); + size_t select_way(size_t set) const override; + void notify_access(size_t set, size_t way, bool valid) override; +}; + +enum class TLBPolicyKind { RAND, LRU, LFU, PLRU }; +std::unique_ptr make_tlb_policy( + TLBPolicyKind kind, + size_t associativity, + size_t set_count +); + +} + +#endif // TLB_POLICY_H diff --git a/src/machine/memory/virtual/page_fault_handler.cpp b/src/machine/memory/virtual/page_fault_handler.cpp new file mode 100644 index 00000000..f88cb9a5 --- /dev/null +++ b/src/machine/memory/virtual/page_fault_handler.cpp @@ -0,0 +1,96 @@ +#include "page_fault_handler.h" + +#include "common/logging.h" +#include "machine.h" +#include "simulator_exception.h" +#include "sv32.h" + +LOG_CATEGORY("machine.PageFaultHandler"); + +using namespace machine; + +PageFaultHandler::PageFaultHandler(Machine *m, TLB &tlb_) : machine(m), tlb(tlb_) {} + +bool PageFaultHandler::handle_exception( + Core *core, + Registers * /*regs*/, + ExceptionCause excause, + Address /*inst_addr*/, + Address /*next_addr*/, + Address /*jump_branch_pc*/, + Address fault_addr) { + if (excause != EXCAUSE_INSN_PAGE_FAULT && excause != EXCAUSE_LOAD_PAGE_FAULT + && excause != EXCAUSE_STORE_PAGE_FAULT) { + return true; + } + + VirtualAddress va(fault_addr.get_raw()); + uint32_t raw_satp = core->get_control_state()->read_internal(CSR::Id::SATP).as_u32(); + uint16_t asid = (raw_satp >> 22) & 0x1FF; + LOG("PageFaultHandler: allocating page for VA=0x%08" PRIx64, va.get_raw()); + perform_page_allocation(va, raw_satp); + tlb.flush_single(va, asid); + (void)tlb.translate_virtual_to_physical(Address { va.get_raw() }); + + return false; +} + +void PageFaultHandler::perform_page_allocation(const VirtualAddress &va, uint32_t raw_satp) { + constexpr unsigned PAGE_SHIFT = 12; + constexpr uint32_t VPN_MASK = (1u << VPN_BITS) - 1; + + uint32_t root_ppn = raw_satp & ((1u << PPN_BITS) - 1); + + uint32_t va_raw = static_cast(va.get_raw()); + uint32_t vpn1 = (va_raw >> VPN1_SHIFT) & VPN_MASK; + uint32_t vpn0 = (va_raw >> VPN0_SHIFT) & VPN_MASK; + + uint32_t current_ppn = root_ppn; + Address pte_addr; + Sv32Pte pte; + + for (int lvl = 1; lvl >= 0; --lvl) { + uint32_t idx = (lvl == 1 ? vpn1 : vpn0); + pte_addr = Address { (uint64_t(current_ppn) << PAGE_SHIFT) + idx * sizeof(uint32_t) }; + + uint32_t raw; + machine->memory_rw()->read( + &raw, Offset(pte_addr.get_raw()), sizeof(raw), { .type = ae::INTERNAL }); + pte = Sv32Pte::from_uint(raw); + + if (!pte.is_valid()) { + if (lvl == 0) { break; } + uint32_t new_ppn = machine->allocate_page(); + Sv32Pte new_pte {}; + new_pte.v = 1; + new_pte.ppn = new_ppn; + uint32_t bits = new_pte.to_uint(); + machine->memory_rw()->write( + Offset(pte_addr.get_raw()), &bits, sizeof(bits), { .type = ae::INTERNAL }); + + LOG("PageFaultHandler: L%u PT alloc PPN=0x%x for VA=0x%08" PRIx64, lvl - 1, new_ppn, + va.get_raw()); + + current_ppn = new_ppn; + continue; + } + + if (pte.is_leaf()) { + throw SIMULATOR_EXCEPTION( + PageFault, "Unexpected leaf PTE during page table creation", + QString::number(pte_addr.get_raw(), 16)); + } + current_ppn = pte.ppn; + } + + uint32_t data_ppn = machine->allocate_page(); + Sv32Pte leaf {}; + leaf.v = leaf.r = leaf.w = leaf.x = leaf.a = leaf.d = 1; + leaf.ppn = data_ppn; + + uint32_t leaf_bits = leaf.to_uint(); + machine->memory_rw()->write( + Offset(pte_addr.get_raw()), &leaf_bits, sizeof(leaf_bits), { .type = ae::INTERNAL }); + + LOG("PageFaultHandler: mapped VA=0x%08" PRIx64 " → PPN=0x%x", va.get_raw(), data_ppn); +} diff --git a/src/machine/memory/virtual/page_fault_handler.h b/src/machine/memory/virtual/page_fault_handler.h new file mode 100644 index 00000000..e2f1868a --- /dev/null +++ b/src/machine/memory/virtual/page_fault_handler.h @@ -0,0 +1,37 @@ +#ifndef PAGE_FAULT_HANDLER_H +#define PAGE_FAULT_HANDLER_H + +#include "core.h" +#include "memory/address.h" +#include "virtual_address.h" + +namespace machine { +class TLB; +class Machine; + +// Catches page-fault exceptions, invokes page allocation, and recovers execution. +class PageFaultHandler : public ExceptionHandler { +public: + PageFaultHandler(Machine *m, TLB &tlb); + + bool handle_exception( + Core *core, + Registers *regs, + ExceptionCause excause, + Address inst_addr, + Address next_addr, + Address jump_branch_pc, + Address fault_addr) override; + + + +private: + Machine *machine; + TLB &tlb; + + void perform_page_allocation(const VirtualAddress &va, uint32_t raw_satp); +}; + +} + +#endif // PAGE_FAULT_HANDLER_H diff --git a/src/machine/memory/virtual/page_table_walker.cpp b/src/machine/memory/virtual/page_table_walker.cpp new file mode 100644 index 00000000..8f8dc17f --- /dev/null +++ b/src/machine/memory/virtual/page_table_walker.cpp @@ -0,0 +1,54 @@ +#include "page_table_walker.h" + +#include "common/logging.h" +#include "machine.h" +#include "memory/backend/backend_memory.h" + +#include + +LOG_CATEGORY("machine.PTW"); + +namespace machine { + +Address PageTableWalker::walk(const VirtualAddress &va, uint32_t raw_satp) const { + if (((raw_satp >> 31) & 1) == 0) return Address { va.get_raw() }; + + auto root_ppn = raw_satp & ((1u << 22) - 1); + auto va_raw = static_cast(va.get_raw()); + auto vpn1 = (va_raw >> VPN1_SHIFT) & VPN_MASK; + auto vpn0 = (va_raw >> VPN0_SHIFT) & VPN_MASK; + auto ppn = root_ppn; + + for (int lvl = 1; lvl >= 0; --lvl) { + uint32_t idx = (lvl == 1 ? vpn1 : vpn0); + Address pte_addr { (uint64_t(ppn) << PAGE_SHIFT) + idx * 4 }; + + uint32_t raw_pte; + memory->read(&raw_pte, pte_addr, sizeof(raw_pte), { .type = ae::INTERNAL }); + LOG("PTW: L%u PTE@0x%08" PRIx64 " = 0x%08x", lvl, pte_addr.get_raw(), raw_pte); + + Sv32Pte pte = Sv32Pte::from_uint(raw_pte); + + if (!pte.is_valid()) { + throw SIMULATOR_EXCEPTION( + PageFault, "PTW: page fault, leaf PTE invalid", + QString::number(pte_addr.get_raw(), 16)); + } + + if (pte.is_leaf()) { + Address pa = make_phys(va_raw, pte, lvl); + LOG("PTW: L%u leaf → PA=0x%08" PRIx64, lvl, pa.get_raw()); + return pa; + } + + if (pte.r || pte.w || pte.x) { + throw SIMULATOR_EXCEPTION( + PageFault, "PTW: invalid non-leaf", QString::number(raw_pte, 16)); + } + ppn = pte.ppn; + } + + throw SIMULATOR_EXCEPTION(PageFault, "PTW: no leaf found", ""); +} + +} // namespace machine diff --git a/src/machine/memory/virtual/page_table_walker.h b/src/machine/memory/virtual/page_table_walker.h new file mode 100644 index 00000000..70d3dcc6 --- /dev/null +++ b/src/machine/memory/virtual/page_table_walker.h @@ -0,0 +1,25 @@ +#ifndef PAGE_TABLE_WALKER_H +#define PAGE_TABLE_WALKER_H + +#include "memory/frontend_memory.h" +#include "sv32.h" +#include "virtual_address.h" + +#include + +namespace machine { + +// Performs multi-level page-table walks (SV32) in memory to resolve a virtual address to a physical one. +class PageTableWalker { +public: + explicit PageTableWalker(FrontendMemory *mem) : memory(mem) {} + + Address walk(const VirtualAddress &va, uint32_t raw_satp) const; + +private: + FrontendMemory *memory; +}; + +} + +#endif //PAGE_TABLE_WALKER_H diff --git a/src/machine/memory/virtual/sv32.h b/src/machine/memory/virtual/sv32.h new file mode 100644 index 00000000..48b57473 --- /dev/null +++ b/src/machine/memory/virtual/sv32.h @@ -0,0 +1,68 @@ +#ifndef SV32_H +#define SV32_H + +// SV32-specific definitions: page-table entry (PTE) bitfields, shifts/masks, and PTE to physical address helpers. +namespace machine { + static constexpr unsigned PAGE_SHIFT = 12; + static constexpr unsigned VPN_BITS = 10; + static constexpr unsigned VPN0_SHIFT = PAGE_SHIFT; + static constexpr unsigned VPN1_SHIFT = PAGE_SHIFT + VPN_BITS; + static constexpr unsigned VPN_MASK = (1u << VPN_BITS) - 1; + static constexpr unsigned PPN_BITS = 22; + static constexpr unsigned PPN_MASK = (1u << PPN_BITS) - 1; + static constexpr uint32_t PHYS_PPN_START = 0x200; + + struct Sv32Pte { + uint32_t v : 1; + uint32_t r : 1; + uint32_t w : 1; + uint32_t x : 1; + uint32_t u : 1; + uint32_t g : 1; + uint32_t a : 1; + uint32_t d : 1; + uint32_t rsw : 2; + uint32_t ppn : PPN_BITS; + + constexpr uint32_t to_uint() const { + return (ppn << 10) | + (d << 7) | + (a << 6) | + (g << 5) | + (u << 4) | + (x << 3) | + (w << 2) | + (r << 1) | + (v << 0); + } + + static constexpr Sv32Pte from_uint(uint32_t raw) { + Sv32Pte p{}; + p.v = (raw >> 0) & 0x1; + p.r = (raw >> 1) & 0x1; + p.w = (raw >> 2) & 0x1; + p.x = (raw >> 3) & 0x1; + p.u = (raw >> 4) & 0x1; + p.g = (raw >> 5) & 0x1; + p.a = (raw >> 6) & 0x1; + p.d = (raw >> 7) & 0x1; + p.rsw = (raw >> 8) & 0x3; + p.ppn = (raw >> 10) & PPN_MASK; + return p; + } + bool is_leaf() const { return r || x; } + bool is_valid() const { return v && (!w || r); } + }; + + inline Address make_phys(uint32_t va_raw, const Sv32Pte &pte, int level) { + uint32_t offset = va_raw & ((1u << PAGE_SHIFT) - 1); + uint32_t phys_ppn = pte.ppn; + if (level == 1) { + uint32_t vpn0 = (va_raw >> PAGE_SHIFT) & VPN_MASK; + phys_ppn = (phys_ppn & ~VPN_MASK) | vpn0; + } + return Address { (uint64_t(phys_ppn) << PAGE_SHIFT) | offset }; + } +} + +#endif //SV32_H diff --git a/src/machine/memory/virtual/virtual_address.h b/src/machine/memory/virtual/virtual_address.h new file mode 100644 index 00000000..f0ad8119 --- /dev/null +++ b/src/machine/memory/virtual/virtual_address.h @@ -0,0 +1,173 @@ +#ifndef VIRTUAL_ADDRESS_H +#define VIRTUAL_ADDRESS_H + +#include "utils.h" + +#include +#include + +using std::uint64_t; + +namespace machine { + +// Lightweight VirtualAddress wrapper offering raw access, alignment checks, arithmetic, and comparisons. +class VirtualAddress { +private: + uint64_t data; // Raw virtual address + +public: + constexpr explicit VirtualAddress(uint64_t); + + constexpr VirtualAddress(); + + constexpr VirtualAddress(const VirtualAddress &address) = default; + constexpr VirtualAddress &operator=(const VirtualAddress &address) = default; + + [[nodiscard]] constexpr uint64_t get_raw() const; + + constexpr explicit operator uint64_t() const; + + constexpr static VirtualAddress null(); + + [[nodiscard]] constexpr bool is_null() const; + + template + [[nodiscard]] constexpr bool is_aligned() const; + + /* Equality */ + constexpr inline bool operator==(const VirtualAddress &other) const; + constexpr inline bool operator!=(const VirtualAddress &other) const; + + /* Ordering */ + constexpr inline bool operator<(const VirtualAddress &other) const; + constexpr inline bool operator>(const VirtualAddress &other) const; + constexpr inline bool operator<=(const VirtualAddress &other) const; + constexpr inline bool operator>=(const VirtualAddress &other) const; + + /* Offset arithmetic */ + constexpr inline VirtualAddress operator+(const uint64_t &offset) const; + constexpr inline VirtualAddress operator-(const uint64_t &offset) const; + inline void operator+=(const uint64_t &offset); + inline void operator-=(const uint64_t &offset); + + /* Bitwise */ + constexpr inline VirtualAddress operator&(const uint64_t &mask) const; + constexpr inline VirtualAddress operator|(const uint64_t &mask) const; + constexpr inline VirtualAddress operator^(const uint64_t &mask) const; + constexpr inline VirtualAddress operator>>(const uint64_t &size) const; + constexpr inline VirtualAddress operator<<(const uint64_t &size) const; + + /* Distance arithmetic */ + constexpr inline int64_t operator-(const VirtualAddress &other) const; +}; + +constexpr VirtualAddress operator"" _vaddr(unsigned long long literal) { + return VirtualAddress(literal); +} + +// Implementations + +constexpr VirtualAddress::VirtualAddress(uint64_t address) : data(address) {} + +constexpr VirtualAddress::VirtualAddress() : data(0) {} + +constexpr uint64_t VirtualAddress::get_raw() const { + return data; +} + +constexpr VirtualAddress::operator uint64_t() const { + return this->get_raw(); +} + +constexpr VirtualAddress VirtualAddress::null() { + return VirtualAddress(0x0); +} + +constexpr bool VirtualAddress::is_null() const { + return this->get_raw() == 0; +} + +template +constexpr bool VirtualAddress::is_aligned() const { + return is_aligned_genericdata), T>(this->data); +} + +/* Equality */ + +constexpr bool VirtualAddress::operator==(const VirtualAddress &other) const { + return this->get_raw() == other.get_raw(); +} + +constexpr bool VirtualAddress::operator!=(const VirtualAddress &other) const { + return this->get_raw() != other.get_raw(); +} + +/* Ordering */ + +constexpr bool VirtualAddress::operator<(const VirtualAddress &other) const { + return this->get_raw() < other.get_raw(); +} + +constexpr bool VirtualAddress::operator>(const VirtualAddress &other) const { + return this->get_raw() > other.get_raw(); +} + +constexpr bool VirtualAddress::operator<=(const VirtualAddress &other) const { + return this->get_raw() <= other.get_raw(); +} + +constexpr bool VirtualAddress::operator>=(const VirtualAddress &other) const { + return this->get_raw() >= other.get_raw(); +} + +/* Offset arithmetic */ + +constexpr VirtualAddress VirtualAddress::operator+(const uint64_t &offset) const { + return VirtualAddress(this->get_raw() + offset); +} + +constexpr VirtualAddress VirtualAddress::operator-(const uint64_t &offset) const { + return VirtualAddress(this->get_raw() - offset); +} + +void VirtualAddress::operator+=(const uint64_t &offset) { + data += offset; +} + +void VirtualAddress::operator-=(const uint64_t &offset) { + data -= offset; +} + +/* Bitwise */ + +constexpr VirtualAddress VirtualAddress::operator&(const uint64_t &mask) const { + return VirtualAddress(this->get_raw() & mask); +} + +constexpr VirtualAddress VirtualAddress::operator|(const uint64_t &mask) const { + return VirtualAddress(this->get_raw() | mask); +} + +constexpr VirtualAddress VirtualAddress::operator^(const uint64_t &mask) const { + return VirtualAddress(this->get_raw() ^ mask); +} + +constexpr VirtualAddress VirtualAddress::operator>>(const uint64_t &size) const { + return VirtualAddress(this->get_raw() >> size); +} + +constexpr VirtualAddress VirtualAddress::operator<<(const uint64_t &size) const { + return VirtualAddress(this->get_raw() << size); +} + +/* Distance arithmetic */ + +constexpr int64_t VirtualAddress::operator-(const VirtualAddress &other) const { + return this->get_raw() - other.get_raw(); +} + +} + +Q_DECLARE_METATYPE(machine::VirtualAddress) + +#endif // VIRTUAL_ADDRESS_H diff --git a/src/machine/programloader.cpp b/src/machine/programloader.cpp index adddf42c..f65c0ed1 100644 --- a/src/machine/programloader.cpp +++ b/src/machine/programloader.cpp @@ -21,34 +21,29 @@ ProgramLoader::ProgramLoader(const QString &file) : elf_file(file) { const GElf_Ehdr *elf_ehdr; // Initialize elf library if (elf_version(EV_CURRENT) == EV_NONE) { - throw SIMULATOR_EXCEPTION( - Input, "Elf library initialization failed", elf_errmsg(-1)); + throw SIMULATOR_EXCEPTION(Input, "Elf library initialization failed", elf_errmsg(-1)); } // Open source file - option QIODevice::ExistingOnly cannot be used on Qt // <5.11 if (!elf_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) { throw SIMULATOR_EXCEPTION( Input, - QString("Can't open input elf file for reading (") + QString(file) - + QString(")"), + QString("Can't open input elf file for reading (") + QString(file) + QString(")"), std::strerror(errno)); } // Initialize elf if (!(this->elf = elf_begin(elf_file.handle(), ELF_C_READ, nullptr))) { - throw SIMULATOR_EXCEPTION( - Input, "Elf read begin failed", elf_errmsg(-1)); + throw SIMULATOR_EXCEPTION(Input, "Elf read begin failed", elf_errmsg(-1)); } // Check elf kind if (elf_kind(this->elf) != ELF_K_ELF) { throw SIMULATOR_EXCEPTION( - Input, "Invalid input file elf format, plain elf file expected", - ""); + Input, "Invalid input file elf format, plain elf file expected", ""); } elf_ehdr = gelf_getehdr(this->elf, &this->hdr); if (!elf_ehdr) { - throw SIMULATOR_EXCEPTION( - Input, "Getting elf file header failed", elf_errmsg(-1)); + throw SIMULATOR_EXCEPTION(Input, "Getting elf file header failed", elf_errmsg(-1)); } executable_entry = Address(elf_ehdr->e_entry); @@ -65,13 +60,11 @@ ProgramLoader::ProgramLoader(const QString &file) : elf_file(file) { // Check elf file class, only 32bit architecture is supported. int elf_class; if ((elf_class = gelf_getclass(this->elf)) == ELFCLASSNONE) { - throw SIMULATOR_EXCEPTION( - Input, "Getting elf class failed", elf_errmsg(-1)); + throw SIMULATOR_EXCEPTION(Input, "Getting elf class failed", elf_errmsg(-1)); } // Get number of program sections in elf file if (elf_getphdrnum(this->elf, &this->n_secs)) { - throw SIMULATOR_EXCEPTION( - Input, "Elf program sections count query failed", elf_errmsg(-1)); + throw SIMULATOR_EXCEPTION(Input, "Elf program sections count query failed", elf_errmsg(-1)); } if (elf_class == ELFCLASS32) { @@ -110,8 +103,7 @@ ProgramLoader::ProgramLoader(const QString &file) : elf_file(file) { } } -ProgramLoader::ProgramLoader(const char *file) - : ProgramLoader(QString::fromLocal8Bit(file)) {} +ProgramLoader::ProgramLoader(const char *file) : ProgramLoader(QString::fromLocal8Bit(file)) {} ProgramLoader::~ProgramLoader() { // Close elf @@ -176,9 +168,7 @@ SymbolTable *ProgramLoader::get_symbol_table() { elf_version(EV_CURRENT); while (true) { - if ((scn = elf_nextscn(this->elf, scn)) == nullptr) { - return p_st; - } + if ((scn = elf_nextscn(this->elf, scn)) == nullptr) { return p_st; } gelf_getshdr(scn, &shdr); if (shdr.sh_type == SHT_SYMTAB) { /* found a symbol table, go print it. */ @@ -194,8 +184,8 @@ SymbolTable *ProgramLoader::get_symbol_table() { GElf_Sym sym; gelf_getsym(data, ii, &sym); p_st->add_symbol( - elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value, - sym.st_size, sym.st_info, sym.st_other); + elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value, sym.st_size, sym.st_info, + sym.st_other); } return p_st; @@ -219,3 +209,22 @@ Endian ProgramLoader::get_endian() const { ArchitectureType ProgramLoader::get_architecture_type() const { return architecture_type; } + +std::vector ProgramLoader::get_load_segments() const { + std::vector segs; + if (architecture_type == ARCH32) { + for (size_t idx : indexes_of_load_sections) { + auto &ph = sections_headers.arch32[idx]; + segs.push_back({ + (uint32_t)ph.p_vaddr, (uint32_t)ph.p_filesz, + (uint32_t)ph.p_flags // PF_R|PF_W|PF_X + }); + } + } else { + for (size_t idx : indexes_of_load_sections) { + auto &ph = sections_headers.arch64[idx]; + segs.push_back({ (uint32_t)ph.p_vaddr, (uint32_t)ph.p_filesz, (uint32_t)ph.p_flags }); + } + } + return segs; +} \ No newline at end of file diff --git a/src/machine/programloader.h b/src/machine/programloader.h index f6e9ccc1..c4e49dd2 100644 --- a/src/machine/programloader.h +++ b/src/machine/programloader.h @@ -3,6 +3,7 @@ #include "common/endian.h" #include "memory/backend/memory.h" +#include "memory/frontend_memory.h" #include "symboltable.h" #include @@ -18,6 +19,12 @@ enum ArchitectureType { ARCH64, }; +struct LoadSegment { + uint32_t vaddr; + uint32_t size; + uint32_t flags; // ELF PF_R / PF_W / PF_X bits +}; + class ProgramLoader { public: explicit ProgramLoader(const char *file); @@ -26,6 +33,8 @@ class ProgramLoader { void to_memory(Memory *mem); // Writes all loaded sections to memory TODO: // really to memory ??? + std::vector get_load_segments() const; + Address end(); // Return address after which there is no more code for // sure Address get_executable_entry() const; diff --git a/src/machine/registers.cpp b/src/machine/registers.cpp index 372d7e04..6fee2626 100644 --- a/src/machine/registers.cpp +++ b/src/machine/registers.cpp @@ -77,5 +77,5 @@ void Registers::reset() { write_gp(i, 0); } write_gp(2_reg, SP_INIT.get_raw()); // initialize to safe RAM area - - // corresponds to Linux + // corresponds to Linux } diff --git a/src/machine/simulator_exception.h b/src/machine/simulator_exception.h index cc69982e..6a4bd364 100644 --- a/src/machine/simulator_exception.h +++ b/src/machine/simulator_exception.h @@ -58,6 +58,7 @@ class SimulatorException : public std::exception { EXCEPTION(UnalignedJump, Runtime) \ EXCEPTION(UnknownMemoryControl, Runtime) \ EXCEPTION(OutOfMemoryAccess, Runtime) \ + EXCEPTION(PageFault, Runtime) \ EXCEPTION(Sanity, ) \ EXCEPTION(SyscallUnknown, Runtime)