動MIPI DSI屏:手寫RTL替代官方IP的完整實戰(zhàn))
去年接了一個顯示相關(guān)的項目平臺是Xilinx Artix-7需要在FPGA上驅(qū)動一塊720x1280的MIPI DSI接口LCD屏做實時圖像顯示。板卡上沒有現(xiàn)成的MIPI輸出只有一個FMC擴展口所以從硬件到FPGA邏輯都得自己折騰。動手之前我也很自然地想走捷徑Xilinx官方不是有MIPI DSI TX Subsystem IP嗎雖然看著配置項復雜但至少是現(xiàn)成的。結(jié)果這一試前前后后折騰了兩周最后還是決定放棄官方方案從DPHY物理層到DSI協(xié)議層全部手寫RTL。本文記錄的就是這條完整的“踩坑”路線包括為什么官方IP不適合這個場景、MIPI DSI的時序和帶寬怎么算、RTL怎么分層、約束怎么寫以及上板后那些讓人懷疑人生的故障現(xiàn)象怎么排查。這篇文章不是理論科普是工程實操記錄。準備接手類似項目、或者正在被MIPI屏折磨的人應該能從中省下不少時間。1. 項目背景與方案選型為什么我放棄了官方IP1.1 項目需求與屏選型先交代具體項目背景。屏的型號這里就不寫具體編號了但規(guī)格很有代表性720x1280豎屏24-bit RGB4 lane MIPI DSI接口支持60Hz刷新。主控端是Artix-7 XC7A35TVivado版本2018.3。圖像源來自FPGA內(nèi)部處理好的視頻流通過AXI4-Stream輸入到顯示模塊。這塊屏需要的初始化序列不多但有幾個關(guān)鍵寄存器要配比如像素格式、地址模式、TE使能等。這些都不是難題真正的難題在MIPI接口本身。選擇Artix-7其實是個很現(xiàn)實的決定成本敏感又不想要Zynq的PS側(cè)。XC7A35T邏輯資源有限LUT大概2萬個出頭所以后面對IP的資源占用也要嚴格控制。這為后面放棄官方IP埋下了伏筆。1.2 官方IP的“三座大山”我一開始的打算是直接用Xilinx的MIPI DSI TX Subsystem。查找IP時倒是一搜就有看起來也很正規(guī)視頻輸入走AXI4-Stream初始化序列和寄存器配置走AXI4-Lite物理層由配套的MIPI D-PHY IP完成。但真做起來我遇到三座大山每一座都足以讓人崩潰。第一座是時鐘域泛濫。這個IP要求的時鐘實在是太多了S_AXI_ACLK走AXI寄存器DPI_PIXEL_CLK走像素BYTE_CLK走串行字節(jié)LP_CTRL_CLK走低功耗控制還要求多個時鐘同步關(guān)系。每個時鐘都得用MMCM/PLL或者BUFG管好少一個、偏一個IP就會在仿真或上板時報莫名其妙的狀態(tài)。Vivado里接入這么多時鐘布線資源和約束復雜度都上去了。第二座是初始化序列的下發(fā)方式。官方IP里屏的初始化命令要通過AXI4-Lite寄存器一條一條寫進去寄存器偏移地址都在IP手冊里。倒不是手冊不給而是不同Vivado版本的寄存器映射還略有不同網(wǎng)上搜到的例程和手冊v版本經(jīng)常對不上。最麻煩的是屏廠提供的初始化序列往往很長動輒幾百個字節(jié)IP的寄存器空間有限還得改寄存器窗口、分批寫。調(diào)試過程中你根本不知道屏是在哪一行命令之后才出問題的。第三座是物理層IP的授權(quán)和配置。7系列沒有硬核MIPI D-PHY只能用軟核D-PHY IP。這個D-PHY IP本身還需要單獨選型部分版本上板有授權(quán)限制不支持就只能在仿真里看波形沒法實際點亮。對做硬件的人來說看到這種要求基本就想放棄了。1.3 什么時候才建議用官方IP這里不是一棍子打死官方IP。如果你的項目用了帶硬核MIPI PHY的UltraScale器件、正好分辨率也跟IP例程一致、而且團隊有Xilinx技術(shù)支持或者已經(jīng)熟悉這套IP的寄存器體系那用官方IP是合理的畢竟省了自己維護協(xié)議棧。但在低成本7系列、非標準分辨率、初始化序列復雜的小項目里官方IP的靈活性和可觀測性都很差。出問題時IP內(nèi)部信號看不到協(xié)議層和物理層協(xié)作黑盒根本沒法定位是物理時序問題還是協(xié)議包問題。手寫RTL雖然工作量不小但至少每個狀態(tài)、每個信號都在你掌控之中出了故障能自己分析。2. MIPI DSI協(xié)議與FPGA側(cè)的時鐘規(guī)劃2.1 D-PHY物理層、LP與HS狀態(tài)機MIPI DSI分三層物理層D-PHY、協(xié)議層DSI、應用層視頻流/命令。FPGA側(cè)寫RTL時最花時間的就是D-PHY。D-PHY的工作模式分LPLow Power和HSHigh Speed。LP模式下差分線Dp和Dn分別用普通數(shù)字電平表示邏輯組成LP-00、LP-01、LP-10、LP-11四種狀態(tài)。屏的初始化命令、寄存器讀寫、休眠喚醒都在LP模式下通過狀態(tài)翻轉(zhuǎn)進行。HS模式下Dp/Dn則是一對高速差分信號用于傳輸像素數(shù)據(jù)和快速命令差分擺幅很低頻率很高。從LP進入HS并不是直接切要經(jīng)過一段嚴格的狀態(tài)序列。簡單說就是從LP-11空閑態(tài)出發(fā)讓Dn先拉低再讓Dp拉低形成HS-0的建立過程然后發(fā)送一串同步碼SoT接收端才認為高速數(shù)據(jù)開始。同樣高速傳輸結(jié)束后也要經(jīng)歷HS-Trail和LP-11恢復。這些狀態(tài)停留時間有最小/最大值要求比如T_LPX、T_HS_PREPARE、T_HS_ZERO、T_HS_TRAIL、T_HS_EXIT等。對于FPGA來說這意味著至少需要一個狀態(tài)機來管理LP/HS切換并且要精確計時。我用的基準時鐘是byte_clk也就是串行速率的1/8。每個狀態(tài)停留多少個周期都要根據(jù)實際時鐘頻率算好。后面在3.2節(jié)我會給出代碼骨架。2.2 數(shù)據(jù)帶寬計算與時鐘設(shè)計手寫MIPI驅(qū)動第一步不是寫代碼而是算時鐘。這個算不明白后面全是坑。以720x128060Hz為例。屏廠給的行場消隱參考值一般是水平HBP80HFP48HSYNC16所以H_TOTAL720801648864垂直VBP12VFP8VSYNC4所以V_TOTAL128012481304幀率約60Hz像素時鐘計算 pclk H_TOTAL x V_TOTAL x 60Hz 864 x 1304 x 60 ≈ 67.6MHz如果直接讓DSI以24-bit RGB傳輸所需原始帶寬就是 帶寬 pclk x 24bit 67.6MHz x 24bit ≈ 1.622Gbps四個lane平均 每lane速率 1.622Gbps / 4 ≈ 405.6Mbps這還只是純視頻數(shù)據(jù)沒算包頭、包尾、Lane空閑和保護間隔。實際取整的時候我留了20%以上裕量最終把lane rate定在500Mbps。這樣byte_clk就是 byte_clk 500Mbps / 8 62.5MHz參數(shù)數(shù)值說明分辨率720x1280x24bitRGB888幀率60Hz含消隱約67.6MHz pclk所需總帶寬1.622Gbps不含協(xié)議開銷Lane數(shù)4每lane約405.6Mbps實際lane rate500Mbps留約23%裕量byte_clk62.5MHzlane rate的1/8這里要提醒一個工程細節(jié)pclk是67.6MHzbyte_clk是62.5MHz兩者并不整除所以像素數(shù)據(jù)不能簡單用同一個時鐘搬運必須在中間加異步FIFO。寫時鐘用pclk讀時鐘用byte_clkFIFO深度按兩行數(shù)據(jù)量起步防止消隱期間突發(fā)的數(shù)據(jù)堆積導致溢出。2.3 DSI包格式與初始化序列DSI協(xié)議層傳兩種包短包和長包。短包用于寄存器配置和簡單命令固定4字節(jié)結(jié)構(gòu)是Data Type1字節(jié)、WordCount2字節(jié)通常為0、ECC1字節(jié)。比如Sleep Out0x11、Display On0x29、Set Pixel Format0x3A這些DCS命令都用短包發(fā)送。長包用于批量寫入。結(jié)構(gòu)是4字節(jié)包頭Data Type Word Count ECC接著是數(shù)據(jù)payload最后是2字節(jié)CRC。初始化序列里那些一長串廠商寄存器配置比如0xB0開頭的定制命令基本都用長包發(fā)送。表里列出幾個我實際用到的Data TypeData Type含義常見用途0x05Short Write No Parameter簡單的零參數(shù)DCS命令0x15Short Write One Parameter帶1字節(jié)參數(shù)的DCS命令0x39Generic Long Write通用長數(shù)據(jù)包0x29DCS Long WriteDCS規(guī)范長命令0x37Set Maximum Return Packet Size設(shè)置讀回包長度CRC計算用的是CRC-16/XMODEM多項式0x1021初值0xFFFF對“包頭payload”整體計算。這個在FPGA里用LFSR逐位算就行很簡單。ECC是漢明碼很多屏其實不校驗但規(guī)范上短包必須帶我后來寫了個查表邏輯出來資源不多順手就做了。初始化序列的發(fā)送順序非常關(guān)鍵。屏規(guī)格書會給你一長串命令比如先把像素格式設(shè)為RGB888再設(shè)置地址模式旋轉(zhuǎn)方向開啟TE使能Sleep Out延時幾十毫秒最后Display On。順序錯了、延時短了屏都是白屏。這些命令在FPGA里建議放到ROM或者RAM里由狀態(tài)機逐個發(fā)送具體架構(gòu)在3.4節(jié)說。2.4 Xilinx 7系列FPGA架構(gòu)映射7系列FPGA沒有真正的D-PHY硬核但可以利用OSERDESE2和OBUFTDS/OBUFDS實現(xiàn)HS高速輸出和LP三態(tài)控制。OSERDESE2能做到8:1串行化也就是把8位并行數(shù)據(jù)轉(zhuǎn)成1位高速串行輸出。配合DDR模式可以讓內(nèi)部邏輯時鐘工作在byte_clk而串行數(shù)據(jù)以8x速率從IOB輸出。對于500Mbps lane ratebyte_clk是62.5MHzOSERDESE2工作在62.5MHz輸出DDR剛好8:1。OBUFTDS是差分離散三態(tài)輸出緩沖T信號控制是否輸出。HS模式下T拉低數(shù)據(jù)直接輸出LP模式下T拉高輸出高阻同時用額外的GPIO來控制Dp/Dn的LP電平。這樣設(shè)計最關(guān)鍵的一點HS和LP不能打架狀態(tài)機必須保證先讓OBUFTDS切到高阻再切LP驅(qū)動不然線上會出現(xiàn)毛刺。3. 從零手寫RTL完整實現(xiàn)路徑3.1 頂層模塊劃分正式開寫前我把整條鏈路拆成幾個模塊每個模塊職責單一方便仿真和ILA調(diào)試dphy_tx_ctrlD-PHY物理層狀態(tài)機負責LP/HS切換、LP電平輸出、HS數(shù)據(jù)輸出控制dsi_packet_gen協(xié)議層包生成支持短包和長包自動加上ECC和CRCdsi_video_ctrl視頻時序控制器從異步FIFO讀像素按行場時序切成DSI burstdsi_init_engine初始化序列引擎從RAM/ROM讀命令字節(jié)按命令間隔延時async_fifo_video跨時鐘域FIFOpclk寫、byte_clk讀模塊之間的連接關(guān)系是init_engine或video_ctrl產(chǎn)生需要發(fā)送的包packet_gen把包內(nèi)容拼成DSI格式dphy_tx_ctrl最終把串行數(shù)據(jù)和LP狀態(tài)送到引腳。視頻流和初始化命令共享同一對差分線所以兩者必須互斥不能同時占用。我加了一個busy仲裁信號誰請求誰發(fā)送寫起來簡單且可靠。3.2 DPHY發(fā)送控制器LP/HS狀態(tài)機關(guān)鍵代碼DPHY狀態(tài)機是整個設(shè)計最核心的部分。我的狀態(tài)編碼是這樣的localparam S_LP_STOP 4d0; // LP-11 空閑 localparam S_LP_ENTRY 4d1; // 進入LP握手 localparam S_HS_PREP 4d2; // HS準備 localparam S_HS_ZERO 4d3; // HS建立 localparam S_HS_SYNC 4d4; // 發(fā)送SoT同步碼 localparam S_HS_DATA 4d5; // 高速數(shù)據(jù) localparam S_HS_TRAIL 4d6; // HS結(jié)束 localparam S_LP_EXIT 4d7; // 回到LPHS模式下OSERDESE2持續(xù)輸出數(shù)據(jù)來源由packet_gen提供。LP模式下Dp和Dn作為普通GPIO輸出狀態(tài)機的LP_ENTRY和LP_EXIT部分負責發(fā)送SoT/EoT序列。為了精確計時我引入T_HS_PREP等常量單位是byte_clk周期。比如500Mbps時byte_clk周期16nsDPHY要求的T_HS_PREPARE約40ns所以至少計數(shù)3拍。OSERDESE2的例化我摘一段OSERDESE2 #( .DATA_RATE_OQ(DDR), .DATA_RATE_TQ(BUF), .DATA_WIDTH(4), .SERDES_MODE(MASTER), .TRISTATE_WIDTH(1) ) u_oserdes_dp ( .D1 (ser_data[0]), .D2 (ser_data[1]), .D3 (ser_data[2]), .D4 (ser_data[3]), .D5 (1b0), .D6 (1b0), .D7 (1b0), .D8 (1b0), .TCE (1b1), .OCE (1b1), .CLK (ser_clk), // 500MHz .CLKDIV (byte_clk), // 62.5MHz .RST (rst), .T (tx_oe ? 1b0 : 1b1), .OQ (dphy_dp_int), .OFB (), .TQ () );注意這里T端口我直接接tx_oe當tx_oe為低時數(shù)據(jù)正常輸出為高時輸出高阻。OBUFTDS再把這個差分信號接到物理引腳OBUFTDS #(.IOSTANDARD(LVDS_25)) u_obuftds_dp ( .I (dphy_dp_int), .T (tx_oe ? 1b0 : 1b1), .O (dphy_dp_p), .OB(dphy_dp_n) );用LVDS_25直接驅(qū)動D-PHY其實是靠著短距離和FMC連接器上接觸良好才能工作的。嚴格來說D-PHY的HS共模電平約200mV和LVDS的1.25V共模差很多長時間運行或者長走線會有可靠性問題。如果是量產(chǎn)產(chǎn)品建議用專門的MIPI DSI橋接芯片或者電阻網(wǎng)絡(luò)做電平適配不要拿LVDS硬懟。這個我在后面踩坑里還會提。3.3 視頻時序生成與數(shù)據(jù)FIFO設(shè)計視頻發(fā)送的思路是按照面板的行場時序在消隱期間盡量讓線空下來進入LP模式在有效顯示區(qū)間把所有像素數(shù)據(jù)通過DSI長包打出去。這種方式就是DSI BURST MODE。我從AXI4-Stream或者內(nèi)部圖像模塊接收像素先存入異步FIFO。FIFO讀端速率是byte_clk寫端是pclk。像素是24bitDSI一次長包最好按字節(jié)對齊所以FIFO位寬我設(shè)計成64bit每次讀出8個像素再拆成24字節(jié)一個長包發(fā)出去。視頻包的數(shù)據(jù)包類型我仍然用0x39通用長包發(fā)送因為很多屏對0x29和0x39都支持0x39在初始化序列里也會用到邏輯復用更簡單。視頻長包與命令長包的區(qū)別只在于Word Count和payload內(nèi)容。行時序上我讓整個行有效區(qū)都處于HS模式連續(xù)發(fā)送一行720個像素24bit RGB共2160字節(jié)。由于帶寬有余量每一行發(fā)完很快就結(jié)束剩下的HFP、HBP時間里DPHY回到LP-11狀態(tài)這也是省功耗的地方。異步FIFO必須有 almost_empty 和 almost_full 信號。視頻模塊只在 almost_full 為低時才寫入否則圖像源前一級停一拍。否則幀率不匹配時FIFO會溢出屏幕直接撕裂。3.4 初始化序列執(zhí)行引擎初始化序列引擎說白了就是一個狀態(tài)機加一塊命令存儲。我的實現(xiàn)方式是在Xilinx Block Design里加一個MicroBlaze軟核通過AXI-Lite接口往FPGA側(cè)的小RAM里寫初始化命令字節(jié)寫完后寫一個啟動寄存器init_engine就開始按命令表發(fā)送。每條命令的存儲格式是一張三元組表命令類型、數(shù)據(jù)長度、延時時間。命令類型用來區(qū)分短包還是長包數(shù)據(jù)長度告訴引擎要發(fā)多少字節(jié)延時時間指的是發(fā)完這條命令后要等待多少ms。比如Sleep Out通常要等120ms這期間引擎就停在等待狀態(tài)發(fā)一個計數(shù)器脈沖過去。如果不用軟核也可以用純RTL狀態(tài)機把命令表寫死在ROM里。臨時項目這樣最省事但后期換屏就要改RTL重新綜合。我這邊因為有MicroBlaze所以把命令表放RAM調(diào)試時直接改軟件不用重新編譯硬件。這里給出一小段命令存儲格式的Verilog定義typedef struct packed { logic [1:0] pkt_type; // 2b00: short, 2b01: long logic [15:0] len; // payload byte count logic [31:0] delay_us; // post-command delay in us } cmd_entry_t;命令表按字節(jié)順序存放在RAM里引擎逐個解析。命令發(fā)送完成后狀態(tài)機拉高 init_done 信號隨后視頻模塊才開始送圖像數(shù)據(jù)。這個先后順序很關(guān)鍵。很多屏如果Display On命令還沒發(fā)就來了視頻流會直接忽略掉結(jié)果就是一直黑屏。3.5 約束文件XDC的關(guān)鍵點MIPI DSI是高速差分接口XDC約束不能只寫個引腳就完事。我實際用的關(guān)鍵約束大概是set_property PACKAGE_PIN AG12 [get_ports {dphy_dp_p[0]}] set_property PACKAGE_PIN AG11 [get_ports {dphy_dp_n[0]}] set_property IOSTANDARD LVDS_25 [get_ports {dphy_dp_p[0]}] set_property IOSTANDARD LVDS_25 [get_ports {dphy_dp_n[0]}] set_property DIFF_TERM TRUE [get_ports {dphy_dp_p[0]}] set_property PACKAGE_PIN AH12 [get_ports {dphy_clk_p}] set_property PACKAGE_PIN AH11 [get_ports {dphy_clk_n}] set_property IOSTANDARD LVDS_25 [get_ports {dphy_clk_p}] set_property IOSTANDARD LVDS_25 [get_ports {dphy_clk_n}] set_property DIFF_TERM TRUE [get_ports {dphy_clk_p}]時鐘約束方面ser_clk是500MHz如果從PLL輸出需要創(chuàng)建生成時鐘create_generated_clock -name ser_clk -source [get_pins mmcm_inst/CLKOUT0] \ -divide_by 1 [get_pins oserdes_inst/CLK]實際做下來最容易被忽略的是差分極性。PCB上P/N一旦接反畫面會出現(xiàn)顏色分量錯位、花屏等問題。排查時先看原理圖確認P和N沒有反接如果反了就把PACKAGE_PIN對調(diào)一下或者換IOSTANDARD里的P/N極性。4. 上板調(diào)試實錄與踩坑記錄4.1 白屏問題的排查第一次上板屏背光亮但畫面全白。這種問題最多見也最需要系統(tǒng)性排查。第一步檢查初始化序列有沒有真正執(zhí)行完。我在init_engine里加了一個計數(shù)器整段命令表發(fā)完以后拉高init_done同時把計數(shù)值通過GPIO送到板上的LED。實測init_done始終為低說明初始化卡在中間的延時等待。用ILA抓了state寄存器發(fā)現(xiàn)引擎一直停在Sleep Out后的120ms等待狀態(tài)。MicroBlaze側(cè)的延時函數(shù)寫的是毫秒但計時基準配錯了導致實際等待時間遠超預期后續(xù)命令一直發(fā)不出去。改掉定時器配置后init_done能拉高了但屏幕依然是白屏。第二步檢查Display On命令。這里有個比較容易忽略的細節(jié)很多屏的初始化流程是先Sleep Out等待然后送顯示配置最后Display On但Display On之前屏幕其實已經(jīng)能接受視頻數(shù)據(jù)只是還沒開啟顯示。如果視頻模塊里沒有等init_done就開始送數(shù)據(jù)像素會被屏內(nèi)部丟棄。我調(diào)整了視頻啟動條件讓init_done為高以后才開始發(fā)視頻長包。第三步如果還是白屏就要懷疑像素格式。我用0x3A命令配置了RGB888但視頻模塊發(fā)送的像素順序是BGR888直接被屏顯示成藍色調(diào)或花白。后來統(tǒng)一了RGB/BGR順序并用ILA抓了第一個長包的字節(jié)序列逐個字節(jié)對才能正常顯示。4.2 花屏和半屏的定位白屏之后最常見的是花屏?;ㄆ练趾脦追N每種背后原因不同?,F(xiàn)象是顏色順序錯亂、出現(xiàn)彩色噪點基本都是RGB/BGR順序或者字節(jié)序問題。MIPI DSI發(fā)送像素時如果按0xRR 0xGG 0xBB順序發(fā)送面板不一定按這個順序解釋要按面板規(guī)格書來。排查時我用ILA抓一串像素數(shù)據(jù)和屏廠給的參考波形比對?,F(xiàn)象是整個畫面偏移、左側(cè)有異常色帶這是porch參數(shù)不對面板的HBP和HFP和視頻模塊配置不一致。比如視頻模塊按HBP80先生成消隱但屏端實際等到了120個像素時鐘才開始采樣圖像就會整體偏移。這時候不要憑空猜直接把初始化代碼里的porch參數(shù)和視頻模塊的porch參數(shù)對著屏廠規(guī)格書一個個查?,F(xiàn)象是上半屏正常、下半屏錯位這個我在另一個項目里遇到原因是TE信號和幀同步?jīng)]有對齊。面板用TE信號作為內(nèi)部幀同步基準視頻源必須保證一幀數(shù)據(jù)從正確時刻開始發(fā)。如果FIFO在讀空之后重新啟動幀頭位置漂了下半屏就會錯。解決方法是把TE信號引入FPGA用TE上升沿清空FIFO并重新啟動幀計數(shù)。4.3 LP通信正常但HS無輸出這個是我在調(diào)試中最頭疼的問題之一。初始化序列能發(fā)說明LP通信正常但初始化完成后屏幕沒有反應ILA顯示HS數(shù)據(jù)確實在發(fā)送但用示波器在引腳上卻看不到差分信號。先懷疑OSERDESE2配置反復對著手冊查了幾遍沒問題。后來用示波器單端探頭去量Dp對地發(fā)現(xiàn)信號大概有300mV左右的噪聲不像正常的0-200mV差分跳變。最后定位到是OBUFTDS的T端口時序問題tx_oe拉低的同時OSERDESE2的輸出數(shù)據(jù)還沒有穩(wěn)定導致HS建立階段出現(xiàn)毛刺D-PHY接收端根本識別不了SoT。解決辦法是在狀態(tài)機里加了一拍緩沖先讓tx_oe拉低、等待至少2個byte_clk周期之后再送SoT同步碼。這對很多FPGA設(shè)計都是個容易踩的坑HS信號必須在差分線上穩(wěn)定建立以后才能開始傳數(shù)據(jù)否則接收端會誤判。4.4 常見問題速查表現(xiàn)象可能原因排查思路白屏背光亮初始化命令未發(fā)完 / Sleep Out后延時不夠 / Display On未執(zhí)行抓init_done確認命令表和延時ILA監(jiān)測狀態(tài)寄存器花屏顏色錯亂RGB/BGR順序錯 / 字節(jié)序錯用ILA對比第一個長包字節(jié)序列與屏廠規(guī)格整體偏移HBP/HFP與屏端不符核對porch參數(shù)與初始化代碼上半屏正常下半屏錯位TE信號未同步幀頭漂移將TE引入FPGA用TE上升沿復位FIFO讀寫指針HS發(fā)送了但屏無反應OBUFTDS的T端口時序不對 / SoT毛刺示波器看Dp/Dn波形tx_oe拉低后至少延遲2拍再發(fā)SoT畫面閃、幀率不穩(wěn)FIFO幾乎溢出/讀空加大FIFO深度檢查almost_full與視頻源反壓邏輯大量高頻噪點排線過長、電平不匹配縮短FPC/FMC走線考慮增加MIPI轉(zhuǎn)接芯片5. 與SDK/軟核的配合5.1 通過AXI GPIO聯(lián)動控制這塊屏的調(diào)試離不開MicroBlaze但不是必須。我選擇MicroBlaze主要是為了靈活修改初始化命令表和PWM調(diào)背光。工程上如果初始化序列固定不變純Verilog狀態(tài)機完全夠用資源更少、啟動更快。Block Design里我掛了三個外設(shè)AXI GPIO控制背光PWM、斷電使能、TE輸入AXI BRAM Controller掛一塊8KB RAM存初始化命令表AXI Timer用于命令延時和PWM周期MicroBlaze上電后先把命令表寫入BRAM然后置位start寄存器。init_engine檢測到start后開始讀BRAM并執(zhí)行初始化序列執(zhí)行完拉高init_done中斷給MicroBlaze。這樣軟件的靈活性體現(xiàn)在換一塊不同型號的屏只需要改命令表不需要動RTL。5.2 寄存器設(shè)計與MCU交互如果你所在項目有外部MCU或者Zynq PS那控制寄存器就更有必要了。我定義了一組簡單的寄存器地址偏移地址寄存器描述0x00CTRLbit0: init_start, bit1: video_enable0x04STATUSbit0: init_done, bit1: fifo_full0x08CMD_ADDR命令表寫地址0x0CCMD_DATA命令表寫數(shù)據(jù)0x10TE_CNTTE脈沖計數(shù)用于幀率監(jiān)測這套寄存器接口非常通用Zynq PS、MicroBlaze、甚至外部SPI主機都能訪問。如果你不想在PL側(cè)做CPU可以直接把這三個寄存器映射到AXI-Lite從機讓外部MCU通過SPI-to-AXI橋來訪問。靈活度很高。5.3 調(diào)試工具與觀測方法MIPI問題用ILA看內(nèi)部邏輯波形是基本功但一定要看對信號。我通常會把下面這些信號引到ILAinit_engine的statedphy_tx_ctrl的lp_state和hs_statevideo_ctrl的line_cnt和frame_cntfifo的rd_en、wr_en、almost_full、almost_emptytx_oe和dphy_dp_int數(shù)據(jù)量會很大ILA深度至少選16384采樣時鐘用byte_clk。用觸發(fā)條件把tx_oe下降沿觸發(fā)看第一包HS數(shù)據(jù)。如果第一包就錯了后面的分析都是白費。示波器方面如果帶寬不夠直接測試Dp/Dn差分信號會很痛苦。我建議用單端探頭分別量Dp和Dn看在HS傳輸時能否看到穩(wěn)定翻轉(zhuǎn)。如果只有微弱的電平抖動大概率是輸出高阻或者電平不匹配。差分探頭當然最好但很多項目里不一定有。6. 寫在最后這套方案的后續(xù)擴展如果你問我現(xiàn)在再做一個MIPI DSI項目我還會選擇手寫RTL嗎我的答案是會但前提是對MIPI協(xié)議已經(jīng)有清晰的理解。手寫方案的最大優(yōu)勢是可控性。任何一個IP黑盒不能告訴你的事在RTL里都是自己代碼。遇到問題ILA一點就能看到狀態(tài)機停在哪而不是拿著IP手冊一頁一頁翻寄存器。尤其是在非標分辨率、定制初始化序列、特殊時序要求的項目中這套方案幾乎是無敵的。往后擴展的方向也很多比如增加雙lane轉(zhuǎn)四lane的分拆邏輯、支持多屏拼接、把初始化命令表放到SD卡里動態(tài)加載、用CRC校驗回讀面板狀態(tài)等。底層DPHY和DSI包生成模塊只要寫好了換屏基本只改參數(shù)和命令表不用動核心RTL。最后分享一個小技巧如果你第一次做MIPI DSI不要一上來就上屏。先用一塊支持loopback的FMC轉(zhuǎn)接板或者邏輯分析儀把D-PHY的LP狀態(tài)翻轉(zhuǎn)和HS數(shù)據(jù)抓下來確認沒問題再接到屏上。省下的不只是調(diào)試時間還有你對著白屏發(fā)呆時逐漸崩潰的信心。