title: Build Software

Prerequisites

Make sure you followed the install instructions to [prepare the system]({{< relref “install_instructions#system-preparation” >}}) and install the [compiler toolchain]({{< relref “install_instructions#compiler-toolchain” >}}).

Building software

OpenTitan software is built using Meson. 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 boot_rom and hello_world binaries for FPGA:

# Configure the Meson environment.
$ cd $REPO_TOP
$ ./meson_init.sh

# Build the two targets we care about, specifically.
$ ninja -C build-out sw/device/boot_rom/boot_rom_export_fpga_nexysvideo
$ ninja -C build-out sw/device/examples/hello_world/hello_world_export_fpga_nexysvideo

# 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 building the foo_export_$DEVICE target.

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.

$ ./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

$ ./meson_init.sh -r

Bringing your own toolchain

./meson_init.sh needs to know where the toolchain you are using is, and which tools from it should be used.

If you are using the lowrisc-provided toolchain (obtained with get-toolchain.py), and it is installed in the default location (/tools/riscv), then ./meson_init.sh does not need additional configuration.

If you are using the lowrisc-provided toolchain, but have located it in a non-default location (using get-toolchain.py -t /path/to/lowrisc/toolchain), you can use the environment variable TOOLCHAIN_PATH to point to your toolchain location, like so:

$ export TOOLCHAIN_PATH=/path/to/lowrisc/toolchain
$ ./meson_init.sh

If you have moved a lowrisc-provided toolchain (obtained with get-toolchain.py), you will need to update paths within the meson toolchain configuration files within the toolchain installation. These are called meson-<triple>-<compiler>.txt, and are present in toolchains since version 20200602-1. You can still use TOOLCHAIN_PATH to point to the toolchain location if you have updated the paths within these files.

If you have built your own toolchain by following option 2 under [Installing Software Build Requirements]({{< relref “doc/ug/install_instructions/index#device-compiler-toolchain-rv32imc” >}}), then you need to point ./meson_init.sh to your custom toolchain file using -t FILE:

$ ./meson_init -t /path/to/toolchain/file

If you do not specify your own toolchain configuration file (using ./meson_init.sh -t), and meson_init.sh cannot find the default configuration in your toolchain, the legacy toolchain.txt from the main OpenTitan repository will be used. If TOOLCHAIN_PATH is set, this will be used to update any paths within the legacy configuration.

Debugging device software

Attaching a debugger

GDB can be used to debug device software running on an FPGA or in a Verilator simulation. Refer to the [Getting started on FPGAs]({{}}) and [Verilator]({{}}) guides for more details.

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.

$ riscv32-unknown-elf-objdump --disassemble-all --headers --line-numbers --source build-bin/sw/device/tests/dif_uart_smoketest_sim_verilator.elf

Refer to the output of riscv32-unknown-elf-objdump --help for a full list of options.