Beyond RTL Lint: 90+ Rules for SystemVerilog Assertions, Constraints, and Coverage
This post highlights the rules that catch real bugs, silent misbehaviour, and tool-specific pitfalls — not naming conventions. Full rationale, violation examples, and correct usage are in the linked docs.
SVALint — Assertion Linting (52 rules)
SVA is deceptively easy to write incorrectly. These rules catch issues that compile cleanly, pass elaboration, and then silently misbehave in simulation or formal verification.
| Rule ID | Bug it catches |
|---|---|
| DELAY_BEFORE_ROSE DELAY_BEFORE_FELL DELAY_BEFORE_CHANGED |
$rose(sig) samples two consecutive clocks. Without a preceding ##1, the assertion may fire on the very first active clock before sig has a defined previous value — causing spurious failures at time zero or after reset. |
| FUNC_AVOID_WEAK_EVENTUALLY | The weak eventually operator passes vacuously if simulation ends before the condition is observed. Use s_eventually (strong form) to require the condition to actually be witnessed. |
| FUNC_MISSING_FAIL_ABLK | Without an else fail action block, a failing assertion produces only a generic simulator error with no design-context information — no signal values, no transaction state. |
| FUNC_AVOID_$_RANGE_IN_CONSEQ_A | An assertion with an unbounded $ range in its consequent can never fail — it will always find a future cycle where the condition holds. This makes the assertion useless for verification. |
| STYLE_NO_CLK_WITHOUT_EDGE | @(clk) without an edge qualifier samples both rising and falling edges, causing the assertion to fire twice per cycle with confusing results. |
CNSTLint — Constraint Block Linting (20 rules)
Constraint bugs are particularly insidious because randomize() returns 1 even when the result is wrong — the solver satisfied some solution, just not the one you intended.
| Rule ID | Bug it catches |
|---|---|
| CNST_DIST_ON_ENUM_TYPE_VLT | Verilator does not reliably honour dist{} weights on enum-typed rand fields. Confirmed across 2000 randomization trials: declared weights 20/50/30 produced observed frequencies of 25/28/47. |
| CNST_NO_ENUM_IN_WITH_CLAUSE_VLT | Comparing an enum literal inside a with (...) clause crashes Verilator with an internal compiler error. No graceful diagnostic — the build simply fails. |
| FUNC_CNST_WRONG_OPER_PRE | SV operator precedence places == above ?:. Without explicit parentheses, lhs == expr ? a : b is parsed as (lhs == expr) ? a : b — a conditional whose condition is the equality check — not the likely intent. The solver satisfies the constraint in an unexpected way with no source-level indication. |
| CNST_ADDITIVE_OVERFLOW_IN_BOUND_VLT | A + B <= MAX can silently pass when the sum wraps — SV constraint addition is performed in the operand's native width without automatic promotion. Applies to any simulator. Rearrange to A <= MAX - B to eliminate the addition. |
FCOVLint — Functional Coverage Linting (20 rules)
Coverage holes and inflated coverage numbers are equally damaging. These rules catch patterns that cause covergroups to sample incorrectly or report misleading results.
| Rule ID | Bug it catches |
|---|---|
| AF_FCOV_PERF_NO_IMPL_SAMP | @(event) in the covergroup header couples sampling to the definition and removes precise control. Explicit cg.sample() calls let you decide exactly when a meaningful sample is recorded. |
| AF_FCOV_FUNC_PER_INSTANCE | Without option.per_instance = 1, all instances share one coverage bucket. One busy instance can fill all bins while other instances are never exercised. |
| AF_FCOV_TRANS_MULTI_VAL_VLT | Verilator supports only single-value transition bins. A comma-separated list on either side of => (e.g., (1,2 => 3,4)) is silently ignored — the bin never accumulates hits. |
| AF_FCOV_CLASS_MEM_REF_VLT | Verilator does not support coverpoints that reference class member variables via the implicit this pointer. The coverpoint silently produces wrong results. |
| AF_FCOV_FUNC_NO_MERGE_INST | type_option.merge_instances = 1 collapses per-instance coverage data into a single type-level report, masking per-instance holes — one active instance covers for all. |
Getting Started
All three tools are open-source, MIT-licensed, and built on Verible — no proprietary parser or license required.
pip install anytree tomli
# Verible: https://github.com/chipsalliance/verible/releases
python3 bin/svalint.py -t my_assertions.sv
python3 bin/cnstlint.py -t my_class.sv
python3 bin/fcovlint.py -t my_coverage.sv
Rules can be selectively disabled via a config.toml file. The full rule reference — rationale, violation examples, correct usage, and severity — is in the documentation linked above.
Contributions and feedback welcome via GitHub issues.
SVALint · CNSTLint · FCOVLint — AsFigo Technologies, UK
Comments
Post a Comment