███████╗ ██████╗ ██████╗ ██████╗ ███████╗ ╚══███╔╝ ██╔════╝██╔═══██╗██╔══██╗██╔════╝ ███╔╝ █████╗██║ ██║ ██║██████╔╝█████╗ ___ ___ ___ _ ███╔╝ ╚════╝██║ ██║ ██║██╔══██╗██╔══╝ | __|| _ \ / __| /_\ ███████╗ ╚██████╗╚██████╔╝██║ ██║███████╗ | _| | _/| (_ | / _ \ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ |_| |_| \___|/_/ \_\
Note: The Block diagram does not include the VGA controller.
Note: For Z-Core detailed processor architecture explanation, pipeline implementation, verification methodology, and ISA compliance documentation, refer to the main Z-Core repository, or the following website, where I dive into the details of the processor: https://paudiazcuesta.com/work/z-core/.
This repository contains the FPGA implementation of the Z-Core RISC-V RV32IMZicsr processor, targeting the Intel DE10-Lite development board (MAX 10 FPGA). The Core features a 32KB direct-mapped instruction cache, a 32KB 2-way set-associative data cache with write-back/write-allocate policy, a branch predictor, and a 64MB SDRAM interface, enabling large applications such as DOOM to run at playable framerates.
The system supports two execution modes: small programs run directly from on-chip Block RAM (16 KB), while large applications like DOOM are loaded into the 64MB SDRAM via a UART bootloader and execute from there with cache assistance.
doom.mp4
Z-Core Running Doom @ 10 FPS (320x200).
| Parameter | Value |
|---|---|
| Target FPGA | Intel MAX 10 (10M50DAF484C7G) |
| Operating Frequency | 50 MHz |
| ISA | RV32IM + Zicsr |
| Instruction Cache | 32 KB direct-mapped (8192 entries) |
| Data Cache | 32 KB 2-way set-associative (write-back, write-allocate) |
| Branch Predictor | 2-Bit Branch Predictor |
| BRAM | 16 KB on-chip (bootloader + small apps) |
| SDRAM | 64 MB IS42S16320D @ 100 MHz (via AXI-CDC bridge) |
| Peripherals | UART, GPIO (16-bit), VGA (320×200 RGB332), 64-bit Timer |
| Development Board | Terasic DE10-Lite |
All measurements on DE10-Lite (MAX 10) at 50 MHz. For a detailed performance analysis on Z-Core, visit: https://paudiazcuesta.com/posts/z-core-performance/.
| Metric | Value |
|---|---|
| CoreMark/MHz | 3.06 |
| Configuration | FPS |
|---|---|
| No cache (SDRAM only) | < 1 FPS |
| 32 KB Instruction Cache only | ~3 FPS |
| 32 KB I-Cache + 32 KB 2-way D-Cache | ~11 FPS |
| 32 KB I-Cache + 32 KB 2-way D-Cache (lower resolution) | ~20 FPS |
| Source | Bandwidth |
|---|---|
| SDRAM (cache miss path) | 4.15 – 4.66 MB/s |
| Data Cache (cache hit path) | 57 – 80 MB/s |
| Tool | Purpose |
|---|---|
| Intel Quartus Prime Lite | FPGA synthesis and programming |
| RISC-V GNU Toolchain | Cross-compilation (riscv32-unknown-elf-, rv32im_zicsr target) |
| Python 3.x | ELF-to-HEX conversion and UART upload script |
The Z-Core SoC includes a UART bootloader stored in Block RAM, allowing you to upload and run applications without recompiling the FPGA hardware.
cd software/
make APP=1 hello.bin
python3 upload.py /dev/ttyUSB0 hello.bin# Build DOOM binary
cd software/Z-Core-DOOM/src/riscv/
make all
# Upload DOOM code + WAD (two segments to SDRAM)
cd software/
python3 upload.py /dev/ttyUSB0 \
--segments Z-Core-DOOM/src/riscv/doom-zcore.bin@0x10000000 \
Z-Core-DOOM/doom1.wad@0x12010000 \
--entry 0x10000000For the complete guide — WAD file sourcing, baud-rate speedup (
--fast), controls, and playing DOOM — see doc/SOFTWARE.md § 10.
| What | Command |
|---|---|
| Build (BRAM) | make APP=1 myprogram.bin |
| Upload + Monitor | python3 upload.py /dev/ttyUSB0 myprogram.bin |
| Upload only | python3 upload.py /dev/ttyUSB0 myprogram.bin -n |
| Clean build | make clean |
| Build DOOM | cd Z-Core-DOOM/src/riscv && make all |
The software/ directory contains examples compiled with the RISC-V GNU Toolchain.
| Program | Description |
|---|---|
hello |
"Hello, World!" via UART |
led_test |
Blink the 10 LEDs on DE10-Lite |
gpio_test |
GPIO read/write test |
vga_test |
VGA color bars and bouncing square |
space |
"Star Assault" VGA space shooter |
pong |
Classic Pong via UART |
multiplication |
RV32IM multiply/divide instruction test |
sdram_test |
SDRAM read/write/byte-strobe test |
Z-Core-DOOM/ |
DOOM port for Z-Core (runs from SDRAM) |
Input GPIO Connection
| GPIO | Function |
|---|---|
| 8 | Paddle 1 Up |
| 9 | Paddle 1 Down |
Connect two push buttons with pull-down resistors to GPIO[8] and GPIO[9].
| Address Range | Peripheral | Size |
|---|---|---|
0x0000_0000 – 0x0000_0FFF |
Bootloader (BRAM) | 4 KB |
0x0000_1000 – 0x0000_3FFF |
Application space (BRAM) | 12 KB |
0x0400_0000 – 0x0400_0FFF |
UART | 4 KB |
0x0400_1000 – 0x0400_1FFF |
GPIO | 4 KB |
0x0400_2000 – 0x0400_2FFF |
Timer | 4 KB |
0x0400_3000 – 0x0400_3FFF |
VGA | 4 KB |
0x1000_0000 – 0x13FF_FFFF |
SDRAM (64 MB) | 64 MB |
0x0000_0000 +------------------+
| Bootloader (4 KB)| ← baked into bitstream from MIF
0x0000_1000 +------------------+
| App space (12 KB)| ← loaded by bootloader over UART
0x0000_4000 +------------------+
| (stack top) |
0x1000_0000 +------------------+
| .text / .rodata | Code + read-only data
| .data / .bss | Initialized and zero data
| heap → | Grows upward from _heap_start
| |
| ← stack | Grows down from 0x11FF_0000
0x11FF_0000 +------------------+ Stack top (64 KB stack)
| (guard region) |
0x1201_0000 +------------------+
| WAD / assets | doom1.wad loaded by bootloader
0x1241_0000 +------------------+
├── rtl/ # Synthesizable RTL
│ ├── z_core_top_model.v # Top-level FPGA wrapper
│ ├── z_core_control_u.v # CPU Control Unit (pipeline)
│ ├── z_core_alu.v # Arithmetic Logic Unit
│ ├── z_core_alu_ctrl.v # ALU Control Unit
│ ├── z_core_decoder.v # Instruction Decoder
│ ├── z_core_reg_file.v # General Purpose Registers
│ ├── z_core_csr_file.v # CSR File (Zicsr extension)
│ ├── z_core_instr_cache.v # 32 KB Direct-Mapped I-Cache
│ ├── z_core_data_cache.v # 32 KB 2-Way Set-Assoc D-Cache
│ ├── z_core_branch_pred.v # Branch Predictor
│ ├── z_core_pma.v # Physical Memory Attributes checker
│ ├── z_core_pma_map.vh # PMA region table (platform-specific)
│ ├── z_core_mult_unit.v # Multiplier Unit
│ ├── z_core_div_unit.v # Division Unit
│ ├── z_core_32b_timer.v # 64-bit Timer core
│ ├── axil_interconnect.v # AXI-Lite Bus Interconnect
│ ├── axil_timer.v # Timer Peripheral
│ ├── axil_vga.v # VGA Controller Peripheral
│ ├── axil_uart.v # UART Peripheral
│ ├── axil_gpio.v # GPIO Peripheral
│ ├── axil_master.v # AXI-Lite Master Interface
│ ├── axil_cdc_bridge.v # AXI Clock-Domain Crossing Bridge
│ ├── axi_mem.v # AXI-Lite RAM Interface
│ ├── axi_sdram_bridge.v # AXI-to-SDRAM Bridge
│ ├── sdram_axi_top.v # SDRAM AXI Top (PLL + controller)
│ ├── sdram_pll0.v # 100 MHz PLL for SDRAM
│ ├── sdram_pll0.qip # Quartus PLL IP
│ ├── Sdram_Control/ # SDRAM Controller (IS42S16320D)
│ │ ├── Sdram_Control.v
│ │ ├── control_interface.v
│ │ ├── command.v
│ │ ├── sdr_data_path.v
│ │ ├── Sdram_WR_FIFO.v / .qip
│ │ ├── Sdram_RD_FIFO.v / .qip
│ │ └── Sdram_Params.h
│ ├── arbiter.v # AXI arbiter
│ ├── priority_encoder.v # Priority encoder
│ └── flist.vc # RTL file list
│
├── software/ # Programs and tools
│ ├── bootloader/ # On-chip UART bootloader
│ │ ├── bootloader.c # Bootloader source (v3.1)
│ │ ├── boot_start.S # Reset vector + trap handler
│ │ ├── Makefile
│ │ └── linker_boot.ld
│ ├── Z-Core-DOOM/ # DOOM port for Z-Core (submodule)
│ │ ├── src/riscv/ # Z-Core-specific DOOM backend
│ │ │ ├── zcore.lds # Linker script (SDRAM target)
│ │ │ ├── config.h # Platform addresses
│ │ │ ├── start.S # DOOM startup
│ │ │ └── ... # Platform driver files
│ │ └── doom1.wad # (not included — see SOFTWARE.md)
│ ├── sdram_test/ # Standalone SDRAM test (SDRAM execution)
│ ├── libs/ # Shared libraries
│ │ ├── uart.c / uart.h # UART driver
│ │ ├── vga.h # VGA header-only library
│ │ ├── timer.c / timer.h # Cycle-counter timer helpers
│ │ ├── printf.c / printf.h # Minimal printf
│ │ └── softint64.c # 64-bit integer helpers
│ ├── hello.c # UART Hello World
│ ├── led_test.c # LED blink
│ ├── gpio_test.c # GPIO test
│ ├── vga_test.c # VGA color bars
│ ├── pong.c # UART Pong game
│ ├── space.c # VGA space shooter
│ ├── multiplication.c # RV32IM multiply/divide test
│ ├── sdram_test.c # SDRAM read/write test (BRAM execution)
│ ├── start.S # RISC-V startup code (BRAM programs)
│ ├── linker.ld # Linker script (BRAM, origin 0x0000)
│ ├── linker_app.ld # Linker script (BRAM app, origin 0x1000)
│ ├── Makefile # GNU Make build system
│ ├── upload.py # UART bootloader client
│ └── elf2hex.py # ELF-to-HEX/MIF utility
│
├── doc/ # Documentation
│ ├── SOFTWARE.md # Complete build & run guide
│ ├── FPGA_DEPLOYMENT.md # FPGA synthesis guide
│ ├── GPIO.md # LED/switch interfacing
│ ├── UART.md # Serial communication
│ ├── VGA.md # VGA controller and API
│ └── TIMER.md # 64-bit Timer and API
│
├── Z-Core.qsf # Quartus Pin Assignments & source files
├── Z-Core.sdc # Timing Constraints
└── LICENSE # MIT License
| Signal | Pin | Description |
|---|---|---|
MAX10_CLK1_50 |
PIN_P11 | 50 MHz clock input |
KEY[0] |
PIN_B8 | Active-low reset |
| LED | Function |
|---|---|
LEDR[0] |
System active (KEY[0] high) |
LEDR[2] |
AXI read address valid (instruction fetch) |
LEDR[3] |
AXI read address ready |
LEDR[7] |
GPIO[0] mirror |
LEDR[8] |
KEY[1] mirror |
LEDR[9] |
Heartbeat (~0.74 Hz) |
| Document | Description |
|---|---|
| SOFTWARE.md | Complete build, upload, and DOOM run guide |
| FPGA_DEPLOYMENT.md | FPGA synthesis and deployment |
| GPIO.md | LED and switch interfacing |
| UART.md | Serial communication |
| VGA.md | VGA controller and API |
| TIMER.md | 64-bit Timer and API |
This project is licensed under the MIT License. See LICENSE for details.
- Alex Forencich — AXI-Lite interconnect and memory arbiter components from the verilog-axi library.
- SDRAM Controller — IS42S16320D controller core used in the SDRAM subsystem.
- DOOM RISC-V port — originally by smunaut, adapted for Z-Core. (Z-Core-DOOM)


