Comprehensive documentation for building Bluespec SystemVerilog (BSV) projects with Buck2 in the Quartz repository.
- Overview
- Quick Start
- Build Rules Reference
- Migration from Cobble
- Best Practices
- Troubleshooting
- Advanced Topics
- Additional Resources
The BSV Buck2 build system provides a modern, efficient build infrastructure for Bluespec SystemVerilog projects, replacing the legacy cobble system. It offers:
- Fast Incremental Builds - Buck2's caching means rebuilds are nearly instant
- Parallel Execution - Multiple targets build simultaneously
- Transitive Dependencies - Automatic dependency graph resolution
- RDL Integration - SystemRDL register definitions generate BSV packages
- Simulation Support - Bluesim testbenches build and run seamlessly
- Verilog Generation - Synthesizable Verilog output for FPGA tools
BSV Source Files (.bsv)
↓
[bsv_library] ← Compiles to .bo (Bluespec Object) files
↓
┌────┴────┐
↓ ↓
[bsv_verilog] [bsv_sim] ← Generate Verilog or Bluesim bytecode
↓ ↓
.v files [bsv_bluesim_binary] ← Link executable testbench
↓
Run simulation
RDL Files (.rdl)
↓
[rdl_file] ← Generate BSV packages, HTML docs, JSON
↓
BSV Package (.bsv)
# Buck2 installed
cargo +nightly-2024-10-13 install --git https://github.com/facebook/buck2.git --tag "2025-02-01" buck2
# Bluespec compiler in PATH
which bsc
# Python packages for RDL
pip install -r tools/requirements.txt
# Optional: Set custom Bluespec library directory in .buckconfig.local
# [bsv]
# libdir = /path/to/bluespec/lib
# (See "Toolchain Configuration" in Advanced Topics for details)- Create a simple BSV module:
// MyModule.bsv
package MyModule;
function Bit#(8) addOne(Bit#(8) x);
return x + 1;
endfunction
endpackage- Create a BUCK file:
load("//tools:bsv.bzl", "bsv_library")
bsv_library(
name = "MyModule",
srcs = ["MyModule.bsv"],
deps = [],
)- Build it:
buck2 build //path/to:MyModule- Success! Your module is now compiled to a .bo file.
- Add a test module:
// MyModuleTest.bsv (add to MyModule.bsv)
module mkMyModuleTest (Empty);
Reg#(Bit#(32)) cycle <- mkReg(0);
rule run;
cycle <= cycle + 1;
if (cycle < 10) begin
$display("Result: %0d", addOne(truncate(cycle)));
end else begin
$finish;
end
endrule
endmodule- Update BUCK file:
load("//tools:bsv.bzl", "bsv_library", "bsv_sim", "bsv_bluesim_binary")
bsv_library(
name = "MyModule",
srcs = ["MyModule.bsv"],
deps = [],
)
bsv_sim(
name = "mymodule_sim",
top = "MyModule.bsv",
modules = ["mkMyModuleTest"],
deps = [":MyModule"],
)
bsv_bluesim_binary(
name = "mymodule_test",
top = ":mymodule_sim",
entry_point = "mkMyModuleTest",
deps = [":MyModule"],
)- Run the test:
buck2 run //path/to:mymodule_testCompiles BSV source files into .bo (Bluespec Object) files.
bsv_library(
name = "LibraryName",
srcs = ["File1.bsv", "File2.bsv"],
deps = [
":OtherLibrary",
"//hdl/ip/bsv:CommonFunctions",
],
bsc_flags = ["-opt-undetermined-vals"],
is_synth = True,
)Attributes:
name(required): Target namesrcs(required): List of .bsv source filesdeps(optional): List of library dependenciesbsc_flags(optional): Additional compiler flagsis_synth(optional): Whether for synthesis (default: True)
Outputs:
.bofiles in a unique output directory- Cached for incremental builds
Example:
bsv_library(
name = "I2CController",
srcs = ["I2CController.bsv"],
deps = [
":I2CCommon",
"//hdl/ip/bsv:Strobe",
],
)Generates synthesizable Verilog from BSV modules.
bsv_verilog(
name = "verilog_output",
top = "TopModule.bsv",
modules = ["mkModuleA", "mkModuleB"],
deps = [":LibraryName"],
bsc_flags = [
"-opt-undetermined-vals",
"-unspecified-to", "X",
],
)Attributes:
name(required): Target nametop(required): Top-level BSV filemodules(required): List of module names to generatedeps(required): Library dependenciesbsc_flags(optional): Verilog generation flags
Outputs:
.vVerilog files (one per module)- Separate directories for each module
Example:
bsv_verilog(
name = "transceiver_verilog",
top = "IgnitionTransceiver.bsv",
modules = ["mkTransceiver"],
deps = [":Transceiver"],
bsc_flags = ["-opt-undetermined-vals"],
)Generates Bluesim bytecode for simulation.
bsv_sim(
name = "simulation",
top = "TestBench.bsv",
modules = ["mkTestBench"],
deps = [":LibraryName"],
bsc_flags = [],
)Attributes:
name(required): Target nametop(required): Top-level BSV file with testbenchmodules(required): List of testbench module namesdeps(required): Library dependenciesbsc_flags(optional): Simulation compiler flags
Outputs:
.baBluesim bytecode files- Used by
bsv_bluesim_binaryfor linking
Example:
bsv_sim(
name = "i2c_controller_sim",
top = "test/I2CControllerTests.bsv",
modules = ["mkI2CReadTest", "mkI2CWriteTest"],
deps = [":I2CController", ":I2CModel"],
)Links Bluesim bytecode into an executable testbench.
bsv_bluesim_binary(
name = "test_executable",
top = ":simulation",
entry_point = "mkTestBench",
deps = [":LibraryName"],
)Attributes:
name(required): Executable nametop(required): Reference tobsv_simtargetentry_point(required): Top module name to executedeps(required): Library dependencies
Outputs:
- Executable shell script
.soshared library
Usage:
buck2 run //path/to:test_executableExample:
bsv_bluesim_binary(
name = "mdio_test",
top = ":mdio_sim",
entry_point = "mkMDIOReadTest",
deps = [":MDIO", ":MDIOModel"],
)Convenience macro that creates both bsv_sim and multiple bsv_bluesim_binary targets.
load("//tools:bsv.bzl", "bsv_bluesim_tests")
bsv_bluesim_tests(
name = "MyTests",
top = "test/MyTests.bsv",
modules = [
"mkTest1",
"mkTest2",
"mkTest3",
],
deps = [":MyLibrary"],
)Generates:
- One
bsv_simtarget namedMyTests - Individual
bsv_bluesim_binarytargets:MyTests_mkTest1,MyTests_mkTest2,MyTests_mkTest3
Run all tests:
buck2 run //path/to:MyTests_mkTest1
buck2 run //path/to:MyTests_mkTest2
buck2 run //path/to:MyTests_mkTest3Generates BSV packages and documentation from SystemRDL register definitions.
load("//tools:rdl.bzl", "rdl_file")
rdl_file(
name = "my_regs_rdl",
src = "my_regs.rdl",
outputs = [
"my_regs_pkg.bsv",
"my_regs.html",
"my_regs.json",
],
deps = [],
)Attributes:
name(required): Target name (must end with_rdl)src(required): Single .rdl source fileoutputs(required): List of output files to generatedeps(optional): Other RDL file dependencies
Output Formats:
.bsv- BSV package with registers, structs, and helper functions.html- Interactive HTML documentation.json- JSON metadata for tooling
Naming Convention:
- Target name must end with
_rdl - Source file must match:
<target_name_without_rdl>.rdl - BSV output:
<target_name_without_rdl>_pkg.bsv
Example:
rdl_file(
name = "sequencer_regs_rdl",
src = "sequencer_regs.rdl",
outputs = [
"sequencer_regs_pkg.bsv",
"sequencer_regs.html",
"sequencer_regs.json",
],
)| Cobble Rule | Buck2 Rule | Notes |
|---|---|---|
bluespec_library |
bsv_library |
Direct mapping |
bluespec_verilog |
bsv_verilog |
Remove env attribute |
bluespec_sim |
bsv_sim |
Direct mapping |
bluesim_binary |
bsv_bluesim_binary |
Add explicit entry_point |
bluesim_tests |
bsv_bluesim_tests |
Direct mapping |
rdl |
rdl_file |
Target name must end with _rdl |
| Cobble | Buck2 | Change |
|---|---|---|
sources |
srcs |
Renamed |
suite |
top |
Renamed (for tests) |
env |
(removed) | No longer needed |
using |
bsc_flags |
Extract from dict |
local |
bsc_flags |
Extract from dict |
Use the conversion tool:
python3 tools/build_to_buck_converter.py hdl/ip/bsv/MyModule/BUILDThis generates a BUCK file automatically. Review and adjust:
- Add
entry_pointtobsv_bluesim_binaryrules - Verify dependency paths
- Check for
<expression>placeholders
Before (Cobble BUILD):
bluespec_library('I2CController',
sources = ['I2CController.bsv'],
deps = [
':I2CCommon',
'//hdl/ip/bsv:Strobe',
])
bluesim_tests('I2CTests',
env = '//bluesim_default',
suite = 'test/I2CTests.bsv',
modules = [
'mkI2CReadTest',
'mkI2CWriteTest',
],
deps = [':I2CController'])After (Buck2 BUCK):
load("//tools:bsv.bzl", "bsv_library", "bsv_bluesim_tests")
bsv_library(
name = "I2CController",
srcs = ["I2CController.bsv"],
deps = [
":I2CCommon",
"//hdl/ip/bsv:Strobe",
],
)
bsv_bluesim_tests(
name = "I2CTests",
top = "test/I2CTests.bsv",
modules = [
"mkI2CReadTest",
"mkI2CWriteTest",
],
deps = [":I2CController"],
)Key Changes:
- Python → Starlark syntax
sources→srcssuite→top- Removed
envattribute - Added
load()statement at top
hdl/ip/bsv/
├── MyProject/
│ ├── BUCK # Build definitions
│ ├── MyModule.bsv # Main module
│ ├── MyHelper.bsv # Helper modules
│ ├── my_regs.rdl # Register definitions (if needed)
│ └── test/
│ ├── MyModuleTest.bsv # Test benches
│ └── MyModel.bsv # Simulation models
- Library targets: Use module name (e.g.,
"I2CController") - Verilog targets: Append
_verilog(e.g.,"controller_verilog") - Simulation targets: Append
_sim(e.g.,"controller_sim") - Test executables: Append
_test(e.g.,"controller_test") - RDL targets: Must end with
_rdl(e.g.,"my_regs_rdl")
-
Use local references for targets in the same BUCK file:
deps = [":LocalTarget"]
-
Use absolute paths for cross-directory dependencies:
deps = ["//hdl/ip/bsv:CommonFunctions"]
-
Order matters: List direct dependencies only; transitive deps are automatic
-
Avoid circular dependencies: Refactor shared code into a common library
Common BSV compiler flags:
bsc_flags = [
"-opt-undetermined-vals", # Optimize undetermined values
"-unspecified-to", "X", # Set unspecified values to X
"-aggressive-conditions", # Optimize conditions
"-check-assert", # Enable assertion checking
]For Verilog generation:
bsc_flags = [
"-opt-undetermined-vals",
"-unspecified-to", "X",
"-remove-dollar", # Remove $ from identifiers
]-
Unit Tests: One test per module function
bsv_bluesim_tests( name = "UnitTests", top = "test/UnitTests.bsv", modules = ["mkAddTest", "mkSubTest", "mkMulTest"], deps = [":MathModule"], )
-
Integration Tests: Test module interactions
bsv_bluesim_binary( name = "integration_test", top = ":integration_sim", entry_point = "mkIntegrationTest", deps = [":ModuleA", ":ModuleB", ":ModuleC"], )
-
Run tests regularly:
buck2 run //path/to:UnitTests_mkAddTest
Cause: Dependency not in search path
Solution: Add missing dependency to deps:
deps = [":MissingModule"]Cause: bsv_bluesim_binary needs explicit entry point
Solution: Add entry_point parameter:
bsv_bluesim_binary(
name = "test",
top = ":sim",
entry_point = "mkTestModule", # Add this
deps = [":Library"],
)Cause: RDL target doesn't follow naming convention
Solution: Rename target to end with _rdl:
rdl_file(
name = "my_regs_rdl", # Must end with _rdl
src = "my_regs.rdl",
...
)-
Check Buck2 daemon status:
buck2 status
-
Restart daemon if needed:
buck2 kill buck2 build //your/target -
Check for excessive dependencies
Clear Buck2 cache:
buck2 cleanFull rebuild:
buck2 clean && buck2 build //your/target-
View build commands:
buck2 build //your/target -v 5
-
Check target outputs:
buck2 targets //your/path: --show-output
-
Inspect dependencies:
buck2 query "deps(//your:target)" -
Find reverse dependencies:
buck2 query "rdeps(//..., //your:target)"
RDL files generate BSV packages automatically:
// my_regs.rdl
addrmap my_regs {
reg {
field { desc = "Enable bit"; } EN[0:0] = 0;
} CTRL @ 0x0;
};
Generated BSV usage:
import my_regs_pkg::*;
Reg#(Ctrl) ctrlReg <- mkReg(defaultValue);
ctrlReg.en <= 1'b1; // Set enable bitProject-specific flags:
bsv_library(
name = "CustomModule",
srcs = ["CustomModule.bsv"],
bsc_flags = [
"-D", "CUSTOM_WIDTH=32", # Define macro
"-Xc++", "-Wno-unused", # C++ compiler flags
],
)The BSV toolchain reads the Bluespec library path from the [bsv] libdir key in .buckconfig. The default is /opt/bsc-2022.01/lib (matching the CI runner environment). To override it locally, create a .buckconfig.local (which is gitignored):
# .buckconfig.local
[bsv]
libdir = /opt/bluespec/libYou can also override on the command line:
buck2 build -c bsv.libdir=/opt/bluespec/lib //hdl/ip/bsv:YourModuleConfiguration Details:
- Config Key:
[bsv] libdir - Default Value:
/opt/bsc-2022.01/lib - Config Location:
.buckconfig(override in.buckconfig.local) - Toolchain File:
toolchains/bsv_toolchain.bzl
Override in BUCK files (if needed):
# toolchains/BUCK
bsv_toolchain(
name = "bsv",
libdir = "/custom/bluespec/path",
visibility = ["PUBLIC"],
)This is useful when:
- Using non-standard Bluespec installations
- Testing with different Bluespec versions
- Working in environments with custom toolchain paths
Buck2 automatically parallelizes:
# Build multiple targets in parallel
buck2 build //path/to:target1 //path/to:target2 //path/to:target3Reference targets in other projects:
deps = [
"//hdl/ip/bsv:CommonFunctions",
"//hdl/projects/ignition:Protocol",
]- Quick Start Tutorial: docs/BSV_QUICK_START.md - Step-by-step guide to creating your first BSV project
- Migration Examples: docs/BSV_MIGRATION_EXAMPLES.md - Real project conversions from BUILD to BUCK
- Optimization Guide: docs/BSV_OPTIMIZATION_GUIDE.md - Performance tuning and advanced features
- Integration Tests: See
hdl/ip/bsv/integration_test/for working examples - Conversion Tool:
tools/build_to_buck_converter.py- Automated BUILD→BUCK conversion - Test Discovery:
tools/bsv-tests.bxl- BXL script for finding and running tests - Benchmarking:
tools/bsv_benchmark.sh- Compare Buck2 vs cobble performance - Rule Definitions:
tools/bsv.bzl- BSV build rule implementations - Buck2 Documentation: https://buck2.build/
- Check integration tests:
hdl/ip/bsv/integration_test/ - Review this guide: Re-read relevant sections
- Examine working examples: Look at existing BUCK files
- Use conversion tool:
python3 tools/build_to_buck_converter.py --dry-run BUILD
Last Updated: Phase 6 - Documentation & Migration