SpireHDL is a hardware description language for digital circuits.
Project description
A modern Python HDL that compiles concise, composable hardware descriptions to synthesizable Verilog and AIG/AAG netlists — with synthesis optimization and a cycle-accurate simulator built in.
- Designed for humans and agents to be effective: a small surface that reads well to engineers and LLMs alike
- Reduces area and delay vs. a traditional Verilog flow: optimization is part of the compile, not an afterthought
- Integrated with ABC and mockturtle: modern synthesis optimization wired directly into the compilation pipeline
- Arithmetic library with automated replacement: swap adders, multipliers, and FP cores driven by an objective
- Cycle-accurate Python simulator" drive inputs, tick clocks, inspect expressions/outputs without leaving Python
- Content-addressed optimization cache: instant re-runs via
@flowy_optimized/@abc_optimizeddecorators
Optimizations built in ⚡
SpireHDL supports source-embedded optimization intent: the designer marks what to optimize (e.g. a module, FSM, or arithmetic block) directly in the HDL source, and the compiler realizes it through synthesis-aware passes.
These optimization layers run inside the compile pipeline, so emitted Verilog is already small and fast before external tools see it. The numbers below are measured against a plain Yosys flow on the same RTL.
🔢 Arithmetic auto-replacement: replace_arithmetic_ops
Drops in topology-tuned adders, multipliers, and MAC fusions against an area / delay / adp objective. On an 8-bit ALU (add + sub + mul):
- −51% transistors with the
areaobjective - 4.2× shorter critical path with the
delayobjective - balanced
adpgets near-minimal area and near-minimal depth at once
MAC patterns (a*b + c) are fused into single column-reduction units, eliminating a full adder stage. See README_arithmetic_optimization.md.
🧠 ABC + mockturtle decorators: @abc_optimized / @flowy_optimized
One decorator stacks modern AIG synthesis (resyn2, &deepsyn, mockturtle) onto any Module or Component, with a content-addressed cache for instant re-runs:
- −69% AIG gates on an 8-bit multiplier (
resyn2) - −83% on a 16-bit multiplier
- stack with
@arithmetic_optimizedfor compounding wins — ABC cleans up after the arithmetic rewriter
See README_optimization_decorators.md.
🎯 FSM + encoding search: optimized_fsm / optimized_encoding
Hopcroft state minimisation and bit-assignment search as two composable context managers. On the canonical 7-state case10 Moore FSM:
- −19% cells with
optimized_encodingalone - −44% cells with
optimized_fsmalone - −69.5% cells when both are nested — a ~3× reduction with two
withblocks, no hand-tuned encoding tables
An 8-opcode CPU decoder sees −66.7% cells from a single optimized_encoding, because the search discovers an opcode layout where each wide OR collapses to one bit-test. See README_fsm_optimization.md.
🛠️ Fine-grained architecture selection: arithmetic_generator
Beyond the automatic passes above, the unified arithmetic generator lets you hand-pick the exact micro-architecture of an adder, multiplier, MAC, or matmul (partial-product generation, compression-tree topology, and final-stage adder), then emit Verilog/AAG, simulate, and collect Yosys metrics for direct comparison. Use it to explore the design space at full granularity when you want to drive the architecture choice yourself rather than leaving it to the objective-driven replacer. See README_arithmetic_generator.md.
Overview
🪶 Minimal core
In its simplest form, SpireHDL only needs these core files. This is intentional — the HDL is kept to a minimal, self-contained core, and higher-level features are layered on top:
spirehdl/spirehdl.py– the expression DSL. It provides bit-precise types such asBool,UInt, andSInt, shared-expression caching, and the overloaded arithmetic / bitwise operators that make the Python syntax feel like an HDL.spirehdl/spirehdl_module.py– structural modeling helpers. TheModuleclass constructs ports, wires, and registers, produces Verilog, and exposes analysis utilities. TheComponentbase class lets you package reusable sub-designs and convert them to or from SpireHDL modules.IOCollectorcan rebuild packed ports from bit-level signals when importing external netlists.spirehdl/spirehdl_simulator.py– a lightweight simulator that can drive inputs, tick clocks, inspect outputs or internal expressions, and capture probes for debugging—all without leaving Python.
📚 Further reading
Other markdown documents in this repository:
README_arithmetic_generator.md— arithmetic generators, evaluation scripts, and extra tooling notesREADME_arithmetic_optimization.md— automatic arithmetic replacement withreplace_arithmetic_ops(adders, multipliers, subtractors)README_optimization_decorators.md— the@abc_optimized/@flowy_optimizedcircuit optimization decoratorsREADME_state_machines.md— finite-state-machine declaration with theState/EncodingAPI andswitch_/case_bodiesREADME_fsm_optimization.md— theoptimized_fsmandoptimized_encodingcontext managers (Hopcroft state minimisation + bit-assignment search)README_memories.md— theMemoryprimitive (FIFOs, ROMs, RAMs), port wiring with<<=, simulation, and reading current memory stateREADME_custom_verilog.md— emit a hand-written Verilog block from aComponentviacustom_verilog(), with or without a Python sim model (blackbox)testing/examples/README.md— example designs exercising SpireHDL features
Installation
git clone https://github.com/huawei-csl/spire-hdl.git
cd spire-hdl
pip install -e .
The library relies on the packages listed in requirements.txt. Optional regression tests require Yosys/Pyosys and aigverse if you plan to exercise the external tooling integration flows.
Quick start
1. Describe a module
from spirehdl.spirehdl_module import Module
from spirehdl.spirehdl import Bool, UInt, mux, cat
m = Module("LogicDemo", with_clock=False, with_reset=False)
a = m.input(UInt(8), "a")
b = m.input(UInt(8), "b")
sel = m.input(Bool(), "sel")
sum_ = m.output(UInt(9), "sum")
mask = m.output(UInt(4), "mask")
out = m.output(UInt(8), "out")
sum_ <<= a + b # automatic width growth
top_bits = cat(a[7], b[7])
mask <<= top_bits # concatenate slices
a_and_b = a & b
b_or_a = a | b
out <<= mux(sel, a_and_b, b_or_a)
print(m.to_verilog())
The Module API checks that every output has a driver and every register has a next-state assignment before emitting Verilog (see spirehdl_module.py).
Registers are created either via the standalone Register class or Module.reg(...). Both take a typ and an optional reset value via the init= keyword (note: the keyword is init, not reset_value / reset). Assign the next-state expression with <<=:
from spirehdl.spirehdl import Register, UInt
m = Module("Counter", with_clock=True, with_reset=True)
cnt = Register(UInt(8), init=0, name="cnt") # or: cnt = m.reg(UInt(8), "cnt", init=0)
cnt <<= cnt + 1 # next-state = cnt + 1
m.output(UInt(8), "q") <<= cnt
2. Simulate the design
from spirehdl.spirehdl_simulator import Simulator
sim = Simulator(m)
sim.set("a", 0x55).set("b", 0x0F).set("sel", 1)
sim.eval() # recompute combinational logic
print(sim.peek_outputs()) # {'sum': 0x64, 'mask': 0x9, 'out': 0x05}
The simulator keeps track of inputs, wires, outputs, and registers, supports eval() for combinational updates, step() for clocked designs, and exposes helpers such as peek, peek_next, and signal watching for deeper inspection (spirehdl_simulator.py).
3. Integrate with external tooling
Modules can be exported to Verilog, AIG, or AAG for downstream synthesis, equivalence checking, or integration into larger verification environments. Import helpers then let you bring optimized or third-party netlists back into SpireHDL for continued composition and simulation (see spirehdl_module.py and multipliers_ext_optimized.py).
Modules and components in detail
Componentsubclasses package reusable structures. They can materialize new modules (to_module), import designs from Verilog or AIG formats (from_verilog,from_aag_lines), and retag ports as internals (make_internal). Components also exposeget_spec()to driveIOCollectorregrouping when you import flattened designs (seespirehdl_module.py).Moduleis typically used at the top level or as an intermediate representation while you are still wiring a design. It offers constructors for inputs, outputs, wires, and registers; utilities for enumerating signals; Verilog emission with automatic width fitting; and amodule_analyze()routine that reports combinational depth and node counts for timing exploration (spirehdl_module.py).IOCollectorhelps rebuild packed buses (e.g.,a[0] … a[N-1]→a[N-1:0]) after reading back designs from AIG/AAG files or external synthesizers (spirehdl_module.py).- Minimal end-to-end component example:
testing/examples/simple_component.py.
Short component + hierarchy usage example:
from dataclasses import dataclass
from spirehdl.spirehdl import UInt, Signal
from spirehdl.spirehdl_module import Component
class SimpleAdder(Component):
def __init__(self, width=8):
self.width = width
@dataclass
class IO:
a: Signal
b: Signal
sum: Signal
self.io = IO(
a=Signal(name="a", typ=UInt(width), kind="input"),
b=Signal(name="b", typ=UInt(width), kind="input"),
sum=Signal(name="sum", typ=UInt(width + 1), kind="output"),
)
self.elaborate()
def elaborate(self):
self.io.sum <<= self.io.a + self.io.b
class Sum3Hierarchical(Component):
def __init__(self):
@dataclass
class IO:
a: Signal
b: Signal
c: Signal
sum: Signal
self.io = IO(
a=Signal(name="a", typ=UInt(8), kind="input"),
b=Signal(name="b", typ=UInt(8), kind="input"),
c=Signal(name="c", typ=UInt(8), kind="input"),
sum=Signal(name="sum", typ=UInt(10), kind="output"),
)
self.elaborate()
def elaborate(self):
add_ab = SimpleAdder(width=8).make_internal() # first sub-component
add_abc = SimpleAdder(width=9).make_internal() # second sub-component
add_ab.io.a <<= self.io.a
add_ab.io.b <<= self.io.b
add_abc.io.a <<= add_ab.io.sum
add_abc.io.b <<= self.io.c
self.io.sum <<= add_abc.io.sum
module = Sum3Hierarchical().to_module(name="Sum3Hier")
print(module.to_verilog()) # one top module, built from internal components
Hierarchical design with components
Components are ideal for assembling hierarchical designs: they let you instantiate another component, adapt its IO, and even swap in a pre-synthesized netlist without leaving Python. One common pattern wraps a reusable building block with make_internal() so that auxiliary logic can surround the core implementation while exposing a compact public interface (see mutipliers_ext.py). A related flow imports an external AIG module, converts it into a Component, and calls from_module(..., make_internal=True) so the imported logic behaves like a native SpireHDL block inside a larger generator (multipliers_ext_optimized.py). These techniques extend to Verilog importers and make it straightforward to mix SpireHDL-authored code with IP produced by external flows.
Aggregate data types
SpireHDL includes structured, bit-packable aggregates for cleaner interfaces and bulk assignments (aggregate/). See README_aggregate_types.md for the full reference with an example for every type:
HDLAggregatedefines the base “pack to bits” API that powers all aggregates (hdl_aggregate.py).Arrayoffers N-dimensional indexing, packed assignment (<<=), and element-wise assignment (@=) for nested vectors or aggregates (aggregate_array.py).AggregateRecordlets you declare bundle-like classes with named fields that remain packable to a flat bitvector (aggregate_record.py).AggregateRecordDynamicis the dataclass-friendly variant whose fields are defined per-instance, ideal for parameterized IO records (aggregate_record_dynamic.py).FixedPointwraps aWireor view with explicit total/frac widths and quantization helpers, keeping arithmetic readable while staying hardware-friendly (aggregate_fixed_point.py).FloatingPointprovides an IEEE-style view withadd/mulhelpers parameterized by exponent / fraction widths (aggregate_floating_point.py).AggregateRegisterstores any aggregate in a single register while preserving a structured view via.value/.Q(aggregate_register.py).
Example:
from spirehdl.aggregate.aggregate_array import Array
from spirehdl.aggregate.aggregate_record import AggregateRecord
from spirehdl.aggregate.aggregate_fixed_point import FixedPoint, FixedPointType
from spirehdl.aggregate.aggregate_register import AggregateRegister
from spirehdl.spirehdl import UInt, Wire
class Bus(AggregateRecord):
data = Wire(UInt(8))
valid = Wire(UInt(1))
payload = Array([Bus(), Bus()])
acc = FixedPoint(FixedPointType(width_total=16, width_frac=8))
acc_reg = AggregateRegister(FixedPoint, acc.ftype, name="acc_reg")
acc_reg <<= acc # packed register write
payload[1] @= payload[0] # element-wise copy between bundles
Simulation notes
The simulator supports both combinational and sequential designs:
eval()recomputes combinational logic and captures registered probes.set()andget()let you drive or inspect signals by name.step()advances the clock, committing register next-state expressions while honoring asynchronous resets.watch()andpeek_next()provide scope-style visibility for debugging complex pipelines.
These capabilities align with the standard SpireHDL development flow: express a design, validate it in Python, then export it to your synthesis or verification stack.
Slices
We follow the indexing of python also in SpireHDL signals. For example sig[4:7] creates a new expression containing of bits 4 and 5 (counted from lsb) of the original expression sig.
Main development flow
- Model logic in Python. Use
Modulein the the top-level file and DSL expressions to capture datapaths, state machines, and control logic. - Factor reusable pieces. Wrap recurring structures in
Componentsubclasses so they can be instantiated, parameterized, or replaced with imported implementations. - Simulate early and often. Drive stimuli with the simulator, observe register evolution, and iterate on the Python source before handing designs to downstream tools.
- Export netlists. Emit Verilog or AIG/AAG when you are ready for synthesis, formal checking, or integration with external flows.
Examples
Check out the testing/examples/ directory for practical examples:
simple_component.py– A minimal example showing how to define a Component with IO ports and generate Verilogcomponent_example.py– Comprehensive examples including hierarchical design and simulationmodule_with_component.py– Shows how to integrate Components within Module-based designsdirect_expression_basics.py– Minimal direct expression examples (y = a + b) plus+,-, unary-,Const(..., Int(...)), typed/plainFalse, and a recursive Horner polynomial buildertesting/riscv/rv32i.py– Minimal RV32I core example; seetesting/riscv/test_rv32i.pyfor simulation-based checks.
See the examples README for detailed documentation and key concepts.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spire_hdl-0.1.0.tar.gz.
File metadata
- Download URL: spire_hdl-0.1.0.tar.gz
- Upload date:
- Size: 327.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19ec90dda5ae97052281a97ddc105ffd97478420a40457422f41874c4aee886a
|
|
| MD5 |
49c65d2c435404eddecc5e9bfb1134ba
|
|
| BLAKE2b-256 |
12e0838a06653ff8262884c7d02fec2a425e7eb63288693cc658475710e707c5
|
Provenance
The following attestation bundles were made for spire_hdl-0.1.0.tar.gz:
Publisher:
ci.yml on huawei-csl/spire-hdl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spire_hdl-0.1.0.tar.gz -
Subject digest:
19ec90dda5ae97052281a97ddc105ffd97478420a40457422f41874c4aee886a - Sigstore transparency entry: 1804131738
- Sigstore integration time:
-
Permalink:
huawei-csl/spire-hdl@01223a412c396f73daf7b40473be12be57f02bea -
Branch / Tag:
refs/heads/main - Owner: https://github.com/huawei-csl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@01223a412c396f73daf7b40473be12be57f02bea -
Trigger Event:
push
-
Statement type:
File details
Details for the file spire_hdl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: spire_hdl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89f89526de69906195fcdd4e208836a9ddabf1210d53d6f30244cee4fb6d9637
|
|
| MD5 |
4d76d2807273d955e92fc8223dd97e39
|
|
| BLAKE2b-256 |
6916f25b9afce9be5db7025c576a6eeed1444f538f5a9a9ec942ee2b5191a038
|
Provenance
The following attestation bundles were made for spire_hdl-0.1.0-py3-none-any.whl:
Publisher:
ci.yml on huawei-csl/spire-hdl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spire_hdl-0.1.0-py3-none-any.whl -
Subject digest:
89f89526de69906195fcdd4e208836a9ddabf1210d53d6f30244cee4fb6d9637 - Sigstore transparency entry: 1804131786
- Sigstore integration time:
-
Permalink:
huawei-csl/spire-hdl@01223a412c396f73daf7b40473be12be57f02bea -
Branch / Tag:
refs/heads/main - Owner: https://github.com/huawei-csl
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@01223a412c396f73daf7b40473be12be57f02bea -
Trigger Event:
push
-
Statement type: