I can honestly say that as of a couple weeks ago, I’ve gone further with SVUnit than I thought was realistic when I first starting looking at it. Having done more TDD, written more tests and recently finished step 2 of my UVM Express example, I’ve come to the conclusion there’s value in unit testing anything! That includes coverage groups and UVM agents… which I talk about here.
At UVM Express step 2, what I’m doing is building a layer of functionality atop the interface BFM that I’ve already built and talked about here. In short, step 1 involved building and testing the APB interface while the goal of step 2 is to build and test a monitor, coverage group and an agent that puts it all together. That means 3 new classes and corresponding unit tests.
I started step 2 by building the monitor. For the monitor, the BFM tasks I created in step 1 are used to passively capture APB bus transactions. I translate the captured bus transactions into apb_xaction objects and write them to an analysis port (the apb_xaction is a new class I derived from the uvm_seq_item). To build the monitor, I ended up writing 10 tests for verifying construction and connectivity, read transactions and write transactions. Having already unit tested the BFM, I could rely on the fact the BFM tasks were working properly. The new tests would focus on the how the pin-level fields were translated to objects and written to the analysis port.
Next was the coverage class. I derived the coverage class from a uvm_subscriber; inside it I declared a covergroup meant to capture a reasonable range of values for address, data and transaction kind (WRITE or READ). Doing TDD of the coverage class is the point where I exceeded what I thought was reasonable with SVUnit. I’ve never unit tested coverage groups – or even thought about unit testing coverage groups for that matter – so successfully building a coverage class using TDD was me surprising myself.
To build the covergroup tests, I alternated between sampling the covergroup and invoking the built-in query functions. Here’s an example:
In that snippet, you can see I’m verifying the sample method sets the sampled object properly as well as verifying 3 coverpoints for:
- the minimum address setting of 0x0
- the minimum data setting of 0x0
- and the kind setting of WRITE
Querying the coverpoint, then sampling, then querying again verifies the coverage number goes from 0 to 100 – in the case of addr_min_cp and data_min_cp – or from 0 to 50 – in the case of kind_cp – as I expect.
I continue this query/sample/query sequence through the rest of the address settings that I’m interested in. Here’s another test that shows how I test the coverpoint with bins between min and max. You can see that it isn’t a simple 0% to 100% transition for the addr_bins_cp coverpoint because there are 16 bins. For the addr_bins_cp I need to calculate the expected percentages that creep up by 1/16 for every bin sampled.
Final test for the address is similar to the first but covers the maximum address.
That wraps up the address related coverpoints. I have tests that similarly go through the data and kind fields. In all I have 6 tests for the coverage class.
That brings us to the last item in the list: the coverage agent. Agents were another type of class where I had initially dismissed the value of creating a unit test. This example changed my mind. The agent testing isn’t complicated but it is valuable in that it verifies connectivity between its internals. Thinking back to the number of connectivity issues I’ve chased down in my day, I reckon unit testing agents and similar container-like components is a low risk-high reward activity.
Here’s a test that verifies the contents of the agent are built…
Here’s the test that that verifies end-to-end connectivity by writing a bus transaction and then verifying the expected coverage results…
With only those 2 tests, I’m able to verify connectivity and functionality of the entire coverage agent.
To summarize, using SVUnit to do TDD of the monitor, coverage class and coverage agent involved me creating a total of 18 tests. I did everything in one evening (roughly 4 hours). With the additions I explain in this post, the UVM Express example I started last week is complete up to step 2. Considering the time I spent on steps 1 and 2 so far, my opinion is that by doing TDD and taking the time to build unit tests, I’ve easily saved myself a few hours of debug time had I instead just written the code and dumped it on to somebody else.
It appears as though all the good things I’ve heard about TDD are coming true. And I have further reinforcement that UVM Express is a decent approach to building verification IP.
This entire UVM Express example is available to SVUnit early adopters. If you’re interested in becoming a SVUnit early adopter, let me know at firstname.lastname@example.org!
Stay tuned for step 3 and the addition of random stimulus!