Support back to back transactions in UVM driver

Hello,

I have a simple driver that handle an interface with 1-cycle valid and data.
after the transaction ends valid should de-assert.

my_driver.sv:


virtual taskmain_phase(uvm_phase phase);
   super.main_phase(phase);

   forever begin
      seq_item_port.get_next_item(req);
      drive_data(reg);
      seq_item_port.item_done();
   end
endtask : main_phase


// --------------------------------------

virtual protected task drive_data(item curr_item);

   // start transaction 
   @(posedge vif.clk);
   vif.valid <= 1;
   vif.data <= curr_item.data;

   // release the bus 
   @ (posedge vif.clk);
   vif.valid <= 0;
   vif.data <= $random();

endtask:drive_data


The driver handle each transaction with 2 clock cycles:
1 cycle driving and second cycle to release.

Now I want to work with full bandwidth,
I mean that valid shouldn’t de-assert between transactions

How can I do it ?

In reply to AL_verif:
You can control it from the sequence by giving the constraints and you can make the driver as reusable


//sequence 

task body()
  req = item::type_id::create("req");
 // By Cange the loop limit you can control the no.of data to be driven continuously 
  repeat(10)
  begin
  start_item(req);
  assert(req.randomize() with{ valid == 1;});
  finish_item(req);
  end
  // After driving data making valid as 0
  start_item(req);
  assert(req.randomize() with{ valid == 0;});
  finish_item(req);
endtask

// Now in driver you need not to do any changes 
virtual task main_phase(uvm_phase phase);
   super.main_phase(phase);
 
   forever begin
      seq_item_port.get_next_item(req);
      drive_data(reg);
      seq_item_port.item_done();
   end
endtask : main_phase
 
 
// --------------------------------------
 
virtual protected task drive_data(item curr_item);
 
   // start transaction 
   @(posedge vif.clk);
   vif.valid <= 1;
   vif.data <= curr_item.data;
 

endtask:drive_data


Regards,
Shanthi
www.maven-silicon.com

In reply to shanthi:

valid is a controö signal you should never put it to the seq_item/transaction. But you could have 2 different operational modes, one for the standars and another one for the full band-width.

In reply to chr_sue:

2 different modes of what ? 2 modes in driver and add in the seq_item additional field to handle it ?

We can’t solve it in driver ?

In reply to chr_sue:

I tried support it in driver and this is working, but I think it is little dangerous the way I did it :

my_driver.sv:



bit new_req;

virtual taskmain_phase(uvm_phase phase);
   super.main_phase(phase);
 
   forever begin
      seq_item_port.get_next_item(req);
      new_req = 1;
      drive_data(reg);
      new_req = 0;
      seq_item_port.item_done();
   end
endtask : main_phase
 
 
// --------------------------------------
 
virtual protected task drive_data(item curr_item);
 
   // start transaction 
   @(posedge vif.clk);
   vif.valid <= 1;
   vif.data <= curr_item.data;
 
   fork 
      begin
         // release the bus 
         #0; // To be done last in simulation tick 
         if (new_req == 0 ) begin
            @ (posedge vif.clk);
            vif.valid <= 0;
            vif.data <= $random();
         end
       end
   join_none
endtask:drive_data


I added a new signal new_req that used as a flag to indicates if new req assert when the current item still didn’t release the bus.

I’m worried from raise issues on this signal.

In reply to AL_verif:
If you don’t want to consider the control signal in the sequence item, you can do this way. In transaction class declare data as a dynamic array & constraint the size of the array from the sequence.


//sequence 
 
task body()
  req = item::type_id::create("req");

 //for single transfer
  begin
  start_item(req);
  assert(req.randomize() with{ data.size == 1;});
  finish_item(req);
  end
  //for multiple transfersi.e.,full band width
  start_item(req);
  assert(req.randomize() with{data.size == 10/*or any other values per your requirement*/;});
  finish_item(req);
endtask
 
// Now in driver you need not to do any changes 
virtual task main_phase(uvm_phase phase);
   super.main_phase(phase);
 
   forever begin
      seq_item_port.get_next_item(req);
      drive_data(reg);
      seq_item_port.item_done();
   end
endtask : main_phase
 
 
// --------------------------------------
 
virtual protected task drive_data(item curr_item);
 
   // start transaction 
   foreach(curr_item.data[i])
   begin
   @(posedge vif.clk);
   vif.valid <= 1;
   vif.data <= curr_item.data[i];
   end
   
   // release the bus 
   @ (posedge vif.clk);
   vif.valid <= 0;
   vif.data <= $random();

 
endtask:drive_data


Regards,
Shanthi
www.maven-silicon.com

In reply to shanthi:

Thank you. your solution is nice.

but the issue is that I don’t have only “data” in my item, I have some fields.
and I don’t think it’s a good Idea to handle some queues or a queue of struct.

I believe that there is a simple solution to be done in driver.
we just need to find it.

In reply to AL_verif:

I have a similar issue. Were you able to fix this problem?

In reply to AL_verif:

Could you please show how your seq_item looks like?

In reply to chr_sue:

