實(shí)驗(yàn):HDMI視頻輸入與環(huán)路輸出全解析)
做FPGA視頻處理這塊最繞不開的基礎(chǔ)實(shí)驗(yàn)就是HDMI視頻輸入與環(huán)路輸出。很多新手拿到開發(fā)板第一步跑個流水燈覺得沒意思直接上HDMI環(huán)路輸出又不知道怎么下手明明只是“輸入接輸出”為什么圖像就是出不來黑金這套“FPGA基礎(chǔ)HDMI視頻輸入與環(huán)路輸出實(shí)驗(yàn)”教程恰好就是這個從零到一的關(guān)鍵跳板。我結(jié)合自己踩過的坑和實(shí)際調(diào)試經(jīng)驗(yàn)把整個實(shí)驗(yàn)從硬件架構(gòu)到Verilog代碼實(shí)現(xiàn)完整拆一遍重點(diǎn)說清楚那些教程里不細(xì)講、但實(shí)際調(diào)試必踩的重點(diǎn)和套路。1. 項(xiàng)目核心思路為什么先做“環(huán)路輸出”1.1 “環(huán)路輸出”到底解決了什么問題先明確這個實(shí)驗(yàn)在FPGA視頻學(xué)習(xí)路徑中的位置。HDMI輸入與環(huán)路輸出Loop Out實(shí)驗(yàn)本質(zhì)上是要讓FPGA完成一次完整的視頻流搬運(yùn)外部視頻源比如電腦、機(jī)頂盒通過HDMI線把信號送進(jìn)FPGA開發(fā)板上的HDMI輸入接口FPGA收到這路視頻流之后做最簡單的透傳處理再從另一個HDMI輸出接口把畫面送出去接到顯示器或電視上。這看起來簡單但它的意義遠(yuǎn)超“通個電亮個屏”。最關(guān)鍵的一點(diǎn)它把FPGA從“生成視頻”變成“接收視頻”這是一個技術(shù)分水嶺。之前你可能做過HDMI輸出實(shí)驗(yàn)比如用FPGA生成彩條、顯示一幅圖片那是FPGA作為視頻源主動往外發(fā)包時序完全由自己掌控。這次反過來FPGA變成視頻接收端要學(xué)會去適應(yīng)外部輸入的時序去解析別人的數(shù)據(jù)格式。環(huán)路輸出最先要解決的其實(shí)是硬件層面的問題HDMI輸入接口的TMDS信號是一對一的高速差分信號包含4對通道3對數(shù)據(jù)通道加1對時鐘通道每通道速率在1080p60下高達(dá)1.485Gbps。FPGA要能接收這么高速的信號再原封不動地轉(zhuǎn)發(fā)出去。最簡單可靠的方式就是在硬件設(shè)計(jì)上把輸入信號經(jīng)過緩沖之后直接接到輸出連接器上不經(jīng)過FPGA內(nèi)部邏輯這叫物理層環(huán)路。另一種方式是把信號收進(jìn)FPGA在邏輯層完成解碼、再編碼、再輸出這叫邏輯層環(huán)路。這個實(shí)驗(yàn)的巧妙之處在于它把兩種方式結(jié)合起來硬件上預(yù)留了信號直通的路徑但在FPGA內(nèi)部你仍然要真正去接收HDMI信號、做數(shù)據(jù)處理、再做輸出。這樣既保證了即使邏輯沒調(diào)通顯示器也能看到畫面方便對照排查又能讓你真正上手HDMI接收的全部流程。我翻了黑金這份實(shí)驗(yàn)手冊發(fā)現(xiàn)它用的正是邏輯層環(huán)路為主、物理層直通為輔的思路下文細(xì)說。1.2 整體架構(gòu)數(shù)據(jù)從哪里來到哪里去從大框架來看這個實(shí)驗(yàn)的數(shù)據(jù)通路可以畫成下面這個樣子HDMI輸入連接器 → TMDS接收端IBUFDSIDDR → 通道對齊與解串 → 像素重組 → 時序與同步解析 → 環(huán)路輸出側(cè)編碼 → TMDS輸出端OSERDESOBUFDS → HDMI輸出連接器這里面的核心有4個模塊物理接收層負(fù)責(zé)把HDMI線纜上傳來的TMDS差分信號變成FPGA內(nèi)部的單端并行數(shù)據(jù)。這一層主要用到Xilinx FPGA的IBUFDS、IDDR、ISERDES這些原語或者直接用硬核HDMI接收IP。通道對齊邏輯HDMI的三個數(shù)據(jù)通道R、G、B分別走一路在發(fā)送端是并行對齊的但經(jīng)過線纜和PCB走線后到達(dá)FPGA引腳的時間會有一點(diǎn)差異。接收端需要通過訓(xùn)練序列Termination Character來做通道對齊把三路數(shù)據(jù)重新對齊到同一時刻。時序解析與像素重組從解串后的數(shù)據(jù)流中解析出視頻的有效像素、行場同步信號、使能信號DE恢復(fù)出完整的像素時鐘和視頻時序。這步可以理解為“HDMI協(xié)議?!钡慕尤雽印]敵鰝?cè)驅(qū)動把恢復(fù)出來的視頻信號再按照HDMI規(guī)范重新編碼輸出。如果只是做環(huán)路可以走半透傳——只把像素?cái)?shù)據(jù)和時序重新打包不做任何圖像處理如果想加點(diǎn)花樣可以在這一級插一個幀緩存或圖像處理模塊。黑金這個實(shí)驗(yàn)的代碼里通道對齊和時序解析用的是自研邏輯加Xilinx原語組合并沒有直接套用太復(fù)雜的IP核這樣做的最大好處是能逼你搞懂HDMI每一層到底在干什么。后面實(shí)操時會詳細(xì)展開。2. 必須吃透的HDMI基礎(chǔ)細(xì)節(jié)2.1 TMDS編碼與通道結(jié)構(gòu)HDMI信號的核心是TMDSTransition Minimized Differential Signaling編碼——最小化轉(zhuǎn)換差分信號。這個概念聽起來高大上本質(zhì)就是為了在高速傳輸時降低電磁干擾EMI和保證信號完整性對8位像素?cái)?shù)據(jù)進(jìn)行編碼變成10位串行數(shù)據(jù)流。具體來說TMDS有三條數(shù)據(jù)通道通道0、1、2分別傳輸藍(lán)色、綠色、紅色分量外加一條獨(dú)立的像素時鐘通道。每一條數(shù)據(jù)通道都用差分信號傳輸正負(fù)兩根線所以HDMI接口物理上有4對差分線。這4對線在PCB上要等長處理差分阻抗控制在100歐姆這是硬件設(shè)計(jì)層面要注意的事。TMDS編碼分兩個階段第一階段把8位數(shù)據(jù)轉(zhuǎn)換為9位第二階段在9位基礎(chǔ)上加一位DC平衡位變成10位。這個編碼的目的有兩個一是讓傳輸線上0和1的數(shù)量盡量均衡避免直流偏置積累二是限制連續(xù)相同電平的個數(shù)讓接收端的時鐘恢復(fù)更容易。我早期在調(diào)試時曾用邏輯分析儀直接去抓TMDS線上的波形發(fā)現(xiàn)抓出來的數(shù)據(jù)完全沒法看就是因?yàn)楹雎粤诉@是10位編碼后的串行流不是直接的像素值。你只有理解了這層關(guān)系才知道后續(xù)解串之后為什么要做“10位到8位的解碼”這一步。2.2 像素時鐘、數(shù)據(jù)速率與帶寬計(jì)算做HDMI實(shí)驗(yàn)最基礎(chǔ)的計(jì)算能力得過關(guān)。各種分辨率對應(yīng)的像素時鐘和TMDS速率是你配置PLL、約束時序的基礎(chǔ)。以最常見的1080p60為例分辨率為1920×1080刷新率60Hz但實(shí)際傳輸時還包括消隱區(qū)blanking水平方向總共2200個像素時鐘垂直方向總共1125行。像素時鐘 2200 × 1125 × 60 148.5MHzTMDS數(shù)據(jù)速率 像素時鐘 × 3三個通道 × 10每8位像素編碼為10位串行 4.455Gbps每條通道的串行速率就是148.5MHz × 10 1.485Gbps??吹竭@里你應(yīng)該明白為什么HDMI接收沒那么簡單了——你的FPGA引腳需要處理1.485Gbps的串行數(shù)據(jù)。好在Xilinx FPGA有ISERDES輸入串并轉(zhuǎn)換器和IDELAY輸入延遲單元這種專用硬件它們就是為這類高速接口準(zhǔn)備的。黑金開發(fā)板用的Artix-7系列其ISERDES支持1:8模式即用148.5MHz的時鐘采樣1.485Gbps的串行流一次轉(zhuǎn)出8位并行數(shù)據(jù)再把4個8位拼成32位4個像素或者直接把3個通道各取1位拼成3位并行看具體設(shè)計(jì)。這里要特別提醒一個很多人會誤解的點(diǎn)HDMI接收端的像素時鐘并不是由FPGA自己生成的而是從HDMI輸入信號的時鐘通道上恢復(fù)出來的。輸入時鐘通道的頻率就是像素時鐘比如148.5MHzFPGA用這個時鐘來驅(qū)動整個接收鏈路。這個時鐘的相位和頻率都來自外部視頻源所以FPGA內(nèi)部用于視頻處理的時鐘域本質(zhì)上是一個“外部時鐘域”你需要用MMCM/PLL對這個時鐘進(jìn)行相位調(diào)整或倍頻但頻率基準(zhǔn)始終是外部源提供的。2.3 控制數(shù)據(jù)段與消隱區(qū)HDMI傳輸?shù)臄?shù)據(jù)流不只是有效像素。在每一行、每一場的消隱區(qū)里還會插入各種控制信號和數(shù)據(jù)島Data Island。其中最核心的是三種控制期Control Period位于消隱區(qū)間傳輸行同步HSYNC、場同步VSYNC以及CTL0~CTL3等控制信號。數(shù)據(jù)島期Data Island Period位于消隱區(qū)傳輸音頻數(shù)據(jù)、輔助信息如色彩格式、分辨率描述等通過包頭Packet Header和包體Packet Body組織。視頻數(shù)據(jù)期Video Data Period也就是有效像素?cái)?shù)據(jù)只在DEData Enable為高時才出現(xiàn)。對做基礎(chǔ)環(huán)路實(shí)驗(yàn)來說最需要關(guān)注的是DE信號。DE為高代表當(dāng)前是有效像素DE為低代表在消隱區(qū)。環(huán)路輸出最省事的做法是輸入DE為高時把輸入像素?cái)?shù)據(jù)直接送給輸出端輸入DE為低時輸出端補(bǔ)上預(yù)設(shè)的消隱電平或?qū)?yīng)的控制信號。我之前給初學(xué)者布線時總發(fā)現(xiàn)有人嘗試從解串后的原始數(shù)據(jù)里去拆HSYNC和VSYNC其實(shí)沒必要。HDMI接收端做環(huán)路透傳時可以把消隱期的控制信號原樣轉(zhuǎn)發(fā)不需要完整解析出來。前提是你能正確識別控制期和數(shù)據(jù)島期的邊界并原樣搬運(yùn)數(shù)據(jù)流。黑金這套實(shí)驗(yàn)代碼里就是按這個思路處理的既保證畫面正確又繞開了音頻、數(shù)據(jù)島解析這些進(jìn)階內(nèi)容。3. 實(shí)操準(zhǔn)備環(huán)境搭建與硬件連接3.1 開發(fā)平臺與工具鏈選擇黑金云課堂這套教程的核心開發(fā)板是黑金的AX7350Artix-7 XC7A35T或更高型號這類板子都板載了HDMI輸入接口、HDMI輸出接口、DDR3顆粒、UART轉(zhuǎn)USB等外設(shè)。做這個實(shí)驗(yàn)?zāi)阈枰獪?zhǔn)備的東西如下FPGA開發(fā)板一塊必須帶HDMI輸入接口芯片建議至少Artix-7級別含ISERDES/OSERDES硬核兩個HDMI接口的顯示器或電視一個接輸入源或輸出顯示用HDMI線兩根質(zhì)量別太差廉價的線在1.485Gbps速率下可能丟包導(dǎo)致花屏一個支持HDMI輸出的視頻源設(shè)備電腦、筆記本、電視盒子都可以Xilinx Vivado開發(fā)環(huán)境建議2018.3以上版本黑金配套教程用的是Vivado 2019.1左右注意早年的黑金開發(fā)板用的是ADV7611ADI的HDMI接收芯片這種外部解碼方案直接輸出并行RGB數(shù)據(jù)而近年來的教程更鼓勵直接用FPGA原語接收TMDS。兩者的區(qū)別很大用外部芯片F(xiàn)PGA只拿到解析后的24位RGB和時鐘難度低但學(xué)不到協(xié)議層的東西直接用FPGA原語接收難度陡增但這是真正玩轉(zhuǎn)HDMI的路徑。黑金這套教程采用的是后者更硬核也更值得學(xué)。3.2 硬件連接與信號完整性注意事項(xiàng)這塊我必須單獨(dú)拿出來說因?yàn)橛布B接是環(huán)路實(shí)驗(yàn)第一道鬼門關(guān)。第一個注意點(diǎn)HDMI輸入的TMDS信號是高速差分對千萬別用杜邦線去飛線連接必須走板載HDMI連接器或者經(jīng)過精確阻抗控制的轉(zhuǎn)接板。我見過有人圖省事把HDMI座子剪下來用飛線連到FPGA引腳上結(jié)果屏幕要么全黑要么滿屏雪花折騰一晚上發(fā)現(xiàn)是信號質(zhì)量完全不行。第二個注意點(diǎn)確認(rèn)開發(fā)板的HDMI輸入接口的I2CDDC通道是否已經(jīng)連到了FPGA或外部的EEPROM。HDMI源設(shè)備比如電腦在輸出信號之前會通過DDC通道讀取顯示端的EDID信息擴(kuò)展顯示標(biāo)識數(shù)據(jù)里面包含了顯示器支持的分辨率、刷新率、色彩格式等參數(shù)。如果FPGA板卡的EDID信息不正確或者讀取失敗視頻源可能根本不輸出信號。黑金的板子一般會在出廠時燒錄好EDID到板載EEPROM但如果你用自己畫的板子或者從別人手里收的板子這一條要優(yōu)先排查。第三個注意點(diǎn)熱插拔檢測Hot Plug DetectHPD引腳要正確處理。HPD是HDMI源端用來檢測顯示端是否就緒的引腳如果FPGA板卡沒有正確拉高HPD視頻源也會認(rèn)為“沒有接顯示器”而不輸出信號。在環(huán)路實(shí)驗(yàn)的硬件連接中確保HPD引腳被拉高并連接正確。開發(fā)板這一步接好之后打開Vivado新建工程選擇對應(yīng)型號的FPGA芯片導(dǎo)入黑金提供的約束文件XDC就可以進(jìn)入代碼階段了。4. 核心代碼邏輯與關(guān)鍵模塊實(shí)現(xiàn)4.1 頂層模塊劃分與端口設(shè)計(jì)基于實(shí)驗(yàn)?zāi)繕?biāo)頂層模塊建議按下面的方式劃分。我以Xilinx Artix-7的寫法為例端口設(shè)計(jì)包含這幾個部分HDMI輸入側(cè)3對TMDS數(shù)據(jù)差分輸入輸入引腳1對TMDS時鐘差分輸入HDMI輸出側(cè)3對TMDS數(shù)據(jù)差分輸出1對TMDS時鐘差分輸出I2C相關(guān)可選連接板載HDMI接收端的DDC通道I2C用于讀取EDID但多數(shù)黑金板卡把EDID放到了板載EEPROMFPGA不需要主動處理狀態(tài)指示LED或者UART輸出用于指示鏈路是否鎖定、分辨率等信息頂層模塊的核心代碼框架大致如下非完整工程僅示意結(jié)構(gòu)module hdmi_loop ( input wire sys_clk, // 板載時鐘比如50MHz用于初始化邏輯 input wire sys_rst_n, // HDMI 輸入 input wire [2:0] hdmi_rx_p, input wire [2:0] hdmi_rx_n, input wire hdmi_rx_clk_p, input wire hdmi_rx_clk_n, // HDMI 輸出 output wire [2:0] hdmi_tx_p, output wire [2:0] hdmi_tx_n, output wire hdmi_tx_clk_p, output wire hdmi_tx_clk_n, // 調(diào)試與狀態(tài) output wire link_ok, output wire [1:0] led_state ); // 內(nèi)部信號接收鏈路解出來的像素時鐘域信號 wire rx_pix_clk; // 恢復(fù)出的像素時鐘舉例148.5MHz wire rx_de; wire [23:0] rx_rgb; wire rx_hsync; wire rx_vsync; // 環(huán)路下來的數(shù)據(jù) wire [23:0] loop_rgb; wire loop_de; wire loop_hsync; wire loop_vsync; // 接收鏈路 hdmi_rx_chain u_rx ( .reset_n (sys_rst_n), .tmds_clk_p (hdmi_rx_clk_p), .tmds_clk_n (hdmi_rx_clk_n), .tmds_data_p (hdmi_rx_p), .tmds_data_n (hdmi_rx_n), .pixel_clk (rx_pix_clk), .rgb_out (rx_rgb), .de_out (rx_de), .hsync_out (rx_hsync), .vsync_out (rx_vsync), .locked (link_ok) ); // 環(huán)路透傳處理 assign loop_rgb rx_rgb; assign loop_de rx_de; assign loop_hsync rx_hsync; assign loop_vsync rx_vsync; // 發(fā)送鏈路 hdmi_tx_chain u_tx ( .reset_n (sys_rst_n), .pixel_clk (rx_pix_clk), .rgb_in (loop_rgb), .de_in (loop_de), .hsync_in (loop_hsync), .vsync_in (loop_vsync), .tmds_clk_p (hdmi_tx_clk_p), .tmds_clk_n (hdmi_tx_clk_n), .tmds_data_p (hdmi_tx_p), .tmds_data_n (hdmi_tx_n) ); endmodule注意頂層里有個特別關(guān)鍵的設(shè)計(jì)細(xì)節(jié)整個環(huán)路的數(shù)據(jù)通路全部工作在rx_pix_clk接收恢復(fù)時鐘域下發(fā)送鏈路也是用同一個時鐘驅(qū)動的。這樣避免了兩側(cè)時鐘不同步導(dǎo)致的數(shù)據(jù)錯位問題。這在環(huán)路實(shí)驗(yàn)中是幾乎必須的選擇但如果后續(xù)你要對圖像做跨時鐘域處理比如寫進(jìn)DDR3做幀緩存再讀出來做處理那就要引入異步FIFO和獨(dú)立的發(fā)送時鐘域復(fù)雜度會高很多。4.2 接收鏈路IBUFDS IDDR 通道對齊HDMI接收鏈路的底層核心是把差分串行數(shù)據(jù)轉(zhuǎn)成并行數(shù)據(jù)。在Xilinx 7系列里建議直接用ISERDESE2原語。但黑金教程為了簡化可能先用IDDR來示范——IDDR可以做到1:2的串并轉(zhuǎn)換但1.485Gbps的速率用IDDR會產(chǎn)生非常高的內(nèi)部時鐘頻率約742.5MHz這在7系列上很難收斂時序。所以正式接收鏈路更合理的寫法是// 每個通道例化一組ISERDESE21:8模式 // 同時配合IDELAYE2做bit級的相位調(diào)整 ISERDESE2 #( .DATA_RATE (DDR), .DATA_WIDTH (8), .INTERFACE_TYPE(NETWORKING), .DYN_CLKDIV_INV_EN(FALSE), .NUM_CE (1) ) iserdes_data[2:0] ( .D (tmds_data_in), .CLK (clk_serial), // 串行時鐘比如1.485GHz實(shí)際由MMCM從像素時鐘倍頻而來 .CLKB (~clk_serial), .CLKDIV (clk_div), // 并行時鐘148.5MHz .RST (~reset_n), .CE1 (1b1), .CE2 (1b1), .O (), .Q1 (q[0]), .Q2 (q[1]), .Q3 (q[2]), .Q4 (q[3]), .Q5 (q[4]), .Q6 (q[5]), .Q7 (q[6]), .Q8 (q[7]) );這里有個隱藏的坑ISERDESE2的CLK需要的是串行時鐘也就是像素時鐘的10倍頻1.485GHz但Artix-7的全局時鐘網(wǎng)絡(luò)BUFG最高只能跑到大約600MHz左右無法直接分發(fā)給ISERDESE2的CLK引腳。實(shí)際工程上7系列處理這種1.485Gbps的串行數(shù)據(jù)通常用“DDR模式 像素時鐘5倍頻”的方式用一個742.5MHz的時鐘配合DDR的上升沿和下降沿都采樣得到每周期2個bit再結(jié)合8位并行得到1:8轉(zhuǎn)換。類似地IDELAYE2用來對輸入bit流做精細(xì)延遲調(diào)整以補(bǔ)償PCB走線和芯片內(nèi)部延遲導(dǎo)致的相位偏差。這一步是接收鏈路最需要耐心調(diào)試的地方也是新手最頭疼的部分之一。我的經(jīng)驗(yàn)是前期先把IDELAY的延遲值固定在一個中間值保證顯示基本正常后再用動態(tài)校準(zhǔn)邏輯去搜索最佳延遲窗口。黑金教程里的代碼也給出了一個相對穩(wěn)定的固定延遲值適合先跑通再優(yōu)化。關(guān)于通道對齊HDMI規(guī)范里規(guī)定每個數(shù)據(jù)通道在消隱期會發(fā)送一個10位的“對齊字符”Alignment Character值是0b1010101011之類的特殊碼型。接收端通過檢測這些特殊碼型在三個通道上的到達(dá)時刻調(diào)整對齊讓三個通道的像素?cái)?shù)據(jù)在時間上重新對齊。如果你用的是Xilinx的HDMI IP核這部分是IP內(nèi)部自動完成的如果自己寫邏輯需要在消隱期監(jiān)測三個通道的碼型計(jì)算出需要補(bǔ)償?shù)难舆t然后在數(shù)據(jù)通路上插入匹配的延遲FIFO。自己寫對齊邏輯的復(fù)雜度確實(shí)不低這也是為什么黑金這套教程適合“有一定FPGA基礎(chǔ)、想往視頻方向深入”的人。如果你只是想快速出圖像直接用Vivado的Video Processing Subsystem或者第三方HDMI-RX IP比如調(diào)理好的開源IP可以省一大半功夫。4.3 輸出鏈路像素?cái)?shù)據(jù)到TMDS串行輸出輸出鏈路相對接收側(cè)來說邏輯上簡單一點(diǎn)但依然有不少細(xì)節(jié)要注意。核心目標(biāo)把并行像素?cái)?shù)據(jù)和時序信號按照TMDS編碼規(guī)則轉(zhuǎn)成10位串行碼流再通過OSERDESE2轉(zhuǎn)成高速差分信號輸出。基本框架// 1. 將RGB 8bit數(shù)據(jù)做TMDS編碼得到10bit編碼值 // 2. 將10bit編碼值通過OSERDESE21:10模式串行化 // 3. 差分輸出OBUFDS驅(qū)動TMDS引腳不過在實(shí)際設(shè)計(jì)中Xilinx 7系列的OSERDESE2最高支持1:8模式DDR要實(shí)現(xiàn)1:10稍微有點(diǎn)繞可以用兩個OSERDES級聯(lián)或者簡化為1:8模式也就是每像素時鐘周期輸出8bit的串行數(shù)據(jù)再做跨時鐘域打拍。黑金教程里為了降低難度普遍采用1:8模式配合一個特殊設(shè)計(jì)的TMDS編碼方案——這種情況下像素時鐘需要是串行時鐘的1/8在7系列上通常是串行時鐘742.5MHz除以8得到92.8MHz左右的并行時鐘。但實(shí)際上對于148.5MHz的像素時鐘和1.485Gbps的串行速率1:8模式下并行時鐘會變成185.6MHz因?yàn)槊?個串行bit對應(yīng)1個并行時鐘周期而每像素對應(yīng)10bit所以嚴(yán)格說還有兩個bit的差異。這塊確實(shí)容易把人繞暈。實(shí)際工程中更常見的做法是像素時鐘仍然是148.5MHz每個像素時鐘周期輸出10bit的TMDS編碼值然后級聯(lián)OSERDESE2實(shí)現(xiàn)1:10串行化其中一個OSERDES輸出前5位另一個輸出后5位最后用MUX選擇。Artix-7上這種方案是成熟的Vivado的HDMI IP核就是內(nèi)部這么干的。[\text{串行時鐘} 148.5\text{MHz} \times 10 1.485\text{GHz}]但對Artix-7的IO來說1.485Gbps的DDR輸出每引腳確實(shí)是極限操作。這也是為什么很多開發(fā)板的HDMI輸出最高只標(biāo)稱支持1080p60再往上比如1440p就需要更高速率的FPGA芯片比如Kintex、Virtex或者使用專門的HDMI發(fā)送芯片。在你寫輸出邏輯時TMDS編碼器本身反而沒那么難。7系列FPGA的OSERDESE2OBUFDS直接可以輸出差分串行信號編碼邏輯就是標(biāo)準(zhǔn)的TMDS編碼器網(wǎng)上有大量參考代碼。真正的難點(diǎn)在于時鐘分配和時序約束。4.4 環(huán)路實(shí)驗(yàn)的“透傳”技巧與常見取舍環(huán)路實(shí)驗(yàn)的名字叫“環(huán)路”實(shí)際代碼里核心就是那幾句assign把接收到的RGB、DE、HSYNC、VSYNC原封不動給到發(fā)送鏈路。但這“原封不動”其實(shí)是動過的——關(guān)鍵在于兩邊的數(shù)據(jù)位寬和時序得完全對齊。第一個取舍像素位寬。HDMI一般支持RGB88824位色每通道8bit但實(shí)際編碼時有些設(shè)計(jì)會先轉(zhuǎn)成4:2:2或4:4:4再進(jìn)行傳輸。做環(huán)路透傳時最穩(wěn)妥的做法是保持每通道8bit不變接收端解出多少位發(fā)送端就原樣發(fā)多少位。不要中途做色彩空間的轉(zhuǎn)換否則環(huán)路延遲會增大而且會引入色偏或帶寬不匹配問題。第二個取舍同步信號的處理。HDMI接收端恢復(fù)出的HSYNC、VSYNC和DE會有若干周期的對齊偏差特別是DE的下降沿可能和HSYNC的邊沿有不同步。做環(huán)路直通時這些信號要保持原始時序關(guān)系不要讓它們各自通過不同深度的FIFO否則顯示畫面可能會出現(xiàn)偏移甚至撕裂。我在實(shí)驗(yàn)中最簡單的做法是所有信號全部都打一拍寄存器同步一次然后直接輸出。第三個取舍時鐘方案。最理想的環(huán)路透傳接收端像素時鐘和發(fā)送端像素時鐘用同一個時鐘——就用接收端恢復(fù)出的像素時鐘。這樣發(fā)送鏈路完全跟隨輸入源不引入異步FIFO。如果你非要讓發(fā)送端用自己的時鐘那就要引入跨時鐘域處理做異步FIFO或者幀緩存這已經(jīng)超出“基礎(chǔ)環(huán)路”的范疇了。5. 實(shí)操驗(yàn)證調(diào)通顯示與常見故障排查5.1 第一次上板從燒錄到出畫面的完整流程假設(shè)你已經(jīng)把工程綜合、實(shí)現(xiàn)、生成了比特流。上板驗(yàn)證的流程建議這么走先把開發(fā)板的HDMI輸出接到顯示器HDMI輸入接到電腦或者電視盒子。注意上電順序——先給開發(fā)板上電讓HDMI輸入端的HPD引腳拉高再打開電腦的顯示輸出如果是筆記本有的機(jī)型需要檢測到顯示器才輸出信號如果是臺式機(jī)獨(dú)立顯卡插上HDMI線后一般會自動探測。然后燒錄比特流。燒錄完成后觀察開發(fā)板上的LED指示信號link_ok等。如果鏈路鎖定了link_ok應(yīng)該拉高如果一直閃爍或保持低電平大概率是接收鏈路有問題需要先用邏輯分析儀或ILA集成邏輯分析儀抓內(nèi)部信號排查。等鏈路鎖定之后顯示器上應(yīng)該出現(xiàn)電腦桌面的畫面或者測試畫面。如果畫面出現(xiàn)但顏色不對、有雪花、間歇性黑屏那就進(jìn)入下一輪的排查環(huán)節(jié)。我的建議是第一次調(diào)試不要追求一次成功把任務(wù)拆成兩步。第一步先在Vivado里看綜合報(bào)告確認(rèn)沒有嚴(yán)重時序違例第二步用ILA抓內(nèi)部信號確認(rèn)rde、rgb數(shù)據(jù)確實(shí)有值最后再看顯示器畫面。很多新手一上來就盯著屏幕看雪花完全無從下手其實(shí)ILA能告訴你信號到底卡在哪一環(huán)。5.2 常見故障黑屏、花屏、顏色異常、沒有信號黑屏或者“無視頻輸入”這個故障范圍很廣但按概率排序通常是這幾個原因HPD沒有正確拉高視頻源壓根沒開始輸出信號。EDID信息缺失或錯誤視頻源無法識別顯示端能力拒絕輸出。輸入線纜或接口接觸不良特別是HDMI的微小尺寸插口容易虛接。FPGA內(nèi)部的MMCM沒有鎖定像素時鐘沒有正確恢復(fù)。這種問題優(yōu)先排查流程先用一塊正常的顯示器替換開發(fā)板的HDMI輸出接到電腦上直接顯示排除電腦輸出異常再用一根確認(rèn)好的HDMI線替換輸入線再檢查開發(fā)板原理圖確認(rèn)HPD和DDC通道連接是否正確。如果這些都沒問題就回到Vivado里看MMCM的locked信號和ILA抓到的時鐘。畫面有嚴(yán)重雪花或斜紋說明TMDS信號接收到了但串并轉(zhuǎn)換的采樣相位不對。這幾乎是IDELAY延遲值不合適導(dǎo)致的。解決辦法是調(diào)整IDELAY的延遲tap值從上到下掃一遍找到一個畫面最清晰的范圍。有的設(shè)計(jì)會做自動訓(xùn)練在消隱期檢測對齊字符并自動調(diào)整延遲如果代碼里沒有自動訓(xùn)練就手動修改延遲值并重新跑實(shí)現(xiàn)直到畫面正常。畫面能出來但顏色明顯不對比如紅色變藍(lán)色這類問題通常是通道錯位。HDMI的通道0、1、2分別對應(yīng)B、G、R分量如果你在代碼里把三個通道的映射接錯了畫面顏色就會“乾坤大挪移”。另一個常見原因TMDS解碼時最高位MSB和最低位LSB接反那會導(dǎo)致同一顏色通道內(nèi)部的高低bit反轉(zhuǎn)畫面會出現(xiàn)很怪異的色斑。排查方式很簡單讓電腦顯示一個純紅色畫面然后看FPGA內(nèi)部的rx_rgb[23:0]抓出來的值是不是0xFF0000如果不是就能立刻定位是通道映射問題還是編碼方向問題。畫面正常但間歇性黑屏閃斷這種一般是鏈路鎖定不穩(wěn)定。原因可能是輸入視頻源的分辨率或刷新率發(fā)生了切換比如電腦在開機(jī)時輸出1080p進(jìn)入桌面后切換成了縮放分辨率或者線纜質(zhì)量太差導(dǎo)致信號抖動過大。另外要注意很多筆記本默認(rèn)開啟HDMI輸出時先把分辨率設(shè)為顯示器EDID里支持的最大值如果你開發(fā)板的EDID只寫入了1080p60而電腦默認(rèn)輸出4K或者2K就會出現(xiàn)周期性無信號。解決方法是固定電腦的輸出分辨率到1080p60或者調(diào)整開發(fā)板的EDID內(nèi)容以匹配你的視頻源。5.3 ILA抓包技巧與波形分析排查HDMI問題最強(qiáng)大的工具就是Vivado的ILA集成邏輯分析儀。但是ILA的核心資源是Block RAM抓取速率越高、深度越深消耗的資源就越多。做HDMI調(diào)試時建議ILA時鐘使用像素時鐘148.5MHz采樣深度設(shè)置4096或者8192就夠用——重點(diǎn)是抓到幾個關(guān)鍵信號的變化rx_pix_clk確認(rèn)恢復(fù)時鐘是否存在、頻率正確link_ok / mmcm_locked確認(rèn)時鐘和鏈路鎖定狀態(tài)rx_de確認(rèn)輸入信號是否進(jìn)入了有效視頻期rx_rgb [23:0]確認(rèn)有效像素期間數(shù)據(jù)是否在變化純色屏?xí)r三通道值是否符合預(yù)期hsync、vsync確認(rèn)同步信號是否存在、周期是否正確有次我調(diào)試一塊板子ILA顯示rx_de確實(shí)拉高了、像素?cái)?shù)據(jù)也不變但屏幕就是全黑。進(jìn)一步抓hsync、vsync才發(fā)現(xiàn)兩個同步信號頻率都正常但寬度不對——后來查了手冊發(fā)現(xiàn)是EDID信息里讀取的行場前肩/后肩參數(shù)與源端輸出不一致導(dǎo)致輸出端重建時序時端部的有效像素被切掉了。這類問題用ILA一抓一個準(zhǔn)效率遠(yuǎn)高于肉眼盯屏幕。6. 進(jìn)階方向從“環(huán)路”到“處理”環(huán)路實(shí)驗(yàn)跑通之后你已經(jīng)具備了FPGA視頻接收和輸出的基本鏈路能力。接下來往哪個方向走我有幾個建議第一個方向加幀緩存。用DDR3做frame buffer把輸入視頻寫入DDR緩存再按需讀取輸出。這樣你可以實(shí)現(xiàn)幀率轉(zhuǎn)換、分辨率縮放、畫中畫等功能。黑金的AX7350板載DDR3教程里也有配套的DDR讀寫實(shí)驗(yàn)可以銜接。第二個方向加圖像處理。環(huán)路基礎(chǔ)上疊加一個圖像算法模塊比如灰度化、邊緣檢測、色彩空間轉(zhuǎn)換。這類算法本身不復(fù)雜但難點(diǎn)在于處理流水線與視頻時序的配合以及DSP/BRAM的資源規(guī)劃。第三個方向升級到MIPI、LVDS等接口。HDMI學(xué)到手之后你會發(fā)現(xiàn)MIPI CSI-2攝像頭接口、LVDS液晶屏接口的接收/發(fā)送邏輯有很多相似之處都是串行差分、并行重組、通道對齊那套東西。知識遷移起來很快。第四個方向?qū)W習(xí)完整的HDMI協(xié)議棧。比如把音頻嵌入到數(shù)據(jù)島、解析EDID做分辨率自適應(yīng)、實(shí)現(xiàn)HDCP內(nèi)容保護(hù)雖然這個需要授權(quán)等等。這些是HDMI的進(jìn)階領(lǐng)域也是工業(yè)級產(chǎn)品里的核心能力。我個人在實(shí)際操作中的體會是這個環(huán)路實(shí)驗(yàn)雖然結(jié)果只是一個“跟屁蟲”一樣的畫面透傳但它把FPGA高速接口的底層鏈路完完整整地在你面前解剖了一遍。你在調(diào)IDELAY時對信號完整性的理解、在抓ILA時對時序關(guān)系的認(rèn)知、在改通道映射時對差分編碼的記憶這些都不會白費(fèi)。之后無論去做相機(jī)采集、屏幕驅(qū)動還是更復(fù)雜的視頻拼接都能用上這套底子。7. 最后再分享一個避坑小技巧調(diào)試HDMI環(huán)路時最容易被忽視的其實(shí)是電源供電。HDMI輸出端的TMDS驅(qū)動電流雖不大但1080p60下每一路高速翻轉(zhuǎn)會導(dǎo)致PCB上的電源紋波明顯變大。如果開發(fā)板用的是USB供電而你又同時給HDMI接收的EDID芯片、FPGA、DDR等外設(shè)供電很可能會出現(xiàn)“靜態(tài)正常、動態(tài)閃屏”的詭異情況。我把手頭一塊小板子從USB供電換成5V/2A的DC電源之后折騰了兩天的閃屏問題直接消失。所以你在做HDMI類實(shí)驗(yàn)時優(yōu)先用直流電源供電觀察電源指示燈是否穩(wěn)定不要迷信USB口那點(diǎn)電流。環(huán)路實(shí)驗(yàn)的整個調(diào)試周期新手大概率會花一到兩個周末。不用急把它當(dāng)成一次高速數(shù)字設(shè)計(jì)的入門歷練調(diào)通之后那種整條鏈路握在自己手里的感覺是直接用現(xiàn)成HDMI IP核的人體會不到的。