Poll

Do you like the discussion forum ?

uvm_driver is a child of uvm_component that has a TLM port to communicate with the sequencer. The driver is a parameterized class with the type of request and response sequence items. This allows the driver to send back a different sequence_item type back to the sequencer as the response. However, most drivers use a response object of the same type as the request sequence item.

The uvm_driver gets request sequence items (REQ) from the sequencer FIFO using a handshake mechanism and optionally returns a response sequence item (RSP) back to the sequencer response FIFO. There are primarily two ways for the driver to get a sequence item from the sequencer. In this article, we'll look at an example that uses the first style.

driver-sequencer-get-next-item-flow

Using get_next_item method in a driver

In this case, the driver requests for a sequence item from the sequencer using the get_next_item method through the seq_item_port TLM handle. Since the implementation of this port is defined in the sequencer, the function call makes the sequencer to pop an item from its internal FIFO and provide it to the driver via the argument provided in get_next_item method.

 
class my_driver extends uvm_driver #(my_data);
  `uvm_component_utils (my_driver)
 
   virtual task run_phase(uvm_phase phase);
      super.run_phase(phase);
 
      // 1. This task will get an item from the sequencer using get_next_item()
      `uvm_info ("DRIVER", $sformatf ("Waiting for data from sequencer"), UVM_MEDIUM)
      seq_item_port.get_next_item(req);
 
      // 2. For simplicity, lets just assume the driver drives the received packet
      // during this time and consumes 20ns to complete driving the transaction
      `uvm_info ("DRIVER", $sformatf ("Start driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
      #20;
 
      // 3. After driver has finished the transaction, it has to let the sequencer know
      // by calling item_done()
      `uvm_info ("DRIVER", $sformatf ("Finish driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
      seq_item_port.item_done();
   endtask
 

Once driver gets the next item, it can drive the data in the received sequence item to the DUT via a virtual interface handle. After the driver has finished driving the item, it has to let the sequencer know that the process has finished using item_done method.

How does the sequencer get these sequence items ?

A uvm_sequence is started on a sequencer which pushes the sequence item onto the sequencer's FIFO.

 
class my_sequence extends uvm_sequence;
  `uvm_object_utils (my_sequence)
 
 
  virtual task body();
    // 1. Create an item the connected sequencer can accept
    my_data tx = my_data::type_id::create("tx");
    `uvm_info ("SEQ", $sformatf("About to call start_item"), UVM_MEDIUM)
 
    // 2. Call the start_item() task which will send this object to the driver
    start_item(tx);
    `uvm_info ("SEQ", $sformatf("start_item() fn call done"), UVM_MEDIUM)
 
    // 3. Because the class handle passed to the driver points to the same object, we 
    // can do late randomization
    tx.randomize();
    `uvm_info ("SEQ", $sformatf("tx randomized with addr=0x%0h data=0x%0h", tx.addr, tx.data), UVM_MEDIUM)
 
    // 4. Call finish_item method so that the sequence waits until the driver lets the 
    // sequencer know that this item has finished
    finish_item(tx);
    `uvm_info ("SEQ", $sformatf("finish_item() fn call done"), UVM_MEDIUM)
  endtask
endclass
 

Example

To illustrate how the get and put method calls between driver and sequencer work, let us build a simple testbench structure like the one shown below.

uvm-driver-sequencer-get-put-testbench

Define a sequence item

To keep things simple, let us define a transaction object class that will become the sequence item used for sequencer-driver communication. Assume that this sequence item contains two variables called addr and data.

 
// Note that this class is dervide from "uvm_sequence_item"
class my_data extends uvm_sequence_item;
  rand bit [7:0]   data;
  rand bit [7:0]   addr;
 
  // Rest of the class contents come here ...
endclass
 

Define the driver

The uvm_driver is parameterized to accept a class object of the type my_data and the driver is expected to unpack this class object and drive the signals appropriately to the DUT via the interface.

 
class my_driver extends uvm_driver #(my_data);
   `uvm_component_utils (my_driver)
   // Other parts of the driver code if they exist
 
   virtual task run_phase(uvm_phase phase);
      super.run_phase(phase);
 
      // 1. Get the next available item from the sequencer. If none exists, then wait until
      // next item is available -> this is blocking in nature
      `uvm_info ("DRIVER", $sformatf ("Waiting for data from sequencer"), UVM_MEDIUM)
      seq_item_port.get_next_item(req);
 
      // 2. Let us assume that the driver actively does the pin wiggling of the DUT during this time and 
      // consider it takes 20ns
      `uvm_info ("DRIVER", $sformatf ("Start driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
      #20;
 
      // 3. After the driver has driven all data to the DUT, it should let the sequencer know that it finished 
      // driving the transaction by calling "item_done". Optionally the response packet can be passed along with
      // the item_done method call and it will be placed in the sequencer's response FIFO
      `uvm_info ("DRIVER", $sformatf ("Finish driving tx addr=0x%0h data=0x%0h", req.addr, req.data), UVM_MEDIUM)
      seq_item_port.item_done();
   endtask
endclass
 

Define the sequence

A sequence item is always started using the start_item and finish_item methods.

 
class my_sequence extends uvm_sequence #(my_data);
    // Rest of the sequence code
 
    virtual task body();
      // 1. Create a sequence item of the given type
      my_data tx = my_data::type_id::create("tx");
      `uvm_info ("SEQ", $sformatf("About to call start_item"), UVM_MEDIUM)
 
      // 2. Start the item on the sequencer which will send this to the driver
      start_item(tx);
      `uvm_info ("SEQ", $sformatf("start_item() fn call done"), UVM_MEDIUM)
 
      // 3. Do some late randomization to create a different content in this transaction object
      tx.randomize();
      `uvm_info ("SEQ", $sformatf("tx randomized with addr=0x%0h data=0x%0h", tx.addr, tx.data), UVM_MEDIUM)
 
      // 4. Call finish_item to let driver continue driving the transaction object or sequence item
      finish_item(tx);
      `uvm_info ("SEQ", $sformatf("finish_item() fn call done"), UVM_MEDIUM)
    endtask
endclass
 

Define the test class

To keep things simple, let us directly create instances of the driver and sequencer inside the test class.

Driver and Sequencer should be instantiated inside an agent. An agent is instantiated in an environment and the the environment in turn should be created in the test.

 
class base_test extends uvm_test;
  // Rest of the test code is here
 
  // The sequencer is parameterized to accept objects of type "my_data" only
    my_driver                  m_drv0;
    uvm_sequencer #(my_data)   m_seqr0;
    my_sequence           m_seq;
 
  // Build the sequencer and driver components
    virtual function void build_phase(uvm_phase phase);
       super.build_phase(phase);
       m_drv0 = my_driver::type_id::create ("m_drv0", this);
       m_seqr0 = uvm_sequencer#(my_data)::type_id::create ("m_seqr0", this);
    endfunction
 
     // Connect the sequencer "export" to the driver's "port"
     virtual function void connect_phase (uvm_phase phase);
       super.connect_phase (phase);
       m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
     endfunction
 
  // Start the sequence on the given sequencer
    virtual task run_phase(uvm_phase phase);
      m_seq = my_sequence::type_id::create("m_seq");
      phase.raise_objection(this);
      m_seq.start(m_seqr0);
      phase.drop_objection(this);
    endtask
endclass
 
Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO testbench.sv(33) @ 0: uvm_test_top.m_top_env.m_drv0 [DRIVER] Waiting for data from sequencer
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_env.m_seqr0@@m_seq [SEQ] About to call start_item
UVM_INFO testbench.sv(73) @ 0: uvm_test_top.m_top_env.m_seqr0@@m_seq [SEQ] start_item() fn call done
UVM_INFO testbench.sv(75) @ 0: uvm_test_top.m_top_env.m_seqr0@@m_seq [SEQ] tx randomized with addr=0x8f data=0x1d
UVM_INFO testbench.sv(35) @ 0: uvm_test_top.m_top_env.m_drv0 [DRIVER] Start driving tx addr=0x8f data=0x1d
UVM_INFO testbench.sv(37) @ 20: uvm_test_top.m_top_env.m_drv0 [DRIVER] Finish driving tx addr=0x8f data=0x1d
UVM_INFO testbench.sv(77) @ 20: uvm_test_top.m_top_env.m_seqr0@@m_seq [SEQ] finish_item() fn call done
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_objection.svh(1271) @ 20: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 20: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Click to try this example in a simulator!   

You may also like:

You consent to our cookies if you continue to use our website. To know more about cookies, see our privacy policy. I accept cookies from this site.

Agree