HDMI display experiment of zynq 7000

I have used the zynq 7000 for many years, but I haven't done any HDMI display experiments. I finally did this experiment a few days ago, and I will make a summary.

My experiment was done under Microphase's z7-lite according to their tutorial. The platform is windows 10, Vivado 2018.3. If the hardware design is different, it is mainly to replace the rgb2dvi module and the gpio interrupt part.

Engineering data download,

Link: https://pan.baidu.com/s/11-RLOYtl1AyxcQ_XGbw2YQ 
extraction code: zvnc 

There are three files in this download, one hdmi_out is 67M, and the other two are included. If it's just ip, you can download hmdiip (375k), if it's just the source code hdmisrc (15M), it's mainly graphics data occupies space .
If you have micro-phase information, it is z7_Lite\03_SDK_Demo\17_hdmi_out

IP preparation

This experiment uses 2 IPs, which can be  downloaded from  https://github.com/Digilent/vivado-library . But what I used in my experiment is rgb2dvi_v1_2 provided by Microphase. This ip is in the ip_repo directory of the project.

There are many custom IPs provided by Digilent in this link, but only two are used in this experiment. Among them, axi_dynclk is the clock generation module, which automatically generates the corresponding pixel clock and serial clock according to different screen resolutions. The rgb2dvi module converts the red, green and blue signals of the image into MDS signals and sends them to the HDMI port. The online version is relatively new. I wanted to use the new version for testing, but when the SDK reported an error, I had to use the version provided by Microphase. The IP file directory is shown in the figure below:


Pay special attention here: The if file directory should also be copied, that is, the if directory, which contains the tmds directory. axi_dynclk, rgb2dvi in ​​the ip directory. The content in the picture is copied to your ip_repo, which is the directory where the IP is centralized, and other IPs are placed in my directory.

At the beginning, I used to copy only the directory under ip, but it couldn't pass.

hardware design

Create a new project under Vivado and name it hdmi_out. The whole hardware design is more complicated, I divide it into several parts:

Add ip directory, add and configure zynq, add VDMA IP, AXI-Stream Subset Converter module, AXI4-Stream to Video Out module, Video Timing Controller module, rgb2dvi module, interrupt merge.

The flow of image data is: Zynq's DDR, VDMA reading, AXI-Stream Subset Converter conversion, AXI4-Stream to Video Out module, rgb2dvi module output.

When you run Automation Connect, generally only check the IP introduced at the time. If you check it more, if it conflicts with the specified operation later, you can select the pin, right-click, and Disconnect Pin to disconnect it.

Add ip directory 

Click the Project Settings option under Project Manager on the left side of the main window to add these two custom IPs to the project. In fact, add the IP storage directory as shown in the figure below:


You can see the IP we added in Project Manager -> IP Catalog. 

Schematic add and configure zynq

Create a new schematic, add zynq, the schematic looks like this:

 Double-click zynq to set:

Click the PS-PL Configuration option, and check S AXI HP0 interface under HP Slave AXI interface. In this experiment, you will use this port to obtain the image data stored in the DDR.


Enable UART0, which is related to the development board, z7_lite corresponds to 14, 15.

Click the Clock Configuration option, in the PL Fabric Clocks window, check FCLK_CLK0 and FCLK_CLK1, and set them to 100 and 140MHz respectively. Among them, FCLK_CLK0 will be used as the clock for Zynq to configure each module, and FCLK_CLK1 will be used as the clock for the image data stream,


Then click DDR Configuration, and select the DDR model consistent with the development board. z7_lite is MT41K256M16 RE-125, 16 Bit

Then click the Interrupt option to activate the IRQ_F2P[15:0] option, click OK, and the configuration is complete.


Manually connect FCLK_CLK0 to MAXI_GPO_ACLK, connect FCLK_CLK1 to S_AXI_HP0_ACLK,

Check and compare whether the schematic diagrams are consistent.

Add VDMA IP 

As shown below:

The VDMA has been added as shown in the figure below:
 


 

Double-click the above picture axi_vdma_0 to configure the parameters. First, with the Basic option, the Address Width is set to 32 bits, and the addressing space can reach 4GB. Set Frame Buffers to 1, cancel the Enable WriteChannel option, because in this experiment VDMA only reads image data from DDR. Under Enable Read Channel, change Line Buffer Depth to 4096, and other defaults, as shown in the figure below:

