# OpenTitan Big Number Accelerator (OTBN) Instruction Set Architecture

This document describes the instruction set for OTBN. For more details about the processor itself, see the OTBN Technical Specification. In particular, this document assumes knowledge of the Processor State section from that guide.

The instruction set is split into base and big number subsets. The base subset (described first) is similar to RISC-V’s RV32I instruction set. It also includes a hardware call stack and hardware loop instructions. The big number subset is designed to operate on 256b WDRs. It doesn’t include any control flow instructions, and just supports load/store, logical and arithmetic operations.

In the instruction documentation that follows, each instruction has a syntax example. For example, the SW instruction has syntax:

  SW <grs2>, <offset>(<grs1>)


This means that it takes three operands, called grs2, offset and grs1. These operands are further documented in a table. Immediate operands like offset show their valid range of values.

Below the table of operands is an encoding table. This shows how the 32 bits of the instruction word are filled in. Ranges of bits that map to an operand are named (in capitals) and those names are used in the operand table. For example, the SW instruction’s offset operand is split across two ranges of bits (31:25 and 11:7) called OFF_1 and OFF_0, respectively.

# Pseudo-code for operation descriptions

Each instruction has an Operation section. This is written in a Python-like pseudo-code, generated from the instruction set simulator (which can be found at hw/ip/otbn/dv/otbnsim). The code is generated from Python, but there are some extra changes made to aid readability.

All instruction operands are considered to be in scope and have integer values. These values come from the encoded bits in the instruction and the operand table for the instruction describes exactly how they are decoded. Some operands are encoded PC-relative. Such an operand has its absolute value (an address) when it appears in the Operation section.

Some state updates are represented as an assignment, but take effect at the end of the instruction. This includes register updates or jumps and branches (updating the PC). To denote this, we use the ⇐ symbol, reminiscent of Verilog’s non-blocking assignment.

The program counter (PC) is represented as a variable called PC.

Machine registers are accessed with an array syntax. These arrays are:

• GPRs: General purpose registers
• WDRs: Wide data registers
• CSRs: Control and status registers
• WSRs: Wide special purpose registers

Accesses to these arrays are as unsigned integers. The instruction descriptions are written to ensure that any value written to a register is representable. For example, a write to GPRs[2] will always have a non-negative value less than 1 << 32.

Memory accesses are represented as function calls. This is because the memory can be accessed on either the narrow or the wide side, which isn’t easy to represent with an array syntax. Memory loads are represented as DMEM.load_u32(addr), DMEM.load_u256(addr). Memory stores are represented as DMEM.store_u32(addr, value) and DMEM.store_u256(addr, value). In all cases, memory values are interpreted as unsigned integers and, as for register accesses, the instruction descriptions are written to ensure that any value stored to memory is representable.

There are a few other helper functions, defined here to avoid having to inline their bodies into each instruction.

def from_2s_complement(n: int) -> int:
'''Interpret the bits of unsigned integer n as a 32-bit signed integer'''
assert 0 <= n < (1 << 32)
return n if n < (1 << 31) else n - (1 << 32)

def to_2s_complement(n: int) -> int:
'''Interpret the bits of signed integer n as a 32-bit unsigned integer'''
assert -(1 << 31) <= n < (1 << 31)
return (1 << 32) + n if n < 0 else n

def logical_byte_shift(value: int, shift_type: int, shift_bytes: int) -> int:
'''Logical shift value by shift_bytes to the left or right.

value should be an unsigned 256-bit value. shift_type should be 0 (shift
left) or 1 (shift right), matching the encoding of the big number
instructions. shift_bytes should be a non-negative number of bytes to shift
by.

Returns an unsigned 256-bit value, truncating on an overflowing left shift.

'''
mask256 = (1 << 256) - 1
assert 0 <= value <= mask256
assert 0 <= shift_type <= 1
assert 0 <= shift_bytes

shift_bits = 8 * shift_bytes
shifted = value << shift_bits if shift_type == 0 else value >> shift_bits

def extract_quarter_word(value: int, qwsel: int) -> int:
'''Extract a 64-bit quarter word from a 256-bit value.'''
assert 0 <= value < (1 << 256)
assert 0 <= qwsel <= 3
return (value >> (qwsel * 64)) & ((1 << 64) - 1)


# Base Instruction Subset

The base instruction set of OTBN is a limited 32b instruction set. It is used together with the 32b wide General Purpose Register file. The primary use of the base instruction set is the control flow in applications.

The base instruction set is an extended subset of RISC-V’s RV32I_Zcsr. Refer to the RISC-V Unprivileged Specification for a detailed instruction specification. Not all RV32 instructions are implemented. The implemented subset is shown below.

ADD <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ADD 0 0 0 0 0 0 0 GRS2 GRS1 0 0 0 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
result = (val1 + val2) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


ADDI <grd>, <grs1>, <imm>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

imm

Valid range: -2048 to 2047.

Decode as signed(IMM)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ADDI IMM GRS1 0 0 0 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
result = (val1 + imm) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


## LUI

LUI <grd>, <imm>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

imm

Valid range: 0 to 1048575.

Decode as unsigned(IMM)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 LUI IMM GRD 0 1 1 0 1 1 1

### Operation

GPRs[grd] ⇐ imm << 12


## SUB

Subtract.

SUB <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SUB 0 1 0 0 0 0 0 GRS2 GRS1 0 0 0 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
result = (val1 - val2) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


## SLL

Logical left shift.

SLL <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SLL 0 0 0 0 0 0 0 GRS2 GRS1 0 0 1 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2] & 0x1f
result = (val1 << val2) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


## SLLI

Logical left shift with Immediate.

SLLI <grd>, <grs1>, <shamt>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

shamt

