A VHDL / Verilog compiler written from scratch in Rust, with no foreign code and no third-party crates.
Reticle is intended to grow into one self-contained hardware toolchain:
- Frontends for Verilog-2005 / synthesisable SystemVerilog and VHDL-2008, both lowering to a single intermediate representation so mixed-language designs need no special handling.
- Simulation with an event-driven 4-state simulator, VCD / FST waveforms,
and a Rust co-simulation API for writing testbenches as
#[test]s. - Synthesis with flip-flop, memory and FSM inference, an AIG-based logic optimiser, and LUT and standard-cell mapping.
- Targets for FPGA (device databases, placement constraints, an own placer and router for open families, interop with nextpnr and vendor tools) and ASIC (Liberty, LEF / DEF, static timing analysis).
- Verification through bounded model checking and equivalence checking on an in-crate SAT solver.
- IP integration: a manifest format for third-party IP, bus interface abstractions (AXI, Wishbone, APB), black-box handling for encrypted vendor cores, a static registry index, an IP-XACT importer, and a first-party IP library tested through the simulator.
The full plan, ordered by dependency and with a definition of done for each phase, is in ROADMAP.md.
Early, but the middle of the pipeline runs end to end. What works today:
| Stage | State |
|---|---|
| Verilog / SystemVerilog | preprocessor, lexer, parser, 28-rule linter, elaboration and lowering to the IR |
| VHDL-2008 | lexer, parser, semantic analysis, elaboration and lowering to the IR, with std, std_logic_1164, numeric_std, numeric_bit, math_real and the Synopsys packages bundled |
| Unified IR | design model, validator, round-tripping .rtl text format |
| Simulation | event-driven 4-state simulator, VCD and FST waveforms, concurrent assertions over an SVA and PSL subset, line and toggle coverage, an interactive session, Rust co-simulation API |
| Synthesis | process lowering, flip-flop / latch / memory / FSM inference, optimisation passes, AIG optimiser, LUT and standard-cell mapping, post-synthesis equivalence checking |
| Emission | Verilog, VHDL, Yosys JSON, BLIF, EDIF |
| Formal | CDCL SAT solver, bit-blaster, bounded model checking, k-induction, equivalence checking |
| ASIC | Liberty, LEF and DEF readers and writers, standard-cell mapping with flip-flop matching, SDC output and an OpenROAD hand-off |
| Timing | static timing analysis with setup and hold, path reports, clock domain crossing checks |
| IP | manifests with dependency resolution and a lock file, bus interfaces, generated interconnect, encrypted-core black boxes, a static registry index, IP-XACT import |
| Tooling | Verilog and VHDL formatters, a language server for both, an incremental build cache, an HTML schematic and reference viewer |
| Embedding | C ABI for use from another tool, and a WebAssembly build with a browser playground under web/ |
| FPGA | device database (iCE40, ECP5, Xilinx 7 series, generic), primitive mapping, placement and IO constraints, nextpnr and vendor export |
Both languages go all the way through, from source to a synthesised netlist, a simulation or a proof.
reticle build --synth reticle.proj
reticle search --index registry/ fifo
reticle add --index registry/ uart_lite ^1.2.0
reticle check counter.v counter.vhd design.rtl
reticle fmt --write counter.v
reticle synth --report --lut 4 --output netlist.rtl counter.vhd
reticle emit --format verilog netlist.rtl
reticle sim --vcd waves.vcd --coverage cov.info testbench.v counter.v
reticle sim --interactive testbench.v counter.v
reticle verify --depth 20 --trace cex.vcd design.rtl
reticle fpga --device ice40-hx1k-tq144 --constraints pins.rcf blinky.v
reticle asic --liberty cells.lib --verilog netlist.v counter.v
reticle timing --constraints clocks.rcf design.v
reticle timing --cdc design.v
reticle cache --top top --output design.rtl leaf.v mid.v top.v
reticle viewer --synth --output-dir docs/design counter.v
reticle lsp # started by an editor, speaks the Language Server Protocolreticle fpga runs the whole target flow and writes the netlist and
constraints the next tool reads, checking first that every cell is a
primitive the device actually has. Which tool that is depends on the
family: nextpnr-ice40 and nextpnr-ecp5 read a JSON netlist and a
.pcf / .lpf, and the Xilinx 7 series takes structural Verilog, an
.xdc and a Vivado script instead.
# A Digilent Basys 3 (Artix-7 XC7A35T): three files and the command
# that turns them into a bitstream.
reticle fpga --device xc7a35t-cpg236 --constraints board/basys3.rcf blinky.v
vivado -mode batch -source blinky.tclReticle can also write the 7-series bitstream itself, from Project
X-Ray's chip database (public domain). The first run downloads a pinned,
hash-checked copy into ~/.cache/reticle; reticle fetch does it ahead
of time, and --chipdb points at a copy of your own:
reticle fpga --device xc7a35t-cpg236 \
--constraints examples/basys3/sw_led.rcf \
--bitstream sw_led.bit examples/basys3/sw_led.vTwo designs built that way have reached a real Basys 3: sw_led, which a
person watched follow two switches, and blink.v, a 100 MHz pad clock
through a global buffer into twenty-six flip-flops, which a person
watched blink at the rate it was written for. A carry chain still does
not route.
docs/fpga-xray.md sets out exactly what is and is
not established, and docs/fpga.md says the same for the rest of the
7-series support.
A second real fabric is read the same way, Gowin's, from Project
Apicula's prebuilt chip database: src/fpga/devices/gowin.dev describes
the GW2A-18 of a Sipeed Tang Primer 20K and fpga::apicula turns that
database into the same architecture, and the same place and route write
a .fs for it. One design has run on that board and been watched
working: a button through a lookup table to an LED
(examples/primer20k). Nothing clocked routes on it yet;
docs/fpga-gowin.md says exactly what is and is
not established.
cargo build --features cli,apicula,program
reticle fpga --device gw2a-18-pg256 \
--constraints examples/primer20k/key_led.rcf \
--bitstream key_led.fs examples/primer20k/key_led.v
reticle program key_led.fsreticle program puts a bitstream into an attached Xilinx 7-series or
Gowin GW2A part over JTAG, through the FTDI adapter a Digilent Basys 3 or
a Tang Primer 20K dock carries on board, with no vendor tool and nothing
else installed:
cargo build --features cli,program
reticle program --probe # read IDCODE and status, write nothing
reticle program design.bit # or design.fsIt writes the part's volatile configuration memory only; a power cycle
undoes it, and there is no flash programming in the crate at all. The
FTDI MPSSE encoding, the IEEE 1149.1 TAP state machine and the UG470
configuration sequence are pure library code in src/program with no
I/O and no unsafe, tested against a model TAP rather than against a
board; only the transport touches a device. It is behind the program
feature, off by default, because it is the one feature with a dependency
(rawusb, a sibling Karpeles Lab crate with none of its own), so
cargo add reticle still resolves to nothing.
It was proved first on a Basys 3, loaded with Project X-Ray's own
Vivado-built harness bitstream, which asserts DONE. docs/programming.md says exactly
what that does and does not establish.
There is a second transport, for boards with no FTDI part on them: a
Great Scott Gadgets Cynthion reaches its Lattice ECP5 through a debug
microcontroller running Apollo firmware, over USB control requests.
docs/apollo-protocol.md is Reticle's own
specification of that wire protocol, written before the implementation
and the thing the implementation was written from, with the provenance
and confidence of every fact and a section on what the board later
contradicted. The two transports share jtag::Plan and nothing below it,
because an MPSSE is a shift engine told about TMS and Apollo is a TAP
controller told about states.
Over that transport the Cynthion's LFE5U-12F has taken a bitstream this
project compiled from Verilog and asserted DONE — six output pads tied
to a constant, constrained to the board's six FPGA LEDs, built from
Project Trellis' own database. The first time, the board's owner looked and
the LEDs were dark: a bank's BANK.VCCIO setting, which lives in a
tile no pad owns, was not being written. That bit is written now and
checked against what Lattice's own packer wrote for the same board, and
with the corrected bitstream in the part the owner reported all six LEDs
on. DONE had been high both times, which is the lesson: it says a
bitstream was accepted, not that it configures what the design asked for.
Only a person looking told the two runs apart.
On 2026-09-26 the backend gained interconnect, and a design with
something to route was built and loaded into the same part:
testdata/fpga/cynthion/button_led.v, the board's USER button through
about thirty rows of the die and a lookup table to two of its LEDs. The
part accepted it and asserted DONE; whether the LEDs follow the button
has not been watched yet, and saying so is the point.
docs/fpga-trellis.md says what was checked
instead — every bit of the bitstream decoded back through Project Trellis'
own database and named, and the pads compared against bitstreams Lattice's
own packer wrote for this very board — and what is still missing, which is
the clock network: globals.json is not read, so nothing sequential can be
placed.
Sources of one language are elaborated together, so a testbench and the
modules it instantiates go on one command line. A design already in the
.rtl IR text format is accepted anywhere a source file is.
Three complete systems are built out of the IP library in examples/,
each with a project manifest, a little user HDL and a test that drives it
from the manifest to the files nextpnr reads:
examples/soc— a RISC-V system on chip aroundip/rv32iandip/uartthat prints a line over a serial port.examples/mos6502_computer— the same system aroundip/mos6502: one bus, RAM at the bottom for zero page and the stack, ROM at the top for the vectors.examples/apple2— an Apple II-compatible machine with 48 KiB, the interleaved text page at$0400and video throughip/dvi_txon an ECP5 orip/vga_outon a Digilent Basys 3, running a monitor ROM and a character generator written for the example. Its tests decode the 40 x 24 character screen back out of a whole frame of video — once off the DVI colour bus and once off the VGA pins.examples/nes— an NES-compatible console aroundip/mos6502withDECIMAL_MODE = 0, which is what the processor in an NES is, plusip/ppu2c02andip/dvi_txon an ECP5 orip/vga_outon a Digilent Basys 3. It runs a demo written for the example and contains no part of any commercial cartridge; the test compares every one of a frame's 61,440 pixels against a frame buffer computed from the documentation, and a second one reads a whole 640 x 480 frame back off the VGA pins with the palette truncated to the board's four bits a channel.
Everything in examples/ was written here, which is a weak test of a
compiler. docs/vhdl-corpus.md points the VHDL
front end at a real, independent library instead — CERN's
Colibri, 103 sources of
production gateware with its own self-checking testbenches — and reports
what happens, which today is: all 231 of its files parse, 77 of its 103
design sources analyse without an error, 20 of its 90 entities elaborate,
and two of them (the 8b/10b codec) have been simulated. None of its
testbenches run, because every one of them needs VUnit, UVVM or OSVVM. The
exercise found eighteen defects in Reticle, all fixed, each pinned by a
test that needs no corpus; that page lists them and the VHDL that is still
unsupported. One clone and one environment variable are enough to run it
yourself.
docs/writing-a-cpu.md is the guide behind the
processors in them: how to package a processor as IP, from the manifest
and the bus contract to testing a core so the test cannot agree with a
wrong core, cycle accuracy, interrupts and reset, reproducing documented
quirks, and what the two cores cost on real parts. The machinery itself
is in docs/ip.md and the blocks in
docs/ip-library.md.
cargo build --release
cargo test
./target/release/reticle --helpEvery stage is a Cargo feature (verilog, vhdl, sim, synth, fpga,
asic, formal, lsp, cache, cli); the default set is the frontends,
simulator, synthesis and the CLI. See Cargo.toml.
asic, formal, lsp, viewer, cli); the default set is the
frontends, simulator, synthesis and the CLI. See Cargo.toml.
- One IR. Everything after elaboration works on the same data structure.
- Precise diagnostics. Every IR object carries a span back to source, and errors are rendered rustc-style with excerpts.
- No foreign code. Hand-written lexers, parsers, simulator, SAT solver and codecs, so the toolchain is auditable and builds anywhere Rust does, including WebAssembly.
- Library first. The CLI is a thin wrapper; testbenches, generators and build scripts use the crate directly.
- Sans-I/O core. The library never touches the filesystem.
MIT, see LICENSE.