silicode · 2026-10-28 · 10 min

Building Closed-Loop Multi-Agent Lint Pipelines for SystemVerilog

Multi-agent RTL workflows pair LLMs with Verilator and formal checks to catch inferred latches, race conditions, and width mismatches before human pull request reviews.

Diagram of an automated RTL generation pipeline routing code through Verilator static lint and formal verification feedback loops.

Engineering teams at MosChip and open-source hardware research labs have converged on a pragmatic architecture for generative hardware design: multi-agent LLM pipelines wired directly to deterministic static analysis tools. Over the past several months, frameworks like MAGE (DAC 2025) and NVIDIA's Spec2RTL-Agent have demonstrated that naked prompt-to-RTL generation has hit an impassable ceiling. Asking an autoregressive language model to write raw SystemVerilog in a single pass routinely produces syntactically valid code that fails completely under real-world static linting, synthesis, and formal verification.

For RTL engineers, FPGA developers, and small ASIC teams, this shift marks the boundary between unusable AI demos and practical developer leverage. The goal of an engineering team is not to generate plausible code. The goal is to eliminate manual boilerplate, clean up module wrappers, and write standard bus decoders without absorbing tape-out risk or spending three days debugging race conditions introduced by a statistical text predictor.

By deploying multi-agent architectures that treat local linters (such as Verilator and open-source formal tools) as deterministic execution environments, teams can automate the compile-lint-fix loop entirely in local CI before any human engineer reviews a pull request.

The Silent Failure Modes of One-Shot Verilog

Language models trained on public code repositories are fundamentally optimized to predict the next most likely token, not to enforce concurrent hardware execution rules. SystemVerilog presents unique hazards because code that compiles cleanly in forgiving simulators can contain catastrophic physical design bugs.

When a general-purpose model outputs hardware description code from a human specification, it consistently falls into four predictable failure modes:

1. Inferred Latches in Combinational Logic

Language models frequently write combinational always @(*) or always_comb blocks with incomplete branch coverage. If an if-else chain or a case statement omits a default branch or fails to assign every output signal in every conditional branch, synthesis tools infer a hardware latch.

In standard digital design, unintended latches degrade static timing analysis (STA), create complex clock gating problems, and cause physical design tools to fail during placement and routing. While SystemVerilog's always_comb keyword instructs modern tools to throw a compile warning when a latch is inferred, standard LLMs often revert to legacy Verilog-2001 always @(*) syntax where latches pass silently through initial compilation.

2. Blocking vs. Non-Blocking Assignment Races

Autoregressive models struggle with the semantic distinction between blocking (=) and non-blocking (<=) assignments. A frequent LLM error pattern is the accidental use of blocking assignments inside sequential always @(posedge clk) blocks, or mixing blocking and non-blocking statements within the same state machine logic.

In simulation, blocking assignments in sequential blocks make the execution order dependent on event scheduling order within the simulator. In hardware, this causes non-deterministic hold-time violations and race conditions across flip-flop stages that pass simple cycle tests but fail under gate-level simulation.

3. Bit-Width Truncation and Implicit Zero-Padding

When implementing arithmetic operations, address decoders, or parameterized FIFOs, LLMs routinely mismanage vector bounds. A model will declare an address wire as logic [7:0] addr and assign it to a 10-bit pointer, relying on implicit truncation without flagging an error.

In arithmetic datapaths, implicit zero-padding or dropped sign bits during signed multiplication create subtle functional bugs. These errors often remain undetected until boundary-value corner cases hit the datapath during formal property verification or post-synthesis regression testing.

4. Multidimensional Packed Array Slicing Errors

Modern SystemVerilog features like multidimensional arrays, packed structures, and streaming operators ({>>{...}}) have limited representation in open-source GitHub corpora. Language models frequently confuse C-style row-major array indexing with Verilog bit-slicing rules, generating out-of-bounds bit shifts that crash simulators or read from unmapped memory addresses.

+-------------------------------------------------------------+
|                 Multi-Agent RTL Iteration Loop              |
+-------------------------------------------------------------+
|                                                             |
|  +--------------------+        +-------------------------+  |
|  | Specification /    | ---->  | Generator Agent         |  |
|  | Architecture Spec  |        | (LLM + SystemVerilog)   |  |
|  +--------------------+        +-------------------------+  |
|                                             |               |
|                                             v               |
|                                  Raw SystemVerilog Source   |
|                                             |               |
|                                             v               |
|  +--------------------+        +-------------------------+  |
|  | Fixer / Refactor   | <----  | Static Linter Agent     |  |
|  | Agent (LLM Loop)   | Error  | (Verilator -Wall lint)  |  |
|  +--------------------+ Logs   +-------------------------+  |
|           |                                 |               |
|           +---------------------------------+ (Pass)        |
|                                             |               |
|                                             v               |
|  +--------------------+        +-------------------------+  |
|  | Formal Proof       | <----  | Bounded Model Checker   |  |
|  | Check Agent        | Pass   | (SymbiYosys / SBY)      |  |
|  +--------------------+        +-------------------------+  |
|           |                                                 |
|           v                                                 |
|    Verified RTL PR                                          |
+-------------------------------------------------------------+

Anatomy of an Agentic Lint Loop

To bridge the gap between statistical text output and hardware reality, recent multi-agent frameworks divide RTL creation into isolated, role-specific agents that communicate via structured diagnostic protocols rather than loose natural language chats.

A robust production pipeline relies on three discrete agents working against deterministic command-line tools.

Agent 1: The Architecture and Generation Agent

The Generation Agent receives the human specification, parameter definitions, and target clock domain requirements. Its role is strictly constrained: emit valid SystemVerilog modules adhering to modern IEEE 1800-2017 standards, including explicit port directions, typed interfaces, and separated sequential and combinational logic blocks.

The Generation Agent is prompted with rigid system instructions prohibiting legacy constructs like wire declarations for procedural blocks, implicit nets, and legacy reg keywords. Every sequential block must use always_ff @(posedge clk or negedge rst_n), and all combinational logic must use always_comb.

Agent 2: The Static Analysis and Linter Agent

Instead of asking an LLM to "verify" its own code, the pipeline writes the generated SystemVerilog files to a temporary disk volume and executes a hardened Verilator lint suite. Verilator is executed in static analysis mode with strict flag enforcement:

verilator --lint-only -Wall \
  -Werror-latch \
  -Werror-width \
  -Werror-caseincomplete \
  -Werror-combdly \
  -Wno-fatal \
  -sv top_module.sv

These flags convert subtle hardware anti-patterns into hard command-line errors:

  • -Werror-latch: Fails compilation instantly if any always_comb block or conditional tree infers an internal latch.
  • -Werror-width: Rejects implicit bit truncation, vector padding, or mismatched operand widths.
  • -Werror-caseincomplete: Flags any case statement lacking an explicit, complete coverage tree or missing a default assignment.
  • -Werror-combdly: Enforces that non-blocking assignments (<=) are not used inside pure combinational paths.

If Verilator exits with a non-zero return code, the raw standard error text is captured by the pipeline controller.

Agent 3: The Context-Pruning Fixer Agent

Feeding raw compiler output directly back into an LLM often fails because large compiler traces contain irrelevant warning cascades that overwhelm the model's context window.

The pipeline controller runs an intermediate AST parser and diagnostic filter. It extracts only the failing module name, the offending line numbers, the precise error tag (such as WIDTHEXPAND or LATCH), and the local code block surrounding the failure (five lines above and below).

The Fixer Agent receives this pruned diagnostic payload along with the original module declaration. Crucially, it is not asked to rewrite the entire file from scratch. It is instructed to output an AST-level patch or a replacement sub-block targeting only the offending logic construct.

Receipts and Benchmark Comparisons

To understand why deterministic lint loops are mandatory, consider benchmark evaluations extracted from recent multi-agent RTL research (including DAC 2025 MAGE data and validation-first orchestration benchmarks on standard VerilogEval and custom bus-controller test sets).

Comparative Static Lint Pass Rates

Pipeline Configuration First-Pass Syntactic Validity Zero Inferred Latches Clean Bit-Width Alignment Verilator -Wall Lint Pass Rate Post-Loop Formal Pass Rate
One-Shot Base LLM (DeepSeek-Coder 33B) 81.2% 54.3% 48.7% 36.1% 22.4%
One-Shot Base LLM (Claude 3.5 Sonnet) 92.4% 71.8% 63.2% 51.5% 38.9%
Multi-Agent (Prompt-to-Prompt Self-Reflection) 93.1% 74.2% 66.8% 58.2% 41.2%
MAGE Multi-Agent + Verilator Lint Loop (3 iters) 99.1% 98.4% 96.2% 94.8% 88.6%
Spec2RTL / Validation-First CI Loop (5 iters) 99.8% 99.2% 98.1% 97.4% 95.9%

Receipt details: Representative data consolidated from MAGE (DAC 2025 benchmark runs on VerilogEval-Human), Spec2RTL-Agent evaluations, and validation-first multi-agent orchestration data (arXiv 2604.19856). First-pass metrics measure baseline zero-shot code generation against standard bus decoders, arithmetic units, and state machines. Agentic loop data reflects up to three to five automated Verilator/Icarus lint-and-repair iterations without human intervention.

The data shows that self-reflection without tools yields marginal improvements over one-shot generation. A model prompted to "review your code for latches" simply hallucinates that its previous code was correct. Real quality jumps only occur when the feedback mechanism is powered by an external, non-probabilistic binary compiler like Verilator.

Beyond Linting: Adding Bounded Formal Proofs

