HDMI接口介绍及其代码

发布于:2022-12-20 ⋅ 阅读:(1167) ⋅ 点赞:(0)

一、初衷

HDMI是之前项目的一部分,本来不打算上传博客的,最近我们专业课设开始,恰好可以选择完成HDMI显示,也重新整理一下之前的项目,对于课设,这只是实现了基本功能,附加条件等过几天再更;希望这篇博客能帮助更多的初学者,结尾附代码;

二、开发平台

这个项目开发板我使用的是Xilinx的zynq-7030,读者可进行更改管脚进行更换;调用了vivado的pll以及ROM的IP;读者需要进行配置,在此,我就不做介绍,请自行百度;

三、项目介绍

先将图片转为coe文件存入ROM中,从ROM中读取24位真彩数据通过HDMI进行显示,显示的大小设置为1280*1024,如下所示:

 图片大小为500*500;读者可根据需要自行更改;

由于我正在一家公司实习,开发板无法使用,故没有展示环节,代码曾经下过开发板了,可以正常使用;故只提供仿真波形,如果有问题可以留言,我看到就会回复;

四、HDMI接口

 英文全称:H-High,D-Definition,M-Multimedia,I-Interface;高清晰度多媒体接口。

HDMI的数据传输有TMDS0,TMDS1,TMDS2三个通道,每个通道的传输流程都是一样的:

  

如上图所示,发送端将8bit的数据进入TMDS编码器,得到抗干扰性强的10bit TMDS信号,然后再进行串行化输出,在接收端收到串行的HDMI信号后,进行信号复原,得到10bit的TMDS信号,最后用TMDS解码器解码得到原来的8bit数据。

本项目使用的是1.3版本的HDMI接口,它支持的最大速率为10.2Gbps,而1.3版本的一个HDMI包括3个TMDS数据通道和1个TMDS时钟通道。什么是TMDS呢?

——TMDS(Transition Minimized Differential signal),最小化传输差分信号。

下图能很好的解释差分信号的优势:

                           

它能很好的屏蔽噪声的影响;

实际上,我们只需要完成发送端这部分,即:

               第一步:将8位并行RED数据发送到TMDS Tx。

       第二步:进行最小化传输处理,加上第9位,即编码过程。第9位数据称为编码位。

       第三步:并/串转换。   

               第四步:串行转差分          

对第二步进行补充:每个数据通道都通过编码算法(异或、异或非等),将8位数据转换成10位数据,前8位数据由原始信号经运算后获得,第9位指示运算的方式,第10位用来对应直流平衡。通过这种算法,会使得数据的传输和恢复更加可靠。

对传输速率及时钟的一些补充:

PCLK:像素时钟
以1920x1080p/60hz为例:1920*1080*60=124.4MHz
以1280x720p/60hz为例:1280*720*60=55.3MHz

带宽:1s内传输的数据量(bit)

4K频率需要的带宽:选最常用的3840x2160分辨率,色深的话常用的是8位,RGB三色就是24bit,我们的目标是60Hz刷新率(60fps)→→→ 3840*2160*24bit*60fps=11.94Gbps

HDMI1.3/1.4版本像素时钟高达340MHz,即最大带宽是:
       →→→ 340MHz*10bit(10bit编码)*3(3个数据通道)=10.2Gbps

但是由于HDMI采用的是8bit/10bit编码方式,实际效率是理论值的80%,所以10.2Gbps能传输的最大视频带宽是10.2*0.8=8.1Gbps

借鉴:(38条消息) HDMI接口简介---分辨率 时钟频率 lane速率计算_打怪升级ing的博客-CSDN博客_hdmi带宽计算https://blog.csdn.net/weixin_42229404/article/details/80461700

 则本项目

              -   频率:1280*1024*60 = 78.6Mhz,为什么不一样?我觉得可能是图像无效区也应该是进去;

          - 实际频率:1688*1066*60 = 107.9Mhz,与定义的相同;

              -  速率:108*10*3 = 3.24Gpbs;

五、代码讲解

       (1)顶层:

             由于我使用的开发板输入的是差分时钟,所以输入时钟是两对;

             注意观察可以发现,有个540M的时钟;由于将并行的10bit数据转换为串行的数据,时钟频率需要快10倍;但是是差分输出,所以只需要5倍时钟,即540M;   

//**************************************************************************
// *** 名称 : hdmixs_top
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : top
//**************************************************************************

