UVM Driver Use Models – Part 1


Since it is evident that Driver is a component in the UVM environment which deals with transaction or sequence item and transform it into pin level signal activities in temporal domain by following a particular protocol or use model & vice versa. 

Another important activity is UVM Driver’s synchronization with the Sequences to produce a verification scenario seamlessly and without any dead lock situation to happen. In case mutual understanding of implementation between UVM Driver and Sequence towards each other is not properly defined – there is a high chances of dead lock situation to occur.

We’ll look into this aspect & try to understand the possible way a UVM Driver can be implemented based on the direction of data flow and pipeline behavior. UVM Driver is capable of accepting the transaction request and responding back with the response items depending on the interface protocol requirements.

Primarily, there are 4 use models being used to implement the UVM Driver. Lets first know about these use models briefly and later we’ll go into details of these:

  1. Unidirectional Non-pipelined

    • In this use model, the data flow is in forward direction and no response data flow takes place which means requests are sent to the Driver but no response is expected from the UVM Driver. An example is PCM interface.
  2. Bidirectional Non-pipelined

    • In this use model, the data flow happens in the both direction which means the request (req) is sent from a Sequence to the UVM Driver and in result UVM Driver responds back to the Sequence with a response. Important thing to note is that the response occurs in lock-step with the request and only one transfer is active at a time. One example of this implementation is AMBA APB bus.
  3. Pipelined

    • In this use model, the data flow is bidirectional. The request phase overlaps with the response phase of the previous request phase. This kind of UVM Driver implementation provides improved performance but at the same time it adds complexity with respect to Sequence Driver synchronization. An Example of this use model is AMBA AHB bus.
  4. Out-of-Order Pipelined

    • This use model provides supports for advanced feature like out-of-order transactions, where response phases need not to be in an order with the request phases. Depending on the availability of the response data (driven from the corresponding slave) can be responded back in a pipelined fashion. Again, such features further improve the system performance with the additional complexity being introduced. Usually these features take support of queues and IDs to handle the complexities. Another variation of this use model is interleaved burst transfer where a running burst can be interleaved by an another transfer. An example of this use model is AMBA AXI bus which are used by most of the advanced SoC now a days.

Now lets dive deep into these UVM Driver use models to understand each one by one in detail along with the corresponding UVM code:

  1. Unidirectional Non-pipelined

In this use model, the data flow is uni-directional. Sequence-Driver combination sends series of transaction items to the DUT interface but don’t receive any response transaction items or sequence_item. That means, request (req) item is being generated from the Sequence to the UVM Driver but no response item is sent back/updated from the UVM Driver to the Sequence.

Here, Driver controls the flow of transaction items by using get_next_item() to fetch the next transaction item to be processed & does’nt call item_done() until it finishes the processing of this transaction item. The Sequence is blocked at its finish_item() call until the item_done() call is made by the Driver.

An Unidirectional Example:

Below is shown a timing diagram for ADPCM packets transmission using the PCM framing protocol.

Diagram 1: PCM Unidirectional Timing Diagram

In the above waveform, first signal called “Frame” gets asserted on the positive edge of the clock i.e. “Clk“. On the next & all upcoming positive edge of the Clk, Data[] bus is driven with the new values till the signal “Frame” gets de-asserted.

Now lets see this use model implementation using the UVM Driver & Sequence Code:

///// Interface Declaration
interface adpcm_int; 
logic clk;  logic frame; 
logic [3:0] data;
endinterface: adpcm_int 
///// Transaction item class adpcm_txn 
class adpcm_txn extends uvm_sequence_item;
`uvm_object_util(adpcm_txn)  rand logic[3:0] data;
rand int delay;   function new (string name); 
endfunction: new   constraint del_bet_pac {delay inside {[1:20]};
}  endclass: adpcm_txn 
///// Driver class adpcm_drv 
class adpcm_drv extends uvm_driver #(adpcm_txn); 
`uvm_component_utils (adpcm_drv)  adpcm_txn req;  
virtual adpcm_int adpcm_if; 
function new(string name, uvm_components parent)  super.new(name, parent); 
endfunction: new    task run_phase (uvm_phase phase);
// Default conditions  adpcm_if.frame <= 0; 
 adpcm_if.data <= 0;  
