CMake Build System
- Quick Start
- beethoven_hardware()
- beethoven_testbench()
- Examples & Troubleshooting
Why Unified CMake?
Traditional Beethoven development required separate steps: run sbt to generate hardware, then separately build C++ testbenches. The unified CMake system combines these into a single build:
cmake .. && make
This workflow:
- Automatically invokes sbt to generate hardware when Scala sources change
- Manages dependencies between hardware generation and C++ compilation
- Keeps generated files in project-local directories (no global
BEETHOVEN_PATHpollution) - Integrates verilator/icarus/VCS simulation into the build graph
Prerequisites
Install Beethoven software library:
cd Beethoven-Software
mkdir build && cd build
cmake .. -DPLATFORM=discrete -DBEETHOVEN_HARDWARE_PATH=/path/to/Beethoven-Hardware
make -j
sudo make install
This installs CMake modules that provide the beethoven_hardware() and beethoven_testbench() functions.
Basic Usage
Minimal Project
Create CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
project(my_accelerator)
set(CMAKE_CXX_STANDARD 20)
find_package(beethoven REQUIRED)
# Generate hardware from Scala
beethoven_hardware(my_accel
MAIN_CLASS com.example.MyAccelBuild
PLATFORM discrete
)
# Build testbench
beethoven_testbench(my_test
SOURCES test.cc
HARDWARE my_accel
SIMULATOR verilator
)
Build:
mkdir build && cd build
export BEETHOVEN_HARDWARE_PATH=/path/to/Beethoven-Hardware
cmake ..
make # Generates hardware, builds sim, compiles test
./my_test # Run
Environment Variables
| Variable | Purpose | Required |
|---|---|---|
BEETHOVEN_HARDWARE_PATH | Path to Beethoven-Hardware sbt project | For unified builds |
VCS_HOME | Synopsys VCS installation | If using VCS simulator |
The system searches for BEETHOVEN_HARDWARE_PATH in order:
- Environment variable
- Installed config (
/usr/local/share/beethoven/beethoven_paths.cmake) - Source tree (
${CMAKE_SOURCE_DIR}/build.sbt)
File Locations
Generated Hardware
<source_dir>/build/gen/<target>_HW_DIR/
hw/
*.v # Verilog modules
build/
beethoven_hardware.h # C++ bindings
beethoven_hardware.cc
cmake_srcs.cmake # Source list
.beethoven_generated_<target> # Stamp file
Build Artifacts
<build_dir>/
<target> # Executable
<target>_sim/ # Simulator output
Vobj_dir/ # Verilator objects (if verilator)
sim.vvp # Icarus binary (if icarus)
CMakeFiles/
beethoven_hardware()
Generates RTL and C++ bindings from your Scala/Chisel configuration.
Syntax
beethoven_hardware(<target_name>
MAIN_CLASS <scala_main_class>
[PLATFORM <discrete|kria|baremetal>]
[BUILD_MODE <Simulation|Synthesis>]
[SCALA_ARGS <-Darg1=val1> <-Darg2=val2> ...]
)
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
MAIN_CLASS | Yes | - | Scala main class extending BeethovenBuild |
PLATFORM | No | discrete | Target platform |
BUILD_MODE | No | Simulation | Build mode (affects memory models) |
SCALA_ARGS | No | - | Arguments passed to sbt (-DPARAM=value) |
How It Works
- Creates local
BEETHOVEN_PATHat${CMAKE_SOURCE_DIR}/build/gen/${target}_HW_DIR/ - Runs:
cd Beethoven-Hardware && BEETHOVEN_PATH=... sbt runMain ${MAIN_CLASS} - Generates:
hw/*.v- Verilog modulesbuild/beethoven_hardware.{h,cc}- C++ bindingsbuild/cmake_srcs.cmake- Generated source list
- Creates CMake target
${target}_hwfor dependency tracking - Exports
${target}_BEETHOVEN_PATHvariable for use bybeethoven_testbench()
Platform-Specific Generation
Discrete (Simulation):
beethoven_hardware(sim_accel
MAIN_CLASS com.example.MyAccelSimBuild
PLATFORM discrete
BUILD_MODE Simulation
)
Generates with DRAMsim3 memory models.
Kria (FPGA):
beethoven_hardware(fpga_accel
MAIN_CLASS com.example.MyAccelFPGABuild
PLATFORM kria
BUILD_MODE Synthesis
)
Generates with Xilinx BRAM/URAM primitives and AXI interfaces.
Parameterized Builds
Pass parameters to Scala via SCALA_ARGS:
beethoven_hardware(param_accel
MAIN_CLASS com.example.ParameterizedBuild
SCALA_ARGS -DROWS=256 -DCOLS=512 -DFREQ=500
)
Access in Scala:
object ParameterizedBuild extends BeethovenBuild(...) {
val rows = BuildArgs.args.getOrElse("ROWS", "128").toInt
val cols = BuildArgs.args.getOrElse("COLS", "256").toInt
}
beethoven_testbench()
Builds C++ testbench with verilated hardware and simulator.
Syntax
beethoven_testbench(<target_name>
SOURCES <source1.cc> [<source2.cc> ...]
HARDWARE <hardware_target>
[SIMULATOR <verilator|icarus|vcs>]
[DRAMSIM_CONFIG <path/to/config.ini>]
)
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
SOURCES | Yes | - | C++ testbench source files |
HARDWARE | Yes | - | Name of beethoven_hardware() target |
SIMULATOR | No | verilator | Simulation backend |
DRAMSIM_CONFIG | No | (default DDR4) | DRAMsim3 config for memory timing |
What It Does
- Validates that
HARDWAREtarget exists - Creates simulator target
${target}_sim:- Verilator: Verilates Verilog to C++ model
- Icarus: Compiles with iverilog
- VCS: Generates VCS shared library
- Creates executable target
${target}:- Links your C++ sources
- Links generated
beethoven_hardware.cc - Links beethoven runtime library
- Links simulator output
- Sets dependencies:
${target}depends on${target}_simdepends on${HARDWARE}_hw
Simulator Options
Verilator (Default):
- Fast compilation, fast simulation
- Best for development iteration
SIMULATOR verilator
Icarus Verilog:
- Slower simulation, easier waveform debugging
- Good for signal-level debugging
SIMULATOR icarus
VCS (Commercial):
- Requires Synopsys VCS license
- Most accurate timing, good for tapeout validation
SIMULATOR vcs
Memory Timing Simulation
Specify custom DRAM timing with DRAMSIM_CONFIG:
beethoven_testbench(timing_test
SOURCES test.cc
HARDWARE my_accel
DRAMSIM_CONFIG ${CMAKE_SOURCE_DIR}/configs/ddr4_2400.ini
)
DRAMsim3 configs control:
- Clock frequency
- Row/column buffer sizes
- Refresh rates
- Bank/rank organization
Multiple Configurations
Build for multiple platforms from single source:
# Simulation build
beethoven_hardware(sim MAIN_CLASS MyAccelSim PLATFORM discrete)
beethoven_testbench(test_sim SOURCES test.cc HARDWARE sim)
# FPGA build
beethoven_hardware(fpga MAIN_CLASS MyAccelFPGA PLATFORM kria BUILD_MODE Synthesis)
beethoven_testbench(test_fpga SOURCES test.cc HARDWARE fpga)
Legacy Functions
For projects with pre-generated hardware (not using unified workflow):
beethoven_build()
Link pre-generated hardware to executable:
beethoven_build(my_test
SOURCES test.cc
PATH /path/to/pregenerated
)
Requires BEETHOVEN_PATH environment variable pointing to generated files.
beethoven_library()
Create shared library with hardware:
beethoven_library(accel_lib
SOURCES lib.cc api.cc
PATH /path/to/pregenerated
)
Example Projects
Simple Accelerator
cmake_minimum_required(VERSION 3.15)
project(simple_accel)
set(CMAKE_CXX_STANDARD 20)
find_package(beethoven REQUIRED)
beethoven_hardware(accel
MAIN_CLASS examples.VectorAddBuild
PLATFORM discrete
)
beethoven_testbench(test
SOURCES test.cc
HARDWARE accel
)
Parameterized Multi-Core
beethoven_hardware(multicore
MAIN_CLASS examples.MultiCoreBuild
SCALA_ARGS -DCORES=4 -DFREQ=500
)
beethoven_testbench(bench_4core
SOURCES bench.cc workload.cc
HARDWARE multicore
SIMULATOR verilator
)
Custom DRAM Timing
beethoven_testbench(timing_test
SOURCES test.cc
HARDWARE accel
DRAMSIM_CONFIG ${PROJECT_SOURCE_DIR}/configs/hbm2e.ini
)
Troubleshooting
"Cannot find Beethoven-Hardware sbt project"
Set BEETHOVEN_HARDWARE_PATH:
export BEETHOVEN_HARDWARE_PATH=/path/to/Beethoven-Hardware
Hardware not regenerating after Scala changes
CMake doesn't track Scala source dependencies. Force regeneration:
rm build/gen/<target>_HW_DIR/.beethoven_generated_*
make
Simulator compilation fails
Check simulator is installed and in PATH:
verilator --version # For verilator
iverilog -v # For icarus
vcs -ID # For VCS
Link errors with beethoven library
Ensure beethoven software is installed:
cd Beethoven-Software/build
sudo make install
Related Documentation
- Software Stack - Runtime API and testbench development
- Hardware Stack - Scala/Chisel accelerator design
- IDE Setup - CLion CMake integration