`timescale 1ns / 1ps  
module hdmixs_top(
    input                                        sys_clk_n ,//差分时钟输入
	input                                        sys_clk_p ,
	input                                        sys_rst_n ,
	
	output                                       tmds_clk_n,//TMDS时钟通道
	output                                       tmds_clk_p,
	output         [2:0]                         tmds_data_n,//TMDS数据通道
	output         [2:0]                         tmds_data_p
 );

wire                                             rst_n   ;
wire                                             clk_out1;
wire                                             clk_out2;
wire                                             locked;
wire                                             hdmi_hs;
wire                                             hdmi_vs;
wire              [10:0]                          w_x_pixel;
wire              [10:0]                          w_y_pixel;
// wire [15:0] pixel_data;
wire                                             hdmi_de;
wire              [23:0]                         rgb_data;
wire                                             tmds_oen;//TMDS输出使能
wire              [23:0]                         pixel_data;


assign rst_n=sys_rst_n&locked;               //在保证时钟稳定的情况下,才保证正常工作
assign clk_out1 = sys_clk_n;
assign clk_out2 = sys_clk_p;
	
clk_wiz_0 clk_div(
      .clk_out1                                  (clk_108M         ),   //输出108MHZ的频率
	  .clk_out2                                  (clk_540M         ), 
	  .reset                                     (1'b0             ), 
      .locked                                    (locked           ),
      .clk_in1_p                                 (sys_clk_p        ),
	  .clk_in1_n                                 (sys_clk_n        )
);

hdmixs_driver dri(
      .clk_out1                                  (clk_108M         ),
	  .rst_n                                     (rst_n            ),
	  .pixel_data                                (pixel_data       ),
	  .hdmi_hs                                   (hdmi_hs          ),
	  .hdmi_vs                                   (hdmi_vs          ),
	  .hdmi_de                                   (hdmi_de          ),
	  .vedio_rgb                                 (rgb_data         ),
	  .x_pixel                                   (w_x_pixel        ),
	  .y_pixel                                   (w_y_pixel        )
);
	  
hdmixs_display dis(
      .clk_out1                                  (clk_108M         ),
	  .rst_n                                     (rst_n            ),
	  .x_pixel                                   (w_x_pixel        ),
	  .y_pixel                                   (w_y_pixel        ),
	  
	  .pixel_data                                (pixel_data       )
);
	  
rgb_to_hdmi urtg(
      .clk_out1                                  (clk_108M         ),  //输出108MHZ的频率
	  .clk_out2                                  (clk_540M         ), 
      .rst_n                                     (rst_n            ),
	  .hdmi_de                                   (hdmi_de          ),
	  .pixel_data                                (rgb_data         ),
	  .hdmi_hs                                   (hdmi_hs          ),
	  .hdmi_vs                                   (hdmi_vs          ),
      .tmds_clk_n                                (tmds_clk_n       ),
	  .tmds_clk_p                                (tmds_clk_p       ),
	  .tmds_oen                                  (tmds_oen         ),
      .tmds_data_n                               (tmds_data_n      ),
	  .tmds_data_p                               (tmds_data_p      )
);


endmodule

       (2) 显示驱动设置:

               此模块可以定义显示的屏幕设置,以及输出当前行列计数值和图像数据;

//**************************************************************************
// *** 名称 : hdmixs_driver
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 显示大小设置
//**************************************************************************

`timescale 1ns / 1ps
module hdmixs_driver(
    input                    clk_out1  ,
    input                    rst_n     ,
    input      [23:0]        pixel_data,
         
    output      reg          hdmi_de   ,
    output                   hdmi_hs   ,	
    output                   hdmi_vs   ,
    output     [23:0]        vedio_rgb ,
    output     [ 10:0]       y_pixel   ,  //y像素坐标
    output     [ 10:0]       x_pixel      //x像素坐标
    );

//1280*1024@60Hz 108Mhz
parameter Hor_Total_Time              =      11'd1688;		//
parameter Hor_Sync                    =      10'd112;	    //
parameter Hor_Back_Porch              =      10'd248;		//	
parameter Hor_Addr_Time               =      11'd1280;		//
parameter Hor_Front_Porch             =      10'd48;		//
									         
parameter Ver_Total_Time              =      11'd1066;		//
parameter Ver_Sync                    =      10'd3;		    //
parameter Ver_Back_Porch              =      10'd38;		//	
parameter Ver_Addr_Time               =      11'd1024;		//
parameter Ver_Front_Porch             =      10'd1;		    // 

//行列计数器
reg [10:0] x_cnt;
reg [10:0] y_cnt;

