From 6e3799eed0839d34d6c743bede045a1be85a04ed Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Mon, 22 Aug 2022 08:45:06 +0100 Subject: [PATCH] First attempt at hacking in triggers, at least seems to have not broken other exception logic. Not yet tested. --- hdl/hazard3.f | 1 + hdl/hazard3_config.vh | 4 + hdl/hazard3_config_inst.vh | 79 +++++++------ hdl/hazard3_core.v | 63 +++++++++- hdl/hazard3_csr.v | 102 ++++++++++++++-- hdl/hazard3_csr_addr.vh | 5 + hdl/hazard3_triggers.v | 158 +++++++++++++++++++++++++ test/sim/sw_testcases/csr_readable.c | 5 +- test/sim/sw_testcases/irq_force.c | 1 + test/sim/sw_testcases/irq_top_bottom.c | 2 +- test/sim/tb_cxxrtl/Makefile | 48 ++++---- 11 files changed, 391 insertions(+), 77 deletions(-) create mode 100644 hdl/hazard3_triggers.v diff --git a/hdl/hazard3.f b/hdl/hazard3.f index e104df4..431d3ef 100644 --- a/hdl/hazard3.f +++ b/hdl/hazard3.f @@ -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 . diff --git a/hdl/hazard3_config.vh b/hdl/hazard3_config.vh index 50d7d84..8b8a36c 100644 --- a/hdl/hazard3_config.vh +++ b/hdl/hazard3_config.vh @@ -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 diff --git a/hdl/hazard3_config_inst.vh b/hdl/hazard3_config_inst.vh index 55526c1..127619c 100644 --- a/hdl/hazard3_config_inst.vh +++ b/hdl/hazard3_config_inst.vh @@ -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 diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index c59659c..169b828 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -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 diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index 5e699b9..664f879 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -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 diff --git a/hdl/hazard3_csr_addr.vh b/hdl/hazard3_csr_addr.vh index 21cc984..ed30207 100644 --- a/hdl/hazard3_csr_addr.vh +++ b/hdl/hazard3_csr_addr.vh @@ -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 diff --git a/hdl/hazard3_triggers.v b/hdl/hazard3_triggers.v new file mode 100644 index 0000000..6b72a5e --- /dev/null +++ b/hdl/hazard3_triggers.v @@ -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 diff --git a/test/sim/sw_testcases/csr_readable.c b/test/sim/sw_testcases/csr_readable.c index 0031c2f..9f360d5 100644 --- a/test/sim/sw_testcases/csr_readable.c +++ b/test/sim/sw_testcases/csr_readable.c @@ -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); diff --git a/test/sim/sw_testcases/irq_force.c b/test/sim/sw_testcases/irq_force.c index dea7637..34eebfe 100644 --- a/test/sim/sw_testcases/irq_force.c +++ b/test/sim/sw_testcases/irq_force.c @@ -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) { diff --git a/test/sim/sw_testcases/irq_top_bottom.c b/test/sim/sw_testcases/irq_top_bottom.c index f47163e..ba3a9ab 100644 --- a/test/sim/sw_testcases/irq_top_bottom.c +++ b/test/sim/sw_testcases/irq_top_bottom.c @@ -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 diff --git a/test/sim/tb_cxxrtl/Makefile b/test/sim/tb_cxxrtl/Makefile index 96db795..5106dd9 100644 --- a/test/sim/tb_cxxrtl/Makefile +++ b/test/sim/tb_cxxrtl/Makefile @@ -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);