TinyRV1 ISA
In Lab 4, you will be implementing the TinyRV1 ISA on your processor. 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. 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
TinyRV1 only supports a 1MB virtual memory address space from
0x00000000
to 0x000fffff
. The result of memory accesses to addresses
larger than 0x000fffff
are undefined. TinyRV1 uses a
little endian memory system.
2. TinyRV1 Instruction and Immediate 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: TinyRV1 Instruction Formats
R-type
I-type
S-type
U-type
2.2: TinyRV1 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 bitn
should be replicated for all bits in the field
I-immediate
S-immediate
J-immediate
B-immediate
3. TinyRV1 Instruction Details
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[\texttt{rx}]\): general-purpose register value for register specifier
rx
- \(CSR[\texttt{csr}]\): control/status register value for register specifier
csr
- \(\text{sext}\): sign extend to 32 bits
- \(M[\texttt{addr}]\) : 4-byte memory value at address
addr
- \(PC\): current program counter
- \(\texttt{imm}\): immediate according to the immediate type
CSRR
- Summary: Move value in control/status register to GPR
- Assembly:
csrr rd, csr
- Format: I-type, I-immediate
- Semantics:
- \(R[\texttt{rd}] \leftarrow CSR[\texttt{csr}]\)
- \(PC \leftarrow PC + 4\)
The control/status register read instruction is used to read a CSR and
write the result to a GPR. The CSRs supported in TinyRV1 are listed in
Section 4. Note that in RISC-V, CSRR
is really a pseudo-instruction for a
specific usage of CSRRS
, but in TinyRV1 we only support the subset of
CSRRS
captured by CSRR
. See Section 5 for more details about
pseudo-instructions.
CSRW
- Summary: Move value in GPR to control/status register
- Assembly:
csrw csr, rs1
- Format: I-type, I-immediate
- Semantics:
- \(CSR[\texttt{csr}] \leftarrow R[\texttt{rs1}]\)
- \(PC \leftarrow PC + 4\)
The control/status register write instruction is used to read a GPR and
write the result to a CSR. The CSRs supported in TinyRV1 are listed in
Section 4. Note that in RISC-V, CSRW
is really a pseudo-instruction for a
specific usage of CSRRW
, but in TinyRV1 we only support the subset of
CSRRW
captured by CSRW
. See Section 5 for more details about
pseudo-instructions.
ADD
- Summary: Addition with 3 GPRs (no overflow)
- Assembly:
add rd, rs1, rs2
- Format: R-type
- Semantics:
- \(R[\texttt{rd}] \leftarrow R[\texttt{rs1}] + R[\texttt{rs2}]\)
- \(PC \leftarrow PC + 4\)
ADDI
- Summary: Add constant
- Assembly:
addi rd, rs1, imm
- Format: I-type, I-immediate
- Semantics:
- \(R[\texttt{rd}] \leftarrow R[\texttt{rs1}] + \text{sext}(\texttt{imm})\)
- \(PC \leftarrow PC + 4\)
MUL
- Summary: Signed multiplication with 3 GPRs (no overflow)
- Assembly:
mul rd, rs1, imm
- Format: R-type
- Semantics:
- \(R[\texttt{rd}] \leftarrow R[\texttt{rs1}] \times R[\texttt{rs2}]\)
- \(PC \leftarrow PC + 4\)
LW
- Summary: Load word from memory
- Assembly:
lw rd, imm(rs1)
- Format: I-type, I-immediate
- Semantics:
- \(R[\texttt{rd}] \leftarrow M\left[R[\texttt{rs1}] + \text{sext}(\texttt{imm})\right]\)
- \(PC \leftarrow 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.
SW
- Summary: Store word in memory
- Assembly:
sw rs2, imm(rs1)
- Format: S-type, S-immediate
- Semantics:
- \(M\left[R[\texttt{rs1}] + \text{sext}(\texttt{imm})\right] \leftarrow R[\texttt{rs2}]\)
- \(PC \leftarrow 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.
JAL
- Summary: Jump to address, place return address in GPR
- Assembly:
jal rd, addr
- Format: U-type, J-immediate
- Semantics:
- \(R[\texttt{rd}] \leftarrow PC + 4\)
- \(PC \leftarrow PC + \text{sext}(\texttt{imm})\)
The encoded immediate imm
is calculated during assembly such that \(PC +
\text{sext}(\texttt{imm}) = \texttt{addr}\). TinyRV1 requires the JAL
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.
JR
- Summary: Jump to address
- Assembly:
jr rs1
- Format: I-type
- Semantics:
- \(PC \leftarrow R[\texttt{rs1}]\)
TinyRV1 requires the JR 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.
BNE
- Summary: Branch if two GPRs are not equal
- Assembly:
bne rs1, rs2, addr
- Format: S-type, B-immediate
- Semantics:
- \(\text{if}(R[\texttt{rs1}] \neq R[\texttt{rs2}]) \ PC \leftarrow PC + \text{sext}(\texttt{imm})\)
- \(\text{else}\qquad\qquad\qquad\ \ \ \ \, PC \leftarrow PC + 4\)
The encoded immediate imm
is calculated during assembly such that \(PC +
\text{sext}(\texttt{imm}) = \texttt{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. Using the terminology in the RISC-V vol 2 ISA manual, TinyRV1 only supports M-mode.
Reset Vector
RISC-V specifies that on reset, \(PC\) will reset to an
implementation-defined value. TinyRV1 defines this to be at 0x00000000
.
This is where assembly tests should reside.
Control/Status Registers
TinyRV1 includes six non-standard CSRs. Here is the mapping:
CSR Name | Privilege | Read/Write | CSR Num | Note |
---|---|---|---|---|
in0 |
M | R | 0xFC2 |
non-standard |
in1 |
M | R | 0xFC3 |
non-standard |
in2 |
M | R | 0xFC4 |
non-standard |
out0 |
M | R/W | 0x7C2 |
non-standard |
out1 |
M | R/W | 0x7C3 |
non-standard |
out2 |
M | R/W | 0x7C4 |
non-standard |
These are chosen to conform to the guidelines in Section 2.1 of the RISC-V vol 2 ISA manual. Here is a description of each of these six CSRs.
in0
/in1
/in2
- Used to communicate data to the processor from an external manager. These registers have register-mapped FIFO-dequeue semantics, meaning reading the registers essentially dequeues the data from the head of a FIFO. Reading the registers will stall if the FIFO has no valid data. Writing the registers is undefined.
out0
/out1
/out2
- Used to communicate data from an external manager to the processor. These registers have register-mapped FIFO-enqueue semantics, meaning writing the registers essentially enqueues the data on the tail of a FIFO. Writing the registers will stall if the FIFO is not ready. Reading the registers is undefined.
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, TinyRV1 only supports a 1MB virtual
memory address space from 0x00000000
to 0x000fffff
, and thus TinyRV1
only supports a \(1MB\) physical memory address space. 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 |
CSRR |
real | pseudo |
CSRW |
real | pseudo |
NOP
is always a pseudo-instruction. It is always equivalent to the
following use of the ADDI
instruction:
JR
is a "real" instruction in TinyRV1, but it is a pseudo-instruction in
RISC-V for the following use of the JALR
instruction:
CSRR
and CSSRW
are real instructions in TinyRV1 but they are
pseudo-instructions for the following use of the CSRRS
and CSRRW
:
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.