@@ -58,12 +58,21 @@ import cv32e40x_pkg::*;
58
58
// IF stage inputs
59
59
input logic [31 : 0 ] pc_if_i,
60
60
input logic ptr_in_if_i,
61
+ input privlvl_t priv_lvl_if_i,
62
+
63
+ // EX stage / LSU inputs
64
+ input logic lsu_valid_ex_i,
65
+ input logic [31 : 0 ] lsu_addr_ex_i,
66
+ input logic lsu_we_ex_i,
67
+ input logic [3 : 0 ] lsu_be_ex_i,
68
+ input privlvl_t priv_lvl_ex_i,
61
69
62
70
// Controller inputs
63
71
input ctrl_fsm_t ctrl_fsm_i,
64
72
65
- // Trigger match output
66
- output logic trigger_match_o
73
+ // Trigger match outputs
74
+ output logic trigger_match_if_o, // Instruction address match
75
+ output logic trigger_match_ex_o // Load/Store address match
67
76
);
68
77
69
78
// CSR write data
@@ -77,6 +86,8 @@ import cv32e40x_pkg::*;
77
86
// Signal for or'ing unused signals for easier lint
78
87
logic unused_signals;
79
88
89
+
90
+
80
91
generate
81
92
if (DBG_NUM_TRIGGERS > 0 ) begin : gen_triggers
82
93
// Internal CSR write enables
@@ -88,9 +99,30 @@ import cv32e40x_pkg::*;
88
99
logic [31 : 0 ] tdata2_q[DBG_NUM_TRIGGERS ];
89
100
logic [31 : 0 ] tselect_q;
90
101
91
- // Fetch stage trigger match
102
+ // CSR read data, possibly WARL resolved
103
+ logic [31 : 0 ] tdata1_rdata[DBG_NUM_TRIGGERS ];
104
+ logic [31 : 0 ] tdata2_rdata[DBG_NUM_TRIGGERS ];
105
+
106
+ // IF and EX stages trigger match
92
107
logic [DBG_NUM_TRIGGERS - 1 : 0 ] trigger_match_if;
108
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] trigger_match_ex;
109
+
110
+ // Instruction address match
111
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] if_addr_match;
112
+
113
+ // LSU address match signals
114
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] lsu_addr_match_en;
115
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] lsu_addr_match;
116
+ logic [3 : 0 ] lsu_byte_addr_match[DBG_NUM_TRIGGERS ];
117
+
118
+ // Enable matching based on privilege level per trigger
119
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] priv_lvl_match_en_if;
120
+ logic [DBG_NUM_TRIGGERS - 1 : 0 ] priv_lvl_match_en_ex;
93
121
122
+ logic [1 : 0 ] lsu_addr_low_lsb; // Lower two bits of the lowest accessed address
123
+ logic [1 : 0 ] lsu_addr_high_lsb; // Lower two bits of the highest accessed address
124
+ logic [31 : 0 ] lsu_addr_low; // The lowest accessed address of an LSU transaction
125
+ logic [31 : 0 ] lsu_addr_high; // The highest accessed address of an LSU transaction
94
126
95
127
// Write data
96
128
always_comb begin
@@ -108,7 +140,7 @@ import cv32e40x_pkg::*;
108
140
4'b0000 , // zero, size (match any size) 19:16
109
141
4'b0001 , // action, WARL(1), enter debug 15:12
110
142
1'b0 , // zero, chain 11
111
- 4'b0000 , // match, WARL(0,2,3) 10:7 todo: resolve WARL
143
+ mcontrol6_match_resolve (csr_wdata_i[ MCONTROL6_MATCH_HIGH : MCONTROL6_MATCH_LOW ]), // match, WARL(0,2,3) 10:7
112
144
csr_wdata_i[6 ], // M 6
113
145
1'b0 , // zero 5
114
146
1'b0 , // zero, S 4
@@ -123,20 +155,98 @@ import cv32e40x_pkg::*;
123
155
tcontrol_n = tcontrol_rdata_o; // Read only
124
156
end
125
157
158
+ // Calculate highest and lowest value of address[1:0] based on lsu_be_ex_i
159
+ always_comb begin
160
+ lsu_addr_high_lsb = 2'b00 ;
161
+ lsu_addr_low_lsb = 2'b00 ;
162
+ // Find highest accessed byte
163
+ for (int b= 0 ; b< 4 ; b++ ) begin : gen_high_byte_checks
164
+ if (lsu_be_ex_i[b]) begin
165
+ lsu_addr_high_lsb = 2 '(b);
166
+ end // if
167
+ end // for
168
+
169
+ // Find lowest accessed byte
170
+ for (int b= 3 ; b>= 0 ; b-- ) begin : gen_low_byte_checks
171
+ if (lsu_be_ex_i[b]) begin
172
+ lsu_addr_low_lsb = 2 '(b);
173
+ end // if
174
+ end // for
175
+ end // always
176
+
177
+ assign lsu_addr_high = { lsu_addr_ex_i[31 : 2 ], lsu_addr_high_lsb} ;
178
+ assign lsu_addr_low = { lsu_addr_ex_i[31 : 2 ], lsu_addr_low_lsb} ;
179
+
126
180
// Generate DBG_NUM_TRIGGERS instances of tdata1, tdata2 and match checks
127
181
for (genvar idx= 0 ; idx< DBG_NUM_TRIGGERS ; idx++ ) begin : tmatch_csr
128
- // Breakpoint matching
129
- // We match against the next address, as the breakpoint must be taken before execution
130
- // Matching is disabled when ctrl_fsm_i.debug_mode == 1'b1
182
+
183
+ // //////////////////////////////////
184
+ // Instruction address match (IF)
185
+ // //////////////////////////////////
186
+
187
+ // With timing=0 we enter debug before executing the instruction at the match address. We use the IF stage PC
188
+ // for comparison, and any trigger match will cause the instruction to act as a NOP with no side effects until it
189
+ // reaches WB where debug mode is entered.
190
+ //
191
+ // Trigger match is disabled while in debug mode.
192
+ //
131
193
// Trigger CSRs can only be written from debug mode, writes from any other privilege level are ignored.
132
194
// Thus we do not have an issue where a write to the tdata2 CSR immediately before the matched instruction
133
195
// could be missed since we must write in debug mode, then dret to machine mode (kills pipeline) before
134
196
// returning to dpc.
135
- // Todo: There is no CLIC spec for trigger matches for pointers.
136
- // todo: use struct or parameters for indexing to make code more readable.
137
- // todo: Check tdata1[6] vs actual priv_lvl and add check for tdata1[3] (PRIV_LVL_U)
138
- assign trigger_match_if[idx] = tdata1_q[idx][2 ] && tdata1_q[idx][6 ] && ! ctrl_fsm_i.debug_mode && ! ptr_in_if_i &&
139
- (pc_if_i[31 : 0 ] == tdata2_q[idx][31 : 0 ]);
197
+ // No instruction address match on any pointer type (CLIC and Zc tablejumps).
198
+
199
+ // Check for address match using tdata2.match for checking rule
200
+ assign if_addr_match[idx] = (tdata1_rdata[idx][MCONTROL6_MATCH_HIGH : MCONTROL6_MATCH_LOW ] == 4'h0 ) ? (pc_if_i == tdata2_rdata[idx]) :
201
+ (tdata1_rdata[idx][MCONTROL6_MATCH_HIGH : MCONTROL6_MATCH_LOW ] == 4'h2 ) ? (pc_if_i >= tdata2_rdata[idx]) : (pc_if_i < tdata2_rdata[idx]);
202
+
203
+ // Check if matching is enabled for the current privilege level from IF
204
+ assign priv_lvl_match_en_if[idx] = (tdata1_rdata[idx][MCONTROL6_M ] && (priv_lvl_if_i == PRIV_LVL_M )) ||
205
+ (tdata1_rdata[idx][MCONTROL6_U ] && (priv_lvl_if_i == PRIV_LVL_U ));
206
+
207
+ // Check for trigger match from IF
208
+ 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 &&
209
+ if_addr_match[idx];
210
+
211
+ // /////////////////////////////////////
212
+ // Load/Store address match (EX)
213
+ // /////////////////////////////////////
214
+
215
+ // todo: LSU address matching must be revisited once the atomics are implemented
216
+ // As for instruction address match, the load/store address match happens before the a bus transaction is visible on the OBI bus.
217
+ // For split misaligned transfers, each transfer is checked separately. This means that half a store may be visible externally even if
218
+ // 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
219
+ // will happen regardless of which transaction matches.
220
+ // The BE of the transaction is used to determine which bytes of a word is being accessed.
221
+
222
+ // Check if any accessed byte matches the lower two bits of tdata2
223
+ always_comb begin
224
+ for (int b= 0 ; b< 4 ; b++ ) begin
225
+ if (lsu_be_ex_i[b] && (2 '(b) == tdata2_rdata[idx][1 : 0 ])) begin
226
+ lsu_byte_addr_match[idx][b] = 1'b1 ;
227
+ end else begin
228
+ lsu_byte_addr_match[idx][b] = 1'b0 ;
229
+ end
230
+ end
231
+ end
232
+
233
+ // Check address matches for (==), (>=) and (<)
234
+ // For ==, check that we match the 32-bit aligned word and that any of the accessed bytes matches tdata2[1:0]
235
+ // For >=, check that the highest accessed address is greater than or equal to tdata2. If this fails, no bytes within the access are >= tdata2
236
+ // For <, check that the lowest accessed address is less than tdata2. If this fails, no bytes within the access are < tdata2.
237
+ 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])) :
238
+ (tdata1_rdata[idx][MCONTROL6_MATCH_HIGH : MCONTROL6_MATCH_LOW ] == 4'h2 ) ? (lsu_addr_high >= tdata2_rdata[idx]) :
239
+ (lsu_addr_low < tdata2_rdata[idx]) ;
240
+
241
+ // Check if matching is enabled for the current privilege level from EX
242
+ assign priv_lvl_match_en_ex[idx] = (tdata1_rdata[idx][MCONTROL6_M ] && (priv_lvl_ex_i == PRIV_LVL_M )) ||
243
+ (tdata1_rdata[idx][MCONTROL6_U ] && (priv_lvl_ex_i == PRIV_LVL_U ));
244
+
245
+ // Enable LSU address matching
246
+ assign lsu_addr_match_en[idx] = lsu_valid_ex_i && ((tdata1_rdata[idx][MCONTROL6_LOAD ] && ! lsu_we_ex_i) || (tdata1_rdata[idx][MCONTROL6_STORE ] && lsu_we_ex_i));
247
+
248
+ // Signal trigger match for LSU address
249
+ 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;
140
250
141
251
142
252
cv32e40x_csr
@@ -168,8 +278,13 @@ import cv32e40x_pkg::*;
168
278
);
169
279
170
280
// Set write enables
171
- assign tdata1_we_int[idx] = tdata1_we_i && (tselect_q == idx);
172
- assign tdata2_we_int[idx] = tdata2_we_i && (tselect_q == idx);
281
+ assign tdata1_we_int[idx] = tdata1_we_i && (tselect_rdata_o == idx);
282
+ assign tdata2_we_int[idx] = tdata2_we_i && (tselect_rdata_o == idx);
283
+
284
+ // Assign read data
285
+ // todo: WARL
286
+ assign tdata1_rdata[idx] = tdata1_q[idx];
287
+ assign tdata2_rdata[idx] = tdata2_q[idx];
173
288
end // for
174
289
175
290
// CSR instance for tselect
@@ -189,14 +304,14 @@ import cv32e40x_pkg::*;
189
304
190
305
// Assign CSR read data outputs
191
306
always_comb begin
192
- tdata1_rdata_o = tdata1_q [0 ];
193
- tdata2_rdata_o = tdata2_q [0 ];
307
+ tdata1_rdata_o = tdata1_rdata [0 ];
308
+ tdata2_rdata_o = tdata2_rdata [0 ];
194
309
195
310
// Iterate through triggers and set tdata1/tdata2 rdata for the currently selected trigger
196
311
for (int i= 0 ; i< DBG_NUM_TRIGGERS ; i++ ) begin
197
- if (tselect_q == i) begin
198
- tdata1_rdata_o = tdata1_q [i];
199
- tdata2_rdata_o = tdata2_q [i];
312
+ if (tselect_rdata_o == i) begin
313
+ tdata1_rdata_o = tdata1_rdata [i];
314
+ tdata2_rdata_o = tdata2_rdata [i];
200
315
end
201
316
end
202
317
end
@@ -207,7 +322,10 @@ import cv32e40x_pkg::*;
207
322
assign tcontrol_rdata_o = 32'h00000000 ;
208
323
209
324
// Set trigger match for IF
210
- assign trigger_match_o = | trigger_match_if;
325
+ assign trigger_match_if_o = | trigger_match_if;
326
+
327
+ // Set trigger match for EX
328
+ assign trigger_match_ex_o = | trigger_match_ex;
211
329
212
330
assign unused_signals = tinfo_we_i | tcontrol_we_i | tdata3_we_i | (| tinfo_n) | (| tdata3_n) | (| tcontrol_n);
213
331
@@ -219,7 +337,8 @@ import cv32e40x_pkg::*;
219
337
assign tselect_rdata_o = '0 ;
220
338
assign tinfo_rdata_o = '0 ;
221
339
assign tcontrol_rdata_o = '0 ;
222
- assign trigger_match_o = '0 ;
340
+ assign trigger_match_if_o = '0 ;
341
+ assign trigger_match_ex_o = '0 ;
223
342
assign tdata1_n = '0 ;
224
343
assign tdata2_n = '0 ;
225
344
assign tdata3_n = '0 ;
0 commit comments