blob: 8593f89ac61122bc28ffc9c00a624dde5eb29d70 [file] [log] [blame] [view]
---
title: Building Software
aliases:
- /doc/ug/getting_started_build_sw
---
_Before following this guide, make sure you've followed the [dependency installation instructions]({{< relref "getting_started" >}})._
## Building software with Meson
OpenTitan software is built using [Meson](https://mesonbuild.com).
However, Meson is not an exact fit for a lot of things OpenTitan does (such as distinguishing between FPGA, ASIC, and simulations), so the setup is a little bit different.
For example, the following commands build the `test_rom` and `hello_world` binaries for FPGA:
```console
# Configure the Meson environment.
cd $REPO_TOP
./meson_init.sh
# Build the two targets we care about, specifically.
ninja -C build-out sw/device/lib/testing/test_rom/test_rom_export_fpga_cw310
ninja -C build-out sw/device/examples/hello_world/hello_world_export_fpga_cw310
# Build *everything*, including targets for other devices.
ninja -C build-out all
```
Note that specific targets are followed by the device they are built for.
OpenTitan needs to link the same device executable for multiple devices, so each executable target is duplicated one for each device we support.
In general, `clean` rules are unnecessary, and Meson will set up `ninja` such that it reruns `meson.build` files which have changed.
Build intermediates will show up in `$REPO_TOP/build-out`, including unlinked object files and libraries, while completed executables are exported to `$REPO_TOP/build-bin`.
As a rule, you should only ever need to refer to artifacts inside of `build-bin`; the exact structure of `build-out` is subject to change.
Complete details of these semantics are documented in `util/build_consts.sh`.
The locations of `build-{out,bin}` can be controled by setting the `$BUILD_ROOT` enviromnent variable, which defaults to `$REPO_TOP`.
`./meson_init.sh` itself is idempotent, but this behavior can be changed with additional flags; see `./meson_init.sh` for more information.
For this reason, most examples involving Meson will include a call to `./meson_init.sh`, but you will rarely need to run it more than once per checkout.
Building an executable `foo` destined to run on the OpenTitan device `$DEVICE` will output the following files under `build-bin/sw/device`:
* `foo_$DEVICE.elf`: the linked program, in ELF format.
* `foo_$DEVICE.bin`: the linked program, as a plain binary with ELF debug information removed.
* `foo_$DEVICE.dis`: the disassembled program with inline source code.
* `foo_$DEVICE.vmem`: a Verilog memory file which can be read by `$readmemh()` in Verilog code.
In general, this executable is built by the `foo_export_$DEVICE` target.
For example, this builds the `pwrmgr_smoketest` test binary for DEVICE `sim_dv`:
```console
ninja -C build-out sw/device/tests/pwrmgr_smoketest_export_sim_dv
```
Building an executable destined to run on a host machine (i.e., under `sw/host`) will output a host excecutable under `build-bin/sw/host`, which can be run directly.
### Troubleshooting the build system
If you encounter an error running `./meson_init.sh` you could re-run using the `-f` flag which will erase any existing building directories to yield a clean build.
This sledgehammer is only intended to be used as a last resort when the existing configuration is seriously broken:
```console
./meson_init.sh -f
```
If any `meson.build` files are changed the configuration can be regenerated by passing the `-r` flag to `./meson_init.sh`:
```console
./meson_init.sh -r
```
## Building Software with Bazel
The Mask ROM and many other software targets are built with [Bazel](https://bazel.build/)
_TLDR: You can run:_
```console
$REPO_TOP/bazelisk.sh test //... --test_tag_filters=-cw310 --disk_cache=~/bazel_cache
```
### Installing Bazel
_Bazelisk and the script that installs it will work, but by installing Bazel you can get some quality of life features like tab completion._
This section is optional and can be skipped by setting the alias:
```console
alias bazel="$REPO_TOP/bazelisk.sh"
```
If you haven't yet installed Bazel, and would like to, you can add it to apt and install it on Ubuntu systems with the following commands [as described in the Bazel documentation](https://bazel.build/install/ubuntu):
```console
sudo apt install apt-transport-https curl gnupg
curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" |
sudo tee /etc/apt/sources.list.d/bazel.list
sudo apt update && sudo apt install bazel-4.2.0
```
or by following [instructions for your system](https://bazel.build/install).
### Invoking Bazel
You can then build and run all the tests all the Bazel rules for OpenTitan within the workspace with the command:
```console
bazel test //...
```
This is likely to include unhealthy or slow tests and build targets so you're likely to want to run more specific builds and tests.
This invocation instructs Bazel to build everything it needs to run all the tests in the workspace.
Specific targets can be built with:
```console
bazel build //<path to build file>:<name of target>
```
### Structure of our Bazel workspace
The rules for Bazel are described in a language called starlark, which looks a lot like Python.
The OpenTitan directory is defined as a Bazel workspace by the `//WORKSPACE` file.
`BUILD` files provide the information Bazel needs to build the targets in a directory.
`BUILD` files also manage any subdirectories that don't have their own `BUILD` files.
OpenTitan uses .bzl files to specify rules to build artifacts that require specific attention like target specific test rules and project specific binaries.
#### WORKSPACE file
The `WORKSPACE` file controls external dependencies such that builds can be made reproducible and hermetic.
Bazel loads specific commits of external git repositories.
It uses them to build OpenTitan targets (like it does with bazel\_embedded) or to satisfy dependencies (as it does with abseil).
To produce increasingly stable releases the `WORKSPACE` file will fix a number of these references to specific commits and not the default branch head.
Dependencies will be added to the workspace so builds and tests are less sensitive to external updates and have a simpler getting started flow.
#### BUILD files
Throughout the OpenTitan repo, `BUILD` files describe targets and dependencies in the same directory and subdirectories that lack their own `BUILD` files.
`BUILD` files are mostly hand-written; to suit the request that hand-written files not be included in autogen directories, there are large `BUILD` files that describe how to build and depend on auto-generated files in autogen subdirectories.
### Linting Starlark
The Linter for Bazel files is called "buildifier" and can be built and run with Bazel by running:
```console
bazel run //:buildifier_fix
```
Which will run the linter on all of the WORKSPACE, BUILD and .bzl files in the workspace.
It's fairly strict so the best way to keep git logs and blame reports clean is to run it before committing, and ask the same during code review.
### Test Execution
Bazel will execute tests when invoked with `bazel test <label-expression>`.
The test rules have various tags applied to them to allow for filtering of tests during dependency analysis.
Our tests are typically tagged with verilator, cw310, or manual.
The `--test_tag_filters` switch can be used to filter out tests that you either cannot or do not want to execute.
For example, if you do not have a CW310 FPGA board, you cannot execute the tests meant to execute on that board.
You can instruct Bazel to not execute those tests:
```console
bazel test --test_tag_filters=-cw310 //...
```
Rather than manually adding that command line switch to every invocation of bazel, you can add it to your `$HOME/.bazelrc` file:
```
test --test_tag_filters=-cw310
```
### Excluding Verilator
Verilator is slow to build and Verilator tests are slow to run.
Many directories have Verilator targets that will cause them to build Verilator when you build all targets with the wildcard `...`.
Tagging allows us to exclude Verilator tests, but doesn't exclude building Verilator for targets that depend on it.
To exclude all targets that depend on Verilator you should use a target pattern that can be generated with Bazel queries, and sed and then passed with xargs:
```console
bazel query "rdeps(//..., //hw:verilator)" \
| sed -e 's/^/-/' \
| xargs bazel test -- //...
```
The Bazel query lists all the dependencies in the workspace that depend on Verilator.
These are inverted for the target pattern by prepending a dash and then the test is invoked by xargs on a pattern which starts with the whole workspace and then excludes each of the targets that depend on Verilator.
### Caching on disk
By default Bazel caches in memory and will evict Verilator binaries often.
If you're regularly running tests that depend on Verilator and have a few GB of disk space available use the `--disk_cache=<filename>` to specify a cache:
```console
bazel build //... --disk_cache=~/bazel_cache
```
Alternatively add the following to `$HOME/.bazelrc`:
```
build --disk_cache=~/bazel_cache
```
## Disassembling device code
A disassembly of all executable sections is produced by the build system by default.
It can be found by looking for files with the `.dis` extension next to the corresponding ELF file.
To get a different type of disassembly, e.g. one which includes data sections in addition to executable sections, objdump can be called manually.
For example the following command shows how to disassemble all sections of the UART DIF smoke test interleaved with the actual source code:
```console
riscv32-unknown-elf-objdump --disassemble-all --headers --line-numbers --source build-bin/sw/device/tests/uart_smoketest_sim_verilator.elf
```
Refer to the output of `riscv32-unknown-elf-objdump --help` for a full list of options.