Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion rtl/cv32e40x_controller_fsm.sv
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,11 @@ module cv32e40x_controller_fsm import cv32e40x_pkg::*;
// The cycle after fencei enters WB, the fencei handshake will be initiated. This must complete and the fencei instruction must retire before allowing debug.
// Any multi operation instruction (table jumps, push/pop and double moves) may not be interrupted once the first operation has completed its operation in WB.
// - This is guarded with using the sequence_interruptible, which tracks sequence progress through the WB stage.
// - Exception if a LSU trigger match happens during push or pop, then we must abort the sequence and enter debug mode.
// - If trigger_match_in_wb is caused by instruction address match, then no side effects will happen for a sequence, and debug mode is entered when the first operation is in WB.
// When a CLIC pointer is in the pipeline stages EX or WB, we must block debug.
// - Debug would otherwise kill the pointer and use the address of the pointer for dpc. A following dret would then return to the mtvt table, losing program progress.
assign debug_allowed = lsu_interruptible_i && !fencei_ongoing && !xif_in_wb && !clic_ptr_in_pipeline && sequence_interruptible;
assign debug_allowed = lsu_interruptible_i && !fencei_ongoing && !xif_in_wb && !clic_ptr_in_pipeline && (sequence_interruptible || trigger_match_in_wb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change in itself SEC clean when ZC_EXT = 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SEC is still running after four days


// Debug pending for any other reason than single step
assign pending_debug = (trigger_match_in_wb) ||
Expand Down
29 changes: 26 additions & 3 deletions rtl/cv32e40x_core.sv
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ module cv32e40x_core import cv32e40x_pkg::*;
logic lsu_valid_wb;
logic lsu_ready_1;

// LSU signals to trigger module
logic [31:0] lsu_addr_ex;
logic lsu_we_ex;
logic [3:0] lsu_be_ex;

logic data_stall_wb;

// Stage ready signals
Expand All @@ -283,8 +288,10 @@ module cv32e40x_core import cv32e40x_pkg::*;
// From cs_registers
dcsr_t dcsr;

// trigger match detected in cs_registers (using ID timing)
// trigger match detected in trigger module (using IF timing)
logic trigger_match_if;
// trigger match detected in trigger module (using EX/LSU timing)
logic trigger_match_ex;

// Controller <-> decoder
logic alu_jmp_id;
Expand Down Expand Up @@ -573,6 +580,9 @@ module cv32e40x_core import cv32e40x_pkg::*;
.csr_illegal_i ( csr_illegal ),
.csr_mnxti_read_i ( csr_mnxti_read ),

// trigger input
.trigger_match_i ( trigger_match_ex ),

// Branch decision
.branch_decision_o ( branch_decision_ex ),
.branch_target_o ( branch_target_ex ),
Expand Down Expand Up @@ -634,11 +644,19 @@ module cv32e40x_core import cv32e40x_pkg::*;
.busy_o ( lsu_busy ),
.interruptible_o ( lsu_interruptible ),

// Trigger match
.trigger_match_0_i ( trigger_match_ex ),

// Stage 0 outputs (EX)
.lsu_split_0_o ( lsu_split_ex ),
.lsu_first_op_0_o ( lsu_first_op_ex ),
.lsu_last_op_0_o ( lsu_last_op_ex ),

// Outputs to trigger module
.lsu_addr_o ( lsu_addr_ex ),
.lsu_we_o ( lsu_we_ex ),
.lsu_be_o ( lsu_be_ex ),

// Stage 1 outputs (WB)
.lsu_err_1_o ( lsu_err_wb ), // To controller (has WB timing, but does not pass through WB stage)
.lsu_rdata_1_o ( lsu_rdata_wb ),
Expand Down Expand Up @@ -792,9 +810,14 @@ module cv32e40x_core import cv32e40x_pkg::*;
.csr_wr_in_wb_flush_o ( csr_wr_in_wb_flush ),

// Debug
.trigger_match_o ( trigger_match_if ),
.trigger_match_if_o ( trigger_match_if ),
.trigger_match_ex_o ( trigger_match_ex ),
.pc_if_i ( pc_if ),
.ptr_in_if_i ( ptr_in_if )
.ptr_in_if_i ( ptr_in_if ),
.lsu_valid_ex_i ( lsu_valid_ex ),
.lsu_addr_ex_i ( lsu_addr_ex ),
.lsu_we_ex_i ( lsu_we_ex ),
.lsu_be_ex_i ( lsu_be_ex )
);

