Implement hardware break-points

This commit is contained in:
Florian Zaruba 2017-07-18 20:40:27 +02:00
parent b352a55106
commit 67b97d12da
2 changed files with 123 additions and 61 deletions

View file

@ -311,25 +311,12 @@ package ariane_pkg;
DBG_HIT = 15'h4,
DBG_IE = 15'h8,
DBG_CAUSE = 15'hC,
DBG_BPCTRL0 = 15'h40,
DBG_BPDATA0 = 15'h44,
DBG_BPCTRL1 = 15'h48,
DBG_BPDATA1 = 15'h4C,
DBG_BPCTRL2 = 15'h50,
DBG_BPDATA2 = 15'h54,
DBG_BPCTRL3 = 15'h58,
DBG_BPDATA3 = 15'h5C,
DBG_BPCTRL4 = 15'h60,
DBG_BPDATA4 = 15'h64,
DBG_BPCTRL5 = 15'h68,
DBG_BPDATA5 = 15'h6C,
DBG_BPCTRL6 = 15'h70,
DBG_BPDATA6 = 15'h74,
DBG_BPCTRL7 = 15'h78,
DBG_BPDATA7 = 15'h7C,
DBG_GPR = 15'h4xx,
DBG_CSR = 15'h5xx,
DBG_BPCTRL = 15'b????000,
DBG_BPDATA = 15'b?????00,
DBG_GPR = 15'h4??,
DBG_CSR = 15'h5??,
DBG_NPC = 15'h2000,
DBG_PPC = 15'h2004
} debug_reg_t;
endpackage

View file

