Unit Testing UVM Sequences

Next to unit testing UVM drivers, which was the topic of Testing UVM Drivers (Without The Sequencer), the second most popular question for which I had no good answer has been “How can I use SVUnit to test my UVM sequences?”.

Thankfully, SVMock seems to have made life easier here as well. With SVMock we can isolate a sequence from sequencer and driver such that it can be unit tested on it’s own before it’s used as part of a larger system. Here’s an example of how it works.

I’m assuming that most of the interesting stuff we do with sequences is found in the sequence body() so the outcomes we’re testing come from code in body. I’m also assuming that the two things we care about in body() are (a) conformance to the sequencer protocol and (b) formation of the sequence items (with heavy emphasis on the latter). The example, therefore, has a test that focuses on sequencer protocol and another to verify the resulting sequence items.

Carrying on with the AMBA APB theme, our example uses a sequence composed of apb_items. The sequence looks like this…

To summarize, we have 7-10 transactions constrained to an address < max_addr while writes have bit 0 of the data fixed to 1. We’re using the `uvm_do_with to send each transaction. That’s a normal UVM macro that goes through all the steps required for create, randomize, start and finish a sequence (i.e. takes care of the protocol).

That’s it for our sequence. It’s simple but we should test it anyway just to make sure it’s solid before we use it. Our first test is going to focus on the protocol. We want a test that confirms start_item() and finish_item() are called for every transaction so the interaction with the driver is solid. While we’re at it, we’ll make sure that the number of transactions is within our bounds of 7 and 10. All that is in the start_and_finish test…

We use EXPECT_CALL.exactly to confirm start_item and finish_item are called the right number of times (i.e. uut.num_transactions as it’s configured in our sequence). We can also use an EXPECT_CALL.at_most and EXPECT_CALL.at_least to confirm start_item is called between 7 and 10 times. The last line of that test is where we start the sequence after which we can confirm our expectations are met. We’re using ‘null’ for the sequencer for the purposes of isolation; to test a sequence you don’t need a real sequencer.

Unsurprisingly, that test passes. The sequence uses `uvm_do_with(…) and should be correct by construction. If it were more complicated though, perhaps invoking start_item and finish_item during decisions made in a series of stateful loops and branches, verifying the sequence operates the protocol properly would be a higher value exercise. Nonetheless, the example shows how it’s done using SVMock.

Now on to the sequence items created. We want to verify the address and data fields are correct for each transaction produced in the sequence. Another simple example confirmed with a simple test…

This time we call uut.start() first, then examine the resulting stream of transactions.

At this point you might be wondering where the EXPECT_CALL comes from and how we can use the get_response() to cycle through the transactions in the sequence. That’s secret sauce that’s hidden in a apb_sequence_mock. We use the mock to override the start_item and finish_item methods to make sure they’re called the right number of times. The mock also overrides the start method so that the transactions that get created are looped back to the response queue in the sequence.

Here’s how the start_item, finish_item and start are overridden using the SVMock macros…

And here’s the functionality we insert in place of the real start method using the SVMOCK_MAP_TASK macro to loop the sequence back to the response queue…

Unit testing a sequence with SVMock seemed like a natural next step after finishing the driver example so I’m hoping these blogs are good news for people that have been waiting to go down this path. The better news, which I wasn’t expecting, is that I’ve been able to extract useful chunks of code into a UVM mock library so people can use pre-made UVM mocks instead of writing their own. So if you’re ready to unit test your UVM sequences, you don’t need to write any of the above code (aside from the tests). Instead, inside your unit test template, you can use the SVMOCK_UVM_SEQUENCE macro on your sequence then go straight to writing tests. That looks like this…

Especially if all you’re after is verifying the sequence items you’re UVM sequence is producing, this SVMOCK_UVM_SEQUENCE will be more than enough. My opinion may change as I use it more, but for now I can’t think of a reason for going further than that.

For reference, you can find all this example code in the SVMock release package under examples/class/uvm_driver/apb_sequence* and src/uvm-mock.

If you’re jumping in mid-conversation without the backstory, you can refer to the SVMock user guide on GitHub for more info on how it works. Reading through the UVM driver example will help, too. And of course, if you’re ready to get started now, you can download SVMock and do just that!


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.