[Priv modes] Add support for U-Mode

- General changes to suport U-mode (fixes #88)
- Update documentation
- Add priv mode flops to CSRs module
- Propagate correct priv mode to PMP module
- Implement CSR priv-mode permission checking
- Implement illegal U-mode instruction checking
- Add extra mstatus bits for U-mode (MPRV and TW)
This commit is contained in:
Tom Roberts 2019-09-09 08:42:48 +01:00 committed by Tom Roberts
parent f54dfe5d4b
commit 2aacd2b98b
10 changed files with 129 additions and 37 deletions

View file

@ -90,18 +90,24 @@ Reset Value: ``0x0000_1800``
+-------+-----+---------------------------------------------------------------------------------+
| Bit# | R/W | Description |
+-------+-----+---------------------------------------------------------------------------------+
| 12:11 | R | **MPP:** Statically 2'b11 and cannot be altered (read-only). |
| 21 | RW | **TW:** Timeout Wait (WFI executed in User Mode will trap to Machine Mode). |
+-------+-----+---------------------------------------------------------------------------------+
| 17 | RW | **MPRV:** Modify Privilege (Loads and stores use MPP for privilege checking). |
+-------+-----+---------------------------------------------------------------------------------+
| 12:11 | RW | **MPP:** Machine Previous Privilege mode. |
+-------+-----+---------------------------------------------------------------------------------+
| 7 | RW | **Previous Interrupt Enable (MPIE)**, i.e., before entering exception handling. |
+-------+-----+---------------------------------------------------------------------------------+
| 3 | RW | **Interrupt Enable (MIE):** If set to 1'b1, interrupts are globally enabled. |
+-------+-----+---------------------------------------------------------------------------------+
When an exception is encountered, ``mstatus``.MPIE will be set to ``mstatus``.MIE.
When the MRET instruction is executed, the value of MPIE will be stored back to ``mstatus``.MIE.
When an exception is encountered, ``mstatus``.MPIE will be set to ``mstatus``.MIE, and ``mstatus``.MPP will be set to the current privilege mode.
When the MRET instruction is executed, the value of MPIE will be stored back to ``mstatus``.MIE, and the privilege mode will be restored from ``mstatus``.MPP.
If you want to enable interrupt handling in your exception handler, set ``mstatus``.MIE to 1'b1 inside your handler code.
Only Machine Mode and User Mode are supported.
Any write to ``mstatus``.MPP of an unsupported value will be interpreted as Machine Mode.
Machine ISA Register (misa)
---------------------------
@ -276,6 +282,15 @@ Reset Value: ``0x0000_0000``
| address[33:2] |
+----------------+
Time Registers (time(h))
------------------------
CSR Address: ``0xC01 / 0xC81``
The User Mode ``time(h)`` registers are not implemented in Ibex.
Any access to these registers will trap.
It is recommended that trap handler software provides a means of accessing platform-defined ``mtime(h)`` timers where available.
.. _csr-mhartid:
Hardware Thread ID (mhartid)

View file

@ -15,6 +15,12 @@ For more information, see the :ref:`cs-registers` documentation.
The core starts fetching at the address made by concatenating the most significant 3 bytes of the boot address and the reset value (0x80) as the least significant byte.
It is assumed that the boot address is supplied via a register to avoid long paths to the instruction fetch unit.
Privilege Modes
---------------
Ibex supports operation in Machine Mode (M-Mode) and User Mode (U-Mode).
The core resets into M-Mode and will jump to M-Mode on any interrupt or exception.
On execution of an MRET instruction, the core will return to the Privilege Mode stored in ``mstatus``.MPP.
Interrupts
----------
@ -80,7 +86,9 @@ Ibex can trigger an exception due to the following exception causes:
+----------------+---------------------------------------------------------------+
| 7 | Store access fault |
+----------------+---------------------------------------------------------------+
| 11 | Environment call from M-mode (ECALL) |
| 8 | Environment call from U-Mode (ECALL) |
+----------------+---------------------------------------------------------------+
| 11 | Environment call from M-Mode (ECALL) |
+----------------+---------------------------------------------------------------+
The illegal instruction exception, instruction access fault, LSU error exceptions and ECALL instruction exceptions cannot be disabled and are always active.

View file

@ -53,7 +53,7 @@ In addition, the following instruction set extensions are available.
Most content of the RISC-V privileged specification is optional.
Ibex currently supports the following features according to the RISC-V Privileged Specification, version 1.11.
* M mode
* M-Mode and U-Mode
* All CSRs listed in :ref:`cs-registers`
* Performance counters as described in :ref:`performance-counters`
* Vectorized trap handling as described at :ref:`exceptions-interrupts`

View file

@ -69,9 +69,9 @@ lint_off -msg UNUSED -file "*/rtl/ibex_core.sv" -lines 199
// Signal unoptimizable: Feedback to clock or circular logic:
// ibex_core.id_stage_i.controller_i.ctrl_fsm_cs
// Issue lowrisc/ibex#211
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_controller.sv" -lines 98
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_controller.sv" -lines 100
// Signal unoptimizable: Feedback to clock or circular logic:
// ibex_core.cs_registers_i.mie_q
// Issue lowrisc/ibex#212
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -lines 149
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -lines 162

View file

@ -74,6 +74,8 @@ module ibex_controller (
output logic csr_restore_mret_id_o,
output logic csr_save_cause_o,
output logic [31:0] csr_mtval_o,
input ibex_pkg::priv_lvl_e priv_mode_i,
input logic csr_mstatus_tw_i,
// stall signals
input logic stall_lsu_i,
@ -108,6 +110,7 @@ module ibex_controller (
logic halt_if;
logic flush_id;
logic illegal_dret;
logic illegal_umode;
logic exc_req_lsu;
logic special_req;
logic enter_debug_mode;
@ -157,11 +160,17 @@ module ibex_controller (
// "Executing DRET outside of Debug Mode causes an illegal instruction exception."
// [Debug Spec v0.13.2, p.41]
assign illegal_dret = dret_insn & ~debug_mode_q;
// 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));
// This is recorded in the illegal_insn_q flop to help timing. Specifically
// it is needed to break the path from ibex_cs_registers/illegal_csr_insn_o
// to pc_set_o. Clear when controller is in FLUSH so it won't remain set
// once illegal instruction is handled.
assign illegal_insn_d = (illegal_insn_i | illegal_dret) & (ctrl_fsm_cs != FLUSH);
assign illegal_insn_d = (illegal_insn_i | illegal_dret | illegal_umode) & (ctrl_fsm_cs != FLUSH);
// exception requests
// requests are flopped in exc_req_q. This is cleared when controller is in
@ -486,7 +495,8 @@ module ibex_controller (
csr_mtval_o = instr_is_compressed_i ? {16'b0, instr_compressed_i} : instr_i;
end else if (ecall_insn) begin
exc_cause_o = EXC_CAUSE_ECALL_MMODE;
exc_cause_o = (priv_mode_i == PRIV_LVL_M) ? EXC_CAUSE_ECALL_MMODE :
EXC_CAUSE_ECALL_UMODE;
end else if (ebrk_insn) begin
if (debug_mode_q) begin

View file

@ -196,7 +196,10 @@ module ibex_core #(
logic csr_mtvec_init;
logic [31:0] csr_mtvec;
logic [31:0] csr_mtval;
priv_lvl_e priv_mode;
logic csr_mstatus_tw;
priv_lvl_e priv_mode_id;
priv_lvl_e priv_mode_if;
priv_lvl_e priv_mode_lsu;
// debug mode and dcsr configuration
logic debug_mode;
@ -412,6 +415,8 @@ module ibex_core #(
.csr_restore_mret_id_o ( csr_restore_mret_id ), // restore mstatus upon MRET
.csr_save_cause_o ( csr_save_cause ),
.csr_mtval_o ( csr_mtval ),
.priv_mode_i ( priv_mode_id ),
.csr_mstatus_tw_i ( csr_mstatus_tw ),
.illegal_csr_insn_i ( illegal_csr_insn_id ),
// LSU
@ -572,7 +577,9 @@ module ibex_core #(
// Hart ID from outside
.hart_id_i ( hart_id_i ),
.priv_mode_o ( priv_mode ),
.priv_mode_id_o ( priv_mode_id ),
.priv_mode_if_o ( priv_mode_if ),
.priv_mode_lsu_o ( priv_mode_lsu ),
// mtvec
.csr_mtvec_o ( csr_mtvec ),
@ -597,6 +604,7 @@ module ibex_core #(
.csr_meip_o ( csr_meip ),
.csr_mfip_o ( csr_mfip ),
.csr_mstatus_mie_o ( csr_mstatus_mie ),
.csr_mstatus_tw_o ( csr_mstatus_tw ),
.csr_mepc_o ( csr_mepc ),
// PMP
@ -640,11 +648,14 @@ module ibex_core #(
if (PMPEnable) begin : g_pmp
logic [33:0] pmp_req_addr [PMP_NUM_CHAN];
pmp_req_e pmp_req_type [PMP_NUM_CHAN];
priv_lvl_e pmp_priv_lvl [PMP_NUM_CHAN];
assign pmp_req_addr[PMP_I] = {2'b00,instr_addr_o[31:0]};
assign pmp_req_type[PMP_I] = PMP_ACC_EXEC;
assign pmp_priv_lvl[PMP_I] = priv_mode_if;
assign pmp_req_addr[PMP_D] = {2'b00,data_addr_o[31:0]};
assign pmp_req_type[PMP_D] = data_we_o ? PMP_ACC_WRITE : PMP_ACC_READ;
assign pmp_priv_lvl[PMP_D] = priv_mode_lsu;
ibex_pmp #(
.PMPGranularity ( PMPGranularity ),
@ -656,13 +667,18 @@ module ibex_core #(
// Interface to CSRs
.csr_pmp_cfg_i ( csr_pmp_cfg ),
.csr_pmp_addr_i ( csr_pmp_addr ),
.priv_mode_i ( priv_mode ),
.priv_mode_i ( pmp_priv_lvl ),
// Access checking channels
.pmp_req_addr_i ( pmp_req_addr ),
.pmp_req_type_i ( pmp_req_type ),
.pmp_req_err_o ( pmp_req_err )
);
end else begin : g_no_pmp
// Unused signal tieoff
priv_lvl_e unused_priv_lvl_if, unused_priv_lvl_ls;
assign unused_priv_lvl_if = priv_mode_if;
assign unused_priv_lvl_ls = priv_mode_lsu;
// Output tieoff
assign pmp_req_err[PMP_I] = 1'b0;
assign pmp_req_err[PMP_D] = 1'b0;
end
@ -675,7 +691,7 @@ module ibex_core #(
rvfi_intr <= '0;
rvfi_order <= '0;
rvfi_insn <= '0;
rvfi_mode <= '0;
rvfi_mode <= {PRIV_LVL_M};
rvfi_rs1_addr <= '0;
rvfi_rs2_addr <= '0;
rvfi_pc_rdata <= '0;
@ -696,7 +712,7 @@ module ibex_core #(
rvfi_intr <= rvfi_intr_d;
rvfi_order <= rvfi_order + 64'(rvfi_valid);
rvfi_insn <= rvfi_insn_id;
rvfi_mode <= PRIV_LVL_M; // TODO: Update for user mode support
rvfi_mode <= {priv_mode_id};
rvfi_rs1_addr <= rvfi_rs1_addr_id;
rvfi_rs2_addr <= rvfi_rs2_addr_id;
rvfi_pc_rdata <= pc_id;

View file

@ -24,7 +24,12 @@ module ibex_cs_registers #(
// Hart ID
input logic [31:0] hart_id_i,
output ibex_pkg::priv_lvl_e priv_mode_o,
// Privilege mode
output ibex_pkg::priv_lvl_e priv_mode_id_o,
output ibex_pkg::priv_lvl_e priv_mode_if_o,
output ibex_pkg::priv_lvl_e priv_mode_lsu_o,
output logic csr_mstatus_tw_o,
// mtvec
output logic [31:0] csr_mtvec_o,
@ -112,8 +117,15 @@ module ibex_cs_registers #(
logic mie;
logic mpie;
priv_lvl_e mpp;
logic mprv;
logic tw;
} Status_t;
typedef struct packed {
logic mpie;
priv_lvl_e mpp;
} StatusStk_t;
// struct for mip/mie CSRs
typedef struct packed {
logic irq_software;
@ -145,6 +157,7 @@ module ibex_cs_registers #(
logic [31:0] exception_pc;
// CSRs
priv_lvl_e priv_lvl_q, priv_lvl_d;
Status_t mstatus_q, mstatus_d;
Interrupts_t mie_q, mie_d;
logic [31:0] mscratch_q, mscratch_d;
@ -160,7 +173,7 @@ module ibex_cs_registers #(
// CSRs for recoverable NMIs
// NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261
Status_t mstack_q, mstack_d;
StatusStk_t mstack_q, mstack_d;
logic [31:0] mstack_epc_q, mstack_epc_d;
logic [5:0] mstack_cause_q, mstack_cause_d;
@ -205,11 +218,11 @@ module ibex_cs_registers #(
assign mhpmcounter_idx = csr_addr[4:0];
// See RISC-V Privileged Specification, version 1.11, Section 2.1
assign illegal_csr_priv = 1'b0; // we only support M-mode
assign illegal_csr_priv = (csr_addr[9:8] > {priv_lvl_q});
assign illegal_csr_write = (csr_addr[11:10] == 2'b11) && csr_wreq;
assign illegal_csr_insn_o = illegal_csr | illegal_csr_write | illegal_csr_priv;
assign illegal_csr_insn_o = csr_access_i & (illegal_csr | illegal_csr_write | illegal_csr_priv);
// mip CSR is purely combintational - must be able to re-enable the clock upon WFI
// mip CSR is purely combinational - must be able to re-enable the clock upon WFI
assign mip.irq_software = irq_software_i & mie_q.irq_software;
assign mip.irq_timer = irq_timer_i & mie_q.irq_timer;
assign mip.irq_external = irq_external_i & mie_q.irq_external;
@ -230,6 +243,7 @@ module ibex_cs_registers #(
csr_rdata_int[CSR_MSTATUS_MIE_BIT] = mstatus_q.mie;
csr_rdata_int[CSR_MSTATUS_MPIE_BIT] = mstatus_q.mpie;
csr_rdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW] = mstatus_q.mpp;
csr_rdata_int[CSR_MSTATUS_MPRV_BIT] = mstatus_q.mprv;
end
// misa
@ -324,7 +338,7 @@ module ibex_cs_registers #(
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCOUNTINHIBIT
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin
illegal_csr = csr_access_i;
illegal_csr = 1'b1;
end
end else if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTER) begin
@ -333,7 +347,7 @@ module ibex_cs_registers #(
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCYCLE
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin // CSR_MINSTRET
illegal_csr = csr_access_i;
illegal_csr = 1'b1;
end
end else if ((csr_addr & CSR_MASK_MCOUNTER) == CSR_OFF_MCOUNTERH) begin
@ -342,10 +356,10 @@ module ibex_cs_registers #(
if ((csr_addr[4:0] == 5'b00000) || // CSR_MCYCLEH
(csr_addr[4:0] == 5'b00001) ||
(csr_addr[4:0] == 5'b00010)) begin // CSR_MINSTRETH
illegal_csr = csr_access_i;
illegal_csr = 1'b1;
end
end else begin
illegal_csr = csr_access_i;
illegal_csr = 1'b1;
end
end
endcase
@ -355,6 +369,7 @@ module ibex_cs_registers #(
always_comb begin
exception_pc = pc_id_i;
priv_lvl_d = priv_lvl_q;
mstatus_d = mstatus_q;
mie_d = mie_q;
mscratch_d = mscratch_q;
@ -382,8 +397,14 @@ module ibex_cs_registers #(
mstatus_d = '{
mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT],
mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT],
mpp: PRIV_LVL_M
mpp: csr_wdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW],
mprv: csr_wdata_int[CSR_MSTATUS_MPRV_BIT],
tw: csr_wdata_int[CSR_MSTATUS_TW_BIT]
};
// Convert illegal values to M-mode
if ((mstatus_d.mpp != PRIV_LVL_M) && (mstatus_d.mpp != PRIV_LVL_U)) begin
mstatus_d.mpp = PRIV_LVL_M;
end
end
// interrupt enable
@ -413,7 +434,10 @@ module ibex_cs_registers #(
CSR_DCSR: begin
dcsr_d = csr_wdata_int;
dcsr_d.xdebugver = XDEBUGVER_STD;
dcsr_d.prv = PRIV_LVL_M; // only M-mode is supported
// Change to PRIV_LVL_M if sofware writes an unsupported value
if ((dcsr_d.prv != PRIV_LVL_M) && (dcsr_d.prv != PRIV_LVL_U)) begin
dcsr_d.prv = PRIV_LVL_M;
end
// currently not supported:
dcsr_d.nmip = 1'b0;
@ -471,15 +495,16 @@ module ibex_cs_registers #(
if (debug_csr_save_i) begin
// all interrupts are masked
// do not update cause, epc, tval, epc and status
dcsr_d.prv = PRIV_LVL_M;
dcsr_d.prv = priv_lvl_q;
dcsr_d.cause = debug_cause_i;
depc_d = exception_pc;
end else begin
priv_lvl_d = PRIV_LVL_M;
mtval_d = csr_mtval_i;
mstatus_d.mie = 1'b0; // disable interrupts
// save current status
mstatus_d.mpie = mstatus_q.mie;
mstatus_d.mpp = PRIV_LVL_M;
mstatus_d.mpp = priv_lvl_q;
mepc_d = exception_pc;
mcause_d = {csr_mcause_i};
// save previous status for recoverable NMI
@ -491,12 +516,15 @@ module ibex_cs_registers #(
end // csr_save_cause_i
csr_restore_mret_i: begin // MRET
priv_lvl_d = mstatus_q.mpp;
mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts
// restore previous status for recoverable NMI
mstatus_d.mpie = mstack_q.mpie;
mstatus_d.mpp = mstack_q.mpp;
mepc_d = mstack_epc_q;
mcause_d = mstack_cause_q;
mstack_d.mpie = 1'b1;
mstack_d.mpp = PRIV_LVL_U;
end // csr_restore_mret_i
default:;
@ -538,6 +566,7 @@ module ibex_cs_registers #(
assign csr_mtvec_o = mtvec_q;
assign csr_mstatus_mie_o = mstatus_q.mie;
assign csr_mstatus_tw_o = mstatus_q.tw;
assign debug_single_step_o = dcsr_q.step;
assign debug_ebreakm_o = dcsr_q.ebreakm;
@ -546,10 +575,13 @@ module ibex_cs_registers #(
// actual registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
priv_lvl_q <= PRIV_LVL_M;
mstatus_q <= '{
mie: 1'b0,
mpie: 1'b0,
mpp: PRIV_LVL_M
mpie: 1'b1,
mpp: PRIV_LVL_U,
mprv: 1'b0,
tw: 1'b0
};
mie_q <= '0;
mscratch_q <= '0;
@ -568,15 +600,15 @@ module ibex_cs_registers #(
dscratch1_q <= '0;
mstack_q <= '{
mie: 1'b0,
mpie: 1'b0,
mpp: PRIV_LVL_M
mpie: 1'b1,
mpp: PRIV_LVL_U
};
mstack_epc_q <= '0;
mstack_cause_q <= '0;
end else begin
// update CSRs
priv_lvl_q <= priv_lvl_d;
mstatus_q <= mstatus_d;
mie_q <= mie_d;
mscratch_q <= mscratch_d;
@ -596,7 +628,12 @@ module ibex_cs_registers #(
end
end
assign priv_mode_o = mstatus_q.mpp;
// Send current priv level to the decoder
assign priv_mode_id_o = priv_lvl_q;
// New instruction fetches need to account for updates to priv_lvl_q this cycle
assign priv_mode_if_o = priv_lvl_d;
// Load/store instructions must factor in MPRV for PMP checking
assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q;
// -----------------
// PMP registers

View file

@ -75,6 +75,8 @@ module ibex_id_stage #(
output logic csr_restore_mret_id_o,
output logic csr_save_cause_o,
output logic [31:0] csr_mtval_o,
input ibex_pkg::priv_lvl_e priv_mode_i,
input logic csr_mstatus_tw_i,
input logic illegal_csr_insn_i,
// Interface to load store unit
@ -438,6 +440,8 @@ module ibex_id_stage #(
.csr_restore_mret_id_o ( csr_restore_mret_id_o ),
.csr_save_cause_o ( csr_save_cause_o ),
.csr_mtval_o ( csr_mtval_o ),
.priv_mode_i ( priv_mode_i ),
.csr_mstatus_tw_i ( csr_mstatus_tw_i ),
// Debug Signal
.debug_mode_o ( debug_mode_o ),

View file

@ -180,6 +180,7 @@ typedef enum logic [5:0] {
EXC_CAUSE_BREAKPOINT = {1'b0, 5'd03},
EXC_CAUSE_LOAD_ACCESS_FAULT = {1'b0, 5'd05},
EXC_CAUSE_STORE_ACCESS_FAULT = {1'b0, 5'd07},
EXC_CAUSE_ECALL_UMODE = {1'b0, 5'd08},
EXC_CAUSE_ECALL_MMODE = {1'b0, 5'd11}
} exc_cause_e;
@ -293,6 +294,8 @@ parameter int unsigned CSR_MSTATUS_MIE_BIT = 3;
parameter int unsigned CSR_MSTATUS_MPIE_BIT = 7;
parameter int unsigned CSR_MSTATUS_MPP_BIT_LOW = 11;
parameter int unsigned CSR_MSTATUS_MPP_BIT_HIGH = 12;
parameter int unsigned CSR_MSTATUS_MPRV_BIT = 17;
parameter int unsigned CSR_MSTATUS_TW_BIT = 21;
// CSR interrupt pending/enable bits
parameter int unsigned CSR_MSIX_BIT = 3;

View file

@ -19,8 +19,7 @@ module ibex_pmp #(
input ibex_pkg::pmp_cfg_t csr_pmp_cfg_i [PMPNumRegions],
input logic [33:0] csr_pmp_addr_i [PMPNumRegions],
input ibex_pkg::priv_lvl_e priv_mode_i, // Current priv mode, assumed to
// be the same for all channels
input ibex_pkg::priv_lvl_e priv_mode_i [PMPNumChan],
// Access checking channels
input logic [33:0] pmp_req_addr_i [PMPNumChan],
input ibex_pkg::pmp_req_e pmp_req_type_i [PMPNumChan],
@ -104,8 +103,8 @@ module ibex_pmp #(
~|region_match_partial[c][r-1:0];
end
end
assign access_fault[c] = (priv_mode_i == PRIV_LVL_M) ? |machine_access_fault[c] :
~|user_access_allowed[c];
assign access_fault[c] = (priv_mode_i[c] == PRIV_LVL_M) ? |machine_access_fault[c] :
~|user_access_allowed[c];
assign pmp_req_err_o[c] = access_fault[c];
end