The display controller consists of six core modules. This document describes the interfaces for the three you interact with: display_clocks
, display_timings
and dvi_generator
. The other three modules are used internally by dvi_generator
. You can see the modules being put to use in the demos and test benches.
The design aims to be as generic as possible but does make use of Xilinx Series 7 specific features, such as SerDes. If you want advice on adapting this design to other FPGAs, then take a look at porting.
See README for more documentation.
- Architecture
- Display Clocks (RTL) - pixel and high-speed clocks for TMDS
- Display Timings (RTL) - generates display timings, including horizontal and vertical sync
- DVI Generator (RTL) - uses
serializer_10to1
andtmds_encode_dvi
to generate a DVI signal
- Display Clocks - synthesizes the pixel and SerDes clocks, for example, 74.25 and 371.25 MHz for 720p
- Display Timings - generates the display sync signals and active pixel position
- Colour Data - the colour of a pixel, taken from a bitmap, sprite, test card etc.
- DVI Generator
- TMDS Encoder - encodes 8-bit red, green, and blue pixel data into 10-bit TMDS values
- 10:1 Serializer - converts parallel 10-bit TMDS value into serial form
- Differential Signal Output - converts TMDS data into differential form for output via two FPGA pins
- Display Clocks - synthesizes the pixel clock, for example, 40 MHz for 800x600
- Display Timings - generates the display sync signals and active pixel position
- Colour Data - the colour of a pixel, taken from a bitmap, sprite, test card etc.
- Parallel Colour Output - external hardware converts this to analogue VGA or TMDS DVI as appropriate
Timing is everything when it comes to working with screens: everything marches in step with the pixel clock. Pixel clocks are typically in the range 25-165 MHz for SD and HD resolutions; for example, 1280x720p60 uses a pixel clock of 74.25 MHz. If our pixel clock deviates by more than 0.5%, it'll be out of Vesa spec and will likely fail to display. This module generates a high-quality pixel clock using the mixed-mode clock manager (MMCM). When doing our own TMDS SerDes, we also need a 5x pixel clock, which this module also generates. (display_clocks.v)
Display parameters includes appropriate parameters for four standard pixel clocks, and you can see examples in the demos.
i_clk
- input clocki_rst
- reset (active high)
The IN_PERIOD
parameter (below) must match the frequency of the input clock.
o_clk_1x
- pixel clocko_clk_5x
- 5x clock for 10:1 DDR SerDeso_locked
- clock locked? (active high)
You shouldn't use the clocks until the locked signal is high. You can safely ignore the o_clk_5x
output if you're not doing TMDS encoding on the FPGA.
MULT_MASTER
- multiplication for both output clocksDIV_MASTER
- division for both output clocksDIV_5X
- division for 5x (SerDes) clockDIV_1X
- division for pixel clockIN_PERIOD
- period of input clock in nanoseconds (ns)
The IN_PERIOD
needs to match the input clock i_clk
frequency. For example, a 100 MHz clock has a 10 ns period, so IN_PERIOD
would be 10.
The output clocks are calculated as follows:
o_clk_1x = (i_clk * MULT_MASTER / DIV_MASTER) / DIV_1X
o_clk_5x = (i_clk * MULT_MASTER / DIV_MASTER) / DIV_5X
For example, 720p60:
74.25 MHz = (100 MHz * 37.125 / 5) / 10
371.25 MHz = (100 MHz * 37.125 / 5) / 2
The display timings module turns timing parameters into appropriately timed sync pulses and provides the current screen coordinates. Accurate timings depend on an accurate pixel clock. (display_timings.v)
i_pix_clk
- pixel clocki_rst
- reset (active high)
The pixel clock must be suitable for the timings given in the parameters (see display clocks, above).
o_hs
- horizontal synco_vs
- vertical synco_de
- display enable: high during active videoo_frame
- high for one tick at the start of each frameo_sx [15:0]
- horizontal screen position (signed)o_sy [15:0]
- vertical screen position (signed)
The current beam position is given by (o_sx,o_sy)
. o_sx
and o_sy
are signed 16-bit values.
When display enable (o_de
) is high, these values provide the active drawing pixel and are always positive. During the blanking interval, one or both of o_sx
and o_sy
will be negative, which allows you to prepare for drawing. For example, if you have a two-cycle latency to retrieve a pixel's colour you can request the data for the first pixel of a line when o_sx == -2
.
The screen coordinate diagram, above, shows a 1280x720p60 frame. At the start of the frame o_sx == -370
and o_sy == -30
. Active drawing starts at o_sx == 0
and o_sy == 0
with the final coordinates being o_sx == 1279
and o_sy == 719
. See drawing coordinates for details on all supported resolutions.
Horizontal and vertical sync may be active high or low depending on the display mode; this is controlled using the H_POL
and V_POL
parameters (below).
H_RES
- active horizontal resolution in pixelsV_RES
- active vertical resolution in linesH_FP
- horizontal front porch length in pixelsH_SYNC
- horizontal sync length in pixelsH_BP
- horizontal back porch length in pixelsV_FP
- vertical front porch length in linesV_SYNC
- vertical sync length in linesV_BP
- vertical back porch length in linesH_POL
- horizontal sync polarity (0:negative, 1:positive)V_POL
- vertical sync polarity (0:negative, 1:positive)
Display parameters includes appropriate timing parameters for four standard resolutions. You can also see examples of the parameters in the demos.
The DVI generator has many inputs but is straightforward to use. You hook it up to the display clocks and display timings then add your pixel colour data. (dvi_generator.v)
DVI generator instantiates two other modules to do the actual work: one for TMDS encoding and the other for 10:1 serialization. The TMDS encoder has a Python model to aid with development and testing.
i_pix_clk
- pixel clock (display clocks provides this)i_pix_clk_5x
- 5x pixel clock for DDR serialization (display clocks provides this)i_rst
- reset (active high)i_de
- display enable (display timings provides this)i_data_ch0 [7:0]
- 8-bit blue colour data (TMDS channel 0)i_data_ch1 [7:0]
- 8-bit green colour data (TMDS channel 1)i_data_ch2 [7:0]
- 8-bit red colour data (TMDS channel 2)i_ctrl_ch0 [1:0]
- channel 0 control data; set to{v_sync, h_sync}
from display timingsi_ctrl_ch1 [1:0]
- channel 1 control data; set to2'b00
i_ctrl_ch2 [1:0]
- channel 2 control data; set to2'b00
The output is the four TMDS encoded serial channels ready for output as differential signals:
o_tmds_ch0_serial
- channel 0 - serial TMDSo_tmds_ch1_serial
- channel 1 - serial TMDSo_tmds_ch2_serial
- channel 2 - serial TMDSo_tmds_chc_serial
- channel clock - serial TMDS
You can use these signals with OBUFDS
, for example:
OBUFDS #(.IOSTANDARD("TMDS_33"))
tmds_buf_ch0 (.I(tmds_ch0_serial), .O(hdmi_tx_p[0]), .OB(hdmi_tx_n[0]));
Where hdmi_tx_p[0]
and hdmi_tx_n[0]
are the differential output pins for TMDS channel 0.
You can see an example of this in the DVI TMDS demo.