While lint cleanliness guarantees that code compiles without latches, race conditions, or width mismatches, it does not guarantee that the module adheres to temporal state machine logic. A FIFO controller can be completely lint-clean while dropping transactions under read/write collision.

The next layer in an automated multi-agent CI pipeline is bounded model checking using open-source formal tools such as SymbiYosys (SBY) paired with Yosys and Boolector.

Automated Assertion Generation

In this tier, a dedicated Verification Agent reads the original module interface and generates inline SystemVerilog Assertions (SVA) or a formal harness (formal_tb.sv). The harness checks standard safety invariants:

  1. Mutual Exclusion: Assert that one-hot state registers never enter illegal multi-bit configurations.
  2. Handshake Integrity: Assert that when valid is asserted without an accompanying ready, the internal data payload remains invariant until the handshake completes.
  3. Pointer Safety: Assert that FIFO count never exceeds buffer DEPTH and that full and empty flags are never asserted simultaneously.
`ifdef FORMAL
  always_ff @(posedge clk) begin
    if (rst_n && !full && wr_en)
      assert (count < DEPTH);
    
    if (rst_n)
      assert (!(full && empty));
  end
`endif

When SymbiYosys executes a bounded model check of 20 clock cycles, any assertion failure produces a counterexample trace (trace.vcd).

The multi-agent controller parses this trace, extracts the specific clock cycle where the property failed, reads the internal register states at cycle $N-1$, and routes that exact temporal trace back to the Fixer Agent. This allows the model to diagnose deadlocks and state transition traps without requiring human waveform inspection.

Practical CI/CD Architecture for Engineering Teams

Integrating multi-agent RTL generation into a production engineering workflow requires strict boundary control. Generative tools should never write directly to main repository branches. Instead, they must operate inside an isolated branch worker managed by standard continuous integration hooks.

+-------------------------------------------------------------+
|                 Production CI/CD Gate Setup                 |
+-------------------------------------------------------------+
|                                                             |
|  [ Feature Spec / Issue #104 ]                              |
|             |                                               |
|             v                                               |
|  [ Multi-Agent Generation Worker ]                          |
|             |                                               |
|             +---> [ Verilator -Wall Lint Loop ]             |
|             |            |                                  |
|             |     (Passes zero warnings)                    |
|             |            v                                  |
|             +---> [ SymbiYosys Formal Bounded Check ]       |
|                          |                                  |
|                   (Passes all SVAs)                         |
|                          v                                  |
|  [ Auto-Generated Pull Request ]                            |
|  Includes: Clean SV + SVAs + Lint Sign-Off Log             |
|             |                                               |
|             v                                               |
|  [ Human Reviewer / Staff RTL Engineer ]                    |
|                                                             |
+-------------------------------------------------------------+

Step-by-Step Pipeline Checklist

  1. Containerize the Toolchain: Package Python, your agent orchestration runtime, Verilator (version 5.020 or newer), Yosys, and SymbiYosys into a single hermetic Docker image. This eliminates host library mismatches and guarantees deterministic lint outputs.
  2. Isolate Code Generation from Review: Configure the generation script to output code to a feature branch (agent/feature-name) and automatically trigger GitHub Actions or GitLab CI runners.
  3. Set Zero-Tolerance Lint Rules: Configure CI to fail if any warnings are emitted. Force flags: -Wall -Werror-latch -Werror-width -Werror-caseincomplete -Werror-combdly.
  4. Run Bounded Formal Reset Checks: Execute a 15-cycle BMC pass to verify that reset cleanly drives all registers to known initial states and that no combinational loops exist.
  5. Emit Structured PR Receipts: When the pipeline opens a pull request for human review, the description must automatically append the static lint log, formal engine pass reports, and coverage metrics.

This workflow shifts the human engineer's role from writing mechanical boilerplate and fixing syntax errors to reviewing architectural specifications, constraint accuracy, and full subsystem integration.

What this means for Silicode

The industry shift toward deterministic, closed-loop multi-agent design validates the architectural foundation of Silicode (silicode.ai). Generative models without compiler execution loops are dangerous toys in hardware engineering.

Silicode embeds formal static linting, bounded model checking, and multi-agent refactoring loops directly into the generation core. By guaranteeing that every emitted block of SystemVerilog has cleared rigorous static analysis and property verification before it reaches the designer's desk, Silicode allows hardware teams to scale module throughput while protecting tape-out integrity.

Direct Answer: How to Automate Lint-Clean Verilog Generation

To generate lint-clean SystemVerilog using LLMs without manual intervention, wrap generative models inside a multi-agent loop driven by Verilator with strict warning-as-error flags (-Wall -Werror-latch -Werror-width). Use an AST-aware diagnostic parser to extract failing lines and error tags, route them to an automated refactoring agent for localized code patching, and require bounded formal proofs before any code is submitted to human pull request review.

Sources

More Silicode Insight

VerilogEDA AutomationVerilatorStatic AnalysisRTL Verification