by Matthew Ballance, Mentor, a Siemens Business
In our previous article, The Nuts and Bolts of Verification: Recasting SystemVerilog for Portable Stimulus, we described how verification teams can jumpstart portable stimulus test description creation using existing SystemVerilog constructs and principles. Now, switching from analogy to idiom, we’ll dive into the “nuts and bolts” of how to restructure SystemVerilog for reuse.
Specifically, we will share basic practical concepts and coding guidelines that make SystemVerilog classes, constraints, and covergroups more amenable to automated reuse in a Portable Stimulus Specification (PSS) description. Because so much content has already been created in SystemVerilog, leveraging portions of SystemVerilog-based descriptions you already have in-house will accelerate creation of PSS descriptions, delivering immediate productivity benefits.
The guidelines for structuring SystemVerilog for reuse presented in this article reflect a preference for a fully-automated flow to reuse information captured and reflect best practices that evolved from the experience of users of the Questa inFact Portable Test tool from Mentor Graphics.
Focus on the constructs
SystemVerilog is a fully-featured language for register-transfer level (RTL) design and verification. As such, it includes constructs inherited from the Verilog language for describing hardware constructs. It also includes object-oriented programming constructs, and constructs for automated stimulus generation and functional coverage collection, that are used for verification of a hardware description.
The PSS language is predominantly a declarative language, with a few procedural constructs. In contrast, SystemVerilog is predominantly a procedural language with a few declarative constructs.
As a consequence of the declarative nature of PSS, the declarative constructs in a SystemVerilog description are easiest to reuse as part of a PSS description: specifically, data structures and constraints on the fields of those data structures, as well as covergroups, used to model functional coverage goals. In general, design patterns that negatively impact reuse of SystemVerilog descriptions are ones that blend procedural and declarative description, or impact encapsulation of a declarative description.
In most cases, the best practices that enable reuse are also simply good SystemVerilog coding practices that result in well-encapsulated code. Of course, there are cases where it is pragmatic or necessary to use constructs and patterns that limit reuse. Even with these cases, a little foresight can maintain a sensible balance between reusable constructs and pragmatic design patterns.
The goal in structuring SystemVerilog for reuse is to create self-contained elements that can be extracted from the SystemVerilog description and added to the PSS description. We will now take a detailed look at some of these elements individually to see how this can be done.
Inline randomization constraints
SystemVerilog allows inline constraints to be specified when an object is randomized. This can be a handy way to customize random transactions to create directed-random test sequences. Figure 1 shows a directed-random test that leverages an existing transaction class and inline constraints to perform a series of reads and writes.
While the use of this design pattern in a SystemVerilog testbench can be very useful, it does limit reuse. The in-line constraints are introduced in a procedural block and often will include references to variables within that procedural block. Consequently, reusing these inline constraints is difficult if not impossible.
Inline constraints can also be abused, of course. The author has worked with testbench environments where up to a hundred lines of inline constraints were specified simply to ensure valid relationships between class fields. Worse, these large blocks of inline constraints were copied across multiple tests resulting in a maintenance headache when these base constraints needed to be adjusted.
You can increase code maintainability and reuse by factoring common constraints out of inline constraint blocks. Figure 2 shows how the read/write inline constraint is factored out by creating a read transaction and a write transaction class. This increases test readability and maintainability, and provides more specific elements that can be reused in a PSS description.
Dynamic enabling/disabling of constraint blocks
Another interaction between SystemVerilog procedural code and declarative constraints is support for dynamically enabling and disabling constraint blocks. Under ideal circumstances, dynamically disabling constraints is used in very limited cases to do things such as enabling generation of illegal transactions. A highly-problematic use of dynamic constraint-block control is where at least one block must be disabled in order for randomization to complete successfully. Figure 3 shows an operation sequence-item class that specifies two conflicting constraints – one that sets “normal” operating modes, and another that sets “extended” operating modes. The user of this class must know to disable at least one of the normal_mode_c or ext_mode_c constraints prior to randomizing the object.
From a reuse perspective, the use of dynamically-controlled constraints presents two challenges. The current PSS language doesn’t provide facilities for dynamically controlling constraints. So, at a minimum, additional changes will be required in the PSS description to adapt the description imported from SystemVerilog. In addition, the use of conflicting constraints presents a usability challenge in both SystemVerilog and PSS descriptions. Valid combinations of enabled/disabled constraint blocks must be documented somewhere. At best, the valid enabled/disabled constraint blocks will be captured as a comment which will likely not propagate to the PSS description. At worst, the valid combinations of enabled/disabled constraint blocks are “tribal knowledge” captured in working test sequences.
Using class hierarchy and constraint overloading instead of dynamic constraint enable/disable results in SystemVerilog sequence items that are easier to use, and a SystemVerilog description that is easy to reuse as part of a PSS. There are two equivalent approaches to structuring a class hierarchy to handle cases often implemented using dynamic constraint enable/disable. The first approach, additive constraint refactoring, is shown in Figure 4. Using this approach, derived classes add in just the constraints needed to achieve their intended purpose. So, the normal_op_item class adds constraints to ensure that only “normal” modes are produced, while the ext_op_item class adds constraints to ensure that only “extended” modes are produced. This approach is a good general approach.
The second approach, subtractive constraint refactoring, is more often used to create exceptional use cases, such as error injection. Taking the example used thus far, let’s assume that “normal” modes are the ones used the majority of the time.
Figure 5 shows the definition of the normal_op_item class with constraints that enforce ‘normal’ operation. In order to create the ‘exceptional’ condition that could be created by disabling the ‘normal_mode_c’ constraint, the ext_op_item class overrides the normal_mode_c constraint with an empty constraint – effectively disabling this constraint in a declarative way.
Using class hierarchy and constraint refactoring results in more-reusable classes in SystemVerilog environments. It also results in SystemVerilog descriptions that are reusable in a PSS description by moving controls from procedural description into the declarative portion of the description.
References to global data
Referring to global data items outside the class presents a challenge for reuse – both in SystemVerilog and in a PSS description. Figure 6 shows such a case, where the coefficient field in the transform_item class is bounded by a global min and max field in the global_data_pkg package.
This has its convenient aspects, of course, in allowing the coefficient bounds to be set once per testbench environment. The downside is that this makes reuse more challenging. Perhaps the coefficient bounds were global in the original testbench environment, but this may not always be the case. Perhaps in the next testbench environment there will be two UVM agents that use the transform_item class. Each of these agents may need to use different bounds for the coefficient.
Figure 7 shows an approach to removing global data references by creating fields inside the class. The procedural SystemVerilog code that randomizes the refactored transform_item class will need to set the coeff_min and coeff_max prior to randomizing the class. However, the refactored code results in a class that is self-contained and easier to reuse. A self-contained class is also much more reusable within a PSS, since there are no references to external entities.
Reusable functional coverage
SystemVerilog covergroups provide an exceptionally flexible construct for monitoring and aggregating testbench data. However, the exceptional flexibility of the construct impacts its reusability.
Figure 8 shows a SystemVerilog covergroup structured in a way that is ideal for reuse in a PSS description. All the coverpoints and cross-coverpoints reference the config_item class field item that specified as a parameter to the covergroup. This ensures that the covergroup is self-contained. In addition, the class monitored by the covergroup is the same class used for generating stimulus. This allows configurations to be generated that efficiently satisfy the coverage goals without the user providing additional directives to relate the stimulus-generation class to the class used to capture coverage data.
The PSS standard allows a single representation of test stimulus, expected results, and coverage goals to be captured in a description that can be reused across test environments from UVM to software-driven test environments.
Using the best practices for modularity and reuse described above helps enable reuse across SystemVerilog environments and PSS descriptions. To summarize, these best practices are:
- Avoid use of inline constraints.
- Avoid enabling and disabling constraints from procedural code.
- Avoid referencing class-external variables from constraints
- Structure functional coverage descriptions to explicitly reference a single class, ideally a stimulus-generation class
Reusing the wealth of existing SystemVerilog descriptions, such as transactions and device configuration classes, allows users to more-quickly take advantage of the benefits of Portable Stimulus tools such as efficient closure on test goals and automated creation of C tests.
Many users of the Questa inFact portable stimulus tool have already taken advantage of this reuse path by using the tool’s SystemVerilog import capability to import existing configuration classes and jump-start creation of portable stimulus models.
This concludes our four-part series on how to use existing UVM and SystemVerilog descriptions to accelerate the creation of portable stimulus models. If you haven’t already, see the previous EE World articles to learn more about how you can catapult your verification effort into a higher level of productivity and reuse.
Matthew Ballance is a Product Engineer and Portable Stimulus Technologist at Mentor Graphics, working with the Questa inFact Portable Stimulus product. Over the past 20 years in the EDA industry, he has worked in product development, marketing, and management roles in the areas of HW/SW co-verification, transaction-level modeling, and IP encapsulation and reuse. Matthew is a graduate of Oregon State University.