top of page

MicroZed Chronicles: Versal AI Edge, VESA LCD Display.

Writer's picture: Adam TaylorAdam Taylor

I have had the Alinx VD100 Versal Edge AI platform for a little while now. A few weeks ago, I set it up in my office so that I could remotely connect to it and develop on it as needed.

One of the things I am keen to investigate about the Versal AI Edge is the implementation of image processing applications. Fortunately, the Alinx VD100 provides developers with dual MIPI camera interfaces and an LCD interface for a display.


To get started with this development, I decided that the LCD interface would be the best place to begin. The reason is that we have worked with similar interfacing before. Therefore, I wanted to demonstrate that an image could be displayed on the LCD screen using the test pattern generator.


The LCD supports a 1280 × 720 WXGA format. To transmit this data, it uses a VESA-standard LVDS interface. If you are not familiar with the VESA standard, it is one of two commonly used LCD panel interface standards, the other being JEIDA.


In a VESA implementation, the pixels and control signals—such as hsync, vsync, etc.—are serialized into four LVDS channels when supporting RGB888. To frame and enable the recovery of these pixels and control signals, a fifth LVDS channel is used to provide a clock. The clock can be aligned at the receiver against a known pattern, typically “1100011,” to extract the data from the four channels.


VESA transmission, therefore, synchronizes seven bits on each channel during one clock period.



To get started with this development, we will first obtain an IP block from the Alinx Git repository that provides the formatting for the VESA stream. The LCD_LVDS IP block can be downloaded from here.


Once the IP block is downloaded and added to the Vivado IP repository, we will then create a block design that includes the following:


  1. CIPS - Configured for the Alinx V100 SoM

  2. NOC – Configured to provide a DDRMC and two MAXI outputs

  3. Video Test Pattern Generator – With its AXI Lite interface connected to the NOC.

  4. Video Timing Generator – With its AXI Lite Interface connected to the NOC

  5. AXI4 Stream to Video out – Connected to the Test Pattern generator and Timing Generator this will generate parallel video.

  6. LCD to LVDS IP Block – This will convert the parallel video into LVD format

  7. Advanced IO Wizard – This will provide the serialisation of the clock and four data channels.


The final design (available on my GitHub) is shown below.


This allows us to control the test pattern using the processors in the CIPS, enabling us to verify that the color channels are correct.


The CIPS code to set up and drive the display is straightforward and is shown below.

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xvtc.h"
#include "xparameters.h"
#include "xv_tpg.h"
#include "xvidc.h"
#include"vga.h"

XV_tpg      tpg;
XVtc	    VtcInst;
VideoMode   video;
XVtc_Config *vtc_config ;

