From 63d661af63304cf518b6c71392031f9c949ce9b8 Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Sat, 10 Jul 2021 18:53:41 +0100 Subject: [PATCH] Start hacking in debug support to the core -- seems to work as well as before adding debug! --- hdl/hazard3_config.vh | 5 + hdl/hazard3_config_inst.vh | 1 + hdl/hazard3_core.v | 123 ++++++++++++++------- hdl/hazard3_cpu_1port.v | 75 +++++++++---- hdl/hazard3_cpu_2port.v | 71 ++++++++---- hdl/hazard3_csr.v | 220 +++++++++++++++++++++++++++++++++++-- hdl/hazard3_decode.v | 22 ++-- hdl/hazard3_frontend.v | 39 +++++-- 8 files changed, 441 insertions(+), 115 deletions(-) diff --git a/hdl/hazard3_config.vh b/hdl/hazard3_config.vh index d216af9..3eb758e 100644 --- a/hdl/hazard3_config.vh +++ b/hdl/hazard3_config.vh @@ -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, diff --git a/hdl/hazard3_config_inst.vh b/hdl/hazard3_config_inst.vh index a6ec8a3..e921218 100644 --- a/hdl/hazard3_config_inst.vh +++ b/hdl/hazard3_config_inst.vh @@ -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), diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index ce59562..776795e 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -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 diff --git a/hdl/hazard3_cpu_1port.v b/hdl/hazard3_cpu_1port.v index ca912d1..005d261 100644 --- a/hdl/hazard3_cpu_1port.v +++ b/hdl/hazard3_cpu_1port.v @@ -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) ); diff --git a/hdl/hazard3_cpu_2port.v b/hdl/hazard3_cpu_2port.v index a18ec3f..751b332 100644 --- a/hdl/hazard3_cpu_2port.v +++ b/hdl/hazard3_cpu_2port.v @@ -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) ); // ---------------------------------------------------------------------------- diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index ba740a5..6618cc7 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -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 diff --git a/hdl/hazard3_decode.v b/hdl/hazard3_decode.v index 0a9624a..cda5c92 100644 --- a/hdl/hazard3_decode.v +++ b/hdl/hazard3_decode.v @@ -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 diff --git a/hdl/hazard3_frontend.v b/hdl/hazard3_frontend.v index 681113a..2b14d3d 100644 --- a/hdl/hazard3_frontend.v +++ b/hdl/hazard3_frontend.v @@ -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