118  The CIRCT Dialect Ecosystem

CIRCT is not one hardware dialect. It is a collection of cooperating dialects that let a compiler preserve the right hardware abstraction at each point in a design flow. Some dialects retain the meaning of a source language, some make scheduling decisions explicit, some provide a shared vocabulary for RTL, and others carry verification, simulation, debug, or emission intent alongside the design.

That is the key to navigating CIRCT: choose a dialect for the information it preserves, not simply because it is “higher” or “lower” than another dialect. A single module can legitimately contain hw structure, comb expressions, seq registers, verif assertions, and sv emission-specific constructs at the same time.

TipThe shortest useful mental model

Intent-rich dialects describe what the designer meant. Core dialects describe the reusable hardware structure and behavior. Backend dialects describe how the result will be implemented, executed, or emitted. Cross-cutting dialects keep verification and source meaning attached throughout that journey.

118.1 The Abstraction Map

The solid arrows below show common directions of lowering. They are not the only legal paths, and not every flow visits every box. Dashed arrows show concerns that can accompany a design across several abstraction levels.

%%{init: {"theme":"base","flowchart":{"curve":"basis","htmlLabels":true},"themeVariables":{"fontFamily":"system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif","fontSize":"17px","lineColor":"#607089","primaryTextColor":"#172033"}}}%%
flowchart TB
  INTENT["<div style='width:540px'><b>1 · PRESERVE DESIGN INTENT</b><br/><br/><b>Source & language</b> · firrtl · chirrtl · moore · llhd<br/><b>Generation & scheduling</b> · calyx · handshake · pipeline · kanagawa · loopschedule · ssp</div>"]
  CORE["<div style='width:540px'><b>2 · SHARE HARDWARE ABSTRACTIONS</b><br/><br/><b>Structure</b> · hw &nbsp; <b>Logic</b> · comb · hwarith &nbsp; <b>State</b> · seq · fsm<br/><b>Dynamic control</b> · dc &nbsp; <b>System composition</b> · esi · msft · om · interop</div>"]
  TARGET["<div style='width:540px'><b>3 · IMPLEMENT, EXECUTE, OR EMIT</b><br/><br/><b>Optimize</b> · datapath · synth &nbsp; <b>Emit</b> · sv · emit · systemc<br/><b>Compile simulation</b> · arc</div>"]
  CROSS["<div style='width:540px'><b>CROSS-CUTTING AT EVERY LEVEL</b><br/><br/><b>Verify & test</b> · verif · ltl · sim · rtg<br/><b>Preserve source meaning</b> · dbg</div>"]

  INTENT --> CORE
  CORE --> TARGET
  INTENT -.-> CROSS
  CORE -.-> CROSS
  TARGET -.-> CROSS

  classDef intent fill:#e8f0ff,stroke:#4d6fdc,stroke-width:2px,color:#172033;
  classDef core fill:#e2f7ed,stroke:#168d66,stroke-width:3px,color:#172033;
  classDef target fill:#ffe8e5,stroke:#ce6259,stroke-width:2px,color:#172033;
  classDef cross fill:#f0e8ff,stroke:#7655c5,stroke-width:2px,color:#172033;
  class INTENT intent;
  class CORE core;
  class TARGET target;
  class CROSS cross;
Figure 118.1: A practical map of the CIRCT dialect ecosystem. Arrows show common relationships, not one mandatory pipeline.

The center of the map is the most reusable part of CIRCT. hw provides modules, ports, instances, hierarchy, and hardware types; comb provides pure bit-vector logic; and seq provides clocks, registers, memories, and other stateful elements. Many otherwise unrelated frontends lower into some combination of these dialects because optimizations and backends can share that common form.

The map is deliberately not a strict hierarchy. esi, for example, is a higher-level communication abstraction, but it can coexist with hw modules. verif and dbg do not sit at one fixed height at all: they carry intent alongside other IR. sv may appear next to core operations whenever a design needs a SystemVerilog construct that the generic hardware dialects should not model.

118.2 Dialect Families

118.2.1 Source And Language Semantics

