A typical verification environment will have hundreds of tests with random seeds and configurations, which can be made simpler by having a variety of sequences to perform individual tasks. Now, you may create top level sequences that can spawn off smaller ones within its
body() task, or create a randomized way of calling multiple sequences. A cleaner way would be to use the sequence library provided by UVM as
uvm_sequence_library. As the name suggests, it keeps a track of the sequences that are registered with it, and calls them a number of times in a random fashion.
Let's take an example of three sequences, all derived from the same
base_sequence class, as we have seen before. Here, these three are individual sequences without being nested inside each other.
Use of sequence macros give the added advantage of inline constraints, however you lose the ability to control the invocation of
post_body methods in the executed sequence. It reduces the number of lines in our code by creating the item, randomizing it and automatically calling the required tasks to start the given sequence or sequence item.
In a previous article, we learned that all
`uvm_do macros ultimately call the code defined in
`uvm_do_on_pri_with. So, let's see how that is structured within UVM to get better clarity behind the process.
In How to create and use a sequence, we saw that a sequence calls on the tasks
finish_item(). You can avoid putting all these statements in your code by simply calling UVM sequence macros
`uvm_do_with. At compile time, these macros will be substituted with calls to
finish_item(). There are primarily four types of UVM macros that can be executed on the default sequencer.
Sequences are made up of several data items, which may form an interesting scenario. For example, you can have a sequence that performs register read/writes to all the registers within the design, a sequence to perform reset, or another one to apply some stimulus to the DUT. So, you'll end up having a number of different sequences that perform different tasks to verify different aspects of the design. Remember that a sequence item refers to a packet of data, while a sequence is just a container for an arrangement of items/sub-sequences. Now, you have a few options :
- Use existing sequences to drive stimulus to the DUT individually
- Combine existing sequences to create new ones - perform reset sequence followed by register read/writes followed by FSM state change sequence
- Pull random sequences from the sequence library and execute them on the DUT