In my last project tutorial using the TE0711 SoM from Trenz Electronic, I went over setting up the hardware in Vivado and making it compatible with my selected baseboard, the TE0701. Since that post ended up being longer than I expected to properly cover the hardware setup, I decided to cover the embedded application development in Vitis for it here in a new project post.
Since the UART COM port is the main method of communication with the host PC that I enabled in the hardware design, I decided to write a simple application for the UART to listen for any input and immediately echo it back. This application will run forever until a user presses the escape key.
First things first, launch Vitis from the Vivado project.
Point to the desired workspace location to create the Vitis project in. I personally like to create a folder with the Vivado project directory titled vitis_workspace to work from.
Once a workspace is pointed to, click launch and Vitis will generate a new empty project workspace.
Select the Create Platform Project option to pull in the hardware platform that was exported from Vivado:
Once the hardware platform is in place a new application project can be created to run on it. Select the Application Project option from the New menu option from the toolbar at the top of the Vitis window. As you walk through the setup windows for the new application, be sure to select the hardware platform that was created by the platform project and your desired programming language (I prefer C for embedded applications). The final screen allows for the selection of a pre-defined application template, I usually use the Hello World template as my starting point for any new applications (and is also what I selected for this project).
After Vitis generates the new application, build the Vitis project to verify everything is working correctly so far.
If Vitis throws error that the application overflows the available memory in the MicroBlaze when attempting to build the application, the instruction and data caches need to be enabled for the MicroBlaze in the Block Design in Vivado.
Skip the initial connection automation that appears when the instruction and data caches are initially enabled on the MicroBlaze, instead add an instance of the AXI BRAM Controller to the block design. After adding the the AXI BRAM Controller, then run Connection Automation. This connects the BRAM via an AXI SmartConnect IP versus the regular AXI Connect IP.
Once the BRAM memory is connected, switch over to the Address Editor window and increase the size of microBlaze_0_local_memory/ilmb_bram_if_cntlr to 16K, microBlaze_0_local_memory/dlmb_bram_if_cntlr to 16K, and both values of axi_bram_ctrl_0 to 64K.
To tell the MicroBlaze it has more memory space to use for the instruction and data caches, relaunch its configuration editor (double-click on that MicroBlaze IP block in the diagram) and increased the Size in Bytes field to 16kB on the Cache page (page 3 in Vivado 2019.2).
Switch back to the diagram window and regenerate the layout then validate the design to verify that there are no errors or critical warnings. Save the block deign, re-create the HDL wrapper, and re-run both synthesis and implementation. Finally, generate a new bitstream and export the hardware platform again. I always point to the same location that I exported the previous version of the hardware platform so it just overwrites it.
Once the hardware platform is exported, switch back to Vitis and right-click on the platform project in the Explorer window. Select the option to Update Hardware Specification. Since I simply over-wrote the previous version, it's already pointed to it. Simply click OK so that Vitis is prompted to re-read the hardware platform so it can pull in the new hardware updates.
Sometimes that linker script doesn't update automatically to allow the system to take advantage of the newly added memory. To force it to update, right-click on the application in the Explorer window and select Generate Linker Script. Click Generate on the window that pops up, make no changes but verify that the new memory sizes appear in the hardware memory map.
The Hello World application template generates a helloworld.c file that contains the main function. I simply added my code to it, but you could also create a new file or import an existing one titled main.c.
As I mentioned at the beginning of this post, this application will simply sit in an infinite loop listening on the UART and echoing back any characters it sees sent to it until the ESC character appears in the console:
#include <stdio.h>
#include "platform.h"
#include "xuartlite.h"
#include "xil_printf.h"
#include "xuartlite_i.h"
#include "xuartlite_l.h"
XUartLite UartLite;
#define CHAR_ESC 0x1b
#define CHAR_ENTER 0x0d
#define CHAR_SPACE 0x20
#define CHAR_NULL 0x00
#define UART_BASEADDR XPAR_JM1_AXI_UARTLITE_0_BASEADDR
#define UART_DEVICEID XPAR_JM1_AXI_UARTLITE_0_DEVICE_ID
int Configure_uart(){
int Status;
/* Initialize the instance of the UART with the Uartlite driver */
Status = XUartLite_Initialize(&UartLite, UART_DEVICEID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Verify the hardware was brought up properly */
Status = XUartLite_SelfTest(&UartLite);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
int main(){
init_platform();
xil_printf("Hello World\n\r");
/* Configure the uartlite interface */
Configure_uart();
u8 RecvChar[1];
u8 EscChar[1];
EscChar[0] = CHAR_ESC;
RecvChar[0] = CHAR_NULL;
unsigned int ReceivedCount = 0;
/*
* Just sits in this loop of echoing characters back to the terminal until
* ESC key is pressed.
*/
while(RecvChar != EscChar){
ReceivedCount = XUartLite_Recv(&UartLite, RecvChar, 1);
if (ReceivedCount != 0){
XUartLite_Send(&UartLite, RecvChar, 1);
}
}
cleanup_platform();
return 0;
}
Build the application after writing the code and saving the file to verify there are no errors.
Program the FPGA by right-clicking on the application from the Explorer window then select Program FPGA. Since the Program FPGA option was selected by right-clicking on the application from the Explorer window, Vitis will auto-populate its respective bitstream and memory map files for you. The auto detect feature will also find the TE0711 on its own so long as the board has been configured per the guidance of my last project post, connected via the mini-USB cable, and powered on.
Once the FPGA has been programmed with the bitstream run application using the System Debugger by again right-clicking on the application from the Explorer window then selecting Debug As and Launch on Hardware (Single Application Debug). This give you the option to either run the application as is or step through it for debugging.
Vitis will take a moment to launch the application on the MicroBlaze and switch over to the Debug view/workflow.
Once switched over to the Debug view, connect to the TE0711's now available serial port from the Vitis Serial Terminal (or you can use any other serial terminal application like Putty if preferred). Vitis' Serial Terminal defaults to a 115200 baud rate, so don't forget to change to to 9600 before connecting or the output will just be random characters.
Step through the application at first if you want to see exactly what's going on, then let the app run so you can test out the echo feature. The buffers allow for more than one character at a time to be entered and echoed back.
I'm sure there is a limit to this before it overflows, but after experimenting with some shortish sentences, it seems pretty robust! Overall, it's a handy little bit of code to keep around.
Comments