Click the Advanced tab again, change GenLock Mode to Master, and click OK to complete the configuration.

The schematic diagram now looks like the following. Click Run Connection Automation to automatically generate the AXI interconnect bus and system reset circuit. In the pop-up dialog box, check All Automation, and click OK.

Then manually connect the two clocks m_axis_mm2s_aclk and m_axi_mm2s_aclk together.

AXI-Stream Subset Converter module

Add AXI-Stream Subset Converter module
 

After the addition is complete, as shown in the figure below:

Double-click axis_subset_converter_0 in the above figure, and configure as follows.

The input of TDATA is 32-bit, which is 4-Byte, and the output is 24-bit, which is 3-Byte.

Modify TKEEP and TLAST to yes, and set USERWidth to 1,

Here I want to focus on the TDATA Remap String item, because the storage mode of the image to be displayed in the DDR is the 24-bit default mode B, G, R, respectively [7:0], [15:8], [23: 16] composes 24-bit, and the pixel order of rgb2dvi used in this example to generate TMDS signal module is R, B, G, so you need to adjust the order in TDATA Remap String. The adjusted string is: tdata[23:16],tdata[7:0],tdata[15:8]

The configuration is shown in the figure below:

Configured, click OK.

Manually connect the S_AXIS port of axis_subset_converter_0 to the M_AXIS_MM2S port of axi_vdma_0.

And connect its aclk to the FCLK_CLK1 clock network. As shown below:

Add a constant output module Constant, pull up the reset port aresetn of axis_subset_convert_0, so that it is always in working state,

Double-click the newly added xlconstant_0, keep the default 1bit width output high level 1 configuration, click OK,

Connect the dout port of xlconstant_0 to the aresetn port of axis_subset_converter_0,
 

AXI4-Stream to Video Out module

Add the AXI4-Stream to Video Out module. This example uses this module to convert the AXI4-Stream read by VDMA from DDR into RGB image data.

Double-click v_axi4s_vid_out_0 to configure.

Change the FIFO Depth to 4096,

Clock Mode is set to Independent,

Set Timing Mode to Master and click OK.

Manually connect the video_in port of v_axi4s_vid_out_0 to the M_AXIS port of axis_subset_converter_0,

Then connect its aclk to the FCLK_CLK1 clock network, as shown in the figure below,
 

Video Timing Controller module

Add Video Timing Controller module. This example uses this module to generate timing control signals at different resolutions.

Double-click v_tc_0 to configure. Uncheck the Enable Detection option, keep the others as default, click OK,

Click Run Connection Automation in the Diagram window to automatically generate related connections for the v_tc_0 module.

Check All Automation and click "OK".
 

Connect the vtiming_out port of v_tc_0 to the vtiming_in port of v_axi4s_vid_out_0,

Connect the gen_clken port of v_tc_0 to the vtg_ce port of v_axi4s_vid_out_0,

rgb2dvi module

Add the rgb2dvi module. This module converts RGB signal to DVI signal, namely TMDS format.

Double-click rgb2dvi_0 to configure. Uncheck Reset active high here

和 Generate SerialClk internally from pixel clock,

Select the option of signal frequency setting <120MHz (720p).

Connect the vid_io_out port of v_axi4s_vid_out_0 to the rgb2dvi_0 port,

Right-click the TMDS port of rgb2dvi_0, select Make External to generate external pins,
 

To add a clock generation module, click "+" in the upper toolbar, enter dynclk in the search box, and press Enter to add a module.


This module uses the default parameters, click Run Connection Automation

Connect the pixel clock PXL_CLK_O port of axi_dynclk_0 to the PixelClk port of rgb2dvi_0,

And connect its serial clock PXL_CLK_5X_O port to the SerialCLk port of rgb2dvi_0,

Then connect the LOCKED_O port of axi_dynclk_0 to the aRst_n port of rgb2dvi_0,

In this way, when the phase-locked loop is locked, that is, when the clock signal is output stably, the rgb2dvi module starts to work.

I didn't take a screenshot to do this, so the following figure also contains some connections for the next step.

