Start hacking in debug support to the core -- seems to work as well as before adding debug!

This commit is contained in:
Luke Wren 2021-07-10 18:53:41 +01:00
parent 83244c6651
commit 63d661af63
8 changed files with 441 additions and 115 deletions

View File

@ -47,6 +47,11 @@ parameter CSR_M_TRAP = 1,
// CSR_COUNTER: Include performance counters and relevant M-mode CSRs
parameter CSR_COUNTER = 1,
// DEBUG_SUPPORT: Support for run/halt and instruction injection from an
// external Debug Module, support for Debug Mode, and Debug Mode CSRs.
// Requires: CSR_M_MANDATORY, CSR_M_TRAP.
parameter DEBUG_SUPPORT = 0,
// NUM_IRQ: Number of external IRQs implemented in meie0 and meip0.
// Minimum 1 (if CSR_M_TRAP = 1), maximum 32.
parameter NUM_IRQ = 32,

View File

@ -9,6 +9,7 @@
.CSR_M_MANDATORY (CSR_M_MANDATORY),
.CSR_M_TRAP (CSR_M_TRAP),
.CSR_COUNTER (CSR_COUNTER),
.DEBUG_SUPPORT (DEBUG_SUPPORT),
.NUM_IRQ (NUM_IRQ),
.MVENDORID_VAL (MVENDORID_VAL),
.MARCHID_VAL (MARCHID_VAL),

View File

