At minimum, you need to have the following tools installed in your environment.
-
Bazel == 9.1.0 (recommended to manage this using Bazelisk)
-
System Python interpreter >= 3.12
Additionally, several tools are needed to build and run all of the tests:
-
Open source
-
Elaboration
-
Slang
-
-
Linting
-
Verible
-
-
Simulation
-
Verilator
-
-
RTL wrapper generation
-
TopStitch
-
-
Additional tools included in the Docker image
-
Yosys, EQY, and Yices
-
Slang
-
XLS support libraries
-
-
-
Proprietary (not provided - purchase your own installation and licenses)
-
Elaboration
-
Verific tclmain
-
-
Linting
-
RealIntent AscentLint
-
-
Simulation
-
Synopsys VCS
-
-
Formal
-
Cadence JasperGold
-
Synopsys VCF
-
-
|
Note
|
We no longer support Altair DSim in CI. Nevertheless, the build system should continue to support it if you wish to use it for your own tests. |
The provided Dockerfile can be used to build an image with the minimum dependencies and open source tools listed above. The Dockerfile uses RockyLinux 8 on x86-64 as the base image. If you want to run tests that leverage proprietary tools, you need to provide your own installations and licenses (which are typically compatible with RockyLinux 8 on x86-64).
If you bring your own EDA tools, you may need to provide a bit of your own Python code that implements the plugin API for verilog_runner.py.
If you want to use a tool that isn’t already supported, update //bazel:verilog.bzl, implement a new verilog runner plugin, and reference your new tool in the appropriate Bazel test targets.
Not all tests necessarily work with every tool.
Refer to the BUILD.bazel files for specifics.
Prebuilt linux/amd64 images are published to the
Bedrock-RTL package on GitHub Container Registry.
Each image has an immutable tag of the form YYYY-MM-DD-<full-git-sha>, where the date is the commit date in UTC.
We intentionally do not publish a moving latest tag.
To use the latest image, open the package page linked above, copy the tag from the most recently published version, and run:
docker pull ghcr.io/xlsynth/bedrock-rtl:<tag>
docker run --rm -it -v "$(pwd)":/src -w /src ghcr.io/xlsynth/bedrock-rtl:<tag> /bin/bashThe package is public, so pulling it does not require GitHub Container Registry authentication. Using an immutable tag makes local and CI environments reproducible; pull a newer tag from the package page when you want to update the toolchain.
To build the image from the checked-out Dockerfile instead:
docker build --platform=linux/amd64 --tag=bedrock-rtl:${USER} .
docker run --rm -it -v "$(pwd)":/src -w /src bedrock-rtl:${USER} /bin/bashThen once inside the container, try bazel build //… && bazel test //fifo/sim/… --test_tag_filters=verilator.
The docker-image-build job in .github/workflows/ci.yml builds the image for every pull request, push to main, and manual CI run.
After that build succeeds, the docker-image-publish job deploys the image to ghcr.io/xlsynth/bedrock-rtl for pushes to main and manually dispatched CI runs.
Pull requests build the image but do not publish it.
The publish job generates the immutable YYYY-MM-DD-<full-git-sha> tag, pushes the linux/amd64 image, attaches BuildKit provenance, and publishes a signed GitHub artifact attestation for the image digest.
Repository maintainers can deploy an image manually by opening the
CI workflow, selecting Run workflow, and choosing the Git ref to publish.
We use pre-commit hooks to enforce basic coding style. To install the hooks, run:
pre-commit installThis installs both the pre-commit and pre-push hooks configured by .pre-commit-config.yaml.
They should automatically run on every commit and push.
You can also run them manually via:
pre-commit runWe’ve tested with pre-commit version 4.0.1.
We use the powerful Bazel build system to assemble filelists and to run all tests (elaboration, lint, simulation, and formal).
A one-step command builds and runs all tests:
bazel test //...|
Important
|
Action required for tests to pass!
The repository includes a Verilator plugin for open-source simulation tests, but most EDA tool plugins are intentionally not provided. Many Bazel tests will fail unless your local or CI environment provides plugins for the proprietary tools used by those tests. We keep those plugins outside the repository because:
For additional tools, implement the plugin API by subclassing |
The Bazel test rule implementations at //bazel:verilog.bzl shell out the //python/verilog_runner/verilog_runner.py tool that presents a generic tool-agnostic API that actually implements the test.
The repository includes a Slang plugin for open-source elaboration tests.
Select Slang explicitly for verilog_elab_test targets:
verilog_elab_test(
name = "example_elab_test",
tool = "slang",
top = "example",
deps = [":example"],
)Slang performs parsing, semantic analysis, hierarchy elaboration, and type checking.
The plugin supports source and header dependencies, preprocessor defines, and top-level parameter overrides supplied through the rule.
For package-only source sets, set compile_only = True to perform compilation and type checking without module elaboration.
We run continuous integration tests with GitHub Actions.
The main CI workflow currently covers pre-commit, build, Python/ecc codegen tests, Stardoc, Verific, Slang, AscentLint, VCS, and Verilator.
Sampled JasperGold formal tests run in the nightly workflow.
See .github/workflows/ci.yml and .github/workflows/nightly.yml for details.
We follow the xlsynth Verilog Style Guide, which is a fork of the lowRISC style guide with some minor differences.
This repository defines several generally-helpful Bazel Verilog rules that you can use in your own projects.
The verilog_library rule is used to collect Verilog source and header files and track their dependencies.
The original definition of the verilog_library rule can be found here.
We pick up that rule dependency transitively (see the top-level MODULE.bazel).
Using verilog_library
load("@rules_hdl//verilog:providers.bzl", "verilog_library")
verilog_library(
name = "bar",
srcs = ["bar.sv"],
hdrs = ["baz.svh"]
)
verilog_library(
name = "foo",
srcs = ["foo.sv"],
deps = [":bar"],
)Please see bazel/verilog_rules.md for documentation on rules defined in this repository.
Usage is best illustrated with an example using the bzlmod dependency management system in Bazel.
|
Tip
|
You are not required to use Bazel to depend on Bedrock-RTL. You can also use the Verilog files directly in your own projects (e.g., with git submodule, git subtree, or some other method). |
In your project’s MODULE.bazel:
MODULE.bazel
module(name = "your-project")
bazel_dep(name = "bedrock-rtl", version = "0.0.1")
git_override(
module_name = "bedrock-rtl",
commit = <fill_in_git_commit_sha>,
remote = "https://github.com/xlsynth/bedrock-rtl",
)
rules_hdl_extension = use_extension("@bedrock-rtl//dependency_support/rules_hdl:extension.bzl", "rules_hdl_extension")
use_repo(rules_hdl_extension, "rules_hdl")If you need to redirect the rules_hdl wrapper module in a larger Bazel dependency graph,
load the extension through a root-module rules_hdl dependency instead:
bazel_dep(name = "rules_hdl", version = "0.0.0", repo_name = "rules_hdl_wrapper_module")
# Use an override only if your root module needs to provide the rules_hdl wrapper
# from a local checkout or another non-registry source.
local_path_override(
module_name = "rules_hdl",
path = "<path-to-rules-hdl-wrapper>",
)
rules_hdl_extension = use_extension("@rules_hdl_wrapper_module//:extension.bzl", "rules_hdl_extension")
use_repo(rules_hdl_extension, "rules_hdl")|
Note
|
Bazel only applies module overrides from the root module. If rules_hdl is available
from your configured Bazel registries, no override is needed. Otherwise, your root
MODULE.bazel must provide the rules_hdl module with an override such as
local_path_override, archive_override, or git_override. The direct
@bedrock-rtl//dependency_support/rules_hdl:extension.bzl load shown in the main example
keeps @rules_hdl//… labels available, but root-module overrides for rules_hdl will not
affect that direct extension load.
|
Then suppose you have the following SystemVerilog module called datapath_join.sv:
datapath_join.sv
// An example design using two Bedrock-RTL modules: br_flow_reg_fwd and br_flow_join.
//
// Joins two or more equal-width datapaths into a single output datapath.
// Uses ready/valid protocol on all flows.
// Push-side is registered.
`include "br_asserts.svh"
module datapath_join #(
parameter int NumFlows = 2, // must be at least 2
parameter int WidthPerFlow = 32 // must be at least 1
) (
input logic clk,
input logic rst,
output logic [NumFlows-1:0] push_ready,
input logic [NumFlows-1:0] push_valid,
input logic [NumFlows-1:0][WidthPerFlow-1:0] push_data,
input logic pop_ready,
output logic pop_valid,
output logic [(NumFlows*WidthPerFlow)-1:0] pop_data
);
`BR_ASSERT_STATIC(numflows_gte_2_a, NumFlows >= 2)
`BR_ASSERT_STATIC(widthperflow_gte_1_a, WidthPerFlow >= 1)
logic [NumFlows-1:0] inter_ready;
logic [NumFlows-1:0] inter_valid;
logic [NumFlows-1:0][WidthPerFlow-1:0] inter_data;
for (genvar i = 0; i < NumFlows; i++) begin : gen_regs
br_flow_reg_fwd #(
.Width(WidthPerFlow)
) br_flow_reg_fwd (
.clk,
.rst,
.push_ready(push_ready[i]),
.push_valid(push_valid[i]),
.push_data (push_data[i]),
.pop_ready (inter_ready[i]),
.pop_valid (inter_valid[i]),
.pop_data (inter_data[i])
);
end
br_flow_join #(
.NumFlows(NumFlows)
) br_flow_join (
.clk,
.rst,
.push_ready(inter_ready),
.push_valid(inter_valid),
.pop_ready (pop_ready),
.pop_valid (pop_valid)
);
assign pop_data = inter_data; // direct concat
endmodule : datapath_joinYour BUILD.bazel file could then do this:
BUILD.bazel
load("@bedrock-rtl//bazel:verilog.bzl", "verilog_elab_and_lint_test_suite", "verilog_elab_test", "verilog_lint_test")
load("@rules_hdl//verilog:providers.bzl", "verilog_library")
package(default_visibility = ["//visibility:private"])
verilog_library(
name = "datapath_join",
srcs = ["datapath_join.sv"],
deps = [
"@bedrock-rtl//flow/rtl:br_flow_join",
"@bedrock-rtl//flow/rtl:br_flow_reg_fwd",
"@bedrock-rtl//macros:br_asserts",
],
)
verilog_elab_test(
name = "datapath_join_elab_test",
tool = "slang",
deps = [":datapath_join"],
)
verilog_lint_test(
name = "datapath_join_lint_test",
deps = [":datapath_join"],
)
verilog_elab_and_lint_test_suite(
name = "datapath_join_test_suite",
params = {
"NumFlows": [
"2",
"3",
],
"WidthPerFlow": [
"1",
"64",
],
},
deps = [":datapath_join"],
)See SystemVerilog Macros for the list of available macros and defines.
See RTL Libraries for the list of available public RTL modules, packages, and formal libraries.
