blob: fec6b6bf0f476070d41b5a6bfc38f8e8480ff67d [file] [log] [blame]
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