Hi Chris,

My Sequence body looks like this:
// Sequence body


task some_seq::body();
 // Start item
  start_item(item_h);
  // finish item
  finish_item(item_h);
  // wait reponse
  get_response(item_h);
endtask

My Driver looks like this:


task drv::run_phase(uvm_phase phase);
  @(m_vif.drv_cb iff (m_vif.rst_n === 0));
  // Reset signals
  reset_signals();
  @(m_vif.drv_cb iff (m_vif.rst_n === 1));
 // Start driving 
  get_and_drive();
endtask : run_phase

task drv::get_and_drive();
  process main_thread;
  process rst_mon_thread;

  forever begin
   //Don't drive during reset
   while(m_vif.rst_n !== 1)
     @(m_vif.drv_cb iff (m_vif.rst_n === 1));

   //Get the next item from the sequencer
   seq_item_port.get_next_item(req);
   if(!$cast(rsp, req.clone())) begin
     `uvm_fatal(get_type_name(), "run_phase::Cast rsp<-req failed!")
   end

   rsp.set_id_info(req);

   //Drive current transaction
   fork 
     //Drive the transaction
     begin
       main_thread=process::self();
       drive_item(rsp);
       if(rst_mon_thread) rst_mon_thread.kill();
     end
     // Monitor the reset signal
     begin
       rst_mon_thread=process::self();
       @(negedge m_vif.rst_n) begin
         if(main_thread) main_thread.kill();
         //Do reset
         reset_signals();
       end
     end
   join_any

   //Send item_done and a response to the sequencer
   seq_item_port.item_done();
   seq_item_port.put_response(rsp);
  end // forever
endtask : get_and_drive

task drv::drive_item(ref item_t item_h);
 //Drive the req
 if(item_h.m_trans_type == FIRST_TYPE) begin 
   automatic int delay_cycles = $urandom_range(m_cfg_h.min_dly,m_cfg_h.max_dly);
   repeat (delay_cycles)
     @(m_vif.drv_cb);
   if(some_condition) begin
    m_vif.drv_cb.One_Valid <= 1;
    m_vif.drv_cb.One_Hdr <= item_h.m_hdr;
   end
   else begin
     automatic item_t auto_item_h = item_h;
     check_one_credits_and_drive(auto_item_h); //time consuming task
   end
    @(m_vif.drv_cb);
    m_vif.drv_cb.One_Valid <= 0;
  end
 else if(item_h.m_trans_type == SECOND_TYPE) begin
   automatic int delay_cycles = $urandom_range(m_cfg_h.min_dly,m_cfg_h.max_dly);
   repeat (delay_cycles)
   if(some_condition) begin
     m_vif.drv_cb.Second_Valid <= 1;
     m_vif.drv_cb.Second_Hdr <= item_h.m_hdr;
     m_vif.drv_cb.Second_Data <= item_h.m_data;
   end
   else begin
     automatic item_t auto_item_h = item_h;
     check_two_credits_and_drive(auto_item_h); //time consuming task
   end
   @(m_vif.drv_cb);
   m_vif.drv_cb.Second_Valid <= 0;
 end 
endtask :drive_item


In reply to yasaswi93:

Thanks for sharing your code. Unfortunately the interesting piece of code was missing. item_t.
Please provide it.
Another remarks:
(1) Using the process variables is not relevant in the UVM. You can remove it. This makes your code more easy.
(2) Do you have low-active reset. The namimg looks like this. In the code your reset is considered as highh active.
(3) You could randomize the delay _cycles outside of theh driver, by putting it to the seq_item item_t.

In reply to chr_sue:

In reply to yasaswi93:
Thanks for sharing your code. Unfortunately the interesting piece of code was missing. item_t.
Please provide it.
It is this:


class item extends uvm_sequence_item;
rand trans_type_e                    m_trans_type;
rand hdr_t                           m_hdr;
rand logic [127:0]                   m_data;
`uvm_object_utils(item)
extern function new(string name="item");
endclass : item

Another remarks:
(1) Using the process variables is not relevant in the UVM. You can remove it. This makes your code more easy.
-Sure.
(2) Do you have low-active reset. The namimg looks like this. In the code your reset is considered as high active.
-It’s active low and there is no issue in the reset logic I think.
(3) You could randomize the delay _cycles outside of the driver, by putting it to the seq_item item_t.
-But these delay parameters are from cfg class. So having cfg handle in every transaction didn’t seem to be good idea to me.

In reply to yasaswi93:

We should go ahead step-by-step.
First mdify your sequence body task like this:

task some_seq::body();
 // Start item
  start_item(item_h);
  item_h.randomize() with { <your constraints here> };
  finish_item(item_h);
  // wait reponse
  get_response(item_h);
endtask

You can pass the constraints from the config object and use it here.
This randomizes your seq_item data memebers wrt your needs.
And I believ you can also simplify your driver code.
If your interface protocol allows the valid signal to be high during execution, then assert it at the beinning of the data transfer and de-assert it after the tarnsfer has been finished.
And please remove the mechanism with the process variables. It is not needed.