Skip to content

TinyRV1 ISA

The TinyRV1 ISA is a very limited subset of the RISC-V ISA, an open-source instruction set architecture that has gained significant popularity in the last decade.

1. TinyRV1 Architectural State

1.1. Data Formats

TinyRV1 only supports 4B (four-byte) signed and unsigned integer values. There are no byte nor half-word values and no floating-point.

1.2. General-Purpose Registers

There are 31 general-purpose registers (GPRs) x1-x31 (called x registers), which hold integer values. Register x0 is hardwired to the constant zero. Each register is 32 bits wide. TinyRV1 uses the same calling convention and symbolic register names as RISC-V:

+----------+----------+--------------------------------------------------------+
| Register | ABI Name | Description                                            |
|----------|----------|--------------------------------------------------------|
| x0       | zero     | The constant value 0                                   |
| x1       | ra       | Return address (caller saved)                          |
| x2       | sp       | Stack pointer (callee saved)                           |
| x3       | gp       | Global pointer                                         |
| x4       | tp       | Thread pointer                                         |
| x5       | t0       | Temporary registers (caller saved)                     |
| x6       | t1       | ↑                                                      |
| x7       | t2       | ↑                                                      |
| x8       | s0/fp    | Saved register or frame pointer (callee saved)         |
| x9       | s1       | Saved register (callee saved)                          |
| x10      | a0       | Function arguments and/or return values (caller saved) |
| x11      | a1       | ↑                                                      |
| x12      | a2       | Function arguments (caller saved)                      |
| x13      | a3       | ↑                                                      |
| x14      | a4       | ↑                                                      |
| x15      | a5       | ↑                                                      |
| x16      | a6       | ↑                                                      |
| x17      | a7       | ↑                                                      |
| x18      | s2       | Saved registers (callee saved)                         |
| x19      | s3       | ↑                                                      |
| x20      | s4       | ↑                                                      |
| x21      | s5       | ↑                                                      |
| x22      | s6       | ↑                                                      |
| x23      | s7       | ↑                                                      |
| x24      | s8       | ↑                                                      |
| x25      | s9       | ↑                                                      |
| x26      | s10      | ↑                                                      |
| x27      | s11      | ↑                                                      |
| x28      | t3       | Temporary registers (caller saved)                     |
| x29      | t4       | ↑                                                      |
| x30      | t5       | ↑                                                      |
| x31      | t6       | ↑                                                      |
+----------+----------+--------------------------------------------------------+

1.3. Memory

While TinyRV1 supports a 32-bit virtual memory address space, only the first 512B of this address space (i.e., addresses 0x00000000 to 0x00001fff) are actually available for storage. The remaining portion of the virtual memory address space is undefined with the exception of the memory-mapped IO addresses described in the TinyRV1 "Privileged ISA". TinyRV1 uses a little endian memory system.

2. TinyRV1 Encoding

The TinyRV1 ISA uses the same instruction encoding as RISC-V. There are four instruction types and four immediate encodings. Each instruction has a specific instruction type, and if that instruction includes an immediate, then it will also have an immediate type.

2.1. Instruction Formats

R-type

I-type

S-type

U-type

2.2. Immediate Formats

RISC-V has an asymmetric immediate encoding which means that the immediates are formed by concatenating different bits in an asymmetric order based on the specific immediate formats. Note that in RISC-V, all immediates are always sign extended, and the sign-bit for the immediate is always in bit 31 of the instruction.

For this, let's separate out the different sections of an instruction:

The following diagrams represent how to re-arrange these sections to create different immediates, based on the encoding for the particular instruction. Here:

  • 0 is used to indicate that a particular bit is 0
  • <-[n]-> is used to indicate that bit n should be replicated for all bits in the field

I-immediate

S-immediate

J-immediate

B-immediate

3. TinyRV1 Instructions

For each instruction we include a brief summary, assembly syntax, instruction semantics, instruction and immediate encoding format, and the actual encoding for the instruction. We use the following conventions when specifying the instruction semantics:

  • R[rx]: general-purpose register value for register specifier rx
  • sext: sign extend to 32 bits
  • M[addr] : 4-byte memory value at address addr
  • PC: current program counter
  • imm: immediate according to the immediate type
  • addr: 32-bit absolute memory address

3.1. ADDI

  • Summary: Add constant
  • Assembly: addi rd, rs1, imm
  • Format: I-type, I-immediate
  • Semantics:
R[rd] <- R[rs1] + sext(imm)
PC <- PC + 4

