Skip to content
11 changes: 8 additions & 3 deletions doc/01_specification/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
====================================

Expand Down Expand Up @@ -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
---------------------
Expand Down
58 changes: 57 additions & 1 deletion doc/03_reference/cs_registers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`` |
+---------+--------------------+--------+-----------------------------------------------+
Expand Down Expand Up @@ -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)
---------------------------------

Expand Down
10 changes: 6 additions & 4 deletions doc/03_reference/exception_interrupts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/riscv/riscv-isa-manual/issues/261>`_.
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 <https://github.com/riscv/riscv-isa-manual/releases/download/riscv-isa-release-056b6ff-2023-10-02/riscv-privileged.pdf#chapter.4>'_.


Exceptions
Expand Down
21 changes: 16 additions & 5 deletions rtl/cve2_controller.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion rtl/cve2_core.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand Down
Loading