@ -53,6 +53,23 @@ module hazard3_core #(
output reg [W_DATA-1:0] bus_wdata_d,
input wire [W_DATA-1:0] bus_rdata_d,
// Debugger run/halt control
input wire dbg_req_halt,
input wire dbg_req_halt_on_reset,
input wire dbg_req_resume,
output wire dbg_halted,
output wire dbg_running,
// Debugger access to data0 CSR
output wire [W_DATA-1:0] dbg_data0_rdata,
input wire [W_DATA-1:0] dbg_data0_wdata,
input wire dbg_data0_wen,
// Debugger instruction injection
input wire [W_DATA-1:0] dbg_instr_data,
input wire dbg_instr_data_vld,
output wire dbg_instr_data_rdy,
output wire dbg_instr_caught_exception,
output wire dbg_instr_caught_ebreak,
// Level-sensitive interrupt sources
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
input wire soft_irq, // -> mip.msip
@ -68,6 +85,10 @@ localparam HSIZE_WORD = 3'd2;
localparam HSIZE_HWORD = 3'd1;
localparam HSIZE_BYTE = 3'd0;
wire debug_mode;
assign dbg_halted = DEBUG_SUPPORT && debug_mode;
assign dbg_running = DEBUG_SUPPORT && !debug_mode;
// ----------------------------------------------------------------------------
// Pipe Stage F
@ -96,29 +117,34 @@ hazard3_frontend #(
.FIFO_DEPTH(2),
`include "hazard3_config_inst.vh"
) frontend (
.clk (clk),
.rst_n (rst_n),
.clk (clk),
.rst_n (rst_n),
.mem_size (f_mem_size),
.mem_addr (bus_haddr_i),
.mem_addr_vld (bus_aph_req_i),
.mem_addr_rdy (bus_aph_ready_i),
.mem_size (f_mem_size),
.mem_addr (bus_haddr_i),
.mem_addr_vld (bus_aph_req_i),
.mem_addr_rdy (bus_aph_ready_i),
.mem_data (bus_rdata_i),
.mem_data_vld (bus_dph_ready_i),
.mem_data (bus_rdata_i),
.mem_data_vld (bus_dph_ready_i),
.jump_target (f_jump_target),
.jump_target_vld (f_jump_req),
.jump_target_rdy (f_jump_rdy),
.jump_target (f_jump_target),
.jump_target_vld (f_jump_req),
.jump_target_rdy (f_jump_rdy),
.cir (fd_cir),
.cir_vld (fd_cir_vld),
.cir_use (df_cir_use),
.cir_lock (df_cir_lock),
.cir (fd_cir),
.cir_vld (fd_cir_vld),
.cir_use (df_cir_use),
.cir_lock (df_cir_lock),
.next_regs_rs1 (f_rs1),
.next_regs_rs2 (f_rs2),
.next_regs_vld (f_regnum_vld)
.next_regs_rs1 (f_rs1),
.next_regs_rs2 (f_rs2),
.next_regs_vld (f_regnum_vld),
.debug_mode (debug_mode),
.dbg_instr_data (dbg_instr_data),
.dbg_instr_data_vld (dbg_instr_data_vld),
.dbg_instr_data_rdy (dbg_instr_data_rdy)
);
// ----------------------------------------------------------------------------
@ -176,6 +202,8 @@ hazard3_decode #(
.d_pc (d_pc),
.x_jump_not_except (x_jump_not_except),
.debug_mode (debug_mode),
.d_starved (d_starved),
.x_stall (x_stall),
.f_jump_now (f_jump_now),
@ -465,38 +493,51 @@ hazard3_csr #(
.XLEN (W_DATA),
`include "hazard3_config_inst.vh"
) inst_hazard3_csr (
.clk (clk),
.rst_n (rst_n),
.clk (clk),
.rst_n (rst_n),
// Debugger signalling
.debug_mode (debug_mode),
.dbg_req_halt (dbg_req_halt),
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
.dbg_req_resume (dbg_req_resume),
.dbg_instr_caught_exception (dbg_instr_caught_exception),
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
.dbg_data0_rdata (dbg_data0_rdata),
.dbg_data0_wdata (dbg_data0_wdata),
.dbg_data0_wen (dbg_data0_wen),
// CSR access port
// *en_soon are early access strobes which are not a function of bus stall.
// Can generate access faults (hence traps), but do not actually perform access.
.addr (d_imm[11:0]), // todo could just connect this to the instruction bits
.wdata (x_csr_wdata),
.wen_soon (d_csr_wen && !m_trap_enter_vld),
.wen (d_csr_wen && !m_trap_enter_vld && !x_stall),
.wtype (d_csr_wtype),
.rdata (x_csr_rdata),
.ren_soon (d_csr_ren && !m_trap_enter_vld),
.ren (d_csr_ren && !m_trap_enter_vld && !x_stall),
.illegal (x_csr_illegal_access),
.addr (d_imm[11:0]), // todo could just connect this to the instruction bits
.wdata (x_csr_wdata),
.wen_soon (d_csr_wen && !m_trap_enter_vld),
.wen (d_csr_wen && !m_trap_enter_vld && !x_stall),
.wtype (d_csr_wtype),
.rdata (x_csr_rdata),
.ren_soon (d_csr_ren && !m_trap_enter_vld),
.ren (d_csr_ren && !m_trap_enter_vld && !x_stall),
.illegal (x_csr_illegal_access),
// Trap signalling
.trap_addr (m_trap_addr),
.trap_is_irq (m_trap_is_irq),
.trap_enter_vld (m_trap_enter_vld),
.trap_enter_rdy (m_trap_enter_rdy),
.mepc_in (m_exception_return_addr),
.trap_addr (m_trap_addr),
.trap_is_irq (m_trap_is_irq),
.trap_enter_vld (m_trap_enter_vld),
.trap_enter_rdy (m_trap_enter_rdy),
.mepc_in (m_exception_return_addr),
// IRQ and exception requests
.delay_irq_entry (xm_delay_irq_entry),
.irq (irq),
.irq_software (soft_irq),
.irq_timer (timer_irq),
.except (xm_except),
.delay_irq_entry (xm_delay_irq_entry),
.irq (irq),
.irq_software (soft_irq),
.irq_timer (timer_irq),
.except (xm_except),
// Other CSR-specific signalling
.instr_ret (|df_cir_use)
.instr_ret (|df_cir_use)
);
wire [W_EXCEPT-1:0] x_except =
@ -568,7 +609,7 @@ assign f_jump_req = x_jump_req || m_trap_enter_vld;
assign f_jump_target = m_trap_enter_vld ? m_trap_addr : x_jump_target;
assign x_jump_not_except = !m_trap_enter_vld;
wire m_bus_stall = !xm_memop[3] && !bus_dph_ready_d;
wire m_bus_stall = !xm_memop[3] && !bus_dph_ready_d;
assign m_stall = m_bus_stall || (m_trap_enter_vld && !m_trap_enter_rdy && !m_trap_is_irq);
// Exception is taken against the instruction currently in M, so walk the PC

View File

