| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| # A machine readable description of the ISA |
| # |
| # This is used for generating documentation, but also for random test |
| # and decoder generation. |
| |
| # The instruction groups (valid values for the "group" field in |
| # instruction entries). Used for documentation where groups appear in |
| # the order of this list and instructions are listed by group. This |
| # list must be nonempty. |
| insn-groups: |
| - key: base |
| title: Base Instruction Subset |
| doc: | |
| 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](https://riscv.org/specifications/isa-spec-pdf/). |
| Refer to the [RISC-V Unprivileged Specification](https://riscv.org/specifications/isa-spec-pdf/) for a detailed instruction specification. |
| Not all RV32 instructions are implemented. |
| The implemented subset is shown below. |
| Many instructions in the base instruction set have an equivalent in the big number instruction subset, enabling processor logic to be shared between the instruction subsets. |
| - key: bignum |
| title: Big Number Instruction Subset |
| doc: | |
| All Big Number (BN) instructions operate on the wide register file WREG. |
| |
| # Instruction encoding schemes |
| # |
| # These define the mapping between instruction operands and bits in the |
| # encoding. A scheme names zero or more named fields. It can also inherit from |
| # zero or more other schemes. |
| # |
| # The direct fields of a scheme are defined as a dictionary, mapping a field |
| # name (which will be matched up with instruction operands) to a value. In |
| # general, this value is itself a dictionary with the following keys: |
| # |
| # bits: A list of ranges of bits. A range is written <msb>-<lsb>, where both |
| # are integers (and msb >= lsb). Multiple ranges can be separated by |
| # commas. A degenerate range (with msb == lsb) can be written as a bare |
| # integer. Required. |
| # |
| # value: Optional. If specified, this should be a binary string for a fixed |
| # value for this field, prefixed with a "b" (to avoid the YAML parser |
| # reading it as a decimal number). Underscores in the string are |
| # ignored (to make it easier to show grouping) and 'x' means don't |
| # care. |
| # |
| # shift: Optional. If specified, this is the number of bits to shift the |
| # encoded value left to get the logical value. |
| # |
| # For brevity, if value and shift have their default values, the bits string |
| # can be used as the value for the field. |
| # |
| # A scheme can inherit from other schemes by listing their names in a 'parents' |
| # attribute. If the child scheme needs to set the value of a parents' field to |
| # something fixed, it can do so with the following syntax: |
| # |
| # parent_name(field_name=b11101, field_name2=b111) |
| # |
| # The fields of a scheme are recursively defined to be its direct fields plus |
| # the fields all its ancestors. |
| # |
| # A scheme is called complete if its fields cover the entire range of bits |
| # (0-31) and partial otherwise. |
| |
| encoding-schemes: |
| # A partial scheme that sets the bottom two bits to 2'b11 (as for all RISC-V |
| # uncompressed instructions) and defines an 'opcode' field for bits 6-2 |
| # (standard for RV32I instructions) |
| rv: |
| fields: |
| opcode: 6-2 |
| uncomp: |
| bits: 1-0 |
| value: b11 |
| |
| # A partial scheme defining a funct3 field in bits 14-12 (used in most RV32I |
| # instructions, and most BN.* custom instructions) |
| funct3: |
| fields: |
| funct3: 14-12 |
| |
| # RISC-V "R-type" encoding (reg <- fun(reg, reg)) |
| R: |
| parents: |
| - rv |
| - funct3 |
| fields: |
| funct7: 31-25 |
| rs2: 24-20 |
| rs1: 19-15 |
| rd: 11-7 |
| |
| # RISC-V "I-type" encoding (reg <- fun(imm, reg)) |
| I: |
| parents: |
| - rv |
| - funct3 |
| fields: |
| imm: 31-20 |
| rs1: 19-15 |
| rd: 11-7 |
| |
| # RISC-V "I-type" encoding sub-type for shifts (reg <- fun(imm, reg)) |
| Is: |
| parents: |
| - rv |
| - funct3 |
| fields: |
| arithmetic: 30 |
| shamt: 24-20 |
| rs1: 19-15 |
| rd: 11-7 |
| unused: |
| bits: 31,29-25 |
| value: b000000 |
| |
| # RISC-V "S-type" encoding (_ <- fun(reg, imm)) |
| S: |
| parents: |
| - rv |
| - funct3 |
| fields: |
| imm: 31-25,11-7 |
| rs2: 24-20 |
| rs1: 19-15 |
| |
| # RISC-V "B-type" encoding (like S, but different immediate layout; used for |
| # branches) |
| B: |
| parents: |
| - rv |
| - funct3 |
| fields: |
| imm: |
| bits: 31,7,30-25,11-8 |
| shift: 1 |
| rs2: 24-20 |
| rs1: 19-15 |
| |
| # RISC-V "U-type" encoding (reg <- fun(imm)) |
| U: |
| parents: |
| - rv |
| fields: |
| imm: |
| bits: 31-12 |
| shift: 12 |
| rd: 11-7 |
| |
| # RISC-V "J-type" encoding (like U, but different immediate layout; used for |
| # jumps) |
| J: |
| parents: |
| - rv |
| fields: |
| imm: |
| bits: 31,19-12,20,30-21 |
| shift: 1 |
| rd: 11-7 |
| |
| # A partial scheme for custom instructions with opcode b00010 |
| custom0: |
| parents: |
| - rv(opcode=b00010) |
| |
| # A partial scheme for custom instructions with opcode b01010 |
| custom1: |
| parents: |
| - rv(opcode=b01010) |
| |
| # A partial scheme for custom instructions with opcode b01110 |
| custom2: |
| parents: |
| - rv(opcode=b01110) |
| |
| # A partial scheme for custom instructions with opcode b11110 |
| custom3: |
| parents: |
| - rv(opcode=b11110) |
| |
| # A partial scheme for instructions that produce a dest WDR. |
| wrd: |
| fields: |
| wrd: 11-7 |
| |
| # A partial scheme for instructions that take two source WDRs and produce a |
| # dest WDR. |
| wdr3: |
| parents: |
| - wrd |
| fields: |
| wrs2: 24-20 |
| wrs1: 19-15 |
| |
| # A partial scheme that defines the 'fg' field (for <flag_group> operands) |
| fg: |
| fields: |
| fg: 31 |
| |
| # A partial scheme that defines the shift fields (type and bytes) |
| shift: |
| fields: |
| shift_type: 30 |
| shift_bytes: 29-25 |
| |
| # A partial scheme that defines a function field at bit 31 for OTBN logical |
| # operations |
| funct31: |
| fields: |
| funct31: 31 |
| |
| # A partial scheme for specialized 2 bit function field, we need a reduced |
| # size in the lower two bits of funct3 as RSHI spills over 1 bit from its |
| # immediate |
| funct2: |
| fields: |
| funct2: 13-12 |
| |
| # A specialised encoding for the loop instruction (only one source, no |
| # destination) |
| loop: |
| parents: |
| - custom3 |
| - funct2(funct2=b00) |
| fields: |
| bodysize: 31-20 |
| grs: 19-15 |
| fixed: |
| bits: 14,11-7 |
| value: bxxxxxx |
| |
| # A specialised encoding for the loopi instruction (which, unusually, has 2 |
| # immediates) |
| loopi: |
| parents: |
| - custom3 |
| - funct2(funct2=b01) |
| fields: |
| bodysize: 31-20 |
| iterations: 19-15,11-7 |
| fixed: |
| bits: 14 |
| value: bx |
| |
| # Used wide logical operations (bn.and, bn.or, bn.xor). |
| bna: |
| parents: |
| - custom1 |
| - wdr3 |
| - funct3 |
| - shift |
| - funct31 |
| |
| # Used for bn.not (no second source reg). |
| bnan: |
| parents: |
| - custom1 |
| - shift |
| - funct31 |
| - wrd |
| fields: |
| wrs1: 24-20 |
| fixed: |
| bits: 19-15 |
| value: bxxxxx |
| |
| # Used for the wide reg/reg ALU instructions. |
| bnaf: |
| parents: |
| - custom1 |
| - wdr3 |
| - funct3 |
| - shift |
| - fg |
| |
| # Used for the wide bn.addi and bn.subi instructions. |
| bnai: |
| parents: |
| - custom1 |
| - wrd |
| - funct3 |
| - fg |
| fields: |
| sub: 30 |
| imm: 29-20 |
| wrs: 19-15 |
| |
| # Used for bn.addm, bn.subm |
| bnam: |
| parents: |
| - custom1 |
| - wdr3 |
| - funct3 |
| fields: |
| sub: 30 |
| fixed: |
| bits: 31,29-25 |
| value: bxxxxxx |
| |
| # Used for bn.mulqacc |
| bnaq: |
| parents: |
| - custom2 |
| - wdr3 |
| fields: |
| wb: 31-30 |
| dh: 29 |
| qs2: 28-27 |
| qs1: 26-25 |
| acc: |
| bits: 14-13 |
| shift: 6 |
| z: 12 |
| |
| # Unusual scheme used for bn.rshi (the immediate bleeds into the usual funct3 |
| # field) |
| bnr: |
| parents: |
| - custom3 |
| - wdr3 |
| fields: |
| imm: 31-25,14 |
| funct2: 13-12 |
| |
| # Used by bn.sel. |
| bns: |
| parents: |
| - custom0 |
| - wdr3 |
| - funct3(funct3=b000) |
| - fg |
| fields: |
| fixed: |
| bits: 30-27 |
| value: bxxxx |
| flag: 26-25 |
| |
| # Used by bn.cmp and bn.cmpb |
| bnc: |
| parents: |
| - custom0 |
| - wdr3(wrd=bxxxxx) |
| - funct3 |
| - shift |
| - fg |
| |
| # Used by bn.lid and bn.sid |
| bnxid: |
| parents: |
| - custom0 |
| - funct3 |
| fields: |
| imm: |
| bits: 24-22,31-25 |
| shift: 4 |
| spp: 21 |
| dpp: 20 |
| rs: 19-15 |
| rd: 11-7 |
| |
| # Used by bn.mov and bn.movr |
| bnmov: |
| parents: |
| - custom0 |
| - funct3(funct3=b110) |
| fields: |
| indirect: 31 |
| fixed_top: |
| bits: 30-22 |
| value: bxxxxxxxxx |
| spp: 21 |
| dpp: 20 |
| src: 19-15 |
| dst: 11-7 |
| |
| # Used by bn.wsrrs and bn.wsrrw |
| wcsr: |
| parents: |
| - custom0 |
| - funct3(funct3=b111) |
| fields: |
| write: 31 |
| wcsr: 27-20 |
| wrs: 19-15 |
| wrd: 11-7 |
| fixed: |
| bits: 30-28 |
| value: bxxx |
| |
| # The instructions. Instructions are listed in the given order within |
| # each instruction group. There are the following fields: |
| # |
| # mnemonic: Instruction mnemonic (required) |
| # |
| # group: The instruction group in which this instruction should |
| # appear. Defaults to the first entry in the insn-groups |
| # list. (optional) |
| # |
| # rv32i: A boolean. If true, this instruction came from the RV32I ISA. |
| # Optional, defaults to false. |
| # |
| # synopsis: A longer name for this instruction. If set, used as a subtitle in |
| # the generated documentation. (optional) |
| # |
| # operands: A list of operand names. These have a special syntax, described |
| # below. (required) |
| # |
| # syntax: The syntax for the operands to the instruction. If not given, |
| # this is assumed to be the operands in order, separated by commas. |
| # If given, it should be a string with operand names in angle |
| # brackets ("<foo>, <bar>"). Any other non-whitespace characters |
| # are taken to be required literal syntax. So "foo<bar>" means "the |
| # string 'foo', followed by the bar operand". |
| # |
| # doc: Documentation for the instruction in markdown. (optional) |
| # |
| # decode: Python pseudocode for decoding instruction objects (optional) |
| # |
| # operation: Python pseudocode for the operation of the instruction (optional) |
| # |
| # note: Text that should appear in a callout banner at the top of the |
| # instruction documentation. (optional) |
| # |
| # glued-ops: A boolean. If true, the first operand in the syntax can appear |
| # immediately after the mnemonic (with no space). Optional, |
| # defaults to false. |
| # |
| # trailing-doc: Documentation that should appear after the syntax example but |
| # before the operand table. Useful for things like alternative |
| # assembly syntax, or deviations from the usual meaning of the |
| # instruction. (optional) |
| # |
| # The operands field should be a list, corresponding to the operands in the |
| # order they will appear in the syntax. Each operand is either a string (the |
| # operand name) or a dictionary. In the latter case, it has the following |
| # fields: |
| # |
| # name: The name of the operand. Required and must be unique. |
| # |
| # type: The type of the operand. A string. If this can be figured out |
| # from the operand name, it is optional. See below for a list of |
| # possible operand types and the rules for recognising them |
| # automatically. |
| # |
| # doc: A fragment of markdown that documents the operand |
| # |
| # The valid operand types are as follows: |
| # |
| # gpr: The name of a general purpose register. Syntax "grs" for a |
| # source; "grd" for a destination. |
| # |
| # wdr: The name of a wide register. Syntax "wrs" for a source; |
| # "wrd" for a destination. |
| # |
| # csr: The name of a CSR. Syntax "csr" (always considered a destination) |
| # |
| # wsr: The name of a WSR. Syntax "wsr" (always considered a destination) |
| # |
| # immediate: An immediate operand. Syntax "imm<n>" where n is the number of |
| # bits or just imm for an unspecified width. |
| # |
| # enum: An immediate with weird syntax. The syntax is "enum(a,b,c,d)" |
| # where a..d are the different possible syntaxes that can be used. |
| # The values are interpreted as immediates in enumeration order (so |
| # a is 0; d is 3). |
| # |
| # option: A 1-bit immediate with weird syntax. Written "option(foo)" to |
| # mean that if the string "foo" appears then the immediate has |
| # value 1 and if it doesn't the immediate has value 0. |
| # |
| # Operand types are inferred from names as follows: |
| # |
| # grd: GPR destination |
| # grs, grs<n> GPR source |
| # wrd: WDR destination |
| # wrs, wrs<n> WDR source |
| # csr CSR name |
| # wsr WSR name |
| # imm, imm<n> Immediate |
| # offset Immediate (unspecified width) |
| |
| insns: |
| - mnemonic: add |
| rv32i: true |
| synopsis: Add |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b000 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: addi |
| rv32i: true |
| synopsis: Add Immediate |
| operands: [grd, grs1, imm] |
| encoding: |
| scheme: I |
| mapping: |
| imm: imm |
| rs1: grs1 |
| funct3: b000 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: lui |
| rv32i: true |
| synopsis: Load Upper Immediate |
| operands: [grd, imm] |
| encoding: |
| scheme: U |
| mapping: |
| imm: imm |
| rd: grd |
| opcode: b01101 |
| |
| - mnemonic: sub |
| rv32i: true |
| synopsis: Subtract |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0100000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b000 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: sll |
| rv32i: true |
| synopsis: Logical left shift |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b001 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: slli |
| rv32i: true |
| synopsis: Logical left shift with Immediate |
| operands: |
| - grd |
| - grs1 |
| - &shamt-operand |
| name: shamt |
| type: imm |
| encoding: |
| scheme: Is |
| mapping: |
| arithmetic: b0 |
| shamt: shamt |
| rs1: grs1 |
| funct3: b001 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: srl |
| rv32i: true |
| synopsis: Logical right shift |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b101 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: srli |
| rv32i: true |
| synopsis: Logical right shift with Immediate |
| operands: |
| - grd |
| - grs1 |
| - *shamt-operand |
| encoding: |
| scheme: Is |
| mapping: |
| arithmetic: b0 |
| shamt: shamt |
| rs1: grs1 |
| funct3: b101 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: sra |
| rv32i: true |
| synopsis: Arithmetic right shift |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0100000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b101 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: srai |
| rv32i: true |
| synopsis: Arithmetic right shift with Immediate |
| operands: |
| - grd |
| - grs1 |
| - *shamt-operand |
| encoding: |
| scheme: Is |
| mapping: |
| arithmetic: b1 |
| shamt: shamt |
| rs1: grs1 |
| funct3: b101 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: and |
| rv32i: true |
| synopsis: Bitwise AND |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b111 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: andi |
| rv32i: true |
| synopsis: Bitwise AND with Immediate |
| operands: [grd, grs1, imm] |
| encoding: |
| scheme: I |
| mapping: |
| imm: imm |
| rs1: grs1 |
| funct3: b111 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: or |
| rv32i: true |
| synopsis: Bitwise OR |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b110 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: ori |
| rv32i: true |
| synopsis: Bitwise OR with Immediate |
| operands: [grd, grs1, imm] |
| encoding: |
| scheme: I |
| mapping: |
| imm: imm |
| rs1: grs1 |
| funct3: b110 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: xor |
| rv32i: true |
| synopsis: Bitwise XOR |
| operands: [grd, grs1, grs2] |
| encoding: |
| scheme: R |
| mapping: |
| funct7: b0000000 |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b100 |
| rd: grd |
| opcode: b01100 |
| |
| - mnemonic: xori |
| rv32i: true |
| synopsis: Bitwise XOR with Immediate |
| operands: [grd, grs, imm] |
| encoding: |
| scheme: I |
| mapping: |
| imm: imm |
| rs1: grs |
| funct3: b100 |
| rd: grd |
| opcode: b00100 |
| |
| - mnemonic: lw |
| rv32i: true |
| synopsis: Load Word |
| operands: [grd, offset, grs1] |
| syntax: <grd>, <offset>(<grs1>) |
| encoding: |
| scheme: I |
| mapping: |
| imm: offset |
| rs1: grs1 |
| funct3: b010 |
| rd: grd |
| opcode: b00000 |
| trailing-doc: | |
| Unaligned accesses are not supported. |
| Any unaligned access will result in an error. |
| |
| - mnemonic: sw |
| rv32i: true |
| synopsis: Store Word |
| operands: [grs2, offset, grs1] |
| syntax: <grs2>, <offset>(<grs1>) |
| encoding: |
| scheme: S |
| mapping: |
| imm: offset |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b010 |
| opcode: b01000 |
| trailing-doc: | |
| Unaligned accesses are not supported. |
| Any unaligned access will result in an error. |
| |
| - mnemonic: beq |
| rv32i: true |
| synopsis: Branch Equal |
| operands: [grs1, grs2, offset] |
| encoding: |
| scheme: B |
| mapping: |
| imm: offset |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b000 |
| opcode: b11000 |
| |
| - mnemonic: bne |
| rv32i: true |
| synopsis: Branch Not Equal |
| operands: [grs1, grs2, offset] |
| encoding: |
| scheme: B |
| mapping: |
| imm: offset |
| rs2: grs2 |
| rs1: grs1 |
| funct3: b001 |
| opcode: b11000 |
| |
| - mnemonic: jal |
| rv32i: true |
| synopsis: Jump And Link |
| operands: [grd, offset] |
| trailing-doc: | |
| 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>`. |
| encoding: |
| scheme: J |
| mapping: |
| imm: offset |
| rd: grd |
| opcode: b11011 |
| |
| - mnemonic: jalr |
| rv32i: true |
| synopsis: Jump And Link Register |
| operands: [grd, grs1, offset] |
| trailing-doc: | |
| 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. |
| encoding: |
| scheme: I |
| mapping: |
| imm: offset |
| rs1: grs1 |
| funct3: b000 |
| rd: grd |
| opcode: b11001 |
| |
| - mnemonic: csrrs |
| rv32i: true |
| synopsis: Atomic Read and Set bits in CSR |
| operands: [grd, csr, grs] |
| encoding: |
| scheme: I |
| mapping: |
| imm: csr |
| rs1: grs |
| funct3: b010 |
| rd: grd |
| opcode: b11100 |
| |
| - mnemonic: csrrw |
| rv32i: true |
| synopsis: Atomic Read/Write CSR |
| operands: [grd, csr, grs] |
| encoding: |
| scheme: I |
| mapping: |
| imm: csr |
| rs1: grs |
| funct3: b001 |
| rd: grd |
| opcode: b11100 |
| |
| - mnemonic: ecall |
| rv32i: true |
| synopsis: Environment Call |
| operands: [] |
| doc: | |
| Triggers the `done` interrupt to indicate the completion of the |
| operation. |
| encoding: |
| scheme: I |
| mapping: |
| imm: b000000000000 |
| rs1: b00000 |
| funct3: b000 |
| rd: b00000 |
| opcode: b11100 |
| |
| - mnemonic: loop |
| synopsis: Loop (indirect) |
| operands: |
| - name: grs |
| doc: Name of the GPR containing the number of iterations |
| - &bodysize-operand |
| name: bodysize |
| type: imm |
| doc: Number of instructions in the loop body |
| note: &loop-note | |
| The LOOP and LOOPI instructions are under-specified, and improvements |
| to them are being discussed. See |
| https://github.com/lowRISC/opentitan/issues/2496 for up-to-date |
| information. |
| doc: | |
| Repeat a sequence of code multiple times. The number of iterations is a |
| GPR value. The length of the loop is given as immediate. |
| trailing-doc: | |
| Alternative assembly notation: The size of the loop body is given by the |
| number of instructions in the parentheses. |
| |
| ``` |
| LOOP <grs> ( |
| # loop body |
| ) |
| ``` |
| encoding: |
| scheme: loop |
| mapping: |
| bodysize: bodysize |
| grs: grs |
| |
| - mnemonic: loopi |
| synopsis: Loop Immediate |
| operands: |
| - name: iterations |
| type: imm |
| doc: Number of iterations |
| - *bodysize-operand |
| note: *loop-note |
| doc: | |
| Repeat a sequence of code multiple times. The number of iterations is |
| given as an immediate, as is the length of the loop. The number of |
| iterations must be larger than zero. |
| trailing-doc: | |
| Alternative assembly notation. The size of the loop body is given by the |
| number of instructions in the parentheses. |
| |
| ``` |
| LOOPI <iterations> ( |
| # loop body |
| ) |
| ``` |
| encoding: |
| scheme: loopi |
| mapping: |
| bodysize: bodysize |
| iterations: iterations |
| |
| - mnemonic: bn.add |
| group: bignum |
| synopsis: Add |
| operands: &bn-add-operands |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - &bn-shift-type-operand |
| name: shift_type |
| type: enum(<<, >>) |
| doc: | |
| The direction of an optional shift applied to `<wrs2>`. |
| - &bn-shift-bytes-operand |
| name: shift_bytes |
| type: imm5 |
| doc: | |
| Number of bytes by which to shift `<wrs2>`. Defaults to 0. |
| - &bn-flag-group-operand |
| name: flag_group |
| type: imm1 |
| doc: Flag group to use. Defaults to 0. |
| syntax: &bn-add-syntax | |
| <wrd>, <wrs1>, <wrs2>[<shift_type> <shift_bytes>B][, FG<flag_group>] |
| doc: | |
| 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 |
| immediate before it is consumed by the operation. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| fg = DecodeFlagGroup(flag_group) |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| (result, flags_out) = AddWithCarry(a, b_shifted, "0") |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnaf |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b000 |
| wrd: wrd |
| |
| - mnemonic: bn.addc |
| group: bignum |
| synopsis: Add with Carry |
| operands: *bn-add-operands |
| syntax: *bn-add-syntax |
| doc: | |
| 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 immediate before it is consumed by the |
| operation. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| fg = DecodeFlagGroup(flag_group) |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| (result, flags_out) = AddWithCarry(a, b_shifted, FLAGS[flag_group].C) |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnaf |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b010 |
| wrd: wrd |
| |
| - mnemonic: bn.addi |
| group: bignum |
| synopsis: Add Immediate |
| operands: |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs |
| doc: Name of the source WDR |
| - name: imm |
| doc: Immediate value |
| - *bn-flag-group-operand |
| syntax: | |
| <wrd>, <wrs>, <imm> [, FG<flag_group>] |
| doc: | |
| Adds a zero-extended immediate to the value of a WDR, writes the result |
| to the destination WDR, and updates the flags. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| |
| fg = DecodeFlagGroup(flag_group) |
| i = ZeroExtend(imm, WLEN) |
| operation: | |
| (result, flags_out) = AddWithCarry(a, i, "0") |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnai |
| mapping: |
| fg: flag_group |
| sub: b0 |
| imm: imm |
| wrs: wrs |
| funct3: b100 |
| wrd: wrd |
| |
| - mnemonic: bn.addm |
| group: bignum |
| synopsis: Pseudo-Modulo Add |
| operands: [wrd, wrs1, wrs2] |
| doc: | |
| Adds two WDR values, subtracts the value of the MOD WSR once if |
| the result is equal or larger than MOD, and writes the result to |
| the destination WDR. This operation is a modulo addition if the |
| sum of the two input registers is smaller than twice the value |
| of the MOD WSR. Flags are not used or saved. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| operation: | |
| (result, ) = AddWithCarry(a, b, "0") |
| |
| if result >= MOD: |
| result = result - MOD |
| |
| WDR[d] = result |
| encoding: |
| scheme: bnam |
| mapping: |
| sub: b0 |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b101 |
| wrd: wrd |
| |
| - mnemonic: bn.mulqacc |
| group: bignum |
| synopsis: Quarter-word Multiply and Accumulate |
| operands: |
| - &mulqacc-zero-acc |
| name: zero_acc |
| type: option(.Z) |
| doc: Zero the accumulator before accumulating the multiply result. |
| - &mulqacc-wrs1 |
| name: wrs1 |
| doc: First source WDR |
| - &mulqacc-wrs1-qwsel |
| name: wrs1_qwsel |
| type: imm2 |
| doc: | |
| 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) |
| - &mulqacc-wrs2 |
| name: wrs2 |
| doc: Second source WDR |
| - &mulqacc-wrs2-qwsel |
| name: wrs2_qwsel |
| type: imm2 |
| doc: | |
| 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) |
| - &mulqacc-acc-shift-imm |
| name: acc_shift_imm |
| type: imm2 |
| doc: | |
| How many quarter-words (`WLEN/4` bits) to shift the `WLEN/2`-bit multiply result before accumulating. |
| syntax: | |
| [<zero_acc>] <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm> |
| glued-ops: true |
| doc: | |
| Multiplies two `WLEN/4` WDR values, shifts the product by `<acc_shift_imm>` and adds the result to the accumulator. |
| |
| For versions of the instruction with writeback, see `BN.MULQACC.WO` and `BN.MULQACC.SO`. |
| decode: | |
| writeback_variant = None |
| zero_accumulator = DecodeMulqaccZeroacc(zero_acc) |
| |
| d = None |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| d_hwsel = None |
| a_qwsel = DecodeQuarterWordSelect(wrs1_qwsel) |
| b_qwsel = DecodeQuarterWordSelect(wrs2_qwsel) |
| operation: &mulqacc-operation | |
| a_qw = GetQuarterWord(a, a_qwsel) |
| b_qw = GetQuarterWord(b, b_qwsel) |
| |
| mul_res = a_qw * b_qw |
| |
| if zero_accumulator: |
| ACC = 0 |
| |
| ACC = ACC + (mul_res << (acc_shift_imm * WLEN / 4)) |
| |
| if writeback_variant == 'shiftout': |
| if d_hwsel == 'L': |
| WDR[d][WLEN/2-1:0] = ACC[WLEN/2-1:0] |
| elif d_hwsel == 'U': |
| WDR[d][WLEN-1:WLEN/2] = ACC[WLEN/2-1:0] |
| ACC = ACC >> (WLEN/2) |
| |
| elif writeback_variant == 'writeout': |
| WDR[d] = ACC |
| encoding: |
| scheme: bnaq |
| mapping: |
| wb: b00 |
| dh: bx |
| qs2: wrs2_qwsel |
| qs1: wrs1_qwsel |
| wrs2: wrs2 |
| wrs1: wrs1 |
| acc: acc_shift_imm |
| z: zero_acc |
| wrd: bxxxxx |
| |
| - mnemonic: bn.mulqacc.wo |
| group: bignum |
| synopsis: Quarter-word Multiply and Accumulate with half-word writeback |
| operands: |
| - *mulqacc-zero-acc |
| - &mulqacc-wrd |
| name: wrd |
| doc: Destination WDR. |
| - *mulqacc-wrs1 |
| - *mulqacc-wrs1-qwsel |
| - *mulqacc-wrs2 |
| - *mulqacc-wrs2-qwsel |
| - *mulqacc-acc-shift-imm |
| syntax: | |
| [<zero_acc>] <wrd>, <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm> |
| glued-ops: true |
| doc: | |
| Multiplies two `WLEN/4` WDR values, shifts the product by `<acc_shift_imm>` and adds the result to the accumulator. |
| Writes the resulting accumulator to `<wrd>`. |
| decode: | |
| writeback_variant = 'writeout' |
| zero_accumulator = DecodeMulqaccZeroacc(zero_acc) |
| |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| d_hwsel = None |
| a_qwsel = DecodeQuarterWordSelect(wrs1_qwsel) |
| b_qwsel = DecodeQuarterWordSelect(wrs2_qwsel) |
| operation: *mulqacc-operation |
| encoding: |
| scheme: bnaq |
| mapping: |
| wb: b01 |
| dh: bx |
| qs2: wrs2_qwsel |
| qs1: wrs1_qwsel |
| wrs2: wrs2 |
| wrs1: wrs1 |
| acc: acc_shift_imm |
| z: zero_acc |
| wrd: wrd |
| |
| - mnemonic: bn.mulqacc.so |
| group: bignum |
| synopsis: Quarter-word Multiply and Accumulate with half-word writeback |
| operands: |
| - *mulqacc-zero-acc |
| - *mulqacc-wrd |
| - name: wrd_hwsel |
| type: enum(L,U) |
| doc: | |
| Half-word select for `<wrd>`. |
| A value of `L` means the less significant half-word; `U` means the more significant half-word. |
| - *mulqacc-wrs1 |
| - *mulqacc-wrs1-qwsel |
| - *mulqacc-wrs2 |
| - *mulqacc-wrs2-qwsel |
| - *mulqacc-acc-shift-imm |
| syntax: | |
| [<zero_acc>] <wrd>.<wrd_hwsel>, |
| <wrs1>.<wrs1_qwsel>, <wrs2>.<wrs2_qwsel>, <acc_shift_imm> |
| glued-ops: true |
| doc: | |
| Multiplies two `WLEN/4` WDR values, shifts the product by `<acc_shift_imm>` and adds the result to the accumulator. |
| Next, shifts the resulting accumulator right by half a word. |
| The bits that are shifted out are written to a half-word of `<wrd>`, selected with `<wrd_hwsel>`. |
| |
| decode: | |
| writeback_variant = 'shiftout' |
| zero_accumulator = DecodeMulqaccZeroacc(zero_acc) |
| |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| d_hwsel = DecodeHalfWordSelect(wrd_hwsel) |
| a_qwsel = DecodeQuarterWordSelect(wrs1_qwsel) |
| b_qwsel = DecodeQuarterWordSelect(wrs2_qwsel) |
| operation: *mulqacc-operation |
| encoding: |
| scheme: bnaq |
| mapping: |
| wb: b1x |
| dh: wrd_hwsel |
| qs2: wrs2_qwsel |
| qs1: wrs1_qwsel |
| wrs2: wrs2 |
| wrs1: wrs1 |
| acc: acc_shift_imm |
| z: zero_acc |
| wrd: wrd |
| |
| - mnemonic: bn.sub |
| group: bignum |
| synopsis: Subtraction |
| operands: &bn-sub-operands |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - *bn-shift-type-operand |
| - *bn-shift-bytes-operand |
| - *bn-flag-group-operand |
| syntax: *bn-add-syntax |
| doc: | |
| 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 immediate before it is consumed by the operation. |
| decode: &bn-sub-decode | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| fg = DecodeFlagGroup(flag_group) |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| (result, flags_out) = AddWithCarry(a, -b_shifted, "0") |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnaf |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b001 |
| wrd: wrd |
| |
| - mnemonic: bn.subb |
| group: bignum |
| synopsis: Subtract with borrow |
| operands: *bn-sub-operands |
| syntax: *bn-add-syntax |
| doc: | |
| 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 immediate before it is consumed by the operation. |
| decode: *bn-sub-decode |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| (result, flags_out) = AddWithCarry(a, -b_shifted, ~FLAGS[flag_group].C) |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnaf |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b011 |
| wrd: wrd |
| |
| - mnemonic: bn.subi |
| group: bignum |
| synopsis: Subtract Immediate |
| operands: |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs |
| doc: Name of the source WDR |
| - name: imm |
| doc: Immediate value |
| - *bn-flag-group-operand |
| syntax: <wrd>, <wrs>, <imm> [, FG<flag_group>] |
| doc: | |
| Subtracts a zero-extended immediate from the value of a WDR, writes the result to the destination WDR, and updates the flags. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| |
| fg = DecodeFlagGroup(flag_group) |
| i = ZeroExtend(imm, WLEN) |
| operation: | |
| (result, flags_out) = AddWithCarry(a, -i, "0") |
| |
| WDR[d] = result |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnai |
| mapping: |
| fg: flag_group |
| sub: b1 |
| imm: imm |
| wrs: wrs |
| funct3: b100 |
| wrd: wrd |
| |
| - mnemonic: bn.subm |
| group: bignum |
| synopsis: Pseudo-modulo subtraction |
| operands: [wrd, wrs1, wrs2] |
| doc: | |
| Subtracts the second WDR value from the first WDR value, performs a modulo operation with the MOD WSR, and writes the result to the destination WDR. |
| This operation is equivalent to a modulo subtraction as long as `wrs1 - wrs2 >= -MOD` holds. |
| This constraint is not checked in hardware. |
| Flags are not used or saved. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| operation: | |
| (result, ) = AddWithCarry(a, -b, "0") |
| |
| if result >= MOD: |
| result = result - MOD |
| |
| WDR[d] = result |
| encoding: |
| scheme: bnam |
| mapping: |
| sub: b1 |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b101 |
| wrd: wrd |
| |
| - mnemonic: bn.and |
| group: bignum |
| synopsis: Bitwise AND |
| operands: &bn-and-operands |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - *bn-shift-type-operand |
| - *bn-shift-bytes-operand |
| syntax: &bn-and-syntax | |
| <wrd>, <wrs1>, <wrs2> [, <shift_type> <shift_bytes>B] |
| doc: | |
| 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. |
| decode: &bn-and-decode | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| result = a & b_shifted |
| |
| WDR[d] = result |
| encoding: |
| scheme: bna |
| mapping: |
| funct31: b0 |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b110 |
| wrd: wrd |
| |
| - mnemonic: bn.or |
| group: bignum |
| synopsis: Bitwise OR |
| operands: *bn-and-operands |
| syntax: *bn-and-syntax |
| doc: | |
| 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. |
| decode: *bn-and-decode |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| result = a | b_shifted |
| |
| WDR[d] = result |
| encoding: |
| scheme: bna |
| mapping: |
| funct31: b1 |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b110 |
| wrd: wrd |
| |
| - mnemonic: bn.not |
| group: bignum |
| synopsis: Bitwise NOT |
| operands: |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs |
| doc: Name of the source WDR |
| - *bn-shift-type-operand |
| - *bn-shift-bytes-operand |
| syntax: | |
| <wrd>, <wrs> [, <shift_type> <shift_bytes>B] |
| doc: | |
| Negates the value in `<wrs>`, storing the result into `<wrd>`. |
| The source value can be shifted by an immediate before it is consumed by the operation. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| a_shifted = ShiftReg(a, st, sb) |
| result = ~a_shifted |
| |
| WDR[d] = result |
| encoding: |
| scheme: bna |
| mapping: |
| funct31: b0 |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs |
| wrs1: bxxxxx |
| funct3: b111 |
| wrd: wrd |
| |
| - mnemonic: bn.xor |
| group: bignum |
| synopsis: Bitwise XOR |
| operands: *bn-and-operands |
| syntax: *bn-and-syntax |
| doc: | |
| 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. |
| decode: *bn-and-decode |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| result = a ^ b_shifted |
| |
| WDR[d] = result |
| encoding: |
| scheme: bnaf |
| mapping: |
| fg: b1 |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b111 |
| wrd: wrd |
| |
| - mnemonic: bn.rshi |
| group: bignum |
| synopsis: Concatenate and right shift immediate |
| operands: |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - name: imm |
| doc: | |
| Number of bits to shift the second source register by. Valid range: 0..(WLEN-1). |
| syntax: | |
| <wrd>, <wrs1>, <wrs2> >> <imm> |
| doc: | |
| The concatenation of the content from the WDRs referenced by `wrs1` and `wrs2` (`wrs1` forms the upper part) is right shifted by an immediate value and truncated to WLEN bit. |
| The result is stored in the WDR referenced by `wrd`. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| shift_bit = Uint(imm) |
| operation: | |
| WDR[d] = (((a << WLEN) | b) >> shift_bit)[WLEN-1:0] |
| encoding: |
| scheme: bnr |
| mapping: |
| imm: imm |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct2: b11 |
| wrd: wrd |
| |
| - mnemonic: bn.sel |
| group: bignum |
| synopsis: Flag Select |
| operands: |
| - name: wrd |
| doc: Name of the destination WDR |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - *bn-flag-group-operand |
| - name: flag |
| type: enum(C, M, L, Z) |
| doc: | |
| Flag to check. Valid values: |
| - C: Carry flag |
| - M: MSB flag |
| - L: LSB flag |
| - Z: Zero flag |
| syntax: | |
| <wrd>, <wrs1>, <wrs2>, [FG<flag_group>.]<flag> |
| doc: | |
| 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. |
| decode: | |
| d = UInt(wrd) |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| fg = DecodeFlagGroup(flag_group) |
| flag = DecodeFlag(flag) |
| operation: | |
| flag_is_set = FLAGS[fg].get(flag) |
| |
| WDR[d] = wrs1 if flag_is_set else wrs2 |
| encoding: |
| scheme: bns |
| mapping: |
| fg: flag_group |
| flag: flag |
| wrs2: wrs2 |
| wrs1: wrs1 |
| wrd: wrd |
| |
| - mnemonic: bn.cmp |
| group: bignum |
| synopsis: Compare |
| operands: &bn-cmp-operands |
| - name: wrs1 |
| doc: Name of the first source WDR |
| - name: wrs2 |
| doc: Name of the second source WDR |
| - *bn-shift-type-operand |
| - *bn-shift-bytes-operand |
| - *bn-flag-group-operand |
| syntax: &bn-cmp-syntax | |
| <wrs1>, <wrs2>[, <shift_type> <shift_bytes>B][, FG<flag_group>] |
| doc: | |
| 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. |
| decode: &bn-cmp-decode | |
| a = UInt(wrs1) |
| b = UInt(wrs2) |
| |
| fg = DecodeFlagGroup(flag_group) |
| sb = UInt(shift_bytes) |
| st = DecodeShiftType(shift_type) |
| operation: | |
| b_shifted = ShiftReg(b, st, sb) |
| (, flags_out) = AddWithCarry(a, -b_shifted, "0") |
| |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnc |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b001 |
| |
| - mnemonic: bn.cmpb |
| group: bignum |
| synopsis: Compare with Borrow |
| operands: *bn-cmp-operands |
| syntax: *bn-cmp-syntax |
| doc: | |
| 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. |
| decode: *bn-cmp-decode |
| operation: | |
| (, flags_out) = AddWithCarry(a, -b, ~FLAGS[flag_group].C) |
| |
| FLAGS[flag_group] = flags_out |
| encoding: |
| scheme: bnc |
| mapping: |
| fg: flag_group |
| shift_type: shift_type |
| shift_bytes: shift_bytes |
| wrs2: wrs2 |
| wrs1: wrs1 |
| funct3: b011 |
| |
| - mnemonic: bn.lid |
| group: bignum |
| synopsis: Load Word (indirect source, indirect destination) |
| operands: |
| - name: grd |
| doc: Name of the GPR referencing the destination WDR |
| - name: grs1 |
| doc: | |
| Name of the GPR containing the memory byte address. |
| The value contained in the referenced GPR must be WLEN-aligned. |
| - name: offset |
| doc: | |
| Offset value. |
| Must be WLEN-aligned. |
| - name: grs1_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grs1>` by WLEN/8 (one word). |
| Cannot be specified together with `grd_inc`. |
| - name: grd_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grd>` by one. |
| Cannot be specified together with `grs1_inc`. |
| syntax: | |
| <grd>[<grd_inc>], <offset>(<grs1>[<grs1_inc>]) |
| doc: | |
| Calculates a byte memory address by adding the offset to the value in the GPR `grs1`. |
| The value from this memory address is then copied into the WDR pointed to by the value in GPR `grd`. |
| |
| After the operation, either the value in the GPR `grs1`, or the value in `grd` can be optionally incremented. |
| |
| - If `grs1_inc` is set, the value in `grs1` is incremented by the value WLEN/8 (one word). |
| - If `grd_inc` is set, the value in `grd` is incremented by the value 1. |
| |
| Only aligned (to WLEN sized regions) accesses are supported. |
| Any unaligned access will result in an error. |
| |
| TODO: how to handle overflows? |
| decode: | |
| rd = UInt(grd) |
| rs1 = UInt(grs1) |
| offset = UInt(offset) |
| operation: | |
| mem_addr = GPR[rs1] + offset |
| wdr_dest = GPR[rd] |
| |
| if grs1_inc and grd_inc: |
| raise Unsupported() # prevented in encoding |
| if mem_addr % (WLEN / 8): |
| raise Unaligned() |
| |
| mem_index = mem_addr // (WLEN / 8) |
| |
| WDR[wdr_dest] = LoadWlenWordFromMemory(mem_index) |
| |
| if grs1_inc: |
| GPR[rs1] = GPR[rs1] + (WLEN / 8) |
| if grd_inc: |
| GPR[rd] = GPR[rd] + 1 |
| encoding: |
| scheme: bnxid |
| mapping: |
| imm: offset |
| spp: grs1_inc |
| dpp: grd_inc |
| rs: grs1 |
| funct3: b100 |
| rd: grd |
| |
| - mnemonic: bn.sid |
| group: bignum |
| synopsis: Store Word (indirect source, indirect destination) |
| operands: |
| - name: grs1 |
| doc: | |
| Name of the GPR containing the memory byte address. |
| The value contained in the referenced GPR must be WLEN-aligned. |
| - name: grs2 |
| doc: Name of the GPR referencing the source WDR. |
| - name: offset |
| doc: | |
| Offset value. |
| Must be WLEN-aligned. |
| - name: grs1_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grs1>` by WLEN/8 (one word). |
| Cannot be specified together with `grs2_inc`. |
| - name: grs2_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grs2>` by one. |
| Cannot be specified together with `grs1_inc`. |
| syntax: | |
| <grs1>[<grs1_inc>], <offset>(<grs2>[<grs2_inc>]) |
| doc: | |
| Calculates a byte memory address by adding the offset to the value in the GPR `grs1`. |
| The value from the WDR pointed to by `grs2` is then copied into the memory. |
| |
| After the operation, either the value in the GPR `grs1`, or the value in `grs2` can be optionally incremented. |
| |
| - 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 incremented by the value 1. |
| |
| Only aligned (to WLEN sized regions) accesses are supported. |
| Any unaligned access will result in an error. |
| decode: | |
| rs1 = UInt(grs1) |
| rs2 = UInt(grs2) |
| offset = UInt(offset) |
| operation: | |
| mem_addr = GPR[rs1] + offset |
| wdr_src = GPR[rs2] |
| |
| if grs1_inc and grs2_inc: |
| raise Unsupported() # prevented in encoding |
| if mem_addr % (WLEN / 8): |
| raise Unaligned() |
| |
| mem_index = mem_addr // (WLEN / 8) |
| |
| StoreWlenWordToMemory(mem_index, WDR[wdr_src]) |
| |
| if grs1_inc: |
| GPR[rs1] = GPR[rs1] + (WLEN / 8) |
| if grs2_inc: |
| GPR[rs2] = GPR[rs2] + 1 |
| encoding: |
| scheme: bnxid |
| mapping: |
| imm: offset |
| spp: grs1_inc |
| dpp: grs2_inc |
| rs: grs1 |
| funct3: b101 |
| rd: grs2 |
| |
| - mnemonic: bn.mov |
| group: bignum |
| synopsis: Copy content between WDRs (direct addressing) |
| operands: [wrd, wrs] |
| decode: | |
| s = UInt(wrs) |
| d = UInt(wrd) |
| operation: WDR[d] = WDR[s] |
| encoding: |
| scheme: bnmov |
| mapping: |
| indirect: b0 |
| spp: bx |
| dpp: bx |
| src: wrs |
| dst: wrd |
| |
| - mnemonic: bn.movr |
| group: bignum |
| synopsis: Copy content between WDRs (register-indirect addressing) |
| operands: |
| - name: grd |
| doc: Name of the GPR containing the destination WDR. |
| - name: grs |
| doc: Name of the GPR referencing the source WDR. |
| - name: grd_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grd>` by one. |
| Cannot be specified together with `grs_inc`. |
| - name: grs_inc |
| type: option(++) |
| doc: | |
| Increment the value in `<grs>` by one. |
| Cannot be specified together with `grd_inc`. |
| syntax: | |
| <grd>[<grd_inc>], <grs>[<grs_inc>] |
| doc: | |
| Copy WDR contents between registers with indirect addressing. |
| Optionally, either the source or the destination register address can be incremented by 1. |
| decode: | |
| s = UInt(grs) |
| d = UInt(grd) |
| operation: | |
| WDR[GPR[d]] = WDR[GPR[s]] |
| |
| if grs_inc: |
| GPR[s] = GPR[s] + 1 |
| if grd_inc: |
| GPR[d] = GPR[d] + 1 |
| encoding: |
| scheme: bnmov |
| mapping: |
| indirect: b1 |
| spp: grs_inc |
| dpp: grd_inc |
| src: grs |
| dst: grd |
| |
| - mnemonic: bn.wsrrs |
| group: bignum |
| synopsis: Atomic Read and Set Bits in WSR |
| operands: [wrd, wsr, wrs] |
| encoding: |
| scheme: wcsr |
| mapping: |
| write: b0 |
| wcsr: wsr |
| wrs: wrs |
| wrd: wrd |
| |
| - mnemonic: bn.wsrrw |
| group: bignum |
| synopsis: Atomic Read/Write WSR |
| operands: [wrd, wsr, wrs] |
| encoding: |
| scheme: wcsr |
| mapping: |
| write: b1 |
| wcsr: wsr |
| wrs: wrs |
| wrd: wrd |