I’m experimenting with a pipelined interface. I found documentation on pipelined drivers in the UVM Cookbook and buried in the UVM source files (header of uvm_transaction), but nothing on pipelined monitors. I made my own pipelined monitor based on the pipelined driver.
I plan to add protocol checks. At first I was going to add my own member variables to record the begin/end times, then I noticed uvm_transaction has accept_tr/begin_tr/end_tr and get_* methods which have the desired behavior. I haven’t seen any monitor example use these methods. Is it save/recommended to use these methods in the monitor?
Is this the correct approach? Is there a better strategy?
task run();
// this monitor supports a two-deep pipeline
fork
do_item();
do_item();
join
endtask
task do_item();
forever begin
mbus_item tr;
lock.get();
tr = mbus_item::type_id::create("tr");
accept_tr(tr);
// wait for qualification
begin_tr(tr);
// collect address phase
ap.write(tr); // put here to prevent clobber
// allows next transaction to begin address phase
lock.put();
if(tr.expectData()) begin
data_lock.get();
// wait for qualification
// collect data phase
data_lock.put();
end
// (may trigger custom "data_phase" event here)
end_tr(tr);
end
endtask: do_item