@ -43,6 +43,23 @@ module hazard3_cpu_1port #(
output wire [W_DATA-1:0] ahblm_hwdata,
input wire [W_DATA-1:0] ahblm_hrdata,
// Debugger run/halt control
input wire dbg_req_halt,
input wire dbg_req_halt_on_reset,
input wire dbg_req_resume,
output wire dbg_halted,
output wire dbg_running,
// Debugger access to data0 CSR
output wire [W_DATA-1:0] dbg_data0_rdata,
input wire [W_DATA-1:0] dbg_data0_wdata,
input wire dbg_data0_wen,
// Debugger instruction injection
input wire [W_DATA-1:0] dbg_instr_data,
input wire dbg_instr_data_vld,
output wire dbg_instr_data_rdy,
output wire dbg_instr_caught_exception,
output wire dbg_instr_caught_ebreak,
// Level-sensitive interrupt sources
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
input wire irq_software, // -> mip.msip
@ -80,35 +97,49 @@ wire [W_DATA-1:0] core_rdata_d;
hazard3_core #(
`include "hazard3_config_inst.vh"
) core (
.clk (clk),
.rst_n (rst_n),
.clk (clk),
.rst_n (rst_n),
`ifdef RISCV_FORMAL
`RVFI_CONN ,
`endif
.bus_aph_req_i (core_aph_req_i),
.bus_aph_panic_i (core_aph_panic_i),
.bus_aph_ready_i (core_aph_ready_i),
.bus_dph_ready_i (core_dph_ready_i),
.bus_dph_err_i (core_dph_err_i),
.bus_hsize_i (core_hsize_i),
.bus_haddr_i (core_haddr_i),
.bus_rdata_i (core_rdata_i),
.bus_aph_req_i (core_aph_req_i),
.bus_aph_panic_i (core_aph_panic_i),
.bus_aph_ready_i (core_aph_ready_i),
.bus_dph_ready_i (core_dph_ready_i),
.bus_dph_err_i (core_dph_err_i),
.bus_hsize_i (core_hsize_i),
.bus_haddr_i (core_haddr_i),
.bus_rdata_i (core_rdata_i),
.bus_aph_req_d (core_aph_req_d),
.bus_aph_ready_d (core_aph_ready_d),
.bus_dph_ready_d (core_dph_ready_d),
.bus_dph_err_d (core_dph_err_d),
.bus_haddr_d (core_haddr_d),
.bus_hsize_d (core_hsize_d),
.bus_hwrite_d (core_hwrite_d),
.bus_wdata_d (core_wdata_d),
.bus_rdata_d (core_rdata_d),
.bus_aph_req_d (core_aph_req_d),
.bus_aph_ready_d (core_aph_ready_d),
.bus_dph_ready_d (core_dph_ready_d),
.bus_dph_err_d (core_dph_err_d),
.bus_haddr_d (core_haddr_d),
.bus_hsize_d (core_hsize_d),
.bus_hwrite_d (core_hwrite_d),
.bus_wdata_d (core_wdata_d),
.bus_rdata_d (core_rdata_d),
.irq (irq),
.soft_irq (soft_irq),
.timer_irq (timer_irq)
.dbg_req_halt (dbg_req_halt),
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
.dbg_req_resume (dbg_req_resume),
.dbg_halted (dbg_halted),
.dbg_running (dbg_running),
.dbg_data0_rdata (dbg_data0_rdata),
.dbg_data0_wdata (dbg_data0_wdata),
.dbg_data0_wen (dbg_data0_wen),
.dbg_instr_data (dbg_instr_data),
.dbg_instr_data_vld (dbg_instr_data_vld),
.dbg_instr_data_rdy (dbg_instr_data_rdy),
.dbg_instr_caught_exception (dbg_instr_caught_exception),
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
.irq (irq),
.soft_irq (soft_irq),
.timer_irq (timer_irq)
);

View File