////////////////////////////////////////////////////////////////////
Expand Down
20 changes: 17 additions & 3 deletions rtl/cv32e40x_cs_registers.sv
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ module cv32e40x_cs_registers import cv32e40x_pkg::*;
// Debug
input logic [31:0] pc_if_i,
input logic ptr_in_if_i,
output logic trigger_match_o
output logic trigger_match_if_o,
output logic trigger_match_ex_o,
input logic lsu_valid_ex_i,
input logic [31:0] lsu_addr_ex_i,
input logic lsu_we_ex_i,
input logic [3:0] lsu_be_ex_i
);

localparam logic [31:0] CORE_MISA =
Expand Down Expand Up @@ -1516,12 +1521,21 @@ module cv32e40x_cs_registers import cv32e40x_pkg::*;
// IF stage inputs
.pc_if_i ( pc_if_i ),
.ptr_in_if_i ( ptr_in_if_i ),
.priv_lvl_if_i ( PRIV_LVL_M ),

// LSU inputs
.lsu_valid_ex_i ( lsu_valid_ex_i),
.lsu_addr_ex_i ( lsu_addr_ex_i ),
.lsu_we_ex_i ( lsu_we_ex_i ),
.lsu_be_ex_i ( lsu_be_ex_i ),
.priv_lvl_ex_i ( PRIV_LVL_M ),

// Controller inputs
.ctrl_fsm_i ( ctrl_fsm_i ),

// Trigger match output
.trigger_match_o ( trigger_match_o )
// Trigger match outputs
.trigger_match_if_o ( trigger_match_if_o ),
.trigger_match_ex_o ( trigger_match_ex_o )
);


Expand Down
142 changes: 124 additions & 18 deletions rtl/cv32e40x_debug_triggers.sv
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,21 @@ import cv32e40x_pkg::*;
// IF stage inputs
input logic [31:0] pc_if_i,
input logic ptr_in_if_i,
input privlvl_t priv_lvl_if_i,

// EX stage / LSU inputs
input logic lsu_valid_ex_i,
input logic [31:0] lsu_addr_ex_i,
input logic lsu_we_ex_i,
input logic [3:0] lsu_be_ex_i,
input privlvl_t priv_lvl_ex_i,

// Controller inputs
input ctrl_fsm_t ctrl_fsm_i,

// Trigger match output
output logic trigger_match_o
// Trigger match outputs
output logic trigger_match_if_o, // Instruction address match
output logic trigger_match_ex_o // Load/Store address match
);

// CSR write data
Expand All @@ -77,6 +86,8 @@ import cv32e40x_pkg::*;
// Signal for or'ing unused signals for easier lint
logic unused_signals;



generate
if (DBG_NUM_TRIGGERS > 0) begin : gen_triggers
// Internal CSR write enables
Expand All @@ -88,9 +99,28 @@ import cv32e40x_pkg::*;
logic [31:0] tdata2_q[DBG_NUM_TRIGGERS];
logic [31:0] tselect_q;

// Fetch stage trigger match
// CSR read data, possibly WARL resolved
logic [31:0] tdata1_rdata[DBG_NUM_TRIGGERS];
logic [31:0] tdata2_rdata[DBG_NUM_TRIGGERS];

// IF and EX stages trigger match
logic [DBG_NUM_TRIGGERS-1 : 0] trigger_match_if;
logic [DBG_NUM_TRIGGERS-1 : 0] trigger_match_ex;

// Instruction address match
logic [DBG_NUM_TRIGGERS-1 : 0] if_addr_match;

// LSU address match signals
logic [DBG_NUM_TRIGGERS-1 : 0] lsu_addr_match_en;
logic [DBG_NUM_TRIGGERS-1 : 0] lsu_addr_match;
logic [3:0] lsu_byte_addr_match[DBG_NUM_TRIGGERS];

// Enable matching based on privilege level per trigger
logic [DBG_NUM_TRIGGERS-1 : 0] priv_lvl_match_en_if;
logic [DBG_NUM_TRIGGERS-1 : 0] priv_lvl_match_en_ex;

logic [1:0] lsu_addr_high_lsb; // Lower two bits of the highest accessed address
logic [31:0] lsu_addr_high; // The highest accessed address of an LSU transaction

