| module ibex_controller ( |
| clk_i, |
| rst_ni, |
| ctrl_busy_o, |
| illegal_insn_i, |
| ecall_insn_i, |
| mret_insn_i, |
| dret_insn_i, |
| wfi_insn_i, |
| ebrk_insn_i, |
| csr_pipe_flush_i, |
| instr_valid_i, |
| instr_i, |
| instr_compressed_i, |
| instr_is_compressed_i, |
| instr_bp_taken_i, |
| instr_fetch_err_i, |
| instr_fetch_err_plus2_i, |
| pc_id_i, |
| instr_valid_clear_o, |
| id_in_ready_o, |
| controller_run_o, |
| instr_req_o, |
| pc_set_o, |
| pc_set_spec_o, |
| pc_mux_o, |
| nt_branch_mispredict_o, |
| exc_pc_mux_o, |
| exc_cause_o, |
| lsu_addr_last_i, |
| load_err_i, |
| store_err_i, |
| wb_exception_o, |
| id_exception_o, |
| branch_set_i, |
| branch_set_spec_i, |
| branch_not_set_i, |
| jump_set_i, |
| csr_mstatus_mie_i, |
| irq_pending_i, |
| irqs_i, |
| irq_nm_i, |
| nmi_mode_o, |
| debug_req_i, |
| debug_cause_o, |
| debug_csr_save_o, |
| debug_mode_o, |
| debug_single_step_i, |
| debug_ebreakm_i, |
| debug_ebreaku_i, |
| trigger_match_i, |
| csr_save_if_o, |
| csr_save_id_o, |
| csr_save_wb_o, |
| csr_restore_mret_id_o, |
| csr_restore_dret_id_o, |
| csr_save_cause_o, |
| csr_mtval_o, |
| priv_mode_i, |
| csr_mstatus_tw_i, |
| stall_id_i, |
| stall_wb_i, |
| flush_id_o, |
| ready_wb_i, |
| perf_jump_o, |
| perf_tbranch_o |
| ); |
| parameter [0:0] WritebackStage = 0; |
| parameter [0:0] BranchPredictor = 0; |
| input wire clk_i; |
| input wire rst_ni; |
| output reg ctrl_busy_o; |
| input wire illegal_insn_i; |
| input wire ecall_insn_i; |
| input wire mret_insn_i; |
| input wire dret_insn_i; |
| input wire wfi_insn_i; |
| input wire ebrk_insn_i; |
| input wire csr_pipe_flush_i; |
| input wire instr_valid_i; |
| input wire [31:0] instr_i; |
| input wire [15:0] instr_compressed_i; |
| input wire instr_is_compressed_i; |
| input wire instr_bp_taken_i; |
| input wire instr_fetch_err_i; |
| input wire instr_fetch_err_plus2_i; |
| input wire [31:0] pc_id_i; |
| output wire instr_valid_clear_o; |
| output wire id_in_ready_o; |
| output reg controller_run_o; |
| output reg instr_req_o; |
| output reg pc_set_o; |
| output reg pc_set_spec_o; |
| output reg [2:0] pc_mux_o; |
| output reg nt_branch_mispredict_o; |
| output reg [1:0] exc_pc_mux_o; |
| output reg [5:0] exc_cause_o; |
| input wire [31:0] lsu_addr_last_i; |
| input wire load_err_i; |
| input wire store_err_i; |
| output wire wb_exception_o; |
| output wire id_exception_o; |
| input wire branch_set_i; |
| input wire branch_set_spec_i; |
| input wire branch_not_set_i; |
| input wire jump_set_i; |
| input wire csr_mstatus_mie_i; |
| input wire irq_pending_i; |
| input wire [17:0] irqs_i; |
| input wire irq_nm_i; |
| output wire nmi_mode_o; |
| input wire debug_req_i; |
| output reg [2:0] debug_cause_o; |
| output reg debug_csr_save_o; |
| output wire debug_mode_o; |
| input wire debug_single_step_i; |
| input wire debug_ebreakm_i; |
| input wire debug_ebreaku_i; |
| input wire trigger_match_i; |
| output reg csr_save_if_o; |
| output reg csr_save_id_o; |
| output reg csr_save_wb_o; |
| output reg csr_restore_mret_id_o; |
| output reg csr_restore_dret_id_o; |
| output reg csr_save_cause_o; |
| output reg [31:0] csr_mtval_o; |
| input wire [1:0] priv_mode_i; |
| input wire csr_mstatus_tw_i; |
| input wire stall_id_i; |
| input wire stall_wb_i; |
| output wire flush_id_o; |
| input wire ready_wb_i; |
| output reg perf_jump_o; |
| output reg perf_tbranch_o; |
| reg [3:0] ctrl_fsm_cs; |
| reg [3:0] ctrl_fsm_ns; |
| reg nmi_mode_q; |
| reg nmi_mode_d; |
| reg debug_mode_q; |
| reg debug_mode_d; |
| reg load_err_q; |
| wire load_err_d; |
| reg store_err_q; |
| wire store_err_d; |
| reg exc_req_q; |
| wire exc_req_d; |
| reg illegal_insn_q; |
| wire illegal_insn_d; |
| reg instr_fetch_err_prio; |
| reg illegal_insn_prio; |
| reg ecall_insn_prio; |
| reg ebrk_insn_prio; |
| reg store_err_prio; |
| reg load_err_prio; |
| wire stall; |
| reg halt_if; |
| reg retain_id; |
| reg flush_id; |
| wire illegal_dret; |
| wire illegal_umode; |
| wire exc_req_lsu; |
| wire special_req; |
| wire special_req_pc_change; |
| wire special_req_flush_only; |
| wire do_single_step_d; |
| reg do_single_step_q; |
| wire enter_debug_mode_prio_d; |
| reg enter_debug_mode_prio_q; |
| wire enter_debug_mode; |
| wire ebreak_into_debug; |
| wire handle_irq; |
| wire id_wb_pending; |
| reg [3:0] mfip_id; |
| wire unused_irq_timer; |
| wire ecall_insn; |
| wire mret_insn; |
| wire dret_insn; |
| wire wfi_insn; |
| wire ebrk_insn; |
| wire csr_pipe_flush; |
| wire instr_fetch_err; |
| assign load_err_d = load_err_i; |
| assign store_err_d = store_err_i; |
| assign ecall_insn = ecall_insn_i & instr_valid_i; |
| assign mret_insn = mret_insn_i & instr_valid_i; |
| assign dret_insn = dret_insn_i & instr_valid_i; |
| assign wfi_insn = wfi_insn_i & instr_valid_i; |
| assign ebrk_insn = ebrk_insn_i & instr_valid_i; |
| assign csr_pipe_flush = csr_pipe_flush_i & instr_valid_i; |
| assign instr_fetch_err = instr_fetch_err_i & instr_valid_i; |
| assign illegal_dret = dret_insn & ~debug_mode_q; |
| assign illegal_umode = (priv_mode_i != 2'b11) & (mret_insn | (csr_mstatus_tw_i & wfi_insn)); |
| assign illegal_insn_d = ((illegal_insn_i | illegal_dret) | illegal_umode) & (ctrl_fsm_cs != 4'd6); |
| assign exc_req_d = (((ecall_insn | ebrk_insn) | illegal_insn_d) | instr_fetch_err) & (ctrl_fsm_cs != 4'd6); |
| assign exc_req_lsu = store_err_i | load_err_i; |
| assign id_exception_o = exc_req_d; |
| assign special_req_flush_only = wfi_insn | csr_pipe_flush; |
| assign special_req_pc_change = ((mret_insn | dret_insn) | exc_req_d) | exc_req_lsu; |
| assign special_req = special_req_pc_change | special_req_flush_only; |
| assign id_wb_pending = instr_valid_i | ~ready_wb_i; |
| generate |
| if (WritebackStage) begin : g_wb_exceptions |
| always @(*) begin |
| instr_fetch_err_prio = 0; |
| illegal_insn_prio = 0; |
| ecall_insn_prio = 0; |
| ebrk_insn_prio = 0; |
| store_err_prio = 0; |
| load_err_prio = 0; |
| if (store_err_q) |
| store_err_prio = 1'b1; |
| else if (load_err_q) |
| load_err_prio = 1'b1; |
| else if (instr_fetch_err) |
| instr_fetch_err_prio = 1'b1; |
| else if (illegal_insn_q) |
| illegal_insn_prio = 1'b1; |
| else if (ecall_insn) |
| ecall_insn_prio = 1'b1; |
| else if (ebrk_insn) |
| ebrk_insn_prio = 1'b1; |
| end |
| assign wb_exception_o = ((load_err_q | store_err_q) | load_err_i) | store_err_i; |
| end |
| else begin : g_no_wb_exceptions |
| always @(*) begin |
| instr_fetch_err_prio = 0; |
| illegal_insn_prio = 0; |
| ecall_insn_prio = 0; |
| ebrk_insn_prio = 0; |
| store_err_prio = 0; |
| load_err_prio = 0; |
| if (instr_fetch_err) |
| instr_fetch_err_prio = 1'b1; |
| else if (illegal_insn_q) |
| illegal_insn_prio = 1'b1; |
| else if (ecall_insn) |
| ecall_insn_prio = 1'b1; |
| else if (ebrk_insn) |
| ebrk_insn_prio = 1'b1; |
| else if (store_err_q) |
| store_err_prio = 1'b1; |
| else if (load_err_q) |
| load_err_prio = 1'b1; |
| end |
| assign wb_exception_o = 1'b0; |
| end |
| endgenerate |
| assign do_single_step_d = (instr_valid_i ? ~debug_mode_q & debug_single_step_i : do_single_step_q); |
| assign enter_debug_mode_prio_d = (debug_req_i | do_single_step_d) & ~debug_mode_q; |
| assign enter_debug_mode = enter_debug_mode_prio_d | (trigger_match_i & ~debug_mode_q); |
| assign ebreak_into_debug = (priv_mode_i == 2'b11 ? debug_ebreakm_i : (priv_mode_i == 2'b00 ? debug_ebreaku_i : 1'b0)); |
| assign handle_irq = (~debug_mode_q & ~nmi_mode_q) & (irq_nm_i | (irq_pending_i & csr_mstatus_mie_i)); |
| always @(*) begin : gen_mfip_id |
| mfip_id = 4'd0; |
| begin : sv2v_autoblock_1 |
| reg signed [31:0] i; |
| for (i = 14; i >= 0; i = i - 1) |
| if (irqs_i[i]) |
| mfip_id = i[3:0]; |
| end |
| end |
| assign unused_irq_timer = irqs_i[16]; |
| function automatic [5:0] sv2v_cast_6; |
| input reg [5:0] inp; |
| sv2v_cast_6 = inp; |
| endfunction |
| always @(*) begin |
| instr_req_o = 1'b1; |
| csr_save_if_o = 1'b0; |
| csr_save_id_o = 1'b0; |
| csr_save_wb_o = 1'b0; |
| csr_restore_mret_id_o = 1'b0; |
| csr_restore_dret_id_o = 1'b0; |
| csr_save_cause_o = 1'b0; |
| csr_mtval_o = 1'sb0; |
| pc_mux_o = 3'd0; |
| pc_set_o = 1'b0; |
| pc_set_spec_o = 1'b0; |
| nt_branch_mispredict_o = 1'b0; |
| exc_pc_mux_o = 2'd1; |
| exc_cause_o = 6'b000000; |
| ctrl_fsm_ns = ctrl_fsm_cs; |
| ctrl_busy_o = 1'b1; |
| halt_if = 1'b0; |
| retain_id = 1'b0; |
| flush_id = 1'b0; |
| debug_csr_save_o = 1'b0; |
| debug_cause_o = 3'h1; |
| debug_mode_d = debug_mode_q; |
| nmi_mode_d = nmi_mode_q; |
| perf_tbranch_o = 1'b0; |
| perf_jump_o = 1'b0; |
| controller_run_o = 1'b0; |
| case (ctrl_fsm_cs) |
| 4'd0: begin |
| instr_req_o = 1'b0; |
| pc_mux_o = 3'd0; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| ctrl_fsm_ns = 4'd1; |
| end |
| 4'd1: begin |
| instr_req_o = 1'b1; |
| pc_mux_o = 3'd0; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| ctrl_fsm_ns = 4'd4; |
| end |
| 4'd2: begin |
| ctrl_busy_o = 1'b0; |
| instr_req_o = 1'b0; |
| halt_if = 1'b1; |
| flush_id = 1'b1; |
| ctrl_fsm_ns = 4'd3; |
| end |
| 4'd3: begin |
| instr_req_o = 1'b0; |
| halt_if = 1'b1; |
| flush_id = 1'b1; |
| if ((((irq_nm_i || irq_pending_i) || debug_req_i) || debug_mode_q) || debug_single_step_i) |
| ctrl_fsm_ns = 4'd4; |
| else |
| ctrl_busy_o = 1'b0; |
| end |
| 4'd4: begin |
| if (id_in_ready_o) |
| ctrl_fsm_ns = 4'd5; |
| if (handle_irq) begin |
| ctrl_fsm_ns = 4'd7; |
| halt_if = 1'b1; |
| end |
| if (enter_debug_mode) begin |
| ctrl_fsm_ns = 4'd8; |
| halt_if = 1'b1; |
| end |
| end |
| 4'd5: begin |
| controller_run_o = 1'b1; |
| pc_mux_o = 3'd1; |
| if (special_req) begin |
| retain_id = 1'b1; |
| if (ready_wb_i | wb_exception_o) |
| ctrl_fsm_ns = 4'd6; |
| end |
| if (branch_set_i || jump_set_i) begin |
| pc_set_o = (BranchPredictor ? ~instr_bp_taken_i : 1'b1); |
| perf_tbranch_o = branch_set_i; |
| perf_jump_o = jump_set_i; |
| end |
| if (BranchPredictor) |
| if (instr_bp_taken_i & branch_not_set_i) |
| nt_branch_mispredict_o = 1'b1; |
| if (branch_set_spec_i || jump_set_i) |
| pc_set_spec_o = (BranchPredictor ? ~instr_bp_taken_i : 1'b1); |
| if ((enter_debug_mode || handle_irq) && (stall || id_wb_pending)) |
| halt_if = 1'b1; |
| if ((!stall && !special_req) && !id_wb_pending) |
| if (enter_debug_mode) begin |
| ctrl_fsm_ns = 4'd8; |
| halt_if = 1'b1; |
| end |
| else if (handle_irq) begin |
| ctrl_fsm_ns = 4'd7; |
| halt_if = 1'b1; |
| end |
| end |
| 4'd7: begin |
| pc_mux_o = 3'd2; |
| exc_pc_mux_o = 2'd1; |
| if (handle_irq) begin |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| csr_save_if_o = 1'b1; |
| csr_save_cause_o = 1'b1; |
| if (irq_nm_i && !nmi_mode_q) begin |
| exc_cause_o = 6'b111111; |
| nmi_mode_d = 1'b1; |
| end |
| else if (irqs_i[14-:15] != 15'b000000000000000) |
| exc_cause_o = sv2v_cast_6({2'b11, mfip_id}); |
| else if (irqs_i[15]) |
| exc_cause_o = 6'b101011; |
| else if (irqs_i[17]) |
| exc_cause_o = 6'b100011; |
| else |
| exc_cause_o = 6'b100111; |
| end |
| ctrl_fsm_ns = 4'd5; |
| end |
| 4'd8: begin |
| pc_mux_o = 3'd2; |
| exc_pc_mux_o = 2'd2; |
| flush_id = 1'b1; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| csr_save_if_o = 1'b1; |
| debug_csr_save_o = 1'b1; |
| csr_save_cause_o = 1'b1; |
| if (trigger_match_i) |
| debug_cause_o = 3'h2; |
| else if (debug_single_step_i) |
| debug_cause_o = 3'h4; |
| else |
| debug_cause_o = 3'h3; |
| debug_mode_d = 1'b1; |
| ctrl_fsm_ns = 4'd5; |
| end |
| 4'd9: begin |
| flush_id = 1'b1; |
| pc_mux_o = 3'd2; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| exc_pc_mux_o = 2'd2; |
| if (ebreak_into_debug && !debug_mode_q) begin |
| csr_save_cause_o = 1'b1; |
| csr_save_id_o = 1'b1; |
| debug_csr_save_o = 1'b1; |
| debug_cause_o = 3'h1; |
| end |
| debug_mode_d = 1'b1; |
| ctrl_fsm_ns = 4'd5; |
| end |
| 4'd6: begin |
| halt_if = 1'b1; |
| flush_id = 1'b1; |
| ctrl_fsm_ns = 4'd5; |
| if ((exc_req_q || store_err_q) || load_err_q) begin |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| pc_mux_o = 3'd2; |
| exc_pc_mux_o = (debug_mode_q ? 2'd3 : 2'd0); |
| if (WritebackStage) begin : g_writeback_mepc_save |
| csr_save_id_o = ~(store_err_q | load_err_q); |
| csr_save_wb_o = store_err_q | load_err_q; |
| end |
| else begin : g_no_writeback_mepc_save |
| csr_save_id_o = 1'b0; |
| end |
| csr_save_cause_o = 1'b1; |
| case (1'b1) |
| instr_fetch_err_prio: begin |
| exc_cause_o = 6'b000001; |
| csr_mtval_o = (instr_fetch_err_plus2_i ? pc_id_i + 32'd2 : pc_id_i); |
| end |
| illegal_insn_prio: begin |
| exc_cause_o = 6'b000010; |
| csr_mtval_o = (instr_is_compressed_i ? {16'b0000000000000000, instr_compressed_i} : instr_i); |
| end |
| ecall_insn_prio: exc_cause_o = (priv_mode_i == 2'b11 ? 6'b001011 : 6'b001000); |
| ebrk_insn_prio: |
| if (debug_mode_q | ebreak_into_debug) begin |
| pc_set_o = 1'b0; |
| pc_set_spec_o = 1'b0; |
| csr_save_id_o = 1'b0; |
| csr_save_cause_o = 1'b0; |
| ctrl_fsm_ns = 4'd9; |
| flush_id = 1'b0; |
| end |
| else |
| exc_cause_o = 6'b000011; |
| store_err_prio: begin |
| exc_cause_o = 6'b000111; |
| csr_mtval_o = lsu_addr_last_i; |
| end |
| load_err_prio: begin |
| exc_cause_o = 6'b000101; |
| csr_mtval_o = lsu_addr_last_i; |
| end |
| default: |
| ; |
| endcase |
| end |
| else if (mret_insn) begin |
| pc_mux_o = 3'd3; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| csr_restore_mret_id_o = 1'b1; |
| if (nmi_mode_q) |
| nmi_mode_d = 1'b0; |
| end |
| else if (dret_insn) begin |
| pc_mux_o = 3'd4; |
| pc_set_o = 1'b1; |
| pc_set_spec_o = 1'b1; |
| debug_mode_d = 1'b0; |
| csr_restore_dret_id_o = 1'b1; |
| end |
| else if (wfi_insn) |
| ctrl_fsm_ns = 4'd2; |
| else if (csr_pipe_flush && handle_irq) |
| ctrl_fsm_ns = 4'd7; |
| if (enter_debug_mode_prio_q && !(ebrk_insn_prio && ebreak_into_debug)) |
| ctrl_fsm_ns = 4'd8; |
| end |
| default: begin |
| instr_req_o = 1'b0; |
| ctrl_fsm_ns = 4'd0; |
| end |
| endcase |
| end |
| assign flush_id_o = flush_id; |
| assign debug_mode_o = debug_mode_q; |
| assign nmi_mode_o = nmi_mode_q; |
| assign stall = stall_id_i | stall_wb_i; |
| assign id_in_ready_o = (~stall & ~halt_if) & ~retain_id; |
| assign instr_valid_clear_o = ~(stall | retain_id) | flush_id; |
| always @(posedge clk_i or negedge rst_ni) begin : update_regs |
| if (!rst_ni) begin |
| ctrl_fsm_cs <= 4'd0; |
| nmi_mode_q <= 1'b0; |
| do_single_step_q <= 1'b0; |
| debug_mode_q <= 1'b0; |
| enter_debug_mode_prio_q <= 1'b0; |
| load_err_q <= 1'b0; |
| store_err_q <= 1'b0; |
| exc_req_q <= 1'b0; |
| illegal_insn_q <= 1'b0; |
| end |
| else begin |
| ctrl_fsm_cs <= ctrl_fsm_ns; |
| nmi_mode_q <= nmi_mode_d; |
| do_single_step_q <= do_single_step_d; |
| debug_mode_q <= debug_mode_d; |
| enter_debug_mode_prio_q <= enter_debug_mode_prio_d; |
| load_err_q <= load_err_d; |
| store_err_q <= store_err_d; |
| exc_req_q <= exc_req_d; |
| illegal_insn_q <= illegal_insn_d; |
| end |
| end |
| endmodule |