blob: 47576ddc3b3e54fb2842beecb38f35346e9872ab [file] [log] [blame]
module ibex_controller (
clk,
rst_n,
fetch_enable_i,
ctrl_busy_o,
first_fetch_o,
is_decoding_o,
deassert_we_o,
illegal_insn_i,
ecall_insn_i,
mret_insn_i,
dret_insn_i,
pipe_flush_i,
ebrk_insn_i,
csr_status_i,
instr_valid_i,
instr_req_o,
pc_set_o,
pc_mux_o,
exc_pc_mux_o,
data_misaligned_i,
branch_in_id_i,
branch_set_i,
jump_set_i,
instr_multicyle_i,
irq_i,
irq_req_ctrl_i,
irq_id_ctrl_i,
m_IE_i,
irq_ack_o,
irq_id_o,
exc_cause_o,
exc_ack_o,
exc_kill_o,
debug_req_i,
debug_cause_o,
debug_csr_save_o,
debug_single_step_i,
debug_ebreakm_i,
csr_save_if_o,
csr_save_id_o,
csr_cause_o,
csr_restore_mret_id_o,
csr_restore_dret_id_o,
csr_save_cause_o,
operand_a_fw_mux_sel_o,
halt_if_o,
halt_id_o,
id_ready_i,
perf_jump_o,
perf_tbranch_o
);
input wire clk;
input wire rst_n;
input wire fetch_enable_i;
output reg ctrl_busy_o;
output reg first_fetch_o;
output reg is_decoding_o;
output wire deassert_we_o;
input wire illegal_insn_i;
input wire ecall_insn_i;
input wire mret_insn_i;
input wire dret_insn_i;
input wire pipe_flush_i;
input wire ebrk_insn_i;
input wire csr_status_i;
input wire instr_valid_i;
output reg instr_req_o;
output reg pc_set_o;
output reg [2:0] pc_mux_o;
output reg [2:0] exc_pc_mux_o;
input wire data_misaligned_i;
input wire branch_in_id_i;
input wire branch_set_i;
input wire jump_set_i;
input wire instr_multicyle_i;
input wire irq_i;
input wire irq_req_ctrl_i;
input wire [4:0] irq_id_ctrl_i;
input wire m_IE_i;
output reg irq_ack_o;
output reg [4:0] irq_id_o;
output reg [5:0] exc_cause_o;
output reg exc_ack_o;
output reg exc_kill_o;
input wire debug_req_i;
output reg [2:0] debug_cause_o;
output reg debug_csr_save_o;
input wire debug_single_step_i;
input wire debug_ebreakm_i;
output reg csr_save_if_o;
output reg csr_save_id_o;
output reg [5:0] csr_cause_o;
output reg csr_restore_mret_id_o;
output reg csr_restore_dret_id_o;
output reg csr_save_cause_o;
output wire operand_a_fw_mux_sel_o;
output reg halt_if_o;
output reg halt_id_o;
input wire id_ready_i;
output reg perf_jump_o;
output reg perf_tbranch_o;
reg [3:0] ctrl_fsm_cs;
reg [3:0] ctrl_fsm_ns;
reg irq_enable_int;
reg debug_mode_q;
reg debug_mode_n;
always @(negedge clk)
if (is_decoding_o && illegal_insn_i)
$display("%t: Illegal instruction (core %0d) at PC 0x%h: 0x%h", $time, ibex_core.core_id_i, ibex_id_stage.pc_id_i, ibex_id_stage.instr_rdata_i);
localparam [3:0] BOOT_SET = 1;
localparam [3:0] DBG_TAKEN_ID = 9;
localparam [3:0] DBG_TAKEN_IF = 8;
localparam [3:0] DECODE = 5;
localparam [3:0] FIRST_FETCH = 4;
localparam [3:0] FLUSH = 6;
localparam [3:0] IRQ_TAKEN = 7;
localparam [3:0] RESET = 0;
localparam [3:0] SLEEP = 3;
localparam [3:0] WAIT_SLEEP = 2;
localparam [2:0] ibex_defines_DBG_CAUSE_EBREAK = 3'h1;
localparam [2:0] ibex_defines_DBG_CAUSE_HALTREQ = 3'h3;
localparam [2:0] ibex_defines_DBG_CAUSE_STEP = 3'h4;
localparam [5:0] ibex_defines_EXC_CAUSE_BREAKPOINT = 6'h03;
localparam [5:0] ibex_defines_EXC_CAUSE_CLEAR = 6'h00;
localparam [5:0] ibex_defines_EXC_CAUSE_ECALL_MMODE = 6'h0b;
localparam [5:0] ibex_defines_EXC_CAUSE_ILLEGAL_INSN = 6'h02;
localparam [2:0] ibex_defines_EXC_PC_BREAKPOINT = 7;
localparam [2:0] ibex_defines_EXC_PC_DBD = 5;
localparam [2:0] ibex_defines_EXC_PC_DBGEXC = 6;
localparam [2:0] ibex_defines_EXC_PC_ECALL = 1;
localparam [2:0] ibex_defines_EXC_PC_ILLINSN = 0;
localparam [2:0] ibex_defines_EXC_PC_IRQ = 4;
localparam [2:0] ibex_defines_PC_BOOT = 0;
localparam [2:0] ibex_defines_PC_DRET = 4;
localparam [2:0] ibex_defines_PC_ERET = 3;
localparam [2:0] ibex_defines_PC_EXCEPTION = 2;
localparam [2:0] ibex_defines_PC_JUMP = 1;
function automatic [5:0] sv2v_cast_6;
input reg [5:0] inp;
sv2v_cast_6 = inp;
endfunction
always @(*) begin
instr_req_o = 1'b1;
exc_ack_o = 1'b0;
exc_kill_o = 1'b0;
csr_save_if_o = 1'b0;
csr_save_id_o = 1'b0;
csr_restore_mret_id_o = 1'b0;
csr_restore_dret_id_o = 1'b0;
csr_save_cause_o = 1'b0;
exc_cause_o = ibex_defines_EXC_CAUSE_CLEAR;
exc_pc_mux_o = ibex_defines_EXC_PC_IRQ;
csr_cause_o = ibex_defines_EXC_CAUSE_CLEAR;
pc_mux_o = ibex_defines_PC_BOOT;
pc_set_o = 1'b0;
ctrl_fsm_ns = ctrl_fsm_cs;
ctrl_busy_o = 1'b1;
is_decoding_o = 1'b0;
first_fetch_o = 1'b0;
halt_if_o = 1'b0;
halt_id_o = 1'b0;
irq_ack_o = 1'b0;
irq_id_o = irq_id_ctrl_i;
irq_enable_int = m_IE_i;
debug_csr_save_o = 1'b0;
debug_cause_o = ibex_defines_DBG_CAUSE_EBREAK;
debug_mode_n = debug_mode_q;
perf_tbranch_o = 1'b0;
perf_jump_o = 1'b0;
case (ctrl_fsm_cs)
RESET: begin
instr_req_o = 1'b0;
pc_mux_o = ibex_defines_PC_BOOT;
pc_set_o = 1'b1;
if (fetch_enable_i)
ctrl_fsm_ns = BOOT_SET;
end
BOOT_SET: begin
instr_req_o = 1'b1;
pc_mux_o = ibex_defines_PC_BOOT;
pc_set_o = 1'b1;
ctrl_fsm_ns = FIRST_FETCH;
end
WAIT_SLEEP: begin
ctrl_busy_o = 1'b0;
instr_req_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
ctrl_fsm_ns = SLEEP;
end
SLEEP: begin
ctrl_busy_o = 1'b0;
instr_req_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
if (((irq_i || debug_req_i) || debug_mode_q) || debug_single_step_i)
ctrl_fsm_ns = FIRST_FETCH;
end
FIRST_FETCH: begin
first_fetch_o = 1'b1;
if (id_ready_i)
ctrl_fsm_ns = DECODE;
if (irq_req_ctrl_i && irq_enable_int) begin
ctrl_fsm_ns = IRQ_TAKEN;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
end
if (debug_req_i && !debug_mode_q) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
end
end
DECODE: begin
is_decoding_o = 1'b0;
case (1'b1)
debug_req_i && !debug_mode_q: begin
ctrl_fsm_ns = DBG_TAKEN_ID;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
end
((irq_req_ctrl_i && irq_enable_int) && !debug_req_i) && !debug_mode_q: begin
ctrl_fsm_ns = IRQ_TAKEN;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
end
default: begin
exc_kill_o = (irq_req_ctrl_i & ~instr_multicyle_i) & ~branch_in_id_i;
if (instr_valid_i) begin
is_decoding_o = 1'b1;
if (branch_set_i || jump_set_i) begin
pc_mux_o = ibex_defines_PC_JUMP;
pc_set_o = 1'b1;
perf_tbranch_o = branch_set_i;
perf_jump_o = jump_set_i;
end
else if ((((((mret_insn_i || dret_insn_i) || ecall_insn_i) || pipe_flush_i) || ebrk_insn_i) || illegal_insn_i) || csr_status_i) begin
ctrl_fsm_ns = FLUSH;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
end
end
end
endcase
if (debug_single_step_i && !debug_mode_q) begin
halt_if_o = 1'b1;
ctrl_fsm_ns = DBG_TAKEN_IF;
end
end
IRQ_TAKEN: begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
exc_pc_mux_o = ibex_defines_EXC_PC_IRQ;
exc_cause_o = sv2v_cast_6({1'b0, irq_id_ctrl_i});
csr_save_cause_o = 1'b1;
csr_cause_o = sv2v_cast_6({1'b1, irq_id_ctrl_i});
csr_save_if_o = 1'b1;
irq_ack_o = 1'b1;
exc_ack_o = 1'b1;
ctrl_fsm_ns = DECODE;
end
DBG_TAKEN_IF: begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
exc_pc_mux_o = ibex_defines_EXC_PC_DBD;
csr_save_if_o = 1'b1;
debug_csr_save_o = 1'b1;
csr_save_cause_o = 1'b1;
if (debug_single_step_i)
debug_cause_o = ibex_defines_DBG_CAUSE_STEP;
else if (debug_req_i)
debug_cause_o = ibex_defines_DBG_CAUSE_HALTREQ;
else if (ebrk_insn_i)
debug_cause_o = ibex_defines_DBG_CAUSE_EBREAK;
debug_mode_n = 1'b1;
ctrl_fsm_ns = DECODE;
end
DBG_TAKEN_ID: begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
exc_pc_mux_o = ibex_defines_EXC_PC_DBD;
if (((ebrk_insn_i && debug_ebreakm_i) && !debug_mode_q) || (debug_req_i && !debug_mode_q)) begin
csr_save_cause_o = 1'b1;
csr_save_id_o = 1'b1;
debug_csr_save_o = 1'b1;
if (debug_req_i)
debug_cause_o = ibex_defines_DBG_CAUSE_HALTREQ;
else if (ebrk_insn_i)
debug_cause_o = ibex_defines_DBG_CAUSE_EBREAK;
end
debug_mode_n = 1'b1;
ctrl_fsm_ns = DECODE;
end
FLUSH: begin
halt_if_o = 1'b1;
halt_id_o = 1'b1;
if (!pipe_flush_i)
ctrl_fsm_ns = DECODE;
else
ctrl_fsm_ns = WAIT_SLEEP;
case (1'b1)
ecall_insn_i: begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = ibex_defines_EXC_PC_ECALL;
exc_cause_o = ibex_defines_EXC_CAUSE_ECALL_MMODE;
csr_cause_o = ibex_defines_EXC_CAUSE_ECALL_MMODE;
end
illegal_insn_i: begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
if (debug_mode_q)
exc_pc_mux_o = ibex_defines_EXC_PC_DBGEXC;
else
exc_pc_mux_o = ibex_defines_EXC_PC_ILLINSN;
exc_cause_o = ibex_defines_EXC_CAUSE_ILLEGAL_INSN;
csr_cause_o = ibex_defines_EXC_CAUSE_ILLEGAL_INSN;
end
mret_insn_i: begin
pc_mux_o = ibex_defines_PC_ERET;
pc_set_o = 1'b1;
csr_restore_mret_id_o = 1'b1;
end
dret_insn_i: begin
pc_mux_o = ibex_defines_PC_DRET;
pc_set_o = 1'b1;
debug_mode_n = 1'b0;
csr_restore_dret_id_o = 1'b1;
end
ebrk_insn_i:
if (debug_mode_q)
ctrl_fsm_ns = DBG_TAKEN_ID;
else if (debug_ebreakm_i)
ctrl_fsm_ns = DBG_TAKEN_ID;
else begin
pc_mux_o = ibex_defines_PC_EXCEPTION;
pc_set_o = 1'b1;
csr_save_id_o = 1'b1;
csr_save_cause_o = 1'b1;
exc_pc_mux_o = ibex_defines_EXC_PC_BREAKPOINT;
exc_cause_o = ibex_defines_EXC_CAUSE_BREAKPOINT;
csr_cause_o = ibex_defines_EXC_CAUSE_BREAKPOINT;
end
default:
;
endcase
end
default: begin
instr_req_o = 1'b0;
ctrl_fsm_ns = RESET;
end
endcase
end
assign deassert_we_o = ~is_decoding_o | illegal_insn_i;
localparam [0:0] ibex_defines_SEL_MISALIGNED = 1;
localparam [0:0] ibex_defines_SEL_REGFILE = 0;
assign operand_a_fw_mux_sel_o = (data_misaligned_i ? ibex_defines_SEL_MISALIGNED : ibex_defines_SEL_REGFILE);
always @(posedge clk or negedge rst_n) begin : UPDATE_REGS
if (!rst_n) begin
ctrl_fsm_cs <= RESET;
debug_mode_q <= 1'b0;
end
else begin
ctrl_fsm_cs <= ctrl_fsm_ns;
debug_mode_q <= debug_mode_n;
end
end
endmodule