在上一篇中,我們介紹了PCIe設備的配置空間,及其設計的目的,最后我們說到了消息路由的設計。所以,這一篇我們就繼續這個話題,來看看PCIe設備之間的通信方式吧。
1. PCIe協議棧
PCIe是以包(Packet)為單位傳輸數據的。和計算機網絡類似,其協議也是分層的。
其協議棧主要分為三層:物理層(Physical Layer),數據鏈路層(Data Link Layer)和事務層(Transaction Layer),如下圖所示:
2. 事務層(Transaction Layer)
PCIe的協議棧最上層叫做事務層,這一層定義了所有和用戶相關的PCIe的操作,所以這也會時大家最感興趣的一層。
2.1. 事務(Transaction)
PCIe的所有操作都被稱為一個事務(Transaction),這些事務分為四種類型:
內存事務(Memory Transaction)
IO事務(IO Transaction)
配置事務(Configuration Transaction)
消息事務(Message Transaction)
一個事務根據其請求的處理方式又被分為兩種:
Non-Posted:每個事務的請求消息發送出去后,會需要一個完成消息(Completion)來完成事務。比如,讀內存。
Posted:請求發送后不需要完成消息,屬于Fire and forget。比如,寫內存和所有的消息事務(這也是唯二的兩類請求)
所以,事務層的消息有三類:Non-Posted(NP),Posted(P)和Completion(Cpl)。
2.2. TLP(Transaction Layer Packet)
PCIe的事務請求和完成消息都是以TLP(Transaction Layer Packet)為單位傳輸的。其結構如下:
TLP Prefix:用來實現一些高級特性,比如精確時間測量(Precision Time Measurement),因為它不是必須的,所以我們先跳過。
TLP Digest:4個字節,可以存放諸如CRC的校驗碼,不過一般不需要開啟,因為后面說的數據鏈路層已經自帶了校驗了,這里相當于是雙保險。
TLP Header:這個是TLP中最重要的部分,我們后面馬上會詳細介紹。
TLP Payload:這個是TLP中的數據部分,根據不同的事務類型,其大小也不同。比如,讀事務就不需要Payload。另外Payload的大小也是有限制的,它不能超過Max_Payload_Size,最大為4096字節。
2.2.1. TLP頭
TLP的頭部根據處理地址長度的不同,會有12字節(稱為3DW)或者16字節(稱為4DW)寬。其前4個字節(第一個DW)是公共的頭部,包含了絕大部分的用于描述該事務本身的信息和行為的字段,其后的8個字節(第二個和第三個DW)會根據事務種類的不同而產生變化。其前四個字節如下:
Fmt: TLP頭的格式
Bit 7:如果是1,則Fmt必須是100,表示這個頭是TLP Prefix
Bit 6:1 = 讀事務(TLP頭之后沒有Payload),0 = 寫事務(TLP頭之后有Payload)
Bit 5:1 = 使用32位地址,頭部長度12字節(3DW Header),0 = 使用64位地址,頭部長度16字節(4DW Header)
Type:事務類型,表示這個事務是什么類型的事務,比如內存事務、IO事務、配置事務、消息事務等
LN(Lightweight Notification):用于標識當前這個內存請求或者完成消息是不是一個輕量級通知
TH(TLP Hints):用于表示TPH(TLP Processing Hint)是否啟用和TPH TLP Prefix是否存在
TD(TLP Digest):1 = 有TLP Digest,0 = 沒有TLP Digest
EP(Error Poisoning):1 = 有錯誤,0 = 沒有錯誤
AT(Address Translation):虛擬化相關的字段,00 = 無地址轉換,01 = 需要地址轉換,10 = 地址轉換已完成,11 = 保留
Length:Payload的長度,單位為DW(Double Word),1DW = 4字節
這里由兩個字段TC和Attr我們沒有介紹,因為它們是事務描述符的一部分,我們馬上就會介紹。
2.2.2. 事務描述符(Transaction Descriptor)
為了幫助通信的雙方知道對方的信息和對消息的處理方式進行描述,在TLP的頭中有幾個公共的字段,合在一起被稱為事務描述符:事務ID(Requester ID和Tag兩個字段),消息的屬性(Attr字段),流量分類(TC字段)。
雖然TLP頭中第二個DW開始的部分會隨著請求類型的不同而發生變化,但是這四個字段幾乎會在所有的消息中存在(某些情況下Tag會被忽略),所以這里我們用一個內存請求的消息來做例子,展示它們在TLP中的位置:
2.2.2.1. 事務ID(Transaction ID)
事務ID由Requester ID和Tag兩個字段組成,用于標識一個事務。其中,
Requester ID:一共16個bit,用于標識發起這個事務的設備,是請求發起者的BDF
Tag:一共10個bit,每個發出的TLP都會被賦予一個唯一的標簽,幫助PCIe進行數據傳輸的跟蹤和管理,比如并行處理,流控或亂序處理。這里注意T8和T9兩個bits,它們和其他的tag的bits不在一起(綠色高亮),且需要修改10-Bit Tag Requester Enable配置寄存器啟用
2.2.2.2. 消息屬性(Attributes)
消息屬性一共有三個bits:高兩位 Attr[2:1](Byte 1 - Bit 2,Byte 2 - Bit 5)用于控制消息處理的順序,而最低位 Attr[0](Byte 2 - Bit 4)用于控制Coherency。
2.2.2.2.1. 消息處理順序(Ordering)
Attr[2:1]這兩個Bits用于控制消息處理的順序,一共有四種情況:
Attr[2] | Attr[1] | 順序類型 | 說明 | |
---|---|---|---|---|
0 | 0 | 強制順序 | 默認值,不允許亂序處理 | |
0 | 1 | Relaxed Ordering | 允許接收者在當前請求沒有完成的時候,同時處理任何后續的請求 | |
1 | 0 | ID-based Ordering | 允許接收者在當前請求沒有完成的時候,同時處理來自其他設備的請求 | |
1 | 1 | 無序 | 相當于是Relaxed Ordering和ID-based Ordering的并集,允許接收者在當前請求沒有完成的時候,同時處理任何的請求 | |
2.2.2.2.2. No Snoop
NoSnoop(Attr[0])使用來控制緩存一致性的。默認的情況下(值為0),PCIe會對請求進行緩存一致性的處理,比如一個內存的讀請求,它會保證先去讀Cache,如果沒有讀到再去讀主內存。但是如果這個值為1,PCIe就會直接跳過Cache,去操作主內存。這樣就有可能導致一致性的問題,因為有可能Cache中的內容還沒有被寫入主內存中,這樣就讀到了錯誤的值。
但是,這并不代表這個flag沒有用,如果我們非常確定我們不需要考慮緩存,那么我們可以啟用這個flag,直接去操作主內存,從而提高性能。
當然,也正因為有一致性的問題,所以這個功能被很多事務禁止使用了:比如配置事務、IO事務、大部分的消息事務和MSI(跳過緩存發起中斷會導致DMA等功能出錯,讀到臟數據)等。
2.2.2.3. 流量分類(Traffic Class)
Traffic Class總共有3個bit,用于把所有的事務分成8個不同的類別,用于流控。
基于TC的流控是通過和VC(Virtual Channel)合作來實現的:
PCIe中的所有物理鏈路(Link)都可以創建多個VC(Virtual Channel),而每個VC都獨立工作,并有著流量控制機制。
一個或者多個TC可以被映射到一個VC上,這樣就可以通過操作TLP的TC來控制TLP走的VC了。
VC通過信用機制來控制發包速度,每個VC都有著自己的Credit Pool,如果一個VC的Credit不為0,那么它就可以發送TLP,并且消耗特定的Credit。每個VC的Credit也會在特定的時候補充,保證通信不會中斷。
TC的默認值是0,也是所有設備必須實現的。它被Hardcoded到了VC0上,所以如果沒有設置TC,那么所有的TLP都會走VC0。
最后,如果兩個包有了不同的VC,或者不同的TC,那么它們之間將沒有順序的保證。
這里我們主要了解TC到VC的映射就好,關于VC的具體機制,我們會在后面數據鏈路層介紹。以下是一個TC和VC相互合作的配置的例子。通過這種方法,我們就可以對PCIe進行流量控制啦!
2.3. TLP事務路由
有了事務ID,我們可以很容易的了解當前事務的源是誰,然而為了能讓通信雙方通信,我們還需要知道事務的目的地是哪里,這樣我們才能把事務發送到正確的地方。
在PCIe中,不同類型的事務中會使用不同的字段和方法來指定目的地,但是總結起來只有兩種:
通過具體的地址來指定目的地:這種路由方式叫做基于地址的路由(Address-Based Routing)。這種方式主要用于內存事務(Memory Transaction)和IO事務(IO Transaction),通過需要訪問的地址,我們就可以通過我們上一篇中介紹的路由機制來進行路由了。
通過BDF來指定目的地:這種路由方式叫做基于ID的路由(ID Based Routing)。這種方式主要用于非內存訪問型的事務,比如:配置事務(Configuration Transaction),消息事務(Message Transaction)和事務完成的消息通知(Completion)。
另外,我們上一篇還提到了一種特殊的ID分配方式ARI(Alternative Routing ID),它的唯一區別就是把Device Number的5個Bit給了Function Number,用以支持更多的Function,如下:
2.4. TLP小結
好了,到此我們已經把最核心的TLP的公用字段都介紹完畢了,包括TLP主題格式,事務如何分類,如何路由,如何進行流控等等。這里,為了再來整體的來看一下事務層的處理,我們可以參照Intel Cyclone 10的總體框圖,如下:
當然在TLP的頭中,我們仍然有很多字段沒有涉足,這些字段都和具體的事務類型相關,所以我們在這一篇中就不會過多的深入了。畢竟,我們這一篇主要是想聚焦在PCIe的通信協議本身上,來展示PCIe是如何進行通信的,關于每個具體的事務及其格式,我們會放在后面單獨的說。
3. 數據鏈路層(Transaction Layer)
當事務層將事務消息準備好之后,就會向下傳遞給數據鏈路層(Data Link Layer)。對于我們發送的事務消息來說,數據鏈路層主要負責一件事情:保證事務消息能正確的傳輸到目的地。
數據鏈路層傳輸的包主要包括兩種,一種用于傳輸TLP事務消息,一種用于傳輸數據鏈路層的控制消息,比如功能(Feature)控制,流量控制,電源管理等等。這兩種類型的包通過物理層的Token來進行區分:STP(Start of TLP)表示TLP消息,SDP(Start of DLLP)表示控制消息(DLLP,Data Link Layer Packet)。我們這里一個一個來看。
3.1. TLP事務消息的傳輸
3.1.1. 數據包格式和數據發送
為了達到這個目的,數據鏈路層會對數據包再進行一層封裝:
在包的前方添加一個序列號(Sequence Number),占用2個字節,用于保證包發送的順序。這個序列號是每個Link獨立的,只有上下游兩端保存的序列號(NEXT_RCV_SEQ)一致,才會被對端接收。在包的后方添加一個CRC校驗碼,叫做LCRC(Link CRC),占用4個字節,用于保證包中數據的正確性。注意,計算CRC的時候,剛剛添加的序列號也會被納入計算范圍中。
封裝完成后,為了保證成功的發送,數據鏈路層會先將包保存在Retry Buffer中,再轉交給物理層(Physical Layer)進行發送。在每條消息發送完畢之后,發送方會等待接收方發送ACK消息,如果接收到的返回消息是失敗消息,比如Seq錯誤,CRC校驗錯誤,或者任何物理層的錯誤,發送方就會把Retry Buffer中的消息拿出來重新發送。[1](3.6 Data Integrity Mechansisms) 。
3.1.2. 數據接收
對于數據的接收方,操作流程則相反。接收方會檢查接收到的數據包的序列號和CRC是否正確,如果不正確,就會發送一個Nak消息,要求發送方進行重傳。如果正確,就會回發一個Ack消息,表示接收成功,而此時發送方在收到了ACK消息后也可以將其從Retry Buffer中移除。這樣,數據鏈路層就保證了TLP的正確傳輸。[1](3.6 Data Integrity Mechansisms) 。
更加具體的數據接收處理流程如下:
3.2. 控制消息:DLLP(Data Link Layer Packet)
除了傳輸TLP數據包之外,數據鏈路層還需要很多專門用于控制的數據包,比如上面提到的Ack和Nak,這些數據包叫做DLLP(Data Link Layer Packet)。其格式如下:
DLLP中DLLP Type用來指定包的類型,而最后16位的CRC用來做校驗,其主要分為以下幾種類型:
名稱 | Type | 描述 | 說明 | |
---|---|---|---|---|
Ack | 00000000b | 用于確認接收到的TLP數據包 | 默認值,不允許亂序處理 | |
Nak | 00010000 | 用于拒絕接收到的TLP數據包 | 允許接收者在當前請求沒有完成的時候,同時處理任何后續的請求 | |
|
(Type較多,后面來說) | 用于流量控制,P/NP/Cpl表示流控類型 | 允許接收者在當前請求沒有完成的時候,同時處理來自其他設備的請求 | |
MRInitFC1/MRInitFC2/MRUpdateFC | <0111/1111/1011>0xxxb | 用于流量控制,P/NP/Cpl表示流控類型 | 相當于是Relaxed Ordering和ID-based Ordering的并集,允許接收者在當前請求沒有完成的時候,同時處理任何的請求 | |
PM_* | 00100xxxb | 用于電源管理,告知對端當前的電源狀態 | ||
NOP | 00110001b | 用于保持鏈路活躍,防止鏈路被關閉 | ||
Data_Link_Feature | 00000010b | 用于告知對端當前鏈路的特性,如支持Scaled Flow Control | ||
Vendor-specific | 00110000b | 用于支持廠商自定義的DLLP,實現廠商特有功能 |
3.2.1. Ack/Nak
我們在TLP事務消息傳輸的里就提到過Ack和Nak消息,它們可以說是DLLP中最常用的消息了。功能顧名思義,Ack表示接收成功,Nak表示接收失敗,需要重傳。這兩個包的格式如下:
其中,AckNak_Seq_Num表示當前已經收到的最新的消息序號,所以和TCP類似,PCIe的Ack和Nak可以進行批量操作:無論是Ack還是Nak,當發送方收到這個消息之后,就可以將Retry Buffer中比這個序號老的消息全部移除了,所以Ack/Nak時只需要將最新的序號帶上即可。Ack/Nak的差別在于:如果是Nak,那么發送方在移除之后,需要對Retry Buffer中這個序號之后的消息全部進行重傳。
最后,DDLP的重傳是由次數限制的,默認閾值是4次。如果超過四次,就出觸發物理層開始重建(retrain)鏈路。如果依然失敗,就會將該鏈路關閉。
3.2.2. VC(Virtual Channel)與流量控制
在說TLP的時候,我們提到了PCIe的流量控制是通過將TC(Traffic Class)映射到VC(Virtual Channel),并且利用VC的信用機制來實現的。這里我們就一起來看看這個信用機制吧!
數據鏈路層中的信用額度管理有兩個重要的特點:
不同處理方式是的TLP消息有著單獨的信用額度管理:Posted(P),Non-Posted(NP)和Completion(Cpl)。這三種消息的信用額度是獨立的,互不影響。
每個VC都有著自己的獨立的信用額度管理,而不是Link。也就是說,如果一個Link上有多個VC,那么每個VC都需要單獨的初始化和更新。參與流量控制的消息有很多,主要有三類,每一類有三個變種(N/NP/Cpl),我們的流量控制也主要分三步,其細節和統一的消息格式如下:
InitFC1-P/NP/Cpl:接收端設備使用此消息向發送端發起初始化流量控制的流程,并初始化信用額度,這是第一步。這個消息有接收端發起的原因是因為,不同的接收端能力不同,所以應該由接收端根據自己的能力,比如緩存的大小,來決定信用額度的大小。
InitFC2-P/NP/Cpl:用于發送端向接收端確認InitFC1的消息,這是第二步。這個消息中會帶有從第一步接收到的信用信息,但是它會被接收端忽略,并沒有什么用。另外,這個消息發送之后,發送端將不會再理會任何后續的InitFC1消息了。
UpdateFC-P/NP/Cpl:用于在信用額度初始化完成之后,接收端向發送端對信用額度進行更新。
這個消息中各個字段含義如下:
Type:消息ID,映射如下:
Type | Id | |||
---|---|---|---|---|
InitFC1-P | 0100b | |||
InitFC1-NP | 0101b | |||
InitFC1-Cpl | 0110b | |||
InitFC2-P | 1100b | |||
InitFC2-NP | 1101b | |||
InitFC2-Cpl | 1110b | |||
UpdateFC-P | 1000b | |||
UpdateFC-NP | 1001b | |||
UpdateFC-Cpl | 1010b |
VC ID(v[2:0]):Virtual Channel的Id,Id一共有3位,代表8個VC。
HdrFC:TLP頭部的Credit數量。在發送時,一個TLP頭對應著一個Header Credit,不論該TLP的大小如何。
DataFC:TLP數據部分的Credit數量。一個 DW(Double Word,雙字,即4字節)對應著一個Data Credit。
舉個例子,我們假設有一個64位地址的內存的寫請求,數據長度為128字節,那么我們會需要發送一個4 DW的TLP頭,加上128字節的Payload,和一個1 DW可選的TLP Digest,所以我們一共最多消耗1個Header Credit,和 (128 + 4) / 4 = 33個Data Credit。
然后,為了保證發送方正常的消息發送,當接收方處理完部分消息后(或者一些特殊情況后),就會根據其當前緩存的大小,向發送方發送UpdateFC消息,告訴發送方,接收方的信用額度還剩下多少。另外,除了這種情況,接收方還會定時的向發送方上報自己的信用額度(最長間隔30us),這么做的原因是為了避免意外情況,如CRC校驗出錯,導致信用額度上報丟失,從而導致發送方停止發送消息的問題。
最后,數據鏈路層還支持Scaled Flow Control,即信用額度的數量可以是2的冪次方,這樣就可以管理更大的信用額度了:
為了幫助理解,我們舉一個例子:
注意:如果查看原始的包,在計算時需要注意,HdrFC和DataFC都沒有對其到字節上,所以記得做好位運算。
首先,PCIe的Endpoint會向Switch發送如下三條消息來進行流控初始化:
當Switch收到這個消息后,也會向Endpoint發送三條類似的消息,進行反向的初始化。因為流程類似,從這里開始,之后Switch向Endpoint發送的反向流程我們就忽略了。
Switch收到了InitFC1 DLLP后,會使用InitFC2 DLLP進行確認:
到此,等兩邊InitFC2的消息交換完畢之后,初始化就完成了!
3.3. 數據鏈路層小結
最后為了幫助理解,我們再來看一下數據鏈路層的整體架構:
到這里,數據鏈路層上和數據傳輸相關的核心內容就都介紹完了!數據鏈路層中其實還有很多其他的內容,比如Link的初始化,狀態機,電源管理,和Vendor-specific DLLP等等,這些內容我們這里就不詳細介紹了,有興趣的讀者可以自行查閱PCIe Spec [1]。
4. 小結
好了,由于篇幅原因,我們這一篇就先到這里。這一篇中我們介紹了PCIe的協議棧,并且詳細的介紹了事務層(Transaction Layer)和數據鏈路層(Data Link Layer)是如何工作的,包括事務的分類,各種消息的格式,數據鏈路層的作用和封裝,以及PCIe基于TC和VC的流控機制。
在下一篇中,我們會繼續介紹PCIe協議棧中遺留的部分 —— 物理層(Physical Layer)。
審核編輯:劉清
-
寄存器
+關注
關注
31文章
5421瀏覽量
123288 -
Cache
+關注
關注
0文章
129瀏覽量
28904 -
PCIe
+關注
關注
16文章
1322瀏覽量
84656 -
CRC校驗
+關注
關注
0文章
84瀏覽量
15497 -
TLP
+關注
關注
0文章
34瀏覽量
15938
原文標題:PCIe協議棧,事務層和數據鏈路層
文章出處:【微信號:Rocker-IC,微信公眾號:路科驗證】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
關于PCIe通信問題
單片機和投影儀之間可以通過網絡通信方式通信嗎?
如何實現兩個處理器之間的通信
PCIe物理層實現了一對收發差分對,可以實現全雙工的通信方式

基于FPGA的PCIe設備如何才能滿足PCIe設備的啟動時間的要求?

簡談PCIe的軟件配置方式

PLC與PLC之間的通信方式設置
聊聊PCIe設備在系統如何發現與訪問?
基于FPGA的PCIE通信測試

評論