MicroBlaze V is AMD's RISC-V soft processor for its FPGAs. This article shows how IronKernel 64, our open-source SID music machine, runs two MicroBlaze V cores on the Artix-7 XC7A200T of a Digilent Nexys Video: the processor settings, the memory map, the link between the cores and the build flow. The IronKernel 64 details come from its specification and its public repository.
What MicroBlaze V is
MicroBlaze is the soft processor from AMD (formerly Xilinx), built from FPGA fabric rather than fixed silicon. MicroBlaze V is the version that runs the RISC-V instruction set, so its software builds with a RISC-V GCC toolchain. In Vivado it is the IP core microblaze_riscv, placed in a block design next to Local Memory Bus (LMB) memory, AXI peripherals and a debug module. Vitis generates the board support package and builds the software.
The instruction set extensions, caches, MMU, FPU and bus ports are all block design settings.
How IronKernel 64 configures its two MicroBlaze V cores
Both cores share one configuration, set by script in fpga/bd/c64_bd.tcl, and both run at 100 MHz.
| Setting | Value |
|---|---|
| Instruction set | RV32IMC with Zicsr, Zba, Zbb and Zbs |
| MMU, FPU | None |
| Caches | 8 KiB instruction, 8 KiB write-through data, over DDR3 only |
| Local memory | Instruction and data LMB on one dual-port block RAM |
| Buses | AXI data port for peripherals, cache ports to DDR3 |
| PC width | 32 bits, because core 1 executes from DDR3 |
| Debug, interrupts | One shared debug module with two ports; one interrupt controller per core |
Two cores, two jobs
Core 0, the "C64" core, has a 256 KiB LMB that holds its firmware and the 64 KiB of emulated C64 RAM. It runs the screen editor and command shell, a 6502 interpreter and the PSID player, and it owns the UART console, the audio codec, the framebuffer and the SID registers. Its release image fills 96.0 % of the LMB.
Core 1, the "1541" core, emulates a disk drive. It has a 64 KiB LMB for a boot stub, vectors and stacks, and runs its code from DDR3, because lwIP and libsmb2 alone are about 215 KB of code. It owns the network path and the QSPI flash. Both cores run bare-metal C with no RTOS.
The MicroBlaze V memory map
Each core has its own address space. The two overlap only where they share hardware.
| Base | Core 0 | Core 1 |
|---|---|---|
| 0x0000_0000 | 256 KiB LMB | 64 KiB LMB |
| 0x4060_0000 | UART Lite | - |
| 0x4080_0000 | AXI IIC (codec) | - |
| 0x4400_0000 | Fabric window 0, 512 KiB | - |
| 0x4480_0000 | - | AXI Quad SPI (flash) |
| 0x44A0_0000 | - | Fabric window 1, 1 MiB |
| 0x4500_0000 | Mailbox port S0 | Mailbox port S1 |
| 0x8000_0000 | DDR3, 512 MiB, cached | DDR3, 512 MiB, cached |
| 0xC000_0000 | Framebuffer, 256 KiB | Network buffers, 128 KiB |
| 0xC100_0000 | Shared BRAM, 128 KiB | Shared BRAM, 128 KiB |
The register maps have one owner: an RTL package defines them, a C header mirrors them, and the firmware build fails if the two differ.
How the two cores talk
The cores share a 128 KiB dual-port block RAM, one port each, on the same clock. It sits outside both caches, so both cores always see the same data. An AMD Mailbox carries the doorbells.
Core 0 writes a request into the shared BRAM and rings the doorbell. Core 1 takes the interrupt, does the work, writes the reply and rings back. Only one request is outstanding at a time, so neither side needs a queue. Core 1 has no UART, so it writes its log into the shared BRAM and core 0 prints it.
Both cores reach DDR3 through one AXI SmartConnect. Each core uses its own 64 MiB region, so the write-through caches need no coherence between the cores.
Toolchain and build flow
- A Vivado 2026.1 Tcl script creates the block design, builds the bitstream and writes the hardware platform file (XSA) and a memory map file (MMI).
- Each firmware build uses Vitis to create a standalone platform for its core from the XSA.
- The RISC-V GCC from the Vitis install compiles the C firmware with warnings treated as errors. Core 0's build fails if the image outgrows its 256 KiB LMB.
- Core 0's firmware and core 1's bootloader are merged into the bitstream's block RAM through the MMI.
For stand-alone boot, one QSPI flash image holds the bitstream plus core 1's firmware and the tune bank. Core 1's bootloader waits for DDR3, checks the image's CRCs, copies the firmware into DDR3 and starts it. During development, a JTAG script loads core 1's firmware instead.
What to watch for with MicroBlaze V
- Set the program counter width to cover every address the core executes from. IronKernel 64 sets 32 bits because core 1 runs from DDR3.
- Choose the cached range on purpose. IronKernel 64 caches only DDR3 and keeps shared memory uncached. When firmware reads DDR3 data written by someone else, it invalidates that range first.
- Budget block RAM early. IronKernel 64 uses 78.49 % of the device's block RAM against an 80 % build limit.
- Watch the LMB fill. Core 0 is at 96.0 %, so its build checks the size on every link.
- Keep block design cell names stable. The build scripts find the processors and their memories by name.
The timing side of the same design is in our article on Artix-7 timing closure, and our FPGA design service page describes how we work on AMD devices.