diff --git a/controller.sv b/controller.sv index 1017e648..c042e62d 100644 --- a/controller.sv +++ b/controller.sv @@ -98,10 +98,9 @@ module controller input logic illegal_c_insn_i, // compressed instruction decode failed output logic illegal_insn_o, // illegal instruction encountered output logic trap_insn_o, // trap instruction encountered - output logic pipe_flush_o, // pipe flush requested by controller input logic pc_valid_i, // is the next_pc currently valid? output logic clear_isr_running_o, // an l.rfe instruction was encountered, exit ISR - input logic pipe_flushed_i, // Pipe is flushed + input logic exc_pipe_flush_i, // flush pipeline after exception handling input logic trap_hit_i, // a trap was hit, so we have to flush EX and WB // Debug Unit Signals @@ -138,7 +137,8 @@ module controller ); // FSM state encoding - enum logic [3:0] { RESET, IDLE, FIRST_FETCH, DECODE, BRANCH, BRANCH_DELAY, + enum logic [3:0] { RESET, SLEEP, FIRST_FETCH, DECODE, BRANCH, BRANCH_DELAY, + FLUSH_EX, FLUSH_WB, DBG_FLUSH_EX, DBG_FLUSH_WB, DBG_SIGNAL, DBG_WAIT } ctrl_fsm_cs, ctrl_fsm_ns; logic reg_d_ex_is_reg_a_id; @@ -159,6 +159,8 @@ module controller logic data_req; logic [1:0] jump_in_id; logic [1:0] csr_op; + logic pipe_flush; + logic trap_insn; logic deassert_we; logic lsu_stall; @@ -176,7 +178,8 @@ module controller logic regb_used; logic regc_used; - logic dbg_halt; + logic halt_if; + logic halt_id; logic illegal_insn_int; ///////////////////////////////////////////// @@ -229,8 +232,8 @@ module controller clear_isr_running_o = 1'b0; illegal_insn_int = 1'b0; - trap_insn_o = 1'b0; - pipe_flush_o = 1'b0; + trap_insn = 1'b0; + pipe_flush = 1'b0; rega_used = 1'b0; regb_used = 1'b0; @@ -817,7 +820,7 @@ module controller 32'h00_10_00_73: // EBREAK begin // debugger trap - trap_insn_o = 1'b1; + trap_insn = 1'b1; end 32'h10_00_00_73: // ERET @@ -830,7 +833,7 @@ module controller 32'h10_20_00_73: // WFI begin // flush pipeline - pipe_flush_o = 1'b1; + pipe_flush = 1'b1; end default: @@ -1008,7 +1011,8 @@ module controller core_busy_o = 1'b1; - dbg_halt = 1'b0; + halt_if = 1'b0; + halt_id = 1'b0; dbg_trap_o = 1'b0; illegal_insn_o = 1'b0; @@ -1030,18 +1034,20 @@ module controller ctrl_fsm_ns = FIRST_FETCH; end - IDLE: + // instruction in IF stage is already valid, so just jump to DECODE + // instead of FIRST_FETCH + SLEEP: begin // we begin execution when either fetch_enable is high or an // interrupt has arrived core_busy_o = 1'b0; - instr_req_o = fetch_enable_i || irq_present_i; + instr_req_o = fetch_enable_i || irq_present_i; if (fetch_enable_i || irq_present_i) begin - ctrl_fsm_ns = FIRST_FETCH; + ctrl_fsm_ns = DECODE; end - end // case: IDLE + end // case: SLEEP FIRST_FETCH: begin @@ -1080,9 +1086,14 @@ module controller illegal_insn_o = 1'b1; end - // the pipeline is flushed and we are requested to go to sleep - if ((pipe_flushed_i == 1'b1) && (fetch_enable_i == 1'b0)) - ctrl_fsm_ns = IDLE; + // handle WFI instruction, flush pipeline and (potentially) go to + // sleep + if (pipe_flush || exc_pipe_flush_i) + begin + halt_if = 1'b1; + + ctrl_fsm_ns = FLUSH_EX; + end // take care of debug // branches take two cycles, jumps just one @@ -1090,7 +1101,8 @@ module controller // TODO: there is a bug here, I'm sure of it if(trap_hit_i == 1'b1 && stall_ex_o == 1'b0 && jump_in_id == 2'b0 && jump_in_ex_i == 2'b0) begin - dbg_halt = 1'b1; + halt_if = 1'b1; + halt_id = 1'b1; ctrl_fsm_ns = DBG_FLUSH_EX; end end @@ -1119,7 +1131,8 @@ module controller DBG_FLUSH_EX: begin - dbg_halt = 1'b1; + halt_if = 1'b1; + halt_id = 1'b1; if(stall_ex_o == 1'b0) ctrl_fsm_ns = DBG_FLUSH_WB; @@ -1127,7 +1140,8 @@ module controller DBG_FLUSH_WB: begin - dbg_halt = 1'b1; + halt_if = 1'b1; + halt_id = 1'b1; if(stall_ex_o == 1'b0) ctrl_fsm_ns = DBG_SIGNAL; @@ -1136,7 +1150,8 @@ module controller DBG_SIGNAL: begin dbg_trap_o = 1'b1; - dbg_halt = 1'b1; + halt_if = 1'b1; + halt_id = 1'b1; ctrl_fsm_ns = DBG_WAIT; end @@ -1149,6 +1164,36 @@ module controller if(dbg_stall_i == 1'b0) ctrl_fsm_ns = DECODE; end + + FLUSH_EX: + begin + halt_if = 1'b1; + halt_id = 1'b1; + + if(~stall_ex_o) + ctrl_fsm_ns = FLUSH_WB; + end + + FLUSH_WB: + begin + halt_if = 1'b1; + halt_id = 1'b1; + + if(~stall_wb_o) + begin + if (fetch_enable_i == 1'b0) + ctrl_fsm_ns = SLEEP; + else + begin + // unstall pipeline and continue operation + halt_if = 1'b1; + halt_id = 1'b0; + + if (~stall_id_o) + ctrl_fsm_ns = DECODE; + end + end + end endcase end @@ -1212,6 +1257,7 @@ module controller assign data_we_o = (deassert_we) ? 1'b0 : data_we; assign data_req_o = (deassert_we) ? 1'b0 : data_req; assign csr_op_o = (deassert_we) ? `CSR_OP_NONE : csr_op; + assign trap_insn_o = (deassert_we) ? 1'b0 : trap_insn; assign jump_in_id_o = (deassert_we) ? `BRANCH_NONE : jump_in_id; @@ -1223,8 +1269,8 @@ module controller // we unstall the if_stage if the debug unit wants to set a new // pc, so that the new value gets written into current_pc_if and is // used by the instr_core_interface - stall_if_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | dbg_halt | dbg_stall_i | (~pc_valid_i) | (jump_in_id_o == `BRANCH_COND); - stall_id_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | dbg_halt | dbg_stall_i; + stall_if_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | halt_if | dbg_stall_i | (~pc_valid_i) | (jump_in_id_o == `BRANCH_COND); + stall_id_o = instr_ack_stall | load_stall | jr_stall | lsu_stall | misalign_stall | halt_id | dbg_stall_i; stall_ex_o = instr_ack_stall | lsu_stall | dbg_stall_i; stall_wb_o = lsu_stall | dbg_stall_i; end diff --git a/debug_unit.sv b/debug_unit.sv index 9cb6fcaf..fa82b734 100644 --- a/debug_unit.sv +++ b/debug_unit.sv @@ -51,7 +51,6 @@ module debug_unit output logic stall_core_o, output logic flush_pipe_o, - input logic pipe_flushed_i, input logic trap_i, output logic sp_mux_o, diff --git a/exc_controller.sv b/exc_controller.sv index 9e9894e7..120e658e 100644 --- a/exc_controller.sv +++ b/exc_controller.sv @@ -58,21 +58,20 @@ module exc_controller input logic illegal_insn_i, // Illegal instruction encountered in ID stage input logic trap_insn_i, // Trap instruction encountered in ID stage input logic drop_instruction_i, // If branch prediction went wrong - input logic pipe_flush_i, // pipe flush requested by controller output logic pc_valid_o, // is the PC in the IF stage currently valid? input logic clear_isr_running_i, // exit ISR routine + output logic exc_pipe_flush_o, // flush pipeline and go back to sleep // Debug Unit Signals input logic dbg_flush_pipe_i, // Pipe flush requested - output logic pipe_flushed_o, // Pipe is flushed input logic dbg_st_en_i, // Single-step trace mode enabled input logic [1:0] dbg_dsr_i, // Debug Stop Register output logic trap_hit_o // Software Trap in ID (l.trap or similar stuff) ); // Exception unit state encoding - enum logic [2:0] { Idle, NopDelay, NopDelayIR, NopID, NopEX, NopWB} exc_fsm_cs, exc_fsm_ns; - enum logic [1:0] { ExcNone, ExcIR, ExcFlush, ExcIllegalInsn } exc_reason_p, exc_reason_n, exc_reason; + enum logic [0:0] { Idle, NopDelayIR } exc_fsm_cs, exc_fsm_ns; + enum logic [1:0] { ExcNone, ExcIR, ExcIllegalInsn } exc_reason_p, exc_reason_n, exc_reason; // Registers logic exc_running_p, exc_running_n; @@ -108,7 +107,8 @@ module exc_controller // The decoder also takes care that no nested exceptions are performed always_comb begin - exc_reason = ExcNone; + exc_reason = ExcNone; + exc_pipe_flush_o = 1'b0; if (illegal_insn_i == 1'b1) begin @@ -127,11 +127,6 @@ module exc_controller if (dbg_dsr_i[`DSR_INTE] == 1'b0) exc_reason = ExcIR; end - else if(pipe_flush_i == 1'b1) - begin - // flushing pipeline because of l.psync - exc_reason = ExcFlush; - end else if (clear_isr_running_i == 1'b1) begin // if we receive an l.rfe instruction when we are not in an @@ -144,7 +139,7 @@ module exc_controller // the CPU should go back to sleep if(fetch_enable_i == 1'b0) - exc_reason = ExcFlush; + exc_pipe_flush_o = 1'b1; end else exc_reason = ExcIllegalInsn; @@ -169,7 +164,6 @@ module exc_controller save_pc_if_o = 1'b0; save_pc_id_o = 1'b0; save_sr_o = 1'b0; - pipe_flushed_o = 1'b0; force_nop_o = 1'b0; pc_valid_o = 1'b1; exc_pc_sel_o = 1'b0; @@ -182,26 +176,6 @@ module exc_controller exc_reason_n = exc_reason; unique case (exc_reason_n) - // A flush of the pipeline was requested by the debug - // unit or an l.psync instruction - // execute pending delay slot (l.psync won't have one), - // flush the pipeline and stop - ExcFlush: begin - if (jump_in_id_i == 2'b00) - begin // no delay slot - force_nop_o = 1'b1; - exc_pc_sel_o = 1'b1; - exc_pc_mux_o = `EXC_PC_NO_INCR; - pc_valid_o = 1'b0; - - exc_fsm_ns = NopID; - end - else - begin // delay slot - exc_fsm_ns = NopDelay; - end - end - // an IRQ is present, execute pending delay slots and jump // to the ISR without flushing the pipeline ExcIR: begin @@ -262,54 +236,6 @@ module exc_controller exc_fsm_ns = Idle; end - - // Execute delay slot, start to force NOPs for new instructions - NopDelay: - begin - force_nop_o = 1'b1; - exc_pc_sel_o = 1'b1; - exc_pc_mux_o = `EXC_PC_NO_INCR; - pc_valid_o = 1'b0; - - exc_fsm_ns = NopID; - end - - // First NOP is in ID stage - NopID: - begin - force_nop_o = 1'b1; - exc_pc_sel_o = 1'b1; - exc_pc_mux_o = `EXC_PC_NO_INCR; - pc_valid_o = 1'b0; - - exc_fsm_ns = NopEX; - end - - // First NOP is in EX stage - NopEX: - begin - force_nop_o = 1'b1; - pc_valid_o = 1'b0; - exc_pc_sel_o = 1'b1; - exc_pc_mux_o = `EXC_PC_NO_INCR; - - exc_fsm_ns = NopWB; - end - - // First NOP is in WB stage - // Pipeline is flushed now - NopWB: begin - exc_pc_sel_o = 1'b1; - exc_pc_mux_o = `EXC_PC_NO_INCR; - pipe_flushed_o = 1'b1; - - pc_valid_o = 1'b0; - force_nop_o = 1'b1; - - clear_exc_reason = 1'b1; - exc_fsm_ns = Idle; - end - default: exc_fsm_ns = Idle; endcase // case (exc_fsm_cs) end @@ -340,7 +266,7 @@ module exc_controller begin : EXC_DISPLAY if ( rst_n == 1'b1 ) begin - if (exc_reason_n != ExcNone && exc_reason_n != ExcFlush) + if (exc_reason_n != ExcNone) $display("%t: Entering exception routine.", $time); end end diff --git a/id_stage.sv b/id_stage.sv index ed1e13a1..c68d940a 100644 --- a/id_stage.sv +++ b/id_stage.sv @@ -128,7 +128,6 @@ module id_stage // Debug Unit Signals input logic dbg_flush_pipe_i, - output logic pipe_flushed_o, input logic dbg_st_en_i, input logic [1:0] dbg_dsr_i, input logic dbg_stall_i, @@ -193,9 +192,9 @@ module id_stage logic illegal_c_insn; logic trap_insn; logic trap_hit; - logic pipe_flush; logic pc_valid; logic clear_isr_running; + logic exc_pipe_flush; logic [4:0] regfile_addr_ra_id; @@ -596,11 +595,10 @@ module id_stage .illegal_c_insn_i ( illegal_c_insn ), .illegal_insn_o ( illegal_insn ), .trap_insn_o ( trap_insn ), - .pipe_flush_o ( pipe_flush ), .pc_valid_i ( pc_valid ), .clear_isr_running_o ( clear_isr_running ), - .pipe_flushed_i ( pipe_flushed_o ), .trap_hit_i ( trap_hit ), + .exc_pipe_flush_i ( exc_pipe_flush ), // Debug Unit Signals .dbg_stall_i ( dbg_stall_i ), @@ -687,14 +685,13 @@ module id_stage .illegal_insn_i ( illegal_insn ), .trap_insn_i ( trap_insn ), .drop_instruction_i ( 1'b0 ), - .pipe_flush_i ( pipe_flush ), .pc_valid_o ( pc_valid ), .clear_isr_running_i ( clear_isr_running ), .trap_hit_o ( trap_hit ), + .exc_pipe_flush_o ( exc_pipe_flush ), // Debug Unit Signals .dbg_flush_pipe_i ( dbg_flush_pipe_i ), - .pipe_flushed_o ( pipe_flushed_o ), .dbg_st_en_i ( dbg_st_en_i ), .dbg_dsr_i ( dbg_dsr_i ) ); diff --git a/riscv_core.sv b/riscv_core.sv index e963e615..59623fc7 100644 --- a/riscv_core.sv +++ b/riscv_core.sv @@ -213,7 +213,6 @@ module riscv_core // Debug Unit logic dbg_stall; logic dbg_flush_pipe; - logic pipe_flushed; logic dbg_trap; logic dbg_st_en; // single-step trace mode enabled logic [1:0] dbg_dsr; // Debug Stop Register @@ -404,7 +403,6 @@ module riscv_core // Debug Unit Signals .dbg_flush_pipe_i ( dbg_flush_pipe ), - .pipe_flushed_o ( pipe_flushed ), .dbg_st_en_i ( dbg_st_en ), .dbg_dsr_i ( dbg_dsr ), .dbg_stall_i ( dbg_stall ), @@ -683,7 +681,6 @@ module riscv_core .dbg_dsr_o ( dbg_dsr ), .stall_core_o ( dbg_stall ), .flush_pipe_o ( dbg_flush_pipe ), - .pipe_flushed_i ( pipe_flushed ), .trap_i ( dbg_trap ), // register file access @@ -748,7 +745,8 @@ module riscv_core rs2 = instr[`REG_S2]; rs2_value = id_stage_i.operand_b_fw_id; - if (id_stage_i.stall_id_o == 1'b0 && id_stage_i.controller_i.ctrl_fsm_cs == id_stage_i.controller_i.DECODE) + // special case for WFI because we don't wait for unstalling there + if ((id_stage_i.stall_id_o == 1'b0 && id_stage_i.controller_i.ctrl_fsm_cs == id_stage_i.controller_i.DECODE) || id_stage_i.controller_i.pipe_flush) begin mnemonic = ""; imm = 0;