forever   begin  seq_item_port.get_next_item(req); 
repeat(req.delay) begin  @(posedge adpcm_if.clk);
end  adpcm_if.frame <= 1; 
for (int i = 0; i  4; 
end  adpcm_if.frame <= 0; 
end  endtask: run_phase  endclass: adpcm_drv                                                                                                                                                              ///// Sequence Class adpcm_seq 
class adpcm_seq extends uvm_sequence #(adpcm_txn);
`uvm_component_utils (adpcm_seq)  adpcm_txn req;  
rand int no_of_reps = 10; 
function new (string name);
endfunction: new  task body; 
req = adpcm_txn::type_id::create("req", this);
for (int i = 0; i < no_of_reps; i++) begin  start_item(req); 
if (!req.randomize()) begin  `uvm_error("BODY", "req randomization failed")  end  finish_item(req); 
`uvm_info("ADPCM_SEQ BODY", $sformatf("Transmitted frame %0d", i), UVM_LOW)  end  endtask: body endclass: adpcm_seq
Here in the above UVM code, we can see that inside the Driver i.e. adpcm_drv class code, virtual interface i.e. adpcm_if is being driven by the transaction item i.e. req request data member i.e. frame & data[] and no response data member is updated as part of Driver’s run_phase() task. At last once the whole of req transaction item is consumed by the Driver, item_done() call is made to unblock the Sequence’s finish_item() which completes the loop.

2. Bidirectional Non-pipelined

In this use model as the name indicates “Bidirectional”, data flow happens in both paths i.e. request path and response path. When the sequence item is sent to the Driver from the Sequence, the request phase of pin level protocol is executed first by the UVM Driver and once its over, in the response phase the UVM Driver sends back the response to the Sequencer/Sequence. The main point here is that – next request phase can not take place unless the previous response phase is completed.

A peripheral bus called AMBA APB is a good example for this kind of protocol. This protocol timing diagram is shown below:

Diagram 2: Peripheral bus APB timing waveform

Out of the above signals ValidRNW (Read Not Write), AddressWrite Data participates in the request phase which is initiated with the assertion of the Valid signal, while Address and RNW defines the type of transfer.

Signals ReadyRead Data & Error participates in the response phase and response phase is completed when the Ready signal got asserted.

Lets see this implementation using UVM code below:

///// APB Bus Interface

interface apb_interface;

 logic clk;

 logic rstn;

 logic [31:0] Addr;

 logic [31:0] Write_Data;

 logic RNW;

 logic Valid;

 logic Ready;

 logic [31:0] Read_Data;

 logic Error;

endinterface: apb_interface

///// APB Bus Sequence Item Class

class apb_seq_item extends uvm_sequence_item;


 /// Request fields

 rand logic [31:0] addr;

 rand logic [31:0] write_data;

 rand bit read_not_write;

 rand int delay;

 /// Response fields

 bit error;

 logic [31:0] read_data;

endclass: apb_seq_item

///// APB Bus Driver Class

class apb_bus_driver extends uvm_driver #(apb_seq_item);


 apb_seq_item req;

 virtual apb_interface apb_if;

 function new (string name, uvm_component parent);

 super.new(name, parent);

 endfunction: new

 task run_phase (uvm_phase phase);


 apb_if.Valid <= 0;

 apb_if.RNW <= 0;

 /// Out of Reset

 @(posedge apb_if.rstn);

 /// Pin Level Transfer




 repeat(req.delay) begin

 @(posedge apb_if.clk);


 apb_if.Valid <= 1;

 apb_if.Addr <= req.addr;

 apb_if.RNW <= req.read_not_write;

 if (req.read_not_write == 0) begin

 apb_if.Write_Data <= req.write_data;


 while(apb_if.Ready != 1) begin

 @(posedge apb_if.clk);


 // At the end of pin level bus transaction

 // Copy response data into the req related fields

 if (req.read_not_write == 1) begin

 req.read_data <= apb_if.Read_Data;


 req.error <= apb_if.Error;

 apb_if.Valid <= 0;



 endtask: run_phase

endclass: apb_bus_driver

///// APB Bus Sequence

class apb_bus_seq extends uvm_sequence #(apb_seq_item)


 function new(string name);


 endfunction: new

 // No. of iterations

 rand int iti = 30

 apb_seq_item req;

task body();

 req = apb_seq_item::type_id::create("req", this);




 assert (!req.randomize() with {addr inside {[32`h1000_0000:32`h1000_001C]};}) begin

 `uvm_error("Body", "Randomization Error");



`uvm_info ("Response Fields", $sformatf("Read Data = %h", req.read_data), UVM_LOW)


endtask: body

endclass: apb_bus_seq 

If we’ll analyze the Driver UVM code above, inside the body() task, first the request phase is completed then Ready signal is waited to be asserted and then in the response phase, all the request fields of the sequence items are updated with the latest values.

The key point here to note is that UVM Driver is sending back the response to the Sequence/Sequencer by updating the fields within the “req” transaction item before making the item_done() call. At the Sequence side of the transfer, the Sequence is blocked in the finish_item() call until the item_done() occurs. When Sequence is unblocked, its req handle is still pointing to the req object which has had its response items/fields/data members updated by the UVM Driver. This means that before starting the next loop (if its applicable) the Sequence can access the response items of the transaction item i.e. req. This we can see in the above code of Sequence i.e. apb_bus_seq. Just next line after the finish_item() call i.e. `uvm_info where we can access the req.read_data response field for the latest updated values by the Driver.

Hence above explanation demonstrates the bidirectional but non-pipelined behavior Driver use model implementation & how it works in tandem with the Sequence.

Bạn Có Đam Mê Với Vi Mạch hay Nhúng      -     Bạn Muốn Trau Dồi Thêm Kĩ Năng

Mong Muốn Có Thêm Cơ Hội Trong Công Việc

Và Trở Thành Một Người Có Giá Trị Hơn

Bạn Chưa Biết Phương Thức Nào Nhanh Chóng Để Đạt Được Chúng

Hãy Để Chúng Tôi Hỗ Trợ Cho Bạn. SEMICON  


Lần cập nhật cuối ( Chủ nhật, 25 Tháng 7 2021 15:01 )