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