In my previous project post, I detailed how I created a base hardware design in Vivado 20232.2 for the Kria KD240 Drives Starter Kit geared towards 3-phase brushless DC motor controller with quadrature encoder support. That post was the first of my getting started series for the Kria KD240 in the AMD FPGA tools version 2023.2, so I'm continuing with this project post that is a tutorial for developing a bare metal (no-OS/standalone) application in the new Vitis Unified IDE.
I was going to skip the bare metal application tutorial for my KD240 in favor of skipping straight to the accelerated application development and other Linux-based application tutorials, but after I found a couple of curve balls with navigating the new Vitis IDE I decided this post was worth putting out there!
Since 2023.2 is the first release of the new Vitis Unified IDE, none of the steps in this tutorial are applicable to any previous versions of Vitis (now referred to as Vitis Classic).
Disable Extensible Platform for Bare Metal AppsEvery time I create a project for the first time in a new version of Vitis, I always run a simple hello world bare metal application as an initial test that I haven't grossly screwed up some basic setting. This is where Vitis Unified threw me its first curve ball.
Since this is a bare metal (no-OS/standalone) application, I found that the hooks for launching debug runs does not exist if you build a platform component (previous referred to as a platform project in Vitis Classic) with an XSA exported from a Vivado project that is configured as an extensible Vitis platform.
So after I created my hello world standalone application, I found that all I could do was run a build and the rest of the options for running, debugging, and generating a boot image were missing:
For reference, this is what the Flow should look like:
After digging through various documentation and recent forum posts, I went back to my Vivado project to generate a new bitstream with the extensible Vitis platform option disabled in project settings:
Then re-exported the hardware:
Ensuring to including the bitstream in the exported XSA:
Since I exported the extensible platform hardware project as "kd240_base.xsa", I chose to export the non-platform version as "kd240_wrapper.xsa" so I'd have both to use going forward as needed.
After that detour, launch Vitis Unified either from Vivado or the command line.
Vitis will launch and the Vitis Components tab will show that no workspace is currently open.
When creating a new workspace, still select Open Workspace from the Vitis Components tab,
Then use the file explorer to create the new directory to use for the new workspace. I've found that it's best that the directory be empty the first time Vitis launches into it, weird things happen if there are existing items in the directory because Vitis tries to pull them into the project as components.
Click Ok and Vitis will take a few moments to launch into the new workspace.
After opening the new workspace, the the Vitis Components tab will then show the options to create new components:
Just like in Vitis Classic, the first step is to create a new platform component. The platform component is what reads in the exported XSA file from Vivado to create the hooks to the hardware design such as the board support package (BSP) and boot components.
Select Create Platform Component from the Vitis Components tab:
Give the platform the desired name:
Then point it to the location of the exported XSA file from Vivado (skip the Emulation option here, it's not applicable to bare metal/standalone apps):
Give the OS and Processor page a few moments to read the XSA. It'll default some options that can then be changed. Be sure to select standalone for the Operating system, psu_cortexa53_0 for the Processor, and enable the options to Generate Boot artifacts and PMU firmware:
Review the selections on the last page then click Finish:
And wait for platform component to generate:
Build the platform component by single left-clicking on Build in the Flow tab. A green check will appear when successfully built
This step is import because Vitis Unified no longer builds the platform automatically if it's out-of-date when a build of the application is run like Vitis Classic used to do. So if this initial build on the platform is not run before trying to build an application component based on it, a non-description error will get thrown from the application component as it can't find any of the output files from the platform component to build with.
Update Target UART Port in KD240 BSPThe Kria KD240 uses both of the UART ports (UART 0 and UART 1) in its Zynq MPSoC's MIO, where UART 1 is the JTAG/UART serial port. I found the hardware the BSP in the platform component populates defaults to UART 0 instead of UART 1 for the JTAG/UART serial port (meaning you won't see anything from print statements in your code).
To update the BSP, open vitis-comp.json in the Settings folder of the platform component. Navigate to psu_cortexa53_0 > standalone_psu_cortexa53_0 > Board Support Package > standalone and change both standalone_stdin and standalone_stdout to psu_uart_1:
Do the same for the FSBL under psu_cortexa53_0 > zynqmp_fsbl > Board Support Package > standalone:
Regenerate the BSP then run another build on the platform component.
Create New Application ComponentWith the platform component built and ready, the next step is to create the application component for the standalone application. The hello world application template and other application templates have moved in Vitis Unified. I am a fan of the new location since it's more akin to the import examples option that XSDK has and has otherwise been buried in the BSP in Vitis Classic.
To create an application from a template, select Examples from the Welcome tab:
Find the Hello World application (not Linux Hello World):
After selecting Hello World from the Examples list, select Create Application Component from Template:
The default application name can be left:
Or a new application name can be specified, like I chose to do since I also wanted to add some code to drive the AXI GPIOs for the user LEDs on my KD240 and my custom motor control logic:
Select the platform component created in the previous steps to build the application on:
Since only the one standalone domain for the first A53 processor core exists in the platform component, that is the only option available to build an application on:
Finally, review the selections and click Finish to generate the application component:
Again, I like to run an initial build on the code as-is before customizing it. This catches any gross configuration errors so you don't mistake them for errors from your custom code.
The next curve ball that's different in Vitis Unified is the new method of targeting peripherals in the PL such as the AXI GPIOs with the system device tree using the base address of the AXI device instead of the device ID. In fact, there is no longer a device ID populated in the xparameters.h file. I personally feel like it actually makes the code simpler so once I figured out the new instantiation, it was very straight forward.
The easiest way to update the instantiation I found is to go ahead and declare any of the functions for a target peripheral as previously done with the device ID, then right-click on the function and select the option Go to Definition. This will open the function source files where the new instantiation can be seen.
My custom motor controller logic in my PL is just controlled by a single GPIO output bit that turns the motor on with logic level high, and turns the motor off with logic level low. I have it driven by an AXI GPIO IP instantiated as axi_gpio_1.
Since the base address of the AXI GPIO IP is needed to drive it with the XGpio functions in the C code, the first step is to open the xparameters.h file to copy+paste it from (you could also get it from the Address Editor in the Vivado block diagram).
The xparameter.h file is located under Application Component > Includes > /<workspace directory>/kd240_platform/export/sw/standalone_psu_cortexa53_0/include:
The I just used crtl+F to search for axi_gpio_1.
I ended up doing the same for the other AXI GPIO block that I tied the two user LEDs to (axi_gpio_0) and wrote the following simple main function in helloworld.c:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "xgpio.h"
#include "xparameters.h"
#include "xil_assert.h"
#include "xil_exception.h"
XGpio GPIO_StartStop_Ptr;
XGpio GPIO_LED_Ptr;
int status;
int main()
{
init_platform();
xil_printf("Hello KD240!\n\r");
XGpio_Initialize(&GPIO_LED_Ptr, XPAR_XGPIO_0_BASEADDR);
XGpio_Initialize(&GPIO_StartStop_Ptr, XPAR_XGPIO_1_BASEADDR);
XGpio_SelfTest(&GPIO_LED_Ptr);
XGpio_SetDataDirection(&GPIO_LED_Ptr, 1, 0x0);
XGpio_SetDataDirection(&GPIO_StartStop_Ptr, 1, 0x0);
xil_printf("Start motor...\n\r");
XGpio_DiscreteWrite(&GPIO_LED_Ptr, 1, 0x01);
XGpio_DiscreteWrite(&GPIO_StartStop_Ptr, 1, 0x01);
for (int i=0; i<100; i++){
xil_printf("Motor running...\n\r");
};
xil_printf("Stop motor...\n\r");
XGpio_DiscreteWrite(&GPIO_LED_Ptr, 1, 0x00);
XGpio_DiscreteWrite(&GPIO_StartStop_Ptr, 1, 0x00);
xil_printf("Successfully ran Hello World application");
cleanup_platform();
return 0;
}
It simply enables the motor and turns on the user LED0 to indicate the application is enabling the motor, lets it run for a bit while outputting some print statements, then turns both the motor and LED0 off.
Debug Application on HardwareThe next thing that I like about Vitis Unified is that it is much simpler to launch a debug run. Select the target application in the Component drop down menu under the Flow window then simply single left-click Debug.
This will switch the IDE to the Debug view, boot the target board, and launch the application before stopping at an automatic breakpoint at the first line in the main function.
One of my favorite parts of firmware/HDL design is moving external peripherals, so this motor kit is definitely one of my favorite peripheral setups I've had for a dev board.
I do have one major gripe with Vitis Unified in that it no longer has a build in serial terminal like XSDK and Vitis Classic had. So it is necessary to use an external serial terminal application such as Putty or TeraTerm to see the output from the UART from the target dev board.
And that's it for the bare metal/standalone application development on the KD240 in Vitis Unified, see the next project posts for Linux-based application tutorials and accelerated application tutorials!
Comments