Connect the clk port of v_tc_0 to the PXL_CLK_O port of axi_dynclk_0. Similarly,
connect the vid_io_out_clk of v_axi4s_vid_out_0 to the PXL_CLK_O network,

Break merge

Add GPIO module, used as HDMI hot plug detection signal HPD

Double-click axi_gpio_0 to configure, set it as a 1-bit input, enable Interrupt, click OK,
 

Change the port name to "HDMI_HPD",
 

Add a concat module to collect all interrupt signals, and then connect to the interrupt input port of the Zynq processor.
 

Double-click xlconcat_0 to configure. In this example, there are 3 interrupt signals, set the number of ports to 3, click OK, as shown in the figure below:
 

Connect the interrupt port ip2intc_irpt of axi_gpio_0, the interrupt port mm2s_introut of axi_vdma_0, and the interrupt port irq of v_tc_0 to In0, In1, In2 of xlconcat_0, and the output port dout of xlconcat_0 to IRQ_F_FP

Because the picture is relatively large, the feet may be far away, you can also choose one of the ports to be connected, such as the interrupt port mm2s_introut of axi_vdma_0, and then right-click and select Make Connetion, the possible connection appears, and then make a selection. In the figure below, select xlconc_0 In1. Look at the picture below:

It looks like this after connection,

The hardware design is completed. If there is a Run Block Automation prompt, click Run Block Automation, select all signal ports in the pop-up dialog box, and click "OK".
 

Click Regenerate Layout to generate the standard layout as shown in the figure below, and then click Validate Design to verify the design:
 

After the verification is successful, click OK in the pop-up window and Ctrl+S to save the design.

The following error occurred during my verification:

[BD 41-1343] Reset pin /v_tc_0/resetn (associated clock /v_tc_0/clk) is connected to reset source /rst_ps7_0_100M/peripheral_aresetn (synchronous to clock source /processing_system7_0/FCLK_CLK0).
This may prevent design from meeting timing. Please add Processor System Reset module to create a reset that is synchronous to the associated clock source /axi_dynclk_0/PXL_CLK_O.

Let me compare it, click pin /v_tc_0/resetn, right click Disconnect Pin, and verify again.

Come to the Source window, generate the design code and top-level file, right-click hdmi_out and select Generate Output Producs, click Generate in the pop-up window, and click OK. Right-click hdmi_out and select Create HDL Wrapper, and click OK in the pop-up window.

Add pin constraints, here you can directly add a constraint file, and then copy the following content.

set_property IOSTANDARD LVCMOS33 [get_ports {HDMI_HPD_tri_i[0]}]
set_property PACKAGE_PIN P19 [get_ports {HDMI_HPD_tri_i[0]}]
set_property PACKAGE_PIN U18 [get_ports TMDS_0_clk_p]
set_property PACKAGE_PIN V20 [get_ports {TMDS_0_data_p[0]}]
set_property PACKAGE_PIN T20 [get_ports {TMDS_0_data_p[1]}]
set_property PACKAGE_PIN N20 [get_ports {TMDS_0_data_p[2]}]

This constraint file is very simple. Looking at the circuit diagram, all pin pairs are used. Although 4 pins are actually 8 pins.

There is a method of setting constraint files in the original text, which is good and worth learning. But copying constraint files is relatively simple.

Generate a bitstream, output the hardware (to include the bitstream), then open the SDK, and start the software design part.

software design

Click File> New> Application Project in the opened SDK software, fill in "hdmi_out" for the project name, select the Empty Application project for the project template, and click Finish.

After the creation is complete, open the data directory hdmi_out/hdmi_out.sdk/hdmi_out/src folder, copy all the files in the folder, and paste them into the same directory of the current project (note that do not overwrite the lscript.ld under this project), right click Click the project hdmi_out and
select Refresh, and then automatic compilation will start. Double-click to open display_demo.c under the src directory. This is the main SDK program.
Next, explain the code, expand the src under the project, and you can see the file structure as shown in the figure below, where the display_ctrl folder contains timing control under different video resolutions, and dynclk contains pixel clock and serial clock generation under different resolutions. .
 

Open the display_demo.c file. This is the main program of this experiment. We will explain the functions of each part in sections.

#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "display_demo.h"
#include "display_ctrl/display_ctrl.h"
#include "display_ctrl/vga_modes.h"

