Verification languages - overview and concepts
Running tests
When running a test you go through the following flow.
- Compile - This part takes all of the source files and compiles them into a single library.
- Elaborate - Resembles software's linking. Here we take the various objects and put them together like lego.
- Run - Once the environment is compiled, you run a specific test on the environment.
Compile and elaborate are usually unified into a single step. It is interesting to understand both steps when debugging failures. Compilation failures will happen quickly and are usually related to issues of syntax. Elaboration failures are usually due to non-compiled components/classes.
Looking at verification
The above is all correct for the design, verification environments on the other hand, are usually dynamic. Not all objects are created or instantiated. Therefore sometimes although the code compiles and runs, the dynamic nature of verification languages causes sometimes failures at the run stage.
Take for example the following code.
flow_numbers = db.flows.all(not .unclassified_flow).id;
The above code will compile and run, but db.flows.all(not .unclassified_flow) might return an empty list. In that case, trying to access id will fail.
Specman
All of the above is mainly true for SystemVerilog. In the case of Specman, we have an extra step that deals with the Specman part of the code.
Specman is loaded (it can also be compiled), meaning it is dynamic and we can play around with it during the test.
During the compilation stage, we load the Specman environment, so that Specman can generate connections to Verilog.
Verification languages
For verification purposes, we use two main languages: Specman and SystemVerilog.
SystemVerilog uses the Object Oriented paradigm (OOP), whereas Specman's strength lays in the fact it uses the Aspect-Oriented paradigm (AOP). Specman can also use OOP, but in general recommends the use of AOP
OOP vs AOP
OOP means that everything is based on objects and inheritance of those objects. If we take as an example a packet. We could define a packet as an object. Let's say it has two fields: header, and payload. When we decide to create a more specific type of packet (e.g. IPv4) using object inheritance, we'll get these fields as part of the inheritance. We can add more fields that are specific to this type of packet.
Accessing an object's function/method will cause the simulator to try and access the most specific version. If it doesn't exist, it will go to the inheritor and use that.
OOP also allows for encapsulation, this means that anything that accepts the parent's type will also accept the child's type.
See the following example: although the foo function accepts something of parent_type, since child_type inherits from parent_type, foo can also work on it.
class parent_type;
function int get_foo();
return 42;
endfunction
endclass : parent_type
class child_type extends parent_type;
endclass : child_type
module bar;
function int foo(parent_type my_object);
return my_object.get_foo();
endfunction : foo
child_type x;
initial begin
x = new;
$display(foo(x));
end
endmodule : bar
AOP, on the other hand, works by changing the aspect of the given class. Instead of creating an inheritance of objects, we modify the behavior of the object by extending it.
This is the reason why most Specman tests are simply a list of extend directives. They modify the behavior of constraints, of methods (using the is only, is first and is also directives) and add functionality.
By contrast, SystemVerilog UVM tests will create an inherited object and use factory overrides to override the created object.
Units vs Structs (Specman) or Components vs Objects (UVM)
In Specman we have unit and struct classes, whereas in UVM they are named component and object respectively. Here I'll use unit and struct
A unit is a type of struct that exists all through the lifetime of the test. It is well-defined hierarchically, and is aware of the various stages of the test.
sys in Specman (and uvm_test in UVM) is the base unit. It is built at the start of the test, and when extending it usually instantiates other units. All of these units are generated at the start of the test. Once built, they have run, extract, check phases and others. Each unit can access its parents and children (not to be confused with parents and children w.r.t inheritance).
Functions vs TCM (time-consuming methods) (Specman) or functions vs tasks (SystemVerilog)
Whereas regular functions, don't consume any time, TCM's (or task in SystemVerilog) are specially crafted functions that allow for time to elapse.
You must ascertain that a regular function doesn't call a TCM. A TCM, on the other hand, can call a regular function.
Events
Both Specman and SystemVerilog have support for events. An event waits for a trigger and can be triggered. In Specman, when the trigger is activated, we can have a on block start running.
on reset_state {
ports.valid$ = 0;
}
Asserts
Assert is a way for us to protect our code from ourselves. We use asserts to check that something behaves correctly. This can be done by checking something to be existing, in the correct boundaries or with a correct value. It is extremely useful when debugging generation.
Constraints
Specman was created from the ground up as a verification language. Because of that, by default, every variable will be randomized. By using the keep directive, we can set constraints on the variable.
If we want someone else to be able to override our keep, we can use keep soft. keep soft, will try to apply this constraint, but any other constraints that collide with it will cause the constraint to be disregarded.
You should be careful when assigning values to randomized variables as constraints might affect one another. For this reason you should try and express yourself with constraints instead.
If a variable is not to be randomized, use the exclamation mark ! in front of the variable to stop it from being randomized.
SystemVerilog, on the other hand, started as a design language and had dynamic properties added on top of it. For this reason, by default variables are not randomized. To get them to be randomized you need to add the rand directive in front of the variable.
Also in SystemVerilog, you have to specify that you want to randomize the class by calling the randomize function.
RegEx
RegEx, aka regular expressions, although not directly used in verification, is a very useful tool for anyone touching any kind of code.
It allows you to search for strings and manipulate them in a rule-type fashion.
There are a bunch of online websites that allow you to play around with regular expressions. They show you how different parts of your text get selected and manipulated.
Take a look at https://regex101.com/.
One thing to note about regular expressions is that they come in a multitude of flavors. RegEx as used by grep is different than the ones used by perl or python.
With regular expressions, you can do things like [A-Z]* (find all uppercase character words) or use . to catch any characters.
One of the places where it is used in verification is in Specman messaging. Using the set_check method, we can specify what we want the behavior to be if a specific message is found.