Yosys tells you a structural mistake will not synthesize. Verilator tells you it will not simulate the way you think. Verible tells you before either of them is involved: the case is missing a default, the parameter is named in the wrong style, the always_comb uses a non-blocking assignment, the packed dimensions are upside down. It is fast, deterministic, and the right first gate for any SystemVerilog repository — a complement to, not a substitute for, an end-to-end RTL verification flow.
Lint vs format: two separate tools
The single most common point of confusion is that “Verible” is not one program. It is a set of independent binaries that share a common parser and syntax-tree library, distributed under Apache-2.0 from github.com/chipsalliance/verible. The two you will use daily are verible-verilog-lint and verible-verilog-format, and they do different jobs:
verible-verilog-lint— the linter. Runs a configurable rule set, emits one finding per line, and exits non-zero if anything fires. It never edits your code unless you explicitly ask for--autofix.verible-verilog-format— the formatter. Opinionated and idempotent, it rewrites whitespace and layout the wayclang-formatreformats C++. It has nothing to do with the lint rule set.verible-verilog-syntax,verible-verilog-ls(a full Language Server),verible-verilog-project, andverible-patch-tool— tree dumps, editor integration, and repository-scale helpers.
Install and run
Releases are tagged on GitHub with prebuilt Linux and macOS tarballs; there are also community Homebrew and Nix packages. There is no build step required to use it.
# Grab a tagged release — prebuilt Linux/macOS tarballs are on the
# GitHub releases page (the asset filename includes version + platform):
# https://github.com/chipsalliance/verible/releases/latest
tar xzf verible-*.tar.gz
export PATH="$PWD/verible-*/bin:$PATH"
# Confirm the build
verible-verilog-lint --versionThe simplest invocation lints one file with the default rule set: verible-verilog-lint rtl/foo.sv. Everything else is configuration on top of that.
The rule taxonomy
Verible ships roughly 80 lint rules. Run verible-verilog-lint --help_rules (or --generate_markdown) for the canonical list against your installed version — rule sets evolve between releases. It is useful to think of them in three buckets: style, correctness-leaning, and tooling-impact. The split matters because a style violation is a taste argument, while a correctness-leaning rule is catching a pattern that turns into a real bug in synthesis or simulation.
Style rules
Pure formatting and naming consistency. Cheap to fix, high signal for code review, no impact on behavior.
| Rule | What it catches |
|---|---|
line-length | Lines past the limit (default 100 chars), configurable. |
no-tabs / no-trailing-spaces | Hard tabs and trailing whitespace — the boring, useful pair. |
parameter-name-style | Parameters not matching the configured naming style (CamelCase / ALL_CAPS by default). |
signal-name-style | Nets, variables, and ports not in lower_snake_case (configurable). |
enum-name-style / struct-union-name-style / interface-name-style / macro-name-style | Naming conventions for the rest of the type and macro space. |
module-filename | File basename must match the module declared in it. |
Correctness-leaning rules (lint-for-bugs)
These are the rules worth treating as fatal in CI. Each maps to a pattern that bites later — an inferred latch, a simulation/synthesis mismatch, or a tool-portability hazard.
| Rule | What it catches |
|---|---|
case-missing-default | A case with no default (unless marked unique) — the canonical inferred-latch source. |
always-comb / always-comb-blocking | Bare always @* instead of always_comb, and non-blocking <= inside combinational logic. |
explicit-parameter-storage-type / explicit-function-task-parameter-type | Untyped parameters and function/task arguments, which silently default to int-ish behavior. |
packed-dimensions-range-ordering | Big-endian packed ranges ([0:7]) where little-endian ([7:0]) is meant. |
suspicious-semicolon | A stray ; that changes behavior but escapes visual inspection (e.g. an empty loop or if body). |
forbid-defparam / forbid-consecutive-null-statements | Legacy defparam overrides and accidental ;;. |
legacy-genvar-declaration | Separate genvar declarations instead of inline loop genvars. |
Tooling-impact rules
truncated-numeric-literal— a sized literal whose value will not fit in its width (4'd20is the canonical example).undersized-binary-literal— a based literal with fewer digits than its declared width, so the value gets implicitly zero-padded. Enabled by default, but out of the box it only checks binary ('b) literals; octal and hex checking are off until you enable them (=undersized-binary-literal:hex:true). One of the few rules with a registered autofix.module-port,unpacked-dimensions-range-ordering,generate-label— keep ports, unpacked ranges (ascending,[0:N-1]), and generate labels in the shape downstream tools expect.
Worked examples: snippet, rule, fix
A case with no default arm is the highest-yield finding Verible reports. It is the source-level cause of a class of synthesis bugs that only surface much later.
// Trips: case-missing-default
// A case with no default leaves 'y' unassigned for unlisted selectors,
// which downstream becomes an inferred latch in synthesis.
always_comb begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
end// Fix: add a default arm (or mark the case 'unique' if truly full).
always_comb begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = '0;
endcase
endNumeric-literal rules catch silent value loss. The width of a literal and the width it is assigned into are two different things, and Verible flags both the value-too-big case and the implicit-padding case — adjacent to the family of width-mismatch bugs an elaborating tool would also catch.
// Trips: truncated-numeric-literal
parameter logic [3:0] MAX = 4'd20; // 20 does not fit in 4 bits
// Trips: undersized-binary-literal -- enabled by default, but by default it
// only checks BINARY ('b) literals. The hex line below is NOT flagged unless
// you turn on hex checking with =undersized-binary-literal:hex:true.
logic [7:0] mask_b = 8'b1010; // 4 'b digits in an 8-bit width -> flagged
logic [31:0] mask_h = 32'hAB; // value fine; only flagged if hex:true is set// Fix: size the value so it fits, and pad binary literals explicitly.
// 'verible-verilog-lint --autofix=inplace' rewrites the padded-binary line.
parameter logic [4:0] MAX = 5'd20;
logic [7:0] mask_b = 8'b0000_1010;
logic [31:0] mask_h = 32'h0000_00AB; // pad hex by hand (or enable hex:true)Combinational-process hygiene is the third high-value family. Using a bare always @* or a non-blocking assignment inside combinational logic is exactly the blocking vs non-blocking mistake that causes simulation/synthesis mismatches.
// Trips: always-comb (and 'always-comb-blocking' if you use <= inside)
// Bare 'always @*' is error-prone; the intent is combinational logic.
always @* begin
next_state <= state + 1; // non-blocking in comb logic, too
end// Fix: use always_comb and blocking '=' for combinational logic.
always_comb begin
next_state = state + 1;
endOutput format
Verible emits findings in a stable, grep-friendly format suitable for CI parsing. The trailing bracketed token is the stable rule ID:
rtl/decoder.sv:14:3: Explicit case default missing. [Style: case-statements] [case-missing-default]
rtl/decoder.sv:21:5: Use blocking assignments, not non-blocking, in always_comb. [always-comb-blocking]
rtl/decoder.sv:34:14: Numeric literal '4'd20' is truncated. [truncated-numeric-literal]Use that rule ID in --rules selections, waiver files, and inline waiver comments.
Configuring the rule set
You select rules in three ways, listed in increasing order of how a real project uses them. The command line --rules=+case-missing-default,-line-length is fine for one-offs. For a project, put the selection in a file and point at it with --rules_config, or let Verible find a .rules.verible_lint by walking up the tree with --rules_config_search. A +prefix enables a rule, - disables it, and = configures it.
# .rules.verible_lint (selected with --rules_config or auto-found via
# --rules_config_search). '+' enables, '-' disables, '=' configures.
+module-filename
+case-missing-default
+always-comb
+always-comb-blocking
+explicit-parameter-storage-type
+packed-dimensions-range-ordering
-line-length # too noisy on this brownfield repo
parameter-name-style=style_regex:"[A-Z][a-zA-Z0-9_]*"Waivers: per-line, per-block, and per-file
When a finding is a deliberate exception, suppress it explicitly so the suppression is reviewable. Inline waiver comments live in the source itself:
// Single line, on the line above the finding:
// verilog_lint: waive case-missing-default
always_comb begin
case (sel) ... endcase
end
// Or trailing the offending line:
logic [0:7] legacy_bus; // verilog_lint: waive packed-dimensions-range-ordering
// Or bracket a region:
// verilog_lint: waive-start line-length
// ... vendor-generated block ...
// verilog_lint: waive-stop line-lengthFor project-wide exceptions that you do not want scattered through the source, use a waiver file passed with --waiver_files. It supports matching by rule, by line, by location glob, and by regex:
# A separate waiver file passed via --waiver_files.
# Each waiver is reviewed in code review, so every suppression is visible.
waive --rule=line-length --location="rtl/legacy_.*\.sv"
waive --rule=parameter-name-style --regex="OldParamName"
waive --rule=case-missing-default --line=88 --location="rtl/decoder.sv"The point of a file-based waiver is that it is reviewed in code review: every suppression is visible and attributable, instead of quietly disabled in someone’s editor.
Autofix
For the subset of rules that ship a fixer, verible-verilog-lint --autofix=MODE can apply the fix for you. The modes are no (default), inplace (rewrite the file), patch (emit a unified diff to review first), patch-interactive and inplace-interactive (choose fix-by-fix), and generate-waiver (turn each finding into a waiver entry). Most semantic rules have no registered fix and are reported for you to handle by hand; the literal-padding rules are the typical autofix candidates.
# Review fixes as a diff before touching the tree
verible-verilog-lint --autofix=patch --autofix_output_file=fixes.patch rtl/foo.sv
# Apply fixes in place
verible-verilog-lint --autofix=inplace rtl/foo.svCI integration: make lint fatal
A linter that does not fail the build is a linter the team learns to ignore. verible-verilog-lint exits non-zero when any rule fires, so the CI step is just the command itself with no parsing required. Pair it with a formatter check in --verify mode, which exits non-zero if any file would be reformatted — the standard pre-commit pattern. This belongs at the front of a broader verification plan, as the cheapest gate before simulation and synthesis.
# CI gate: lint is fatal, format must be a no-op.
set -euo pipefail
# 1. Lint with the project rule set; non-zero exit on any finding.
verible-verilog-lint \
--rules_config=.rules.verible_lint \
--waiver_files=.waiver.verible \
rtl/**/*.sv
# 2. Format check: --verify exits non-zero if any file would change.
verible-verilog-format --verify rtl/**/*.svHow Verible compares to other linters
- svlint — a Rust-based SystemVerilog linter with a smaller, sharply-curated rule set and TOML-style configuration. It overlaps with Verible on style and a few correctness rules; many teams run both, since the rule sets only partly intersect. Neither elaborates the design.
- Verilator’s lint mode (
--lint-only -Wall) is a different category: it elaborates the design and finds semantic problems Verible cannot — real width mismatches, unused/undriven signals, latch inference, blocking/non-blocking races across an actual elaboration. It is slower and needs a resolvable file list. See Verilator simulation. - Commercial linters (the established sign-off tools) carry far larger rule libraries, methodology-aligned rule decks, CDC-aware checks, and waiver databases. They are the sign-off authority where one is required; Verible is the open-source, scriptable gate you can run on every push for free. The two are complementary, not competing.
The honest framing: Verible is a fast syntactic linter. Pair it with an elaborating tool such as Yosys or Verilator for the semantic checks, and it earns its place as the first and cheapest gate.
Limitations
- Verible is syntactic. It analyzes one file’s parse tree and does not elaborate, resolve parameters, or trace data flow across modules. “Is this signal actually driven?” is not a question it can answer.
- Heavy macro / preprocessor projects can confuse the parser. A project that depends on
`includeordering may need a clean, ordered file-list and--rules_config_search. - The rule set is style-leaning. It prevents the same class of bug from being committed twice; it does not replace a CDC tool, a formal property check, or a coverage run.
How ChipVerify AI runs Verible
ChipVerify AI (a pre-sign-off, closed-beta evidence platform) runs Verible as one engine among several. When you invoke it, we call verible-verilog-lint with a curated default rule set (the case-default, blocking-in-always_comb, packed-dim, and parameter-style classes that correlate with real silicon bugs) and surface the findings as evidence on the score panel, each with a file:line reference. The formatter runs in --verify mode to flag drift without overwriting source. It is one fast gate, not a foundry sign-off.
FAQ
Is Verible a linter or a formatter?
Both, but as separate binaries. verible-verilog-lint reports style and correctness-leaning rule violations and never edits your code unless you pass --autofix. verible-verilog-format is an independent, opinionated reformatter that rewrites whitespace and layout. They share the same parser but are run and configured separately.
How do I install Verible?
Download a prebuilt release from github.com/chipsalliance/verible (Linux and macOS tarballs are attached to each tagged release), unpack it, and put the bin directory on your PATH. There are also community Homebrew formulae and Nix packages. Verify the build with verible-verilog-lint --version.
How do I disable one Verible rule on a single line?
Add an inline waiver comment: put // verilog_lint: waive rule-name on the line above the offending code, or on the same line after it. For a block, bracket it with // verilog_lint: waive-start rule-name and // verilog_lint: waive-stop rule-name. Project-wide suppressions go in a --waiver_files file instead.
Can Verible fix violations automatically?
For the subset of rules that ship a fixer, yes. Run verible-verilog-lint --autofix=inplace to apply fixes to the file, or --autofix=patch to emit a unified diff you can review first. Rules without a registered fix (most semantic ones) are reported but left for you to fix by hand.
Does Verible replace Verilator or a synthesis tool?
No. Verible is a syntactic linter: it analyzes the parse tree of one file at a time and does not elaborate, resolve parameters, or trace signals across module boundaries. Catching an undriven signal, a real width mismatch, or a multiply-driven net needs an elaborating tool such as Verilator or Yosys.
Related reading
- RTL verification — where lint sits in the full pre-sign-off flow.
- Inferred latches — the bug behind
case-missing-default. - Width mismatches in Verilog — the semantic cousin of the literal-sizing rules.
- Verilator simulation and Yosys synthesis — the elaborating tools that catch what a syntactic linter cannot.
- Writing a verification plan — making lint a gate, not a suggestion.
Run Verible on your RTL inside ChipVerify AI
Drop a SystemVerilog file into the public Tiny Tapeout scanner at /tinytapeout for an instant Verible + Yosys + Verilator report, or request access to the full project workspace with lint, format-diff, synthesis, and simulation, every finding carrying a file:line reference.
Lint your SystemVerilog with Verible
Sign in and ChipVerify AI runs Verible lint on your design and returns style and structural findings with file-and-line locators — pre-signoff evidence, not a foundry signoff.