These dialects retain the rules and concepts of an input language or event-driven hardware model. Use them while those source-level distinctions still matter.

  • firrtl represents Chisel/FIRRTL designs, including connects, aggregates, annotations, width inference, memories, and other FIRRTL semantics.
  • chirrtl is FIRRTL’s temporary behavioral-memory layer. It preserves memory use until concrete FIRRTL port kinds can be inferred.
  • moore represents elaborated SystemVerilog semantics. It is useful after parsing but before source-language behavior has been reduced to generic hardware.
  • llhd represents signals, time, events, processes, and drives. It is especially useful for event-driven HDL semantics and simulation lowering.

These are not interchangeable source dialects. Choose the one that matches the frontend and the semantics that must survive parsing.

118.2.2 Hardware Generation And Scheduling

These dialects preserve decisions that disappear once a design becomes plain RTL: resource allocation, concurrency, dataflow, pipeline stages, and the schedule of operations.

  • calyx separates allocated hardware resources from a control program that schedules their use. It fits control-oriented HLS and accelerator generation.
  • handshake represents dynamically scheduled dataflow in which values move through explicit handshaking operations.
  • pipeline represents unscheduled or scheduled pipelines before stage boundaries become ordinary registers.
  • kanagawa represents class- and method-oriented hardware with static scheduling concepts before lowering to core hardware.
  • loopschedule attaches pipeline schedules and stage structure to affine loops.
  • ssp serializes static scheduling problems and solutions. It is primarily a scheduling interchange and testing dialect, not a circuit representation.

Stay at this level while a compiler still needs to move operations between cycles, share resources, alter concurrency, or solve a schedule. After lowering to registers and muxes, recovering that intent is difficult.

118.2.3 Shared Hardware Core

The core dialects are the common meeting point for many CIRCT flows. They are low enough for hardware-specific optimization but still preserve useful design structure.

  • hw owns modules, ports, instances, hierarchy, parameters, names, and aggregate hardware types.
  • comb owns stateless arithmetic, comparisons, muxes, concatenation, extraction, and other combinational bit-vector logic.
  • seq owns state and time across cycles: registers, clocks, memories, FIFOs, and initialization.
  • hwarith keeps width and signedness behavior explicit while hardware arithmetic is still being normalized.
  • fsm keeps states, transitions, guards, and actions explicit before a state machine becomes registers and combinational next-state logic.
  • dc represents dynamic control using tokens and FIFO-like values, separating control movement from payload data.

The most common beginner mistake is to ask which one of these dialects a module uses. In practice they divide responsibilities. An hw.module commonly contains comb logic feeding a seq.compreg, and an fsm or dc construct may later lower into that same combination.

118.2.4 System Composition And Design Metadata

This family operates around and across compute blocks. It handles how blocks communicate, how tools describe them, and how external systems refer to them.

  • esi provides typed channels, bundles, services, buffering, host connectivity, and system manifests for hardware communication.
  • msft carries support constructs and physical-design intent used by Microsoft-oriented flows, including placement and Tcl collateral.
  • om is a typed object model for design metadata such as clocks, power domains, interfaces, paths, and software-facing descriptions.
  • interop models procedural interaction across module and simulator boundaries, especially SystemC or Verilated integration.

Use these dialects when the problem is larger than the logic inside one block: connecting accelerators, preserving physical constraints, describing the system to software, or crossing a simulation boundary.

118.2.5 Implementation, Simulation, And Output

These dialects move from logical hardware intent toward a chosen implementation, executable model, or emitted artifact.

  • datapath exposes compressor trees and partial products so arithmetic circuits can be optimized before final carry propagation.
  • synth represents synthesis-oriented Boolean networks and rewriting choices, including AIG-like logic.
  • sv represents SystemVerilog-specific surface constructs such as procedural blocks, interfaces, macros, assertions, and verbatim text.
  • emit controls files, file lists, fragments, references, and other output packaging rather than hardware behavior.
  • systemc represents modules, signals, processes, and C++ constructs for structured SystemC emission.
  • arc turns hardware state transfer into an executable, compiler-friendly simulation model used by Arcilator.

sv is usually a late representation, but it is not synonymous with “the backend.” Core operations can be exported directly alongside SV operations, and emit may independently control how the resulting files are organized.

118.2.6 Verification, Testing, And Debug

