verilaxi
July 5, 2026 · View on GitHub
verilaxi is a lightweight, Verilator-friendly AXI verification library written in SystemVerilog.
It provides simple, task-based AXI, AXI-Lite, and AXI-Stream drivers, monitors, and test environments designed for fast RTL bring-up — without UVM.
For a deeper walkthrough of the architecture, module inventory, register maps, and verification flow, see the Developer Guide. For the video IP pipeline, both VDMA engines, and the real-image (PNG) round-trip harness, see VIDEO.md.
✨ Features
- ✅ AXI4, AXI-Lite, and AXI-Stream support
- ✅ UART core, AXI-Lite GPIO, and UART↔AXI-Lite control-plane blocks
- ✅ AXI DMA, CDMA, and AXI VDMA with triple-buffer frame store and genlock
- ✅ AXI-Stream packet, beat, and weighted round-robin arbitration
- ✅ AXI-Stream width converters — integer-ratio (k:1 up, 1:k down) and rational-ratio (e.g. 16↔24)
- ✅ Synchronous and asynchronous AXI-Stream FIFOs
- ✅ Video pipeline: timing generator, colour-bar pattern, video↔AXI-Stream adapters, RGB24 pack/unpack, and capture/display CDC modules
- ✅ Verilator-first (tested with FST + Surfer)
- ✅ No UVM, no factory, no phases
- ✅ Task-based drivers (
write,read,write_burst,read_burst) - ✅ Parameterized widths (ADDR, DATA, ID)
- ✅ AXI stream backpressure (SRC_BP / SINK_BP) and AXI slave ready backpressure (READY_PROB)
- ✅ Selectable test scenarios via TESTTYPE plusarg
- ✅ Clean Makefile-driven test selection with interactive menu
- ✅ SVA protocol checkers for AXI4-Full, AXI-Lite, and AXI-Stream, plus 4KB AXI burst-boundary checks (Verilator
--assert)
Related Articles
These sistenix posts are the narrative layer for the repository. The intent is that a reader can understand the design choices in the blog, then inspect the exact RTL, VIP, and testbench code here in verilaxi.
- Synchronous and Asynchronous FIFOs — sync FIFO flags, async FIFO Gray pointers, CDC headroom, and AXI-Stream async FIFO framing
- AXI-Stream Arbitration in SystemVerilog — packet, beat, and weighted round-robin AXI-Stream arbitration, with RTL and testbench examples
- Building AXI-Stream Width Converters — integer up/down sizing, rational byte repacking, TKEEP handling, and byte-accurate verification
- AXI DMA: Moving Data Without the CPU — S2MM/MM2S and CDMA architecture, 4KB boundary handling, partial strobes, circular mode, and throughput
- Video basics for hardware engineers — pixels, YCbCr/chroma subsampling, video timing, pixel clocks, video↔AXI-Stream, CDC, and the PNG image harness
- Video DMA: triple-buffering, genlock and temporal taps — single-stream VDMA, frame ownership, genlock, multi-tap temporal reads, throughput, and real-image squirrel round-trip
- Writing a CSR Block Using AXI-Lite — self-clearing control bits, sticky status, and AXI-Lite software control patterns
- Building SystemVerilog AXI VIP for Fast Bring-Up — the task-based VIP style used for fast bring-up in this repo
- Checking AXI Protocol with SystemVerilog Assertions — the assertion layer used to check AXI, AXI-Lite, AXI-Stream, and 4KB burst rules
- Building a Verilator Testbench for AXI Designs — how
sim_main.cpp,testbench.sv, plusargs, and--timingfit together - Reproducible RTL Simulation with Docker and GitHub Actions — containerized simulation, sweeps, and CI flow
📁 Repository Structure
verilaxi/
├── rtl/
│ ├── axi/ snix_axi_dma, snix_axi_cdma, snix_axi_vdma, snix_axi_mm2mm, mm2s, s2mm
│ ├── axil/ snix_axil_register, snix_axil_gpio, snix_uart_axil_slave, snix_uart_axil_master
│ │ snix_axi_dma_csr, snix_axi_cdma_csr
│ ├── axis/ snix_axis_arbiter, snix_axis_fifo, snix_axis_afifo, snix_axis_register
│ │ snix_axis_upsizer, snix_axis_downsizer
│ │ snix_axis_rr_converter, snix_axis_rr_upsizer, snix_axis_rr_downsizer
│ ├── video/ timing, pattern, video↔AXIS, RGB24 pack/unpack, capture/display CDC
│ ├── uart/ snix_uart_lite
│ └── common/ snix_sync_fifo, snix_async_fifo, snix_register_slice
├── tb/
│ ├── classes/ axi_master, axi_slave, axil_master, axis_source, axis_sink, video helpers …
│ ├── interfaces/ axi4_if, axil_if, axis_if
│ ├── packages/ axi_pkg, axi_dma_pkg, axi_cdma_pkg, axi_vdma_pkg
│ ├── assertions/ axis_checker, axil_checker, axi_mm_checker, axi_4k_checker
│ └── tests/ test_dma, test_cdma, test_vdma, test_video_*, test_axis_*
├── filelists/ common.f, tb_top.f
├── mk/ config.mk, build.mk, menu.mk, help.mk
└── Makefile
🚀 Getting Started
Clone
git clone https://github.com/nelsoncsc/verilaxi.git
cd verilaxi
Requirements
- Verilator 5.048
- Yosys 0.66
- SystemVerilog support enabled
- Surfer (recommended) or GTKWave for FST viewing
This repository is validated against Verilator 5.048 and Yosys 0.66. Older packaged 5.x Verilator releases may fail to parse or build parts of the testbench and should not be assumed to work. Older Yosys releases such as 0.33 may also fail on some of the newer SystemVerilog syntax used by modules like snix_axis_arbiter.
The protocol checkers use strict AXI stability properties. Once VALID is observed while READY is low, VALID and its payload must remain unchanged through the next sampling edge. Earlier Verilator 5.046 testing used $fell(ready) antecedent guards and || ready consequent escapes as scheduling workarounds. With race-free BFM edge timing, Verilator 5.048 passes the strict properties without those guards.
Build and Run
Interactive menu (prompts for test scenario and backpressure settings):
make
make run TESTNAME=dma # AXI4 DMA (stream → memory, memory → stream)
make run TESTNAME=cdma # AXI4 CDMA (memory-to-memory copy)
make run TESTNAME=vdma READY_PROB=70 # AXI4 video DMA frame-buffer test
scripts/vdma_validate.sh # Larger-frame VDMA validation at READY_PROB=100/70/50/30
make run TESTNAME=axil_register # AXI-Lite register
make run TESTNAME=axil_gpio # AXI-Lite GPIO with user LEDs, RGB LEDs, and debounced buttons
make run TESTNAME=uart_lite # UART core loopback test
make run TESTNAME=uart_axil_slave # AXI-Lite UART peripheral
make run TESTNAME=uart_axil_master # UART-to-AXI-Lite bridge
make run TESTNAME=axis_register # AXI-Stream register slice
make run TESTNAME=axis_arbiter # AXI-Stream round-robin arbiter (packet mode)
make run TESTNAME=axis_arbiter_beat # AXI-Stream beat-mode arbiter
make run TESTNAME=axis_arbiter_weighted # AXI-Stream weighted packet arbiter
make run TESTNAME=axis_fifo # AXI-Stream FIFO
make run TESTNAME=axis_afifo # AXI-Stream async FIFO / CDC FIFO
make run TESTNAME=axis_upsizer # AXI-Stream integer upsizer (IN=8 → OUT=32)
make run TESTNAME=axis_downsizer # AXI-Stream integer downsizer (IN=32 → OUT=8)
make run TESTNAME=axis_rr_converter # AXI-Stream rational-ratio converter (IN=32 → OUT=48)
make run TESTNAME=axis_rr_upsizer # AXI-Stream rational-ratio upsizer (IN=16 → OUT=24)
make run TESTNAME=axis_rr_downsizer # AXI-Stream rational-ratio downsizer (IN=24 → OUT=16)
make run TESTNAME=video_axis_loopback # video timing/pattern → AXIS → video
make run TESTNAME=video_fifo_loopback # video loopback through sync FIFO
make run TESTNAME=video_afifo_loopback # video loopback through async FIFO/CDC
make run TESTNAME=video_adapter_errors # adapter overflow/underflow/framing checks
make run TESTNAME=video_mode_clocks # common video mode pixel-clock periods
make run TESTNAME=video_rgb_cdc # RGB24 pack/unpack across capture/AXI/display clocks

