Soft constraints and inheritance
Keep protocol rules hard and defaults soft, predict which soft constraint wins, and extend a transaction class without deleting its rules.
- Lesson
- 5.4
- Time
- 25 min
- Runs on
- Verilator
- Check
- 5 questions
A burst class wrote its alignment rule as soft addr[1:0] == 0;, so that error tests could override it. A normal test then asked for addr == 8'h41 by mistake, and the solver quietly dropped the soft rule and produced a misaligned burst. The DUT answered with an error response, and the team spent two days debugging a DUT that was right.
One column per burst. Nothing in the testbench complained; only the DUT did.
Requirements and preferences
A hard constraint must hold in every successful call. A soft constraint, written with the keyword soft, is a default: the solver keeps it when it can and drops it when it conflicts with a hard constraint.
A soft constraint can never cause a failure. If every soft constraint is dropped and the hard rules still have no solution, the call fails because of the hard rules alone.
That makes soft constraints right for typical traffic, such as a usual burst length, and wrong for anything the protocol requires. The difference shows up only when a request conflicts with the rule.
Soft rule meets a conflicting request
constraint defaults { soft beats == 4; }
...
t.randomize() with { beats == 2; }
Succeeds with 2 beats: the soft default is dropped without a message.
Hard rule meets a conflicting request
constraint legal { beats inside {[1:8]}; }
...
t.randomize() with { beats == 12; }
Fails and returns 0: a hard rule is never dropped.
Both behaviours are useful, as long as each rule is in the right group. A soft length lets any test ask for another length without editing the class. A hard alignment rule turns a mistaken request, like the one in the opening story, into a failed call that the test reports at once.
Soft constraints also suit bring-up. soft addr < 8'h40; keeps early tests inside the first 64 bytes, and later tests widen the range with an inline constraint instead of editing the class.
Error injection is the usual reason people make a protocol rule soft. Lesson 5.5 shows the better tool: switch the hard rule off with constraint_mode(0) for the one object that needs illegal traffic.
Which soft constraint wins
Two soft constraints can disagree with each other. Then the one with the higher priority is kept and the other is dropped.
| The two soft constraints | Which one wins | Measured beats |
|---|---|---|
base class soft beats == 4, derived class soft beats == 2 | the derived class | 2 |
class soft beats == 4, inline with { soft beats == 6; } | the inline one | 6 |
soft beats == 3; soft beats == 5; in one block | the later one | 5 |
Soft-constraint priority, each case run on Verilator 5.052.
Priority matters only in that case. When soft constraints agree, and no hard rule conflicts, the solver keeps all of them. A soft constraint dropped in one call is back for the next, because the decision is made afresh every time.
The rule of thumb is that the more specific or later constraint wins. A test can therefore set its own default with an inline soft constraint and still let a later, more specific request override it.
To drop the defaults for one call, write disable soft beats; inside the with block. It removes every soft constraint on beats for that call and leaves the hard rules in place.
Inheritance adds rules, and a same-named block replaces one
A derived class inherits every constraint block of its base and can add more. A block with a new name adds rules, and a block with the same name as a base block replaces that block completely.
| Derived class declares | Effect on the base block legal | Effect on the base block defaults |
|---|---|---|
constraint defaults { soft beats == 2; } | kept | replaced: new default 2 |
constraint short_only { beats <= 4; } | kept | kept |
constraint legal { beats inside {[1:16]}; } | replaced: alignment and the 8-beat limit are gone | kept |
The name of a block decides whether a derived class adds to it or replaces it.
That makes block names part of what a class promises to its users. Replacing defaults to change a preference is the intended use. Replacing legal by accident, as wide_burst does in the example, deletes the protocol rules, and nothing reports it.
Keep the protocol rules in one block with a distinctive name, and treat that name as reserved. An error-injection class should turn the rules off on purpose, as lesson 5.5 shows, never by a name collision.
Derived classes are how a test library offers variants such as short_burst, long_burst and error_burst. Each one changes defaults or adds rules, a factory (Module 4) chooses which one to build, and no test has to edit the base class.
All the layers combine in one solve: the base rules, the derived class's blocks and any with block from the test. Before you extend a class, read its block names; a one-line comment on what each block holds is the most useful note a transaction class can carry.
Try it
The base class gives 4 by default, accepts an inline request for 2 by dropping its soft default, and rejects 12 because the hard range wins. short_burst changes only the default.
wide_burst replaced the block named legal, so a 12-beat burst at a misaligned address now succeeds. Rename its block to wide_range and predict the last line.
base default: ok=1 beats=4 legal override: ok=1 beats=2 %Warning-UNSATCONSTR: main.sv:4: Unsatisfied constraint: 'constraint legal { addr[1:0] == 0; beats inside {[1:8]}; }' %Warning-UNSATCONSTR: main.sv:26: Unsatisfied constraint: 'ok = b.randomize() with { beats == 12; };' hard conflict: ok=0 beats=2 short default: ok=1 beats=2 wide, 12 beats at 41: ok=1Check yourself
Remember
- Hard constraints must hold; soft constraints are defaults that give way to any conflicting hard constraint.
- Put protocol rules in hard constraints and typical traffic in soft ones.
- Between soft constraints, a derived, inline or later one wins.
- A derived block with a new name adds rules; one with the same name replaces the base block.
- Treat the name of the protocol-rules block as reserved.
Practice
- Override a soft default Implement · Easy