From fc0d878a322efccccde4b5497685280b9904dc4d Mon Sep 17 00:00:00 2001 From: ChinYikMing Date: Mon, 24 Jun 2024 09:07:05 +0800 Subject: [PATCH] Preliminary support for trap handling during block emulation Traps can occur during block emulation, such as from misaligned access. To handle a trap, we temporarily exit the block and transfer control to the trap handler, processing each instruction by stepping. After the trap is resolved, execution resumes from the point where it occurred. We use a flag called 'is_trapped' to indicate when the emulation is in trap-handling mode. Additionally, the 'sret' instruction is used to adjust the relevant supervisor CSR before returning from a trap. Support for S-mode has been enhanced by adding most S-mode CSRs to 'riscv_internal', along with helper macros for easier manipulation of S-mode and M-mode CSRs. Although M-mode CSRs are not directly used during emulation, the helpers have been added for potential future use. Conditional checks for 'SYSTEM' were added where necessary. The 'LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE' functionality is disabled during trap handling, as traps are managed one instruction at a time rather than as a chained block. Related: #310 --- Makefile | 5 +- src/common.h | 2 + src/decode.c | 2 + src/decode.h | 4 +- src/emulate.c | 198 ++++++++++++++++++++++++++++++++------------ src/riscv.c | 14 ++++ src/riscv.h | 55 ++++++++++++ src/riscv_private.h | 94 ++++++++++++++------- src/rv32_constopt.c | 2 + src/rv32_template.c | 53 ++++++++---- src/t2c.c | 4 +- src/t2c_template.c | 2 + 12 files changed, 337 insertions(+), 98 deletions(-) diff --git a/Makefile b/Makefile index 1ce6c5bc..8d8a4a12 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,9 @@ CFLAGS = -std=gnu99 -O2 -Wall -Wextra CFLAGS += -Wno-unused-label CFLAGS += -include src/common.h +ENABLE_SYSTEM ?= 0 +$(call set-feature, SYSTEM) + # Enable link-time optimization (LTO) ENABLE_LTO ?= 1 ifeq ($(call has, LTO), 1) @@ -134,7 +137,7 @@ endif ENABLE_JIT ?= 0 $(call set-feature, JIT) ifeq ($(call has, JIT), 1) -OBJS_EXT += jit.o +OBJS_EXT += jit.o # tier-2 JIT compiler powered LLVM LLVM_CONFIG = llvm-config-17 LLVM_CONFIG := $(shell which $(LLVM_CONFIG)) diff --git a/src/common.h b/src/common.h index f6337f8d..da84ebdb 100644 --- a/src/common.h +++ b/src/common.h @@ -25,6 +25,8 @@ #define ARRAYS_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#define MASK(n) (~((~0U << (n)))) + /* Alignment macro */ #if defined(__GNUC__) || defined(__clang__) #define __ALIGNED(x) __attribute__((aligned(x))) diff --git a/src/decode.c b/src/decode.c index c434b0fa..4dbce5bd 100644 --- a/src/decode.c +++ b/src/decode.c @@ -841,9 +841,11 @@ static inline bool op_system(rv_insn_t *ir, const uint32_t insn) case 0x202: /* HRET: return from traps in H-mode */ /* illegal instruction */ return false; +#if RV32_HAS(SYSTEM) case 0x102: /* SRET: return from traps in S-mode */ ir->opcode = rv_insn_sret; break; +#endif case 0x302: /* MRET */ ir->opcode = rv_insn_mret; break; diff --git a/src/decode.h b/src/decode.h index 6af8ef2e..865ba7cf 100644 --- a/src/decode.h +++ b/src/decode.h @@ -78,7 +78,9 @@ enum op_field { /* RISC-V Privileged Instruction */ \ _(wfi, 0, 4, 0, ENC(rs1, rd)) \ _(uret, 0, 4, 0, ENC(rs1, rd)) \ - _(sret, 1, 4, 0, ENC(rs1, rd)) \ + IIF(RV32_HAS(SYSTEM))( \ + _(sret, 1, 4, 0, ENC(rs1, rd)) \ + ) \ _(hret, 0, 4, 0, ENC(rs1, rd)) \ _(mret, 1, 4, 0, ENC(rs1, rd)) \ _(sfencevma, 1, 4, 0, ENC(rs1, rs2, rd)) \ diff --git a/src/emulate.c b/src/emulate.c index a8856afb..e631f1ca 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -43,7 +43,7 @@ extern struct target_ops gdbstub_ops; /* RISC-V exception code list */ /* clang-format off */ -#define RV_EXCEPTION_LIST \ +#define RV_TRAP_LIST \ IIF(RV32_HAS(EXT_C))(, \ _(insn_misaligned, 0) /* Instruction address misaligned */ \ ) \ @@ -51,74 +51,119 @@ extern struct target_ops gdbstub_ops; _(breakpoint, 3) /* Breakpoint */ \ _(load_misaligned, 4) /* Load address misaligned */ \ _(store_misaligned, 6) /* Store/AMO address misaligned */ \ - _(ecall_M, 11) /* Environment call from M-mode */ + IIF(RV32_HAS(SYSTEM))(, \ + _(ecall_M, 11) /* Environment call from M-mode */ \ + ) /* clang-format on */ enum { -#define _(type, code) rv_exception_code##type = code, - RV_EXCEPTION_LIST +#define _(type, code) rv_trap_code_##type = code, + RV_TRAP_LIST #undef _ }; -static void rv_exception_default_handler(riscv_t *rv) +static void rv_trap_default_handler(riscv_t *rv) { rv->csr_mepc += rv->compressed ? 2 : 4; rv->PC = rv->csr_mepc; /* mret */ } -/* When a trap occurs in M-mode, mtval is either initialized to zero or +/* + * Trap might occurs during block emulation. For instance, page fault. + * In order to handle trap, we have to escape from block and execute + * registered trap handler. This trap_handler function helps to execute + * the registered trap handler, PC by PC. Once the trap is handled, + * resume the previous execution flow where cause the trap. + * + * Since the system emulation has not yet included in rv32emu, the page + * fault is not practical in current test suite. Instead, we try to + * emulate the misaligned handling in the test suite. + */ +#if RV32_HAS(SYSTEM) +static void trap_handler(riscv_t *rv); +#endif + +/* When a trap occurs in M-mode/S-mode, m/stval is either initialized to zero or * populated with exception-specific details to assist software in managing - * the trap. Otherwise, the implementation never modifies mtval, although + * the trap. Otherwise, the implementation never modifies m/stval, although * software can explicitly write to it. The hardware platform will define * which exceptions are required to informatively set mtval and which may * consistently set it to zero. * * When a hardware breakpoint is triggered or an exception like address * misalignment, access fault, or page fault occurs during an instruction - * fetch, load, or store operation, mtval is updated with the virtual address - * that caused the fault. In the case of an illegal instruction trap, mtval + * fetch, load, or store operation, m/stval is updated with the virtual address + * that caused the fault. In the case of an illegal instruction trap, m/stval * might be updated with the first XLEN or ILEN bits of the offending - * instruction. For all other traps, mtval is simply set to zero. However, - * it is worth noting that a future standard could redefine how mtval is + * instruction. For all other traps, m/stval is simply set to zero. However, + * it is worth noting that a future standard could redefine how m/stval is * handled for different types of traps. + * + * For simplicity and clarity, abstracting stval and mtval into a single + * identifier called tval, as both are handled by TRAP_HANDLER_IMPL. */ -#define EXCEPTION_HANDLER_IMPL(type, code) \ - static void rv_except_##type(riscv_t *rv, uint32_t mtval) \ - { \ - /* mtvec (Machine Trap-Vector Base Address Register) \ - * mtvec[MXLEN-1:2]: vector base address \ - * mtvec[1:0] : vector mode \ - */ \ - const uint32_t base = rv->csr_mtvec & ~0x3; \ - const uint32_t mode = rv->csr_mtvec & 0x3; \ - /* mepc (Machine Exception Program Counter) \ - * mtval (Machine Trap Value Register) \ - * mcause (Machine Cause Register): store exception code \ - * mstatus (Machine Status Register): keep track of and controls the \ - * hart’s current operating state \ - */ \ - rv->csr_mepc = rv->PC; \ - rv->csr_mtval = mtval; \ - rv->csr_mcause = code; \ - rv->csr_mstatus = MSTATUS_MPP; /* set privilege mode */ \ - if (!rv->csr_mtvec) { /* in case CSR is not configured */ \ - rv_exception_default_handler(rv); \ - return; \ - } \ - switch (mode) { \ - case 0: /* DIRECT: All exceptions set PC to base */ \ - rv->PC = base; \ - break; \ - /* VECTORED: Asynchronous interrupts set PC to base + 4 * code */ \ - case 1: \ - rv->PC = base + 4 * code; \ - break; \ - } \ +#define TRAP_HANDLER_IMPL(type, code) \ + static void rv_trap_##type(riscv_t *rv, uint32_t tval) \ + { \ + /* m/stvec (Machine/Supervisor Trap-Vector Base Address Register) \ + * m/stvec[MXLEN-1:2]: vector base address \ + * m/stvec[1:0] : vector mode \ + * m/sepc (Machine/Supervisor Exception Program Counter) \ + * m/stval (Machine/Supervisor Trap Value Register) \ + * m/scause (Machine/Supervisor Cause Register): store exception code \ + * m/sstatus (Machine/Supervisor Status Register): keep track of and \ + * controls the hart’s current operating state \ + */ \ + uint32_t base; \ + uint32_t mode; \ + /* user or supervisor */ \ + if (RV_PRIV_IS_U_OR_S_MODE()) { \ + const uint32_t sstatus_sie = \ + (rv->csr_sstatus & SSTATUS_SIE) >> SSTATUS_SIE_SHIFT; \ + rv->csr_sstatus |= (sstatus_sie << SSTATUS_SPIE_SHIFT); \ + rv->csr_sstatus &= ~(SSTATUS_SIE); \ + rv->csr_sstatus |= (rv->priv_mode << SSTATUS_SPP_SHIFT); \ + rv->priv_mode = RV_PRIV_S_MODE; \ + base = rv->csr_stvec & ~0x3; \ + mode = rv->csr_stvec & 0x3; \ + rv->csr_sepc = rv->PC; \ + rv->csr_stval = tval; \ + rv->csr_scause = code; \ + } else { /* machine */ \ + const uint32_t mstatus_mie = \ + (rv->csr_mstatus & MSTATUS_MIE) >> MSTATUS_MIE_SHIFT; \ + rv->csr_mstatus |= (mstatus_mie << MSTATUS_MPIE_SHIFT); \ + rv->csr_mstatus &= ~(MSTATUS_MIE); \ + rv->csr_mstatus |= (rv->priv_mode << MSTATUS_MPP_SHIFT); \ + rv->priv_mode = RV_PRIV_M_MODE; \ + base = rv->csr_mtvec & ~0x3; \ + mode = rv->csr_mtvec & 0x3; \ + rv->csr_mepc = rv->PC; \ + rv->csr_mtval = tval; \ + rv->csr_mcause = code; \ + if (!rv->csr_mtvec) { /* in case CSR is not configured */ \ + rv_trap_default_handler(rv); \ + return; \ + } \ + } \ + switch (mode) { \ + /* DIRECT: All traps set PC to base */ \ + case 0: \ + rv->PC = base; \ + break; \ + /* VECTORED: Asynchronous traps set PC to base + 4 * code */ \ + case 1: \ + /* MSB of code is used to indicate whether the trap is interrupt \ + * or exception, so it is not considered as the 'real' code */ \ + rv->PC = base + 4 * (code & MASK(31)); \ + break; \ + } \ + IIF(RV32_HAS(SYSTEM))(if (rv->is_trapped) trap_handler(rv);, ) \ } /* RISC-V exception handlers */ -#define _(type, code) EXCEPTION_HANDLER_IMPL(type, code) -RV_EXCEPTION_LIST +#define _(type, code) TRAP_HANDLER_IMPL(type, code) +RV_TRAP_LIST #undef _ /* wrap load/store and insn misaligned handler @@ -135,7 +180,8 @@ RV_EXCEPTION_LIST rv->compressed = compress; \ rv->csr_cycle = cycle; \ rv->PC = PC; \ - rv_except_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \ + IIF(RV32_HAS(SYSTEM))(rv->is_trapped = true, ); \ + rv_trap_##type##_misaligned(rv, IIF(IO)(addr, mask_or_pc)); \ return false; \ } @@ -196,6 +242,26 @@ static uint32_t *csr_get_ptr(riscv_t *rv, uint32_t csr) case CSR_FCSR: return (uint32_t *) (&rv->csr_fcsr); #endif + case CSR_SSTATUS: + return (uint32_t *) (&rv->csr_sstatus); + case CSR_SIE: + return (uint32_t *) (&rv->csr_sie); + case CSR_STVEC: + return (uint32_t *) (&rv->csr_stvec); + case CSR_SCOUNTEREN: + return (uint32_t *) (&rv->csr_scounteren); + case CSR_SSCRATCH: + return (uint32_t *) (&rv->csr_sscratch); + case CSR_SEPC: + return (uint32_t *) (&rv->csr_sepc); + case CSR_SCAUSE: + return (uint32_t *) (&rv->csr_scause); + case CSR_STVAL: + return (uint32_t *) (&rv->csr_stval); + case CSR_SIP: + return (uint32_t *) (&rv->csr_sip); + case CSR_SATP: + return (uint32_t *) (&rv->csr_satp); default: return NULL; } @@ -377,9 +443,10 @@ enum { }; #if RV32_HAS(GDBSTUB) -#define RVOP_NO_NEXT(ir) (!ir->next | rv->debug_mode) +#define RVOP_NO_NEXT(ir) \ + (!ir->next | rv->debug_mode IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, )) #else -#define RVOP_NO_NEXT(ir) (!ir->next) +#define RVOP_NO_NEXT(ir) (!ir->next IIF(RV32_HAS(SYSTEM))(| rv->is_trapped, )) #endif /* record whether the branch is taken or not during emulation */ @@ -565,8 +632,10 @@ FORCE_INLINE bool insn_is_unconditional_branch(uint8_t opcode) case rv_insn_ebreak: case rv_insn_jal: case rv_insn_jalr: - case rv_insn_sret: case rv_insn_mret: +#if RV32_HAS(SYSTEM) + case rv_insn_sret: +#endif #if RV32_HAS(EXT_C) case rv_insn_cj: case rv_insn_cjalr: @@ -598,7 +667,7 @@ static void block_translate(riscv_t *rv, block_t *block) /* decode the instruction */ if (!rv_decode(ir, insn)) { rv->compressed = is_compressed(insn); - rv_except_illegal_insn(rv, insn); + rv_trap_illegal_insn(rv, insn); break; } ir->impl = dispatch_table[ir->opcode]; @@ -1048,17 +1117,42 @@ void rv_step(void *arg) #endif } +#if RV32_HAS(SYSTEM) +static void trap_handler(riscv_t *rv) +{ + rv_insn_t *ir = mpool_alloc(rv->block_ir_mp); + assert(ir); + + /* set to false by sret/mret implementation */ + uint32_t insn; + while (rv->is_trapped && !rv_has_halted(rv)) { + insn = rv->io.mem_ifetch(rv->PC); + assert(insn); + + rv_decode(ir, insn); + ir->impl = dispatch_table[ir->opcode]; + rv->compressed = is_compressed(insn); + ir->impl(rv, ir, rv->csr_cycle, rv->PC); + } +} +#endif + void ebreak_handler(riscv_t *rv) { assert(rv); - rv_except_breakpoint(rv, rv->PC); + rv_trap_breakpoint(rv, rv->PC); } void ecall_handler(riscv_t *rv) { assert(rv); - rv_except_ecall_M(rv, 0); +#if RV32_HAS(SYSTEM) syscall_handler(rv); + rv->PC += 4; +#else + rv_trap_ecall_M(rv, 0); + syscall_handler(rv); +#endif } void memset_handler(riscv_t *rv) diff --git a/src/riscv.c b/src/riscv.c index f163e854..ce77a5a8 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -303,6 +303,20 @@ riscv_t *rv_create(riscv_user_t rv_attr) #endif #endif +#if RV32_HAS(SYSTEM) + /* + * System simulation defaults to S-mode as + * it does not rely on M-mode software like OpenSBI. + */ + rv->priv_mode = RV_PRIV_S_MODE; + + /* not being trapped */ + rv->is_trapped = false; +#else + /* ISA simulation defaults to M-mode */ + rv->priv_mode = RV_PRIV_M_MODE; +#endif + return rv; } diff --git a/src/riscv.h b/src/riscv.h index c4b7091b..30d6369b 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -96,6 +96,61 @@ enum { #define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT) #define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT) +/* The mstatus register keeps track of and controls the hart’s current operating + * state */ +#define MSTATUS_SIE_SHIFT 1 +#define MSTATUS_MIE_SHIFT 3 +#define MSTATUS_SPIE_SHIFT 5 +#define MSTATUS_UBE_SHIFT 6 +#define MSTATUS_MPIE_SHIFT 7 +#define MSTATUS_SPP_SHIFT 8 +#define MSTATUS_MPP_SHIFT 11 +#define MSTATUS_MPRV_SHIFT 17 +#define MSTATUS_SUM_SHIFT 18 +#define MSTATUS_MXR_SHIFT 18 +#define MSTATUS_TVM_SHIFT 20 +#define MSTATUS_TW_SHIFT 21 +#define MSTATUS_TSR_SHIFT 22 +#define MSTATUS_SIE (1 << MSTATUS_SIE_SHIFT) +#define MSTATUS_MIE (1 << MSTATUS_MIE_SHIFT) +#define MSTATUS_SPIE (1 << MSTATUS_SPIE_SHIFT) +#define MSTATUS_UBE (1 << MSTATUS_UBE_SHIFT) +#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT) +#define MSTATUS_SPP (1 << MSTATUS_SPP_SHIFT) +#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT) +#define MSTATUS_MPRV (1 << MSTATUS_MPRV_SHIFT) +#define MSTATUS_SUM (1 << MSTATUS_SUM_SHIFT) +#define MSTATUS_MXR (1 << MSTATUS_MXR_SHIFT) +#define MSTATUS_TVM (1 << MSTATUS_TVM_SHIFT) +#define MSTATUS_TW (1 << MSTATUS_TW_SHIFT) +#define MSTATUS_TSR (1 << MSTATUS_TSR_SHIFT) + +/* sstatus, a restricted view of mstatus */ +#define SSTATUS_SIE_SHIFT 1 +#define SSTATUS_SPIE_SHIFT 5 +#define SSTATUS_UBE_SHIFT 6 +#define SSTATUS_SPP_SHIFT 8 +#define SSTATUS_SUM_SHIFT 18 +#define SSTATUS_MXR_SHIFT 19 +#define SSTATUS_SIE (1 << SSTATUS_SIE_SHIFT) +#define SSTATUS_SPIE (1 << SSTATUS_SPIE_SHIFT) +#define SSTATUS_UBE (1 << SSTATUS_UBE_SHIFT) +#define SSTATUS_SPP (1 << SSTATUS_SPP_SHIFT) +#define SSTATUS_SUM (1 << SSTATUS_SUM_SHIFT) +#define SSTATUS_MXR (1 << SSTATUS_MXR_SHIFT) + +#define SIP_SSIP_SHIFT 1 +#define SIP_STIP_SHIFT 5 +#define SIP_SEIP_SHIFT 9 +#define SIP_SSIP (1 << SIP_SSIP_SHIFT) +#define SIP_STIP (1 << SIP_STIP_SHIFT) +#define SIP_SEIP (1 << SIP_SEIP_SHIFT) + +#define RV_PRIV_U_MODE 0 +#define RV_PRIV_S_MODE 1 +#define RV_PRIV_M_MODE 3 +#define RV_PRIV_IS_U_OR_S_MODE() (rv->priv_mode <= RV_PRIV_S_MODE) + /* * SBI functions must return a pair of values: * diff --git a/src/riscv_private.h b/src/riscv_private.h index 5ca740a1..757e940c 100644 --- a/src/riscv_private.h +++ b/src/riscv_private.h @@ -25,9 +25,31 @@ /* CSRs */ enum { /* floating point */ - CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ - CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ - CSR_FCSR = 0x003, /* Floating-point control and status register */ + CSR_FFLAGS = 0x001, /* Floating-point accrued exceptions */ + CSR_FRM = 0x002, /* Floating-point dynamic rounding mode */ + CSR_FCSR = 0x003, /* Floating-point control and status register */ + + /* Supervisor trap setup */ + CSR_SSTATUS = 0x100, /* Supervisor status register */ + CSR_SIE = 0x104, /* Supervisor interrupt-enable register */ + CSR_STVEC = 0x105, /* Supervisor trap-handler base address */ + CSR_SCOUNTEREN = 0x106, /* Supervisor counter enable */ + + /* Supervisor trap handling */ + CSR_SSCRATCH = 0x140, /* Supervisor register for machine trap handlers */ + CSR_SEPC = 0x141, /* Supervisor exception program counter */ + CSR_SCAUSE = 0x142, /* Supervisor trap cause */ + CSR_STVAL = 0x143, /* Supervisor bad address or instruction */ + CSR_SIP = 0x144, /* Supervisor interrupt pending */ + + /* Supervisor protection and translation */ + CSR_SATP = 0x180, /* Supervisor address translation and protection */ + + /* Machine information registers */ + CSR_MVENDORID = 0xF11, /* Vendor ID */ + CSR_MARCHID = 0xF12, /* Architecture ID */ + CSR_MIMPID = 0xF13, /* Implementation ID */ + CSR_MHARTID = 0xF14, /* Hardware thread ID */ /* Machine trap setup */ CSR_MSTATUS = 0x300, /* Machine status register */ @@ -39,26 +61,21 @@ enum { CSR_MCOUNTEREN = 0x306, /* Machine counter enable */ /* machine trap handling */ - CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ - CSR_MEPC = 0x341, /* Machine exception program counter */ - CSR_MCAUSE = 0x342, /* Machine trap cause */ - CSR_MTVAL = 0x343, /* Machine bad address or instruction */ - CSR_MIP = 0x344, /* Machine interrupt pending */ + CSR_MSCRATCH = 0x340, /* Scratch register for machine trap handlers */ + CSR_MEPC = 0x341, /* Machine exception program counter */ + CSR_MCAUSE = 0x342, /* Machine trap cause */ + CSR_MTVAL = 0x343, /* Machine bad address or instruction */ + CSR_MIP = 0x344, /* Machine interrupt pending */ /* low words */ - CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ - CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ + CSR_CYCLE = 0xC00, /* Cycle counter for RDCYCLE instruction */ + CSR_TIME = 0xC01, /* Timer for RDTIME instruction */ CSR_INSTRET = 0xC02, /* high words */ CSR_CYCLEH = 0xC80, CSR_TIMEH = 0xC81, CSR_INSTRETH = 0xC82, - - CSR_MVENDORID = 0xF11, /* Vendor ID */ - CSR_MARCHID = 0xF12, /* Architecture ID */ - CSR_MIMPID = 0xF13, /* Implementation ID */ - CSR_MHARTID = 0xF14, /* Hardware thread ID */ }; /* translated basic block */ @@ -125,21 +142,37 @@ struct riscv_internal { #endif /* csr registers */ - uint64_t csr_cycle; /* Machine cycle counter */ - uint32_t csr_time[2]; /* Performance counter */ - uint32_t csr_mstatus; /* Machine status register */ - uint32_t csr_mtvec; /* Machine trap-handler base address */ - uint32_t csr_misa; /* ISA and extensions */ - uint32_t csr_mtval; /* Machine bad address or instruction */ - uint32_t csr_mcause; /* Machine trap cause */ - uint32_t csr_mscratch; /* Scratch register for machine trap handler */ - uint32_t csr_mepc; /* Machine exception program counter */ - uint32_t csr_mip; /* Machine interrupt pending */ - uint32_t csr_mvendorid; /* vendor ID */ - uint32_t csr_marchid; /* Architecture ID */ - uint32_t csr_mimpid; /* Implementation ID */ + uint64_t csr_cycle; /* Machine cycle counter */ + uint32_t csr_time[2]; /* Performance counter */ + uint32_t csr_mstatus; /* Machine status register */ + uint32_t csr_mtvec; /* Machine trap-handler base address */ + uint32_t csr_misa; /* ISA and extensions */ + uint32_t csr_mtval; /* Machine bad address or instruction */ + uint32_t csr_mcause; /* Machine trap cause */ + uint32_t csr_mscratch; /* Scratch register for machine trap handler */ + uint32_t csr_mepc; /* Machine exception program counter */ + uint32_t csr_mip; /* Machine interrupt pending */ + uint32_t csr_mie; /* Machine interrupt enable */ + uint32_t csr_mideleg; /* Machine interrupt delegation register */ + uint32_t csr_medeleg; /* Machine exception delegation register */ + uint32_t csr_mvendorid; /* vendor ID */ + uint32_t csr_marchid; /* Architecture ID */ + uint32_t csr_mimpid; /* Implementation ID */ uint32_t csr_mbadaddr; + uint32_t csr_sstatus; /* supervisor status register */ + uint32_t csr_stvec; /* supervisor trap vector base address register */ + uint32_t csr_sip; /* supervisor interrupt pending register */ + uint32_t csr_sie; /* supervisor interrupt enable register */ + uint32_t csr_scounteren; /* supervisor counter-enable register */ + uint32_t csr_sscratch; /* supervisor scratch register */ + uint32_t csr_sepc; /* supervisor exception program counter */ + uint32_t csr_scause; /* supervisor cause register */ + uint32_t csr_stval; /* supervisor trap value register */ + uint32_t csr_satp; /* supervisor address translation and protection */ + + uint32_t priv_mode; /* U-mode or S-mode or M-mode */ + bool compressed; /**< current instruction is compressed or not */ #if !RV32_HAS(JIT) block_map_t block_map; /**< basic block map */ @@ -170,6 +203,11 @@ struct riscv_internal { */ bool is_interrupted; #endif + +#if RV32_HAS(SYSTEM) + /* The flag is used to indicate the current emulation is in a trap */ + bool is_trapped; +#endif }; /* sign extend a 16 bit value */ diff --git a/src/rv32_constopt.c b/src/rv32_constopt.c index 9ae5a4e3..0f78271a 100644 --- a/src/rv32_constopt.c +++ b/src/rv32_constopt.c @@ -374,8 +374,10 @@ CONSTOPT(wfi, {}) /* URET: return from traps in U-mode */ CONSTOPT(uret, {}) +#if RV32_HAS(SYSTEM) /* SRET: return from traps in S-mode */ CONSTOPT(sret, {}) +#endif /* HRET: return from traps in H-mode */ CONSTOPT(hret, {}) diff --git a/src/rv32_template.c b/src/rv32_template.c index c021ae0e..e8f9ee99 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -199,19 +199,24 @@ RVOP( #if !RV32_HAS(JIT) #define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ /* lookup branch history table */ \ - for (int i = 0; i < HISTORY_SIZE; i++) { \ - if (ir->branch_table->PC[i] == PC) { \ - MUST_TAIL return ir->branch_table->target[i]->impl( \ - rv, ir->branch_table->target[i], cycle, PC); \ + IIF(RV32_HAS(SYSTEM)(if (!rv->is_trapped), )) \ + { \ + for (int i = 0; i < HISTORY_SIZE; i++) { \ + if (ir->branch_table->PC[i] == PC) { \ + MUST_TAIL return ir->branch_table->target[i]->impl( \ + rv, ir->branch_table->target[i], cycle, PC); \ + } \ + } \ + block_t *block = block_find(&rv->block_map, PC); \ + if (block) { \ + /* update branch history table */ \ + ir->branch_table->PC[ir->branch_table->idx] = PC; \ + ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ + ir->branch_table->idx = \ + (ir->branch_table->idx + 1) % HISTORY_SIZE; \ + MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, \ + PC); \ } \ - } \ - block_t *block = block_find(&rv->block_map, PC); \ - if (block) { \ - /* update branch history table */ \ - ir->branch_table->PC[ir->branch_table->idx] = PC; \ - ir->branch_table->target[ir->branch_table->idx] = block->ir_head; \ - ir->branch_table->idx = (ir->branch_table->idx + 1) % HISTORY_SIZE; \ - MUST_TAIL return block->ir_head->impl(rv, block->ir_head, cycle, PC); \ } #else #define LOOKUP_OR_UPDATE_BRANCH_HISTORY_TABLE() \ @@ -1004,15 +1009,26 @@ RVOP( })) /* SRET: return from traps in S-mode */ +#if RV32_HAS(SYSTEM) RVOP( sret, { - /* FIXME: Implement */ - return false; + rv->is_trapped = false; + rv->priv_mode = (rv->csr_sstatus & SSTATUS_SPP) >> SSTATUS_SPP_SHIFT; + rv->csr_sstatus &= ~(SSTATUS_SPP); + + const uint32_t sstatus_spie = + (rv->csr_sstatus & SSTATUS_SPIE) >> SSTATUS_SPIE_SHIFT; + rv->csr_sstatus |= (sstatus_spie << SSTATUS_SIE_SHIFT); + rv->csr_sstatus |= SSTATUS_SPIE; + + rv->PC = rv->csr_sepc; + return true; }, GEN({ assert; /* FIXME: Implement */ })) +#endif /* HRET: return from traps in H-mode */ RVOP( @@ -1029,7 +1045,14 @@ RVOP( RVOP( mret, { - rv->csr_mstatus = MSTATUS_MPIE; + rv->priv_mode = (rv->csr_mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT; + rv->csr_mstatus &= ~(MSTATUS_MPP); + + const uint32_t mstatus_mpie = + (rv->csr_mstatus & MSTATUS_MPIE) >> MSTATUS_MPIE_SHIFT; + rv->csr_mstatus |= (mstatus_mpie << MSTATUS_MIE_SHIFT); + rv->csr_mstatus |= MSTATUS_MPIE; + rv->PC = rv->csr_mepc; return true; }, diff --git a/src/t2c.c b/src/t2c.c index 17be7893..8e13ceae 100644 --- a/src/t2c.c +++ b/src/t2c.c @@ -159,8 +159,10 @@ FORCE_INLINE bool t2c_insn_is_terminal(uint8_t opcode) case rv_insn_ecall: case rv_insn_ebreak: case rv_insn_jalr: - case rv_insn_sret: case rv_insn_mret: +#if RV32_HAS(SYSTEM) + case rv_insn_sret: +#endif #if RV32_HAS(EXT_C) case rv_insn_cjalr: case rv_insn_cjr: diff --git a/src/t2c_template.c b/src/t2c_template.c index fd2ded42..2ee33720 100644 --- a/src/t2c_template.c +++ b/src/t2c_template.c @@ -361,7 +361,9 @@ T2C_OP(wfi, { __UNREACHABLE; }) T2C_OP(uret, { __UNREACHABLE; }) +#if RV32_HAS(SYSTEM) T2C_OP(sret, { __UNREACHABLE; }) +#endif T2C_OP(hret, { __UNREACHABLE; })