This is SPU32 ("Small Processing Unit 32"), a compact RISC-V processor implementing the RV32I instruction set.
A demo-SoC is also included, featuring some peripherals.
The project ist writting in Verilog and is designed to be synthesizable using the open-source Yosys open synthesis suite.
An example SoC:
graph TD;
CPU[CPU<br>RISC-V, 32 bit] --- WB[Wishbone bus<br>8 or 32 bit, pipelined]
WB --- BROM[Boot ROM]
WB --- RAM[RAM]
WB --- UART[UART<br>115200 baud]
WB --- SPI[SPI bus]
SPI --- SDCARD[SD card]
WB --- TIMER[Timer<br>Interrupt-capable]
WB --- PRNG[PRNG<br>32 bit predictable<br>random numbers]
WB --- LEDS[LEDs<br>blinky board LEDs]
WB --- IR[IR decoder<br>NEC protocol]
IR --- IRREC[IR receiver<br>38 kHz carrier]
WB --- VGA[VGA graphics<br>text and bitmaps]
For Linux, in a nutshell (adapt paths as desired):
mkdir riscv-gcc
cd riscv-gcc
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=/opt/riscv32 --with-arch=rv32i --with-abi=ilp32
make -j$(nproc)
Following vectors are used by the CPU and can be configured via parameters when instantiating the CPU module:
VECTOR_RESET
: The memory address where the CPU will start execution after reset. By default set to 0x00000000
VECTOR_EXCEPTION
: The memory address where the CPU will jump to to handle interrupts (for example, external interrupts or software interrupts) and exceptions (for example, illegal instructions). By default set to 0x00000010
.The CPU supports following types of interrupts and exceptions:
ecall
and ebreak
instructionsIf an interrupt of any type occurs, the CPU will jump to VECTOR_EXCEPTION
, where a handling routine should be present.
The mode of interrupt/exception-handling is inspired by the privileged RISC-V specification, but much simplified for sake of implementation compactness. It therefore does not conform to an official specification, but should feel somewhat similar.
Interrupt- and exception-handling is controlled using machine-status registers that can be accessed using the csrrw
instruction (the other csr-instructions are not supported in hardware and will raise an illegal-instruction exception, which can be handled in software if so desired). All status-registers have a read-write address (to allow swapping values with normal registers) and read-only address (to allow reading status-registers without changing their contents).
Following machine-status registers (MSRs) are used to control interrupt/exception-handling:
0xFC0
0x7C0
Following information is encoded:
MSR_STATUS[31-3]
: reserved, always reads zeroMSR_STATUS[2]
: status of external interrupt line, i.e., 1
if an external interrupt is currently requestedMSR_STATUS[1]
: previous state of the external interrupt enable flag (meie_prev
)MSR_STATUS[0]
: current state of the external interrupt enable flag (meie
)When an interrupt/exception occurs, the value of meie
is saved to meie_prev
. The meie
-flag is set to zero, which ensures that external interrupts are ignored until the current interrupt is handled or the meie
-flag is reinstated.
The mret
-instruction is used to return from an interrupt/exception. meie
is set to the value of meie_prev
and execution is resumed at the address stored in MSR_EPC
.
On reset, meie
is set to zero, which means that external interrupts will be ignored. To enable external interrupts, meie
needs to be set to 1
.
Software-interrupts are always processed.
0xFC1
0x7C1
This status-register encodes the cause of of the raised interrupt/exception:
MSR_CAUSE[31] |
MSR_CAUSE[3-0] |
type of exception |
---|---|---|
1 | 1011 | external interrupt |
0 | 0010 | invalid instruction |
0 | 0011 | ebreak -instruction |
0 | 1011 | ecall -instruction |
MSR_CAUSE[30]
to MSR_CAUSE[4]
are reserved and always read as zero.
Note that MSR_CAUSE[31]
can be used to easily distinguish external interrupts from software-interrupts. A neat effect of this encoding is that external interrupt and software-interrupts can be distinguished by signed comparison with zero, e.g., by using the bltz
(branch if less than zero) instruction.
0xFC2
0x7C2
This status-register contains the address of the instruction where the last interrupt/exception occurred. The mret
-instruction will jump to the address stored in this status-register.
Please note: In case of software interrupts/exceptions, this status-register will point to the instruction that caused the interrupt/exception. When directly issuing mret
, execution will resume at exactly the same instruction, which means that yet another interrupt/exception will be raised immediately. To resume normal program flow, one needs to increment the value of MSR_EPC
by 4
(i.e., by the length of one instruction) to resume execution a the next instruction. This can be detected by MSR_CAUSE[31]
.
External interrupts directly resume execution at the address stored in MSR_EPC
, so no increment is needed prior to mret
.
0xFC3
0x7C3
This status-register specifies the memory address the CPU will jump to when an interrupt/exception occurs. By default initialized to VECTOR_EXCEPTION
, but may be changed to any memory address where an interrupt service routine is located.