PGL50H FPGA PCIe通信開發(fā)實(shí)戰(zhàn):從IP核配置到鏈路識(shí)別)
折騰了差不多兩個(gè)禮拜終于把紫光同創(chuàng)PGL50H開發(fā)板的PCIe鏈路完整跑通從PDS里導(dǎo)入IP核、配置PCIE硬核、生成bitstream到把板卡插進(jìn)電腦最后在Windows設(shè)備管理器里看到那個(gè)設(shè)備出現(xiàn)。這個(gè)過程比我預(yù)想的要順利但也踩了不少文檔里沒有細(xì)寫的坑。這篇教程我會(huì)把操作順序和背后原理一起講清楚適合手頭已經(jīng)有PGL50H開發(fā)板、正準(zhǔn)備做PCIe通信的朋友參考如果你是從Xilinx的Vivado轉(zhuǎn)過來想快速上手國(guó)產(chǎn)工具鏈直接照著做也完全行。先說清楚這玩意兒到底能干什么。PGL50H是紫光同創(chuàng)Logos系列里經(jīng)常被拿來當(dāng)原型驗(yàn)證板的一顆FPGA自帶PCIe硬核可以直接在板卡上做成一個(gè)標(biāo)準(zhǔn)的PCIe端點(diǎn)設(shè)備。硬核幫你把物理層、數(shù)據(jù)鏈路層、事務(wù)層大部分事情都做了作為使用者我們真正要操作的是配置空間、寄存器接口和數(shù)據(jù)通路這一層。相比用邏輯自己搭收發(fā)器這個(gè)難度已經(jīng)是友好太多了。文章最后做到的效果就是Windows設(shè)備管理器能正確識(shí)別到這塊FPGA并且配置空間能被正常讀取說明鏈路從物理層到事務(wù)層全部打通后續(xù)做DMA、數(shù)據(jù)采集或者寄存器讀寫都有了一個(gè)可靠基礎(chǔ)。1. 項(xiàng)目全貌與準(zhǔn)備工作1.1 PGL50H的PCIe能力定位在開始操作之前我建議你先搞清楚手上的開發(fā)板到底把PCIe引腳引到了哪里、金手指是x1還是x4、參考時(shí)鐘怎么供給。紫光同創(chuàng)PGL50H這顆器件本身帶有PCIe硬核理論上可以配置成Endpoint模式支持的鏈路寬度和速率取決于具體封裝、板卡設(shè)計(jì)和IP核版本。我這次調(diào)試用的是x1鏈路Gen2速率也就是單通道5GT/s對(duì)驗(yàn)證通信鏈路來說已經(jīng)完全夠用。這里多說一句PCIe和普通邏輯調(diào)試最大的區(qū)別是它不是上電就能用的。FPGA內(nèi)部邏輯跑起來之后PCIe硬核還要經(jīng)過復(fù)位釋放、訓(xùn)練序列、配置空間枚舉這一整套流程電腦主板才會(huì)認(rèn)為你插了一個(gè)正常的PCIe設(shè)備。任何一個(gè)環(huán)節(jié)斷掉Windows設(shè)備管理器里就什么都看不到而且不會(huì)給你任何報(bào)錯(cuò)。所以做這個(gè)項(xiàng)目要有心理準(zhǔn)備很多時(shí)候問題不在代碼而在鏈路建立的時(shí)序和硬件環(huán)境上。1.2 軟硬件清單與開發(fā)環(huán)境我這次實(shí)際用的環(huán)境如下供你對(duì)照自己的情況參考。硬件方面一塊PGL50H開發(fā)板帶PCIe金手指那種不是拿普通排線引出來的一臺(tái)有PCIe插槽的Windows電腦最好是有人用過的舊機(jī)器避免BIOS安全啟動(dòng)之類的東西干擾一個(gè)配套的JTAG調(diào)試器用來下載bitstream另外我推薦準(zhǔn)備一個(gè)PCIe轉(zhuǎn)接卡不是所有開發(fā)板都能剛好插進(jìn)機(jī)箱很多時(shí)候需要豎起來放轉(zhuǎn)接卡能省掉不少物理上的麻煩。軟件方面紫光同創(chuàng)的FPGA開發(fā)軟件PDS是必須的注意裝的時(shí)候路徑不要有中文建議直接裝到磁盤根目錄下的某個(gè)英文文件夾里。仿真用的ModelSim或Questa我也裝了雖然這個(gè)項(xiàng)目不寫復(fù)雜邏輯但I(xiàn)P核生成之后做一遍行為仿真的價(jià)值很大能幫你在上板前就排除掉時(shí)鐘和復(fù)位的問題。還有一個(gè)容易被忽略的是串口調(diào)試終端用來看FPGA內(nèi)部運(yùn)行狀態(tài)后面排查問題會(huì)用到。PDS這個(gè)工具如果你之前只用過Vivado第一感覺會(huì)有點(diǎn)熟悉又有點(diǎn)陌生。它也有工程管理、綜合、布局布線、bitstream生成這一整套流程但一些操作入口和叫法不太一樣。建議先花半小時(shí)新建一個(gè)點(diǎn)亮LED的簡(jiǎn)單工程跑通下載流程確保JTAG鏈路和板卡配置流程是好的再進(jìn)入PCIe的調(diào)試。跳過這一步直接搞PCIe的話出了問題很難判斷是工具問題還是硬件問題。1.3 讓License順利生效紫光同創(chuàng)PDS的License是很容易卡住人的一個(gè)地方。軟件裝好后第一次啟動(dòng)會(huì)要求加載License文件。這個(gè)文件需要去官網(wǎng)注冊(cè)賬號(hào)申請(qǐng)一般發(fā)給FAE或者下載中心填個(gè)表格就能拿到。不要試圖自己找許可證生成器之類的東西國(guó)產(chǎn)FPGA工具鏈的License服務(wù)經(jīng)常更新老老實(shí)實(shí)申請(qǐng)才是正路。拿到License文件之后在PDS啟動(dòng)界面選擇“使用License文件”把路徑指過去就行。注意License文件和PDS版本要匹配我用的是一個(gè)較新的PDS版本搭配官方對(duì)應(yīng)版本的License才識(shí)別正常。另外License有時(shí)效性申請(qǐng)的時(shí)候看清有效期如果調(diào)試到一半License過期IP核生成會(huì)直接報(bào)錯(cuò)你可能會(huì)以為是配置問題其實(shí)是授權(quán)問題。實(shí)踐經(jīng)驗(yàn)把License文件放在PDS安裝目錄下的一個(gè)固定文件夾里并且手動(dòng)添加環(huán)境變量LM_LICENSE_FILE指向它這樣以后啟動(dòng)不用每次手動(dòng)選。還有一種情況是裝的殺毒軟件把License讀取攔截了如果啟動(dòng)PDS提示找不到License且路徑?jīng)]問題關(guān)掉殺毒軟件再試一次。2. PCIe IP核安裝與參數(shù)配置2.1 三步把IP核裝進(jìn)PDSPDS安裝完成后自帶的IP庫里是有PCIe IP的但具體到某個(gè)器件型號(hào)有時(shí)需要單獨(dú)下載對(duì)應(yīng)版本的IP包導(dǎo)入。這個(gè)步驟在Vivado里非常順滑因?yàn)閄ilinx把IP都集成在軟件里了紫光同創(chuàng)目前的流程稍微繞一點(diǎn)。我這次實(shí)際走的流程大致可以總結(jié)成三步。第一步去官網(wǎng)下載中心找PGL50H對(duì)應(yīng)的PCIe IP包注意IP包有版本號(hào)和工具版本對(duì)應(yīng)關(guān)系下載時(shí)看清說明。第二步解壓IP包你會(huì)看到里面有一個(gè)類似.pso或者.zip格式的發(fā)布文件這個(gè)文件就是給PDS導(dǎo)入用的。第三步在PDS中打開菜單欄里的IP相關(guān)管理入口選擇導(dǎo)入IP包指定剛才解壓出來的文件系統(tǒng)會(huì)自動(dòng)幫你安裝到當(dāng)前版本的IP庫里。導(dǎo)入完成之后新建工程的時(shí)候或者在IP Compiler里就能看到PCIe IP了。如果沒有看到多半是版本不匹配或者導(dǎo)入操作沒有真正成功。我遇到過一種情況是解壓路徑有中文導(dǎo)致導(dǎo)入一直失敗把文件換個(gè)目錄重新解壓就好了。導(dǎo)入IP包這一步對(duì)使用PDS的人來說屬于基礎(chǔ)操作但坑就在版本匹配上一定不要拿別的器件型號(hào)的IP包硬套。2.2 配置PCIe IP核的六個(gè)關(guān)鍵點(diǎn)打開IP Compiler選擇PCIe IP命名之后進(jìn)入配置界面。這個(gè)界面里的選項(xiàng)有點(diǎn)多但不是每個(gè)都要?jiǎng)印N疫@次實(shí)際配置的時(shí)候重點(diǎn)關(guān)注下面這幾個(gè)位置你照著設(shè)置一遍基本不會(huì)走彎路。鏈路寬度選擇x1還是x4取決于你板卡金手指實(shí)際引出了幾對(duì)收發(fā)器。這個(gè)不是想設(shè)多少就設(shè)多少的設(shè)多了可能因?yàn)橛布蠜]接線導(dǎo)致訓(xùn)練失敗設(shè)少了浪費(fèi)性能。我第一次就是圖省事配置成x4結(jié)果板子是x1的引線訓(xùn)練一直失敗后來才發(fā)現(xiàn)問題。速率方面我選了Gen2。如果你參考時(shí)鐘質(zhì)量一般或者走線比較長(zhǎng)可以先用Gen1驗(yàn)證鏈路穩(wěn)定后再上Gen2。這個(gè)改動(dòng)只是IP核配置里一個(gè)下拉框的事但影響的是整個(gè)SerDes的工作狀態(tài)建議從低速率開始。參考時(shí)鐘配置上PCIe標(biāo)準(zhǔn)要求100MHz差分時(shí)鐘。IP核界面里通常會(huì)讓你選擇參考時(shí)鐘來源和頻率注意如果板子上是125MHz或者別的頻率一定要改過來。還有彈性緩存那塊物理層會(huì)自動(dòng)處理兩端的時(shí)鐘頻率偏差不需要手動(dòng)干預(yù)你只需要保證給到IP核的參考時(shí)鐘頻率正確就好。端點(diǎn)模式必須設(shè)為Endpoint如果你是做根復(fù)合體實(shí)驗(yàn)才選Root Complex。BAR空間這里我開了BAR0大小設(shè)置為4KB地址空間映射方式保持默認(rèn)。BAR空間是Windows枚舉后給設(shè)備分配的內(nèi)存窗口后續(xù)讀寫寄存器都靠它。中斷方式我這次直接選了MSI比傳統(tǒng)INTx中斷在Windows下好使很多。數(shù)據(jù)接口部分PCIe IP核一般會(huì)引出類似AXI-Stream或者自定義接口。紫光同創(chuàng)這個(gè)IP核生成的接口形式和Xilinx的XDMA思路有相似之處但寄存器定義和信號(hào)名是各家自己定的。我這次用了最簡(jiǎn)單的寄存器讀寫接口先不上DMA等鏈路穩(wěn)定再考慮大數(shù)據(jù)傳輸。這一點(diǎn)后面例化的時(shí)候還要再提。2.3 IP核生成后先做這兩件事IP核配置完成后點(diǎn)生成PDS會(huì)生成一個(gè)IP核的例化模板和一套配套文件。我建議此刻不要著急寫代碼先做兩件事。第一件打開生成的例化模板把頂層端口名大致掃一遍確認(rèn)時(shí)鐘、復(fù)位、收發(fā)器引腳、配置接口這些信號(hào)都在。很多IP核的端口會(huì)在名字后面標(biāo)注方向仔細(xì)看看哪些信號(hào)是需要你從頂層引下來的。第二件事找到IP核生成的時(shí)序約束文件。PCIe硬核的時(shí)序約束通常已經(jīng)由IP核自身帶出來了不需要你在用戶約束文件里重復(fù)寫。但如果你看到IP核目錄里有約束文件記得確認(rèn)它被加到了工程約束列表里這個(gè)很容易忽略。我在第一次做的時(shí)候就沒留意導(dǎo)致綜合完之后時(shí)序報(bào)告一團(tuán)糟排查到最后發(fā)現(xiàn)是約束文件被PDS默認(rèn)排除了。如果你準(zhǔn)備做仿真IP核生成目錄下還會(huì)帶仿真模型和測(cè)試bench模板。把這些文件加到仿真工程里直接跑一下默認(rèn)仿真能很快確認(rèn)硬核的復(fù)位時(shí)鐘鏈路是否正常。這步雖然不能驗(yàn)證板級(jí)鏈路但至少能驗(yàn)證IP核配置本身沒有邏輯錯(cuò)誤。3. 板級(jí)設(shè)計(jì)與仿真驗(yàn)證要點(diǎn)3.1 耦合電容、阻抗與等長(zhǎng)這里要說的是很多人容易忽視的硬件問題。如果你用的是現(xiàn)成開發(fā)板這部分通常不用你操心但如果你想自己畫板子加PCIe接口或者做一個(gè)小轉(zhuǎn)接板那耦合電容擺放位置、阻抗控制、差分對(duì)等長(zhǎng)這三個(gè)問題會(huì)在聯(lián)調(diào)時(shí)給你狠狠上一課。PCIe的收發(fā)差分信號(hào)需要AC耦合電容標(biāo)準(zhǔn)做法是在發(fā)送端那一側(cè)串接一個(gè)電容。這個(gè)電容的擺放位置很有講究要盡量靠近發(fā)送端的引腳而不是靠近連接器。原因很簡(jiǎn)單AC耦合電容會(huì)帶來阻抗不連續(xù)點(diǎn)把它放在走線靠近源端的位置后面那段走線可以幫忙恢復(fù)一部分阻抗信號(hào)完整性會(huì)比放在連接器附近好很多。容值方面PCIe Gen1和Gen2常用0.1uFGen3推薦0.22uF選擇容值要考慮低速信號(hào)比如參考時(shí)鐘的頻率分量。擺放時(shí)還要注意差分對(duì)的兩顆電容要對(duì)稱焊盤尺寸盡量一致不然兩個(gè)方向的寄生參數(shù)不相同差分轉(zhuǎn)換會(huì)引入共模噪聲。還有一點(diǎn)容易被忽略耦合電容附近的地平面要完整不要在電容下方穿線否則這條走線會(huì)成為整個(gè)鏈路里最嚴(yán)重的阻抗破壞點(diǎn)。阻抗控制上PCIe差分阻抗要求是85歐姆左右老版本規(guī)范寫的是100歐姆新設(shè)計(jì)一般都按85歐姆去控制。做板子的時(shí)候告訴板廠“PCIe走線差分阻抗85”他們就知道該怎么調(diào)線寬和間距了。另外在布線時(shí)一對(duì)差分線內(nèi)部要保持等長(zhǎng)誤差控制在5mil以內(nèi)通道與通道之間的誤差可以放寬到幾十個(gè)mil這個(gè)要求并不苛刻。3.2 參考時(shí)鐘、電源與復(fù)位PCIe鏈路能不能訓(xùn)練成功參考時(shí)鐘質(zhì)量是第一位。100MHz差分參考時(shí)鐘要由連接器或者板載晶振提供抖動(dòng)指標(biāo)得符合PCIe規(guī)范。有人想用普通有源晶振單端輸出直接接PCIe參考時(shí)鐘引腳這里要提醒一下PCIe參考時(shí)鐘是差分形式單端轉(zhuǎn)差分電路如果設(shè)計(jì)不好出來的時(shí)鐘抖動(dòng)可能完全不合格。電源方面PCIe硬核通常需要獨(dú)立的模擬電源和數(shù)字電源。我這次用的開發(fā)板是官方設(shè)計(jì)電源樹不用我操心但如果你自己設(shè)計(jì)板卡一定要參考芯片手冊(cè)里的上電時(shí)序要求尤其是SerDes那塊供電。上電順序不對(duì)輕則鏈路訓(xùn)練不穩(wěn)重則直接枚舉不到設(shè)備而且這種問題非常難查。復(fù)位信號(hào)也值得多花兩句話。PCIe設(shè)備的復(fù)位一般來自金手指上的PERST#信號(hào)或者板載復(fù)位控制。FPGA內(nèi)部邏輯需要在復(fù)位釋放之后再給PCIe硬核一個(gè)確定的復(fù)位釋放時(shí)間。不要用FPGA內(nèi)部的全局復(fù)位按鍵直接控制PCIe硬核因?yàn)槟惆存I釋放的時(shí)機(jī)跟PCIe規(guī)范要求的復(fù)位時(shí)序很可能對(duì)不上。我這次在頂層里加了一個(gè)簡(jiǎn)單的復(fù)位同步邏輯用系統(tǒng)復(fù)位信號(hào)打兩拍之后再去復(fù)位PCIe硬核實(shí)測(cè)穩(wěn)定很多。3.3 用仿真確認(rèn)鏈路基礎(chǔ)狀態(tài)上板之前先跑一遍仿真能省掉很多在現(xiàn)場(chǎng)抓頭的時(shí)間。PDS和ModelSim聯(lián)合仿真步驟不復(fù)雜關(guān)鍵是IP核自帶的測(cè)試bench能不能正常編譯。我把IP核生成的仿真模型和testbench加入ModelSim跑了一遍主要看三個(gè)東西參考時(shí)鐘有沒有正常起振、復(fù)位信號(hào)是否正確釋放、AXI接口的寄存器讀寫事務(wù)能不能執(zhí)行。這里有個(gè)經(jīng)驗(yàn)如果仿真階段AXI寄存器讀寫的狀態(tài)機(jī)就不是預(yù)期的樣子那么上板之后大概率也讀不到正確數(shù)據(jù)。仿真能發(fā)現(xiàn)的問題一般是邏輯邏輯層面的比如某個(gè)信號(hào)打拍順序不對(duì)、復(fù)位周期不夠長(zhǎng)等等。這類問題在真機(jī)上調(diào)試非常痛苦因?yàn)闆]有波形給你看而仿真里幾秒鐘就能定位。不過仿真的局限性也要清楚它驗(yàn)證不了物理層的實(shí)際訓(xùn)練結(jié)果因?yàn)槟莻€(gè)需要真正的外部對(duì)端設(shè)備。仿真過了只說明IP核配置正確、內(nèi)部邏輯自洽不代表插到電腦上就一定能識(shí)別。真正決定能否識(shí)別的還是硬件鏈路狀態(tài)。4. 頂層例化與比特流生成全流程4.1 例化PCIe硬核與AXI接口對(duì)接IP核生成之后要寫一個(gè)頂層模塊把它例化出來。這一步其實(shí)是整個(gè)流程里最直接的部分照著IP核的例化模板改就行但有幾個(gè)信號(hào)名要特別注意。下面這段是我這次簡(jiǎn)化后的例化示意端口名不是每個(gè)版本都完全一樣關(guān)鍵是把對(duì)應(yīng)關(guān)系理清最終以你生成的模板為準(zhǔn)。pcie_ep_0 pcie_ep_inst ( .ref_clk (pcie_ref_clk), // 100MHz 差分參考時(shí)鐘 .perst_n (pcie_perst_n), // PCIe復(fù)位信號(hào) .rxp (pcie_rxp), // 接收差分對(duì)正 .rxn (pcie_rxn), // 接收差分對(duì)負(fù) .txp (pcie_txp), // 發(fā)送差分對(duì)正 .txn (pcie_txn), // 發(fā)送差分對(duì)負(fù) .user_clk (user_clk), // 用戶時(shí)鐘輸出 .user_reset (user_reset), // 用戶復(fù)位輸出 .reg_wr_en (reg_wr_en), // 寄存器寫使能 .reg_wr_addr (reg_wr_addr), // 寄存器寫地址 .reg_wr_data (reg_wr_data), // 寄存器寫數(shù)據(jù) .reg_rd_en (reg_rd_en), // 寄存器讀使能 .reg_rd_addr (reg_rd_addr), // 寄存器讀地址 .reg_rd_data (reg_rd_data) // 寄存器讀數(shù)據(jù) );這里想多說一下user_clk和user_reset這兩個(gè)信號(hào)。PCIe硬核輸出的用戶時(shí)鐘是鏈路訓(xùn)練完成后才穩(wěn)定輸出的所以不要在頂層里用你自己的時(shí)鐘去驅(qū)動(dòng)這些AXI接口邏輯要用IP核輸出的用戶時(shí)鐘作為整個(gè)用戶邏輯的主時(shí)鐘。我當(dāng)時(shí)剛開始做的時(shí)候偷懶直接用板載時(shí)鐘管用戶邏輯結(jié)果寄存器讀寫時(shí)好時(shí)壞很難穩(wěn)定復(fù)現(xiàn)。如果你只是做到“設(shè)備管理器識(shí)別”這一步其實(shí)不需要寫太多邏輯把寄存器接口掛到一組簡(jiǎn)單的讀回邏輯上就行比如讀寫寄存器0x00存一個(gè)固件版本號(hào)Windows端讀回來能對(duì)得上就說明通路通了。這樣設(shè)計(jì)的最小驗(yàn)證邏輯既不會(huì)引入復(fù)雜狀態(tài)機(jī)又能驗(yàn)證配置空間之外的用戶邏輯鏈路。4.2 約束文件只需要管三件事PCIe收發(fā)器引腳是IP核內(nèi)部綁定的硬核引腳普通用戶約束文件里不需要也不能手動(dòng)分配。這一點(diǎn)和普通GPIO完全不同千萬別試圖在PDS的引腳約束里手動(dòng)指定txp/rxp的位置那是無效的。用戶約束文件里實(shí)際上只需要關(guān)注三件事。第一是參考時(shí)鐘或者用戶時(shí)鐘的時(shí)序約束對(duì)于PCIe硬核參考時(shí)鐘IP核生成文件里已有時(shí)鐘約束但如果你在邏輯中額外使用了一個(gè)板載時(shí)鐘要把它約束成主時(shí)鐘避免時(shí)序報(bào)告完全沒法看。第二是復(fù)位按鍵和LED這些普通IO的引腳分配以及它們的電平標(biāo)準(zhǔn)。第三是如果用了串口把UART引腳分配好。時(shí)鐘約束其實(shí)很簡(jiǎn)單在PDS約束編輯器里選中時(shí)鐘信號(hào)設(shè)置周期和占空比就行。不用做太復(fù)雜的多周期約束先把基礎(chǔ)約束配好保證綜合之后時(shí)序不紅即可。PCIe內(nèi)部的高速時(shí)序由IP核自己的約束保證跟你寫的用戶約束基本不相關(guān)。4.3 綜合、布線到燒寫的完整順序約束配好之后回到PDS流程界面依次執(zhí)行綜合、布局布線、生成比特流。PDS的綜合速度比Vivado慢一些PGL50H這種規(guī)模一個(gè)簡(jiǎn)單設(shè)計(jì)大約幾分鐘到十幾分鐘別著急。如果綜合階段報(bào)錯(cuò)優(yōu)先看是不是IP核版本和工具版本不匹配其次是代碼里的語法問題。布局布線完成之后打開時(shí)序報(bào)告重點(diǎn)看有沒有建立時(shí)間違例。如果出現(xiàn)紅字先去檢查user_clk是否真的用了IP核輸出的時(shí)鐘很多時(shí)候時(shí)序違例都是因?yàn)榭鐣r(shí)鐘域沒有做同步處理造成的。PCIe硬核本身因?yàn)镮P核約束完善時(shí)序一般不會(huì)有問題問題都出在用戶邏輯跨時(shí)鐘域處理上。比特流生成之后用JTAG下載器把它燒到開發(fā)板。這里我強(qiáng)烈建議再準(zhǔn)備一個(gè)燒寫到配置Flash的操作因?yàn)镴TAG在線配置只對(duì)當(dāng)前會(huì)話有效拔掉調(diào)試器或者重新上電之后FPGA又回到空白狀態(tài)。而Windows識(shí)別PCIe設(shè)備通常要求設(shè)備在開機(jī)時(shí)就存在所以最好把bitstream轉(zhuǎn)成燒寫文件刻到配置Flash里這樣FPGA上電自動(dòng)加載PCIe鏈路在上電后自動(dòng)建立。這個(gè)燒寫Flash的操作和普通FPGA配置完全一樣PDS界面里有專門的工具入口。5. Windows設(shè)備管理器識(shí)別與驅(qū)動(dòng)處理5.1 讓PC在枚舉階段就能發(fā)現(xiàn)FPGA這一步是整個(gè)項(xiàng)目里最考驗(yàn)?zāi)托牡碾A段也是很多人卡住的地方。先說結(jié)論如果想在設(shè)備管理器里看到設(shè)備最好讓FPGA在上電時(shí)就已經(jīng)配置好也就是燒到配置Flash里讓W(xué)indows開機(jī)時(shí)直接跑PCIe枚舉。如果FPGA里沒有配置Windows開機(jī)時(shí)主板根本發(fā)現(xiàn)不了這個(gè)PCIe設(shè)備之后再通過JTAG在線配置Windows也不會(huì)自動(dòng)重新掃描PCIe總線。實(shí)際過程我用兩種方式都試過。第一種是把bitstream燒進(jìn)配置Flash然后關(guān)機(jī)插入PCIe開發(fā)板開機(jī)進(jìn)入Windows設(shè)備管理器刷新設(shè)備出現(xiàn)了。第二種是把開發(fā)板先插在PCIe插槽上開機(jī)進(jìn)入Windows再通過JTAG下載配置然后從設(shè)備管理器菜單里選“掃描檢測(cè)硬件改動(dòng)”。這種方式有時(shí)有效有時(shí)無效取決于主板對(duì)PCIe熱插拔事件的支持程度。所以我最后的建議是老老實(shí)實(shí)燒Flash別偷懶。還有一個(gè)容易忽略的地方開發(fā)板插到電腦上之后即使FPGA配置正確如果PCIe鏈路因?yàn)槟承┰驔]有訓(xùn)練成功Windows同樣看不到設(shè)備。判斷鏈路是否建立有一個(gè)很實(shí)用的方法在系統(tǒng)POST階段看板卡上有沒有一個(gè)LED在閃爍如果有專門指示PCIe link up的LED最好沒有的話可以用邏輯分析儀看用戶時(shí)鐘是否輸出。我這次在頂層邏輯里加了一個(gè)LED當(dāng)user_clk正常鎖定時(shí)點(diǎn)亮這樣一眼就能判斷鏈路是否link up。5.2 設(shè)備管理器里看到設(shè)備后的驅(qū)動(dòng)問題當(dāng)你打開Windows設(shè)備管理器在“其他設(shè)備”分類下看到一個(gè)帶黃色感嘆號(hào)的“PCI Device”或者“未知設(shè)備”恭喜這說明PCIE枚舉已經(jīng)成功鏈路已經(jīng)訓(xùn)練到L0狀態(tài)Windows成功讀到了FPGA的Vendor ID和Device ID。到這個(gè)階段FPGA端的PCIe物理層、事務(wù)層以及配置空間都沒有問題。接下來是驅(qū)動(dòng)。如果你只是驗(yàn)證鏈路看到這個(gè)黃色感嘆號(hào)已經(jīng)算達(dá)標(biāo)了。但如果想進(jìn)一步做數(shù)據(jù)通信就需要給設(shè)備安裝一個(gè)驅(qū)動(dòng)。驅(qū)動(dòng)這塊有幾個(gè)路線。第一個(gè)路線用紫光同創(chuàng)或者開發(fā)板廠商提供的參考驅(qū)動(dòng)有些開發(fā)板會(huì)配套一個(gè)上位機(jī)測(cè)試工具安裝工具時(shí)會(huì)自動(dòng)安裝驅(qū)動(dòng)。第二個(gè)路線用WinDriver這類工具生成一個(gè)通用驅(qū)動(dòng)WinDriver有評(píng)估版可以生成一個(gè)INF文件安裝之后設(shè)備管理器里的設(shè)備名會(huì)變成WinDriver設(shè)備名做寄存器讀寫測(cè)試完全夠用。第三個(gè)路線自己用WDF框架寫一個(gè)驅(qū)動(dòng)這個(gè)門檻較高項(xiàng)目需要的時(shí)候再深挖本次驗(yàn)證用不到。Windows在驅(qū)動(dòng)簽名方面比較嚴(yán)格如果系統(tǒng)提示驅(qū)動(dòng)未簽名無法安裝可以臨時(shí)進(jìn)入高級(jí)啟動(dòng)選項(xiàng)選擇“禁用驅(qū)動(dòng)強(qiáng)制簽名”或者給測(cè)試模式開起來。我這邊驗(yàn)證的時(shí)候WinDriver生成的驅(qū)動(dòng)是簽過名的所以沒遇到這個(gè)阻礙但如果你用自編譯驅(qū)動(dòng)就一定會(huì)有這一關(guān)。5.3 用配置空間驗(yàn)證鏈路真正可用看到設(shè)備管理器里出現(xiàn)設(shè)備之后我建議再用工具讀取PCIe配置空間確認(rèn)設(shè)備信息到底對(duì)不對(duì)。這個(gè)驗(yàn)證有兩個(gè)作用一是確認(rèn)Windows枚舉出來的Vendor ID和Device ID跟IP核配置里寫的是否一致二是看看BAR0有沒有被分配地址空間。我在Windows下用過兩款工具一個(gè)是RWEverything一個(gè)是PCITree。這兩款工具都能枚舉當(dāng)前系統(tǒng)里的PCIe設(shè)備顯示配置空間的前256字節(jié)。打開之后找到設(shè)備名對(duì)應(yīng)的條目看Vendor ID、Device ID、Class Code這些字段。PCIe設(shè)備的Class Code一般是0x05或者其他值具體是內(nèi)存控制器、數(shù)據(jù)傳輸控制器還是串行控制器取決于IP核配置。默認(rèn)情況下如果配置空間里所有字節(jié)都是FF說明鏈路實(shí)際上沒訓(xùn)練成功Windows只是拿到了一個(gè)空殼設(shè)備的占位信息不過這種情況很少見。確認(rèn)BAR0有地址之后可以嘗試通過工具直接讀寫B(tài)AR0映射的寄存器。比如我設(shè)計(jì)里的固化版本寄存器地址是0x00在工具里往這個(gè)地址寫一個(gè)值再讀回來數(shù)據(jù)一致說明不光是PCIe鏈路通了用戶邏輯數(shù)據(jù)通路也是通的。這一步做完整條鏈路從FPGA內(nèi)核到Windows用戶態(tài)就算徹底驗(yàn)證完畢。6. 常見問題與排查技巧實(shí)錄6.1 設(shè)備管理器里完全沒有PCIe設(shè)備這是最令人頭疼的問題因?yàn)閃indows給你一個(gè)沒有任何線索的“沒有”。一旦遇到這個(gè)情況不要先懷疑代碼按照下面的順序去排查。第一步看開發(fā)板配置是否成功確認(rèn)bitstream下載過程中沒有報(bào)錯(cuò)再加一個(gè)LED翻轉(zhuǎn)邏輯來確認(rèn)FPGA確實(shí)在運(yùn)行。第二步看PCIe鏈路是否link up檢查參考時(shí)鐘是否起振、復(fù)位釋放是否正常有邏輯分析儀的話抓一下IP核輸出的LTSSM狀態(tài)寄存器的值。這里給一個(gè)具體的參考PCIe鏈路訓(xùn)練有一個(gè)有限狀態(tài)機(jī)大致從Detect到Polling到Configuration到L0。如果抓到的狀態(tài)一直停留在Detect說明物理層信號(hào)有問題如果停在Configuration說明鏈路寬度協(xié)商或者速率協(xié)商出了問題。PDS自帶調(diào)試核類似Xilinx的ILA可以在線抓取這些信號(hào)。調(diào)試核的使用需要額外占一些LUT和BRAM約束加好后布線會(huì)稍微變慢但排查問題時(shí)非常值。另外一個(gè)很實(shí)際的建議是把PCIe速率降級(jí)到Gen1再試。很多時(shí)候主板BIOS對(duì)Gen2的兼容性一般或者板卡走線質(zhì)量差了一點(diǎn)Gen2速率訓(xùn)練不上降級(jí)到Gen1之后一切正常。我這次調(diào)試中途就被這個(gè)問題坑過后來在IP核配置里把最大速率改成Gen1訓(xùn)練一次成功然后再改回Gen2前后花了很長(zhǎng)時(shí)間。下次我會(huì)直接從Gen1開始穩(wěn)定后再提升速率。6.2 鏈路訓(xùn)練不上時(shí)的排查順序鏈路訓(xùn)練不上可以從信號(hào)質(zhì)量、配置空間、平臺(tái)三方面排查。信號(hào)質(zhì)量方面用示波器看參考時(shí)鐘是否干凈看差分信號(hào)有沒有幅度和振鈴。如果沒有示波器就默認(rèn)參考時(shí)鐘有問題檢查晶振和端接電阻。配置空間方面確認(rèn)Vendor ID和Device ID是不是非零且合理確認(rèn)BAR配置是否正確。平臺(tái)方面要特別說一下不是所有電腦主板都適合做PCIe調(diào)試。有的主板BIOS會(huì)關(guān)閉未識(shí)別的PCIe插槽的枚舉或者某些插槽和M.2共享帶寬導(dǎo)致設(shè)備插上后被禁用。我建議調(diào)試時(shí)用臺(tái)式機(jī)上PCIe x16插槽即使你的設(shè)備是x1也可以插到x16插槽里物理上沒問題。盡量不要用PCIe轉(zhuǎn)接卡接出來的插槽除非確定它供電充足。PCIe設(shè)備需要3.3V和12V電源PCIe x16插槽能提供更高功率比一些轉(zhuǎn)接方案穩(wěn)定。還有一個(gè)容易被忽略的是PCIe插槽的卡扣和固定如果板卡插入不到位金手指接觸不良也會(huì)導(dǎo)致鏈路訓(xùn)練失敗而且這種問題是間歇性的最難查。我最后一次調(diào)試時(shí)發(fā)現(xiàn)鏈路時(shí)通時(shí)斷排查了半天最后重新插拔了幾次才解決。6.3 識(shí)別正常但驅(qū)動(dòng)安裝總失敗設(shè)備管理器能看到設(shè)備但安裝驅(qū)動(dòng)時(shí)總是報(bào)錯(cuò)這個(gè)問題通常跟驅(qū)動(dòng)簽名、設(shè)備實(shí)例ID和廠商提供的INF文件有關(guān)。先確認(rèn)驅(qū)動(dòng)是針對(duì)你這個(gè)設(shè)備寫的檢查INF文件里的VID和DID是否與設(shè)備管理器里顯示的一致如果不一致Windows會(huì)提示“找不到匹配的設(shè)備”。如果INF文件是對(duì)的但安裝時(shí)提示驅(qū)動(dòng)簽名問題可以嘗試禁用Windows的強(qiáng)制簽名驅(qū)動(dòng)功能。重啟電腦在開機(jī)過程中選擇高級(jí)啟動(dòng)選項(xiàng)進(jìn)入“疑難解答-高級(jí)選項(xiàng)-啟動(dòng)設(shè)置”選擇“禁用驅(qū)動(dòng)程序強(qiáng)制簽名”然后正常進(jìn)入系統(tǒng)再安裝驅(qū)動(dòng)。這種方式每次重啟后需要重新設(shè)置但用來臨時(shí)驗(yàn)證驅(qū)動(dòng)完全沒問題。最后如果驅(qū)動(dòng)還是安裝不上可以先試試Windows自帶的通用PCI驅(qū)動(dòng)右鍵更新驅(qū)動(dòng)選擇“自動(dòng)搜索驅(qū)動(dòng)程序”讓W(xué)indows自己找。對(duì)某些類別的PCIe設(shè)備Windows會(huì)裝上一個(gè)通用驅(qū)動(dòng)雖然功能有限但設(shè)備管理器里不會(huì)再有黃色感嘆號(hào)。到了這一步至少后續(xù)用工具訪問BAR空間就不會(huì)被驅(qū)動(dòng)報(bào)錯(cuò)打斷已經(jīng)能滿足寄存器讀寫驗(yàn)證了?;叵肫鹫麄€(gè)過程我覺得做PCIe通信和調(diào)普通FPGA邏輯最大的不同在于鏈路問題不是看波形就能直接定位的它需要你把硬件設(shè)計(jì)、IP核配置、驅(qū)動(dòng)枚舉這三個(gè)層面串起來總體思考。我的習(xí)慣是每改動(dòng)一個(gè)變量就固定其他變量比如先固定Gen1速率固定x1寬度排除掉變量之后再一個(gè)一個(gè)放開。這個(gè)習(xí)慣讓我少走很多彎路。如果你正準(zhǔn)備開始這個(gè)項(xiàng)目希望這篇東西能幫你把前期的坑提前填上早點(diǎn)看到設(shè)備管理器里那個(gè)期待已久的設(shè)備出現(xiàn)。