// Write data
always_comb begin
Expand All @@ -108,7 +138,7 @@ import cv32e40x_pkg::*;
4'b0000, // zero, size (match any size) 19:16
4'b0001, // action, WARL(1), enter debug 15:12
1'b0, // zero, chain 11
4'b0000, // match, WARL(0,2,3) 10:7 todo: resolve WARL
mcontrol6_match_resolve(csr_wdata_i[MCONTROL6_MATCH_HIGH:MCONTROL6_MATCH_LOW]), // match, WARL(0,2,3) 10:7
csr_wdata_i[6], // M 6
1'b0, // zero 5
1'b0, // zero, S 4
Expand All @@ -123,20 +153,87 @@ import cv32e40x_pkg::*;
tcontrol_n = tcontrol_rdata_o; // Read only
end

// Calculate highest value of address[1:0] based on lsu_be_ex_i
always_comb begin
for (int b=0; b<4; b++) begin : gen_byte_checks
if (lsu_be_ex_i[b]) begin
lsu_addr_high_lsb = 2'(b);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add default assignment for lsu_addr_high_lsb (with XIF none of the lsu_be_ex_i bits might be high)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

end // if
end // for
end // always

assign lsu_addr_high = {lsu_addr_ex_i[31:2], lsu_addr_high_lsb};

// Generate DBG_NUM_TRIGGERS instances of tdata1, tdata2 and match checks
for (genvar idx=0; idx<DBG_NUM_TRIGGERS; idx++) begin : tmatch_csr
// Breakpoint matching
// We match against the next address, as the breakpoint must be taken before execution
// Matching is disabled when ctrl_fsm_i.debug_mode == 1'b1

////////////////////////////////////
// Instruction address match (IF)
////////////////////////////////////

// With timing=0 we enter debug before executing the instruction at the match address. We use the IF stage PC
// for comparison, and any trigger match will cause the instruction to act as a NOP with no side effects until it
// reaches WB where debug mode is entered.
//
// Trigger match is disabled while in debug mode.
//
// Trigger CSRs can only be written from debug mode, writes from any other privilege level are ignored.
// Thus we do not have an issue where a write to the tdata2 CSR immediately before the matched instruction
// could be missed since we must write in debug mode, then dret to machine mode (kills pipeline) before
// returning to dpc.
// Todo: There is no CLIC spec for trigger matches for pointers.
// todo: use struct or parameters for indexing to make code more readable.
// todo: Check tdata1[6] vs actual priv_lvl and add check for tdata1[3] (PRIV_LVL_U)
assign trigger_match_if[idx] = tdata1_q[idx][2] && tdata1_q[idx][6] && !ctrl_fsm_i.debug_mode && !ptr_in_if_i &&
(pc_if_i[31:0] == tdata2_q[idx][31:0]);
// No instruction address match on any pointer type (CLIC and Zc tablejumps).