# AXI-Stream arbiter with sink and source backpressure
make run TESTNAME=axis_arbiter SRC_BP=1 SINK_BP=1
# Beat-mode arbiter
make run TESTNAME=axis_arbiter_beat SRC_BP=1 SINK_BP=1
# Weighted packet arbiter
make run TESTNAME=axis_arbiter_weighted SRC_BP=1 SINK_BP=1
# AXI-Stream async FIFO in streaming mode
make run TESTNAME=axis_afifo FRAME_FIFO=0 TESTTYPE=1 SRC_BP=1 SINK_BP=1
# AXI-Stream async FIFO in frame-store-and-forward mode
make run TESTNAME=axis_afifo FRAME_FIFO=1 TESTTYPE=1 SRC_BP=1 SINK_BP=1
Select a test scenario and stress with AXI slave backpressure:
# CDMA: 4KB boundary test with 80% AXI ready probability
make run TESTNAME=cdma TESTTYPE=1 READY_PROB=80

# DMA: 4KB boundary test
make run TESTNAME=dma TESTTYPE=3 READY_PROB=80

AXI Video DMA
snix_axi_vdma is a full-frame scatter-gather DMA for progressive video. A capture engine (S2MM) writes incoming pixel data into AXI4 memory one frame at a time. A triple-buffer frame store rotates through three independent buffer slots and tracks which is newest. A playback engine (MM2S) reads frames back out over AXI4 and presents them on an AXI-Stream output. Software controls everything through a 16-register AXI-Lite CSR bank.
Key features:
- Triple-buffer frame store with automatic slot rotation, newest-frame tracking, and overwrite detection
- Genlock — playback restarts only after a new capture frame completes, eliminating display tearing
- Frame delay — configurable 0/1/2 frame pipeline lag between capture and playback
- Park mode — lock MM2S to a fixed buffer slot for freeze-frame or diagnostic output
- Per-frame IRQ with independent enables for S2MM done, MM2S done, and errors
- Telemetry counters — saturating 4-bit counts for underrun, overwrite, and genlock sync-loss events
- AXI fault reporting — sticky flags for non-OKAY BRESP/RRESP responses; failed-frame suppression prevents the slot from being published to the frame store
# VDMA: triple-buffer frame capture and playback with 70% AXI ready probability
make run TESTNAME=vdma READY_PROB=70
# Larger 64×32 validation profile across READY_PROB=100/70/50/30
scripts/vdma_validate.sh
The validation profile enforces hard minimum throughput assertions on both paths. Measured on the 64×32 frame: MM2S sustains ~88% bus efficiency across tested READY_PROB settings; S2MM reaches ~86% with no memory backpressure and scales with injected source-stall conditions. Remaining optimisation work is mainly reducing per-line/burst turnaround gaps on the write path.
A multi-tap temporal variant, snix_axi_multi_vdma, exposes several past frames at once (tap 0 = newest, tap N−1 = oldest) from an (N+1)-slot frame store — for temporal denoise, motion detection, and frame differencing. It is exercised by targeted directed tests plus a real-image (PNG) round-trip with per-frame throughput instrumentation. On the display side, snix_video_alt_pattern_gen (a source that alternates phase every frame) and snix_video_axis_mixer2 (current / previous / average / difference / mask / overlay / blend / XOR views) compose into a self-checking motion view. See VIDEO.md for the full video pipeline, both VDMA engines, the PNG harness, and the squirrel motion round-trip.
Video Infrastructure
The video modules form a composable pipeline from pixel clock to AXI-Stream and back.
| Module | Role |
|---|---|
snix_video_timing_gen | Horizontal/vertical counters, HSYNC, VSYNC, SOF, EOL |
snix_video_pattern_gen | Eight-bar colour test pattern, combinational |
snix_video_alt_pattern_gen | Bars + colour-inverted box alternating position per frame (motion source) |
snix_video_to_axis | Native video DE/SOF/EOL/data → AXI-Stream; detects overflow |
snix_axis_to_video | AXI-Stream → display timing window; detects underflow and frame errors |
snix_video_axis_mixer2 | Joins two frame-locked pixel streams for temporal views: current, previous, average, diff, mask, overlay, blend, XOR |
snix_video_rgb24_pack | 24-bit pixels → wide AXI beat (rational-ratio repacker) |
snix_video_rgb24_unpack | Wide AXI beat → 24-bit pixels (symmetric) |
snix_video_rgb32_pack | 24-bit pixels → XRGB8888 beats |
snix_video_rgb32_unpack | XRGB8888 beats → 24-bit pixels (symmetric) |
snix_video_rgb_to_ycbcr | BT.601 RGB → YCbCr colour-space converter |
snix_video_ycbcr_to_rgb | BT.601 YCbCr → RGB colour-space converter |
snix_video_csc_422 | 4:4:4 → 4:2:2 chroma subsampler |
snix_video_csc_422_expand | 4:2:2 → 4:4:4 chroma expander |
snix_video_capture_cdc | Pack + async FIFO CDC (pixel clock → AXI clock) |
snix_video_display_cdc | Async FIFO CDC + unpack (AXI clock → display pixel clock) |
make run TESTNAME=video_axis_loopback # timing → pattern → to_axis → axis_to_video → check
make run TESTNAME=video_fifo_loopback # same path through sync FIFO
make run TESTNAME=video_afifo_loopback # same path through async FIFO / CDC
make run TESTNAME=video_adapter_errors # overflow, underflow, and frame_error flags
make run TESTNAME=video_mode_clocks # pixel-clock period checks for VGA/HD/FHD/UHD presets
make run TESTNAME=video_rgb_cdc # RGB24 pack/unpack across capture, AXI, and display clocks
make run TESTNAME=video_rgb32 # XRGB8888 pack/unpack
make run TESTNAME=video_csc_rgb_ycbcr # BT.601 RGB↔YCbCr round-trip (bounded error)
make run TESTNAME=video_csc_422 # 4:4:4 ↔ 4:2:2 chroma subsample round-trip
make run TESTNAME=video_mixer # frame-alternating pattern through all 8 temporal mixer modes
Synthesis
make synth SYNTH_NAME=axis_arbiter SYNTH_TARGET=generic
make synth SYNTH_NAME=uart_lite SYNTH_TARGET=generic
make synth SYNTH_NAME=axil_gpio SYNTH_TARGET=generic
make synth SYNTH_NAME=uart_axil_slave SYNTH_TARGET=generic
make synth SYNTH_NAME=uart_axil_master SYNTH_TARGET=generic
make synth SYNTH_NAME=axis_fifo SYNTH_TARGET=generic
make synth SYNTH_NAME=axis_fifo_pkt SYNTH_TARGET=generic
make synth SYNTH_NAME=axis_afifo SYNTH_TARGET=artix7
make synth SYNTH_NAME=axis_upsizer SYNTH_TARGET=artix7
make synth SYNTH_NAME=axis_downsizer SYNTH_TARGET=artix7
make synth SYNTH_NAME=axis_rr_converter SYNTH_TARGET=artix7
make synth SYNTH_NAME=axis_rr_upsizer SYNTH_TARGET=artix7
make synth SYNTH_NAME=axis_rr_downsizer SYNTH_TARGET=artix7
make synth SYNTH_NAME=dma SYNTH_TARGET=artix7
make synth SYNTH_NAME=cdma SYNTH_TARGET=generic
make synth SYNTH_NAME=vdma SYNTH_TARGET=generic
make synth SYNTH_NAME=multi_vdma SYNTH_TARGET=artix7
make synth SYNTH_NAME=video_csc_422 SYNTH_TARGET=artix7
make synth SYNTH_NAME=video_alt_pattern_gen SYNTH_TARGET=artix7
make synth SYNTH_NAME=video_axis_mixer2 SYNTH_TARGET=artix7
Simulation logs and FST waveforms are written with parameter-aware filenames so sweep runs do not overwrite each other. For example, make run TESTNAME=axis_afifo FRAME_FIFO=1 TESTTYPE=1 SRC_BP=1 SINK_BP=1 produces work/logs/axis_afifo_ff1_tt1_src1_sink1.log and work/waves/axis_afifo_ff1_tt1_src1_sink1.fst.
Docker
A minimal Docker environment is included for reproducible Linux runs with Verilator 5.048, Yosys 0.66, and make.
docker build -t verilaxi .
docker run --rm -it -v "$PWD":/workspace -w /workspace verilaxi \
make run OBJ_DIR=work/obj_dir_linux TESTNAME=axis_afifo FRAME_FIFO=1 TESTTYPE=1 SRC_BP=1 SINK_BP=1
This is also the recommended cross-platform path for macOS, Linux, and Windows via WSL2 when you want a consistent tool environment. Using a container-specific OBJ_DIR avoids collisions between host-built binaries and Linux container builds.
Useful Docker examples:
# Tool versions
docker run --rm -it -v "$PWD":/workspace -w /workspace verilaxi \
bash -lc "verilator --version && yosys -V"
# Synthesis
docker run --rm -it -v "$PWD":/workspace -w /workspace verilaxi \
make synth SYNTH_NAME=axis_afifo SYNTH_TARGET=generic
# Sweep wrapper
docker run --rm -it -v "$PWD":/workspace -w /workspace verilaxi \
./scripts/sweep.sh synth generic
Sweep Script
A convenience regression script is included for broader simulation and synthesis sweeps:
scripts/sweep.sh sim
scripts/sweep.sh synth both
scripts/sweep.sh all both
The simulation sweep covers the AXIS register, UART core, AXI-Lite GPIO, UART/AXI-Lite control-plane blocks, arbiter (packet/beat/weighted), FIFO, AFIFO, upsizer, downsizer, rr_converter, rr_upsizer, rr_downsizer matrices, standalone video tests, axil_register, DMA/CDMA runs with READY_PROB=70, and the VDMA frame-buffer test. The synthesis sweep covers all supported designs for generic, artix7, or both, including VDMA. The current Verilator 5.048/Yosys 0.66 checkpoint is 89/89 simulation cases and 38/38 synthesis cases passing.
Acknowledgements
This project was developed independently. Credit is due to the wider open-source AXI community for helping shape good engineering practice around AXI design and verification. In particular, ZipCPU's AXI articles and examples, together with Alex Forencich's AXI and AXI-Stream component work, were useful sources of inspiration and reference.
This repository does not use code from my current or previous employers; rather, it is a summary of my learnings during my spare time. AI tools were used as auxiliary tools for debugging, scripting, and documentation support.