From ed793471bbae48f3fa4aa485670f3e658226f02f Mon Sep 17 00:00:00 2001 From: gabriel Date: Fri, 31 May 2024 07:27:47 +0200 Subject: [PATCH] adding semhosting --- src/iss/arch/riscv_hart_m_p.h | 6 +- src/iss/arch/riscv_hart_mu_p.h | 6 +- src/iss/semihosting/semihosting.cpp | 210 +++- src/iss/semihosting/semihosting.h | 9 +- src/main.cpp | 3 +- src/vm/asmjit/vm_tgc5c.cpp | 4 +- src/vm/interp/vm_tgc5c.cpp | 4 +- src/vm/tcc/vm_tgc5c.cpp | 4 +- src_test/iss/arch/tgc5a.cpp | 70 ++ src_test/iss/arch/tgc5a.h | 209 ++++ src_test/iss/arch/tgc5b.cpp | 70 ++ src_test/iss/arch/tgc5b.h | 225 ++++ src_test/vm/interp/vm_tgc5a.cpp | 1776 +++++++++++++++++++++++++++ src_test/vm/interp/vm_tgc5b.cpp | 1776 +++++++++++++++++++++++++++ 14 files changed, 4335 insertions(+), 37 deletions(-) create mode 100644 src_test/iss/arch/tgc5a.cpp create mode 100644 src_test/iss/arch/tgc5a.h create mode 100644 src_test/iss/arch/tgc5b.cpp create mode 100644 src_test/iss/arch/tgc5b.h create mode 100644 src_test/vm/interp/vm_tgc5a.cpp create mode 100644 src_test/vm/interp/vm_tgc5b.cpp diff --git a/src/iss/arch/riscv_hart_m_p.h b/src/iss/arch/riscv_hart_m_p.h index 99a7f06..c487f2b 100644 --- a/src/iss/arch/riscv_hart_m_p.h +++ b/src/iss/arch/riscv_hart_m_p.h @@ -292,7 +292,7 @@ public: void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - void set_semihosting_callback(std::function& cb) { semihosting_cb = cb; }; + void set_semihosting_callback(semihosting_cb_t cb) { semihosting_cb = cb; }; protected: struct riscv_instrumentation_if : public iss::instrumentation_if { @@ -351,7 +351,7 @@ protected: bool tohost_lower_written = false; riscv_instrumentation_if instr_if; - std::function semihosting_cb; + semihosting_cb_t semihosting_cb; using mem_type = util::sparse_array; using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; @@ -1283,7 +1283,7 @@ template uint64_t riscv_hart_m_p::e #endif CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; - semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/); + semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); return this->reg.NEXT_PC; } } diff --git a/src/iss/arch/riscv_hart_mu_p.h b/src/iss/arch/riscv_hart_mu_p.h index 362fece..1f6051f 100644 --- a/src/iss/arch/riscv_hart_mu_p.h +++ b/src/iss/arch/riscv_hart_mu_p.h @@ -319,7 +319,7 @@ public: void set_irq_num(unsigned i) { mcause_max_irq = 1 << util::ilog2(i); } - void set_semihosting_callback(std::function& cb) { semihosting_cb = cb; }; + void set_semihosting_callback(semihosting_cb_t cb) { semihosting_cb = cb; }; protected: struct riscv_instrumentation_if : public iss::instrumentation_if { @@ -378,7 +378,7 @@ protected: bool tohost_lower_written = false; riscv_instrumentation_if instr_if; - std::function semihosting_cb; + semihosting_cb_t semihosting_cb; using mem_type = util::sparse_array; using csr_type = util::sparse_array::reg_t, 1ULL << 12, 12>; @@ -1505,7 +1505,7 @@ template uint64_t riscv_hart_mu_p:: #endif CLOG(INFO, disass) << "Semihosting call at address " << buffer.data() << " occurred "; - semihosting_callback(this, this->reg.X10 /*a0*/, this->reg.X11 /*a1*/); + semihosting_cb(this, &(this->reg.X10) /*a0*/, &(this->reg.X11) /*a1*/); return this->reg.NEXT_PC; } } diff --git a/src/iss/semihosting/semihosting.cpp b/src/iss/semihosting/semihosting.cpp index 00124fb..af7006e 100644 --- a/src/iss/semihosting/semihosting.cpp +++ b/src/iss/semihosting/semihosting.cpp @@ -1,16 +1,61 @@ #include "semihosting.h" #include +#include #include #include +#include // explanation of syscalls can be found at https://github.com/SpinalHDL/openocd_riscv/blob/riscv_spinal/src/target/semihosting_common.h -template void semihosting_callback(iss::arch_if* arch_if_ptr, T call_number, T parameter) { - switch(static_cast(call_number)) { + +const char *SYS_OPEN_MODES_STRS[] = { "r", "rb", "r+", "r+b", "w", "wb", "w+", "w+b", "a", "ab", "a+", "a+b" }; + +template T sh_read_field(iss::arch_if* arch_if_ptr, T addr, int len=4) { + uint8_t bytes[4]; + auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, addr, 4, &bytes[0]); + //auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); + + if(res != iss::Ok){ + return 0; //TODO THROW ERROR + } else return static_cast(bytes[0]) | (static_cast(bytes[1]) << 8) | (static_cast(bytes[2]) << 16) | (static_cast(bytes[3]) << 24); +} + +template std::string sh_read_string(iss::arch_if* arch_if_ptr, T addr, T str_len){ + std::vector buffer(str_len); + for (int i = 0; i < str_len; i++ ) { + buffer[i] = sh_read_field(arch_if_ptr, addr + i, 1); + } + std::string str(buffer.begin(), buffer.end()); + return str; +} + + +template void semihosting_callback::operator()(iss::arch_if* arch_if_ptr, T* call_number, T* parameter) { + static std::map openFiles; + static T file_count = 3; + static T semihostingErrno; + + switch(static_cast(*call_number)) { case semihosting_syscalls::SYS_CLOCK: { - throw std::runtime_error("Semihosting Call not Implemented"); + auto end = std::chrono::high_resolution_clock::now(); // end measurement + auto elapsed = end - timeVar; + auto millis = std::chrono::duration_cast(elapsed).count(); + *call_number = millis; //TODO get time now break; } case semihosting_syscalls::SYS_CLOSE: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = *parameter; + if (openFiles.size() <= file_handle && file_handle < 0) { + semihostingErrno = EBADF; + return; + } + auto file = openFiles[file_handle]; + openFiles.erase(file_handle); + if (!(file == stdin || file == stdout || file == stderr)) { + int i = fclose(file); + *call_number = i; + } else { + *call_number = -1; + semihostingErrno = EINTR; + } break; } case semihosting_syscalls::SYS_ELAPSED: { @@ -18,7 +63,7 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal break; } case semihosting_syscalls::SYS_ERRNO: { - throw std::runtime_error("Semihosting Call not Implemented"); + *call_number = semihostingErrno; break; } case semihosting_syscalls::SYS_EXIT: { @@ -31,7 +76,15 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal break; } case semihosting_syscalls::SYS_FLEN: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = *parameter; + auto file = openFiles[file_handle]; + + size_t currentPos = ftell(file); + if (currentPos < 0) throw std::runtime_error("SYS_FLEN negative value"); + fseek(file, 0, SEEK_END); + size_t length = ftell(file); + fseek(file, currentPos, SEEK_SET); + *call_number = (T)length; break; } case semihosting_syscalls::SYS_GET_CMDLINE: { @@ -43,39 +96,127 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal break; } case semihosting_syscalls::SYS_ISERROR: { - throw std::runtime_error("Semihosting Call not Implemented"); + T value = *parameter; + *call_number = (value != 0); break; } case semihosting_syscalls::SYS_ISTTY: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = *parameter; + *call_number = (file_handle == 0 || file_handle == 1 || file_handle == 2); break; } case semihosting_syscalls::SYS_OPEN: { - throw std::runtime_error("Semihosting Call not Implemented"); + T path_str_addr = sh_read_field(arch_if_ptr, *parameter); + T mode = sh_read_field(arch_if_ptr, 4+(*parameter)); + T path_len = sh_read_field(arch_if_ptr, 8+(*parameter)); + + std::string path_str = sh_read_string(arch_if_ptr, path_str_addr, path_len); + + //TODO LOG INFO + + if (mode >= 12) { + //TODO throw ERROR + return; + } + + FILE *file = nullptr; + if(path_str == ":tt") { + if (mode < 4) + file = stdin; + else if (mode < 8) + file = stdout; + else + file = stderr; + } else { + file = fopen(path_str.c_str(), SYS_OPEN_MODES_STRS[mode]); + if (file == nullptr) { + //TODO throw error + return; + } + } + T file_handle = file_count++; + openFiles[file_handle] = file; + *call_number = file_handle; break; + } case semihosting_syscalls::SYS_READ: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = sh_read_field(arch_if_ptr, (*parameter)+4); + T addr = sh_read_field(arch_if_ptr, *parameter); + T count = sh_read_field(arch_if_ptr, (*parameter)+8); + + auto file = openFiles[file_handle]; + + std::vector buffer(count); + size_t num_read = 0; + if (file == stdin) + { + // when reading from stdin: mimic behaviour from read syscall + // and return on newline. + while (num_read < count) + { + char c = fgetc(file); + buffer[num_read] = c; + num_read++; + if (c == '\n') + break; + } + } else { + num_read = fread(buffer.data(), 1, count, file); + } + buffer.resize(num_read); + for(int i = 0; iwrite(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, addr+i, 1, &buffer[i]); + if(res != iss::Ok) + return; + } + *call_number = count - num_read; break; + } case semihosting_syscalls::SYS_READC: { - throw std::runtime_error("Semihosting Call not Implemented"); + uint8_t character = getchar(); + //character = getchar(); + /*if(character != iss::Ok) + std::cout << "Not OK"; + return;*/ + *call_number = character; break; } case semihosting_syscalls::SYS_REMOVE: { - throw std::runtime_error("Semihosting Call not Implemented"); + T path_str_addr = sh_read_field(arch_if_ptr, *parameter); + T path_len = sh_read_field(arch_if_ptr, (*parameter)+4); + std::string path_str = sh_read_string(arch_if_ptr, path_str_addr, path_len); + + if(remove(path_str.c_str())<0) *call_number = -1; break; } case semihosting_syscalls::SYS_RENAME: { - throw std::runtime_error("Semihosting Call not Implemented"); + T path_str_addr_old = sh_read_field(arch_if_ptr, *parameter); + T path_len_old = sh_read_field(arch_if_ptr, (*parameter)+4); + T path_str_addr_new = sh_read_field(arch_if_ptr, (*parameter)+8); + T path_len_new = sh_read_field(arch_if_ptr, (*parameter)+12); + + std::string path_str_old = sh_read_string(arch_if_ptr, path_str_addr_old, path_len_old); + std::string path_str_new = sh_read_string(arch_if_ptr, path_str_addr_new, path_len_new); + rename(path_str_old.c_str(), path_str_new.c_str()); break; } case semihosting_syscalls::SYS_SEEK: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = sh_read_field(arch_if_ptr, *parameter); + T pos = sh_read_field(arch_if_ptr, (*parameter)+1); + auto file = openFiles[file_handle]; + + int retval = fseek(file, pos, SEEK_SET); + if(retval<0) throw std::runtime_error("SYS_SEEK negative return value"); + break; } case semihosting_syscalls::SYS_SYSTEM: { - throw std::runtime_error("Semihosting Call not Implemented"); + T cmd_addr = sh_read_field(arch_if_ptr, *parameter); + T cmd_len = sh_read_field(arch_if_ptr, (*parameter)+1); + std::string cmd = sh_read_string(arch_if_ptr, cmd_addr, cmd_len); + system(cmd.c_str()); break; } case semihosting_syscalls::SYS_TICKFREQ: { @@ -83,20 +224,43 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal break; } case semihosting_syscalls::SYS_TIME: { - throw std::runtime_error("Semihosting Call not Implemented"); + //returns time in seconds scince 01.01.1970 00:00 + *call_number = time(NULL); break; } case semihosting_syscalls::SYS_TMPNAM: { - throw std::runtime_error("Semihosting Call not Implemented"); + T buffer_addr = sh_read_field(arch_if_ptr, *parameter); + T identifier = sh_read_field(arch_if_ptr, (*parameter)+1); + T buffer_len = sh_read_field(arch_if_ptr, (*parameter)+2); + + if (identifier > 255) { + *call_number = -1; + return; + } + std::stringstream ss; + ss << "tmp/file-" << std::setfill('0') << std::setw(3) << identifier; + std::string filename = ss.str(); + + for(int i = 0; i < buffer_len; i++) { + uint8_t character = filename[i]; + auto res = arch_if_ptr->write(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, (*parameter)+i, 1, &character); + if(res != iss::Ok) return; + } break; } case semihosting_syscalls::SYS_WRITE: { - throw std::runtime_error("Semihosting Call not Implemented"); + T file_handle = sh_read_field(arch_if_ptr, (*parameter)+4); + T addr = sh_read_field(arch_if_ptr, *parameter); + T count = sh_read_field(arch_if_ptr, (*parameter)+8); + + auto file = openFiles[file_handle]; + std::string str = sh_read_string(arch_if_ptr, addr, count); + fwrite(&str[0], 1, count, file); break; } case semihosting_syscalls::SYS_WRITEC: { uint8_t character; - auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, parameter, 1, &character); + auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); if(res != iss::Ok) return; putchar(character); @@ -105,13 +269,13 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal case semihosting_syscalls::SYS_WRITE0: { uint8_t character; while(1) { - auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, parameter, 1, &character); + auto res = arch_if_ptr->read(iss::address_type::PHYSICAL, iss::access_type::DEBUG_READ, 0, *parameter, 1, &character); if(res != iss::Ok) return; if(character == 0) break; putchar(character); - parameter++; + (*parameter)++; } break; } @@ -128,5 +292,5 @@ template void semihosting_callback(iss::arch_if* arch_if_ptr, T cal break; } } -template void semihosting_callback(iss::arch_if* arch_if_ptr, uint32_t call_number, uint32_t parameter); -template void semihosting_callback(iss::arch_if* arch_if_ptr, uint64_t call_number, uint64_t parameter); +template class semihosting_callback; +template class semihosting_callback; diff --git a/src/iss/semihosting/semihosting.h b/src/iss/semihosting/semihosting.h index e551f78..295c4d4 100644 --- a/src/iss/semihosting/semihosting.h +++ b/src/iss/semihosting/semihosting.h @@ -1,6 +1,8 @@ #ifndef _SEMIHOSTING_H_ #define _SEMIHOSTING_H_ #include +#include +#include /* * According to: * "Semihosting for AArch32 and AArch64, Release 2.0" @@ -48,6 +50,11 @@ enum class semihosting_syscalls { USER_CMD_0x1FF = 0x1FF, }; -template void semihosting_callback(iss::arch_if* arch_if_ptr, T call_number, T parameter); +template struct semihosting_callback{ + std::chrono::high_resolution_clock::time_point timeVar; + semihosting_callback(): timeVar(std::chrono::high_resolution_clock::now()) {} + void operator()(iss::arch_if* arch_if_ptr, T* call_number, T* parameter); +}; +template using semihosting_cb_t = std::function; #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index aad92da..950a685 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -117,7 +117,8 @@ int main(int argc, char* argv[]) { // instantiate the simulator iss::vm_ptr vm{nullptr}; iss::cpu_ptr cpu{nullptr}; - std::function semihosting_cb = &semihosting_callback; + semihosting_callback cb{}; + semihosting_cb_t semihosting_cb = [&cb](iss::arch_if* i, uint32_t* a0, uint32_t* a1) {cb(i,a0,a1);}; std::string isa_opt(clim["isa"].as()); if(isa_opt.size() == 0 || isa_opt == "?") { auto list = f.get_names(); diff --git a/src/vm/asmjit/vm_tgc5c.cpp b/src/vm/asmjit/vm_tgc5c.cpp index 497cc1e..5b5d779 100644 --- a/src/vm/asmjit/vm_tgc5c.cpp +++ b/src/vm/asmjit/vm_tgc5c.cpp @@ -3757,7 +3757,7 @@ volatile std::array dummy = { auto vm = new asmjit::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; @@ -3767,7 +3767,7 @@ volatile std::array dummy = { auto vm = new asmjit::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; diff --git a/src/vm/interp/vm_tgc5c.cpp b/src/vm/interp/vm_tgc5c.cpp index 10b6296..6b80f2e 100644 --- a/src/vm/interp/vm_tgc5c.cpp +++ b/src/vm/interp/vm_tgc5c.cpp @@ -2700,7 +2700,7 @@ volatile std::array dummy = { auto vm = new interp::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; @@ -2710,7 +2710,7 @@ volatile std::array dummy = { auto vm = new interp::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; diff --git a/src/vm/tcc/vm_tgc5c.cpp b/src/vm/tcc/vm_tgc5c.cpp index 9015757..b58d1cb 100644 --- a/src/vm/tcc/vm_tgc5c.cpp +++ b/src/vm/tcc/vm_tgc5c.cpp @@ -3654,7 +3654,7 @@ volatile std::array dummy = { auto vm = new tcc::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; @@ -3664,7 +3664,7 @@ volatile std::array dummy = { auto vm = new tcc::tgc5c::vm_impl(*cpu, false); if (port != 0) debugger::server::run_server(vm, port); if(init_data){ - auto* cb = reinterpret_cast::reg_t, arch::traits::reg_t)>*>(init_data); + auto* cb = reinterpret_cast::reg_t>*>(init_data); cpu->set_semihosting_callback(*cb); } return {cpu_ptr{cpu}, vm_ptr{vm}}; diff --git a/src_test/iss/arch/tgc5a.cpp b/src_test/iss/arch/tgc5a.cpp new file mode 100644 index 0000000..0f4f6ad --- /dev/null +++ b/src_test/iss/arch/tgc5a.cpp @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (C) 2017 - 2020 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +// clang-format off +#include "tgc5a.h" +#include "util/ities.h" +#include +#include +#include +#include + +using namespace iss::arch; + +constexpr std::array iss::arch::traits::reg_names; +constexpr std::array iss::arch::traits::reg_aliases; +constexpr std::array iss::arch::traits::reg_bit_widths; +constexpr std::array iss::arch::traits::reg_byte_offsets; + +tgc5a::tgc5a() = default; + +tgc5a::~tgc5a() = default; + +void tgc5a::reset(uint64_t address) { + auto base_ptr = reinterpret_cast::reg_t*>(get_regs_base_ptr()); + for(size_t i=0; i::NUM_REGS; ++i) + *(base_ptr+i)=0; + reg.PC=address; + reg.NEXT_PC=reg.PC; + reg.PRIV=0x3; + reg.trap_state=0; + reg.icount=0; +} + +uint8_t *tgc5a::get_regs_base_ptr() { + return reinterpret_cast(®); +} + +tgc5a::phys_addr_t tgc5a::virt2phys(const iss::addr_t &addr) { + return phys_addr_t(addr.access, addr.space, addr.val&traits::addr_mask); +} +// clang-format on diff --git a/src_test/iss/arch/tgc5a.h b/src_test/iss/arch/tgc5a.h new file mode 100644 index 0000000..4389f3f --- /dev/null +++ b/src_test/iss/arch/tgc5a.h @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (C) 2017 - 2021 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +#ifndef _TGC5A_H_ +#define _TGC5A_H_ +// clang-format off +#include +#include +#include +#include + +namespace iss { +namespace arch { + +struct tgc5a; + +template <> struct traits { + + constexpr static char const* const core_type = "TGC5A"; + + static constexpr std::array reg_names{ + {"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "pc", "next_pc", "priv", "dpc"}}; + + static constexpr std::array reg_aliases{ + {"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "pc", "next_pc", "priv", "dpc"}}; + + enum constants {MISA_VAL=1073741840ULL, MARCHID_VAL=2147483649ULL, CLIC_NUM_IRQ=0ULL, XLEN=32ULL, INSTR_ALIGNMENT=4ULL, RFS=16ULL, fence=0ULL, fencei=1ULL, fencevmal=2ULL, fencevmau=3ULL, CSR_SIZE=4096ULL}; + + constexpr static unsigned FP_REGS_SIZE = 0; + + enum reg_e { + X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, PC, NEXT_PC, PRIV, DPC, NUM_REGS, TRAP_STATE=NUM_REGS, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH + }; + + using reg_t = uint32_t; + + using addr_t = uint32_t; + + using code_word_t = uint32_t; //TODO: check removal + + using virt_addr_t = iss::typed_addr_t; + + using phys_addr_t = iss::typed_addr_t; + + static constexpr std::array reg_bit_widths{ + {32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,8,32,32,32,64,64,64,32,32}}; + + static constexpr std::array reg_byte_offsets{ + {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,73,77,81,85,93,101,109,113}}; + + static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1); + + enum sreg_flag_e { FLAGS }; + + enum mem_type_e { MEM, FENCE, RES, CSR }; + + enum class opcode_e { + LUI = 0, + AUIPC = 1, + JAL = 2, + JALR = 3, + BEQ = 4, + BNE = 5, + BLT = 6, + BGE = 7, + BLTU = 8, + BGEU = 9, + LB = 10, + LH = 11, + LW = 12, + LBU = 13, + LHU = 14, + SB = 15, + SH = 16, + SW = 17, + ADDI = 18, + SLTI = 19, + SLTIU = 20, + XORI = 21, + ORI = 22, + ANDI = 23, + SLLI = 24, + SRLI = 25, + SRAI = 26, + ADD = 27, + SUB = 28, + SLL = 29, + SLT = 30, + SLTU = 31, + XOR = 32, + SRL = 33, + SRA = 34, + OR = 35, + AND = 36, + FENCE = 37, + ECALL = 38, + EBREAK = 39, + MRET = 40, + WFI = 41, + CSRRW = 42, + CSRRS = 43, + CSRRC = 44, + CSRRWI = 45, + CSRRSI = 46, + CSRRCI = 47, + FENCE_I = 48, + MAX_OPCODE + }; +}; + +struct tgc5a: public arch_if { + + using virt_addr_t = typename traits::virt_addr_t; + using phys_addr_t = typename traits::phys_addr_t; + using reg_t = typename traits::reg_t; + using addr_t = typename traits::addr_t; + + tgc5a(); + ~tgc5a(); + + void reset(uint64_t address=0) override; + + uint8_t* get_regs_base_ptr() override; + + inline uint64_t get_icount() { return reg.icount; } + + inline bool should_stop() { return interrupt_sim; } + + inline uint64_t stop_code() { return interrupt_sim; } + + virtual phys_addr_t virt2phys(const iss::addr_t& addr); + + virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; } + + inline uint32_t get_last_branch() { return reg.last_branch; } + + +#pragma pack(push, 1) + struct TGC5A_regs { + uint32_t X0 = 0; + uint32_t X1 = 0; + uint32_t X2 = 0; + uint32_t X3 = 0; + uint32_t X4 = 0; + uint32_t X5 = 0; + uint32_t X6 = 0; + uint32_t X7 = 0; + uint32_t X8 = 0; + uint32_t X9 = 0; + uint32_t X10 = 0; + uint32_t X11 = 0; + uint32_t X12 = 0; + uint32_t X13 = 0; + uint32_t X14 = 0; + uint32_t X15 = 0; + uint32_t PC = 0; + uint32_t NEXT_PC = 0; + uint8_t PRIV = 0; + uint32_t DPC = 0; + uint32_t trap_state = 0, pending_trap = 0; + uint64_t icount = 0; + uint64_t cycle = 0; + uint64_t instret = 0; + uint32_t instruction = 0; + uint32_t last_branch = 0; + } reg; +#pragma pack(pop) + std::array addr_mode; + + uint64_t interrupt_sim=0; + + uint32_t get_fcsr(){return 0;} + void set_fcsr(uint32_t val){} + +}; + +} +} +#endif /* _TGC5A_H_ */ +// clang-format on diff --git a/src_test/iss/arch/tgc5b.cpp b/src_test/iss/arch/tgc5b.cpp new file mode 100644 index 0000000..3b80493 --- /dev/null +++ b/src_test/iss/arch/tgc5b.cpp @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (C) 2017 - 2020 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +// clang-format off +#include "tgc5b.h" +#include "util/ities.h" +#include +#include +#include +#include + +using namespace iss::arch; + +constexpr std::array iss::arch::traits::reg_names; +constexpr std::array iss::arch::traits::reg_aliases; +constexpr std::array iss::arch::traits::reg_bit_widths; +constexpr std::array iss::arch::traits::reg_byte_offsets; + +tgc5b::tgc5b() = default; + +tgc5b::~tgc5b() = default; + +void tgc5b::reset(uint64_t address) { + auto base_ptr = reinterpret_cast::reg_t*>(get_regs_base_ptr()); + for(size_t i=0; i::NUM_REGS; ++i) + *(base_ptr+i)=0; + reg.PC=address; + reg.NEXT_PC=reg.PC; + reg.PRIV=0x3; + reg.trap_state=0; + reg.icount=0; +} + +uint8_t *tgc5b::get_regs_base_ptr() { + return reinterpret_cast(®); +} + +tgc5b::phys_addr_t tgc5b::virt2phys(const iss::addr_t &addr) { + return phys_addr_t(addr.access, addr.space, addr.val&traits::addr_mask); +} +// clang-format on diff --git a/src_test/iss/arch/tgc5b.h b/src_test/iss/arch/tgc5b.h new file mode 100644 index 0000000..7e407c5 --- /dev/null +++ b/src_test/iss/arch/tgc5b.h @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (C) 2017 - 2021 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +#ifndef _TGC5B_H_ +#define _TGC5B_H_ +// clang-format off +#include +#include +#include +#include + +namespace iss { +namespace arch { + +struct tgc5b; + +template <> struct traits { + + constexpr static char const* const core_type = "TGC5B"; + + static constexpr std::array reg_names{ + {"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", "pc", "next_pc", "priv", "dpc"}}; + + static constexpr std::array reg_aliases{ + {"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", "pc", "next_pc", "priv", "dpc"}}; + + enum constants {MISA_VAL=1073742080ULL, MARCHID_VAL=2147483650ULL, CLIC_NUM_IRQ=0ULL, XLEN=32ULL, INSTR_ALIGNMENT=4ULL, RFS=32ULL, fence=0ULL, fencei=1ULL, fencevmal=2ULL, fencevmau=3ULL, CSR_SIZE=4096ULL}; + + constexpr static unsigned FP_REGS_SIZE = 0; + + enum reg_e { + X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, PC, NEXT_PC, PRIV, DPC, NUM_REGS, TRAP_STATE=NUM_REGS, PENDING_TRAP, ICOUNT, CYCLE, INSTRET, INSTRUCTION, LAST_BRANCH + }; + + using reg_t = uint32_t; + + using addr_t = uint32_t; + + using code_word_t = uint32_t; //TODO: check removal + + using virt_addr_t = iss::typed_addr_t; + + using phys_addr_t = iss::typed_addr_t; + + static constexpr std::array reg_bit_widths{ + {32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,8,32,32,32,64,64,64,32,32}}; + + static constexpr std::array reg_byte_offsets{ + {0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128,132,136,137,141,145,149,157,165,173,177}}; + + static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1); + + enum sreg_flag_e { FLAGS }; + + enum mem_type_e { MEM, FENCE, RES, CSR }; + + enum class opcode_e { + LUI = 0, + AUIPC = 1, + JAL = 2, + JALR = 3, + BEQ = 4, + BNE = 5, + BLT = 6, + BGE = 7, + BLTU = 8, + BGEU = 9, + LB = 10, + LH = 11, + LW = 12, + LBU = 13, + LHU = 14, + SB = 15, + SH = 16, + SW = 17, + ADDI = 18, + SLTI = 19, + SLTIU = 20, + XORI = 21, + ORI = 22, + ANDI = 23, + SLLI = 24, + SRLI = 25, + SRAI = 26, + ADD = 27, + SUB = 28, + SLL = 29, + SLT = 30, + SLTU = 31, + XOR = 32, + SRL = 33, + SRA = 34, + OR = 35, + AND = 36, + FENCE = 37, + ECALL = 38, + EBREAK = 39, + MRET = 40, + WFI = 41, + CSRRW = 42, + CSRRS = 43, + CSRRC = 44, + CSRRWI = 45, + CSRRSI = 46, + CSRRCI = 47, + FENCE_I = 48, + MAX_OPCODE + }; +}; + +struct tgc5b: public arch_if { + + using virt_addr_t = typename traits::virt_addr_t; + using phys_addr_t = typename traits::phys_addr_t; + using reg_t = typename traits::reg_t; + using addr_t = typename traits::addr_t; + + tgc5b(); + ~tgc5b(); + + void reset(uint64_t address=0) override; + + uint8_t* get_regs_base_ptr() override; + + inline uint64_t get_icount() { return reg.icount; } + + inline bool should_stop() { return interrupt_sim; } + + inline uint64_t stop_code() { return interrupt_sim; } + + virtual phys_addr_t virt2phys(const iss::addr_t& addr); + + virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; } + + inline uint32_t get_last_branch() { return reg.last_branch; } + + +#pragma pack(push, 1) + struct TGC5B_regs { + uint32_t X0 = 0; + uint32_t X1 = 0; + uint32_t X2 = 0; + uint32_t X3 = 0; + uint32_t X4 = 0; + uint32_t X5 = 0; + uint32_t X6 = 0; + uint32_t X7 = 0; + uint32_t X8 = 0; + uint32_t X9 = 0; + uint32_t X10 = 0; + uint32_t X11 = 0; + uint32_t X12 = 0; + uint32_t X13 = 0; + uint32_t X14 = 0; + uint32_t X15 = 0; + uint32_t X16 = 0; + uint32_t X17 = 0; + uint32_t X18 = 0; + uint32_t X19 = 0; + uint32_t X20 = 0; + uint32_t X21 = 0; + uint32_t X22 = 0; + uint32_t X23 = 0; + uint32_t X24 = 0; + uint32_t X25 = 0; + uint32_t X26 = 0; + uint32_t X27 = 0; + uint32_t X28 = 0; + uint32_t X29 = 0; + uint32_t X30 = 0; + uint32_t X31 = 0; + uint32_t PC = 0; + uint32_t NEXT_PC = 0; + uint8_t PRIV = 0; + uint32_t DPC = 0; + uint32_t trap_state = 0, pending_trap = 0; + uint64_t icount = 0; + uint64_t cycle = 0; + uint64_t instret = 0; + uint32_t instruction = 0; + uint32_t last_branch = 0; + } reg; +#pragma pack(pop) + std::array addr_mode; + + uint64_t interrupt_sim=0; + + uint32_t get_fcsr(){return 0;} + void set_fcsr(uint32_t val){} + +}; + +} +} +#endif /* _TGC5B_H_ */ +// clang-format on diff --git a/src_test/vm/interp/vm_tgc5a.cpp b/src_test/vm/interp/vm_tgc5a.cpp new file mode 100644 index 0000000..875e5a7 --- /dev/null +++ b/src_test/vm/interp/vm_tgc5a.cpp @@ -0,0 +1,1776 @@ +/******************************************************************************* + * Copyright (C) 2021 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +// clang-format off +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include + +#include +#include + +namespace iss { +namespace interp { +namespace tgc5a { +using namespace iss::arch; +using namespace iss::debugger; +using namespace std::placeholders; + +struct memory_access_exception : public std::exception{ + memory_access_exception(){} +}; + +template class vm_impl : public iss::interp::vm_base { +public: + using traits = arch::traits; + using super = typename iss::interp::vm_base; + using virt_addr_t = typename super::virt_addr_t; + using phys_addr_t = typename super::phys_addr_t; + using code_word_t = typename super::code_word_t; + using addr_t = typename super::addr_t; + using reg_t = typename traits::reg_t; + using mem_type_e = typename traits::mem_type_e; + using opcode_e = typename traits::opcode_e; + + vm_impl(); + + vm_impl(ARCH &core, unsigned core_id = 0, unsigned cluster_id = 0); + + void enableDebug(bool enable) { super::sync_exec = super::ALL_SYNC; } + + target_adapter_if *accquire_target_adapter(server_if *srv) override { + debugger_if::dbg_enabled = true; + if (super::tgt_adapter == nullptr) + super::tgt_adapter = new riscv_target_adapter(srv, this->get_arch()); + return super::tgt_adapter; + } + +protected: + using this_class = vm_impl; + using compile_ret_t = virt_addr_t; + using compile_func = compile_ret_t (this_class::*)(virt_addr_t &pc, code_word_t instr); + + inline const char *name(size_t index){return indexcore.reg.trap_state = trap_val; + this->template get_reg(traits::NEXT_PC) = std::numeric_limits::max(); + } + + inline void leave(unsigned lvl){ + this->core.leave_trap(lvl); + } + + inline void wait(unsigned type){ + this->core.wait_until(type); + } + + using yield_t = boost::coroutines2::coroutine::push_type; + using coro_t = boost::coroutines2::coroutine::pull_type; + std::vector spawn_blocks; + + template::type> + inline S sext(U from) { + auto mask = (1ULL<::opcode_e op; + }; + struct decoding_tree_node{ + std::vector instrs; + std::vector children; + uint32_t submask = std::numeric_limits::max(); + uint32_t value; + decoding_tree_node(uint32_t value) : value(value){} + }; + + decoding_tree_node* root {nullptr}; + const std::array instr_descr = {{ + /* entries are: size, valid value, valid mask, function ptr */ + {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, arch::traits::opcode_e::LUI}, + {32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, arch::traits::opcode_e::AUIPC}, + {32, 0b00000000000000000000000001101111, 0b00000000000000000000000001111111, arch::traits::opcode_e::JAL}, + {32, 0b00000000000000000000000001100111, 0b00000000000000000111000001111111, arch::traits::opcode_e::JALR}, + {32, 0b00000000000000000000000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BEQ}, + {32, 0b00000000000000000001000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BNE}, + {32, 0b00000000000000000100000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BLT}, + {32, 0b00000000000000000101000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BGE}, + {32, 0b00000000000000000110000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BLTU}, + {32, 0b00000000000000000111000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BGEU}, + {32, 0b00000000000000000000000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LB}, + {32, 0b00000000000000000001000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LH}, + {32, 0b00000000000000000010000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LW}, + {32, 0b00000000000000000100000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LBU}, + {32, 0b00000000000000000101000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LHU}, + {32, 0b00000000000000000000000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SB}, + {32, 0b00000000000000000001000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SH}, + {32, 0b00000000000000000010000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SW}, + {32, 0b00000000000000000000000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ADDI}, + {32, 0b00000000000000000010000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SLTI}, + {32, 0b00000000000000000011000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SLTIU}, + {32, 0b00000000000000000100000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::XORI}, + {32, 0b00000000000000000110000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ORI}, + {32, 0b00000000000000000111000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ANDI}, + {32, 0b00000000000000000001000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLLI}, + {32, 0b00000000000000000101000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRLI}, + {32, 0b01000000000000000101000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRAI}, + {32, 0b00000000000000000000000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::ADD}, + {32, 0b01000000000000000000000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SUB}, + {32, 0b00000000000000000001000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLL}, + {32, 0b00000000000000000010000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLT}, + {32, 0b00000000000000000011000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLTU}, + {32, 0b00000000000000000100000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::XOR}, + {32, 0b00000000000000000101000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRL}, + {32, 0b01000000000000000101000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRA}, + {32, 0b00000000000000000110000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::OR}, + {32, 0b00000000000000000111000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::AND}, + {32, 0b00000000000000000000000000001111, 0b00000000000000000111000001111111, arch::traits::opcode_e::FENCE}, + {32, 0b00000000000000000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::ECALL}, + {32, 0b00000000000100000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::EBREAK}, + {32, 0b00110000001000000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::MRET}, + {32, 0b00010000010100000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::WFI}, + {32, 0b00000000000000000001000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRW}, + {32, 0b00000000000000000010000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRS}, + {32, 0b00000000000000000011000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRC}, + {32, 0b00000000000000000101000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRWI}, + {32, 0b00000000000000000110000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRSI}, + {32, 0b00000000000000000111000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRCI}, + {32, 0b00000000000000000001000000001111, 0b00000000000000000111000001111111, arch::traits::opcode_e::FENCE_I}, + }}; + + iss::status fetch_ins(virt_addr_t pc, uint8_t * data){ + if(this->core.has_mmu()) { + auto phys_pc = this->core.virt2phys(pc); +// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary +// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err; +// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction +// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) +// return iss::Err; +// } else { + if (this->core.read(phys_pc, 4, data) != iss::Ok) + return iss::Err; +// } + } else { + if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok) + return iss::Err; + + } + return iss::Ok; + } + + void populate_decoding_tree(decoding_tree_node* root){ + //create submask + for(auto instr: root->instrs){ + root->submask &= instr.mask; + } + //put each instr according to submask&encoding into children + for(auto instr: root->instrs){ + bool foundMatch = false; + for(auto child: root->children){ + //use value as identifying trait + if(child->value == (instr.value&root->submask)){ + child->instrs.push_back(instr); + foundMatch = true; + } + } + if(!foundMatch){ + decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask); + child->instrs.push_back(instr); + root->children.push_back(child); + } + } + root->instrs.clear(); + //call populate_decoding_tree for all children + if(root->children.size() >1) + for(auto child: root->children){ + populate_decoding_tree(child); + } + else{ + //sort instrs by value of the mask, this works bc we want to have the least restrictive one last + std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) { + return instr1.mask > instr2.mask; + }); + } + } + typename arch::traits::opcode_e decode_instr(decoding_tree_node* node, code_word_t word){ + if(!node->children.size()){ + if(node->instrs.size() == 1) return node->instrs[0].op; + for(auto instr : node->instrs){ + if((instr.mask&word) == instr.value) return instr.op; + } + } + else{ + for(auto child : node->children){ + if (child->value == (node->submask&word)){ + return decode_instr(child, word); + } + } + } + return arch::traits::opcode_e::MAX_OPCODE; + } +}; + +template void debug_fn(CODE_WORD insn) { + volatile CODE_WORD x = insn; + insn = 2 * x; +} + +template vm_impl::vm_impl() { this(new ARCH()); } + +// according to +// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation +#ifdef __GCC__ +constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); } +#elif __cplusplus < 201402L +constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); } +constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; } +#else +constexpr size_t bit_count(uint32_t u) { + size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); + return ((uCount + (uCount >> 3)) & 030707070707) % 63; +} +#endif + +template +vm_impl::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) +: vm_base(core, core_id, cluster_id) { + root = new decoding_tree_node(std::numeric_limits::max()); + for(auto instr:instr_descr){ + root->instrs.push_back(instr); + } + populate_decoding_tree(root); +} + +inline bool is_count_limit_enabled(finish_cond_e cond){ + return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT; +} + +inline bool is_jump_to_self_enabled(finish_cond_e cond){ + return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF; +} + +template +typename vm_base::virt_addr_t vm_impl::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){ + auto pc=start; + auto* PC = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::PC]); + auto* NEXT_PC = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::NEXT_PC]); + auto& trap_state = this->core.reg.trap_state; + auto& icount = this->core.reg.icount; + auto& cycle = this->core.reg.cycle; + auto& instret = this->core.reg.instret; + auto& instr = this->core.reg.instruction; + // we fetch at max 4 byte, alignment is 2 + auto *const data = reinterpret_cast(&instr); + + while(!this->core.should_stop() && + !(is_count_limit_enabled(cond) && icount >= icount_limit)){ + if(fetch_ins(pc, data)!=iss::Ok){ + this->do_sync(POST_SYNC, std::numeric_limits::max()); + pc.val = super::core.enter_trap(std::numeric_limits::max(), pc.val, 0); + } else { + if (is_jump_to_self_enabled(cond) && + (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' + auto inst_id = decode_instr(root, instr); + // pre execution stuff + this->core.reg.last_branch = 0; + if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast(inst_id)); + try{ + switch(inst_id){ + case arch::traits::opcode_e::LUI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,20>(instr) << 12)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#05x}", fmt::arg("mnemonic", "lui"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)imm); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::AUIPC: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,20>(instr) << 12)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#08x}", fmt::arg("mnemonic", "auipc"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + (int32_t)imm); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::JAL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,8>(instr) << 12) | (bit_sub<20,1>(instr) << 11) | (bit_sub<21,10>(instr) << 1) | (bit_sub<31,1>(instr) << 20)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#0x}", fmt::arg("mnemonic", "jal"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + 4); + } + *NEXT_PC = (uint32_t)(*PC + (int32_t)sext<21>(imm)); + this->core.reg.last_branch = 1; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::JALR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm:#0x}", fmt::arg("mnemonic", "jalr"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t addr_mask = (uint32_t)- 2; + uint32_t new_pc = (uint32_t)((*(X+rs1) + (int16_t)sext<12>(imm)) & addr_mask); + if(new_pc % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + 4); + } + *NEXT_PC = new_pc; + this->core.reg.last_branch = 1; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BEQ: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "beq"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) == *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BNE: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bne"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) != *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BLT: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "blt"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if((int32_t)*(X+rs1) < (int32_t)*(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BGE: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bge"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if((int32_t)*(X+rs1) >= (int32_t)*(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BLTU: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bltu"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) < *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BGEU: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bgeu"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) >= *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LB: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lb"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int8_t res_23 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int8_t res = (int8_t)res_23; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LH: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lh"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int16_t res_24 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int16_t res = (int16_t)res_24; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LW: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lw"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int32_t res_25 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int32_t res = (int32_t)res_25; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LBU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lbu"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + uint8_t res_26 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint8_t res = res_26; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LHU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lhu"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + uint16_t res_27 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint16_t res = res_27; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SB: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sb"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint8_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SH: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sh"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint16_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SW: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sw"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint32_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ADDI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "addi"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "slti"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = ((int32_t)*(X+rs1) < (int16_t)sext<12>(imm))? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTIU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "sltiu"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (*(X+rs1) < (uint32_t)((int16_t)sext<12>(imm)))? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::XORI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "xori"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) ^ (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ORI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "ori"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) | (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ANDI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "andi"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) & (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLLI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "slli"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) << shamt; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRLI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "srli"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) >> shamt; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRAI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "srai"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> shamt); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ADD: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "add"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) + *(X+rs2)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SUB: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sub"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) - *(X+rs2)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sll"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) << (*(X+rs2) & (traits::XLEN - 1)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLT: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "slt"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (int32_t)*(X+rs1) < (int32_t)*(X+rs2)? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sltu"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) < *(X+rs2)? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::XOR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "xor"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) ^ *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "srl"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) >> (*(X+rs2) & (traits::XLEN - 1)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRA: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sra"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> (*(X+rs2) & (traits::XLEN - 1))); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::OR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "or"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) | *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::AND: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "and"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) & *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::FENCE: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t succ = ((bit_sub<20,4>(instr))); + uint8_t pred = ((bit_sub<24,4>(instr))); + uint8_t fm = ((bit_sub<28,4>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {pred}, {succ} ({fm} , {rs1}, {rd})", fmt::arg("mnemonic", "fence"), + fmt::arg("pred", pred), fmt::arg("succ", succ), fmt::arg("fm", fm), fmt::arg("rs1", name(rs1)), fmt::arg("rd", name(rd))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + super::template write_mem(traits::FENCE, traits::fence, (uint8_t)pred << 4 | succ); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ECALL: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "ecall"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + raise(0, 11); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::EBREAK: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "ebreak"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + raise(0, 3); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::MRET: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "mret"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + leave(3); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::WFI: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "wfi"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + wait(1); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRW: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrw"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t xrs1 = *(X+rs1); + if(rd != 0) { + uint32_t res_28 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_28; + super::template write_mem(traits::CSR, csr, xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + *(X+rd) = xrd; + } + else { + super::template write_mem(traits::CSR, csr, xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRS: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrs"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_29 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_29; + uint32_t xrs1 = *(X+rs1); + if(rs1 != 0) { + super::template write_mem(traits::CSR, csr, xrd | xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRC: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrc"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_30 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_30; + uint32_t xrs1 = *(X+rs1); + if(rs1 != 0) { + super::template write_mem(traits::CSR, csr, xrd & ~ xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRWI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrwi"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_31 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_31; + super::template write_mem(traits::CSR, csr, (uint32_t)zimm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRSI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrsi"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_32 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_32; + if(zimm != 0) { + super::template write_mem(traits::CSR, csr, xrd | (uint32_t)zimm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRCI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrci"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_33 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_33; + if(zimm != 0) { + super::template write_mem(traits::CSR, csr, xrd & ~ ((uint32_t)zimm)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::FENCE_I: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rd}, {imm}", fmt::arg("mnemonic", "fence_i"), + fmt::arg("rs1", name(rs1)), fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + super::template write_mem(traits::FENCE, traits::fencei, imm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + break; + }// @suppress("No break at end of case") + default: { + *NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2); + raise(0, 2); + } + } + }catch(memory_access_exception& e){} + // post execution stuff + process_spawn_blocks(); + if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast(inst_id)); + // if(!this->core.reg.trap_state) // update trap state if there is a pending interrupt + // this->core.reg.trap_state = this->core.reg.pending_trap; + // trap check + if(trap_state!=0){ + super::core.enter_trap(trap_state, pc.val, instr); + } else { + icount++; + instret++; + } + cycle++; + pc.val=*NEXT_PC; + this->core.reg.PC = this->core.reg.NEXT_PC; + this->core.reg.trap_state = this->core.reg.pending_trap; + } + } + return pc; +} + +} // namespace tgc5a + +template <> +std::unique_ptr create(arch::tgc5a *core, unsigned short port, bool dump) { + auto ret = new tgc5a::vm_impl(*core, dump); + if (port != 0) debugger::server::run_server(ret, port); + return std::unique_ptr(ret); +} +} // namespace interp +} // namespace iss + +#include +#include +#include +namespace iss { +namespace { +volatile std::array dummy = { + core_factory::instance().register_creator("tgc5a|m_p|interp", [](unsigned port, void*) -> std::tuple{ + auto* cpu = new iss::arch::riscv_hart_m_p(); + auto vm = new interp::tgc5a::vm_impl(*cpu, false); + if (port != 0) debugger::server::run_server(vm, port); + return {cpu_ptr{cpu}, vm_ptr{vm}}; + }), + core_factory::instance().register_creator("tgc5a|mu_p|interp", [](unsigned port, void*) -> std::tuple{ + auto* cpu = new iss::arch::riscv_hart_mu_p(); + auto vm = new interp::tgc5a::vm_impl(*cpu, false); + if (port != 0) debugger::server::run_server(vm, port); + return {cpu_ptr{cpu}, vm_ptr{vm}}; + }) +}; +} +} +// clang-format on \ No newline at end of file diff --git a/src_test/vm/interp/vm_tgc5b.cpp b/src_test/vm/interp/vm_tgc5b.cpp new file mode 100644 index 0000000..98fb681 --- /dev/null +++ b/src_test/vm/interp/vm_tgc5b.cpp @@ -0,0 +1,1776 @@ +/******************************************************************************* + * Copyright (C) 2021 MINRES Technologies GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +// clang-format off +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include + +#include +#include + +namespace iss { +namespace interp { +namespace tgc5b { +using namespace iss::arch; +using namespace iss::debugger; +using namespace std::placeholders; + +struct memory_access_exception : public std::exception{ + memory_access_exception(){} +}; + +template class vm_impl : public iss::interp::vm_base { +public: + using traits = arch::traits; + using super = typename iss::interp::vm_base; + using virt_addr_t = typename super::virt_addr_t; + using phys_addr_t = typename super::phys_addr_t; + using code_word_t = typename super::code_word_t; + using addr_t = typename super::addr_t; + using reg_t = typename traits::reg_t; + using mem_type_e = typename traits::mem_type_e; + using opcode_e = typename traits::opcode_e; + + vm_impl(); + + vm_impl(ARCH &core, unsigned core_id = 0, unsigned cluster_id = 0); + + void enableDebug(bool enable) { super::sync_exec = super::ALL_SYNC; } + + target_adapter_if *accquire_target_adapter(server_if *srv) override { + debugger_if::dbg_enabled = true; + if (super::tgt_adapter == nullptr) + super::tgt_adapter = new riscv_target_adapter(srv, this->get_arch()); + return super::tgt_adapter; + } + +protected: + using this_class = vm_impl; + using compile_ret_t = virt_addr_t; + using compile_func = compile_ret_t (this_class::*)(virt_addr_t &pc, code_word_t instr); + + inline const char *name(size_t index){return indexcore.reg.trap_state = trap_val; + this->template get_reg(traits::NEXT_PC) = std::numeric_limits::max(); + } + + inline void leave(unsigned lvl){ + this->core.leave_trap(lvl); + } + + inline void wait(unsigned type){ + this->core.wait_until(type); + } + + using yield_t = boost::coroutines2::coroutine::push_type; + using coro_t = boost::coroutines2::coroutine::pull_type; + std::vector spawn_blocks; + + template::type> + inline S sext(U from) { + auto mask = (1ULL<::opcode_e op; + }; + struct decoding_tree_node{ + std::vector instrs; + std::vector children; + uint32_t submask = std::numeric_limits::max(); + uint32_t value; + decoding_tree_node(uint32_t value) : value(value){} + }; + + decoding_tree_node* root {nullptr}; + const std::array instr_descr = {{ + /* entries are: size, valid value, valid mask, function ptr */ + {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, arch::traits::opcode_e::LUI}, + {32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, arch::traits::opcode_e::AUIPC}, + {32, 0b00000000000000000000000001101111, 0b00000000000000000000000001111111, arch::traits::opcode_e::JAL}, + {32, 0b00000000000000000000000001100111, 0b00000000000000000111000001111111, arch::traits::opcode_e::JALR}, + {32, 0b00000000000000000000000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BEQ}, + {32, 0b00000000000000000001000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BNE}, + {32, 0b00000000000000000100000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BLT}, + {32, 0b00000000000000000101000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BGE}, + {32, 0b00000000000000000110000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BLTU}, + {32, 0b00000000000000000111000001100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::BGEU}, + {32, 0b00000000000000000000000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LB}, + {32, 0b00000000000000000001000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LH}, + {32, 0b00000000000000000010000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LW}, + {32, 0b00000000000000000100000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LBU}, + {32, 0b00000000000000000101000000000011, 0b00000000000000000111000001111111, arch::traits::opcode_e::LHU}, + {32, 0b00000000000000000000000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SB}, + {32, 0b00000000000000000001000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SH}, + {32, 0b00000000000000000010000000100011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SW}, + {32, 0b00000000000000000000000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ADDI}, + {32, 0b00000000000000000010000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SLTI}, + {32, 0b00000000000000000011000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::SLTIU}, + {32, 0b00000000000000000100000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::XORI}, + {32, 0b00000000000000000110000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ORI}, + {32, 0b00000000000000000111000000010011, 0b00000000000000000111000001111111, arch::traits::opcode_e::ANDI}, + {32, 0b00000000000000000001000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLLI}, + {32, 0b00000000000000000101000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRLI}, + {32, 0b01000000000000000101000000010011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRAI}, + {32, 0b00000000000000000000000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::ADD}, + {32, 0b01000000000000000000000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SUB}, + {32, 0b00000000000000000001000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLL}, + {32, 0b00000000000000000010000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLT}, + {32, 0b00000000000000000011000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SLTU}, + {32, 0b00000000000000000100000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::XOR}, + {32, 0b00000000000000000101000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRL}, + {32, 0b01000000000000000101000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::SRA}, + {32, 0b00000000000000000110000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::OR}, + {32, 0b00000000000000000111000000110011, 0b11111110000000000111000001111111, arch::traits::opcode_e::AND}, + {32, 0b00000000000000000000000000001111, 0b00000000000000000111000001111111, arch::traits::opcode_e::FENCE}, + {32, 0b00000000000000000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::ECALL}, + {32, 0b00000000000100000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::EBREAK}, + {32, 0b00110000001000000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::MRET}, + {32, 0b00010000010100000000000001110011, 0b11111111111111111111111111111111, arch::traits::opcode_e::WFI}, + {32, 0b00000000000000000001000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRW}, + {32, 0b00000000000000000010000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRS}, + {32, 0b00000000000000000011000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRC}, + {32, 0b00000000000000000101000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRWI}, + {32, 0b00000000000000000110000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRSI}, + {32, 0b00000000000000000111000001110011, 0b00000000000000000111000001111111, arch::traits::opcode_e::CSRRCI}, + {32, 0b00000000000000000001000000001111, 0b00000000000000000111000001111111, arch::traits::opcode_e::FENCE_I}, + }}; + + iss::status fetch_ins(virt_addr_t pc, uint8_t * data){ + if(this->core.has_mmu()) { + auto phys_pc = this->core.virt2phys(pc); +// if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary +// if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err; +// if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction +// if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) +// return iss::Err; +// } else { + if (this->core.read(phys_pc, 4, data) != iss::Ok) + return iss::Err; +// } + } else { + if (this->core.read(phys_addr_t(pc.access, pc.space, pc.val), 4, data) != iss::Ok) + return iss::Err; + + } + return iss::Ok; + } + + void populate_decoding_tree(decoding_tree_node* root){ + //create submask + for(auto instr: root->instrs){ + root->submask &= instr.mask; + } + //put each instr according to submask&encoding into children + for(auto instr: root->instrs){ + bool foundMatch = false; + for(auto child: root->children){ + //use value as identifying trait + if(child->value == (instr.value&root->submask)){ + child->instrs.push_back(instr); + foundMatch = true; + } + } + if(!foundMatch){ + decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask); + child->instrs.push_back(instr); + root->children.push_back(child); + } + } + root->instrs.clear(); + //call populate_decoding_tree for all children + if(root->children.size() >1) + for(auto child: root->children){ + populate_decoding_tree(child); + } + else{ + //sort instrs by value of the mask, this works bc we want to have the least restrictive one last + std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) { + return instr1.mask > instr2.mask; + }); + } + } + typename arch::traits::opcode_e decode_instr(decoding_tree_node* node, code_word_t word){ + if(!node->children.size()){ + if(node->instrs.size() == 1) return node->instrs[0].op; + for(auto instr : node->instrs){ + if((instr.mask&word) == instr.value) return instr.op; + } + } + else{ + for(auto child : node->children){ + if (child->value == (node->submask&word)){ + return decode_instr(child, word); + } + } + } + return arch::traits::opcode_e::MAX_OPCODE; + } +}; + +template void debug_fn(CODE_WORD insn) { + volatile CODE_WORD x = insn; + insn = 2 * x; +} + +template vm_impl::vm_impl() { this(new ARCH()); } + +// according to +// https://stackoverflow.com/questions/8871204/count-number-of-1s-in-binary-representation +#ifdef __GCC__ +constexpr size_t bit_count(uint32_t u) { return __builtin_popcount(u); } +#elif __cplusplus < 201402L +constexpr size_t uCount(uint32_t u) { return u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); } +constexpr size_t bit_count(uint32_t u) { return ((uCount(u) + (uCount(u) >> 3)) & 030707070707) % 63; } +#else +constexpr size_t bit_count(uint32_t u) { + size_t uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); + return ((uCount + (uCount >> 3)) & 030707070707) % 63; +} +#endif + +template +vm_impl::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) +: vm_base(core, core_id, cluster_id) { + root = new decoding_tree_node(std::numeric_limits::max()); + for(auto instr:instr_descr){ + root->instrs.push_back(instr); + } + populate_decoding_tree(root); +} + +inline bool is_count_limit_enabled(finish_cond_e cond){ + return (cond & finish_cond_e::COUNT_LIMIT) == finish_cond_e::COUNT_LIMIT; +} + +inline bool is_jump_to_self_enabled(finish_cond_e cond){ + return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF; +} + +template +typename vm_base::virt_addr_t vm_impl::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){ + auto pc=start; + auto* PC = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::PC]); + auto* NEXT_PC = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::NEXT_PC]); + auto& trap_state = this->core.reg.trap_state; + auto& icount = this->core.reg.icount; + auto& cycle = this->core.reg.cycle; + auto& instret = this->core.reg.instret; + auto& instr = this->core.reg.instruction; + // we fetch at max 4 byte, alignment is 2 + auto *const data = reinterpret_cast(&instr); + + while(!this->core.should_stop() && + !(is_count_limit_enabled(cond) && icount >= icount_limit)){ + if(fetch_ins(pc, data)!=iss::Ok){ + this->do_sync(POST_SYNC, std::numeric_limits::max()); + pc.val = super::core.enter_trap(std::numeric_limits::max(), pc.val, 0); + } else { + if (is_jump_to_self_enabled(cond) && + (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' + auto inst_id = decode_instr(root, instr); + // pre execution stuff + this->core.reg.last_branch = 0; + if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast(inst_id)); + try{ + switch(inst_id){ + case arch::traits::opcode_e::LUI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,20>(instr) << 12)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#05x}", fmt::arg("mnemonic", "lui"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)imm); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::AUIPC: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,20>(instr) << 12)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#08x}", fmt::arg("mnemonic", "auipc"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + (int32_t)imm); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::JAL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint32_t imm = ((bit_sub<12,8>(instr) << 12) | (bit_sub<20,1>(instr) << 11) | (bit_sub<21,10>(instr) << 1) | (bit_sub<31,1>(instr) << 20)); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm:#0x}", fmt::arg("mnemonic", "jal"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + 4); + } + *NEXT_PC = (uint32_t)(*PC + (int32_t)sext<21>(imm)); + this->core.reg.last_branch = 1; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::JALR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm:#0x}", fmt::arg("mnemonic", "jalr"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t addr_mask = (uint32_t)- 2; + uint32_t new_pc = (uint32_t)((*(X+rs1) + (int16_t)sext<12>(imm)) & addr_mask); + if(new_pc % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*PC + 4); + } + *NEXT_PC = new_pc; + this->core.reg.last_branch = 1; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BEQ: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "beq"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) == *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BNE: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bne"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) != *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BLT: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "blt"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if((int32_t)*(X+rs1) < (int32_t)*(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BGE: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bge"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if((int32_t)*(X+rs1) >= (int32_t)*(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BLTU: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bltu"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) < *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::BGEU: { + uint16_t imm = ((bit_sub<7,1>(instr) << 11) | (bit_sub<8,4>(instr) << 1) | (bit_sub<25,6>(instr) << 5) | (bit_sub<31,1>(instr) << 12)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rs2}, {imm:#0x}", fmt::arg("mnemonic", "bgeu"), + fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(*(X+rs1) >= *(X+rs2)) { + if(imm % traits::INSTR_ALIGNMENT) { + raise(0, 0); + } + else { + *NEXT_PC = (uint32_t)(*PC + (int16_t)sext<13>(imm)); + this->core.reg.last_branch = 1; + } + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LB: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lb"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int8_t res_23 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int8_t res = (int8_t)res_23; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LH: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lh"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int16_t res_24 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int16_t res = (int16_t)res_24; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LW: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lw"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + int32_t res_25 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + int32_t res = (int32_t)res_25; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LBU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lbu"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + uint8_t res_26 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint8_t res = res_26; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::LHU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {imm}({rs1})", fmt::arg("mnemonic", "lhu"), + fmt::arg("rd", name(rd)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t load_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + uint16_t res_27 = super::template read_mem(traits::MEM, load_address); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint16_t res = res_27; + if(rd != 0) { + *(X+rd) = (uint32_t)res; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SB: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sb"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint8_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SH: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sh"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint16_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SW: { + uint16_t imm = ((bit_sub<7,5>(instr)) | (bit_sub<25,7>(instr) << 5)); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs2}, {imm}({rs1})", fmt::arg("mnemonic", "sw"), + fmt::arg("rs2", name(rs2)), fmt::arg("imm", imm), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rs2 >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t store_address = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + super::template write_mem(traits::MEM, store_address, (uint32_t)*(X+rs2)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ADDI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "addi"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) + (int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "slti"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = ((int32_t)*(X+rs1) < (int16_t)sext<12>(imm))? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTIU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "sltiu"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (*(X+rs1) < (uint32_t)((int16_t)sext<12>(imm)))? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::XORI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "xori"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) ^ (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ORI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "ori"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) | (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ANDI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {imm}", fmt::arg("mnemonic", "andi"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) & (uint32_t)((int16_t)sext<12>(imm)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLLI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "slli"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) << shamt; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRLI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "srli"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) >> shamt; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRAI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t shamt = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {shamt}", fmt::arg("mnemonic", "srai"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("shamt", shamt)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> shamt); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ADD: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "add"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) + *(X+rs2)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SUB: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sub"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)(*(X+rs1) - *(X+rs2)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sll"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) << (*(X+rs2) & (traits::XLEN - 1)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLT: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "slt"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (int32_t)*(X+rs1) < (int32_t)*(X+rs2)? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SLTU: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sltu"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) < *(X+rs2)? 1 : 0; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::XOR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "xor"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) ^ *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRL: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "srl"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) >> (*(X+rs2) & (traits::XLEN - 1)); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::SRA: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "sra"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = (uint32_t)((int32_t)*(X+rs1) >> (*(X+rs2) & (traits::XLEN - 1))); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::OR: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "or"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) | *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::AND: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t rs2 = ((bit_sub<20,5>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {rs1}, {rs2}", fmt::arg("mnemonic", "and"), + fmt::arg("rd", name(rd)), fmt::arg("rs1", name(rs1)), fmt::arg("rs2", name(rs2))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS || rs2 >= traits::RFS) { + raise(0, 2); + } + else { + if(rd != 0) { + *(X+rd) = *(X+rs1) & *(X+rs2); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::FENCE: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint8_t succ = ((bit_sub<20,4>(instr))); + uint8_t pred = ((bit_sub<24,4>(instr))); + uint8_t fm = ((bit_sub<28,4>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {pred}, {succ} ({fm} , {rs1}, {rd})", fmt::arg("mnemonic", "fence"), + fmt::arg("pred", pred), fmt::arg("succ", succ), fmt::arg("fm", fm), fmt::arg("rs1", name(rs1)), fmt::arg("rd", name(rd))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + super::template write_mem(traits::FENCE, traits::fence, (uint8_t)pred << 4 | succ); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::ECALL: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "ecall"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + raise(0, 11); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::EBREAK: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "ebreak"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + raise(0, 3); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::MRET: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "mret"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + leave(3); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::WFI: { + if(this->disass_enabled){ + /* generate console output when executing the command */ + this->core.disass_output(pc.val, "wfi"); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + wait(1); + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRW: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrw"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t xrs1 = *(X+rs1); + if(rd != 0) { + uint32_t res_28 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_28; + super::template write_mem(traits::CSR, csr, xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + *(X+rd) = xrd; + } + else { + super::template write_mem(traits::CSR, csr, xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRS: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrs"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_29 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_29; + uint32_t xrs1 = *(X+rs1); + if(rs1 != 0) { + super::template write_mem(traits::CSR, csr, xrd | xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRC: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {rs1}", fmt::arg("mnemonic", "csrrc"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("rs1", name(rs1))); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS || rs1 >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_30 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_30; + uint32_t xrs1 = *(X+rs1); + if(rs1 != 0) { + super::template write_mem(traits::CSR, csr, xrd & ~ xrs1); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRWI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrwi"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_31 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_31; + super::template write_mem(traits::CSR, csr, (uint32_t)zimm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRSI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrsi"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_32 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_32; + if(zimm != 0) { + super::template write_mem(traits::CSR, csr, xrd | (uint32_t)zimm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::CSRRCI: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t zimm = ((bit_sub<15,5>(instr))); + uint16_t csr = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rd}, {csr}, {zimm:#0x}", fmt::arg("mnemonic", "csrrci"), + fmt::arg("rd", name(rd)), fmt::arg("csr", csr), fmt::arg("zimm", zimm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers + auto* X = reinterpret_cast(this->regs_base_ptr+arch::traits::reg_byte_offsets[arch::traits::X0]);// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + if(rd >= traits::RFS) { + raise(0, 2); + } + else { + uint32_t res_33 = super::template read_mem(traits::CSR, csr); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + uint32_t xrd = res_33; + if(zimm != 0) { + super::template write_mem(traits::CSR, csr, xrd & ~ ((uint32_t)zimm)); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + if(rd != 0) { + *(X+rd) = xrd; + } + } + } + break; + }// @suppress("No break at end of case") + case arch::traits::opcode_e::FENCE_I: { + uint8_t rd = ((bit_sub<7,5>(instr))); + uint8_t rs1 = ((bit_sub<15,5>(instr))); + uint16_t imm = ((bit_sub<20,12>(instr))); + if(this->disass_enabled){ + /* generate console output when executing the command */ + auto mnemonic = fmt::format( + "{mnemonic:10} {rs1}, {rd}, {imm}", fmt::arg("mnemonic", "fence_i"), + fmt::arg("rs1", name(rs1)), fmt::arg("rd", name(rd)), fmt::arg("imm", imm)); + this->core.disass_output(pc.val, mnemonic); + } + // used registers// calculate next pc value + *NEXT_PC = *PC + 4; + // execute instruction + { + super::template write_mem(traits::FENCE, traits::fencei, imm); + if(this->core.reg.trap_state>=0x80000000UL) throw memory_access_exception(); + } + break; + }// @suppress("No break at end of case") + default: { + *NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2); + raise(0, 2); + } + } + }catch(memory_access_exception& e){} + // post execution stuff + process_spawn_blocks(); + if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast(inst_id)); + // if(!this->core.reg.trap_state) // update trap state if there is a pending interrupt + // this->core.reg.trap_state = this->core.reg.pending_trap; + // trap check + if(trap_state!=0){ + super::core.enter_trap(trap_state, pc.val, instr); + } else { + icount++; + instret++; + } + cycle++; + pc.val=*NEXT_PC; + this->core.reg.PC = this->core.reg.NEXT_PC; + this->core.reg.trap_state = this->core.reg.pending_trap; + } + } + return pc; +} + +} // namespace tgc5b + +template <> +std::unique_ptr create(arch::tgc5b *core, unsigned short port, bool dump) { + auto ret = new tgc5b::vm_impl(*core, dump); + if (port != 0) debugger::server::run_server(ret, port); + return std::unique_ptr(ret); +} +} // namespace interp +} // namespace iss + +#include +#include +#include +namespace iss { +namespace { +volatile std::array dummy = { + core_factory::instance().register_creator("tgc5b|m_p|interp", [](unsigned port, void*) -> std::tuple{ + auto* cpu = new iss::arch::riscv_hart_m_p(); + auto vm = new interp::tgc5b::vm_impl(*cpu, false); + if (port != 0) debugger::server::run_server(vm, port); + return {cpu_ptr{cpu}, vm_ptr{vm}}; + }), + core_factory::instance().register_creator("tgc5b|mu_p|interp", [](unsigned port, void*) -> std::tuple{ + auto* cpu = new iss::arch::riscv_hart_mu_p(); + auto vm = new interp::tgc5b::vm_impl(*cpu, false); + if (port != 0) debugger::server::run_server(vm, port); + return {cpu_ptr{cpu}, vm_ptr{vm}}; + }) +}; +} +} +// clang-format on \ No newline at end of file