Pyvsc: Systemverilog-Style Constraints and Coverage in Python
Total Page:16
File Type:pdf, Size:1020Kb
PyVSC: SystemVerilog-Style Constraints, and Coverage in Python M. Ballance Milwaukie, OR, USA [email protected] Abstract—Constrained-randomization and functional Coverage is captured as a set of individual coverpoint coverage are key elements in the widely-used SystemVerilog-based instances, and is not organized into covergroup types and verification flow. The use of Python in functional verification is instances in the same way that SystemVerilog does. growing in popularity, but Python has historically lacked support for the constraint and coverage features provided by B. SystemC SystemVerilog. This paper describes PyVSC, a library that SystemC has been one of the longer-lasting languages used provides these features. for modeling and verifying hardware that is built as an embedded domain-specific language within a general-purpose Keywords—functional verification, Python, constrained programming language (C++). While libraries for constraints random, functional coverage and coverage targeting SystemC are not directly applicable in I. INTRODUCTION Python, it’s worthwhile considering approaches taken. Verification flows based on constrained-random stimulus The SystemC Verification Library (SCV) provides basic generation and functional coverage collection have become randomizations and constraints, using a Binary Decision dominant where SystemVerilog is used for the testbench Diagram (BDD) solver. BDD-based constraint solvers are environment. Consequently, verification engineers have known to have performance and capacity challenges, especially significant experience with the constructs and patterns provided with sizable constraint spaces. No support for capturing by SystemVerilog. functional coverage is provided. There is growing interest in making use of Python in The CRAVE [4] library sought to bring higher performance verification environments – either as a complete verification and capacity to randomization in SystemC using Satifiability environment or as an adjunct to a SystemVerilog verification Modulo Theories (SMT) constraint solvers. While there is some environment. It is desirable in both of these cases to be able to evidence that some support for functional coverage was make use of constrained random stimulus generation and to developed [5], this is not part of the published CRAVE library. capture functional coverage. The PyVSC[1] library brings these features to Python, with the goal of providing as similar a user III. MODELING CONSTRAINED-RANDOM STIMULUS experience as possible to what SystemVerilog-knowledgeable PyVSC is considered an embedded domain-specific verification engineers are familiar with. The PyVSC library also language (eDSL) because it effectively extends the Python takes advantage of advances in solver technology to support language with new semantics for constraint solving and constraint complexity on par with what is in use in coverage capture. There are several key Python language SystemVerilog-based environments. features that are used to layer these new semantics on top of the Python language. Library classes and methods are used to II. RELATED WORK capture some constraints and coverage constructs. Python Other projects have, of course, sought to bring constrained decorators are used to identify Python classes and methods as randomization and/or functional coverage collection to having specific significance. Python introspection is used to languages used for functional verification that lack built-in obtain source locations for declarations, to automatically-assign support. names to elements, and to dynamically modify user-declared classes. Python ‘with’ statements are used to identify statement A. cocotb-coverage blocks. PyVSC uses the Boolector SMT solver[6] to solve the Possibly the most relevant of these packages, given the target user-specified constraints. of Python, is the cocotb-coverage library [2][3]. This library A. Data Fields and Classes supports basic randomization and functional coverage collection. The constraints used by the cocotb-coverage library Both SystemVerilog and PyVSC use data fields with a are captured as executable Python methods and are evaluated specified bit width. This is important to ensure that both using a pure-Python SAT solver. This approach to capturing and constraints and coverage constructs are interpreted correctly. solving constraints restricts each field to having no more than PyVSC supports creating data elements individually, but data two constraints, and can result in poor performance and elements are typically grouped with constraints in randomizable capacity. classes. XXX-X-XXXX-XXXX-X/XX/$XX.00 ©20XX IEEE @vsc.randobj @vsc.randobj class my_s(object): class my_base_s(object): def __init__(self): def __init__(self): self.a = vsc.rand_bit_t(8) self.a = vsc.rand_bit_t(8) self.b = vsc.rand_uint8_t() self.b = vsc.rand_bit_t(8) @vsc.constraint @vsc.constraint def ab_c(self): def ab_c(self): self.a < self.b self.a < self.b Figure 1 - Randomizable Class Declaration @vsc.randobj class my_ext_s(my_base_s): As shown in Figure 1, a randomizable class is identified with the @vsc.randobj decorator. This builds infrastructure into the def __init__(self): super().__init__() class to support randomization, and ensures that constraint elaboration occurs after a class instance is created. @vsc.constraint Class members that can be constrained are created using def ab_c(self): methods provided by the library. The width of data fields can self.a > self.b either be specified directly, or via convenience methods such Figure 3 - Overriding Constraints as vsc.rand_uint8_t that create data fields with commonly- used widths. In the example in Figure 3, the relationship a > b will hold for all instances of class my_ext_s because the ab_c constraint B. Class Constraints in the my_ext_s type overrides the ab_c constraint in the Constraints are specified as Python statements within Python my_base_s type. methods decorated with @vsc.constraint. Constraint methods are evaluated once to construct a constraint data model from the Figure 4 summarizes the constraint statements currently statements within the method. supported by PyVSC and those supported by SystemVerilog. @vsc.constraint def ab_c(self): Feature SystemVerilog PyVSC with vsc.if_then(self.a == 1): Algebraic constraints Y Y self.b == 1 Integer fields Y Y with vsc.else_if(self.a == 2): Enum fields Y Y self.b == 2 with vsc.else_if(self.a == 3): Fixed-size arrays Y Y self.b == 4 Variable-size arrays Y Y with vsc.else_if(self.a == 4): dist constraint Y Y self.b == 8 soft constraint Y Y with vsc.else_if(self.a == 5): self.b == 16 inside constraint Y Y solve ordering Y Y Figure 2 - If/else Constraints unique constraint Y Y foreach constraint Y Y The difference between Python statements and PyVSC constraint_mode Y Y constraints is most visible when control-flow constraints are used, as shown in Figure 2. In these cases, Python statements rand_mode Y Y cannot be used directly. It is necessary to use the PyVSC- if/else constraint Y Y provided construct to capture the constraint intent. Figure 4 - Supported Constraint Statements Just as with SystemVerilog, constraint blocks are considered C. Randomizing a Data Field virtual, in that a same-named constraint in a sub-class overrides PyVSC randomziable classes have randomize and the constraint in the super-class. randomize_with methods that are used for randomizing fields within that class. @vsc.randobj @vsc.covergroup class my_base_s(object): class my_covergroup(object): def __init__(self): def __init__(self): self.a = vsc.rand_bit_t(8) self.with_sample( self.b = vsc.rand_bit_t(8) a=vsc.bit_t(4) ) @vsc.constraint self.cp1 = vsc.coverpoint( def ab_c(self): self.a, bins={ self.a < self.b "a" : vsc.bin(1, 2, 4), "b" : vsc.bin(8, [12,15]) item = my_base_s() }) for i in range(10): Figure 7 - Declaring a covergroup type with item.randomize_with() as it: it.a == i PyVSC covergroups support several mechanisms to provide Figure 5 - Randomizing a Class with Additional Constraints coverage data for sampling. Figure 7 shows using the with_sample method, which creates a method named sample on PyVSC supports randomization with additional constraints, the covergroup class. Coverage data is provided via method as well as randomization considering just the constraints parameters each time the sample function is called. declared within the class. Figure 5 shows an example of adding additional constraints. The randomize_with class method is @covergroup called using a Python with clause. Additional constraints are class my_covergroup(object): specified within the with clause. All legal constraints supported by PyVSC may be used with an inline constraint. def __init__(self, a): super().__init__() @vsc.randobj self.cp1 = coverpoint(a, class my_item(object): bins=dict( a = bin_array([], [1,15]) def __init__(self): )) self.a = vsc.rand_bit_t(8) a = 0; self.b = vsc.rand_bit_t(8) cg = my_covergroup(lambda:a) a=1 @vsc.constraint cg.sample() # Hit the first bin of cp1 def valid_ab_c(self): self.a < self.b Figure 8 - Binding sampling data at instantiation item = my_item() Another approach is shown in Figure 8. In this case, # Always generate valid values sampling data is provided via a lambda function that is specified for i in range(10): when the covergroup is instanced. Calling ‘sample’ on the item.randomize() covergroup causes that lambda to be called and read the current value of the data to be sampled. item.valid_ab_c.constraint_mode(False) B. Specifying Coverpoints and Bins # Allow invalid values