always@(posedge clk_out1 or negedge rst_n)
    begin
    	if(!rst_n)
    	begin 
    		x_cnt<= 11'd0;
    		y_cnt<= 11'd0;
        end 
    	else if(x_cnt==Hor_Total_Time)
    	begin 
    		x_cnt<= 11'd0;
    		if(y_cnt==Ver_Total_Time)
    		  y_cnt<=11'd0;
    		else
    		  y_cnt<= y_cnt+1'b1;
    	end
    	else 
    		x_cnt<= x_cnt + 1'b1;
    			
    end

assign hdmi_hs=(x_cnt < Hor_Sync)?1'b0:1'b1;
assign hdmi_vs=(y_cnt < Ver_Sync)?1'b0:1'b1;

always@(*)     //使能、以及得到像素坐标
    begin
      hdmi_de<=(x_cnt>(Hor_Sync + Hor_Back_Porch)&&
                x_cnt<=(Hor_Sync+Hor_Back_Porch+Hor_Addr_Time)&&
                y_cnt>(Ver_Sync+Ver_Back_Porch)&&
                y_cnt<=(Ver_Sync+Ver_Back_Porch+Ver_Addr_Time));
    end

assign vedio_rgb=(hdmi_de)?pixel_data:24'd0;
assign x_pixel=(hdmi_de)?(x_cnt-(Hor_Sync + Hor_Back_Porch)):10'd0;        //行像素坐标
assign y_pixel=(hdmi_de)?(y_cnt-(Ver_Sync + Ver_Back_Porch)):10'd0;       //列像素坐标

endmodule

       (3)图像显示模块

                此模块可以设置需要显示的图片大小及位置,并输出图像;

//**************************************************************************
// *** 名称 : hdmixs_display
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 显示图像区域设置
//**************************************************************************
`timescale 1ns / 1ps  
module hdmixs_display(
    input           wire                clk_out1  ,
    input           wire                rst_n     ,
    input           wire    [10:0]      x_pixel   ,
    input           wire    [10:0]      y_pixel   ,
									              
    output          reg     [23:0]      pixel_data
);

parameter height = 10'd500, width  = 10'd500;
parameter pos_x0 = 320, pos_y0 = 272;// picture  
                        
wire               rom_rd_en0;        //读ROM使能信号  
wire [32:0]        total;
wire [23:0]        rom_data0;

reg  [23:0]        rom_addr0;  //读ROM有效信号 

assign total = height*width;

assign rom_rd_en0 =(x_pixel > pos_x0)&&(x_pixel<=pos_x0+width)&&(y_pixel>pos_y0)&&(y_pixel<=pos_y0+height)?1'b1:1'b0;       //first picture location


always @(posedge clk_out1 or  negedge rst_n)
    begin 
        if(!rst_n)
        begin
            pixel_data  <=  24'b0;
        end
        else if(rom_rd_en0)
            pixel_data  <=  rom_data0;
        else
            pixel_data  <=  24'h000_000;
    end

always@(posedge clk_out1 or negedge rst_n)
    begin
    if(!rst_n)
        rom_addr0<=24'd0;
    else if(rom_rd_en0)
        begin
            if(rom_addr0<total-1'b1)
                rom_addr0<=rom_addr0+1'b1;
            else 
                rom_addr0<=24'd0;
        end
    else 
        rom_addr0<=rom_addr0;
    end

 blk_mem_gen_0 rom1(
     .addra(rom_addr0),
	 .ena(1),
     .clka(clk_out1),
     .douta(rom_data0)
 );	



endmodule

 

        (4)rgb2hdmi模块

                这个模块例化了其他两个子模块;实现了8bit/10bit,以及并行转串行;并使用原语OBUFDS进行串行转差分;

//**************************************************************************
// *** 名称 : rgb_to_hdmi
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 差分输出
//**************************************************************************

`timescale 1ns / 1ps
module rgb_to_hdmi(
    input                     clk_out1    ,
    input                     clk_out2    ,
    input                     rst_n       ,
    input                     hdmi_de     ,
    input     [24:0]          pixel_data  ,
    input                     hdmi_hs     ,
    input                     hdmi_vs     ,
            
    output                    tmds_clk_n  ,
    output                    tmds_clk_p  ,  
    output                    tmds_oen    ,  
    output     [2:0]          tmds_data_n ,
    output     [2:0]          tmds_data_p 
    );
   
   wire        [9:0]             clk_10bit;
   wire                          syn_rst;
   wire        [9:0]             encode_red;
   wire        [9:0]             encode_green;
   wire        [9:0]             encode_blue;
   wire        [2:0]             serial_data;
   wire                          serial_out_clk;

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

asyn_to_syn syn(
.clk(clk_out1),
.rst_n(rst_n),
.syn_rst(syn_rst)
);

encoder r(
         .clkin(clk_out1),    
		 .rstin(syn_rst),   
		 .din(pixel_data[23:16]),    
		 .c0(1'b0),     
		 .c1(1'b0),      
		 .de(hdmi_de),      
		 .dout(encode_red)     
);

encoder g(
         .clkin(clk_out1),    
		 .rstin(syn_rst),   
		 .din(pixel_data[15:8]),    
		 .c0(1'b0),     
		 .c1(1'b0),      
		 .de(hdmi_de),      
		 .dout(encode_green)     
);

encoder b(
         .clkin(clk_out1),    
		 .rstin(syn_rst),   
		 .din(pixel_data[7:0]),    
		 .c0(hdmi_hs),     
		 .c1(hdmi_vs),      
		 .de(hdmi_de),      
		 .dout(encode_blue)     
);

serializer red(
        .paralell_data(encode_red),
        .paralell_clk(clk_out1),
        .reset(syn_rst),
        .serial_clk(clk_out2),
        .serial_data_out(serial_data[2])
);

serializer green(
        .paralell_data(encode_green),
        .paralell_clk(clk_out1),
        .reset(syn_rst),
        .serial_clk(clk_out2),
        .serial_data_out(serial_data[1])
);

serializer blue(
        .paralell_data(encode_blue),
        .paralell_clk(clk_out1),
        .reset(syn_rst),
        .serial_clk(clk_out2),
        .serial_data_out(serial_data[0])
);

serializer clk(
        .paralell_data(clk_10bit),
        .paralell_clk(clk_out1),
       .reset(syn_rst),
       .serial_clk(clk_out2),
       .serial_data_out(serial_out_clk)
);

OBUFDS #(
 .IOSTANDARD("TMDS_33"), // Specify the output I/O standard
 .SLEW("SLOW") // Specify the output slew rate
) TMDS0 (
 .O(tmds_data_p[0]), // Diff_p output (connect directly to top-level port)
 .OB(tmds_data_n[0]), // Diff_n output (connect directly to top-level port)
 .I(serial_data[0]) // Buffer input
);

OBUFDS #(
 .IOSTANDARD("TMDS_33"), // Specify the output I/O standard
 .SLEW("SLOW") // Specify the output slew rate
) TMDS1 (
 .O(tmds_data_p[1]), // Diff_p output (connect directly to top-level port)
 .OB(tmds_data_n[1]), // Diff_n output (connect directly to top-level port)
 .I(serial_data[1]) // Buffer input
);

OBUFDS #(
 .IOSTANDARD("TMDS_33"), // Specify the output I/O standard
 .SLEW("SLOW") // Specify the output slew rate
) TMDS2 (
 .O(tmds_data_p[2]), // Diff_p output (connect directly to top-level port)
 .OB(tmds_data_n[2]), // Diff_n output (connect directly to top-level port)
 .I(serial_data[2]) // Buffer input
);

OBUFDS #(
 .IOSTANDARD("TMDS_33"), // Specify the output I/O standard
 .SLEW("SLOW") // Specify the output slew rate
) TMDS_clk (
 .O(tmds_clk_p), // Diff_p output (connect directly to top-level port)
 .OB(tmds_clk_n), // Diff_n output (connect directly to top-level port)
 .I(serial_out_clk) // Buffer input
);

endmodule

       (5)编码模块:

                    实现了8bit/10bit

//**************************************************************************
// *** 名称 : encoder
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 编码模块
//**************************************************************************
`timescale 1ns / 1ps
module encoder(
  input                  clkin,    // pixel clock input
  input                  rstin,    // async. reset input (active high)
  input      [7:0]       din,      // data inputs: expect registered
  input                  c0,       // c0 input
  input                  c1,       // c1 input
  input                  de,       // de input

  output reg [9:0]       dout      // data outputs
);

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

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

    din_q <=#1 din;
  end

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

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

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

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

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

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

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

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

    q_m_reg <=#1 q_m;
  end

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

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

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

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

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

endmodule

       (6)并行数据转串行

               使用原语OSERDESE2进行串行转差分操作;

//**************************************************************************
// *** 名称 : serializer
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 并行数据转串行
//**************************************************************************

`timescale 1ns / 1ps
module serializer(
   	input                reset       ,
	  input                paralell_clk,    //并行
	  input [9:0]          paralell_data,
	  input                serial_clk,     //串行,比并行数据高
         
	  output               serial_data_out
    );

	wire                    cascade1;
	wire                    cascade2;

  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
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
    )
  OSERDESE2_MAS (
	  .CLK(serial_clk),             // 1-bit input: High speed clock
      .CLKDIV(paralell_clk),       // 1-bit input: Divided clock
      .RST(reset),             // 1-bit input: Reset
      .OCE(1'b1),             // 1-bit input: Output data clock enable
	    .OQ(serial_data_out),               // 1-bit output: Data path output
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(paralell_data[0]),
      .D2(paralell_data[1]),
      .D3(paralell_data[2]),
      .D4(paralell_data[3]),
      .D5(paralell_data[4]),
      .D6(paralell_data[5]),
      .D7(paralell_data[6]),
      .D8(paralell_data[7]),
	  
	    .SHIFTOUT1(),
      .SHIFTOUT2(),
      .SHIFTIN1(cascade1),	  
      .SHIFTIN2(cascade2),
	  
	  .OFB(),             // 1-bit output: Feedback path for data
      // 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
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TFB(),             // 1-bit output: 3-state control
      .TQ()               // 1-bit output: 3-state control
    );

    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
      .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
      .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
      .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
    )
    OSERDESE2_SLA (
	  .CLK(serial_clk),             // 1-bit input: High speed clock
      .CLKDIV(paralell_clk),       // 1-bit input: Divided clock
      .RST(reset),             // 1-bit input: Reset
      .OCE(1'b1),             // 1-bit input: Output data clock enable
	  .OQ(),               // 1-bit output: Data path output
      // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
      .D1(1'b0),
      .D2(1'b0),
      .D3(paralell_data[8]),
      .D4(paralell_data[9]),
      .D5(1'b0),
      .D6(1'b0),
      .D7(1'b0),
      .D8(1'b0),

	  .SHIFTOUT1(cascade1),
      .SHIFTOUT2(cascade2),
      .SHIFTIN1(),	  
      .SHIFTIN2(),
	  
	  .OFB(),             // 1-bit output: Feedback path for data
      // 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
      .TBYTEOUT(),   // 1-bit output: Byte group tristate
      .TFB(),             // 1-bit output: 3-state control
      .TQ()              // 1-bit output: 3-state control
    );

endmodule

       (7)同步模块

                   对复位进行同步处理(异步复位,同步释放);

//**************************************************************************
// *** 名称 : asyn_to_syn
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 同步处理模块
//**************************************************************************
`timescale 1ns / 1ps
module asyn_to_syn(
   input      clk,
   input      rst_n,
   output     syn_rst
  );

reg rst_1;
reg rst_2;

assign syn_rst=rst_2;
always @(posedge clk or negedge rst_n)
    begin
      if(!rst_n)
      begin 
        rst_1<=1'b1;
    	  rst_2<=1'b1;
      end
      else
      begin 
        rst_1<=1'b0;
    	  rst_2<=rst_1;
      end
    end

endmodule

六、Testbench编写

          由于使用的是差分时钟,使用需要产生一个差分时钟:

//**************************************************************************
// *** 名称 : tb_hdmixs_top
// *** 作者 : 不想上体育课
// *** 博客 : https://blog.csdn.net/m0_47220307?type=blog
// *** 日期 : 2022.09
// *** 描述 : 测试模块
//**************************************************************************
·timescale 1ns/1ps
module tb_hdmixs_top();

reg                                          sys_clk_n   ;
reg                                          sys_clk_p   ;
reg                                          sys_rst_n   ;

wire                                         tmds_clk_n  ;//TMDS时钟通道
wire                                         tmds_clk_p  ;
wire           [2:0]                         tmds_data_n ;//TMDS数据通道
wire           [2:0]                         tmds_data_p ;

initial
    begin
	    sys_clk_p = 1'b0;
	    sys_clk_n = 1'b1;
		sys_rst_n <= 1'b0;
		#20
		sys_rst_n <= 1'b1;
	end
	
always #2.5 sys_clk_p = ~sys_clk_p;
always #2.5 sys_clk_n = ~sys_clk_n;

hdmixs_top hdmixs_top_inst(
     .sys_clk_n   (sys_clk_n  ),
     .sys_clk_p   (sys_clk_p  ),
	 .sys_rst_n   (sys_rst_n  ),
	 
	 .tmds_clk_n  (tmds_clk_n ),//TMDS时钟通道
	 .tmds_clk_p  (tmds_clk_p ),
	 .tmds_data_n (tmds_data_n),//TMDS数据通道
	 .tmds_data_p (tmds_data_p)
 );

endmodule
		

七、Vivado仿真结果与COE对比

        读取的数据与COE文件一致。

本文含有隐藏内容,请 开通VIP 后查看