← All articles

MicroBlaze V on an Artix-7: running two RISC-V soft cores

How IronKernel 64 configures, connects and builds firmware for two MicroBlaze V processors on a Nexys Video.

IronKernel ·

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.

SettingValue
Instruction setRV32IMC with Zicsr, Zba, Zbb and Zbs
MMU, FPUNone
Caches8 KiB instruction, 8 KiB write-through data, over DDR3 only
Local memoryInstruction and data LMB on one dual-port block RAM
BusesAXI data port for peripherals, cache ports to DDR3
PC width32 bits, because core 1 executes from DDR3
Debug, interruptsOne 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.

BaseCore 0Core 1
0x0000_0000256 KiB LMB64 KiB LMB
0x4060_0000UART Lite-
0x4080_0000AXI IIC (codec)-
0x4400_0000Fabric window 0, 512 KiB-
0x4480_0000-AXI Quad SPI (flash)
0x44A0_0000-Fabric window 1, 1 MiB
0x4500_0000Mailbox port S0Mailbox port S1
0x8000_0000DDR3, 512 MiB, cachedDDR3, 512 MiB, cached
0xC000_0000Framebuffer, 256 KiBNetwork buffers, 128 KiB
0xC100_0000Shared BRAM, 128 KiBShared 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

  1. 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).
  2. Each firmware build uses Vitis to create a standalone platform for its core from the XSA.
  3. 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.
  4. 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.

Further reading

Planning a MicroBlaze V design?

We build soft-processor systems on AMD FPGAs, from the block design and memory map to the bare-metal firmware.