不同於並行匯流排,PCIe 這樣的序列匯流排不使用匯流排上的控制訊號來表示某時刻鏈路上正在發生什麼。相反地,PCIe 鏈路上的傳送方發出的位元流必須要有一個預期的大小,還要有一個可供接收方辨認的格式,這樣接收方才能理解位元流的內容。此外,PCIe 在傳輸資料包時並不使用任何直接握手機制(immediate handshake)。
除了邏輯空閒符號(Logical Idle symbol)和 Ordered Set 的物理層包外,在活躍的 PCIe 鏈路上傳輸的資訊的基本組塊被稱為 Packet(包),包是由符號組成的。鏈路上交換的兩類主要的資料包為高層的 TLP(Transaction Layer Packet,事務層包),和低層的用於鏈路維護的包稱為 DLLP(Data Link Layer Packet,資料鏈路層包)。這些包和它們的傳輸流如圖 5‑1 所示。物理層的 Ordered Set 也是一種包,但是它並不像 TLP 和 DLLP 一樣會被封裝上包起始符號和包結束符號(也就是前面章節所講的組幀符號),並且 Ordered Set 也並沒有像 TLP 和 DLLP 一樣的位元組條帶化過程,相反地,Ordered Set 會在鏈路的每個通道(lane)上都複製一份,而不是像位元組條帶化一樣把資訊按位元組分配到各個通道上。
圖 5‑1 TLP 和 DLLP 包
5.1.2 使用基於資料包協議的動機
使用基於包的協議(Packet-Based Protocol)有三個明顯的優點,特別是對於資料完整性(data integrity)來說:
5.1.2.1 精心定義的資料包格式
像 PCI 這種早期的匯流排,它們允許匯流排上不確定資料量大小的傳輸,這使得只要傳輸沒有結束就無法識別出資料荷載(payload)的邊界。此外,任何一個裝置都可以在此次傳輸完成前終止這個傳輸,這使得傳送方很難去計算並傳輸一個覆蓋了整個資料荷載的校驗和或者 CRC。相反地,PCI 使用了一種簡單的奇偶校驗的方案,並在每個資料階段(data phase)進行奇偶校驗。(這裡如果沒理解請參閱 Chapter 1 內的 PCI 相關內容)
相比之下,PCIe 包具有一個已知的大小和格式。位於包的開頭的部分為 Header,它用來指示這個包的型別,幷包含了必需欄位(required field)和可選欄位(optional field)。除了地址欄位以外,Header 中的其他欄位長度都是固定的,地址欄位可以為 32bit 也可以為 64bit。一旦一次傳輸開始,接收方不能暫停或者提前終止它。這種結構化的格式使得我們可以在 TLP 中加入一些資訊來更有利於進行可靠的傳輸,例如加入組幀符號(framing symbol)、CRC、以及一個包的序列號(Sequence Number)。
5.1.2.2 使用組幀符號定義包的邊界
在 PCIe Gen1 和 Gen2 操作模式中使用的是 8b/10b 編碼,因此在這 Gen1 和 Gen2 中每個 TLP 和 DLLP 在傳送前都會使用起始符號(Start)和結束符號(End)這兩種控制符號來進行組幀,這樣就可以給接收方清晰地定義出包的邊界。這是在 PCI 和 PCI-X 上的重大改進,在 PCI 和 PCI-X 中使用一個單獨的 FRAME# 訊號來指示一個事務的開始和結束,如果 FRAME# 出現了毛刺,或者其他的控制訊號出現了毛刺,那麼都有可能造成目標裝置對匯流排行為的誤解。一個 PCIe 接收方必須在最後的鏈路活動開始或結束之前,就正確完成一個完整 10bit 符號的譯碼,這樣接收方能更容易識別不期望出現或者無法識別的符號,並且將其作為一個錯誤來處理。
對於 PCIe Gen3 使用的 128b/130b 編碼來說,控制字元不再被使用,並且也沒有組幀符號了。對於更多的關於 Gen3 編碼與早期版本的差異的內容,請參閱 Chapter 12“Physical Layer-Logical(Gen3)”。
5.1.2.3 使用 CRC 保障整個資料包的正確傳輸
在 PCI 體系結構中,會在地址階段(address phase)和資料階段(data phase)中使用奇偶校驗邊帶(side-band)訊號,但是在 PCIe 中則不同。PCIe 中使用帶內(in-band)的 CRC 值來驗證整個資料包是否進行了無錯誤的傳輸。同時 TLP 還會被髮送方的資料鏈路層新增上一個序列號(Sequence Number),這使得當這個序列號的資料包傳輸出錯時可以很簡單的定位到它,並進行自動的重傳。傳送方會在自己的 Retry Buffer 內儲存每個 TLP 的一個副本,直到接收方確認了這個 TLP 成功無錯傳輸後才會將副本清除。這種 TLP 的確認機制被稱為 Ack/Nak 協議(更多內容請參閱 Chapter 10 “Ack/Nak Protocol” ),它用來形成基礎的鏈路級 TLP 錯誤檢測和糾正機制。這種 Ack/Nak 協議提供的錯誤恢復機制使得我們可以在問題發生的地方或者鏈路上及時的解決問題,但是這也要求有一個本地的硬體解決方案來支援這種協議。
5.2 TLP 細節(TLP Details)
在 PCIe 中,高層次事務起源於傳送方的 Device Core,終止於接收方的 Device Core。事務層會處理這些請求,其中,傳送端的事務層組裝 TLP,接收端的事務層解析 TLP。在這個過程中,每個裝置的資料鏈路層和物理層也會參與包的組裝。
5.2.1 TLP 的組包和拆包
如圖 5‑2 所示的是鏈路的傳送端組裝 TLP 和接收端拆解 TLP 的一般流程。現在讓我們講一講從一個包的生成,到它被傳送到接收端的 Device Core 的各個步驟。下面列出了 TLP 組包和拆包的關鍵階段,列出的編號與圖 5‑2 中的編號相對應。
圖 5‑2 PCIe TLP 的組包與拆包
傳送方
- 裝置 A 的 Device Core 向它的 PCIe 介面傳送一個請求(具體 Device Core 是如何給 PCIe 介面傳送請求的,這並不是 PCIe 協議或者本書的討論範疇)。這個請求中包括:
-
目標地址或者 ID(也就是路由資訊)
-
源端資訊,例如 Requester ID 和 Tag
-
事務型別/資料包型別(需要執行的命令,例如一個記憶體讀取 MRd)
-
資料荷載大小 payload size 和資料荷載內容(如果 TLP 需要帶資料)
-
流量型別(Traffic Class,用於分配資料包的優先順序)
-
請求的自身屬性(No Snoop 無窺探、Relaxed Ordering 寬鬆排序,等等)
-
基於這個請求,事務層將會組建 TLP Header,並在其後附上資料荷載(如果有),以及如果啟用並支援可選項的話也可以再附上 ECRC(End-to-End CRC)。隨後 TLP 就會被放入一個虛擬通道 Buffer。這個虛擬通道會根據事務排序規則來管理 TLP 的順序,並在向下轉發 TLP 到資料鏈路層之前,確認接收方有足夠的 Buffer 來接收一個 TLP。
-
當 TLP 到達資料鏈路層,它會被分配一個序列號(Sequence Number),並基於 TLP 的內容和序列號來計算出一個 LCRC(Link CRC)來附加在原 TLP 後。然後會將經過這些處理過程之後的 TLP 儲存一個副本,這個副本會儲存在資料鏈路層的重傳 Buffer(Replay Buffer,也可稱為 Retry Buffer)中,這是為了應對傳輸出錯的情況。與此同時,這個 TLP 也會被向下轉發至物理層。
-
物理層將會進行一系列的操作來準備對這個資料包進行序列傳輸,包括位元組條帶化(Byte Striping)、擾碼(Scrambling)、編碼(Encoding)以及並串轉換(Serializing)。對於 Gen1 和 Gen2 的裝置,當進行 8b/10b 編碼時,會將 STP 和 END 這兩個控制字元分別加在 TLP 的首端和尾端。最後,這個資料包透過鏈路進行傳輸。在 Gen3 操作模式中,STP 令牌(STP token)會被新增在 TLP 的首端,但是並不會在尾端加上 END,而是在 STP 令牌中包含 TLP 大小的資訊來判斷 TLP 的尾部位置。
接收方
-
在接收方(本例中是 Device B),為了傳送包所做的一切準備現在都必須撤銷。物理層將對位元流進行串並轉換(deserialize)、對串並轉換後的符號進行解碼,然後再進行位元組反條帶化(un-stripes)。控制字元將被移除,因為它們僅在物理層有意義,然後這個資料包就會被向上轉發至資料鏈路層。
-
資料鏈路層將會計算 CRC(具體一點是 LCRC)並與 TLP 中的 CRC 進行比較。如果 CRC 比較結果相同,那麼就再檢查序列號(Sequence Number)。如果都沒有出現錯誤,那就把 CRC 和序列號都從 TLP 剝除,並隨後將 TLP 向上轉發給接收方事務層,與此同時要透過返回給傳送方一個 Ack DLLP 來通知傳送方這個 TLP 被成功接收。相反地,如果前面的過程中檢查出了錯誤,那麼就要返回給傳送方一個 Nak DLLP,這樣傳送方將會使用它的重傳 Buffer 來對 TLP 進行重傳。
-
在事務層,TLP 被進行解碼,並將 TLP 內的資訊傳遞給 Device Core 來進行相應的操作。如果當前接收裝置就是資料包的最終目的地,那麼它可以檢查 ECRC 錯誤,並在發現任何 ECRC 錯誤時報告給 Device Core。
5.2.2 TLP 結構
一個事務層包 TLP 中每個欄位域的基本用法在表 5‑1 中進行了定義。
表 5‑1 TLP Header 的 Type 欄位定義了事務不同種類
下面對錶 5‑1 中的內容進行復述。
-
Header
-
協議層次:事務層
-
該元件用法:大小為 3 或 4DW(12 或 16Bytes)。Header 的格式會隨型別而變化,但是 Header 也定義了一些引數,包括:
-
事務型別(Transaction Type)
-
目標地址、ID 等
-
傳輸資料量大小(如果有資料)、位元組使能(Byte Enable)
-
屬性(Attribute)
-
流量型別(Traffic Class)
-
-
Data
-
協議層次:事務層
-
該元件用法:可選的 1-1024DW 大小的資料荷載,具體的大小由位元組使能或者位元組對齊的開始和結束地址來進行描述。需要注意指定的長度不能為 0,但是一個 0 長度的讀取可以透過指定長度為 1DW 然後將位元組使能全部置為 0 來進行近似處理(在某些情況下會使用)。來自 Completer 的結果資料雖然是未定義的種類,但是 Requester 並不使用它,因此就和指定 0 長度的目的等效了。
-
Digest/ECRC
-
協議層次:事務層
-
該元件用法:可選的功能。當需要使用時,ECRC 的大小永遠為 1DW。
5.2.3 通用 TLP Header 格式(Generic TLP Header Format)
5.2.3.1 概括(General)
如圖 5‑3 中,展示了一個 4DW 的通用 TLP Header 的格式和內容。在本節內,會對幾乎所有事務的 TLP Header 中的公共欄位進行總結,並會在稍後討論與特定事務型別相關 Header 格式差異。
圖 5‑3 通用 TLP Header 的各欄位域
5.2.3.2 通用 Header 各欄位摘要
表 5‑2 中對通用 TLP Header 中的每個欄位的大小和用途進行了總結。需要注意的是,在圖 5‑3 中被標註為“R”的欄位是保留欄位(reserve),應該被置為 0。
Header 中的欄位名 | Header 中的位置 | 欄位作用 |
---|---|---|
Fmt[2:0] 格式 (Format) | Byte 0 Bit 7:5 | 這些 bit 的編碼資訊是關於 Header 的大小,以及這個 TLP 中是否存在資料荷載部分: 000b:3DW Header,無資料荷載 001b:4DW Header,無資料荷載 010b:3DW Header,有資料荷載 011b:4DW Header,有資料荷載 100b:1DW,屬於 Prefix TLP 不難發現,除了 Prefix TLP 外,只要 Fmt[0]=0 那麼就是 3DW Header,反之則為 4DW Header。若 Fmt[1]=0 那麼就無資料荷載,反之則有資料荷載。 對於低於 4GB 的地址,必須使用 3DW Header。協議規定,如果使用 4DW Header 但是地址小於 4GB,也就是說將 64bit 地址的高 32bit 置為 0,那麼這種情況下接收方的行為是未進行定義的(undefined)。 |
Type[4:0] 型別 | Byte 0 Bit 4:0 | 這些 bit 編碼的資訊是 TLP 的不同事務型別。Type 欄位用來跟 Fmt[1:0]欄位一起指定了事務型別、Header 大小、以及是否存在資料荷載。更多詳細資訊請參閱“Generic Header Field Details”一節。 |
TC[2:0] 流量型別 (Traffic Class) | Byte 1 Bit 6:4 | 這些 bit 表示將應用於這個 TLP 和與之完成相關(如果需要完成包)的流量型別: 000b:Traffic Class 0(預設) 001b:Traffic Class 1 …… 111b:Traffic Class 7 TC 0 是預設型別,而 TC 1-7 是用來提供差異化的服務。更多資訊請參閱“Traffic Class”一節。 |
Attr[2] 屬性 (Attribute) | Byte 1 Bit 2 | 這個第三位的 Attribute 位(它共有 3 位)用於表示這個 TLP 是否使用基於 ID 的排序(ID-based Ordering)。更多內容請參閱“ID Based Ordering”一節。 |
TH TLP 處理提示 (TLP Processing Hints) | Byte 1 Bit 0 | 它用於表示何時 TLP 中會包含 TLP 提示(TLP Hints),以便讓系統瞭解如何更好的處理這個 TLP。更多內容請參閱“TPH(TLP Processing Hints)”。 |
TD TLP 摘要 (TLP Digest) | Byte 2 Bit 7 | 如果 TD=1,那麼這個 TLP 中將包括可選的 4Byte TLP Digest 欄位,也就是 ECRC 值。 它有一些規則: 所有的接收者都必須要透過這個 bit 來檢查是否存在 Digest 欄位。 如果一個 TLP 的 TD=1,但是它又沒有 Digest,那麼它將被當做畸形 TLP(Malformed TLP)處理。 如果接收裝置支援 ECRC 校驗,且此 TLP 內 TD=1,那麼這個接收裝置必須進行校驗。 如果一個作為 TLP 最終目的地的裝置並不支援 ECRC 校驗(因為這是可選功能),那麼它必須要忽略 Digest 欄位。 更多內容請參閱“CRC”和“ECRC Generating and Checking”這兩節。 |
EP 受汙染的資料 (Poisoned Data) | Byte 2 Bit 6 | 如果 EP=1,那麼就認為所有伴隨此資料的資料都是無效的,儘管相關事務依然允許正常的完成。關於 Poisoned 資料包的更多內容,請參閱“Data Poisoning”一節。 |
Attr[1:0] 屬性 (Attributes) | Byte 2 Bit 5:4 | Bit 5 = Relax Ordering(寬鬆排序):當它被置為 1 時,這個 TLP 會啟用 PCI-X 的寬鬆排序。如果它為 0,則是用 PCI 的嚴格的 PCI 排序(strict PCI ordering)。 Bit 4 = No Snoop(無窺探):當置為 1 時,Requester 的意思是這個 TLP 不會存在 host cache 一致性的問題(host cache coherency issues),因此係統硬體可以透過跳過普通處理器對這個請求的 cache 窺探,以此來節省時間。而當這一位被置為 0 時,需要進行 PCI-type 的 cache 窺探保護。 |
AT[1:0] 地址型別 (Address Type) | Byte 2 Bit 3:2 | 對於 Memory 和 Atomic 請求來說,這個欄位用於支援虛擬化系統(virtualized system)的地址轉換。這個地址轉換協議由一個單獨的規範進行描述,被稱為 Address Translation Services,可以看到該欄位的編碼為: 00 = 預設/未轉換(Default/Untranslated) 01 = 轉換請求(Translation Request) 10 = 已轉換(Translated) 11 = 保留 reserve |
Length[9:0] 長度 | Byte 2 Bit 1:0 Byte 3 Bit 7:0 | TLP 資料荷載傳輸量的大小,單位為 DW,編碼方式為: 00 0000 0001b = 1DW 00 0000 0010b = 2DW …… 11 1111 1111b = 1023DW 00 0000 0000 = 1024DW |
Last DW BE[3:0] 末尾 DW 的位元組使能 (Last DW Byte Enable) | Byte 7 Bit 7:4 | 這個欄位中的 4 個高有效的 bit,與資料荷載中最後一個 DW 中的 4 個 Byte 一一對應。 Bit 7 = 1:末尾 DW 的 Byte 3 是有效的;否則為無效的。 Bit 6 = 1:末尾 DW 的 Byte 2 是有效的;否則為無效的。 Bit 5 = 1:末尾 DW 的 Byte 1 是有效的;否則為無效的。 Bit 4 = 1:末尾 DW 的 Byte 0 是有效的;否則為無效的。 |
1st DW BE[3:0] 第一個 DW 的位元組使能 (First DW Byte Enable) | Byte 7 Bit 3:0 | 這個欄位中的 4 個高有效的 bit,與資料荷載中第一個 DW 中的 4 個 Byte 一一對應。 Bit 3 = 1:末尾 DW 的 Byte 3 是有效的;否則為無效的。 Bit 2 = 1:末尾 DW 的 Byte 2 是有效的;否則為無效的。 Bit 1 = 1:末尾 DW 的 Byte 1 是有效的;否則為無效的。 Bit 0 = 1:末尾 DW 的 Byte 0 是有效的;否則為無效的。 |