These dialects describe how to check or understand hardware rather than only how to implement it.

  • verif represents assertions, assumptions, coverage, symbolic values, formal problems, simulation tests, and contracts.
  • ltl represents temporal sequences and properties over clocked behavior.
  • sim represents simulator services such as printing, DPI, plusargs, files, queues, formatting, and simulation control.
  • rtg describes randomized test templates, target resources, sequences, and instruction-oriented test generation. It does not represent synthesizable hardware.
  • dbg maps optimized IR values back to source variables, scopes, aggregates, arrays, and types for human-facing debug views.

Verification and debug are cross-cutting. Add them at the level where their meaning is clearest, then use the appropriate lowering or preservation passes as the implementation changes underneath them.

118.3 What Can You Build With CIRCT?

CIRCT supports several distinct kinds of work:

  • Compile hardware generators and HDLs. Preserve Chisel/FIRRTL or SystemVerilog meaning, normalize it, and produce shared hardware IR or HDL.
  • Build hardware from programs. Convert control flow, loops, or dataflow into scheduled pipelines, handshaking networks, accelerators, and RTL.
  • Transform and analyze RTL. Work directly with hierarchy, logic, clocks, state, memories, names, and parameters without first reducing everything to gates or text.
  • Integrate accelerator systems. Connect blocks with typed channels and services, preserve placement intent, and generate host-visible metadata and collateral.
  • Verify and test designs. Carry assertions and temporal properties, build formal or simulation problems, and generate randomized tests.
  • Generate artifacts or executable models. Emit SystemVerilog or SystemC, organize output files, or compile a design into an efficient simulator.

No single CIRCT pipeline does all of these. The dialect set is a toolbox from which a frontend or compiler flow selects the abstractions it needs.

118.4 Common Journeys Through The Stack

The following paths are representative. Exact pass sequences and intermediate dialects vary with the frontend, chosen options, and CIRCT version.

118.4.1 Chisel Or FIRRTL To SystemVerilog

Chisel / .fir
  -> firrtl + chirrtl
  -> hw + comb + seq + verif/ltl/sim as needed
  -> sv
  -> ExportVerilog
  -> .sv files

FIRRTL is where source semantics, annotations, aggregates, and inference are resolved. The HW/Comb/Seq layer exposes reusable RTL structure. SV carries the remaining output-language constructs.

118.4.2 SystemVerilog To Core IR Or Simulation

SystemVerilog source
  -> frontend elaboration
  -> moore
  -> hw + comb + seq and/or llhd
  -> sv output or arc executable model

Moore preserves the source language after parsing and elaboration. Core dialects are better for generic hardware transformation; LLHD and Arc serve event-driven or executable simulation paths.

118.4.3 High-Level Synthesis

control flow, affine loops, or dataflow
  -> calyx / handshake / loopschedule / pipeline
  -> fsm / dc / hw + comb + seq
  -> sv
  -> hardware

Different HLS paths make different choices. Calyx emphasizes resources and an explicit control schedule. Handshake emphasizes dynamic dataflow. Pipeline and LoopSchedule preserve static stage decisions. They should not be treated as four spellings of the same abstraction.

118.4.4 System Integration

typed accelerator blocks and services
  -> esi + hw + om/msft metadata
  -> concrete channels, modules, interfaces, and placement collateral
  -> SystemVerilog + manifests + support files

The hardware output is only one product in this flow. Software APIs, manifests, placement constraints, and other collateral may be equally important.

118.4.5 Verification And Debug

design at any useful abstraction level
  + verif / ltl / sim
  + dbg source mappings
  -> SystemVerilog assertions or simulation
  -> formal/SMT checking
  -> source-correlated traces and debug information

Verification is a parallel concern, not merely the final box in a lowering pipeline.

118.5 Which Dialect Should I Reach For?

