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)