mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-23 05:27:30 -04:00
Allow nested interrupts and save current value of MSTATUS to MESTATUS
upon entering an interrupt handler
This commit is contained in:
parent
d99c3c9f24
commit
c39e27f3ac
8 changed files with 120 additions and 71 deletions
|
@ -71,7 +71,8 @@ module riscv_controller
|
|||
|
||||
input logic trap_hit_i, // a trap was hit, so we have to flush EX and WB
|
||||
|
||||
output logic save_pc_id_o,
|
||||
output logic exc_save_id_o,
|
||||
output logic exc_restore_id_o,
|
||||
|
||||
// Debug Unit Signals
|
||||
input logic dbg_stall_i, // Pipeline stall is requested
|
||||
|
@ -158,23 +159,24 @@ module riscv_controller
|
|||
always_comb
|
||||
begin
|
||||
// Default values
|
||||
instr_req_o = 1'b1;
|
||||
instr_req_o = 1'b1;
|
||||
|
||||
exc_ack_o = 1'b0;
|
||||
save_pc_id_o = 1'b0;
|
||||
exc_ack_o = 1'b0;
|
||||
exc_save_id_o = 1'b0;
|
||||
exc_restore_id_o = 1'b0;
|
||||
|
||||
pc_mux_o = `PC_BOOT;
|
||||
pc_set_o = 1'b0;
|
||||
jump_done = jump_done_q;
|
||||
pc_mux_o = `PC_BOOT;
|
||||
pc_set_o = 1'b0;
|
||||
jump_done = jump_done_q;
|
||||
|
||||
ctrl_fsm_ns = ctrl_fsm_cs;
|
||||
ctrl_fsm_ns = ctrl_fsm_cs;
|
||||
|
||||
core_busy_o = 1'b1;
|
||||
is_decoding_o = 1'b0;
|
||||
core_busy_o = 1'b1;
|
||||
is_decoding_o = 1'b0;
|
||||
|
||||
halt_if_o = 1'b0;
|
||||
halt_id_o = 1'b0;
|
||||
dbg_trap_o = 1'b0;
|
||||
halt_if_o = 1'b0;
|
||||
halt_id_o = 1'b0;
|
||||
dbg_trap_o = 1'b0;
|
||||
|
||||
unique case (ctrl_fsm_cs)
|
||||
// We were just reset, wait for fetch_enable
|
||||
|
@ -227,7 +229,7 @@ module riscv_controller
|
|||
|
||||
// TODO: This assumes that the pipeline is always flushed before
|
||||
// going to sleep.
|
||||
save_pc_id_o = 1'b1;
|
||||
exc_save_id_o = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -266,7 +268,7 @@ module riscv_controller
|
|||
exc_ack_o = 1'b1;
|
||||
|
||||
halt_id_o = 1'b1; // we don't want to propagate this instruction to EX
|
||||
save_pc_id_o = 1'b1;
|
||||
exc_save_id_o = 1'b1;
|
||||
|
||||
// we don't have to change our current state here as the prefetch
|
||||
// buffer is automatically invalidated, thus the next instruction
|
||||
|
@ -276,8 +278,9 @@ module riscv_controller
|
|||
end
|
||||
|
||||
if (eret_insn_i) begin
|
||||
pc_mux_o = `PC_ERET;
|
||||
pc_set_o = 1'b1;
|
||||
pc_mux_o = `PC_ERET;
|
||||
pc_set_o = 1'b1;
|
||||
exc_restore_id_o = 1'b1;
|
||||
end
|
||||
|
||||
// handle WFI instruction, flush pipeline and (potentially) go to
|
||||
|
|
|
@ -55,10 +55,11 @@ module riscv_cs_registers
|
|||
|
||||
// Interrupts
|
||||
output logic irq_enable_o,
|
||||
output logic [31:0] epcr_o,
|
||||
output logic [31:0] mepc_o,
|
||||
|
||||
input logic [31:0] curr_pc_id_i,
|
||||
input logic save_pc_id_i,
|
||||
input logic exc_save_i,
|
||||
input logic exc_restore_i,
|
||||
|
||||
input logic [5:0] exc_cause_i,
|
||||
input logic save_exc_cause_i,
|
||||
|
@ -115,17 +116,16 @@ module riscv_cs_registers
|
|||
logic is_pcer;
|
||||
logic is_pcmr;
|
||||
|
||||
// Generic CSRs
|
||||
logic [31:0] mepc_q, mepc_n;
|
||||
|
||||
// CSR update logic
|
||||
logic [31:0] csr_wdata_int;
|
||||
logic [31:0] csr_rdata_int;
|
||||
logic csr_we_int;
|
||||
|
||||
// Interrupt control signals
|
||||
logic irq_enable, irq_enable_n;
|
||||
logic [5:0] exc_cause, exc_cause_n;
|
||||
logic [31:0] mepc_q, mepc_n;
|
||||
logic [ 0:0] mestatus_q, mestatus_n;
|
||||
logic [ 0:0] mstatus_q, mstatus_n;
|
||||
logic [ 5:0] exc_cause, exc_cause_n;
|
||||
|
||||
|
||||
////////////////////////////////////////////
|
||||
|
@ -144,7 +144,7 @@ module riscv_cs_registers
|
|||
|
||||
case (csr_addr_i)
|
||||
// mstatus: always M-mode, contains IE bit
|
||||
12'h300: csr_rdata_int = {29'b0, 2'b11, irq_enable};
|
||||
12'h300: csr_rdata_int = {29'b0, 2'b11, mstatus_q};
|
||||
|
||||
// mepc: exception program counter
|
||||
12'h341: csr_rdata_int = mepc_q;
|
||||
|
@ -165,6 +165,8 @@ module riscv_cs_registers
|
|||
12'h7B4: csr_rdata_int = hwlp_start_i[1];
|
||||
12'h7B5: csr_rdata_int = hwlp_end_i[1];
|
||||
12'h7B6: csr_rdata_int = hwlp_cnt_i[1];
|
||||
|
||||
12'h7C0: csr_rdata_int = {29'b0, 2'b11, mestatus_q};
|
||||
endcase
|
||||
end
|
||||
|
||||
|
@ -173,14 +175,15 @@ module riscv_cs_registers
|
|||
always_comb
|
||||
begin
|
||||
mepc_n = mepc_q;
|
||||
irq_enable_n = irq_enable;
|
||||
mestatus_n = mestatus_q;
|
||||
mstatus_n = mstatus_q;
|
||||
exc_cause_n = exc_cause;
|
||||
hwlp_we_o = '0;
|
||||
hwlp_regid_o = '0;
|
||||
|
||||
case (csr_addr_i)
|
||||
// mstatus: IE bit
|
||||
12'h300: if (csr_we_int) irq_enable_n = csr_wdata_int[0];
|
||||
12'h300: if (csr_we_int) mstatus_n = csr_wdata_int[0];
|
||||
|
||||
// mepc: exception program counter
|
||||
12'h341: if (csr_we_int) mepc_n = csr_wdata_int;
|
||||
|
@ -194,7 +197,24 @@ module riscv_cs_registers
|
|||
12'h7B4: if (csr_we_int) begin hwlp_we_o = 3'b001; hwlp_regid_o = 1'b1; end
|
||||
12'h7B5: if (csr_we_int) begin hwlp_we_o = 3'b010; hwlp_regid_o = 1'b1; end
|
||||
12'h7B6: if (csr_we_int) begin hwlp_we_o = 3'b100; hwlp_regid_o = 1'b1; end
|
||||
|
||||
// mestatus: machine exception status
|
||||
12'h7C0: if (csr_we_int) mestatus_n = csr_wdata_int[0];
|
||||
endcase
|
||||
|
||||
// exception controller gets priority over other writes
|
||||
if (exc_save_i) begin
|
||||
mepc_n = curr_pc_id_i;
|
||||
mestatus_n = mstatus_q;
|
||||
mstatus_n = 1'b0;
|
||||
end
|
||||
|
||||
if (save_exc_cause_i)
|
||||
exc_cause_n = exc_cause_i;
|
||||
|
||||
if (exc_restore_i) begin
|
||||
mstatus_n = mestatus_q;
|
||||
end
|
||||
end
|
||||
|
||||
assign hwlp_data_o = csr_wdata_int;
|
||||
|
@ -231,8 +251,8 @@ module riscv_cs_registers
|
|||
|
||||
|
||||
// directly output some registers
|
||||
assign irq_enable_o = irq_enable;
|
||||
assign epcr_o = mepc_q;
|
||||
assign irq_enable_o = mstatus_q[0];
|
||||
assign mepc_o = mepc_q;
|
||||
|
||||
|
||||
// actual registers
|
||||
|
@ -240,25 +260,20 @@ module riscv_cs_registers
|
|||
begin
|
||||
if (rst_n == 1'b0)
|
||||
begin
|
||||
irq_enable <= 1'b0;
|
||||
mstatus_q <= '0;
|
||||
mepc_q <= '0;
|
||||
mestatus_q <= '0;
|
||||
exc_cause <= '0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// update CSRs
|
||||
irq_enable <= irq_enable_n;
|
||||
mstatus_q <= mstatus_n;
|
||||
|
||||
// exception controller gets priority over other writes
|
||||
if (save_pc_id_i == 1'b1)
|
||||
mepc_q <= curr_pc_id_i;
|
||||
else
|
||||
mepc_q <= mepc_n;
|
||||
mepc_q <= mepc_n;
|
||||
mestatus_q <= mestatus_n;
|
||||
|
||||
if (save_exc_cause_i)
|
||||
exc_cause <= exc_cause_i;
|
||||
else
|
||||
exc_cause <= exc_cause_n;
|
||||
exc_cause <= exc_cause_n;
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,9 +18,10 @@ as possible and avoid any overhead that we do not explicitely need.
|
|||
00 & 11 & 01 & 000001 & 0x341 & MEPC & R/W & Machine exception program counter \\ \hline
|
||||
00 & 11 & 01 & 000010 & 0x342 & MCAUSE & R/W & Machine trap cause \\ \hline
|
||||
01 & 11 & 00 & 0XXXXX & 0x780 - 0x79F & PCCRs & R/W & Performance Counter Counter Registers \\ \hline
|
||||
01 & 11 & 10 & 200000 & 0x7A0 & PCER & R/W & Performance Counter Enable Register \\ \hline
|
||||
01 & 11 & 10 & 100000 & 0x7A0 & PCER & R/W & Performance Counter Enable Register \\ \hline
|
||||
01 & 11 & 10 & 100001 & 0x7A1 & PCMR & R/W & Performance Counter Mode Register \\ \hline
|
||||
01 & 11 & 10 & 110XXX & 0x7B0 - 0x7B6 & HWLP & R/W & Hardware Loop Registers \\ \hline
|
||||
01 & 11 & 10 & 111000 & 0x7C0 & MESTATUS & R/W & Machine exception Status Register \\ \hline
|
||||
11 & 11 & 00 & 000000 & 0xF00 & MCPUID & R & CPU description \\ \hline
|
||||
11 & 11 & 00 & 000001 & 0xF01 & MIMPID & R & Vendor ID and version number \\ \hline
|
||||
11 & 11 & 00 & 010000 & 0xF10 & MHARTID & R & Hardware Thread ID \\ \bottomrule
|
||||
|
@ -41,6 +42,28 @@ as possible and avoid any overhead that we do not explicitely need.
|
|||
}
|
||||
|
||||
Note that \signal{PRV[1:0]} is statically \signal{2'b11} and cannot be altered (read-only).
|
||||
When en exception is encountered, \signal{Interrupt Enable} will be set to
|
||||
\signal{1'b0}. When the \signal{eret} instruction is executed, the original
|
||||
value of \signal{Interrupt Enable} will be restored, as \signal{MESTATUS} will
|
||||
replace \signal{MSTATUS}.
|
||||
If you want to enable interrupt handling in your exception hanlder, set the
|
||||
\signal{Interrupt Enable} to \signal{1'b1} inside your handler code.
|
||||
|
||||
\subsection{MESTATUS}
|
||||
\csrDesc{0x7C0}{0x0000\_0006}{MESTATUS}{
|
||||
\begin{bytefield}[endianness=big,bitheight=60pt]{32}
|
||||
\bitheader{31,2,1,0} \\
|
||||
\bitbox{29}{ Unused }
|
||||
\bitbox{2}{\rotatebox{90}{\tiny PRV[1:0] }}
|
||||
\bitbox{1}{\rotatebox{90}{\tiny Interrupt Enable }}
|
||||
\end{bytefield}
|
||||
}
|
||||
|
||||
Note that \signal{PRV[1:0]} is statically \signal{2'b11} and cannot be altered (read-only).
|
||||
|
||||
When an exception is encountered, the current value of \signal{MSTATUS} is saved
|
||||
in \signal{MESTATUS}. When an \instr{eret} instruction is executed, the value
|
||||
from \signal{MESTATUS} replaces the \signal{MSTATUS} register.
|
||||
|
||||
|
||||
\subsection{MEPC}
|
||||
|
@ -52,9 +75,9 @@ Note that \signal{PRV[1:0]} is statically \signal{2'b11} and cannot be altered (
|
|||
}
|
||||
|
||||
When an exception is encountered, the current program counter is saved in
|
||||
\signal{mepc} and the core jumps to the exception address.
|
||||
When an \instr{eret} instruction is executed, the value from \signal{mepc}
|
||||
replaces the current program counter.
|
||||
\signal{MEPC}, and the core jumps to the exception address. When an \instr{eret}
|
||||
instruction is executed, the value from \signal{MEPC} replaces the current
|
||||
program counter.
|
||||
|
||||
\subsection{MCAUSE}
|
||||
\csrDesc{0x341}{0x0000\_0000}{MCAUSE}{
|
||||
|
|
|
@ -29,7 +29,7 @@ exception/interrupt.
|
|||
interrupt lines. Interrupts can only be enabled/disabled on a global basis and
|
||||
not individually. It is assumed that there is an event/interrupt controller
|
||||
outside of the core that performs masking and buffering of the interrupt lines.
|
||||
The global interrupt enable is done via the CSR register \signal{mstatus}.
|
||||
The global interrupt enable is done via the CSR register \signal{MSTATUS}.
|
||||
|
||||
\section{Exceptions}
|
||||
|
||||
|
@ -38,16 +38,21 @@ exceptions and ecall instruction exceptions can not be disabled and are always
|
|||
active.
|
||||
|
||||
The illegal instruction exception and the load and store invalid memory access
|
||||
exceptions are precise exceptions, i.e. the value of \signal{mepc} will be the
|
||||
exceptions are precise exceptions, i.e. the value of \signal{MEPC} will be the
|
||||
instruction address that caused it.
|
||||
|
||||
\section{Handling}
|
||||
|
||||
\rvcore does not support nested interrupt/exception handling. Exceptions inside
|
||||
interrupt/exception handlers are ignored and thus a user must ensure that such
|
||||
a situation does not happen, as otherwise the behaviour is undefined.
|
||||
\rvcore does support nested interrupt/exception handling. Exceptions inside
|
||||
interrupt/exception handlers cause another exception, thus exceptions during the
|
||||
critical part of your exception handlers, i.e. before having saved the
|
||||
\signal{MEPC} and \signal{MESTATUS} registers, will cause those register to be
|
||||
overwritten.
|
||||
Interrupts during interrupt/exception handlers are disabled by default, but can
|
||||
be explicitely enabled if desired.
|
||||
|
||||
Upon executing an \instr{eret} instruction, the core jumps to the program
|
||||
counter saved in the CSR register \signal{mepc}. When entering an
|
||||
interrupt/exception handler, the core sets \signal{mepc} to the current program
|
||||
counter.
|
||||
counter saved in the CSR register \signal{MEPC} and restores the value of
|
||||
register \signal{MESTATUS} to \signal{MSTATUS}. When entering an
|
||||
interrupt/exception handler, the core sets \signal{MEPC} to the current program
|
||||
counter and saves the current value of \signal{MSTATUS} in \signal{MESTATUS}.
|
||||
|
|
|
@ -64,7 +64,7 @@ module riscv_exc_controller
|
|||
);
|
||||
|
||||
|
||||
enum logic [1:0] { IDLE, WAIT_CONTROLLER, IN_ISR } exc_ctrl_cs, exc_ctrl_ns;
|
||||
enum logic [0:0] { IDLE, WAIT_CONTROLLER } exc_ctrl_cs, exc_ctrl_ns;
|
||||
|
||||
logic req_int;
|
||||
logic [1:0] pc_mux_int, pc_mux_int_q;
|
||||
|
@ -173,7 +173,7 @@ module riscv_exc_controller
|
|||
|
||||
if (ack_i) begin
|
||||
save_cause_o = 1'b1;
|
||||
exc_ctrl_ns = IN_ISR;
|
||||
exc_ctrl_ns = IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -184,16 +184,10 @@ module riscv_exc_controller
|
|||
|
||||
if (ack_i) begin
|
||||
save_cause_o = 1'b1;
|
||||
exc_ctrl_ns = IN_ISR;
|
||||
exc_ctrl_ns = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
IN_ISR:
|
||||
begin
|
||||
if (eret_insn_i)
|
||||
exc_ctrl_ns = IDLE;
|
||||
end
|
||||
|
||||
default:
|
||||
begin
|
||||
exc_ctrl_ns = IDLE;
|
||||
|
|
|
@ -140,7 +140,8 @@ module riscv_id_stage
|
|||
output logic [5:0] exc_cause_o,
|
||||
output logic save_exc_cause_o,
|
||||
|
||||
output logic save_pc_id_o,
|
||||
output logic exc_save_id_o,
|
||||
output logic exc_restore_id_o,
|
||||
|
||||
input logic lsu_load_err_i,
|
||||
input logic lsu_store_err_i,
|
||||
|
@ -714,7 +715,8 @@ module riscv_id_stage
|
|||
.exc_ack_o ( exc_ack ),
|
||||
.trap_hit_i ( trap_hit ),
|
||||
|
||||
.save_pc_id_o ( save_pc_id_o ),
|
||||
.exc_save_id_o ( exc_save_id_o ),
|
||||
.exc_restore_id_o ( exc_restore_id_o ),
|
||||
|
||||
// Debug Unit Signals
|
||||
.dbg_stall_i ( dbg_stall_i ),
|
||||
|
|
|
@ -190,11 +190,12 @@ module riscv_core
|
|||
|
||||
// Interrupts
|
||||
logic irq_enable;
|
||||
logic [31:0] epcr;
|
||||
logic [31:0] mepc;
|
||||
|
||||
logic [5:0] exc_cause;
|
||||
logic save_exc_cause;
|
||||
logic save_pc_id;
|
||||
logic exc_save_id;
|
||||
logic exc_restore_id;
|
||||
|
||||
|
||||
// Hardware loop controller signals
|
||||
|
@ -280,7 +281,7 @@ module riscv_core
|
|||
// control signals
|
||||
.clear_instr_valid_i ( clear_instr_valid ),
|
||||
.pc_set_i ( pc_set ),
|
||||
.exception_pc_reg_i ( epcr ), // exception return address
|
||||
.exception_pc_reg_i ( mepc ), // exception return address
|
||||
.pc_mux_i ( pc_mux_id ), // sel for pc multiplexer
|
||||
.exc_pc_mux_i ( exc_pc_mux_id ),
|
||||
.exc_vec_pc_mux_i ( exc_vec_pc_mux_id ),
|
||||
|
@ -425,7 +426,8 @@ module riscv_core
|
|||
.irq_enable_i ( irq_enable ), // global interrupt enable
|
||||
.exc_cause_o ( exc_cause ),
|
||||
.save_exc_cause_o ( save_exc_cause ),
|
||||
.save_pc_id_o ( save_pc_id ), // control signal to save pc
|
||||
.exc_save_id_o ( exc_save_id ), // control signal to save pc
|
||||
.exc_restore_id_o ( exc_restore_id ), // control signal to restore pc
|
||||
.lsu_load_err_i ( lsu_load_err ),
|
||||
.lsu_store_err_i ( lsu_store_err ),
|
||||
|
||||
|
@ -611,10 +613,11 @@ module riscv_core
|
|||
|
||||
// Interrupt related control signals
|
||||
.irq_enable_o ( irq_enable ),
|
||||
.epcr_o ( epcr ),
|
||||
.mepc_o ( mepc ),
|
||||
|
||||
.curr_pc_id_i ( current_pc_id ), // from IF stage
|
||||
.save_pc_id_i ( save_pc_id ),
|
||||
.exc_save_i ( exc_save_id ),
|
||||
.exc_restore_i ( exc_restore_id ),
|
||||
|
||||
.exc_cause_i ( exc_cause ),
|
||||
.save_exc_cause_i ( save_exc_cause ),
|
||||
|
@ -775,6 +778,7 @@ module riscv_core
|
|||
.is_compressed ( is_compressed_id ),
|
||||
.id_valid ( id_stage_i.id_valid_o ),
|
||||
.is_decoding ( id_stage_i.is_decoding_o ),
|
||||
.is_illegal ( id_stage_i.illegal_insn_dec ),
|
||||
.pipe_flush ( id_stage_i.controller_i.pipe_flush_i ),
|
||||
|
||||
.ex_valid ( ex_valid ),
|
||||
|
|
|
@ -44,6 +44,7 @@ module riscv_simchecker
|
|||
input logic is_compressed,
|
||||
input logic id_valid,
|
||||
input logic is_decoding,
|
||||
input logic is_illegal,
|
||||
input logic pipe_flush,
|
||||
|
||||
input logic ex_valid,
|
||||
|
@ -239,8 +240,10 @@ module riscv_simchecker
|
|||
while(1) begin
|
||||
@(negedge clk);
|
||||
|
||||
// special case for WFI because we don't wait for unstalling there
|
||||
if ((id_valid && is_decoding) || pipe_flush)
|
||||
// - special case for WFI because we don't wait for unstalling there
|
||||
// - special case for illegal instructions, since they will not go through
|
||||
// the pipe
|
||||
if ((id_valid && is_decoding) || pipe_flush || (is_decoding && is_illegal))
|
||||
begin
|
||||
trace = new ();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue