【Verilog HDL设计】基于FPGA的HDMI协议实现v0.1

1 协议简介

HDMI协议常见用的有v1.4 v2.0 v2.1等版本,后两个版本基于v1.4版本发展而来,要想深入学习HDMI协议,从v1.4版本开始更容易上手。关于HDMI v1.4的协议内容,网上已经有很多前辈作了详细介绍,例如博主“芒果木有籽”的这篇“HDMI 1.4 协议详解”就讲解的很细致。但毕竟在一篇或者几篇博文中想要把一个协议没有遗漏的展现出来是非常困难的。更详细的协议内容协议详见《High-Definition Multimedia Interface Specification Version 1.4》协议手册。

2 功能需求

HDMI v1.4接口FPGA软件v1.0版本(后简称HDMI模块)为前期功能验证版本,仅需实现用多种常见分辨率点亮显示器的功能并输出渐变灰度彩条。因此,HDMI模块需要实现以下功能:

  1. 支持不同分辨率情况下显示时序生成;
  2. 支持发送数据帧、控制帧;
  3. 支持对数据帧、控制帧进行TMDS编码;
  4. 支持TMDS数据并转串发送功能。

3 总体方案

3.1方案介绍

根据功能需求可知,HDMI模块需要实现视频时序生成及TMDS编码等功能;因此HDMI模块可以大致划分为下文描述的几个模块。
视频时序生成模块,该模块主要根据不同分辨率产生行场同步信号及测试用的灰度渐变彩条数据信号,是HDMI模块的测试数据源。
数据组帧模块,将视频数据及行场同步信号分别打包为数据帧、控制帧等。
TMDS编码模块,该模块对数据帧、控制帧、消息帧进行编码,将数据统一编码为10BIT数据。
并转串模块,该模块实现物理层数据串行转换发送的功能。
HDMI模块FPGA软件代码方案框图如图 3-1所示。
【Verilog HDL设计】基于FPGA的HDMI协议实现v0.1_第1张图片 图 3-1 HDMI模块方案框图

其中tmds_phy模块通过ISERDES原语实现TMDS编码数据的10:1并转串功能,并用ODDR原语将单端信号转换为差分信号。tmds_encode模块对输入数据进行编码,根据hdmi协议对控制帧采用传输最大化编码;对消息帧采用TREC4编码,对数据帧采用传输最小化编码。tmds_remap模块将视频数据、行场同步信号、消息数据等按对应格式进行组帧,该模块还预留了在深色模式下对像素时钟和TMDS时钟的同步处理。pixel_timing_gen模块根据分辨率参数产生行场同步信号及视频测试数据。

3.2时钟与复位

如图 3-1所示。HDMI模块共使用了三个时钟:pixel_clock,tmds_clock,phy_clock。pixel_clock是像素时钟,与显示分辨率有关系,各种显示分辨率与时钟的关系可在文档《A DTV Profile for Uncompressed High Speed Digital Interfaces》(协议CEA-861-D)中查阅。tmds_clock是tmds时钟,与颜色深度有关,在24bit模式下tmds_clock 等于pixel_clock,在其他颜色深度模式下则不等于,详情请查阅HDMI v1.4协议。phy_clock为串行数据时钟,其与tmds时钟的比例为5:1(phy_clock:tmds_clock)。目前时钟频率仅支持修改IP核参数的方式修改。
HDMI模块采用异步低电平复位的方式,复位信号有效电平宽度不能低于2个像素时钟周期,保证能够被正确采样。

4关键模块方案

无。

5测试

5.1功能仿真

搭建如图 5-1所示仿真平台对模块代码进行功能仿真,HDMI_tx_demo使用的是开发板提供的DEMO文件。时钟复位产生模块生成仿真需要的时钟复位激励信号。
【Verilog HDL设计】基于FPGA的HDMI协议实现v0.1_第2张图片
图 5-1功能仿真方案框图

5.2测试结果

通过功能仿真对比HDMI模块与HDMI_tx_demo两个模块的输出串行数据,两者结果一致。
采用器件型号为XC7A100T-2IFGG484的开发板进行烧写验证,能够成功点亮显示屏。切换不同分辨率均能正常点亮,目前已测试1280x720@60Hz与1024x768@60Hz两种分辨率。
测试结果如图 5-2所示。
【Verilog HDL设计】基于FPGA的HDMI协议实现v0.1_第3张图片
图 5-2测试结果示意图

5.3测试总结

HDMI模块代码满足功能需求。

6附录

6.1tmds_phy模块代码