If you need to… Start with… Why
Define modules, ports, instances, and hierarchy hw It is CIRCT’s shared structural hardware vocabulary.
Express pure bit-vector logic comb It preserves combinational operations without source-language syntax.
Model registers, clocks, memories, or FIFOs seq It keeps stateful hardware explicit.
Retain Chisel/FIRRTL semantics firrtl and chirrtl They preserve FIRRTL inference, connects, annotations, and memory intent.
Retain elaborated SystemVerilog semantics moore It models the source language before generic hardware lowering.
Describe time, events, signals, and processes llhd It is designed for event-driven hardware behavior.
Allocate resources and explicitly schedule their use calyx It separates structural resources from control.
Build dynamically scheduled dataflow hardware handshake or dc They make token flow and latency-insensitive control explicit.
Assign computations to pipeline stages pipeline or loopschedule They preserve scheduling before registers are materialized.
Connect blocks with typed channels and services esi It abstracts communication protocols and system services.
Represent states and guarded transitions fsm It retains the state-machine model before RTL encoding.
Write assertions, assumptions, or coverage verif and ltl They preserve verification and temporal intent.
Optimize arithmetic or Boolean implementation datapath or synth They expose implementation-oriented structures.
Control SystemVerilog-specific output sv It models constructs that should survive to emitted SV.
Control generated files and fragments emit It describes output organization, not circuit behavior.
Produce an executable hardware model arc It represents state transfer for compiled simulation.
Preserve source-level debug views dbg It connects source variables and types to lowered values.

118.6 A Rule For Choosing The Right Level

Start with the highest-level dialect that directly represents the decisions you still want to change. Lower only when that information is no longer needed.

If you still want to reschedule an operation, keep a scheduling dialect. If you only need to optimize an adder, use comb, datapath, or synth. If the remaining concern is exact SystemVerilog spelling, use sv. Moving downward usually makes implementation details more explicit, but it also discards choices that are expensive or impossible to reconstruct later.

The chapters that follow use this map as their shared context. Read hw, comb, and seq first for the reusable core; then choose the source, scheduling, integration, verification, or backend family that matches the flow you are trying to build.

118.7 A Potential Workflow Using CIRCT

Suppose you have a small FPGA board and a trained PyTorch model. You want to turn that model into a custom accelerator: not software running on a soft CPU, but actual multipliers, adders, registers, memories, and control logic placed in the FPGA fabric.

This is possible, but it is important to be precise about what exists today. Torch-MLIR can bring a PyTorch program into MLIR and lower it to tensor-oriented dialects. CIRCT can lower suitable loop and control-flow programs through HLS dialects to SystemVerilog. The middle step—turning tensor operations into a particular scheduled, quantized, memory-aware hardware architecture—is not currently a turnkey torch-mlir-to-CIRCT pipeline. That step is the FPGA accelerator compiler you would be building.

ImportantScope: compile inference, not training

The realistic first target is a frozen inference model: model.eval(), fixed weights, static input shapes, a batch size of one, and no arbitrary Python behavior. Training requires gradients, mutable optimizer state, much more memory, and a substantially larger compiler and runtime problem.

118.7.1 The End-To-End Shape

%%{init: {"theme":"base","flowchart":{"curve":"basis","htmlLabels":true,"nodeSpacing":18,"rankSpacing":28},"themeVariables":{"fontFamily":"system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif","fontSize":"15px","lineColor":"#607089","primaryTextColor":"#172033"}}}%%
flowchart TB
  subgraph TOP[" "]
    direction LR
    MODEL["<b>1 · PyTorch</b><br/>frozen, static,<br/>quantized model"]
    TORCH["<b>2 · Torch-MLIR</b><br/>Linalg / TOSA /<br/>StableHLO"]
    MAP["<b>3 · FPGA mapping</b><br/>precision, tiling,<br/>memory, parallelism"]
    LOOPS["<b>4 · Loop IR</b><br/>scf/cf + arith<br/>+ memref"]
    MODEL --> TORCH --> MAP --> LOOPS
  end

  subgraph BOTTOM[" "]
    direction RL
    HLS["<b>5 · CIRCT HLS</b><br/>Calyx or<br/>Handshake/DC"]
    RTL["<b>6 · CIRCT RTL</b><br/>hw + comb + seq<br/>+ fsm + esi → sv"]
    FPGA["<b>7 · FPGA tools</b><br/>synthesize, place,<br/>route, bitstream"]
    HLS --> RTL --> FPGA
  end

  LOOPS --> HLS

  classDef model fill:#e8f0ff,stroke:#4d6fdc,stroke-width:2px,color:#172033;
  classDef bridge fill:#fff2d8,stroke:#d88a10,stroke-width:3px,color:#172033;
  classDef circt fill:#e2f7ed,stroke:#168d66,stroke-width:2px,color:#172033;
  classDef output fill:#ffe8e5,stroke:#ce6259,stroke-width:2px,color:#172033;
  class MODEL,TORCH model;
  class MAP bridge;
  class LOOPS,HLS,RTL circt;
  class FPGA output;
  style TOP fill:transparent,stroke:transparent
  style BOTTOM fill:transparent,stroke:transparent
