SV Part -5 fork-join and constraints

fork-join
need for fork join :
Test bench components (BFM,Generator,Monitor) can have multiple concurrent running process.
in verilog module, concurrent process can be implemented using multiple always blocks.
3 concurrent process -->use 3 always blocks.
class does not have always block, how to achive concurrent nature?
fork join is used to achive concurrent running behaviour in class.
fork join triggers all the processes defined inside the block at same time.
fork join family
We need the fork-join family because different situations require different synchronization: sometimes you want to wait for all tasks, sometimes just one, and sometimes none. The single fork...join cannot cover all these use cases.
fork join : if all the process is done then only start executing outside statements.
fork join_any : if any one of the process is done then start executing outside statements.
fork join_none : if none of the process is done then start executing outside the statements.
Labelling
- Labeling in SystemVerilog assigns identifiers to code blocks (e.g.,
begin...end,fork...join) to enhance readability, target specific sections for revisions, control execution (e.g., viadisable), and improve debugging by tracing block-specific outputs in logs.
| Q1. simple example how we can disable label |
| Q2 simple example how we can disable lable |
| Q3. disable fork |
| Q4. disable nested fork |
Constrained Random Verification (CRV)
Putting together constraints and randomization is called as CRV.
While verifying a complex design, we can’t use a single test case to do the whole verification.
if test case fails, we are not sure what feature is causing the failure.
By dividing the design feature in to different testcases, when a test fails, we know what feature is not working.
By using constraints, we create a scenario that targets a specific feature.
seed:
input to the tool used as a starting point for randomization.
vsim top -sv_seed 1000
seed helps generate different random patterns for the same testcase by using different seed values.
importance of seed in projects :
A complex design has multiple test cases.
When a test case fails.
possible reasons for failure:
some test bench or test case issue.
Verification engineer will review and fix it.
Should not email or contact RTL team for this.
some bug in the design
Design engineer will review and fix it.
Design team releases a new version of RTL code.
Verification engineer should take same TB where testcase failed, only change the RTL code.
Return the same test case with same seed.
if test passes, then RTL fix is working.
if test fails, then RTL fix is not working.
Directed Verification
User manually decides the scenario to apply, this limits the scope of verification since every time same scenario is targeted when test is run(even with different Seeds).
Directed verification
- addr = 110; // Fixed value => this indicates directed
Constrained random verification
- addr inside {[110:150]};
In directed verification, since user doesn’t generate different scenarios, possibility of finding bugs is less.
Whereas with constrained random verification, different scenarios gets created for the same test case(using different seed), hence it improves the chances of finding bugs in the design.
when we use directed verification:
initial stages of project use constrained random verification.
Final stages of verification we do directed verification
targeted for closing functional and code coverage
till we get 95% functional and code coverage, use CRV
to close the remaining 5% coverage, use directed scenario.
Constraints importance
In any project, verification engineer spends lot of time in developing testcases
Test cases are developed as per the test plan
Adding new testcase for functional and code coverage closure.
New Test cases coding is about writing inline constraints to create a different scenario.
if transaction or packet class is coded with right set of constraints, overall testcases coding effort reduces significantly.
Test case development majorly involves using constraints to generate stimulus specific to the test.
constraints
constraints are limitations of signals
Before constraints: In Verilog, if you wanted random values, you had to write your own code using
$randomand then manually check ranges, relationships, and conditions. This was tedious and error‑prone because you had to enforce rules yourself.Problem: No built‑in way to express rules like “address must be aligned,” “packet length must be > 0,” or “two fields must be consistent.” You ended up writing a lot of procedural code to filter or regenerate values until they matched your requirements.
Constraints (SystemVerilog): Introduced to overcome this. You can directly declare rules inside a class (
constraintblocks), and the solver automatically generates random values that satisfy them.
Why we need constraints:
They replace manual $random filtering with a powerful, declarative way to specify rules. This makes testbench stimulus generation faster, cleaner, and less error‑prone.
constraints can be applicable for two major things.
it tells about that valid input will be applied to the design or not.
in every test, it will focus on the specific feature of the design.
Types of constraints :
we have 2 types of constraints
in-line constraints
- in-class constraints
in-line constraints
the constraints which is mentioned in the same line of the randomization.
we will use “with” clause for in-line constraints.
syntax: object.randomize() with {constraint};
in-class constraints
the constraints which are mentioned inside the class are known as in-class constraints.
simple constraints
distributive constraints
if-else constraints
implicit constraint
iterative constraints
unique constraints
soft constraints
variable order constraints
simple constraints
the constraint which is mentioned with simple mathematical expression.ta
| 1. simple example for simple constraint |
| 2.generate the a,b,c random values which are increasing order ? values in range (150 to 250) |
distributive constraints
- it will generate the random values basing on the weightages
in distributive constraint we need to consider “dist“
providing weightages for values
- syntax: <variable> dist {<value>:=<weightage1>,<value2>:=weitage2};
providing weightages for ranges
syntax: <variable> dist {<range1>:/<weightage1>,<range2>:/weitage2};
if-else constraints
- basing on the conditions, it will generate the random values
in constraint we can’t able to consider the begin and end, so representing the multiple lines in the blocks we can be use “{}”
| Q. write a constraint for two variables a and b, a is +ve then bis also +ve, a is -ve then b also -ve. |
| Q. another example for if-else constraint |
implicit constraints
it is same as if-else constraints, but here we need to consider implication operator
implication operator can be represented as ()
syntax: constraint constraint_name{
(condition1) → (logic1);
(condition2) → (logic2);
(condition3) → (logic3);
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
}
Q. consider two variables a and b, if a is +ve then b is also +ve, if a is -ve then b is also -ve. |
iterative constraints
where we have multiple variables or multibit variables we need to consider this iterative constraints.
for accessing the arrays, queues,vectors, assosiative arrays we need to consider this means we need to consider loops.
for loop, while loop, forever loop, repeat loop, foreach loop.
in constraints only foreach loop is applicable, Remaining loops are not applicable because, in the foreach loop, we do not need to increment the iterative variable.
syntax: constraint constraint_name{
foreach(arr[i]){
// iterative logic;
}
}
Same thing for Dynamic arrays |
Note: 4 ways of size allocation
function new()
pre_randomize()
inside the constraint.(arry.size()==len)
inside the module obj.arr_name = new[size];
soft constraints
if any constraint conflict will be happened, randomization will be failed.
to avoid that constraint conflict we need to mention one of the constraint as soft.
constraint c{
a<50;
a>100; // randomization will be failed due to constraint conflict.
}
priorities
| in-line | In-class | priority |
| normal | normal | conflict |
| normal | soft | In-line |
| soft | normal | in-class |
| soft | soft | in-line |
Unique constraints
it is used to generate the unique values b/w two or more variables
syntax: constraint constraint_name{
unique {variable1,variable2,variable3………};
}
| Q1. simple example for understanding the unique constraint |
| Q2. consider an array of size 10 and assign unique values in the range 10 to 30 by using constraints |
variable order constraints
randomization function will be happened at ones. so any variable dependency are presented, if the solvation will not happened properly than it will go to unknown states. to avoid those things we need to consider this variable order constraint. we need to use keywords “solve“ and “before“
syntax: constraint constraint_name{
(condition) - > (logic)
solve <condition> before <logic>;
}
simple example for understanding the variable order constraint |