// +FHDR============================================================================/
// Author       : Administrator
// Creat Time   : 2023/01/14 14:00:25
// File Name    : hdmi_tx_phy.v
// Module Ver   : Vx.x
//
//
// All Rights Reserved
//
// ---------------------------------------------------------------------------------/
//
// Modification History:
// V1.0         initial
//
// -FHDR============================================================================/
// 
// hdmi_tx_phy
//    |---
// 
`timescale 1ns/1ps

module hdmi_tx_phy #
(
    parameter                           U_DLY = 1                     // 
)
(
// ---------------------------------------------------------------------------------
// CLock & Reset
// ---------------------------------------------------------------------------------
    input                               clk_line                    , 
    input                               clk_tdms                    , 
    input                               rst_n                       ,
// ---------------------------------------------------------------------------------
// Phy Data
// ---------------------------------------------------------------------------------
    input                         [9:0] phy_clk                     , 
    input                        [29:0] phy_data                    , 
// ---------------------------------------------------------------------------------
// HMDI
// ---------------------------------------------------------------------------------
    output                              hdmi_clk_p                  , 
    output                              hdmi_clk_n                  , 
    output                        [2:0] hdmi_data_p                 , 
    output                        [2:0] hdmi_data_n                   
);

wire                                    cshifth                     ; 
wire                                    cshiftl                     ; 

wire                              [2:0] dshifth                     ; 
wire                              [2:0] dshiftl                     ; 

wire                                    hdmi_clk                    ; 
wire                              [2:0] hdmi_data                   ; 

genvar                                  i                           ;

OSERDESE2 #
(
    .DATA_RATE_OQ                   ("DDR"                      ), // DDR, SDR
    .DATA_RATE_TQ                   ("SDR"                      ), // DDR, BUF, SDR
    .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)
)
u0_OSERDESE2
(
    .OFB                            (                           ), // 1-bit output: Feedback path for data
    .OQ                             (hdmi_clk                   ), // 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                            (clk_line                   ), // 1-bit input: High speed clock
    .CLKDIV                         (clk_tdms                   ), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1                             (phy_clk[0]                 ), 
    .D2                             (phy_clk[1]                 ), 
    .D3                             (phy_clk[2]                 ), 
    .D4                             (phy_clk[3]                 ), 
    .D5                             (phy_clk[4]                 ), 
    .D6                             (phy_clk[5]                 ), 
    .D7                             (phy_clk[6]                 ), 
    .D8                             (phy_clk[7]                 ), 
    .OCE                            (1'b1                       ), // 1-bit input: Output data clock enable
    .RST                            (!rst_n                     ), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
    .SHIFTIN1                       (cshifth                    ), 
    .SHIFTIN2                       (cshiftl                    ), 
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
    .T1                             (1'b0                       ), 
    .T2                             (1'b0                       ), 
    .T3                             (1'b0                       ), 
    .T4                             (1'b0                       ), 
    .TBYTEIN                        (1'b0                       ), // 1-bit input: Byte group tristate
    .TCE                            (1'b0                       )  // 1-bit input: 3-state clock enable
);

OSERDESE2 #
(
    .DATA_RATE_OQ                   ("DDR"                      ), // DDR, SDR
    .DATA_RATE_TQ                   ("SDR"                      ), // DDR, BUF, SDR
    .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)
)
u1_OSERDESE2
(
    .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                      (cshifth                    ), 
    .SHIFTOUT2                      (cshiftl                    ), 
    .TBYTEOUT                       (                           ), // 1-bit output: Byte group tristate
    .TFB                            (               a            ), // 1-bit output: 3-state control
    .TQ                             (                           ), // 1-bit output: 3-state control
    .CLK                            (clk_line                   ), // 1-bit input: High speed clock
    .CLKDIV                         (clk_tdms                   ), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1                             (1'b0                       ), 
    .D2                             (1'b0                       ), 
    .D3                             (phy_clk[8]                 ), 
    .D4                             (phy_clk[9]                 ), 
    .D5                             (1'b0                       ), 
    .D6                             (1'b0                       ), 
    .D7                             (1'b0                       ), 
    .D8                             (1'b0                       ), 
    .OCE                            (1'b1                       ), // 1-bit input: Output data clock enable
    .RST                            (!rst_n                     ), // 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                             (1'b0                       ), 
    .T2                             (1'b0                       ), 
    .T3                             (1'b0                       ), 
    .T4                             (1'b0                       ), 
    .TBYTEIN                        (1'b0                       ), // 1-bit input: Byte group tristate
    .TCE                            (1'b0                       )  // 1-bit input: 3-state clock enable
);

OBUFDS #
(
    .SLEW                           ("FAST"                     )  // Specify the output slew rate
) 
u0_OBUFDS
(
    .I                              (hdmi_clk                   ), // Buffer input 
    .O                              (hdmi_clk_p                 ), // Diff_p output (connect directly to top-level port)
    .OB                             (hdmi_clk_n                 )  // Diff_n output (connect directly to top-level port)
);

generate
for(i=0;i<3;i=i+1)
begin:phy_data_loop

OSERDESE2 #
(
    .DATA_RATE_OQ                   ("DDR"                      ), // DDR, SDR
    .DATA_RATE_TQ                   ("SDR"                      ), // DDR, BUF, SDR
    .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)
)                                                  
u2_OSERDESE2                                       
(                                                  
    .OFB                            (                           ), // 1-bit output: Feedback path for data
    .OQ                             (hdmi_data[i]               ), // 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                            (clk_line                   ), // 1-bit input: High speed clock
    .CLKDIV                         (clk_tdms                   ), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1                             (phy_data[i*10+0]           ), 
    .D2                             (phy_data[i*10+1]           ), 
    .D3                             (phy_data[i*10+2]           ), 
    .D4                             (phy_data[i*10+3]           ), 
    .D5                             (phy_data[i*10+4]           ), 
    .D6                             (phy_data[i*10+5]           ), 
    .D7                             (phy_data[i*10+6]           ), 
    .D8                             (phy_data[i*10+7]           ), 
    .OCE                            (1'b1                       ), // 1-bit input: Output data clock enable
    .RST                            (!rst_n                     ), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
    .SHIFTIN1                       (dshifth[i]                 ), 
    .SHIFTIN2                       (dshiftl[i]                 ), 
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
    .T1                             (1'b0                       ), 
    .T2                             (1'b0                       ), 
    .T3                             (1'b0                       ), 
    .T4                             (1'b0                       ), 
    .TBYTEIN                        (1'b0                       ), // 1-bit input: Byte group tristate
    .TCE                            (1'b0                       )  // 1-bit input: 3-state clock enable
);

OSERDESE2 #
(
    .DATA_RATE_OQ                   ("DDR"                      ), // DDR, SDR
    .DATA_RATE_TQ                   ("SDR"                      ), // DDR, BUF, SDR
    .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)
)
u3_OSERDESE2
(
    .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                      (dshifth[i]                 ), 
    .SHIFTOUT2                      (dshiftl[i]                 ), 
    .TBYTEOUT                       (                           ), // 1-bit output: Byte group tristate
    .TFB                            (                           ), // 1-bit output: 3-state control
    .TQ                             (                           ), // 1-bit output: 3-state control
    .CLK                            (clk_line                   ), // 1-bit input: High speed clock
    .CLKDIV                         (clk_tdms                   ), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
    .D1                             (1'b0                       ), 
    .D2                             (1'b0                       ), 
    .D3                             (phy_data[i*10+8]           ), 
    .D4                             (phy_data[i*10+9]           ), 
    .D5                             (1'b0                       ), 
    .D6                             (1'b0                       ), 
    .D7                             (1'b0                       ), 
    .D8                             (1'b0                       ), 
    .OCE                            (1'b1                       ), // 1-bit input: Output data clock enable
    .RST                            (!rst_n                     ), // 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                             (1'b0                       ), 
    .T2                             (1'b0                       ), 
    .T3                             (1'b0                       ), 
    .T4                             (1'b0                       ), 
    .TBYTEIN                        (1'b0                       ), // 1-bit input: Byte group tristate
    .TCE                            (1'b0                       )  // 1-bit input: 3-state clock enable
);

OBUFDS #
(
    .SLEW                           ("FAST"                     )  // Specify the output slew rate
) 
u1_OBUFDS
(
    .I                              (hdmi_data[i]               ), // Buffer input 
    .O                              (hdmi_data_p[i]             ), // Diff_p output (connect directly to top-level port)
    .OB                             (hdmi_data_n[i]             )  // Diff_n output (connect directly to top-level port)
);

end
endgenerate

endmodule

6.2tmds_encode模块代码

// +FHDR============================================================================/
// Author       : Administrator
// Creat Time   : 2023/01/14 14:35:48
// File Name    : hdmi_tx_encode.v
// Module Ver   : Vx.x
//
//
// All Rights Reserved
//
// ---------------------------------------------------------------------------------/
//
// Modification History:
// V1.0         initial
//
// -FHDR============================================================================/
// 
// hdmi_tx_encode
//    |---
// 
`timescale 1ns/1ps

module hdmi_tx_encode #
(
    parameter                           U_DLY = 1                     // 
)
(
// ---------------------------------------------------------------------------------
// CLock & Reset
// ---------------------------------------------------------------------------------
    input                               clk_tdms                    , 
    input                               rst_n                       ,
// ---------------------------------------------------------------------------------
// Vedio & Commond Data
// ---------------------------------------------------------------------------------
//  tdms_cmd[15:0] :
//          bit15~12 --> data type: 4'b4 -> data island period
//                                  4'b5 -> data island period LGB
//                                  4'b6 -> data island period TGB
//                                  4'b8 -> data period  
//                                  4'b9 -> data period LGB
//                                  4'bc -> control period                             
//
//          bit11~8 --> same as bit 7~4
//          
//          bit7~4   --> When data island period,packet data
//                       When control period,bit4~3 -> preamble
//                                           bit7~6 -> rsv.
//          bit3~2   --> Packet header(data island period only),bit2(header),bit1(first 1'b0,others 1'b1)
//          bit1~0   --> Video synchronization signal,bit1(VSYNC),bit0(HSYNC).

    input                        [15:0] tdms_cmd                    , 
    input                        [23:0] tdms_data                   , 
// ---------------------------------------------------------------------------------
// Phy Data
// ---------------------------------------------------------------------------------
    output                        [9:0] phy_clk                     , 
    output reg                   [29:0] phy_data                      
);

