Start hacking in debug support to the core -- seems to work as well as before adding debug!
This commit is contained in:
parent
83244c6651
commit
63d661af63
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue