Understanding the inner workings of UVM - Part 2

UVM Basics Part 2 of 3

Vatsal Choksi, Applications Engineer
Like(2)  Comments  (0)

In this blog, my major focus is on explaining the concepts such as Sequence, Sequencer, Driver and showing how the communication takes place from sequence to sequencer and from sequencer to driver. In the previous blog, I included a top-level diagram of the UVM structure, showing different base classes. If you need refresh your memory on where the classes Sequence, Sequencer and Drivers stand please click https://www.aldec.com/en/company/blog/149--understanding-the-inner-workings-of-uvm.

 

So, let’s look at the main concepts and follow the communication mechanism they use for the effective execution of a test.

 

What is Sequence? It is used to generate a number of Sequence_items; randomize the data fields used in Sequence_items and send it to the Sequencer on request. In order to maintain synchronization with the test-vectors, you can create more than one Sequence, each sequence having been assigned to a particular data-field or task. Sequence consists of REQ and RSP handles to the sequence_item. Basically, REQ is used to send a request to the Sequencer to send sequence_item to the driver. RSP is used as a response from the driver when it completes a particular operation. After writing a Sequence, it can be executed by calling start() in the test.

 

Fig 1 : N number of Sequences

Sequence_name.start(sequencer_name);

Fig 2 : Top Level Structure of Sequence and Sequencer

 

Now there is an important property of Sequence to just touch on again, namely body().

 

When you call the start() function, some pre-defined callbacks get executed in order. Body() contains the majority of the part of those callback methods and hence it is an important property.

 

Here is an example of how to write a Sequence class.

 

Class bus_trans_sequence extends uvm_sequence#(bus_trans);

 

bus_trans req;

 

function new(bus_trans_sequence);

super.new(name);

endfunction

 

virtual task body();

 

req = bus_trans::type_id::create(“req”);           -- creating req handle generating seq_item.
wait_for_grant();                                             -- wait for grant signal from the driver to the sequencer.
assert(req.randomize());                                  -- Randomizing the data-fields in the seq_item.
send_request(req);                                          -- Sending the req handle to the sequencer.
wait_for_item_done();                                     -- Wait till item_done() response received from driver.

 

endtask()

 

endclass

 

Now, here are few things to observe about the above.

The task in the above example shows how a sequence communicates with the driver via sequencer. Take a look at the diagram below.

Fig 3 : Communication basics between Sequence, Sequencer and Driver.

 

As shown in figure 3, Sequence, first generates the sequence_item. Next, it lets Sequencer know that it is ready to send sequence_item given permission. Sequencer at the other end works as an arbitrator. It holds Sequence’s request till it receives seq_item request from driver. As soon as it gets the data request from the driver, it unblocks the Sequence by returning wait_for_grant() (a blocking statement by the way!) address to the seq_item of Sequence. Next, data-fields inside seq_item are randomized. Depending on the needs of the user, constraints can be provided as part of the UVM default macros.

 

In essence, figure 3 illustrates the communication standards set in UVM between Sequence, Sequencer and Driver step-by-step. One important thing to note here is that communications between Sequencer and Driver are TLM-based. Driver has TLM ports defined for communications with Sequencer. Driver also has pre-defined methods.

 

get_next_item(), item_done() and put() are the methods as part of Driver. get_next_item() is basically shown as arrow number 2 in figure 3. It works as a blocking statement as long as there is not a seq_item request available in the Sequencer. item_done() at the same time is a non-blocking method which confirms the completion of the Driver-Sequence communication. put() is not a mandatory method, though it is used to place the response from the driver in the Sequencer.

 

I hope the information provided in this blog made sense to you guys. As In summary, we looked at the UVM communication protocols set between Sequence, Sequencer and Driver, and how the sequence works.

 

I look forward to writing further blogs on UVM, and other subjects. Also, if you have any questions or recommendations for other blog subjects, please contact us through the Aldec website.

 

Until then, check out the following application note that shows the UVM implementation in Aldec environment.

https://www.aldec.com/en/support/resources/documentation/articles/1919

 

Also, for clarity of basics, consider taking a look at the following UVM webinar.

https://www.aldec.com/en/company/blog/106--dont-be-afraid-of-uvm-webinar-on-youtube

Vatsal provides technical support for Aldec’s software products such as Active-HDL and Riviera-PRO. He is proficient in FPGA/ASIC digital design and verification.As a technical support engineer, Vatsal has deep understanding of verification languages such VHDL, Verilog/SystemVerilog, SystemC and methodologies such as OSVVM, UVVM and UVM. Vatsal received his master’s degree in the field of Electrical and Electronics Engineering from California State University Sacramento in year 2015. His desire and passion is to know and learn more about design and verification that makes him suitable at solving complex verification issues.

Comments

Ask Us a Question
x
Ask Us a Question
x
Captcha ImageReload Captcha
Incorrect data entered.
Thank you! Your question has been submitted. Please allow 1-3 business days for someone to respond to your question.
Internal error occurred. Your question was not submitted. Please contact us using Feedback form.