Add support for additional HW breakpoints

Add parameter `DbgHwBreakNum` to configure the number of HW breakpoints.
The parameters controls the number of trigger registers available if
debug support is enabled with `DbgTriggerEn`.

Closes #1070
This commit is contained in:
Tobias Wölfel 2020-09-29 18:27:42 +02:00 committed by Tobias Wölfel
parent dbd92c5d4b
commit 4431023516
5 changed files with 89 additions and 44 deletions

View file

@ -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:

View file

@ -38,6 +38,8 @@ Parameters
+---------------------+-----------------------------------------------------------------+
| ``DbgTriggerEn`` | Enable support for debug triggers |
+---------------------+-----------------------------------------------------------------+
| ``DbgHwBreakNum`` | Number of debug triggers |
+---------------------+-----------------------------------------------------------------+
Core Debug Registers
--------------------

View file

@ -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 ),

View file

@ -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 ),

View file

@ -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;