【接口协议】基于 FPGA 的 HMDI 彩条显示实验

时间:2022-10-18 08:55:28

目录

HDMI 介绍

HDMI 引脚定义

TMDS 介绍

编码模块

代码实现

并转串模块

视频时序标准

传输通道顶层

顶层模块

工程搭建


HDMI 介绍

HDMI,高清晰度多媒体接口(High Definition Multimedia Interface)是标准的数字化视频/音频接口技术,可用于机顶盒、DVD播放机、个人电脑与电视机。HDMI可以同时传送音频和影音信号,能高品质地传输未经压缩的高清视频和多声道音频数据,最高数据传输速度为 50Gbps 左右。

HDMI 引脚定义

常见的 HDMI 有19 根 pin,此外还有二十多跟 pin 脚的 HDMI 接口这里也只介绍此类常见的 HDMI接口,接口序号和定义如下:

引脚

信号

引脚

信号

1

数据2+

11

时钟屏蔽

2

数据2屏蔽

12

时钟-

3

数据2-

13

CEC

4

数据1+

14

保留

5

数据1屏蔽

15

SCL

6

数据1-

16

SDA

7

数据0+

17

DDC/CEC地

8

数据0屏蔽

18

+5v电源

9

数据0-

19

热插拔检测

10

时钟+

TMDS 介绍

过渡调制差分信号,也被称为最小化传输差分信号,是指通过异或及异或非等逻辑算法将原始信号数据转换成 10 位,前 8 位数据由原始信号经运算后获得,第9位指示运算的方式,第 10 位用来对应直流平衡(DC-balanced,就是指在编码过程中保证信道中直流偏移为零,电平转化实现不同逻辑接口间的匹配),转换后的数据以差分传动方式传送。

下图为 HDMI 链路的框架图

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

TMDS 的链路如下图所示,可以看到链路主要有两部分:source 和 sink 端,source 端为传输数据的起始端,sink 端为传输数据的目的端。同时链路由四条通道组成,分别为蓝色通道、、绿色通道、红色通道和时钟通道。

蓝色通道

蓝色通道包含8位的蓝色数据通道,两位的行同步和场同步信号,以及四位的音频信号。

绿色通道

绿色通道包含8位的绿色数据通道,两位的控制信号,以及四位的音频信号。

红色通道

红色通道包含8位的红色数据通道,两位的控制信号,以及四位的音频信号。

时钟通道

为什么要导读设立一个时钟通道呢?这是为了使source端和sink端的数据保持在同一个pixel clock下,便于数据的稳定发送和接收。

在 source 端,每个通道的中还包含 Encoder 和 Serializer 模块,即编码模块和并转串模块,对应到 sink 端为解码模块和串转并模块。

在编码阶段,编码器将视频源中的像素数据、HDMI的音频/附件数据,以及行、场同步信号分别编码成10位的字符流。

在并串转换阶段将上述的10位字符流转换成串行数据流,并将其从三个差分输出通道发送出去。这个10:1的并转串过程所产生的串行速率是实际像素时钟速率的10倍。但是在并转串中采用的为DDR即双沿采样,因此时钟频率可以减小为原来的一半,也就是像素时钟的 5 倍。

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

编码模块

下图为 TMDS 的编码算法的流程示意图,每一个数据通道都通过编码算法,将 8 位的视、音频数据转换成最小化传输、直流平衡的 10 位数据。这使得数据的传输和恢复更加可靠。最小化传输差分信号是通过异或及异或非等逻辑算法将原始 8位信号数据转换成 10 位,前 8 为数据由原始信号经运算后获得,第 9 位指示运算的方式,第 10位用来对应直流平衡。这种算法可以减小传输信号过程的上冲和下冲,什么是上冲和下冲呢,顾名思义上冲就是由0转变为1,下冲就是由1转变为0,如果频繁的上下冲就会导致信号传输不稳定,而DC平衡使信号对传输线的电磁干扰减少,DC平衡指的是在码流中0和1的个数相当。总结来说,信号频繁的0、1变化和0和1数量不均衡都会影响信号的质量,编码的目的就在于此。

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