Valid range: 0 to 31.

Decode as unsigned(SHAMT)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SLLI 0 0 0 0 0 0 0 SHAMT GRS1 0 0 1 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
result = (val1 << shamt) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


## SRL

Logical right shift.

SRL <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SRL 0 0 0 0 0 0 0 GRS2 GRS1 1 0 1 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2] & 0x1f
result = val1 >> val2
GPRs[grd] ⇐ result


## SRLI

Logical right shift with Immediate.

SRLI <grd>, <grs1>, <shamt>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

shamt

Valid range: 0 to 31.

Decode as unsigned(SHAMT)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SRLI 0 0 0 0 0 0 0 SHAMT GRS1 1 0 1 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
result = val1 >> shamt
GPRs[grd] ⇐ result


## SRA

Arithmetic right shift.

SRA <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SRA 0 1 0 0 0 0 0 GRS2 GRS1 1 0 1 GRD 0 1 1 0 0 1 1

### Operation

val1 = from_2s_complement(GPRs[grs1])
val2 = GPRs[grs2] & 0x1f
result = val1 >> val2
GPRs[grd] ⇐ to_2s_complement(result)


## SRAI

Arithmetic right shift with Immediate.

SRAI <grd>, <grs1>, <shamt>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

shamt

Valid range: 0 to 31.

Decode as unsigned(SHAMT)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SRAI 0 1 0 0 0 0 0 SHAMT GRS1 1 0 1 GRD 0 0 1 0 0 1 1

### Operation

val1 = from_2s_complement(GPRs[grs1])
val2 = shamt
result = val1 >> val2
GPRs[grd] ⇐ to_2s_complement(result)


## AND

Bitwise AND.

AND <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 AND 0 0 0 0 0 0 0 GRS2 GRS1 1 1 1 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
result = val1 & val2
GPRs[grd] ⇐ result


## ANDI

Bitwise AND with Immediate.

ANDI <grd>, <grs1>, <imm>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

imm

Valid range: -2048 to 2047.

Decode as signed(IMM)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ANDI IMM GRS1 1 1 1 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = to_2s_complement(imm)
result = val1 & val2
GPRs[grd] ⇐ result


## OR

Bitwise OR.

OR <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 OR 0 0 0 0 0 0 0 GRS2 GRS1 1 1 0 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
result = val1 | val2
GPRs[grd] ⇐ result


## ORI

Bitwise OR with Immediate.

ORI <grd>, <grs1>, <imm>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

imm

Valid range: -2048 to 2047.

Decode as signed(IMM)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ORI IMM GRS1 1 1 0 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = to_2s_complement(imm)
result = val1 | val2
GPRs[grd] ⇐ result


## XOR

Bitwise XOR.

XOR <grd>, <grs1>, <grs2>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 XOR 0 0 0 0 0 0 0 GRS2 GRS1 1 0 0 GRD 0 1 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
result = val1 ^ val2
GPRs[grd] ⇐ result


## XORI

Bitwise XOR with Immediate.

XORI <grd>, <grs1>, <imm>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

imm

Valid range: -2048 to 2047.

Decode as signed(IMM)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 XORI IMM GRS1 1 0 0 GRD 0 0 1 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = to_2s_complement(imm)
result = val1 ^ val2
GPRs[grd] ⇐ result


## LW

Load Word. Loads a 32b word from address offset + grs1 in data memory, writing the result to grd. Unaligned loads are not supported. Any address that is unaligned or is above the top of memory will result in an error (setting bit bad_data_addr in ERR_BITS). This instruction takes 2 cycles.

LW <grd>, <offset>(<grs1>)


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

offset

Valid range: -2048 to 2047.

Decode as signed(OFF)

grs1

Decode as unsigned(GRS1)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 LW OFF GRS1 0 1 0 GRD 0 0 0 0 0 1 1

### Operation

base = GPRs[grs1]
addr = (base + offset) & ((1 << 32) - 1)
GPRs[grd] ⇐ result


## SW

Store Word. Stores a 32b word in grs2 to address offset + grs1 in data memory. Unaligned stores are not supported. Any address that is unaligned or is above the top of memory will result in an error (setting bit bad_data_addr in ERR_BITS).

SW <grs2>, <offset>(<grs1>)


This instruction is defined in the RV32I instruction set.

OperandDescription

grs2

Decode as unsigned(GRS2)

offset

Valid range: -2048 to 2047.

Decode as signed({OFF_1, OFF_0})

grs1

Decode as unsigned(GRS1)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 SW OFF_1 GRS2 GRS1 0 1 0 OFF_0 0 1 0 0 0 1 1

### Operation

base = GPRs[grs1]
addr = (base + offset) & ((1 << 32) - 1)
value = GPRs[grs2]


## BEQ

Branch Equal.

BEQ <grs1>, <grs2>, <offset>


This instruction is defined in the RV32I instruction set.

OperandDescription

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

offset

Valid range: -4096 to 4094 in steps of 2. This is encoded PC-relative but appears as an absolute value in assembly. To write a raw value in an assembly file, write something in the range .-4096 to .+4094.

