Welcome back to the fourth installment of a special multi-part edition of the App Note Spotlight, where we’ll continue highlighting an interesting app note that you may have overlooked—Simulation Performance Coding Guidelines for SystemVerilog. This app note overviews all sorts of coding guidelines and helpful tips to help optimize your SystemVerilog code’s performance. These strategies aren’t specific to just the Xcelium Parallel Simulator—in fact, they’ll help you no matter what simulator you’re using.
Today, we’ll be highlighting dynamic objects.
1) Don’t do so much in object constructors.
SystemVerilog, similar to other object-oriented programming languages, allows you to use constructors to initialize dynamic objects when they’re instantiated. Using this is a lot more convenient than manually setting up your objects each time. Technically, you can put whatever you want inside a constructor—however, this is not advised. Any code put into a constructor will be run for all objects of that type, so only put code in the constructor that is required for any object of that type’s initialization.
2) Make copying the consumer’s responsibility
Making deep copies is a large-overhead operation for heap management, garbage collection, and data initialization. Generally, objects are passed around by reference in SystemVerilog. It’s typical for the producer to make a deep copy of an object before passing it along, and it’s also typical for consumers to do another deep copy if they need to keep a safe one lying around. That means there’s a lot of expensive operations happening that don’t lead to anything particularly important. Thus, you should leave the copying of objects to the consumers if they require the full object – consumers should minimize the data they copy to that which they really need. Then, the producer can reuse objects when applicable, and consumers, which usually don’t need all of an object, just take what they need.
3) Make fewer objects through object pools or singleton objects.
If you use a dynamic object a lot, the overhead from regenerating it constantly can add up. Sometimes, you can use a singleton object or an object pool instead of a uniquely created dynamic object, and you can shrink your overhead this way. Keep an eye out for opportunities to do this.
4) Use structs for basic data used in classes
Since classes are heap objects, they’ve got a lot of overhead compared with simple structures. If you’re passing data elements around and operating on them a lot, consider using structs instead, as they don’t require garbage collection. Inside of classes it is often convenient to used structs (packed or unpacked) for metadata used by the class.
5) Work in interfaces instead of class tasks
Interfaces are common in verification. They allow class objects to attach to a structural part of the design. Often times, verification components which directly interact with signals, like drivers and monitors, contain state machines and similar code that applies to interface signals. Having state machines in your class tasks has three main drawbacks: it reduces code reuse, since all class objects that do similar work on the same interface types have to replicate the work; it’s a little slower because classes are dynamic objects whereas interfaces are static objects; and classes aren’t synthesizable, so you can’t put them in hardware.
That’s all we have for today—check back soon for the next installment!