Showing results for 
Show  only  | Search instead for 
Did you mean: 

Executing MicroBlaze Applications from the PSU DDR in Vitis

3 0 961

The MicroBlaze™ CPU is a family of drop-in, modifiable preset 32-bit/64-bit RISC microprocessor configurations.

The MicroBlaze processor meets the requirements of many diverse applications including Industrial, Medical, Automotive, Consumer, and Communications markets. The MicroBlaze uses a Harvard architecture, and typically this is via a dual LMB BRAM in the PL. However, In this Blog we will discuss how we can execute a MicroBlaze Application from the PSU DDR on the Zynq UltraScale ZCU104 board.

Upon exiting reset, the MicroBlaze will fetch its instructions from the memory address specified in the C_BASE_VECTORS parameter in the MicroBlaze Configuration. In our case, this C_BASE_VECTORS will be set to a region in the PSU DDR.

However, this poses a potential problem as depending on the boot flow employed, the PSU DDR might not be configured before the PL. This can cause the MicroBlaze to hang. To prevent this, we can use the Reset Mode signal on the MicroBlaze.


In the table above, we can see the various configurations for the reset_mode. The configurations that best suits our use case is "01", where the MicroBlaze is held in reset until we are ready. We will use the GPIO via the EMIO on the PSU to bring the MicroBlaze out of reset.

Creating the Hardware:

The Vivado IP Integrator Block Design can be seen below:


Users can customize their design as they see fit.

The key components configurations are discussed below.

MicroBlaze Configuration:

Use the Block Automation tool in Vivado IP Integrator to Initially configure the MicroBlaze as shown below. Here, I have removed the local memory, and used 64K of cache:


To enable the reset_mode and wakeup pins, users need to enable the Discrete Ports in the MicroBlaze configuration:


In the Zynq UltraScale PS configurations, I have enabled two slave ports; one for memory, one for the peripherals:


I have enabled the GPIO via the EMIO so that I can control the MicroBlaze wakeup:


We can also access all of the Zynq UltraScale IP that are enabled from our MicroBlaze. To do this, enable the Address Fragmentation:


We discussed the reset mode above. We will hold the MicroBlaze in reset until we are ready.

To hold the MicroBlaze in reset, we can use the Constant IP from the IP catalog:


Finally, connect the GPIO and the Constant IP:


The MicroBlaze Address map is shown below:


Users can right click on an address segment, and either add or exclude from the address map.

That is it for the hardware configuration. We can Generate the Output Products, Create the HDL wrapper, and Generate the Bitstream then export the Hardware.

This will generate the XSA to be used in Vitis to generate our Software.

Creating the Software:

The Vitis IDE can be used to generate the MicroBlaze application. Vitis will also create the boot images including the FSBL and the PMUFW automatically.

However, because we want to control the GPIO EMIO from the FSBL, we will opt to not have the tools generate the boot images:


Generate the MicroBlaze Hello World application:



We can add new project applications as shown below. For example, for the Zynq FSBL:



Note: the FSBL does not support 64 bit to 32 bit handoff. So, because the MicroBlaze is 32 bits, we should also have the FSBL set to 32 bits:



Repeat for the PMUFW:



You should see all of the application projects as shown below:


Because we added the PS UART to the MicroBlaze address map, we can use this for the STDIN/OUT in the BSP:



We can see the MicroBlaze Hello World Linker here:


This is placed at the base address of the DDR (0x00000000). Users need to keep this in mind when creating a boot image so that applications do not overlap.

Programming via the SD Card

First update the FSBL.

We can use the Xil_Out32 API to toggle the GPIO.

Add the xil_io header file to the xfsbl_handoff.c file:



/***************************** Include Files *********************************/
#include "xfsbl_hw.h"
#include "xil_cache.h"
#include "psu_init.h"
#include "xfsbl_main.h"
#include "xfsbl_image_header.h"
#include "xfsbl_bs.h"
#include "xil_io.h"



I have added this to the XFsbl_HandoffExit function



Xil_Out32(0xff0a0018, 0x1);
Xil_Out32(0xff0a02c4, 0x1);
Xil_Out32(0xff0a02c8, 0x1);
Xil_Out32(0xff0a004c, 0x1);

XFsbl_Printf(DEBUG_GENERAL,"Toggle GPIO \n\r");



I have also enabled the FSBL debug info:


Create the boot image:


I have removed the FSBL and PMUFW and added them again with the correct image type.

Make sure that the partition images are in the correct order (use the up and down controls to rearrange them)



Note: because we are sharing the same UART here, users can place a delay in the Hello World app.

Programming via JTAG:



fpga -no-rev -f design_1_wrapper/bitstream/design_1_wrapper.bit
# Add the Microblaze PMU to target
targets -set -nocase -filter {name =~ "PSU"}
mwr 0xFFCA0038 0x1FF
# Download PMUFW to PMU
target -set -filter {name =~ "MicroBlaze PMU"}
dow zynqmp_pmufw/Debug/zynqmp_pmufw.elf
# write bootloop and release A53-0 reset
targets -set -nocase -filter {name =~ "PSU"}
mwr 0xffff0000 0x14000000
mwr 0xFD1A0104 0x380E
# Download FSBL to A53 #0, and the Hello World app to DDR
targets -set -filter {name =~ "Cortex-A53 #0"}
dow zynqmp_fsbl/Debug/zynqmp_fsbl.elf
after 500
dow hello_world/Debug/hello_world.elf
# Handle PS PL
targets -set -nocase -filter {name =~ "PSU"}
source design_1_wrapper/hw/psu_init.tcl
after 500
after 500
after 500
# Wake up Microblaze
targets -set -nocase -filter {name =~ "PSU"}
mwr -force 0xff0a0018 0x00000001
mwr -force 0xff0a02c4 0x00000001
mwr -force 0xff0a02c8 0x00000001
mwr -force 0xff0a004c 0x00000001



You should see output similar to the following: