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.
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.
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.
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 this application note that shows the UVM implementation in Aldec environment.
Also, for clarity of basics, consider taking a look at this UVM webinar.