天天看點

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擷取

繼續閱讀