// Image data for each resolution
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//#include "pic_800_600.h"
//#include "pic_1280_720.h"
//#include "pic_1280_1024.h"
#include "pic_1920_1080.h"
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

/*
 * XPAR redefines
 */
#define DYNCLK_BASEADDR     XPAR_AXI_DYNCLK_0_BASEADDR
#define VGA_VDMA_ID         XPAR_AXIVDMA_0_DEVICE_ID
#define DISP_VTC_ID         XPAR_VTC_0_DEVICE_ID

/* ------------------------------------------------------------ */
/*				Global Variables								*/
/* ------------------------------------------------------------ */

/*
 * Display Driver struct
 */
DisplayCtrl dispCtrl;
XAxiVdma vdma;

/*
 * Frame buffers for video data
 */
u8 frameBuf[DISPLAY_NUM_FRAMES][DEMO_MAX_FRAME];
u8 *pFrames[DISPLAY_NUM_FRAMES];  // array of pointers to the frame buffers

/* ------------------------------------------------------------ */
/*				Procedure Definitions							*/
/* ------------------------------------------------------------ */

int main(void)
{
	int i;
	int Status;
	XAxiVdma_Config *vdmaConfig;

	/*
	 * Initialize an array of pointers to the 3 frame buffers
	 */
	for (i = 0; i < DISPLAY_NUM_FRAMES; i++)
	{
		pFrames[i] = frameBuf[i];
	}

	/*
	 * Initialize VDMA driver, get the hardware VDMA configurations
	 */
	vdmaConfig = XAxiVdma_LookupConfig(VGA_VDMA_ID);
	if (vdmaConfig == NULL)
	{
		xil_printf("No video DMA found for ID %d\r\n", VGA_VDMA_ID);
	}

	/*
	 * Use hardware VDMA configurations to initialize the driver
	 */
	Status = XAxiVdma_CfgInitialize(&vdma, vdmaConfig, vdmaConfig->BaseAddress);
	if (Status != XST_SUCCESS)
	{
		xil_printf("VDMA Configuration Initialization failed %d\r\n", Status);
	}

	/*
	 * Initialize the Display controller and start it
	 */

	// Video Mode for each resolution
	//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
	//VideoMode VMODE = VMODE_800x600;
	//VideoMode VMODE = VMODE_1280x720;
	//VideoMode VMODE = VMODE_1280x1024;
	VideoMode VMODE = VMODE_1920x1080;
	//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

	Status = DisplayInitialize(&dispCtrl, &vdma, DISP_VTC_ID, DYNCLK_BASEADDR, pFrames, DEMO_STRIDE, VMODE);
	if (Status != XST_SUCCESS)
	{
		xil_printf("Display Ctrl initialization failed during demo initialization%d\r\n", Status);
	}

	Status = DisplayStart(&dispCtrl);
	if (Status != XST_SUCCESS)
	{
		xil_printf("Couldn't start display during demo initialization%d\r\n", Status);
	}

	DemoPrintTest(dispCtrl.framePtr[dispCtrl.curFrame], dispCtrl.vMode.width, dispCtrl.vMode.height, dispCtrl.stride);

	return 0;
}

void DemoPrintTest(u8 *frame, u32 width, u32 height, u32 stride)
{
	u32 xcoi, ycoi;
	u32 linesStart = 0;
	u32 pixelIdx = 0;

	for(ycoi = 0; ycoi < height; ycoi++)
	{
		for(xcoi = 0; xcoi < (width * 4); xcoi+=4)
		{

			//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

			/*
			// 800 x 600
			frame[linesStart + xcoi    ] = Pixel_800_600[pixelIdx++];  // Blue
			frame[linesStart + xcoi + 1] = Pixel_800_600[pixelIdx++];  // Green
			frame[linesStart + xcoi + 2] = Pixel_800_600[pixelIdx++];  // Red
			*/

			/*
			// 1280 x 720
			frame[linesStart + xcoi    ] = Pixel_1280_720[pixelIdx++];
			frame[linesStart + xcoi + 1] = Pixel_1280_720[pixelIdx++];
			frame[linesStart + xcoi + 2] = Pixel_1280_720[pixelIdx++];
			*/

			/*
			// 1280 x 1024
			frame[linesStart + xcoi    ] = Pixel_1280_1024[pixelIdx++];
			frame[linesStart + xcoi + 1] = Pixel_1280_1024[pixelIdx++];
			frame[linesStart + xcoi + 2] = Pixel_1280_1024[pixelIdx++];
			*/

			// 1920 x 1080
			frame[linesStart + xcoi    ] = Pixel_1920_1080[pixelIdx++];
			frame[linesStart + xcoi + 1] = Pixel_1920_1080[pixelIdx++];
			frame[linesStart + xcoi + 2] = Pixel_1920_1080[pixelIdx++];

			//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
		}

		linesStart += stride;
	}

	/*
	 * Flush the frame buffer memory range to ensure changes are written to the
	 * actual memory, and therefore accessible by the VDMA.
	 */
	Xil_DCacheFlushRange((unsigned int) frame, DEMO_MAX_FRAME);
}