3.2. ADD

  • Summary: Addition with 3 GPRs (no overflow)
  • Assembly: add rd, rs1, rs2
  • Format: R-type
  • Semantics:
R[rd] <- R[rs1] + R[rs2]
PC <- PC + 4

3.3. MUL

  • Summary: Signed multiplication with 3 GPRs (no overflow)
  • Assembly: mul rd, rs1, rs2
  • Format: R-type
  • Semantics:
R[rd] <- R[rs1] * R[rs2]
PC <- PC + 4

3.4. LW

  • Summary: Load word from memory
  • Assembly: lw rd, imm(rs1)
  • Format: I-type, I-immediate
  • Semantics:
R[rd] <- M[ R[rs1] + sext(imm) ]
PC <- PC + 4

TinyRV1 does not support unaligned memory access. The address used in any LW instruction must be 4-byte aligned (i.e., the bottom two bits must be zero). An unaligned LW address results in undefined behavior.

3.5. SW

  • Summary: Store word in memory
  • Assembly: sw rs2, imm(rs1)
  • Format: S-type, S-immediate
  • Semantics:
M[ R[rs1] + sext(imm) ] <- R[rs2]
PC <- PC + 4

TinyRV1 does not support self-modifying code. Using a SW instruction to writing memory locations which will eventually be fetched as instructions results in undefined behavior. TinyRV1 does not support unaligned memory access. The address used in any SW instruction must be 4-byte aligned (i.e., the bottom two bits must be zero). An unaligned SW address results in undefined behavior.

3.6. JAL

  • Summary: Jump to address, place return address in GPR
  • Assembly: jal rd, addr
  • Format: U-type, J-immediate
  • Semantics:
R[rd] <- PC + 4
PC <- addr

The encoded immediate imm is calculated during assembly such that PC + sext(imm) = addr. TinyRV1 requires the JAL target address to always be four-byte aligned (i.e., the bottom two bits must be zero). An unaligned JAL target address results in undefined behavior.

3.7. JR

  • Summary: Jump to address
  • Assembly: jr rs1
  • Format: I-type
  • Semantics:
PC <- R[rs1]

TinyRV1 requires the JR target address to always be four-byte aligned (i.e., the bottom two bits must be zero). An unaligned JR target address results in undefined behavior.

3.8. BNE

  • Summary: Branch if two GPRs are not equal
  • Assembly: bne rs1, rs2, addr
  • Format: S-type, B-immediate
  • Semantics:
if ( R[rs1] != R[rs2] )
  PC <- addr
else
  PC <- PC+4

The encoded immediate imm is calculated during assembly such that PC + sext(imm) = addr. TinyRV1 requires the BNE target address to always be four-byte aligned (i.e., the bottom two bits must be zero). An unaligned BNE target address results in undefined behavior.

4. TinyRV1 "Privileged" ISA

TinyRV1 does not support any kind of distinction between user and privileged mode (which is why privileged is in quotes). Using the terminology in the RISC-V vol 2 ISA manual, TinyRV1 only supports M-mode.

4.1. Reset Vector

RISC-V specifies that on reset, PC will reset to an implementation-defined value. TinyRV1 defines this to be at 0x00000000.

4.2. Memory-Mapped I/O

TBD

4.3. Address Translation

TinyRV1 only supports the most basic form of address translation. Every logical address is directly mapped to the corresponding physical address. As mentioned above, while TinyRV1 supports a 32-bit address space, only the first 512B can be used for storage. So the virtual addresses from 0x00000000 to `0x00001fff are directly mapped to the corresponding physical addresses. All other addresses are undefined with the exception of the memory-mapped IO addresses. In the RISC-V vol 2 ISA manual this is called a Mbare addressing environment.

5. TinyRV1 Pseudo-Instructions

It is very important to understand the relationship between the "real" instructions presented in this manual, the "real" instructions in the official RISC-V ISA manual, and pseudo-instructions. There are two instructions we need to be careful with: NOP and JR. The following table illustrates which ISAs contain which of these two instructions, and whether or not the instruction is considered a "real" instruction or a "pseudo-instruction".

| | TinyRV1 | RISC-V | |----------|---------|--------| | `NOP` | pseudo | pseudo | | `JR` | real | pseudo |

NOP is always a pseudo-instruction. It is always equivalent to the following use of the ADDI instruction:

addi x0, x0, 0

JR is a "real" instruction in TinyRV1, but it is a pseudo-instruction in RISC-V for the following use of the JALR instruction:

jalr x0, rs1, 0

None of this changes the encodings. In TinyRV1, JR is encoded the same way as the corresponding use of the JALR instruction in RISC-V.