@ -56,6 +56,23 @@ module hazard3_cpu_2port #(
output wire [W_DATA-1:0] d_hwdata,
input wire [W_DATA-1:0] d_hrdata,
// Debugger run/halt control
input wire dbg_req_halt,
input wire dbg_req_halt_on_reset,
input wire dbg_req_resume,
output wire dbg_halted,
output wire dbg_running,
// Debugger access to data0 CSR
output wire [W_DATA-1:0] dbg_data0_rdata,
input wire [W_DATA-1:0] dbg_data0_wdata,
input wire dbg_data0_wen,
// Debugger instruction injection
input wire [W_DATA-1:0] dbg_instr_data,
input wire dbg_instr_data_vld,
output wire dbg_instr_data_rdy,
output wire dbg_instr_caught_exception,
output wire dbg_instr_caught_ebreak,
// Level-sensitive interrupt sources
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
input wire soft_irq, // -> mip.msip
@ -100,28 +117,42 @@ hazard3_core #(
`RVFI_CONN ,
`endif
.bus_aph_req_i (core_aph_req_i),
.bus_aph_panic_i (core_aph_panic_i),
.bus_aph_ready_i (core_aph_ready_i),
.bus_dph_ready_i (core_dph_ready_i),
.bus_dph_err_i (core_dph_err_i),
.bus_hsize_i (core_hsize_i),
.bus_haddr_i (core_haddr_i),
.bus_rdata_i (core_rdata_i),
.bus_aph_req_i (core_aph_req_i),
.bus_aph_panic_i (core_aph_panic_i),
.bus_aph_ready_i (core_aph_ready_i),
.bus_dph_ready_i (core_dph_ready_i),
.bus_dph_err_i (core_dph_err_i),
.bus_hsize_i (core_hsize_i),
.bus_haddr_i (core_haddr_i),
.bus_rdata_i (core_rdata_i),
.bus_aph_req_d (core_aph_req_d),
.bus_aph_ready_d (core_aph_ready_d),
.bus_dph_ready_d (core_dph_ready_d),
.bus_dph_err_d (core_dph_err_d),
.bus_haddr_d (core_haddr_d),
.bus_hsize_d (core_hsize_d),
.bus_hwrite_d (core_hwrite_d),
.bus_wdata_d (core_wdata_d),
.bus_rdata_d (core_rdata_d),
.bus_aph_req_d (core_aph_req_d),
.bus_aph_ready_d (core_aph_ready_d),
.bus_dph_ready_d (core_dph_ready_d),
.bus_dph_err_d (core_dph_err_d),
.bus_haddr_d (core_haddr_d),
.bus_hsize_d (core_hsize_d),
.bus_hwrite_d (core_hwrite_d),
.bus_wdata_d (core_wdata_d),
.bus_rdata_d (core_rdata_d),
.irq (irq),
.soft_irq (soft_irq),
.timer_irq (timer_irq)
.dbg_req_halt (dbg_req_halt),
.dbg_req_halt_on_reset (dbg_req_halt_on_reset),
.dbg_req_resume (dbg_req_resume),
.dbg_halted (dbg_halted),
.dbg_running (dbg_running),
.dbg_data0_rdata (dbg_data0_rdata),
.dbg_data0_wdata (dbg_data0_wdata),
.dbg_data0_wen (dbg_data0_wen),
.dbg_instr_data (dbg_instr_data),
.dbg_instr_data_vld (dbg_instr_data_vld),
.dbg_instr_data_rdy (dbg_instr_data_rdy),
.dbg_instr_caught_exception (dbg_instr_caught_exception),
.dbg_instr_caught_ebreak (dbg_instr_caught_ebreak),
.irq (irq),
.soft_irq (soft_irq),
.timer_irq (timer_irq)
);
// ----------------------------------------------------------------------------

View File

