[dbg] Add minimal hardware breakpoint support

- Add the minimum amount of trigger system to support GDB hbreak
- Only a single trigger is implemented
- Only instruction address matching
- Only break into debug mode (no native debug)
- Fixes #382

Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
This commit is contained in:
Tom Roberts 2019-12-04 08:42:36 +00:00 committed by Tom Roberts
parent cd39a31498
commit 088cd11593
11 changed files with 275 additions and 8 deletions

View file

@ -46,6 +46,18 @@ Ibex implements all the Control and Status Registers (CSRs) listed in the follow
+---------+--------------------+--------+-----------------------------------------------+
| 0x3BF | ``pmpaddr15`` | WARL | PMP Address Register |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7A0 | ``tselect`` | WARL | Trigger Select Register |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7A1 | ``tdata1`` | WARL | Trigger Data Register 1 |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7A2 | ``tdata2`` | WARL | Trigger Data Register 2 |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7A3 | ``tdata3`` | WARL | Trigger Data Register 3 |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7A8 | ``mcontext`` | WARL | Machine Context Register |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7AA | ``scontext`` | WARL | Supervisor Context Register |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7B0 | ``dcsr`` | WARL | Debug Control and Status Register |
+---------+--------------------+--------+-----------------------------------------------+
| 0x7B1 | ``dpc`` | RW | Debug PC |
@ -282,6 +294,122 @@ Reset Value: ``0x0000_0000``
| address[33:2] |
+----------------+
.. _csr-tselect:
Trigger Select Register (tselect)
---------------------------------
CSR Address: ``0x7A0``
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.
.. _csr-tdata1:
Trigger Data Register 1 (tdata1)
--------------------------------
CSR Address: ``0x7A1``
Reset Value: ``0x2800_1000``
Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter).
Since native triggers are not supported, writes to this register from M-Mode will be ignored.
Ibex only implements one type of trigger, instruction address match.
Most fields of this register will read as a fixed value to reflect the mode that is supported.
+-------+------+------------------------------------------------------------------+
| Bit# | R/W | Description |
+-------+------+------------------------------------------------------------------+
| 31:28 | R | **type:** 2 = Address/Data match trigger type. |
+-------+------+------------------------------------------------------------------+
| 27 | R | **dmode:** 1 = Only debug mode can write tdata registers |
+-------+------+------------------------------------------------------------------+
| 26:21 | R | **maskmax:** 0 = Only exact matching supported. |
+-------+------+------------------------------------------------------------------+
| 20 | R | **hit:** 0 = Hit indication not supported. |
+-------+------+------------------------------------------------------------------+
| 19 | R | **select:** 0 = Only address matching is supported. |
+-------+------+------------------------------------------------------------------+
| 18 | R | **timing:** 0 = Break before the instruction at the specified |
| | | address. |
+-------+------+------------------------------------------------------------------+
| 17:16 | R | **sizelo:** 0 = Match accesses of any size. |
+-------+------+------------------------------------------------------------------+
| 15:12 | R | **action:** 1 = Enter debug mode on match. |
+-------+------+------------------------------------------------------------------+
| 11 | R | **chain:** 0 = Chaining not supported. |
+-------+------+------------------------------------------------------------------+
| 10:7 | R | **match:** 0 = Match the whole address. |
+-------+------+------------------------------------------------------------------+
| 6 | R | **m:** 1 = Match in M-Mode. |
+-------+------+------------------------------------------------------------------+
| 5 | R | zero. |
+-------+------+------------------------------------------------------------------+
| 4 | R | **s:** 0 = S-Mode not supported. |
+-------+------+------------------------------------------------------------------+
| 3 | R | **u:** 1 = Match in U-Mode. |
+-------+------+------------------------------------------------------------------+
| 2 | RW | **execute:** Enable matching on instruction address. |
+-------+------+------------------------------------------------------------------+
| 1 | R | **store:** 0 = Store address / data matching not supported. |
+-------+------+------------------------------------------------------------------+
| 0 | R | **load:** 0 = Load address / data matching not supported. |
+-------+------+------------------------------------------------------------------+
Details of these configuration bits can be found in the RISC-V Debug Specification, version 0.13.2 (see Trigger Registers, Section 5.2).
.. _csr-tdata2:
Trigger Data Register 2 (tdata2)
--------------------------------
CSR Address: ``0x7A2``
Reset Value: ``0x0000_0000``
Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter).
Since native triggers are not supported, writes to this register from M-Mode will be ignored.
This register stores the instruction address to match against for a breakpoint trigger.
Trigger Data Register 3 (tdata3)
--------------------------------
CSR Address: ``0x7A3``
Reset Value: ``0x0000_0000``
Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter).
Ibex does not support the features requiring this register, so writes are ignored and it will always read as zero.
Machine Context Register (mcontext)
-----------------------------------
CSR Address: ``0x7A8``
Reset Value: ``0x0000_0000``
Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter).
Ibex does not support the features requiring this register, so writes are ignored and it will always read as zero.
Supervisor Context Register (scontext)
--------------------------------------
CSR Address: ``0x7AA``
Reset Value: ``0x0000_0000``
Accessible in Debug Mode or M-Mode when trigger support is enabled (using the DbgTriggerEn parameter).
Ibex does not support the features requiring this register, so writes are ignored and it will always read as zero.
.. _csr-dcsr:
Debug Control and Status Register (dcsr)