Figure 118.2: A potential PyTorch-to-FPGA flow. The orange box is the custom accelerator-mapping bridge that a complete toolchain must supply.

The arrows are abstraction changes, not file-format conversions. Each one commits to decisions that become harder to reverse later. In particular, the FPGA architecture mapping decides whether a mathematical operation becomes one large parallel circuit, a small circuit reused over many cycles, or something between those extremes.

118.7.2 A Concrete First Model

A good first experiment is deliberately small:

import torch
from torch import nn

class TinyMLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(16, 8),
            nn.ReLU(),
            nn.Linear(8, 4),
        )

    def forward(self, x):
        return self.layers(x)

Compile this for a fixed input such as tensor<1x16xi8>, not an arbitrary ranked tensor. Quantize weights and activations to eight-bit integers, use a wider accumulator such as 24 or 32 bits, and requantize between layers. The exact accumulator width must be derived from the operand ranges and the number of summed products; simply keeping every intermediate at eight bits would overflow.

This model is small enough to simulate exhaustively with many inputs, yet it contains the central problems of neural-network hardware: matrix multiplication, bias addition, activation, precision conversion, weights, and resource scheduling.

118.7.3 Stage 1: Capture And Normalize PyTorch

The model first needs to become a closed graph. A PyTorch export or FX-based frontend captures tensor operations and constants. Torch-MLIR then represents the program in the torch dialect and normalizes it to its backend contract.

Torch-MLIR currently documents three principal output families:

  • Linalg-on-Tensors, plus tensor and arith;
  • TOSA, a compact tensor operator set that is useful for inference and quantized operations;
  • StableHLO, a portable high-level tensor computation representation.

For this experiment, Linalg-on-Tensors is the shortest route toward loops. The two linear layers become matrix multiplication and elementwise operations; ReLU becomes a maximum or select operation. Schematically, the important part looks like this:

tensor<1x16xi8> input
  -> linalg.matmul with tensor<16x8xi8> weights
  -> add tensor<8xi32> bias
  -> elementwise max(value, 0)
  -> requantize
  -> linalg.matmul with tensor<8x4xi8> weights
  -> add tensor<4xi32> bias
  -> tensor<1x4xi8> result

At this level, the compiler knows the computation but does not yet know how many FPGA multipliers to create or where any tensor is stored.

118.7.4 Stage 2: Quantize And Make Shapes Static

Hardware needs finite widths and finite storage. Before HLS, the pipeline must:

  1. Resolve every tensor dimension needed by the accelerator.
  2. Freeze weights as constants or external memory images.
  3. Choose signed integer or fixed-point types for inputs, weights, accumulators, and outputs.
  4. Insert explicit rescaling, rounding, saturation, and clamping operations.
  5. Verify the quantized MLIR result against the quantized PyTorch model.

Quantization is not merely an optimization. It changes numerical behavior. Every lowering stage should therefore be checked against the same reference inputs with a declared tolerance or, where possible, bit-exact expected outputs.

For a first design, store the weights on chip. A tiny model can use constants, distributed ROM, or block RAM. A large model introduces an external-memory controller and a bandwidth problem that can dominate the arithmetic.

118.7.5 Stage 3: Choose The Accelerator Architecture

Consider the first 16 × 8 linear layer. It needs 128 multiply operations per inference. The tensor IR does not say how those multiplications occupy space and time. Some possible implementations are:

Architecture Multiplier hardware Approximate schedule Tradeoff
Fully parallel 128 multipliers All products begin together Highest throughput and largest area
Eight MAC lanes 8 multipliers Reuse each lane across input elements A useful small-FPGA compromise
One MAC lane 1 multiplier Serialize nearly all products Smallest area and longest latency

