RISC-V Test SoC
Github: http://github.com/ultraembedded/riscv_soc
A basic RISC-V test SoC with Timer, UART, SPI and GPIO peripherals...
Cloning
# Clone repository and submodules
git clone https://github.com/ultraembedded/riscv_soc.git --recursive
Directories
Name |
Contents |
core |
RISC-V core (http://github.com/ultraembedded/riscv) |
fpga/arty |
Digilent Artix-7 Arty FPGA Dev Board project |
soc |
Verilog for peripherals, interconnect, etc |
tb |
System-C testbench for the project |
Features
The top (riscv_soc in riscv_soc.v) contains;
- RISC-V core (RV32IM instructions supported).
- 16KB (8KB x 2-way) instruction cache.
- Timer, UART, SPI and interrupt controller peripherals.
- AXI4-Lite slave port for external bus master/debug access to peripherals / main memory.
- AXI4 master port for access to main memory, e.g. SDRAM (external to the design).
Interfaces
Name |
Description |
clk_i |
Clock input |
rst_i |
Async reset, active-high. Reset SoC (excluding CPU core). |
rst_cpu_i |
Async reset, active-high. Reset CPU core. |
reset_vector_i |
Initial boot address. |
inport_* |
AXI4-Lite slave interface for access to SoC / memory. |
mem_* |
AXI4 master interface to main memory. |
spi_* |
SPI interface |
gpio_* |
GPIO interface |
uart_rxd_o |
UART Tx (connect to remote receiver) |
uart_txd_i |
UART Rx (connect to remote transmitter) |
Testbench
A basic System-C / Verilator based testbench for the design is provided.
Dependancies;
- gcc
- make
- libelf
- System-C (specify path using SYSTEMC_HOME)
- Verilator (specify path using VERILATOR_SRC)
To build the testbench;
cd tb
make
To run the provided test executable;
cd tb
make run
FPGA
This project is ready to run on the 'Digilent Artix-7 Arty' FPGA dev board;
A pre-cooked bitstream for this board is located in 'fpga/arty/top.bit'.
The test project for FPGA uses the UART to AXI dbg bridge to allow code to be loaded into DDR prior to de-asserting the CPU's reset.
The 'rv32imsu' core (as used in the provided bitstream) is capable of booting Linux;
cd fpga/arty
# Load bitstream onto target
vivado -mode tcl -source program.tcl
# Load test app into DDR and release reset (change ttyUSB2 as appropriate)
./run.py -d /dev/ttyUSB2 -f ../../images/linux_riscv_soc.elf
ELF: Loading 0x80000000 - size 7KB
|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| 100.0%
ELF: Loading 0x80400000 - size 5368KB
|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| 100.0%
ELF: Loading 0x81f00000 - size 2KB
|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| 100.0%
[Console]: Enter UART mode
Booting...
OF: fdt: Ignoring memory range 0x80000000 - 0x80400000
Linux version 4.19.0-29706-g1479c35-dirty (build@vm) (gcc version 7.2.0 (GCC)) #531 Sat Mar 16 22:07:04 GMT 2019
bootconsole [early0] enabled
initrd not found or empty - disabling initrd
Zone ranges:
Normal [mem 0x0000000080400000-0x0000081effffffff]
Movable zone start for each node
Early memory node ranges
node 0: [mem 0x0000000080400000-0x0000000081efffff]
Initmem setup node 0 [mem 0x0000000080400000-0x0000000081efffff]
On node 0 totalpages: 6912
Normal zone: 54 pages used for memmap
Normal zone: 0 pages reserved
Normal zone: 6912 pages, LIFO batch:0
elf_hwcap is 0x1101
pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
pcpu-alloc: [0] 0
Built 1 zonelists, mobility grouping on. Total pages: 6858
Kernel command line: console=ttyUL0,1000000 debug
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Sorting __ex_table...
Memory: 21992K/27648K available (3664K kernel code, 138K rwdata, 547K rodata, 792K init, 220K bss, 5656K reserved, 0K cma-reserved)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
irq-xilinx: /soc/interrupt-controller@90000000: num_irq=9, edge=0x100
clocksource: timer: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 76450417870 ns
Console: colour dummy device 80x25
Calibrating delay loop (skipped), value calculated using timer frequency.. 50.00 BogoMIPS (lpj=100000)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
devtmpfs: initialized
random: get_random_u32 called from bucket_table_alloc.isra.7+0xa0/0x208 with crng_init=0
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
futex hash table entries: 256 (order: -1, 3072 bytes)
NET: Registered protocol family 16
random: fast init done
clocksource: Switched to clocksource timer
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
workingset: timestamp_bits=30 max_order=13 bucket_order=0
NET: Registered protocol family 38
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
io scheduler mq-deadline registered
io scheduler kyber registered
92000000.serial: ttyUL0 at MMIO 0x92000000 (irq = 2, base_baud = 0) is a uartlite
console [ttyUL0] enabled
console [ttyUL0] enabled
bootconsole [early0] disabled
bootconsole [early0] disabled
loop: module loaded
NET: Registered protocol family 10
Segment Routing with IPv6
sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
NET: Registered protocol family 17
Freeing unused kernel memory: 792K
This architecture does not have kernel memory protection.
Run /init as init process
init started: BusyBox v1.29.3 (2018-11-13 23:09:48 GMT)
Please press Enter to activate this console.
BusyBox v1.29.3 (2018-11-13 23:09:48 GMT) built-in shell (ash)
# ls
bin dev etc init lib mnt proc sbin sys test
#
Size
SoC + Small Core (core/rv32i_spartan6)
Xilinx Vivado (for XC7) |
Used |
Slice LUTs |
3654 |
Slice Registers |
1468 |
SoC + Larger Core (core/rv32imsu)
Xilinx Vivado (for XC7) |
Used |
Slice LUTs |
7046 |
Slice Registers |
3170 |
Memory Map
Range |
Description |
0x8000_0000 - 0x8fff_ffff |
Main memory (external to the design) |
0x9000_0000 - 0x90ff_ffff |
Peripheral - IRQ controller |
0x9100_0000 - 0x91ff_ffff |
Peripheral - Timer |
0x9200_0000 - 0x92ff_ffff |
Peripheral - UART |
0x9300_0000 - 0x93ff_ffff |
Peripheral - SPI |
0x9400_0000 - 0x94ff_ffff |
Peripheral - GPIO |
Interrupt Sources
Index |
Source |
0 |
Peripheral - Timer |
1 |
Peripheral - UART |
2 |
Peripheral - SPI |
3 |
Peripheral - GPIO |
Peripheral Register Map
Offset |
Name |
Description |
0x9000_0000 |
IRQ_ISR |
[RW] Interrupt Status Register |
0x9000_0004 |
IRQ_IPR |
[R] Interrupt Pending Register |
0x9000_0008 |
IRQ_IER |
[RW] Interrupt Enable Register |
0x9000_000c |
IRQ_IAR |
[W] Interrupt Acknowledge Register |
0x9000_0010 |
IRQ_SIE |
[W] Set Interrupt Enable bits |
0x9000_0014 |
IRQ_CIE |
[W] Clear Interrupt Enable bits |
0x9000_0018 |
IRQ_IVR |
[RW] Interrupt Vector Register |
0x9000_001c |
IRQ_MER |
[RW] Master Enable Register |
0x9100_0008 |
TIMER_CTRL0 |
[RW] Control |
0x9100_000c |
TIMER_CMP0 |
[RW] Compare value (interrupt on match) |
0x9100_0010 |
TIMER_VAL0 |
[RW] Current Value |
0x9100_0014 |
TIMER_CTRL1 |
[RW] Control |
0x9100_0018 |
TIMER_CMP1 |
[RW] Compare value (interrupt on match) |
0x9100_001c |
TIMER_VAL1 |
[RW] Current Value |
0x9200_0000 |
ULITE_RX |
[R] UART Data Register |
0x9200_0004 |
ULITE_TX |
[W] UART Data Register |
0x9200_0008 |
ULITE_STATUS |
[R] UART Status Register |
0x9200_000c |
ULITE_CONTROL |
[RW] UART Configuration Register |
0x9300_001c |
SPI_DGIER |
[RW] Device Global Interrupt Enable Register |
0x9300_0020 |
SPI_IPISR |
[RW] IP Interrupt Status Register |
0x9300_0028 |
SPI_IPIER |
[RW] IP Interrupt Enable Register |
0x9300_0040 |
SPI_SRR |
[RW] Software Reset Register |
0x9300_0060 |
SPI_CR |
[RW] SPI Control Register |
0x9300_0064 |
SPI_SR |
[R] SPI Status Register |
0x9300_0068 |
SPI_DTR |
[W] SPI Data Transmit Register |
0x9300_006c |
SPI_DRR |
[R] SPI Data Receive Register |
0x9300_0070 |
SPI_SSR |
[RW] SPI Slave Select Register |
0x9400_0000 |
GPIO_DIRECTION |
[RW] Configuration Register |
0x9400_0004 |
GPIO_INPUT |
[R] GPIO Input Status |
0x9400_0008 |
GPIO_OUTPUT |
[RW] GPIO Output Control |
0x9400_000c |
GPIO_OUTPUT_SET |
[W] GPIO Output Control Set Alias |
0x9400_0010 |
GPIO_OUTPUT_CLR |
[W] GPIO Output Control Clr Alias |
0x9400_0014 |
GPIO_INT_MASK |
[RW] GPIO Interrupt Enable Mask |
0x9400_0018 |
GPIO_INT_SET |
[W] GPIO Interrupt Set |
0x9400_001c |
GPIO_INT_CLR |
[W] GPIO Interrupt Clear |
0x9400_0020 |
GPIO_INT_STATUS |
[R] GPIO Interrupt Raw Status |
0x9400_0024 |
GPIO_INT_LEVEL |
[RW] GPIO Interrupt Level |
0x9400_0028 |
GPIO_INT_MODE |
[RW] GPIO Interrupt Mode |
Peripheral Register Fields
IRQ Register: IRQ_ISR
Bits |
Name |
Description |
3:0 |
STATUS |
Pending interrupt (unmasked) bitmap. |
IRQ Register: IRQ_IPR
Bits |
Name |
Description |
3:0 |
PENDING |
Pending interrupts (masked) bitmap. |
IRQ Register: IRQ_IER
Bits |
Name |
Description |
3:0 |
ENABLE |
Interrupt enable mask. |
IRQ Register: IRQ_IAR
Bits |
Name |
Description |
3:0 |
ACK |
Bitmap of interrupts to acknowledge. |
IRQ Register: IRQ_SIE
Bits |
Name |
Description |
3:0 |
SET |
Bitmap of interrupts to enable. |
IRQ Register: IRQ_CIE
Bits |
Name |
Description |
3:0 |
CLR |
Bitmap of interrupts to disable. |
IRQ Register: IRQ_IVR
Bits |
Name |
Description |
31:0 |
VECTOR |
Highest priority active interrupt number. |
IRQ Register: IRQ_MER
Bits |
Name |
Description |
0 |
ME |
Master Enable |
Timer Register: TIMER_CTRLx
Bits |
Name |
Description |
1 |
INTERRUPT |
Interrupt enable. |
2 |
ENABLE |
Timer enable. |
Timer Register: TIMER_CMPx
Bits |
Name |
Description |
31:0 |
VALUE |
Match value. |
Timer Register: TIMER_VALx
Bits |
Name |
Description |
31:0 |
CURRENT |
Current timer value. |
UART Register: ULITE_RX
Bits |
Name |
Description |
7:0 |
DATA |
Date byte |
UART Register: ULITE_TX
Bits |
Name |
Description |
7:0 |
DATA |
Date byte |
UART Register: ULITE_STATUS
Bits |
Name |
Description |
4 |
IE |
Interrupt enabled |
3 |
TXFULL |
Transmit buffer full |
2 |
TXEMPTY |
Transmit buffer empty |
1 |
RXFULL |
Receive buffer full |
0 |
RXVALID |
Receive buffer not empty |
UART Register: ULITE_CONTROL
Bits |
Name |
Description |
4 |
IE |
Interrupt enable |
1 |
RST_RX |
Flush Rx Buffer |
0 |
RST_TX |
Flush Tx Buffer |
SPI Register: SPI_DGIER
Bits |
Name |
Description |
31 |
GIE |
Global interrupt enable. |
SPI Register: SPI_IPISR
Bits |
Name |
Description |
2 |
TX_EMPTY |
Tx FIFO empty interrupt status. |
SPI Register: SPI_IPIER
Bits |
Name |
Description |
2 |
TX_EMPTY |
Tx FIFO interrupt enable. |
SPI Register: SPI_SRR
Bits |
Name |
Description |
31:0 |
RESET |
Software FIFO reset. |
SPI Register: SPI_CR
Bits |
Name |
Description |
0 |
LOOP |
Loopback enable (MOSI to MISO). |
1 |
SPE |
SPI Enable. |
2 |
MASTER |
Master mode (slave mode not currently supported). |
3 |
CPOL |
Clock polarity. |
4 |
CPHA |
Clock phase. |
5 |
TXFIFO_RST |
Tx FIFO reset. |
6 |
RXFIFO_RST |
Rx FIFO reset. |
7 |
MANUAL_SS |
Manual chip select mode (auto mode not supported). |
8 |
TRANS_INHIBIT |
Transfer inhibit. |
9 |
LSB_FIRST |
Data LSB first (1) or MSB first (0). |
SPI Register: SPI_SR
Bits |
Name |
Description |
0 |
RX_EMPTY |
Rx FIFO empty. |
1 |
RX_FULL |
Rx FIFO full. |
2 |
TX_EMPTY |
Tx FIFO empty. |
3 |
TX_FULL |
Tx FIFO full. |
SPI Register: SPI_DTR
Bits |
Name |
Description |
7:0 |
DATA |
Date byte |
SPI Register: SPI_DRR
Bits |
Name |
Description |
7:0 |
DATA |
Date byte |
SPI Register: SPI_SSR
Bits |
Name |
Description |
0 |
VALUE |
Chip select value |
GPIO Register: GPIO_DIRECTION
Bits |
Name |
Description |
31:0 |
OUTPUT |
0 = Input, 1 = Output |
GPIO Register: GPIO_INPUT
Bits |
Name |
Description |
31:0 |
VALUE |
Raw input status |
GPIO Register: GPIO_OUTPUT
Bits |
Name |
Description |
31:0 |
DATA |
GPIO output value |
GPIO Register: GPIO_OUTPUT_SET
Bits |
Name |
Description |
31:0 |
DATA |
GPIO output mask - set for high |
GPIO Register: GPIO_OUTPUT_CLR
Bits |
Name |
Description |
31:0 |
DATA |
GPIO output mask - set for low |
GPIO Register: GPIO_INT_MASK
Bits |
Name |
Description |
31:0 |
ENABLE |
GPIO Interrupt Enable Mask |
GPIO Register: GPIO_INT_SET
Bits |
Name |
Description |
31:0 |
SW_IRQ |
Write 1 to assert an interrupt |
GPIO Register: GPIO_INT_CLR
Bits |
Name |
Description |
31:0 |
ACK |
Write 1 to clear an interrupt |
GPIO Register: GPIO_INT_STATUS
Bits |
Name |
Description |
31:0 |
RAW |
Set if interrupt active (regardless of INT_MASK) |
GPIO Register: GPIO_INT_LEVEL
Bits |
Name |
Description |
31:0 |
ACTIVE_HIGH |
GPIO Interrupt Level - 1 = active high / rising edge, 0 = active low / falling edge |
GPIO Register: GPIO_INT_MODE
Bits |
Name |
Description |
31:0 |
EDGE |
GPIO Interrupt Mode - 1 = edge triggered, 0 = level |