int main()
{
    XVtc_SourceSelect SourceSelect;
    XVtc_Timing vtcTiming;
    u32 height,width,status;
    init_platform();

    print("Setting up Timing\n\r");
    vtc_config = XVtc_LookupConfig(XPAR_XVTC_0_BASEADDR);        
    XVtc_CfgInitialize(&VtcInst,vtc_config ,XPAR_XVTC_0_BASEADDR);
    
    print("Setting up Video\n\r");
    video = VMODE_1280x720 ;
	vtcTiming.HActiveVideo = video.width;	
	vtcTiming.HFrontPorch = video.hps - video.width;	
	vtcTiming.HSyncWidth = video.hpe - video.hps;		
	vtcTiming.HBackPorch = video.hmax - video.hpe + 1;	
	vtcTiming.HSyncPolarity = video.hpol;	
	vtcTiming.VActiveVideo = video.height;	
	vtcTiming.V0FrontPorch = video.vps - video.height;	
	vtcTiming.V0SyncWidth = video.vpe - video.vps;	
	vtcTiming.V0BackPorch = video.vmax - video.vpe + 1;;	
	vtcTiming.V1FrontPorch = video.vps - video.height;	
	vtcTiming.V1SyncWidth = video.vpe - video.vps;	
	vtcTiming.V1BackPorch = video.vmax - video.vpe + 1;
	vtcTiming.VSyncPolarity = video.vpol;	
	vtcTiming.Interlaced = 0;

    print("Setting up TPG\n\r");
        
    	XV_tpg_Initialize(&tpg,XPAR_XV_TPG_0_BASEADDR );
    	status = XV_tpg_IsIdle(&tpg);
    	XV_tpg_Set_height(&tpg, (u32) video.height);
	XV_tpg_Set_width(&tpg, (u32) video.width);
	height = XV_tpg_Get_height(&tpg);
	width = XV_tpg_Get_width(&tpg);
	XV_tpg_Set_colorFormat(&tpg,XVIDC_CSF_RGB);
    	XV_tpg_Set_bckgndId(&tpg,XTPG_BKGND_TARTAN_COLOR_BARS);
	XV_tpg_Set_maskId(&tpg, 0x0);
	XV_tpg_Set_motionSpeed(&tpg, 0x4);
    	XV_tpg_EnableAutoRestart(&tpg);
	XV_tpg_Start(&tpg);
    
    print("Setting up Source\n\r");

    memset((void *)&SourceSelect, 0, sizeof(XVtc_SourceSelect));
	SourceSelect.VBlankPolSrc = 1;
	SourceSelect.VSyncPolSrc = 1;
	SourceSelect.HBlankPolSrc = 1;
	SourceSelect.HSyncPolSrc = 1;
	SourceSelect.ActiveVideoPolSrc = 1;
	SourceSelect.ActiveChromaPolSrc= 1;
	SourceSelect.VChromaSrc = 1;
	SourceSelect.VActiveSrc = 1;
	SourceSelect.VBackPorchSrc = 1;
	SourceSelect.VSyncSrc = 1;
	SourceSelect.VFrontPorchSrc = 1;
	SourceSelect.VTotalSrc = 1;
	SourceSelect.HActiveSrc = 1;
	SourceSelect.HBackPorchSrc = 1;
	SourceSelect.HSyncSrc = 1;
	SourceSelect.HFrontPorchSrc = 1;
	SourceSelect.HTotalSrc = 1;

    print("Run Timing Gen\n\r");    
	
	XVtc_SetGeneratorTiming(&VtcInst, &vtcTiming);
	XVtc_SetSource(&VtcInst, &SourceSelect);
	XVtc_EnableGenerator(&VtcInst);
    XVtc_RegUpdateEnable(&VtcInst);
	XVtc_Enable(&VtcInst);

    while(1){

    };

    cleanup_platform();
    return 0;
}

 

When this is run, the images displayed on the LCD confirm that everything is working as expected.

 


 

The most interesting aspect of this design is how serialization functions. How do we achieve 7-bit serialization using the Advanced IO Wizard, which typically operates with 8-bit serialization? This is an important concept, so let’s explore it further.


Apart from the custom top-level module that arranges the pixel format, the code within the LCD to LVDS display comes from an AMD (formerly Xilinx) application note. The main file is called tx_piso_7to1. Despite its name, this file does not directly perform 7:1 serialization. Instead, it uses a gearbox to first convert 7 bits to 4 bits, and this 4-bit output is then serialized using the Advanced IO Wizard with a 4-bit serialization factor.


We will revisit the Advanced IO Wizard separately, as I find it to be a very interesting module with capabilities that we need to understand. I also believe we could optimize this design, as the Advanced IO Wizard supports a range of serialization factors, including 7. By leveraging this, we could simplify the LCD_LVDS interface, directly creating the parallel stream and eliminating the need for the gearbox. I’ll add this to my list of things to investigate.


For now, we have successfully implemented the output stage of the image processing pipeline, so we can move on to exploring the front end with MIPI.


Workshops and Webinars


If you enjoyed the blog why not take a look at the free webinars, workshops and training courses we have created over the years. Highlights include



Boards


Get an Adiuvo development board



Embedded System Book   


Do you want to know more about designing embedded systems from scratch? Check out our book on creating embedded systems. This book will walk you through all the stages of requirements, architecture, component selection, schematics, layout, and FPGA / software design. We designed and manufactured the board at the heart of the book! The schematics and layout are available in Altium here   Learn more about the board (see previous blogs on Bring up, DDR validation, USB, Sensors) and view the schematics here.



Comments


bottom of page