Skip to main content

CI-native documentation layer for RTL projects

Project description

rtl-aid

CI-native documentation layer for RTL projects.

Parses Verilog/SystemVerilog source files, extracts module structure, and generates Markdown documentation that stays in sync with your RTL — automatically.


Tools

Command Purpose
rtldoc Generate and maintain module documentation
rtllint Run verilator lint and tag warnings inline in source

Installation

pip install rtl-aid

Or from source:

git clone https://github.com/vishwaksen-1/rtl-aid
cd rtl-aid
pip install -e .

Requirements

  • Python 3.7+
  • rtllint requires verilator — a system binary, not installable via pip:
# Debian / Ubuntu
sudo apt install verilator

# macOS
brew install verilator

rtldoc has no external dependencies. If verilator is missing, rtllint will exit with a clear error and install instructions.


rtldoc

Quick start

# Document all modules in a directory
rtldoc -d rtl/

# Document specific files
rtldoc -f rtl/core/alu.v rtl/core/decoder.v

# Custom output directory
rtldoc -d rtl/ -o docs/modules/

What gets generated

For each module, a Markdown file is created or updated:

# alu

## Description
TODO: Add description

## Parameters
- DATA_WIDTH = 8
- OP_WIDTH = 4

## Inputs
- clk
- rst
- op [OP_WIDTH-1:0]
- operand_a [DATA_WIDTH-1:0]
- operand_b [DATA_WIDTH-1:0]

## Outputs
- result [DATA_WIDTH-1:0]
- overflow

## Calls
- [mux4](mux4.md)

## Called By
- [cpu_core](cpu_core.md)

The Description section is never overwritten — you write it once, rtldoc preserves it on every run. All other sections are auto-managed.

