天天看点

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

目录

  • 1、前言
  • 2、设计思路和框架
    • TMDS 编码算法
    • OSERDESE串并转换
  • 3、顶层源码和IP封装
  • 4、源码和IP获取

1、前言

本设计使用Xilinx原语和自己手写的代码实现了HDMI发送功能,纯verilog手写,有源码,也提供封装好的IP,你喜欢用例化的方式就用源码,你喜欢搭建BD工程就用IP,目前IP的适应器件为zynq,如果是用7系列FPGA的兄弟可以改一下芯片型号即可使用。

这个HDMI发送模块我已在项目中多次使用,稳定性和实用性可以保证,最大支持[email protected]帧率。

本文详细描述了设计方案,工程代码编译通过后上板调试验证,可直接项目移植,适用于在校学生、研究生项目开发,也适用于在职工程师做项目开发,可应用于医疗、军工等行业的数字成像和图像传输领域;

提供完整的、跑通的工程源码和技术支持;

工程源码和技术支持的获取方式放在了文章末尾,请耐心看到最后;

关于HDMI的理论介绍这里不多说,请自行搜索,很简单,本文只讲如何代码层级实现的干货。

2、设计思路和框架

HDMI编码框图如下:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

Encoder 模块负责对数据进行编码,Serializer 模块对编码后的数据进行并串转换,最后

通过 OBUFDS 转化成 TMDS 差分信号传输。

整个系统需要两个输入时钟,一个是视频的像素时钟 Pixel Clk,另外一个时钟 Pixel Clk x5 的频率是像

素时钟的五倍。由前面的简介部分我们知道,并串转换过程的实现的是 10:1 的转换率,理论上转换器需要一个 10 倍像素时钟频率的串行时钟。这里我们只用了一个 5 倍的时钟频率,这是因为 OSERDESE2 模块可以实现 DDR 的功能,即它在五倍时钟频率的基础上又实现了双倍数据速率。

TMDS 连接的时钟通道我们采用与数据通道相同的并转串逻辑来实现。通过对 10 位二进制序列10’b11111_00000 在 10 倍像素时钟频率下进行并串转换,就可以得到像素时钟频率下的 TMDS 参考时钟。

另外需要注意的是,图中左下脚 HDMI 的音频/附加数据输入在本次实验中并未用到,因此以虚线表示。

TMDS 编码算法

Encoder 模块完成TMDS 编码算法,dvi_encoder 模块按照 DVI 接口规范中 TMDS 编码算法对输入的 8 位像素数据以及 2 位行场同步信号进行编码。该模块是 Xilinx 应用笔记 XAPP460 中所提供的编码模块,其具体实现的编码算法如下图所示:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

TMDS 通过逻辑算法将 8 位字符数据通过最小转换编码为 10 位字符数据,前 8 位数据由原始信号经运

算后获得,第 9 位表示运算的方式,1 表示异或 0 表示异或非。经过 DC 平衡后(第 10 位),采用差分信号传输数据。第 10 位实际是一个反转标志位,1 表示进行了反转而 0 表示没有反转,从而达到 DC 平衡。

接收端在收到信号后,再进行相反的运算。TMDS 和 LVDS、TTL 相比有较好的电磁兼容性能。这种算

法可以减小传输信号过程的上冲和下冲,而 DC 平衡使信号对传输线的电磁干扰减少,可以用低成本的专用电缆实现长距离、高质量的数字信号传输。

算法中各个参数的含义如下图所示:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

Encoder 模块顶层接口如下:

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

代码部分并不复杂,就是将TMDS 编码算法的流程图翻译成了 verilog语言。

OSERDESE串并转换

TMDS 编码之后的数据由 Serializer 模块进行并串转换,通过调用 OSERDESE2 原语来实现 10:1 的并串转换。原语是 Xilinx 器件底层硬件中的功能模块,它使用专用的资源来实现一系列的功能。相比于 IP 核,原语的调用方法更简单,但是一般只用于实现一些简单的功能。

需要注意的是,一个 OSERDESE2 只能实现最多 8:1 的转换率,在这里我们通过位宽扩展实现了 10:1

的并串转换,如下图所示:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

OSERDESE2 位宽扩展通过两个 OSERDESE2 模块来实现,其中一个作为 Master,

另一个作为 Slave,通过这种方式最多可实现 14:1 的并串转换。需要注意的是,在位宽扩展时,Slave 模块的数据输入端只能使用 D3 至 D8。

OSERDESE串并转换模块顶层接口如下:

module serializer_10_to_1(
    input              reset,              
    input              paralell_clk,       
    input              serial_clk_5x,    
    input   [9:0]   paralell_data,     
    output 		 serial_data_out    
    );
           

3、顶层源码和IP封装

将各个功能模块进行例化,即完成了本设计,为了适应不同的使用需求,提供源码的同时也将源码进行了IP封装,两者一并提供。

顶层源码如下:

module helai_hdmi_out(
	input         clk_hdmi     ,
	input         clk_hdmix5   ,
	input         reset_n      ,
	input         i_vga_hs     ,
	input         i_vga_vs     ,
	input         i_vga_de     ,	
    input  [23:0] i_vga_rgb    ,   
    output        o_hdmi_clk_p ,
    output        o_hdmi_clk_n ,
    output [2: 0] o_hdmi_data_p,
	output [2: 0] o_hdmi_data_n            
);
   
wire        reset;
    
//²¢ÐÐÊý¾Ý
wire [9:0]  red_10bit;
wire [9:0]  green_10bit;
wire [9:0]  blue_10bit;
wire [9:0]  clk_10bit;  
  
//´®ÐÐÊý¾Ý
wire [2:0]  tmds_data_serial;
wire        tmds_clk_serial;

//*****************************************************
//**                    main code
//*****************************************************   
assign clk_10bit = 10'b1111100000;

//Òì²½¸´Î»£¬Í¬²½ÊÍ·Å
asyn_rst_syn reset_syn(
    .reset_n    (reset_n),
    .clk        (clk_hdmi),
    .syn_reset  (reset)    //¸ßÓÐЧ
    );
  
//¶ÔÈý¸öÑÕɫͨµÀ½øÐбàÂë
dvi_encoder encoder_b (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din        (i_vga_rgb[7:0]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(blue_10bit)
    ) ;

dvi_encoder encoder_g (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din		(i_vga_rgb[15:8]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(green_10bit)
    ) ;
    
dvi_encoder encoder_r (
    .clkin      (clk_hdmi),
    .rstin	    (reset),
    
    .din		(i_vga_rgb[23:16]),
    .c0			(i_vga_hs),
    .c1			(i_vga_vs),
    .de			(i_vga_de),
    .dout		(red_10bit)
    ) ;
    
//¶Ô±àÂëºóµÄÊý¾Ý½øÐв¢´®×ª»»
serializer_10_to_1 serializer_b(
    .reset              (reset),                // ¸´Î»,¸ßÓÐЧ
    .paralell_clk       (clk_hdmi),                 // ÊäÈë²¢ÐÐÊý¾ÝʱÖÓ
    .serial_clk_5x      (clk_hdmix5),              // ÊäÈë´®ÐÐÊý¾ÝʱÖÓ
    .paralell_data      (blue_10bit),           // ÊäÈë²¢ÐÐÊý¾Ý
//    .paralell_data      (10'h146),           // ÊäÈë²¢ÐÐÊý¾Ý

    .serial_data_out    (tmds_data_serial[0])   // Êä³ö´®ÐÐÊý¾Ý
    );    
    
serializer_10_to_1 serializer_g(
    .reset              (reset),
    .paralell_clk       (clk_hdmi),
    .serial_clk_5x      (clk_hdmix5),
    .paralell_data      (green_10bit),
 //     .paralell_data      (10'h146),   

    .serial_data_out    (tmds_data_serial[1])
    );
    
serializer_10_to_1 serializer_r(
    .reset              (reset),
    .paralell_clk       (clk_hdmi),
    .serial_clk_5x      (clk_hdmix5),
    .paralell_data      (red_10bit),
 //    .paralell_data      (10'h146),    

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

    .serial_data_out    (tmds_clk_serial)
    );
    
//ת»»²î·ÖÐźŠ 
OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS0 (
    .I                  (tmds_data_serial[0]),
    .O                  (o_hdmi_data_p[0]),
    .OB                 (o_hdmi_data_n[0]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS1 (
    .I                  (tmds_data_serial[1]),
    .O                  (o_hdmi_data_p[1]),
    .OB                 (o_hdmi_data_n[1]) 
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS2 (
    .I                  (tmds_data_serial[2]), 
    .O                  (o_hdmi_data_p[2]), 
    .OB                 (o_hdmi_data_n[2])  
);

OBUFDS #(
    .IOSTANDARD         ("TMDS_33")    // I/Oµçƽ±ê׼ΪTMDS
) TMDS3 (
    .I                  (tmds_clk_serial), 
    .O                  (o_hdmi_clk_p),
    .OB                 (o_hdmi_clk_n) 
);
  
endmodule
           

封装后的IP如下:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

4、源码和IP获取

福利:工程代码的获取

代码太大,无法邮箱发送,以某度网盘链接方式发送,

资料获取方式:点击获取

网盘资料如下:

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取
FPGA纯verilog手写HDMI发送IP 提供源码和技术支持1、前言2、设计思路和框架3、顶层源码和IP封装4、源码和IP获取

继续阅读