FPGA实现HDMI传输(二)

之前的文章简单介绍了HDMI接口、TMDS编码以及ADV611工作原理和寄存器配置,本篇博客将给出具体的代码以及板级验证结果,代码参考自米联客的教程。

一.ADV7611配置

1.i2c驱动模块

`timescale 1ns / 1ps

module uii2c#
(
    parameter       WMEN_LEN        =       8'd0,                                           //写入字节长度
    parameter       RMEN_LEN        =       8'd0,                                           //读出字节长度
    parameter       CLK_DIV         =       16'd499                                         //i2c时钟分频系数
)
(
    input                                       clk_i,                                      //系统时钟
    input            [7:0]                      rd_cnt,                                     //读数据长度,包含器件地址
    input                                       iic_en,                                     //i2c使能信号
    input                                       iic_mode,                                   //i2c工作模式
    input            [WMEN_LEN*8-1'b1:0]        wr_data,                                    //写入数据
    input            [7:0]                      wr_cnt,                                     //写数据长度,包含器件地址

    output       reg [RMEN_LEN*8-1'b1:0]        rd_data     =   0,                          //读取数据
    output       reg                            iic_scl     =   0 ,                         //i2c时钟总线
    output       reg                            iic_busy    =   0,                          //i2c忙碌,拉高时不进行操作
    inout        wire                           iic_sda,                                    //i2c数据总线
    output       reg                            sda_dg      =   1'b1                        //用于调试
);

    parameter               IDLE        =       4'd0;                                       //空闲状态
    parameter               START       =       4'd1;                                       //开始
    parameter               W_WAIT      =       4'd2;                                       //写入数据
    parameter               W_ACK       =       4'd3;                                       //写响应
    parameter               R_WAIT      =       4'd4;                                       //读取数据
    parameter               R_ACK       =       4'd5;                                       //读响应
    parameter               STOP1       =       4'd6;                                       //停止信号1
    parameter               STOP2       =       4'd7;                                       //停止信号2

    parameter               OFFSET      =       CLK_DIV - CLK_DIV/4;                        //产生i2c时钟

    reg         [2:0]       IIC_S       =       4'd0;                                       //i2c工作状态
    reg         [15:0]      clkdiv      =       16'd0;                                      //时钟分频计数器
    reg                     scl_clk     =       1'b0;                                       //i2c工作时钟
    reg                     scl_r       =       1'b1;                                       //i2c时钟寄存器
    reg                     sda_o       =       1'b0;                                       //sda输出
    reg         [7:0]       sda_r       =       8'd0;                                       //i2c数据寄存器
    reg         [7:0]       sda_i_r     =       8'd0;                                       //i2c输入数据寄存器
    reg         [7:0]       wcnt        =       8'd0;                                       //写数据计数
    reg         [7:0]       rcnt        =       8'd0;                                       //读数据计数
    reg         [2:0]       bcnt        =       3'd0;                                       //读写字节计数
    reg                     rd_en       =       1'b0;                                       //读使能
    reg                     iic_sda_r   =       1'b1;                                       //数据总线缓存

    wire                    scl_offset  =       (clkdiv == OFFSET);                         //scl delay output to fit timing
    wire                    sda_i;                                                          //i2c数据总线输入
 
    //利用计数器产生i2c驱动时钟
    always@(posedge clk_i)begin
        if(clkdiv < CLK_DIV)    
            clkdiv <= clkdiv + 1'b1;
        else begin
            clkdiv <= 16'd0; 
            scl_clk <= !scl_clk;
        end
    end

    //三态门处理
    IOBUF #(
        .DRIVE(12), // Specify the output drive strength
        .IBUF_LOW_PWR("TRUE"),  // Low Power - "TRUE", High Performance = "FALSE" 
        .IOSTANDARD("DEFAULT"), // Specify the I/O standard
        .SLEW("SLOW") // Specify the output slew rate
    ) IOBUF_inst (
        .O(sda_i),    // Buffer output
        .IO(iic_sda), // Buffer inout port (connect directly to top-level port)
        .I(sda_o),    // Buffer input
        .T(sda_o)     // 3-state enable input, high=input, low=output
    );

    //时钟总线输出    
    always @(posedge clk_i) 
        iic_scl <=  scl_offset ?  scl_r : iic_scl;       

    PULLUP PULLUP_inst (.O(iic_sda));
    
    always @(*) begin
        if(IIC_S == IDLE || IIC_S == STOP1 || IIC_S == STOP2)
            scl_r <= 1'b1;
        else 
            scl_r <= ~scl_clk;
    end   
    //sda output 
    always @(*) begin
        if(IIC_S == START || IIC_S == STOP1 || (IIC_S == R_ACK && (rcnt != rd_cnt)))
            sda_o <= 1'b0;
        else if(IIC_S == W_WAIT)
            sda_o <= sda_r[7]; 
        else  
            sda_o <= 1'b1;
    end   

    //sda输出转换
    always @(negedge scl_clk) begin
        if(IIC_S == W_ACK || IIC_S == START)begin
            sda_r <= wr_data[(wcnt*8) +: 8];
            if( rd_en ) sda_r <= {wr_data[7:1],1'b1};
        end
        else if(IIC_S == W_WAIT)
            sda_r <= {sda_r[6:0],1'b1};
        else 
            sda_r <= sda_r;
    end

    //sda输入转换  
    always @(posedge scl_clk) begin
        if(IIC_S == R_WAIT ||IIC_S == W_ACK ) begin
            sda_i_r <= {sda_i_r[6:0],sda_i};
        end
        else if(IIC_S == R_ACK)
            rd_data[((rcnt-1'b1)*8) +: 8] <= sda_i_r[7:0];
        else if(IIC_S == IDLE)begin
            sda_i_r <= 8'd0;
        end 
    end

    always @(posedge scl_clk) 
        iic_sda_r <= sda_i;

    always @(posedge clk_i) 
        sda_dg <= sda_i;

    //iic状态机
    always @(negedge scl_clk)begin
        case(IIC_S) //sda = 1 scl =1
            IDLE://idle wait iic_en == 1'b1 start trasmit   rd_en == 1'b1 restart 
            if(iic_en == 1'b1 || rd_en == 1'b1)begin 
                iic_busy <= 1'b1;        
                IIC_S  <= START;
            end
        else begin
           iic_busy <= 1'b0;
           wcnt <= 8'd0;
           rcnt <= 8'd0;
           rd_en <= 1'b0;
        end
        START:begin //sda = 0  then scl_clk =0 scl =0 generate start
           bcnt <= 3'd7;          
           IIC_S  <= W_WAIT;
        end           
        W_WAIT://write data 
        begin
           if(bcnt > 3'd0)
               bcnt  <= bcnt - 1'b1; 
           else begin
               wcnt <= wcnt + 1'b1; 
               IIC_S  <= W_ACK;
           end
        end 
        W_ACK://write data ack
        begin 
           if(wcnt < wr_cnt)begin 
              bcnt <= 3'd7;
              IIC_S <= W_WAIT;
           end
           else if(rd_cnt > 3'd0)begin// read data
              if(rd_en == 1'b0 && iic_mode == 1'b1)begin 
                  rd_en <= 1'b1;
                  IIC_S <= IDLE;  
              end
              else 
                  IIC_S <= R_WAIT;
              bcnt <= 3'd7;
           end
           else
              IIC_S <= STOP1; 
              if(iic_sda_r == 1'b1)
              IIC_S <= STOP1;
        end  
        R_WAIT://read data
        begin
           rd_en <= 1'b0;
           bcnt  <= bcnt - 1'b1; 
           if(bcnt == 3'd0)begin
              rcnt <= (rcnt < rd_cnt) ? (rcnt + 1'b1) : rcnt;
              IIC_S  <= R_ACK;
           end
        end
        R_ACK://read date ack
        begin
           bcnt <= 3'd7;
           IIC_S <= (rcnt < rd_cnt) ? R_WAIT : STOP1; 
        end  
        STOP1://sda = 0 scl = 1
            IIC_S <= STOP2;
        STOP2://sda = 1 scl = 1
            IIC_S <= IDLE;          
        default:
            IIC_S <= IDLE;
    endcase
end

endmodule

2.adv7611寄存器数据

`timescale 1ns / 1ps

module ui7611reg(
    input               [8 :0]              REG_INDEX,                      //对已配置寄存器计数

    output          reg [31:0]              REG_DATA,                       //寄存器地址+写入数据
    output              [8 :0]              REG_SIZE                        //需配置寄存器个数
);

    assign	                                REG_SIZE    =   9'd182;

//-----------------------------------------------------------------
/	Config Data REG	  //	
always@(*)
case(REG_INDEX)
//write Data Index
    0   : REG_DATA	=	{8'h98,8'hF4, 8'h80};	//Manufacturer ID Byte - High (Read only)
    1   : REG_DATA	=	{8'h98,8'hF5, 8'h7c};	//Manufacturer ID Byte - Low (Read only)
    2   : REG_DATA	= 	{8'h98,8'hF8, 8'h4c};	// BIT[7]-Reset all the Reg 
    3   : REG_DATA	= 	{8'h98,8'hF9, 8'h64};	//DC offset for analog process
    4   : REG_DATA	= 	{8'h98,8'hFA, 8'h6c};	//COM10 : href/vsync/pclk/data reverse(Vsync H valid)
    5   : REG_DATA	= 	{8'h98,8'hFB, 8'h68};	//VGA :	8'h22;	QVGA :	8'h3f;
    6   : REG_DATA	= 	{8'h98,8'hFD, 8'h44};	//VGA :	8'ha4;	QVGA :	8'h50;
    7   : REG_DATA	=	{8'h98,8'h01, 8'h05};	//VGA :	8'h07;	QVGA :	8'h03;
    8   : REG_DATA	= 	{8'h98,8'h00, 8'h1F};	//VGA :	8'hf0;	QVGA :	8'h78;
    9   : REG_DATA	= 	{8'h98,8'h02, 8'hF7};	//HREF	/ 8'h80
    10  : REG_DATA  = 	{8'h98,8'h03, 8'h40};	//VGA :	8'hA0;	QVGA :	8'hF0
    11  : REG_DATA  = 	{8'h98,8'h04, 8'h42};	//VGA :	8'hF0;	QVGA :	8'h78
    12  : REG_DATA	=	{8'h98,8'h05, 8'h28};	//
    13  : REG_DATA	= 	{8'h98,8'h06, 8'ha7};	//
    14  : REG_DATA	= 	{8'h98,8'h0b, 8'h44};	//BIT[6] :	0 :VGA; 1;QVGA
    15  : REG_DATA	= 	{8'h98,8'h0C, 8'h42};	//
    16  : REG_DATA	= 	{8'h98,8'h15, 8'h80};	//
    17  : REG_DATA	= 	{8'h98,8'h19, 8'h8a};	//
    18  : REG_DATA	= 	{8'h98,8'h33, 8'h40};	//
    19  : REG_DATA	= 	{8'h98,8'h14, 8'h3f};	//
    20  : REG_DATA	= 	{8'h44,8'hba, 8'h01};	//
    21  : REG_DATA	= 	{8'h44,8'h7c, 8'h01};	//	
    22  : REG_DATA	= 	{8'h64,8'h40, 8'h81};	//DSP_Ctrl4 :00/01 : YUV or RGB; 10 : RAW8; 11 : RAW10		
    23  : REG_DATA	=	{8'h68,8'h9b, 8'h03};   //ADI recommanded setting
    24  : REG_DATA	=	{8'h68,8'hc1, 8'h01};	//ADI recommanded setting
    25  : REG_DATA	=	{8'h68,8'hc2, 8'h01};	//ADI recommanded setting
    26  : REG_DATA	=	{8'h68,8'hc3, 8'h01};	//ADI recommanded setting
    27  : REG_DATA	=	{8'h68,8'hc4, 8'h01};	//ADI recommanded setting
    28  : REG_DATA	=	{8'h68,8'hc5, 8'h01};	//ADI recommanded setting
    29  : REG_DATA	=	{8'h68,8'hc6, 8'h01};	//ADI recommanded setting
    30  : REG_DATA	=	{8'h68,8'hc7, 8'h01};	//ADI recommanded setting
    31  : REG_DATA	=	{8'h68,8'hc8, 8'h01};	//ADI recommanded setting
    32  : REG_DATA	=	{8'h68,8'hc9, 8'h01};	//ADI recommanded settin g
    33  : REG_DATA	=	{8'h68,8'hca, 8'h01};	//ADI recommanded setting
    34  : REG_DATA	=	{8'h68,8'hcb, 8'h01};	//ADI recommanded setting
    35  : REG_DATA	=	{8'h68,8'hcc, 8'h01};	//ADI recommanded setting
    36  : REG_DATA	=	{8'h68,8'h00, 8'h00}; 	//Set HDMI input Port A
    37  : REG_DATA	=	{8'h68,8'h83, 8'hfe};	//terminator for Port A
    38  : REG_DATA	=	{8'h68,8'h6f, 8'h08};	//ADI recommended setting
    39  : REG_DATA	=	{8'h68,8'h85, 8'h1f};	//ADI recommended setting
    40  : REG_DATA	=	{8'h68,8'h87, 8'h70};	//ADI recommended setting
    41  : REG_DATA	=	{8'h68,8'h8d, 8'h04};	//LFG
    42  : REG_DATA	=	{8'h68,8'h8e, 8'h1e};	//HFG
    43  : REG_DATA	=	{8'h68,8'h1a, 8'h8a};	//unmute audio
    44  : REG_DATA	=	{8'h68,8'h57, 8'hda};	// ADI recommended setting
    45  : REG_DATA	=	{8'h68,8'h58, 8'h01};
    46  : REG_DATA	=	{8'h68,8'h75, 8'h10}; 
    47  : REG_DATA	= 	{8'h68,8'h6c ,8'ha3};//enable manual HPA
    48  : REG_DATA	= 	{8'h98,8'h20 ,8'h70};//HPD low
    49  : REG_DATA	= 	{8'h64,8'h74 ,8'h00};//disable internal EDID 
//edid 
//0: REG_DATA	= 	{8'h68,8'h6c ,8'ha3}; enable manual HPA
//1: REG_DATA	= 	{8'h98,8'h20 ,8'h70};//HPD low
//2: REG_DATA	= 	{8'h64,8'h74 ,8'h00};//disable internal EDID  
//edid par
    50  : REG_DATA	= 	{8'h6c,8'd0  , 8'h00};
    51  : REG_DATA	= 	{8'h6c,8'd1  , 8'hFF};
    52  : REG_DATA	= 	{8'h6c,8'd2  , 8'hFF};
    53  : REG_DATA	= 	{8'h6c,8'd3  , 8'hFF};
    54  : REG_DATA	= 	{8'h6c,8'd4  , 8'hFF};
    55  : REG_DATA	= 	{8'h6c,8'd5  , 8'hFF};
    56  : REG_DATA	= 	{8'h6c,8'd6  , 8'hFF};
    57  : REG_DATA	= 	{8'h6c,8'd7  , 8'h00};
    58  : REG_DATA	= 	{8'h6c,8'd8  , 8'h20};
    59  : REG_DATA	= 	{8'h6c,8'd9  , 8'hA3};
    60  : REG_DATA	= 	{8'h6c,8'd10 , 8'h29};
    61  : REG_DATA	= 	{8'h6c,8'd11 , 8'h00};
    62  : REG_DATA	= 	{8'h6c,8'd12 , 8'h01};
    63  : REG_DATA	= 	{8'h6c,8'd13 , 8'h00};
    64  : REG_DATA	= 	{8'h6c,8'd14 , 8'h00};
    65  : REG_DATA	= 	{8'h6c,8'd15 , 8'h00};
    66  : REG_DATA	= 	{8'h6c,8'd16 , 8'h23};
    67  : REG_DATA	= 	{8'h6c,8'd17 , 8'h12};
    68  : REG_DATA	= 	{8'h6c,8'd18 , 8'h01};
    69  : REG_DATA	= 	{8'h6c,8'd19 , 8'h03};
    70  : REG_DATA	= 	{8'h6c,8'd20 , 8'h80};
    71  : REG_DATA	= 	{8'h6c,8'd21 , 8'h73};
    72  : REG_DATA	= 	{8'h6c,8'd22 , 8'h41};
    73  : REG_DATA	= 	{8'h6c,8'd23 , 8'h78};
    74  : REG_DATA	= 	{8'h6c,8'd24 , 8'h0A};
    75  : REG_DATA	= 	{8'h6c,8'd25 , 8'hF3};
    76  : REG_DATA	= 	{8'h6c,8'd26 , 8'h30};
    77  : REG_DATA	= 	{8'h6c,8'd27 , 8'hA7};
    78  : REG_DATA	= 	{8'h6c,8'd28 , 8'h54};
    79  : REG_DATA	= 	{8'h6c,8'd29 , 8'h42};
    80  : REG_DATA	= 	{8'h6c,8'd30 , 8'hAA};
    81  : REG_DATA	= 	{8'h6c,8'd31 , 8'h26};
    82  : REG_DATA	= 	{8'h6c,8'd32 , 8'h0F};
    83  : REG_DATA	= 	{8'h6c,8'd33 , 8'h50};
    84  : REG_DATA	= 	{8'h6c,8'd34 , 8'h54};
    85  : REG_DATA	= 	{8'h6c,8'd35 , 8'h25};
    86  : REG_DATA	= 	{8'h6c,8'd36 , 8'hC8};
    87  : REG_DATA	= 	{8'h6c,8'd37 , 8'h00};
    88  : REG_DATA	= 	{8'h6c,8'd38 , 8'h61};
    89  : REG_DATA	= 	{8'h6c,8'd39 , 8'h4F};
    90  : REG_DATA	= 	{8'h6c,8'd40 , 8'h01};
    91  : REG_DATA	= 	{8'h6c,8'd41 , 8'h01};
    92  : REG_DATA	= 	{8'h6c,8'd42 , 8'h01};
    93  : REG_DATA	= 	{8'h6c,8'd43 , 8'h01};
    94  : REG_DATA	= 	{8'h6c,8'd44 , 8'h01};
    95  : REG_DATA	= 	{8'h6c,8'd45 , 8'h01};
    96  : REG_DATA	= 	{8'h6c,8'd46 , 8'h01};
    97  : REG_DATA	= 	{8'h6c,8'd47 , 8'h01};
    98  : REG_DATA	= 	{8'h6c,8'd48 , 8'h01};
    99  : REG_DATA	= 	{8'h6c,8'd49 , 8'h01};
    100  : REG_DATA	= 	{8'h6c,8'd50 , 8'h01};
    101  : REG_DATA	= 	{8'h6c,8'd51 , 8'h01};
    102  : REG_DATA	= 	{8'h6c,8'd52 , 8'h01};
    103  : REG_DATA	= 	{8'h6c,8'd53 , 8'h01};
    104  : REG_DATA	= 	{8'h6c,8'd54 , 8'h02};
    105  : REG_DATA	= 	{8'h6c,8'd55 , 8'h3A};
    106  : REG_DATA	= 	{8'h6c,8'd56 , 8'h80};
    107  : REG_DATA	= 	{8'h6c,8'd57 , 8'h18};
    108  : REG_DATA	= 	{8'h6c,8'd58 , 8'h71};
    109  : REG_DATA	= 	{8'h6c,8'd59 , 8'h38};
    110  : REG_DATA	= 	{8'h6c,8'd60 , 8'h2D};
    111  : REG_DATA	= 	{8'h6c,8'd61 , 8'h40};
    112  : REG_DATA	= 	{8'h6c,8'd62 , 8'h58};
    113  : REG_DATA	= 	{8'h6c,8'd63 , 8'h2C};
    114  : REG_DATA	= 	{8'h6c,8'd64 , 8'h45};
    115  : REG_DATA	= 	{8'h6c,8'd65 , 8'h00};
    116  : REG_DATA	= 	{8'h6c,8'd66 , 8'h80};
    117  : REG_DATA	= 	{8'h6c,8'd67 , 8'h88};
    118  : REG_DATA	= 	{8'h6c,8'd68 , 8'h42};
    119  : REG_DATA	= 	{8'h6c,8'd69 , 8'h00};
    120  : REG_DATA	= 	{8'h6c,8'd70 , 8'h00};
    121  : REG_DATA	= 	{8'h6c,8'd71 , 8'h1E};
    122  : REG_DATA	= 	{8'h6c,8'd72 , 8'h8C};
    123  : REG_DATA	= 	{8'h6c,8'd73 , 8'h0A};
    124  : REG_DATA	= 	{8'h6c,8'd74 , 8'hD0};
    125  : REG_DATA	= 	{8'h6c,8'd75 , 8'h8A};
    126  : REG_DATA	= 	{8'h6c,8'd76 , 8'h20};
    127  : REG_DATA	= 	{8'h6c,8'd77 , 8'hE0};
    128  : REG_DATA	= 	{8'h6c,8'd78 , 8'h2D};
    129  : REG_DATA	= 	{8'h6c,8'd79 , 8'h10};
    130  : REG_DATA	= 	{8'h6c,8'd80 , 8'h10};
    131  : REG_DATA	= 	{8'h6c,8'd81 , 8'h3E};
    132  : REG_DATA	= 	{8'h6c,8'd82 , 8'h96};
    133  : REG_DATA	= 	{8'h6c,8'd83 , 8'h00};
    134  : REG_DATA	= 	{8'h6c,8'd84 , 8'h80};
    135  : REG_DATA	= 	{8'h6c,8'd85 , 8'h88};
    136  : REG_DATA	= 	{8'h6c,8'd86 , 8'h42};
    137  : REG_DATA	= 	{8'h6c,8'd87 , 8'h00};
    138  : REG_DATA	= 	{8'h6c,8'd88 , 8'h00};
    139  : REG_DATA	= 	{8'h6c,8'd89 , 8'h18};
    140  : REG_DATA	= 	{8'h6c,8'd90 , 8'h00};
    141  : REG_DATA	= 	{8'h6c,8'd91 , 8'h00};
    142  : REG_DATA	= 	{8'h6c,8'd92 , 8'h00};
    143  : REG_DATA	= 	{8'h6c,8'd93 , 8'hFC};
    144  : REG_DATA	= 	{8'h6c,8'd94 , 8'h00};
    145  : REG_DATA	= 	{8'h6c,8'd95 , 8'h48};
    146  : REG_DATA	= 	{8'h6c,8'd96 , 8'h44};
    147  : REG_DATA	= 	{8'h6c,8'd97 , 8'h4D};
    148  : REG_DATA	= 	{8'h6c,8'd98 , 8'h49};
    149  : REG_DATA	= 	{8'h6c,8'd99 , 8'h20};
    150  : REG_DATA	= 	{8'h6c,8'd100 , 8'h20};
    151  : REG_DATA	= 	{8'h6c,8'd101 , 8'h20};
    152  : REG_DATA	= 	{8'h6c,8'd102 , 8'h20};
    153  : REG_DATA	= 	{8'h6c,8'd103 , 8'h0A};
    154  : REG_DATA	= 	{8'h6c,8'd104 , 8'h20};
    155  : REG_DATA	= 	{8'h6c,8'd105 , 8'h20};
    156  : REG_DATA	= 	{8'h6c,8'd106 , 8'h20};
    157  : REG_DATA	= 	{8'h6c,8'd107 , 8'h20};
    158  : REG_DATA	= 	{8'h6c,8'd108 , 8'h00};
    159  : REG_DATA	= 	{8'h6c,8'd109 , 8'h00};
    160  : REG_DATA	= 	{8'h6c,8'd110 , 8'h00};
    161  : REG_DATA	= 	{8'h6c,8'd111 , 8'hFD};
    162  : REG_DATA	= 	{8'h6c,8'd112 , 8'h00};
    163  : REG_DATA	= 	{8'h6c,8'd113 , 8'h32};
    164  : REG_DATA	= 	{8'h6c,8'd114 , 8'h55};
    165  : REG_DATA	= 	{8'h6c,8'd115 , 8'h1F};
    166  : REG_DATA	= 	{8'h6c,8'd116 , 8'h45};
    167  : REG_DATA	= 	{8'h6c,8'd117 , 8'h0F};
    168  : REG_DATA	= 	{8'h6c,8'd118 , 8'h00};
    169  : REG_DATA	= 	{8'h6c,8'd119 , 8'h0A};
    170  : REG_DATA	= 	{8'h6c,8'd120 , 8'h20};
    171  : REG_DATA	= 	{8'h6c,8'd121 , 8'h20};
    172  : REG_DATA	= 	{8'h6c,8'd122 , 8'h20};
    173  : REG_DATA	= 	{8'h6c,8'd123 , 8'h20};
    174  : REG_DATA	= 	{8'h6c,8'd124 , 8'h20};
    175  : REG_DATA	= 	{8'h6c,8'd125 , 8'h20};
    176  : REG_DATA	= 	{8'h6c,8'd126 , 8'h01};
    177  : REG_DATA	= 	{8'h6c,8'd127 , 8'h24};
    178  : REG_DATA	= 	{8'h64,8'h74  , 8'h01};// enable internal EDID
    179  : REG_DATA	= 	{8'h98,8'h20  , 8'hf0};// HPD high
    180  : REG_DATA	= 	{8'h68,8'h6c  , 8'ha2};// disable manual HPA	
    181  : REG_DATA	=   {8'h98,8'hf4  , 8'h00};
    default:REG_DATA =0;
endcase


endmodule

3.配置模块顶层

`timescale 1ns / 1ps

module uicfg7611#(
        parameter	        CLK_DIV         =           16'd999
)
(
    input                                   clk_i,                                          //系统时钟
    input                                   rst_n,                                          //系统复位

    output                                  adv_scl,                                        //i2c时钟总线
    inout                                   adv_sda,                                        //i2c数据总线
    output reg                              cfg_done                                        //寄存器配置完成信号
);	
    
    reg                     [8 :0]          rst_cnt     =   9'd0;                           //延迟复位计数器
    reg                                     iic_en;                                         //i2c使能信号
    reg                     [31:0]          wr_data;                                        //寄存器地址+写入数据
    reg                     [1 :0]          TS_S        =   2'd0;                           //状态机工作状态
    reg                     [8 :0]          byte_cnt    =   9'd0;                           //读写字节计数器
    reg                     [8 :0]          REG_INDEX;                                      //寄存器配置
    
    wire                                    iic_busy;                                       //i2c忙碌
    wire                    [23:0]          REG_DATA;
    wire                    [8 :0]          REG_SIZE;

    always@(posedge clk_i) begin
        if(!rst_n)
            rst_cnt <= 9'd0;
        else if(!rst_cnt[8]) 
            rst_cnt <= rst_cnt + 1'b1;
    end

    //状态机工作状态转换以及输出
    always@(posedge clk_i) begin
    if(!rst_cnt[8])begin
        REG_INDEX<= 9'd0;
        iic_en  <= 1'b0;
        wr_data <= 32'd0;
        cfg_done<= 1'b0;
        TS_S    <= 2'd0;    
    end
    else begin
        case(TS_S)
        0:if(cfg_done == 1'b0)
            TS_S <= 2'd1;
        1:if(!iic_busy)begin//write data
            iic_en  <= 1'b1; 
			wr_data[7  :0] <= REG_DATA[23:16];   
			wr_data[15 :8] <= REG_DATA[15: 8];   
			wr_data[23:16] <= REG_DATA[7 : 0];
        end
        else 
            TS_S    <= 2'd2;
        2:begin
            iic_en  <= 1'b0; 
            if(!iic_busy)begin 
			    REG_INDEX<= REG_INDEX + 1'b1;
			    TS_S    <= 2'd3;
            end
        end
        3:begin//read rtc register
            if(REG_INDEX == REG_SIZE)begin
				cfg_done <= 1'b1;
			end
			TS_S 	<= 2'd0;
        end 
    endcase
   end
end

    uii2c#(
        .WMEN_LEN(4),
        .RMEN_LEN(1),
        .CLK_DIV(CLK_DIV)//499 for 50M 999 for 100M
    )
    uii2c_inst(
        .clk_i(clk_i),
        .iic_scl(adv_scl),
        .iic_sda(adv_sda),
        .wr_data(wr_data),
        .wr_cnt(8'd3),//write data max len = 4BYTES
        .rd_data(),   //read not used
        .rd_cnt(8'd0),//read not used
        .iic_mode(1'b0),
        .iic_en(iic_en),
        .iic_busy(iic_busy)
    );
    //7611reg
    ui7611reg  ui7611reg_inst(
        .REG_SIZE(REG_SIZE),
        .REG_INDEX(REG_INDEX),
        .REG_DATA(REG_DATA));   

endmodule

二.HDMI输出

1.TMDS编码

module TMDSEncoder(
    input									    clk		        ,//系统时钟信号;
    input									    rst  	        ,//系统复位信号,高电平有效;

    input				[7 : 0]	                din		        ,//输入待编码数据
    input                                       c0              ,//控制信号C0
    input                                       c1              ,//控制信号c1
    input                                       de              ,//输入数据有效指示信号;
    output reg			[9 : 0]	                q_out	         //编码输出数据
);
    localparam          CTRLTOKEN0      =       10'b1101010100  ;
    localparam          CTRLTOKEN1      =       10'b0010101011  ;
    localparam          CTRLTOKEN2      =       10'b0101010100  ;
    localparam          CTRLTOKEN3      =       10'b1010101011  ;
    
    reg                 [7 : 0]                 din_r           ;//
    reg                 [1 : 0]                 de_r,c0_r,c1_r  ;
    reg                 [3 : 0]                 n1d,n1q_m,n0q_m ;
    reg                 [5 : 0]                 cnt             ;
    reg                 [8 : 0]                 q_m_r           ;

    wire                [8 : 0]                 q_m             ;//
    wire                                        condition1      ;
    wire                                        condition2      ;
    wire                                        condition3      ;
    //统计待编码输入数据中1的个数,最多8个1,所以位宽为4。
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            n1d <= 4'd0;
        end
        else if(de)begin//当DE为高电平,统计输入数据中1的个数。
            n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
        end
        else begin//当DE为低电平时,对控制信号编码,此时不需要统计输入信号中1的个数,故清零。
            n1d <= 4'd0;
        end
    end
    
    //移位寄存器将输入数据暂存,与后续信号对齐。
    always@(posedge clk)begin
        din_r   <= din;
        de_r    <= {de_r[0],de};
        c0_r    <= {c0_r[0],c0};
        c1_r    <= {c1_r[0],c1};
        q_m_r   <= q_m;
    end

    //判断条件1,输入数据1的个数多余4或者1的个数等于4并且最低位为0时拉高,其余时间拉低。
    assign condition1 = ((n1d > 4'd4) || ((n1d == 4'd4) && (~din_r[0])));
    
    //对输入的信号进行异或运算。
    assign q_m[0] = din_r[0];
    assign q_m[1] = condition1 ? ~((q_m[0] ^ din_r[1])) : (q_m[0] ^ din_r[1]);
    assign q_m[2] = condition1 ? ~((q_m[1] ^ din_r[2])) : (q_m[1] ^ din_r[2]);
    assign q_m[3] = condition1 ? ~((q_m[2] ^ din_r[3])) : (q_m[2] ^ din_r[3]);
    assign q_m[4] = condition1 ? ~((q_m[3] ^ din_r[4])) : (q_m[3] ^ din_r[4]);
    assign q_m[5] = condition1 ? ~((q_m[4] ^ din_r[5])) : (q_m[4] ^ din_r[5]);
    assign q_m[6] = condition1 ? ~((q_m[5] ^ din_r[6])) : (q_m[5] ^ din_r[6]);
    assign q_m[7] = condition1 ? ~((q_m[6] ^ din_r[7])) : (q_m[6] ^ din_r[7]);
    assign q_m[8] = ~condition1;
    
    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            n1q_m <= 4'd0;
            n0q_m <= 4'd0;
        end
        else if(de_r[0])begin//对输入有效数据时,q_m中1和0的个数进行统计;
            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'd8 - (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
        else begin//输入数据无效时清零。
            n1q_m <= 4'd0;
            n0q_m <= 4'd0;
        end
    end

    //判断条件2,一行已编码数据中1的个数等于0的个数或者本次编码数据中1的个数等于0的个数。
    assign condition2 = ((cnt == 6'd0) || (n1q_m == n0q_m));
    //判断条件3,已编码数据中1的多余0并且本次编码中间数据1的个数也多与0的个数或者已编码数据中0的个数较多并且此次编码中0的个数也比较多时拉高,其余时间拉低。
    assign condition3 = (((~cnt[5]) && (n1q_m > n0q_m)) || (cnt[5] && (n1q_m < n0q_m)));

    always@(posedge clk)begin
        if(rst)begin//初始值为0;
            cnt <= 6'd0;
            q_out <= 10'd0;
        end
        else if(de_r[1])begin
            q_out[8] <= q_m_r[8];//第8位为编码方式位,直接输出即可。
            if(condition2)begin
                q_out[9] <= ~q_m_r[8];
                q_out[7:0] <= q_m_r[8] ? q_m_r[7:0] : ~q_m_r[7:0];
                //进行cnt的计算;
                cnt <= q_m_r[8] ? (cnt + n1q_m - n0q_m) : (cnt + n0q_m - n1q_m);
            end
            else if(condition3)begin
                q_out[9] <= 1'b1;
                q_out[7:0] <= ~q_m_r[7:0];
                //进行cnt的计算;
                cnt <= cnt + {q_m_r[8],1'b0} + n0q_m - n1q_m;
            end
            else begin
                q_out[9] <= 1'b0;
                q_out[7:0] <= q_m_r[7:0];
                //进行cnt的计算;
                cnt <= cnt - {~q_m_r[8],1'b0} + n1q_m - n0q_m;
            end
        end
        else begin
            cnt <= 6'd0;//对控制信号进行编码时,将计数器清零。
            case ({c1_r[1],c0_r[1]})
                2'b00   : q_out <= CTRLTOKEN0;
                2'b01   : q_out <= CTRLTOKEN1;
                2'b10   : q_out <= CTRLTOKEN2;
                2'b11   : q_out <= CTRLTOKEN3;
            endcase
        end
    end

endmodule

2.并串转换以及差分输出

`timescale 1ns / 1ps

module oserdese2_10to1(
   input                [9:0]             txdata,                                      //输入并行10位数据
   input                                  pclk,                                        //并行数据时钟
   input                                  clkdiv2,                                     //并串转换时钟
   input                                  txrst,                                       //复位信号

   output                                 tx_p,                                        //差分输出
   output                                 tx_n
); 

   wire                 [13:0]            tx_data;
   wire                                   cascade_do, cascade_di, cascade_to, cascade_ti;
   reg                                    int_rst;
   wire                                   dai;

   assign tx_data = {4'd0,txdata[9:0]};                                                //最高支持14位并串转换

   always @(*)
	   if(txrst == 1'b1)
		   int_rst <= 1'b1;
	   else if(pclk)
		   int_rst <= 1'b0;
      else 
         int_rst <= int_rst;
   

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

//----------------------------------------------------------------------------------
//-- Cascaded OSERDES for 10:1 ratio (DDR)
//----------------------------------------------------------------------------------
OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
      .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
      .SERDES_MODE("MASTER"), // MASTER, SLAVE
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
   oserdese2_master (
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(tx_data[0]),
      .D2(tx_data[1]),
      .D3(tx_data[2]),
      .D4(tx_data[3]),
      .D5(tx_data[4]),
      .D6(tx_data[5]),
      .D7(tx_data[6]),
      .D8(tx_data[7]),  
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(1'b0),
      .T2(1'b0),
      .T3(1'b0),
      .T4(1'b0), 
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(cascade_di),
      .SHIFTIN2(cascade_ti),
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(),
      .SHIFTOUT2(),   
      .OCE(1'b1),             // 1-bit input: Output data clock enable    
      .CLK(clkdiv2),        // 1-bit input: High speed clock
      .CLKDIV(pclk),     // 1-bit input: Divided clock                       
      .OQ(dai),               // 1-bit output: Data path output
      .TQ(),               // 1-bit output: 3-state control
      .OFB(),             // 1-bit output: Feedback path for data
      .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate      
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TFB(),             // 1-bit output: 3-state control
      .TCE(1'b0),          // 1-bit input: 3-state clock enable      
      .RST(int_rst)             // 1-bit input: Reset
   );

OSERDESE2 #(
      .DATA_RATE_OQ("DDR"),   // DDR, SDR
      .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
      .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
      .SERDES_MODE("SLAVE"), // MASTER, SLAVE
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
   )
   oserdese2_slave (
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(1'b0),
      .D2(1'b0),
      .D3(tx_data[8]),
      .D4(tx_data[9]),
      .D5(tx_data[10]),
      .D6(tx_data[11]),
      .D7(tx_data[12]),
      .D8(tx_data[13]),  
      // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
      .T1(1'b0),
      .T2(1'b0),
      .T3(1'b0),
      .T4(1'b0), 
      // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
      .SHIFTOUT1(cascade_di),
      .SHIFTOUT2(cascade_ti),         
      // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
      .SHIFTIN1(1'b0),
      .SHIFTIN2(1'b0),
      .OCE(1'b1),             // 1-bit input: Output data clock enable    
      .CLK(clkdiv2),        // 1-bit input: High speed clock
      .CLKDIV(pclk),     // 1-bit input: Divided clock                       
      .OQ(),               // 1-bit output: Data path output
      .TQ(),               // 1-bit output: 3-state control
      .OFB(),             // 1-bit output: Feedback path for data
      .TFB(),             // 1-bit output: 3-state control
      .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate      
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TCE(1'b0),          // 1-bit input: 3-state clock enable      
      .RST(int_rst)             // 1-bit input: Reset
   ); 
    
endmodule

三.顶层模块

`timescale 1ns / 1ps

module top(
    input                                               clk_i,                              //系统时钟
    input                                               hs_i,                               //行同步信号
    input                                               vs_i,                               //场同步信号
    input                                               de_i,                               //数据有效信号
    input                                               pclk_i,                             //像素时钟
    input               [23:0]                          rgb_i,                              //输入图像数据

    output                                              adv_rst,                            //adv7611复位信号
    output                                              adv_scl,                            //时钟总线
    inout                                               adv_sda,                            //数据总线
    output                                              HDMI_CLK_P,                         //差分时钟输出
    output                                              HDMI_CLK_N,
    output              [2:0]                           HDMI_TX_P,                          //差分数据输出
    output              [2:0]                           HDMI_TX_N
);

    assign                                              adv_rst = 1'b1;                     
    wire                                                cfg_done;                           //寄存器配置完成信号
    wire                                                locked ;                            //锁相环工作完成信号
    wire                                                pclkx1,pclkx5;                      //锁相环输出时钟
    reg                                                 hs_r_0,hs_r_1,hs_r;                 
    reg                                                 vs_r_0,vs_r_1,vs_r;
    reg                                                 de_r_0,de_r_1,de_r;
    reg                 [23:0]                          rgb_r_0;
    reg                 [23:0]                          rgb_r_1;
    reg                 [23:0]                          rgb_r;
    wire                                                rst_o1,rst_o2;
    wire                                                vid_rst,vid_clk,vid_vs,vid_hs,vid_de;
    reg                                                 tpg_vs_r = 1'b0;
    reg                                                 tpg_hs_r = 1'b0;


clk_wiz_0 clk_wiz_inst(
    .clk_out1(pclkx1),
    .clk_out2(pclkx5),
    .locked(locked), 
    .clk_in1(pclk_i));

uihdmitx uihdmitx_inst
(
.RSTn_i(cfg_done&&locked),
.HS_i(hs_i),
.VS_i(vs_i),
.VDE_i(de_i),
.RGB_i({rgb_i[7:0],rgb_i[15:8],rgb_i[23:16]}),
.PCLKX1_i(pclkx1),
.PCLKX2_5_i(1'b0),
.PCLKX5_i(pclkx5),
.TMDS_TX_CLK_P(HDMI_CLK_P),
.TMDS_TX_CLK_N(HDMI_CLK_N),
.TMDS_TX_P(HDMI_TX_P),
.TMDS_TX_N(HDMI_TX_N)
);

uicfg7611 uicfg7611_inst
(
.clk_i(clk_i),
.rst_n(1'b1), 
.adv_scl(adv_scl),
.adv_sda(adv_sda),
.cfg_done(cfg_done)
);


 
endmodule

四.板级验证

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值