top of page

MicroZed Chronicles: Video Frame Buffer Write

When I’ve previously created image processing blogs or applications, I have tended to use the Video Direct Memory Access (VDMA) module to read and write video streams to and from DDR memory.


In addition to the VDMA, there are also two modules -- the Video Frame Buffer Write and Video Frame Buffer Read -- that can also be used to perform the same application and are often recommended specifically for the task These modules are designed to provide high-bandwidth DMA to and from AXI Streams and were initially created for use with the MPSoC Video Codec Unit. However, they can be used across all Xilinx devices, from 7 series to Versal. Indeed, if we are going to be using PetaLinux, the Video Frame Read and Write Buffers are the structures we should be using for our applications because they integrate with Linux Video4Linux, DRM and GStreamer.


In this blog, we are going to look at the Video Frame Buffer Write IP module and create a simple design which shows the data being written into DDR memory. The source of the video stream in this reference will be the Test Pattern Generator.


The Video Frame Buffer Write IP block receives an AXIS input and outputs an AXI4 interface. The IP supports several color spaces including RGB, YUV 4:4:4, 4:2:2 and 4:2:0 in addition to luma only. To enable support of these color spaces, the IP can support up to three color planes. Depending on the color space selected, the memory will be packed in an efficient manner.


Configuration of the IP block is very simple and only needs to know the image width and height, color space format, and stride. However, the SW APIs provide both a basic bare metal driver and also a level-two API which provides an abstraction for the most used configurations.


To get started looking at how the Video Frame Buffer Write works, we need to implement within our Vivado design. This design will contain a MicroBlaze, MIG to access DDR3, TPG, and, of course, the Video Frame Buffer Write.

The Video Frame Buffer Write IP has a pretty simple configuration. It only needs the number of pixels per clock along with the maximum sizes and maximum number of pixel bits being defined. With the maximum number of bits per pixels defined, we are able to select the appropriate pixel formats (color spaces) that we wish to be able to work with in the end application. Of course, when we run this in a full deployment, we also need to make sure the rest of the image processing pipeline has the same configurations (e.g. Pixels Per Clock etc.).

With the Vivado design completed, we are able to export the XSA and create a Vitis application project based on a platform created from the XSA.


Within the software, we need to do the following:


1. Initialize, configure, and start the test pattern generator

2. Initalize the Video Frame Buffer Write

3. Configure the Video Frame Buffer Write

4. Start the Video Frame Buffer Write


We should be using the APIs defined within the header file to work with the Video Frame Buffer Write.

#include "xv_frmbufwr.h"

The initialization uses the Video Frame Buffer Write configuration definition within the Xparameters.h file to configure the instance of the VFB-W.

XV_frmbufwr_Initialize(&frmbufwr,  XPAR_V_FRMBUF_WR_0_DEVICE_ID);

Once initialized, all we need to do is leverage the API functions to set the image width, height, and color format. We must also configure the stride which is the width of a line multiplied by the number of bytes in the pixel.

XV_frmbufwr_Set_HwReg_width(&frmbufwr, 1920);
XV_frmbufwr_Set_HwReg_height(&frmbufwr, 1080);
XV_frmbufwr_Set_HwReg_stride(&frmbufwr, 4*1920);
XV_frmbufwr_Set_HwReg_video_format(&frmbufwr, XVIDC_CSF_MEM_RGB8);
XV_frmbufwr_Set_HwReg_frm_buffer_V(&frmbufwr, XVFRMBUFWR_BUFFER_BASEADDR);

We also need to define the memory locations where the image frames should be stored. Up to three can be defined for multi-planar color formats.


The complete application can be seen below.


#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xvidc.h"
#include "xv_tpg.h"
#include "xv_frmbufwr.h"
#define DDR_BASEADDR XPAR_MIG7SERIES_0_BASEADDR
#define XVFRMBUFWR_BUFFER_BASEADDR (DDR_BASEADDR + (0x1000000))

XV_frmbufwr     frmbufwr;
XV_frmbufwr_Config frmbufwr_cfg;
XV_tpg_Config *tpg_config;
XV_tpg tpg;
XVidC_VideoStream VidStream;

int main()
{
	u32 height,width,status;

	init_platform();

    print("Hello World\n\r");
    print("Successfully ran Hello World application");

    tpg_config = XV_tpg_LookupConfig(XPAR_XV_TPG_0_DEVICE_ID);
	XV_tpg_CfgInitialize(&tpg, tpg_config, tpg_config->BaseAddress);
	//XVFrmbufWr_Initialize(&frmbufwr, XPAR_V_FRMBUF_WR_0_DEVICE_ID);

	XV_frmbufwr_Initialize(&frmbufwr,  XPAR_V_FRMBUF_WR_0_DEVICE_ID);
	status = XV_tpg_IsReady(&tpg);
	printf("TPG Status %u \n\r", (unsigned int) status);

	XV_tpg_Set_height(&tpg, (u32) 1080);
	XV_tpg_Set_width(&tpg, (u32) 1920);
	height = XV_tpg_Get_height(&tpg);
	width = XV_tpg_Get_width(&tpg);
	XV_tpg_Set_colorFormat(&tpg,XVIDC_CSF_RGB);
	XV_tpg_Set_maskId(&tpg, 0x0);
	XV_tpg_Set_motionSpeed(&tpg, 0x4);

	printf("info from tpg %u %u \n\r", (unsigned int)height, (unsigned int)width);
	XV_tpg_Set_bckgndId(&tpg,XTPG_BKGND_SOLID_RED); //);

	status = XV_tpg_Get_bckgndId(&tpg);
	printf("Status %x \n\r", (unsigned int) status);
	XV_tpg_EnableAutoRestart(&tpg);
	XV_tpg_Start(&tpg);

	status = XV_tpg_IsIdle(&tpg);



	XV_frmbufwr_Set_HwReg_width(&frmbufwr, 1920);
	XV_frmbufwr_Set_HwReg_height(&frmbufwr, 1080);
	XV_frmbufwr_Set_HwReg_stride(&frmbufwr, 4*1920);
	XV_frmbufwr_Set_HwReg_video_format(&frmbufwr, XVIDC_CSF_MEM_RGB8);
	XV_frmbufwr_Set_HwReg_frm_buffer_V(&frmbufwr, XVFRMBUFWR_BUFFER_BASEADDR);
	XV_frmbufwr_EnableAutoRestart(&frmbufwr);
	XVFrmbufWr_Start(&frmbufwr);

	while(1){

	}

    cleanup_platform();
    return 0;
}

Running this on the Arty S7 board enables me to observe the DDR memory contents at the location of the frame buffer and observe the DDR contents as expected by the settings of the test pattern generator.

Next, we will look at the Video Frame Read Buffer.


1 comment

1 Comment


Wonderful ! I still have several questions:

1. Can we easily replace the Microblaze MCU with a state machine to save resources in the FPGA ?

2. If with XVIDC_CSF_MEM_RGB8 (3Bpp) we set stride to 4*1920, then what it would be with XVIDC_CSF_RGBA (4Bpp) ?

3. Clock for the TPG - what framerate will you get when clocking using UI clock for the RAM MIG ?

4. To organize double buffering, it seems we need to reprogram the VFB Write after each frame sent. Can we do this in interrupt handler (irq from VFB Write) ?

5. We declared "XVidC_VideoStream VidStream;" but never used it. What use does it have.

Like
bottom of page