The exact cycle counts also depend on multiplier latency, accumulation structure, memory ports, and pipelining. The table expresses architecture, not a timing guarantee.

A compiler pass or FPGA-specific MLIR dialect must record decisions such as:

  • tile the matrix operation into groups that fit the chosen number of lanes;
  • unroll only the loops that should become parallel hardware;
  • pipeline the multiply-accumulate loop;
  • map arrays to registers, ROMs, or block RAMs with realistic port counts;
  • decide whether layers stream directly into one another or materialize an activation buffer;
  • expose a start/done interface or a ready/valid streaming interface;
  • attach latency and resource information required by scheduling.

This is the creative center of the compiler. Lowering tensor syntax without making these choices may produce loops, but it does not produce a useful FPGA architecture.

118.7.6 Stage 4: Lower Tensors To A Hardware-Ready Program

After fusion, tiling, and architecture selection, upstream MLIR transformations can bufferize tensors and lower structured computation into explicit loops, loads, stores, arithmetic, and control flow. The desired boundary for CIRCT HLS looks roughly like:

func.func @accelerator(...)
  scf.for / affine.for       statically bounded iteration
  arith.muli / arith.addi    fixed-width arithmetic
  memref.load / store        explicit activation and weight storage
  scf.if / cf.cond_br        explicit control
  func.return                accelerator result

This boundary is where the two repositories can meet. Torch-MLIR produces the tensor computation; upstream MLIR removes the remaining tensor abstraction; CIRCT receives a program made from operations its HLS conversions understand.

The bridge still needs legalization. Not every upstream operation is accepted by every CIRCT HLS path. Unsupported math functions may need lookup tables or custom hardware operators, dynamic allocations must disappear, and memory layouts must match the capabilities of the selected lowering.

118.7.7 Stage 5: Select A CIRCT HLS Route

There are two especially useful routes for this example.

118.7.7.1 Static Scheduling Through Calyx

func + scf + arith + memref
  -> calyx components, cells, groups, and control
  -> fsm control
  -> hw + comb + seq
  -> sv
  -> SystemVerilog

Use calyx when the accelerator should explicitly allocate resources and schedule their reuse. A MAC unit can be a cell; groups describe the assignments for one action; Calyx control determines which actions run in sequence, in parallel, or in a loop. This is a natural fit for the eight-lane or one-lane implementations above.

CIRCT’s hlstool contains a Calyx flow that lowers SCF to Calyx, compiles Calyx control through fsm, lowers to hw, and then converts the result toward sv.

118.7.7.2 Dynamic Scheduling Through Handshake

func + scf/cf + arith + memref
  -> handshake dataflow
  -> optional dc control network
  -> hw + comb + seq
  -> sv
  -> SystemVerilog

Use handshake when operations should communicate through latency-insensitive channels. Forks, joins, merges, buffers, and memory interfaces make the availability of data and control explicit. This route is attractive for streaming accelerators and computations whose blocks may have variable or decoupled latency.

The official CIRCT HLS documentation describes hlstool and points to integration tests that convert CF-level programs to Verilog. The CIRCT repository includes a matrix multiplication Handshake test that starts with func, cf, arith, and memref, invokes hlstool --dynamic-hw --verilog, and verifies the emitted design with cocotb. That does not make PyTorch-to-FPGA automatic, but it demonstrates that the lower half of this proposed pipeline is real and executable.

WarningCIRCT HLS maturity

CIRCT describes these HLS flows as actively developed research flows rather than production-ready tools. Expect unsupported operations, changing pass pipelines, and cases where a new legalization or lowering must be implemented. That makes this workflow suitable for learning and compiler development, but not yet a drop-in replacement for a vendor HLS product.

118.7.8 Stage 6: Build The RTL And Its Interface

After HLS, the dialect responsibilities become concrete:

  • hw defines the accelerator module, ports, instances, and hierarchy.
  • comb represents adders, multipliers, comparisons, muxes, extracts, and other combinational logic.
  • seq represents pipeline registers, state, clocks, and memories.
  • fsm can preserve controller states before they become RTL.
  • esi can describe typed streaming channels and system services before they become concrete ports and buffers.
  • sv carries SystemVerilog-specific constructs needed for readable emission.

