FPGA(5)HDMI

5. HDMI

5.1 简介

HDMI:High-Definition Multimedia Interface,高清多媒体接口。

DVI(数字视频接口):只能传输视频,不能同时传输音频。

HDMI向下兼容DVI。DVI和HDMI接口协议在物理层均使用TMDS标准传输音视频数据。

5.2 TMDS

Transition Minimized Differential Signaling,最小化传输差分信号。使用两个引脚来传输一路信号,利用两个引脚间的电压差来决定传输的数据。

如上图,DVI或HDMI视频传输所使用的TMDS连接通过四个串行通道实现。左边Source是发送端,负责编码(并转串);右边Sink是接收端,负责解码(串转并)。左边三个编码器数据端输入的分别是蓝、绿、红的像素值(8位),以及2位的控制信号(其中Channel 0的控制信号是行同步和场同步,下面两个Channel的CTL用不到),音频信号本节也用不到。最下面还有用于同步的像素时钟通道。

在编码阶段,编码器将视频源中的像素数据、HDMI的音频/附加数据,以及行同步和场同步信号分别编码成10位的字符流。在并串转换阶段将上述的10位字符流转换成串行数据流,并将其从三个差分输出通道发送出去。这个10:1的并转串过程所生成的串行数据速率是实际像素时钟速率的10倍。

TMDS通过如上的编码算法,使01的数量大致相等,跳变次数在5次以内(控制信号有7次以上跳变),有助于减小传输信号过程的上冲和下冲,降低电磁干扰。

5.3 HDMI模块

DVI编码中只有VDE信号,拉高时传输有效像素字符,拉低时传输控制字符。

上图HDMI的控制信号除了VED还有ADE,即控制是否传输音频的控制信号。

类似TMDS的连接图,上图HDMI模块也是四个TMDS的通道。DDC是传输一些协议数据的,CEC是用于电气控制的,不用管。

引脚定义如下:

5.4 程序设计

5.4.1 总体架构

5.4.2 行同步和场同步

  • 行同步时序

  • 场同步时序:其中的有效数据就是很多个上面的行同步时序。

5.4.3 时序参数

参考《Proposed Monitor Timing Standard》。

5.4.4 编码和串并转换

因为使用了SerDes,时钟上升下降沿都能传输数据,因此串行时钟只需要5倍。下面的10'b11111_00000是时钟通道。

5.5 代码

5.5.1 顶层模块

module hdmi_colorbar_top (
    input        sys_clk,
    input        sys_rst_n,
    
    output       tmds_clk_p,    // TMDS 时钟通道
    output       tmds_clk_n,
    output [2:0] tmds_data_p,   // TMDS 数据通道
    output [2:0] tmds_data_n
);

    // wire define
    wire          pixel_clk;
    wire          pixel_clk_5x;
    wire          clk_locked;

    wire  [10:0]  pixel_xpos_w;
    wire  [10:0]  pixel_ypos_w;
    wire  [23:0]  pixel_data_w;

    wire          video_hs;
    wire          video_vs;
    wire          video_de;
    wire  [23:0]  video_rgb;


    // 例化MMCM/PLL IP核
    clk_wiz_0  clk_wiz_0 (
        .clk_in1        (sys_clk),
        .clk_out1       (pixel_clk),        // 像素时钟(时序参数中的时钟)
        .clk_out2       (pixel_clk_5x),     // 5倍像素时钟
        
        .reset          (~sys_rst_n), 
        .locked         (clk_locked)
    );

    // 例化视频显示驱动模块
    video_driver u_video_driver (
        .pixel_clk      (pixel_clk),
        .sys_rst_n      (sys_rst_n),

        .video_hs       (video_hs),
        .video_vs       (video_vs),
        .video_de       (video_de),
        .video_rgb      (video_rgb),

        .pixel_xpos     (pixel_xpos_w),
        .pixel_ypos     (pixel_ypos_w),
        .pixel_data     (pixel_data_w)
    );

    // 例化视频显示模块
    video_display  u_video_display (
        .pixel_clk      (pixel_clk),
        .sys_rst_n      (sys_rst_n),

        .pixel_xpos     (pixel_xpos_w),
        .pixel_ypos     (pixel_ypos_w),
        .pixel_data     (pixel_data_w)
    );

    // 例化HDMI驱动模块
    dvi_transmitter_top u_rgb2dvi_0 (
        .pclk           (pixel_clk),
        .pclk_x5        (pixel_clk_5x),
        .reset_n        (sys_rst_n & clk_locked),
                    
        .video_din      (video_rgb),
        .video_hsync    (video_hs), 
        .video_vsync    (video_vs),
        .video_de       (video_de),
                    
        .tmds_clk_p     (tmds_clk_p),
        .tmds_clk_n     (tmds_clk_n),
        .tmds_data_p    (tmds_data_p),
        .tmds_data_n    (tmds_data_n), 
        .tmds_oen       ()                        //预留的端口,本次实验未用到
    );

