diff --git a/doc/01_specification/index.rst b/doc/01_specification/index.rst index 36e38b6583..f8bf3afaa1 100644 --- a/doc/01_specification/index.rst +++ b/doc/01_specification/index.rst @@ -193,12 +193,18 @@ identify the versions of RISC-V extensions from these specifications. Privileged Architecture Extension, version 0.9-draft, 3/15/2022, https://github.com/riscv/riscv-fast-interrupt/blob/master/clic.pdf +.. [SMRNMI] "Smrnmi" Standard Extension for Resumable Non-Maskable Interrupts + https://github.com/riscv/riscv-isa-manual/releases/download/riscv-isa-release-056b6ff-2023-10-02/riscv-privileged.pdf#chapter.4 + Other documents =============== .. [FPGAreset] Ken Chapman, “Get Smart About Reset: Think Local, Not Global”, Xilinx WP272 white paper, https://docs.xilinx.com/v/u/en-US/wp272 +.. [Ibex] Production-quality open source 32-bit RISC-V CPU core written in SystemVerilog + https://ibex-core.readthedocs.io/en/latest/index.html + CV32E20 core functional requirements ==================================== @@ -709,9 +715,8 @@ IRQs, but is not yet ratified at the time of specification. +---------+------------------------------------------------------------+ .. note:: - It should be noted that Ibex had implemented a custom mechanism for NMI - recovery. A standard RISC-V way of NMI recovery is in draft stage. In - future, the custom mechanism could be reworked to follow the standard. + [Ibex] had implemented a custom mechanism for NMI recovery. In CVE2 this + custom mechanism is reworked to follow the drafted standard [SMRNMI]_. Coprocessor interface --------------------- diff --git a/doc/03_reference/cs_registers.rst b/doc/03_reference/cs_registers.rst index c772aa744a..fd72bafefd 100644 --- a/doc/03_reference/cs_registers.rst +++ b/doc/03_reference/cs_registers.rst @@ -46,7 +46,13 @@ Ibex implements all the Control and Status Registers (CSRs) listed in the follow +---------+--------------------+--------+-----------------------------------------------+ | 0x3BF | ``pmpaddr15`` | WARL | PMP Address Register | +---------+--------------------+--------+-----------------------------------------------+ -| . . . . | +| 0x740 | ``mnscratch`` | WARL | Resumable NMI scratch register | ++---------+--------------------+--------+-----------------------------------------------+ +| 0x741 | ``mnepc`` | WARL | Resumable NMI program counter | ++---------+--------------------+--------+-----------------------------------------------+ +| 0x742 | ``mncause`` | WARL | Resumable NMI cause register | ++---------+--------------------+--------+-----------------------------------------------+ +| 0x744 | ``mnstatus`` | WARL | Resumable NMI status register | +---------+--------------------+--------+-----------------------------------------------+ | 0x757 | ``mseccfgh`` | WARL | Upper 32 bits of ``mseccfg`` | +---------+--------------------+--------+-----------------------------------------------+ @@ -310,6 +316,56 @@ Reset Value: ``0x0000_0000`` .. _csr-tselect: +Resumable NMI Machine Exception PC (mnepc) +--------------------------- + +CSR Address: ``0x741`` + +Reset Value: ``0x0000_0000`` + +When an NMI exception is encountered, the current program counter is saved in ``mnepc``, and the core jumps to the exception address. +When an MNRET instruction is executed, the value from ``mnepc`` replaces the current program counter. + + +Resumable NMI Machine Cause (mncause) +---------------------- + +CSR Address: ``0x742`` + +Reset Value: ``0x0000_0000`` + ++-------+------+------------------------------------------------------------------+ +| Bit# | R/W | Description | ++-------+------+------------------------------------------------------------------+ +| 31 | R | **Interrupt:** This bit is set when the exception was triggered | +| | | by an interrupt. | ++-------+------+------------------------------------------------------------------+ +| 30:0 | WARL | **NMI Cause** | ++-------+------+------------------------------------------------------------------+ + +The mncause CSR holds the reason for the NMI, with bit MXLEN-1 set to 1, and the NMI cause +encoded in the least-significant bits or zero if NMI causes are not supported. + +Resumable NMI Machine Status (mnstatus) +------------------------ + +CSR Address: ``0x744`` + +Reset Value: ``0x0000_0000`` + ++-------+-----+---------------------------------------------------------------------------------+ +| Bit# | R/W | Description | ++-------+-----+---------------------------------------------------------------------------------+ +| 12:11 | RW | **MNPP:** Machine Previous Privilege mode. | ++-------+-----+---------------------------------------------------------------------------------+ +| 3 | RW | **Interrupt Enable (NMIE):** If set to 1'b1, interrupts are globally enabled. | ++-------+-----+---------------------------------------------------------------------------------+ + +When an RNMI interrupt is detected, The ``mnstatus``.NMIE bit is cleared, masking all interrupts. If you want to enable interrupt handling in your exception handler, set ``mnstatus``.NMIE back to 1'b1 inside your handler code. + +Only Machine Mode and User Mode are supported. +Any write to ``mnstatus``.MNPP of an unsupported value will be interpreted as Machine Mode. + Trigger Select Register (tselect) --------------------------------- diff --git a/doc/03_reference/exception_interrupts.rst b/doc/03_reference/exception_interrupts.rst index 3a010e78de..1477ba4ea0 100644 --- a/doc/03_reference/exception_interrupts.rst +++ b/doc/03_reference/exception_interrupts.rst @@ -67,11 +67,13 @@ In Debug Mode, all interrupts including the NMI are ignored independent of ``mst Recoverable Non-Maskable Interrupt ---------------------------------- -To support recovering from an NMI happening during a trap handling routine, Ibex features additional CSRs for backing up ``mstatus``.MPP, ``mstatus``.MPIE, ``mepc`` and ``mcause``. -These CSRs are not accessible by software running on the core. +The base machine-level architecture supports only unresumable non-maskable interrupts (UNMIs), where the NMI jumps to a handler in machine mode, overwriting the current ``mepc`` and ``mcause`` +register values. If the hart had been executing machine-mode code in a trap handler, the previous values in ``mepc`` and ``mcause`` would not be recoverable and so execution is not generally resumable. -These CSRs are nonstandard. -For more information, see `the corresponding proposal `_. +To support recovering from an NMI (RNMI) happening during a trap handling routine, CVE2 supports the Smrnmi extension. The extension adds four new CSRs (``mnepc``, ``mncause``, ``mnstatus``, and ``mnscratch``) to hold the +interrupted state, and one new instruction, MNRET, to resume from the RNMI handler. + +For more information, see chapter '"Smrnmi" Standard Extension for Resumable Non-Maskable Interrupts"' in draft of 'The RISC-V Instruction Set Manual '_. Exceptions diff --git a/rtl/cve2_controller.sv b/rtl/cve2_controller.sv index 43d8b9fc57..26df6bf98b 100644 --- a/rtl/cve2_controller.sv +++ b/rtl/cve2_controller.sv @@ -22,6 +22,7 @@ module cve2_controller #( input logic illegal_insn_i, // decoder has an invalid instr input logic ecall_insn_i, // decoder has ECALL instr input logic mret_insn_i, // decoder has MRET instr + input logic mnret_insn_i, // decoder has MNRET instr input logic dret_insn_i, // decoder has DRET instr input logic wfi_insn_i, // decoder has WFI instr input logic ebrk_insn_i, // decoder has EBREAK instr @@ -81,6 +82,7 @@ module cve2_controller #( output logic csr_save_if_o, output logic csr_save_id_o, output logic csr_restore_mret_id_o, + output logic csr_restore_mnret_id_o, output logic csr_restore_dret_id_o, output logic csr_save_cause_o, output logic [31:0] csr_mtval_o, @@ -146,6 +148,7 @@ module cve2_controller #( logic ecall_insn; logic mret_insn; + logic mnret_insn; logic dret_insn; logic wfi_insn; logic ebrk_insn; @@ -176,6 +179,7 @@ module cve2_controller #( // Decoder doesn't take instr_valid into account, factor it in here. assign ecall_insn = ecall_insn_i & instr_valid_i; assign mret_insn = mret_insn_i & instr_valid_i; + assign mnret_insn = mnret_insn_i & instr_valid_i; assign dret_insn = dret_insn_i & instr_valid_i; assign wfi_insn = wfi_insn_i & instr_valid_i; assign ebrk_insn = ebrk_insn_i & instr_valid_i; @@ -189,7 +193,7 @@ module cve2_controller #( // Some instructions can only be executed in M-Mode assign illegal_umode = (priv_mode_i != PRIV_LVL_M) & // MRET must be in M-Mode. TW means trap WFI to M-Mode. - (mret_insn | (csr_mstatus_tw_i & wfi_insn)); + (mret_insn | mnret_insn | (csr_mstatus_tw_i & wfi_insn)); // This is recorded in the illegal_insn_q flop to help timing. Specifically // it is needed to break the path from cve2_cs_registers/illegal_csr_insn_o @@ -218,7 +222,7 @@ module cve2_controller #( assign special_req_flush_only = wfi_insn | csr_pipe_flush; // These special requests cause a change in PC - assign special_req_pc_change = mret_insn | dret_insn | exc_req_d | exc_req_lsu; + assign special_req_pc_change = mret_insn | mnret_insn | dret_insn | exc_req_d | exc_req_lsu; // generic special request signal, applies to all instructions assign special_req = special_req_pc_change | special_req_flush_only; @@ -326,6 +330,7 @@ module cve2_controller #( csr_save_if_o = 1'b0; csr_save_id_o = 1'b0; csr_restore_mret_id_o = 1'b0; + csr_restore_mnret_id_o = 1'b0; csr_restore_dret_id_o = 1'b0; csr_save_cause_o = 1'b0; csr_mtval_o = '0; @@ -660,10 +665,16 @@ module cve2_controller #( endcase end else begin // special instructions and pipeline flushes - if (mret_insn) begin - pc_mux_o = PC_ERET; + if (mret_insn | mnret_insn) begin pc_set_o = 1'b1; - csr_restore_mret_id_o = 1'b1; + if(mret_insn) begin + csr_restore_mret_id_o = 1'b1; + pc_mux_o = PC_ERET; + end + if(mnret_insn) begin + csr_restore_mnret_id_o = 1'b1; + pc_mux_o = PC_NRET; + end if (nmi_mode_q) begin nmi_mode_d = 1'b0; // exit NMI mode end diff --git a/rtl/cve2_core.sv b/rtl/cve2_core.sv index 4f79b67f9e..9008da9010 100644 --- a/rtl/cve2_core.sv +++ b/rtl/cve2_core.sv @@ -223,7 +223,7 @@ module cve2_core import cve2_pkg::*; #( logic nmi_mode; irqs_t irqs; logic csr_mstatus_mie; - logic [31:0] csr_mepc, csr_depc; + logic [31:0] csr_mepc, csr_mnepc, csr_depc; // PMP signals logic [33:0] csr_pmp_addr [PMPNumRegions]; @@ -235,6 +235,7 @@ module cve2_core import cve2_pkg::*; #( logic csr_save_if; logic csr_save_id; logic csr_restore_mret_id; + logic csr_restore_mnret_id; logic csr_restore_dret_id; logic csr_save_cause; logic csr_mtvec_init; @@ -329,6 +330,7 @@ module cve2_core import cve2_pkg::*; #( // CSRs .csr_mepc_i (csr_mepc), // exception return address + .csr_mnepc_i (csr_mnepc), // NMI return address .csr_depc_i (csr_depc), // debug return address .csr_mtvec_i (csr_mtvec), // trap-vector base address .csr_mtvec_init_o(csr_mtvec_init), @@ -417,6 +419,7 @@ module cve2_core import cve2_pkg::*; #( .csr_save_if_o (csr_save_if), // control signal to save PC .csr_save_id_o (csr_save_id), // control signal to save PC .csr_restore_mret_id_o(csr_restore_mret_id), // restore mstatus upon MRET + .csr_restore_mnret_id_o(csr_restore_mnret_id), // restore mstatus upon MNRET .csr_restore_dret_id_o(csr_restore_dret_id), // restore mstatus upon MRET .csr_save_cause_o (csr_save_cause), .csr_mtval_o (csr_mtval), @@ -712,6 +715,7 @@ module cve2_core import cve2_pkg::*; #( .csr_mstatus_mie_o(csr_mstatus_mie), .csr_mstatus_tw_o (csr_mstatus_tw), .csr_mepc_o (csr_mepc), + .csr_mnepc_o (csr_mnepc), // PMP .csr_pmp_cfg_o (csr_pmp_cfg), @@ -734,6 +738,7 @@ module cve2_core import cve2_pkg::*; #( .csr_save_if_i (csr_save_if), .csr_save_id_i (csr_save_id), .csr_restore_mret_i(csr_restore_mret_id), + .csr_restore_mnret_i(csr_restore_mnret_id), .csr_restore_dret_i(csr_restore_dret_id), .csr_save_cause_i (csr_save_cause), .csr_mcause_i (exc_cause), diff --git a/rtl/cve2_cs_registers.sv b/rtl/cve2_cs_registers.sv index dfce09858e..191e982a87 100644 --- a/rtl/cve2_cs_registers.sv +++ b/rtl/cve2_cs_registers.sv @@ -59,6 +59,7 @@ module cve2_cs_registers #( output cve2_pkg::irqs_t irqs_o, // interrupt requests qualified with mie output logic csr_mstatus_mie_o, output logic [31:0] csr_mepc_o, + output logic [31:0] csr_mnepc_o, // PMP output cve2_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions], @@ -82,6 +83,7 @@ module cve2_cs_registers #( input logic csr_save_if_i, input logic csr_save_id_i, input logic csr_restore_mret_i, + input logic csr_restore_mnret_i, input logic csr_restore_dret_i, input logic csr_save_cause_i, input cve2_pkg::exc_cause_e csr_mcause_i, @@ -134,9 +136,9 @@ module cve2_cs_registers #( } status_t; typedef struct packed { - logic mpie; - priv_lvl_e mpp; - } status_stk_t; + logic nmie; + priv_lvl_e mnpp; + } nm_status_t; typedef struct packed { x_debug_ver_e xdebugver; @@ -185,11 +187,14 @@ module cve2_cs_registers #( logic dscratch0_en, dscratch1_en; // CSRs for recoverable NMIs - // NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261 - status_stk_t mstack_q, mstack_d; - logic mstack_en; - logic [31:0] mstack_epc_q, mstack_epc_d; - logic [6:0] mstack_cause_q, mstack_cause_d; + // NOTE: these CSRS are following draft implementation of SMRNMI in priviledged + // see chapter 4.3 + // (https://github.com/riscv/riscv-isa-manual/releases/download/riscv-isa-release-1239329-2023-05-23/riscv-privileged.pdf) + // beware it is a subject of changes and might (will) be different than the final standard implementation + nm_status_t mnstatus_q, mnstatus_d; + logic mnstatus_en, mnscratch_en, mnepc_en, mncause_en; + logic [31:0] mnepc_q, mnepc_d, mnscratch_q, mnscratch_d; + logic [6:0] mncause_q, mncause_d; // PMP Signals logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS]; @@ -285,6 +290,13 @@ module cve2_cs_registers #( csr_rdata_int[CSR_MSTATUS_TW_BIT] = mstatus_q.tw; end + // mnstatus: always M-mode, contains IE bit + CSR_MNSTATUS: begin + csr_rdata_int = '0; + csr_rdata_int[CSR_MNSTATUS_NMIE_BIT] = mnstatus_q.nmie; + csr_rdata_int[CSR_MNSTATUS_MNPP_BIT_HIGH:CSR_MNSTATUS_MNPP_BIT_LOW] = mnstatus_q.mnpp; + end + // mstatush: All zeros for Ibex (fixed little endian and all other bits reserved) CSR_MSTATUSH: csr_rdata_int = '0; @@ -317,9 +329,15 @@ module cve2_cs_registers #( // mepc: exception program counter CSR_MEPC: csr_rdata_int = mepc_q; + // mnepc: exception program counter + CSR_MNEPC: csr_rdata_int = mnepc_q; + // mcause: exception cause CSR_MCAUSE: csr_rdata_int = {mcause_q[6], 25'b0, mcause_q[5:0]}; + // mcause: exception cause + CSR_MNCAUSE: csr_rdata_int = {mncause_q[6], 25'b0, mncause_q[5:0]}; + // mtval: trap value CSR_MTVAL: csr_rdata_int = mtval_q; @@ -507,11 +525,14 @@ module cve2_cs_registers #( dscratch0_en = 1'b0; dscratch1_en = 1'b0; - mstack_en = 1'b0; - mstack_d.mpie = mstatus_q.mpie; - mstack_d.mpp = mstatus_q.mpp; - mstack_epc_d = mepc_q; - mstack_cause_d = mcause_q; + mnstatus_en = 1'b0; + mnstatus_d = mnstatus_q; + mnscratch_en = 1'b0; + mnepc_en = 1'b0; + mnstatus_d.mnpp = mstatus_q.mpp; + mnepc_d = {csr_wdata_int[31:1], 1'b0}; + mncause_d = {csr_wdata_int[31], csr_wdata_int[5:0]}; + mncause_en = 1'b0; mcountinhibit_we = 1'b0; mhpmcounter_we = '0; @@ -535,17 +556,38 @@ module cve2_cs_registers #( end end + // mnstatus: IE bit + CSR_MNSTATUS: begin + mnstatus_en = 1'b1; + mnstatus_d = '{ + nmie: csr_wdata_int[CSR_MNSTATUS_NMIE_BIT], + mnpp: priv_lvl_e'(csr_wdata_int[CSR_MNSTATUS_MNPP_BIT_HIGH:CSR_MNSTATUS_MNPP_BIT_LOW]) + }; + // Convert illegal values to M-mode + if ((mnstatus_d.mnpp != PRIV_LVL_M) && (mnstatus_d.mnpp != PRIV_LVL_U)) begin + mnstatus_d.mnpp = PRIV_LVL_M; + end + end + // interrupt enable CSR_MIE: mie_en = 1'b1; CSR_MSCRATCH: mscratch_en = 1'b1; + CSR_MNSCRATCH: mnscratch_en = 1'b1; + // mepc: exception program counter CSR_MEPC: mepc_en = 1'b1; + // mnepc: rNMI program counter + CSR_MNEPC: mnepc_en = 1'b1; + // mcause CSR_MCAUSE: mcause_en = 1'b1; + // mncause + CSR_MNCAUSE: mncause_en = 1'b1; + // mtval: trap value CSR_MTVAL: mtval_en = 1'b1; @@ -651,14 +693,16 @@ module cve2_cs_registers #( mstatus_en = 1'b1; mstatus_d.mie = 1'b0; // disable interrupts // save current status - mstatus_d.mpie = mstatus_q.mie; - mstatus_d.mpp = priv_lvl_q; - mepc_en = 1'b1; - mepc_d = exception_pc; - mcause_en = 1'b1; - mcause_d = {csr_mcause_i}; + mnstatus_en = 1'b1; + //mnstatus_d.mpie = mstatus_q.mie; + mnstatus_d.mnpp = priv_lvl_q; + mnepc_en = 1'b1; + mnepc_d = exception_pc; + mncause_en = 1'b1; + mnscratch_en = 1'b1; + mncause_d = {csr_mcause_i}; // save previous status for recoverable NMI - mstack_en = 1'b1; + mnstatus_en = 1'b1; end end // csr_save_cause_i @@ -680,13 +724,13 @@ module cve2_cs_registers #( // SEC_CM: EXCEPTION.CTRL_FLOW.GLOBAL_ESC if (nmi_mode_i) begin - // when returning from an NMI restore state from mstack CSR - mstatus_d.mpie = mstack_q.mpie; - mstatus_d.mpp = mstack_q.mpp; + // when returning from an NMI restore state from mstatus CSR + mstatus_d.mpie = mstatus_q.mpie; + mstatus_d.mpp = mnstatus_q.mnpp; mepc_en = 1'b1; - mepc_d = mstack_epc_q; + mepc_d = mepc_q; mcause_en = 1'b1; - mcause_d = mstack_cause_q; + mcause_d = mcause_q; end else begin // otherwise just set mstatus.MPIE/MPP // See RISC-V Privileged Specification, version 1.11, Section 3.1.6.1 @@ -695,6 +739,27 @@ module cve2_cs_registers #( end end // csr_restore_mret_i + csr_restore_mnret_i: begin // MNRET + priv_lvl_d = mnstatus_q.mnpp; + mstatus_en = 1'b1; + mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts + + if (mnstatus_q.mnpp != PRIV_LVL_M) begin + mstatus_d.mprv = 1'b0; + end + + // SEC_CM: EXCEPTION.CTRL_FLOW.LOCAL_ESC + // SEC_CM: EXCEPTION.CTRL_FLOW.GLOBAL_ESC + + // when returning from an NMI restore state from mnstatus CSR + mstatus_d.mpie = mstatus_q.mpie; + mstatus_d.mpp = mnstatus_q.mnpp; + mepc_en = 1'b1; + mepc_d = mnepc_q; + mcause_en = 1'b1; + mcause_d = mncause_q; + end // csr_restore_mnret_i + default:; endcase end @@ -733,6 +798,7 @@ module cve2_cs_registers #( // directly output some registers assign csr_mepc_o = mepc_q; + assign csr_mnepc_o = mnepc_q; assign csr_depc_o = depc_q; assign csr_mtvec_o = mtvec_q; @@ -815,6 +881,20 @@ module cve2_cs_registers #( .rd_error_o() ); + // MNSCRATCH + cve2_csr #( + .Width (32), + .ShadowCopy(1'b0), + .ResetValue('0) + ) u_mnscratch_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int), + .wr_en_i (mnscratch_en), + .rd_data_o (mnscratch_q), + .rd_error_o() + ); + // MCAUSE cve2_csr #( .Width (7), @@ -918,46 +998,48 @@ module cve2_cs_registers #( .rd_error_o() ); - // MSTACK - localparam status_stk_t MSTACK_RESET_VAL = '{mpie: 1'b1, mpp: PRIV_LVL_U}; + // MNSTATUS + localparam nm_status_t MNSTATUS_RESET_VAL = '{nmie: 1'b0, + mnpp: PRIV_LVL_M}; + cve2_csr #( - .Width ($bits(status_stk_t)), + .Width ($bits(nm_status_t)), .ShadowCopy(1'b0), - .ResetValue({MSTACK_RESET_VAL}) - ) u_mstack_csr ( + .ResetValue({MNSTATUS_RESET_VAL}) + ) u_mnstatus_csr ( .clk_i (clk_i), .rst_ni (rst_ni), - .wr_data_i ({mstack_d}), - .wr_en_i (mstack_en), - .rd_data_o (mstack_q), + .wr_data_i (mnstatus_d), + .wr_en_i (mnstatus_en), + .rd_data_o (mnstatus_q), .rd_error_o() ); - // MSTACK_EPC + // MNEPC cve2_csr #( .Width (32), .ShadowCopy(1'b0), .ResetValue('0) - ) u_mstack_epc_csr ( + ) u_mnepc_csr ( .clk_i (clk_i), .rst_ni (rst_ni), - .wr_data_i (mstack_epc_d), - .wr_en_i (mstack_en), - .rd_data_o (mstack_epc_q), + .wr_data_i (mnepc_d), + .wr_en_i (mnepc_en), + .rd_data_o (mnepc_q), .rd_error_o() ); - // MSTACK_CAUSE + // MNCAUSE cve2_csr #( .Width (7), .ShadowCopy(1'b0), .ResetValue('0) - ) u_mstack_cause_csr ( + ) u_mncause_csr ( .clk_i (clk_i), .rst_ni (rst_ni), - .wr_data_i (mstack_cause_d), - .wr_en_i (mstack_en), - .rd_data_o (mstack_cause_q), + .wr_data_i (mncause_d), + .wr_en_i (mncause_en), + .rd_data_o (mncause_q), .rd_error_o() ); diff --git a/rtl/cve2_csr.sv b/rtl/cve2_csr.sv index b464bccedb..abe095f1f0 100644 --- a/rtl/cve2_csr.sv +++ b/rtl/cve2_csr.sv @@ -52,6 +52,6 @@ module cve2_csr #( assign rd_error_o = 1'b0; end - `ASSERT_KNOWN(IbexCSREnValid, wr_en_i) + `ASSERT_KNOWN(CVE2CSREnValid, wr_en_i) endmodule diff --git a/rtl/cve2_decoder.sv b/rtl/cve2_decoder.sv index 5b9cbca148..e882d9ca13 100644 --- a/rtl/cve2_decoder.sv +++ b/rtl/cve2_decoder.sv @@ -26,6 +26,8 @@ module cve2_decoder #( output logic ebrk_insn_o, // trap instr encountered output logic mret_insn_o, // return from exception instr // encountered + output logic mnret_insn_o, // return from NM exception instr + // encountered output logic dret_insn_o, // return from debug instr encountered output logic ecall_insn_o, // syscall instr encountered output logic wfi_insn_o, // wait for interrupt instr encountered @@ -222,6 +224,7 @@ module cve2_decoder #( illegal_insn = 1'b0; ebrk_insn_o = 1'b0; mret_insn_o = 1'b0; + mnret_insn_o = 1'b0; dret_insn_o = 1'b0; ecall_insn_o = 1'b0; wfi_insn_o = 1'b0; @@ -595,6 +598,9 @@ module cve2_decoder #( 12'h302: // mret mret_insn_o = 1'b1; + 12'h702: // mnret + mnret_insn_o = 1'b1; + 12'h7b2: // dret dret_insn_o = 1'b1; diff --git a/rtl/cve2_id_stage.sv b/rtl/cve2_id_stage.sv index e989ff295e..f1613bf3bb 100644 --- a/rtl/cve2_id_stage.sv +++ b/rtl/cve2_id_stage.sv @@ -85,6 +85,7 @@ module cve2_id_stage #( output logic csr_save_if_o, output logic csr_save_id_o, output logic csr_restore_mret_id_o, + output logic csr_restore_mnret_id_o, output logic csr_restore_dret_id_o, output logic csr_save_cause_o, output logic [31:0] csr_mtval_o, @@ -159,6 +160,7 @@ module cve2_id_stage #( logic illegal_insn_dec; logic ebrk_insn; logic mret_insn_dec; + logic mnret_insn_dec; logic dret_insn_dec; logic ecall_insn_dec; logic wfi_insn_dec; @@ -346,6 +348,7 @@ module cve2_id_stage #( .illegal_insn_o(illegal_insn_dec), .ebrk_insn_o (ebrk_insn), .mret_insn_o (mret_insn_dec), + .mnret_insn_o (mnret_insn_dec), .dret_insn_o (dret_insn_dec), .ecall_insn_o (ecall_insn_dec), .wfi_insn_o (wfi_insn_dec), @@ -456,6 +459,7 @@ module cve2_id_stage #( .illegal_insn_i (illegal_insn_o), .ecall_insn_i (ecall_insn_dec), .mret_insn_i (mret_insn_dec), + .mnret_insn_i (mnret_insn_dec), .dret_insn_i (dret_insn_dec), .wfi_insn_i (wfi_insn_dec), .ebrk_insn_i (ebrk_insn), @@ -501,6 +505,7 @@ module cve2_id_stage #( .csr_save_if_o (csr_save_if_o), .csr_save_id_o (csr_save_id_o), .csr_restore_mret_id_o(csr_restore_mret_id_o), + .csr_restore_mnret_id_o(csr_restore_mnret_id_o), .csr_restore_dret_id_o(csr_restore_dret_id_o), .csr_save_cause_o (csr_save_cause_o), .csr_mtval_o (csr_mtval_o), diff --git a/rtl/cve2_if_stage.sv b/rtl/cve2_if_stage.sv index cbb050547b..f6eb174681 100644 --- a/rtl/cve2_if_stage.sv +++ b/rtl/cve2_if_stage.sv @@ -63,6 +63,8 @@ module cve2_if_stage import cve2_pkg::*; #( // CSRs input logic [31:0] csr_mepc_i, // PC to restore after handling // the interrupt/exception + input logic [31:0] csr_mnepc_i, // PC to restore after handling + // the interrupt/exception input logic [31:0] csr_depc_i, // PC to restore after handling // the debug request input logic [31:0] csr_mtvec_i, // base PC to jump to on exception @@ -139,6 +141,7 @@ module cve2_if_stage import cve2_pkg::*; #( PC_JUMP: fetch_addr_n = branch_target_ex_i; PC_EXC: fetch_addr_n = exc_pc; // set PC to exception handler PC_ERET: fetch_addr_n = csr_mepc_i; // restore PC when returning from EXC + PC_NRET: fetch_addr_n = csr_mnepc_i; // restore PC when returning from EXC PC_DRET: fetch_addr_n = csr_depc_i; default: fetch_addr_n = { boot_addr_i[31:8], 8'h00 }; endcase diff --git a/rtl/cve2_pkg.sv b/rtl/cve2_pkg.sv index 6cd8f4b1e9..296356d46c 100644 --- a/rtl/cve2_pkg.sv +++ b/rtl/cve2_pkg.sv @@ -275,6 +275,7 @@ package cve2_pkg; PC_JUMP, PC_EXC, PC_ERET, + PC_NRET, PC_DRET, PC_BP } pc_sel_e; @@ -409,6 +410,12 @@ package cve2_pkg; CSR_PMPADDR14 = 12'h3BE, CSR_PMPADDR15 = 12'h3BF, + // Resumable NMI + CSR_MNSCRATCH = 12'h740, + CSR_MNEPC = 12'h741, + CSR_MNCAUSE = 12'h742, + CSR_MNSTATUS = 12'h744, + // ePMP control CSR_MSECCFG = 12'h747, CSR_MSECCFGH = 12'h757, @@ -538,6 +545,12 @@ package cve2_pkg; parameter int unsigned CSR_MSTATUS_MPRV_BIT = 17; parameter int unsigned CSR_MSTATUS_TW_BIT = 21; + // CSR mnstatus bits + parameter int unsigned CSR_MNSTATUS_NMIE_BIT = 3; + parameter int unsigned CSR_MNSTATUS_MNPV_BIT = 7; + parameter int unsigned CSR_MNSTATUS_MNPP_BIT_LOW = 11; + parameter int unsigned CSR_MNSTATUS_MNPP_BIT_HIGH = 12; + // CSR machine ISA parameter logic [1:0] CSR_MISA_MXL = 2'd1; // M-XLEN: XLEN in M-Mode for RV32 diff --git a/rtl/cve2_tracer.sv b/rtl/cve2_tracer.sv index 707419e449..80811736d5 100644 --- a/rtl/cve2_tracer.sv +++ b/rtl/cve2_tracer.sv @@ -893,6 +893,7 @@ module cve2_tracer ( INSN_ECALL: decode_mnemonic("ecall"); INSN_EBREAK: decode_mnemonic("ebreak"); INSN_MRET: decode_mnemonic("mret"); + INSN_MNRET: decode_mnemonic("mnret"); INSN_DRET: decode_mnemonic("dret"); INSN_WFI: decode_mnemonic("wfi"); // RV32M diff --git a/rtl/cve2_tracer_pkg.sv b/rtl/cve2_tracer_pkg.sv index 700dd37b38..a65dd647ad 100644 --- a/rtl/cve2_tracer_pkg.sv +++ b/rtl/cve2_tracer_pkg.sv @@ -57,6 +57,7 @@ package cve2_tracer_pkg; parameter logic [31:0] INSN_ECALL = { 12'b000000000000, 13'b0, {OPCODE_SYSTEM} }; parameter logic [31:0] INSN_EBREAK = { 12'b000000000001, 13'b0, {OPCODE_SYSTEM} }; parameter logic [31:0] INSN_MRET = { 12'b001100000010, 13'b0, {OPCODE_SYSTEM} }; + parameter logic [31:0] INSN_MNRET = { 12'b011100000010, 13'b0, {OPCODE_SYSTEM} }; parameter logic [31:0] INSN_DRET = { 12'b011110110010, 13'b0, {OPCODE_SYSTEM} }; parameter logic [31:0] INSN_WFI = { 12'b000100000101, 13'b0, {OPCODE_SYSTEM} };