代码实现

这个编码代码可以直接调用Xilinx官网的,在例化此模块时,需要对每个通道分别进行例化。

//
//
//  Xilinx, Inc. 2008                 www.xilinx.com
//
//
//
//  File name :       encode.v
//
//  Description :     TMDS encoder  
//
//  Date - revision : Jan. 2008 - v 1.0
//
//  Author :          Bob Feng
//
//  Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are
//              provided to you "as is". Xilinx and its licensors make and you
//              receive no warranties or conditions, express, implied,
//              statutory or otherwise, and Xilinx specifically disclaims any
//              implied warranties of merchantability, non-infringement,or
//              fitness for a particular purpose. Xilinx does not warrant that
//              the functions contained in these designs will meet your
//              requirements, or that the operation of these designs will be
//              uninterrupted or error free, or that defects in the Designs
//              will be corrected. Furthermore, Xilinx does not warrantor
//              make any representations regarding use or the results of the
//              use of the designs in terms of correctness, accuracy,
//              reliability, or otherwise.
//
//              LIMITATION OF LIABILITY. In no event will Xilinx or its
//              licensors be liable for any loss of data, lost profits,cost
//              or procurement of substitute goods or services, or for any
//              special, incidental, consequential, or indirect damages
//              arising from the use or operation of the designs or
//              accompanying documentation, however caused and on any theory
//              of liability. This limitation will apply even if Xilinx
//              has been advised of the possibility of such damage. This
//              limitation shall apply not-withstanding the failure of the
//              essential purpose of any limited remedies herein.
//
//  Copyright © 2006 Xilinx, Inc.
//  All rights reserved
//
//  
`timescale 1 ps / 1ps

module encode (
  input            clkin,    // pixel clock input
  input            rstin,    // async. reset input (active high)
  input      [7:0] din,      // data inputs: expect registered
  input            c0,       // c0 input
  input            c1,       // c1 input
  input            de,       // de input
  output reg [9:0] dout      // data outputs
);

  
  // Counting number of 1s and 0s for each incoming pixel
  // component. Pipe line the result.
  // Register Data Input so it matches the pipe lined adder
  // output
  
  reg [3:0] n1d; //number of 1s in din
  reg [7:0] din_q;

  always @ (posedge clkin) begin
    n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

    din_q <= din;
  end

  ///
  // Stage 1: 8 bit -> 9 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  ///
  wire decision1;

  assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));
/*
  reg [8:0] q_m;
  always @ (posedge clkin) begin
    q_m[0] <=#1 din_q[0];
    q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
    q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
    q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
    q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
    q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
    q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
    q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
    q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1;
  end
*/
  wire [8:0] q_m;
  assign q_m[0] = din_q[0];
  assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
  assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
  assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
  assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
  assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
  assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
  assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
  assign q_m[8] = (decision1) ? 1'b0 : 1'b1;

  /
  // Stage 2: 9 bit -> 10 bit
  // Refer to DVI 1.0 Specification, page 29, Figure 3-5
  /
  reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
  always @ (posedge clkin) begin
    n1q_m  <= q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
    n0q_m  <= 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
  end

  parameter CTRLTOKEN0 = 10'b1101010100;
  parameter CTRLTOKEN1 = 10'b0010101011;
  parameter CTRLTOKEN2 = 10'b0101010100;
  parameter CTRLTOKEN3 = 10'b1010101011;

  reg [4:0] cnt; //disparity counter, MSB is the sign bit
  wire decision2, decision3;

  assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
  /
  // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
  /
  assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));

  
  // pipe line alignment
  
  reg       de_q, de_reg;
  reg       c0_q, c1_q;
  reg       c0_reg, c1_reg;
  reg [8:0] q_m_reg;

  always @ (posedge clkin) begin
    de_q    <= de;
    de_reg  <= de_q;
    
    c0_q    <= c0;
    c0_reg  <= c0_q;
    c1_q    <= c1;
    c1_reg  <= c1_q;

    q_m_reg <= q_m;
  end

  ///
  // 10-bit out
  // disparity counter
  ///
  always @ (posedge clkin or posedge rstin) begin
    if(rstin) begin
      dout <= 10'h0;
      cnt <= 5'h0;
    end else begin
      if (de_reg) begin
        if(decision2) begin
          dout[9]   <= ~q_m_reg[8]; 
          dout[8]   <= q_m_reg[8]; 
          dout[7:0] <= (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];

          cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
        end else begin
          if(decision3) begin
            dout[9]   <= 1'b1;
            dout[8]   <= q_m_reg[8];
            dout[7:0] <= ~q_m_reg[7:0];

            cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
          end else begin
            dout[9]   <= 1'b0;
            dout[8]   <= q_m_reg[8];
            dout[7:0] <= q_m_reg[7:0];

            cnt <= cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
          end
        end
      end else begin
        case ({c1_reg, c0_reg})
          2'b00:   dout <= CTRLTOKEN0;
          2'b01:   dout <= CTRLTOKEN1;
          2'b10:   dout <= CTRLTOKEN2;
          default: dout <= CTRLTOKEN3;
        endcase

        cnt <= 5'h0;
      end
    end
  end
  
endmodule

并转串模块

并串模块可以直接调用原语OSERDESE2,该原语可以将并行数据转为串行数据,支持的位宽为2-8、10、14,并且支持DDR还是SDR采样等模式配置,经过并转串原语出来的串行数据,由于TMDS的传输为差分信号,因此还需要将单端信号转变为差分信号,同样也可以借用原语实现,OBUFDS就可以实现这样的功能,将输出的单端信号转为差分信号。注意需要对每个通道进行例化。

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Author      : Linest-5
// File        : serial10_1.v
// Create      : 2022-10-04 15:41:01
// Revise      : 2022-10-04 17:07:13
// Module Name : 
// Description :
// Editor      : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

module serial10_1(
	input              divclk,   //并行时钟
	input              serclk,   //串行时钟
	input              rst,
	input  [9:0]       din,
	output             do_p,
	output             do_n
	);
reg     init_rst;
wire    cascade_di1;
wire    cascade_di2;
wire    dout;

always @(posedge divclk or posedge rst) begin
	if (rst) begin
		init_rst <= 'd1;
	end
	else begin
		init_rst <= 'd0;
	end
end

OBUFDS #(
      .IOSTANDARD("DEFAULT"), // Specify the output I/O standard
      .SLEW("SLOW")           // Specify the output slew rate
) OBUFDS_inst (
      .O(do_p),     // Diff_p output (connect directly to top-level port)
      .OB(do_n),   // Diff_n output (connect directly to top-level port)
      .I(dout)      // Buffer input
   );

OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR 控制OQ输出的buffer是输出还是三态
      .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
      .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
      .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
      .SERDES_MODE("MASTER"), // MASTER, SLAVE
      .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
      .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
OSERDESE2_inst_M (
      .OFB(),             // 1-bit output: Feedback path for data
      .OQ(dout),               // 1-bit output: Data path output
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TFB(),             // 1-bit output: 3-state control
      .TQ(),               // 1-bit output: 3-state control
      .CLK(serclk),             // 1-bit input: High speed clock
      .CLKDIV(divclk),       // 1-bit input: Divided clock
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(din[0]),
      .D2(din[1]),
      .D3(din[2]),
      .D4(din[3]),
      .D5(din[4]),
      .D6(din[5]),
      .D7(din[6]),
      .D8(din[7]),
      .OCE(1'b1),             // 1-bit input: Output data clock enable
      .RST(init_rst),             // 1-bit input: Reset
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(cascade_di1),
      .SHIFTIN2(cascade_di2),
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(0),
      .T2(0),
      .T3(0),
      .T4(0),
      .TBYTEIN(0),     // 1-bit input: Byte group tristate
      .TCE(0)              // 1-bit input: 3-state clock enable
   );

OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR 控制OQ输出的buffer是输出还是三态
      .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
      .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
      .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
      .SERDES_MODE("SLAVE"), // MASTER, SLAVE
      .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
      .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
OSERDESE2_inst_S (
      .OFB(),             // 1-bit output: Feedback path for data
      .OQ(),               // 1-bit output: Data path output
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(cascade_di1),
      .SHIFTOUT2(cascade_di2),
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TFB(),             // 1-bit output: 3-state control
      .TQ(),               // 1-bit output: 3-state control
      .CLK(serclk),             // 1-bit input: High speed clock
      .CLKDIV(divclk),       // 1-bit input: Divided clock
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(),
      .D2(),
      .D3(din[8]),
      .D4(din[9]),
      .D5(0),
      .D6(0),
      .D7(0),
      .D8(0),
      .OCE(1'b1),             // 1-bit input: Output data clock enable
      .RST(init_rst),             // 1-bit input: Reset
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(),
      .SHIFTIN2(),
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(0),
      .T2(0),
      .T3(0),
      .T4(0),
      .TBYTEIN(0),     // 1-bit input: Byte group tristate
      .TCE(0)              // 1-bit input: 3-state clock enable
   );

endmodule

视频时序标准

这部分在 VGA 中讲过,不多叙述,可以在VESA查询不同分辨率和帧数所对应的参数

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

VGA的时序驱动代码也在VGA中有,不同分辨率的参数设定都已经参数化,根据不同的情况更改参数即可。

以下为 VGA 驱动设计系列博客直达链接。

【接口协议】FPGA 驱动 VGA 显示实验(一)原理部分

【接口协议】FPGA 驱动 VGA 显示实验(二)实验设计部分

传输通道顶层

在次顶层模块对编码模块和并转串模块进行例化,需要对每个通道都进行例化一遍。

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Author      : Linest-5
// File        : hdmi_trans.v
// Create      : 2022-10-04 16:39:39
// Revise      : 2022-10-04 16:57:56
// Module Name : 
// Description :
// Editor      : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
module hdmi_trans(
	input               clk1x,
	input               clk5x,
	input               rst,
	input               locked,
	input  [7:0]        vga_r,
	input  [7:0]        vga_g,
	input  [7:0]        vga_b,
	input  	            de,
	input  	            v_sync,
	input  	            h_sync,
	output              hdmi_clk_p,
	output              hdmi_clk_n,
	output              hdmi_chan0_p,
	output              hdmi_chan0_n,
	output              hdmi_chan1_p,
	output              hdmi_chan1_n,
	output              hdmi_chan2_p,
	output              hdmi_chan2_n
	);

wire         sysrst;
wire [9:0]   do_red;
wire [9:0]   do_green;
wire [9:0]   do_blue;

assign sysrst = rst | (~locked);

encode inst_encode_red (
	.clkin (clk1x),
	.rstin (sysrst),
	.din   (vga_r),
	.c0    (1'b0),
	.c1    (1'b0),
	.de    (de),
	.dout  (do_red)
	);

encode inst_encode_green (
	.clkin (clk1x),
	.rstin (sysrst),
	.din   (vga_g),
	.c0    (1'b0),
	.c1    (1'b0),
	.de    (de),
	.dout  (do_green)
	);

encode inst_encode_blue (
	.clkin (clk1x),
	.rstin (sysrst),
	.din   (vga_b),
	.c0    (h_sync),
	.c1    (v_sync),
	.de    (de),
	.dout  (do_blue)
	);

serial10_1 inst_serial10_1_clk(
	.divclk (clk1x),
	.serclk (clk5x),
	.rst    (sysrst),
	.din    (10'b1111_0000),
	.do_p   (hdmi_clk_p),
	.do_n   (hdmi_clk_n)
);

serial10_1 inst_serial10_1_red(
	.divclk (clk1x),
	.serclk (clk5x),
	.rst    (sysrst),
	.din    (do_red),
	.do_p   (hdmi_chan2_p),
	.do_n   (hdmi_chan2_n)
);

serial10_1 inst_serial10_1_green(
	.divclk (clk1x),
	.serclk (clk5x),
	.rst    (sysrst),
	.din    (do_green),
	.do_p   (hdmi_chan1_p),
	.do_n   (hdmi_chan1_n)
);

serial10_1 inst_serial10_1_blue(
	.divclk (clk1x),
	.serclk (clk5x),
	.rst    (sysrst),
	.din    (do_blue),
	.do_p   (hdmi_chan0_p),
	.do_n   (hdmi_chan0_n)
);

endmodule

顶层模块

最后进行最顶层模块的书写,将通带次顶层模块和VGA时序模块及逆行例化,同时由于我使用的板子输入时钟为200M的差分时钟,所以还需要例化锁相环模块。

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Author      : Linest-5
// File        : hdmi_top.v
// Create      : 2022-10-04 20:54:00
// Revise      : 2022-10-04 21:16:21
// Module Name : 
// Description :
// Editor      : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns / 1ps

module hdmi_top(
	input          clk_p,
	input          clk_n,
	output         hdmi_clk_p,
	output         hdmi_clk_n,
	output         hdmi_chan0_p,
	output         hdmi_chan0_n,
	output         hdmi_chan1_p,
	output         hdmi_chan1_n,
	output         hdmi_chan2_p,
	output         hdmi_chan2_n 
    );

wire            clk1x; 
wire            clk5x;    
wire            locked;
wire  [7:0]     vga_r; 
wire  [7:0]     vga_g;
wire  [7:0]     vga_b;
wire            de;
wire            v_sync;
wire            h_sync;
  


diff2single inst_diff2single(
    // Clock out ports
    .clk_out1(clk1x),     // output clk_out1
    .clk_out2(clk5x),     // output clk_out2
    // Status and control signals
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1_p(clk_p),    // input clk_in1_p
    .clk_in1_n(clk_n));    // input clk_in1_n

hdmi_trans inst_hdmi_trans (
	.clk1x        (clk1x),
	.clk5x        (clk5x),
	.rst          (0),
	.locked       (locked),
	.vga_r        (vga_r),
	.vga_g        (vga_g),
	.vga_b        (vga_b),
	.de           (de),
	.v_sync       (v_sync),
	.h_sync       (h_sync),
	.hdmi_clk_p   (hdmi_clk_p),
	.hdmi_clk_n   (hdmi_clk_n),
	.hdmi_chan0_p (hdmi_chan0_p),
	.hdmi_chan0_n (hdmi_chan0_n),
	.hdmi_chan1_p (hdmi_chan1_p),
	.hdmi_chan1_n (hdmi_chan1_n),
	.hdmi_chan2_p (hdmi_chan2_p),
	.hdmi_chan2_n (hdmi_chan2_n)
);

VGA_TIMING inst_VGA_TIMING (
	.sclk      (clk1x),
	.rst_n     (locked),
	.po_vga_r  (vga_r),
	.po_vga_g  (vga_g),
	.po_vga_b  (vga_b),
	.po_de     (de),
	.po_v_sync (v_sync),
	.po_h_sync (h_sync)
	);




endmodule

工程搭建

在 Vivado 对工程进行搭建,将几个文件导入,并添加相应的 IP 模块即可。

以下为实验工程的模块间框图

【接口协议】基于 FPGA 的 HMDI 彩条显示实验

最后绑定管脚并生成比特流文件,烧录至FPGA中,将HDMI输出口接到带有HDMI口的显示器,就可以看到在显示屏上呈现彩条。