diff --git a/controller.sv b/controller.sv index 06782fc7..d1b9f97b 100644 --- a/controller.sv +++ b/controller.sv @@ -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 diff --git a/cs_registers.sv b/cs_registers.sv index 7c4ea95f..6069003a 100644 --- a/cs_registers.sv +++ b/cs_registers.sv @@ -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 diff --git a/docs/datasheet/content/csr.tex b/docs/datasheet/content/csr.tex index 92dde154..83b598b4 100644 --- a/docs/datasheet/content/csr.tex +++ b/docs/datasheet/content/csr.tex @@ -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}{ diff --git a/docs/datasheet/content/exceptions.tex b/docs/datasheet/content/exceptions.tex index 67eca5ca..4c5d1c53 100644 --- a/docs/datasheet/content/exceptions.tex +++ b/docs/datasheet/content/exceptions.tex @@ -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}. diff --git a/exc_controller.sv b/exc_controller.sv index 02954ac2..22f940e0 100644 --- a/exc_controller.sv +++ b/exc_controller.sv @@ -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; diff --git a/id_stage.sv b/id_stage.sv index 5bd28ffb..588934f7 100644 --- a/id_stage.sv +++ b/id_stage.sv @@ -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 ), diff --git a/riscv_core.sv b/riscv_core.sv index 4299204f..146f0795 100644 --- a/riscv_core.sv +++ b/riscv_core.sv @@ -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 ), diff --git a/riscv_simchecker.sv b/riscv_simchecker.sv index 5b2194db..02863594 100644 --- a/riscv_simchecker.sv +++ b/riscv_simchecker.sv @@ -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 ();