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_csr.v
|
||||||
file hazard3_regfile_1w2r.v
|
file hazard3_regfile_1w2r.v
|
||||||
file hazard3_pmp.v
|
file hazard3_pmp.v
|
||||||
|
file hazard3_triggers.v
|
||||||
include .
|
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.
|
// Requires: CSR_M_MANDATORY, CSR_M_TRAP.
|
||||||
parameter DEBUG_SUPPORT = 0,
|
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
|
// External interrupt support
|
||||||
|
|
||||||
|
|
|
@ -12,47 +12,48 @@
|
||||||
// except for MHARTID_VAL. It must be defined once before each include of
|
// except for MHARTID_VAL. It must be defined once before each include of
|
||||||
// this file.
|
// this file.
|
||||||
|
|
||||||
.RESET_VECTOR (RESET_VECTOR),
|
.RESET_VECTOR (RESET_VECTOR),
|
||||||
.MTVEC_INIT (MTVEC_INIT),
|
.MTVEC_INIT (MTVEC_INIT),
|
||||||
.EXTENSION_A (EXTENSION_A),
|
.EXTENSION_A (EXTENSION_A),
|
||||||
.EXTENSION_C (EXTENSION_C),
|
.EXTENSION_C (EXTENSION_C),
|
||||||
.EXTENSION_M (EXTENSION_M),
|
.EXTENSION_M (EXTENSION_M),
|
||||||
.EXTENSION_ZBA (EXTENSION_ZBA),
|
.EXTENSION_ZBA (EXTENSION_ZBA),
|
||||||
.EXTENSION_ZBB (EXTENSION_ZBB),
|
.EXTENSION_ZBB (EXTENSION_ZBB),
|
||||||
.EXTENSION_ZBC (EXTENSION_ZBC),
|
.EXTENSION_ZBC (EXTENSION_ZBC),
|
||||||
.EXTENSION_ZBS (EXTENSION_ZBS),
|
.EXTENSION_ZBS (EXTENSION_ZBS),
|
||||||
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
.EXTENSION_ZBKB (EXTENSION_ZBKB),
|
||||||
.EXTENSION_XH3B (EXTENSION_XH3B),
|
.EXTENSION_XH3B (EXTENSION_XH3B),
|
||||||
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
.EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI),
|
||||||
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
.CSR_M_MANDATORY (CSR_M_MANDATORY),
|
||||||
.CSR_M_TRAP (CSR_M_TRAP),
|
.CSR_M_TRAP (CSR_M_TRAP),
|
||||||
.CSR_COUNTER (CSR_COUNTER),
|
.CSR_COUNTER (CSR_COUNTER),
|
||||||
.U_MODE (U_MODE),
|
.U_MODE (U_MODE),
|
||||||
.PMP_REGIONS (PMP_REGIONS),
|
.PMP_REGIONS (PMP_REGIONS),
|
||||||
.PMP_GRAIN (PMP_GRAIN),
|
.PMP_GRAIN (PMP_GRAIN),
|
||||||
.PMP_HARDWIRED (PMP_HARDWIRED),
|
.PMP_HARDWIRED (PMP_HARDWIRED),
|
||||||
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
.PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR),
|
||||||
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
.PMP_HARDWIRED_CFG (PMP_HARDWIRED_CFG),
|
||||||
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
.DEBUG_SUPPORT (DEBUG_SUPPORT),
|
||||||
.NUM_IRQS (NUM_IRQS),
|
.BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS),
|
||||||
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
.NUM_IRQS (NUM_IRQS),
|
||||||
.MVENDORID_VAL (MVENDORID_VAL),
|
.IRQ_PRIORITY_BITS (IRQ_PRIORITY_BITS),
|
||||||
.MIMPID_VAL (MIMPID_VAL),
|
.MVENDORID_VAL (MVENDORID_VAL),
|
||||||
|
.MIMPID_VAL (MIMPID_VAL),
|
||||||
`ifndef HAZARD3_CONFIG_INST_NO_MHARTID
|
`ifndef HAZARD3_CONFIG_INST_NO_MHARTID
|
||||||
.MHARTID_VAL (MHARTID_VAL),
|
.MHARTID_VAL (MHARTID_VAL),
|
||||||
`endif
|
`endif
|
||||||
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
.MCONFIGPTR_VAL (MCONFIGPTR_VAL),
|
||||||
.REDUCED_BYPASS (REDUCED_BYPASS),
|
.REDUCED_BYPASS (REDUCED_BYPASS),
|
||||||
.MULDIV_UNROLL (MULDIV_UNROLL),
|
.MULDIV_UNROLL (MULDIV_UNROLL),
|
||||||
.MUL_FAST (MUL_FAST),
|
.MUL_FAST (MUL_FAST),
|
||||||
.MUL_FASTER (MUL_FASTER),
|
.MUL_FASTER (MUL_FASTER),
|
||||||
.MULH_FAST (MULH_FAST),
|
.MULH_FAST (MULH_FAST),
|
||||||
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
.FAST_BRANCHCMP (FAST_BRANCHCMP),
|
||||||
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
.BRANCH_PREDICTOR (BRANCH_PREDICTOR),
|
||||||
.MTVEC_WMASK (MTVEC_WMASK),
|
.MTVEC_WMASK (MTVEC_WMASK),
|
||||||
.RESET_REGFILE (RESET_REGFILE),
|
.RESET_REGFILE (RESET_REGFILE),
|
||||||
.W_ADDR (W_ADDR),
|
.W_ADDR (W_ADDR),
|
||||||
.W_DATA (W_DATA)
|
.W_DATA (W_DATA)
|
||||||
|
|
||||||
`ifdef HAZARD3_CONFIG_INST_NO_MHARTID
|
`ifdef HAZARD3_CONFIG_INST_NO_MHARTID
|
||||||
`undef 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 [1:0] xm_addr_align;
|
||||||
reg [W_MEMOP-1:0] xm_memop;
|
reg [W_MEMOP-1:0] xm_memop;
|
||||||
reg [W_EXCEPT-1:0] xm_except;
|
reg [W_EXCEPT-1:0] xm_except;
|
||||||
|
reg xm_except_to_d_mode;
|
||||||
reg xm_wfi;
|
reg xm_wfi;
|
||||||
reg xm_delay_irq_entry;
|
reg xm_delay_irq_entry;
|
||||||
|
|
||||||
|
@ -377,6 +378,11 @@ assign x_stall =
|
||||||
|
|
||||||
wire m_wfi_stall_clear;
|
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
|
// Execution logic
|
||||||
|
|
||||||
|
@ -465,9 +471,6 @@ hazard3_alu #(
|
||||||
|
|
||||||
// Load/store bus request
|
// Load/store bus request
|
||||||
|
|
||||||
wire x_loadstore_pmp_fail;
|
|
||||||
wire x_exec_pmp_fail;
|
|
||||||
|
|
||||||
wire x_unaligned_addr = d_memop != MEMOP_NONE && (
|
wire x_unaligned_addr = d_memop != MEMOP_NONE && (
|
||||||
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
||||||
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0]
|
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0]
|
||||||
|
@ -608,6 +611,7 @@ always @ (*) begin
|
||||||
x_stall_on_exclusive_overlap ||
|
x_stall_on_exclusive_overlap ||
|
||||||
x_loadstore_pmp_fail ||
|
x_loadstore_pmp_fail ||
|
||||||
x_exec_pmp_fail ||
|
x_exec_pmp_fail ||
|
||||||
|
x_trig_break ||
|
||||||
x_unaligned_addr ||
|
x_unaligned_addr ||
|
||||||
m_trap_enter_soon ||
|
m_trap_enter_soon ||
|
||||||
(xm_wfi && !m_wfi_stall_clear) // FIXME will cause a timing issue, better to stall til *after* clear
|
(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
|
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 && (
|
assign x_btb_set = |BRANCH_PREDICTOR && (
|
||||||
x_jump_req_unchecked && d_addr_offs[W_ADDR - 1] && !x_branch_was_predicted &&
|
x_jump_req_unchecked && d_addr_offs[W_ADDR - 1] && !x_branch_was_predicted &&
|
||||||
|
@ -809,6 +813,46 @@ end else begin: no_pmp
|
||||||
end
|
end
|
||||||
endgenerate
|
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
|
// CSRs and Trap Handling
|
||||||
|
|
||||||
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
|
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_ADDR-1:0] m_exception_return_addr;
|
||||||
|
|
||||||
wire [W_EXCEPT-1:0] x_except =
|
wire [W_EXCEPT-1:0] x_except =
|
||||||
|
x_trig_break ? EXCEPT_EBREAK :
|
||||||
x_exec_pmp_fail ? EXCEPT_INSTR_FAULT :
|
x_exec_pmp_fail ? EXCEPT_INSTR_FAULT :
|
||||||
x_jump_req_unchecked && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN :
|
x_jump_req_unchecked && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN :
|
||||||
x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL :
|
x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL :
|
||||||
|
@ -925,12 +970,19 @@ hazard3_csr #(
|
||||||
.irq_software (soft_irq),
|
.irq_software (soft_irq),
|
||||||
.irq_timer (timer_irq),
|
.irq_timer (timer_irq),
|
||||||
.except (xm_except),
|
.except (xm_except),
|
||||||
|
.except_to_d_mode (xm_except_to_d_mode),
|
||||||
|
|
||||||
.pmp_cfg_addr (x_pmp_cfg_addr),
|
.pmp_cfg_addr (x_pmp_cfg_addr),
|
||||||
.pmp_cfg_wen (x_pmp_cfg_wen),
|
.pmp_cfg_wen (x_pmp_cfg_wen),
|
||||||
.pmp_cfg_wdata (x_pmp_cfg_wdata),
|
.pmp_cfg_wdata (x_pmp_cfg_wdata),
|
||||||
.pmp_cfg_rdata (x_pmp_cfg_rdata),
|
.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
|
// Other CSR-specific signalling
|
||||||
.permit_wfi (x_permit_wfi),
|
.permit_wfi (x_permit_wfi),
|
||||||
.instr_ret (x_instr_ret)
|
.instr_ret (x_instr_ret)
|
||||||
|
@ -942,6 +994,7 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
if (!rst_n) begin
|
if (!rst_n) begin
|
||||||
xm_memop <= MEMOP_NONE;
|
xm_memop <= MEMOP_NONE;
|
||||||
xm_except <= EXCEPT_NONE;
|
xm_except <= EXCEPT_NONE;
|
||||||
|
xm_except_to_d_mode <= 1'b0;
|
||||||
xm_wfi <= 1'b0;
|
xm_wfi <= 1'b0;
|
||||||
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
||||||
end else begin
|
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.
|
// 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_memop <= x_unaligned_addr || x_exec_pmp_fail || x_loadstore_pmp_fail ? MEMOP_NONE : d_memop;
|
||||||
xm_except <= x_except;
|
xm_except <= x_except;
|
||||||
|
xm_except_to_d_mode <= x_trig_break_d_mode;
|
||||||
xm_wfi <= d_wfi && !x_exec_pmp_fail;
|
xm_wfi <= d_wfi && !x_exec_pmp_fail;
|
||||||
// Note the d_starved term is required because it is possible
|
// Note the d_starved term is required because it is possible
|
||||||
// (e.g. PMP X permission fail) to except when the frontend is
|
// (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_rd <= {W_REGADDR{1'b0}};
|
||||||
xm_memop <= MEMOP_NONE;
|
xm_memop <= MEMOP_NONE;
|
||||||
xm_except <= EXCEPT_NONE;
|
xm_except <= EXCEPT_NONE;
|
||||||
|
xm_except_to_d_mode <= 1'b0;
|
||||||
xm_wfi <= 1'b0;
|
xm_wfi <= 1'b0;
|
||||||
end
|
end
|
||||||
end else if (bus_dph_err_d) begin
|
end else if (bus_dph_err_d) begin
|
||||||
|
|
|
@ -89,6 +89,7 @@ module hazard3_csr #(
|
||||||
|
|
||||||
// Exceptions must *not* be a function of bus stall.
|
// Exceptions must *not* be a function of bus stall.
|
||||||
input wire [W_EXCEPT-1:0] except,
|
input wire [W_EXCEPT-1:0] except,
|
||||||
|
input wire except_to_d_mode,
|
||||||
|
|
||||||
// Level-sensitive interrupt sources
|
// Level-sensitive interrupt sources
|
||||||
input wire delay_irq_entry,
|
input wire delay_irq_entry,
|
||||||
|
@ -102,6 +103,13 @@ module hazard3_csr #(
|
||||||
output wire [W_DATA-1:0] pmp_cfg_wdata,
|
output wire [W_DATA-1:0] pmp_cfg_wdata,
|
||||||
input wire [W_DATA-1:0] pmp_cfg_rdata,
|
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
|
// Other CSR-specific signalling
|
||||||
output wire permit_wfi,
|
output wire permit_wfi,
|
||||||
input wire instr_ret
|
input wire instr_ret
|
||||||
|
@ -619,6 +627,25 @@ end
|
||||||
assign dbg_data0_wdata = wdata;
|
assign dbg_data0_wdata = wdata;
|
||||||
assign dbg_data0_wen = debug_mode && wen && addr == DMDATA0;
|
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
|
// Read port + detect addressing of unmapped CSRs
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -637,6 +664,7 @@ always @ (*) begin
|
||||||
decode_match = 1'b0;
|
decode_match = 1'b0;
|
||||||
rdata = {XLEN{1'b0}};
|
rdata = {XLEN{1'b0}};
|
||||||
pmp_cfg_wen = 1'b0;
|
pmp_cfg_wen = 1'b0;
|
||||||
|
trig_cfg_wen = 1'b0;
|
||||||
case (addr)
|
case (addr)
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -1047,12 +1075,61 @@ always @ (*) begin
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Trigger Module CSRs
|
// Trigger Module CSRs
|
||||||
|
|
||||||
// If triggers aren't supported, OpenOCD expects the following:
|
// If triggers aren't supported, we implement tselect as hardwired 0,
|
||||||
// - tselect must be present
|
// tinfo as hardwired 1 (meaning type=0 always) and tdata as hardwired 0
|
||||||
// - tselect must raise an exception when written to
|
// (meaning type=0). The debugger will see the first trigger as
|
||||||
// Otherwise it returns an error instead of 0 count when enumerating triggers
|
// unimplemented, and immediately halt discovery.
|
||||||
TSELECT: if (DEBUG_SUPPORT) begin
|
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
|
end
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -1218,10 +1295,10 @@ end
|
||||||
wire exception_req_any;
|
wire exception_req_any;
|
||||||
wire halt_delayed_by_exception = exception_req_any || loadstore_dphase_pending;
|
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 && (
|
wire want_halt_except = DEBUG_SUPPORT && !debug_mode && (
|
||||||
dcsr_ebreakm && m_mode && except == EXCEPT_EBREAK ||
|
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
|
// 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;
|
wire want_halt_irq = want_halt_irq_if_no_exception && !halt_delayed_by_exception;
|
||||||
|
|
||||||
assign dcause_next =
|
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)
|
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)
|
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)
|
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_addr = addr;
|
||||||
assign pmp_cfg_wdata = wdata_update;
|
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:
|
// Effective privilege for execution. Used for:
|
||||||
// - Privilege level of branch target fetches (frontend keeps fetch privilege
|
// - Privilege level of branch target fetches (frontend keeps fetch privilege
|
||||||
// constant during sequential fetch)
|
// constant during sequential fetch)
|
||||||
|
@ -1441,6 +1522,11 @@ always @ (posedge clk) begin
|
||||||
assert(!trap_is_irq);
|
assert(!trap_is_irq);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// This must be a breakpoint exception (presumably from the trigger unit).
|
||||||
|
if (except_to_d_mode) begin
|
||||||
|
assert(except == EXCEPT_EBREAK);
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
`endif
|
`endif
|
||||||
|
|
|
@ -188,6 +188,11 @@ localparam SLEEP = 12'h8f0; // U-mode subset of M-mode sleep control
|
||||||
// Trigger Module
|
// Trigger Module
|
||||||
|
|
||||||
localparam TSELECT = 12'h7a0;
|
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
|
// 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
|
-> exception, mcause = 2
|
||||||
CSR was 303
|
CSR was 303
|
||||||
-> exception, mcause = 2
|
-> exception, mcause = 2
|
||||||
CSR was 7a1
|
|
||||||
-> exception, mcause = 2
|
|
||||||
CSR was 7b0
|
CSR was 7b0
|
||||||
-> exception, mcause = 2
|
-> exception, mcause = 2
|
||||||
CSR was 7b1
|
CSR was 7b1
|
||||||
|
@ -132,6 +130,9 @@ int main() {
|
||||||
(void)read_csr(mhpmevent3);
|
(void)read_csr(mhpmevent3);
|
||||||
(void)read_csr(tselect);
|
(void)read_csr(tselect);
|
||||||
(void)read_csr(tdata1);
|
(void)read_csr(tdata1);
|
||||||
|
(void)read_csr(tdata2);
|
||||||
|
(void)read_csr(tinfo);
|
||||||
|
(void)read_csr(tcontrol);
|
||||||
(void)read_csr(dcsr);
|
(void)read_csr(dcsr);
|
||||||
(void)read_csr(dpc);
|
(void)read_csr(dpc);
|
||||||
(void)read_csr(dscratch0);
|
(void)read_csr(dscratch0);
|
||||||
|
|
|
@ -12,6 +12,7 @@ int main() {
|
||||||
tb_assert(NUM_IRQS <= 32, "Test invalid for >32 IRQs");
|
tb_assert(NUM_IRQS <= 32, "Test invalid for >32 IRQs");
|
||||||
global_irq_enable(true);
|
global_irq_enable(true);
|
||||||
external_irq_enable(true);
|
external_irq_enable(true);
|
||||||
|
|
||||||
// Dry run: Check that IRQ force array can be written/read and that it
|
// Dry run: Check that IRQ force array can be written/read and that it
|
||||||
// sets the pending array appropriately
|
// sets the pending array appropriately
|
||||||
for (int i = 0; i < NUM_IRQS; ++i) {
|
for (int i = 0; i < NUM_IRQS; ++i) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// the next descending priority
|
// the next descending priority
|
||||||
// - Each bottom half hander re-pends the top half handler (which immediately
|
// - Each bottom half hander re-pends the top half handler (which immediately
|
||||||
// preempts it) to set the next lower-numbered bottom handler
|
// 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
|
// which re-pends the top
|
||||||
// - So on until all handlers have fired
|
// - So on until all handlers have fired
|
||||||
// - This should stack within two exception frames and enter the EIRQ vector
|
// - This should stack within two exception frames and enter the EIRQ vector
|
||||||
|
|
|
@ -1,32 +1,33 @@
|
||||||
TOP := tb
|
TOP := tb
|
||||||
DOTF := tb.f
|
DOTF := tb.f
|
||||||
|
|
||||||
CPU_RESET_VECTOR := 32'h40
|
CPU_RESET_VECTOR := 32'h40
|
||||||
|
|
||||||
EXTENSION_C := 1
|
EXTENSION_C := 1
|
||||||
EXTENSION_M := 1
|
EXTENSION_M := 1
|
||||||
EXTENSION_ZBA := 1
|
EXTENSION_ZBA := 1
|
||||||
EXTENSION_ZBB := 1
|
EXTENSION_ZBB := 1
|
||||||
EXTENSION_ZBC := 1
|
EXTENSION_ZBC := 1
|
||||||
EXTENSION_ZBS := 1
|
EXTENSION_ZBS := 1
|
||||||
|
|
||||||
DEBUG_SUPPORT := 1
|
DEBUG_SUPPORT := 1
|
||||||
U_MODE := 1
|
BREAKPOINT_TRIGGERS := 4
|
||||||
PMP_REGIONS := 4
|
U_MODE := 1
|
||||||
|
PMP_REGIONS := 4
|
||||||
|
|
||||||
NUM_IRQS := 32
|
NUM_IRQS := 32
|
||||||
IRQ_PRIORITY_BITS := 4
|
IRQ_PRIORITY_BITS := 4
|
||||||
|
|
||||||
MULDIV_UNROLL := 2
|
MULDIV_UNROLL := 2
|
||||||
MUL_FAST := 1
|
MUL_FAST := 1
|
||||||
MUL_FASTER := 1
|
MUL_FASTER := 1
|
||||||
MULH_FAST := 1
|
MULH_FAST := 1
|
||||||
FAST_BRANCHCMP := 1
|
FAST_BRANCHCMP := 1
|
||||||
REDUCED_BYPASS := 0
|
REDUCED_BYPASS := 0
|
||||||
|
|
||||||
MVENDORID_VAL := 32'hdeadbeef
|
MVENDORID_VAL := 32'hdeadbeef
|
||||||
MIMPID_VAL := 32'h12345678
|
MIMPID_VAL := 32'h12345678
|
||||||
MCONFIGPTR_VAL := 32'h9abcdef0
|
MCONFIGPTR_VAL := 32'h9abcdef0
|
||||||
|
|
||||||
.PHONY: clean all
|
.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_ZBC $(EXTENSION_ZBC) $(TOP);
|
||||||
SYNTH_CMD += chparam -set EXTENSION_ZBS $(EXTENSION_ZBS) $(TOP);
|
SYNTH_CMD += chparam -set EXTENSION_ZBS $(EXTENSION_ZBS) $(TOP);
|
||||||
SYNTH_CMD += chparam -set DEBUG_SUPPORT $(DEBUG_SUPPORT) $(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 U_MODE $(U_MODE) $(TOP);
|
||||||
SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP);
|
SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP);
|
||||||
SYNTH_CMD += chparam -set NUM_IRQS $(NUM_IRQS) $(TOP);
|
SYNTH_CMD += chparam -set NUM_IRQS $(NUM_IRQS) $(TOP);
|
||||||
|
|
Loading…
Reference in New Issue