@ -39,6 +39,10 @@ module debug_unit (
output logic debug_csr_we_o,
output logic [63:0] debug_csr_wdata_o,
input logic [63:0] debug_csr_rdata_i,
// CPU Control
output logic [63:0] debug_pc_o,
output logic debug_set_pc_o,
// HWPB
// External Debug Interface
input logic debug_req_i,
output logic debug_gnt_o,
@ -59,7 +63,7 @@ module debug_unit (
logic rvalid_n, rvalid_q;
logic rdata_n, rdata_q;
// save previous PC
logic ppc_n, ppc_q;
logic dbg_ppc_n, dbg_ppc_q;
// ---------------
// Debug Register
// ---------------
@ -69,15 +73,21 @@ module debug_unit (
logic [63:0] dbg_cause_n, dbg_cause_q;
// single step mode
logic dbg_ss_n, dbg_ss_q;
logic dbg_ssth_n, dbg_ssth_q;
logic [63:0] dbg_hit_n, dbg_hit_q;
// hardware breakpoints
logic [7:0][3:0] dbg_hwbp_ctrl_n, dbg_hwbp_ctrl_q;
logic [7:0][63:0] dbg_hwbp_data_n, dbg_hwbp_data_q;
// ----------------------
// Debug Control Signals
// ----------------------
// change debug mode
logic halt_req, resume_req;
logic stepped_single;
// assigns, we can output a couple of signals directly
assign debug_rvalid_o = rvalid_q;
assign debug_rdata_o = rdata_q;
assign debug_halted_o = (CS == HALTED);
// | Address | Name | Description |
// |---------------|-----------------|---------------------------------------------------------------------|
// | 0x0000-0x007F | Debug Registers | Always accessible, even when the core is running |
@ -85,20 +95,21 @@ module debug_unit (
// | 0x0500-0x05FF | FPR (f0-f31) | Reserved. Not used in the Ariane. |
// | 0x2000-0x20FF | Debug Registers | Only accessible if the core is halted |
// | 0x4000-0x7FFF | CSR | Control and Status Registers. Only accessible if the core is halted |
always_comb begin : debug_ctrl
debug_gnt_o = 1'b0;
rdata_n = 'b0;
rvalid_n = 1'b0;
debug_gnt_o = 1'b0;
rdata_n = 'b0;
rvalid_n = 1'b0;
halt_req = 1'b0;
halt_req = 1'b0;
// update the previous PC if got a valid commit
ppc_n = (commit_ack_i) ? commit_instr_i.pc : ppc_q;
dbg_ppc_n = (commit_ack_i) ? commit_instr_i.pc : dbg_ppc_q;
// debug registers
dbg_ie_n = dbg_ie_q;
dbg_cause_n = dbg_cause_q;
dbg_ss_n = dbg_ss_q;
dbg_ssth_n = dbg_ssth_q;
dbg_ie_n = dbg_ie_q;
dbg_cause_n = dbg_cause_q;
dbg_ss_n = dbg_ss_q;
dbg_hit_n = dbg_hit_q;
dbg_hwbp_ctrl_n = dbg_hwbp_ctrl_q;
dbg_hwbp_data_n = dbg_hwbp_data_q;
// GPR defaults
debug_gpr_req_o = 1'b0;
debug_gpr_addr_o = debug_addr_i[6:2];
@ -109,9 +120,13 @@ module debug_unit (
debug_csr_addr_o = debug_addr_i[13:2];
debug_csr_we_o = 1'b0;
debug_csr_wdata_o = 64'b0;
// change ctrl flow
debug_pc_o = 64'b0;
debug_set_pc_o = 1'b0;
// we did one single step, set the sticky bit
if (stepped_single)
dbg_ssth_n = 1'b1;
dbg_hit_n = 1'b1;
// ----------
// Read
@ -121,13 +136,24 @@ module debug_unit (
// we can immediately grant the request
debug_gnt_o = 1'b1;
// decode debug address
casex (debug_addr_i)
DBG_CTRL: rdata_n = {32'b0, 15'b0, debug_halted_o, 15'b0, dbg_ss_q};
DBG_HIT: rdata_n = {63'b0, dbg_ssth_q};
DBG_IE: rdata_n = dbg_ie_q;
DBG_CAUSE: rdata_n = dbg_cause_q;
DBG_NPC: rdata_n = commit_instr_i.pc;
DBG_PPC: rdata_n = ppc_q;
casez (debug_addr_i)
DBG_CTRL: rdata_n = {32'b0, 15'b0, debug_halted_o, 15'b0, dbg_ss_q};
DBG_HIT: rdata_n = {63'b0, dbg_hit_q};
DBG_IE: rdata_n = dbg_ie_q;
DBG_CAUSE: rdata_n = dbg_cause_q;
// all breakpoints are implemented
DBG_BPCTRL: rdata_n = {57'b0, dbg_hwbp_ctrl_q[debug_addr_i[5:3]], 2'b0, 1'b1};
DBG_BPDATA: rdata_n = dbg_hwbp_data_q[debug_addr_i[5:3]];
DBG_NPC: begin
if (debug_halted_o)
rdata_n = commit_instr_i.pc;
end
DBG_PPC: begin
if (debug_halted_o)
rdata_n = dbg_ppc_q;
end
DBG_GPR: begin
if (debug_halted_o) begin
@ -151,7 +177,7 @@ module debug_unit (
// we can also immediately grant
debug_gnt_o = 1'b1;
// decode debug address
case (debug_addr_i)
casez (debug_addr_i)
DBG_CTRL: begin
// check if requested a halt
halt_req = debug_wdata_i[16];
@ -159,10 +185,22 @@ module debug_unit (
// enable/disable single step
dbg_ss_n = debug_wdata_i[0];
end
DBG_HIT: dbg_ssth_n = debug_wdata_i[0];
DBG_IE: dbg_ie_n = debug_wdata_i;
DBG_HIT: dbg_hit_n = {debug_wdata_i[15:8], debug_wdata_i[0]};
DBG_IE: dbg_ie_n = debug_wdata_i;
DBG_CAUSE: dbg_cause_n = debug_wdata_i;
// Only triggering on instruction fetch is allowed at the moment
DBG_BPCTRL: dbg_hwbp_ctrl_n[debug_addr_i[5:3]] = {3'b0, debug_wdata_i[1]};
DBG_BPDATA: dbg_hwbp_data_n[debug_addr_i[5:3]] = debug_wdata_i;
DBG_NPC: begin
// Change CTRL Flow to debug PC
debug_pc_o = debug_wdata_i;
debug_set_pc_o = 1'b1;
end
// PPC is read-only
DBG_PPC:;
DBG_GPR: begin
if (debug_halted_o) begin
debug_gpr_req_o = 1'b1;
@ -187,12 +225,22 @@ module debug_unit (
// save the cause why we entered the exception
dbg_cause_n = ex_i.cause;
end
// --------------------
// HW Breakpoints
// --------------------
// check all possible breakpoints
for (logic [7:0] i = 0; i < 8; i++) begin
// check if a breakpoint is triggering, therefore check if it is enabled
if (dbg_hwbp_ctrl_q[1]) begin
// check if the PC is matching and the processor is currently retiring the instruction
if (commit_instr_i.pc == dbg_hwbp_data_q && commit_ack_i) begin
halt_req = 1'b1;
dbg_hit_n[15:8] = i;
end
end
end
end
// --------------------
// HW Breakpoints
// --------------------
// --------------------
// Stall Control
// --------------------
@ -204,7 +252,9 @@ module debug_unit (
case (CS)
// CPU is running normally
RUNNING: begin
// external debugger requested to halt the CPU
// 1. external debugger requested to halt the CPU
// 2. cross-trigger requested a halt
// 3. a break-point hit
if (halt_req || debug_halt_i) begin
NS = HALT_REQ;
end
@ -236,7 +286,7 @@ module debug_unit (
NS = RUNNING;
// resume from single step, check if single stepping is enabled and if the sticky bit is cleared
if (dbg_ss_q && !dbg_ssth_q) begin
if (dbg_ss_q && !dbg_hit_q) begin
NS = SINGLE_STEP;
end
end
@ -249,19 +299,44 @@ module debug_unit (
// --------------------
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
CS <= RUNNING;
rdata_q <= '0;
rvalid_q <= 1'b0;
ppc_q <= 64'b0;
dbg_ie_q <= 64'b0;
dbg_cause_q <= 64'b0;
CS <= RUNNING;
rdata_q <= '0;
rvalid_q <= 1'b0;
dbg_ppc_q <= 64'b0;
dbg_ie_q <= 64'b0;
dbg_cause_q <= 64'b0;
dbg_hwbp_ctrl_q <= '0;
dbg_hwbp_data_q <= '0;
dbg_ss_q <= 1'b0;
dbg_hit_q <= 1'b0;
end else begin
CS <= NS;
rdata_q <= rdata_n;
rvalid_q <= rvalid_n;
ppc_q <= ppc_n;
dbg_ie_q <= dbg_ie_n;
dbg_cause_q <= dbg_cause_n;
CS <= NS;
rdata_q <= rdata_n;
rvalid_q <= rvalid_n;
dbg_ppc_q <= dbg_ppc_n;
dbg_ie_q <= dbg_ie_n;
dbg_cause_q <= dbg_cause_n;
dbg_ss_q <= dbg_ss_n;
dbg_hit_q <= dbg_hit_n;
dbg_hwbp_ctrl_q <= dbg_hwbp_ctrl_n;
dbg_hwbp_data_q <= dbg_hwbp_data_n;
end
end
//--------------
// Assertions
//--------------
// check that no registers are accessed when we are not in debug mode
assert property (
@(posedge clk_i) (debug_req_i) |-> ((debug_halted_o == 1'b1) ||
((debug_addr_i[14] != 1'b1) &&
(debug_addr_i[13:7] != 5'b0_1001) &&
(debug_addr_i[13:7] != 5'b0_1000)) ) )
else $warning("Trying to access internal debug registers while core is not stalled");
// check that all accesses are word-aligned
assert property (
@(posedge clk_i) (debug_req_i) |-> (debug_addr_i[1:0] == 2'b00) );
endmodule