In reply to rtawade:
oy… ok my first response would be to migrate to UVM.
Since that’s out of the way, I may not be the best to answer this question, but…
I would have SB threads which call monitor tasks in the BFM. E.g. from SB, fork off a forever statement calling bfm.monitor_tx_bus (monitor the serial TX bus). When this task returns, the SB knows a serial transmission just occurred, and it returns the whole dataword it saw off bus. This needs to be compared to a predicted value, which should have ALREADY been generated from your test.
- test writes 'hAB to serial TX
- SB stores predicted output 'hAB in local fifo
- SB bfm.monitor_tx() returns, has data 'hAB.
- check passed!
You can queue up several predicted values in a row before actual values appear on bus.
Same deal on RX. Whatever is driving the RX bus can provide the data words for the predicted values. If it’s a loopback, well you already know the answer.
Your forked tasks can also use that time to check parity, whatever.
I think it would only have 2 perpetual tasks tho:
fork
monitor_rx();
monitor_tx();
join_none
task monitor_rx();
forever begin
bfm.monitor_rx();
// got something!
// perform checking…
begin
endtask
task monitor_tx();
forever begin
bfm.monitor_tx();
// got something!
// perform checking…
begin
endtask
task execute();
tester_h = new(bfm);
scoreboard_h = new(bfm);
coverage_h = new(bfm);
fork
scoreboard_h.execute(); // start the scoreboard. runs all simulation.
tester_h.execute(); // start the tests
join_none
endtask : execute
if the SB has tasks it needs to fork, it can do that itself.