The neural-network datapath is not a complete FPGA design by itself. A usable top level also needs:

  • clock and reset handling;
  • input and output handshaking;
  • a way to load inputs and read predictions;
  • memory initialization or a weight-loading interface;
  • optional AXI, Avalon, FIFO, UART, or board-specific wrappers;
  • constraints describing clock frequency and physical pins.

For the first experiment, a ready/valid input stream and output stream are much simpler than a full external-memory subsystem. Inputs can be sent from a test bench or a small host controller, and all weights can remain on chip.

118.7.9 Stage 7: Verify At Every Boundary

Do not wait for a bitstream to discover a numerical or control bug. Keep a reference result and compare after every major lowering:

Checkpoint What to compare
PyTorch versus quantized PyTorch Accuracy loss introduced by quantization
Quantized PyTorch versus tensor MLIR Imported operations, shapes, constants, and rounding
Tensor MLIR versus loop-level MLIR Bufferization, tiling, indexing, and fixed-width arithmetic
Loop-level MLIR versus Calyx/Handshake simulation Scheduling, memory accesses, and channel behavior
CIRCT RTL versus previous stage Cycle-level protocol and bit-exact outputs
Post-synthesis netlist versus RTL Reset behavior, initialization, and synthesis assumptions
FPGA versus test vectors Board interface, timing, and real data movement

Useful tools include MLIR interpreters or reference backends at the tensor level, handshake-runner for suitable Handshake IR, cocotb or Verilator for RTL simulation, and verif or ltl for properties such as “an accepted input eventually produces exactly one output.”

118.7.10 Stage 8: Produce The Bitstream

Exported SystemVerilog is synthesizable source code, not yet an FPGA image. A board toolchain must still perform:

SystemVerilog
  -> RTL synthesis
  -> mapping to LUTs, flip-flops, DSPs, and block RAMs
  -> placement and routing
  -> static timing analysis
  -> bitstream generation
  -> program the FPGA

AMD/Xilinx designs commonly use Vivado, Intel/Altera designs use Quartus, and some FPGA families are supported by open-source flows built around Yosys and nextpnr. This stage determines whether the design actually fits and meets its clock constraint. If it does not, the useful feedback travels upward: reduce parallelism, change bit widths, add pipeline stages, or revise memory banking.

118.7.11 Evidence That The Larger Idea Works

The exact torch-mlir-to-CIRCT bridge proposed here is a compiler project, but PyTorch-to-FPGA compilation is not hypothetical.

  • hls4ml converts supported PyTorch and other ML models into configurable HLS projects for FPGA toolchains.
  • FINN’s end-to-end flow starts from a trained PyTorch/Brevitas quantized network and can produce a running dataflow accelerator, including bitstreams and drivers for supported AMD/Xilinx boards.
  • Torch-MLIR already supplies the PyTorch-to-tensor-MLIR frontend, while CIRCT’s HLS integration tests supply examples of loop/control-flow-to-Verilog lowering. The work needed here is to connect those capabilities with a hardware-aware mapping layer.

These systems also demonstrate an important lesson: successful FPGA compilers do not lower an arbitrary model blindly. They constrain supported operators, make shapes and precision explicit, expose architecture knobs, and repeatedly measure accuracy, resource use, latency, throughput, and timing.

118.7.12 A Practical First Milestone

A credible first success is not “compile every PyTorch model.” It is:

  1. Support the TinyMLP above with a batch size of one.
  2. Use eight-bit inputs and weights with a proven accumulator width.
  3. Keep weights and activations on chip.
  4. Implement one resource schedule, such as eight reusable MAC lanes.
  5. Lower through Calyx or Handshake to SystemVerilog.
  6. Prove bit-exact agreement in simulation for hundreds of test vectors.
  7. Synthesize for one named FPGA and report LUT, register, DSP, and block-RAM use together with maximum clock frequency.
  8. Run the same vectors on the programmed board.

Once that path works, expand one dimension at a time: add convolution, stream layers together, introduce external memory, support more quantization schemes, or explore different scheduling strategies. That incremental path turns the potential workflow into a real compiler without hiding where the difficult hardware decisions occur.