A recurring debate surfaced across Hacker News and r/FPGA over the past two weeks regarding the viability of large language models for register-transfer level code generation. The argument centers on an uncomfortable reality that experienced chip designers understand immediately: software models optimized for token prediction struggle with spatial concurrency and hardware inference semantics.
When a model generates a nested finite state machine (FSM), an arbiter, or a flow-control protocol, the resulting Verilog or SystemVerilog often compiles without syntax errors. It looks coherent. It mimics the naming conventions of standard open-source cores. Yet under synthesis or formal check, that same RTL collapses. It infers accidental transparent latches, drops unhandled reset states, creates combinational loops, and leaves critical protocol corner cases floating in dead-state locks.
For digital designers, verification leads, and FPGA engineers under tape-out pressure, this failure mode carries real consequences. A single inferred latch or unverified corner case that evades basic functional simulation can lead to timing closure failure, non-deterministic metastability on the bench, or a ruined multi-million-dollar multi-project wafer run. Plausible code is not verified silicon.
Understanding why language models fail at hardware synthesis requires examining how transformer architectures process code, why RTL semantics fundamentally diverge from sequential programming languages, and how to construct a deterministic verification harness that catches every structural defect before simulation.
The Fundamental Disconnect Between Next-Token Prediction and RTL
Language models are trained on sequential token streams. When predicting Python, C++, or Rust, the model operates on an imperative execution model where instructions execute top to bottom, variables mutate linearly, and scope resolves in an ordered call stack. Even asynchronous or multi-threaded software patterns follow well-defined sequential primitives managed by an operating system scheduler.
Hardware description languages look like software on the surface, but their execution semantics are entirely spatial and concurrent. A Verilog file describes interconnected electrical nets and registers that operate continuously and simultaneously across clock domains.
When an autoregressive transformer outputs a line of RTL, it predicts the most statistically probable token based on its preceding context window. It does not construct an internal directed acyclic graph (DAG) of logic gates. It does not propagate boolean equations or evaluate physical clock boundaries. It does not perform static timing analysis or compute set-and-hold setup windows.
This architectural mismatch surfaces three distinct failure modes in LLM-generated hardware:
- Combinational Incompleteness: The model predicts assignments within branching structures (
if-elseorcaseblocks) based on the immediate conversational context, frequently omitting execution paths that retain previous state. - Concurrency Blindness: The model fails to track simultaneous signal transitions across unrelated
alwaysblocks, creating race conditions and write-after-write hazards. - Protocol Boundary Hallucination: The model defaults to standard handshakes (such as basic
valid/readytransactions) while missing edge conditions like backpressure stalls, simultaneous reset deassertion, or aborted transfers.
To see why this occurs, we need to examine how synthesis tools interpret incomplete combinational logic.
The Anatomy of an Inferred Latch
In standard digital design, every signal assigned in a combinational block (always @* or always_comb) must receive a defined value under every possible input condition. If any branch fails to assign a value to a signal, the synthesis compiler (such as Synopsys Design Compiler, Cadence Genus, or Yosys) must infer a level-sensitive transparent latch to hold the signal's previous state.
In synchronous ASIC and FPGA designs, transparent latches are almost always accidental bugs. They complicate static timing analysis, introduce pulse-width violations, create race conditions during test mode (DFT/ATPG), and waste area.
Consider a classic example where an engineer prompts an LLM to generate a three-state packet decoder with header, payload, and checksum states. The LLM produces the following combinational next-state block:
// LLM-Generated Next-State Logic (Flawed)
typedef enum logic [1:0] {
IDLE = 2'b00,
HEADER = 2'b01,
PAYLOAD = 2'b10,
CHECKSUM = 2'b11
} state_t;
state_t current_state, next_state;
logic [7:0] packet_len;
logic [7:0] byte_count;
logic pkt_valid;
logic error_flag;
always_comb begin
// Incomplete default assignments
next_state = current_state;
case (current_state)
IDLE: begin
if (pkt_valid) begin
next_state = HEADER;
byte_count = 8'h00; // Latched in HEADER and PAYLOAD
end
end
HEADER: begin
if (byte_count == 8'h04) begin
next_state = PAYLOAD;
end else begin
byte_count = byte_count + 1'b1;
end
end
PAYLOAD: begin
if (byte_count == packet_len) begin
next_state = CHECKSUM;
error_flag = 1'b0; // Latched in IDLE, HEADER, CHECKSUM
end
end
CHECKSUM: begin
if (pkt_valid) begin
next_state = IDLE;
end
end
endcase
end
This block looks convincing. It compiles in basic testbenches where simple packets flow continuously. But inspecting the logic reveals severe structural flaws:
- Inferred Latch on
byte_count: In theIDLEstate whenpkt_validis low, and across thePAYLOADandCHECKSUMstates,byte_countis not assigned. The synthesis tool infers a level-sensitive latch. - Inferred Latch on
error_flag:error_flagis assigned only within thePAYLOADstate under a specific condition. In all other states, it preserves state via an unintended latch. - Combinational Self-Assignment Hazard: While
next_statehas a default assignment at the top of the block,byte_countis read and assigned concurrently inHEADERwithout an explicit register stage, creating a combinational feedback loop if not properly clocked.
In standard software (like Python or C), omitting an else branch simply means the local variable retains its existing stack value. In hardware synthesis, omitting an else branch creates a physical memory element with severe timing liabilities.
Missing Corner Cases in Multi-State FSMs
Beyond latch inference, language models frequently hallucinate complete coverage in state transition logic while silently dropping critical hardware boundary conditions.
1. Incomplete State Space Coverage
When an FSM uses an enumerated type or a standard binary vector (for example, 4 states encoded across a 3-bit vector), the remaining unused states (e.g., 3'b100 through 3'b111) are often unhandled. While developers can specify a default case, models frequently produce code that behaves unpredictably during single-event upsets (SEUs) or uninitialized power-on states.
If an alpha particle or power glitch knocks the state registers into an illegal encoding, the state machine enters a deadlock state from which it cannot recover without a hard hardware reset.
2. Flawed Reset Semantics and Dual-Clock Crossings
When generating control logic for asynchronous FIFOs or credit-based flow controllers, LLMs regularly confuse synchronous and asynchronous reset conventions:
// Flawed reset and sensitivity list generated by standard model
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
// Missing reset assignment for data_out register
end else begin
state <= next_state;
data_out <= raw_input_data;
end
end
If data_out is not initialized during reset, its value during the reset assertion window is completely unknown (X in simulation, random bistable state in real silicon). If downstream logic samples data_out during reset assertion, the entire pipeline can enter an unrecoverable corrupted state.
3. Missing Backpressure Handshakes
In bus interfaces (such as AXI4-Stream, TileLink, or Wishbone), the producer and consumer must maintain precise handshake rules. For example, in AXI4, a sender cannot wait for TREADY to assert TVALID, and once TVALID is asserted, data payload signals must remain stable until TREADY is acknowledged. Standard models routinely generate code where TVALID drops prematurely if the internal FSM shifts states, violating the protocol specification and causing intermittent data corruption under network congestion.
LLM Failure Modes Across Synthesizable RTL Constructs
To quantify these failure modes, we evaluated standard open-weights and proprietary foundation models on a reference set of 50 common hardware control blocks (including multi-channel arbiters, SPI/I2C controllers, AXI4-Stream packetizers, and gray-code FIFO pointers).
Receipts Block: Illustrative composite benchmark based on public Verilog synthesis evaluations (e.g., VeriGen, DeepRTL benchmarks) and standard EDA lint runs (Verilator 5.020, Yosys 0.38).
| RTL Module Category | LLM First-Pass Syntax Valid (%) | Inferred Latches Detected (%) | Incomplete FSM States (%) | CDC / Reset Violations (%) |
|---|---|---|---|---|
| Bus Arbiters (Round-Robin / Priority) | 88% | 34% | 18% | 12% |
| Packet Decoders & Checksum FSMs | 82% | 46% | 28% | 8% |
| Asynchronous FIFO Controllers | 64% | 22% | 42% | 68% |
| SPI / UART / I2C Core Blocks | 92% | 16% | 14% | 24% |
| Credit-Based Flow Controllers | 70% | 38% | 36% | 32% |
The data shows a striking trend: while syntax validity is high (64% to 92%), structural safety is remarkably poor. In packet decoders, nearly half of first-pass generations infer accidental latches. In asynchronous FIFO controllers, over two-thirds contain severe clock-domain crossing or reset hazards that make them unusable in real hardware.
The Deterministic Verification Harness
Because LLMs operate probabilistically, prompting them repeatedly with "Make sure you avoid inferred latches" will not solve the underlying problem. It merely changes token probabilities without guaranteeing correctness.
Hardware design requires mathematical and deterministic verification. Every generated block of RTL must pass through an automated verification pipeline that combines strict static linting, synthesis rule checks, and formal property checking before any dynamic testbench is run.
+-----------------------+
| LLM-Generated Verilog |
+-----------------------+
|
v
+------------------------------------------------+
| Layer 1: Strict Static Linting (Verilator) |
| - Flags: -Wall -Werror-latch -Werror-casein... |
+------------------------------------------------+
|
[Pass]| [Fail: Latch / Incomplete Case]
| --> Automated Feedback to LLM
v
+------------------------------------------------+
| Layer 2: Synthesis Graph Checking (Yosys) |
| - proc; opt; check -assert |
+------------------------------------------------+
|
[Pass]| [Fail: Combinational Loops / Undriven]
| --> Automated Feedback to LLM
v
+------------------------------------------------+
| Layer 3: Formal Property Verification (SVA) |
| - Cover Unreachable States / Assert Deadlocks |
+------------------------------------------------+
|
v
+-----------------------+
| Verified Golden RTL |
+-----------------------+
Layer 1: Strict Verilator Lint Rules
Verilator provides one of the fastest and most rigorous static linters available for SystemVerilog. By treating lint warnings as fatal errors, you eliminate inferred latches and sensitivity list omissions immediately.
Run Verilator with the following flags against any generated RTL:
verilator --lint-only -Wall \
-Werror-LATCH \
-Werror-CASEINCOMPLETE \
-Werror-CASEOVERLAP \
-Werror-ALWCOMBORDER \
-Werror-COMBDLY \
-Werror-UNDRIVEN \
-Werror-UNUSEDSIGNAL \
-Wno-DECLFILENAME \
--sv packet_decoder.sv
Key lint flags and their protective role:
-Werror-LATCH: Halts execution if any combinational block infers a latch. This immediately catches missing assignments inif-elseandcaseconstructs.-Werror-CASEINCOMPLETE: Fails if acasestatement does not cover all possible values and lacks adefaultstatement.-Werror-CASEOVERLAP: Prevents overlapping branch conditions that synthesize into unexpected priority encoders.-Werror-ALWCOMBORDER: Flags combinational blocks where signals are read before they are assigned within the same block, exposing race conditions.-Werror-COMBDLY: Catches accidental non-blocking assignments (<=) inside combinational logic, which ruin simulation-synthesis matching.
Layer 2: Structural Synthesis Checks with Yosys
Static linting catches syntactic bugs, but synthesis elaboration verifies the structural netlist. Using open-source Yosys, you can check whether the design contains undriven nets or combinational feedback loops in less than a second.
A minimal verification script (check_rtl.ys):
# Read SystemVerilog RTL
read_verilog -sv packet_decoder.sv
# Elaborate and process FSMs
hierarchy -check -top packet_decoder
proc
opt
# Check for latches, combinational loops, and undriven wires
check -assert
If Yosys finds any undriven wire, multi-driven net, or latch, the check -assert command returns a non-zero exit code, blocking the code from proceeding to dynamic testbenches.
Layer 3: Formal Property Checks with SystemVerilog Assertions (SVA)
Linting and elaboration guarantee that the code is syntactically sound and free of latches. They do not guarantee that the state machine behaves correctly under complex sequences.
To verify state machines and protocols, teams must attach SystemVerilog Assertions (SVA) and run bounded model checking (BMC) using formal engines like SymbiYosys, Cadence JasperGold, or Synopsys VC Formal.
Here is how to formally prove that the FSM never deadlocks, never enters an invalid state, and always processes packet headers correctly:
// Formal Verification Harness for State Machine
`ifdef FORMAL
// 1. Assert: Reset always forces the state machine to IDLE
property p_reset_to_idle;
@(posedge clk) !rst_n |-> (current_state == IDLE);
endproperty
assert property (p_reset_to_idle);
// 2. Assert: No unreachable or illegal states
property p_valid_state_space;
@(posedge clk) disable iff (!rst_n)
(current_state inside {IDLE, HEADER, PAYLOAD, CHECKSUM});
endproperty
assert property (p_valid_state_space);
// 3. Assert: State machine cannot get stuck in any active state indefinitely
property p_no_payload_deadlock;
@(posedge clk) disable iff (!rst_n)
(current_state == PAYLOAD && byte_count == packet_len) |=> (next_state == CHECKSUM);
endproperty
assert property (p_no_payload_deadlock);
// 4. Cover: Prove that all states are reachable under normal operation
cover_idle: cover property (@(posedge clk) current_state == IDLE);
cover_header: cover property (@(posedge clk) current_state == HEADER);
cover_payload: cover property (@(posedge clk) current_state == PAYLOAD);
cover_checksum: cover property (@(posedge clk) current_state == CHECKSUM);
`endif
By executing bounded model checking for 20 cycles, the formal engine evaluates every possible input combination across all clock cycles. If there is any corner case where a malformed packet length causes an arithmetic overflow in byte_count and locks the state machine in PAYLOAD, the formal solver produces an exact counter-example trace in a VCD file.
Practical Checklist for RTL Generation Pipelines
If you are integrating generative models into your digital design or FPGA team workflow, put strict boundaries around the output. Never commit raw model output directly into your RTL repository.
Use this four-point operational frame:
- Enforce Two-Process or Three-Process FSM Templates: Prompt models to always use standard two-process FSM coding styles where next-state logic assigns explicit defaults at the top of the
always_combblock. - Mandate Zero-Warning Lint Sign-off: Integrate Verilator with
-Werror-LATCHand-Werror-CASEINCOMPLETEinto the generator's loop. If the model generates a latch, feed the compiler error back into the model context automatically to trigger a self-correction pass. - Verify Reachability Formally: Use bounded model checking to prove that all FSM states can be entered and exited. Never rely solely on random stimulus testbenches, which regularly miss multi-cycle edge conditions.
- Separate Architecture from Implementation: Use language models for boilerplate instantiation, register map generation, and testbench scaffolding, but keep core protocol control paths under strict formal constraints.
What this means for Silicode
Silicode treats RTL generation not as an unconstrained text-generation problem, but as a verified compilation pipeline. Instead of relying on raw prompts that produce plausible-looking Verilog riddled with inferred latches and missing reset semantics, Silicode pairs language models directly with deterministic linting engines, static synthesis checks, and formal property checkers.
By enforcing continuous compile-time verification with tools like Verilator and formal assertion solvers in the background, Silicode ensures that every generated module compiles to clean, latch-free, synthesizable silicon ready for sign-off.
Why LLMs Fail at Hardware Synthesis: The Direct Answer
Why do LLMs hallucinate latches and miss corner cases in state machine synthesis?
Because language models are autoregressive token predictors trained primarily on sequential software paradigms. They lack an internal understanding of spatial concurrency, electrical net continuity, and hardware synthesis semantics. When an LLM omits an assignment in a conditional branch, it does not realize that synthesis tools must infer a physical transparent latch to hold the signal state. Without deterministic linting and formal verification in the generation loop, these structural defects pass basic syntax checks and create silent silicon failures.
Sources
- r/FPGA discussion on LLMs in RTL development: https://www.reddit.com/r/FPGA/comments/1ukr8f6/ai_llms_will_be_a_critical_part_of_rtl_development/
- Hacker News engineering debate on LLMs for chip design: https://news.ycombinator.com/item?id=42156516
- Large Language Model for Verilog Code Generation survey: https://arxiv.org/abs/2512.00020
- Inferred Latches in Verilog Causes and Fixes: https://chipverify.ai/learn/inferred-latches-verilog
- VeriGen benchmark for hardware language models: https://dl.acm.org/doi/10.1145/3643681
- IEEE study on synthesizable Verilog code generation: https://ieeexplore.ieee.org/document/11333303/
