Issue with UVM Driver to send request transactions on parallel bus

Hi,
I read the blog post on UVM Driver models and am trying to implement a driver for the case (simplified) below.
On the interface, there is an address channel & a Data channel.
On the address bus, four addresses are sent back to back.
After 4 address requests are placed, at the end of A4 Data bytes are placed on the data bus in succession
A1 A2 A3 A4
xx xx xx xx D1 D2 D3 D4

To realize this I used fork_join none blocks in the run_phase of the driver which has three threads running.

  1. Thread 1 will collect sequence items from the sequence and pushes them on a two queues
  2. Thread 2 drives address on VIF from one queue
  3. Thread 3 drives data from another queue after 3 cycles delay.

When simulated I see simulation terminates abruptly with no clear message ( on edaplayground). Could someone point the flaw in my implementation?

UVM_INFO dut_parallel_driver.sv(45) @ 3: uvm_test_top.env.agent.driver [t_driver] Outof Reset
./run.sh: line 8: 608 Killed ./simv +vcs+lic+wait ‘+UVM_TESTNAME=uvmtest’ ‘+UVM_VERBOSITY=UVM_MEDIUM’
Exit code expected: 0, received: 1



class t_driver extends uvm_driver #(packet);
  
  `uvm_component_utils(t_driver)
  
  virtual p_intf.tb_mp vif_tb;

  
  packet pa[$];
  packet pd[$];
  
  
  function new(string name, uvm_component parent);
    super.new(name,parent);
    
  endfunction
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    if(!(uvm_config_db #(virtual p_intf.tb_mp)::get(this,"","virtual_intf", vif_tb)))       		begin
      `uvm_error(this.get_full_name,"Virtual Interface not set from agent");
      
   		 end

    
    
    endfunction 
  
  
  
  
  virtual task run_phase(uvm_phase phase);
   packet req;  
    if(vif_tb.reset) 
        begin
          `uvm_info(get_type_name(),$sformatf("Inside DUT Reset"),UVM_LOW);
       
          vif_tb.cb.valid    <= 0;       
      
          wait(!vif_tb.reset);
          
      end 
    
          

 forever
   begin  
     
    fork 
      begin
        
        seq_item_port.get_next_item(req);
         `uvm_info(get_type_name(),$sformatf("Fetching next request"),UVM_LOW);
     
        pa.push_back(req);
        pd.push_back(req);
     
         seq_item_port.item_done(req); 
      end 
 
   
         drive_address(pa);
         drive_data(pd);
    join_none   
   
   end
    
  endtask

virtual  task automatic drive_address(packet req[$]);
    packet pkt_ad; 
    //wait until there are packets in Queue
    while (req.size() == 0) 
               begin
              @(vif_tb.cb); 
               end 
    `uvm_info(get_type_name(),$sformatf("Addr Queue Size = %0x", req.size()),UVM_LOW);
    
    while (req.size()!=0)
      begin
    	pkt_ad = req.pop_front();
    
    	`uvm_info(get_type_name(),$sformatf("Executing Addr = %0x", pkt_ad.addr),UVM_LOW);
   
          vif_tb.cb.addr  <= pkt_ad.addr;
          vif_tb.cb.valid <= 1'b1;
 
          @(vif_tb.cb);
           vif_tb.cb.valid <= 1'b0;
      end
   
    
  endtask
  
  
 virtual task automatic drive_data(packet req_d[$]);
        packet pkt_dat; 
    //wait until there are packets in Queue
    while (req_d.size() == 0) 
               begin
              @(vif_tb.cb); 
               end 
    `uvm_info(get_type_name(),$sformatf("Data Queue Size = %0x", req_d.size()),UVM_LOW);
    
    while(req_d.size()!=0)
      begin
    pkt_dat = req_d.pop_front();
    `uvm_info(get_type_name(),$sformatf("Executing Data = %0x", pkt_dat.data),UVM_LOW);
 
    repeat(3) @(vif_tb.cb);
     
                 vif_tb.cb.data <= pkt_dat.data;
      end

    
  endtask
  
  
endclass 


In reply to Nagarjuna Chary:

It would really help to show a complete example showing the behavior you are seeing.

Your driver is taking all of the sequence_items from the sequencer and storing them in a queue in the driver. I’m guessing that when your sequence has completed generating all of the sequence_items, your test ends immediately and the driver never has a chance to actually drive any transactions.

In reply to cgales:

To prevent the test not to exit I added a dummy delay in the sequence class ( which should otherwise wait until it got all the responses through the scoreboard etc). Let me know if providing the link to code on EDAPlayground is fine.


  
class t_sequence extends uvm_sequence #(packet);
  
  `uvm_object_utils(t_sequence)
  
     int num_trans;
 
  
  function new(string name="my_sequence");
    super.new(name);
  endfunction
  
  virtual task body();
 
  
    packet pkt; 
    $display("In Body Task");
 
    if(!(uvm_config_db#(int)::get(null,this.get_full_name,"req_count",num_trans)))
      begin
      `uvm_warning(get_type_name(),"Transaction count not specified in Test Top")
   
        num_trans =10;
      end
    else `uvm_info(this.get_full_name,$sformatf("Running sequence with %d requests",num_trans), UVM_HIGH);
    
    
    pkt = packet::type_id::create("pkt");
    
    use_response_handler(1); // Enable Response Handler
    
   
    
    for(int i=0;i<num_trans;i++)
      begin
       
       `uvm_info(this.get_full_name,$sformatf("Sending request number %d",count), UVM_HIGH);
     
        start_item(pkt);
        assert(pkt.randomize());
        
        `uvm_info(get_type_name,$sformatf("Addr=%0x Data=%0x",pkt.addr, pkt.data), UVM_MEDIUM); 
 
        
        finish_item(pkt);
      
        
       
      end
    
 
    
    #100;
    `uvm_info(this.get_full_name,$sformatf("Idle waiting"), UVM_MEDIUM);
    
    wait(count == num_trans);
    
  endtask
  
   function void response_handler(uvm_sequence_item response);
    count++;
     `uvm_info(get_type_name,$sformatf("Responses Received=%0x",count), UVM_MEDIUM);
   endfunction: response_handler
  
  
  
  
endclass

In reply to Nagarjuna Chary:

Per the blog post I referenced, posting a link to your example on EDA Playground is ideal.

In reply to cgales:

Here is the link. file name being discussed about is “dut_parallel_driver.sv”

In reply to Nagarjuna Chary:

You have some issues in the parallel driver where you are trying to fork processes when you receive packets. The ideal implementation is to have the address/data processes running all the time and waiting for transactions to appear in the queue.

You can see the changes I made here.

In reply to cgales:

Thanks much cgales! I was struggling to create the parallel threads which would get requests from the sequence class & drive the dut and was focusing on packing them in a single fork/forever construct. Thanks also for the usage of ‘phase_ready_to_end’. I was under the assumption that such a ‘wait’ is only possible with a scoreboard.