From ebf046f587e2462bda6a442a2199f71319bac68f Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Sat, 27 Jan 2024 14:52:00 -0600 Subject: [PATCH] add Smrnmi extension --- c_emulator/riscv_platform.c | 20 +++++++++ c_emulator/riscv_platform.h | 5 +++ c_emulator/riscv_platform_impl.c | 4 ++ c_emulator/riscv_platform_impl.h | 4 ++ c_emulator/riscv_sim.c | 15 +++++++ model/riscv_csr_map.sail | 7 ++++ model/riscv_insts_base.sail | 19 +++++++++ model/riscv_insts_zicsr.sail | 12 ++++++ model/riscv_step.sail | 5 ++- model/riscv_sys_control.sail | 69 ++++++++++++++++++++++++++++--- model/riscv_sys_exceptions.sail | 16 ++++++- model/riscv_sys_regs.sail | 35 +++++++++++++++- model/riscv_types.sail | 6 ++- ocaml_emulator/platform.ml | 11 +++++ ocaml_emulator/platform_impl.ml | 7 ++++ ocaml_emulator/riscv_ocaml_sim.ml | 8 +++- 16 files changed, 232 insertions(+), 11 deletions(-) diff --git a/c_emulator/riscv_platform.c b/c_emulator/riscv_platform.c index 2fdb63f92..c1f383baa 100644 --- a/c_emulator/riscv_platform.c +++ b/c_emulator/riscv_platform.c @@ -16,6 +16,7 @@ static mach_bits reservation = 0; static bool reservation_valid = false; +static int is_rnmi_pending = 1; bool sys_enable_rvc(unit u) { @@ -176,3 +177,22 @@ unit memea(mach_bits len, sail_int n) { return UNIT; } + +// Smrnmi : resumable NMI pending +bool rnmi_pending(bool nmie) +{ + if (is_rnmi_pending && nmie) { + is_rnmi_pending = 0; + return true; + } else { + return false; + } +} +mach_bits rnmi_exc_trap_vector(unit u) +{ + return (mach_bits)smrnmi_exc_trap_vec; +} +mach_bits rnmi_int_trap_vector(unit u) +{ + return (mach_bits)smrnmi_int_trap_vec; +} diff --git a/c_emulator/riscv_platform.h b/c_emulator/riscv_platform.h index 341bd5964..b9176b0b8 100644 --- a/c_emulator/riscv_platform.h +++ b/c_emulator/riscv_platform.h @@ -42,3 +42,8 @@ unit plat_term_write(mach_bits); mach_bits plat_htif_tohost(unit); unit memea(mach_bits, sail_int); + +// Smrnmi : resumable NMI +bool rnmi_pending(bool); +mach_bits rnmi_int_trap_vector(unit u); +mach_bits rnmi_exc_trap_vector(unit u); diff --git a/c_emulator/riscv_platform_impl.c b/c_emulator/riscv_platform_impl.c index 077fc50dc..17be36f87 100644 --- a/c_emulator/riscv_platform_impl.c +++ b/c_emulator/riscv_platform_impl.c @@ -26,6 +26,10 @@ uint64_t rv_ram_size = UINT64_C(0x4000000); uint64_t rv_rom_base = UINT64_C(0x1000); uint64_t rv_rom_size = UINT64_C(0x100); +/* Smrnmi : Resumable NMI vectors */ +uint64_t smrnmi_int_trap_vec = UINT64_C(0); +uint64_t smrnmi_exc_trap_vec = UINT64_C(0); + // Provides entropy for the scalar cryptography extension. uint64_t rv_16_random_bits(void) { diff --git a/c_emulator/riscv_platform_impl.h b/c_emulator/riscv_platform_impl.h index c4289e679..e42590800 100644 --- a/c_emulator/riscv_platform_impl.h +++ b/c_emulator/riscv_platform_impl.h @@ -30,6 +30,10 @@ extern uint64_t rv_ram_size; extern uint64_t rv_rom_base; extern uint64_t rv_rom_size; +/* Smrnmi : Resumable NMI vectors */ +extern uint64_t smrnmi_int_trap_vec; +extern uint64_t smrnmi_exc_trap_vec; + // Provides entropy for the scalar cryptography extension. extern uint64_t rv_16_random_bits(void); diff --git a/c_emulator/riscv_sim.c b/c_emulator/riscv_sim.c index 3a9bfc08d..3a07960c5 100644 --- a/c_emulator/riscv_sim.c +++ b/c_emulator/riscv_sim.c @@ -56,6 +56,9 @@ const char *RV32ISA = "RV32IMAC"; #define OPT_ENABLE_SVINVAL 1004 #define OPT_ENABLE_ZCB 10014 +#define OPT_SMRNMI_INT_TRAP_VEC 1004 +#define OPT_SMRNMI_EXC_TRAP_VEC 1005 + static bool do_dump_dts = false; static bool do_show_times = false; struct tv_spike_t *s = NULL; @@ -152,6 +155,8 @@ static struct option options[] = { #ifdef SAILCOV {"sailcov-file", required_argument, 0, 'c' }, #endif + {"smrnmi_int_trap_vec", required_argument, 0, OPT_SMRNMI_INT_TRAP_VEC }, + {"smrnmi_exc_trap_vec", required_argument, 0, OPT_SMRNMI_EXC_TRAP_VEC }, {0, 0, 0, 0 } }; @@ -408,6 +413,16 @@ static int process_args(int argc, char **argv) trace_log_path = optarg; fprintf(stderr, "using %s for trace output.\n", trace_log_path); break; + case OPT_SMRNMI_INT_TRAP_VEC: + smrnmi_int_trap_vec = strtoull(optarg, NULL, 16); + fprintf(stderr, "using 0x%lx as SMRNMI Int. trap vector.\n", + smrnmi_int_trap_vec); + break; + case OPT_SMRNMI_EXC_TRAP_VEC: + smrnmi_exc_trap_vec = strtoull(optarg, NULL, 16); + fprintf(stderr, "using 0x%lx as SMRNMI Exc. trap vector.\n", + smrnmi_exc_trap_vec); + break; case '?': print_usage(argv[0], 1); break; diff --git a/model/riscv_csr_map.sail b/model/riscv_csr_map.sail index 22cffd5a6..b470898b0 100644 --- a/model/riscv_csr_map.sail +++ b/model/riscv_csr_map.sail @@ -163,6 +163,13 @@ mapping clause csr_name_map = 0xB02 <-> "minstret" mapping clause csr_name_map = 0xB80 <-> "mcycleh" mapping clause csr_name_map = 0xB82 <-> "minstreth" /* TODO: other hpm counters and events */ + +/* Smrnmi - resumable NMI CSRs */ +mapping clause csr_name_map = 0x740 <-> "mnscratch" +mapping clause csr_name_map = 0x741 <-> "mnepc" +mapping clause csr_name_map = 0x742 <-> "mncause" +mapping clause csr_name_map = 0x744 <-> "mnstatus" + /* trigger/debug */ mapping clause csr_name_map = 0x7a0 <-> "tselect" mapping clause csr_name_map = 0x7a1 <-> "tdata1" diff --git a/model/riscv_insts_base.sail b/model/riscv_insts_base.sail index 26915f22c..1b8e1662b 100644 --- a/model/riscv_insts_base.sail +++ b/model/riscv_insts_base.sail @@ -729,3 +729,22 @@ function clause execute SFENCE_VMA(rs1, rs2) = { mapping clause assembly = SFENCE_VMA(rs1, rs2) <-> "sfence.vma" ^ spc() ^ reg_name(rs1) ^ sep() ^ reg_name(rs2) + +/* ****************************************************************** */ +union clause ast = MNRET : unit + +mapping clause encdec = MNRET() if haveSmrnmi() + <-> 0b0111000 @ 0b00010 @ 0b00000 @ 0b000 @ 0b00000 @ 0b1110011 if haveSmrnmi() + +function clause execute MNRET() = { + if cur_privilege != Machine + then { handle_illegal(); RETIRE_FAIL } + else if not(ext_check_mnret_priv ()) + then { ext_fail_mnret_priv(); RETIRE_FAIL } + else { + set_next_pc(exception_handler(cur_privilege, CTL_MNRET(), PC)); + RETIRE_SUCCESS + } +} + +mapping clause assembly = MNRET() <-> "mnret" diff --git a/model/riscv_insts_zicsr.sail b/model/riscv_insts_zicsr.sail index f2980fb9e..27ea99237 100644 --- a/model/riscv_insts_zicsr.sail +++ b/model/riscv_insts_zicsr.sail @@ -71,6 +71,12 @@ function readCSR csr : csreg -> xlenbits = { (0xC21, _) => vtype.bits, (0xC22, _) => vlenb, + /* Smrnmi - resumable NMI */ + (0x740, _) => mnscratch, + (0x741, _) => mnepc & pc_alignment_mask(), + (0x742, _) => mncause.bits, + (0x744, _) => mnstatus.bits, + /* trigger/debug */ (0x7a0, _) => ~(tselect), /* this indicates we don't have any trigger support */ @@ -152,6 +158,12 @@ function writeCSR (csr : csreg, value : xlenbits) -> unit = { (0xB80, 32) => { mcycle[63 .. 32] = value; Some(value) }, (0xB82, 32) => { minstret[63 .. 32] = value; minstret_increment = false; Some(value) }, + /* Smrnmi : resumable NMI */ + (0x740, _) => { mnscratch = value; Some(mnscratch) }, + (0x741, _) => { mnepc = value & pc_alignment_mask(); Some(mnepc & pc_alignment_mask()) }, + (0x742, _) => { mncause = legalize_mncause(mncause, value); Some(mncause.bits) }, + (0x744, _) => { mnstatus = legalize_mnstatus(mnstatus, value); Some(mnstatus.bits) }, + /* trigger/debug */ (0x7a0, _) => { tselect = value; Some(tselect) }, diff --git a/model/riscv_step.sail b/model/riscv_step.sail index 5ff7e4bc0..6e5f6e39c 100644 --- a/model/riscv_step.sail +++ b/model/riscv_step.sail @@ -28,7 +28,10 @@ function step(step_no : int) -> bool = { Some(intr, priv) => { if get_config_print_instr() then print_bits("Handling interrupt: ", interruptType_to_bits(intr)); - handle_interrupt(intr, priv); + match intr { + I_M_RNMI => { set_next_pc(rnmi_handler(PC)) }, + _ => { handle_interrupt(intr, priv); } + }; (RETIRE_FAIL, false) }, None() => { diff --git a/model/riscv_sys_control.sail b/model/riscv_sys_control.sail index 95ed4d6a5..d2ff54971 100644 --- a/model/riscv_sys_control.sail +++ b/model/riscv_sys_control.sail @@ -44,6 +44,12 @@ function is_CSR_defined (csr : csreg, p : Privilege) -> bool = // pmpcfgN 0x3A @ idx : bits(4) => p == Machine & sys_pmp_count() > unsigned(idx) & (idx[0] == bitzero | sizeof(xlen) == 32), + /* machine mode: Smrnmi RNMi handling */ + 0x740 => p == Machine & haveSmrnmi(), // mnscratch + 0x741 => p == Machine & haveSmrnmi(), // mnepc + 0x742 => p == Machine & haveSmrnmi(), // mncause + 0x744 => p == Machine & haveSmrnmi(), // mnstatus + // pmpaddrN. Unfortunately the PMP index does not nicely align with the CSR index bits. 0x3B @ idx : bits(4) => p == Machine & sys_pmp_count() > unsigned(0b00 @ idx), 0x3C @ idx : bits(4) => p == Machine & sys_pmp_count() > unsigned(0b01 @ idx), @@ -161,6 +167,7 @@ val load_reservation = {ocaml: "Platform.load_reservation", interpreter: "Platfo val match_reservation = {ocaml: "Platform.match_reservation", interpreter: "Platform.match_reservation", lem: "match_reservation", c: "match_reservation"} : xlenbits -> bool val cancel_reservation = {ocaml: "Platform.cancel_reservation", interpreter: "Platform.cancel_reservation", c: "cancel_reservation", lem: "cancel_reservation"} : unit -> unit + /* Exception delegation: given an exception and the privilege at which * it occured, returns the privilege at which it should be handled. */ @@ -260,6 +267,8 @@ function getPendingSet(priv : Privilege) -> option((xlenbits, Privilege)) = { } } +/* Smrnmi : whether RNMI is pending */ +val rnmi_pending = {ocaml: "Platform.rnmi_pending", interpreter: "Platform.rnmi_pending", c: "rnmi_pending", lem: "rnmi_pending"} : bool -> bool /* Examine the current interrupt state and return an interrupt to be * * handled (if any), and the privilege it should be handled at. */ @@ -267,7 +276,9 @@ function dispatchInterrupt(priv : Privilege) -> option((InterruptType, Privilege /* If we don't have different privilege levels, we don't need to check delegation. * Absence of U-mode implies absence of S-mode. */ - if not(haveUsrMode()) | (not(haveSupMode()) & not(haveNExt())) then { + if haveSmrnmi() & rnmi_pending(mnstatus[NMIE] == 0b1) & mnstatus[NMIE] == 0b1 then { + let r = (I_M_RNMI, Machine) in Some(r) + } else if not(haveUsrMode()) | (not(haveSupMode()) & not(haveNExt())) then { assert(priv == Machine, "invalid current privilege"); let enabled_pending = mip.bits & mie.bits; match findPendingInterrupt(enabled_pending) { @@ -288,10 +299,11 @@ function dispatchInterrupt(priv : Privilege) -> option((InterruptType, Privilege /* types of privilege transitions */ union ctl_result = { - CTL_TRAP : sync_exception, - CTL_SRET : unit, - CTL_MRET : unit, - CTL_URET : unit + CTL_TRAP : sync_exception, + CTL_SRET : unit, + CTL_MRET : unit, + CTL_MNRET : unit, + CTL_URET : unit } /* trap value */ @@ -392,6 +404,30 @@ function trap_handler(del_priv : Privilege, intr : bool, c : exc_code, pc : xlen } }; } +/* Smrnmi - resumable NMI */ +val rnmi_int_trap_vector = {ocaml: "Platform.rnmi_int_trap_vector", interpreter: "Platform.rnmi_int_trap_vector", c: "rnmi_int_trap_vector", lem: "rnmi_int_trap_vector"} : unit -> xlenbits +function rnmi_handler(pc : xlenbits) -> xlenbits = { + + rvfi_trap(); + if get_config_print_platform() + then print_platform("handling RNMI"); + + cancel_reservation(); + + mncause[Cause] = zero_extend(0b0); + mnstatus[NMIE] = 0b0; + mnstatus[MNPP] = privLevel_to_bits(cur_privilege); + mnepc = pc; + + cur_privilege = Machine; + + handle_trap_extension(Machine, pc, None()); + + if get_config_print_reg() + then print_reg("CSR mnstatus <- " ^ BitStr(mnstatus.bits())); + + rnmi_int_trap_vector(); +} function exception_handler(cur_priv : Privilege, ctl : ctl_result, pc: xlenbits) -> xlenbits = { @@ -420,6 +456,21 @@ function exception_handler(cur_priv : Privilege, ctl : ctl_result, cancel_reservation(); prepare_xret_target(Machine) & pc_alignment_mask() }, + (_, CTL_MNRET()) => { + let prev_priv = cur_privilege; + mnstatus[NMIE] = 0b1; + cur_privilege = privLevel_of_bits(mnstatus.MNPP()); + if cur_privilege != Machine + then mstatus[MPRV] = 0b0; + + if get_config_print_reg() + then print_reg("CSR mnstatus <- " ^ BitStr(mnstatus.bits)); + if get_config_print_platform() + then print_platform("ret-ing from " ^ to_str(prev_priv) ^ " to " ^ to_str(cur_privilege)); + + cancel_reservation(); + mnepc & pc_alignment_mask() + }, (_, CTL_SRET()) => { let prev_priv = cur_privilege; mstatus[SIE] = mstatus[SPIE]; @@ -529,6 +580,14 @@ function init_sys() -> unit = { menvcfg.bits = zero_extend(0b0); senvcfg.bits = zero_extend(0b0); + + /* Srnmi : resumable NMI */ + mncause[Cause] = zero_extend(0b0); + mncause[IsInterrupt] = 0b1; + mnepc = zero_extend(0b0); + mnscratch = zero_extend(0b0); + mnstatus.bits = zero_extend(0b0); + /* initialize vector csrs */ elen = 0b1; /* ELEN=64 as the common case */ vlen = 0b0100; /* VLEN=512 as a default value */ diff --git a/model/riscv_sys_exceptions.sail b/model/riscv_sys_exceptions.sail index 0059e2a7f..1859c1b99 100644 --- a/model/riscv_sys_exceptions.sail +++ b/model/riscv_sys_exceptions.sail @@ -15,12 +15,26 @@ function ext_check_xret_priv (p : Privilege) : Privilege -> bool = true /* Called if above check fails */ function ext_fail_xret_priv () : unit -> unit = () +/* Is MNRET permitted by extension? */ +function ext_check_mnret_priv () : unit -> bool = true +/* Called if above MNRET check fails */ +function ext_fail_mnret_priv () : unit -> unit = () + function handle_trap_extension(p : Privilege, pc : xlenbits, u : option(unit)) -> unit = () +/* Smrnmi : resumable NMI exception trap vector */ +val rnmi_exc_trap_vector = {ocaml: "Platform.rnmi_exc_trap_vector", interpreter: "Platform.rnmi_exc_trap_vector", c: "rnmi_exc_trap_vector", lem: "rnmi_exc_trap_vector"} : unit -> xlenbits + /* used for traps and ECALL */ function prepare_trap_vector(p : Privilege, cause : Mcause) -> xlenbits = { + + let to_rnmi_vec : bool = if haveSmrnmi() & p == Machine & mnstatus[NMIE] == 0b0 & + privLevel_of_bits(mstatus[MPP]) == Machine then true + else false; + let rnmi_exc_trap_vec : Mtvec = Mk_Mtvec(rnmi_exc_trap_vector()); + let tvec : Mtvec = match p { - Machine => mtvec, + Machine => if to_rnmi_vec then rnmi_exc_trap_vec else mtvec, Supervisor => stvec, User => utvec }; diff --git a/model/riscv_sys_regs.sail b/model/riscv_sys_regs.sail index 65d141923..5580e00e1 100644 --- a/model/riscv_sys_regs.sail +++ b/model/riscv_sys_regs.sail @@ -168,6 +168,12 @@ function haveZalrsc() -> bool = haveAtomics() /* Zicond extension support */ function haveZicond() -> bool = true +/* Smrnmi extension support */ +function haveSmrnmi() -> bool = true + +/* H extension support */ +function haveHext() -> bool = false + /* * Illegal values legalized to least privileged mode supported. * Note: the only valid combinations of supported modes are M, M+U, M+S+U. @@ -227,8 +233,18 @@ bitfield Mstatus : xlenbits = { } register mstatus : Mstatus +/* Smrnmi : resumable NMI registers */ +bitfield Mnstatus : xlenbits = { + MNPP : 12 .. 11, + MNPV : 7, + NMIE : 3, +} +register mnstatus : Mnstatus +register mnscratch : xlenbits +register mnepc : xlenbits + function effectivePrivilege(t : AccessType(ext_access_type), m : Mstatus, priv : Privilege) -> Privilege = - if t != Execute() & m[MPRV] == 0b1 + if t != Execute() & m[MPRV] == 0b1 & ((mnstatus[NMIE] == 0b1) | not(haveSmrnmi())) then privLevel_of_bits(m[MPP]) else priv @@ -444,6 +460,8 @@ bitfield Mcause : xlenbits = { Cause : xlen - 2 .. 0 } register mcause : Mcause +/* Smrnmi : resumable NMI cause register */ +register mncause : Mcause /* Interpreting the trap-vector address */ function tvec_addr(m : Mtvec, c : Mcause) -> option(xlenbits) = { @@ -480,6 +498,21 @@ function pc_alignment_mask() -> xlenbits = register mtval : xlenbits register mscratch : xlenbits +function legalize_mnstatus(o : Mnstatus, v : xlenbits) -> Mnstatus = { + let m = o; + let v = Mk_Mnstatus(v); + let m = [m with MNPV = if haveHext() then v[MNPV] else 0b0]; + let m = [m with MNPP = if have_privLevel(v[MNPP]) then v[MNPP] + else privLevel_to_bits(lowest_supported_privLevel())]; + /* NMIE can be set but not cleared */ + let m = [m with NMIE = (m[NMIE] | v[NMIE])]; + m +} +function legalize_mncause(o : Mcause, v : xlenbits) -> Mcause = { + let v = Mk_Mcause(v); + [o with Cause = v[Cause]]; +} + /* counters */ bitfield Counteren : bits(32) = { diff --git a/model/riscv_types.sail b/model/riscv_types.sail index 2b1c132ec..a875bea90 100644 --- a/model/riscv_types.sail +++ b/model/riscv_types.sail @@ -147,7 +147,8 @@ enum InterruptType = { I_M_Timer, I_U_External, I_S_External, - I_M_External + I_M_External, + I_M_RNMI } val interruptType_to_bits : InterruptType -> exc_code @@ -161,7 +162,8 @@ function interruptType_to_bits (i) = I_M_Timer => 0x07, I_U_External => 0x08, I_S_External => 0x09, - I_M_External => 0x0b + I_M_External => 0x0b, + I_M_RNMI => 0xff } /* architectural exception definitions */ diff --git a/ocaml_emulator/platform.ml b/ocaml_emulator/platform.ml index 69f271496..49683cde7 100644 --- a/ocaml_emulator/platform.ml +++ b/ocaml_emulator/platform.ml @@ -181,3 +181,14 @@ let init arch elf_file = in ( write_rom 0 rom; get_slice_int (cur_arch_bitwidth (), rom_base, Big_int.zero) ) + +(* Srnmi - resumable NMI *) +let rnmi_exc_trap_vector() = arch_bits_of_int !P.config_smrnmi_exc_trap_vec +let rnmi_int_trap_vector() = arch_bits_of_int !P.config_smrnmi_int_trap_vec +let is_rnmi_pending = ref true + +let rnmi_pending nmie = + let was_rnmi_pending = !is_rnmi_pending in + if was_rnmi_pending && nmie + then is_rnmi_pending := false; + was_rnmi_pending && nmie diff --git a/ocaml_emulator/platform_impl.ml b/ocaml_emulator/platform_impl.ml index 39de43776..ea4821e23 100644 --- a/ocaml_emulator/platform_impl.ml +++ b/ocaml_emulator/platform_impl.ml @@ -194,6 +194,13 @@ let show_bytes s = let dump_dts arch = show_bytes (make_dts arch) let dump_dtb arch = show_bytes (bytes_to_string (make_dtb (make_dts arch))) +(* Smrnmi : Resumable NMI *) +let config_smrnmi_exc_trap_vec = ref (Int.(0)) +let config_smrnmi_int_trap_vec = ref (Int.(0)) +let set_smrnmi_exc_trap_vec v = config_smrnmi_exc_trap_vec := v +let set_smrnmi_int_trap_vec v = config_smrnmi_int_trap_vec := v + + (* let save_string_to_file s fname = let out = open_out fname in diff --git a/ocaml_emulator/riscv_ocaml_sim.ml b/ocaml_emulator/riscv_ocaml_sim.ml index 56be8d8a8..ccdcaca0e 100644 --- a/ocaml_emulator/riscv_ocaml_sim.ml +++ b/ocaml_emulator/riscv_ocaml_sim.ml @@ -88,7 +88,13 @@ let options = Arg.align ([("-dump-dts", " requested isa"); ("-with-dtc", Arg.String PI.set_dtc, - " full path to dtc to use") + " full path to dtc to use"); + ("-smrnmi_int_trap_vec", + Arg.Int PI.set_smrnmi_int_trap_vec, + " configure SMRNMI interrupt trap vector"); + ("-smrnmi_exc_trap_vec", + Arg.Int PI.set_smrnmi_exc_trap_vec, + " configure SMRNMI exception trap vector") ]) let usage_msg = "RISC-V platform options:"