Bazel rules for Verilator-based SystemVerilog simulation using the Bazel Central Registry (BCR) Verilator toolchain.
- Uses BCR Verilator for better reproducibility and version management
- Supports both C++ and SystemC output
- Support incremental hierarchical builds
- Optional waveform tracing support
- Repo-wide
verilator --lint-onlyaspect
Add the following to your MODULE.bazel:
bazel_dep(name = "rules_verilator", version = "1.1.0")
register_toolchains(
"@rules_verilator//verilator:verilator_toolchain",
)The default toolchain supports C++ output only and does not require SystemC.
If you need SystemC output, add the SystemC dependency and register the SystemC-enabled toolchain:
bazel_dep(name = "rules_verilator", version = "1.1.0")
# Register the SystemC-enabled toolchain
register_toolchains(
"@rules_verilator//verilator:verilator_toolchain_with_systemc",
)You can check verilator/tests for examples as well.
load("@rules_verilator//verilator:defs.bzl", "verilator_cc_library")
load("@rules_verilog//verilog:defs.bzl", "verilog_library")
verilog_library(
name = "my_module",
srcs = ["my_top_module.sv"],
top_module = "my_top_module",
)
verilator_cc_library(
name = "my_verilated_lib",
module = ":my_module",
timing = True, # Enable timing support (--timing), optional
trace_mode = "vcd", # Enable waveform tracing ("vcd", "fst", "saif"), optional
vopts = [
"-Wall",
"--x-assign fast",
"--x-initial fast",
],
)
cc_binary(
name = "my_test",
srcs = ["testbench.cpp"],
deps = [":my_verilated_lib"],
)module_topremains available as an override when you need to verilate a different top than the one declared on theverilog_library, but the recommended style is to put the canonical top inverilog_library(top_module = ...).- Use the dedicated
timingattribute instead of passing--timingor--no-timingthroughvopts. - Use the dedicated
trace_modeattribute instead of passing--trace,--trace-vcd,--trace-fst, or--trace-saifthroughvopts. trace = Trueremains supported as a compatibility alias fortrace_mode = "vcd".
load("@rules_verilator//verilator:defs.bzl", "verilator_cc_library")
load("@rules_verilog//verilog:defs.bzl", "verilog_library")
verilog_library(
name = "my_module",
srcs = ["my_top_module.sv"],
top_module = "my_top_module",
)
verilator_cc_library(
name = "my_verilated_sc_lib",
module = ":my_module",
systemc = True, # Generate SystemC output
timing = True,
trace_mode = "vcd",
vopts = ["-Wall"],
)
cc_binary(
name = "my_sc_test",
srcs = ["testbench_sc.cpp"],
deps = [":my_verilated_sc_lib"],
)For verilator's hierarchical verilation, enable hierarchical = True on verilator_cc_library.
Hierarchical Verilation splits a large design into multiple Verilated libraries instead of compiling the whole RTL graph as one flat model. The top-level model then calls into the generated models for selected lower-level blocks. This usually helps when a design is large enough that flat Verilation becomes expensive in time or memory, and when incremental rebuilds should reuse cached results for unchanged blocks.
The trade-off is that hierarchical mode is less globally optimized than flat Verilation. It may reduce simulation performance, and it inherits Verilator's hierarchy-boundary restrictions. Use it when build scalability matters more than getting the most aggressive whole-design scheduling.
This rule discovers hierarchy automatically from verilog_library(top_module = ...) declarations. A library with a non-empty top_module becomes a hierarchy boundary. A library without top_module stays transparent and is compiled into the nearest ancestor boundary.
load("@rules_verilator//verilator:defs.bzl", "verilator_cc_library")
load("@rules_verilog//verilog:defs.bzl", "verilog_library")
verilog_library(
name = "block_a_sv",
srcs = ["block_a.sv"],
top_module = "block_a",
)
verilog_library(
name = "block_b_sv",
srcs = ["block_b.sv"],
top_module = "block_b",
)
verilog_library(
name = "top_local_sv",
srcs = ["top.sv"],
)
verilog_library(
name = "full_design_sv",
deps = [
":block_a_sv",
":block_b_sv",
":top_local_sv",
],
top_module = "top",
)
verilator_cc_library(
name = "top_verilated",
module = ":full_design_sv",
hierarchical = True, # Enable hierarchical verilation
)How the top is chosen:
module_toponverilator_cc_library, if set, overrides everything else.- Otherwise the rule uses
module[VerilogInfo].top_module. - If neither is set, analysis fails.
How child libraries are treated:
top_module != "": the child becomes its own hierarchical node.top_module == "": the child stays transparent and is merged into the nearest ancestor node.- The root target is always represented by the resolved top of the current
verilator_cc_library, even if that top came frommodule_topoverride.
Important behavior:
- Hierarchy recognition only looks at the current
modulegraph. Otherverilator_cc_librarytargets do not affect it. hierarchical = Truechanges the compile strategy for this target only. The sameverilog_librarygraph can still be consumed by a separate flatverilator_cc_library.- Rebuilds happen at hierarchy-node granularity: if one child node changes, unchanged sibling nodes can still be reused from cache, while the changed node and its ancestor chain are rebuilt.
systemc = Trueis supported only for flat Verilation today.- The rule auto-generates the temporary
.vltcontrol file. Users do not need to maintain it manually.
verilator_lint_aspect runs verilator --lint-only on every target that provides VerilogInfo. It pays for itself in two cases the compile path doesn't cover:
- Intermediate libraries. A
verilog_librarythat's only ever consumed as a dependency (never wrapped in averilator_cc_library) otherwise has nothing exercising it. The aspect lints it in isolation against its owntop_module, so issues at a leaf module (unused signals, width truncation, incomplete cases) surface where they live instead of getting masked by the integrated top. - A stricter, repo-wide flag set. Compile
voptsare tuned per target for buildability. The aspect's flags come from the newlint_voptsattr onverilator_toolchain(default["-Wall"]), so CI can enforce a stricter posture without changing how any individualverilator_cc_librarybuilds.
It's also cheap — no C++ codegen, no link — so it's reasonable to fan out over //... on every build.
bazel build \
--aspects=@rules_verilator//verilator:defs.bzl%verilator_lint_aspect \
--output_groups=verilator_lint_checks \
//...Clean lint runs produce no output; failures replay Verilator's diagnostics to stderr and fail the action with Verilator's exit code.
Set lint_vopts on your verilator_toolchain to override the default:
verilator_toolchain(
name = "verilator_toolchain_impl",
verilator = "@verilator//:verilator_executable",
lint_vopts = [
"-Wall",
"-Wpedantic",
],
)The same managed-flag validation that applies to extra_vopts and vopts applies here — --cc, --sc, --timing, and --trace* are rejected; use the dedicated rule attrs.
Tag a target with no-lint or no-verilator-lint to skip it during aspect runs. Underscores and hyphens are interchangeable, so no_lint and no_verilator_lint also work:
verilog_library(
name = "legacy_block",
srcs = ["legacy.sv"],
top_module = "legacy_block",
tags = ["no-verilator-lint"],
)Targets without a top_module are skipped automatically — they can't be linted in isolation.
Tip
This was a fork of the Verilator rules from hdl/bazel_rules_hdl
- No bundled Verilator: Requires users to declare BCR Verilator dependency explicitly
- Optional SystemC: SystemC is not bundled; users add it only when needed
- Bzlmod only: Designed for MODULE.bazel, not legacy WORKSPACE
- Focused scope: Only Verilator rules, no synthesis/PnR tools
- More feature: Newer version of Verilator, supporting incremental hierarchical builds
- Bazel >=7.6.0, >=8.1.0, 9
- Verilator >= 5.046.bcr.3 from BCR
- SystemC 3.0.2 from BCR (optional, for SystemC output)
Apache License 2.0 (inherited from bazel_rules_hdl)