之前的博客詳細介紹了 VGA 的原理和基本使用,這次換成 HDMI,增加一個技能點。
一、HDMI 基本介紹
HDMI 向下兼容 DVI,但是 DVI(數字視頻接口)只能用來傳輸視頻,而不能同時傳輸音頻,這是兩者最主要的差別。此外,DVI 接口的尺寸明顯大於 HDMI 接口,如下圖所示:
HDMI 的引腳定義如下:

TMDS(Transition Minimized Differential Signaling,最小化傳輸差分信號)是美國 Silicon Image 公司開發的一項高速數據傳輸技術,在 DVI 和 HDMI 視頻接口中使用差分信號傳輸高速串行數據。
TMDS 差分傳輸技術使用兩個引腳(如圖中的“數據 2+”和“數據 2-”)來傳輸一路信號,利用這兩個引腳間的電壓差的正負極性和大小來決定傳輸數據的數值(0 或 1)。TMDS 傳輸系統分為兩個部分:發送端和接收端。 TMDS 鏈路包括 3 個傳輸 RGB 信號的數據通道和 1 個傳輸時鍾信號的通道。TMDS 發送端對這些數據進行編碼和並/串轉換,再將數據分別分配到獨立的傳輸通道發送出去。接收端接收來自發送端的串行信號,對其進行解碼和串/並轉換,然后發送到顯示器的控制端。與此同時也接收時鍾信號,以實現同步。每一個數據通道都通過編碼算法,將 8 位數據轉換成最小化傳輸、直流平衡的 10 位數據。這使得數據的傳輸和恢復更加可靠。最小化傳輸差分信號是通過異或及異或非等邏輯算法將原始 8 位信號數據轉換成 10 位,前 8 為數據由原始信號經運算后獲得,第 9 位指示運算的方式,第 10 位用來對應直流平衡。如下圖所示:

一般來說,HDMI 傳輸的編碼格式中要包含視頻數據、控制數據和數據包(數據包中包噸音頻數據和附加信息數據,例如糾錯碼等)。 TMDS 每個通道在傳輸時要包含一個 2bit 的控制數據、 8bit 的視頻數據或者 4bit 的數據包即可。在 HDMI 信息傳輸過程中,可以分為三個階段:視頻數據傳輸周期、控制數據傳輸周期和數據島傳輸周期,分別對應上述的三種數據類型。
1、傳輸最小化
8 位數據經過編碼和直流平衡得到 10 位最小化數據,這仿佛增加了冗余位,對傳輸鏈路的帶寬要求更高,但事實上,通過這種算法得到的 10 位數據在更長的同軸電纜中傳輸的可靠性增強了。下圖是一個例子,說明對一個 8 位的並行 RED 數據編碼、並/串轉換。
(1)將 8 位並行 RED 數據發送到 TMDS 収送端。
(2)並/串轉換。
(3)進行最小化傳輸處理,加上第 9 位,即編碼過程。第 9 位數據稱為編碼位。
2、直流平衡
直流平衡(DC-balanced)就是指在編碼過程中保證信道中直流偏移為零。方法是在原來的 9 位數據癿后面加上第 10 位數據,返樣,傳輸的數據趨於直流平衡,使信號對傳輸線的電磁干擾減少,提高信號傳輸的可靠性。
3. 差分信號
TMDS 差分傳動技術是一種利用2個引腳間電壓差來傳送信號的技術。傳輸數據的數值(“0”或者“1”)由兩腳間電壓正負極性和大小決定。即,采用 2 根線來傳輸信號,一根線上傳輸原來的信號,另一根線上傳輸與原來信號相反的信號。這樣接收端就可以通過讓一根線上的信號減去另一根線上的信號的方式來屏蔽電磁干擾,從而得到正確的信號。
另外,還有一個顯示數據通道(DDC),是用於讀取表示接收端顯示器的清晰度等顯示能力的擴展顯示標識數據(EDID)的信號線。搭載 HDCP(High-bandwidth Digital Content Protection,高帶寬數字內容保護技術)的發送、接收設備之間也利用 DDC 線進行密碼鍵的認證。
二、DVI 和 HDMI

