Switch to RISC-V spec compliant trap handling

For exceptions, the Ibex always jumps to the trap vector base address
specified in `mtvec`. The exception cause is specified in `mcause` with
possibly additional information in `mtval`.

Interrupts are handled in vectored mode as before.
This commit is contained in:
Pirmin Vogel 2019-06-27 17:00:07 +01:00
parent e9bdbaddd6
commit 07214f626d
7 changed files with 92 additions and 119 deletions

View file

@ -3,28 +3,17 @@
Exceptions and Interrupts
=========================
Ibex currently implements a free-form vectored trap handler mechanism for interrupts and exceptions.
The base address of the interrupt vector table is given by the boot address (must be aligned to 256 bytes, i.e., its least significant byte must be 0x00).
Ibex implements trap handling for interrupts and exceptions according to the RISC-V Privileged Specification, version 1.11.
All exceptions cause the core to jump to the base address of the vector table in the ``mtvec`` CSR.
Interrupts are handled in vectored mode, i.e., the core jumps to the base address plus four times the interrupt cause number.
The base address of the vector table is given by the boot address (must be aligned to 256 bytes, i.e., its least significant byte must be 0x00).
The most significant 3 bytes of the boot address given to the core are used for the first instruction fetch of the core and as the basis of the interrupt vector table.
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.
The boot address can be changed after the first instruction was fetched to change the interrupt vector table address.
It is assumed that the boot address is supplied via a register to avoid long paths to the instruction fetch unit.
The table below lists the supported interrupts and exceptions and the corresponding address offset in the interrupt vector table.
+------------+-----------------------------+
| Address | Description |
+============+=============================+
| **0x00** - | Interrupts 0 31 |
| **0x7C** | |
+------------+-----------------------------+
| **0x80** | Reset |
+------------+-----------------------------+
| **0x84** | Illegal Instruction |
+------------+-----------------------------+
| **0x88** | ECALL Instruction Executed |
+------------+-----------------------------+
| **0x8C** | LSU Error |
+------------+-----------------------------+
Interrupts
----------
@ -36,9 +25,26 @@ It is assumed that there is a separate event/interrupt controller outside of the
When an interrupt is taken, the core gives an acknowledge signal to the external event/interrupt controller as well as the interrupt ID taken.
Check :ref:`interrupts` for more details.
Exceptions
----------
Ibex can trigger an exception due to the following exception causes:
+----------------+---------------------------------------------------------------+
| Exception Code | Description |
+----------------+---------------------------------------------------------------+
| 2 | Illegal instruction |
+----------------+---------------------------------------------------------------+
| 3 | Breakpoint |
+----------------+---------------------------------------------------------------+
| 5 | Load access fault |
+----------------+---------------------------------------------------------------+
| 7 | Store access fault |
+----------------+---------------------------------------------------------------+
| 11 | Environment call from M-mode (ECALL) |
+----------------+---------------------------------------------------------------+
The illegal instruction exception, LSU error exceptions and ECALL instruction exceptions cannot be disabled and are always active.

View file

