diff --git a/doc/03_reference/cs_registers.rst b/doc/03_reference/cs_registers.rst index a108e3cf..5294ac52 100644 --- a/doc/03_reference/cs_registers.rst +++ b/doc/03_reference/cs_registers.rst @@ -309,7 +309,11 @@ Reset Value: ``0x0000_0000`` Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter). -Ibex implements a single trigger, therefore this register will always read as zero. +Number of the currently selected trigger starting at 0. +The number of triggers is configured by the DbgHwNumLen parameter. + +Writing a value larger than or equal to the number of supported triggers will write the highest valid index. +This allows a debugger to detect the allowed number of triggers by reading back the value. .. _csr-tdata1: diff --git a/doc/03_reference/debug.rst b/doc/03_reference/debug.rst index a0801a43..aef413a7 100644 --- a/doc/03_reference/debug.rst +++ b/doc/03_reference/debug.rst @@ -38,6 +38,8 @@ Parameters +---------------------+-----------------------------------------------------------------+ | ``DbgTriggerEn`` | Enable support for debug triggers | +---------------------+-----------------------------------------------------------------+ +| ``DbgHwBreakNum`` | Number of debug triggers | ++---------------------+-----------------------------------------------------------------+ Core Debug Registers -------------------- diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index eb495e5c..cffb75ed 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -28,6 +28,7 @@ module ibex_core #( parameter bit ICacheECC = 1'b0, parameter bit BranchPredictor = 1'b0, parameter bit DbgTriggerEn = 1'b0, + parameter int unsigned DbgHwBreakNum = 1, parameter bit SecureIbex = 1'b0, parameter int unsigned DmHaltAddr = 32'h1A110800, parameter int unsigned DmExceptionAddr = 32'h1A110808 @@ -954,6 +955,7 @@ module ibex_core #( ibex_cs_registers #( .DbgTriggerEn ( DbgTriggerEn ), + .DbgHwBreakNum ( DbgHwBreakNum ), .DataIndTiming ( DataIndTiming ), .DummyInstructions ( DummyInstructions ), .ICache ( ICache ), diff --git a/rtl/ibex_core_tracing.sv b/rtl/ibex_core_tracing.sv index e731b9d6..a14eb5af 100644 --- a/rtl/ibex_core_tracing.sv +++ b/rtl/ibex_core_tracing.sv @@ -22,6 +22,7 @@ module ibex_core_tracing #( parameter bit ICacheECC = 1'b0, parameter bit BranchPredictor = 1'b0, parameter bit DbgTriggerEn = 1'b0, + parameter int unsigned DbgHwBreakNum = 1, parameter bit SecureIbex = 1'b0, parameter int unsigned DmHaltAddr = 32'h1A110800, parameter int unsigned DmExceptionAddr = 32'h1A110808 @@ -118,6 +119,7 @@ module ibex_core_tracing #( .ICacheECC ( ICacheECC ), .BranchPredictor ( BranchPredictor ), .DbgTriggerEn ( DbgTriggerEn ), + .DbgHwBreakNum ( DbgHwBreakNum ), .WritebackStage ( WritebackStage ), .SecureIbex ( SecureIbex ), .DmHaltAddr ( DmHaltAddr ), diff --git a/rtl/ibex_cs_registers.sv b/rtl/ibex_cs_registers.sv index 64fd64dc..9ac9dcbf 100644 --- a/rtl/ibex_cs_registers.sv +++ b/rtl/ibex_cs_registers.sv @@ -14,6 +14,7 @@ module ibex_cs_registers #( parameter bit DbgTriggerEn = 0, + parameter int unsigned DbgHwBreakNum = 1, parameter bit DataIndTiming = 1'b0, parameter bit DummyInstructions = 1'b0, parameter bit ICache = 1'b0, @@ -1199,75 +1200,109 @@ module ibex_cs_registers #( ///////////////////////////// if (DbgTriggerEn) begin : gen_trigger_regs + localparam int unsigned DbgHwNumLen = DbgHwBreakNum > 1 ? $clog2(DbgHwBreakNum) : 1; // Register values - logic tmatch_control_d, tmatch_control_q; - logic [31:0] tmatch_value_d, tmatch_value_q; + logic [DbgHwNumLen-1:0] tselect_d, tselect_q; + logic tmatch_control_d; + logic [DbgHwBreakNum-1:0] tmatch_control_q; + logic [31:0] tmatch_value_d; + logic [31:0] tmatch_value_q[DbgHwBreakNum]; // Write enables - logic tmatch_control_we; - logic tmatch_value_we; + logic tselect_we; + logic [DbgHwBreakNum-1:0] tmatch_control_we; + logic [DbgHwBreakNum-1:0] tmatch_value_we; + // Trigger comparison result + logic [DbgHwBreakNum-1:0] trigger_match; // Write select - assign tmatch_control_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TDATA1); - assign tmatch_value_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TDATA2); + assign tselect_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TSELECT); + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_we + assign tmatch_control_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & + (csr_addr_i == CSR_TDATA1); + assign tmatch_value_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & + (csr_addr_i == CSR_TDATA2); + end + // Debug interface tests the available number of triggers by writing and reading the trigger + // select register. Only allow changes to the register if it is within the supported region. + assign tselect_d = (csr_wdata_int < DbgHwBreakNum) ? csr_wdata_int[DbgHwNumLen-1:0] : + DbgHwBreakNum-1; // tmatch_control is enabled when the execute bit is set assign tmatch_control_d = csr_wdata_int[2]; assign tmatch_value_d = csr_wdata_int[31:0]; // Registers ibex_csr #( - .Width (1), + .Width (DbgHwNumLen), .ShadowCopy (1'b0), .ResetValue ('0) - ) u_tmatch_control_csr ( + ) u_tselect_csr ( .clk_i (clk_i), .rst_ni (rst_ni), - .wr_data_i (tmatch_control_d), - .wr_en_i (tmatch_control_we), - .rd_data_o (tmatch_control_q), + .wr_data_i (tselect_d), + .wr_en_i (tselect_we), + .rd_data_o (tselect_q), .rd_error_o () ); - ibex_csr #( - .Width (32), - .ShadowCopy (1'b0), - .ResetValue ('0) - ) u_tmatch_value_csr ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .wr_data_i (tmatch_value_d), - .wr_en_i (tmatch_value_we), - .rd_data_o (tmatch_value_q), - .rd_error_o () + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_reg + ibex_csr #( + .Width (1), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_tmatch_control_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (tmatch_control_d), + .wr_en_i (tmatch_control_we[i]), + .rd_data_o (tmatch_control_q[i]), + .rd_error_o () ); + ibex_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_tmatch_value_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (tmatch_value_d), + .wr_en_i (tmatch_value_we[i]), + .rd_data_o (tmatch_value_q[i]), + .rd_error_o () + ); + end + // Assign read data - // TSELECT - only one supported - assign tselect_rdata = 'b0; + // TSELECT - number of supported triggers defined by parameter DbgHwBreakNum + assign tselect_rdata = {'b0, tselect_q}; // TDATA0 - only support simple address matching - assign tmatch_control_rdata = {4'h2, // type : address/data match - 1'b1, // dmode : access from D mode only - 6'h00, // maskmax : exact match only - 1'b0, // hit : not supported - 1'b0, // select : address match only - 1'b0, // timing : match before execution - 2'b00, // sizelo : match any access - 4'h1, // action : enter debug mode - 1'b0, // chain : not supported - 4'h0, // match : simple match - 1'b1, // m : match in m-mode - 1'b0, // 0 : zero - 1'b0, // s : not supported - 1'b1, // u : match in u-mode - tmatch_control_q, // execute : match instruction address - 1'b0, // store : not supported - 1'b0}; // load : not supported + assign tmatch_control_rdata = {4'h2, // type : address/data match + 1'b1, // dmode : access from D mode only + 6'h00, // maskmax : exact match only + 1'b0, // hit : not supported + 1'b0, // select : address match only + 1'b0, // timing : match before execution + 2'b00, // sizelo : match any access + 4'h1, // action : enter debug mode + 1'b0, // chain : not supported + 4'h0, // match : simple match + 1'b1, // m : match in m-mode + 1'b0, // 0 : zero + 1'b0, // s : not supported + 1'b1, // u : match in u-mode + tmatch_control_q[tselect_q], // execute : match instruction address + 1'b0, // store : not supported + 1'b0}; // load : not supported // TDATA1 - address match value only - assign tmatch_value_rdata = tmatch_value_q; + assign tmatch_value_rdata = tmatch_value_q[tselect_q]; // Breakpoint matching // We match against the next address, as the breakpoint must be taken before execution - assign trigger_match_o = tmatch_control_q & (pc_if_i[31:0] == tmatch_value_q[31:0]); + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_trigger_match + assign trigger_match[i] = tmatch_control_q[i] & (pc_if_i[31:0] == tmatch_value_q[i]); + end + assign trigger_match_o = |trigger_match; end else begin : gen_no_trigger_regs assign tselect_rdata = 'b0;