[Edit of Image1]
Hey it's a me again @drifter1!
Today we continue with the Logic Design series on SystemVerilog in order to talk about Constraint Blocks.
Let's note that we will not cover the various constraint types, but only how a constraint can be defined - the syntax, rules, internal / external, static, etc.
So, without further ado, let's get straight into it!
A constraint block is a class member with a unique identifier, where various conditions are defined for the purpose of limiting the range of random variables.
More specifically, the conditions of the solver are specified within curly brackets ({}) and each condition expression ends with a semicolon (;).
constraint <identifier> { expr1; expr2; ... exprN; }
Because they are basically declarations only {} can be used, instead of the typical begin end that we saw in other blocks.
The expressions listed within a single constraint don't have to refer to every variable of the class, nor do they have to be limited to a single variable. However, conflicting constraints are forbidden, and have to be spread out to different constraint blocks. Which of the conflicting constraint should be used, will have to be specified using the constraint_mode() method.
Constraints can also be defined outside of classes and be accessed using the scope resolution operator (::) for the purpose of declaration. Additionally, external constraints can also be declared in an implicit or explicit way.
An explicit external constraint is defined by adding the keyword extern to the constraint name. In this case, if no constraint block is defined outside of the class then an error will occur.
class className;
...
extern constraint c_explicit;
endclass
constraint className::c_explicit { ... };
An implicit external constraint is defined when no extern keyword is added. The remaining syntax is basically the same. The only difference is that no error will occur, but there may be issued a warning by the simulator.
class className;
...
constraint c_implicit;
endclass
constraint className::c_implicit { ... };
Similar to static variables, constraints can also be declared as static by adding the static keyword to the definition. That way, they will be shared across all class instances, allowing every instance of the class to be affected, when they are turned on or off using the constraint_mode() method.
static constraint <identifier> { ... }
By default, all constraints are hard, which means that they are mandatory for the solver and have to be satisfied. If no solution can be found for them the randomization will fail.
To improve the flexibility of constraints and basically add priorities to them, some of the conditions can be declared as soft. That way, when contradicting conditions are specified, the soft ones will be of lower priority than the hard ones.
constraint <identifier> { soft expr; ... }
Suppose a class with well-written constraints. Some additional constraints / conditions may have to be added without affecting it when randomizing some instance of it. For that purpose, SystemVerilog allows for the declaration of in-line constraints. These are added by using the with construct when calling the randomize() method, and are considered along the original constraints.
className instance;
instance.randomize() with { ... };
Of course, providing some conflicting in-line constraint will cause the randomization to fail.
Block diagrams and other visualizations were made using draw.io
And this is actually it for today's post!
Next time we will cover the various types of constraints...
See Ya!

Keep on drifting!