試的實戰(zhàn)指南)
1. 從一根插槽說起PCIe的RC與EP到底在聊什么但凡做過板級調(diào)試、系統(tǒng)bring-up或者驅(qū)動開發(fā)的兄弟遲早都會撞上PCIe這堵墻。你手里可能有一塊FPGA加速卡、一張網(wǎng)卡、一顆NVMe盤或者干脆就是兩塊板子對著插結(jié)果上電之后系統(tǒng)里lspci什么都看不到或者看到了但速率死活協(xié)商不上去。這時候老手第一句話往往不是問“驅(qū)動裝了嗎”而是問“你這頭配的是RC還是EP”PCIePeripheral Component Interconnect Express本質(zhì)上是一個高速串行點對點互連總線它取代了早期PCI那種共享并行的總線結(jié)構(gòu)。而RCRoot Complex根復(fù)合體和EPEndpoint端點是PCIe拓?fù)淅飪蓚€最基礎(chǔ)、也最容易被混淆的角色。簡單打個比方RC就像一棟大樓的物業(yè)總控室它掌管著整棵PCIe樹的枚舉、地址分配和對外通道EP則是樓里的一個個租戶設(shè)備比如網(wǎng)卡、顯卡、SSD控制器。租戶不能自己去改大樓的門牌號得等物業(yè)來分配——這個“分配門牌號”的過程就是PCIe枚舉。這個系列我打算把協(xié)議標(biāo)準(zhǔn)里那些真正影響實操的點掰開揉碎講。第一篇就聚焦RC和EP這兩種模式它們各自是什么、配置空間里哪些寄存器決定了身份、枚舉過程怎么走、為什么很多掉卡和降速問題最后都追到模式配置上。適合正在做FPGA PCIe開發(fā)、嵌入式系統(tǒng)bring-up、或者被AER報錯折磨的工程師參考。不管你是剛接觸PCIe的新手還是已經(jīng)調(diào)過幾輪鏈路的老兵這里面的細(xì)節(jié)應(yīng)該都能對上你踩過的某個坑。2. RC與EP的角色定位與核心差異2.1 用“物業(yè)與租戶”理解PCIe拓?fù)銹CIe的拓?fù)涫且豢脴?。樹根是RC樹干是Switch交換器樹葉是EP。RC是整個系統(tǒng)的“根”它通常集成在CPU或SoC內(nèi)部負(fù)責(zé)發(fā)起配置事務(wù)、管理地址空間、處理中斷。EP則是具體干活的設(shè)備它響應(yīng)RC發(fā)來的配置讀寫、內(nèi)存讀寫和中斷請求。這里有個關(guān)鍵點RC是配置空間訪問的發(fā)起者RequesterEP是響應(yīng)者Completer。在Type 0配置請求里RC通過配置讀寫去探測總線上有沒有設(shè)備、設(shè)備是什么類型、需要多少BAR空間。EP不能主動去枚舉別人它只能被動等待被枚舉。這就像物業(yè)可以挨家挨戶敲門登記租戶不能跑去敲物業(yè)的門要求登記自己。但現(xiàn)實里有個例外RC也可以作為Completer。當(dāng)EP發(fā)起Memory Write或Memory Read比如DMA寫內(nèi)存RC要負(fù)責(zé)響應(yīng)這些請求。所以RC和EP的身份不是絕對的“主”和“從”而是看誰在發(fā)起哪類事務(wù)。配置事務(wù)永遠(yuǎn)是RC發(fā)起但內(nèi)存事務(wù)雙方都可能發(fā)起。2.2 RC模式的核心職責(zé)拆解RC模式下的設(shè)備核心職責(zé)有這么幾塊配置空間枚舉從Bus 0開始逐級掃描每個Device和Function讀取Vendor ID和Device ID判斷設(shè)備是否存在。地址空間分配根據(jù)EP的BARBase Address Register請求分配Memory空間和I/O空間現(xiàn)代PCIe基本只用Memory空間。中斷管理處理INTx傳統(tǒng)中斷和MSI/MSI-X消息中斷。事務(wù)路由把CPU發(fā)出的內(nèi)存訪問路由到正確的EP把EP的DMA請求路由到內(nèi)存。錯誤處理管理AERAdvanced Error Reporting寄存器記錄和上報鏈路錯誤。在FPGA開發(fā)里如果你用的是Xilinx的XDMA或者Intel的PCIe Hard IP通常IP核會提供一個“Root Port”配置選項。選了這個FPGA就扮演RC角色可以主動去枚舉掛在它下面的EP設(shè)備。這種場景常見于FPGA作為主控去管理下游的NVMe盤或者另一塊FPGA加速卡。2.3 EP模式的核心職責(zé)拆解EP模式下的設(shè)備職責(zé)相對“被動”但同樣關(guān)鍵響應(yīng)配置請求正確返回Vendor ID、Device ID、Class Code、Header Type等信息。BAR空間聲明告訴RC“我需要多大的地址空間”等待RC分配。響應(yīng)內(nèi)存讀寫處理RC發(fā)來的MMIO讀寫以及自己發(fā)起的DMA讀寫。中斷上報通過MSI/MSI-X或INTx向RC報告事件。鏈路訓(xùn)練上電后與RC或Switch進(jìn)行LTSSM鏈路訓(xùn)練協(xié)商速率和寬度。EP模式是絕大多數(shù)外設(shè)的工作模式。你的網(wǎng)卡、顯卡、NVMe盤上電后都是EP。FPGA做加速卡時通常也配成EP插在主機(jī)板的PCIe插槽上由主機(jī)CPU的RC來枚舉它。2.4 一張表看清RC與EP的關(guān)鍵區(qū)別對比維度RCRoot ComplexEPEndpoint拓?fù)湮恢脴涓鶚淙~配置事務(wù)角色發(fā)起者響應(yīng)者枚舉能力主動掃描總線被動等待枚舉BAR空間通常不請求BAR或作為RC自身寄存器必須聲明BAR需求中斷角色接收中斷發(fā)起中斷典型載體CPU/SoC內(nèi)置、FPGA Root Port IP網(wǎng)卡、顯卡、NVMe、FPGA加速卡地址路由管理全局地址映射使用RC分配的地址常見問題枚舉失敗、資源不足掉卡、降速、AER報錯這張表建議存下來調(diào)試時對著看能快速定位問題出在哪一側(cè)。3. 配置空間與寄存器身份是怎么被“認(rèn)出來”的3.1 Type 0和Type 1配置空間頭PCIe配置空間每個Function有4KB前256字節(jié)是PCI兼容區(qū)后面是PCIe擴(kuò)展區(qū)。前64字節(jié)是配置空間頭分Type 0和Type 1兩種。Type 0用于EP設(shè)備。Header Type字段的bit 7為0。Type 1用于RC和Switch的端口。Header Type字段的bit 7為1。RC在枚舉時讀到Header Type bit 7為0就知道這是個EP可以給它分配BAR。讀到bit 7為1就知道這是個橋或RC端口需要繼續(xù)往下掃描次級總線。這里有個實操細(xì)節(jié)很多FPGA的PCIe IP核在配置成EP時Header Type默認(rèn)就是0x00配置成RC時Header Type是0x01。如果你發(fā)現(xiàn)主機(jī)枚舉不到你的FPGA卡第一件事就是確認(rèn)IP核的配置模式有沒有選錯。我見過不止一次有人把EP配成了RC結(jié)果主機(jī)掃不到設(shè)備查了半天驅(qū)動。3.2 BAR寄存器的請求與分配機(jī)制BAR是EP向RC“要地址”的窗口。EP在BAR里寫入全1然后讀回來RC就能知道這個BAR需要多大的空間。比如一個64位Memory BAR低4位是屬性位高位全1讀回來是0xFFFF_FFF0說明需要4KB空間。RC枚舉到EP后會讀取所有BAR的請求大小然后從系統(tǒng)地址空間里劃一塊給它。分配完成后RC把基地址寫回BAR。之后CPU訪問這個地址就會被路由到對應(yīng)的EP。注意BAR空間大小必須是2的冪次方。如果你需要3KB空間實際得請求4KB。這是硬件限制不是軟件能改的。在FPGA EP設(shè)計里BAR的配置直接決定了主機(jī)能訪問你多少寄存器空間。常見做法是BAR0放控制寄存器BAR1放DMA描述符BAR2放大塊數(shù)據(jù)緩沖。每個BAR的大小和類型32位/64位、Memory/IO都要在IP核里提前配好綜合之后就不能改了。3.3 Capability結(jié)構(gòu)里的關(guān)鍵信息配置空間里有一串Capability鏈表每個Capability有一個ID。對RC和EP來說幾個關(guān)鍵的Capability包括PCI Express CapabilityID 0x10里面有Device Type字段明確標(biāo)識這是RC、Switch還是EP。還有Link Capabilities、Link Status、Link Control等寄存器鏈路速率和寬度就在這里。MSI CapabilityID 0x05消息中斷配置。MSI-X CapabilityID 0x11增強(qiáng)版消息中斷支持更多向量。Power Management CapabilityID 0x01電源管理。AER CapabilityID 0x01在擴(kuò)展區(qū)高級錯誤報告。Device Type字段特別值得關(guān)注。它的編碼是0EP1Legacy EP4RC Integrated Endpoint5RC Event Collector6RC Integrated Endpoint Root Port9RC Root Port。如果你在調(diào)試時發(fā)現(xiàn)Device Type不對那模式配置肯定有問題。3.4 枚舉過程中RC如何“認(rèn)人”枚舉過程大致是這樣的RC從Bus 0、Device 0、Function 0開始掃描。讀取Vendor ID如果是0xFFFF說明沒有設(shè)備跳到下一個Function。如果有設(shè)備讀取Header Type判斷是EP還是橋。如果是EP讀取BAR請求分配地址空間。如果是橋配置次級總線號繼續(xù)掃描下級總線。遞歸直到整棵樹掃描完畢。這個過程里RC需要維護(hù)三個寄存器Primary Bus Number、Secondary Bus Number、Subordinate Bus Number。它們決定了橋下面掛的總線號范圍。如果這三個值配錯了下游設(shè)備就會枚舉不到。實操心得如果你用FPGA做RC去枚舉下游EP發(fā)現(xiàn)只能看到第一級設(shè)備看不到Switch下面的設(shè)備大概率是Subordinate Bus Number沒配對。確保它大于等于所有下游總線的最大編號。4. 實操從零配置一個RC與EP通信鏈路4.1 場景設(shè)定與硬件選型假設(shè)我們要搭一個最小系統(tǒng)一塊FPGA做RC另一塊FPGA做EP兩塊板子通過PCIe金手指或線纜對接。目標(biāo)是RC能枚舉到EP并且能通過BAR空間讀寫EP的寄存器。硬件選型上Xilinx UltraScale系列的PCIe Hard IP比較典型。RC側(cè)用XDMA IP配成Root Port模式EP側(cè)用XDMA IP配成Endpoint模式。兩塊板子各自有參考時鐘PCIe要求100MHz差分參考時鐘抖動要控制好。4.2 RC側(cè)IP核配置要點在Vivado里配置XDMA IP時RC模式的關(guān)鍵選項Mode選Root Port。Device Port Type選Root Port。Lane Width根據(jù)硬件選x1、x4、x8或x16。Max Link Speed選5.0 GT/sGen2或8.0 GT/sGen3。BAR配置RC側(cè)通常不需要大BAR但需要配置RC自身的寄存器空間。AXI接口RC側(cè)需要AXI Master去發(fā)起配置讀寫和內(nèi)存讀寫。配置完成后RC側(cè)的邏輯需要實現(xiàn)枚舉狀態(tài)機(jī)。Xilinx提供了示例設(shè)計但實際用的時候往往要自己改。枚舉狀態(tài)機(jī)的大致流程是等待鏈路訓(xùn)練完成然后從Bus 0開始掃描讀Vendor ID分配BAR配置橋寄存器。4.3 EP側(cè)IP核配置要點EP側(cè)的XDMA配置Mode選Endpoint。Device Port Type選Endpoint。BAR0配成64位Memory BAR大小比如1MB用于控制寄存器。BAR1配成64位Memory BAR大小比如256MB用于DMA數(shù)據(jù)緩沖。MSI-X使能配置中斷向量表。Vendor ID和Device ID填你自己的ID別用默認(rèn)的否則驅(qū)動可能認(rèn)錯。EP側(cè)還需要實現(xiàn)配置空間的響應(yīng)邏輯。XDMA IP會自動處理大部分配置請求但BAR的地址分配需要你在邏輯里配合。具體來說當(dāng)RC寫B(tài)AR寄存器時IP會給出信號你需要把基地址存下來后續(xù)地址譯碼用。4.4 鏈路訓(xùn)練與速率協(xié)商的觀察方法上電后第一件事是看鏈路有沒有訓(xùn)練成功。在Vivado里可以通過ILA抓LTSSM狀態(tài)機(jī)的狀態(tài)。正常流程是Detect - Polling - Configuration - L0。如果卡在Polling說明參考時鐘或差分對有問題如果卡在Configuration可能是Lane映射不對。鏈路訓(xùn)練成功后讀Link Status寄存器PCIe Capability里的偏移0x12看Negotiated Link Width和Current Link Speed。如果協(xié)商下來是x1 Gen1而你的硬件是x4 Gen3那就要查參考時鐘是否滿足要求。差分對是否接反。金手指或連接器是否有信號完整性問題。IP核的Lane配置是否和硬件一致。踩過的坑有一次兩塊板子對接鏈路死活只能到Gen1。查了半天發(fā)現(xiàn)是EP側(cè)的參考時鐘用了普通晶振抖動太大。換成專用時鐘芯片后直接協(xié)商到Gen3。PCIe對參考時鐘的要求比想象中嚴(yán)格。4.5 枚舉成功的驗證步驟枚舉成功的標(biāo)志是RC能讀到EP的Vendor ID和Device ID。驗證步驟RC側(cè)發(fā)起配置讀讀Bus 0、Device 0、Function 0的Vendor ID。如果返回0xFFFF檢查鏈路是否訓(xùn)練成功。如果返回正確ID繼續(xù)讀Header Type和BAR。給BAR分配地址寫回BAR寄存器。通過分配的地址讀寫EP的BAR空間驗證數(shù)據(jù)通路。在Linux下如果RC是主機(jī)CPU可以直接用lspci -vvv看設(shè)備樹。如果是FPGA做RC就需要自己寫邏輯去讀配置空間通過ILA或串口打印出來。5. 常見問題與排查技巧實錄5.1 掉卡問題的典型原因“掉卡”是PCIe調(diào)試?yán)镒钭屓祟^疼的問題之一。表現(xiàn)是系統(tǒng)運(yùn)行一段時間后lspci里設(shè)備消失了或者驅(qū)動報設(shè)備無響應(yīng)。常見原因電源問題EP側(cè)供電不足導(dǎo)致鏈路不穩(wěn)定。特別是FPGA加速卡功耗大電源紋波要控制好。參考時鐘抖動時鐘質(zhì)量差會導(dǎo)致鏈路誤碼率升高最終掉鏈路。信號完整性差分對阻抗不匹配、走線過長、過孔太多都會導(dǎo)致眼圖閉合。AER錯誤累積Correctable Error累積到閾值會觸發(fā)鏈路重訓(xùn)練嚴(yán)重時設(shè)備掉出總線。散熱問題溫度過高導(dǎo)致SerDes工作異常。排查時先看AER寄存器Correctable Error和Uncorrectable Error分別是什么類型。如果是Receiver Error多查信號完整性如果是Bad TLP多查鏈路訓(xùn)練參數(shù)。5.2 降速與降寬問題的排查思路降速Speed downgrade和降寬Lane downgrade通常一起出現(xiàn)。比如x4 Gen3的卡協(xié)商成x2 Gen1。排查順序確認(rèn)雙方IP核的Max Link Speed和Max Link Width配置一致。檢查參考時鐘頻率和抖動。檢查差分對是否接反或虛焊。檢查Lane映射是否和硬件一致。有些板子Lane0和Lane1是反的IP核里要配Lane Reversal。用示波器看眼圖確認(rèn)信號質(zhì)量。經(jīng)驗如果只有部分Lane降速比如x4里L(fēng)ane2和Lane3沒起來重點查這兩對差分對的走線和連接器。如果是全部Lane都降速查參考時鐘和IP配置。5.3 AER報錯解讀與處理AERAdvanced Error Reporting是PCIe的錯誤報告機(jī)制。Correctable Error包括Receiver Error、Bad TLP、Bad DLLP、Replay Timer Timeout等。Uncorrectable Error包括Malformed TLP、Poisoned TLP、Completion Timeout、Unsupported Request等。處理AER報錯的思路Receiver Error物理層問題查信號完整性。Bad TLP/DLLP鏈路層問題可能是鏈路訓(xùn)練參數(shù)不優(yōu)。Completion TimeoutEP沒在規(guī)定時間內(nèi)響應(yīng)查EP邏輯是否卡死。Unsupported RequestRC發(fā)了EP不支持的請求查地址映射。在Linux下可以用lspci -vvv看AER寄存器或者用aer-inject工具注入錯誤做測試。5.4 枚舉失敗的快速定位表現(xiàn)象可能原因排查方法讀Vendor ID返回0xFFFF鏈路未訓(xùn)練成功查LTSSM狀態(tài)、參考時鐘只能看到第一級設(shè)備橋的Subordinate Bus Number配錯檢查橋配置寄存器BAR分配失敗BAR請求大小超過可用空間查系統(tǒng)地址空間映射設(shè)備時有時無電源或時鐘不穩(wěn)定查電源紋波、時鐘抖動配置讀寫超時EP響應(yīng)邏輯卡死查EP狀態(tài)機(jī)、Completion機(jī)制這張表建議打印出來貼在工位上遇到問題先對照排查。5.5 熱插拔功能的注意事項PCIe熱插拔Hot-Plug在服務(wù)器和存儲場景很常見。實現(xiàn)熱插拔需要Attention Button用戶按鍵請求插拔。Power Indicator指示電源狀態(tài)。Attention Indicator指示注意狀態(tài)。Slot Power Controller控制槽位電源。熱插拔的軟件流程是用戶按鍵 - 系統(tǒng)收到中斷 - 驅(qū)動卸載 - 電源關(guān)閉 - 用戶拔卡。插入時反過來。調(diào)試熱插拔時重點查Hot-Plug Capability寄存器里的各狀態(tài)位以及中斷有沒有正確上報。注意不是所有PCIe插槽都支持熱插拔。硬件上需要有Hot-Plug Controller軟件上需要驅(qū)動支持。FPGA做EP時如果要支持熱插拔IP核里要配置Hot-Plug相關(guān)寄存器。6. 從協(xié)議到實戰(zhàn)幾個容易忽略的細(xì)節(jié)6.1 配置空間讀寫的事務(wù)類型RC訪問EP配置空間用的是CfgRd0/CfgWr0Type 0或CfgRd1/CfgWr1Type 1。Type 0用于直接訪問EPType 1用于通過橋訪問下游EP。RC在枚舉時對Bus 0上的設(shè)備用Type 0對橋下面的設(shè)備用Type 1。這里有個細(xì)節(jié)Type 1配置請求里的Bus Number字段是目標(biāo)設(shè)備的總線號。橋收到Type 1請求后如果目標(biāo)總線號在它的Secondary和Subordinate之間就把Type 1轉(zhuǎn)成Type 0發(fā)到下游。如果不在就忽略或轉(zhuǎn)發(fā)給上級。調(diào)試時如果發(fā)現(xiàn)下游設(shè)備枚舉不到可以抓配置事務(wù)的TLP看Bus Number字段對不對。6.2 Memory BAR和IO BAR的選擇現(xiàn)代PCIe基本只用Memory BARIO BAR很少用了。Memory BAR分32位和64位。64位BAR占兩個BAR寄存器位置低4位是屬性高32位在下一個BAR里。選擇BAR類型時控制寄存器用小BAR比如4KB或8KB32位或64位都行。數(shù)據(jù)緩沖用大BAR比如64MB或256MB必須64位因為32位BAR最大只能到4GB地址空間。Prefetchable如果BAR空間支持預(yù)取設(shè)Prefetchable位。但要注意Prefetchable BAR不能有讀副作用。實操建議FPGA EP設(shè)計時BAR0放控制寄存器BAR2放DMA緩沖。BAR0用64位4KBBAR2用64位256MB。這樣主機(jī)驅(qū)動可以分別映射互不干擾。6.3 MSI-X中斷的配置與調(diào)試MSI-X比MSI更靈活支持更多中斷向量每個向量可以獨立配置地址和數(shù)據(jù)。配置MSI-X需要在配置空間里使能MSI-X Capability。分配MSI-X Table和PBA的BAR空間。驅(qū)動填寫每個向量的Message Address和Message Data。EP觸發(fā)中斷時向?qū)?yīng)的Address寫入Data。調(diào)試MSI-X時常見問題是中斷收不到。排查MSI-X Enable位有沒有置1。Table里的Address和Data有沒有填對。EP有沒有正確發(fā)起Memory Write TLP。RC側(cè)的中斷控制器有沒有正確路由。在Linux下可以用cat /proc/interrupts看中斷計數(shù)確認(rèn)中斷有沒有上來。6.4 鏈路訓(xùn)練中的LTSSM狀態(tài)機(jī)LTSSMLink Training and Status State Machine是PCIe物理層的核心狀態(tài)機(jī)。主要狀態(tài)包括Detect檢測接收端有沒有設(shè)備。Polling發(fā)送訓(xùn)練序列協(xié)商極性。Configuration協(xié)商鏈路寬度和速率。L0正常工作狀態(tài)。Recovery鏈路出錯后重訓(xùn)練。L1/L2低功耗狀態(tài)。調(diào)試時如果鏈路起不來用ILA抓LTSSM狀態(tài)看卡在哪個狀態(tài)。Detect卡住查參考時鐘和差分對Polling卡住查極性Configuration卡住查Lane映射和速率配置。6.5 地址映射與路由的驗證方法RC需要維護(hù)一張地址映射表把CPU的物理地址映射到PCIe總線地址。EP的BAR被分配地址后CPU訪問這個地址RC就把請求路由到對應(yīng)的EP。驗證地址映射的方法在RC側(cè)讀EP的BAR空間看能不能讀到預(yù)期值。用DMA從EP寫數(shù)據(jù)到RC內(nèi)存看數(shù)據(jù)對不對。檢查RC的地址譯碼邏輯確認(rèn)沒有地址重疊。如果地址映射錯了輕則讀寫失敗重則寫到別的設(shè)備上導(dǎo)致系統(tǒng)崩潰。所以枚舉完成后一定要做地址空間的沖突檢查。7. 寫在最后一些個人體會PCIe的RC和EP模式看起來只是兩個配置選項但背后牽扯的是整個枚舉流程、地址分配、中斷路由和錯誤處理。我調(diào)過不少PCIe的板子最深的體會是協(xié)議標(biāo)準(zhǔn)里的每一個寄存器位都是有人踩過坑之后加上的。你忽略的那個bit可能就是別人熬夜查了一周的問題。新手常犯的錯誤是只關(guān)注IP核的配置界面不讀協(xié)議標(biāo)準(zhǔn)。但真正遇到詭異問題時最后還是要回到協(xié)議里找答案。比如為什么BAR大小必須是2的冪次方為什么配置空間前64字節(jié)的布局是那樣這些在協(xié)議里都有解釋。另外調(diào)試PCIe一定要有耐心。鏈路訓(xùn)練、枚舉、地址分配、中斷每一步都可能出問題。建議按順序來先確保物理層鏈路訓(xùn)練成功再搞枚舉再搞地址映射最后搞中斷和DMA。跳步調(diào)試只會讓你更迷茫。最后分享一個小技巧如果你手頭沒有PCIe協(xié)議分析儀可以用FPGA的ILA抓TLP。雖然不如專業(yè)分析儀方便但勝在成本低而且能看到IP核內(nèi)部的信號。抓TLP時重點看Header里的Type字段、Requester ID、Completer ID和Tag這幾個字段能幫你快速定位事務(wù)流向。