Rework pipeline flushes and exceptions

WFI is working again, exception controller now only handles exceptions
(untested) and no flushes anymore
This commit is contained in:
Andreas Traber 2015-08-31 10:02:55 +02:00
parent dd57252f60
commit 88b91c20c5
5 changed files with 80 additions and 114 deletions

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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 )
);

View file

@ -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;