rules_vivado

Bazel rules for Xilinx Vivado FPGA synthesis, placement, routing, and bitstream generation.

Overview

rules_vivado wires Xilinx Vivado into Bazel as a set of ordinary build and test rules. HDL sources flow in through rules_verilog (VerilogInfo) and rules_vhdl (VhdlInfo); the same *_library targets can be reused for simulation and synthesis. The build phases are each their own rule (vivado_synthesize, vivado_placement, vivado_routing, vivado_write_bitstream, …) so checkpoints are cached between phases, or you can chain the whole flow with the vivado_flow macro.

The Xilinx install itself is resolved via a registered vivado_toolchain — there is no per-target install path to configure once a toolchain is in place.

Quick start

The walkthrough below takes a Verilog top module from source to bitstream with the vivado_flow macro.

MODULE.bazel

bazel_dep(name = "rules_verilog", version = "1.1.1")
bazel_dep(name = "rules_vhdl", version = "0.1.1")
bazel_dep(name = "rules_vivado", version = "{version}")

register_toolchains("//tools/vivado:vivado_toolchain")

A vivado_toolchain is mandatory — every vivado_* rule resolves the Xilinx install through it. See Toolchains for how to author one.

tools/vivado/vivado.sh

#!/usr/bin/env bash
exec /opt/Xilinx/Vivado/2024.2/bin/vivado "$@"

Mark it executable: chmod +x tools/vivado/vivado.sh.

tools/vivado/BUILD.bazel

load("@rules_vivado//vivado:toolchain.bzl", "vivado_toolchain")

vivado_toolchain(
    name = "vivado_local",
    vivado = "vivado.sh",
    env = {
        "XILINXD_LICENSE_FILE": "2100@license.example.com",
        "HOME": "/tmp",
    },
)

toolchain(
    name = "vivado_toolchain",
    toolchain = ":vivado_local",
    toolchain_type = "@rules_vivado//vivado:toolchain_type",
)

See Toolchains for license-server and multi-version setup.

hello/hello.sv

module hello (
    input  wire clk,
    input  wire rst,
    output reg  led
);
  always_ff @(posedge clk) begin
    if (rst) led <= 1'b0;
    else     led <= ~led;
  end
endmodule

hello/BUILD.bazel

load("@rules_verilog//verilog:defs.bzl", "verilog_library")
load("@rules_vivado//vivado:defs.bzl", "vivado_flow")

verilog_library(
    name = "hello",
    srcs = ["hello.sv"],
    data = ["hello.xdc"],
)

vivado_flow(
    name = "hello_bitstream",
    module = ":hello",
    module_top = "hello",
    part_number = "xczu28dr-ffvg1517-2-e",
)

Build it

$ bazel build //hello:hello_bitstream
$ ls bazel-bin/hello/
hello_bitstream.bit  hello_bitstream_route.dcp  ...

vivado_flow is a convenience macro — it expands to the per-phase rules below so each checkpoint is cached on its own:

  • hello_bitstream_synth — synthesis (.dcp)
  • hello_bitstream_synth_opt — post-synthesis optimization
  • hello_bitstream_placement — placement
  • hello_bitstream_place_opt — post-placement optimization
  • hello_bitstream_route — routing
  • hello_bitstream — final .bit

Build any one of them directly to stop the flow early or to inspect intermediate reports.

Going further

  • Toolchains — author a vivado_toolchain, register multiple versions, gate them with constraints and platforms.
  • Rules — every public rule, indexed by build phase.