endmodule

MMCM需要根据时序参数设置(如1280*720,60Hz):

5.5.2 视频显示模块

在该模块中绘制图像。输入像素坐标,输出该点像素值。

module video_display (
    input                pixel_clk,
    input                sys_rst_n,
    
    input        [10:0]  pixel_xpos,  // 像素点横坐标
    input        [10:0]  pixel_ypos,  // 像素点纵坐标
    output  reg  [23:0]  pixel_data   // 像素点数据
);

    // parameter define
    parameter  H_DISP = 11'd1280;                       // 行分辨率
    parameter  V_DISP = 11'd720;                        // 列分辨率

    localparam WHITE  = 24'b11111111_11111111_11111111;  // RGB 白色
    localparam BLACK  = 24'b00000000_00000000_00000000;  // RGB 黑色
    localparam RED    = 24'b11111111_00001100_00000000;  // RGB 红色
    localparam GREEN  = 24'b00000000_11111111_00000000;  // RGB 绿色
    localparam BLUE   = 24'b00000000_00000000_11111111;  // RGB 蓝色


    // 根据当前像素点坐标指定当前像素点颜色数据,在屏幕上显示彩条
    always @(posedge pixel_clk) begin
        if (!sys_rst_n)
            pixel_data <= 16'd0;
        else begin
            if((pixel_xpos >= 0) && (pixel_xpos < (H_DISP/5)*1))
                pixel_data <= WHITE;
            else if((pixel_xpos >= (H_DISP/5)*1) && (pixel_xpos < (H_DISP/5)*2))
                pixel_data <= BLACK;  
            else if((pixel_xpos >= (H_DISP/5)*2) && (pixel_xpos < (H_DISP/5)*3))
                pixel_data <= RED;  
            else if((pixel_xpos >= (H_DISP/5)*3) && (pixel_xpos < (H_DISP/5)*4))
                pixel_data <= GREEN;
            else 
                pixel_data <= BLUE;
        end
    end

endmodule

5.5.3 视频显示驱动模块

按照时序参数,产生有效数据(来自视频显示模块)和控制信号。