reg                              [47:0] tdms_cmd_dly                ; 
wire                             [11:0] cmd_ctrl                    ; 
wire                             [11:0] cmd_island                  ; 
wire                              [1:0] cmd_gb                      ; 
reg                                     cmd_video                   ; 
wire                              [3:0] cmd_mux                     ; 


reg                              [29:0] max_code_data               ; 
reg                              [29:0] phy_data_ctrl               ; 

reg                              [29:0] terc4_code_data             ; 
reg                              [29:0] phy_data_island             ; 

reg                              [29:0] ctrllgb_code_data           ; 
reg                              [29:0] phy_data_island_gb          ; 

wire                             [11:0] cnt_num_d1s                 ; 
reg                              [26:0] q_m                         ; 
reg                              [26:0] q_m_reg                     ; 
reg                              [11:0] cnt_num_qm1s                ; 
reg                              [11:0] cnt_num_qm0s                ; 
reg                              [14:0] cnt_delta                   ; 
reg                              [29:0] phy_data_video              ; 

wire                             [29:0] phy_data_video_gb           ; 

genvar                                  i                           ;

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        tdms_cmd_dly <= #U_DLY 48'd0;
    else
        tdms_cmd_dly <= #U_DLY {tdms_cmd_dly[31:0],tdms_cmd};
end

