XRET_JUMP:
begin
is_decoding_o = 1'b0;
ctrl_fsm_ns = DECODE;
unique case(1'b1)
mret_dec_i: begin
//mret
pc_mux_o = debug_mode_q ? PC_EXCEPTION : PC_MRET;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBE; // only used if in debug_mode
end
uret_dec_i: begin
//uret
pc_mux_o = debug_mode_q ? PC_EXCEPTION : PC_URET;
pc_set_o = 1'b1;
exc_pc_mux_o = EXC_PC_DBE; // only used if in debug_mode
end
dret_dec_i: begin
//dret
// this case is only reachable while in debug_mode
pc_mux_o = PC_DRET;
pc_set_o = 1'b1;
debug_mode_n = 1'b0;
end
default:;
endcase
if (debug_single_step_i && ~debug_mode_q) begin
ctrl_fsm_ns = DBG_TAKEN_IF;
end
end
// a branch was in ID when trying to go to debug rom. Wait until we can
// determine branch target address (for saving into dpc) before proceeding
DBG_WAIT_BRANCH:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
if (branch_taken_ex_i) begin
// there is a branch in the EX stage that is taken
pc_mux_o = PC_BRANCH;
pc_set_o = 1'b1;
end
ctrl_fsm_ns = DBG_FLUSH;
end
// We enter this state when we encounter
// 1. ebreak during debug mode
// 2. trigger match
// 3. ebreak with forced entry into debug mode (ebreakm or ebreaku set).
// 4. halt request during decode
// Regular ebreak's go through FLUSH_EX and FLUSH_WB.
// For 1. we don't update dcsr and dpc while for 2., 3., & 4. we do
// dpc is set to the address of ebreak and trigger match
// not to the next instruction's (which is why we save the pc in id).
DBG_TAKEN_ID:
begin
is_decoding_o = 1'b0;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_DBD;
// If not in debug mode then save cause and dpc csrs
// else it was an ebreak in debug mode, so don't update csrs
if (~debug_mode_q) begin
csr_save_cause_o = 1'b1;
csr_save_id_o = 1'b1;
debug_csr_save_o = 1'b1;
if (trigger_match_i)
debug_cause_o = DBG_CAUSE_TRIGGER; // pri 4 (highest)
else if (ebrk_force_debug_mode & ebrk_insn_i)
debug_cause_o = DBG_CAUSE_EBREAK; // pri 3
else if (debug_req_entry_q)
debug_cause_o = DBG_CAUSE_HALTREQ;// pri 2 and 1
end
debug_req_entry_n = 1'b0;
ctrl_fsm_ns = DECODE;
debug_mode_n = 1'b1;
end
在XRET_JUMP
、DBG_WAIT_BRANCH
和DBG_TAKEN_ID
这三个状态中,处理器控制器采取了不同的行为来处理特定的指令和调试事件。
XRET_JUMP
在XRET_JUMP
状态中,处理器响应mret
、uret
或dret
指令,这些指令用于从异常或调试模式返回到正常执行流程。
- mret_dec_i:处理
mret
指令,用于从机器模式异常返回。 - uret_dec_i:处理
uret
指令,用于从用户模式异常返回。 - dret_dec_i:处理
dret
指令,用于从调试模式返回。在执行dret
指令后,将关闭调试模式。
如果启用了单步调试模式(debug_single_step_i
),则在完成返回操作后,处理器将进入调试状态(DBG_TAKEN_IF
)。
DBG_WAIT_BRANCH
在DBG_WAIT_BRANCH
状态中,处理器正在等待分支指令的目标地址确定,这是为了正确更新调试寄存器(如dpc)。如果EX阶段有分支指令被采纳(branch_taken_ex_i
),则设置程序计数器(PC)跳转到分支目标地址。完成后,处理器进入DBG_FLUSH
状态,以处理调试事件。
DBG_TAKEN_ID
在DBG_TAKEN_ID
状态中,处理器响应以下事件进入调试模式:
- 在调试模式下遇到
ebreak
指令。 - 触发器匹配。
- 强制进入调试模式(由
ebreakm
或ebreaku
设置)。 - 在解码阶段收到停止请求。
在此状态中,处理器设置程序计数器跳转到异常处理程序,但特别是在调试模式下遇到ebreak
时,不更新调试相关的CSR。对于其他情况,则更新相应的调试寄存器。完成后,处理器返回到DECODE
状态继续执行指令,并将调试模式标志设置为激活(debug_mode_n = 1'b1
)。
这些状态允许处理器在面对特定系统指令、异常和调试事件时灵活处理,确保了正确的程序流程和调试功能的实现。
// We enter this state for single stepping
// DPC is set the next instruction to be executed/fetched
DBG_TAKEN_IF:
begin
is_decoding_o = 1'b0;
pc_set_o = 1'b1;
pc_mux_o = PC_EXCEPTION;
exc_pc_mux_o = EXC_PC_DBD;
csr_save_cause_o = 1'b1;
debug_csr_save_o = 1'b1;
if (debug_force_wakeup_q)
debug_cause_o = DBG_CAUSE_HALTREQ;
else if (debug_single_step_i)
debug_cause_o = DBG_CAUSE_STEP; // pri 0
csr_save_if_o = 1'b1;
ctrl_fsm_ns = DECODE;
debug_mode_n = 1'b1;
debug_force_wakeup_n = 1'b0;
end
DBG_FLUSH:
begin
is_decoding_o = 1'b0;
halt_if_o = 1'b1;
halt_id_o = 1'b1;
perf_pipeline_stall_o = data_load_event_i;
if (data_err_i)
begin //data error
// the current LW or SW have been blocked by the PMP
csr_save_ex_o = 1'b1;
csr_save_cause_o = 1'b1;
data_err_ack_o = 1'b1;
//no jump in this stage as we have to wait one cycle to go to Machine Mode
csr_cause_o = {
1'b0, data_we_ex_i ? EXC_CAUSE_STORE_FAULT : EXC_CAUSE_LOAD_FAULT};
ctrl_fsm_ns = FLUSH_WB;
end //data error
else begin
if(debug_mode_q |
trigger_match_i |
(ebrk_force_debug_mode & ebrk_insn_i) |
data_load_event_i |
debug_req_entry_q )
begin
ctrl_fsm_ns = DBG_TAKEN_ID;
end else
begin
/