This part provides the pixel data of 4 different resolution images, which are included in the project directory in the form of header files, such as pic_800_600.h. Take 800x600 image pixels as an example, as shown in the figure below, each pixel is represented by three primary colors, a total of 800x600x3 = 1440000 data points. The blue, green, and red values ​​of the first pixel at the coordinates (0,0) in the upper left corner of the screen are 0xF1, 0xDC, 0xDB, and the pixel values ​​of other points are deduced by analogy.

The content of the pic_800_600.h file actually defines the graphic constants:

const unsigned char Pixel_800_600[1440000] = {
0XF1,0XDC,0XDB,0XF1,0XDC,0XDB,0XF3,0XDE,0XDD,0XF4,0XDF,0XDE,0XF5,0XE0,0XDF,0XF6,0XE1,0XE0,
0XF7,0XE2,0XE1,0XF7,0XE2,0XE1,0XFC,0XE7,0XE6,0XFB,0XE7,0XE6,0XFB,0XE7,0XE6,0XFC,0XE8,0XE7,

...

};

The beginning of the main program is the definition of 4 global variables

/*
 * Display Driver struct
 */
DisplayCtrl dispCtrl;
XAxiVdma vdma;

/*
 * Frame buffers for video data
 */
u8 frameBuf[DISPLAY_NUM_FRAMES][DEMO_MAX_FRAME];
u8 *pFrames[DISPLAY_NUM_FRAMES];  // array of pointers to the frame buffers

The display control structure DisplayCtrl and VDMA are instantiated (the reader can press and hold the Ctrl key while clicking the left mouse button to see what these two structures contain).

Then comes the display data definition.

main function:

First initialize pFrames (pointer to frame buffers)

 get the hardware VDMA configurations  :vdmaConfig

Initialize the driver

The display starts.

Finally, the display data is copied to the display buffer through the function DemoPrintTest, and the image is displayed

End of program

The program code is for 1600x1200. If you want to change the method, there are 3 places that need to be modified:

1,#include "pic_1920_1080.h"

2 : VideoMode VMODE = VMODE_1920x1080;

3: In the DemoPrintTest function

            // 1920 x 1080
            frame[linesStart + xcoi    ] = Pixel_1920_1080[pixelIdx++];
            frame[linesStart + xcoi + 1] = Pixel_1920_1080[pixelIdx++];
            frame[linesStart + xcoi + 2] = Pixel_1920_1080[pixelIdx++];

These 3 places have//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@

Included, it should be easy to find.

verification

After compiling, you can verify the display.

Connect the computer USB to the JTAG port of the development board, connect another USB of the computer to the UART port of the development board, make sure that the startup mode of the development board is set to JTAG, connect the HDMI port of the development board to the HDMI display, and power the development board. Connect the serial port according to the operation method of the previous experiment (in this experiment, if no error is sent, the serial port will not print information). In fact, each stage is finished, and it is better to display each stage, you can add some print.
Xilinx->program FPGA download bit stream, then Run-> Run Configuration, make the following selections, then Run

For the first time, I directly Run As ->Launch on Hardware, but the result is not displayed. It will only be displayed according to the above method. It may take a little longer to wait. I did this new experiment, Run As -> Launch on Hardware can also be displayed.

It takes a lot of time to complete this experiment, which is a bit difficult. After successful display, there is still a sense of accomplishment, do it again, and write this article.

Guess you like

Origin blog.csdn.net/leon_zeng0/article/details/113236364