MicroZed Chronicles: Combining MicroBlaze and the Zynq MPSoC
The Arm processors available in both the Zynq and the Zynq MPSoC provide significant processing power for our applications.
Of course, thanks to the programmable logic available within the Zynq / Zynq MPSoC architecture, we are able to easily optimize our application to achieve the best performance.
One often used approach to increase performance is to accelerate high performance / calculation intensive functions into the PL, taking advantage of the inherent parallelism of logic.
However, we can also offload some of the lower level processor functions allowing the processors to function on higher level aspects of the application. For example sensor and actuator interfacing or control loop implementation, could be offloaded into the PL.
To perform functions such as above, we have the choice of implementing a complex FPGA-based state machine, or alternatively implementing a MicroBlaze softcore in the PL.
For many applications using a MicroBlaze, it makes the most sense as we are then able to easily update the design if required.
When it comes to implementing the MicroBlaze, we want to be able to ensure we can update the design easily. To do this there are two methods we can use:
Use Dual Port BRAMs for the data and instruction memory with the MicroBlaze connected to one side and the processor system on the interface.
Enable the MicroBlaze to run its application from the PS DDR or OCM.
Right now, we are going to look at option two. I will look at option one soon. I chose option two as it presents the more complex case.
For this example, I am using the Ultra96 board — the first thing to do in Vivado is to implement a Zynq MPSoC processing system. The second thing is to implement a MicroBlaze processing system. To get this example working, we need to configure the following:
Enable discrete ports
No local memory bus interfaces
Enable the AXI instruction and data interfaces
We can configure these options in the standard view of the MicroBlaze customization wizard.
Once we have made these customizations we need to switch the MicroBlaze customization wizard view into the advanced mode and set the vector base address.
What we set the vector base address to depends upon which memory we want to use in the PS. We have the choice of on chip memory or DDR.
Be warned though, if you want to use OCM you will need to do something smart in the deplorable system. As the first stage boot loader (FSBL) runs from OCM.
This example therefore runs the application from the DDR memory, as such the vector base address is set to 0x00000000.
With that completed on the block diagram, we need to add in the necessary structures to correctly configure the MicroBlaze and allow the PS to control the MicroBlaze.
What we need to add is pretty simple — a constant block and GPIO, both of which are connected to the discrete ports on the MicroBlaze we just enabled.
We need to do this as we want to force the MicroBlaze to go to the sleep state following reset release, and only to be awoken and start executing code under control of the PS.
To do this, we use two of the discrete ports reset_mode[0:1] and wakeup[0:1].
We will connect the constant block to the reset_mode[0:1], set to 0x01. This defines the behavior of the MicroBlaze following reset release. If it is 0x00, then it will start attempting to execute code immediately; if it is 0x01, the MicroBlaze will sleep until awoken.
This is the behavior we want as once the PL has been configured during the boot process, we will still not be ready for it to immediately begin executing code.
To wake the MicroBlaze, we use the wakeup[1:0] input. It is this signal we are going to connect to the Zynq MPSoC EMIO. We can then assert this once we are ready for the MicroBlaze to begin executing code.
To allow the MicroBlaze to access the DDR RAM and the UART in the Zynq MPSoC for communication, we need to enable a slave AXI port on the Zynq MPSoC. For this application, as I am working from DDR, I have enabled a high performance slave in the full power domain.
I also enabled address fragmentation.
Once this is complete, we can build the Vivado design and export the HDF to SDK.
In SDK, we need to create a new project targeting the MicroBlaze we just implemented in the PL.
Let the project create the BSP and select the simple hello world example. Once the BSP has been created, as we are working with the Ultra96 and using the JTAG/UART pd, we need to set the stdin/stdout to PSU_UART1.
To download and run the application, we need to use the FSBL to load in the bit file and the MicroBlaze image to the DDR.
However, we need to update the FSBL to toggle the GPIO before it completes to wake up the MicroBlaze and start program execution.
We do this by adding the following code to the XFsbl_HookBeforeHandoff() function, which is within the XFsbl_hooks.c.
This code enables the GPIO as an output and drives a high output.
Before we build the FSBL, if we want to see the debug prints we need to include the symbol FSBL_DEBUG_INFO.
With the FSBL and application built, we can create the boot image and down load it onto the board.
Although, if we want to debug the application and we probably will, we can use the XSCT terminal to connect to the MicroBlaze.
Let’s take a quick look at how we can do this. The first thing to do in XSCT is connect by typing connect.
Once connected, if you look under the system debugger, when it is attached to a running target you will see the following.
If you type the commands target within XSCT, you will see the following list of targets.
To connect to the MicroBlaze, type in ta 5
Once this is completed, we can stop the MicroBlaze with the command STOP.
We can then download the application using the command dow app.elf. To download the application, note ensures the working directly is correct for the application location; otherwise you need to include a path.
Once the application is downloaded, we can run the application and you should see the hello world output at the end of the FSBL output if you look in a terminal window.
Being able to use MicroBlazes in the PL in this manner provides maximum flexibility for our Zynq / Zynq MPSoC systems.
As I mentioned above, we will also look at how we can configure the MicroBlaze using dual port BRAM in a blog soon.