What is the right way to write a UVM Driver code which effectively manages delays between transactions i.e. De-Assertion of 'valid' after the transaction finishes

Hi,

I am trying to create a UVM driver logic to an interface which is very similar to an APB Interface.
In the run_phase of the driver, I have created simple tasks ‘send_write_tx’ and ‘send_read_tx’ which drives the respective write and read transactions over the Interface :

Interface Code :


interface proc_interface #(int DSIZE = 32) (input clk, input rst);
logic       valid;
logic       ready;
logic       wr_rd;
logic [2:0] addr;
logic [DSIZE-1:0] wr_data;
logic [DSIZE-1:0] rd_data;

// Clocking block
clocking cb @(posedge clk);
  output valid;
  input  ready;
  output wr_rd;
  output addr;
  output wr_data;
  input  rd_data;
endclocking
endinterface

Driver Code Logic :

// Run Phase


task run_phase(uvm_phase phase);
  tx_typ  proc_seq_item;
  forever
    begin
      // Wait for reset being deasserted
      wait(proc_vif.rst == 1'b0);
      seq_item_port.get_next_item(proc_seq_item);
      if(proc_seq_item.wr_rd == 1'b1)
        begin send_write_tx(proc_seq_item); end
      else
        begin send_read_tx(proc_seq_item); end
      seq_item_port.item_done();
    end
endtask

// Task for Sending Write
task send_write_tx(tx_typ  tx);
  `uvm_info(report_id,"Sending Write Transaction", UVM_LOW)
  tx.print();
  wait(proc_vif.ready);
  @(posedge proc_vif.clk)
    begin
      proc_vif.wr_rd   <= tx.wr_rd;
      proc_vif.valid   <= 1'b1;
      proc_vif.wr_data <= tx.wr_data;
      proc_vif.addr    <= tx.addr;
    end
endtask

// Task for Sending Read
task send_read_tx(tx_typ  tx);
  wait(proc_vif.ready);
  @(posedge proc_vif.clk)
    begin
      proc_vif.wr_rd   <= tx.wr_rd;
      proc_vif.valid   <= 1'b1;
      tx.rd_data       <= proc_vif.rd_data;
      proc_vif.addr    <= tx.addr;
    end
endtask

With the above said implementation, what is the correct way to de-assert the ‘valid’ after a transaction is complete ?.
My point being, that if ‘valid’ is de-asserted, the de-assertion will consume 1 clock.

But this compromised clock could have been another valid transaction (Read or Write), i.e. what if another transaction was in pipeline ?.
Also if left unhandled (i.e. left asserted), the next clock will assume the transaction as still valid, since ‘valid’ remains asserted.

Thanks
Jayant

In reply to jayant_yadav_07:

You probably want to model your driver more like a state machine where you call try_next_item() every clock cycle instead of get_net_item().

task run_phase(uvm_phase phase);
  // tx_typ  proc_seq_item; there is no need for this - 'req' is already declared in driver base class
  forever
    begin
      // Wait for reset being deasserted
      @(posedge proc_vif.clk iff (proc_vif.rst == 1'b0))
      if (proc_vif.valid) seq_item_port.item_done(); // item was sent last cycle. 

      proc_vif.valid   <= 1'b0;
      if (req == null) // get a new item if we don't already have one
         seq_item_port.try_next_item(req);
      if (proc_seq_item != null && !proc_vif.ready) break; // not ready to send a valid item
      if(proc_seq_item.wr_rd == 1'b1)
        begin send_write_tx; end
      else
        begin send_read_tx; end
    end
endtask
 
// Task for Sending Write
task send_write_tx(tx_typ  tx);
  `uvm_info(report_id,"Sending Write Transaction", UVM_LOW)
  tx.print();
    begin
      proc_vif.wr_rd   <= req.wr_rd;
      proc_vif.valid   <= 1'b1;
      proc_vif.wr_data <= req.wr_data;
      proc_vif.addr    <= req.addr;
    end
endtask
 
// Task for Sending Read
task send_read_tx;
    begin
      proc_vif.wr_rd   <= req.wr_rd;
      proc_vif.valid   <= 1'b1;
      tx.rd_data       <= proc_vif.rd_data; // this won't work because rd_date is in the next cycle
      proc_vif.addr    <= req.addr;
    end
endtask