@ -98,7 +98,6 @@ module ibex_controller (
output logic csr_restore_mret_id_o,
output logic csr_restore_dret_id_o,
output logic csr_save_cause_o,
output ibex_defines::exc_cause_e csr_cause_o,
output logic [31:0] csr_mtval_o,
// stall signals
@ -160,15 +159,14 @@ module ibex_controller (
csr_restore_mret_id_o = 1'b0;
csr_restore_dret_id_o = 1'b0;
csr_save_cause_o = 1'b0;
csr_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00
csr_mtval_o = '0;
exc_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00
exc_pc_mux_o = EXC_PC_IRQ;
pc_mux_o = PC_BOOT;
pc_set_o = 1'b0;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00
ctrl_fsm_ns = ctrl_fsm_cs;
ctrl_busy_o = 1'b1;
@ -319,15 +317,13 @@ module ibex_controller (
end
IRQ_TAKEN: begin
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_IRQ;
exc_cause_o = exc_cause_e'({1'b0, irq_id_ctrl_i});
exc_cause_o = exc_cause_e'({1'b1, irq_id_ctrl_i});
csr_save_cause_o = 1'b1;
csr_cause_o = exc_cause_e'({1'b1, irq_id_ctrl_i});
csr_save_if_o = 1'b1;
irq_ack_o = 1'b1;
@ -340,7 +336,7 @@ module ibex_controller (
DBG_TAKEN_IF:
begin
// Jump to debug exception handler in debug memory
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBD;
@ -372,7 +368,7 @@ module ibex_controller (
// not to the next instruction's (which is why we save the pc in id).
DBG_TAKEN_ID: begin
// Jump to debug exception handler in debug memory
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBD;
@ -414,27 +410,25 @@ module ibex_controller (
unique case(1'b1)
ecall_insn_i: begin
//ecall
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_EXC;
exc_cause_o = EXC_CAUSE_ECALL_MMODE;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = EXC_PC_ECALL;
exc_cause_o = EXC_CAUSE_ECALL_MMODE;
csr_cause_o = EXC_CAUSE_ECALL_MMODE;
end
illegal_insn_i: begin
//exceptions
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
if (debug_mode_q) begin
exc_pc_mux_o = EXC_PC_DBGEXC;
exc_pc_mux_o = EXC_PC_DBG_EXC;
end else begin
exc_pc_mux_o = EXC_PC_ILLINSN;
exc_pc_mux_o = EXC_PC_EXC;
end
exc_cause_o = EXC_CAUSE_ILLEGAL_INSN;
csr_cause_o = EXC_CAUSE_ILLEGAL_INSN;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
csr_mtval_o = instr_is_compressed_i ? {16'b0, instr_compressed_i} : instr_i;
end
mret_insn_i: begin
@ -478,33 +472,30 @@ module ibex_controller (
* ECALL or EBREAK instruction itself, not the address of the
* following instruction." (Privileged Spec, p. 40)
*/
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_EXC;
exc_cause_o = EXC_CAUSE_BREAKPOINT;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = EXC_PC_BREAKPOINT;
exc_cause_o = EXC_CAUSE_BREAKPOINT;
csr_cause_o = EXC_CAUSE_BREAKPOINT;
end
end
load_err_q: begin
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_EXC;
exc_cause_o = EXC_CAUSE_LOAD_ACCESS_FAULT;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = EXC_PC_LOAD;
exc_cause_o = EXC_CAUSE_LOAD_ACCESS_FAULT;
csr_cause_o = EXC_CAUSE_LOAD_ACCESS_FAULT;
csr_mtval_o = lsu_addr_last_i;
end
store_err_q: begin
pc_mux_o = PC_EXCEPTION;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_EXC;
exc_cause_o = EXC_CAUSE_STORE_ACCESS_FAULT;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = EXC_PC_STORE;
exc_cause_o = EXC_CAUSE_STORE_ACCESS_FAULT;
csr_cause_o = EXC_CAUSE_STORE_ACCESS_FAULT;
csr_mtval_o = lsu_addr_last_i;
end

View file

@ -119,7 +119,6 @@ module ibex_core #(
logic clear_instr_valid;
logic pc_set;
pc_sel_e pc_mux_id; // Mux selector for next PC
exc_pc_sel_e exc_pc_mux_id; // Mux selector for exception PC
exc_cause_e exc_cause; // Exception cause
@ -197,16 +196,15 @@ module ibex_core #(
// Interrupts
logic m_irq_enable;
logic [31:0] mepc, depc;
logic [31:0] csr_mepc, csr_depc;
logic csr_save_if;
logic csr_save_id;
logic csr_restore_mret_id;
logic csr_restore_dret_id;
logic csr_save_cause;
exc_cause_e csr_cause;
logic [31:0] csr_mtval;
logic [31:0] csr_mtvec;
logic [31:0] csr_mtval;
// debug mode and dcsr configuration
dbg_cause_e debug_cause;
@ -325,15 +323,16 @@ module ibex_core #(
// control signals
.clear_instr_valid_i ( clear_instr_valid ),
.pc_set_i ( pc_set ),
.exception_pc_reg_i ( mepc ), // exception return address
.depc_i ( depc ), // debug return address
.pc_mux_i ( pc_mux_id ), // sel for pc multiplexer
.pc_mux_i ( pc_mux_id ),
.exc_pc_mux_i ( exc_pc_mux_id ),
.exc_vec_pc_mux_i ( exc_cause ),
.exc_cause ( exc_cause ),
// Jump targets
// jump targets
.jump_target_ex_i ( jump_target_ex ),
// CSRs
.csr_mepc_i ( csr_mepc ), // exception return address
.csr_depc_i ( csr_depc ), // debug return address
.csr_mtvec_o ( csr_mtvec ), // trap-vector base address
// pipeline stalls
@ -414,7 +413,6 @@ module ibex_core #(
.csr_restore_mret_id_o ( csr_restore_mret_id ), // control signal to restore pc
.csr_restore_dret_id_o ( csr_restore_dret_id ), // control signal to restore pc
.csr_save_cause_o ( csr_save_cause ),
.csr_cause_o ( csr_cause ),
.csr_mtval_o ( csr_mtval ),
.illegal_csr_insn_i ( illegal_csr_insn_id ),
@ -583,12 +581,12 @@ module ibex_core #(
// Interrupt related control signals
.m_irq_enable_o ( m_irq_enable ),
.mepc_o ( mepc ),
.csr_mepc_o ( csr_mepc ),
// debug
.csr_depc_o ( csr_depc ),
.debug_cause_i ( debug_cause ),
.debug_csr_save_i ( debug_csr_save ),
.depc_o ( depc ),
.debug_single_step_o ( debug_single_step ),
.debug_ebreakm_o ( debug_ebreakm ),
@ -600,9 +598,9 @@ module ibex_core #(
.csr_restore_mret_i ( csr_restore_mret_id ),
.csr_restore_dret_i ( csr_restore_dret_id ),
.csr_save_cause_i ( csr_save_cause ),
.csr_cause_i ( csr_cause ),
.csr_mtval_i ( csr_mtval ),
.csr_mtvec_i ( csr_mtvec ),
.csr_mcause_i ( exc_cause ),
.csr_mtval_i ( csr_mtval ),
.illegal_csr_insn_o ( illegal_csr_insn_id ),
// performance counter related signals

View file

@ -48,12 +48,12 @@ module ibex_cs_registers #(
// Interrupts
output logic m_irq_enable_o,
output logic [31:0] mepc_o,
output logic [31:0] csr_mepc_o,
// debug
input ibex_defines::dbg_cause_e debug_cause_i,
input logic debug_csr_save_i,
output logic [31:0] depc_o,
output logic [31:0] csr_depc_o,
output logic debug_single_step_o,
output logic debug_ebreakm_o,
@ -65,9 +65,9 @@ module ibex_cs_registers #(
input logic csr_restore_mret_i,
input logic csr_restore_dret_i,
input logic csr_save_cause_i,
input logic [31:0] csr_mtval_i,
input logic [31:0] csr_mtvec_i,
input ibex_defines::exc_cause_e csr_cause_i,
input ibex_defines::exc_cause_e csr_mcause_i,
input logic [31:0] csr_mtval_i,
output logic illegal_csr_insn_o, // access to non-existent CSR,
// with wrong priviledge level, or
@ -421,7 +421,7 @@ module ibex_cs_registers #(
mstatus_n.mie = 1'b0;
mstatus_n.mpp = PRIV_LVL_M;
mepc_n = exception_pc;
mcause_n = {csr_cause_i};
mcause_n = {csr_mcause_i};
mtval_n = csr_mtval_i;
end
end //csr_save_cause_i
@ -465,9 +465,9 @@ module ibex_cs_registers #(
assign csr_rdata_o = csr_rdata_int;
// directly output some registers
assign m_irq_enable_o = mstatus_q.mie;
assign mepc_o = mepc_q;
assign depc_o = depc_q;
assign m_irq_enable_o = mstatus_q.mie;
assign csr_mepc_o = mepc_q;
assign csr_depc_o = depc_q;
assign debug_single_step_o = dcsr_q.step;
assign debug_ebreakm_o = dcsr_q.ebreakm;

View file

@ -162,21 +162,17 @@ typedef enum logic [2:0] {
typedef enum logic [2:0] {
PC_BOOT,
PC_JUMP,
PC_EXCEPTION,
PC_EXC,
PC_ERET,
PC_DRET
} pc_sel_e;
// Exception PC mux selection
typedef enum logic [2:0] {
EXC_PC_ILLINSN,
EXC_PC_ECALL,
EXC_PC_LOAD,
EXC_PC_STORE,
typedef enum logic [1:0] {
EXC_PC_EXC,
EXC_PC_IRQ,
EXC_PC_DBD,
EXC_PC_DBGEXC, // Exception while in debug mode
EXC_PC_BREAKPOINT
EXC_PC_DBG_EXC // Exception while in debug mode
} exc_pc_sel_e;
// Exception cause
@ -189,21 +185,6 @@ typedef enum logic [5:0] {
EXC_CAUSE_ECALL_MMODE = 6'h0B
} exc_cause_e;
// Exceptions offsets
// target address = {boot_addr[31:8], EXC_OFF} (boot_addr must be 32 BYTE aligned!)
// offset 00 to 7e is used for external interrupts
// TODO: The behavior below follows an outdated (pre-1.10) RISC-V Privileged
// Spec to implement a "free-form" vectored trap handler.
// We need to update this code and crt0.S to follow the new mtvec spec.
typedef enum logic [7:0] {
EXC_OFF_RST = 8'h80,
EXC_OFF_ILLINSN = 8'h84,
EXC_OFF_ECALL = 8'h88,
EXC_OFF_LSUERR = 8'h8c,
EXC_OFF_BREAKPOINT = 8'h90
} exc_off_e;
// Debug cause
typedef enum logic [2:0] {
DBG_CAUSE_NONE = 3'h0,

View file

@ -98,7 +98,6 @@ module ibex_id_stage #(
output logic csr_restore_mret_id_o,
output logic csr_restore_dret_id_o,
output logic csr_save_cause_o,
output ibex_defines::exc_cause_e csr_cause_o,
output logic [31:0] csr_mtval_o,
input logic illegal_csr_insn_i,
@ -528,7 +527,6 @@ module ibex_id_stage #(
.csr_restore_mret_id_o ( csr_restore_mret_id_o ),
.csr_restore_dret_id_o ( csr_restore_dret_id_o ),
.csr_save_cause_o ( csr_save_cause_o ),
.csr_cause_o ( csr_cause_o ),
.csr_mtval_o ( csr_mtval_o ),
// Debug Signal

View file

@ -63,13 +63,13 @@ module ibex_if_stage #(
// Forwarding ports - control signals
input logic clear_instr_valid_i, // clear instr valid bit in IF-ID
input logic pc_set_i, // set the PC to a new value
input logic [31:0] exception_pc_reg_i, // PC to restore after handling
input logic [31:0] csr_mepc_i, // PC to restore after handling
// the interrupt/exception
input logic [31:0] depc_i, // PC to restore after handling
input logic [31:0] csr_depc_i, // PC to restore after handling
// the debug request
input ibex_defines::pc_sel_e pc_mux_i, // selector for PC multiplexer
input ibex_defines::exc_pc_sel_e exc_pc_mux_i, // selects ISR address
input ibex_defines::exc_cause_e exc_vec_pc_mux_i, // selects ISR address for
input ibex_defines::exc_cause_e exc_cause, // selects ISR address for
// vectorized interrupt lines
// jump and branch target and decision
@ -105,37 +105,36 @@ module ibex_if_stage #(
logic [31:0] exc_pc;
logic [5:0] irq_id;
logic unused_irq_bit;
// extract interrupt ID from exception cause
assign irq_id = {exc_cause};
assign unused_irq_bit = irq_id[5]; // MSB distinguishes interrupts from exceptions
// trap-vector base address, mtvec.MODE set to vectored
assign csr_mtvec_o = {boot_addr_i[31:8], 6'b0, 2'b01};
// exception PC selection mux
always_comb begin : exc_pc_mux
// TODO: The behavior below follows an outdated (pre-1.10) RISC-V Privileged
// Spec to implement a "free-form" vectored trap handler.
// We need to update this code and crt0.S to follow the new mtvec spec.
unique case (exc_pc_mux_i)
EXC_PC_ILLINSN: exc_pc = { boot_addr_i[31:8], {EXC_OFF_ILLINSN} };
EXC_PC_ECALL: exc_pc = { boot_addr_i[31:8], {EXC_OFF_ECALL} };
EXC_PC_LOAD: exc_pc = { boot_addr_i[31:8], {EXC_OFF_LSUERR} };
EXC_PC_STORE: exc_pc = { boot_addr_i[31:8], {EXC_OFF_LSUERR} };
EXC_PC_BREAKPOINT: exc_pc = { boot_addr_i[31:8], {EXC_OFF_BREAKPOINT} };
EXC_PC_IRQ: exc_pc = { boot_addr_i[31:8], {exc_vec_pc_mux_i}, 2'b0 };
EXC_PC_DBD: exc_pc = DmHaltAddr;
EXC_PC_DBGEXC: exc_pc = DmExceptionAddr;
default: exc_pc = 'X;
EXC_PC_EXC: exc_pc = { boot_addr_i[31:8], 8'h00 };
EXC_PC_IRQ: exc_pc = { boot_addr_i[31:8], 1'b0, irq_id[4:0], 2'b00 };
EXC_PC_DBD: exc_pc = DmHaltAddr;
EXC_PC_DBG_EXC: exc_pc = DmExceptionAddr;
default: exc_pc = 'X;
endcase
end
// fetch address selection mux
always_comb begin : fetch_addr_mux
unique case (pc_mux_i)
PC_BOOT: fetch_addr_n = {boot_addr_i[31:8], {EXC_OFF_RST}};
PC_JUMP: fetch_addr_n = jump_target_ex_i;
PC_EXCEPTION: fetch_addr_n = exc_pc; // set PC to exception handler
PC_ERET: fetch_addr_n = exception_pc_reg_i; // PC is restored when returning
// from IRQ/exception
PC_DRET: fetch_addr_n = depc_i;
default: fetch_addr_n = 'X;
PC_BOOT: fetch_addr_n = { boot_addr_i[31:8], 8'h80 };
PC_JUMP: fetch_addr_n = jump_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_DRET: fetch_addr_n = csr_depc_i;
default: fetch_addr_n = 'X;
endcase
end