上圖是 TMDS 發送端和接收端的連接示意圖。DVI 或 HDMI 視頻傳輸所使用的 TMDS 連接通過四個串行通道實現。對於DVI來說,其中三個通道分別用於傳輸視頻中每個像素點的紅、綠、藍三個顏色分量(RGB4:4:4格式)。HDMI 默認也是使用三個 RGB 通道,但是它同樣可以選擇傳輸像素點的亮度和色度信息(YCrCb4:4:4或YCrCb 4:2:2格式)。第四個通道是時鍾通道,用於傳輸像素時鍾。獨立的 TMDS 時鍾通道為接收端提供接收的參考頻率,保證數據在接收端能夠正確恢復。
在傳輸視頻圖像的過程中,數據通道上傳輸的是編碼后的有效像素字符。而在每一幀圖像的行與行之間,以及視頻中不同幀之間的時間間隔(消隱期)內,數據通道上傳輸的則是控制字符。每個通道上有兩位控制信號的輸入接口,共對應四種不同的控制字符。這些控制字符提供了視頻的行同步(HZYNC)以及幀同步(VSYNC)信息,也可以用來指定所傳輸數據的邊界(用於同步)。對於 DVI 傳輸,整個視頻的消隱期都用來傳輸控制字符。而 HDMI 傳輸的消隱期除了控制字符之外,還可以用於傳輸音頻或者其他附加數據(例如字母信息),4-bit 音頻和附加數據將通過 TERC4 編碼機制轉換成 10-bit TERC4 字符,然后再綠色和紅色通道上傳輸。從上圖也可以看出這一差別,即“Auxiliary Data”接口標有“HDMI Olny”,即它是 HDMI 所獨有的接口。如果我們不需要附加數據,只傳輸視頻數據的話,完全可以把 HDMI 接口當做 DVI 接口進行驅動。關於其他區別,可以查看這個表:
下圖是 DVI 編碼器示意圖:
每個通道輸入的視頻像素數據都要使用 DVI 規范中的 TMDS 編碼算法進行編碼。每個 8-bit 的數據都將被轉換成 460 個特定 10-bit 字符中的一個。這個編碼機制大致上實現了傳輸過程中的直流平衡,即一段時間內傳輸的高電平(數字“1”)的個數大致等於低電平(數字“0”)的個數。同時,每個編碼后的 10-bit 字符中狀態跳轉(“由 1 到 0”或者“由 0 到 1”)的次數將被限制在五次以內。除了視頻數據之外,每個通道 2-bit 控制信號的狀態也要進行編碼,編碼后分別對應四個不同的 10-bit 控制字符,分別是 10'b1101010100,10'b0010101011,10'b0101010100,和 10'b1010101011。可以看出,每個控制字符都有七次以上的狀態跳轉。視頻字符和控制字符狀態跳轉次數的不同將會被用於發送和接收設備的同步。
再重復一遍,HDMI 在輸入附加數據的同時,還需要輸入 ADE(Aux/Audio Data Enable)信號,其作用和 VDE 是類似的:當 ADE 為高電平時,表明輸入端的附加數據或者音頻數據有效,DIV 是不能傳音頻的。想了解更多有關 HDMI的細節,可以參考HDMI 接口規范——《High-Definition Multimedia Interface Specification Version 1.3a》,英語不好的也可以查看文檔《HDMI1.4規范中文版》。
三、HDMI 電路原理
這里直接引用正點原子領航者 ZYNQ 底板的 HDMI 接口原理圖來說明:
- HDMI_CEC 指的是用戶電氣控制(Consumer Electronics Control),它用於 HDMI 連接線上的設備之間進行信息交換。當一個設備的狀態發生變化時,CEC 可以使用遠程控制或自動改變設置來命令連接的關聯設備的狀態發生相應的變化。例如,如果用戶放置一張碟片到藍光播放器並開始播放,那么高清電視機將會自動打開電源,設置正確的視頻輸入格式和打開環繞聲設備等等,這種關聯通信提供了一個更好的客戶體驗。
- HDMI_HPD 指的是熱拔插檢測(Hot Plug Detect),當視頻設備與接收設備通過 HDMI 連接時,接收設備將 HPD 置為高電平,通知發送設備。當發送設備檢測到 HPD 為低電平時,表明斷開連接。
- HDMI_OUT_EN 信號,用於設置 HDMI 接口的輸入輸出模式,當其為高電平時作為輸出端,此時由 FPGA 底板輸出 HDMI 接口的 5V 電源。同時,HDMI_HPD 將作為輸入信號使用。反之,當 HDMI_OUT_EN 為低電平時,HDMI_HPD 將輸出高電平,用於指示 HDMI 連接狀態。
- HDMI_SCL 和 HDMI_SDA信號, 是 HDMI 接口的顯示數據通道(DDC,Display Data Channel),用於 HDMI 發送端和接收端之間交換一些配置信息,通過 I2C 協議通信。發送端通過 DDC 通道,讀取接收端保存在 EEPROM 中的 EDID 數據,獲取接收端的信息,確認接收端終端顯示的設置和功能,決定跟接收端之間以什么格式傳輸音/視頻數據。
set_property IOSTANDARD TMDS_33 [get_ports HDMI_clk_p]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_clk_n]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d0_p]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d0_n]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d1_p]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d1_n]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d2_p]
set_property IOSTANDARD TMDS_33 [get_ports HDMI_d2_n]
set_property PACKAGE_PIN C18 [get_ports HDMI_clk_p]
set_property PACKAGE_PIN D20 [get_ports HDMI_d0_p]
set_property PACKAGE_PIN C22 [get_ports HDMI_d1_p]
set_property PACKAGE_PIN B21 [get_ports HDMI_d2_p]
四、HDMI 程序設計
TMDS 連接從邏輯功能上可以划分成兩個階段:編碼和並串轉換。在編碼階段,編碼器將視頻源中的像素數據、HDMI 的音頻/附加數據、行同步和場同步信號分別編碼成 10 位的字符流,然后在並串轉換階段將上述的字符流轉換成串行數據流,並將其從三個差分輸出通道發送出去。示意圖如下所示:
將其放大,可以得到下面這個框圖:
Encoder 模塊負責對數據進行編碼,Serializer 模塊對編碼后的數據進行並串轉換,最后通過 OBUFDS 轉化成 TMDS 差分信號傳輸。 圖中左下腳 HDMI 的音頻/附加數據輸入在本次實驗中並未用到,因此以虛線表示。
1、encoder:8B/10B
8B10B 編碼是可以從 Xilinx 官網下載到此模塊,模塊名為 encode,此模塊把 8 比特的數據進行從新映射為 10bit 數據,防止連續的 0 和 1 出現導致直流不平衡造成誤碼率升高。
1 `timescale 1 ps / 1ps 2 3 module encode ( 4 input clkin, // pixel clock input 5 input rstin, // async. reset input (active high) 6 input [7:0] din, // data inputs: expect registered 7 input c0, // c0 input 8 input c1, // c1 input 9 input de, // de input 10 output reg [9:0] dout // data outputs 11 ); 12 13 //////////////////////////////////////////////////////////// 14 // Counting number of 1s and 0s for each incoming pixel 15 // component. Pipe line the result. 16 // Register Data Input so it matches the pipe lined adder 17 // output 18 //////////////////////////////////////////////////////////// 19 reg [3:0] n1d; //number of 1s in din 20 reg [7:0] din_q; 21 22 always @ (posedge clkin) begin 23 n1d <= din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; 24 25 din_q <= din; 26 end 27 28 /////////////////////////////////////////////////////// 29 // Stage 1: 8 bit -> 9 bit 30 // Refer to DVI 1.0 Specification, page 29, Figure 3-5 31 /////////////////////////////////////////////////////// 32 wire decision1; 33 34 assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); 35 /* 36 reg [8:0] q_m; 37 always @ (posedge clkin) begin 38 q_m[0] <=#1 din_q[0]; 39 q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); 40 q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); 41 q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); 42 q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); 43 q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); 44 q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); 45 q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); 46 q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; 47 end 48 */ 49 wire [8:0] q_m; 50 assign q_m[0] = din_q[0]; 51 assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); 52 assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); 53 assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); 54 assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); 55 assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); 56 assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); 57 assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); 58 assign q_m[8] = (decision1) ? 1'b0 : 1'b1; 59 60 ///////////////////////////////////////////////////////// 61 // Stage 2: 9 bit -> 10 bit 62 // Refer to DVI 1.0 Specification, page 29, Figure 3-5 63 ///////////////////////////////////////////////////////// 64 reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m 65 always @ (posedge clkin) begin 66 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]; 67 n0q_m <= 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]); 68 end 69 70 parameter CTRLTOKEN0 = 10'b1101010100; 71 parameter CTRLTOKEN1 = 10'b0010101011; 72 parameter CTRLTOKEN2 = 10'b0101010100; 73 parameter CTRLTOKEN3 = 10'b1010101011; 74 75 reg [4:0] cnt; //disparity counter, MSB is the sign bit 76 wire decision2, decision3; 77 78 assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); 79 ///////////////////////////////////////////////////////////////////////// 80 // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] 81 ///////////////////////////////////////////////////////////////////////// 82 assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); 83 84 //////////////////////////////////// 85 // pipe line alignment 86 //////////////////////////////////// 87 reg de_q, de_reg; 88 reg c0_q, c1_q; 89 reg c0_reg, c1_reg; 90 reg [8:0] q_m_reg; 91 92 always @ (posedge clkin) begin 93 de_q <= de; 94 de_reg <= de_q; 95 96 c0_q <= c0; 97 c0_reg <= c0_q; 98 c1_q <= c1; 99 c1_reg <= c1_q; 100 101 q_m_reg <= q_m; 102 end 103 104 /////////////////////////////// 105 // 10-bit out 106 // disparity counter 107 /////////////////////////////// 108 always @ (posedge clkin or posedge rstin) begin 109 if(rstin) begin 110 dout <= 10'h0; 111 cnt <= 5'h0; 112 end else begin 113 if (de_reg) begin 114 if(decision2) begin 115 dout[9] <= ~q_m_reg[8]; 116 dout[8] <= q_m_reg[8]; 117 dout[7:0] <= (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; 118 119 cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); 120 end else begin 121 if(decision3) begin 122 dout[9] <= 1'b1; 123 dout[8] <= q_m_reg[8]; 124 dout[7:0] <= ~q_m_reg[7:0]; 125 126 cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); 127 end else begin 128 dout[9] <= 1'b0; 129 dout[8] <= q_m_reg[8]; 130 dout[7:0] <= q_m_reg[7:0]; 131 132 cnt <= cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); 133 end 134 end 135 end else begin 136 case ({c1_reg, c0_reg}) 137 2'b00: dout <= CTRLTOKEN0; 138 2'b01: dout <= CTRLTOKEN1; 139 2'b10: dout <= CTRLTOKEN2; 140 default: dout <= CTRLTOKEN3; 141 endcase 142 143 cnt <= 5'h0; 144 end 145 end 146 end 147 148 endmodule
encoder 模塊按照 DVI 接口規范中 TMDS 編碼算法對輸入的 8 位像素數據以及 2 位行場同步信號進 行編碼.該模塊是 Xilinx 應用筆記 XAPP460 中所提供的編碼模塊,其具體實現的編碼算法如下圖所示:
TMDS 通過邏輯算法將 8 位字符數據通過最小轉換編碼為 10 位字符數據,前 8 位數據由原始信號經運算后獲得,第 9 位表示運算的方式,1 表示異或 0 表示異或非。經過 DC 平衡后(第 10 位),采用差分信號傳輸數據。第 10 位實際是一個反轉標志位,1 表示進行了反轉而 0 表示沒有反轉,從而達到 DC 平衡。接收端在收到信號后,再進行相反的運算。TMDS 和 LVDS、TTL 相比有較好的電磁兼容性能。這種算法可以減小傳輸信號過程的上沖和下沖,而 DC 平衡使信號對傳輸線的電磁干擾減少,可以用低成本的專用電纜實現長距離、高質量的數字信號傳輸。圖中所描述的算法是 DVI 接口規范所定義的,我們不作深入研究,算法中各個參數的含義如下圖所示:
D | 視頻信號 |
C0 C1 | 控制信號 |
DE | 使能信號 |
Cnt | 寄存器參數 |
N0{X} | 輸入視頻信號“1”的個數 |
N1{X} | 輸入視頻信號“0”的個數 |
q_out | 編碼輸出 |
encoder模塊的調用代碼如下所示,注意 u_encode_blue 里的 c0 和 c1 輸入了 hsync 和 vsync,另兩個顏色通道則不需要。
//========================================================================== //== encode 例化,RGB三個通道 //========================================================================== encode u_encode_red ( .clkin (HDMI_clk1x ), .rstin (HDMI_rst ), .din (VGA_r ), .c0 (1'b0 ), .c1 (1'b0 ), .de (VGA_de ), .dout (dout_r ) ); encode u_encode_green ( .clkin (HDMI_clk1x ), .rstin (HDMI_rst ), .din (VGA_g ), .c0 (1'b0 ), .c1 (1'b0 ), .de (VGA_de ), .dout (dout_g ) ); encode u_encode_blue ( .clkin (HDMI_clk1x ), .rstin (HDMI_rst ), .din (VGA_b ), .c0 (VGA_hsync ), //hsync .c1 (VGA_vsync ), //vsync .de (VGA_de ), .dout (dout_b ) );
2、Serializer:並轉串
Serializer10_1 模塊通過調用 OSERDESE2 原語來實現 10:1 的並串轉換。原語是 Xilinx 器件底層硬件中的功能模塊,它使用專用的資源來實現一系列的功能。相比於 IP 核,原語的調用方法更簡單,但是一般只用於實現一些簡單的功能。OSERDESE2 詳細介紹見 Xilinx 官方提供的UG768手冊。OSERDESE2 原語模型如下所示:
一個 OSERDESE2 只能實現最多 8:1 的轉換率,在這里我們通過位寬擴展實現了 10:1 的並串轉換,如下圖所示:
OSERDESE2原語的文檔中介紹有:該原語可以進行8:1、10:1、14:1轉換,當進行8:1轉換的時候,采用一個原語即可,當采用10:1或者14:1轉換的時候,需要采用兩個原語級聯的方式。我們需要將 10bit 數據進行串行化,因此根據文檔說明,我們需要使用兩個OSERDESE2原語進行級聯,級聯后的框圖如下圖所示。OSERDESE2 原語的 Master 端承接 10bit 數據的低 8 位,OSERDESE2 原語的 Slave 端的 D1、D2 不使用,D3 和 D4 承接10bit數據的高 2bit,(若是14:1則可以繼續使用D5,D6,D7,D8,依然不使用D1,D2)。並將 Slave 的輸出 SHIFTOUT1, SHIFTOUT2 連接到 master 的 SHIFTIN1, SHIFTIN2,輸入的 10bit 數據將會按照由低位到高位的順序從 DataOut 端輸出。
OSERDESE2原語獲取方法如下所示:
整個系統需要兩個輸入時鍾,一個是視頻的像素時鍾 div_clk,另外一個時鍾 ser_clk 的頻率是像素時鍾的五倍。由前面的簡介部分我們知道,並串轉換過程的實現的是 10:1 的轉換率,理論上轉換器需要一個 10 倍像素頻率的時鍾。這里我們只需要一個 5 倍的時鍾頻率,這是因為 OSERDESE2 模塊可以實現 DDR 的功能,即它在五倍時鍾頻率的基礎上又實現了雙倍數據速率。
注意,OSERDESE2 模塊要求復位信號高電平有效,並且需要將異步復位信號同步到串行時鍾域,因此還要加入一個異步復位信號處理代碼,完整的 OSERDESE2 模塊的調用代碼如下所示:
//========================================================================== //== 時鍾同步 //========================================================================== always @(posedge divclk or posedge rst) begin if (rst) begin ini_rst <= 1'b1; end else begin ini_rst <= 1'b0; end end //========================================================================== //== OSERDESE2 master 原語例化1:0-7 //========================================================================== OSERDESE2 #( .DATA_RATE_OQ ("DDR" ), // 最終輸出的數據類型:DDR,SDR .DATA_RATE_TQ ("SDR" ), // 輸出的buffer: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) ) OSERDESE2_inst_master ( .OFB ( ), // 1-bit output: Feedback path for data,未用到,刪除即關閉 .OQ (do ), // 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 (serclk ), // 1-bit input: High speed clock .CLKDIV (divclk ), // 1-bit input: Divided clock // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each),數據 .D1 (din[0] ), .D2 (din[1] ), .D3 (din[2] ), .D4 (din[3] ), .D5 (din[4] ), .D6 (din[5] ), .D7 (din[6] ), .D8 (din[7] ), .OCE (1'b1 ), // 1-bit input: Output data clock enable .RST (ini_rst ), // 1-bit input: Reset,高電平有效的同步時鍾 // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each) .SHIFTIN1 (cascade_di1 ), // 連接到SLAVE上 .SHIFTIN2 (cascade_di2 ), // 連接到SLAVE上 // 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 slave 原語例化:8-9 //========================================================================== 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) ) OSERDESE2_inst_slave ( .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 (cascade_di1 ), .SHIFTOUT2 (cascade_di2 ), .TBYTEOUT ( ), // 1-bit output: Byte group tristate .TFB ( ), // 1-bit output: 3-state control .TQ ( ), // 1-bit output: 3-state control .CLK (serclk ), // 1-bit input: High speed clock .CLKDIV (divclk ), // 1-bit input: Divided clock // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each) .D1 ( ), //為什么要空?看框圖 .D2 ( ), //為什么要空?看框圖 .D3 (din[8] ), .D4 (din[9] ), .D5 (1'b0 ), .D6 (1'b0 ), .D7 (1'b0 ), .D8 (1'b0 ), .OCE (1'b1 ), // 1-bit input: Output data clock enable .RST (ini_rst ), // 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 );
3、OBUFDS:差分輸出
OBUFDS獲取途徑如下所示:
為了程序簡單化,可以將此模塊也放入上面 Serializer10_1 模塊的 OSERDESE2 調用后面,如下所示:
//========================================================================== //== OBUFDS 原語例化,將串行數據轉為一對差分信號 //========================================================================== OBUFDS #( .IOSTANDARD ("DEFAULT" ), // Specify the output I/O standard .SLEW ("SLOW" ) // Specify the output slew rate ) OBUFDS_inst ( .O (do_p ), // Diff_p output (connect directly to top-level port) .OB (do_n ), // Diff_n output (connect directly to top-level port) .I (do ) // Buffer input );
4、HDMI_trans:模塊連接
創建一個 HDMI_trans 模塊,將上述三個模塊連接到一起,有幾點需要說明:
(1)u_encode_blue 的例化中的 c0 和 c1 包括了 HSYNC 和 VSYNC,而 red 和 blue 中給的是 0,原因見上文。
(2)Serializer10_1 里包含了 OSERDESE2 原語和 OBUFDS 原語。
(3)上面說過,整個系統需要兩個輸入時鍾,一個是視頻的像素時鍾 HDMI_clk1x(div_clk) ,另外一個時鍾 HDMI_clk5x(ser_clk)的頻率是像素時鍾的五倍。TMDS 連接的時鍾通道我們采用與數據通道相同的並轉串邏輯來實現。通過對 10 位二進制序列 10’b11111_00000 在 10 倍像素時鍾頻率下進行並串轉換,就可以得到像素時鍾頻率下的 TMDS 參考時鍾 ser_clk。
1 //************************************************************************** 2 // *** 名稱 : HDMI_trans.v 3 // *** 作者 : xianyu_FPGA 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 5 // *** 日期 : 2019-08-10 6 // *** 描述 : VGA協議轉成HDMI協議輸出 7 //************************************************************************** 8 module HDMI_trans 9 //========================< 端口 >========================================== 10 ( 11 input wire HDMI_clk1x , 12 input wire HDMI_clk5x , 13 input wire HDMI_rst , 14 //VGA ----------------------------------------------- 15 input wire VGA_de , 16 input wire VGA_vsync , 17 input wire VGA_hsync , 18 input wire [7:0] VGA_r , 19 input wire [7:0] VGA_g , 20 input wire [7:0] VGA_b , 21 //HDMI ---------------------------------------------- 22 output wire HDMI_clk_p , 23 output wire HDMI_clk_n , 24 output wire HDMI_chn0_p , 25 output wire HDMI_chn0_n , 26 output wire HDMI_chn1_p , 27 output wire HDMI_chn1_n , 28 output wire HDMI_chn2_p , 29 output wire HDMI_chn2_n 30 ); 31 //========================< 信號 >========================================== 32 wire [9:0] dout_r ; //紅 33 wire [9:0] dout_g ; //綠 34 wire [9:0] dout_b ; //藍 35 //========================================================================== 36 //== encode 例化,RGB三個通道進行8B轉10B轉換 37 //========================================================================== 38 encode u_encode_red 39 ( 40 .clkin (HDMI_clk1x ), 41 .rstin (HDMI_rst ), 42 .din (VGA_r ), 43 .c0 (1'b0 ), 44 .c1 (1'b0 ), 45 .de (VGA_de ), 46 .dout (dout_r ) 47 ); 48 49 encode u_encode_green 50 ( 51 .clkin (HDMI_clk1x ), 52 .rstin (HDMI_rst ), 53 .din (VGA_g ), 54 .c0 (1'b0 ), 55 .c1 (1'b0 ), 56 .de (VGA_de ), 57 .dout (dout_g ) 58 ); 59 60 encode u_encode_blue 61 ( 62 .clkin (HDMI_clk1x ), 63 .rstin (HDMI_rst ), 64 .din (VGA_b ), 65 .c0 (VGA_hsync ), 66 .c1 (VGA_vsync ), 67 .de (VGA_de ), 68 .dout (dout_b ) 69 ); 70 //========================================================================== 71 //== 並轉串模塊例化:時鍾,給固定值 72 //========================================================================== 73 Serializer10_1 u_Serializer_clk 74 ( 75 .divclk (HDMI_clk1x ), 76 .serclk (HDMI_clk5x ), 77 .rst (HDMI_rst ), 78 .din (10'b11111_00000 ), //固定值 79 .do_p (HDMI_clk_p ), 80 .do_n (HDMI_clk_n ) 81 ); 82 //========================================================================== 83 //== 並轉串模塊例化:數據 84 //========================================================================== 85 Serializer10_1 u_Serializer_red 86 ( 87 .divclk (HDMI_clk1x ), 88 .serclk (HDMI_clk5x ), 89 .rst (HDMI_rst ), 90 .din (dout_r ), 91 .do_p (HDMI_chn2_p ), 92 .do_n (HDMI_chn2_n ) 93 ); 94 95 Serializer10_1 u_Serializer_green 96 ( 97 .divclk (HDMI_clk1x ), 98 .serclk (HDMI_clk5x ), 99 .rst (HDMI_rst ), 100 .din (dout_g ), 101 .do_p (HDMI_chn1_p ), 102 .do_n (HDMI_chn1_n ) 103 ); 104 105 Serializer10_1 u_Serializer_blue 106 ( 107 .divclk (HDMI_clk1x ), 108 .serclk (HDMI_clk5x ), 109 .rst (HDMI_rst ), 110 .din (dout_b ), 111 .do_p (HDMI_chn0_p ), 112 .do_n (HDMI_chn0_n ) 113 ); 114 115 116 endmodule
5、HDMI_top:模塊完整封裝
可以看出 HDMI 模塊還是比較復雜的,因此我們添加 VGA 時序模塊,和 HDMI_trans 模塊一起封裝成 HDMI_top 模塊,以后可以直接把這個模塊拿來用。HDMI_top 的框圖如下所示:
HDMI_top 模塊代碼如下所示:
1 `timescale 1ns / 1ps 2 //************************************************************************** 3 // *** 名稱 : HDMI_top.v 4 // *** 作者 : xianyu_FPGA 5 // *** 博客 : https://www.cnblogs.com/xianyufpga/ 6 // *** 日期 : 2019-08-10 7 // *** 描述 : HDMI頂層文件 8 //************************************************************************** 9 module HDMI_top 10 //========================< 端口 >========================================== 11 ( 12 //system -------------------------------------------- 13 input wire HDMI_clk1x , 14 input wire HDMI_clk5x , 15 input wire rst , 16 //HDMI in ------------------------------------------- 17 output wire HDMI_req , 18 input wire [15:0] HDMI_data , 19 //HDMI out ------------------------------------------ 20 output wire HDMI_clk_p , 21 output wire HDMI_clk_n , 22 output wire HDMI_d0_p , 23 output wire HDMI_d0_n , 24 output wire HDMI_d1_p , 25 output wire HDMI_d1_n , 26 output wire HDMI_d2_p , 27 output wire HDMI_d2_n 28 ); 29 //========================< 連線 >========================================== 30 wire VGA_req ; 31 wire [15:0] VGA_data ; 32 wire VGA_de ; 33 wire VGA_vsync ; 34 wire VGA_hsync ; 35 wire [ 7:0] VGA_r ; 36 wire [ 7:0] VGA_g ; 37 wire [ 7:0] VGA_b ; 38 //========================================================================== 39 //== VGA控制器 40 //========================================================================== 41 VGA_driver u_VGA_driver 42 ( 43 .clk (HDMI_clk1x ), 44 .rst (rst ), 45 .VGA_data (HDMI_data ), 46 .VGA_req (HDMI_req ), 47 .VGA_de (VGA_de ), 48 .VGA_vsync (VGA_vsync ), 49 .VGA_hsync (VGA_hsync ), 50 .VGA_r (VGA_r ), 51 .VGA_g (VGA_g ), 52 .VGA_b (VGA_b ) 53 ); 54 //========================================================================== 55 //== HDMI轉換 56 //========================================================================== 57 HDMI_trans u_HDMI_trans 58 ( 59 .HDMI_clk1x (HDMI_clk1x ), 60 .HDMI_clk5x (HDMI_clk5x ), 61 .HDMI_rst (rst ), 62 //----------------------------------------------- 63 .VGA_de (VGA_de ), 64 .VGA_vsync (VGA_vsync ), 65 .VGA_hsync (VGA_hsync ), 66 .VGA_r (VGA_r ), 67 .VGA_g (VGA_g ), 68 .VGA_b (VGA_b ), 69 //----------------------------------------------- 70 .HDMI_clk_p (HDMI_clk_p ), 71 .HDMI_clk_n (HDMI_clk_n ), 72 .HDMI_chn0_p (HDMI_d0_p ), 73 .HDMI_chn0_n (HDMI_d0_n ), 74 .HDMI_chn1_p (HDMI_d1_p ), 75 .HDMI_chn1_n (HDMI_d1_n ), 76 .HDMI_chn2_p (HDMI_d2_p ), 77 .HDMI_chn2_n (HDMI_d2_n ) 78 ); 79 80 81 endmodule
五、上板現象
將 HDMI_top 模塊和 DDR3 控制模塊一起建立工程,編譯下載,那么 HDMI 接口的屏幕會顯示亂序的彩條,表明此次實驗設計成功。
參考資料:
[1]威三學院FPGA教程
[2]正點原子FPGA開發指南