assign cmd_ctrl = tdms_cmd_dly[0*16+:12];
assign cmd_island = tdms_cmd_dly[0*16+:12];
assign cmd_gb = tdms_cmd_dly[0*16+:2];
assign cmd_mux = tdms_cmd_dly[47:44];

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        cmd_video <= #U_DLY 1'b0;
    else
        begin
            if(tdms_cmd[15:12] == 4'b1000)
                cmd_video <= #U_DLY 1'b1;
            else
                cmd_video <= #U_DLY 1'b0;
        end
end

// Control data encode
generate
for(i=0;i<3;i=i+1)
begin: control_encode_loop
    
always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        max_code_data[i*10+:10] <= #U_DLY 10'd0;
    else
        case(cmd_ctrl[i*4+:2])
            2'b00   : max_code_data[i*10+:10] <= #U_DLY 10'b11_0101_0100;
            2'b01   : max_code_data[i*10+:10] <= #U_DLY 10'b00_1010_1011;
            2'b10   : max_code_data[i*10+:10] <= #U_DLY 10'b01_0101_0100; 
            2'b11   : max_code_data[i*10+:10] <= #U_DLY 10'b10_1010_1011;
        endcase
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        phy_data_ctrl[i*10+:10] <= #U_DLY 10'd0;
    else
        phy_data_ctrl[i*10+:10] <= #U_DLY max_code_data[i*10+:10];
end

end
endgenerate

// Island data encode
generate
for(i=0;i<3;i=i+1)
begin: island_encode_loop

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        terc4_code_data[i*10+:10] <= #U_DLY 10'd0;
    else
        case(cmd_island[i*4+:4])
            4'b0000 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1001_1100; 
            4'b0001 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_0110_0011; 
            4'b0010 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1110_0100; 
            4'b0011 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1110_0010; 
            4'b0100 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_0111_0001; 
            4'b0101 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_0001_1110; 
            4'b0110 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_1000_1110; 
            4'b0111 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_0011_1100; 
            4'b1000 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1100_1100; 
            4'b1001 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_0011_1001; 
            4'b1010 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_1001_1100; 
            4'b1011 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1100_0110; 
            4'b1100 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1000_1110; 
            4'b1101 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_0111_0001; 
            4'b1110 : terc4_code_data[i*10+:10] <= #U_DLY 10'b01_0110_0011; 
            4'b1111 : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1100_0011; 
            default : terc4_code_data[i*10+:10] <= #U_DLY 10'b10_1001_1100; 
        endcase
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        phy_data_island[i*10+:10] <= #U_DLY 10'd0;
    else
        phy_data_island[i*10+:10] <= #U_DLY terc4_code_data[i*10+:10];
end

end
endgenerate

// Island data Guard Bands
always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        ctrllgb_code_data <= #U_DLY 30'd0;
    else
        begin
            case(cmd_gb[1:0])
                2'b00   : ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b10_1000_1110; 
                2'b01   : ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b10_0111_0001; 
                2'b10   : ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b01_0110_0011; 
                2'b11   : ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b10_1100_0011; 
                default : ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b10_1000_1110; 
            endcase 
            ctrllgb_code_data[1*10+:10] <= #U_DLY 10'b01_0011_0011;
            ctrllgb_code_data[2*10+:10] <= #U_DLY 10'b01_0011_0011;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        phy_data_island_gb <= #U_DLY  30'd0;
    else
        phy_data_island_gb <= #U_DLY ctrllgb_code_data;
end

// Video data encode
generate
for(i=0;i<3;i=i+1)
begin: video_encode_loop
    
assign cnt_num_d1s[i*4+:4] = tdms_data[i*8+0] + tdms_data[i*8+1] + tdms_data[i*8+2] + tdms_data[i*8+3]
                             + tdms_data[i*8+4] + tdms_data[i*8+5] + tdms_data[i*8+6] + tdms_data[i*8+7];

always @ (*)
begin
    if((cnt_num_d1s[i*4+:4] > 4'd4) || ({cnt_num_d1s[i*4+:4],tdms_data[i*8+0]} == 5'b0_1000))
        begin
            q_m[i*9+0] = tdms_data[i*8+0];
            q_m[i*9+1] = tdms_data[i*8+0] ^~ tdms_data[i*8+1];
            q_m[i*9+2] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2];
            q_m[i*9+3] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2] ^~ tdms_data[i*8+3];
            q_m[i*9+4] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2]
                      ^~ tdms_data[i*8+3] ^~ tdms_data[i*8+4]; 
            q_m[i*9+5] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2]
                      ^~ tdms_data[i*8+3] ^~ tdms_data[i*8+4] ^~ tdms_data[i*8+5];
            q_m[i*9+6] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2] ^~ tdms_data[i*8+3] 
                      ^~ tdms_data[i*8+4] ^~ tdms_data[i*8+5] ^~ tdms_data[i*8+6];
            q_m[i*9+7] = tdms_data[i*8+0] ^~ tdms_data[i*8+1] ^~ tdms_data[i*8+2] ^~ tdms_data[i*8+3] 
                      ^~ tdms_data[i*8+4] ^~ tdms_data[i*8+5] ^~ tdms_data[i*8+6] ^~ tdms_data[i*8+7];
            q_m[i*9+8] = 1'b0; 
        end
    else            
        begin
            q_m[i*9+0] = tdms_data[i*8+0];
            q_m[i*9+1] = tdms_data[i*8+0] ^ tdms_data[i*8+1];
            q_m[i*9+2] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2];
            q_m[i*9+3] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2] ^ tdms_data[i*8+3];
            q_m[i*9+4] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2]
                       ^ tdms_data[i*8+3] ^ tdms_data[i*8+4];
            q_m[i*9+5] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2]
                       ^ tdms_data[i*8+3] ^ tdms_data[i*8+4] ^ tdms_data[i*8+5];
            q_m[i*9+6] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2] ^ tdms_data[i*8+3] 
                       ^ tdms_data[i*8+4] ^ tdms_data[i*8+5] ^ tdms_data[i*8+6];
            q_m[i*9+7] = tdms_data[i*8+0] ^ tdms_data[i*8+1] ^ tdms_data[i*8+2] ^ tdms_data[i*8+3] 
                       ^ tdms_data[i*8+4] ^ tdms_data[i*8+5] ^ tdms_data[i*8+6] ^ tdms_data[i*8+7]; 
            q_m[i*9+8] = 1'b1;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin
            cnt_num_qm1s[i*4+:4] <= #U_DLY 4'd0;
            cnt_num_qm0s[i*4+:4] <= #U_DLY 4'd0;
        end
    else
        begin
            cnt_num_qm1s[i*4+:4] <= #U_DLY q_m[i*9+0] + q_m[i*9+1] + q_m[i*9+2] + q_m[i*9+3]
                                         + q_m[i*9+4] + q_m[i*9+5] + q_m[i*9+6] + q_m[i*9+7];
            cnt_num_qm0s[i*4+:4] =4'd8 - cnt_num_qm1s[i*4+:4];
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        q_m_reg[i*9+:9] <= #U_DLY 9'd0;
    else
        q_m_reg[i*9+:9] <= #U_DLY q_m[i*9+:9];
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        cnt_delta[i*5+:5] <= #U_DLY 5'd0;
    else
        begin
            if(cmd_video == 1'b1)
                begin
                    if((|cnt_delta[i*5+:5] == 1'b0) || ({cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 2'b11))
                        begin
                            if(q_m[i*9+8] == 1'b1)
                                cnt_delta[i*5+:5] <= #U_DLY cnt_delta[i*5+:5] + ({cnt_num_qm1s[i*4+3],cnt_num_qm1s[i*4+:4]}
                                                         - {cnt_num_qm0s[i*4+3],cnt_num_qm0s[i*4+:4]});
                            else
                                cnt_delta[i*5+:5] <= #U_DLY cnt_delta[i*5+:5] + ({cnt_num_qm0s[i*4+3],cnt_num_qm0s[i*4+:4]}
                                                         - {cnt_num_qm1s[i*4+3],cnt_num_qm1s[i*4+:4]});
                        end
                    else if(({cnt_delta[i*5+4],cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 3'b010) || 
                            ({cnt_delta[i*5+4],cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 3'b101))
                        cnt_delta[i*5+:5] <= #U_DLY cnt_delta[i*5+:5] + {3'd0,q_m[i*9+8],1'b0} 
                                                         + ({cnt_num_qm0s[i*4+3],cnt_num_qm0s[i*4+:4]}
                                                         - {cnt_num_qm1s[i*4+3],cnt_num_qm1s[i*4+:4]});
                    else 
                        cnt_delta[i*5+:5] <= #U_DLY cnt_delta[i*5+:5] - {3'd0,~q_m[i*9+8],1'b0} 
                                                         + ({cnt_num_qm1s[i*4+3],cnt_num_qm1s[i*4+:4]}
                                                         - {cnt_num_qm0s[i*4+3],cnt_num_qm0s[i*4+:4]});
                end                                                         
            else
                ;                
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        phy_data_video[i*10+:10] <= #U_DLY 10'd0;
    else
        begin
            if((|cnt_delta[i*5+:5] == 1'b0) || ({cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 2'b11))
                begin
                    phy_data_video[i*10+9]  <= #U_DLY ~q_m_reg[i*9+8];
                    phy_data_video[i*10+8]  <= #U_DLY q_m_reg[i*9+8];
                    phy_data_video[i*10+:8] <= #U_DLY q_m_reg[i*9+8] ? q_m_reg[i*9+:8] : ~q_m_reg[i*9+:8];
                end
            else if(({cnt_delta[i*5+4],cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 3'b010) || 
                    ({cnt_delta[i*5+4],cnt_num_qm1s[i*4+3],cnt_num_qm0s[i*4+3]} == 3'b101))
                begin
                    phy_data_video[i*10+9]  <= #U_DLY 1'b1;
                    phy_data_video[i*10+8]  <= #U_DLY q_m_reg[i*9+8];
                    phy_data_video[i*10+:8] <= #U_DLY ~q_m_reg[i*9+:8];
                end
            else
                begin
                    phy_data_video[i*10+9]  <= #U_DLY 1'b0;
                    phy_data_video[i*10+8]  <= #U_DLY q_m_reg[i*9+8];
                    phy_data_video[i*10+:8] <= #U_DLY q_m_reg[i*9+:8];
                end
        end
end

end
endgenerate

assign phy_data_video_gb[0*10+:10] = 10'b10_1100_1100;
assign phy_data_video_gb[1*10+:10] = 10'b01_0011_0011;
assign phy_data_video_gb[2*10+:10] = 10'b10_1100_1100;

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        phy_data <= #U_DLY 30'd0;
    else
        case(cmd_mux)
            4'h4    : phy_data <= #U_DLY phy_data_island;
            4'h5    : phy_data <= #U_DLY phy_data_island_gb;
            4'h6    : phy_data <= #U_DLY phy_data_island_gb;
            4'h8    : phy_data <= #U_DLY phy_data_video;
            4'h9    : phy_data <= #U_DLY phy_data_video_gb;
            4'hc    : phy_data <= #U_DLY phy_data_ctrl;
            default : phy_data <= #U_DLY phy_data_ctrl;
        endcase
end

assign phy_clk = (cmd_mux[3] == 1'b1) ? 10'b11_1110_0000 : 10'd0;

endmodule

6.3tmds_remap模块代码

// +FHDR============================================================================/
// Author       : Administrator
// Creat Time   : 2023/01/23 15:13:04
// File Name    : hdmi_tx_tdms_remap.v
// Module Ver   : Vx.x
//
//
// All Rights Reserved
//
// ---------------------------------------------------------------------------------/
//
// Modification History:
// V1.0         initial
//
// -FHDR============================================================================/
// 
// hdmi_tx_tdms_remap
//    |---
// 
`timescale 1ns/1ps

module hdmi_tx_tdms_remap #
(
    parameter                           U_DLY = 1                     // 
)
(
// ---------------------------------------------------------------------------------
// CLock & Reset
// ---------------------------------------------------------------------------------
    input                               clk_pixel                   , 
    input                               clk_tdms                    , 
    input                               rst_n                       , 
// ---------------------------------------------------------------------------------
// Pixel & Info Data
// ---------------------------------------------------------------------------------
// pixel_en [2:0] (unused)
//          bit2 --> rsv // video data valid
//          bit1 --> rsv // info valid 
//          bit0 --> cmd  valid
// pixel_cmd [7:0] 
//          bit7~6 --> deep color mode.2'd0(8bit),2'd1(10bit),2'd2(12bit),2'd3(16bit)
//          bit5   --> Island period first flag.
//          bit4   --> Control period first flag.
//          bit3   --> Guard Band first flag.
//          bit2   --> Video pixel group first fragment flag.
//          bit1~0 --> Video synchronization signal,bit1(VSYNC),bit0(HSYNC).
//
// pixel_info[15:0]
//          bit15~12 --> same as tdms_cmd[15:12]
//          bit11~2  --> same as tdms_cmd[11:2]
//          bit1~0   --> rsv.
//
// pixel_data[47:0]
//      RGB444 or YCrCb444 mode
//          bit47~32 --> B or Cb,same as R 
//          bit31~16 --> G or Cr,same as R 
//          bit15~0  --> R or Y
//              8  bit mode -> bit15~8,rsv
//                          -> bit 7~0,R data,
//              10 bit mode -> bit15~10,rsv
//                          -> bit 9~0,R data,
//              12 bit mode -> bit15~12,rsv
//                          -> bit11~0,R data
//      YCrCb422 mode
//          bit47~40 --> rsv.
//          bit39~32 --> Cb or Cr[11:4]pixel_data
//          bit31~24 --> rsv.
//          bit23~16 --> Y[11:4].
//          bit15~8 --> rsv
//          bit7~4  --> Cb or Cr[3:0]      
//          bit3~0  --> Y[3:0]     
    input                         [2:0] pixel_en                    , 
    input                         [7:0] pixel_cmd                   , 
    input                        [15:0] pixel_info                  , 
    input                        [47:0] pixel_data                  , 
// ---------------------------------------------------------------------------------
// Vedio & Commond Data
// ---------------------------------------------------------------------------------
//  tdms_cmd[15:0] :
//          bit15~12 --> data type: 4'h4 -> data island period
//                                  4'h5 -> data island period LGB
//                                  4'h6 -> data island period TGB
//                                  4'h8 -> data period  
//                                  4'h9 -> data period LGB
//                                  4'hc -> control period(preamble)                             
//          bit11~8 --> same as bit 7~4,preamble(c3,c2)
//          
//          bit7~4   --> When data island period,packet data
//                       When control period,bit5~4 -> preamble(c1,c0)
//                                           bit7~6 -> rsv.
//          bit3~2   --> Packet header(data island period only),bit2(header),bit1(first 1'b0,others 1'b1)
//          bit1~0   --> Video synchronization signal,bit1(VSYNC),bit0(HSYNC).
    output reg                   [15:0] tdms_cmd                    , 
    output reg                   [23:0] tdms_data                     
);

reg                               [1:0] wrstp_data                  ; 
reg                               [1:0] wrstp_cnt                   ; 

reg                                     dfifo_wr_en_tmp             ; 
reg                          [3*40-1:0] dfifo_wr_data_d8            ; 
reg                          [3*40-1:0] dfifo_wr_data_d10           ; 
reg                          [3*40-1:0] dfifo_wr_data_d12           ; 
reg                          [3*40-1:0] dfifo_wr_data_d16           ; 

reg                                     dfifo_wr_en                 ; 
//  dfifo_wr_data[i*40+:40]
//      --> 8bit mode  : bit3rdistp_data <= #U_DLY 5'd0;9~8,rsv
//                       bit7~0,1 pixel video data
//      --> 10bit mode : bit39~0,4 pixel video data 
//      --> 12bit mode : bit39~24,rsv
//                       bit23~0,2 pixel video data
//      --> 16bit mode : bit39~16,rsv                  
//                       bit15~0,1 pixel video data   
reg                          [3*40-1:0] dfifo_wr_data               ; 


reg                                     rdcstp_en                   ; 
reg                               [2:0] rdcstp_data                 ; 
reg                               [2:0] rdcbufen_data               ; 
reg                               [2:0] rdcstp_cnt                  ; 

reg                                     cfifo_rd_en                 ; 
// cfifo_rd_data[7:0]
//      bit7~0 --> Same as tdms_cmd [15:0] 
wire                              [7:0] cfifo_rd_data               ; 
wire                                    cfifo_empty                 ; 
reg                                     cfifo_empty_tmp             ; 
reg                               [1:0] hvsync_data                 ; 
 

reg                                     rdistp_en                   ; 
reg                               [4:0] rdistp_data                 ; 
reg                               [4:0] rdistp_cnt                  ; 

wire                                    ififo_rd_en                 ; 
// ififo_rd_data[15:0]
//      bit15~0 --> Same as pixel_info [15:0] 
wire                             [15:0] ififo_rd_data               ; 


reg                                     rddstp_en                   ; 
reg                               [2:0] rddstp_cnt                  ; 

reg                                     dfifo_rd_en                 ; 
wire                         [3*64-1:0] dfifo_rd_data               ; 
wire                              [2:0] dfifo_empty                 ; 

reg                              [23:0] tdms_data_tmp               ; 

genvar                                  i                           ;

// pixel video data frame.

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wrstp_data <= #U_DLY 2'd0;
    else
        case(pixel_cmd[7:6])
            2'd0    : wrstp_data <= #U_DLY 2'd0;
            2'd1    : wrstp_data <= #U_DLY 2'd3;
            2'd2    : wrstp_data <= #U_DLY 2'd1;
            2'd3    : wrstp_data <= #U_DLY 2'd0;
            default : wrstp_data <= #U_DLY 2'd0;
        endcase
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        wrstp_cnt <= #U_DLY 2'd0;
    else
        begin
            if((pixel_cmd[2] == 1'b1) && (wrstp_cnt < wrstp_data))
                wrstp_cnt <= #U_DLY wrstp_cnt + 2'd1;
            else
                wrstp_cnt <= #U_DLY 2'd0;
        end
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_en_tmp <= #U_DLY 1'd0;
    else
        begin
            if((pixel_cmd[2] == 1'b1) && (wrstp_cnt >= wrstp_data))
                dfifo_wr_en_tmp <= #U_DLY 1'd1;
            else
                dfifo_wr_en_tmp <= #U_DLY 1'd0;
        end
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_en <= #U_DLY 1'd0;
    else
        begin
            if(dfifo_wr_en_tmp == 1'b1)
                dfifo_wr_en <= #U_DLY 1'd1;
            else
                dfifo_wr_en <= #U_DLY 1'd0;
        end
end

generate
for(i=0;i<3;i=i+1)
begin:video_remap_loop

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_data_d8[i*40+:40] <= #U_DLY 40'd0;
    else
        case(wrstp_cnt)
            2'd0    : dfifo_wr_data_d8[(i*40+0*8)+:8] <= #U_DLY pixel_data[i*16+:8];
            2'd1    : dfifo_wr_data_d8[(i*40+1*8)+:8] <= #U_DLY pixel_data[i*16+:8];
            2'd2    : dfifo_wr_data_d8[(i*40+2*8)+:8] <= #U_DLY pixel_data[i*16+:8];
            2'd3    : dfifo_wr_data_d8[(i*40+3*8)+:8] <= #U_DLY pixel_data[i*16+:8];
            default : ;
        endcase
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_data_d10[i*40+:40] <= #U_DLY 40'd0;
    else
        case(wrstp_cnt)
            2'd0    : dfifo_wr_data_d10[(i*40+0*10)+:10] <= #U_DLY pixel_data[i*16+:10];
            2'd1    : dfifo_wr_data_d10[(i*40+1*10)+:10] <= #U_DLY pixel_data[i*16+:10];
            2'd2    : dfifo_wr_data_d10[(i*40+2*10)+:10] <= #U_DLY pixel_data[i*16+:10];
            2'd3    : dfifo_wr_data_d10[(i*40+3*10)+:10] <= #U_DLY pixel_data[i*16+:10];
            default : ;
        endcase
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_data_d12[i*40+:40] <= #U_DLY 40'd0;
    else
        case(wrstp_cnt[0])
            1'd0    : dfifo_wr_data_d12[(i*40+0*12)+:12] <= #U_DLY pixel_data[i*16+:12];
            1'd1    : dfifo_wr_data_d12[(i*40+1*12)+:12] <= #U_DLY pixel_data[i*16+:12];
            default : ;
        endcase
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_data_d16[i*40+:40] <= #U_DLY 40'd0;
    else
        case(wrstp_cnt[0])
            1'd0    : dfifo_wr_data_d16[(i*40+0*16)+:16] <= #U_DLY pixel_data[i*16+:16];
            1'd1    : dfifo_wr_data_d16[(i*40+1*16)+:16] <= #U_DLY pixel_data[i*16+:16];
            default : ;
        endcase
end

always @ (posedge clk_pixel or negedge rst_n)
begin
    if(rst_n == 1'b0)
        dfifo_wr_data[i*40+:40] <= #U_DLY 40'd0;
    else
        case(pixel_cmd[7:6])
            2'd0    : dfifo_wr_data[i*40+:40] <= #U_DLY dfifo_wr_data_d8[i*40+:40];
            2'd1    : dfifo_wr_data[i*40+:40] <= #U_DLY dfifo_wr_data_d10[i*40+:40];
            2'd2    : dfifo_wr_data[i*40+:40] <= #U_DLY dfifo_wr_data_d12[i*40+:40];
            2'd3    : dfifo_wr_data[i*40+:40] <= #U_DLY dfifo_wr_data_d16[i*40+:40];
            default : ;
        endcase
end
end
endgenerate


// Tdms fragment
//always @ (posedge clk_tdms or negedge rst_n)
//begin
//    if(rst_n == 1'b0)
//        cfifo_empty_tmp <= #U_DLY 1'd0;
//    else
//        cfifo_empty_tmp <= #U_DLY cfifo_empty;
//end


always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdcstp_en <= #U_DLY 1'b0;
    else
        begin
            if(cfifo_empty == 1'b0)
                rdcstp_en <= #U_DLY 1'b1;
            else
                rdcstp_en <= #U_DLY 2'b0;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdcstp_data <= #U_DLY 3'd0;
    else
        case(cfifo_rd_data[7:6])
            2'd0    : rdcstp_data <= #U_DLY 3'd0;
            2'd1    : rdcstp_data <= #U_DLY 3'd4;
            2'd2    : rdcstp_data <= #U_DLY 3'd2;
            2'd3    : rdcstp_data <= #U_DLY 3'd1;
            default : rdcstp_data <= #U_DLY 3'd0;
        endcase
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdcbufen_data <= #U_DLY 3'd0;
    else
        case(cfifo_rd_data[7:6])
            2'd0    : rdcbufen_data <= #U_DLY 3'd0;
            2'd1    : rdcbufen_data <= #U_DLY 3'd3;
            2'd2    : rdcbufen_data <= #U_DLY 3'd1;
            2'd3    : rdcbufen_data <= #U_DLY 3'd0;
            default : rdcbufen_data <= #U_DLY 3'd0;
        endcase
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdcstp_cnt <= #U_DLY 3'd0;
    else
        begin
            if((rdcstp_en == 1'b1) &&(rdcstp_cnt < rdcstp_data))
                rdcstp_cnt <= #U_DLY rdcstp_cnt +3'd1;
            else
                rdcstp_cnt <= #U_DLY 3'd0;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        cfifo_rd_en <= #U_DLY 1'b0;
    else
        begin
            if((rdcstp_en == 1'b1) &&(rdcstp_cnt <= rdcbufen_data))
                cfifo_rd_en <= #U_DLY 1'b1;
            else
                cfifo_rd_en <= #U_DLY 1'b0;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        hvsync_data <= #U_DLY 2'd0;
    else
        if(cfifo_rd_en == 1'b1)
            hvsync_data <= #U_DLY cfifo_rd_data[1:0];
        else
            ;
end

// Get island or control data
always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdistp_en <= #U_DLY 1'b0;
    else
        begin
            if({cfifo_rd_en,|cfifo_rd_data[5:3]} == 2'b11)
                rdistp_en <= #U_DLY 1'b1;
            else if(rdistp_cnt >= rdistp_data)
                rdistp_en <= #U_DLY 1'b0;
            else
                ;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdistp_data <= #U_DLY 5'd0;
    else
        case({cfifo_rd_en,cfifo_rd_data[5:3]})
            4'b1001 : rdistp_data <= #U_DLY 5'd1;
            4'b1010 : rdistp_data <= #U_DLY 5'd7;
            4'b1100 : rdistp_data <= #U_DLY 5'd31;
            default : ;
        endcase
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rdistp_cnt <= #U_DLY 5'd0;
    else
        begin
            if((rdistp_en == 1'b1) && (rdistp_cnt < rdistp_data))
                rdistp_cnt <= #U_DLY rdistp_cnt + 5'd1;
            else
                rdistp_cnt <= #U_DLY 5'd0;
        end
end

assign ififo_rd_en = rdistp_en;

// Get pixel data
always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rddstp_en <= #U_DLY 1'b0;
    else
        begin
            if({cfifo_rd_en,cfifo_rd_data[2]} == 2'b11)
                rddstp_en <= #U_DLY 1'b1;
            else if(rddstp_cnt >= rdcstp_data)
                rddstp_en <= #U_DLY 1'b0;
            else
                ;
        end
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rddstp_cnt <= #U_DLY 3'd0;
    else
        begin
            if((rddstp_en == 1'b1) && (rddstp_cnt < rdcstp_data))
                rddstp_cnt <= #U_DLY rddstp_cnt + 3'd1;
            else
                rddstp_cnt <= #U_DLY 3'd0;
        end
end

always @ (*)
begin
    if((rddstp_en == 1'b1) && (rddstp_cnt == rdcstp_data))
        dfifo_rd_en = 1'b1;
    else
        dfifo_rd_en = 1'b0;
end

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        tdms_cmd <= #U_DLY 16'd0;
    else
        case({cfifo_rd_en,ififo_rd_en,dfifo_rd_en})
            3'b100   : tdms_cmd <= #U_DLY {4'hc,10'd0,hvsync_data};
            3'b101   : tdms_cmd <= #U_DLY {4'h8,12'd0};
            3'b110   : tdms_cmd <= #U_DLY {ififo_rd_data[15:2],hvsync_data};
            default  : tdms_cmd <= #U_DLY {4'hc,12'd0};
        endcase
end

generate
for(i=0;i<3;i=i+1)
begin:video_getd_loop

always @ (posedge clk_tdms or negedge rst_n)
begin
    if(rst_n == 1'b0)
        tdms_data[i*8+:8] <= #U_DLY 8'd0;
    else
        tdms_data[i*8+:8] <= #U_DLY dfifo_rd_data[(i*64+rddstp_cnt*8)+:8];
end

end
endgenerate

//always @ (posedge clk_tdms or negedge rst_n)
//begin
//    if(rst_n == 1'b0)
//        tdms_data <= #U_DLY 24'd0;
//    else
//        tdms_data <= #U_DLY tdms_data_tmp;
//end

fifo_d512w8_fwft u0_cfifo
(
    .wr_clk                         (clk_pixel                  ), // input wire wr_clk
    .rd_clk                         (clk_tdms                   ), // input wire rd_clk
    .rst                            (!rst_n                     ), // input wire rst
    .wr_en                          (pixel_en[0]                ), // input wire wr_en
    .din                            (pixel_cmd[7:0]             ), // input wire [7 : 0] din
    .rd_en                          (cfifo_rd_en                ), // input wire rd_en
    .dout                           (cfifo_rd_data[7:0]         ), // output wire [7 : 0] dout
    .prog_full_thresh               (9'd500                     ), // input wire [8 : 0] prog_full_thresh
    .prog_full                      (                           ), // output wire prog_full
    .full                           (                           ), // output wire full
    .empty                          (cfifo_empty                ), // output wire empty
    .almost_empty                   (                           ), // output wire almost_empty
    .wr_rst_busy                    (                           ), // output wire wr_rst_busy
    .rd_rst_busy                    (                           )  // output wire rd_rst_busy
);

fifo_d512w16_fwft u0_ififo
(
    .wr_clk                         (clk_pixel                  ), // input wire wr_clk
    .rd_clk                         (clk_tdms                   ), // input wire rd_clk
    .rst                            (!rst_n                     ), // input wire rst
    .wr_en                          (|pixel_cmd[4:3]            ), // input wire wr_en
    .din                            (pixel_info[15:0]           ), // input wire [15 : 0] din
    .rd_en                          (ififo_rd_en                ), // input wire rd_en
    .dout                           (ififo_rd_data[15:0]        ), // output wire [15 : 0] dout
    .prog_full_thresh               (9'd500                     ), // input wire [8 : 0] prog_full_thresh
    .prog_full                      (                           ), // output wire prog_full
    .full                           (                           ), // output wire full
    .empty                          (                           ), // output wire empty
    .almost_empty                   (                           ), // output wire almost_empty
    .wr_rst_busy                    (                           ), // output wire wr_rst_busy
    .rd_rst_busy                    (                           )  // output wire rd_rst_busy
);

generate
for(i=0;i<3;i=i+1)
begin:video_buf_loop
fifo_d512w40_fwft u0_dfifo
(
    .wr_clk                         (clk_pixel                  ), // input wire wr_clk
    .rd_clk                         (clk_tdms                   ), // input wire rd_clk
    .rst                            (!rst_n                     ), // input wire rst
    .wr_en                          (dfifo_wr_en                ), // input wire wr_en
    .din                            (dfifo_wr_data[i*40+:40]    ), // input wire [239 : 0] din
    .rd_en                          (dfifo_rd_en                ), // input wire rd_en
    .dout                           (dfifo_rd_data[i*64+:40]    ), // output wire [239 : 0] dout
    .prog_full_thresh               (9'd500                     ), // input wire [8 : 0] prog_full_thresh
    .prog_full                      (                           ), // output wire prog_full
    .full                           (                           ), // output wire full
    .empty                          (dfifo_empty[i]             ), // output wire empty
    .almost_empty                   (                           ), // output wire almost_empty
    .wr_rst_busy                    (                           ), // output wire wr_rst_busy
    .rd_rst_busy                    (                           )  // output wire rd_rst_busy
);

assign dfifo_rd_data[(i*64+40)+:24] = 24'd0;

end
endgenerate

endmodule 



6.4pixel_timing_gen模块代码

// +FHDR============================================================================/
// Author       : Administrator
// Creat Time   : 2023/02/18 14:14:07
// File Name    : hdmi_tx_hvgen.v
// Module Ver   : Vx.x
//
//
// All Rights Reserved
//
// ---------------------------------------------------------------------------------/
//
// Modification History:
// V1.0         initial
//
// -FHDR============================================================================/
// 
// hdmi_tx_hvgen
//    |---
// 

//  _____                                           _____
// |     |_________________________________________|     |__________
//        _________________________________________       __________
// |_____|       |                       |         |_____|  
// ||       |                       |         |    
// |<----hbb---->|                       |         |
// |<----------------hde---------------->|         |
// |<---------------------hba--------------------->|  
//
//  _____                                           _____
// |     |_________________________________________|     |__________       
//        _________________________________________       __________
// |_____|       |                       |         |_____|  
// ||       |                       |         |
// |<----vbb---->|                       |         |
// |<---------------vdata--------------->|         |
// |<---------------------vba--------------------->|  

`timescale 1ns/1ps




module hdmi_tx_hvgen #
(
    parameter                           HBF = 16'd40                ,
    parameter                           HBB = 16'd260               ,
    parameter                           HDE = 16'd1540              ,
    parameter                           HBA = 16'd1650              ,                  
    parameter                           VBF = 16'd6                 ,
    parameter                           VBB = 16'd35                ,
    parameter                           VDE = 16'd803               ,
    parameter                           VBA = 16'd806               ,                                
    parameter                           U_DLY = 1                     // 
)
(
// ---------------------------------------------------------------------------------
// CLock & Reset
// ---------------------------------------------------------------------------------
    input                               clk_sys                     ,
    input                               rst_n                       ,
// ---------------------------------------------------------------------------------
// HV
// ---------------------------------------------------------------------------------
    output reg                          hsync                       , 
    output reg                          vsync                       , 
    output reg                          hv_de                       , 
    output reg                   [23:0] rgbdata                     , 
// ---------------------------------------------------------------------------------
// Pixel & Info Data
// ---------------------------------------------------------------------------------
// pixel_en [2:0] (unused)
//          bit2 --> rsv // video data valid
//          bit1 --> rsv // info valid 
//          bit0 --> cmd  valid
// pixel_cmd [7:0] 
//          bit7~6 --> deep color mode.2'd0(8bit),2'd1(10bit),2'd2(12bit),2'd3(16bit)
//          bit5   --> Island period first flag.
//          bit4   --> Control period first flag.
//          bit3   --> Guard Band first flag.
//          bit2   --> Video pixel group first fragment flag.
//          bit1~0 --> Video synchronization signal,bit1(VSYNC),bit0(HSYNC).
//
// pixel_info[15:0]
//          bit15~12 --> same as tdms_cmd[15:12]
//          bit11~2  --> same as tdms_cmd[11:2]
//          bit1~0   --> rsv.
//
// pixel_data[47:0]
//      RGB444 or YCrCb444 mode
//          bit47~32 --> B or Cb,same as R 
//          bit31~16 --> G or Cr,same as R 
//          bit15~0  --> R or Y
//              8  bit mode -> bit15~8,rsv
//                          -> bit 7~0,R(h_cnt >= HBB + 16'd1) && (h_cnt < HDE + 16'd1) data,
//              10 bit mode -> bit15~10,rsv
//                          -> bit 9~0,R data,
//              12 bit mode -> bit15~12,rsv
//                          -> bit11~0,R data
//      YCrCb422 mode
//          bit47~40 --> rsv.
//          bit39~32 --> Cb or Cr[11:4]pixel_data
//          bit31~24 --> rsv.
//          bit23~16 --> Y[11:4].
//          bit15~8 --> rsv
//          bit7~4  --> Cb or Cr[3:0]      
//          bit3~0  --> Y[3:0]     
    output reg                    [2:0] pixel_en                    , 
    output reg                    [7:0] pixel_cmd                   , 
    output reg                   [15:0] pixel_info                  , 
    output reg                   [47:0] pixel_data                    
);

reg                               [7:0] stp_cnt                     ; 

reg                              [15:0] h_cnt                       ; 
reg                              [15:0] v_cnt                       ; 

//reg                                     hsync                       ; 
//reg                                     vsync                       ; 
//reg                                     hv_de                       ; 

reg                                     scan_en                     ; 

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        stp_cnt <= #U_DLY 8'd0;
    else
        begin
            if(&stp_cnt == 1'b0)
                stp_cnt <= #U_DLY stp_cnt + 8'd1;
            else
                ;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        h_cnt <= #U_DLY 16'd0;
    else
        begin
            if((h_cnt < HBA - 16'd1) && (stp_cnt[7] == 1'b1))
                h_cnt <= #U_DLY h_cnt + 16'd1;
            else
                h_cnt <= #U_DLY 16'd0;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        v_cnt <= #U_DLY 16'd0;
    else
        begin
            if(h_cnt >= HBA - 16'd1)
                begin
                    if(v_cnt < VBA - 16'd1)
                        v_cnt <= #U_DLY v_cnt + 16'd1;
                    else
                        v_cnt <= #U_DLY 16'd0;
                end
            else
                ;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        hsync <= #U_DLY 1'b0;
    else
        begin
            if((h_cnt < HBF) && (stp_cnt[7] == 1'b1))
                hsync <= #U_DLY 1'b0;
            else
                hsync <= #U_DLY 1'b1;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        vsync <= #U_DLY 1'b0;
    else
        begin
            if((h_cnt == 16'd0) && (stp_cnt[7] == 1'b1))
                begin
                    if(v_cnt < VBF)
                        vsync <= #U_DLY 1'b0;
                    else
                        vsync <= #U_DLY 1'b1;
                end
            else
                ;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin
            scan_en <= #U_DLY 1'b0;
            hv_de <= #U_DLY 1'b0;
        end
    else
        begin
            if(stp_cnt[7] == 1'b1)
                scan_en <= #U_DLY 1'b1;
            else
                scan_en <= #U_DLY 1'b0;

            if((v_cnt >= VBB) && (v_cnt < VDE) &&
               (h_cnt >= HBB) && (h_cnt < HDE+10))
                hv_de <= #U_DLY 1'b1;
            else
                hv_de <= #U_DLY 1'b0;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        rgbdata <= #U_DLY 24'd0;
    else
        begin
            if(hv_de == 1'b1)
                begin
                    rgbdata[0*8+:8] <= #U_DLY rgbdata[0*8+:8] + 8'd1;
                    rgbdata[1*8+:8] <= #U_DLY rgbdata[1*8+:8] + 8'd1;
                    rgbdata[2*8+:8] <= #U_DLY rgbdata[2*8+:8] + 8'd1;
                end
            else
                rgbdata <= #U_DLY 24'd0;
        end
end

always @ (posedge clk_sys or negedge rst_n)
begin
    if(rst_n == 1'b0)
        begin
            pixel_en <= #U_DLY 3'b0;
            pixel_cmd <= #U_DLY 8'd0;
            pixel_info <= #U_DLY 16'd0;
            pixel_data <= #U_DLY 48'd0;
        end
    else
        begin
            pixel_en <= #U_DLY {2'b0,scan_en};
            pixel_cmd[7:5] <= #U_DLY 3'b0;

            if((v_cnt >= VBB) && (v_cnt < VDE) &&
               (h_cnt >= HBB - 16'd9) && (h_cnt < HBB - 16'd1))
                pixel_cmd[4] <= #U_DLY 1'b1;
            else
                pixel_cmd[4] <= #U_DLY 1'b0;

            if((v_cnt >= VBB) && (v_cnt < VDE) &&
               (h_cnt >= HBB - 16'd1) && (h_cnt < HBB + 16'd1))
                pixel_cmd[3] <= #U_DLY 1'b1;
            else
                pixel_cmd[3] <= #U_DLY 1'b0;
                
            pixel_cmd[2] <= #U_DLY hv_de;
            pixel_cmd[1] <= #U_DLY vsync;
            pixel_cmd[0] <= #U_DLY hsync;

            if((v_cnt >= VBB) && (v_cnt < VDE))
                begin
                    if((h_cnt >= HBB - 16'd9) && (h_cnt < HBB - 16'd1))
                        begin
                            pixel_info[15:12] <= #U_DLY 4'hc;
                            pixel_info[11:8] <= #U_DLY 4'd0;
                            pixel_info[7:4] <= #U_DLY 4'd1;
                            pixel_info[3:0] <= #U_DLY 4'd0;
                        end
                    else if((h_cnt >= HBB - 16'd1) && (h_cnt < HBB + 16'd1))
                        begin
                            pixel_info[15:12] <= #U_DLY 4'h9;
                            pixel_info[11:0] <= #U_DLY 12'd0;
                        end
                    else if((h_cnt >= HBB + 16'd1) && (h_cnt < HDE + 16'd1))
                        begin
                            pixel_info[15:12] <= #U_DLY 4'h8;
                            pixel_info[11:0] <= #U_DLY 12'd0;
                        end
                    else
                        pixel_info <= #U_DLY 16'hc000;
                end
            else
                pixel_info <= #U_DLY 16'hc000;

            if((v_cnt >= VBB) && (v_cnt < VDE) &&
               (h_cnt >= HBB + 16'd2) && (h_cnt < HDE + 16'd2))
                begin
                    pixel_data[0*16+:16] <= #U_DLY pixel_data[0*16+:8] + 8'd1;
                    pixel_data[1*16+:16] <= #U_DLY pixel_data[0*16+:8] + 8'd1;
                    pixel_data[2*16+:16] <= #U_DLY pixel_data[0*16+:8] + 8'd1;
                end
            else
                pixel_data <= #U_DLY 48'd0;
        end
end

endmodule


7 参考文献

  1. 《High-Definition Multimedia Interface Specification Version 1.4》
  2. 《A DTV Profile for Uncompressed High Speed Digital Interfaces》

你可能感兴趣的:(fpga开发)