@ -30,8 +30,22 @@ module hazard3_csr #(
,
`include "hazard3_width_const.vh"
) (
input wire clk,
input wire rst_n,
input wire clk,
input wire rst_n,
// Debug signalling
output reg debug_mode,
input wire dbg_req_halt,
input wire dbg_req_halt_on_reset,
input wire dbg_req_resume,
output wire dbg_instr_caught_exception,
output wire dbg_instr_caught_ebreak,
output wire [W_DATA-1:0] dbg_data0_rdata,
input wire [W_DATA-1:0] dbg_data0_wdata,
input wire dbg_data0_wen,
// Read port is combinatorial.
// Write port is synchronous, and write effects will be observed on the next clock cycle.
@ -225,6 +239,13 @@ localparam MEIE0 = 12'hbe0; // External interrupt enable register 0
localparam MEIP0 = 12'hfe0; // External interrupt pending register 0
localparam MLEI = 12'hfe4; // Lowest external interrupt number
// ----------------------------------------------------------------------------
// D-mode CSRs
localparam DCSR = 12'h7b0;
localparam DPC = 12'h7b1;
localparam DATA0 = 12'h7b2; // DSCRATCH0 would be here if implemented
// ----------------------------------------------------------------------------
// CSR state + update logic
// ----------------------------------------------------------------------------
@ -252,6 +273,18 @@ begin
end
endfunction
wire enter_debug_mode;
wire exit_debug_mode;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
debug_mode <= 1'b0;
end else if (DEBUG_SUPPORT) begin
debug_mode <= (debug_mode && !exit_debug_mode) || enter_debug_mode;
end
end
// ----------------------------------------------------------------------------
// Implementation-defined control register
@ -274,6 +307,8 @@ wire midcr_eivect = midcr[0];
// ----------------------------------------------------------------------------
// Trap-handling CSRs
wire debug_suppresses_trap_update = DEBUG_SUPPORT && (debug_mode || enter_debug_mode);
// Two-level interrupt enable stack, shuffled on entry/exit:
reg mstatus_mpie;
reg mstatus_mie;
@ -334,7 +369,7 @@ always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
mepc <= X0;
end else if (CSR_M_TRAP) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET && !debug_suppresses_trap_update) begin
mepc <= mepc_in & MEPC_MASK;
end else if (wen && addr == MEPC) begin
mepc <= update(mepc) & MEPC_MASK;
@ -375,7 +410,7 @@ always @ (posedge clk or negedge rst_n) begin
mcause_irq <= 1'b0;
mcause_code <= 6'h0;
end else if (CSR_M_TRAP) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET && !debug_suppresses_trap_update) begin
mcause_irq <= mcause_irq_next;
mcause_code <= mcause_code_next;
end else if (wen && addr == MCAUSE) begin
@ -437,10 +472,10 @@ always @ (posedge clk or negedge rst_n) begin
end else if (CSR_COUNTER) begin
// Optionally hold the top (2 * XLEN - W_COUNTER) bits constant to
// save gates (noncompliant if enabled)
if (!mcountinhibit_cy)
if (!(mcountinhibit_cy || debug_mode))
{mcycleh, mcycle} <= (({mcycleh, mcycle} + 1'b1) & ~({2*XLEN{1'b1}} << W_COUNTER))
| ({mcycleh, mcycle} & ({2*XLEN{1'b1}} << W_COUNTER));
if (!mcountinhibit_ir && instr_ret)
if (!(mcountinhibit_ir || debug_mode) && instr_ret)
{minstreth, minstret} <= (({minstreth, minstret} + 1'b1) & ~({2*XLEN{1'b1}} << W_COUNTER))
| ({minstreth, minstret} & ({2*XLEN{1'b1}} << W_COUNTER));
if (wen) begin
@ -462,6 +497,73 @@ always @ (posedge clk or negedge rst_n) begin
end
end
// ----------------------------------------------------------------------------
// Debug-mode CSRs
// The following DCSR bits are read/writable as normal:
// - ebreakm (bit 15)
// - step (bit 2)
// The following are read-only volatile:
// - cause (bits 8:6)
// All others are hardwired constants.
reg dcsr_ebreakm;
reg dcsr_step;
reg [2:0] dcsr_cause;
wire [2:0] dcause_next;
localparam DCSR_CAUSE_EBREAK = 3'h1;
localparam DCSR_CAUSE_TRIGGER = 3'h2;
localparam DCSR_CAUSE_HALTREQ = 3'h3;
localparam DCSR_CAUSE_STEP = 3'h4;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
dcsr_ebreakm <= 1'b0;
dcsr_step <= 1'b0;
dcsr_cause <= 3'h0;
end else if (DEBUG_SUPPORT) begin
if (debug_mode && wen && addr == DCSR) begin
{dcsr_ebreakm, dcsr_step} <=
wtype == CSR_WTYPE_C ? {dcsr_ebreakm, dcsr_step} & ~{wdata[15], wdata[2]} :
wtype == CSR_WTYPE_S ? {dcsr_ebreakm, dcsr_step} | {wdata[15], wdata[2]} :
{wdata[15], wdata[2]} ;
end
if (enter_debug_mode) begin
dcsr_cause <= dcause_next;
end
end
end
reg [XLEN-1:0] dpc;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
dpc <= X0;
end else if (DEBUG_SUPPORT) begin
if (enter_debug_mode)
dpc <= mepc_in;
else if (debug_mode && wen && addr == DPC)
dpc <= update(dpc);
end
end
reg [XLEN-1:0] data0;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
data0 <= X0;
end else if (DEBUG_SUPPORT) begin
if (dbg_data0_wen)
data0 <= dbg_data0_wdata;
else if (debug_mode && wen && addr == DATA0)
data0 <= update(data0);
end
end
assign dbg_data0_rdata = data0;
// ----------------------------------------------------------------------------
// Read port + detect addressing of unmapped CSRs
// ----------------------------------------------------------------------------
@ -709,6 +811,38 @@ always @ (*) begin
rdata = minstreth;
end
// ------------------------------------------------------------------------
// Debug CSRs
DCSR: if (DEBUG_SUPPORT && debug_mode) begin
decode_match = 1'b1;
rdata = {
4'h4, // xdebugver = 4, for 0.13.2 debug spec
12'd0, // reserved
dcsr_ebreakm,
3'h0, // No other modes besides M to break from
1'b0, // stepie = 0, no interrupts in single-step mode (FIXME implement this)
1'b1, // stopcount = 1, no counter increment in debug mode
1'b1, // stoptime = 0, no core-local timer increment in debug mode
dcsr_cause,
1'b0, // reserved
1'b0, // mprven = 0, we don't have MPRV support
1'b0, // nmip = 0, we have no NMI
dcsr_step,
2'h3 // prv = 3, we only have M mode
};
end
DPC: if (DEBUG_SUPPORT && debug_mode) begin
decode_match = 1'b1;
rdata = dpc;
end
DATA0: if (DEBUG_SUPPORT && debug_mode) begin
decode_match = 1'b1;
rdata = data0;
end
// ------------------------------------------------------------------------
// Custom CSRs
@ -738,6 +872,55 @@ end
assign illegal = (wen_soon || ren_soon) && !decode_match;
// ----------------------------------------------------------------------------
// Debug run/halt
reg have_just_reset;
reg step_halt_req;
reg pending_dbg_resume;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
have_just_reset <= |DEBUG_SUPPORT;
step_halt_req <= 1'b0;
pending_dbg_resume <= 1'b0;
end else if (DEBUG_SUPPORT) begin
if (instr_ret)
have_just_reset <= 1'b0;
if (debug_mode)
step_halt_req <= 1'b0;
else if (dcsr_step && instr_ret)
step_halt_req <= 1'b1;
pending_dbg_resume <= (pending_dbg_resume || dbg_req_resume) && debug_mode;
end
end
// We can enter the halted state in an IRQ-like manner (squeeze in between the
// instructions in stage 2 and stage 3) or in an exception-like manner
// (replace the instruction in stage 3).
// This would also include triggers, if/when those are implemented:
wire want_halt_except = DEBUG_SUPPORT && !debug_mode && (
dcsr_ebreakm && except == EXCEPT_EBREAK
);
// Note all exception-like causes (trigger, ebreak) are higher priority than IRQ-like
wire want_halt_irq = DEBUG_SUPPORT && !debug_mode && !want_halt_except && (
dbg_req_halt || (dbg_req_halt_on_reset && have_just_reset) || step_halt_req
);
assign dcause_next =
// Trigger would be highest priority if implemented
except == EXCEPT_EBREAK ? 3'h1 : // ebreak (priority 3)
dbg_req_halt || (dbg_req_halt_on_reset && have_just_reset) ? 3'h3 : // halt or reset-halt (priority 1, 2)
3'h4; // single-step (priority 0)
assign enter_debug_mode = (want_halt_irq || want_halt_except) && trap_enter_rdy;
assign exit_debug_mode = pending_dbg_resume && trap_enter_rdy;
// ----------------------------------------------------------------------------
// Trap request generation
@ -794,21 +977,34 @@ hazard3_priority_encode #(
.gnt (standard_irq_num)
);
wire exception_req_any = except != EXCEPT_NONE;
// ebreak may be treated as a halt-to-debugger or a regular M-mode exception,
// depending on dcsr.ebreakm.
wire exception_req_any = except != EXCEPT_NONE && !(except == EXCEPT_EBREAK && dcsr_ebreakm);
wire [5:0] vector_sel =
wire [5:0] vector_sel =
exception_req_any || !irq_vector_enable ? 6'd0 :
standard_irq_active ? {2'h0, standard_irq_num} :
external_irq_active ? {1'h0, external_irq_num} + 6'd16 : 6'd0;
assign trap_addr = except == EXCEPT_MRET ? mepc : mtvec | {24'h0, vector_sel, 2'h0};
assign trap_is_irq = !exception_req_any;
assign trap_enter_vld = CSR_M_TRAP && (exception_req_any ||
!delay_irq_entry && (standard_irq_active || external_irq_active));
assign trap_addr =
except == EXCEPT_MRET ? mepc :
pending_dbg_resume ? dpc : mtvec | {24'h0, vector_sel, 2'h0};
assign trap_is_irq = !exception_req_any || (DEBUG_SUPPORT && want_halt_irq);
assign trap_enter_vld =
CSR_M_TRAP && (exception_req_any ||
!delay_irq_entry && (standard_irq_active || external_irq_active)) ||
DEBUG_SUPPORT && (want_halt_irq || want_halt_except || pending_dbg_resume);
assign mcause_irq_next = !exception_req_any;
assign mcause_code_next = exception_req_any ? {2'h0, except} : vector_sel;
// Report back to DM instruction injector to tell it its instruction sequence
// has finished or crashed out
assign dbg_instr_caught_ebreak = debug_mode && except == EXCEPT_EBREAK;
// Note we exclude ebreak from here regardless of dcsr.ebreakm!
assign dbg_instr_caught_exception = debug_mode && except != EXCEPT_NONE && except != EXCEPT_EBREAK;
// ----------------------------------------------------------------------------
`ifdef RISCV_FORMAL

