diff options
49 files changed, 556 insertions, 276 deletions
@@ -13,8 +13,10 @@ Spike supports the following RISC-V ISA features: - RV32E and RV64E base ISAs, v1.9 - Zifencei extension, v2.0 - Zicsr extension, v2.0 + - Zicntr extension, v2.0 - M extension, v2.0 - A extension, v2.1 + - B extension, v1.0 - F extension, v2.2 - D extension, v2.2 - Q extension, v2.2 @@ -37,11 +39,22 @@ Spike supports the following RISC-V ISA features: - Svnapot extension, v1.0 - Svpbmt extension, v1.0 - Svinval extension, v1.0 + - Svadu extension, v1.0 - Sdext extension, v1.0-STABLE - Sdtrig extension, v1.0-STABLE - Smepmp extension v1.0 - Smstateen extension, v1.0 + - Smdbltrp extension, v1.0 - Sscofpmf v0.5.2 + - Ssdbltrp extension, v1.0 + - Ssqosid extension, v1.0 + - Zaamo extension, v1.0 + - Zalrsc extension, v1.0 + - Zabha extension, v1.0 + - Zacas extension, v1.0 + - Zawrs extension, v1.0 + - Zicfiss extension, v1.0 + - Zicfilp extension, v1.0 - Zca extension, v1.0 - Zcb extension, v1.0 - Zcf extension, v1.0 @@ -61,8 +74,9 @@ Spike supports the following RISC-V ISA features: - Zvkt extension, v1.0 - Zvkn, Zvknc, Zvkng extension, v1.0 - Zvks, Zvksc, Zvksg extension, v1.0 - - Zilsd extension, v0.9.0 - - Zcmlsd extension, v0.9.0 + - Zicond extension, v1.0 + - Zilsd extension, v0.10 + - Zclsd extension, v0.10 Versioning and APIs ------------------- diff --git a/ci-tests/create-ci-binary-tarball b/ci-tests/create-ci-binary-tarball index abc9ee0..73a549e 100755 --- a/ci-tests/create-ci-binary-tarball +++ b/ci-tests/create-ci-binary-tarball @@ -4,7 +4,7 @@ set -e rm -rf build mkdir -p build/pk && cd "$_" -`git rev-parse --show-toplevel`/../riscv-pk/configure --host=riscv64-unknown-elf +`git rev-parse --show-toplevel`/../riscv-pk/configure --host=riscv64-unknown-elf --with-arch=rv64gc_zifencei make -j4 cd - @@ -16,9 +16,14 @@ mkdir -p build/dummy-slliuw && cd "$_" riscv64-unknown-elf-gcc -O2 -o dummy-slliuw `git rev-parse --show-toplevel`/ci-tests/dummy-slliuw.c cd - +mkdir -p build/dummycsr && cd "$_" +riscv64-unknown-elf-gcc -O2 -o customcsr `git rev-parse --show-toplevel`/ci-tests/customcsr.c +cd - + mv build/pk/pk . mv build/hello/hello . mv build/dummy-slliuw/dummy-slliuw . -tar -cf spike-ci.tar pk hello dummy-slliuw +mv build/dummycsr/customcsr . +tar -cf spike-ci.tar pk hello dummy-slliuw customcsr -rm pk hello dummy-slliuw +rm pk hello dummy-slliuw customcsr diff --git a/ci-tests/custom-csr.cc b/ci-tests/custom-csr.cc new file mode 100644 index 0000000..a605dc3 --- /dev/null +++ b/ci-tests/custom-csr.cc @@ -0,0 +1,81 @@ +#include <riscv/extension.h> +#include <riscv/sim.h> + + +class dummycsr_t: public csr_t { + public: + dummycsr_t(processor_t *proc, const reg_t addr): csr_t(proc, addr) {} + + reg_t read() const noexcept override { + return 42; + } + + void verify_permissions(insn_t insn, bool write) const override {} + + protected: + bool unlogged_write(const reg_t val) noexcept override { + return true; + } +}; + +// dummy extension with dummy CSRs. Nice. +struct xdummycsr_t : public extension_t { + const char *name() { return "dummycsr"; } + + xdummycsr_t() {} + + std::vector<insn_desc_t> get_instructions() override { + return {}; + } + + std::vector<disasm_insn_t *> get_disasms() override { + return {}; + } + + std::vector<csr_t_p> get_csrs(processor_t &proc) const override { + return {std::make_shared<dummycsr_t>(&proc, /*Addr*/ 0xfff)}; + } +}; + +REGISTER_EXTENSION(dummycsr, []() { return new xdummycsr_t; }) + +// Copied from spike main. +// TODO: This should really be provided in libriscv +static std::vector<std::pair<reg_t, abstract_mem_t *>> +make_mems(const std::vector<mem_cfg_t> &layout) { + std::vector<std::pair<reg_t, abstract_mem_t *>> mems; + mems.reserve(layout.size()); + for (const auto &cfg : layout) { + mems.push_back(std::make_pair(cfg.get_base(), new mem_t(cfg.get_size()))); + } + return mems; +} + +int main(int argc, char **argv) { + cfg_t cfg; + cfg.isa = "RV64IMAFDCV_Zicsr_xdummycsr"; + std::vector<device_factory_sargs_t> plugin_devices; + if (argc != 3) { + std::cerr << "Error: invalid arguments\n"; + exit(1); + } + std::vector<std::string> htif_args{argv[1] /* pk */, argv[2] /* executable */}; + debug_module_config_t dm_config = {.progbufsize = 2, + .max_sba_data_width = 0, + .require_authentication = false, + .abstract_rti = 0, + .support_hasel = true, + .support_abstract_csr_access = true, + .support_abstract_fpr_access = true, + .support_haltgroups = true, + .support_impebreak = true}; + std::vector<std::pair<reg_t, abstract_mem_t *>> mems = + make_mems(cfg.mem_layout); + sim_t sim(&cfg, false, mems, plugin_devices, htif_args, dm_config, + nullptr, // log_path + true, // dtb_enabled + nullptr, // dtb_file + false, // socket_enabled + nullptr); // cmd_file + sim.run(); +} diff --git a/ci-tests/customcsr.c b/ci-tests/customcsr.c new file mode 100644 index 0000000..7d02689 --- /dev/null +++ b/ci-tests/customcsr.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +int main() { + int x = 1; + // dummycsr + asm("csrr %0, 0xfff" : "=r"(x)); + if (x == 42) + printf("Executed successfully\n"); + else + printf("FAIL. Got value: %d instead of 42\n", x); + return 0; +} diff --git a/ci-tests/test-spike b/ci-tests/test-spike index 9826232..36b748a 100755 --- a/ci-tests/test-spike +++ b/ci-tests/test-spike @@ -15,9 +15,11 @@ time ../install/bin/spike --isa=rv64gc pk hello | grep "Hello, world! Pi is app # check that including sim.h in an external project works g++ -std=c++2a -I../install/include -L../install/lib $DIR/testlib.cc -lriscv -o test-libriscv g++ -std=c++2a -I../install/include -L../install/lib $DIR/test-customext.cc -lriscv -o test-customext +g++ -std=c++2a -I../install/include -L../install/lib $DIR/custom-csr.cc -lriscv -o test-custom-csr # check that all installed headers are functional g++ -std=c++2a -I../install/include -L../install/lib $DIR/testlib.cc -lriscv -o /dev/null -include ../install-hdrs-list.h LD_LIBRARY_PATH=../install/lib ./test-libriscv pk hello| grep "Hello, world! Pi is approximately 3.141588." LD_LIBRARY_PATH=../install/lib ./test-customext pk dummy-slliuw | grep "Executed successfully" +LD_LIBRARY_PATH=../install/lib ./test-custom-csr pk customcsr | grep "Executed successfully" diff --git a/config.h.in b/config.h.in index aef3596..a8918c6 100644 --- a/config.h.in +++ b/config.h.in @@ -6,12 +6,6 @@ /* Define if subproject MCPPBS_SPROJ_NORM is enabled */ #undef CUSTOMEXT_ENABLED -/* Default value for --isa switch */ -#undef DEFAULT_ISA - -/* Default value for --priv switch */ -#undef DEFAULT_PRIV - /* Define if subproject MCPPBS_SPROJ_NORM is enabled */ #undef DISASM_ENABLED @@ -737,8 +737,6 @@ with_boost with_boost_libdir with_boost_asio with_boost_regex -with_isa -with_priv with_target enable_dual_endian ' @@ -1403,9 +1401,6 @@ Optional Packages: use the Regex library from boost - it is possible to specify a certain library for the linker e.g. --with-boost-regex=boost_regex-gcc-mt-d-1_33_1 - --with-isa=RV64IMAFDC_zicntr_zihpm - Sets the default RISC-V ISA - --with-priv=MSU Sets the default RISC-V privilege modes supported --with-target=riscv64-unknown-elf Sets the default target config @@ -6562,34 +6557,6 @@ fi -# Check whether --with-isa was given. -if test ${with_isa+y} -then : - withval=$with_isa; -printf "%s\n" "#define DEFAULT_ISA \"$withval\"" >>confdefs.h - -else $as_nop - -printf "%s\n" "#define DEFAULT_ISA \"RV64IMAFDC_zicntr_zihpm\"" >>confdefs.h - -fi - - - -# Check whether --with-priv was given. -if test ${with_priv+y} -then : - withval=$with_priv; -printf "%s\n" "#define DEFAULT_PRIV \"$withval\"" >>confdefs.h - -else $as_nop - -printf "%s\n" "#define DEFAULT_PRIV \"MSU\"" >>confdefs.h - -fi - - - # Check whether --with-target was given. if test ${with_target+y} then : diff --git a/configure.ac b/configure.ac index 701bd99..b46dc15 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ #========================================================================= # Toplevel configure.ac for the Modular C++ Build System #========================================================================= -# Please read the documenation in 'mcppbs-doc.txt' for more details on +# Please read the documentation in 'mcppbs-doc.txt' for more details on # how the Modular C++ Build System works. For most new projects, a # developer will only need to make the following changes: # diff --git a/disasm/disasm.cc b/disasm/disasm.cc index c3ba62a..a2acd4e 100644 --- a/disasm/disasm.cc +++ b/disasm/disasm.cc @@ -2,6 +2,7 @@ #include "disasm.h" #include "decode_macros.h" +#include "platform.h" #include <cassert> #include <string> #include <vector> @@ -735,16 +736,31 @@ static void NOINLINE add_vector_vv_insn(disassembler_t* d, const char* name, uin d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &vs2, &vs1, opt, &vm})); } +static void NOINLINE add_vector_multiplyadd_vv_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) +{ + d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &vs1, &vs2, opt, &vm})); +} + static void NOINLINE add_vector_vx_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) { d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &vs2, &xrs1, opt, &vm})); } +static void NOINLINE add_vector_multiplyadd_vx_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) +{ + d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &xrs1, &vs2, opt, &vm})); +} + static void NOINLINE add_vector_vf_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) { d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &vs2, &frs1, opt, &vm})); } +static void NOINLINE add_vector_multiplyadd_vf_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) +{ + d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &frs1, &vs2, opt, &vm})); +} + static void NOINLINE add_vector_vi_insn(disassembler_t* d, const char* name, uint32_t match, uint32_t mask) { d->add_insn(new disasm_insn_t(name, match, mask, {&vd, &vs2, &v_simm5, opt, &vm})); @@ -1497,7 +1513,7 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DISASM_INSN("c.addiw", c_addiw, 0, {&xrd, &rvc_imm}); } - if (isa->get_max_xlen() == 64 || isa->extension_enabled(EXT_ZCMLSD)) { + if (isa->get_max_xlen() == 64 || isa->extension_enabled(EXT_ZCLSD)) { DISASM_INSN("c.ld", c_ld, 0, {&rvc_rs2s, &rvc_ld_address}); DISASM_INSN("c.ldsp", c_ldsp, 0, {&xrd, &rvc_ldsp_address}); DISASM_INSN("c.sd", c_sd, 0, {&rvc_rs2s, &rvc_ld_address}); @@ -1642,8 +1658,11 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) #define DEFINE_VECTOR_V(code) add_vector_v_insn(this, #code, match_##code, mask_##code) #define DEFINE_VECTOR_VV(code) add_vector_vv_insn(this, #code, match_##code, mask_##code) + #define DEFINE_VECTOR_MULTIPLYADD_VV(code) add_vector_multiplyadd_vv_insn(this, #code, match_##code, mask_##code) #define DEFINE_VECTOR_VX(code) add_vector_vx_insn(this, #code, match_##code, mask_##code) + #define DEFINE_VECTOR_MULTIPLYADD_VX(code) add_vector_multiplyadd_vx_insn(this, #code, match_##code, mask_##code) #define DEFINE_VECTOR_VF(code) add_vector_vf_insn(this, #code, match_##code, mask_##code) + #define DEFINE_VECTOR_MULTIPLYADD_VF(code) add_vector_multiplyadd_vf_insn(this, #code, match_##code, mask_##code) #define DEFINE_VECTOR_VI(code) add_vector_vi_insn(this, #code, match_##code, mask_##code) #define DEFINE_VECTOR_VIU(code) add_vector_viu_insn(this, #code, match_##code, mask_##code) @@ -1659,6 +1678,10 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_VECTOR_VV(name##_vv); \ DEFINE_VECTOR_VX(name##_vx) + #define DISASM_OPIV_MULTIPLYADD_VX__INSN(name, sign) \ + DEFINE_VECTOR_MULTIPLYADD_VV(name##_vv); \ + DEFINE_VECTOR_MULTIPLYADD_VX(name##_vx) + #define DISASM_OPIV__XI_INSN(name, sign) \ DEFINE_VECTOR_VX(name##_vx); \ if (sign) \ @@ -1678,6 +1701,8 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) #define DISASM_OPIV__X__INSN(name, sign) DEFINE_VECTOR_VX(name##_vx) + #define DISASM_OPIV_MULTIPLYADD__X__INSN(name, sign) DEFINE_VECTOR_MULTIPLYADD_VX(name##_vx) + #define DEFINE_VECTOR_VVM(name) \ add_vector_vvm_insn(this, #name, match_##name, mask_##name | mask_vm) @@ -1821,10 +1846,10 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DISASM_OPIV_VX__INSN(vmul, 1); DISASM_OPIV_VX__INSN(vmulhsu, 0); DISASM_OPIV_VX__INSN(vmulh, 1); - DISASM_OPIV_VX__INSN(vmadd, 1); - DISASM_OPIV_VX__INSN(vnmsub, 1); - DISASM_OPIV_VX__INSN(vmacc, 1); - DISASM_OPIV_VX__INSN(vnmsac, 1); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vmadd, 1); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vnmsub, 1); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vmacc, 1); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vnmsac, 1); //0b11_0000 DISASM_OPIV_VX__INSN(vwaddu, 0); @@ -1838,10 +1863,10 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DISASM_OPIV_VX__INSN(vwmulu, 0); DISASM_OPIV_VX__INSN(vwmulsu, 0); DISASM_OPIV_VX__INSN(vwmul, 1); - DISASM_OPIV_VX__INSN(vwmaccu, 0); - DISASM_OPIV_VX__INSN(vwmacc, 1); - DISASM_OPIV__X__INSN(vwmaccus, 1); - DISASM_OPIV_VX__INSN(vwmaccsu, 0); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vwmaccu, 0); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vwmacc, 1); + DISASM_OPIV_MULTIPLYADD__X__INSN(vwmaccus, 1); + DISASM_OPIV_MULTIPLYADD_VX__INSN(vwmaccsu, 0); #undef DISASM_OPIV_VXI_INSN #undef DISASM_OPIV_VX__INSN @@ -1858,6 +1883,10 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_VECTOR_VV(name##_vv); \ DEFINE_VECTOR_VF(name##_vf) + #define DISASM_OPIV_MULTIPLYADD_VF_INSN(name) \ + DEFINE_VECTOR_MULTIPLYADD_VV(name##_vv); \ + DEFINE_VECTOR_MULTIPLYADD_VF(name##_vf) + #define DISASM_OPIV_WF_INSN(name) \ DEFINE_VECTOR_VV(name##_wv); \ DEFINE_VECTOR_VF(name##_wf) @@ -1925,14 +1954,14 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DISASM_OPIV_VF_INSN(vfmul); DISASM_OPIV__F_INSN(vfrsub); - DISASM_OPIV_VF_INSN(vfmadd); - DISASM_OPIV_VF_INSN(vfnmadd); - DISASM_OPIV_VF_INSN(vfmsub); - DISASM_OPIV_VF_INSN(vfnmsub); - DISASM_OPIV_VF_INSN(vfmacc); - DISASM_OPIV_VF_INSN(vfnmacc); - DISASM_OPIV_VF_INSN(vfmsac); - DISASM_OPIV_VF_INSN(vfnmsac); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfmadd); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfnmadd); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfmsub); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfnmsub); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfmacc); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfnmacc); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfmsac); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfnmsac); //0b11_0000 DISASM_OPIV_VF_INSN(vfwadd); @@ -1942,10 +1971,10 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DISASM_OPIV_WF_INSN(vfwadd); DISASM_OPIV_WF_INSN(vfwsub); DISASM_OPIV_VF_INSN(vfwmul); - DISASM_OPIV_VF_INSN(vfwmacc); - DISASM_OPIV_VF_INSN(vfwnmacc); - DISASM_OPIV_VF_INSN(vfwmsac); - DISASM_OPIV_VF_INSN(vfwnmsac); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfwmacc); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfwnmacc); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfwmsac); + DISASM_OPIV_MULTIPLYADD_VF_INSN(vfwnmsac); #undef DISASM_OPIV_VF_INSN #undef DISASM_OPIV__F_INSN diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc index 79203df..69c3e34 100644 --- a/disasm/isa_parser.cc +++ b/disasm/isa_parser.cc @@ -9,6 +9,26 @@ static std::string strtolower(const char* str) return res; } +static unsigned long safe_stoul(const std::string& s) +{ + int old_errno = errno; + errno = 0; + + char* endp; + unsigned long ret = strtoul(s.c_str(), &endp, 10); + + int new_errno = errno; + errno = old_errno; + + if (endp == s.c_str() || *endp) + throw std::invalid_argument("stoul"); + + if (new_errno) + throw std::out_of_range("stoul"); + + return ret; +} + static void bad_option_string(const char *option, const char *value, const char *msg) { @@ -30,7 +50,7 @@ static void bad_priv_string(const char* priv) isa_parser_t::isa_parser_t(const char* str, const char *priv) { isa_string = strtolower(str); - const char* all_subsets = "mafdqcpvhb"; + const char* all_subsets = "mafdqcbpvh"; if (isa_string.compare(0, 4, "rv32") == 0) max_xlen = 32; @@ -247,8 +267,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) if (max_xlen != 32) bad_isa_string(str, "'Zilsd' requires RV32"); extension_table[EXT_ZILSD] = true; - } else if (ext_str == "zcmlsd") { - extension_table[EXT_ZCMLSD] = true; + } else if (ext_str == "zclsd") { + extension_table[EXT_ZCLSD] = true; } else if (ext_str == "zvbb") { extension_table[EXT_ZVBB] = true; } else if (ext_str == "zvbc") { @@ -327,17 +347,17 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) } else if (ext_str.substr(0, 3) == "zvl") { reg_t new_vlen; try { - new_vlen = std::stol(ext_str.substr(3, ext_str.size() - 4)); + new_vlen = safe_stoul(ext_str.substr(3, ext_str.size() - 4)); } catch (std::logic_error& e) { new_vlen = 0; } - if ((new_vlen & (new_vlen - 1)) != 0 || new_vlen < 32) + if ((new_vlen & (new_vlen - 1)) != 0 || new_vlen < 32 || ext_str.back() != 'b') bad_isa_string(str, ("Invalid Zvl string: " + ext_str).c_str()); vlen = std::max(vlen, new_vlen); } else if (ext_str.substr(0, 3) == "zve") { reg_t new_elen; try { - new_elen = std::stol(ext_str.substr(3, ext_str.size() - 4)); + new_elen = safe_stoul(ext_str.substr(3, ext_str.size() - 4)); } catch (std::logic_error& e) { new_elen = 0; } @@ -355,6 +375,8 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) elen = std::max(elen, new_elen); } else if (ext_str == "ssdbltrp") { extension_table[EXT_SSDBLTRP] = true; + } else if (ext_str == "smdbltrp") { + extension_table[EXT_SMDBLTRP] = true; } else if (ext_str[0] == 'x') { extension_table['X'] = true; if (ext_str.size() == 1) { @@ -394,12 +416,12 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) extension_table[EXT_ZCD] = true; } - if (extension_table[EXT_ZCMLSD] && extension_table[EXT_ZCF]) { - bad_isa_string(str, "'Zcmlsd' extension conflicts with 'Zcf' extensions"); + if (extension_table[EXT_ZCLSD] && extension_table[EXT_ZCF]) { + bad_isa_string(str, "'Zclsd' extension conflicts with 'Zcf' extensions"); } - if (extension_table[EXT_ZCMLSD] && (!extension_table[EXT_ZCA] || !extension_table[EXT_ZILSD])) { - bad_isa_string(str, "'Zcmlsd' extension requires 'Zca' and 'Zilsd' extensions"); + if (extension_table[EXT_ZCLSD] && (!extension_table[EXT_ZCA] || !extension_table[EXT_ZILSD])) { + bad_isa_string(str, "'Zclsd' extension requires 'Zca' and 'Zilsd' extensions"); } if (extension_table[EXT_ZFBFMIN] && !extension_table['F']) { diff --git a/fesvr/htif_pthread.h b/fesvr/htif_pthread.h index c00c382..ab56007 100644 --- a/fesvr/htif_pthread.h +++ b/fesvr/htif_pthread.h @@ -13,7 +13,7 @@ class htif_pthread_t : public htif_t htif_pthread_t(int argc, char** argv); virtual ~htif_pthread_t(); - // target inteface + // target interface void send(const void* buf, size_t size); void recv(void* buf, size_t size); bool recv_nonblocking(void* buf, size_t size); diff --git a/riscv/cfg.cc b/riscv/cfg.cc index 8763240..2f9a229 100644 --- a/riscv/cfg.cc +++ b/riscv/cfg.cc @@ -18,13 +18,15 @@ bool mem_cfg_t::check_if_supported(reg_t base, reg_t size) // the regions in the first place, but we have them here to make sure that // we can't end up describing memory regions that don't make sense. They // ask that the page size is a multiple of the minimum page size, that the - // page is aligned to the minimum page size, that the page is non-empty and - // that the top address is still representable in a reg_t. + // page is aligned to the minimum page size, that the page is non-empty, + // that the size doesn't overflow size_t, and that the top address is still + // representable in a reg_t. // // Note: (base + size == 0) part of the assertion is to handle cases like // { base = 0xffff_ffff_ffff_f000, size: 0x1000 } return (size % PGSIZE == 0) && (base % PGSIZE == 0) && + (size_t(size) == size) && (size > 0) && ((base + size > base) || (base + size == 0)); } diff --git a/riscv/csr_init.cc b/riscv/csr_init.cc index d72c925..a03d188 100644 --- a/riscv/csr_init.cc +++ b/riscv/csr_init.cc @@ -86,7 +86,6 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) add_csr(which_mevent, mevent[i]); } } - add_csr(CSR_MCOUNTINHIBIT, std::make_shared<const_csr_t>(proc, CSR_MCOUNTINHIBIT, 0)); add_const_ext_csr(EXT_SSCOFPMF, CSR_SCOUNTOVF, std::make_shared<scountovf_csr_t>(proc, CSR_SCOUNTOVF)); add_csr(CSR_MIE, mie = std::make_shared<mie_csr_t>(proc, CSR_MIE)); add_csr(CSR_MIP, mip = std::make_shared<mip_csr_t>(proc, CSR_MIP)); @@ -134,6 +133,7 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) add_supervisor_csr(CSR_MIDELEG, mideleg = std::make_shared<mideleg_csr_t>(proc, CSR_MIDELEG)); const reg_t counteren_mask = (proc->extension_enabled_const(EXT_ZICNTR) ? 0x7UL : 0x0) | (proc->extension_enabled_const(EXT_ZIHPM) ? 0xfffffff8ULL : 0x0); add_user_csr(CSR_MCOUNTEREN, mcounteren = std::make_shared<masked_csr_t>(proc, CSR_MCOUNTEREN, counteren_mask, 0)); + add_csr(CSR_MCOUNTINHIBIT, mcountinhibit = std::make_shared<masked_csr_t>(proc, CSR_MCOUNTINHIBIT, counteren_mask & (~MCOUNTEREN_TIME), 0)); add_supervisor_csr(CSR_SCOUNTEREN, scounteren = std::make_shared<masked_csr_t>(proc, CSR_SCOUNTEREN, counteren_mask, 0)); nonvirtual_sepc = std::make_shared<epc_csr_t>(proc, CSR_SEPC); add_hypervisor_csr(CSR_VSEPC, vsepc = std::make_shared<epc_csr_t>(proc, CSR_VSEPC)); @@ -205,13 +205,14 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa) add_csr(CSR_TDATA2, tdata2 = std::make_shared<tdata2_csr_t>(proc, CSR_TDATA2)); add_csr(CSR_TDATA3, std::make_shared<tdata3_csr_t>(proc, CSR_TDATA3)); add_csr(CSR_TINFO, std::make_shared<tinfo_csr_t>(proc, CSR_TINFO)); - add_csr(CSR_TCONTROL, tcontrol = std::make_shared<masked_csr_t>(proc, CSR_TCONTROL, CSR_TCONTROL_MPTE | CSR_TCONTROL_MTE, 0)); + if (!proc->extension_enabled_const('S')) { + add_csr(CSR_TCONTROL, tcontrol = std::make_shared<masked_csr_t>(proc, CSR_TCONTROL, CSR_TCONTROL_MPTE | CSR_TCONTROL_MTE, 0)); + } } else { add_csr(CSR_TDATA1, std::make_shared<const_csr_t>(proc, CSR_TDATA1, 0)); add_csr(CSR_TDATA2, tdata2 = std::make_shared<const_csr_t>(proc, CSR_TDATA2, 0)); add_csr(CSR_TDATA3, std::make_shared<const_csr_t>(proc, CSR_TDATA3, 0)); add_csr(CSR_TINFO, std::make_shared<const_csr_t>(proc, CSR_TINFO, 0)); - add_csr(CSR_TCONTROL, tcontrol = std::make_shared<const_csr_t>(proc, CSR_TCONTROL, 0)); } unsigned scontext_length = (xlen == 32 ? 16 : 32); // debug spec suggests 16-bit for RV32 and 32-bit for RV64 add_supervisor_csr(CSR_SCONTEXT, scontext = std::make_shared<masked_csr_t>(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0)); diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 6fdd6a3..a62f63a 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -548,13 +548,16 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { | (has_page ? MSTATUS_TVM : 0) | (has_gva ? MSTATUS_GVA : 0) | (has_mpv ? MSTATUS_MPV : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | (proc->extension_enabled(EXT_ZICFILP) ? (MSTATUS_SPELP | MSTATUS_MPELP) : 0) | (proc->extension_enabled(EXT_SSDBLTRP) ? SSTATUS_SDT : 0) ; const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP)); const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp); - const reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + reg_t new_mstatus = (read() & ~mask) | (adjusted_val & mask); + new_mstatus = (new_mstatus & MSTATUS_MDT) ? (new_mstatus & ~MSTATUS_MIE) : new_mstatus; + new_mstatus = (new_mstatus & MSTATUS_SDT) ? (new_mstatus & ~MSTATUS_SIE) : new_mstatus; maybe_flush_tlb(new_mstatus); this->val = adjust_sd(new_mstatus); return true; @@ -569,6 +572,7 @@ reg_t mstatus_csr_t::compute_mstatus_initial_value() const noexcept { | (proc->extension_enabled_const('U') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_UXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->extension_enabled_const('S') && (proc->get_const_xlen() != 32) ? set_field((reg_t)0, MSTATUS_SXL, xlen_to_uxl(proc->get_const_xlen())) : 0) | (proc->get_mmu()->is_target_big_endian() ? big_endian_bits : 0) + | (proc->extension_enabled(EXT_SMDBLTRP) ? MSTATUS_MDT : 0) | 0; // initial value for mstatus } @@ -1340,9 +1344,10 @@ dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr): ebreaku(false), ebreakvs(false), ebreakvu(false), - halt(false), v(false), cause(0), + ext_cause(0), + cetrig(0), pelp(elp_t::NO_LP_EXPECTED) { } @@ -1363,6 +1368,9 @@ reg_t dcsr_csr_t::read() const noexcept { result = set_field(result, DCSR_STOPCOUNT, 0); result = set_field(result, DCSR_STOPTIME, 0); result = set_field(result, DCSR_CAUSE, cause); + result = set_field(result, DCSR_EXTCAUSE, ext_cause); + if (proc->extension_enabled(EXT_SMDBLTRP)) + result = set_field(result, DCSR_CETRIG, cetrig); result = set_field(result, DCSR_STEP, step); result = set_field(result, DCSR_PRV, prv); result = set_field(result, CSR_DCSR_V, v); @@ -1382,12 +1390,14 @@ bool dcsr_csr_t::unlogged_write(const reg_t val) noexcept { v = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_V) : false; pelp = proc->extension_enabled(EXT_ZICFILP) ? static_cast<elp_t>(get_field(val, DCSR_PELP)) : elp_t::NO_LP_EXPECTED; + cetrig = proc->extension_enabled(EXT_SMDBLTRP) ? get_field(val, DCSR_CETRIG) : false; return true; } -void dcsr_csr_t::update_fields(const uint8_t cause, const reg_t prv, +void dcsr_csr_t::update_fields(const uint8_t cause, uint8_t ext_cause, const reg_t prv, const bool v, const elp_t pelp) noexcept { this->cause = cause; + this->ext_cause = ext_cause; this->prv = prv; this->v = v; this->pelp = pelp; @@ -1400,8 +1410,9 @@ float_csr_t::float_csr_t(processor_t* const proc, const reg_t addr, const reg_t void float_csr_t::verify_permissions(insn_t insn, bool write) const { masked_csr_t::verify_permissions(insn, write); - require_fs; - if (!proc->extension_enabled('F') && !proc->extension_enabled(EXT_ZFINX)) + + if (!((proc->extension_enabled('F') && STATE.sstatus->enabled(SSTATUS_FS)) + || proc->extension_enabled(EXT_ZFINX))) throw trap_illegal_instruction(insn.bits()); if (proc->extension_enabled(EXT_SMSTATEEN) && proc->extension_enabled(EXT_ZFINX)) { @@ -1421,7 +1432,8 @@ void float_csr_t::verify_permissions(insn_t insn, bool write) const { } bool float_csr_t::unlogged_write(const reg_t val) noexcept { - dirty_fp_state; + if (!proc->extension_enabled(EXT_ZFINX)) + dirty_fp_state; return masked_csr_t::unlogged_write(val); } diff --git a/riscv/csrs.h b/riscv/csrs.h index db61fba..278bdb3 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -684,7 +684,7 @@ class dcsr_csr_t: public csr_t { dcsr_csr_t(processor_t* const proc, const reg_t addr); virtual void verify_permissions(insn_t insn, bool write) const override; virtual reg_t read() const noexcept override; - void update_fields(const uint8_t cause, const reg_t prv, + void update_fields(const uint8_t cause, const uint8_t ext_cause, const reg_t prv, const bool v, const elp_t pelp) noexcept; protected: virtual bool unlogged_write(const reg_t val) noexcept override; @@ -696,9 +696,10 @@ class dcsr_csr_t: public csr_t { bool ebreaku; bool ebreakvs; bool ebreakvu; - bool halt; bool v; uint8_t cause; + uint8_t ext_cause; + bool cetrig; elp_t pelp; }; diff --git a/riscv/debug_module.cc b/riscv/debug_module.cc index 5d49605..7c59744 100644 --- a/riscv/debug_module.cc +++ b/riscv/debug_module.cc @@ -462,9 +462,9 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value) } } - // We don't allow selecting non-existant harts through + // We don't allow selecting non-existent harts through // hart_array_mask, so the only way it's possible is by writing a - // non-existant hartsel. + // non-existent hartsel. dmstatus.anynonexistant = dmcontrol.hartsel >= sim->get_cfg().nprocs(); result = set_field(result, DM_DMSTATUS_IMPEBREAK, diff --git a/riscv/decode_macros.h b/riscv/decode_macros.h index 0f32a3a..807ad98 100644 --- a/riscv/decode_macros.h +++ b/riscv/decode_macros.h @@ -22,6 +22,7 @@ #define RS2 READ_REG(insn.rs2()) #define RS3 READ_REG(insn.rs3()) #define WRITE_RD(value) WRITE_REG(insn.rd(), value) +#define CHECK_RD() CHECK_REG(insn.rd()) /* 0 : int * 1 : floating @@ -30,9 +31,9 @@ * 4 : csr */ #define WRITE_REG(reg, value) ({ \ + CHECK_REG(reg); \ reg_t wdata = (value); /* value may have side effects */ \ if (DECODE_MACRO_USAGE_LOGGED) STATE.log_reg_write[(reg) << 4] = {wdata, 0}; \ - CHECK_REG(reg); \ STATE.XPR.write(reg, wdata); \ }) #define WRITE_FREG(reg, value) ({ \ @@ -83,7 +84,7 @@ // Zilsd macros #define WRITE_RD_D(value) (xlen == 32 ? WRITE_RD_PAIR(value) : WRITE_RD(value)) -// Zcmlsd macros +// Zclsd macros #define WRITE_RVC_RS2S_PAIR(value) WRITE_REG_PAIR(insn.rvc_rs2s(), value) #define RVC_RS2S_PAIR READ_REG_PAIR(insn.rvc_rs2s()) #define RVC_RS2_PAIR READ_REG_PAIR(insn.rvc_rs2()) @@ -164,7 +165,6 @@ static inline bool is_aligned(const unsigned val, const unsigned pos) #define require_extension(s) require(p->extension_enabled(s)) #define require_either_extension(A,B) require(p->extension_enabled(A) || p->extension_enabled(B)); #define require_impl(s) require(p->supports_impl(s)) -#define require_fs require(STATE.sstatus->enabled(SSTATUS_FS)) #define require_fp STATE.fflags->verify_permissions(insn, false) #define require_accelerator require(STATE.sstatus->enabled(SSTATUS_XS)) #define require_vector_vs require(p->any_vector_extensions() && STATE.sstatus->enabled(SSTATUS_VS)) diff --git a/riscv/dts.cc b/riscv/dts.cc index 7ca7c4e..9751ffe 100644 --- a/riscv/dts.cc +++ b/riscv/dts.cc @@ -99,13 +99,10 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, return s.str(); } -std::string dtc_compile(const std::string& dtc_input, const std::string& input_type, const std::string& output_type) +static std::string dtc_compile(const std::string& dtc_input, bool compile) { - if (input_type == output_type) - std::cerr << "Must have differing {in,out}put types for running " DTC << std::endl; - - if (!((input_type == "dts" && output_type == "dtb") || (input_type == "dtb" && output_type == "dts"))) - std::cerr << "Invalid {in,out}put types for running " DTC ": Must convert from 'dts' to 'dtb' (or vice versa)" << std::endl; + const char* input_type = compile ? "dts" : "dtb"; + const char* output_type = compile ? "dtb" : "dts"; int dtc_input_pipe[2]; pid_t dtc_input_pid; @@ -147,7 +144,7 @@ std::string dtc_compile(const std::string& dtc_input, const std::string& input_t close(dtc_input_pipe[1]); close(dtc_output_pipe[0]); close(dtc_output_pipe[1]); - execlp(DTC, DTC, "-O", output_type.c_str(), "-I", input_type.c_str(), (char *)0); + execlp(DTC, DTC, "-O", output_type, "-I", input_type, nullptr); std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl; exit(1); } @@ -186,6 +183,16 @@ std::string dtc_compile(const std::string& dtc_input, const std::string& input_t return dtc_output.str(); } +std::string dtb_to_dts(const std::string& dtc_input) +{ + return dtc_compile(dtc_input, false); +} + +std::string dts_to_dtb(const std::string& dtc_input) +{ + return dtc_compile(dtc_input, true); +} + int fdt_get_node_addr_size(const void *fdt, int node, reg_t *addr, unsigned long *size, const char *field) { diff --git a/riscv/dts.h b/riscv/dts.h index 987f269..730dea7 100644 --- a/riscv/dts.h +++ b/riscv/dts.h @@ -11,7 +11,8 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, std::vector<std::pair<reg_t, abstract_mem_t*>> mems, std::string device_nodes); -std::string dtc_compile(const std::string& dtc_input, const std::string& input_type, const std::string& output_type); +std::string dts_to_dtb(const std::string& dtc_input); +std::string dtb_to_dts(const std::string& dtc_input); int fdt_get_node_addr_size(const void *fdt, int node, reg_t *addr, unsigned long *size, const char *field); diff --git a/riscv/encoding.h b/riscv/encoding.h index 675b4f6..dcd4e24 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -4,7 +4,7 @@ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (c55d30f) + * https://github.com/riscv/riscv-opcodes (47862ce) */ #ifndef RISCV_CSR_ENCODING_H @@ -110,6 +110,9 @@ #define DCSR_CAUSE_STEP 4 #define DCSR_CAUSE_HALT 5 #define DCSR_CAUSE_GROUP 6 +#define DCSR_CAUSE_EXTCAUSE 7 + +#define DCSR_EXTCAUSE_CRITERR 0 #define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) #define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) @@ -117,7 +120,7 @@ #define MCONTROL_SELECT (1<<19) #define MCONTROL_TIMING (1<<18) -#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_ACTION (0xf<<12) #define MCONTROL_CHAIN (1<<11) #define MCONTROL_MATCH (0xf<<7) #define MCONTROL_M (1<<6) @@ -220,6 +223,17 @@ #define MHPMEVENTH_MINH 0x40000000 #define MHPMEVENTH_OF 0x80000000 +#define MCOUNTEREN_CY_SHIFT 0 +#define MCOUNTEREN_TIME_SHIFT 1 +#define MCOUNTEREN_IR_SHIFT 2 + +#define MCOUNTEREN_CY (1U << MCOUNTEREN_CY_SHIFT) +#define MCOUNTEREN_TIME (1U << MCOUNTEREN_TIME_SHIFT) +#define MCOUNTEREN_IR (1U << MCOUNTEREN_IR_SHIFT) + +#define MCOUNTINHIBIT_CY MCOUNTEREN_CY +#define MCOUNTINHIBIT_IR MCOUNTEREN_IR + #define HENVCFG_FIOM 0x00000001 #define HENVCFG_LPE 0x00000004 #define HENVCFG_SSE 0x00000008 @@ -3005,6 +3019,7 @@ #define INSN_FIELD_MOP_RR_T_30 0x40000000 #define INSN_FIELD_MOP_RR_T_27_26 0xc000000 #define INSN_FIELD_C_MOP_T 0x700 +#define INSN_FIELD_RS2_EQ_RS1 0x1f00000 #endif #ifdef DECLARE_INSN DECLARE_INSN(add, MATCH_ADD, MASK_ADD) diff --git a/riscv/execute.cc b/riscv/execute.cc index cc77d88..1fa6111 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -213,12 +213,12 @@ void processor_t::step(size_t n) { if (!state.debug_mode) { if (halt_request == HR_REGULAR) { - enter_debug_mode(DCSR_CAUSE_DEBUGINT); + enter_debug_mode(DCSR_CAUSE_DEBUGINT, 0); } else if (halt_request == HR_GROUP) { - enter_debug_mode(DCSR_CAUSE_GROUP); - } - else if (state.dcsr->halt) { - enter_debug_mode(DCSR_CAUSE_HALT); + enter_debug_mode(DCSR_CAUSE_GROUP, 0); + } else if (halt_on_reset) { + halt_on_reset = false; + enter_debug_mode(DCSR_CAUSE_HALT, 0); } } @@ -257,7 +257,7 @@ void processor_t::step(size_t n) if (unlikely(!state.serialized && state.single_step == state.STEP_STEPPED)) { state.single_step = state.STEP_NONE; if (!state.debug_mode) { - enter_debug_mode(DCSR_CAUSE_STEP); + enter_debug_mode(DCSR_CAUSE_STEP, 0); // enter_debug_mode changed state.pc, so we can't just continue. break; } @@ -286,6 +286,17 @@ void processor_t::step(size_t n) disasm(fetch.insn); pc = execute_insn_logged(this, pc, fetch); advance_pc(); + + // Resume from debug mode in critical error + if (state.critical_error && !state.debug_mode) { + if (state.dcsr->read() & DCSR_CETRIG) { + enter_debug_mode(DCSR_CAUSE_EXTCAUSE, DCSR_EXTCAUSE_CRITERR); + } else { + // Handling of critical error is implementation defined + // For now just enter debug mode + enter_debug_mode(DCSR_CAUSE_HALT, 0); + } + } } } else while (instret < n) @@ -311,13 +322,23 @@ void processor_t::step(size_t n) take_trap(t, pc); n = instret; + // If critical error then enter debug mode critical error trigger enabled + if (state.critical_error) { + if (state.dcsr->read() & DCSR_CETRIG) { + enter_debug_mode(DCSR_CAUSE_EXTCAUSE, DCSR_EXTCAUSE_CRITERR); + } else { + // Handling of critical error is implementation defined + // For now just enter debug mode + enter_debug_mode(DCSR_CAUSE_HALT, 0); + } + } // Trigger action takes priority over single step auto match = TM.detect_trap_match(t); if (match.has_value()) take_trigger_action(match->action, 0, state.pc, 0); else if (unlikely(state.single_step == state.STEP_STEPPED)) { state.single_step = state.STEP_NONE; - enter_debug_mode(DCSR_CAUSE_STEP); + enter_debug_mode(DCSR_CAUSE_STEP, 0); } } catch (triggers::matched_t& t) @@ -330,7 +351,7 @@ void processor_t::step(size_t n) } catch(trap_debug_mode&) { - enter_debug_mode(DCSR_CAUSE_SWBP); + enter_debug_mode(DCSR_CAUSE_SWBP, 0); } catch (wait_for_interrupt_t &t) { @@ -344,10 +365,12 @@ void processor_t::step(size_t n) in_wfi = true; } - state.minstret->bump(instret); + if (!(state.mcountinhibit->read() & MCOUNTINHIBIT_IR)) + state.minstret->bump(instret); // Model a hart whose CPI is 1. - state.mcycle->bump(instret); + if (!(state.mcountinhibit->read() & MCOUNTINHIBIT_CY)) + state.mcycle->bump(instret); n -= instret; } diff --git a/riscv/extension.h b/riscv/extension.h index de6ece3..991da7e 100644 --- a/riscv/extension.h +++ b/riscv/extension.h @@ -13,6 +13,7 @@ class extension_t public: virtual std::vector<insn_desc_t> get_instructions() = 0; virtual std::vector<disasm_insn_t*> get_disasms() = 0; + virtual std::vector<csr_t_p> get_csrs ([[maybe_unused]] processor_t &proc) const { return {}; }; virtual const char* name() = 0; virtual void reset() {}; virtual void set_debug(bool UNUSED value) {} diff --git a/riscv/insn_template.cc b/riscv/insn_template.cc index 9194d19..168e2dc 100644 --- a/riscv/insn_template.cc +++ b/riscv/insn_template.cc @@ -5,24 +5,29 @@ #define DECODE_MACRO_USAGE_LOGGED 0 +#define PROLOGUE \ + reg_t npc = sext_xlen(pc + insn_length(OPCODE)) + +#define EPILOGUE \ + trace_opcode(p, OPCODE, insn); \ + return npc + reg_t fast_rv32i_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 32 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } reg_t fast_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 64 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } #undef DECODE_MACRO_USAGE_LOGGED @@ -31,21 +36,19 @@ reg_t fast_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc) reg_t logged_rv32i_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 32 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } reg_t logged_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 64 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } #undef CHECK_REG @@ -57,21 +60,19 @@ reg_t logged_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc) reg_t fast_rv32e_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 32 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } reg_t fast_rv64e_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 64 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } #undef DECODE_MACRO_USAGE_LOGGED @@ -80,19 +81,17 @@ reg_t fast_rv64e_NAME(processor_t* p, insn_t insn, reg_t pc) reg_t logged_rv32e_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 32 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } reg_t logged_rv64e_NAME(processor_t* p, insn_t insn, reg_t pc) { #define xlen 64 - reg_t npc = sext_xlen(pc + insn_length(OPCODE)); + PROLOGUE; #include "insns/NAME.h" - trace_opcode(p, OPCODE, insn); + EPILOGUE; #undef xlen - return npc; } diff --git a/riscv/insns/c_ld.h b/riscv/insns/c_ld.h index 18e0d5e..951243c 100644 --- a/riscv/insns/c_ld.h +++ b/riscv/insns/c_ld.h @@ -1,5 +1,5 @@ require_extension(EXT_ZCA); -require((xlen == 64) || p->extension_enabled(EXT_ZCMLSD)); +require((xlen == 64) || p->extension_enabled(EXT_ZCLSD)); if (xlen == 32) { WRITE_RVC_RS2S_PAIR(MMU.load<int64_t>(RVC_RS1S + insn.rvc_ld_imm())); diff --git a/riscv/insns/c_ldsp.h b/riscv/insns/c_ldsp.h index d8c8ec8..9f44fec 100644 --- a/riscv/insns/c_ldsp.h +++ b/riscv/insns/c_ldsp.h @@ -1,5 +1,5 @@ require_extension(EXT_ZCA); -require((xlen == 64) || p->extension_enabled(EXT_ZCMLSD)); +require((xlen == 64) || p->extension_enabled(EXT_ZCLSD)); require(insn.rvc_rd() != 0); if (xlen == 32) { diff --git a/riscv/insns/c_sd.h b/riscv/insns/c_sd.h index dba9b07..860e2de 100644 --- a/riscv/insns/c_sd.h +++ b/riscv/insns/c_sd.h @@ -1,5 +1,5 @@ require_extension(EXT_ZCA); -require((xlen == 64) || p->extension_enabled(EXT_ZCMLSD)); +require((xlen == 64) || p->extension_enabled(EXT_ZCLSD)); if (xlen == 32) { MMU.store<uint64_t>(RVC_RS1S + insn.rvc_ld_imm(), RVC_RS2S_PAIR); diff --git a/riscv/insns/c_sdsp.h b/riscv/insns/c_sdsp.h index e95aefa..e6b1f62 100644 --- a/riscv/insns/c_sdsp.h +++ b/riscv/insns/c_sdsp.h @@ -1,5 +1,5 @@ require_extension(EXT_ZCA); -require((xlen == 64) || p->extension_enabled(EXT_ZCMLSD)); +require((xlen == 64) || p->extension_enabled(EXT_ZCLSD)); if (xlen == 32) { MMU.store<uint64_t>(RVC_SP + insn.rvc_sdsp_imm(), RVC_RS2_PAIR); diff --git a/riscv/insns/dret.h b/riscv/insns/dret.h index 60aaf21..0f94f88 100644 --- a/riscv/insns/dret.h +++ b/riscv/insns/dret.h @@ -4,8 +4,16 @@ if (ZICFILP_xLPE(STATE.dcsr->v, STATE.dcsr->prv)) { STATE.elp = STATE.dcsr->pelp; } p->set_privilege(STATE.dcsr->prv, STATE.dcsr->v); -if (STATE.prv < PRV_M) +if (STATE.prv < PRV_M) { STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MPRV); + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MDT); +} + +if (STATE.dcsr->prv == PRV_U || STATE.dcsr->v) + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); + +if (STATE.dcsr->v && STATE.dcsr->prv == PRV_U) + STATE.vsstatus->write(STATE.vsstatus->read() & ~SSTATUS_SDT); if (STATE.dcsr->prv == PRV_U || STATE.dcsr->v) STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); diff --git a/riscv/insns/jal.h b/riscv/insns/jal.h index cd59964..e7a75c7 100644 --- a/riscv/insns/jal.h +++ b/riscv/insns/jal.h @@ -1,3 +1,4 @@ +CHECK_RD(); reg_t tmp = npc; set_pc(JUMP_TARGET); WRITE_RD(tmp); diff --git a/riscv/insns/jalr.h b/riscv/insns/jalr.h index 0606f67..de84e89 100644 --- a/riscv/insns/jalr.h +++ b/riscv/insns/jalr.h @@ -1,3 +1,4 @@ +CHECK_RD(); reg_t tmp = npc; set_pc((RS1 + insn.i_imm()) & ~reg_t(1)); WRITE_RD(tmp); diff --git a/riscv/insns/mret.h b/riscv/insns/mret.h index 140ebde..479bfca 100644 --- a/riscv/insns/mret.h +++ b/riscv/insns/mret.h @@ -13,11 +13,12 @@ if (ZICFILP_xLPE(prev_virt, prev_prv)) { STATE.elp = static_cast<elp_t>(get_field(s, MSTATUS_MPELP)); } s = set_field(s, MSTATUS_MPELP, elp_t::NO_LP_EXPECTED); -if (prev_prv == PRV_U || prev_virt) +s = set_field(s, MSTATUS_MDT, 0); +if (prev_prv == PRV_U || (prev_virt && prev_prv != PRV_M)) s = set_field(s, MSTATUS_SDT, 0); if (prev_virt && prev_prv == PRV_U) STATE.vsstatus->write(STATE.vsstatus->read() & ~SSTATUS_SDT); STATE.mstatus->write(s); if (STATE.mstatush) STATE.mstatush->write(s >> 32); // log mstatush change -STATE.tcontrol->write((STATE.tcontrol->read() & CSR_TCONTROL_MPTE) ? (CSR_TCONTROL_MPTE | CSR_TCONTROL_MTE) : 0); +if (STATE.tcontrol) STATE.tcontrol->write((STATE.tcontrol->read() & CSR_TCONTROL_MPTE) ? (CSR_TCONTROL_MPTE | CSR_TCONTROL_MTE) : 0); p->set_privilege(prev_prv, prev_virt); diff --git a/riscv/insns/sret.h b/riscv/insns/sret.h index fe007d3..efb4fa6 100644 --- a/riscv/insns/sret.h +++ b/riscv/insns/sret.h @@ -26,6 +26,15 @@ if (!STATE.v) { if (ZICFILP_xLPE(prev_virt, prev_prv)) { STATE.elp = static_cast<elp_t>(get_field(s, SSTATUS_SPELP)); } + +if (STATE.prv == PRV_M) { + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MDT); + if (prev_prv == PRV_U || prev_virt) + STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_SDT); + if (prev_virt && prev_prv == PRV_U) + STATE.vsstatus->write(STATE.vsstatus->read() & ~SSTATUS_SDT); +} + s = set_field(s, SSTATUS_SPELP, elp_t::NO_LP_EXPECTED); if (STATE.prv == PRV_S) { diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index 45f637c..90e1ec1 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -23,7 +23,7 @@ typedef enum { EXT_ZCB, EXT_ZCD, EXT_ZCF, - EXT_ZCMLSD, + EXT_ZCLSD, EXT_ZCMP, EXT_ZCMT, EXT_ZKND, @@ -82,6 +82,7 @@ typedef enum { EXT_ZICFILP, EXT_ZICFISS, EXT_SSDBLTRP, + EXT_SMDBLTRP, EXT_SMMPM, EXT_SMNPM, EXT_SSNPM, diff --git a/riscv/log_file.h b/riscv/log_file.h index d039859..9e210bb 100644 --- a/riscv/log_file.h +++ b/riscv/log_file.h @@ -31,7 +31,7 @@ public: FILE *get() { return wrapped_file ? wrapped_file.get() : stderr; } private: - std::unique_ptr<FILE, decltype(&fclose)> wrapped_file; + std::unique_ptr<FILE, int(*)(FILE*)> wrapped_file; }; #endif diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 94997a2..8b99c1d 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -404,7 +404,7 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty int maxgpabits = vm.levels * vm.idxbits + vm.widenbits + PGSHIFT; reg_t maxgpa = (1ULL << maxgpabits) - 1; - bool mxr = proc->state.sstatus->readvirt(false) & MSTATUS_MXR; + bool mxr = !is_for_vs_pt_addr && (proc->state.sstatus->readvirt(false) & MSTATUS_MXR); // tinst is set to 0x3000/0x3020 - for RV64 read/write respectively for // VS-stage address translation (for spike HSXLEN == VSXLEN always) else // tinst is set to 0x2000/0x2020 - for RV32 read/write respectively for @@ -553,8 +553,8 @@ reg_t mmu_t::walk(mem_access_info_t access_info) // not shadow stack access xwr=110 or xwr=010 page cause page fault // shadow stack access with PTE_X moved to following check break; - } else if (ss_page && (type == STORE && !ss_access)) { - // not shadow stack store and xwr = 010 cause access-fault + } else if (ss_page && ((type == STORE && !ss_access) || access_info.flags.clean_inval)) { + // non-shadow-stack store or CBO with xwr = 010 causes access-fault throw trap_store_access_fault(virt, addr, 0, 0); } else if (ss_page && type == FETCH) { // fetch from shadow stack pages cause instruction access-fault @@ -614,7 +614,7 @@ void mmu_t::register_memtracer(memtracer_t* t) } reg_t mmu_t::get_pmlen(bool effective_virt, reg_t effective_priv, xlate_flags_t flags) const { - if (!proc || proc->get_xlen() != 64 || (in_mprv() && (proc->state.sstatus->read() & MSTATUS_MXR)) || flags.hlvx) + if (!proc || proc->get_xlen() != 64 || ((proc->state.sstatus->readvirt(false) | proc->state.sstatus->readvirt(effective_virt)) & MSTATUS_MXR) || flags.hlvx) return 0; reg_t pmm = 0; @@ -643,6 +643,7 @@ mem_access_info_t mmu_t::generate_access_info(reg_t addr, access_type type, xlat return {addr, addr, 0, false, {}, type}; bool virt = proc->state.v; reg_t mode = proc->state.prv; + reg_t transformed_addr = addr; if (type != FETCH) { if (in_mprv()) { mode = get_field(proc->state.mstatus->read(), MSTATUS_MPP); @@ -653,10 +654,11 @@ mem_access_info_t mmu_t::generate_access_info(reg_t addr, access_type type, xlat virt = true; mode = get_field(proc->state.hstatus->read(), HSTATUS_SPVP); } + auto xlen = proc->get_const_xlen(); + reg_t pmlen = get_pmlen(virt, mode, xlate_flags); + reg_t satp = proc->state.satp->readvirt(virt); + bool is_physical_addr = mode == PRV_M || get_field(satp, SATP64_MODE) == SATP_MODE_OFF; + transformed_addr = is_physical_addr ? zext(addr, xlen - pmlen) : sext(addr, xlen - pmlen); } - reg_t pmlen = get_pmlen(virt, mode, xlate_flags); - reg_t satp = proc->state.satp->readvirt(virt); - bool is_physical_addr = mode == PRV_M || get_field(satp, SATP64_MODE) == SATP_MODE_OFF; - reg_t transformed_addr = is_physical_addr ? zext(addr, 64 - pmlen) : sext(addr, 64 - pmlen); return {addr, transformed_addr, mode, virt, xlate_flags, type}; } diff --git a/riscv/mmu.h b/riscv/mmu.h index 0aa1f96..224b8f5 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -19,7 +19,7 @@ #define PGSHIFT 12 const reg_t PGSIZE = 1 << PGSHIFT; const reg_t PGMASK = ~(PGSIZE-1); -#define MAX_PADDR_BITS 56 // imposed by Sv39 / Sv48 +#define MAX_PADDR_BITS 64 struct insn_fetch_t { @@ -43,9 +43,10 @@ struct xlate_flags_t { const bool hlvx : 1 {false}; const bool lr : 1 {false}; const bool ss_access : 1 {false}; + const bool clean_inval : 1 {false}; bool is_special_access() const { - return forced_virt || hlvx || lr || ss_access; + return forced_virt || hlvx || lr || ss_access || clean_inval; } }; @@ -232,14 +233,14 @@ public: } void clean_inval(reg_t addr, bool clean, bool inval) { - auto access_info = generate_access_info(addr, LOAD, {}); + auto access_info = generate_access_info(addr, LOAD, {.clean_inval = true}); reg_t transformed_addr = access_info.transformed_vaddr; auto base = transformed_addr & ~(blocksz - 1); for (size_t offset = 0; offset < blocksz; offset += 1) check_triggers(triggers::OPERATION_STORE, base + offset, false, transformed_addr, std::nullopt); convert_load_traps_to_store_traps({ - const reg_t paddr = translate(generate_access_info(transformed_addr, LOAD, {}), 1); + const reg_t paddr = translate(access_info, 1); if (sim->reservable(paddr)) { if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD)) tracer.clean_invalidate(paddr, blocksz, clean, inval); diff --git a/riscv/platform.h b/riscv/platform.h index 7fffdc8..c8a5bf4 100644 --- a/riscv/platform.h +++ b/riscv/platform.h @@ -4,6 +4,8 @@ #define DEFAULT_KERNEL_BOOTARGS "console=ttyS0 earlycon" #define DEFAULT_RSTVEC 0x00001000 +#define DEFAULT_ISA "rv64imafdc_zicntr_zihpm" +#define DEFAULT_PRIV "MSU" #define CLINT_BASE 0x02000000 #define CLINT_SIZE 0x000c0000 #define PLIC_BASE 0x0c000000 diff --git a/riscv/plic.cc b/riscv/plic.cc index 78eb1d2..14de6df 100644 --- a/riscv/plic.cc +++ b/riscv/plic.cc @@ -55,7 +55,7 @@ #define PENDING_BASE 0x1000 /* - * Each hart context has a vector of interupt enable bits associated with it. + * Each hart context has a vector of interrupt enable bits associated with it. * There's one bit for each interrupt source. */ #define ENABLE_BASE 0x2000 diff --git a/riscv/processor.cc b/riscv/processor.cc index d480f8f..ecdf392 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -159,6 +159,8 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) elp = elp_t::NO_LP_EXPECTED; + critical_error = false; + csr_init(proc, max_isa); } @@ -184,8 +186,6 @@ void processor_t::reset() { xlen = isa.get_max_xlen(); state.reset(this, isa.get_max_isa()); - state.dcsr->halt = halt_on_reset; - halt_on_reset = false; if (any_vector_extensions()) VU.reset(); in_wfi = false; @@ -304,8 +304,8 @@ void processor_t::take_interrupt(reg_t pending_interrupts) const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); if (!state.debug_mode && nmie && enabled_interrupts) { // nonstandard interrupts have highest priority - if (enabled_interrupts >> (IRQ_M_EXT + 1)) - enabled_interrupts = enabled_interrupts >> (IRQ_M_EXT + 1) << (IRQ_M_EXT + 1); + if (enabled_interrupts >> (IRQ_LCOF + 1)) + enabled_interrupts = enabled_interrupts >> (IRQ_LCOF + 1) << (IRQ_LCOF + 1); // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI else if (enabled_interrupts & MIP_MEIP) enabled_interrupts = MIP_MEIP; @@ -379,11 +379,11 @@ const char* processor_t::get_privilege_string() abort(); } -void processor_t::enter_debug_mode(uint8_t cause) +void processor_t::enter_debug_mode(uint8_t cause, uint8_t extcause) { const bool has_zicfilp = extension_enabled(EXT_ZICFILP); state.debug_mode = true; - state.dcsr->update_fields(cause, state.prv, state.v, state.elp); + state.dcsr->update_fields(cause, extcause, state.prv, state.v, state.elp); state.elp = elp_t::NO_LP_EXPECTED; set_privilege(PRV_M, false); state.dpc->write(state.pc); @@ -444,7 +444,7 @@ void processor_t::take_trap(trap_t& t, reg_t epc) // An unexpected trap - a trap when SDT is 1 - traps to M-mode if ((state.prv <= PRV_S && bit < max_xlen) && (((vsdeleg >> bit) & 1) || ((hsdeleg >> bit) & 1))) { - reg_t s = curr_virt ? state.nonvirtual_sstatus->read() : state.sstatus->read(); + reg_t s = state.sstatus->read(); supv_double_trap = get_field(s, MSTATUS_SDT); if (supv_double_trap) vsdeleg = hsdeleg = 0; @@ -500,10 +500,23 @@ void processor_t::take_trap(trap_t& t, reg_t epc) // Handle the trap in M-mode const reg_t vector = (state.mtvec->read() & 1) && interrupt ? 4 * bit : 0; const reg_t trap_handler_address = (state.mtvec->read() & ~(reg_t)1) + vector; - // RNMI exception vector is implementation-defined. Since we don't model // RNMI sources, the feature isn't very useful, so pick an invalid address. + // RNMI exception vector is implementation-defined. Since we don't model const reg_t rnmi_trap_handler_address = 0; const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE)); + + reg_t s = state.mstatus->read(); + if ( extension_enabled(EXT_SMDBLTRP)) { + if (get_field(s, MSTATUS_MDT) || !nmie) { + // Critical error - Double trap in M-mode or trap when nmie is 0 + // RNMI is not modeled else double trap in M-mode would trap to + // RNMI handler instead of leading to a critical error + state.critical_error = 1; + return; + } + s = set_field(s, MSTATUS_MDT, 1); + } + state.pc = !nmie ? rnmi_trap_handler_address : trap_handler_address; state.mepc->write(epc); state.mcause->write(supv_double_trap ? CAUSE_DOUBLE_TRAP : t.cause()); @@ -511,7 +524,6 @@ void processor_t::take_trap(trap_t& t, reg_t epc) state.mtval2->write(supv_double_trap ? t.cause() : t.get_tval2()); state.mtinst->write(t.get_tinst()); - reg_t s = state.mstatus->read(); s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_MIE)); s = set_field(s, MSTATUS_MPP, state.prv); s = set_field(s, MSTATUS_MIE, 0); @@ -521,7 +533,7 @@ void processor_t::take_trap(trap_t& t, reg_t epc) state.elp = elp_t::NO_LP_EXPECTED; state.mstatus->write(s); if (state.mstatush) state.mstatush->write(s >> 32); // log mstatush change - state.tcontrol->write((state.tcontrol->read() & CSR_TCONTROL_MTE) ? CSR_TCONTROL_MPTE : 0); + if (state.tcontrol) state.tcontrol->write((state.tcontrol->read() & CSR_TCONTROL_MTE) ? CSR_TCONTROL_MPTE : 0); set_privilege(PRV_M, false); } } @@ -537,7 +549,7 @@ void processor_t::take_trigger_action(triggers::action_t action, reg_t breakpoin switch (action) { case triggers::ACTION_DEBUG_MODE: - enter_debug_mode(DCSR_CAUSE_HWBP); + enter_debug_mode(DCSR_CAUSE_HWBP, 0); break; case triggers::ACTION_DEBUG_EXCEPTION: { trap_breakpoint trap(virt, breakpoint_tval); @@ -699,6 +711,8 @@ void processor_t::register_extension(extension_t *x) { fprintf(stderr, "extensions must have unique names (got two named \"%s\"!)\n", x->name()); abort(); } + for (auto &csr: x->get_csrs(*this)) + state.add_csr(csr->address, csr); x->set_processor(this); } diff --git a/riscv/processor.h b/riscv/processor.h index 26d0878..4f22cbd 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -62,7 +62,7 @@ struct insn_desc_t }; // regnum, data -typedef std::unordered_map<reg_t, freg_t> commit_log_reg_t; +typedef std::map<reg_t, freg_t> commit_log_reg_t; // addr, value, size typedef std::vector<std::tuple<reg_t, uint64_t, uint8_t>> commit_log_mem_t; @@ -99,6 +99,7 @@ struct state_t csr_t_p medeleg; csr_t_p mideleg; csr_t_p mcounteren; + csr_t_p mcountinhibit; csr_t_p mevent[N_HPMCOUNTERS]; csr_t_p mnstatus; csr_t_p mnepc; @@ -190,6 +191,8 @@ struct state_t elp_t elp; + bool critical_error; + private: void csr_init(processor_t* const proc, reg_t max_isa); }; @@ -405,7 +408,7 @@ private: void register_insn(insn_desc_t, bool); int paddr_bits(); - void enter_debug_mode(uint8_t cause); + void enter_debug_mode(uint8_t cause, uint8_t ext_cause); void debug_output_log(std::stringstream *s); // either output to interactive user or write to log file diff --git a/riscv/riscv.ac b/riscv/riscv.ac index 378fd10..cd7cfe2 100644 --- a/riscv/riscv.ac +++ b/riscv/riscv.ac @@ -8,18 +8,6 @@ AC_CHECK_LIB([boost_system], [main], [], []) AC_CHECK_LIB([boost_regex], [main], [], []) -AC_ARG_WITH(isa, - [AS_HELP_STRING([--with-isa=RV64IMAFDC_zicntr_zihpm], - [Sets the default RISC-V ISA])], - AC_DEFINE_UNQUOTED([DEFAULT_ISA], "$withval", [Default value for --isa switch]), - AC_DEFINE_UNQUOTED([DEFAULT_ISA], "RV64IMAFDC_zicntr_zihpm", [Default value for --isa switch])) - -AC_ARG_WITH(priv, - [AS_HELP_STRING([--with-priv=MSU], - [Sets the default RISC-V privilege modes supported])], - AC_DEFINE_UNQUOTED([DEFAULT_PRIV], "$withval", [Default value for --priv switch]), - AC_DEFINE_UNQUOTED([DEFAULT_PRIV], "MSU", [Default value for --priv switch])) - AC_ARG_WITH(target, [AS_HELP_STRING([--with-target=riscv64-unknown-elf], [Sets the default target config])], diff --git a/riscv/sim.cc b/riscv/sim.cc index e9928f5..d47ceae 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -103,8 +103,8 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, cfg, this, cfg->hartids[i], halted, log_file.get(), sout_)); harts[cfg->hartids[i]] = procs[i]; - return; } + return; } // otherwise, generate the procs by parsing the DTS // Only make a CLINT (Core-Local INTerrupt controller) and PLIC (Platform- @@ -133,7 +133,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, std::stringstream strstream; strstream << fin.rdbuf(); dtb = strstream.str(); - dts = dtc_compile(dtb, "dtb", "dts"); + dts = dtb_to_dts(dtb); } else { std::pair<reg_t, reg_t> initrd_bounds = cfg->initrd_bounds; std::string device_nodes; @@ -143,7 +143,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, device_nodes.append(factory->generate_dts(this, sargs)); } dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, cfg, mems, device_nodes); - dtb = dtc_compile(dts, "dts", "dtb"); + dtb = dts_to_dtb(dts); } int fdt_code = fdt_check_header(dtb.c_str()); @@ -195,7 +195,7 @@ sim_t::sim_t(const cfg_t *cfg, bool halted, exit(1); } - procs.push_back(new processor_t(isa_str, DEFAULT_PRIV, + procs.push_back(new processor_t(isa_str, cfg->priv, cfg, this, hartid, halted, log_file.get(), sout_)); harts[hartid] = procs[cpu_idx]; @@ -336,7 +336,8 @@ void sim_t::set_procs_debug(bool value) static bool paddr_ok(reg_t addr) { - return (addr >> MAX_PADDR_BITS) == 0; + static_assert(MAX_PADDR_BITS == 8 * sizeof(addr)); + return true; } bool sim_t::mmio_load(reg_t paddr, size_t len, uint8_t* bytes) diff --git a/riscv/triggers.cc b/riscv/triggers.cc index de3da40..e130a87 100644 --- a/riscv/triggers.cc +++ b/riscv/triggers.cc @@ -55,12 +55,54 @@ void trigger_t::tdata3_write(processor_t * const proc, const reg_t val) noexcept sselect = (sselect_t)((proc->extension_enabled_const('S') && get_field(val, CSR_TEXTRA_SSELECT(xlen)) <= SSELECT_MAXVAL) ? get_field(val, CSR_TEXTRA_SSELECT(xlen)) : SSELECT_IGNORE); } +static reg_t tcontrol_value(const state_t * state) { + if (state->tcontrol) + return state->tcontrol->read(); + else + return 0; +} + bool trigger_t::common_match(processor_t * const proc, bool use_prev_prv) const noexcept { auto state = proc->get_state(); auto prv = use_prev_prv ? state->prev_prv : state->prv; auto v = use_prev_prv ? state->prev_v : state->v; - auto m_enabled = get_action() != 0 || (state->tcontrol->read() & CSR_TCONTROL_MTE); - return (prv < PRV_M || m_enabled) && mode_match(prv, v) && textra_match(proc); + + if (!mode_match(prv, v)) + return false; + + if (!textra_match(proc)) + return false; + + if (get_action() == ACTION_DEBUG_EXCEPTION) { + if (proc->extension_enabled('S')) { + // The hardware prevents triggers with action=0 from matching or firing + // while in M-mode and while MIE in mstatus is 0. If medeleg [3]=1 then it + // prevents triggers with action=0 from matching or firing while in S-mode + // and while SIE in sstatus is 0. If medeleg [3]=1 and hedeleg [3]=1 then + // it prevents triggers with action=0 from matching or firing while in + // VS-mode and while SIE in vstatus is 0. + + const bool mstatus_mie = state->mstatus->read() & MSTATUS_MIE; + if (prv == PRV_M && !mstatus_mie) + return false; + + const bool sstatus_sie = state->sstatus->read() & MSTATUS_SIE; + const bool medeleg_breakpoint = (state->medeleg->read() >> CAUSE_BREAKPOINT) & 1; + if (prv == PRV_S && !v && medeleg_breakpoint && !sstatus_sie) + return false; + + const bool vsstatus_sie = state->vsstatus->read() & MSTATUS_SIE; + const bool hedeleg_breakpoint = (state->hedeleg->read() >> CAUSE_BREAKPOINT) & 1; + if (prv == PRV_S && v && medeleg_breakpoint && hedeleg_breakpoint && !vsstatus_sie) + return false; + } else { + // mte and mpte in tcontrol is implemented. medeleg [3] is hard-wired to 0. + if (prv == PRV_M && !(tcontrol_value(state) & CSR_TCONTROL_MTE)) + return false; + } + } + + return true; } bool trigger_t::mode_match(reg_t prv, bool v) const noexcept @@ -110,21 +152,6 @@ bool trigger_t::textra_match(processor_t * const proc) const noexcept return true; } -bool trigger_t::allow_action(const state_t * const state) const -{ - if (get_action() == ACTION_DEBUG_EXCEPTION) { - const bool mstatus_mie = state->mstatus->read() & MSTATUS_MIE; - const bool sstatus_sie = state->sstatus->read() & MSTATUS_SIE; - const bool vsstatus_sie = state->vsstatus->read() & MSTATUS_SIE; - const bool medeleg_breakpoint = (state->medeleg->read() >> CAUSE_BREAKPOINT) & 1; - const bool hedeleg_breakpoint = (state->hedeleg->read() >> CAUSE_BREAKPOINT) & 1; - return (state->prv != PRV_M || mstatus_mie) && - (state->prv != PRV_S || state->v || !medeleg_breakpoint || sstatus_sie) && - (state->prv != PRV_S || !state->v || !medeleg_breakpoint || !hedeleg_breakpoint || vsstatus_sie); - } - return true; -} - reg_t disabled_trigger_t::tdata1_read(const processor_t * const proc) const noexcept { auto xlen = proc->get_xlen(); @@ -146,7 +173,7 @@ reg_t mcontrol_t::tdata1_read(const processor_t * const proc) const noexcept { auto xlen = proc->get_xlen(); v = set_field(v, MCONTROL_TYPE(xlen), CSR_TDATA1_TYPE_MCONTROL); v = set_field(v, CSR_MCONTROL_DMODE(xlen), dmode); - v = set_field(v, MCONTROL_MASKMAX(xlen), 0); + v = set_field(v, MCONTROL_MASKMAX(xlen), maskmax); v = set_field(v, CSR_MCONTROL_HIT, hit); v = set_field(v, MCONTROL_SELECT, select); v = set_field(v, MCONTROL_TIMING, timing); @@ -171,7 +198,7 @@ void mcontrol_t::tdata1_write(processor_t * const proc, const reg_t val, const b timing = legalize_timing(val, MCONTROL_TIMING, MCONTROL_SELECT, MCONTROL_EXECUTE, MCONTROL_LOAD); action = legalize_action(val, MCONTROL_ACTION, CSR_MCONTROL_DMODE(xlen)); chain = allow_chain ? get_field(val, MCONTROL_CHAIN) : 0; - match = legalize_match(get_field(val, MCONTROL_MATCH)); + match = legalize_match(get_field(val, MCONTROL_MATCH), maskmax); m = get_field(val, MCONTROL_M); s = proc->extension_enabled_const('S') ? get_field(val, CSR_MCONTROL_S) : 0; u = proc->extension_enabled_const('U') ? get_field(val, CSR_MCONTROL_U) : 0; @@ -195,13 +222,17 @@ bool mcontrol_common_t::simple_match(unsigned xlen, reg_t value) const { return value < tdata2; case MATCH_MASK_LOW: { - reg_t mask = tdata2 >> (xlen/2); - return (value & mask) == (tdata2 & mask); + reg_t tdata2_high = tdata2 >> (xlen/2); + reg_t tdata2_low = tdata2 & ((reg_t(1) << (xlen/2)) - 1); + reg_t value_low = value & ((reg_t(1) << (xlen/2)) - 1); + return (value_low & tdata2_high) == tdata2_low; } case MATCH_MASK_HIGH: { - reg_t mask = tdata2 >> (xlen/2); - return ((value >> (xlen/2)) & mask) == (tdata2 & mask); + reg_t tdata2_high = tdata2 >> (xlen/2); + reg_t tdata2_low = tdata2 & ((reg_t(1) << (xlen/2)) - 1); + reg_t value_high = value >> (xlen/2); + return (value_high & tdata2_high) == tdata2_low; } } assert(0); @@ -231,7 +262,7 @@ std::optional<match_result_t> mcontrol_common_t::detect_memory_access_match(proc value &= 0xffffffff; } - if (simple_match(xlen, value) && allow_action(proc->get_state())) { + if (simple_match(xlen, value)) { /* This is OK because this function is only called if the trigger was not * inhibited by the previous trigger in the chain. */ set_hit(timing ? HIT_IMMEDIATELY_AFTER : HIT_BEFORE); @@ -240,11 +271,11 @@ std::optional<match_result_t> mcontrol_common_t::detect_memory_access_match(proc return std::nullopt; } -mcontrol_common_t::match_t mcontrol_common_t::legalize_match(reg_t val) noexcept +mcontrol_common_t::match_t mcontrol_common_t::legalize_match(reg_t val, reg_t maskmax) noexcept { switch (val) { + case MATCH_NAPOT: if (maskmax == 0) return MATCH_EQUAL; case MATCH_EQUAL: - case MATCH_NAPOT: case MATCH_GE: case MATCH_LT: case MATCH_MASK_LOW: @@ -261,7 +292,14 @@ bool mcontrol_common_t::legalize_timing(reg_t val, reg_t timing_mask, reg_t sele return TIMING_AFTER; if (get_field(val, execute_mask)) return TIMING_BEFORE; - return get_field(val, timing_mask); + if (timing_mask) { + // Use the requested timing. + return get_field(val, timing_mask); + } else { + // For mcontrol6 you can't request a timing. Default to before since that's + // most useful to the user. + return TIMING_BEFORE; + } } reg_t mcontrol6_t::tdata1_read(const processor_t * const proc) const noexcept { @@ -290,13 +328,14 @@ void mcontrol6_t::tdata1_write(processor_t * const proc, const reg_t val, const auto xlen = proc->get_const_xlen(); assert(get_field(val, CSR_MCONTROL6_TYPE(xlen)) == CSR_TDATA1_TYPE_MCONTROL6); dmode = get_field(val, CSR_MCONTROL6_DMODE(xlen)); + const reg_t maskmax6 = xlen - 1; vs = get_field(val, CSR_MCONTROL6_VS); vu = get_field(val, CSR_MCONTROL6_VU); hit = hit_t(2 * get_field(val, CSR_MCONTROL6_HIT1) + get_field(val, CSR_MCONTROL6_HIT0)); // 2-bit field {hit1,hit0} select = get_field(val, CSR_MCONTROL6_SELECT); action = legalize_action(val, CSR_MCONTROL6_ACTION, CSR_MCONTROL6_DMODE(xlen)); chain = allow_chain ? get_field(val, CSR_MCONTROL6_CHAIN) : 0; - match = legalize_match(get_field(val, CSR_MCONTROL6_MATCH)); + match = legalize_match(get_field(val, CSR_MCONTROL6_MATCH), maskmax6); m = get_field(val, CSR_MCONTROL6_M); s = proc->extension_enabled_const('S') ? get_field(val, CSR_MCONTROL6_S) : 0; u = proc->extension_enabled_const('U') ? get_field(val, CSR_MCONTROL6_U) : 0; @@ -312,7 +351,7 @@ void mcontrol6_t::tdata1_write(processor_t * const proc, const reg_t val, const std::optional<match_result_t> icount_t::detect_icount_fire(processor_t * const proc) noexcept { - if (!common_match(proc) || !allow_action(proc->get_state())) + if (!common_match(proc)) return std::nullopt; std::optional<match_result_t> ret = std::nullopt; @@ -327,7 +366,7 @@ std::optional<match_result_t> icount_t::detect_icount_fire(processor_t * const p void icount_t::detect_icount_decrement(processor_t * const proc) noexcept { - if (!common_match(proc) || !allow_action(proc->get_state())) + if (!common_match(proc)) return; if (count >= 1) { @@ -419,7 +458,7 @@ std::optional<match_result_t> trap_common_t::detect_trap_match(processor_t * con bool interrupt = (t.cause() & ((reg_t)1 << (xlen - 1))) != 0; reg_t bit = t.cause() & ~((reg_t)1 << (xlen - 1)); assert(bit < xlen); - if (simple_match(interrupt, bit) && allow_action(proc->get_state())) { + if (simple_match(interrupt, bit)) { hit = true; return match_result_t(TIMING_AFTER, action); } diff --git a/riscv/triggers.h b/riscv/triggers.h index 24f9206..3f1e86f 100644 --- a/riscv/triggers.h +++ b/riscv/triggers.h @@ -99,7 +99,6 @@ public: protected: static action_t legalize_action(reg_t val, reg_t action_mask, reg_t dmode_mask) noexcept; bool common_match(processor_t * const proc, bool use_prev_prv = false) const noexcept; - bool allow_action(const state_t * const state) const; reg_t tdata2; bool vs = false; @@ -221,7 +220,7 @@ private: bool simple_match(unsigned xlen, reg_t value) const; protected: - static match_t legalize_match(reg_t val) noexcept; + static match_t legalize_match(reg_t val, reg_t maskmax) noexcept; static bool legalize_timing(reg_t val, reg_t timing_mask, reg_t select_mask, reg_t execute_mask, reg_t load_mask) noexcept; bool dmode = false; action_t action = ACTION_DEBUG_EXCEPTION; @@ -243,6 +242,7 @@ public: private: bool hit = false; + const reg_t maskmax = 0; }; class mcontrol6_t : public mcontrol_common_t { diff --git a/riscv/v_ext_macros.h b/riscv/v_ext_macros.h index b6365aa..3e7dc45 100644 --- a/riscv/v_ext_macros.h +++ b/riscv/v_ext_macros.h @@ -491,7 +491,7 @@ static inline bool is_overlapped_widen(const int astart, int asize, BODY; \ } -// comparision result to masking register +// comparison result to masking register #define VI_LOOP_CMP_BODY(PARAMS, BODY) \ VI_LOOP_CMP_BASE \ INSNS_BASE(PARAMS, BODY) \ diff --git a/softfloat/f64_to_bf16.c b/softfloat/f64_to_bf16.c index a320979..73ecf0e 100644 --- a/softfloat/f64_to_bf16.c +++ b/softfloat/f64_to_bf16.c @@ -43,12 +43,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. bfloat16_t f64_to_bf16( float64_t a ) { - uint_fast8_t roundingMode = softfloat_roundingMode; - softfloat_roundingMode = softfloat_round_odd; + union ui64_f64 uA; + uint_fast64_t uiA; + bool sign; + int_fast16_t exp; + uint_fast64_t frac; + struct commonNaN commonNaN; + uint_fast16_t uiZ, frac16; + union ui16_f16 uZ; - float32_t f32A = f64_to_f32( a ); - - softfloat_roundingMode = roundingMode; - - return f32_to_bf16( f32A ); + /*------------------------------------------------------------------------ + *------------------------------------------------------------------------*/ + uA.f = a; + uiA = uA.ui; + sign = signF64UI( uiA ); + exp = expF64UI( uiA ); + frac = fracF64UI( uiA ); + /*------------------------------------------------------------------------ + *------------------------------------------------------------------------*/ + if ( exp == 0x7FF ) { + if ( frac ) { + softfloat_f64UIToCommonNaN( uiA, &commonNaN ); + uiZ = softfloat_commonNaNToBF16UI( &commonNaN ); + } else { + uiZ = packToBF16UI( sign, 0xFF, 0 ); + } + goto uiZ; + } + /*------------------------------------------------------------------------ + *------------------------------------------------------------------------*/ + frac16 = softfloat_shortShiftRightJam64( frac, 38 ); + if ( ! (exp | frac16) ) { + uiZ = packToBF16UI( sign, 0, 0 ); + goto uiZ; + } + /*------------------------------------------------------------------------ + *------------------------------------------------------------------------*/ + return softfloat_roundPackToBF16( sign, exp - 0x381, frac16 | 0x4000 ); + uiZ: + uZ.ui = uiZ; + return uZ.f; } diff --git a/spike_dasm/spike-dasm.cc b/spike_dasm/spike-dasm.cc index 3e42df5..547f044 100644 --- a/spike_dasm/spike-dasm.cc +++ b/spike_dasm/spike-dasm.cc @@ -6,9 +6,9 @@ // enclosed hexadecimal number, interpreted as a RISC-V // instruction. -#include "config.h" #include "disasm.h" #include "extension.h" +#include "platform.h" #include <iostream> #include <string> #include <cstdint> diff --git a/spike_main/spike-log-parser.cc b/spike_main/spike-log-parser.cc index 55ff999..2c9a543 100644 --- a/spike_main/spike-log-parser.cc +++ b/spike_main/spike-log-parser.cc @@ -5,7 +5,6 @@ // in its inputs, then output the RISC-V instruction with the disassembly // enclosed hexadecimal number. -#include "config.h" #include <iostream> #include <string> #include <cstdint> @@ -14,6 +13,7 @@ #include "disasm.h" #include "extension.h" +#include "platform.h" using namespace std; diff --git a/spike_main/spike.cc b/spike_main/spike.cc index 1a298f2..69ce256 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -38,7 +38,7 @@ static void help(int exit_code = 1) fprintf(stderr, " -s Command I/O via socket (use with -d)\n"); #endif fprintf(stderr, " -h, --help Print this help message\n"); - fprintf(stderr, " -H Start halted, allowing a debugger to connect\n"); + fprintf(stderr, " --halted Start halted, allowing a debugger to connect\n"); fprintf(stderr, " --log=<name> File name for option -l\n"); fprintf(stderr, " --debug-cmd=<name> Read commands from file (use with -d)\n"); fprintf(stderr, " --isa=<name> RISC-V ISA string [default %s]\n", DEFAULT_ISA); @@ -191,6 +191,31 @@ merge_overlapping_memory_regions(std::vector<mem_cfg_t> mems) return merged_mem; } +static mem_cfg_t create_mem_region(unsigned long long base, unsigned long long size) +{ + // page-align base and size + auto base0 = base, size0 = size; + size += base0 % PGSIZE; + base -= base0 % PGSIZE; + if (size % PGSIZE != 0) + size += PGSIZE - size % PGSIZE; + + if (size != size0) { + fprintf(stderr, "Warning: the memory at [0x%llX, 0x%llX] has been realigned\n" + "to the %ld KiB page size: [0x%llX, 0x%llX]\n", + base0, base0 + size0 - 1, long(PGSIZE / 1024), base, base + size - 1); + } + + if (!mem_cfg_t::check_if_supported(base, size)) { + fprintf(stderr, "Unsupported memory region " + "{base = 0x%llX, size = 0x%llX} specified\n", + base, size); + exit(EXIT_FAILURE); + } + + return mem_cfg_t(base, size); +} + static std::vector<mem_cfg_t> parse_mem_layout(const char* arg) { std::vector<mem_cfg_t> res; @@ -200,9 +225,9 @@ static std::vector<mem_cfg_t> parse_mem_layout(const char* arg) auto mb = strtoull(arg, &p, 0); if (*p == 0) { reg_t size = reg_t(mb) << 20; - if (size != (size_t)size) - throw std::runtime_error("Size would overflow size_t"); - res.push_back(mem_cfg_t(reg_t(DRAM_BASE), size)); + if ((size >> 20) != mb) + throw std::runtime_error("Memory size too large"); + res.push_back(create_mem_region(DRAM_BASE, size)); return res; } @@ -213,42 +238,7 @@ static std::vector<mem_cfg_t> parse_mem_layout(const char* arg) help(); auto size = strtoull(p + 1, &p, 0); - // page-align base and size - auto base0 = base, size0 = size; - size += base0 % PGSIZE; - base -= base0 % PGSIZE; - if (size % PGSIZE != 0) - size += PGSIZE - size % PGSIZE; - - if (size != size0) { - fprintf(stderr, "Warning: the memory at [0x%llX, 0x%llX] has been realigned\n" - "to the %ld KiB page size: [0x%llX, 0x%llX]\n", - base0, base0 + size0 - 1, long(PGSIZE / 1024), base, base + size - 1); - } - - if (!mem_cfg_t::check_if_supported(base, size)) { - fprintf(stderr, "Unsupported memory region " - "{base = 0x%llX, size = 0x%llX} specified\n", - base, size); - exit(EXIT_FAILURE); - } - - const unsigned long long max_allowed_pa = (1ull << MAX_PADDR_BITS) - 1ull; - assert(max_allowed_pa <= std::numeric_limits<reg_t>::max()); - mem_cfg_t mem_region(base, size); - if (mem_region.get_inclusive_end() > max_allowed_pa) { - int bits_required = 64 - clz(mem_region.get_inclusive_end()); - fprintf(stderr, "Unsupported memory region " - "{base = 0x%" PRIX64 ", size = 0x%" PRIX64 "} specified," - " which requires %d bits of physical address\n" - " The largest accessible physical address " - "is 0x%llX (defined by MAX_PADDR_BITS constant, which is %d)\n", - mem_region.get_base(), mem_region.get_size(), bits_required, - max_allowed_pa, MAX_PADDR_BITS); - exit(EXIT_FAILURE); - } - - res.push_back(mem_region); + res.push_back(create_mem_region(base, size)); if (!*p) break; @@ -385,8 +375,7 @@ int main(int argc, char** argv) #endif parser.option('p', 0, 1, [&](const char* s){nprocs = atoul_nonzero_safe(s);}); parser.option('m', 0, 1, [&](const char* s){cfg.mem_layout = parse_mem_layout(s);}); - // I wanted to use --halted, but for some reason that doesn't work. - parser.option('H', 0, 0, [&](const char UNUSED *s){halted = true;}); + parser.option(0, "halted", 0, [&](const char UNUSED *s){halted = true;}); parser.option(0, "rbb-port", 1, [&](const char* s){use_rbb = true; rbb_port = atoul_safe(s);}); parser.option(0, "pc", 1, [&](const char* s){cfg.start_pc = strtoull(s, 0, 0);}); parser.option(0, "hartids", 1, [&](const char* s){ |