element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog Arty S7 - Imaging System Part 2: Building HDMI source controller
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join FPGA to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: saadtiwana_int
  • Date Created: 19 Jul 2022 3:35 PM Date Created
  • Views 3018 views
  • Likes 8 likes
  • Comments 9 comments
  • spartan 7
  • 7 Ways to Leave Your Spartan-6 FPGA
  • hdmi
  • xilinx
  • fpga
  • Imaging system
  • arty s7
  • 7 Ways to Leave Your Spartan-6 FPGA: Arty S7 Activities
  • Spartan_Migration
Related
Recommended

Arty S7 - Imaging System Part 2: Building HDMI source controller

saadtiwana_int
saadtiwana_int
19 Jul 2022
Arty S7 - Imaging System Part 2: Building HDMI source controller

In this blog post, we will add HDMI video output to our Arty S7. We will build upon the basic microblaze system that we constructed in the previous blog post.

The first thing you might be wondering is how to get a physical HDMI output from the Arty-S7, since there is no HDMI ports on the board. Well, this is one of those situations that really shows the flexibility and power of FPGAs. More on this later. Let's get the FPGA design up first. 

We start with the basic hello microblaze design that we created in the previous blog post. 

On a high-level, we will add:

  • RGB-to-DVI IP -> We will use a Digilent IP for this (we will need to edit the IP in Vivado's IP packager to add Spartan-7 as supported device family and repackage the IP)
  • Video Test Pattern Generato (TPG)r -> Xilinx makes available this IP free-of-cost
  • AXI4-Stream to Video Out IP -> To convert the TPG output from AXIStream to RGB format with Video Sync, valid signals
  • Video Timing Controller -> Xilinx IP, available free-of-cost. Generates the video timing signals (HSync, VSync, DataValid) for a certain video format
  • Clocking wizard to generate the pixel clock as well as a 5x serial clock needed for HDMI output. This will use the "ui_clk" clock output from the MIG (Memory Interface Generator) as the clock source

Open the block design from the hello microblaze project in Vivado. First of all, add Clocking a "Clocking WIzard" IP to the block design:

image

We will be using the output clock of MIG to run our subsequent design's clocks

Connect clk_in1 (input) of this Clocking Wizard block to the "ui_clk" output of MIG

Connect reset (input) of this Clocking Wizard block to the "ui_clk_sync_rst" output from the MIG.

Now, double-click the Clocking Wizard IP block to open and set outputs as following. We are setting up clocks for 640x480, 60fps output. We go for a lower resolution fist to get the whole system up before trying to push for higher performance.

image

Click OK to save and close the dialog box.

Next, we add "Video Timing Controller" IP. Once added to block design, double click to edit. 

On the first tab we un-check the "Enable Detection" option, since we will only use it to generate video timing signals. We also reduct the maximum resolution values to save some resources.

image

We dont' change the rest of settings as those we will configure over the AXI4Lite interface from microblaze code. Click OK to close.

We will run the IP from the HDMI pixel clock, so connect the "clk" input to "clk_hdmi" output of Clocking Wizard IP

image

Next we add a "Video Test Pattern Generator" IP from the IP catalog and double click it to customize it as following:

image

Click OK to close. For clocking this IP, since the output is in AXIStream, we connect "ap_clk" input to the "ui_clk" clock ouput from MIG7 IP.

Since the output of the Test Pattern Generator is in AXIStream format, we need to convert this into the native video format (pixel data, sync signals), so we add a "AXI4-Stream to Video Out" IP from the IP catalog. Customize it as following:

image

We use "Master" timing mode as we want this IP to be the clocking master for the Video Timing Controller. For that effect, also connect the "vtg_ce" output of this IP to the "gen_clken" input fo the Video Timing Controller IP added earlier.

image

To clock the  "AXI4-Stream to Video Out" IP's "aclk" input, we use the "ui_clk" output from the MIG7. This sets the clock for the AXIStream side.

And the "video_io_out_clk" input we can connect to the "clk_hdmi" of clocking wizard. This sets the clock for the Native video output from this IP.

Also, connect the Video Input and Timing inputs as following:

image

Next we add a HDMI encoder. For this, I am using the "RGB to DVI Video Encoder (Source)" IP from Digilent. I have already setup my Vivado to include the IPs from Digilent. (Let me know in comments if you are not able to do that). Also note that this IP is not enabled for Spartan7 family, so I had to edit the IP in IP packager to add Spartan7 as supported family. It's a very simple process, but I plan to write about this process in a separate blog post so that this one doesn't get too long. Please leave a comment if you need help with this.

After adding the IP, customize it as following. Since we already have a clock and 5x clock for HDMI, we don't need to generate the 5x clock internally.

image

Click OK to close.

Connect the PixelClk and Serial Clk of the HDMI IP to the "clk_hdmi" and "clk_hdmi5x" from the clocking wizard.

image

Also connect the Video Path as shown here:

image

 

Right click on the TMDS port and select "Make external" to tell Vivado we will be connecting this externally

image

Once the external port is created, select it and change the name to "hdmi_tx_0" from the property editor. This is something we don't NEED to do. But I renamed it to this since I will be using this name to assign pin constraints later.

image

At this point, we can run "connection Automation" to complete the remaining connections automatically

image

We connect the "locked" output of the clocking wizard as the reset input for the HDMI encoder. This way, only once the clock is locked, the HDMI IP will come out of reset

image

Optionally (and recommended), we can also add one or more "ILA"  or "System ILA" IPs to monitor the critical signals in the design later.

At this point, we can validate the design to make sure there are no errors.

image

Here's our final block design

image

Next, we need to tell Vivado where the external ports are connected. Note that anything that we used from the "Boards" tab will automatically have it's constraints added by Vivado and we don't need to bother with those. In this case, we added our own HDMI transmitter, so we need to tell Vivado which pins we're going to use for this. We do this by adding a "Constraints" file to our project. Vivado will read this in addition to the board's own constraint file. 

Click Sources -> Constraints -> constrs_1 (right-click) -> Add Sources

image

Add an empty Contraints file by following these steps:

image

image

Once the newly created file opens, we add the following lines to it and save it:

set_property -dict {PACKAGE_PIN M18 IOSTANDARD TMDS_33} [get_ports hdmi_tx_0_clk_p]
set_property -dict {PACKAGE_PIN M16 IOSTANDARD TMDS_33} [get_ports {hdmi_tx_0_data_p[2]}]
set_property -dict {PACKAGE_PIN M14 IOSTANDARD TMDS_33} [get_ports {hdmi_tx_0_data_p[1]}]
set_property -dict {PACKAGE_PIN L17 IOSTANDARD TMDS_33} [get_ports {hdmi_tx_0_data_p[0]}]

Basically we are assigning the Pins for the TMDS pairs for HDMI (HDMI uses TMDS signalling). For differential pairs, we only need to specify the positive pin for a pair. The negative pin is automatically assigned. I will talk more about the pin assignment in the next blog post when we add a physical HDMI port to our board.

Anyway, now we have done all that we needed to do on the Vivado side. All that's left is to generate the Bitstream, and export it.

image

Once this process completes successfuly, we can re-export the updated bitstream:

File -> Export -> Export Hardware.

Since we already exported the previous "Hello microblaze" to the same folder, it will ask if it can overwrite the previous bitstream. Answer YES.

After this, we can switch to Vitis IDE

Click Tools -> Launch VITIS IDE

Once VITIS opens, we edit our previously created helloworld.c to the following code:

/*
 * helloworld.c: simple test application
 *
 * This application configures UART to baud rate 9600. *
 */

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"

#include "xparameters.h"
#include "xvtc.h"
#include "xv_tpg.h"

#define XVTC_DEVICE_ID			XPAR_VTC_0_DEVICE_ID

XVtc	VtcInst;		/**< Instance of the VTC core. */

typedef struct {
	char label[64]; /* Label describing the resolution */
	u32 width; /*Width of the active video frame*/
	u32 height; /*Height of the active video frame*/
	u32 hps; /*Start time of Horizontal sync pulse, in pixel clocks (active width + H. front porch)*/
	u32 hpe; /*End time of Horizontal sync pulse, in pixel clocks (active width + H. front porch + H. sync width)*/
	u32 hmax; /*Total number of pixel clocks per line (active width + H. front porch + H. sync width + H. back porch) */
	u32 hpol; /*hsync pulse polarity*/
	u32 vps; /*Start time of Vertical sync pulse, in lines (active height + V. front porch)*/
	u32 vpe; /*End time of Vertical sync pulse, in lines (active height + V. front porch + V. sync width)*/
	u32 vmax; /*Total number of lines per frame (active height + V. front porch + V. sync width + V. back porch) */
	u32 vpol; /*vsync pulse polarity*/
	double freq; /*Pixel Clock frequency*/
} VideoMode;

static const VideoMode VMODE_640x480 =
{
	.label = "640x480@60Hz",
	.width = 640,
	.height = 480,
	.hps = 656,//640+16
	.hpe = 752,//
	.hmax = 799,
	.hpol = 0,
	.vps = 490,
	.vpe = 492,
	.vmax = 524,
	.vpol = 0,
	.freq = 25.0
};


VideoMode vMode;


int configureStartVTC()
{
	int Status;
	XVtc_Config *Config;
	XVtc_Signal *SignalCfgPtr;

	XVtc_Timing vtcTiming;
	XVtc_SourceSelect SourceSelect;



	/* Initialize the VTC driver so that it's ready to use look up
	 * configuration in the config table, then initialize it.
	 */
	Config = XVtc_LookupConfig(XVTC_DEVICE_ID);

	/* Checking Config variable */
	if (NULL == Config) {
		return (XST_FAILURE);
	}

	Status = XVtc_CfgInitialize(&VtcInst, Config, Config->BaseAddress);

	/* Checking status */
	if (Status != (XST_SUCCESS)) {
		return (XST_FAILURE);
	}

	/* Perform a self-test  */
	Status = XVtc_SelfTest(&VtcInst);

	/* Checking status */
	if (Status != (XST_SUCCESS)) {
		return (XST_FAILURE);
	}



	vMode = VMODE_640x480;

	/*
	 * Configure the vtc core with the display mode timing parameters
	 */
	vtcTiming.HActiveVideo = vMode.width; /**< Horizontal Active Video Size */
	vtcTiming.HFrontPorch = vMode.hps - vMode.width; /**< Horizontal Front Porch Size */
	vtcTiming.HSyncWidth = vMode.hpe - vMode.hps; /**< Horizontal Sync Width */
	vtcTiming.HBackPorch = vMode.hmax - vMode.hpe + 1; /**< Horizontal Back Porch Size */
	vtcTiming.HSyncPolarity = vMode.hpol; /**< Horizontal Sync Polarity */
	vtcTiming.VActiveVideo = vMode.height; /**< Vertical Active Video Size */
	vtcTiming.V0FrontPorch = vMode.vps - vMode.height; /**< Vertical Front Porch Size */
	vtcTiming.V0SyncWidth = vMode.vpe - vMode.vps; /**< Vertical Sync Width */
	vtcTiming.V0BackPorch = vMode.vmax - vMode.vpe + 1;
	; /**< Horizontal Back Porch Size */
	vtcTiming.V1FrontPorch = vMode.vps - vMode.height; /**< Vertical Front Porch Size */
	vtcTiming.V1SyncWidth = vMode.vpe - vMode.vps; /**< Vertical Sync Width */
	vtcTiming.V1BackPorch = vMode.vmax - vMode.vpe + 1;
	; /**< Horizontal Back Porch Size */
	vtcTiming.VSyncPolarity = vMode.vpol; /**< Vertical Sync Polarity */
	vtcTiming.Interlaced = 0; /**< Interlaced / Progressive video */

	/* Setup the VTC Source Select config structure. */
	/* 1=Generator registers are source */
	/* 0=Detector registers are source */
	memset((void *) &SourceSelect, 0, sizeof(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;

	Status = XVtc_SelfTest(&VtcInst);

	XVtc_RegUpdateEnable(&VtcInst);
	XVtc_SetGeneratorTiming(&VtcInst, &vtcTiming);
	XVtc_SetSource(&VtcInst, &SourceSelect);
	
	/*
	 * Enable VTC core
	 */
	XVtc_EnableGenerator(&VtcInst);




	return (XST_SUCCESS);
}



int configureStartTPG()
{
	int Status;

	XV_tpg * tpgInst;
	XV_tpg_Config *tpg_Config;

	tpg_Config = XV_tpg_LookupConfig(XPAR_V_TPG_0_DEVICE_ID);
	if (tpg_Config == NULL)
	{
		xil_printf("ERR:: TPG device not found\r\n");
		return (XST_DEVICE_NOT_FOUND);
	}

	Status = XV_tpg_CfgInitialize(tpgInst, tpg_Config, tpg_Config->BaseAddress);
	if (Status != XST_SUCCESS)
	{
		xil_printf("ERR:: TPG Initialization failed %d\r\n", Status);
		return (XST_FAILURE);
	}

	// Must RESET the Tpg since it's a HLS IP (This must be done through GPIO on IP reset port)
	// RESET HERE!

	XV_tpg_Set_height(tpgInst, vMode.height);
	XV_tpg_Set_width(tpgInst, vMode.width);
	XV_tpg_Set_colorFormat(tpgInst, 0);
	XV_tpg_Set_bckgndId(tpgInst, XTPG_BKGND_COLOR_BARS);//XTPG_BKGND_RAINBOW_COLOR //XTPG_BKGND_CHECKER_BOARD //XTPG_BKGND_HV_RAMP//XTPG_BKGND_H_RAMP//XTPG_BKGND_RAINBOW_COLOR
	XV_tpg_Set_ovrlayId(tpgInst, 0);
	XV_tpg_Set_enableInput(tpgInst, 0);	// Enable (1) or disable (0) the video pass through
	XV_tpg_Set_passthruStartX(tpgInst, 0);
	XV_tpg_Set_passthruStartY(tpgInst, 0);
	XV_tpg_Set_passthruEndX(tpgInst, vMode.width);
	XV_tpg_Set_passthruEndY(tpgInst, vMode.height);
	XV_tpg_WriteReg(tpg_Config->BaseAddress, XV_TPG_CTRL_ADDR_AP_CTRL,	0x81); //start, auto-restart

	return XST_SUCCESS;
}

int main()
{
    init_platform();

    print("Hello World\n\r");

    configureStartVTC();
    configureStartTPG();

    print("Successfully ran Hello World application");

    while(1)
    {
    	;
    }
    cleanup_platform();
    return 0;
}

The main() function at the bottom of the code file gives an overview of the code. It does the following things:

1. Configure and Start Video Timing Controller at 640x480 resolution.

2. We configure and start the Video Test Pattern Generator. 

The code in the functions themselves have more details of what is done to achieve the above. Please leave a comment if anything is not clear.

Before we build the application code, we need to update the hardware specification based on the hardware we just exported (because we previously used the same project for the "hello microblaze"). We will also need to repeat this step if we generate and export the bitstream again in future.

Right-click design_1_wrapper (the Platform Project), then select "Update Hardware Specification".

image

Wait for successful completion message:

image

Now, we can rebuild the project with the newly added code. Right-click the application project and click "Build Project"

image

The project build status can be confirmed in the console output.

image

 

Now the last thing that needs to be done is to build the special PMOD-to-HDMI cable that we need, and then test it. I will address this in the next blog post.

  • Sign in to reply
  • saadtiwana_int
    saadtiwana_int over 2 years ago in reply to cstanton

    Thanks, I will try the insert -> image approach next time. Hopefully it can get fixed sometime in near future. Snip-and-paste is definitely more convenient for tutorial style posts which require a lot of screenshot images. 

    Thanks,
    Saad

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to saadtiwana_int

    cstanton posted that this is a known issue, being fixed.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • cstanton
    cstanton over 2 years ago in reply to saadtiwana_int

    Hello,
    Polite reminder that problems should be reported to Feedback and Support then it definitely gets attention.

    I'll see if I can get the development team to look into this, but you should really try using the insert->image approach to see if that behaves differently.

    Thanks!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • saadtiwana_int
    saadtiwana_int over 2 years ago in reply to jc2048

    Hi jc2048, Thanks for your detailed message. As I mentioned in my previous message, I DID try loading the pages while logged out, and from PCs/phones that I never used to login to element14, but all from within my city, over various networks (home, office, cellular 4g). 

    Let's get some help from the element14 team here. The tutorial-style blogs take quite a bit of time to write; it would be a shame if they're not useful to anyone due to images not loading.

    Hi cstanton Can you please help look into this matter of some images on my blog posts not loading for others? Will appreciate your help with this.

    Regards,
    Saad

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 2 years ago in reply to saadtiwana_int

    The images that aren't being displayed are being pulled from a different server directory to the ones that are. So it might be a permissions issue. You are the author, so when you're logged you might have permissions that the rest of us don't have. Try looking at the page whilst logged out of the site and see if the images disappear (you may need to clear your browser cache).

    The directory path includes the word 'multiple', so perhaps it's a place where multiple transferred images can be worked on and separated.

    The person to ask for help is cstanton

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube