First attempt at hacking in triggers, at least seems to have not broken other exception logic. Not yet tested.
This commit is contained in:
parent
5d6b5a80b0
commit
6e3799eed0
|
@ -16,4 +16,5 @@ file hazard3_decode.v
|
|||
file hazard3_csr.v
|
||||
file hazard3_regfile_1w2r.v
|
||||
file hazard3_pmp.v
|
||||
file hazard3_triggers.v
|
||||
include .
|
||||
|
|
|
@ -120,6 +120,10 @@ parameter PMP_HARDWIRED_CFG = PMP_REGIONS > 0 ? {PMP_REGIONS{8'h00}} : 1'b0,
|
|||
// Requires: CSR_M_MANDATORY, CSR_M_TRAP.
|
||||
parameter DEBUG_SUPPORT = 0,
|
||||
|
||||
// BREAKPOINT_TRIGGERS: Number of triggers which support type=2 execute=1
|
||||
// (but not store/load=1, i.e. not a watchpoint). Requires: DEBUG_SUPPORT
|
||||
parameter BREAKPOINT_TRIGGERS = 0,
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// External interrupt support
|
||||
|
||||
|
|
|
@ -12,47 +12,48 @@
|
|||
// except for MHARTID_VAL. It must be defined once before each include of
|
||||
// this file.
|
||||
|
||||
.RESET_VECTOR (RESET_VECTOR),
|
||||
.MTVEC_INIT (MTVEC_INIT),
|
||||
.EXTENSION_A (EXTENSION_A),
|
||||
.EXTENSION_C (EXTENSION_C),
|
||||
.EXTENSION_M (EXTENSION_M),
|
||||
.EXTENSION_ZBA (EXTENSION_ZBA),
|
||||
.EXTENSION_ZBB (EXTENSION_ZBB),
|
||||
.EXTENSION_ZBC (EXTENSION_ZBC),
|
||||
.EXTENSION_ZBS (EXTENSION_ZBS),
|
||||
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
||||
.EXTENSION_XH3B (EXTENSION_XH3B),
|
||||
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
||||
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||
.CSR_M_TRAP (CSR_M_TRAP),
|
||||
.CSR_COUNTER (CSR_COUNTER),
|
||||
.U_MODE (U_MODE),
|
||||
.PMP_REGIONS (PMP_REGIONS),
|
||||
.PMP_GRAIN (PMP_GRAIN),
|
||||
.PMP_HARDWIRED (PMP_HARDWIRED),
|
||||
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
||||
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
||||
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
||||
.NUM_IRQS (NUM_IRQS),
|
||||
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
||||
.MVENDORID_VAL (MVENDORID_VAL),
|
||||
.MIMPID_VAL (MIMPID_VAL),
|
||||
.RESET_VECTOR (RESET_VECTOR),
|
||||
.MTVEC_INIT (MTVEC_INIT),
|
||||
.EXTENSION_A (EXTENSION_A),
|
||||
.EXTENSION_C (EXTENSION_C),
|
||||
.EXTENSION_M (EXTENSION_M),
|
||||
.EXTENSION_ZBA (EXTENSION_ZBA),
|
||||
.EXTENSION_ZBB (EXTENSION_ZBB),
|
||||
.EXTENSION_ZBC (EXTENSION_ZBC),
|
||||
.EXTENSION_ZBS (EXTENSION_ZBS),
|
||||
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
||||
.EXTENSION_XH3B (EXTENSION_XH3B),
|
||||
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
||||
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||
.CSR_M_TRAP (CSR_M_TRAP),
|
||||
.CSR_COUNTER (CSR_COUNTER),
|
||||
.U_MODE (U_MODE),
|
||||
.PMP_REGIONS (PMP_REGIONS),
|
||||
.PMP_GRAIN (PMP_GRAIN),
|
||||
.PMP_HARDWIRED (PMP_HARDWIRED),
|
||||
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
||||
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
||||
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
||||
.BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS),
|
||||
.NUM_IRQS (NUM_IRQS),
|
||||
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
||||
.MVENDORID_VAL (MVENDORID_VAL),
|
||||
.MIMPID_VAL (MIMPID_VAL),
|
||||
`ifndef HAZARD3_CONFIG_INST_NO_MHARTID
|
||||
.MHARTID_VAL (MHARTID_VAL),
|
||||
.MHARTID_VAL (MHARTID_VAL),
|
||||
`endif
|
||||
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
||||
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||
.MUL_FAST (MUL_FAST),
|
||||
.MUL_FASTER (MUL_FASTER),
|
||||
.MULH_FAST (MULH_FAST),
|
||||
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
||||
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
||||
.MTVEC_WMASK (MTVEC_WMASK),
|
||||
.RESET_REGFILE (RESET_REGFILE),
|
||||
.W_ADDR (W_ADDR),
|
||||
.W_DATA (W_DATA)
|
||||
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
||||
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||
.MUL_FAST (MUL_FAST),
|
||||
.MUL_FASTER (MUL_FASTER),
|
||||
.MULH_FAST (MULH_FAST),
|
||||
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
||||
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
||||
.MTVEC_WMASK (MTVEC_WMASK),
|
||||
.RESET_REGFILE (RESET_REGFILE),
|
||||
.W_ADDR (W_ADDR),
|
||||
.W_DATA (W_DATA)
|
||||
|
||||
`ifdef HAZARD3_CONFIG_INST_NO_MHARTID
|
||||
`undef HAZARD3_CONFIG_INST_NO_MHARTID
|
||||
|
|
|
@ -296,6 +296,7 @@ reg [W_DATA-1:0] xm_result;
|
|||
reg [1:0] xm_addr_align;
|
||||
reg [W_MEMOP-1:0] xm_memop;
|
||||
reg [W_EXCEPT-1:0] xm_except;
|
||||
reg xm_except_to_d_mode;
|
||||
reg xm_wfi;
|
||||
reg xm_delay_irq_entry;
|
||||
|
||||
|
@ -377,6 +378,11 @@ assign x_stall =
|
|||
|
||||
wire m_wfi_stall_clear;
|
||||
|
||||
wire x_loadstore_pmp_fail;
|
||||
wire x_exec_pmp_fail;
|
||||
wire x_trig_break;
|
||||
wire x_trig_break_d_mode;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Execution logic
|
||||
|
||||
|
@ -465,9 +471,6 @@ hazard3_alu #(
|
|||
|
||||
// Load/store bus request
|
||||
|
||||
wire x_loadstore_pmp_fail;
|
||||
wire x_exec_pmp_fail;
|
||||
|
||||
wire x_unaligned_addr = d_memop != MEMOP_NONE && (
|
||||
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
||||
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0]
|
||||
|
@ -608,6 +611,7 @@ always @ (*) begin
|
|||
x_stall_on_exclusive_overlap ||
|
||||
x_loadstore_pmp_fail ||
|
||||
x_exec_pmp_fail ||
|
||||
x_trig_break ||
|
||||
x_unaligned_addr ||
|
||||
m_trap_enter_soon ||
|
||||
(xm_wfi && !m_wfi_stall_clear) // FIXME will cause a timing issue, better to stall til *after* clear
|
||||
|
@ -750,7 +754,7 @@ wire x_jump_req_unchecked = !x_stall_on_raw && (
|
|||
d_branchcond == BCOND_NZERO && x_branch_cmp
|
||||
);
|
||||
|
||||
assign x_jump_req = x_jump_req_unchecked && !x_jump_misaligned && !x_exec_pmp_fail;
|
||||
assign x_jump_req = x_jump_req_unchecked && !x_jump_misaligned && !x_exec_pmp_fail || x_trig_break;
|
||||
|
||||
assign x_btb_set = |BRANCH_PREDICTOR && (
|
||||
x_jump_req_unchecked && d_addr_offs[W_ADDR - 1] && !x_branch_was_predicted &&
|
||||
|
@ -809,6 +813,46 @@ end else begin: no_pmp
|
|||
end
|
||||
endgenerate
|
||||
|
||||
// Triggers
|
||||
|
||||
wire [11:0] x_trig_cfg_addr;
|
||||
wire x_trig_cfg_wen;
|
||||
wire [W_DATA-1:0] x_trig_cfg_wdata;
|
||||
wire [W_DATA-1:0] x_trig_cfg_rdata;
|
||||
wire x_trig_m_en;
|
||||
|
||||
generate
|
||||
if (BREAKPOINT_TRIGGERS > 0) begin: have_triggers
|
||||
|
||||
hazard3_triggers #(
|
||||
`include "hazard3_config_inst.vh"
|
||||
) triggers (
|
||||
.clk (clk),
|
||||
.rst_n (rst_n),
|
||||
|
||||
.cfg_addr (x_trig_cfg_addr),
|
||||
.cfg_wen (x_trig_cfg_wen),
|
||||
.cfg_wdata (x_trig_cfg_wdata),
|
||||
.cfg_rdata (x_trig_cfg_rdata),
|
||||
.trig_m_en (x_trig_m_en),
|
||||
|
||||
.pc (d_pc),
|
||||
.m_mode (x_mmode_execution),
|
||||
.d_mode (debug_mode),
|
||||
|
||||
.break (x_trig_break),
|
||||
.break_d_mode (x_trig_break_d_mode)
|
||||
);
|
||||
|
||||
end else begin: no_triggers
|
||||
|
||||
assign x_trig_cfg_rdata = {W_DATA{1'b0}};
|
||||
assign x_trig_break = 1'b0;
|
||||
assign x_trig_break_d_mode = 1'b0;
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// CSRs and Trap Handling
|
||||
|
||||
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
|
||||
|
@ -850,6 +894,7 @@ end
|
|||
wire [W_ADDR-1:0] m_exception_return_addr;
|
||||
|
||||
wire [W_EXCEPT-1:0] x_except =
|
||||
x_trig_break ? EXCEPT_EBREAK :
|
||||
x_exec_pmp_fail ? EXCEPT_INSTR_FAULT :
|
||||
x_jump_req_unchecked && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN :
|
||||
x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL :
|
||||
|
@ -925,12 +970,19 @@ hazard3_csr #(
|
|||
.irq_software (soft_irq),
|
||||
.irq_timer (timer_irq),
|
||||
.except (xm_except),
|
||||
.except_to_d_mode (xm_except_to_d_mode),
|
||||
|
||||
.pmp_cfg_addr (x_pmp_cfg_addr),
|
||||
.pmp_cfg_wen (x_pmp_cfg_wen),
|
||||
.pmp_cfg_wdata (x_pmp_cfg_wdata),
|
||||
.pmp_cfg_rdata (x_pmp_cfg_rdata),
|
||||
|
||||
.trig_cfg_addr (x_trig_cfg_addr),
|
||||
.trig_cfg_wen (x_trig_cfg_wen),
|
||||
.trig_cfg_wdata (x_trig_cfg_wdata),
|
||||
.trig_cfg_rdata (x_trig_cfg_rdata),
|
||||
.trig_m_en (x_trig_m_en),
|
||||
|
||||
// Other CSR-specific signalling
|
||||
.permit_wfi (x_permit_wfi),
|
||||
.instr_ret (x_instr_ret)
|
||||
|
@ -942,6 +994,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
if (!rst_n) begin
|
||||
xm_memop <= MEMOP_NONE;
|
||||
xm_except <= EXCEPT_NONE;
|
||||
xm_except_to_d_mode <= 1'b0;
|
||||
xm_wfi <= 1'b0;
|
||||
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
||||
end else begin
|
||||
|
@ -950,6 +1003,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
// If some X-sourced exception has squashed the address phase, need to squash the data phase too.
|
||||
xm_memop <= x_unaligned_addr || x_exec_pmp_fail || x_loadstore_pmp_fail ? MEMOP_NONE : d_memop;
|
||||
xm_except <= x_except;
|
||||
xm_except_to_d_mode <= x_trig_break_d_mode;
|
||||
xm_wfi <= d_wfi && !x_exec_pmp_fail;
|
||||
// Note the d_starved term is required because it is possible
|
||||
// (e.g. PMP X permission fail) to except when the frontend is
|
||||
|
@ -959,6 +1013,7 @@ always @ (posedge clk or negedge rst_n) begin
|
|||
xm_rd <= {W_REGADDR{1'b0}};
|
||||
xm_memop <= MEMOP_NONE;
|
||||
xm_except <= EXCEPT_NONE;
|
||||
xm_except_to_d_mode <= 1'b0;
|
||||
xm_wfi <= 1'b0;
|
||||
end
|
||||
end else if (bus_dph_err_d) begin
|
||||
|
|
|
@ -89,6 +89,7 @@ module hazard3_csr #(
|
|||
|
||||
// Exceptions must *not* be a function of bus stall.
|
||||
input wire [W_EXCEPT-1:0] except,
|
||||
input wire except_to_d_mode,
|
||||
|
||||
// Level-sensitive interrupt sources
|
||||
input wire delay_irq_entry,
|
||||
|
@ -102,6 +103,13 @@ module hazard3_csr #(
|
|||
output wire [W_DATA-1:0] pmp_cfg_wdata,
|
||||
input wire [W_DATA-1:0] pmp_cfg_rdata,
|
||||
|
||||
// Trigger config interface
|
||||
output wire [11:0] trig_cfg_addr,
|
||||
output reg trig_cfg_wen,
|
||||
output wire [W_DATA-1:0] trig_cfg_wdata,
|
||||
input wire [W_DATA-1:0] trig_cfg_rdata,
|
||||
output wire trig_m_en,
|
||||
|
||||
// Other CSR-specific signalling
|
||||
output wire permit_wfi,
|
||||
input wire instr_ret
|
||||
|
@ -619,6 +627,25 @@ end
|
|||
assign dbg_data0_wdata = wdata;
|
||||
assign dbg_data0_wen = debug_mode && wen && addr == DMDATA0;
|
||||
|
||||
reg tcontrol_mte;
|
||||
reg tcontrol_mpte;
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) begin
|
||||
tcontrol_mpte <= 1'b0;
|
||||
tcontrol_mte <= 1'b0;
|
||||
end else if (wen_m_mode && addr == TCONTROL) begin
|
||||
tcontrol_mpte <= wdata_update[7] && DEBUG_SUPPORT;
|
||||
tcontrol_mte <= wdata_update[3] && DEBUG_SUPPORT;
|
||||
end else if (DEBUG_SUPPORT && trapreg_update_enter) begin
|
||||
tcontrol_mte <= 1'b0;
|
||||
tcontrol_mpte <= tcontrol_mte;
|
||||
end else if (DEBUG_SUPPORT && trapreg_update_exit) begin
|
||||
tcontrol_mte <= tcontrol_mpte;
|
||||
tcontrol_mpte <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Read port + detect addressing of unmapped CSRs
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -637,6 +664,7 @@ always @ (*) begin
|
|||
decode_match = 1'b0;
|
||||
rdata = {XLEN{1'b0}};
|
||||
pmp_cfg_wen = 1'b0;
|
||||
trig_cfg_wen = 1'b0;
|
||||
case (addr)
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -1047,12 +1075,61 @@ always @ (*) begin
|
|||
// ------------------------------------------------------------------------
|
||||
// Trigger Module CSRs
|
||||
|
||||
// If triggers aren't supported, OpenOCD expects the following:
|
||||
// - tselect must be present
|
||||
// - tselect must raise an exception when written to
|
||||
// Otherwise it returns an error instead of 0 count when enumerating triggers
|
||||
// If triggers aren't supported, we implement tselect as hardwired 0,
|
||||
// tinfo as hardwired 1 (meaning type=0 always) and tdata as hardwired 0
|
||||
// (meaning type=0). The debugger will see the first trigger as
|
||||
// unimplemented, and immediately halt discovery.
|
||||
TSELECT: if (DEBUG_SUPPORT) begin
|
||||
decode_match = match_mro;
|
||||
decode_match = match_mrw;
|
||||
if (BREAKPOINT_TRIGGERS > 0) begin
|
||||
trig_cfg_wen = match_mrw && wen;
|
||||
rdata = trig_cfg_rdata;
|
||||
end else begin
|
||||
rdata = 32'h00000000;
|
||||
end
|
||||
end
|
||||
|
||||
TDATA1: if (DEBUG_SUPPORT) begin
|
||||
decode_match = match_mrw;
|
||||
if (BREAKPOINT_TRIGGERS > 0) begin
|
||||
trig_cfg_wen = match_mrw && wen;
|
||||
rdata = trig_cfg_rdata;
|
||||
end else begin
|
||||
rdata = 32'h00000000;
|
||||
end
|
||||
end
|
||||
|
||||
TDATA2: if (DEBUG_SUPPORT) begin
|
||||
decode_match = match_mrw;
|
||||
if (BREAKPOINT_TRIGGERS > 0) begin
|
||||
trig_cfg_wen = match_mrw && wen;
|
||||
rdata = trig_cfg_rdata;
|
||||
end else begin
|
||||
rdata = 32'h00000000;
|
||||
end
|
||||
end
|
||||
|
||||
TINFO: if (DEBUG_SUPPORT) begin
|
||||
// Note tinfo is a read-write CSR (writes don't trap) even though it
|
||||
// is entirely read-only.
|
||||
decode_match = match_mrw;
|
||||
if (BREAKPOINT_TRIGGERS > 0) begin
|
||||
trig_cfg_wen = match_mrw && wen;
|
||||
rdata = trig_cfg_rdata;
|
||||
end else begin
|
||||
rdata = 32'h00000001;
|
||||
end
|
||||
end
|
||||
|
||||
TCONTROL: if (DEBUG_SUPPORT && BREAKPOINT_TRIGGERS > 0) begin
|
||||
decode_match = match_mrw;
|
||||
rdata = {
|
||||
24'h0,
|
||||
tcontrol_mpte,
|
||||
3'h0,
|
||||
tcontrol_mte,
|
||||
3'h0
|
||||
};
|
||||
end
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -1218,10 +1295,10 @@ end
|
|||
wire exception_req_any;
|
||||
wire halt_delayed_by_exception = exception_req_any || loadstore_dphase_pending;
|
||||
|
||||
// This would also include triggers, if/when those are implemented:
|
||||
wire want_halt_except = DEBUG_SUPPORT && !debug_mode && (
|
||||
dcsr_ebreakm && m_mode && except == EXCEPT_EBREAK ||
|
||||
dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK
|
||||
dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK ||
|
||||
except_to_d_mode && except == EXCEPT_EBREAK
|
||||
);
|
||||
|
||||
// Note exception-like causes (trigger, ebreak) are higher priority than
|
||||
|
@ -1243,7 +1320,7 @@ wire want_halt_irq_if_no_exception = DEBUG_SUPPORT && !debug_mode && !want_halt_
|
|||
wire want_halt_irq = want_halt_irq_if_no_exception && !halt_delayed_by_exception;
|
||||
|
||||
assign dcause_next =
|
||||
// Trigger would be highest priority if implemented
|
||||
except == EXCEPT_EBREAK && except_to_d_mode ? 3'h2 : // trigger (priority 4)
|
||||
except == EXCEPT_EBREAK ? 3'h1 : // ebreak (priority 3)
|
||||
dbg_req_halt_prev || (dbg_req_halt_on_reset && have_just_reset) ? 3'h3 : // halt or reset-halt (priority 1, 2)
|
||||
3'h4; // single-step (priority 0)
|
||||
|
@ -1346,6 +1423,10 @@ assign mcause_code_next = exception_req_any ? except : mcause_irq_num;
|
|||
assign pmp_cfg_addr = addr;
|
||||
assign pmp_cfg_wdata = wdata_update;
|
||||
|
||||
assign trig_cfg_addr = addr;
|
||||
assign trig_cfg_wdata = wdata_update;
|
||||
assign trig_m_en = tcontrol_mte;
|
||||
|
||||
// Effective privilege for execution. Used for:
|
||||
// - Privilege level of branch target fetches (frontend keeps fetch privilege
|
||||
// constant during sequential fetch)
|
||||
|
@ -1441,6 +1522,11 @@ always @ (posedge clk) begin
|
|||
assert(!trap_is_irq);
|
||||
end
|
||||
|
||||
// This must be a breakpoint exception (presumably from the trigger unit).
|
||||
if (except_to_d_mode) begin
|
||||
assert(except == EXCEPT_EBREAK);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
`endif
|
||||
|
|
|
@ -188,6 +188,11 @@ localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control
|
|||
// Trigger Module
|
||||
|
||||
localparam TSELECT = 12'h7a0;
|
||||
localparam TDATA1 = 12'h7a1;
|
||||
localparam TDATA2 = 12'h7a2;
|
||||
localparam TINFO = 12'h7a4;
|
||||
localparam TCONTROL = 12'h7a5;
|
||||
localparam MCONTEXT = 12'h7a8;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// D-mode CSRs
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
/*****************************************************************************\
|
||||
| Copyright (C) 2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
`default_nettype none
|
||||
|
||||
// Trigger unit. Currently only breakpoint (type=2 execute=1 select=0)
|
||||
// triggers are supported. Only exact address matches are supported, and the
|
||||
// timing is always "early".
|
||||
|
||||
module hazard3_triggers #(
|
||||
`include "hazard3_config.vh"
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
|
||||
// Config interface passed through CSR block
|
||||
input wire [11:0] cfg_addr,
|
||||
input wire cfg_wen,
|
||||
input wire [W_DATA-1:0] cfg_wdata,
|
||||
output reg [W_DATA-1:0] cfg_rdata,
|
||||
|
||||
// Global trigger-to-M-mode enable (e.g. from tcontrol or mstatus.mie)
|
||||
input wire trig_m_en,
|
||||
|
||||
// PC query
|
||||
input wire [W_ADDR-1:0] pc,
|
||||
input wire m_mode,
|
||||
input wire d_mode,
|
||||
|
||||
// Break request
|
||||
output wire break,
|
||||
output wire break_d_mode
|
||||
);
|
||||
|
||||
`include "hazard3_csr_addr.vh"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration state
|
||||
|
||||
parameter W_TSELECT = $clog2(BREAKPOINT_TRIGGERS);
|
||||
|
||||
reg [W_TSELECT-1:0] tselect;
|
||||
|
||||
// Note tdata1 and mcontrol are the same CSR. tdata1 refers to the universal
|
||||
// fields (type/dmode) and mcontrol refers to those fields specific to
|
||||
// type=2 (address/data match), the only trigger type we implement.
|
||||
|
||||
reg tdata1_dmode [0:BREAKPOINT_TRIGGERS-1];
|
||||
reg mcontrol_action [0:BREAKPOINT_TRIGGERS-1];
|
||||
reg mcontrol_m [0:BREAKPOINT_TRIGGERS-1];
|
||||
reg mcontrol_u [0:BREAKPOINT_TRIGGERS-1];
|
||||
reg [W_DATA-1:0] tdata2 [0:BREAKPOINT_TRIGGERS-1];
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration write port
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin: cfg_update
|
||||
integer i;
|
||||
if (!rst_n) begin
|
||||
tselect <= {W_TSELECT{1'b0}};
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
tdata1_dmode[i] <= 1'b0;
|
||||
mcontrol_action[i] <= 1'b0;
|
||||
mcontrol_m[i] <= 1'b0;
|
||||
mcontrol_u[i] <= 1'b0;
|
||||
tdata2[i] <= {W_DATA{1'b0}};
|
||||
end
|
||||
end else if (cfg_wen && cfg_addr == TSELECT) begin
|
||||
tselect <= cfg_wdata[W_TSELECT-1:0];
|
||||
end else if (cfg_wen && tselect < BREAKPOINT_TRIGGERS && !(tdata1_dmode[i] && !d_mode)) begin
|
||||
// Handle writes to tselect-indexed registers (note writes to D-mode
|
||||
// triggers in non-D-mode are ignored rather than raising an exception)
|
||||
if (cfg_addr == TDATA1) begin
|
||||
tdata1_dmode[tselect] <= cfg_wdata[27];
|
||||
mcontrol_action[tselect] <= cfg_wdata[12];
|
||||
mcontrol_m[tselect] <= cfg_wdata[6];
|
||||
mcontrol_u[tselect] <= cfg_wdata[3];
|
||||
end else if (cfg_addr == TDATA2) begin
|
||||
tdata2[tselect] <= cfg_wdata;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Configuration read port
|
||||
|
||||
always @ (*) begin
|
||||
cfg_rdata = {W_DATA{1'b0}};
|
||||
if (cfg_addr == TSELECT) begin
|
||||
cfg_rdata = {{W_DATA-W_TSELECT{1'b0}}, tselect};
|
||||
end else if (cfg_addr == TDATA1) begin
|
||||
if (tselect >= BREAKPOINT_TRIGGERS) begin
|
||||
// Nonexistent -> type=0
|
||||
cfg_rdata = {W_DATA{1'b0}};
|
||||
end else begin
|
||||
cfg_rdata = {
|
||||
4'h2, // type = address/data match
|
||||
tdata1_dmode[tselect],
|
||||
6'h00, // maskmax = 0, exact match only
|
||||
1'b0, // hit = 0, not implemented
|
||||
1'b0, // select = 0, address match only
|
||||
1'b0, // timing = 0, trigger before execution
|
||||
2'h0, // sizelo = 0, unsized
|
||||
{3'h0, mcontrol_action[tselect]}, // action = 0/1, break to M-mode/D-mode
|
||||
1'b0, // chain = 0, chaining is useless for exact matches
|
||||
3'h0, // match = 0, exact match only
|
||||
mcontrol_m[tselect],
|
||||
1'b0,
|
||||
1'b0, // s = 0, no S-mode
|
||||
mcontrol_u[tselect],
|
||||
1'b1, // execute = 1, this is a breakpoint
|
||||
1'b0, // store = 0, this is not a watchpoint
|
||||
1'b0 // load = 0, this is not a watchpoint
|
||||
};
|
||||
end
|
||||
end else if (cfg_rdata == TDATA2) begin
|
||||
if (tselect >= BREAKPOINT_TRIGGERS) begin
|
||||
cfg_rdata = {W_DATA{1'b0}};
|
||||
end else begin
|
||||
cfg_rdata = tdata2[tselect];
|
||||
end
|
||||
end else if (cfg_rdata == TINFO) begin
|
||||
if (tselect >= BREAKPOINT_TRIGGERS) begin
|
||||
cfg_rdata = 32'h00000001; // type = 0, no trigger
|
||||
end else begin
|
||||
cfg_rdata = 32'h00000004; // type = 2, address/data match
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Trigger match logic
|
||||
|
||||
reg [BREAKPOINT_TRIGGERS-1:0] want_d_mode_break;
|
||||
reg [BREAKPOINT_TRIGGERS-1:0] want_m_mode_break;
|
||||
|
||||
always @ (*) begin: match_pc
|
||||
integer i;
|
||||
want_m_mode_break = {BREAKPOINT_TRIGGERS{1'b0}};
|
||||
want_d_mode_break = {BREAKPOINT_TRIGGERS{1'b0}};
|
||||
for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin
|
||||
if (tdata2[i] == pc && !d_mode && (m_mode ? mcontrol_m[tselect] : mcontrol_u[tselect])) begin
|
||||
want_d_mode_break[i] = mcontrol_action[i] && tdata1_dmode[i];
|
||||
want_m_mode_break[i] = !mcontrol_action[i] && trig_m_en;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign break = |want_m_mode_break || |want_d_mode_break;
|
||||
assign break_d_mode = |want_d_mode_break;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
|
@ -24,8 +24,6 @@ CSR was 302
|
|||
-> exception, mcause = 2
|
||||
CSR was 303
|
||||
-> exception, mcause = 2
|
||||
CSR was 7a1
|
||||
-> exception, mcause = 2
|
||||
CSR was 7b0
|
||||
-> exception, mcause = 2
|
||||
CSR was 7b1
|
||||
|
@ -132,6 +130,9 @@ int main() {
|
|||
(void)read_csr(mhpmevent3);
|
||||
(void)read_csr(tselect);
|
||||
(void)read_csr(tdata1);
|
||||
(void)read_csr(tdata2);
|
||||
(void)read_csr(tinfo);
|
||||
(void)read_csr(tcontrol);
|
||||
(void)read_csr(dcsr);
|
||||
(void)read_csr(dpc);
|
||||
(void)read_csr(dscratch0);
|
||||
|
|
|
@ -12,6 +12,7 @@ int main() {
|
|||
tb_assert(NUM_IRQS <= 32, "Test invalid for >32 IRQs");
|
||||
global_irq_enable(true);
|
||||
external_irq_enable(true);
|
||||
|
||||
// Dry run: Check that IRQ force array can be written/read and that it
|
||||
// sets the pending array appropriately
|
||||
for (int i = 0; i < NUM_IRQS; ++i) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// the next descending priority
|
||||
// - Each bottom half hander re-pends the top half handler (which immediately
|
||||
// preempts it) to set the next lower-numbered bottom handler
|
||||
// - When top returns to bottom, and that bottom returns to the *next* bottom,
|
||||
// - Then top returns to bottom, and that bottom returns to the *next* bottom,
|
||||
// which re-pends the top
|
||||
// - So on until all handlers have fired
|
||||
// - This should stack within two exception frames and enter the EIRQ vector
|
||||
|
|
|
@ -1,32 +1,33 @@
|
|||
TOP := tb
|
||||
DOTF := tb.f
|
||||
TOP := tb
|
||||
DOTF := tb.f
|
||||
|
||||
CPU_RESET_VECTOR := 32'h40
|
||||
CPU_RESET_VECTOR := 32'h40
|
||||
|
||||
EXTENSION_C := 1
|
||||
EXTENSION_M := 1
|
||||
EXTENSION_ZBA := 1
|
||||
EXTENSION_ZBB := 1
|
||||
EXTENSION_ZBC := 1
|
||||
EXTENSION_ZBS := 1
|
||||
EXTENSION_C := 1
|
||||
EXTENSION_M := 1
|
||||
EXTENSION_ZBA := 1
|
||||
EXTENSION_ZBB := 1
|
||||
EXTENSION_ZBC := 1
|
||||
EXTENSION_ZBS := 1
|
||||
|
||||
DEBUG_SUPPORT := 1
|
||||
U_MODE := 1
|
||||
PMP_REGIONS := 4
|
||||
DEBUG_SUPPORT := 1
|
||||
BREAKPOINT_TRIGGERS := 4
|
||||
U_MODE := 1
|
||||
PMP_REGIONS := 4
|
||||
|
||||
NUM_IRQS := 32
|
||||
IRQ_PRIORITY_BITS := 4
|
||||
NUM_IRQS := 32
|
||||
IRQ_PRIORITY_BITS := 4
|
||||
|
||||
MULDIV_UNROLL := 2
|
||||
MUL_FAST := 1
|
||||
MUL_FASTER := 1
|
||||
MULH_FAST := 1
|
||||
FAST_BRANCHCMP := 1
|
||||
REDUCED_BYPASS := 0
|
||||
MULDIV_UNROLL := 2
|
||||
MUL_FAST := 1
|
||||
MUL_FASTER := 1
|
||||
MULH_FAST := 1
|
||||
FAST_BRANCHCMP := 1
|
||||
REDUCED_BYPASS := 0
|
||||
|
||||
MVENDORID_VAL := 32'hdeadbeef
|
||||
MIMPID_VAL := 32'h12345678
|
||||
MCONFIGPTR_VAL := 32'h9abcdef0
|
||||
MVENDORID_VAL := 32'hdeadbeef
|
||||
MIMPID_VAL := 32'h12345678
|
||||
MCONFIGPTR_VAL := 32'h9abcdef0
|
||||
|
||||
.PHONY: clean all
|
||||
|
||||
|
@ -40,6 +41,7 @@ SYNTH_CMD += chparam -set EXTENSION_ZBB $(EXTENSION_ZBB) $(TOP);
|
|||
SYNTH_CMD += chparam -set EXTENSION_ZBC $(EXTENSION_ZBC) $(TOP);
|
||||
SYNTH_CMD += chparam -set EXTENSION_ZBS $(EXTENSION_ZBS) $(TOP);
|
||||
SYNTH_CMD += chparam -set DEBUG_SUPPORT $(DEBUG_SUPPORT) $(TOP);
|
||||
SYNTH_CMD += chparam -set BREAKPOINT_TRIGGERS $(BREAKPOINT_TRIGGERS) $(TOP);
|
||||
SYNTH_CMD += chparam -set U_MODE $(U_MODE) $(TOP);
|
||||
SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP);
|
||||
SYNTH_CMD += chparam -set NUM_IRQS $(NUM_IRQS) $(TOP);
|
||||
|
|
Loading…
Reference in New Issue