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