// Check for address match using tdata2.match for checking rule
assign if_addr_match[idx] = (tdata1_rdata[idx][MCONTROL6_MATCH_HIGH:MCONTROL6_MATCH_LOW] == 4'h0) ? (pc_if_i == tdata2_rdata[idx]) :
(tdata1_rdata[idx][MCONTROL6_MATCH_HIGH:MCONTROL6_MATCH_LOW] == 4'h2) ? (pc_if_i >= tdata2_rdata[idx]) : (pc_if_i < tdata2_rdata[idx]);

// Check if matching is enabled for the current privilege level from IF
assign priv_lvl_match_en_if[idx] = (tdata1_rdata[idx][MCONTROL6_M] && (priv_lvl_if_i == PRIV_LVL_M)) ||
(tdata1_rdata[idx][MCONTROL6_U] && (priv_lvl_if_i == PRIV_LVL_U));

// Check for trigger match from IF
assign trigger_match_if[idx] = tdata1_rdata[idx][MCONTROL6_EXECUTE] && priv_lvl_match_en_if[idx] && !ctrl_fsm_i.debug_mode && !ptr_in_if_i &&
if_addr_match[idx];

///////////////////////////////////////
// Load/Store address match (EX)
///////////////////////////////////////

// todo: LSU address matching must be revisited once the atomics are implemented
// As for instruction address match, the load/store address match happens before the a bus transaction is visible on the OBI bus.
// For split misaligned transfers, each transfer is checked separately. This means that half a store may be visible externally even if
// the second part matches tdata2 and debug is entered. For loads the RF write will not happen until the last part finished anyway, so no state update
// will happen regardless of which transaction matches.
// The BE of the transaction is used to determine which bytes of a word is being accessed.

// Check if any accessed byte matches the lower two bits of tdata2
always_comb begin
for (int b=0; b<4; b++) begin
if (lsu_be_ex_i[b] && (2'(b) == tdata2_rdata[idx][1:0])) begin
lsu_byte_addr_match[idx][b] = 1'b1;
end else begin
lsu_byte_addr_match[idx][b] = 1'b0;
end
end
end

// Check address matches for (==), (>=) and (<)
// For ==, check that we match the 32-bit aligned word and that any of the accessed bytes matches tdata2[1:0]
// For >=, check that the highest accessed address is greater than or equal to tdata2
// For <, check that the highest accessed address is less than tdata2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment and implementation not according to debug spec

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

assign lsu_addr_match[idx] = (tdata1_rdata[idx][MCONTROL6_MATCH_HIGH:MCONTROL6_MATCH_LOW] == 4'h0) ? ((lsu_addr_ex_i[31:2] == tdata2_rdata[idx][31:2]) && (|lsu_byte_addr_match[idx])) :
(tdata1_rdata[idx][MCONTROL6_MATCH_HIGH:MCONTROL6_MATCH_LOW] == 4'h2) ? (lsu_addr_high >= tdata2_rdata[idx]) :
(lsu_addr_high < tdata2_rdata[idx]) ;

// Check if matching is enabled for the current privilege level from EX
assign priv_lvl_match_en_ex[idx] = (tdata1_rdata[idx][MCONTROL6_M] && (priv_lvl_ex_i == PRIV_LVL_M)) ||
(tdata1_rdata[idx][MCONTROL6_U] && (priv_lvl_ex_i == PRIV_LVL_U));

// Enable LSU address matching
assign lsu_addr_match_en[idx] = lsu_valid_ex_i && ((tdata1_q[idx][MCONTROL6_LOAD] && !lsu_we_ex_i) || (tdata1_q[idx][MCONTROL6_STORE] && lsu_we_ex_i));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add todo to revisit this code when supporting Atomics

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


// Signal trigger match for LSU address
assign trigger_match_ex[idx] = priv_lvl_match_en_ex[idx] && lsu_addr_match_en[idx] && lsu_addr_match[idx] && !ctrl_fsm_i.debug_mode;


cv32e40x_csr
Expand Down Expand Up @@ -170,6 +267,11 @@ import cv32e40x_pkg::*;
// Set write enables
assign tdata1_we_int[idx] = tdata1_we_i && (tselect_q == idx);
assign tdata2_we_int[idx] = tdata2_we_i && (tselect_q == idx);

// Assign read data
// todo: WARL
assign tdata1_rdata[idx] = tdata1_q[idx];
assign tdata2_rdata[idx] = tdata2_q[idx];
end // for

// CSR instance for tselect
Expand All @@ -189,14 +291,14 @@ import cv32e40x_pkg::*;

// Assign CSR read data outputs
always_comb begin
tdata1_rdata_o = tdata1_q[0];
tdata2_rdata_o = tdata2_q[0];
tdata1_rdata_o = tdata1_rdata[0];
tdata2_rdata_o = tdata2_rdata[0];

// Iterate through triggers and set tdata1/tdata2 rdata for the currently selected trigger
for (int i=0; i<DBG_NUM_TRIGGERS; i++) begin
if(tselect_q == i) begin
tdata1_rdata_o = tdata1_q[i];
tdata2_rdata_o = tdata2_q[i];
tdata1_rdata_o = tdata1_rdata[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tselect_q -> tselect_rdata

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

tdata2_rdata_o = tdata2_rdata[i];
end
end
end
Expand All @@ -207,7 +309,10 @@ import cv32e40x_pkg::*;
assign tcontrol_rdata_o = 32'h00000000;

// Set trigger match for IF
assign trigger_match_o = |trigger_match_if;
assign trigger_match_if_o = |trigger_match_if;

// Set trigger match for EX
assign trigger_match_ex_o = |trigger_match_ex;

assign unused_signals = tinfo_we_i | tcontrol_we_i | tdata3_we_i | (|tinfo_n) | (|tdata3_n) | (|tcontrol_n);

Expand All @@ -219,7 +324,8 @@ import cv32e40x_pkg::*;
assign tselect_rdata_o = '0;
assign tinfo_rdata_o = '0;
assign tcontrol_rdata_o = '0;
assign trigger_match_o = '0;
assign trigger_match_if_o = '0;
assign trigger_match_ex_o = '0;
assign tdata1_n = '0;
assign tdata2_n = '0;
assign tdata3_n = '0;
Expand Down
Loading