Decode as PC + signed({OFF_3, OFF_2, OFF_1, OFF_0, 1'b0})

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BEQ OFF_3 OFF_2 GRS2 GRS1 0 0 0 OFF_1 OFF_0 1 1 0 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
if val1 == val2:
PC ⇐ offset


## BNE

Branch Not Equal.

BNE <grs1>, <grs2>, <offset>


This instruction is defined in the RV32I instruction set.

OperandDescription

grs1

Decode as unsigned(GRS1)

grs2

Decode as unsigned(GRS2)

offset

Valid range: -4096 to 4094 in steps of 2. This is encoded PC-relative but appears as an absolute value in assembly. To write a raw value in an assembly file, write something in the range .-4096 to .+4094.

Decode as PC + signed({OFF_3, OFF_2, OFF_1, OFF_0, 1'b0})

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BNE OFF_3 OFF_2 GRS2 GRS1 0 0 1 OFF_1 OFF_0 1 1 0 0 0 1 1

### Operation

val1 = GPRs[grs1]
val2 = GPRs[grs2]
if val1 != val2:
PC ⇐ offset


## JAL

JAL <grd>, <offset>


This instruction is defined in the RV32I instruction set.

The JAL instruction has the same behavior as in RV32I, jumping by the given offset and writing PC+4 as a link address to the destination register. OTBN has a hardware managed call stack, accessed through x1, which should be used when calling subroutines. Do so by using x1 as the link register: jal x1, <offset>.

OperandDescription

grd

Decode as unsigned(GRD)

offset

Valid range: -1048576 to 1048574 in steps of 2. This is encoded PC-relative but appears as an absolute value in assembly. To write a raw value in an assembly file, write something in the range .-1048576 to .+1048574.

Decode as PC + signed({OFF_3, OFF_2, OFF_1, OFF_0, 1'b0})

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 JAL OFF_3 OFF_2 OFF_1 OFF_0 GRD 1 1 0 1 1 1 1

### Operation

link_pc = (state.pc + 4) & ((1 << 32) - 1)
PC ⇐ offset


## JALR

JALR <grd>, <grs1>, <offset>


This instruction is defined in the RV32I instruction set.

The JALR instruction has the same behavior as in RV32I, jumping by <grs1> + <offset> and writing PC+4 as a link address to the destination register. OTBN has a hardware managed call stack, accessed through x1, which should be used when calling and returning from subroutines. To return from a subroutine, use jalr x0, x1, 0. This pops a link address from the call stack and branches to it. To call a subroutine through a function pointer, use jalr x1, <grs1>, 0. This jumps to the address in <grs1> and pushes the link address onto the call stack.

OperandDescription

grd

Decode as unsigned(GRD)

grs1

Decode as unsigned(GRS1)

offset

Valid range: -2048 to 2047.

Decode as signed(OFFSET)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 JALR OFFSET GRS1 0 0 0 GRD 1 1 0 0 1 1 1

### Operation

val1 = GPRs[grs1]
link_pc = (state.pc + 4) & ((1 << 32) - 1)

PC ⇐ (val1 + offset) & ((1 << 32) - 1)


## CSRRS

Atomic Read and Set bits in CSR. Reads the value of the CSR csr, and writes it to the destination GPR grd. The initial value in grs1 is treated as a bit mask that specifies bits to be set in the CSR. Any bit that is high in grs1 will cause the corresponding bit to be set in the CSR, if that CSR bit is writable. Other bits in the CSR are unaffected (though CSRs might have side effects when written).

CSRRS <grd>, <csr>, <grs1>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

csr

Valid range: 0 to 4095.

Decode as unsigned(CSR)

grs1

Decode as unsigned(GRS1)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 CSRRS CSR GRS1 0 1 0 GRD 1 1 1 0 0 1 1

### Operation

old_val = CSRs[csr]
bits_to_set = GPRs[grs1]
new_val = old_val | bits_to_set

GPRs[grd] ⇐ old_val
CSRs[csr] ⇐ new_val


## CSRRW

Atomic Read/Write CSR. Atomically swaps values in the CSR csr with the value in the GPR grs1. Reads the old value of the CSR, and writes it to the GPR grd. Writes the initial value in grs1 to the CSR csr. If grd == x0 the instruction does not read the CSR or cause any read-related side-effects.

CSRRW <grd>, <csr>, <grs1>


This instruction is defined in the RV32I instruction set.

OperandDescription

grd

Decode as unsigned(GRD)

csr

Valid range: 0 to 4095.

Decode as unsigned(CSR)

grs1

Decode as unsigned(GRS1)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 CSRRW CSR GRS1 0 0 1 GRD 1 1 1 0 0 1 1

### Operation

new_val = GPRs[grs1]

if grd != 0:
old_val = CSRs[csr]
GPRs[grd] ⇐ old_val

CSRs[csr] ⇐ new_val


## ECALL

Environment Call. Triggers the done interrupt to indicate the completion of the operation.

ECALL


This instruction is defined in the RV32I instruction set.

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ECALL 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1

### Operation

# Set INTR_STATE.done and STATUS, reflecting the fact we've stopped.
state.stop_at_end_of_cycle(err_bits=0)


## LOOP

Loop (indirect). Repeats a sequence of code multiple times. The number of iterations is read from grs, treated as an unsigned value. The number of instructions in the loop is given in the bodysize immediate.

The LOOP instruction doesn’t support a zero iteration count. If the value in grs is zero, OTBN stops, setting bit loop in ERR_BITS. Starting a loop pushes an entry on to the loop stack. If the stack is already full, OTBN stops, setting bit loop in ERR_BITS.

LOOP, LOOPI, jump and branch instructions are all permitted inside a loop but may not appear as the last instruction in a loop. OTBN will stop on that instruction, setting bit loop in ERR_BITS.

For more information on how to correctly use LOOP see loop nesting.

LOOP <grs>, <bodysize>

OperandDescription

grs

Name of the GPR containing the number of iterations

Decode as unsigned(GRS)

bodysize

Number of instructions in the loop body

Valid range: 1 to 4096.

Decode as unsigned(SZ) + 1

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 LOOP SZ GRS 0 0 0 1 1 1 1 0 1 1

### Operation

num_iters = GPRs[grs]
if num_iters == 0:
state.stop_at_end_of_cycle(err_bits.LOOP)
else:
state.loop_start(num_iters, bodysize)


## LOOPI

Loop Immediate. Repeats a sequence of code multiple times. The number of iterations is given in the iterations immediate. The number of instructions in the loop is given in the bodysize immediate.

The LOOPI instruction doesn’t support a zero iteration count. If the value of iterations is zero, OTBN stops with the ErrCodeLoop error. Starting a loop pushes an entry on to the loop stack. If the stack is already full, OTBN stops, setting bit loop in ERR_BITS.

LOOP, LOOPI, jump and branch instructions are all permitted inside a loop but may not appear as the last instruction in a loop. OTBN will stop on that instruction, setting bit loop in ERR_BITS.

For more information on how to correctly use LOOPI see loop nesting.

LOOPI <iterations>, <bodysize>

OperandDescription

iterations

Number of iterations

Valid range: 0 to 1023.

Decode as unsigned({ITERATIONS_1, ITERATIONS_0})

bodysize

Number of instructions in the loop body

Valid range: 1 to 4096.

Decode as unsigned(SZ) + 1

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 LOOPI SZ ITERATIONS_1 0 0 1 ITERATIONS_0 1 1 1 1 0 1 1

### Operation

state.loop_start(iterations, bodysize)


## NOP

No Operation. A pseudo-operation that has no effect.

NOP


This instruction is defined in the RV32I instruction set.

This instruction is a pseudo-operation and expands to the following instruction sequence:

ADDI x0, x0, 0


## LI

Load Immediate. Loads a 32b signed immediate value into a GPR. This uses ADDI and LUI, expanding to one or two instructions, depending on the immediate (small non-negative immediates or immediates with all lower bits zero can be loaded with just ADDI or LUI, respectively; general immediates need a LUI followed by an ADDI).

LI <grd>, <imm>


This instruction is defined in the RV32I instruction set.

## LA

LA <grd>, <imm>


This instruction is defined in the RV32I instruction set.

## RET

Return from subroutine.

RET


This instruction is defined in the RV32I instruction set.

This instruction is a pseudo-operation and expands to the following instruction sequence:

JALR x0, x1, 0


# Big Number Instruction Subset

All Big Number (BN) instructions operate on the Wide Data Registers (WDRs).

Add. Adds two WDR values, writes the result to the destination WDR and updates flags. The content of the second source WDR can be shifted by an unsigned immediate before it is consumed by the operation.

BN.ADD <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.ADD FG ST SB WRS2 WRS1 0 0 0 WRD 0 1 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

full_result = a + b_shifted
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


Add with Carry. Adds two WDR values and the Carry flag value, writes the result to the destination WDR, and updates the flags. The content of the second source WDR can be shifted by an unsigned immediate before it is consumed by the operation.

BN.ADDC <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.ADDC FG ST SB WRS2 WRS1 0 1 0 WRD 0 1 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

carry = int(FLAGs[flag_group].C)
full_result = a + b_shifted + carry
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


Add Immediate. Adds a zero-extended unsigned immediate to the value of a WDR, writes the result to the destination WDR, and updates the flags.

BN.ADDI <wrd>, <wrs>, <imm>[, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs

Name of the source WDR

Decode as unsigned(WRS)

imm

Immediate value

Valid range: 0 to 1023.

Decode as unsigned(IMM)

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.ADDI FG 0 IMM WRS 1 0 0 WRD 0 1 0 1 0 1 1

### Operation

a = WDRs[wrs]
b = imm

full_result = a + b
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


The values in <wrs1> and <wrs2> are summed to get an intermediate result (of width WLEN + 1). If this result is greater than MOD then MOD is subtracted from it. The result is then truncated to 256 bits and stored in <wrd>.

This operation correctly implements addition modulo MOD, providing that the intermediate result is less than 2 * MOD. The intermediate result is small enough if both inputs are less than MOD.

Flags are not used or saved.

BN.ADDM <wrd>, <wrs1>, <wrs2>

OperandDescription

wrd

Decode as unsigned(WRD)

wrs1

Decode as unsigned(WRS1)

wrs2

Decode as unsigned(WRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.ADDM 0 WRS2 WRS1 1 0 1 WRD 0 1 0 1 0 1 1

### Operation

a = WDRs[wrs1]
b = WDRs[wrs2]
result = a + b

mod_val = MOD
if result >= mod_val:
result -= mod_val

result = result & ((1 << 256) - 1)
WDRs[wrd] ⇐ result


## BN.MULQACC

Quarter-word Multiply and Accumulate. Multiplies two WLEN/4 WDR values, shifts the product by acc_shift_imm bits, and adds the result to the accumulator.

For versions of the instruction with writeback, see BN.MULQACC.WO and BN.MULQACC.SO.

BN.MULQACC[<zero_acc>] <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm>

OperandDescription

zero_acc

Zero the accumulator before accumulating the multiply result.

To specify, use the literal syntax .z

Decode as unsigned(ZA)

wrs1

First source WDR

Decode as unsigned(WRS1)

wrs1_qwsel

Quarter-word select for <wrs1>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q1)

wrs2

Second source WDR

Decode as unsigned(WRS2)

wrs2_qwsel

Quarter-word select for <wrs2>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q2)

acc_shift_imm

The number of bits to shift the WLEN/2-bit multiply result before accumulating.

Valid range: 0 to 192 in steps of 64.

Decode as unsigned({SHIFT, 6'b0})

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.MULQACC 0 0 Q2 Q1 WRS2 WRS1 SHIFT ZA 0 1 1 1 0 1 1

### Operation

In the listing below, operand zero_acc is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]

a_qw = extract_quarter_word(a, wrs1_qwsel)
b_qw = extract_quarter_word(b, wrs2_qwsel)

mul_res = a_qw * b_qw

acc = ACC
if zero_acc:
acc = 0

acc += (mul_res << acc_shift_imm)

truncated = acc & ((1 << 256) - 1)
ACC ⇐ truncated


## BN.MULQACC.WO

Quarter-word Multiply and Accumulate with half-word writeback. Multiplies two WLEN/4 WDR values, shifts the product by acc_shift_imm bits, and adds the result to the accumulator. Writes the resulting accumulator to wrd.

BN.MULQACC.WO[<zero_acc>] <wrd>, <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm>[, FG<flag_group>]

OperandDescription

zero_acc

Zero the accumulator before accumulating the multiply result.

To specify, use the literal syntax .z

Decode as unsigned(ZA)

wrd

Destination WDR.

Decode as unsigned(WRD)

wrs1

First source WDR

Decode as unsigned(WRS1)

wrs1_qwsel

Quarter-word select for <wrs1>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q1)

wrs2

Second source WDR

Decode as unsigned(WRS2)

wrs2_qwsel

Quarter-word select for <wrs2>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q2)

acc_shift_imm

The number of bits to shift the WLEN/2-bit multiply result before accumulating.

Valid range: 0 to 192 in steps of 64.

Decode as unsigned({SHIFT, 6'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.MULQACC.WO FG 0 1 Q2 Q1 WRS2 WRS1 SHIFT ZA WRD 0 1 1 1 0 1 1

### Operation

In the listing below, operand zero_acc is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]

a_qw = extract_quarter_word(a, wrs1_qwsel)
b_qw = extract_quarter_word(b, wrs2_qwsel)

mul_res = a_qw * b_qw

acc = ACC
if zero_acc:
acc = 0

acc += (mul_res << acc_shift_imm)

truncated = acc & ((1 << 256) - 1)
WDRs[wrd] ⇐ truncated
ACC ⇐ truncated
state.set_mlz_flags(flag_group, truncated)


## BN.MULQACC.SO

Quarter-word Multiply and Accumulate with half-word writeback. Multiplies two WLEN/4 WDR values, shifts the product by acc_shift_imm bits and adds the result to the accumulator. Next, shifts the resulting accumulator right by half a word (128 bits). The bits that are shifted out are written to a half-word of wrd, selected with wrd_hwsel.

This instruction never changes the C flag. If wrd_hwsel is zero (so the instruction is updating the lower half-word of wrd), it updates the L and Z flags and leaves M unchanged. The L flag is set iff the bottom bit of the shifted-out result is zero. The Z flag is set iff the shifted-out result is zero.

If wrd_hwsel is one (so the instruction is updating the upper half-word of wrd), it updates the M and Z flags and leaves L unchanged. The M flag is set iff the top bit of the shifted-out result is zero. The Z flag is left unchanged if the shifted-out result is zero and cleared if not.

BN.MULQACC.SO[<zero_acc>] <wrd>.<wrd_hwsel>, <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm>[, FG<flag_group>]

OperandDescription

zero_acc

Zero the accumulator before accumulating the multiply result.

To specify, use the literal syntax .z

Decode as unsigned(ZA)

wrd

Destination WDR.

Decode as unsigned(WRD)

wrd_hwsel

Half-word select for <wrd>. A value of L means the less significant half-word; U means the more significant half-word.

Assembly Syntax Value
l 0
u 1

Decode as unsigned(DH)

wrs1

First source WDR

Decode as unsigned(WRS1)

wrs1_qwsel

Quarter-word select for <wrs1>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q1)

wrs2

Second source WDR

Decode as unsigned(WRS2)

wrs2_qwsel

Quarter-word select for <wrs2>.

Valid values:

• 0: Select wrs1[WLEN/4-1:0] (least significant quarter-word)
• 1: Select wrs1[WLEN/2:WLEN/4]
• 2: Select wrs1[WLEN/4*3-1:WLEN/2]
• 3: Select wrs1[WLEN-1:WLEN/4*3] (most significant quarter-word)

Valid range: 0 to 3.

Decode as unsigned(Q2)

acc_shift_imm

The number of bits to shift the WLEN/2-bit multiply result before accumulating.

Valid range: 0 to 192 in steps of 64.

Decode as unsigned({SHIFT, 6'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.MULQACC.SO FG 1 DH Q2 Q1 WRS2 WRS1 SHIFT ZA WRD 0 1 1 1 0 1 1

### Operation

In the listing below, operands zero_acc and wrd_hwsel are referred to by their integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]

a_qw = extract_quarter_word(a, wrs1_qwsel)
b_qw = extract_quarter_word(b, wrs2_qwsel)

mul_res = a_qw * b_qw

acc = ACC
if zero_acc:
acc = 0

acc += (mul_res << acc_shift_imm)
truncated = acc & ((1 << 256) - 1)

# Split the result into low and high parts
lo_part = truncated & ((1 << 128) - 1)
hi_part = truncated >> 128

# Shift out the low part of the result
hw_shift = 128 * wrd_hwsel
hw_mask = ((1 << 128) - 1) << hw_shift
old_wrd = WDRs[wrd]
new_wrd = (old_wrd & ~hw_mask) | (lo_part << hw_shift)
WDRs[wrd] ⇐ new_wrd

# Write back the high part of the result
ACC ⇐ hi_part

old_flags = FLAGs[flag_group]
if wrd_hwsel:
new_flags = FlagReg(C=old_flags.C,
M=bool((lo_part >> 127) & 1),
L=old_flags.L,
Z=old_flags.Z and lo_part == 0)
else:
new_flags = FlagReg(C=old_flags.C,
M=old_flags.M,
L=bool(lo_part & 1),
Z=lo_part == 0)
FLAGs[flag_group] ⇐ new_flags


## BN.SUB

Subtraction. Subtracts the second WDR value from the first one, writes the result to the destination WDR and updates flags. The content of the second source WDR can be shifted by an unsigned immediate before it is consumed by the operation.

BN.SUB <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SUB FG ST SB WRS2 WRS1 0 0 1 WRD 0 1 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

full_result = a - b_shifted
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


## BN.SUBB

Subtract with borrow. Subtracts the second WDR value and the Carry from the first one, writes the result to the destination WDR, and updates the flags. The content of the second source WDR can be shifted by an unsigned immediate before it is consumed by the operation.

BN.SUBB <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SUBB FG ST SB WRS2 WRS1 0 1 1 WRD 0 1 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)
borrow = int(FLAGs[flag_group].C)

full_result = a - b_shifted - borrow
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


## BN.SUBI

Subtract Immediate. Subtracts a zero-extended unsigned immediate from the value of a WDR, writes the result to the destination WDR, and updates the flags.

BN.SUBI <wrd>, <wrs>, <imm>[, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs

Name of the source WDR

Decode as unsigned(WRS)

imm

Immediate value

Valid range: 0 to 1023.

Decode as unsigned(IMM)

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SUBI FG 1 IMM WRS 1 0 0 WRD 0 1 0 1 0 1 1

### Operation

a = WDRs[wrs]
b = imm

full_result = a - b
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


## BN.SUBM

Pseudo-modulo subtraction. Subtract <wrs2> from <wrs1>, modulo the MOD WSR.

The intermediate result is treated as a signed number (of width WLEN + 1). If it is negative, MOD is added to it. The 2’s-complement result is then truncated to 256 bits and stored in <wrd>.

This operation correctly implements subtraction modulo MOD, providing that the intermediate result at least -MOD and at most MOD - 1. This is guaranteed if both inputs are less than MOD.

Flags are not used or saved.

BN.SUBM <wrd>, <wrs1>, <wrs2>

OperandDescription

wrd

Decode as unsigned(WRD)

wrs1

Decode as unsigned(WRS1)

wrs2

Decode as unsigned(WRS2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SUBM 1 WRS2 WRS1 1 0 1 WRD 0 1 0 1 0 1 1

### Operation

a = WDRs[wrs1]
b = WDRs[wrs2]

mod_val = MOD

diff = a - b
if diff < 0:
diff += mod_val

result = diff & ((1 << 256) - 1)
WDRs[wrd] ⇐ result


## BN.AND

Bitwise AND. Performs a bitwise and operation. Takes the values stored in registers referenced by wrs1 and wrs2 and stores the result in the register referenced by wrd. The content of the second source register can be shifted by an immediate before it is consumed by the operation. The M, L and Z flags in flag group 0 are updated with the result of the operation.

BN.AND <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.AND FG ST SB WRS2 WRS1 0 1 0 WRD 1 1 1 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

result = a & b_shifted
WDRs[wrd] ⇐ result
state.set_mlz_flags(flag_group, result)


## BN.OR

Bitwise OR. Performs a bitwise or operation. Takes the values stored in WDRs referenced by wrs1 and wrs2 and stores the result in the WDR referenced by wrd. The content of the second source WDR can be shifted by an immediate before it is consumed by the operation. The M, L and Z flags in flag group 0 are updated with the result of the operation.

BN.OR <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.OR FG ST SB WRS2 WRS1 1 0 0 WRD 1 1 1 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

result = a | b_shifted
WDRs[wrd] ⇐ result
state.set_mlz_flags(flag_group, result)


## BN.NOT

Bitwise NOT. Negates the value in wrs and stores the result in the register referenced by wrd. The source value can be shifted by an immediate before it is consumed by the operation. The M, L and Z flags in flag group 0 are updated with the result of the operation.

BN.NOT <wrd>, <wrs>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs

Name of the source WDR

Decode as unsigned(WRS)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.NOT FG ST SB WRS 1 0 1 WRD 1 1 1 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs]
a_shifted = logical_byte_shift(a, shift_type, shift_bytes)

result = a_shifted ^ ((1 << 256) - 1)
WDRs[wrd] ⇐ result
state.set_mlz_flags(flag_group, result)


## BN.XOR

Bitwise XOR. Performs a bitwise xor operation. Takes the values stored in WDRs referenced by wrs1 and wrs2 and stores the result in the WDR referenced by wrd. The content of the second source WDR can be shifted by an immediate before it is consumed by the operation. The M, L and Z flags in flag group 0 are updated with the result of the operation.

BN.XOR <wrd>, <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.XOR FG ST SB WRS2 WRS1 1 1 0 WRD 1 1 1 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

result = a ^ b_shifted
WDRs[wrd] ⇐ result
state.set_mlz_flags(flag_group, result)


## BN.RSHI

Concatenate and right shift immediate. Concatenates the content of WDRs referenced by wrs1 and wrs2 (wrs1 forms the upper part), shifts it right by an immediate value and truncates to WLEN bit. The result is stored in the WDR referenced by wrd.

BN.RSHI <wrd>, <wrs1>, <wrs2> >> <imm>

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

imm

Number of bits to shift the second source register by. Valid range: 0..(WLEN-1).

Valid range: 0 to 255.

Decode as unsigned({IMM_1, IMM_0})

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.RSHI IMM_1 WRS2 WRS1 IMM_0 1 1 WRD 1 1 1 1 0 1 1

### Operation

a = WDRs[wrs1]
b = WDRs[wrs2]

result = (((a << 256) | b) >> imm) & ((1 << 256) - 1)
WDRs[wrd] ⇐ result


## BN.SEL

Flag Select. Returns in the destination WDR the value of the first source WDR if the flag in the chosen flag group is set, otherwise returns the value of the second source WDR.

BN.SEL <wrd>, <wrs1>, <wrs2>, [FG<flag_group>.]<flag>

OperandDescription

wrd

Name of the destination WDR

Decode as unsigned(WRD)

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

flag

Flag to check. Valid values:

• C: Carry flag
• M: MSB flag
• L: LSB flag
• Z: Zero flag
Assembly Syntax Value
c 0
m 1
l 2
z 3

Decode as unsigned(FLAG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SEL FG FLAG WRS2 WRS1 0 0 0 WRD 0 0 0 1 0 1 1

### Operation

In the listing below, operand flag is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

flag_is_set = FLAGs[flag_group].get_by_idx(flag)
wrs = wrs1 if flag_is_set else wrs2
value = WDRs[wrs]
WDRs[wrd] ⇐ value


## BN.CMP

Compare. Subtracts the second WDR value from the first one and updates flags. This instruction is identical to BN.SUB, except that no result register is written.

BN.CMP <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.CMP FG ST SB WRS2 WRS1 0 0 1 0 0 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)

full_result = a - b_shifted
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


## BN.CMPB

Compare with Borrow. Subtracts the second WDR value from the first one and updates flags. This instruction is identical to BN.SUBB, except that no result register is written.

BN.CMPB <wrs1>, <wrs2>[ <shift_type> <shift_bits>][, FG<flag_group>]

OperandDescription

wrs1

Name of the first source WDR

Decode as unsigned(WRS1)

wrs2

Name of the second source WDR

Decode as unsigned(WRS2)

shift_type

The direction of an optional shift applied to <wrs2>.

Assembly Syntax Value
<< 0
>> 1

Decode as unsigned(ST)

shift_bits

Number of bits by which to shift <wrs2>. Defaults to 0.

Valid range: 0 to 248 in steps of 8.

Decode as unsigned({SB, 3'b0})

flag_group

Flag group to use. Defaults to 0.

Valid range: 0 to 1.

Decode as unsigned(FG)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.CMPB FG ST SB WRS2 WRS1 0 1 1 0 0 0 1 0 1 1

### Operation

In the listing below, operand shift_type is referred to by its integer value. The operand table above shows how this corresponds to assembly syntax.

a = WDRs[wrs1]
b = WDRs[wrs2]
b_shifted = logical_byte_shift(b, shift_type, shift_bytes)
borrow = int(FLAGs[flag_group].C)

full_result = a - b_shifted - borrow
mask256 = (1 << 256) - 1
carry_flag = bool((full_result >> 256) & 1)

FLAGs[flag_group] ⇐ flags


## BN.LID

Load Word (indirect source, indirect destination). Load a WLEN-bit little-endian value from data memory.

The load address is offset plus the value in the GPR grs1. The loaded value is stored into the WDR given by the bottom 5 bits of the GPR grd.

After the operation, either the value in the GPR grs1, or the value in grd can be optionally incremented. Specifying both grd_inc and grs1_inc results in an error (with error code ErrCodeIllegalInsn).

• If grd_inc is set, grd is updated to be *grd + 1.
• If grs1_inc is set, the value in grs1 is incremented by value WLEN/8 (one word).

The memory address must be aligned to WLEN bits. Any address that is unaligned or is above the top of memory results in an error (setting bit bad_data_addr in ERR_BITS). Any *grd value greater than 31 before executing the instruction results in an error (setting bit illegal_insn in ERR_BITS) and no load or optional increment occurring. This instruction takes 2 cycles.

BN.LID <grd>[<grd_inc>], <offset>(<grs1>[<grs1_inc>])

OperandDescription

grd

Name of the GPR referencing the destination WDR

Decode as unsigned(GRD)

grs1

Name of the GPR containing the memory byte address. The value contained in the referenced GPR must be WLEN-aligned.

Decode as unsigned(GRS1)

offset

Offset value. Must be WLEN-aligned.

Valid range: -16384 to 16352 in steps of 32.

Decode as signed({OFF_1, OFF_0, 5'b0})

grs1_inc

Increment the value in <grs1> by WLEN/8 (one word). Cannot be specified together with grd_inc.

To specify, use the literal syntax ++

Decode as unsigned(INC1)

grd_inc

Increment the value in <grd> by one. Cannot be specified together with grs1_inc.

To specify, use the literal syntax ++

Decode as unsigned(INCD)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.LID OFF_1 GRD GRS1 1 0 0 OFF_0 INC1 INCD 0 0 0 1 0 1 1

### Operation

In the listing below, operands grs1_inc and grd_inc are referred to by their integer value. The operand table above shows how this corresponds to assembly syntax.

grs1_val = GPRs[grs1]
addr = (grs1_val + offset) & ((1 << 32) - 1)
grd_val = GPRs[grd]

if grd_val > 31:
state.stop_at_end_of_cycle(ILLEGAL_INSN)
else:
wrd = grd_val & 0x1f
WDRs[wrd] ⇐ value

if grd_inc:
new_grd_val = grd_val + 1
GPRs[grd] ⇐ new_grd_val

if grs1_inc:
new_grs1_val = (grs1_val + 32) & ((1 << 32) - 1)
GPRs[grs1] ⇐ new_grs1_val


## BN.SID

Store Word (indirect source, indirect destination). Store a WDR to memory as a WLEN-bit little-endian value.

The store address is offset plus the value in the GPR grs1. The value to store is taken from the WDR given by the bottom 5 bits of the GPR grs2.

After the operation, either the value in the GPR grs1, or the value in grs2 can be optionally incremented. Specifying both grs1_inc and grs2_inc results in an error (with error code ErrCodeIllegalInsn).

• If grs1_inc is set, the value in grs1 is incremented by the value WLEN/8 (one word).
• If grs2_inc is set, the value in grs2 is updated to be *grs2 + 1.

The memory address must be aligned to WLEN bits. Any address that is unaligned or is above the top of memory results in an error (setting bit bad_data_addr in ERR_BITS). Any *grs2 value greater than 31 before executing the instruction results in an error (setting bit illegal_insn in ERR_BITS) and no store or optional increment occurring.

BN.SID <grs2>[<grs2_inc>], <offset>(<grs1>[<grs1_inc>])

OperandDescription

grs1

Name of the GPR containing the memory byte address. The value contained in the referenced GPR must be WLEN-aligned.

Decode as unsigned(GRS1)

grs2

Name of the GPR referencing the source WDR.

Decode as unsigned(GRS2)

offset

Offset value. Must be WLEN-aligned.

Valid range: -16384 to 16352 in steps of 32.

Decode as signed({OFF_1, OFF_0, 5'b0})

grs1_inc

Increment the value in <grs1> by WLEN/8 (one word). Cannot be specified together with grs2_inc.

To specify, use the literal syntax ++

Decode as unsigned(INC1)

grs2_inc

Increment the value in <grs2> by one. Cannot be specified together with grs1_inc.

To specify, use the literal syntax ++

Decode as unsigned(INC2)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.SID OFF_1 GRS2 GRS1 1 0 1 OFF_0 INC1 INC2 0 0 0 1 0 1 1

### Operation

In the listing below, operands grs1_inc and grs2_inc are referred to by their integer value. The operand table above shows how this corresponds to assembly syntax.

grs1_val = GPRs[grs1]
addr = (grs1_val + offset) & ((1 << 32) - 1)

grs2_val = GPRs[grs2]

if grs2_val > 31:
state.stop_at_end_of_cycle(ILLEGAL_INSN)
else:
wrs = grs2_val & 0x1f
wrs_val = WDRs[wrs]

if grs1_inc:
new_grs1_val = (grs1_val + 32) & ((1 << 32) - 1)
GPRs[grs1] ⇐ new_grs1_val

if grs2_inc:
new_grs2_val = grs2_val + 1
GPRs[grs2] ⇐ new_grs2_val


## BN.MOV

Copy content between WDRs (direct addressing).

BN.MOV <wrd>, <wrs>

OperandDescription

wrd

Decode as unsigned(WRD)

wrs

Decode as unsigned(WRS)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.MOV 0 WRS 1 1 0 WRD 0 0 0 1 0 1 1

### Operation

value = WDRs[wrs]
WDRs[wrd] ⇐ value


## BN.MOVR

Copy content between WDRs (register-indirect addressing). Copies WDR contents between registers with indirect addressing.

After the operation, either the value in the GPR grd, or the value in grs can be optionally incremented. Specifying both grd_inc and grs_inc results in an error (with error code ErrCodeIllegalInsn).

• If grd_inc is set, grd is updated to be *grd + 1.
• If grs_inc is set, grs is updated to be *grs + 1.

Any *grd or *grs value greater than 31 results in an error (setting bit illegal_insn in ERR_BITS)

BN.MOVR <grd>[<grd_inc>], <grs>[<grs_inc>]

OperandDescription

grd

Name of the GPR containing the destination WDR.

Decode as unsigned(GRD)

grs

Name of the GPR referencing the source WDR.

Decode as unsigned(GRS)

grd_inc

Increment the value in <grd> by one. Cannot be specified together with grs_inc.

To specify, use the literal syntax ++

Decode as unsigned(GRD_INC)

grs_inc

Increment the value in <grs> by one. Cannot be specified together with grd_inc.

To specify, use the literal syntax ++

Decode as unsigned(GRS_INC)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.MOVR 1 GRD GRS 1 1 0 GRS_INC GRD_INC 0 0 0 1 0 1 1

### Operation

In the listing below, operands grd_inc and grs_inc are referred to by their integer value. The operand table above shows how this corresponds to assembly syntax.

grd_val = GPRs[grd]
grs_val = GPRs[grs]

if grd_val > 31:
state.stop_at_end_of_cycle(ILLEGAL_INSN)
elif grs_val > 31:
state.stop_at_end_of_cycle(ILLEGAL_INSN)
else:
wrd = grd_val & 0x1f
wrs = grs_val & 0x1f

value = WDRs[wrs]
WDRs[wrd] ⇐ value

if grd_inc:
new_grd_val = grd_val + 1
GPRs[grd] ⇐ new_grd_val

if grs_inc:
new_grs_val = grs_val + 1
GPRs[grs] ⇐ new_grs_val


## BN.WSRR

Read WSR to register. Reads a WSR to a WDR. If wsr isn’t the index of a valid WSR, this halts with an error (TODO: Specify error code).

BN.WSRR <wrd>, <wsr>

OperandDescription

wrd

Destination WDR

Decode as unsigned(WRD)

wsr

Valid range: 0 to 255.

Decode as unsigned(WSR)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.WSRR 0 WSR 1 1 1 WRD 0 0 0 1 0 1 1

### Operation

val = WSRs[wsr]
WDRs[wrd] ⇐ val


## BN.WSRW

Write WSR from register. Writes a WDR to a WSR. If wsr isn’t the index of a valid WSR, this halts with an error (TODO: Specify error code).

BN.WSRW <wsr>, <wrs>

OperandDescription

wsr

Valid range: 0 to 255.

Decode as unsigned(WSR)

wrs

Source WDR

Decode as unsigned(WRS)

 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 BN.WSRW 1 WSR WRS 1 1 1 0 0 0 1 0 1 1

### Operation

val = WDRs[wrs]
WSRs[wsr] ⇐ val