diff --git a/doc/exception_interrupts.rst b/doc/exception_interrupts.rst index b97a9f07..a4011281 100644 --- a/doc/exception_interrupts.rst +++ b/doc/exception_interrupts.rst @@ -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. diff --git a/rtl/ibex_controller.sv b/rtl/ibex_controller.sv index aae04166..84e047aa 100644 --- a/rtl/ibex_controller.sv +++ b/rtl/ibex_controller.sv @@ -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 diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index eba90238..c4a5acb9 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -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 diff --git a/rtl/ibex_cs_registers.sv b/rtl/ibex_cs_registers.sv index 001a7ed7..6164bf8e 100644 --- a/rtl/ibex_cs_registers.sv +++ b/rtl/ibex_cs_registers.sv @@ -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; diff --git a/rtl/ibex_defines.sv b/rtl/ibex_defines.sv index 7b939fbb..01804ac9 100644 --- a/rtl/ibex_defines.sv +++ b/rtl/ibex_defines.sv @@ -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, diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index 2626b173..58126080 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -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 diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index c5218371..825fece7 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -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