module video_driver (
    input           pixel_clk,
    input           sys_rst_n,
    
    //RGB接口
    output          video_hs,     // 行同步信号
    output          video_vs,     // 场同步信号
    output          video_de,     // 数据使能
    output  [23:0]  video_rgb,    // RGB颜色数据
    
    input   [23:0]  pixel_data,   // 像素点数据
    output  [10:0]  pixel_xpos,   // 像素点横坐标
    output  [10:0]  pixel_ypos    // 像素点纵坐标
);

    // parameter define

    // 1280*720 分辨率时序参数
    parameter  H_SYNC   =  11'd40;   // 行同步
    parameter  H_BACK   =  11'd220;  // 行显示后沿
    parameter  H_DISP   =  11'd1280; // 行有效数据
    parameter  H_FRONT  =  11'd110;  // 行显示前沿
    parameter  H_TOTAL  =  11'd1650; // 行扫描周期

    parameter  V_SYNC   =  11'd5;    // 场同步
    parameter  V_BACK   =  11'd20;   // 场显示后沿
    parameter  V_DISP   =  11'd720;  // 场有效数据
    parameter  V_FRONT  =  11'd5;    // 场显示前沿
    parameter  V_TOTAL  =  11'd750;  // 场扫描周期

    // reg define
    reg  [10:0] cnt_h;
    reg  [10:0] cnt_v;

    // wire define
    wire       video_en;
    wire       data_req;


    // 行同步、场同步信号赋值(对应时序图的同步阶段)
    assign video_hs  = (cnt_h < H_SYNC) ? 1'b0 : 1'b1;
    assign video_vs  = (cnt_v < V_SYNC) ? 1'b0 : 1'b1;

    // 使能RGB数据输出(行和场计数属于有效数据时拉高)
    assign video_en  = (((cnt_h >= H_SYNC + H_BACK) && (cnt_h < H_SYNC + H_BACK + H_DISP)) &&
                        ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0;
    assign video_de  = video_en;

    // RGB数据输出
    assign video_rgb = video_en ? pixel_data : 24'd0;

    // 请求像素点颜色数据输入
    assign data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1) && (cnt_h < H_SYNC + H_BACK + H_DISP - 1'b1)) &&  // 提前一拍获取数据
                    ((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_DISP))) ? 1'b1 : 1'b0;

    // 像素点坐标(提前一拍)
    assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 11'd0;
    assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 11'd0;

    // 行计数器对像素时钟计数
    always @(posedge pixel_clk) begin
        if (!sys_rst_n)
            cnt_h <= 11'd0;
        else begin
            if (cnt_h < H_TOTAL - 1'b1)
                cnt_h <= cnt_h + 1'b1;
            else 
                cnt_h <= 11'd0;
        end
    end

    // 场计数器对行计数
    always @(posedge pixel_clk) begin
        if (!sys_rst_n)
            cnt_v <= 11'd0;
        else if (cnt_h == H_TOTAL - 1'b1) begin
            if (cnt_v < V_TOTAL - 1'b1)
                cnt_v <= cnt_v + 1'b1;
            else 
                cnt_v <= 11'd0;
        end
    end

endmodule

5.5.4 HDMI驱动模块顶层

  • 输入是视频显示驱动模块的输出,8bit的红绿蓝有效像素数据将按照TMDS编码算法编码为10bit,控制信号也会编码为10bit。
  • 然后对10bit数据进行串并转换。
  • 最后差分输出。
module dvi_transmitter_top (
    input        pclk,           // pixel clock
    input        pclk_x5,        // pixel clock x5
    input        reset_n,        // reset
    
    input [23:0] video_din,      // RGB video in
    input        video_hsync,    // hsync data
    input        video_vsync,    // vsync data
    input        video_de,       // data enable
    
    output       tmds_clk_p,    // TMDS 时钟通道
    output       tmds_clk_n,
    output [2:0] tmds_data_p,   // TMDS 数据通道
    output [2:0] tmds_data_n,
    output       tmds_oen       // TMDS 输出使能
);
    
    // wire define    
    wire        reset;
        
    //并行数据
    wire [9:0]  red_10bit;
    wire [9:0]  green_10bit;
    wire [9:0]  blue_10bit;
    wire [9:0]  clk_10bit;  
    
    //串行数据
    wire [2:0]  tmds_data_serial;
    wire        tmds_clk_serial;


    assign tmds_oen = 1'b1;  
    assign clk_10bit = 10'b1111100000;

    // 异步复位,同步释放
    asyn_rst_syn reset_syn (
        .reset_n    (reset_n),
        .clk        (pclk),
        
        .syn_reset  (reset)    // 高有效
    );
    
    //对三个颜色通道进行编码
    dvi_encoder encoder_b (
        .clkin      (pclk),
        .rstin	    (reset),
        
        .din        (video_din[7:0]),
        .c0			(video_hsync),
        .c1			(video_vsync),
        .de			(video_de),
        .dout		(blue_10bit)
    );

    dvi_encoder encoder_g (
        .clkin      (pclk),
        .rstin	    (reset),
        
        .din		(video_din[15:8]),
        .c0			(1'b0),
        .c1			(1'b0),
        .de			(video_de),
        .dout		(green_10bit)
    );
        
    dvi_encoder encoder_r (
        .clkin      (pclk),
        .rstin	    (reset),
        
        .din		(video_din[23:16]),
        .c0			(1'b0),
        .c1			(1'b0),
        .de			(video_de),
        .dout		(red_10bit)
    );
        
    // 对编码后的数据进行并串转换
    serializer_10_to_1 serializer_b (
        .reset              (reset),                // 复位,高有效
        .paralell_clk       (pclk),                 // 输入并行数据时钟
        .serial_clk_5x      (pclk_x5),              // 输入串行数据时钟
        .paralell_data      (blue_10bit),           // 输入并行数据

        .serial_data_out    (tmds_data_serial[0])   // 输出串行数据
    );    
        
    serializer_10_to_1 serializer_g (
        .reset              (reset),
        .paralell_clk       (pclk),
        .serial_clk_5x      (pclk_x5),
        .paralell_data      (green_10bit),

        .serial_data_out    (tmds_data_serial[1])
    );
        
    serializer_10_to_1 serializer_r (
        .reset              (reset),
        .paralell_clk       (pclk),
        .serial_clk_5x      (pclk_x5),
        .paralell_data      (red_10bit),

        .serial_data_out    (tmds_data_serial[2])
    );
                
    serializer_10_to_1 serializer_clk (
        .reset              (reset),
        .paralell_clk       (pclk),
        .serial_clk_5x      (pclk_x5),
        .paralell_data      (clk_10bit),

        .serial_data_out    (tmds_clk_serial)
    );
        
    // 转换差分信号  
    OBUFDS #(
        .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
    ) TMDS0 (
        .I                  (tmds_data_serial[0]),
        .O                  (tmds_data_p[0]),
        .OB                 (tmds_data_n[0]) 
    );

    OBUFDS #(
        .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
    ) TMDS1 (
        .I                  (tmds_data_serial[1]),
        .O                  (tmds_data_p[1]),
        .OB                 (tmds_data_n[1]) 
    );

    OBUFDS #(
        .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
    ) TMDS2 (
        .I                  (tmds_data_serial[2]), 
        .O                  (tmds_data_p[2]), 
        .OB                 (tmds_data_n[2])  
    );

    OBUFDS #(
        .IOSTANDARD         ("TMDS_33")    // I/O电平标准为TMDS
    ) TMDS3 (
        .I                  (tmds_clk_serial), 
        .O                  (tmds_clk_p),
        .OB                 (tmds_clk_n) 
    );
  
endmodule

5.5.5 TMDS编码模块

3周期流水线编码。

//////////////////////////////////////////////////////////////////////////////
//
//  Xilinx, Inc. 2008                 www.xilinx.com
//
//////////////////////////////////////////////////////////////////////////////
//
//  File name :       dvi_encoder.v
//  Description :     TMDS encoder  
//  Date - revision : Jan. 2008 - v 1.0
//  Author :          Bob Feng
//  Copyright 2006 Xilinx, Inc.
//  All rights reserved
//
//////////////////////////////////////////////////////////////////////////////  
`timescale 1 ps / 1ps

module dvi_encoder (
    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 <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];

        din_q <=#1 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));

    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  <=#1 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  <=#1 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    <=#1 de;
        de_reg  <=#1 de_q;
        
        c0_q    <=#1 c0;
        c0_reg  <=#1 c0_q;
        c1_q    <=#1 c1;
        c1_reg  <=#1 c1_q;

        q_m_reg <=#1 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]   <=#1 ~q_m_reg[8]; 
            dout[8]   <=#1 q_m_reg[8]; 
            dout[7:0] <=#1 (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 1'b1;
                dout[8]   <=#1 q_m_reg[8];
                dout[7:0] <=#1 ~q_m_reg[7:0];

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

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

            cnt <=#1 5'h0;
        end
        end
    end
  
endmodule

5.5.6 串并转换模块

使用了SerDes原语,要传输10bit需要两个OSERDESE。其中master输入8bit;slave输入2bit,通过扩展位接到master上。

module serializer_10_to_1 (
    input           reset,              // 复位,高有效
    input           paralell_clk,       // 输入并行数据时钟
    input           serial_clk_5x,      // 输入串行数据时钟
    input   [9:0]   paralell_data,      // 输入并行数据

    output 			serial_data_out     // 输出串行数据
);
    
    // wire define
    wire		cascade1;     // 用于两个OSERDESE2级联的信号
    wire		cascade2;
    
        
    // 例化OSERDESE2原语,实现并串转换,Master模式
    OSERDESE2 #(
        .DATA_RATE_OQ   ("DDR"),       // 设置双倍数据速率
        .DATA_RATE_TQ   ("SDR"),       // DDR, BUF, SDR
        .DATA_WIDTH     (10),           // 输入的并行数据宽度为10bit
        .SERDES_MODE    ("MASTER"),    // 设置为Master,用于10bit宽度扩展
        .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_Master (
        .CLK        (serial_clk_5x),    // 串行数据时钟,5倍时钟频率
        .CLKDIV     (paralell_clk),     // 并行数据时钟
        .RST        (reset),            // 1-bit input: Reset
        .OCE        (1'b1),             // 1-bit input: Output data clock enable
        
        .OQ         (serial_data_out),  // 串行输出数据
        
        .D1         (paralell_data[0]), // D1 - D8: 并行数据输入
        .D2         (paralell_data[1]),
        .D3         (paralell_data[2]),
        .D4         (paralell_data[3]),
        .D5         (paralell_data[4]),
        .D6         (paralell_data[5]),
        .D7         (paralell_data[6]),
        .D8         (paralell_data[7]),
    
        .SHIFTIN1   (cascade1),         // SHIFTIN1 用于位宽扩展
        .SHIFTIN2   (cascade2),         // SHIFTIN2
        .SHIFTOUT1  (),                 // SHIFTOUT1: 用于位宽扩展
        .SHIFTOUT2  (),                 // SHIFTOUT2
            
        .OFB        (),                 // 以下是未使用信号
        .T1         (1'b0),             
        .T2         (1'b0),
        .T3         (1'b0),
        .T4         (1'b0),
        .TBYTEIN    (1'b0),             
        .TCE        (1'b0),             
        .TBYTEOUT   (),                 
        .TFB        (),                 
        .TQ         ()                  
    );
    
    // 例化OSERDESE2原语,实现并串转换,Slave模式
    OSERDESE2 #(
        .DATA_RATE_OQ   ("DDR"),       // 设置双倍数据速率
        .DATA_RATE_TQ   ("SDR"),       // DDR, BUF, SDR
        .DATA_WIDTH     (10),           // 输入的并行数据宽度为10bit
        .SERDES_MODE    ("SLAVE"),     // 设置为Slave,用于10bit宽度扩展
        .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_Slave (
        .CLK        (serial_clk_5x),    // 串行数据时钟,5倍时钟频率
        .CLKDIV     (paralell_clk),     // 并行数据时钟
        .RST        (reset),            // 1-bit input: Reset
        .OCE        (1'b1),             // 1-bit input: Output data clock enable
        
        .OQ         (),                 // 串行输出数据
        
        .D1         (1'b0),             // D1 - D8: 并行数据输入
        .D2         (1'b0),
        .D3         (paralell_data[8]),
        .D4         (paralell_data[9]),
        .D5         (1'b0),
        .D6         (1'b0),
        .D7         (1'b0),
        .D8         (1'b0),
    
        .SHIFTIN1   (),                 // SHIFTIN1 用于位宽扩展
        .SHIFTIN2   (),                 // SHIFTIN2
        .SHIFTOUT1  (cascade1),         // SHIFTOUT1: 用于位宽扩展
        .SHIFTOUT2  (cascade2),         // SHIFTOUT2
            
        .OFB        (),                 // 以下是未使用信号
        .T1         (1'b0),             
        .T2         (1'b0),
        .T3         (1'b0),
        .T4         (1'b0),
        .TBYTEIN    (1'b0),             
        .TCE        (1'b0),             
        .TBYTEOUT   (),                 
        .TFB        (),                 
        .TQ         ()                  
    );  
        
endmodule

5.5.7 异步复位同步释放

module asyn_rst_syn (
    input clk,          // 目的时钟域
    input reset_n,      // 异步复位,低有效
    
    output syn_reset    // 高有效
);
    
    // reg define
    reg reset_1;
    reg reset_2;


    assign syn_reset  = reset_2;
        
    // 对异步复位信号进行同步释放,并转换成高有效
    always @ (posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            reset_1 <= 1'b1;
            reset_2 <= 1'b1;
        end
        else begin
            reset_1 <= 1'b0;
            reset_2 <= reset_1;
        end
    end
    
endmodule

5.5.8 XDC

set_property -dict {PACKAGE_PIN N20 IOSTANDARD TMDS_33} [get_ports {tmds_data_p[2]}]
set_property -dict {PACKAGE_PIN T20 IOSTANDARD TMDS_33} [get_ports {tmds_data_p[1]}]
set_property -dict {PACKAGE_PIN V20 IOSTANDARD TMDS_33} [get_ports {tmds_data_p[0]}]
set_property -dict {PACKAGE_PIN N18 IOSTANDARD TMDS_33} [get_ports tmds_clk_p]


FPGA(5)HDMI
https://shuusui.site/blog/2025/04/07/fpga-5-hdmi/
作者
Shuusui
发布于
2025年4月7日
许可协议