grlib: Gaisler’s GRLIB and the NOEL-V Core as a Bazel Module
The grlib Bazel module packages GRLIB, Gaisler’s (Frontgrade’s) GPL
VHDL IP library, so that the NOEL-V RV64 RISC-V core and the AMBA
infrastructure around it can be consumed as ordinary Bazel
dependencies. It is the module that puts the CPU into Cocoapuffs, the SoC
that boots Fuchsia’s Zircon kernel on an Artix-7 FPGA. It is also
the module where I learned the most about what it costs to move a large,
make-era VHDL codebase into a modern build system, which is what this post
is mostly about. It follows rules_vivado in my series on the modules
behind cocoapuffs-fpga.
A library from a different build era
GRLIB is distributed as a large tree of VHDL organized into libraries
(grlib, gaisler, techmap, …) and driven by a make-based flow. That
flow encodes two decades of institutional knowledge, and two of its habits
fight a hermetic build system directly.
The first habit: configuration through generated config.vhd files full of
global CFG_* constants. Within Gaisler’s flow this works fine; one design,
one config. But it does not compose. Two designs configured differently
collide on the same global names, so you cannot build both from one
workspace, and nothing about the configuration is visible to the build
system as a dependency.
The second habit: compile order as tribal knowledge. VHDL requires packages to be compiled before their users, and the make flow computes a workable order for you. Bazel has no such global pass; every ordering constraint has to exist as an explicit dependency edge. Teasing the real package dependency graph out of the tree, and fixing the places where the generated build files got it wrong, was a long grind of elaboration failures, one missing edge at a time. The build files for the tree are generated by a maintained script rather than written by hand, because upstream GRLIB releases keep coming and the extraction has to be repeatable.
The module in three layers
MODULE.bazeldownloads the exact upstream GPL release (grlib-gpl-2025.2-b4298) from Gaisler’s server, pinned by integrity hash, and re-exposes every VHDL source through the@grlib_srcsrepository as tool-agnosticfilegrouptargets. (The module is namedgrliband the sources repositorygrlib_srcs; the first attempt used the same name for both, and Bazel disagreed.)- Aggregate targets
@grlib//:grlib,:gaisler,:techmap, and:NOELV_RV64group the standard libraries and the NOEL-V core bundle. - A Kconfig layer (built on
rules_kconfig) namespaces the configuration. Every symbol becomes a build setting prefixed by its GRLIB path, soIU_NWINDOWSfromlib/gaisler/leon3/leon3.inturns into@grlib_config//:CONFIG_LIB_GAISLER_LEON3_LEON3_IU_NWINDOWS, and one flag (CONFIG_ACTIVE_DESIGN_PREFIX) promotes a chosen design’s configuration back to the genericCFG_*names the VHDL expects. Different designs stop stepping on one another, and a configuration change is now an input Bazel can see, cache against, and invalidate on.
Making 1993-era VHDL serve 2019 tools
GRLIB targets the common denominator of commercial simulators, which in
practice means conservative VHDL. I wanted the same sources usable under
modern standards, so the module has a --@grlib//:vhdl_standard flag
covering 1987 through 2019. Getting the tree to actually compile under the
newer standards took version-specific file swapping and narrow, range-based
patches to the handful of files where the standards disagree; a blunt
tree-wide patch breaks on every upstream release, and the surgical version
survives them.
The other stress test was the simulator itself. The NOEL-V simulation in
the module runs under NVC, and GRLIB’s heavy generic-and-record
VHDL found real elaboration limits there: at one point noelvsys would
not elaborate until the module folded some MSB-selection wrappers in
cctrl5nv, and for a while the module shipped a workaround patch against
the NVC rules that a later pinned NVC release made unnecessary. Packaging
an IP library turns out to include being its simulators’ test suite.
In cocoapuffs-fpga
The SoC’s Vivado libraries pull NOEL-V core files (cpucorenv.vhd,
cctrl5nv.vhd, and friends), the ACLINT, APLIC, JTAG, and misc peripherals
directly from the fetched archive, while the repo vendors a thin noelvsys
wrapper of its own. The //:grlib, //:gaisler, //:techmap, and
//:NOELV_RV64 aliases show up in about forty BUILD files, from the board
design in boards/noelv/ down to the interrupt controller in ip/plic/.
The same sources feed Vivado for synthesis and NVC for simulation, which is
the point of keeping the filegroups tool-agnostic.
Prior art and references
- GRLIB itself, and the NOEL-V processor page; the module downloads from Gaisler’s release server.
- FuseSoC is the non-Bazel approach to packaging exactly this kind of IP library (rules_fusesoc, next in this series, wraps it).
- bazel_rules_hdl is the broader Bazel-for-hardware ecosystem this slots into.
The source is at filmil/bazel_grlib, published through my Bazel registry. For what all this wiring is for, see the Cocoapuffs post.