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 registersWDRs
: Wide data registersCSRs
: Control and status registersWSRs
: 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
return shifted & mask256
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
Add.
ADD <grd>, <grs1>, <grs2>
This instruction is defined in the RV32I instruction set.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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
Add Immediate.
ADDI <grd>, <grs1>, <imm>
This instruction is defined in the RV32I instruction set.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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
Load Upper Immediate.
LUI <grd>, <imm>
This instruction is defined in the RV32I instruction set.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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
).
LW <grd>, <offset>(<grs1>)
This instruction is defined in the RV32I instruction set.
This instruction takes 2 cycles.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
|
Decode as |
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)
result = DMEM.load_u32(addr)
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.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
|
Decode as |
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]
DMEM.store_u32(addr, value)
BEQ
Branch Equal.
BEQ <grs1>, <grs2>, <offset>
This instruction is defined in the RV32I instruction set.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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
Jump And Link.
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>
.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
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)
GPRs[grd] ⇐ link_pc
PC ⇐ offset
JALR
Jump And Link Register.
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.
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Valid range: Decode as |
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)
GPRs[grd] ⇐ link_pc
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.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
|
Decode as |
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.
Operand | Description |
---|---|
|
Decode as |
|
Valid range: Decode as |
|
Decode as |
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 with the, setting bit loop
in ERR_BITS
.
For more information on how to correctly use LOOP
see loop nesting.
LOOP <grs>, <bodysize>
Operand | Description |
---|---|
|
Name of the GPR containing the number of iterations Decode as |
|
Number of instructions in the loop body Valid range: Decode as |
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>
Operand | Description |
---|---|
|
Number of iterations Valid range: Decode as |
|
Number of instructions in the loop body Valid range: Decode as |
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.
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).
BN.ADD
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
FLAGs[flag_group] ⇐ flags
BN.ADDC
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
FLAGs[flag_group] ⇐ flags
BN.ADDI
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>]
Operand | Description |
---|---|
|
Name of the destination WDR Decode as |
|
Name of the source WDR Decode as |
|
Immediate value Valid range: Decode as |
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
FLAGs[flag_group] ⇐ flags
BN.ADDM
Pseudo-Modulo Add. Add two WDR values, modulo the MOD WSR.
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>
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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>
Operand | Description |
---|---|
|
Zero the accumulator before accumulating the multiply result. To specify, use the literal syntax Decode as |
|
First source WDR Decode as |
|
Quarter-word select for Valid values:
Valid range: Decode as |
|
Second source WDR Decode as |
|
Quarter-word select for Valid values:
Valid range: Decode as |
|
The number of bits to shift the Valid range: Decode as |
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>]
Operand | Description |
---|---|
|
Zero the accumulator before accumulating the multiply result. To specify, use the literal syntax Decode as |
|
Destination WDR. Decode as |
|
First source WDR Decode as |
|
Quarter-word select for Valid values:
Valid range: Decode as |
|
Second source WDR Decode as |
|
Quarter-word select for Valid values:
Valid range: Decode as |
|
The number of bits to shift the Valid range: Decode as |
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Zero the accumulator before accumulating the multiply result. To specify, use the literal syntax Decode as | ||||||
|
Destination WDR. Decode as | ||||||
|
Half-word select for
Decode as | ||||||
|
First source WDR Decode as | ||||||
|
Quarter-word select for Valid values:
Valid range: Decode as | ||||||
|
Second source WDR Decode as | ||||||
|
Quarter-word select for Valid values:
Valid range: Decode as | ||||||
|
The number of bits to shift the Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
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>]
Operand | Description |
---|---|
|
Name of the destination WDR Decode as |
|
Name of the source WDR Decode as |
|
Immediate value Valid range: Decode as |
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
WDRs[wrd] ⇐ masked_result
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>
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
|
Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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>
Operand | Description |
---|---|
|
Name of the destination WDR Decode as |
|
Name of the first source WDR Decode as |
|
Name of the second source WDR Decode as |
|
Number of bits to shift the second source register by. Valid range: 0..(WLEN-1). Valid range: Decode as |
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>
Operand | Description | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
Name of the destination WDR Decode as | ||||||||||
|
Name of the first source WDR Decode as | ||||||||||
|
Name of the second source WDR Decode as | ||||||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as | ||||||||||
|
Flag to check. Valid values:
Decode as |
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
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>]
Operand | Description | ||||||
---|---|---|---|---|---|---|---|
|
Name of the first source WDR Decode as | ||||||
|
Name of the second source WDR Decode as | ||||||
|
The direction of an optional shift applied to
Decode as | ||||||
|
Number of bits by which to shift Valid range: Decode as | ||||||
|
Flag group to use. Defaults to 0. Valid range: Decode as |
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
masked_result = full_result & mask256
carry_flag = bool((full_result >> 256) & 1)
flags = FlagReg.mlz_for_result(carry_flag, masked_result)
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) & 0x1f
. - If
grs1_inc
is set, the value ingrs1
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
).
BN.LID <grd>[<grd_inc>], <offset>(<grs1>[<grs1_inc>])
This instruction takes 2 cycles.
Operand | Description |
---|---|
|
Name of the GPR referencing the destination WDR Decode as |
|
Name of the GPR containing the memory byte address. The value contained in the referenced GPR must be WLEN-aligned. Decode as |
|
Offset value. Must be WLEN-aligned. Valid range: Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
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]
wrd = grd_val & 0x1f
value = DMEM.load_u256(addr)
WDRs[wrd] ⇐ value
if grd_inc:
new_grd_val = (grd_val + 1) & 0x1f
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 ingrs1
is incremented by the value WLEN/8 (one word). - If
grs2_inc
is set, the value ingrs2
is updated to be(*grs2 + 1) & 0x1f
.
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
).
BN.SID <grs2>[<grs2_inc>], <offset>(<grs1>[<grs1_inc>])
Operand | Description |
---|---|
|
Name of the GPR containing the memory byte address. The value contained in the referenced GPR must be WLEN-aligned. Decode as |
|
Name of the GPR referencing the source WDR. Decode as |
|
Offset value. Must be WLEN-aligned. Valid range: Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
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]
wrs = grs2_val & 0x1f
wrs_val = WDRs[wrs]
DMEM.store_u256(addr, wrs_val)
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) & 0x1f
GPRs[grs2] ⇐ new_grs2_val
BN.MOV
Copy content between WDRs (direct addressing).
BN.MOV <wrd>, <wrs>
Operand | Description |
---|---|
|
Decode as |
|
Decode as |
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) & 0x1f
. - If
grs_inc
is set,grs
is updated to be(*grs + 1) & 0x1f
.
BN.MOVR <grd>[<grd_inc>], <grs>[<grs_inc>]
Operand | Description |
---|---|
|
Name of the GPR containing the destination WDR. Decode as |
|
Name of the GPR referencing the source WDR. Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
|
Increment the value in To specify, use the literal syntax Decode as |
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]
wrd = grd_val & 0x1f
wrs = grs_val & 0x1f
value = WDRs[wrs]
WDRs[wrd] ⇐ value
if grd_inc:
new_grd_val = (grd_val + 1) & 0x1f
GPRs[grd] ⇐ new_grd_val
if grs_inc:
new_grs_val = (grs_val + 1) & 0x1f
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>
Operand | Description |
---|---|
|
Destination WDR Decode as |
|
The WSR to read Valid range: Decode as |
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>
Operand | Description |
---|---|
|
The WSR to read Valid range: Decode as |
|
Source WDR Decode as |
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