Specification
This part summarises the specification for the design, including the instructions that needs supporting, the CSR registers, and the interrupts/exceptions. Throughout the specification, the RISC-V specification is referenced in the following format:
-
v1_c9
means RISC-V volume 1 (unprivileged ISA), chapter 9 -
v1_t9.1
means RISC-V volume 1 (unprivileged ISA), table 9.1 -
v2_s2.3
means RISC-V volume 2 (privileged architecture), section 2.3
All references to volume 1 refer to version 20191213 (document riscv-spec-20191213.pdf
), and references to volume 2 refer to version 20211203 (document riscv-privileged-20211203.pdf
). Both documents are available from the official specification page.
Other non-ISA specifications are also used. References to the Platform-Level Interrupt Controller (PLIC) Specification refer to version 1.0.0, 3/2023 (document riscv-plic-1.0.0.pdf
, available here). References use a format like:
-
plic_f1
means RISC-V PLIC specification, figure 1
CPU State
The registers required by the RISC-V implementation are summarised in this section. The register file contains 32 registers which are each 32 bits wide, called x0
-x31
. The first register x0
is writable, but always returns zero when read. The program counter is 32 bits wide, and is initialised to the reset vector.
Control and Status Registers
A certain minimal set of control and status registers (CSRs) are required by the RISC-V privileged specification. Each is listed in the format "`CSR-address CSR-name`: description" below.
The following informational registers are required to be present, but can be read-only zero:
-
0xf11 mvendorid
: read-only; returns 0 to indicate not implemented. -
0xf12 marchid
: read-only; returns 0 to indicate not implemented. -
0xf13 mimpid
: read-only; returns 0 to indicate not implemented. -
0xf14 mhartid
: read-only; single hart system, returns 0 to indicate hart 0. -
0xf15 mconfigptr
: read-only zero, configuration platform-specification defined -
0x301 misa
: read/write; single legal value 0 always returned (WARL), meaning architecture is determined by non-standard means (it is rv32im_zicsr implementing M-mode only).
The status of the CPU is stored in the following status registers:
-
0x300 mstatus
: read/write, containing both WPRI and WARL fields. The bit fields which are non-zero are as follows (assumes only M-mode):-
bit 3: MIE (interrupt enable), read/write
-
bit 7: MPIE (previous value of interrupt enable), read/write (?)
-
bits [12:11]: MPP (previous privilege mode), WARL always 0b11 (?)
-
-
0x310 mstatush
: read/write, upper 32-bit of status; all fields are read-only zero (only little-endian memory is supported).
In addition to the global bit in the status register, interrupts are controlled and monitored by these registers:
-
0x304 mie
: read/write interrupt-enable register. To enable an interrupt in M-mode, both mstatus.MIE and the bit in mie must be set. Bits corresponding to interrupts that cannot occur must be read-only zero. -
0x344 mip
: 32-bit read/write interrupt-pending register. Since all fields are read-only, writes to this CSR are no-op. The following bits are defined:-
bit 3: machine software interrupt pending (read-only)
-
bit 7: machine timer interrupt pending (read-only)
-
bit 11: machine-level external interrupt pending (read-only)
-
When a trap occurs (either due to an interrupt or a exception), the following registers determine where control flow is transferred to and where it returns to after the trap:
-
0x305 mtvec
: read-only, trap handler vector table base address-
bits [1:0]: 1 (vectored mode)
-
bits [31:2]: trap vector table base address (4-byte aligned)
-
-
0x341 mepc
: 32-bit, read/write register, stores the return-address from trap handler. WARL, valid values are allowed physical addresses (4-byte aligned and fit within physical memory address width).
Information about the trap is obtained from these registers:
-
0x342 mcause
: 32-bit, read/write, stores exception code and bit indicating whether trap is interrupt. Exception code is WLRL. -
0x343 mtval
: read-only zero
In addition, software may use this register in any way while processing traps:
-
0x340 mscratch
: 32-bit read/write register for use by trap handlers
The following privileged-mode registers hold performance monitoring information:
-
0xb00 mcycle
: low 32 bits of read/write 64-bit register incrementing at a constant rate -
0xb80 mcycleh
: high 32 bits of read/write, 64-bit register containing number of clock cycles executed by the processor. -
0xb02 minstret
: low 32 bits of read/write, 64-bit register containing number of instructions retired by the processor. -
0xb82 minstreth
: high 64 bits of read/write, 64-bit register containing number of instructions retired by the processor.
These registers are also accessible via the following shadows (intended for use by unprivileged mode; however, there is only M-mode here, so there is no distinction between privilege levels):
-
0bc00 cycle
: read-only shadow of mcycle -
0xc80 cycleh
: read-only shadow of mcycleh -
0xc02 instret
: read-only shadow of minstret -
0xc82 instreth
: read-only shadow of minstreth
The 64-bit mtime
register is memory-mapped (it is not a CSR); however, it does have a read-only shadow as a CSR:
-
0xc01 time
: read-only shadow of lower 32 bits of memory mapped 64-bit mtime -
0xc81 timeh
: read-only shadow of upper 32 bits of memory mapped 64-bit mtime
Finally, the following required performance monitoring counters are all implemented as read-only zero
-
(0xb00 + n) mhpmcountern
: read-only zero (n
ranges from 3 to 32) -
(0xb80 + n) mhpmcounternh
: read-only zero (n
ranges from 3 to 32) -
(0x320 + n) mhpmevent
: read-only zero (n
ranges from 3 to 32) -
(0xc00 + n) hpmcountern
: read-only zero (n
ranges from 3 to 32) -
(0xc80 + n) hpmcounternh
: read-only zero (n
ranges from 3 to 32)
Traps
The standard privileged architecture requires support for exceptions and interrupts.
-
At the start of each instruction cycle, a trap is triggered transferring control to an interrupt vector, if both (
v2_s3.1.9
):-
Interrupts are globally enabled (
MIE
bit set inmstatus
) -
An interrupt enable bit (
mie
) and corresponding interrupt pending bit (mip
) are both set
-
-
While an instruction is in execution, if it raises an exception, a trap is triggered transferring control to the exception vector.
The process of triggering a trap takes one cycle, and is the same for both an exception and an interrupt:
-
The
mepc
CSR is set to the current program counter (instruction interrupted or raising the exception) (v2_s3.1.14
) -
The program counter is set to a trap vector. For exceptions, this is the
base
address inmtvec
(v2_s3.1.7
). For interrupts, it isbase + 4*cause
, wherecause
is from the exception code column inv2_t3.6
. (This design uses vectored interrupts.) -
The
mcause
CSR is set to the exception code inv2_t3.6
. The interrupt bit is set for an interrupt (v2_s3.1.15
). -
The
MIE
bit inmstatus
is copied toMPIE
, and theMIE
bit is set to 0 (v2_s3.1.6.1
)
Interrupts
The basic interrupt mechanism follows the Core Local Interruptor (CLINT) specification, based on SiFive chips. The CLINT memory map begins at address 0x1000_0000
. The meaning of the memory-mapped I/O registers in this region are as follows:
-
msip
: write 1 to this bit to request a software interrupt, and write 0 to clear it. The value of this bit is reflected in the read-onlyMSIP
field of themip
CSR. The register is initialised to zero. -
mtimecmp
: this is the 64-bit timer compare register as described in the RISC-V privileged ISA specification. A timer interrupt becomes pending exactly whenmtime >= mtimecmp
. It is initialised to zero. -
mtime
: this is the 64-bit real time register, which increments at a constant rate with wall clock time. It is initialised to zero.
The external interrupt bit meip
in mip
(the interrupt-pending register) comes directly from an external interrupt controller which follows the PLIC specification (plic_f1
). There is only a single hart in this system, which only implements M-mode, therefore there is only one signal line from the PLIC to the hart (the M-mode external interrupt signal; see plic_s1.3
).
Exceptions
When an instruction is executed, it may synchronously raise an exception (meaning the exception is associated with the instruction being executed, and raising the exception will deterministically trigger a trap on the next instruction). The following subset of exceptions defined in v2_t3.6
is implemented in the design:
Exception code | Exception | Caused by |
---|---|---|
0 |
Instruction address misaligned |
Program counter not four-byte-aligned on an unconditional jump or branch taken ( |
1 |
Instruction access fault |
Program counter does not fall within the instruction memory address region. Exception raised on the instruction with invalid program counter. |
2 |
Illegal instruction |
The fetched instruction is not supported by this implementation (i.e. is not RV32I, Zicsr, |
3 |
Breakpoint |
The |
5 |
Load access fault |
a load instruction was executed with an address which is not in main memory or an I/O region |
7 |
Store access fault |
a store instruction was executed with an address which is not in main memory or an I/O region, or the address is read-only |
11 |
(M-mode) Environment call |
The |
In this implementation, load/store instructions do not have alignment requirements, so the load/store address misaligned exceptions are not required.
Memory Map
The memory map for the physical address space is as follows (all addresses are in hexadecimal):
Memory Region | Address range | Size | Physical Memory Attributes | Notes |
---|---|---|---|---|
Instruction ROM |
0000_0000 - 1000_0000 |
1024 |
Instruction fetch only |
|
I/O |
1000_0000 - 2000_0000 |
Sum of special register sizes |
Read/write (load/store) |
See Special registers in I/O memory below |
Main Memory |
2000_0000 - 2000_0400 |
1024 |
Read/write (load/store) |
The addresses in the I/O region are vacant by default (load/store accesses generate an illegal instruction exception). The accessible addresses are given below:
Address | Register name | Register size | Attributes |
---|---|---|---|
1000_0000 |
msip |
4 |
Read/write, but only bit 0 is modified |
1000_4000 |
mtimecmp |
8 |
Read/write ( |
1000_bff8 |
mtime |
8 |
Read/write ( |
Certain addresses in the instruction memory region have the following defined purposes:
Start Address | Memory-mapped Register | Size |
---|---|---|
0000_0000 |
Reset vector (initial PC) |
4 |
0000_0004 |
NMI vector |
4 |
0000_0008 |
Exception vector (mtvec) |
4 |
0000_0014 |
Software interrupt vector |
4 |
0000_0024 |
Timer interrupt vector |
4 |
0000_0034 |
External interrupt vector |
4 |
Instructions
The required instructions are the 32-bit RV32I base integer ISA, the Zicsr extension for CSR manipulation, and the privileged-architecture instructions mret
and wfi
. Each category of instructions is defined below.
Unless otherwise specified, all programs are assumed to increment the program counter by four bytes.
Many instructions involve building an immediate from instruction fields. The clearest diagram of how to build immediates for most instruction formats is v1_f2.4
. Most immediates are sign-extended based on bit 31 of the instruction. The CSR instructions are an exception, which use the rs2
field as an unsigned immediate (not sign extended). Normally, the (resulting 32-bit) immediate is used as an operand to an addition, or is written directly to a register.
Whenever calculations below involve rs1 , rs2 , or rd , the calculation involves the value in the indexed register, not the register index itself.
|
Privileged Instructions
The following instructions are required for code executing in machine mode (the only privilege level in this design) (v2_s3.3
): ecall
, ebreak
, mret
, and wfi
.
The ecall
and ebreak
instructions are required by the unprivileged specification RV32I (v1_s2.8
). Since the unprivileged instructions are also executing in M-mode in this design, these ecall
and ebreak
instructions do the same thing as the M-mode versions.
The instructions ecall
and ebreak
raise the exceptions shown in Supported exceptions, and take no further action.
The instruction mret
is executed by software to return from a trap. It performs the following steps:
-
Copies the
MPIE
bit toMIE
inmstatus
-
Sets the
MPIE
to 1 -
Sets the program counter to
mepc
(v2_s3.3.2
)
The wfi
is implemented as a NOP (v2_s3.3.3
)
Upper Immediate (RV32I)
Upper immediate instructions lui
and auipc
use an immediate imm
, where imm[31:12]
is stored in the U-type instruction, and imm[11:0]
is 0. Then:
-
lui
storesimm
in the registerrd
-
auipc
storesimm + pc
in the registerrd
No exceptions are raised.
Register-Register (RV32I)
The instructions add
, sub
, sll
, slt
sltu
, xor
, srl
, sra
, or
, and and
, all execute in the same way:
-
Perform an operation between the two registers
rs1
andrs2
-
Store the result to the register
rd
No exceptions are raised.
Register-Immediate (RV32I)
The instructions addi
, slli
, slti
sltiu
, xori
, srli
, srai
, ori
, and andi
, all execute in the same way:
-
Perform an operation between register
rs1
and the sign-extended immediate{ 20{instr[31]} , instr[31:20] }
-
Store the result to the register
rd
No exceptions are raised.
Unconditional Jump (RV32I)
The jal
and jalr
instructions store pc + 4
in the register rd
, and unconditionally update the program counter. For both instructions, a signed 32-bit immediate imm
is encoded in the instruction. (The encoding is different for jal
and jalr
.) The new program counter is calculated as follows:
-
For
jal
,pc = pc + imm
-
For
jalr
,pc = 0xffff_fffe & (rs1 + imm)
If the new pc
is not four-byte aligned, an instruction address misaligned exception is raised (and the program counter is not updated).
Conditional Branch (RV32I)
The conditional branch instructions beq
, bne
, blt
, bge
, bltu
, and bgeu
, check a condition between registers rs1
and rs2
, and update the program counter if the condition is satisfied.
The new program counter is pc = pc + imm
, where imm
is a signed 32-bit immediate encoded in the instruction. If the new pc
is not four-byte aligned, an instruction misaligned exception is raised (and the program counter is not updated).
If the condition is not satisfied, the program counter is set to pc + 4
as normal.
Load (RV32I)
Load instructions lb
, lh
, lw
, lbu
, and lhu
, attempt to read a location in memory and write it to rd
.
The address for the attempted read is rs1 + imm
, where imm
is a signed 32-bit immediate stored in the instruction.
The width of the read is given by the instruction. If the address and width means that a byte falls outside the valid memory region for reading (in main memory or I/O), a load access fault exception is raised, and no registers are modified.
If the read is valid, the 1-byte, 2-byte, or 4-byte result is either sign- or zero-extended, and written to rd
.
The behaviour of exceptions does not depend on whether rd
is x0
(v1_s2.6
).
In this design, all alignments are supported (in both loads and stores), and so load/store misalignment exceptions are not raised. |
Store (RV32I)
Store instructions sb
, sh
, and sw
, attempt to write the contents of rs2
to a location in memory.
The address for the attempted write is rs1 + imm
, where imm
is a signed 32-bit immediate stored in the instruction. (The encoding is not the same as for load instructions.)
The width of the write is given by the instruction. If the address and width means that a byte falls outside the valid memory region for write (in main memory or I/O), a store access fault exception is raised, and no registers are modified.
If the write is valid, the 1-byte, 2-byte, or 4-byte value from the low bits of rs2
is written to the address.
Control and Status Register (Zicsr)
The unprivileged specification (v1_ch9
) defines the behaviour of the instructions which manipulate CSRs, in the Zicsr ISA extension. The behaviour of the instructions is as follows:
-
csrrw
: read the addressed CSR into destination registerrd
, and then write the source registerrs1
to the addressed CSR. -
csrrwi
: read the addressed CSR into destination registerrd
, and then zero extend the immediateuimm
and write it to the addressed CSR. -
csrrs
: read the addressed CSR into destination registerrd
. Then, only if the source registerrs1
is notx0
, bitwise-OR the current value of the CSR withrs1
, and write the result back to the CSR (i.e. set bits in the CSR where there is a 1 inrs1
). -
csrrsi
: read the addressed CSR into destination registerrd
. Then, only if the immediateuimm
is not0
, bitwise-OR the current value of the CSR withuimm
(zero-extended to 32 bits), and write the result back to the CSR (i.e. set bits in the CSR where there is a 1 inuimm
). -
csrrc
: read the addressed CSR into destination registerrd
. Then, only if the source registerrs1
is notx0
, bitwise-AND the current value of the CSR with !rs1
, and write the result back to the CSR (i.e. clear bits in the CSR where there is a 1 inrs1
). -
csrrci
: read the addressed CSR into destination registerrd
. Then, only if the immediateuimm
is not0
, bitwise-AND the current value of the CSR with!uimm
(zero-extended to 32 bits after negation), and write the result back to the CSR (i.e. clear bits in the CSR where there is a 1 inuimm
).
Any instructions that write to a CSR:
-
will raise an illegal instruction exception if the CSR is read-only. In this case, the state of registers will be as if the instruction did not occur.
-
will not change the value of an CSR bits that are read-only in otherwise writable registers
-
for WLRL fields in writable CSRs, any value that is written (even an invalid one) will be written anyway, without any checking in hardware.
-
for WARL fields in writable CSRs, any attempt to write an invalid value will cause no change in the CSR field (the old value will be retained).
-
the write will displace any other automatic modification of the CSR by hardware; for example, writing to
instret
will stop auto-increment ofinstret
on that instruction (v1_s9.1
). This is also interpreted as applying to all counters, includingmcycle
, etc.)
Instructions that read CSRs read the value of the CSR as it was just prior to instruction execution (e.g. the value of instret
is taken before incrementing in due to the read instruction itself).
Instructions that attempt to perform an operation on a non-existent CSR raise an illegal instruction exception.
Notes on behaviour
-
The instructions are defined (
v1_s9.1
) to atomically read and write CSRs. Since there is only one hart in this design, this required is satisfied by a single read/write operation. -
The
csrrw
andcsrrwi
instructions are defined (v1_t9.1
) to omit the CSR read if the destination registerrd
isx0
, and not trigger any side effects that would occur on a read. In this design, no CSR has a side effect that occurs on a read, so for simplicity therw
instructions can perform a read irrespective ofrd
, and attempt the write tord
(which will have no effect ifrd
isx0
). -
In this design, all CSRs are 32-bits wide, so there is no need to zero-extend them before writing to registers.
-
In this design, writing invalid values to WLRL fields does not raise an illegal instruction exception (
v2_s2.3
).