Allow nested interrupts and save current value of MSTATUS to MESTATUS

upon entering an interrupt handler
This commit is contained in:
Andreas Traber 2016-02-09 09:21:26 +01:00
parent d99c3c9f24
commit c39e27f3ac
8 changed files with 120 additions and 71 deletions

View file

@ -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

View file

@ -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

View file

@ -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}{

View file

@ -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}.

View file

@ -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;

View file

@ -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 ),

View file

@ -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 ),

View file

@ -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 ();