verilator/AGENTS.md

7.2 KiB

Verilator Guidelines for AI Coding Agents

These files are the general layer an agent loads first -- nearest file wins, so you read this repository-root file plus the one for the directory you are editing. They stay deliberately high-level: where to start, how the tree is laid out, and the conventions reviewers otherwise enforce by hand. They are an index, not the architecture reference -- for depth (how a pass works internally, the algorithms, node lifetime) they point you to docs/internals.rst. When the guidance here is not enough, that is where to look next.

This file has two parts. Orientation gets you productive in the codebase from a cold start. Before you open a PR is the checklist of conventions reviewers otherwise have to enforce by hand -- read it before submitting any change.

Then read the directory guide for the area you are editing:


Orientation

What Verilator is

Verilator is a compiler, not an interpreter. It translates synthesizable (and much behavioral) SystemVerilog into a cycle-accurate C++ model that you then compile and run. Almost every decision is made at compile ("verilation") time; the generated C++ just advances state each evaluation. Optimize for verilation- time work over runtime work.

The pipeline is the spine

A run is an ordered sequence of passes over one shared AST (abstract syntax tree). In source order:

Stage What it does Key files
Preprocess + parse Lex and parse text into a raw AST -- builds nodes only, no semantic checks verilog.l, verilog.y
Link / elaborate Resolve names, scopes, parameters; instantiate the hierarchy V3LinkParse, V3LinkDot, V3Param
Width / type Assign and check data types and bit widths V3Width
Transform / optimize / schedule Constant fold, lower language features, schedule events V3Const, V3Randomize, V3Assert*, V3Sched, V3Timing, V3Dfg
Emit Lower the final AST to generated C++ V3EmitC*
Runtime Library the generated model links against include/verilated*

This table is the map; docs/internals.rst has the detail behind each stage.

Where to make a change

Map the symptom to the pass that owns it, then start by reading that pass's top-of-file comment.

Symptom or feature area Start in
Type/width error, "what type is this", implicit conversion V3Width
Name/scope/parameter resolution ("Can't find...", hierarchy) V3LinkDot, V3Param
randomize / constraint / rand / randc V3Randomize
assert / property / sequence / cover V3Assert, V3AssertPre, V3AssertNfa
fork / timing / #delay / NBA / event scheduling V3Sched, V3Timing, V3Fork
Syntax wrongly accepted or rejected verilog.y, verilog.l
Wrong generated C++ V3EmitC*
Runtime model behavior include/verilated*

Build and run a test

  • Build in the source tree: autoconf && ./configure && make -j8. Configure with --enable-ccwarn so a new compiler warning stops the build.
  • Run one test from the repository root: test_regress/t/t_<name>.py.
  • Run the full regression with make test. The complete suite requires configuring with --enable-longtests (works on every OS, including macOS).

Before you open a PR

Scope and process

  • Searched open PRs and issues -- duplicating in-flight work wastes review time.
  • Fixed the general root cause, not just the reported case -- if it also affects other modules/classes/interfaces, cover them or expect rejection.
  • PR is single-purpose. Refactors, drive-by fixes found along the way, and new features each go in separate PRs; land standalone cleanups first.
  • Every bug fix has a test that fails without the fix; include the issue's own reproducer when possible.
  • New code aims for 100% line coverage; branch coverage far below line coverage signals guards callers never violate -- justify or remove them.
  • Ran make format (clang-format), make cppcheck, and make lint-py; self-reviewed the diff for leftover debug code, stale comments, and copy-paste errors.
  • Ran the full regression on at least one OS before submitting. Partial runs are fine during development, but the submitted PR is expected to pass every test.
  • Did not edit docs/CONTRIBUTORS (humans only) or Changes (maintainer updates it near release).

Pick the right diagnostic (and its required test)

The API you choose determines which test must accompany the change.

API Output Meaning Required test
v3error("...") %Error: User wrote invalid SystemVerilog t_*_bad*.v + .out golden
v3error("Unsupported: ...") %Error-UNSUPPORTED: Legal SV that Verilator does not yet support t_*_unsup*.v + .out golden
v3warn(CODE, "...") %Warning-CODE: Legal but suspicious code warning test + .out golden
v3fatalSrc("...") %Error: Internal Error Should-never-happen internal assertion none -- not user-triggerable
  • Every v3error/v3warn needs a test in test_regress/t/ -- enforced by the warn-coverage distribution test. v3fatalSrc is exempt.
  • Reserve "Unsupported:" for not-yet-implemented features, never for user mistakes.
  • When an error enforces a spec-defined restriction, cite the clause (IEEE 1800-2023 11.4.7) so it is verifiable. Update docs/guide/warnings.rst when adding or changing a warning.
  • On error paths, clean up or replace invalid AST (e.g. AstConst::BitFalse) so later passes do not crash after the error.

Cross-cutting code rules

  • No non-ASCII characters in C++ sources or headers: write -- (two ASCII hyphens) rather than a Unicode em-dash, and a plain ' rather than a smart quote. At write time, not when CI complains.
  • Lists stay sorted: lexer/parser tokens, option declarations, enum values, configure feature lists, documented option lists.
  • bin/ scripts are Python (distributed cross-platform); nodist/ may use bash and platform-specific code (developer-only, not packaged).
  • Runtime code in include/ targets C++14 (--no-timing builds must work); C++20 only in timing code paths.
  • In include/ public headers, prefix public classes with Verilated/Vl and document the API with /// comments.
  • A new code pattern is applied globally or not at all -- no one-off convention in a single file.

Commits

  • Subject line is short and imperative and conventionally ends with the PR number: Support property case (#7721). A body is optional and common for non-trivial changes.