blob: cf17e5a00eb6bcfa05178ed3c8995ce616584ed2 [file] [log] [blame] [view]
# Writing and building software for OTBN
OTBN is the OpenTitan Big Number accelerator and this document describes how to write and build software for it.
The [OTBN reference manual](../../../hw/ip/otbn/README.md) describes the hardware and associated ISA.
Since OTBN is designed for cryptographic offload, not general computation, it has no C compiler.
However, it does have the usual binutils programs: an assembler, linker and disassembler.
The core of OTBN's instruction set is based on RV32I, the RISC-V base integer instruction set.
As such, we implement the toolchain as a thin wrapper around RISC-V binutils.
## Assembly format and examples
The OTBN ISA and programmer model are described in the [OTBN reference manual](../../../hw/ip/otbn/README.md).
In particular, note that the register `x1` has special stack-like behaviour.
There are some example programs at `sw/otbn/code-snippets`.
These range from simple examples of how to use pseudo-operations up to example cryptographic primitives.
For specific formatting and secure coding guidelines, see the [OTBN style guide](../style_guides/otbn_style_guide.md).
## The tools
### Assembler
The OTBN assembler is called `otbn_as.py` and can be found at `hw/ip/otbn/util/otbn_as.py`.
This has the same command line interface as `riscv32-unknown-elf-as` (indeed, it's a wrapper around that program).
The only difference in default flags is that `otbn_as.py` passes `-mno-relax`, telling the assembler not to request linker relaxation.
This is needed because one of these relaxations generates GP-relative loads, which assume `x3` is treated as a global pointer (not true for OTBN code).
To assemble some code in `foo.s` to an ELF object called `foo.o`, run:
```shell
hw/ip/otbn/util/otbn_as.py -o foo.o foo.s
```
### Linker
The OTBN linker is called `otbn_ld.py` and can be found at `hw/ip/otbn/util/otbn_ld.py`.
This is a thin wrapper around `riscv32-unknown-elf-ld`, but supplies a default linker script that matches the OTBN memory layout.
This linker script creates `.start`, `.text` and `.data` output sections.
The `.start` and `.text` sections go to IMEM, with `.start` coming first.
The `.data` section goes to DMEM.
Since OTBN has a strict Harvard architecture with IMEM and DMEM both starting at address zero, the `.start` and the `.data` sections will both start at VMA zero.
The instruction and data segments have distinct LMAs (for addresses, see the IMEM and DMEM windows at `hw/ip/otbn/data/otbn.hjson`).
Since the entry point for OTBN is always address zero, the entry vector should be the one and only thing in the `.start` section.
To achieve that, put your entry point (and nothing else) in the `.text.start` input section like this:
```asm
.section .text.start
jal x0, main
.text
...
```
This ensure that, even if there are multiple objects being linked together, the intended entry point will appear in the right place.
To link ELF object files to an OTBN ELF binary, run
```shell
hw/ip/otbn/util/otbn_ld.py -o foo foo0.o foo1.o foo2.o
```
### Objdump
To disassemble OTBN code, use `otbn_objdump.py`, which can be found at `hw/ip/otbn/util/otbn_objdump.py`.
This wraps `riscv32-unknown-elf-objdump`, but correctly disassembles OTBN instructions when run with the `-d` flag.
To disassemble the ELF binary linked in the previous section, run
```shell
hw/ip/otbn/util/otbn_objdump.py -d foo
```