Bazelizing Third-Party FPGA Designs: Two Case Studies
Converting existing open-source FPGA projects to Bazel and rules_openxc7 makes hardware builds reproducible, eliminates machine-specific paths, and enables one-command bitstream generation without vendor licensing hurdles. This article examines two real conversions of external repositories: mmRISC-2, a silicon-proven RISC-V SoC with heavy SystemVerilog hierarchies, and Custom RISC CPU with Systolic Matrix Accelerator, a pipelined processor paired with a 2D systolic array coprocessor. Both examples illustrate how Bazel transforms fragile build scripts into reliable, sandboxed workflows.
The problem with traditional FPGA repositories
Most FPGA repositories published online share several recurring maintenance challenges:
- Vendor GUI project files: Repositories often check in proprietary IDE
project files (such as Vivado
.xprfiles) that change on every save and do not track cleanly across tool versions. - Machine-specific hardcoded paths: Simulation models and memory loaders often hardcode absolute paths pointing to private home directories on the author’s original workstation.
- Implicit host dependencies: Build instructions often require the reader to install dozens of gigabytes of vendor software or manually compile open-source EDA tools from source.
- Poor automation ergonomics: Running a build requires manual GUI clicking or invoking non-deterministic shell scripts that fail when run inside headless CI pipelines.
Bazel solves these friction points by providing sandboxed execution, explicit
input tracking, and hermetic toolchains. With rules_openxc7, Bazel downloads
prebuilt binaries for Yosys, nextpnr-xilinx, Project X-Ray, and openFPGALoader.
Anyone with Bazel installed can clone a repository and produce a verified
bitstream with one terminal command.
For an extensive architectural guide to authoring hardware rules in Bazel, consult the book Hardware Development with Bazel.
Case study 1: mmRISC-2 (Munetomo Maruyama)
The mmRISC-2 repository by Munetomo Maruyama is a complete, silicon-proven RISC-V system-on-chip. It features a pipelined CPU implementing RV32IMAC or RV64GC instruction sets, an AXI4 bus infrastructure, cache controllers, an integrated debug module, and a collection of peripheral interfaces.
Converting mmRISC-2 presented two engineering hurdles:
- Large-scale SystemVerilog code: The design spans dozens of interconnected source files and package definitions with complex parameterization and forward references.
- Target device support: The target evaluation board was the Digilent
Arty A7-100T, which uses the Xilinx Artix-7 100T FPGA (
xc7a100tcsg324-1).
What Bazel and rules_openxc7 accomplished
- Upstream toolchain enhancement: To support the Arty A7-100T board, we
extended
rules_openxc7with definitions for thexc7a100tcsg324-1part in rules_openxc7 PR #14. - Yosys Slang frontend integration: Traditional Yosys Verilog frontends
struggle with complex SystemVerilog package hierarchies. We configured the
synthesis target to use the Slang plugin frontend with flags
--allow-use-before-declareand--unroll-limit 100000. - Clean boundary interfaces: In
RTL/BUILD.bazel, all source files, include headers, and Xilinx primitive blackboxes (such asBUFGandMMCME2_BASE) were declared with fine-grained dependencies. - Successful bitstream generation: Yosys synthesized 39,010 logic cells,
and
nextpnr-xilinxplaced and routed the design with zero timing or routing errors, outputting a validtop_pnr.bitfile.
The complete conversion is available in mmRISC-2 PR #1. Developers can now build and flash the SoC onto an Arty A7-100T board with:
# Synthesize the entire SoC
bazel build //:top_synth
# Place and route, generating top_pnr.bit
bazel build //:top_pnr
# Program the attached Arty A7 board via openFPGALoader
bazel run //:top_prog
Case study 2: Custom RISC CPU with Systolic Array (Mathew George)
The Custom RISC CPU with Systolic Matrix Accelerator by Mathew George couples a custom pipelined RISC core with a 2D 4x4 (16 Processing Element) systolic array coprocessor designed for matrix multiplication and 2D image filtering.
This project presented a classic hardware portability problem:
- Hardcoded workstation paths: In
instruction_memory.vanddata_memory.v, the memory arrays were initialized using$readmemhcalls pointing to absolute paths on the original developer’s computer:/home/mathew/Pixel_Processor_SystolicArray/program.hexand.../image_data2.hex. - Missing fixtures: Neither
.hexfile was included in the upstream git tree, causing synthesis and simulation to fail immediately on any other system.
What Bazel and rules_openxc7 accomplished
-
Verilog parameterization: Instead of hardcoding paths, we refactored the memory modules to accept a parameter:
module instruction_memory #( parameter MEM_FILE = "" ) ( input wire [31:0] address, output reg [31:0] instruction ); reg [31:0] memory [0:1023]; initial begin if (MEM_FILE != "") begin $readmemh(MEM_FILE, memory); end end ... -
Decoupled data dependencies: We checked in clean, minimal test fixture files (
fixtures/program.hexandfixtures/image_data2.hex). In Bazel, firmware files are passed as explicitdata = [...]dependencies. Users can swap application firmware without modifying the underlying Verilog source code. -
Board integration wrapper: We added a top-level wrapper (
arty_top.v) and pin constraints (arty_a7_100t.xdc) mapping CPU stall flags, coprocessor execution states, and RAM writes to user LEDs on the Arty A7-100T. -
High-speed execution: In the Bazel sandbox, the complete build from empty cache to finished bitstream takes only 14 seconds.
The full pull request is documented in Custom-RISC PR #1.
# Generate bitstream in 14 seconds
bazel build //:top_pnr
Why Bazel matters for hardware design
The lessons from these two conversions apply to both human hardware teams and autonomous coding agents:
- Hermeticity eliminates workstation divergence: When builds run in sandboxes with declared inputs and outputs, “works on my machine” bugs vanish.
- Open-source EDA becomes accessible: Requiring contributors to install proprietary vendor tools creates a high barrier to entry. Bazel automates the retrieval of open-source tools without manual compilation.
- Fast regression cycles: Action caching ensures that unchanged Verilog modules, constraint files, and test benches are never re-evaluated.
- Agent and CI ready: Headless, deterministic tools enable automated agents to verify pull requests, run formal proofs, and generate bitstreams without human intervention.
To learn how to structure your own hardware workspaces and author custom EDA rules, read Hardware Development with Bazel.