It was recently brought to my attention that I haven’t done a very good job of telling people about the examples that come with SVUnit. That’s unfortunate. The examples are there to help so if people don’t know they’re there or what they do, I think I’m safe in saying they’re not helping anyone!
Let’s change that.
I have a couple examples in the SVUnit release package that are there to help verification engineers using UVM. I think both provide a pretty decent starting point. Some of what I’ll say here is a rehash of what I’ve written in past posts but I think going over it again is worth it for people unsure of how to get started with SVUnit. We’ll tackle the first example, called simple_model, in this post. I’ll follow up shortly with the uvm_express example.
NOTE: If you don’t have SVUnit yet but you want to follow along and run the example, you can go to our SVUnit page for instructions on how to download SVUnit. And if you’re really new to SVUnit, it’s probably worth watching the videos in the SVUnit Demo Series for the basics, then coming back.
What’s In the Simple Model Example?
You’ll find simple_model in the svunit-code/examples/uvm/simple_model
directory.
This is a simple example that demonstrates how to use SVUnit to unit test a UVM component. Here’s a comment from the component under test that sums up what we’re looking at…
Pretty straightforward description of a simple reference model: retrieve an input transaction, perform some simple data transformation and send the result to an output.
Taking a look at what’s in the simple model directory, you’ll see…
The component under test is in simple_model.sv while the unit test class is in simple_model_unit_test.sv. When I built the example, I first created simple_model.sv with only the class/endclass lines like so…
I then used the simple_model.sv to generate unit test class by doing this…
From there, I started adding unit tests in simple_model_unit_test.sv and writing the corresponding implementation in simple_model.sv. You’ll see I also have a simple_xaction.sv. In there is the transaction type that the simple model operates on.
The first thing you’ll do when verifying UVM components is include the uvm mocks. In there are important control features you’ll need to be able to isolate and iteratively run UVM components.
If you follow through the simple_model_unit_test.sv, you’ll see in the constructor of the unit test class is where I instantiate my component under test and make some connections I use later on to apply/sample input/output (look for the put_port and get_port).
The next thing to do if you’re verifying UVM components is to fill in the setup and teardown methods because that’s where components are activated for testing and the run-phases are initiated.
In the setup, there’s a component activation that happens first followed by a test start. These are two things you need to do with UVM that you don’t need to do with simple SystemVerilog classes because of the tight integration between UVM components and the UVM phasing. If you want to know more about that, you can read about it here. Otherwise, trust me for now that the svunit_activate_uvm_component() and svunit_uvm_test_start() are required in the setup method :).
Similarly, in the teardown there are corresponding calls to svunit_uvm_test_finish and svunit_deactivate_uvm_component which are there to make your life easier just like the activation and start in the setup. You’ll also notice that I flush the output fifo I connect to my model. This is an example of something you might need to do to clear any state in your mini-test harness. Remember that tests are run iteratively within the same executable so there may be some general clean-up required to avoid having stale data from one test polluting the next test.
Ok… we have a component under test with a means for driving and sampling transactions, we have setup and teardown methods, now we can get to writing tests.
The first tests I write generally target the existence and functionality of the component interfaces so these are the first tests in the simple_model_unit_test.sv. The first test simply confirms the existence of the input port by confirming it’s not null. This is obviously a baby step but it’s a baby step I’d recommend considering I’ve lost count of the number of times I’ve failed to properly instantiate component interfaces!
Next, I want to make sure that the transactions I send into my simple model are actually consumed. So I build a test that does exactly that: drive a transaction to the input fifo then immediately verify the simple model pulls it in. Again, you could look at this as a baby step. But even a simple test like this will verify some basic component connectivity on the input as well as proper use of the run phases (i.e. the thread that consumes transactions is properly started via the run_phase or main_phase). These are other details I regularly overlook.
With the input logic functioning properly, I’ve chosen to test output functionality next with a test that sends a transaction in and expects a transaction out. You’ll see that the content isn’t important at this point because I don’t care what’s in the output transaction. We’re only concerned with connectivity at this point – or should I say broken connectivity – so that’s what we’re testing here: connectivity from input to output.
Last but not least, we want to check the transformation algorithm of our simple model. Algorithm is a bit of an overstatement here since all our model does is multiply ‘field’, which is a property of our simple_xaction, and send the transaction to the output.
Assuming you’ll be building algorithms more complicated that that, you’ll have far more tests here that would verify all the different features of your model.
Running the Simple Model Example
If you haven’t run SVUnit at all yet and you want to run the simple model example or use it to start your own unit test suite, this is where I’d strongly suggest pausing to watch the SVUnit Demo Series videos. If you’ve run SVUnit already, read on.
The README has instructions for running the example.
It’s currently setup to run with either VCS or Questa though there’s nothing in here to keep you from running it with a different simulator.
Depending on whether or not you’re using your own version of UVM or you’re using the default library of the simulator’s choosing, simple model gives an example of doing either.
If you use Questa, the example is setup to use a user specified version of UVM with the pre-compiled DPI functions. If that’s what you want, then svunit-questa.f will be helpful:
If you use VCS, the example is setup to use the simulator’s version of UVM. You can see how that looks in svunit-vcs.f:
I only have the 2 files in this example so it can run with either simulator. Assuming you’ll be targeting only 1 simulator, you’ll want to add your commands to svunit.f. Note that this is a normal simulator ‘-f <file>’. It includes file pointers and defines used in the simulation and it’s automatically detected by the SVUnit framework. You can put whatever you like in that file, just as you normally could with any ‘-f <file>’.
The last thing you’ll want to look at is the svunit.mk file. This is where you’ll see how to load configuration to run a particular simulator.
Simple Model Lessons Learned
The simple model example shows you how to interact with UVM components using transaction interfaces, which in the case of the simple_model are blocking put/get ports with TLM FIFOs. You can follow that same approach when interacting with your own component under test.
Isolation is critical when iteratively testing UVM components and handling the isolation is done in the setup and teardown methods. In the simple model example, it’s easy to pick out the component activation/deactivation as well as the test start/finish. You’ll want to copy and paste the setup and teardown methods when starting with your own components (the activation and test control functions come from the svunit_uvm_mock_pkg just in case you’re wondering).
I think test sequence is important which is why the last thing I’ll recommend here is to start writing tests by verifying connectivity and communications first, then carry on to the functionality. Simple tests that verify the construction of and connectivity between interfaces are easy to write and can save debug time down the road.
If you’re interested in SVUnit and you want to use it to verify UVM components, then simple_model should be your first point of reference. It may be simple, but it’s got everything you need to get started.
That’s all for now. I’ll follow up with a description of the UVM Express example as soon as I can!
-neil
I recently developed a non-trivial uvm_monitor (including an interface and the uvm_sequence_item that the monitor produces instances of), and using svunit in the development was incredibly helpful. Writing little unit tests to generate stimulus for the monitor and to check that the monitor produced the expected uvm_sequence_items really helped me think through the monitor design. Being able to compile and run the suite of unit tests against my monitor code in about a second was just unreal. Getting feedback on code changes in that tiny amount of time is a *huge* productivity boost. Yes, at first it takes time to develop the unit tests and it was tempting to just use my existing tests and DUT to test out my monitor instead, but I’m so glad I stuck with the unit tests.
bryan, nice to hear comments like this from people who are getting good use out of the framework. thanks for sharing your experience!
-neil
I was looking at verifying my scoreboard component in a standalone environment and came across this. I am planning to try this out.
Thanks a lot for this initiative.