OpenTitan Big Number Accelerator (OTBN) Technical Specification
Note on the status of this document
This specification is work in progress and will see significant changes before it can be considered final. We invite input of all kind through the standard means of the OpenTitan project; a good starting point is filing an issue in our GitHub issue tracker.
Overview
This document specifies functionality of the OpenTitan Big Number Accelerator, or OTBN. OTBN is a coprocessor for asymmetric cryptographic operations like RSA or Elliptic Curve Cryptography (ECC).
This module conforms to the Comportable guideline for peripheral functionality. See that document for integration overview within the broader top level system.
Features
- Processor optimized for wide integer arithmetic
- 32b wide control path with 32 32b wide registers
- 256b wide data path with 32 256b wide registers
- Full control-flow support with conditional branch and unconditional jump instructions, hardware loops, and hardware-managed call/return stacks.
- Reduced, security-focused instruction set architecture for easier verification and the prevention of data leaks.
- Built-in access to random numbers. Note: The (quality) properties of the provided random numbers are not currently specified; this gap in the specification will be addressed in a future revision.
Description
OTBN is a processor, specialized for the execution of security-sensitive asymmetric (public-key) cryptography code, such as RSA or ECC. Such algorithms are dominated by wide integer arithmetic, which are supported by OTBN’s 256b wide data path, registers, and instructions which operate these wide data words. On the other hand, the control flow is clearly separated from the data, and reduced to a minimum to avoid data leakage.
The data OTBN processes is security-sensitive, and the processor design centers around that. The design is kept as simple as possible to reduce the attack surface and aid verification and testing. For example, no interrupts or exceptions are included in the design, and all instructions are designed to be executable within a single cycle.
OTBN is designed as a self-contained co-processor with its own instruction and data memory, which is accessible as a bus device.
Compatibility
OTBN is not designed to be compatible with other cryptographic accelerators. It received some inspiration from assembly code available from the Chromium EC project, which has been formally verified within the Fiat Crypto project.
Instruction Set
OTBN is a processor with a custom instruction set. The full ISA description can be found in our ISA manual. The instruction set is split into two groups:
- The base instruction subset operates on the 32b General Purpose Registers (GPRs). Its instructions are used for the control flow of a OTBN application. The base instructions are inspired by RISC-V’s RV32I instruction set, but not compatible with it.
- The big number instruction subset operates on 256b Wide Data Registers (WDRs). Its instructions are used for data processing.
Processor State
General Purpose Registers (GPRs)
OTBN has 32 General Purpose Registers (GPRs), each of which is 32b wide. The GPRs are defined in line with RV32I and are mainly used for control flow. They are accessed through the base instruction subset. GPRs aren’t used by the main data path; this operates on the wide data registers, a separate register file, controlled by the big number instructions.
x0 |
Zero register. Reads as 0; writes are ignored. |
x1 |
Access to the call stack |
x2 ... x31 |
General purpose registers |
Note: Currently, OTBN has no “standard calling convention,” and GPRs other than x0
and x1
can be used for any purpose.
If a calling convention is needed at some point, it is expected to be aligned with the RISC-V standard calling conventions, and the roles assigned to registers in that convention.
Even without a agreed-on calling convention, software authors are encouraged to follow the RISC-V calling convention where it makes sense.
For example, good choices for temporary registers are x6
, x7
, x28
, x29
, x30
, and x31
.
Call Stack
OTBN has an in-built call stack which is accessed through the x1
GPR.
This is intended to be used as a return address stack, containing return addresses for the current stack of function calls.
See the documentation for JAL
and JALR
for a description of how to use it for this purpose.
The call stack has a maximum depth of 8 elements.
Each instruction that reads from x1
pops a single element from the stack.
Each instruction that writes to x1
pushes a single element onto the stack.
An instruction that reads from an empty stack or writes to a full stack causes OTBN to stop, raising an alert and setting the ErrBitCallStack
bit in the ERR_BITS register.
A single instruction can both read and write to the stack. In this case, the read is ordered before the write. Providing the stack has at least one element, this is allowed, even if the stack is full.
Control and Status Registers (CSRs)
Control and Status Registers (CSRs) are 32b wide registers used for “special” purposes, as detailed in their description;
they are not related to the GPRs.
CSRs can be accessed through dedicated instructions, CSRRS
and CSRRW
.
Number | Privilege | Description | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0x7C0 | RW |
FG0.
Wide arithmetic flag group 0.
This CSR provides access to flag group 0 used by wide integer arithmetic.
FLAGS, FG0 and FG1 provide different views on the same underlying bits.
|
||||||||||||||||||
0x7C1 | RW |
FG1.
Wide arithmetic flag group 1.
This CSR provides access to flag group 1 used by wide integer arithmetic.
FLAGS, FG0 and FG1 provide different views on the same underlying bits.
|
||||||||||||||||||
0x7C8 | RW |
FLAGS.
Wide arithmetic flag groups.
This CSR provides access to both flags groups used by wide integer arithmetic.
FLAGS, FG0 and FG1 provide different views on the same underlying bits.
|
||||||||||||||||||
0x7D0 | RW | MOD0. Bits [31:0] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D1 | RW | MOD1. Bits [63:32] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D2 | RW | MOD2. Bits [95:64] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D3 | RW | MOD3. Bits [127:96] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D4 | RW | MOD4. Bits [159:128] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D5 | RW | MOD5. Bits [191:160] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D6 | RW | MOD6. Bits [223:192] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0x7D7 | RW | MOD7. Bits [255:224] of the modulus operand, used in the BN.ADDM/BN.SUBM instructions. This CSR is mapped to the MOD WSR. | ||||||||||||||||||
0xFC0 | R | RND. A random number. |
Wide Data Registers (WDRs)
In addition to the 32b wide GPRs, OTBN has a second “wide” register file, which is used by the big number instruction subset. This register file consists of NWDR = 32 Wide Data Registers (WDRs). Each WDR is WLEN = 256b wide.
Wide Data Registers (WDRs) and the 32b General Purpose Registers (GPRs) are separate register files.
They are only accessible through their respective instruction subset:
GPRs are accessible from the base instruction subset, and WDRs are accessible from the big number instruction subset (BN
instructions).
Register |
---|
w0 |
w1 |
… |
w31 |
Wide Special Purpose Registers (WSRs)
OTBN has 256b Wide Special purpose Registers (WSRs).
These are analogous to the 32b CSRs, but are used by big number instructions.
They can be accessed with the BN.WSRRS
and BN.WSRRW
instructions.
Number | Name | R/W | Description |
---|---|---|---|
0x0 | MOD | RW |
The modulus used by the |
0x1 | RND | R | A random number. |
0x2 | ACC | RW | The accumulator register used by the BN.MULQACC instruction. |
Flags
In addition to the wide register file, OTBN maintains global state in two groups of flags for the use by wide integer operations.
Flag groups are named Flag Group 0 (FG0
), and Flag Group 1 (FG1
).
Each group consists of four flags.
Each flag is a single bit.
-
C
(Carry flag). Set to 1 an overflow occurred in the last arithmetic instruction. -
M
(MSb flag) The most significant bit of the result of the last arithmetic or shift instruction. -
L
(LSb flag). The least significant bit of the result of the last arithmetic or shift instruction. -
Z
(Zero Flag) Set to 1 if the result of the last operation was zero; otherwise 0.
The M
, L
, and Z
flags are determined based on the result of the operation as it is written back into the result register, without considering the overflow bit.
Loop Stack
OTBN has two instructions for hardware-assisted loops: LOOP
and LOOPI
.
Both use the same state for tracking control flow.
This is a stack of tuples containing a loop count, start address and end address.
The stack has a maximum depth of eight and the top of the stack is the current loop.
Loop nesting
OTBN permits loop nesting and branches and jumps inside loops. However, it doesn’t have support for early termination of loops: there’s no way to pop an entry from the loop stack without executing the last instruction of the loop the correct number of times. It can also only pop one level of the loop stack per instruction.
To avoid polluting the loop stack or avoid surprising behaviour, the programmer must ensure that:
- Even if there are branches and jumps within a loop body, the final instruction of the loop body gets executed exactly once per iteration.
- Nested loops have distinct end addresses.
- The end instruction of an outer loop is not executed before an inner loop finishes.
OTBN does not detect these conditions being violated, so no error will be signalled should they occur.
(Note indentation in the code examples is for clarity and has no functional impact).
The following loops are well nested:
LOOP x2, 3
LOOP x3, 1
ADDI x4, x4, 1
# The NOP ensures that the outer and inner loops end on different instructions
NOP
# Both inner and outer loops call some_fn, which returns to
# the body of the loop
LOOP x2, 5
JAL x1, some_fn
LOOP x3, 2
JAL x1, some_fn
ADDI x4, x4, 1
NOP
# Control flow leaves the immediate body of the outer loop but eventually
# returns to it
LOOP x2, 4
BEQ x4, x5, some_label
branch_back:
LOOP x3, 1
ADDI x6, x6, 1
NOP
some_label:
...
JAL x0, branch_back
The following loops are not well nested:
# Both loops end on the same instruction
LOOP x2, 2
LOOP x3, 1
ADDI x4, x4, 1
# Inner loop jumps into outer loop body (executing the outer loop end
# instruction before the inner loop has finished)
LOOP x2, 5
LOOP x3, 3
ADDI x4, x4 ,1
BEQ x4, x5, outer_body
ADD x6, x7, x8
outer_body:
SUBI x9, x9, 1
Theory of Operations
Block Diagram
Hardware Interfaces
Referring to the
Comportable guideline for peripheral device functionality,
the module otbn
has
the following hardware interfaces defined.
Primary Clock: clk_i
Other Clocks:
Bus Device Interface: tlul
Bus Host Interface: none
Peripheral Pins for Chip IO: none
Interrupts:
Interrupt Name | Description |
---|---|
done | OTBN has completed the operation |
Security Alerts:
Alert Name | Description |
---|---|
fatal | A fatal error. Fatal alerts are non-recoverable and will be asserted until a hard reset. |
recov | A recoverable error. Just sent once (as the processor stops). |
Design Details
Memories
The OTBN processor core has access to two dedicated memories: an instruction memory (IMEM), and a data memory (DMEM). Each memory is 4 kiB in size.
The memory layout follows the Harvard architecture. Both memories are byte-addressed, with addresses starting at 0.
The instruction memory (IMEM) is 32b wide and provides the instruction stream to the OTBN processor. It cannot be read from or written to by user code through load or store instructions.
The data memory (DMEM) is 256b wide and read-write accessible from the base and big number instruction subsets of the OTBN processor core.
There are four instructions that can access data memory.
In the base instruction subset, there are LW
(load word) and SW
(store word).
These access 32b-aligned 32b words.
In the big number instruction subset, there are BN.LID
(load indirect) and BN.SID
(store indirect).
These access 256b-aligned 256b words.
Both memories can be accessed through OTBN’s register interface (DMEM and IMEM). These accesses are ignored if OTBN is busy. A host processor can check whether OTBN is busy by reading the STATUS.busy flag. All memory accesses through the register interface must be word-aligned 32b word accesses.
Error Handling and Reporting
By design, OTBN is a simple processor and provides no error handling support to code that runs on it.
Whenever OTBN observes an error, it will generate an alert. This gets sent to the alert manager. The alert will either be fatal or recoverable, depending on the class of error: see Alerts and ERR_BITS below for details.
If OTBN was running when the alert occurred (this is true whenever STATUS.busy is high), it will also:
- Immediately stop fetching and executing instructions.
- Set INTR_STATE.done and clear STATUS.busy, marking the operation as completed.
- Set the ERR_BITS register to a non-zero value describing the error.
Note that OTBN can detect some errors even when it isn’t running. One example of this is an error caused by an ECC failure when reading or programming OTBN’s memories over the bus. In this case, the ERR_BITS register will not change. This avoids race conditions with the host processor’s error handling software. However, every error that OTBN detects when it isn’t running causes a fatal alert. This means that the cause will be reflected in FATAL_ALERT_CAUSE, as described below in Alerts. This way, no alert is generated without setting an error code somewhere.
TODO
When the implementation is finished, document more precisely how OTBN stops on error. Can we claim to cancel all register and memory writes in that cycle? Is there a way for that to make sense for errors that aren’t related to a particular instruction (e.g. shadow register mis-matches; FSM glitches)?
Also, once it is decided, document behaviour on a bus access when OTBN is running.
Alerts
OTBN has two alerts, one recoverable and one fatal. The ERR_BITS register documentation has a detailed list of error conditions, those with ‘fatal’ in the name raise a fatal alert, otherwise they raise a recoverable alert.
A recoverable alert is a one-time triggered alert for recoverable error conditions. Recoverable alerts are only triggered when OTBN is running, so will always imply a write to ERR_BITS. The error that caused the alert can be determined by reading the ERR_BITS register.
A fatal alert is a continuously triggered alert after unrecoverable error conditions.
The error that caused the alert can be determined by reading the FATAL_ALERT_CAUSE register.
If OTBN was running, this value will also be reflected in the ERR_BITS register.
A fatal alert can only be cleared by resetting OTBN through the rst_ni
line.
Running applications on OTBN
OTBN is a specialized coprocessor which is used from the host CPU. This section describes how to interact with OTBN from the host CPU to execute an existing OTBN application. The section Writing OTBN applications describes how to write such applications.
High-level operation sequence
The high-level sequence by which the host processor should use OTBN is as follows.
- Write the OTBN application binary to IMEM, starting at address 0.
- Optional: Write constants and input arguments, as mandated by the calling convention of the loaded application, to DMEM.
- Start the operation on OTBN by writing
1
to CMD.start. Now neither data nor instruction memory may be accessed from the host CPU. After it has been started the OTBN application runs to completion without further interaction with the host. - Wait for the operation to complete (see below). As soon as the OTBN operation has completed the data and instruction memories can be accessed again from the host CPU.
- Check if the operation was successful by reading the ERR_BITS register.
- Optional: Retrieve results by reading DMEM, as mandated by the calling convention of the loaded application.
OTBN applications are run to completion. The host CPU can determine if an application has completed by either polling STATUS.busy or listening for an interrupt.
- To poll for a completed operation, software should repeatedly read the STATUS.busy register.
While the operation is in progress, STATUS.busy reads as
1
. The operation is completed if STATUS.busy is0
. - Alternatively, software can listen for the
done
interrupt to determine if the operation has completed. The standard sequence of working with interrupts has to be followed, i.e. the interrupt has to be enabled, an interrupt service routine has to be registered, etc. The DIF contains helpers to do so conveniently.
Note: This operation sequence only covers functional aspects. Depending on the application additional steps might be necessary, such as deleting secrets from the memories.
Device Interface Functions (DIFs)
To use this DIF, include the following C header:
#include "sw/device/lib/dif/dif_otbn.h"
This header provides the following device interface functions:
dif_otbn_dmem_read
Read from OTBN's data memory (DMEM)dif_otbn_dmem_write
Write to OTBN's data memory (DMEM)dif_otbn_get_dmem_size_bytes
Get the size of OTBN's data memory in bytes.dif_otbn_get_err_bits
Get the error bits set by the device if the operation failed.dif_otbn_get_imem_size_bytes
Get the size of OTBN's instruction memory in bytes.dif_otbn_imem_read
Read from OTBN's instruction memory (IMEM)dif_otbn_imem_write
Write an OTBN application into its instruction memory (IMEM)dif_otbn_init
Initialize a OTBN device usingconfig
and return its internal state.dif_otbn_irq_control
OTBN interrupt control.dif_otbn_irq_force
OTBN interrupt force.dif_otbn_irq_state_clear
OTBN clear requested IRQ state.dif_otbn_irq_state_get
OTBN get requested IRQ state.dif_otbn_irqs_disable
OTBN disable interrupts.dif_otbn_irqs_restore
OTBN restore IRQ state.dif_otbn_is_busy
Is OTBN busy executing an application?dif_otbn_reset
Reset OTBN device.dif_otbn_start
Start the execution of the application loaded into OTBN at the start address.
Driver
A higher-level driver for the OTBN block is available at sw/device/lib/runtime/otbn.h
(API documentation).
Register Table
otbn.INTR_STATE @ 0x0
Interrupt State Register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | rw1c | 0x0 | done | OTBN has completed the operation |
otbn.INTR_ENABLE @ 0x4
Interrupt Enable Register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | rw | 0x0 | done | Enable interrupt when |
otbn.INTR_TEST @ 0x8
Interrupt Test Register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | wo | 0x0 | done | Write 1 to force |
otbn.ALERT_TEST @ 0xc
Alert Test Register Reset default = 0x0, mask 0x3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | wo | 0x0 | fatal | Write 1 to trigger one alert event of this kind. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | wo | 0x0 | recov | Write 1 to trigger one alert event of this kind. |
otbn.CMD @ 0x10
command register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | r0w1c | x | start | Start the operation The completion is signalled by the done interrupt. |
otbn.STATUS @ 0x14
Status Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | ro | x | busy | OTBN is performing an operation. |
otbn.ERR_BITS @ 0x18
Error bitfield. Reads as non-zero if an error was seen during OTBN operation Reset default = 0x0, mask 0xff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | ro | x | bad_data_addr | A DMEM read or write occurred with an out of bounds or unaligned address. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | ro | x | bad_insn_addr | An IMEM read occurred with an out of bounds or unaligned address. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2 | ro | x | call_stack | A instruction tried to pop from an empty call stack or push to a full call stack. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3 | ro | x | illegal_insn | One of the following happened:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4 | ro | x | loop | One of the following happened:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
5 | ro | x | fatal_imem | A fatal failure was seen on an instruction fetch. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6 | ro | x | fatal_dmem | A fatal failure was seen on a DMEM read. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7 | ro | x | fatal_reg | A fatal failure was seen on a GPR or WDR read. |
otbn.START_ADDR @ 0x1c
Start byte address in the instruction memory Reset default = 0x0, mask 0xffffffff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31:0 | wo | 0x0 | start_addr | Byte address in the instruction memory OTBN starts to execute from
when instructed to do so with the |
otbn.FATAL_ALERT_CAUSE @ 0x20
The cause of a fatal alert. Reset default = 0x0, mask 0x7
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
The bits of this register correspond to errors that can cause a fatal alert. Software can read these bits to see what went wrong. Once set, these bits cannot be cleared. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | ro | 0x0 | imem_error | Set on any ECC error in IMEM | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 | ro | 0x0 | dmem_error | Set on any ECC error in DMEM | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2 | ro | 0x0 | reg_error | Set on any ECC error in a register file |
otbn.IMEM @ + 0x4000
1024 item rw window
Byte writes are not supported
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Instruction Memory. This register should only be accesed while OTBN is not busy, as indicated by the TODO: The exact behavior is yet to be determined, see https://github.com/lowRISC/opentitan/issues/2696 for details. |
otbn.DMEM @ + 0x8000
1024 item rw window
Byte writes are not supported
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Data Memory. This register should only be accesed while OTBN is not busy, as indicated by the TODO: The exact behavior is yet to be determined, see https://github.com/lowRISC/opentitan/issues/2696 for details. |
Writing OTBN applications
OTBN applications are (small) pieces of software written in OTBN assembly.
The full instruction set is described in the ISA manual, and example software is available in the sw/otbn
directory of the OpenTitan source tree.
A hands-on user guide to develop OTBN software can be found in the section Writing and building software for OTBN.
Toolchain support
OTBN comes with a toolchain consisting of an assembler, a linker, and helper tools such as objdump. The toolchain wraps a RV32 GCC toolchain and supports many of its features.
The following tools are available:
otbn-as
: The OTBN assembler.otbn-ld
: The OTBN linker.otbn-objdump
: objdump for OTBN.
Other tools from the RV32 toolchain can be used directly, such as objcopy.
Passing of data between the host CPU and OTBN
Passing data between the host CPU and OTBN is done through the data memory (DMEM). No standard or required calling convention exists, every application is free to pass data in and out of OTBN in whatever format it finds convenient. All data passing must be done when OTBN is not running, as indicated by the STATUS.busy bit; during the OTBN operation both the instruction and the data memory are inaccessible from the host CPU.
Returning from an application
The software running on OTBN signals completion by executing the ECALL
instruction.
When it executes this instruction, OTBN:
- Stops fetching and executing instructions.
- Sets INTR_STATE.done and clears STATUS.busy, marking the operation as completed.
- Writes zero to ERR_BITS.
The DMEM can be used to pass data back to the host processor, e.g. a “return value” or an “exit code”. Refer to the section Passing of data between the host CPU and OTBN for more information.
Algorithic Examples: Multiplication with BN.MULQACC
The big number instruction subset of OTBN generally operates on WLEN bit numbers.
BN.MULQACC
operates with WLEN/4 bit operands (with a full WLEN accumulator).
This section outlines two techniques to perform larger multiplies by composing multiple BN.MULQACC
instructions.
Multiplying two WLEN/2 numbers with BN.MULQACC
This instruction sequence multiplies the lower half of w0
by the upper half of
w0
placing the result in w1
.
BN.MULQACC.Z w0.0, w0.2, 0
BN.MULQACC w0.0, w0.3, 64
BN.MULQACC w0.1, w0.2, 64
BN.MULQACC.WO w1, w0.1, w0.3, 128
Multiplying two WLEN numbers with BN.MULQACC
The shift out functionality can be used to perform larger multiplications without extra adds.
The table below shows how two registers w0
and w1
can be multiplied together to give a result in w2
and w3
.
The cells on the right show how the result is built up a0:a3 = w0.0:w0.3
and b0:b3 = w1.0:w1.3
.
The sum of a column represents WLEN/4 bits of a destination register, where c0:c3 = w2.0:w2.3
and d0:d3 = w3.0:w3.3
.
Each cell with a multiply in takes up two WLEN/4-bit columns to represent the WLEN/2-bit multiply result.
The current accumulator in each instruction is represented by highlighted cells where the accumulator value will be the sum of the highlighted cell and all cells above it.
The outlined technique can be extended to arbitrary bit widths but requires unrolled code with all operands in registers.
d3 | d2 | d1 | d0 | c3 | c2 | c1 | c0 | |
---|---|---|---|---|---|---|---|---|
BN.MULQACC.Z w0.0, w1.0, 0 |
a0 * b0 |
|||||||
BN.MULQACC w0.1, w1.0, 64 |
a1 * b0 |
|||||||
BN.MULQACC.SO w2.l, w0.0, w1.1, 64 |
a0 * b1 |
|||||||
BN.MULQACC w0.2, w1.0, 0 |
a2 * b0 |
|||||||
BN.MULQACC w0.1, w1.1, 0 |
a1 * b1 |
|||||||
BN.MULQACC w0.0, w1.2, 0 |
a0 * b2 |
|||||||
BN.MULQACC w0.3, w1.0, 64 |
a3 * b0 |
|||||||
BN.MULQACC w0.2, w1.1, 64 |
a2 * b1 |
|||||||
BN.MULQACC w0.1, w1.2, 64 |
a1 * b2 |
|||||||
BN.MULQACC.SO w2.u, w0.0, w1.3, 64 |
a0 * b3 |
|||||||
BN.MULQACC w0.3, w1.1, 0 |
a3 * b1 |
|||||||
BN.MULQACC w0.2, w1.2, 0 |
a2 * b2 |
|||||||
BN.MULQACC w0.1, w1.3, 0 |
a1 * b3 |
|||||||
BN.MULQACC w0.3, w1.2, 64 |
a3 * b2 |
|||||||
BN.MULQACC.SO w3.l, w0.2, w1.3, 64 |
a2 * b3 |
|||||||
BN.MULQACC.SO w3.u, w0.3, w1.3, 0 |
a3 * b3 |
Code snippets giving examples of 256x256 and 384x384 multiplies can be found in sw/otbn/code-snippets/mul256.s
and sw/otbn/code-snippets/mul384.s
.