View File

@ -31,6 +31,8 @@ module hazard3_decode #(
output wire df_cir_lock,
output wire [W_ADDR-1:0] d_pc,
input wire debug_mode,
output wire d_starved,
input wire x_stall,
input wire f_jump_now,
@ -185,16 +187,16 @@ always @ (*) begin
d_except = EXCEPT_NONE;
casez (d_instr)
RV_BEQ: begin d_rd = X0; d_aluop = ALUOP_SUB; d_branchcond = BCOND_ZERO; end
RV_BNE: begin d_rd = X0; d_aluop = ALUOP_SUB; d_branchcond = BCOND_NZERO; end
RV_BLT: begin d_rd = X0; d_aluop = ALUOP_LT; d_branchcond = BCOND_NZERO; end
RV_BGE: begin d_rd = X0; d_aluop = ALUOP_LT; d_branchcond = BCOND_ZERO; end
RV_BLTU: begin d_rd = X0; d_aluop = ALUOP_LTU; d_branchcond = BCOND_NZERO; end
RV_BGEU: begin d_rd = X0; d_aluop = ALUOP_LTU; d_branchcond = BCOND_ZERO; end
RV_JALR: begin d_branchcond = BCOND_ALWAYS; d_jump_is_regoffs = 1'b1; d_rs2 = X0; d_aluop = ALUOP_ADD; d_alusrc_a = ALUSRCA_PC; d_alusrc_b = ALUSRCB_IMM; d_imm = d_instr_is_32bit ? 32'h4 : 32'h2; end
RV_JAL: begin d_branchcond = BCOND_ALWAYS; d_rs1 = X0; d_rs2 = X0; d_aluop = ALUOP_ADD; d_alusrc_a = ALUSRCA_PC; d_alusrc_b = ALUSRCB_IMM; d_imm = d_instr_is_32bit ? 32'h4 : 32'h2; end
RV_BEQ: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_SUB; d_branchcond = BCOND_ZERO; end
RV_BNE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_SUB; d_branchcond = BCOND_NZERO; end
RV_BLT: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_LT; d_branchcond = BCOND_NZERO; end
RV_BGE: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_LT; d_branchcond = BCOND_ZERO; end
RV_BLTU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_LTU; d_branchcond = BCOND_NZERO; end
RV_BGEU: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_aluop = ALUOP_LTU; d_branchcond = BCOND_ZERO; end
RV_JALR: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_branchcond = BCOND_ALWAYS; d_jump_is_regoffs = 1'b1; d_rs2 = X0; d_aluop = ALUOP_ADD; d_alusrc_a = ALUSRCA_PC; d_alusrc_b = ALUSRCB_IMM; d_imm = d_instr_is_32bit ? 32'h4 : 32'h2; end
RV_JAL: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_branchcond = BCOND_ALWAYS; d_rs1 = X0; d_rs2 = X0; d_aluop = ALUOP_ADD; d_alusrc_a = ALUSRCA_PC; d_alusrc_b = ALUSRCB_IMM; d_imm = d_instr_is_32bit ? 32'h4 : 32'h2; end
RV_LUI: begin d_aluop = ALUOP_ADD; d_imm = d_imm_u; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; d_rs1 = X0; end
RV_AUIPC: begin d_aluop = ALUOP_ADD; d_imm = d_imm_u; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; d_alusrc_a = ALUSRCA_PC; d_rs1 = X0; end
RV_AUIPC: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_aluop = ALUOP_ADD; d_imm = d_imm_u; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; d_alusrc_a = ALUSRCA_PC; d_rs1 = X0; end
RV_ADDI: begin d_aluop = ALUOP_ADD; d_imm = d_imm_i; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; end
RV_SLLI: begin d_aluop = ALUOP_SLL; d_imm = d_imm_i; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; end
RV_SLTI: begin d_aluop = ALUOP_LT; d_imm = d_imm_i; d_alusrc_b = ALUSRCB_IMM; d_rs2 = X0; end
@ -231,7 +233,7 @@ always @ (*) begin
RV_REM: if (EXTENSION_M) begin d_aluop = ALUOP_MULDIV; d_mulop = M_OP_REM; end else begin d_invalid_32bit = 1'b1; end
RV_REMU: if (EXTENSION_M) begin d_aluop = ALUOP_MULDIV; d_mulop = M_OP_REMU; end else begin d_invalid_32bit = 1'b1; end
RV_FENCE: begin d_rd = X0; end // NOP
RV_FENCE_I: begin d_rd = X0; d_rs1 = X0; d_rs2 = X0; d_branchcond = BCOND_NZERO; d_imm[31] = 1'b1; end // FIXME this is probably busted now. Maybe implement as an exception?
RV_FENCE_I: begin d_invalid_32bit = DEBUG_SUPPORT && debug_mode; d_rd = X0; d_rs1 = X0; d_rs2 = X0; d_branchcond = BCOND_NZERO; d_imm[31] = 1'b1; end // FIXME this is probably busted now. Maybe implement as an exception?
RV_CSRRW: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = 1'b1 ; d_csr_ren = |d_rd; d_csr_wtype = CSR_WTYPE_W; end else begin d_invalid_32bit = 1'b1; end
RV_CSRRS: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_S; end else begin d_invalid_32bit = 1'b1; end
RV_CSRRC: if (HAVE_CSR) begin d_imm = d_imm_i; d_csr_wen = |d_rs1; d_csr_ren = 1'b1 ; d_csr_wtype = CSR_WTYPE_C; end else begin d_invalid_32bit = 1'b1; end

