CRC HDL code generator: Verilog-2001, SystemVerilog, VHDL-1993
Project description
crcZero
Parallel CRC HDL code generator — Verilog-2001, SystemVerilog, VHDL-1993, plus a portable C reference. Hardware tested on Arty A7-100T.
Generates synthesizable, parallel CRC modules from a built-in catalog of 80+ named algorithms (reveng-verified), or from user-supplied polynomial parameters. Includes self-checking testbenches, AXI4-Stream wrappers, and multi-vendor synthesis checks.
Features
- Three output targets — Verilog-2001, SystemVerilog, VHDL-1993, and a portable C reference
- 80+ named algorithms — CRC-8 through CRC-64, parameters sourced from the reveng CRC catalogue, all check values verified
- Custom polynomials — normal (Williams) or Koopman notation; check value auto-computed when not provided
- Any data width — 8, 16, 32, 64, or any positive integer bits per clock
- AXI4-Stream wrappers — clocked wrapper with slave (data in) and master (CRC out) interfaces, 2-state FSM, full backpressure support
- Self-checking testbenches — Verilog and VHDL (iverilog/vvp, ghdl, and Vivado xsim compatible),
25 test vectors each, VCD waveform output saved to
tests/vcd/ - CLI + Python API — use from the command line or import in any Python project
- Zero runtime dependencies — pure Python 3.9+, stdlib only
- Synthesis-verified — all outputs pass Yosys (
yowasp-yosys) across AMD/Xilinx, Altera/Intel, Lattice, Microchip, Efinix, and Gowin targets, and GHDL (--synth); pure behavioural RTL targets ASIC flows - Hardware-tested on silicon — CRC-32/ISO-HDLC D=32 validated on Arty A7-100T (Artix-7 FPGA)
via JTAG-AXI; 21/21 test vectors (1–1024 beats) confirmed correct on real hardware (see
hw_test/)
How It Works
crcZero uses GF(2) iterative symbolic unrolling to derive, for each CRC output bit, the exact
set of crc_in and data_in bits that must be XORed together.
A state vector of N bitmasks (one per CRC register bit) is maintained. Each bitmask tracks which
input bits contribute to that state bit. Starting from state[i] = (1 << i), D data bits are fed
one-by-one through the LFSR update rule symbolically. After D iterations, the state encodes the
complete parallel combinatorial equations as XOR trees, which the renderers emit directly as HDL assigns.
No matrix exponentiation — handles any CRC width and data width, including both reflected
(ref_in == ref_out == True) and normal modes.
Installation
git clone https://github.com/bard0-design/crcZero
cd crcZero
pip install -e .
PyPI package (pip install crczero) is planned — see Roadmap.
Quick Start
CLI
# List all 80+ built-in algorithms
crcZero --list-algorithms
# Generate Verilog for CRC-32 (Ethernet/ZIP)
crcZero --algorithm CRC-32/ISO-HDLC --data-width 8 --lang verilog
# Generate all outputs to files (Verilog/SV/VHDL + C header/source)
crcZero --algorithm CRC-32/ISO-HDLC --data-width 8 --lang all --output crc32
# Generate with testbench and simulate immediately (requires iverilog in PATH)
crcZero --algorithm CRC-32/ISO-HDLC --data-width 8 --lang verilog \
--output crc32 --testbench --simulate
# Generate AXI4-Stream wrapper alongside the CRC core
crcZero --algorithm CRC-32/ISO-HDLC --data-width 8 --lang verilog \
--output crc32 --axi-stream
# Produces: crc32.v crc32_axis.v
# Generate a portable C reference implementation
crcZero --algorithm CRC-32/ISO-HDLC --lang c --output crc32_ref
# Produces: crc32_ref.h crc32_ref.c
# Custom polynomial (Koopman notation)
crcZero --poly-koopman 0x82608EDB --width 32 --init 0xFFFFFFFF \
--ref-in --ref-out --xor-out 0xFFFFFFFF --data-width 8 --lang sv
Python API
from crczero import CrcGenerator, catalog
gen = CrcGenerator(catalog["CRC-32/ISO-HDLC"], data_width=8)
assert gen.self_test()
print(gen.generate_verilog())
print(gen.generate_systemverilog())
print(gen.generate_vhdl())
header, source = gen.generate_c()
print(gen.generate_testbench_verilog())
# AXI4-Stream wrappers
print(gen.generate_axi_stream_verilog())
print(gen.generate_axi_stream_sv())
print(gen.generate_axi_stream_vhdl())
# Custom polynomial
from crczero import Algorithm, poly_from_koopman
alg = Algorithm(
name="MY-CRC32", width=32, poly=0x04C11DB7,
init=0xFFFFFFFF, ref_in=True, ref_out=True,
xor_out=0xFFFFFFFF, check=0xCBF43926, residue=0xDEBB20E3,
)
gen = CrcGenerator(alg, data_width=32)
Generated Output
The generated module has three ports:
| Port | Direction | Width | Description |
|---|---|---|---|
data_in |
input | D bits | Data word for this clock cycle |
crc_in |
input | N bits | CRC register input (chain from previous word) |
crc_out |
output | N bits | Updated CRC register output |
Usage in hardware:
- Set
crc_in = INITfor the first word - Connect
crc_out → crc_infor subsequent words - After the last word:
final_crc = crc_out XOR XOR_OUT
Example (CRC-32/ISO-HDLC, 8-bit data):
// Purely combinatorial generated module
module crc_32_iso_hdlc_d8 (
input [7:0] data_in,
input [31:0] crc_in,
output [31:0] crc_out
);
assign crc_out[0] = crc_in[1] ^ crc_in[2] ^ crc_in[8] ^ ...;
// ...
endmodule
// Wire into a clocked parent design
module eth_tx (
input wire clk, rst_n, valid,
input wire [7:0] data,
output reg [31:0] fcs
);
reg [31:0] crc_reg;
wire [31:0] crc_next;
crc_32_iso_hdlc_d8 u_crc (.data_in(data), .crc_in(crc_reg), .crc_out(crc_next));
always @(posedge clk or negedge rst_n)
if (!rst_n) crc_reg <= 32'hFFFFFFFF;
else if (valid) crc_reg <= crc_next;
assign fcs = ~{crc_reg[7:0], crc_reg[15:8], crc_reg[23:16], crc_reg[31:24]};
endmodule
SystemVerilog and VHDL follow the same pattern. See sample_output/ for complete generated files.
AXI4-Stream Wrapper
The --axi-stream flag generates a clocked wrapper around the combinatorial CRC core.
Interface
| Signal | Direction | Width | Description |
|---|---|---|---|
clk |
in | 1 | Clock |
rst_n |
in | 1 | Active-low synchronous reset |
s_axis_tdata |
in | D | Data input (slave) |
s_axis_tvalid |
in | 1 | Slave valid |
s_axis_tready |
out | 1 | Slave ready (de-asserted during WAIT_ACK) |
s_axis_tlast |
in | 1 | Last beat of packet |
m_axis_tdata |
out | N | Final CRC (XOR_OUT already applied) |
m_axis_tvalid |
out | 1 | Master valid |
m_axis_tready |
in | 1 | Master ready (backpressure from downstream) |
m_axis_tlast |
out | 1 | 1 when m_axis_tvalid is high |
FSM
TLAST accepted m_axis_tready
ACCUM ─────────────────────► WAIT_ACK ──────────────────► ACCUM
│ s_axis_tready = 1 │ s_axis_tready = 0 │
│ accumulate crc_reg │ hold m_axis_tdata │
└─────────────────────────────┴────────────────────────────┘
On TLAST: m_axis_tdata = crc_result ^ XOR_OUT, m_axis_tvalid asserted.
On handshake: crc_reg resets to HW_INIT for the next packet.
Usage
crcZero --algorithm CRC-32/ISO-HDLC --data-width 8 --output crc32 \
--lang all --axi-stream
# Produces: crc32.v crc32_axis.v crc32.sv crc32_axis.sv crc32.vhd crc32_axis.vhd crc32.h crc32.c
A self-checking AXI4-Stream testbench is available via
gen.generate_testbench_axi_verilog() / gen.generate_testbench_axi_vhdl().
Runs 10 packets × 2 passes (LCG stall + no-stall) = 20 CRC checks per run.
Custom Polynomials
| Notation | CRC-32 value |
|---|---|
| Normal (Williams) | 0x04C11DB7 |
| Koopman | 0x82608EDB |
# Williams normal form
crcZero --poly 0x04C11DB7 --width 32 --init 0xFFFFFFFF \
--ref-in --ref-out --xor-out 0xFFFFFFFF --data-width 8 --lang verilog
# Koopman notation
crcZero --poly-koopman 0x82608EDB --width 32 --init 0xFFFFFFFF \
--ref-in --ref-out --xor-out 0xFFFFFFFF --data-width 8 --lang verilog
The check value is auto-computed when --check is not supplied.
from crczero import poly_from_koopman, poly_to_koopman
normal = poly_from_koopman(0x82608EDB, width=32) # → 0x04C11DB7
koopman = poly_to_koopman(0x04C11DB7, width=32) # → 0x82608EDB
Testbench and Simulation
# Verilog (iverilog)
crcZero --algorithm CRC-32/ISO-HDLC --output crc32 --lang verilog --testbench --simulate
# VHDL (ghdl)
crcZero --algorithm CRC-32/ISO-HDLC --output crc32 --lang vhdl --testbench --simulate
Each testbench applies 25 test vectors (b"123456789" decomposed byte-by-byte + 16 deterministic
random words) and reports PASS/FAIL per vector. VCDs are saved to tests/vcd/.
Compatible with iverilog/vvp, ghdl, and Vivado xsim.
Verification
Python self-test
from crczero import CrcGenerator, catalog
gen = CrcGenerator(catalog["CRC-32/ISO-HDLC"], data_width=8)
assert gen.self_test() # verifies software oracle matches catalog check value
Test suite
pip install -e ".[dev]"
pytest tests/
# 477 tests — catalog check values, equation derivation, renderer output,
# CLI flags, testbench renderers, AXI4-Stream wrapper, and full RTL simulation
Synthesis (multi-vendor, Yosys)
pip install yowasp-yosys
python synth/run_synth.py # all six vendors
python synth/run_synth.py --vendor xilinx lattice # subset
LUT counts for CRC-32/ISO-HDLC, D=8:
| Vendor | Family | LUTs |
|---|---|---|
| AMD/Xilinx | 7-series (XC7), LUT6 | 62 |
| Altera/Intel | Cyclone V / Cyclone 10 GX (ALM) | 60 |
| Lattice | ECP5, LUT4 | 83 |
The generated RTL is purely combinatorial XOR logic with no vendor primitives — synthesises equally well for ASIC flows.
Resource usage
Minimum 2-input XOR operations per clock cycle (actual gate count after synthesis may be lower due
to sharing). Rule of thumb: LUTs ≈ XOR_count / 5 on a 6-input-LUT FPGA.
| Algorithm | D=8 | D=16 | D=32 | D=64 |
|---|---|---|---|---|
| CRC-32/ISO-HDLC (Ethernet FCS) | 220 | 414 | 872 | 1390 |
| CRC-32/MPEG-2 / BZIP2 | 220 | 414 | 872 | 1390 |
| CRC-32C / ISCSI (Castagnoli) | 268 | 528 | 1036 | 1470 |
| CRC-8/SMBUS | 44 | — | — | — |
| CRC-16/ARC | 68 | 128 | — | — |
| CRC-64/GO-ISO | 56 | — | 224 | 468 |
| CRC-64/ECMA-182 | 524 | — | 2048 | 4004 |
Algorithm Catalog
Use crcZero --list-algorithms to see all 80+ entries. A sample:
| Algorithm | Width | Polynomial | RefIn | RefOut | Check |
|---|---|---|---|---|---|
| CRC-8/SMBUS | 8 | 0x07 | No | No | 0xF4 |
| CRC-16/ARC | 16 | 0x8005 | Yes | Yes | 0xBB3D |
| CRC-16/MODBUS | 16 | 0x8005 | Yes | Yes | 0x4B37 |
| CRC-16/KERMIT | 16 | 0x1021 | Yes | Yes | 0x2189 |
| CRC-24/BLE | 24 | 0x00065B | Yes | Yes | 0xC25A56 |
| CRC-32/ISO-HDLC | 32 | 0x04C11DB7 | Yes | Yes | 0xCBF43926 |
| CRC-32/MPEG-2 | 32 | 0x04C11DB7 | No | No | 0x0376E6E7 |
| CRC-32C (Castagnoli) | 32 | 0x1EDC6F41 | Yes | Yes | 0xE3069283 |
| CRC-64/GO-ISO | 64 | 0x000000000000001B | Yes | Yes | 0xB90956C775A41001 |
| CRC-64/ECMA-182 | 64 | 0x42F0E1EBA9EA3693 | No | No | 0x6C40DF5F0B497347 |
CLI Reference
crcZero [--list-algorithms]
[--algorithm NAME | --poly HEX | --poly-koopman HEX]
[--width INT] [--init HEX] [--ref-in] [--ref-out]
[--xor-out HEX] [--check HEX] [--residue HEX] [--name STR]
[--data-width INT] [--lang {verilog,sv,vhdl,c,all}]
[--output PATH] [--module-name NAME]
[--no-self-test] [--testbench] [--simulate] [--axi-stream]
| Flag | Default | Description |
|---|---|---|
--list-algorithms |
— | Print full algorithm catalog and exit |
--algorithm NAME |
— | Select named algorithm from catalog |
--poly HEX |
— | Custom polynomial (Williams normal form) |
--poly-koopman HEX |
— | Custom polynomial (Koopman notation) |
--width INT |
— | CRC width in bits (required for custom poly) |
--init HEX |
0x0 |
Initial register value |
--ref-in |
off | Reflect each input byte |
--ref-out |
off | Reflect final register |
--xor-out HEX |
0x0 |
Final XOR mask |
--check HEX |
auto | Check value (auto-computed if omitted) |
--data-width INT |
8 |
Data bits per clock cycle |
--lang |
verilog |
Output language: verilog, sv, vhdl, c, all |
--output PATH |
stdout | Output file stem (extension added automatically) |
--module-name NAME |
auto | Override generated module/entity name |
--no-self-test |
off | Skip self-test |
--testbench |
off | Also generate self-checking testbench |
--simulate |
off | Auto-invoke iverilog+vvp or ghdl after generation |
--axi-stream |
off | Also generate AXI4-Stream wrapper (<stem>_axis.*) |
Hardware Test (Arty A7-100T)
The hw_test/ directory contains a complete Vivado flow that validates the generated
AXI4-Stream wrapper on a real Artix-7 FPGA using JTAG-AXI — no soft processor required.
Requirements
- Vivado 2022.2+ (tested on 2025.2)
- Arty A7-100T connected via USB-JTAG
crczeroinstalled (pip install -e .)
Steps
1. Generate the RTL (if not already in sample_output/):
crcZero --algorithm CRC-32/ISO-HDLC --data-width 32 --lang verilog \
--output sample_output/crc_32_iso_hdlc_d32 --axi-stream
2. Create the Vivado project (Tcl console):
source hw_test/tcl/create_project.tcl
3. Synthesise, implement, generate bitstream (Tcl console):
source hw_test/tcl/run_impl.tcl
Typical runtime: ~5 min synthesis + ~4 min implementation.
4. Program the FPGA and run the test (Tcl console, Hardware Manager open):
source hw_test/tcl/hw_test.tcl
The script programs the FPGA, resets the AXI FIFO, sends 10 test packets (including back-to-back without FIFO reset) via JTAG-AXI, and compares each result against the software oracle:
=== CRC-32/ISO-HDLC D=32 Hardware Test ===
Test Got Expected Result
------------------------------------------------------------------------
b'1234' 1-word 0x9BE3E0A3 0x9BE3E0A3 PASS
b'12345678' 2-word 0x9AE0DAAF 0x9AE0DAAF PASS
...
b'123456789012' 3-word 0x5D34EB96 0x5D34EB96 PASS
back-to-back pkt A (1234) 0x9BE3E0A3 0x9BE3E0A3 PASS
back-to-back pkt B (0000) 0x2144DF1C 0x2144DF1C PASS
long-1-beat 0x1A5A601F 0x1A5A601F PASS
...
long-1024-beat 0x20C17E20 0x20C17E20 PASS
------------------------------------------------------------------------
ALL 21/21 TESTS PASSED
A heartbeat LED (LD0) blinks at ~1.5 Hz on power-up to confirm the design is alive.
See hw_test/README.md for full details.
Roadmap
| Phase | Feature | Status |
|---|---|---|
| 3 | Registered / pipelined output (latency-1, configurable N-stage) | Planned |
| 4 | CRC checker module (crc_ok output, residue-based verification) |
Planned |
| 5 | Byte-enable / TKEEP support (partial last word) | Planned |
| 6 | Wishbone B4 and APB3/APB4 memory-mapped wrappers | Planned |
| 7 | SVA formal verification assertions | Planned |
| 8 | PyPI publish (pip install crczero) |
In progress |
| 9 | C reference implementation for MCU cross-validation | Completed |
| 10 | Web UI — browser-based online code generator | Planned |
Contributing
Bug reports and pull requests welcome — see CONTRIBUTING.md.
Authors
Leonardo Capossio — bard0 design hello@bard0.com
License
MIT — see LICENSE.
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 crczero-1.2.0.tar.gz.
File metadata
- Download URL: crczero-1.2.0.tar.gz
- Upload date:
- Size: 87.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
048d69ac09ccbcce570dc83b525d651dda587ddeee0f6486eeda9134e1211281
|
|
| MD5 |
a406d4b48b1f7574e9fcda533d03362c
|
|
| BLAKE2b-256 |
5228175598b3914a9a3ebe384babb085a412c7d77852f92cf0cee12de159e5c4
|
File details
Details for the file crczero-1.2.0-py3-none-any.whl.
File metadata
- Download URL: crczero-1.2.0-py3-none-any.whl
- Upload date:
- Size: 41.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
165cbd191ea10a6ca92aa839f226cff978a897eb9a5863e3b871fcd41111ed94
|
|
| MD5 |
52bc26985f5ee8baa46e66e21469b482
|
|
| BLAKE2b-256 |
690a12a7a904c583035f7cbb384459b3d10bfef33d7e8191cf07f53525be100a
|