Port and parameter detail

  • Vector ports show their packed width next to the name: operand_a [DATA_WIDTH-1:0].
  • Struct- or enum-typed ports (typedef'd, not a base type) show the type name instead: p (pair_t), st (state_t).
  • Plain scalar ports (input logic clk) render as just the name — no width or type to add.
  • Parameters resolve simple integer arithmetic and show both the raw expression and the resolved value: DERIVED = BASE * 2 (= 8). Anything rtldoc can't resolve (sized literals, unknown references, non-arithmetic expressions) is marked (unresolved) rather than guessed at.
  • localparam entries are listed alongside parameters, suffixed (localparam) to distinguish them: DEPTH = WIDTH*2 (= 16) (localparam).

CLI reference

rtldoc (-d DIR [DIR...] | -f FILE [FILE...]) [options]

Input:
  -d, --dir DIR [DIR...]    Recursively scan directories for .v / .sv files
  -f, --file FILE [FILE...]  Parse specific files (no directory traversal)

Output:
  -o, --out OUT_DIR          Output directory (default: temp/docs/modules)
  --json-graph               Write dependency graph to graph.json

Filtering:
  --exclude PATTERN [...]    Exclude paths containing any of these strings

Run modes:
  --dry-run                  Show what would be written without touching files
  --ci                       Fail (exit 1) on missing descriptions, no-IO modules,
                             or self-instantiations
  --print-errors             Print CI issues to stdout (in addition to the error log)

Verbosity:
  -v                         Print modified file paths
  -vv                        Print modified files + section-level diffs (+adds/-removals)

CI integration

# .github/workflows/docs.yml
- name: Check RTL docs
  run: rtldoc -d rtl/ -o docs/modules/ --ci --print-errors

Exit codes: 0 = clean, 1 = CI check failed.

Testbench files (_tb.v, _tb.sv, _bench.v, _testbench.v, and .sv variants) are automatically excluded from scanning.

Dry run

Preview what would change before committing:

rtldoc -d rtl/ --dry-run
[DRY RUN] Would write:
  docs/modules/alu.md
  docs/modules/decoder.md

5 module(s) processed — 2 doc(s) would be written, 3 unchanged

Dependency graph

rtldoc -d rtl/ --json-graph -o docs/modules/

Writes docs/modules/graph.json:

{
  "cpu_core": {
    "calls": ["alu", "decoder", "register_file"],
    "called_by": []
  },
  "alu": {
    "calls": ["mux4"],
    "called_by": ["cpu_core"]
  }
}

rtllint

Runs verilator --lint-only -Wall on a file, plus two rtl-aid-native checks verilator's -Wall doesn't cover, and tags each warned line with an inline comment. Useful for tracking lint debt without blocking a build.

Beyond verilator, rtllint also flags:

  • Incomplete sensitivity lists — always @(a) y = a & b; with b missing from the list
  • Unlabeled generate blocks — generate ... begin ... end ... endgenerate with no : label

Both run directly on the source text, independent of the verilator subprocess call.

Usage

rtllint rtl/core/alu.v

# With include directories
rtllint -I rtl/includes -I rtl/common rtl/core/alu.v

# Multiple files
rtllint rtl/core/*.v

# Preview without modifying files
rtllint --dry-run rtl/core/alu.v

What gets written

Given a verilator warning on line 75:

%Warning-WIDTHEXPAND: rtl/alu.v:75:12: Operator ADD generates 9 bits ...

rtllint modifies alu.v in place:

// lint-test: verilator --lint-only -Wall rtl/alu.v
// tb-test: tba
...
assign result = a + b;  /* Check[WIDTHEXPAND]: Operator ADD generates 9 bits ... */

When a rule ID is available — verilator's own (e.g. WIDTHEXPAND) or rtl-aid's custom checks (SENSINCOMPLETE, GENUNNAMED) — the tag includes it as /* Check[ID]: message */ so it can be cited when suppressing or grepped for later. A bare %Error with no dashed ID falls back to plain /* Check: message */.

Re-running rtllint replaces existing /* Check: */ tags — it does not stack duplicates.

CLI reference

rtllint FILE [FILE...] [options]

  -I, --include DIR    Add include directory (repeatable)
  --dry-run            Show issues without modifying files
  -v                   Print full verilator output

Exit codes: 0 = no issues found, 1 = one or more warnings/errors.


Design principles

  • No heavy dependencies — stdlib only, no parser frameworks
  • CI-first — every flag is designed for scripted use
  • Safe for teams — manual descriptions are never overwritten
  • Diff-aware — files are only touched when content actually changes

Supported syntax

Feature Status
Verilog-2001 (.v) Supported
SystemVerilog (.sv) Supported (port/param parsing)
ANSI port declarations Supported
Comma-inherited port direction (output reg a, b) Supported
Parameterised modules (#(parameter ...)) Supported
localparam (distinguished from parameter) Supported
Simple integer parameter expressions (BASE * 2) Supported (resolved alongside the raw expression)
Port widths and struct/enum types in generated docs Supported
`define macro substitution before parsing Supported (single-file only)
`include across files Not supported
Module instantiations with #() param override Supported
Testbench auto-exclusion Supported
Pre-2001 Verilog style Not supported
Multi-module files Not supported (first module only)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rtl_aid-0.2.0.tar.gz (17.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rtl_aid-0.2.0-py3-none-any.whl (15.0 kB view details)

Uploaded Python 3

File details

Details for the file rtl_aid-0.2.0.tar.gz.

File metadata

  • Download URL: rtl_aid-0.2.0.tar.gz
  • Upload date:
  • Size: 17.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for rtl_aid-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7128e788ddbe077b82b37fc48da5c5462d61d3bd1471eeb259276069e2c4382c
MD5 ac34cb2b9199cc7933171d1ba307c765
BLAKE2b-256 e5ac92cec4143f406a8f30f55aa8fd588f9cd8ebbaf1ae1495f78762532d447d

See more details on using hashes here.

File details

Details for the file rtl_aid-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: rtl_aid-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 15.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for rtl_aid-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 95e89cfa64d1d40866e8c3ac2cbc3142b58694c47c655e943749becffa7498f2
MD5 413f267fb3a632a1145121d0886c42dd
BLAKE2b-256 1a7a0a5e6629b0996b7c43feeeb2d99cbc062e79b709a84baf6af341b741cf82

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page