View File

@ -61,11 +61,19 @@ module hazard3_frontend #(
// from being trashed by incoming fetch data;
// jump instructions have other side effects besides jumping!
// Provide the rs1/rs2 register numbers which will be in CIR on the next
// cycle. These go straight to the register file read ports.
// Provide the rs1/rs2 register numbers which will be in CIR on the next
// cycle. These go straight to the register file read ports.
output wire [4:0] next_regs_rs1,
output wire [4:0] next_regs_rs2,
output wire next_regs_vld
output wire next_regs_vld,
// Debugger instruction injection: instruction fetch is suppressed when in
// debug halt state, and the DM can then inject instructions into the last
// entry of the prefetch queue using the vld/rdy handshake.
input wire debug_mode,
input wire [W_DATA-1:0] dbg_instr_data,
input wire dbg_instr_data_vld,
output wire dbg_instr_data_rdy,
);
localparam W_BUNDLE = W_DATA / 2;
@ -96,14 +104,15 @@ wire fifo_almost_full = FIFO_DEPTH == 1 || (!fifo_valid[FIFO_DEPTH - 1] && fifo_
wire fifo_push;
wire fifo_pop;
wire fifo_dbg_inject = DEBUG_SUPPORT && dbg_instr_data_vld && dbg_instr_data_rdy;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
fifo_valid <= {FIFO_DEPTH+1{1'b0}};
end else if (jump_now) begin
fifo_valid[FIFO_DEPTH-1:0] <= {FIFO_DEPTH{1'b0}};
end else if (fifo_push || fifo_pop) begin
fifo_valid[FIFO_DEPTH-1:0] <= ~(~fifo_valid << fifo_push) >> fifo_pop;
end else if (fifo_push || fifo_pop || fifo_dbg_inject) begin
fifo_valid[FIFO_DEPTH-1:0] <= ~(~fifo_valid << (fifo_push || fifo_dbg_inject)) >> fifo_pop;
end
end
@ -114,6 +123,11 @@ always @ (posedge clk) begin: fifo_data_shift
fifo_mem[i] <= fifo_valid[i + 1] ? fifo_mem[i + 1] : fifo_wdata;
end
end
// Allow DM to inject instructions directly into the lowest-numbered queue
// entry. This mux should not extend critical path since it is balanced
// with the instruction-assembly muxes on the queue bypass path.
if (fifo_dbg_inject)
fifo_mem[0] <= dbg_instr_data;
end
// ----------------------------------------------------------------------------
@ -126,9 +140,13 @@ reg [1:0] pending_fetches;
reg [1:0] ctr_flush_pending;
wire [1:0] pending_fetches_next = pending_fetches + (mem_addr_vld && !mem_addr_hold) - mem_data_vld;
// Debugger only injects instructions when the frontend is at rest and empty.
assign dbg_instr_data_rdy = DEBUG_SUPPORT && !fifo_valid[0] && ~|ctr_flush_pending;
wire cir_must_refill;
// If fetch data is forwarded past the FIFO, ensure it is not also written to it.
assign fifo_push = mem_data_vld && ~|ctr_flush_pending && !(cir_must_refill && fifo_empty);
assign fifo_push = mem_data_vld && ~|ctr_flush_pending && !(cir_must_refill && fifo_empty)
&& !(DEBUG_SUPPORT && debug_mode);
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
@ -235,10 +253,11 @@ always @ (*) begin
mem_addr_vld_r = 1'b1;
mem_size_r = 1'b1; // almost all accesses are 32 bit
case (1'b1)
mem_addr_hold : begin mem_addr_r = {fetch_addr[W_ADDR-1:2], unaligned_jump_aph, 1'b0}; mem_size_r = !unaligned_jump_aph; end
jump_target_vld : begin mem_addr_r = jump_target; mem_size_r = !unaligned_jump_now; end
!fetch_stall : begin mem_addr_r = fetch_addr; end
default : begin mem_addr_vld_r = 1'b0; end
mem_addr_hold : begin mem_addr_r = {fetch_addr[W_ADDR-1:2], unaligned_jump_aph, 1'b0}; mem_size_r = !unaligned_jump_aph; end
jump_target_vld : begin mem_addr_r = jump_target; mem_size_r = !unaligned_jump_now; end
DEBUG_SUPPORT && debug_mode : begin mem_addr_vld_r = 1'b0; end
!fetch_stall : begin mem_addr_r = fetch_addr; end
default : begin mem_addr_vld_r = 1'b0; end
endcase
end