handshake - DilipKrishnappa/UVM GitHub Wiki
Driver Sequence Handshake
Introduction
In Universal Verification Methodology (UVM), The method called Driver Sequence Handshake is a mechanism, where we send the transactions from the sequence to the Driver in the order to provide stimulus to the DUT.Since we know that the whole understanding of different type of transactions is imbided into the sequences and Sequencers are being used as the physical component to execute those Sequences. A particular Sequence is directed to run on a Sequencer which in turns further breaks down into a series of transaction items and these transaction items are needs to be transferred to the Driver where these transaction items are converted into cycle based signal/pin level transitions.
The Sequencer get the series of the transaction item/sequence_item through the sequence. This transaction items are needs to be transferred to the Driver where these transaction items are converted into cycle based signal/pin level transitions.
Generally, the Sequence as six function/operation for communicate with the Driver they are
1.create_item() / create req.
2.wait_for_grant().
3.randomize the req.
4.send the req.
5.wait for item done.
6.get response.
The above Sequence can be done with three function/operation for communicate with the Driver they are
- start_item(txn);
- txn.randomize.
- finish_item(txn);
Sequence-Driver-Sequencer communication
Based on the varieties of methods available with sequence and driver, they are two Methods
-
Using get_next_item and item_done methods in the driver
-
Using get and put methods in driver
1.Using get_next_item and item_done methods in the driver (1st procedure)
The below Fig.1 show the Transaction Exchange flow between the component Having said that, sending a transaction/sequence_item from a Sequencer to a Driver is a 4 step process which is discussed below.
Fig.1:Transaction Exchange between Driver Sequencer
- The Driver class contains the TLM port called uvm_seq_item_pull_port is connected to the uvm_seq_item_pull_Export called sequencer Both are connected in the run phase of UVM_AGENT. The driver can use the TLM function to get the next item from the Sequencer when it is required.
Sequencer side operation:
1.Creating the “transaction item” with the declared handle using factory mechanism.
-
Calling “start_item(<transaction_item_handle>)“. This call blocks the Sequencer till it grants the Sequence and transaction access to the Driver.
-
Randomizing the transaction OR randomizing the transaction with in-line constraints. Now the transaction is ready to be used by the Driver.
-
Calling “finish_item(<transaction_item_handle>)“. This call which is blocking in nature waits till Driver transfer the protocol related transaction data.
- These are the operational steps from a Sequence which we want to execute using a Sequencer that is connected to a Driver inside an Agent component. Whole of this process is shown in the Figure 1
Driver side operation:
-
Declaring the “transaction item” with a handle.
-
Calling the “get_next_item(<transaction_item_handle>)“. Default transaction_handle is “req”. “get_next_item()” blocks the processing until the “req” transaction object is available in the sequencer request FIFO & later “get_next_item” returns with the pointer of the “req” object.
-
Next, Driver completes its side protocol transfer while working with the virtual interface.
-
Calling the “item_done()” OR “item_done(rsp)“. It indicates to the sequencer the completion of the process. “item_done” is a non-blocking call & can be processed with an argument or without an argument. If a response is expected by the Sequencer/Sequence then item_done(rsp) is called. It results in Sequencer response FIFO is updated with the “rsp” object handle.
Using get_next_item and item_done methods in the driver (2nd procedure)
Fig.2: Flow of Transactions between Sequencer,Driver and DUT.
-
create_item(): Create a sequence item and register in the factory using the create_item function call.
Example: req = **_seq_item::type_id::create(req); -
wait_for_grant() The wait_for_grant issues request to the sequencer and wait for the grant from the sequencer.
It returns when the sequencer has granted the sequence. -
Randomize() Randomize the sequence item and send it to the sequencer using send_request call. There should not be any simulation time delay between wait_for_grant and send_request method call. The sequencer forwards the sequence item to the driver with the help of REQ FIFO. This unblocks the get_next_item() call and the driver receives the sequence item.
-
wait_for_item_done()
The wait_for_item_done() call from sequence gets blocked until the driver responds back. -
In the meantime, the driver drives the sequence item to the DUT using a virtual interface handle. Once it is completed, the item_done method is called. This unblocks the wait_for_item_done method from the sequence.
Example
Code snippet
Transaction class
class transaction extends uvm_sequence_item;
rand bit [3:0] a;
rand bit [3:0] b;
bit [4:0] y;
function new(input string name = "transaction");
super.new(name);
endfunction
`uvm_object_utils_begin(transaction)
`uvm_field_int(a,UVM_DEFAULT)
`uvm_field_int(b,UVM_DEFAULT)
`uvm_field_int(y,UVM_DEFAULT)
`uvm_object_utils_end
endclass
Sequence class
class sequence1 extends uvm_sequence#(transaction);
`uvm_object_utils(sequence1)
transaction trans;
function new(string name = "sequence1");
super.new(name);
endfunction
virtual task body();
begin
//creating an item
trans = transaction::type_id::create("trans");
`uvm_info("Sequence","Sequence item is created",UVM_NONE)
wait_for_grant();
`uvm_info("Sequence","Grant received now randomizing the data",UVM_NONE)
assert(trans.randomize());
`uvm_info("Sequence",$sformatf("randomised value, a:%0d , b:%0d" ,trans.a,trans.b), UVM_NONE);
`uvm_info("Sequence","Randmization done and now sent request to driver",UVM_NONE)
send_request(trans);
`uvm_info("Sequence","Waiting for item done response from driver",UVM_NONE);
wait_for_item_done();
`uvm_info("Sequence","Ack.recieved from driver,Sequence ended",UVM_NONE)
end
endtask
endclass
Driver class
class driver extends uvm_driver#(transaction);
`uvm_component_utils(driver)
transaction trans;
function new(input string name = "DRV", uvm_component parent);
super.new(name,parent);
endfunction
virtual task run_phase(uvm_phase phase);
trans = transaction::type_id::create("trans");
forever begin
`uvm_info("Driver", "Waiting for data from sequencer", UVM_NONE);
//get an item from sequencer using get_next method
//Instead of get_next_item, we can use get(req)
seq_item_port.get_next_item(req);
`uvm_info("Driver", $sformatf("Start driving data, a : %0d b:%0d", req.a, req.b), UVM_NONE);
//call the item_done method to send the request item back to sequencer
//Instead of item_done, we can use put(req)
seq_item_port.item_done();
`uvm_info("Driver", $sformatf("Finish driving data, a : %0d b:%0d", req.a, req.b), UVM_NONE);
end
endtask
endclass
Output
Below figure shows the output of Driver sequence handshake mechanism.
Figure.2.Output of sequence driver handshake mechanism
- Initially the driver is waiting for data from the sequence through sequencer using TLM ports.
- In sequence class the sequence items are created using create method.
- When the sequencer receives the wait_for_grant call, it sends to sequence block and the data gets randomized.
- After randomization i.e a=6 and b=15, the request is send to driver through sequencer and it is waiting for item_done response from driver.
- Then the driver starts driving data i.e a=6 and b=15, after finishes, the driver will wait for the next data from the sequence and at the same time driver will send acknowledgement to sequencer as item done.
- After these process, then it will create next item.
2. Using get and put methods in driver
Figure.3. get() and put() method
-
create_item() Create a sequence item and register in the factory using the create_item function call. Example: req = **_seq_item::type_id::create(req);
-
wait_for_grant() The wait_for_grant issues the request to the sequencer and wait for the grant from the sequencer. It returns when the sequencer has granted the sequence.
-
randomize() Randomize the sequence item and send it to the sequencer using send_request call. There should not be any simulation time delay between wait_for_grant and send_request method call. The sequencer forwards the sequence item to the driver with the help of REQ FIFO. This unblocks the get() call and the driver receives the sequence item.
-
wait_for_item_done() wait_for_item_done() call from the sequence gets blocked until the driver calls the get method.Once the get method is called, the wait_for_item_done() call from sequence gets unblocked immediately without caring about driving the virtual interface.
-
get_response() The get_response call is necessary to call that completes the communication. The get_response method is blocked until the driver calls put(RSP).
In the meantime, the driver drives the sequence item to the DUT using a virtual interface handle. Once it is completed, the put(RSP) method is called. This unblocks the get_response method from the sequence. The RSP item is communicated to the sequence by a sequencer with help of RSP FIFO.