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)

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.


📁 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

Simulation Waveform

# 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

Simulation Waveform

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

DMA simulation waveform

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.

Video pipeline

ModuleRole
snix_video_timing_genHorizontal/vertical counters, HSYNC, VSYNC, SOF, EOL
snix_video_pattern_genEight-bar colour test pattern, combinational
snix_video_alt_pattern_genBars + colour-inverted box alternating position per frame (motion source)
snix_video_to_axisNative video DE/SOF/EOL/data → AXI-Stream; detects overflow
snix_axis_to_videoAXI-Stream → display timing window; detects underflow and frame errors
snix_video_axis_mixer2Joins two frame-locked pixel streams for temporal views: current, previous, average, diff, mask, overlay, blend, XOR
snix_video_rgb24_pack24-bit pixels → wide AXI beat (rational-ratio repacker)
snix_video_rgb24_unpackWide AXI beat → 24-bit pixels (symmetric)
snix_video_rgb32_pack24-bit pixels → XRGB8888 beats
snix_video_rgb32_unpackXRGB8888 beats → 24-bit pixels (symmetric)
snix_video_rgb_to_ycbcrBT.601 RGB → YCbCr colour-space converter
snix_video_ycbcr_to_rgbBT.601 YCbCr → RGB colour-space converter
snix_video_csc_4224:4:4 → 4:2:2 chroma subsampler
snix_video_csc_422_expand4:2:2 → 4:4:4 chroma expander
snix_video_capture_cdcPack + async FIFO CDC (pixel clock → AXI clock)
snix_video_display_cdcAsync 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.