View file

@ -26,10 +26,14 @@ Parameters
+---------------------+-----------------------------------------------------------------+
| ``DmExceptionAddr`` | Address to jump to when an exception occurs while in Debug Mode |
+---------------------+-----------------------------------------------------------------+
| ``DbgTriggerEn`` | Enable support for debug triggers |
+---------------------+-----------------------------------------------------------------+
Core Debug Registers
--------------------
Ibex implements four core debug registers, namely :ref:`csr-dcsr`, :ref:`csr-dpc`, and two debug scratch registers.
If the ``DbgTriggerEn`` parameter is set, debug trigger registers are available.
See :ref:`csr-tselect`, :ref:`csr-tdata1` and :ref:`csr-tdata2` for details.
All those registers are accessible from Debug Mode only.
If software tries to access them without the core being in Debug Mode, an illegal instruction exception is triggered.

View file

@ -20,6 +20,7 @@ Instantiation Template
.RV32E ( 0 ),
.RV32M ( 1 ),
.MultiplierImplementation ( "fast" ),
.DbgTriggerEn ( 0 ),
.DmHaltAddr ( 32'h1A110800 ),
.DmExceptionAddr ( 32'h1A110808 )
) u_core (
@ -88,6 +89,8 @@ Parameters
+------------------------------+-------------+------------+-----------------------------------------------------------------+
| ``MultiplierImplementation`` | string | "fast" | Multiplicator type, "slow", or "fast" |
+------------------------------+-------------+------------+-----------------------------------------------------------------+
| ``DbgTriggerEn`` | bit | 0 | Enable debug trigger support (one trigger only) |
+------------------------------+-------------+------------+-----------------------------------------------------------------+
| ``DmHaltAddr`` | int | 0x1A110800 | Address to jump to when entering Debug Mode |
+------------------------------+-------------+------------+-----------------------------------------------------------------+
| ``DmExceptionAddr`` | int | 0x1A110808 | Address to jump to when an exception occurs while in Debug Mode |

View file

@ -3,13 +3,14 @@
// SPDX-License-Identifier: Apache-2.0
module tb_cs_registers #(
parameter bit DbgTriggerEn = 0,
parameter int unsigned MHPMCounterNum = 8,
parameter int unsigned MHPMCounterWidth = 40,
parameter bit PMPEnable = 0,
parameter int unsigned PMPGranularity = 0,
parameter int unsigned PMPNumRegions = 4,
parameter bit RV32E = 0,
parameter bit RV32M = 0
parameter bit RV32E = 0,
parameter bit RV32M = 0
) (
// Clock and Reset
inout wire clk_i,
@ -64,6 +65,7 @@ module tb_cs_registers #(
logic debug_single_step_o;
logic debug_ebreakm_o;
logic debug_ebreaku_o;
logic trigger_match_o;
logic [31:0] pc_if_i;
logic [31:0] pc_id_i;
@ -116,6 +118,7 @@ module tb_cs_registers #(
`endif
ibex_cs_registers #(
.DbgTriggerEn (DbgTriggerEn),
.MHPMCounterNum (MHPMCounterNum),
.MHPMCounterWidth (MHPMCounterWidth),
.PMPEnable (PMPEnable),

View file

@ -57,4 +57,4 @@ lint_off -msg UNUSED -file "*/rtl/ibex_pmp.sv" -lines 16
// Signal unoptimizable: Feedback to clock or circular logic:
// ibex_core.cs_registers_i.mie_q
// Issue lowrisc/ibex#212
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -lines 164
lint_off -msg UNOPTFLAT -file "*/rtl/ibex_cs_registers.sv" -lines 166

View file

@ -68,6 +68,7 @@ module ibex_controller (
input logic debug_single_step_i,
input logic debug_ebreakm_i,
input logic debug_ebreaku_i,
input logic trigger_match_i,
output logic csr_save_if_o,
output logic csr_save_id_o,
@ -197,7 +198,9 @@ module ibex_controller (
// instruction valid otherwise the core will immediately enter debug mode
// due to a recently flushed IF (or a delay in an instruction returning from
// memory) before it has had anything to single step.
assign enter_debug_mode = (debug_req_i | (debug_single_step_i & instr_valid_i)) & ~debug_mode_q;
// Also enter debug mode on a trigger match (hardware breakpoint)
assign enter_debug_mode = (debug_req_i | (debug_single_step_i & instr_valid_i) |
trigger_match_i) & ~debug_mode_q;
// Set when an ebreak should enter debug mode rather than jump to exception
// handler
@ -420,7 +423,7 @@ module ibex_controller (
DBG_TAKEN_IF: begin
// enter debug mode and save PC in IF to dpc
// jump to debug exception handler in debug memory
if (debug_single_step_i || debug_req_i) begin
if (debug_single_step_i || debug_req_i || trigger_match_i) begin
flush_id = 1'b1;
pc_mux_o = PC_EXC;
pc_set_o = 1'b1;
@ -430,7 +433,9 @@ module ibex_controller (
debug_csr_save_o = 1'b1;
csr_save_cause_o = 1'b1;
if (debug_single_step_i) begin
if (trigger_match_i) begin
debug_cause_o = DBG_CAUSE_TRIGGER;
end else if (debug_single_step_i) begin
debug_cause_o = DBG_CAUSE_STEP;
end else begin
debug_cause_o = DBG_CAUSE_HALTREQ;

View file

@ -19,6 +19,7 @@ module ibex_core #(
parameter bit RV32E = 1'b0,
parameter bit RV32M = 1'b1,
parameter MultiplierImplementation = "fast",
parameter bit DbgTriggerEn = 1'b0,
parameter int unsigned DmHaltAddr = 32'h1A110800,
parameter int unsigned DmExceptionAddr = 32'h1A110808
) (
@ -209,6 +210,7 @@ module ibex_core #(
logic debug_single_step;
logic debug_ebreakm;
logic debug_ebreaku;
logic trigger_match;
// performance counter related signals
logic instr_ret;
@ -448,6 +450,7 @@ module ibex_core #(
.debug_single_step_i ( debug_single_step ),
.debug_ebreakm_i ( debug_ebreakm ),
.debug_ebreaku_i ( debug_ebreaku ),
.trigger_match_i ( trigger_match ),
// write data to commit in the register file
.regfile_wdata_lsu_i ( regfile_wdata_lsu ),
@ -565,6 +568,7 @@ module ibex_core #(
assign valid_csr_id = instr_new_id & ~instr_fetch_err;
ibex_cs_registers #(
.DbgTriggerEn ( DbgTriggerEn ),
.MHPMCounterNum ( MHPMCounterNum ),
.MHPMCounterWidth ( MHPMCounterWidth ),
.PMPEnable ( PMPEnable ),
@ -620,6 +624,7 @@ module ibex_core #(
.debug_single_step_o ( debug_single_step ),
.debug_ebreakm_o ( debug_ebreakm ),
.debug_ebreaku_o ( debug_ebreaku ),
.trigger_match_o ( trigger_match ),
.pc_if_i ( pc_if ),
.pc_id_i ( pc_id ),

View file

@ -15,6 +15,7 @@ module ibex_core_tracing #(
parameter bit RV32E = 1'b0,
parameter bit RV32M = 1'b1,
parameter MultiplierImplementation = "fast",
parameter bit DbgTriggerEn = 1'b0,
parameter int unsigned DmHaltAddr = 32'h1A110800,
parameter int unsigned DmExceptionAddr = 32'h1A110808
) (
@ -98,6 +99,7 @@ module ibex_core_tracing #(
.MHPMCounterWidth ( MHPMCounterWidth ),
.RV32E ( RV32E ),
.RV32M ( RV32M ),
.DbgTriggerEn ( DbgTriggerEn ),
.MultiplierImplementation ( MultiplierImplementation ),
.DmHaltAddr ( DmHaltAddr ),
.DmExceptionAddr ( DmExceptionAddr )

View file

@ -10,13 +10,14 @@
* Specification, draft version 1.11
*/
module ibex_cs_registers #(
parameter bit DbgTriggerEn = 0,
parameter int unsigned MHPMCounterNum = 8,
parameter int unsigned MHPMCounterWidth = 40,
parameter bit PMPEnable = 0,
parameter int unsigned PMPGranularity = 0,
parameter int unsigned PMPNumRegions = 4,
parameter bit RV32E = 0,
parameter bit RV32M = 0
parameter bit RV32E = 0,
parameter bit RV32M = 0
) (
// Clock and Reset
input logic clk_i,
@ -68,6 +69,7 @@ module ibex_cs_registers #(
output logic debug_single_step_o,
output logic debug_ebreakm_o,
output logic debug_ebreaku_o,
output logic trigger_match_o,
input logic [31:0] pc_if_i,
input logic [31:0] pc_id_i,
@ -196,6 +198,11 @@ module ibex_cs_registers #(
logic [31:0] mhpmevent [32];
logic [4:0] mhpmcounter_idx;
// Debug / trigger registers
logic [31:0] tselect_rdata;
logic [31:0] tmatch_control_rdata;
logic [31:0] tmatch_value_rdata;
// CSR update logic
logic [31:0] csr_wdata_int;
logic [31:0] csr_rdata_int;
@ -368,6 +375,32 @@ module ibex_cs_registers #(
csr_rdata_int = mhpmcounter_q[mhpmcounter_idx][63:32];
end
// Debug triggers
CSR_TSELECT: begin
csr_rdata_int = tselect_rdata;
illegal_csr = ~DbgTriggerEn;
end
CSR_TDATA1: begin
csr_rdata_int = tmatch_control_rdata;
illegal_csr = ~DbgTriggerEn;
end
CSR_TDATA2: begin
csr_rdata_int = tmatch_value_rdata;
illegal_csr = ~DbgTriggerEn;
end
CSR_TDATA3: begin
csr_rdata_int = '0;
illegal_csr = ~DbgTriggerEn;
end
CSR_MCONTEXT: begin
csr_rdata_int = '0;
illegal_csr = ~DbgTriggerEn;
end
CSR_SCONTEXT: begin
csr_rdata_int = '0;
illegal_csr = ~DbgTriggerEn;
end
default: begin
illegal_csr = 1'b1;
end
@ -899,4 +932,78 @@ module ibex_cs_registers #(
end
end
/////////////////////////////
// Debug trigger registers //
/////////////////////////////
if (DbgTriggerEn) begin : gen_trigger_regs
// Register values
logic tmatch_control_d, tmatch_control_q;
logic [31:0] tmatch_value_d, tmatch_value_q;
// Write enables
logic tmatch_control_we;
logic tmatch_value_we;
// 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);
// tmatch_control is enabled when the execute bit is set
assign tmatch_control_d = tmatch_control_we ? csr_wdata_int[2] :
tmatch_control_q;
// tmatch_value has its own clock gate
assign tmatch_value_d = csr_wdata_int[31:0];
// Registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
tmatch_control_q <= 'b0;
end else begin
tmatch_control_q <= tmatch_control_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
tmatch_value_q <= 'b0;
end else if (tmatch_value_we) begin
tmatch_value_q <= tmatch_value_d;
end
end
// Assign read data
// TSELECT - only one supported
assign tselect_rdata = 'b0;
// 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
// TDATA1 - address match value only
assign tmatch_value_rdata = tmatch_value_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]);
end else begin : gen_no_trigger_regs
assign tselect_rdata = 'b0;
assign tmatch_control_rdata = 'b0;
assign tmatch_value_rdata = 'b0;
assign trigger_match_o = 'b0;
end
endmodule

View file

@ -109,6 +109,7 @@ module ibex_id_stage #(
input logic debug_single_step_i,
input logic debug_ebreakm_i,
input logic debug_ebreaku_i,
input logic trigger_match_i,
// Write back signal
input logic [31:0] regfile_wdata_lsu_i,
@ -452,6 +453,7 @@ module ibex_id_stage #(
.debug_single_step_i ( debug_single_step_i ),
.debug_ebreakm_i ( debug_ebreakm_i ),
.debug_ebreaku_i ( debug_ebreaku_i ),
.trigger_match_i ( trigger_match_i ),
// stall signals
.stall_lsu_i ( stall_lsu ),

View file

@ -257,6 +257,14 @@ typedef enum logic[11:0] {
CSR_PMPADDR14 = 12'h3BE,
CSR_PMPADDR15 = 12'h3BF,
// Debug trigger
CSR_TSELECT = 12'h7A0,
CSR_TDATA1 = 12'h7A1,
CSR_TDATA2 = 12'h7A2,
CSR_TDATA3 = 12'h7A3,
CSR_MCONTEXT = 12'h7A8,
CSR_SCONTEXT = 12'h7AA,
// Debug/trace
CSR_DCSR = 12'h7b0,
CSR_DPC = 12'h7b1,