Progress in open source SystemVerilog / UVM support in Verilator


Topics: Open source tools, Open ASICs

Verilator is a shining example of a widely-accepted open source tool which provides state-of-the-art results in the ASIC design space. It is commonly used for simulation and testing, but originally, due to the lack of capability to run event-driven simulations, Verilator wasn’t even considered capable of handling UVM (Universal Verification Methodology) testbenches implemented in SystemVerilog which require scheduling and other features notably absent from the tool. For some time now, Antmicro, together with Western Digital, Google and others in the CHIPS Alliance, has been working on enabling fully open source support for SystemVerilog UVM testbenches in Verilator. This has already resulted in significant breakthroughs we have described in this blog, such as implementing event-driven simulation capabilities in Verilator which has enabled new and exciting use cases. Since then, on the road to proper UVM testbench support, Antmicro has been working on adding various SystemVerilog constructs and UVM-specific elements into Verilator, which bring us much closer to the goal - the current status and next goals will be described in more detail in this blog note.

Verilator and UVM illustration

Tracking progress with a custom test suite

In the usual CHIPS Alliance fashion, in order to track progress in this long-term project and catch potential regressions, a custom test suite and dashboard was created to provide a concise overview of the current status and some statistics for the tests being executed. This is both informative and motivating, as ticking off consecutive corner cases is a tangible measure of progress. Potentially, this also makes it easy to provide new input to the effort in the form of test cases (e.g. SystemVerilog constructs or elements of testbenches that are known not to work) that need to be addressed, without the necessity to touch the internals of Verilator itself.

Thanks to a tight collaboration with Verilator maintainers, as well as a clean and traceable development methodology, many features and improvements outlined in this article, along with the event-driven simulation capability, are now part of Verilator 5.0, which opens the road to eventual mainline UVM support.

Verification features report screenshot

Improving existing Verilator features

A significant part of Antmicro’s efforts involved expanding features that were implemented in Verilator to some extent, but missing specific corner cases or syntactic capabilities. That was the case for concurrent assertions, which are checked with each clock cycle and return true if a certain signal change/state happened. In addition to assertions themselves, we added value sampling. As all sampled value functions are defined in Verilator in terms of the $past function, we focused on this one first, and ultimately ended up adding support for the following keywords:

  • $rose
  • $fell
  • $changed
  • $stable
  • $past

Along with sampling improvements, we also added support for the “not” keyword and named properties in assertions.

Another improvement involved $test$plusargs: Verilator did not support expressions (e.g. arbitrary string concatenation) in the arguments of the $test$plusargs statement - it worked only for string literals for argument names, whereas now it works for arbitrary string-type expressions:

string bar = "bar";

Previously, Verilator had only rudimentary support for classes, however many of the more specific features were not supported. In UVM, class constructors often end with the return keyword, and in those cases Verilator returned an error due to a missing return type. Antmicro provided a fix for that, along with an improvement to the way calls are handled - some of the UVM classes contained constructors with arguments and when a child class called this constructor explicitly using, Verilator reported an error. This improvement is necessary to make it possible to inherit from a base class that has a constructor with arguments.

We’ve also added the possibility to use a foreach loop with an object type of string, as shown in the example below:

function foo;
	string bar;
	foreach (bar[i])

The do while loop was also lacking support for the break and continue keywords, which required changes to how this kind of loop was processed.

Although Verilator supported the unique method, it did not support it with the with keyword. This keyword is used for specifying lambda-like constructs and is now supported in unique, unique_index, min and max in queues. The with keyword is now also supported for queue methods in some contexts, namely when accessing members of complex types and passing queue elements to functions.

Lastly, Verilator had rudimentary support for randomization using the randomize method, there was no support for constrained randomization - constraint declarations were silently ignored. To support them, the CRAVE library was extended and heavily adapted to mainline Verilator as a common frontend for several solvers. This enabled support for several cases:

  • value-based constraints are supported,
  • soft constraints can be added and relaxed,
  • with clauses can be used to define dynamic constraints.

Note that adding full support for constrained randomization is a significant effort and the partial support has not been mainlined yet. If you still want to give this feature a try before it’s fully ready, you can use our fork which includes it.

Adding new features to Verilator

On top of the improvements mentioned above, we have also identified and implemented new features that can benefit Verilator and its users. For designs and testbenches we are working with, there are specific signal types that were not supported by Verilator. For example, we’ve added support for wildcard associative arrays (using wildcard index).

So far, Verilator only supported packed struts which cannot contain various types within them e.g. strings. We have now added support for unpacked structs and for the --structs-packed flag, allowing for their different memory layout as well as various types.

Another feature that we added to Verilator is support for virtual interfaces, which are required by UVM. Virtual interfaces are “pointers” to interfaces, their value represents an instance of the interface. That’s also the only way to access interfaces from within classes in SystemVerilog (an interface instance cannot be defined in class).

UVM makes extensive use of built-in classes provided by the std package. Until recently, Verilator didn’t allow instantiating classes defined outside the current package. The example below shows usage of the “foo” class which is outside of the “bar” package, which would not work before our changes.

class foo;

package bar;
	function foo get_foo;
    	foo baz = new;
    	return baz;

There were several outstanding issues in Verilator where the this keyword used as a reference or function argument was unsupported - they are now fixed. Verilator also lacked support for some of the classes present in the std package, namely std::semaphore and std::mailbox (parametrized variant), which are used in several places in the UVM library. Both classes are now supported.

Parameterized classes are the backbone of UVM. A significant portion of custom objects definitions in UVM is based on parameterized classes. Verilator did not support parameters in classes at all but thanks to the improvements introduced by Antmicro, the following parameterization features have been implemented:

  • class parameters,
  • type parameters in classes,
  • parameterized subclasses,
  • scope resolution when accessing members of parameterized classes.

Signal strengths are used for so-called “conflict resolution”. Very basic signal strength support has been added to Verilator, including the following features:

The future of open source UVM support in Verilator

The improvements and additions described in this note, combined with our work on scaling Verilator even for very large designs, are moving us one step closer to the next significant milestone, running the first SystemVerilog UVM testbench through Verilator. However, there are still some features that need to be added in order for some basic UVM testbenches to pass. These include the built-in process class, srandom class method, static local variables, recursive function/task calls, zero delays, constraint_mode method, more complete randomize(...) function support and more thorough type-parameterized classes support. With upcoming new developments within this area, we will be posting updates in the near future. Also, soon to be released is Michael Gielda’s presentation from the Chips Alliance Fall Technological Update titled “Open source SystemVerilog / UVM support and scaling for large designs in Verilator 5.0 and beyond”.

Other goals for the future include working with open source projects like Caliptra and enabling instruction stream based verification frameworks like RISC-V DV. Reach out to us at to learn more about our efforts related to open source tools for ASIC development and how they can be implemented into your workflow, whether it’s based on UVM or not.

See Also: