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 optimizationhello_bitstream_placement— placementhello_bitstream_place_opt— post-placement optimizationhello_bitstream_route— routinghello_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.
Toolchains
rules_vivado resolves the Xilinx Vivado install through Bazel
toolchain resolution. You declare a
vivado_toolchain, wrap it in toolchain(...),
and register it from MODULE.bazel. Every vivado_* rule then picks
it up automatically — there is no per-target xilinx_env to thread
through.
Registering a toolchain is required.
Implementing a toolchain
See vivado_toolchain — the rule's docstring
has the worked quickstart, attribute reference, and the env vs
xilinx_env framing.
Network vs. node-locked licenses
vivado_toolchain.requires_network defaults to True, which is
correct for a floating/network license server
(XILINXD_LICENSE_FILE=PORT@HOST). It sets the requires-network
execution requirement on every vivado_* action.
Set it to False for license-free editions (Vivado ML Standard /
WebPACK) or for node-locked .lic files read from disk. Sandboxed and
remote-execution builds need network disabled to be reproducible
without the license server, so be deliberate here.
Constraining toolchains
To run multiple Vivado versions side-by-side, gate each
vivado_toolchain with one of the per-version constraint_values in
//vivado/constraints/BUILD.bazel.
Each constraint corresponds to one entry in VIVADO_VERSIONS (defined
in
//vivado/private:versions.bzl).
The vivado_toolchain docstring has the
full multi-version walkthrough — platform(...) setup,
register_execution_platforms, and the --platforms switch.
For per-target switching without a global flag, use a wrapper rule
with cfg = transition(...); see
tests/transition.bzl
for a with_vivado_version wrapper that takes a list of targets and
pins the version for the whole group.
Constraints are the only mechanism — there is no parallel build-setting
/ flag-driven path. This keeps per-version metadata (constraints,
exec_properties like container-image) all on the platform object
where it belongs and avoids the two-sources-of-truth problem.
Reference
See vivado_toolchain for the full attribute
set and VivadoToolchainInfo for the
resolved provider that downstream rules consume.
Toolchain for the Xilinx Vivado tool.
Defines VivadoToolchainInfo and the vivado_toolchain rule. Users register a
vivado_toolchain instance via register_toolchains(...) against the
//vivado:toolchain_type toolchain type so every vivado_* rule automatically
resolves the Xilinx environment.
Quickstart
-
Author a small bash shim that
execs your Vivado install. The shim is the file Bazel tracks; it hard-codes the install path (typically a fixed location baked into a container image):#!/usr/bin/env bash # tools/vivado/vivado.sh exec /opt/Xilinx/Vivado/2024.2/bin/vivado "$@"Mark it executable:
chmod +x tools/vivado/vivado.sh. -
Declare a
vivado_toolchainand atoolchain()wrapper in BUILD, pointing at the shim. Put your license server and any extra env inenv: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", ) -
Register it from MODULE.bazel:
register_toolchains("//tools/vivado:vivado_toolchain")
Every vivado_* rule resolves this toolchain automatically.
xilinx_env is an optional escape hatch — a shell script sourced inside the
action immediately before vivado runs — for shell-side env composition
neither env nor the shim itself covers. Prefer env and the shim's own
preamble first.
Constraining toolchains
Register multiple vivado_toolchain instances side-by-side and let Bazel pick
one per action via exec_compatible_with against the per-version
constraint_values in //vivado/constraints/BUILD.bazel. Each constraint
corresponds to one entry in //vivado/private:versions.bzl VIVADO_VERSIONS.
load("@rules_vivado//vivado:toolchain.bzl", "vivado_toolchain")
vivado_toolchain(
name = "vivado_2024_2",
vivado = "vivado_2024_2.sh",
)
toolchain(
name = "vivado_toolchain_2024_2",
exec_compatible_with = ["@rules_vivado//vivado/constraints/version:2024.2"],
toolchain = ":vivado_2024_2",
toolchain_type = "@rules_vivado//vivado:toolchain_type",
)
platform(
name = "vivado_2024_2_platform",
constraint_values = ["@rules_vivado//vivado/constraints/version:2024.2"],
exec_properties = {
"container-image": "docker://your.registry/vivado:2024.2",
},
parents = ["@platforms//host"],
)
Register both the toolchain and the platform from MODULE.bazel:
register_toolchains("//tools/vivado:vivado_toolchain_2024_2")
register_execution_platforms("//tools/vivado:vivado_2024_2_platform")
The first registered exec platform becomes the default. Switch versions per
build with --platforms=//tools/vivado:vivado_2024_2_platform (which also lets
target_compatible_with = ["@rules_vivado//vivado/constraints/version:2024.2"]
on a target evaluate against the right constraint), or use a wrapper rule with
cfg = transition(...) to switch per target. See
//tests/transition.bzl for an example with_vivado_version wrapper.
Rules
Providers
vivado_toolchain
load("@rules_vivado//vivado:toolchain.bzl", "vivado_toolchain")
vivado_toolchain(name, env, requires_network, version, vivado, xilinx_env)
Declares a Vivado toolchain.
Wrap with toolchain(...) and register via register_toolchains(...) in
MODULE.bazel so every vivado_* rule resolves it automatically. Multiple
instances can be registered side-by-side and selected via target_settings
(flag-driven) or exec_compatible_with (platform-driven). See the
//vivado:toolchain.bzl module docstring and the rules_vivado README for full
walkthroughs.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| env | Environment variables passed to every Vivado action. | Dictionary: String -> String | optional | {} |
| requires_network | Whether Vivado actions need network access. True (the default) is correct for a floating/network license server (XILINXD_LICENSE_FILE=PORT@HOST). Set to False for license-free editions (Vivado ML Standard / WebPACK) or node-locked .lic files read from disk. Controls whether the requires-network execution requirement is set on every vivado_* action. | Boolean | optional | True |
| version | The version of Vivado associated with this toolchain. | String | optional | "" |
| vivado | The Vivado executable. Typically a small bash shim that execs the real vivado out of a known install path (e.g. baked into a container image), but any *_binary rule works too — runfiles travel along. Defaults to a stock shim that calls vivado from the exec platform's PATH as a migration aid; production toolchains should pin the install path with their own shim. | Label | optional | "@rules_vivado//vivado/private:vivado.sh" |
| xilinx_env | Optional escape hatch — a shell script sourced inside the action shell immediately before vivado runs, for shell-side env composition env cannot express. Prefer env. | Label | optional | None |
VivadoToolchainInfo
load("@rules_vivado//vivado:toolchain.bzl", "VivadoToolchainInfo")
VivadoToolchainInfo(env, requires_network, version, vivado, xilinx_env)
Toolchain info for the Xilinx Vivado tool.
FIELDS
Rules
Every public rule in rules_vivado, grouped by the build phase it
belongs to. All of them resolve their Xilinx install through a
registered vivado_toolchain.
Project setup
vivado_create_project— emit a Vivado project without running synthesis (useful for IDE handoff).
Synthesis
vivado_synthesize— run synthesis from an HDL library and produce a synthesis checkpoint (.dcp).vivado_synthesis_optimize— post-synthesis optimization pass on a synthesis checkpoint.
Implementation
vivado_placement— placement on a synthesis checkpoint.vivado_place_optimize— post-placement optimization on a placement checkpoint.vivado_routing— routing on a placement checkpoint.
Bitstream
vivado_write_bitstream— emit the final.bit(and optionally.xsa) from a routing checkpoint.
End-to-end flow
vivado_flow— convenience macro (loaded from@rules_vivado//vivado:defs.bzl) that chains synthesis → opt → placement → place-opt → routing → bitstream into one target name. See the Quick start for a worked example.
IP packaging
vivado_create_ip— package an HDL module as a Vivado IP core.vivado_interface_definition— generate IP-XACT bus + abstraction definitions from a SystemVerilog interface.vivado_create_interface_ip— register an interface definition as an IP catalog entry so block designs can use it.
Simulation
xsim_test— run a Vivado XSim simulation as a Bazeltesttarget.
Toolchain
vivado_toolchain— declare a Vivado install for toolchain resolution. See Toolchains for the full workflow.
Providers
VivadoToolchainInfoand friends — the providers passed between phases (VivadoSynthCheckpointInfo,VivadoPlacementCheckpointInfo,VivadoRoutingCheckpointInfo,VivadoIPBlockInfo,VivadoInterfaceInfo).
vivado_create_project rule: build a Vivado project without synthesizing.
Rules
vivado_create_project
load("@rules_vivado//vivado:project.bzl", "vivado_create_project")
vivado_create_project(name, create_project_tcl_template, ip_blocks, jobs, module, module_top,
part_number)
Create a Vivado project from a verilog_library without running synthesis.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| create_project_tcl_template | The create project tcl template | Label | optional | "@rules_vivado//vivado/private:create_project.tcl.template" |
| ip_blocks | Ip blocks to include in this design. | List of labels | optional | [] |
| jobs | Jobs to pass to vivado which defines the amount of parallelism. | Integer | optional | 4 |
| module | The top level build. | Label | required | |
| module_top | The name of the top level verilog module. | String | required | |
| part_number | The targeted xilinx part. | String | required |
Synthesis-phase rules: vivado_synthesize and vivado_synthesis_optimize.
Rules
vivado_synthesis_optimize
load("@rules_vivado//vivado:synthesis.bzl", "vivado_synthesis_optimize")
vivado_synthesis_optimize(name, checkpoint, opt_directive, synthesis_optimize_template, threads,
with_probes)
Run post-synthesis optimization on a synthesis checkpoint.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| checkpoint | Synthesis checkpoint. | Label | required | |
| opt_directive | The optimization directive. | String | optional | "Explore" |
| synthesis_optimize_template | The synthesis optimization tcl template | Label | optional | "@rules_vivado//vivado/private:synth_optimize.tcl.template" |
| threads | Threads to pass to vivado which defines the amount of parallelism. | Integer | optional | 8 |
| with_probes | Create debug probes. | Boolean | optional | False |
vivado_synthesize
load("@rules_vivado//vivado:synthesis.bzl", "vivado_synthesize")
vivado_synthesize(name, create_project_tcl_template, ip_blocks, jobs, module, module_top,
part_number, synth_strategy)
Create a Vivado project and run synthesis on it.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| create_project_tcl_template | The create project tcl template | Label | optional | "@rules_vivado//vivado/private:create_project.tcl.template" |
| ip_blocks | Ip blocks to include in this design. | List of labels | optional | [] |
| jobs | Jobs to pass to vivado which defines the amount of parallelism. | Integer | optional | 4 |
| module | The top level build. | Label | required | |
| module_top | The name of the top level verilog module. | String | required | |
| part_number | The targeted xilinx part. | String | required | |
| synth_strategy | The synthesis strategy to use. | String | optional | "Vivado Synthesis Defaults" |
Implementation-phase rules: placement, physical optimization, routing.
Rules
vivado_place_optimize
load("@rules_vivado//vivado:implementation.bzl", "vivado_place_optimize")
vivado_place_optimize(name, checkpoint, phys_opt_directive, place_optimize_template, threads)
Run post-placement physical optimization.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| checkpoint | Placement checkpoint. | Label | required | |
| phys_opt_directive | The optimization directive. | String | optional | "AggressiveExplore" |
| place_optimize_template | The placement tcl template | Label | optional | "@rules_vivado//vivado/private:place_optimize.tcl.template" |
| threads | Threads to pass to vivado which defines the amount of parallelism. | Integer | optional | 8 |
vivado_placement
load("@rules_vivado//vivado:implementation.bzl", "vivado_placement")
vivado_placement(name, checkpoint, placement_directive, placement_template, threads)
Run placement on a (synthesis-optimized) checkpoint.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| checkpoint | Synthesis checkpoint. | Label | required | |
| placement_directive | The optimization directive. | String | optional | "Explore" |
| placement_template | The placement tcl template | Label | optional | "@rules_vivado//vivado/private:placement.tcl.template" |
| threads | Threads to pass to vivado which defines the amount of parallelism. | Integer | optional | 8 |
vivado_routing
load("@rules_vivado//vivado:implementation.bzl", "vivado_routing")
vivado_routing(name, checkpoint, route_directive, route_template, threads)
Run routing on a placement checkpoint.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| checkpoint | Placement checkpoint. | Label | required | |
| route_directive | The routing directive. | String | optional | "Explore" |
| route_template | The routing tcl template | Label | optional | "@rules_vivado//vivado/private:route.tcl.template" |
| threads | Threads to pass to vivado which defines the amount of parallelism. | Integer | optional | 8 |
Bitstream-phase rule: vivado_write_bitstream.
Rules
vivado_write_bitstream
load("@rules_vivado//vivado:bitstream.bzl", "vivado_write_bitstream")
vivado_write_bitstream(name, checkpoint, threads, with_xsa, write_bitstream_template)
Write a Vivado bitstream (.bit) from a routed checkpoint, optionally including a .xsa.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| checkpoint | Routed checkpoint. | Label | required | |
| threads | Threads to pass to vivado which defines the amount of parallelism. | Integer | optional | 8 |
| with_xsa | Generate xsa too | Boolean | optional | False |
| write_bitstream_template | The write bitstream tcl template | Label | optional | "@rules_vivado//vivado/private:write_bitstream.tcl.template" |
IP packaging rules
Rules
vivado_create_interface_ip
load("@rules_vivado//vivado:ip.bzl", "vivado_create_interface_ip")
vivado_create_interface_ip(name, create_interface_ip_template, description, interface, module,
part_number, vendor_display_name)
Package a Vivado interface definition as an IP block. Unlike vivado_create_ip, this does not require a top module.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| create_interface_ip_template | The TCL template for creating interface IP. | Label | optional | "@rules_vivado//vivado/private:create_interface_ip.tcl.template" |
| description | Description for the IP block. | String | optional | "" |
| interface | The interface definition to package. | Label | required | |
| module | The verilog_library containing the interface source file(s). | Label | optional | None |
| part_number | The targeted xilinx part. | String | required | |
| vendor_display_name | Display name for the vendor. | String | optional | "" |
vivado_create_ip
load("@rules_vivado//vivado:ip.bzl", "vivado_create_ip")
vivado_create_ip(name, create_ip_block_template, encrypt, ip_blocks, ip_library, ip_vendor,
ip_version, jobs, keyfile, module, module_top, part_number)
Use vivado to package a module into an IP core
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| create_ip_block_template | The create project tcl template | Label | optional | "@rules_vivado//vivado/private:create_ip_block.tcl.template" |
| encrypt | Encrypt the sources. Note: This requires a license. See: https://support.xilinx.com/s/article/68071?language=en_US | Boolean | optional | False |
| ip_blocks | Ip blocks to include in this design. | List of labels | optional | [] |
| ip_library | The version of this ip core. | String | required | |
| ip_vendor | The version of this ip core. | String | required | |
| ip_version | The version of this ip core. | String | required | |
| jobs | Jobs to pass to vivado which defines the amount of parallelism. | Integer | optional | 4 |
| keyfile | The keyfile to use when optionally encrypting | Label | optional | "@rules_vivado//vivado/private:xilinx_keyfile.txt" |
| module | The top level build. | Label | required | |
| module_top | The name of the top level verilog module. | String | required | |
| part_number | The targeted xilinx part. | String | required |
vivado_interface_definition
load("@rules_vivado//vivado:ip.bzl", "vivado_interface_definition")
vivado_interface_definition(name, src, abstraction_definition_template, bus_definition_template,
description, direct_connection, interface_name, interface_setup_template,
is_addressable, library, max_masters, max_slaves, parser, vendor, version)
Generate Vivado IP-XACT interface definition files (bus definition and abstraction definition XML).
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| src | The SystemVerilog interface source file to parse. | Label | required | |
| abstraction_definition_template | The abstraction definition XML template. | Label | optional | "@rules_vivado//vivado/private:abstraction_definition.xml.template" |
| bus_definition_template | The bus definition XML template. | Label | optional | "@rules_vivado//vivado/private:bus_definition.xml.template" |
| description | Description for the interface. | String | optional | "" |
| direct_connection | Whether direct connections are allowed. | Boolean | optional | True |
| interface_name | The name of the interface (e.g., 'hbm_reader'). | String | required | |
| interface_setup_template | The interface setup TCL template. | Label | optional | "@rules_vivado//vivado/private:interface_setup.tcl.template" |
| is_addressable | Whether the interface is addressable. | Boolean | optional | True |
| library | The library VLNV component (e.g., 'interface'). | String | optional | "interface" |
| max_masters | Maximum number of masters. | Integer | optional | 1 |
| max_slaves | Maximum number of slaves. | Integer | optional | 1 |
| parser | Python parser script (SV -> JSON). Override to customize SV parsing. | Label | optional | "@rules_vivado//vivado/private:parse_sv_interface" |
| vendor | The vendor VLNV component (e.g., 'mycompany.com'). | String | required | |
| version | The version VLNV component (e.g., '1.0'). | String | optional | "1.0" |
Simulation rule: xsim_test.
Rules
xsim_test
load("@rules_vivado//vivado:simulation.bzl", "xsim_test")
xsim_test(name, ip_blocks, module, module_top, part_number, with_waveform, xsim_test_template)
Run a Vivado xsim simulation as a Bazel test.
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| ip_blocks | Ip blocks to include in this design. | List of labels | optional | [] |
| module | The top level build. | Label | required | |
| module_top | The name of the top level verilog module. | String | required | |
| part_number | The targeted xilinx part. | String | required | |
| with_waveform | Generate with a waveform | Boolean | optional | False |
| xsim_test_template | The tcl template to run on vivado. | Label | optional | "@rules_vivado//vivado/private:xsim_test.tcl.template" |
Vivado providers.
Providers
- VivadoIPBlockInfo
- VivadoInterfaceInfo
- VivadoPlacementCheckpointInfo
- VivadoRoutingCheckpointInfo
- VivadoSynthCheckpointInfo
VivadoIPBlockInfo
load("@rules_vivado//vivado:providers.bzl", "VivadoIPBlockInfo")
VivadoIPBlockInfo(is_interface, library, module_top, repo, vendor, version)
Info for a vivado ip block
FIELDS
VivadoInterfaceInfo
load("@rules_vivado//vivado:providers.bzl", "VivadoInterfaceInfo")
VivadoInterfaceInfo(abstraction_definition, bus_definition, library, name, setup_tcl, vendor,
version)
Info for a Vivado IP-XACT interface definition
FIELDS
VivadoPlacementCheckpointInfo
load("@rules_vivado//vivado:providers.bzl", "VivadoPlacementCheckpointInfo")
VivadoPlacementCheckpointInfo(checkpoint)
Contains information at output of placement.
FIELDS
VivadoRoutingCheckpointInfo
load("@rules_vivado//vivado:providers.bzl", "VivadoRoutingCheckpointInfo")
VivadoRoutingCheckpointInfo(checkpoint)
Contains information at output of routing.
FIELDS
VivadoSynthCheckpointInfo
load("@rules_vivado//vivado:providers.bzl", "VivadoSynthCheckpointInfo")
VivadoSynthCheckpointInfo(checkpoint)
Contains information at output of synthesis.
FIELDS