@@ -17,9 +17,11 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
17
17
uvm_tlm_analysis_fifo # (ibex_ifetch_seq_item) ifetch_port;
18
18
uvm_tlm_analysis_fifo # (ibex_ifetch_pmp_seq_item) ifetch_pmp_port;
19
19
20
- virtual core_ibex_instr_monitor_if instr_vif;
20
+ virtual core_ibex_instr_monitor_if instr_vif;
21
+ virtual core_ibex_dut_probe_if dut_vif;
21
22
22
23
uvm_event reset_e;
24
+ uvm_event check_inserted_iside_error_e;
23
25
24
26
bit failed_iside_accesses [bit [31 : 0 ]];
25
27
bit iside_pmp_failure [bit [31 : 0 ]];
@@ -36,13 +38,14 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
36
38
function new (string name= " " , uvm_component parent= null );
37
39
super .new (name, parent);
38
40
39
- rvfi_port = new (" rvfi_port" , this );
40
- dmem_port = new (" dmem_port" , this );
41
- imem_port = new (" imem_port" , this );
42
- ifetch_port = new (" ifetch_port" , this );
43
- ifetch_pmp_port = new (" ifetch_pmp_port" , this );
44
- cosim_handle = null ;
45
- reset_e = new ();
41
+ rvfi_port = new (" rvfi_port" , this );
42
+ dmem_port = new (" dmem_port" , this );
43
+ imem_port = new (" imem_port" , this );
44
+ ifetch_port = new (" ifetch_port" , this );
45
+ ifetch_pmp_port = new (" ifetch_pmp_port" , this );
46
+ cosim_handle = null ;
47
+ reset_e = new ();
48
+ check_inserted_iside_error_e = new ();
46
49
endfunction
47
50
48
51
function void build_phase (uvm_phase phase);
@@ -57,6 +60,11 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
57
60
`uvm_fatal (`gfn , " Cannot get instr_monitor_if" )
58
61
end
59
62
63
+ if (! uvm_config_db # (virtual core_ibex_dut_probe_if):: get (null , " " , " dut_if" ,
64
+ dut_vif)) begin
65
+ `uvm_fatal (`gfn , " Cannot get dut_probe_if" )
66
+ end
67
+
60
68
init_cosim ();
61
69
endfunction : build_phase
62
70
@@ -86,6 +94,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
86
94
run_cosim_rvfi ();
87
95
run_cosim_dmem ();
88
96
run_cosim_imem_errors ();
97
+ run_cosim_prune_imem_errors ();
89
98
if (cfg.probe_imem_for_errs) begin
90
99
run_cosim_imem ();
91
100
end else begin
@@ -107,17 +116,19 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
107
116
forever begin
108
117
rvfi_port.get (rvfi_instr);
109
118
110
- // Remove entries from iside_error_queue where the instruction never reaches the RVFI
111
- // interface because it was flushed.
112
- while (iside_error_queue.size () > 0 && iside_error_queue[0 ].order < rvfi_instr.order) begin
113
- iside_error_queue.pop_front ();
114
- end
119
+ if (iside_error_queue.size () > 0 ) begin
120
+ // Remove entries from iside_error_queue where the instruction never reaches the RVFI
121
+ // interface because it was flushed.
122
+ while (iside_error_queue.size () > 0 && iside_error_queue[0 ].order < rvfi_instr.order) begin
123
+ iside_error_queue.pop_front ();
124
+ end
115
125
116
- // Check if the top of the iside_error_queue relates to the current RVFI instruction. If so
117
- // notify the cosim environment of an instruction error.
118
- if (iside_error_queue.size () != 0 && iside_error_queue[0 ].order == rvfi_instr.order) begin
119
- riscv_cosim_set_iside_error (cosim_handle, iside_error_queue[0 ].addr);
120
- iside_error_queue.pop_front ();
126
+ // Check if the top of the iside_error_queue relates to the current RVFI instruction. If so
127
+ // notify the cosim environment of an instruction error.
128
+ if (iside_error_queue.size () != 0 && iside_error_queue[0 ].order == rvfi_instr.order) begin
129
+ riscv_cosim_set_iside_error (cosim_handle, iside_error_queue[0 ].addr);
130
+ iside_error_queue.pop_front ();
131
+ end
121
132
end
122
133
123
134
riscv_cosim_set_nmi (cosim_handle, rvfi_instr.nmi);
@@ -242,6 +253,16 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
242
253
wait (instr_vif.instr_cb.valid_id &&
243
254
instr_vif.instr_cb.instr_new_id &&
244
255
latest_order != instr_vif.instr_cb.rvfi_order_id);
256
+
257
+ latest_order = instr_vif.instr_cb.rvfi_order_id;
258
+
259
+ if (dut_vif.dut_cb.wb_exception)
260
+ // If an exception in writeback occurs the instruction in ID will be flushed and hence not
261
+ // produce an iside error so skip the rest of the loop. A writeback exception may occur
262
+ // after this cycle before the instruction in ID moves out of the ID stage. The
263
+ // `run_cosim_prune_imem_errors` task deals with this case.
264
+ continue ;
265
+
245
266
// Determine if the instruction comes from an address that has seen an error that wasn't a PMP
246
267
// error (the icache records both PMP errors and fetch errors with the same error bits). If a
247
268
// fetch error was seen add the instruction order ID and address to iside_error_queue.
@@ -252,6 +273,7 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
252
273
begin
253
274
iside_error_queue.push_back ('{ order : instr_vif.instr_cb.rvfi_order_id,
254
275
addr : aligned_addr} );
276
+ check_inserted_iside_error_e.trigger ();
255
277
end else if (! instr_vif.instr_cb.is_compressed_id &&
256
278
(instr_vif.instr_cb.pc_id & 32'h3 ) != 0 &&
257
279
failed_iside_accesses.exists (aligned_next_addr) &&
@@ -261,12 +283,36 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
261
283
// side of the boundary
262
284
iside_error_queue.push_back ('{ order : instr_vif.instr_cb.rvfi_order_id,
263
285
addr : aligned_next_addr} );
286
+ check_inserted_iside_error_e.trigger ();
264
287
end
265
288
266
- latest_order = instr_vif.instr_cb.rvfi_order_id;
267
289
end
268
290
endtask : run_cosim_imem_errors ;
269
291
292
+ task run_cosim_prune_imem_errors ();
293
+ // Errors are added to the iside error queue the first cycle the instruction that sees the error
294
+ // is in the ID stage. Cycles following this the writeback stage may cause an exception flushing
295
+ // the ID stage so the iside error never occurs. When this happens we need to pop the new iside
296
+ // error off the queue.
297
+ forever begin
298
+ // Wait until the `run_cosim_imem_errors` task notifies us it's added a error to the queue
299
+ check_inserted_iside_error_e.wait_ptrigger ();
300
+ // Wait for the next clock
301
+ @ (instr_vif.instr_cb);
302
+ // Wait for a new instruction or a writeback exception. When a new instruction has entered the
303
+ // ID stage and we haven't seen a writeback exception we know the instruction associated with the
304
+ // error just added to the queue isn't getting flushed.
305
+ wait (instr_vif.instr_cb.instr_new_id || dut_vif.dut_cb.wb_exception);
306
+
307
+ if (! instr_vif.instr_cb.instr_new_id && dut_vif.dut_cb.wb_exception) begin
308
+ // If we hit a writeback exception without seeing a new instruction then the newly added
309
+ // error relates to an instruction just flushed from the ID stage so pop it from the
310
+ // queue.
311
+ iside_error_queue.pop_back ();
312
+ end
313
+ end
314
+ endtask : run_cosim_prune_imem_errors
315
+
270
316
function string get_cosim_error_str ();
271
317
string error = " Cosim mismatch " ;
272
318
for (int i = 0 ; i < riscv_cosim_get_num_errors (cosim_handle); ++ i) begin
0 commit comments