This project continues the second part (Part 2) of the Kria KV260 4K TPG Standalone(Bare-metal) design. If you have not checked Part 1 of this project, you can check it by the link here.
As you might have noticed, in part 1, I covered Hardware Design in Xilinx Vivado Design Suite, in this part 2, I will cover Software Design in Xilinx Vitis IDE.
Design FlowAs usual, the Vitis software application design begins after exporting the hardware design from vivado.
The steps for creating the Application Project and the Platform Project are just the same as you normally do for most of your design. That's why I have skipped all of its steps. However, you can give the application project name as "app" for this moment. And obviously, we begin with hello world application template.
Let's look at the software application architecture as shown below.
The picture clearly illustrates the flow, or say, code structure of our software application design. Let's discuss this in brief in the following manner.
I mostly use C-programming language to write standalone programs in Vitis/SDK. Xilinx has provided many IP driver APIs written in the C language. However, you can also choose the C++ programming method. The hello world template app is in the C language.
Remember, our program starts from the main function, which houses the following coding steps.
Step1:Initialization of peripherals.
This includes the initialization of several peripheral interfaces such as Ethernet, UART, IIC, IP hardware, and so on. But in our design case, we mainly have to focus on hardware deviceinitialization. This is because of the fact that the SoC FPGA has PS and PL sides. Both sides can have many interfaces. PS interfaces are mostly initialized and configured automatically when PS is initialized. But PL peripheral interfaces have to be manually initialized and configured.
Hence, the PL device initialization includes the initialization of hardware IPs.
That is,
- Video Test Pattern Generator (TPG),
- Video Timing Controller (VTC).
If you notice the vivado hardware block design in Part 1, we have TPG and VTC which have AXI-Lite interfaces. That's why, these IPs require initialization and configuration so that, these IPs can be programmed and controlled from the software side.
Step 2: Configuration of Interfaces.
As you might have learned above why IP initialization and configuration are required, now we have to configure those IPs in this step so that IPs can start working when we run our design on hardware.
The configuration includes the configuration of TPG and VTC IPs.
Step 3: Initialization and Configuration of Display Port (DP)
In the Kria KV260 board, the DP interface is coming out from the PS side. Although the PS interfaces are mostly initialized and configured automatically, this DP interface requires manual initialization and configuration.
DP Source ImportXilinx has provided software API for DP interface. Users can leverage it to quickly implement the DP interface in their design. They do not have to write the application code by themselves.
You can find the API code and import it by following the steps shown in the pictures below.
You will notice that the example application project, named as xdpdma_video_example_1, will be visible in the Vitis Explorer pane after importing.
Expand the design directory and under src, you will find three source files, namely,
- xdpdma_video_example.c
- xdpdma_video_example.h
- xdppsu_interrupt.c
Copy these files and paste them into your application project src directory. And it would be seen like this.
Note: Probably, you might have noticed, the different file names. Clearly, those files have been renamed. You can let them as it is if you like.
Function Prototype AdditionWell, we also need to make a few changes in the xdpdma sources files.
To be able to call dpdma function from main function, we have to include function prototype in the header file.
Let's do as shown below. When you are a pro-C-programmer, I believe you are already familiar with following steps.
- Open up xdpdma_video_example.c and find main function of it. Let's rename the function name, e.g., run_dppsu. And obviously, you can set the name as your own.
- Let's copy the entire function name, which is, int run_dppsu().
- Open up xdpdma_video_example.h file and paste the name under Funtion Prototype section.
We still need to do some video resolution-related parameters modification in xdpdma source code.
- Let's open up xdpdma_video_example.c source file and find out BUFFERSIZE definition and change as according to the following;
- On the same source file, find InitRunConfig function. Change the VideoMode as shown below;
- On the same source file, find InitDpDmaSubsystem function, inside which you can find the following default input video select function.
- In the case of our design, we need to change 2nd and 3rd parameters as shown below.
Now, everything is done. The full code is given in the Code Section of this article.
Build & Run ConfigurationAs you have all the necessary source codes, this time you can proceed with building the application. The build steps are the same as you do in most application projects. You should get no error for the build. The successful build produces the.elf files, which will be loaded into the board along with the bitstream file.
You should be happy when the build goes all good. Let's hurry up for running the design.
Before running, let's do some run configuration as shown in the picture.
Note: follow the steps application project > Right-Click > Run As > Run Configuration to open configuration window.
Uncheck use FSBL flow for initialization
Okay, now you are ready to go. Click on Apply and then Run.
Wait wait...
Let's not forgot to set up your board and prepare the UART terminal on your PC. I mostly use gtkterm for the UART.
DemoHave fun! I believe you are also getting the same output as shown in the demo below.
Code*
* main.c
*
* Author: nikil thapa
* https://www.linkedin.com/in/nikil-thapa-7b2aa41b2/
*
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xv_tpg.h"
#include "xvtc.h"
#include "xvidc.h"
#include "xdpdma_video.h" //change according to your file name
XV_tpg tpg;
XVtc vtc;
XVtc_Config *vtc_config;
void driverInit() {
XV_tpg_Initialize(&tpg, 0);
vtc_config = XVtc_LookupConfig(XPAR_VTC_0_DEVICE_ID);
XVtc_CfgInitialize(&vtc, vtc_config, vtc_config->BaseAddress);
}
void ConfigVtc(XVidC_VideoStream *StreamPtr) {
XVtc_Timing vtc_timing = { 0 };
u16 PixelsPerClock = 1;
vtc_timing.HActiveVideo = StreamPtr->Timing.HActive / PixelsPerClock;
vtc_timing.HFrontPorch = StreamPtr->Timing.HFrontPorch / PixelsPerClock;
vtc_timing.HSyncWidth = StreamPtr->Timing.HSyncWidth / PixelsPerClock;
vtc_timing.HBackPorch = StreamPtr->Timing.HBackPorch / PixelsPerClock;
vtc_timing.HSyncPolarity = StreamPtr->Timing.HSyncPolarity;
vtc_timing.VActiveVideo = StreamPtr->Timing.VActive;
vtc_timing.V0FrontPorch = StreamPtr->Timing.F0PVFrontPorch;
vtc_timing.V0SyncWidth = StreamPtr->Timing.F0PVSyncWidth;
vtc_timing.V0BackPorch = StreamPtr->Timing.F0PVBackPorch;
vtc_timing.VSyncPolarity = StreamPtr->Timing.VSyncPolarity;
XVtc_SetGeneratorTiming(&vtc, &vtc_timing);
XVtc_Enable(&vtc);
XVtc_EnableGenerator(&vtc);
XVtc_RegUpdateEnable(&vtc);
}
void ConfigTpg(XVidC_VideoStream *StreamPtr) {
XV_tpg_DisableAutoRestart(&tpg);
XV_tpg_Set_height(&tpg, StreamPtr->Timing.VActive);
XV_tpg_Set_width(&tpg, StreamPtr->Timing.HActive);
XV_tpg_Set_colorFormat(&tpg, XVIDC_CSF_RGB);
XV_tpg_Set_bckgndId(&tpg, XTPG_BKGND_COLOR_BARS);
XV_tpg_Set_ovrlayId(&tpg, 1);
XV_tpg_Set_boxSize(&tpg, 100);
XV_tpg_Set_motionSpeed(&tpg, 10);
XV_tpg_EnableAutoRestart(&tpg);
XV_tpg_Start(&tpg);
}
int main() {
init_platform();
print("---------------------------------\n\r");
print("----------KV260 4K TPG-----------\n\r");
print("---------------------------------\n\r");
XVidC_VideoTiming const *TimingPtr;
XVidC_VideoMode TestModes[2] = { XVIDC_VM_1080_60_P, XVIDC_VM_UHD_30_P };
XVidC_VideoStream VidStream;
/*Set stream parameters*/
VidStream.PixPerClk = tpg.Config.PixPerClk;
VidStream.ColorFormatId = XVIDC_CSF_RGB;
VidStream.ColorDepth = tpg.Config.MaxDataWidth;
VidStream.VmId = TestModes[1];
TimingPtr = XVidC_GetTimingInfo(VidStream.VmId);
VidStream.Timing = *TimingPtr;
VidStream.FrameRate = XVidC_GetFrameRate(VidStream.VmId);
xil_printf("\r\n********************************************\r\n");
xil_printf("Test Input Stream: %s (%s)\r\n",
XVidC_GetVideoModeStr(VidStream.VmId),
XVidC_GetColorFormatStr(VidStream.ColorFormatId));
xil_printf("********************************************\r\n");
driverInit();
ConfigTpg(&VidStream);
ConfigVtc(&VidStream);
run_dppsu();
cleanup_platform();
return 0;
}
What's Next?I believe you had a great time with this project. Thanks a lot for your support. It will definitely encourage me to post more and more standalone/bare-metal/bare-bone as well as Linux-based FPGA design projects here.
Comments