--- tags: I/O, System Software --- # PCI/PCIe(1): 基礎篇 ## Introduction ### PCI [Peripheral Component Interconnect(PCI)](https://en.wikipedia.org/wiki/Peripheral_Component_Interconnect) 是一種連接電腦主機板和外部裝置的匯流排標準。其取代了 [ISA](https://en.wikipedia.org/wiki/Industry_Standard_Architecture) 和 [VESA](https://en.wikipedia.org/wiki/Video_Electronics_Standards_Association),成為現代電腦中常見的標準。 特性上,PCI 是採同步傳輸(Synchronous transaction)。這意味著 PCI bus 只使用一個 clock。預設情況下,時脈運行在 33MHz,但允許更低到甚至 0MHz 以省電。如果硬體支援,則可以運行較高的 66MHz。 PCI 是 32 位元匯流排,採用 BURST 傳輸。所謂 BURST,意指傳輸的方式為: * 開始傳輸 * 第一個 clock cycle 用於指定 32 位元位址 * 之後的許多 clock cycles 用來傳遞資料 * 結束傳輸 在 BURST 傳輸下,每次傳遞資料時不重新指定位址,位址會在每個資料的 clock cycle 自動遞增。如果要改變位址,需停止目前的傳輸並重啟另一個新的。 PCI 的另一關鍵特性是隨插即用[("plug-and-play")](https://en.wikipedia.org/wiki/Plug_and_play)。Host 有能力確定 PCI bus 中每個 PCI 裝置的身份,例如製造商、功能等。也可以判斷每個 PCI 裝置需要多少記憶體空間、多少中斷等資訊。除此之外,可以重新分配/映射每個裝置的記憶體空間。 ### PCIe [Peripheral Component Interconnect Express(PCIe)](https://zh.wikipedia.org/zh-tw/PCI_Express),是電腦匯流排(bus)的一個重要分支。沿用既有的 PCI 概念及訊號標準,並且構建了更加高速的序列通訊(Serial communication)系統標準。目前這一標準由 PCI-SIG 組織制定和維護。PCIe 僅應用於內部互連。由於 PCIe 是基於既有的 PCI 系統,所以只需修改實體層而無須修改軟體就可將現有 PCI 系統轉換為 PCIe。 PCIe 標準的演進相當快速,也反映了該規格在計算機中重要的地位。與 PCI 是 shared bus 不同,PCIe 是點對點(point-to-point)的匯流排。這意味著每個裝置都有其專屬的連接,不像PCI 那樣共享 bandwidth。也因此與 33MHz 的 PCI 相比,GEN 1 的 PCIe 就可達 2.5GHz 的速度。 ![image](https://hackmd.io/_uploads/SyGNkAmFC.png) > [PCIe_vs_PCI](https://commons.wikimedia.org/wiki/File:PCIe_vs_PCI.gif) 截至撰文為止,各代的速度如下表所顯示: ![image](https://hackmd.io/_uploads/Bykt3IAjp.png) > [PCI_Express](https://zh.wikipedia.org/zh-tw/PCI_Express) ## PCIe Connector 下圖展示了一個 1 lane 的 PCIe 的連結器(connector)。可以看到共有 36 個 Pin 腳,又分成上下個 18 個成為兩列。其中只有 6 個 pin 與傳輸資料有關(其他是電源或輔助信號)。分別是: * 一對 REFCLK * 一對 PER,與資料接收有關(receive) * 一對 PET,與資料發送有關(transmit) ![image](https://hackmd.io/_uploads/BJP4_TQF0.png) ![image](https://hackmd.io/_uploads/HyJSupmtA.png) > [fpga4fun: PCI Express - Connector](https://www.fpga4fun.com/PCI-Express1.html) 這些成對的訊號通稱為「[差分訊號(differential signal)](https://en.wikipedia.org/wiki/Differential_signalling)」。因為對中的兩個信號攜帶實際上攜帶相同的資料,但相位相反。其目的主要是為了傳輸的可靠性。 PCIe 可以通過使用多個 lane 來提升速度。由於 REFCLK pair 不需要重複,因此以具有 2 個 lane 的 PCIe 為例,需要使用 1 對 REFCLK + 2 對 PET + 2 對 PER。 ## PCIe Transaction PCIe 是一種序列匯流排(serial bus)。在 PCIe 協議上,所有操作都以封包(packet)的形式完成。舉例來說,假設 CPU 想要將一些資料寫入裝置。它將命令轉送到 PCIe Bridge,然後 PCIe bridge 會建立一個封包、之中寫入位址和資料、然後以序列方式轉送到目標。目標裝置收到封包後,再進行解析並執行之。 ![image](https://hackmd.io/_uploads/rJ_bW1EtC.png) > [PCI Express® Base Specification Revision 5.0](https://picture.iczhiku.com/resource/eetop/SYkDTqhOLhpUTnMx.pdf) PCIe 是一個多層協定,由事務層(Transaction)、資料交換層(Data Link)和實體層(Physical)構成。簡述三層各自所扮演的角色為: * 事務層: host/作業系統只需接觸此層級。一旦封包抵達此層級,其被預設是完好的。 * 資料連接層: 負責校驗資料完整性(CRC)、重送等機制。 * 實體層: 與 pin 的相位切換有關。管理 PCIe 鏈路的電氣特性,包括訊號的編碼、傳輸和接收。 ### Transaction layer 從系統軟體的角度,事務層(Transaction layer)的運作原理是我們理解 PCIe 重要的一環。在事務層,封包被接收。而封包可以有許多類型:比如讀寫記憶體、讀寫 I/O、表示完成訊息等。這一類封包在標準中稱為「事務層封包(Transaction Layer Packet, TLP)」。 PCIe 是 32 bits 的 bus,因此每 32 bits 的資料在 PCIe 上可稱為 double word(以下簡稱 DW)。每個 TLP 由一般 header (3-4 個 DW)和 data payload (0-1023 個 DW)組成。 :::info 為求容易理解,這裡暫時忽略可選的 TLP Prefix 和 TLP Digest。詳情請翻閱 PCIe Specification 的第二章 Transaction Layer Specification。 ::: Header 的格式有許多種,但第一個 DW 會是相同的。`Fmt` 一欄位標示 header 的格式,比如 header 大小以及是否有 data payload 等。結合 `Type` 則描述了 TLP 對應的操作。詳情請見 [PCI Express® Base Specification Revision 5.0](https://picture.iczhiku.com/resource/eetop/SYkDTqhOLhpUTnMx.pdf) 的 `Table 2-3 Fmt[2:0] and Type[4:0] Field Encodings`。 ![image](https://hackmd.io/_uploads/r1OjJk4Y0.png) ![image](https://hackmd.io/_uploads/Syx0ZGyVK0.png) https://xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-1 ## PCIe Topology 如下圖是一個 [PCI Express(PCIe)](https://en.wikipedia.org/wiki/PCI_Express) topology: ![image](https://hackmd.io/_uploads/B16DewesT.png) > [PCI-SIG specifications](https://pcisig.com/specifications) 在 PCIe 系統中,有幾個關鍵名詞/角色: * Hierarchy 是含 Root Complex 的 port 上,以及通過 Switch、Bridge 關聯至 RC 上的所有裝置形成的網路,如上圖所呈現即是一個 Hierachy * [Root Complex(RC)](https://en.wikipedia.org/wiki/Root_complex) 是整個 I/O 架構的根節點,其將 CPU 和 memory subsystem 連接到 Switch 或 Endpoint,以構成 PCIe 的 Hierarchy * PCIe to PCI/PCI-X Bridge 提供 PCIe Hierarchy 和 PCI/PCI-X Hierarchy 之間的連結 * Switch 是多個虛擬 PCI-PCI bridge 的邏輯組合 * Endpoint 在 topology 中是指一種 function,可以是 PCIe 任務的 requester 或 completer,又可分三種 * Legacy * PCIe * RCiEP 有了連接的機制,CPU 啟動後,Host 會試著去讀不同的 bus 和 device numbers 之組合下的 function 0 之 VID/DID(DFS),若成功讀取就代表這個 BDF 下具有 device,這個流程被稱作 **Bus Enumeration/PCI Enumeration**。 ![](https://i.imgur.com/h240Shj.png =600x) 從 bus -> device -> function 的 **BDF** 結構構成了每個 PCIe 設備節點的 identifier。 :::info 關於硬體相關基礎知識,由於非專案因此不多深入。更多詳細知識推薦閱讀以下文章: > [深入PCI与PCIe之一:硬件篇](https://zhuanlan.zhihu.com/p/26172972) ::: ## PCI Configuration Space PCI Configuration Space 和一般記憶體空間是分離的,通常我們會以 BDF 做為 PCI 裝置的唯一位址,並以此透過晶片廠商提供的方式存取 PCI Configuration Space。以 Intel 的晶片組為例,其使用 I/O 空間的 0xCF8 來設定 Configuration Address Register 來決定要存取的 BDF(參照下圖),然後再以 0xCFC 位址來讀取或寫入 Configuration Data Register 以得到想存取的 Configuration Space 內容。 ![image](https://hackmd.io/_uploads/r14b--Aqp.png) > [[BIOS]PCI Read/Write](https://medium.com/@jacksonchen_43335/bios-pci-read-write-14356b013c43) 傳統的 PCI 裝置的 Configuration Space 包含了 64 bytes 的 header space 和 192 bytes 的 capability space,加起來是 256 bytes。CPU 可以透過 BDF 加上 register offset 的方式來存取 I/O address space 讀取 PCI header。 ![](https://i.imgur.com/jTexQz3.png) 而對 PCIe 而言,Configuration Space 擴展至 4096 bytes。剩餘的 (4096 - 256) bytes 必須以 Memory map I/O 進行存取。硬體上,因為有 root complex,如果 CPU 想讀 PCIe device 的數據,可以先讓 root complex 把數據從 PCIe device 讀到 CPU memory,然後 CPU 再從 memory 讀取數據即可;反之,如果 CPU 要往 device 寫數據,先把數據在 memory 中寫入,然後再通過 root complex 寫入 PCIe 即可。 包含裝置上的 RAM 和 ROM 也可能可以透過 MMIO 的方式進行操作。總結來說,即下圖所示: ![](https://i.imgur.com/CP5PSCc.png) ![](https://i.imgur.com/y6XDvyc.png) 下面我們特別說明幾個重要的 configuration 欄位: * CMD (0x4): 一般情形下,Host會在 init controller 時將 BME 跟 MSE bit 設為 1 * BME : 允許 controller 能夠向 Host 發送 DMA Read/Write Memory request * MSE : 控制能不能存取 controller register * CC(class code, 0x9): 可用來判別 controller 類型為 NVMe * BAR: 透過讀寫 BARs 可以得知目標 function 需要的記憶體空間大小,並可藉其提供配置空間位址的資訊 * BAR0: – Memory Register Base Address, lower 32-bits * BAR1: – Memory Register Base Address, upper 32-bits * capability pointer(0x34): linked capability 那麼 MMIO 的映射位址該如何得知呢? ![](https://i.imgur.com/ujl2phH.png) 該 mapping 的位置需透過過 PCI header 的 BAR0 和 BAR1 取得。當 Host 準備配置空間時,對 BAR register 發送 write request,嘗試將所有 bit 寫為 1,PCIe 收到 request 後,因為 BAR 有些 bit 是 read only 的,我們可以從保持不變的值取得所需空間的信息。以上圖為例,寫完後 低 12 bits 沒改變,表示需要 mapping 的空間大小是 4KB,此外注意到低 4 bits 表示了該存儲空間的一些屬性。 ## PCIe Signaling ### Reapter 隨著 PCIe 的演進,傳輸速度也不斷提升,加上現代硬體設計更加複雜。在實際的硬體應用上,訊號的完整性問題成為一大問題。 為克服上述的訊號問題,可以在 CPU 和 Endpoint 之間加上 Repeater 以解決。Reapter 能做為一種訊號調節的裝置,以確保 Root complex 和 endpoint 之間的溝通品質。Repeater 又可以分為兩大類: Redriver 和 Retimer: * Redriver: 主要用於訊號增強,能夠增強 PCIe 訊號的強度和穩定性,補償訊號衰減,常用於較短距離的連接,如主機板內部 * Retimer: 不僅可以增強訊號,還能重整訊號、提供時脈並復原數據等功能。可以在長距離連接中恢復和補償傳輸時的時序失真,確保高資料率傳輸的品質 詳細可閱讀: > [車用PCIe中繼IC:訊號的穩定高速之旅](https://phisonblog.com/zh-tw/the-automotive-pcie-repeater-a-high-speed-journey-of-stable-signals/) ### Signal 在 PCIe 的傳輸上,訊號協議如下圖所呈現。 ![image](https://hackmd.io/_uploads/ByYx7XWgyg.png) > [從硬體的角度,解析 PCIe 介面初始化](https://www.eagletek.com.tw/post/analyzes-pcie-interface-initialization-from-a-hardware-perspective) 除了傳輸資料(DATA Rx/Tx)的訊號以外,REFCLK 即參考時脈(reference clock),是 PCIe 可以傳輸資料的前提。PCIe 採用 100MHz 的 reference block 以使在鏈結上的 PCIe 裝置可以高速的交換資料。 此外,有三個重要的控制訊號: * PERST#: PERST# 被稱基礎復位(fundamental reset),該訊號要一直被拉低直到 power rail 和 reference clock 穩定,當鏈路初始化的開始,便由低電位轉為高電位,通常這也就意外著鏈路的初始化(Link Initialization)完成 * CLKREQ#: 與低功耗狀態的轉換有關。這是一個 active-low 並用來請求 reference clock 的訊號 * WAKE#: 與低功耗狀態的轉換有關。同樣是一個 active-low 訊號,用於將 PCIe 裝置從低功耗模式重置到運作(active)模式 ### Link Training 當鏈路上的所有裝置獲得電源與參考時脈後,PCIe 中的實體層c會進行鏈路初始化(Link Initialization & Training)。 細節可參考 [PCIe Link Training Overview](https://www.ti.com/lit/an/snla415/snla415.pdf) 一文即影片。 ## 驅動程式介面 有關描述 PCI driver 的結構,以及如何註冊 PCI driver 的詳細說明,可參考以下資料: * [How To Write Linux PCI Drivers: pci_register_driver() call](https://docs.kernel.org/PCI/pci.html#pci-register-driver-call) ## Reference * [深入PCI与PCIe之一:硬件篇](https://zhuanlan.zhihu.com/p/26172972) * [深入PCI与PCIe之二:软件篇](https://zhuanlan.zhihu.com/p/26244141) * [原來 PCIe 技術原理這麼簡單!](https://www.gushiciku.cn/dc_tw/110022319) * [老男孩读PCIe之六:配置和地址空间](http://www.ssdfans.com/?p=8210) * [PCIe(一) —— 基础概念与设备树](https://r12f.com/posts/pcie-1-basics/) * [PCI Bus Subsystem](https://docs.kernel.org/PCI/index.html) * [Linux makes your PCIe topology visible in sysfs (/sys)](https://utcc.utoronto.ca/~cks/space/blog/linux/PCIeTopologyInSysfs) * [Fun and Easy PCIE - How the PCI Express Protocol works](https://www.youtube.com/watch?v=sRx2YLzBIqk) * [fpga4fun - PCI](https://www.fpga4fun.com/PCI.html) * [fpga4fun - PCIe](https://www.fpga4fun.com/PCI-Express.html)