【轉載】老男孩讀PCIe

老男孩讀PCIe

原文信息
來源: http://www.ssdfans.com/?p=3672

老男孩讀PCIe之一:從PCIe速度說起

從今天開始,老男孩要開始講PCIe了。對我來說,這是個很大的挑戰:首先,我自己本身,對PCIe並沒有做到胸有成竹,我的PCIe知識也只是停留在理論階段,我並沒有實際做過任何有關PCIe的東西;其次,我要把PCIe講得深入淺出,讓讀者輕易接受,我覺得很難,根本原因就是我還沒有做到胸有PCIe;最後,我的文章都會通過ssdfans公衆號推出(還沒有關注的同學,趕快關注),很多讀者都是PCIe高手,班門弄斧,我深感壓力。但儘管如此,我還是決定出發,我自己努力學習,盡我最大能力把我學到的東西分享給大家,也希望各位PCIe大拿們,幫忙斧正文章的錯誤,我希望不是我一個人,而是和各位大拿們一起,帶領大家攀上PCIe的高峯。

我是從事SSD工作的,爲什麼要講PCIe,原因很簡單,現在很多SSD都開始使用PCIe接口。那爲什麼SSD要用PCIe接口?因爲它快,比SATA快。它究竟有多快?我們今天首先從PCIe接口的速度開始我們的PCIe之旅。

PCIe發展到現在,從PCIe 1.0,PCIe 2.0,到現在的PCIe 3.0,速度一代比一代快。

img

圖1.1

Link Width這一行,我們看到X1,X2,X4…,這是什麼意思?這是指PCIe連接的通道數(Lane)。就像高速公路一樣,有單根道,有2根道的,有4根道的,不過像8根道或者更多道的公路不常見,但PCIe是可以最多32條道的。

img

圖1.2

兩個設備之間的PCIe連接,叫做一個Link,如下圖所示:

img

圖1.3

A到B之間是個雙向連接,車可以從A駛向B,同時車也可以從B駛向A,各行其道。兩個PCIe設備之間,有專門的發送和接收通道,數據可以同時往兩個方向傳輸,PCIe spec稱這種工作模式爲雙單工模式(dual-simplex),可以理解爲全雙工模式。

SATA是什麼工作模式呢?

img

圖1.4

和PCIe一樣,SATA也有獨立的發送和接收通道,但與PCIe工作模式不一樣:同一時間,只有一條道可以進行數據傳輸,也就是說,你在一條道上發送數據,另外一條道上不能接收數據,反之亦然。這種工作模式應該是半雙工模式。PCIe猶如我們的手機,雙方可以同時講話,而SATA就是對講機了,一個人在說話,另外一個人就只能聽不能說。

回到前面PCIe帶寬那張表,上面的帶寬,比如PCIe3.0x1,帶寬爲2GB/s,是指雙向帶寬,即讀寫帶寬。如果單指讀或者寫,該值應該減半,即1GB/s的讀速度或者寫速度。

我們來看看錶裏面的帶寬是怎麼算出來的。

PCIe是串行總線,PCIe1.0的線上比特傳輸速率爲2.5Gb/s,物理層使用8/10編碼,即8比特的數據,實際在物理線路上是需要傳輸10比特的,因此:

PCIe1.0 x 1的帶寬=(2.5Gb/s x 2(雙向通道))/ 10bit = 0.5GB/s

這是單條Lane的帶寬,有幾條Lane,那麼整個帶寬就0.5GB/s乘以Lane的數目。

PCIe2.0的線上比特傳輸速率在PCIe1.0的基礎上翻了一倍,爲5Gb/s,物理層同樣使用8/10編碼,所以:

PCIe2.0 x 1的帶寬=(5Gb/s x 2(雙向通道))/ 10bit = 1GB/s

同樣,有多少條Lane,帶寬就是1GB/s乘以Lane的數目。

PCIe3.0的線上比特傳輸速率沒有在PCIe2.0的基礎上翻倍,不是10Gb/s,而是8Gb/s,但物理層使用的是128/130編碼進行數據傳輸,所以:

PCIe3.0 x 1的帶寬=(8Gb/s x 2(雙向通道))/ 8bit = 2GB/s

同樣,有多少條Lane,帶寬就是2GB/s乘以Lane的數目。

由於採用了128/130編碼,128比特的數據,只額外增加了2bit的開銷,有效數據傳輸比率增大,雖然線上比特傳輸率沒有翻倍,但有效數據帶寬還是在PCIe2.0的基礎上做到翻倍。

這裏值得一提的是,上面算出的數據帶寬已經考慮到8/10或者128/130編碼,因此,大家在算帶寬的時候,沒有必要再考慮線上編碼的問題了。

和SATA單通道不同,PCIe連接可以通過增加通道數擴展帶寬,彈性十足。通道數越多,速度越快。不過,通道數越多,成本越高,佔用更多空間,還有就是更耗電。因此,使用多少通道,應該在性能和其他因素之間進行一個綜合考慮。單考慮性能的話,PCIe最高帶寬可達64GB/s,PCIe 3.0 x 32對應的帶寬,很恐怖的一個數據。不過,現有的PCIe接口SSD,一般最多使用4通道,如PCIe3.0x4,雙向帶寬爲8GB/s,讀或者寫帶寬爲4GB/s。

img

圖1.5

幾個GB/s的傳輸速度,讀寫小電影那是槓槓的。

在此,順便來算算PCIe3.0x4理論上最大的4K IOPS。PCIe3.0x4理論最大讀或者寫的速度爲4GB/s,不考慮協議開銷,每秒可以傳輸4GB/4K個4K大小的IO,該值爲1M,即理論上最大IOPS爲1000K。因此,一個SSD,不管你底層用什麼介質,flash還是3d xpoint,接口速度就這麼塊,最大IOPS是不可能超過這個值的。

PCIe是從PCI發展過來的,PCIe的”e”是express的簡稱,快的意思。PCIe怎麼就能比PCI(或者PCI-X)快呢?PCIe在物理傳輸上,跟PCI有着本質的區別:PCI使用並口傳輸數據,而PCIe使用的是串口傳輸。我PCI並行總線,單個時鐘週期可以傳輸32bit或者64bit,怎麼就比不了你單個時鐘週期傳輸1個bit數據的串行總線呢?

在實際時鐘頻率比較低的情況下,並口因爲可以同時傳輸若干比特,速率確實比串口快。隨着技術的發展,數據傳輸速率要求越來越快,要求時鐘頻率也越來越快,但是,並行總線時鐘頻率不是想快就能快的。

img

圖1.6

在發送端,數據在某個時鐘沿傳出去(左邊時鐘第一個上升沿),在接收端,數據在下個時鐘沿(右邊時鐘第二個上升沿)接收。因此,要在接收端能正確採集到數據,要求時鐘的週期必須大於數據傳輸的時間(從發送端到接收端,flight time)。受限於數據傳輸時間(該時間還隨着數據線長度的增加而增加),因此時鐘頻率不能做得太高。另外,時鐘信號在線上傳輸的時候,也會存在相位偏移(clock skew ),影響接收端的數據採集;還有,並行傳輸,接收端必須等最慢的那個bit數據到了以後,才能鎖住整個數據 (signal skew)。

PCIe使用串行總線進行數據傳輸就沒有這些問題。它沒有外部時鐘信號,它的時鐘信息通過8/10編碼或者128/130編碼嵌入在數據流,接收端可以從數據流裏面恢復時鐘信息,因此,它不受數據在線上傳輸時間的限制,你導線多長都沒有問題,你數據傳輸頻率多快也沒有問題;沒有外部時鐘信號,自然就沒有所謂的clock skew問題;由於是串行傳輸,只有一個bit傳輸,所以不存在signal skew問題。但是,如果使用多條lane傳輸數據(串行中又有並行,哈哈),這個問題又回來了,因爲接收端同樣要等最慢的那個lane上的數據到達才能處理整個數據。

老男孩讀PCIe之二:PCIe拓撲結構

“計算機網絡的拓撲結構是引用拓撲學中研究與大小、形狀無關的點、線關係的方法,把網絡中的計算機和通信設備抽象爲一個點,把傳輸介質抽象爲一條線,由點和線組成的幾何圖形就是計算機網絡的拓撲結構。”

計算機網絡的最主要的拓撲結構有總線型拓撲、環形拓撲、樹形拓撲、星形拓撲、混合型拓撲以及網狀拓撲。

PCI採用的是總線型拓撲結構,一條PCI總線上掛着若干個PCI終端設備或者PCI橋設備,大家共享該條PCI總線,哪個人想說話,必須獲得總線使用權,然後才能發言。下面是一個基於PCI的傳統計算機系統:

在這裏插入圖片描述

北橋下面的那根PCI總線,掛載了以太網設備、SCSI設備、南橋以及其他設備,他們共享那條總線,某個設備只有獲得總線使用權才能進行數據傳輸。

而PCIe則採用樹形拓撲結構,一個簡單而又典型的PCIe拓撲結構如下:

在這裏插入圖片描述

整個PCIe拓撲結構是一個樹形結構,Root Complex(RC)是樹的根。RC爲CPU代言,與整個計算機系統其它部分通訊,比如CPU通過它訪問內存,通過它訪問PCIe系統中的設備。

CPU像皇上一樣高高在上,而RC好比皇上身邊當紅的太監,皇上想叫下面的人做點事情,通過太監傳達;下面的人也是通過太監,向皇上反應一些情況。不過,這個太監不尋常,它是有根(root)的。

RC的內部實現很複雜,PCIe Spec也沒有規定RC該做什麼,還是不該做什麼。我們也不需要知道那麼多,只需清楚:它一般實現了一條內部PCIe總線(BUS 0),以及通過若干個PCIe bridge,擴展出一些PCIe Port,如下圖所示:

在這裏插入圖片描述

PCIe Endpoint,就是PCIe終端設備,比如PCIe SSD,PCIe網卡等等,而Legacy Endpoint,接口是PCIe,但是內部的行爲卻和傳統的PCI或者PCI-x一樣(比如支持IO空間)。這些Endpoint可以直接連在RC上,也可以通過Switch連到PCIe總線上。Switch用於擴展鏈路,提供更多的端口用以連接Endpoint。拿USB打比方,我們計算機主板上提供的USB口有限,如果你要連接很多USB設備,比如無線網卡、無線鼠標、USB攝像頭、USB打印機、U盤等等,這時候不夠用,怎麼辦?我會去找馬雲,向它買個USB HUB,下面這個就不錯:

在這裏插入圖片描述

Switch擴展了PCIe端口,靠近RC的那個端口,我們叫上游端口(upstream port),而分出來的其他端口,我們叫下游端口(downstream port)。一個Switch只有一個上游端口,可以擴展出若干個下游端口。下游端口可以直接連接Endpoint,也可以連接Switch,擴展出更多的PCIe端口。

在這裏插入圖片描述

對每個Switch來說,它下面的Endpoint或者Switch,都是歸他管的:上游下來的數據,它需要甄別數據是傳給它下面哪個設備,然後進行轉發;下面設備向RC傳數據,也要通過Switch代爲轉發的。因此,Switch的作用就是擴展PCIe端口,併爲掛在它上面的設備(endpoint 或者switch)提供路由和轉發服務。

每個Switch內部,也是有一根內部PCIe總線的,然後通過若干個Bridge,擴展出若干個下游端口,如下圖所示:

在這裏插入圖片描述

最後小結一下:

PCIe採用的是樹形拓撲結構,RC是樹的根,或者主幹,它爲CPU代言,與PCIe系統其它部分通訊,一般爲通訊的發起者;Switch是樹枝,樹枝上有葉子(Endpoint),也可節外生枝,Switch上連Switch,歸根結底,是爲了連接更多的Endpoint。Switch爲它下面的Endpoint或Switch提供路由轉發服務;Endpoint是樹葉,諸如SSD,網卡,顯卡等等,實現某些特定功能(function)。我們還看到有所謂的Bridge,用以將PCIe總線轉換成PCI總線,或者反過來,不是我們要講的重點,忽略之。PCIe與採用總線共享式通訊方式的PCI不同,PCIe採用點到點(Endpoint to Endpoint)通訊方式,每個設備獨享通道帶寬,速度和效率都比PCI好。

最後,以一個實際的計算機系統例子結束本文:

在這裏插入圖片描述

老男孩讀PCIe之三:PCIe分層結構

絕大多數的總線或者接口,都是採用分層實現的。PCIe也不例外,它的層次結構如下:

在這裏插入圖片描述

PCIe定義了下三層(彩色部分):事務層(Transaction Layer),數據鏈路層(Data Link Layer)和物理層(Physical Layer),每層職能是不同的,且下層是爲上層服務的。分層設計的一個好處:如果層次分得夠好,接口版本升級時,硬件設計可能只需要改動某一層,其它層次可以保持不動。

PCIe傳輸的數據從上到下,都是以packet的形式傳輸的,每個packet都是有其固定的格式的。

事務層的主要職責是創建(發送)或者解析(接收)TLP (Transaction Layer packet),流量控制,QoS,事務排序等。

數據鏈路層的主要職責是創建(發送)或者解析(接收)DLLP(Data Link Layer packet),Ack/Nak協議(鏈路層檢錯和糾錯),流控,電源管理等。

物理層的主要職責是處理所有的Packet數據物理傳輸,發送端數據分發到各個Lane傳輸(stripe),接收端把各個Lane上的數據彙總起來(De-stripe),每個Lane上加擾(Scramble,目的是讓0和1分佈均勻,去除信道的電磁干擾EMI)去擾(De-scramble),以及8/10或者128/130編碼解碼,等等。

今天只講個大概,這三層以後還會專門細講。這裏先貼個這三層的細節圖,自己看:

在這裏插入圖片描述

數據從上到下,一層一層打包,上層打包完的數據,作爲下層的原始數據,再打包。就像人穿衣服一樣,穿了內衣穿襯衫,穿了襯衫穿外套。

在這裏插入圖片描述

紅色的是TLP的格式,Data是事務層上層給的數據,事務層給它頭上加個Header,然後尾巴上再加個CRC校驗,就構成了一個TLP;這個TLP下傳到數據鏈路層,又被數據鏈路層頭上加了個包序列號,尾巴再加個CRC校驗,構成一個DLLP;然後DLLP下傳到物理層,頭上加個Start,尾巴加個End符號,把這些數據分派到各個Lane上,然後每個Lane上加擾碼,經8/10或128/130編碼,最後通過物理傳輸介質傳輸給接收方。

接收方物理層是最先接收到這些數據的,然後執行逆操作;在數據鏈路層,校驗序列號和LCRC,如果沒錯,剝掉序列號和LCRC,往事務層走;如果校驗出差,通知對方重傳;在事務層,校驗ECRC,有錯,數據拋棄,沒錯,去掉ECRC,獲得數據。整個過程猶如脫衣睡覺,外套脫了,襯衫脫了,內衣也脫了,光溜溜鑽進被窩。

在這裏插入圖片描述

和PCI數據裸奔不同,PCIe的數據是穿有衣服的。PCIe數據以packet的形式傳輸,比起PCI冷冰冰的數據,PCIe的數據是鮮活有生命的。

每個Endpoint都需要實現這三層,每個Switch的每個Port也是需要實現這三層的:

在這裏插入圖片描述

上圖中,如果RC要與EP1通信,中間要經歷怎樣的一個過程?

在這裏插入圖片描述

如果把前述的數據發送和接收過程,我們通俗的叫做穿衣脫衣,那麼,RC與EP1數據傳輸過程中,則存在好幾次這樣穿衣脫衣過程:RC跟數據穿好衣服,發送給Switch的上游端口,A爲了知道該筆數據發送給誰,就需要脫掉該數據的衣服,找到裏面的地址信息。衣服脫光後,Switch發現它是往EP1的,又幫它換了身新衣服,發送給端口B。B又不嫌麻煩的脫掉它的衣服,換上新衣服,最後發送給EP1。

Switch的主要功能是轉發數據,爲什麼還需要實現事務層?Switch必須實現這三層,因爲數據的目的地信息是在TLP中的,如果不實現這一層,就無法知道目的地址,也就無法實現數據尋址路由。

cross

老男孩讀PCIe之四:TLP類型

Host與PCIe設備之間,或者PCIe設備與設備之間,數據傳輸都是以Packet形式進行的。事務層根據上層(軟件層或者應用層)請求(Request)的類型、目的地址和其它相關屬性,把這些請求打包,產生TLP,也就是Transaction Layer Packet。然後這些TLP往下,經歷數據鏈路層,物理層,最終到達目標設備。

根據軟件層的不同請求,事務層產生四種不同的TLP請求:

  1. Memory
  2. IO
  3. Configuration
  4. Message

前三種分別用於訪問內存空間、IO空間、配置空間,這三種請求在PCI或者PCI-X時代就有了;最後的Message請求是PCIe新加的。在PCI或者PCI-X時代,像中斷、錯誤以及電源管理相關信息,都是通過邊帶信號(sideband signal)進行傳輸的,但PCIe幹掉了這些邊帶信號線,所有的通訊都是走帶內信號,即通過Packet傳輸,因此,過去一些由邊帶信號線傳輸的數據,比如中斷信息、錯誤信息等,現在就交由Message來傳輸了。

我們知道,一個設備的物理空間,可以通過內存映射(Memory map)的方式映射到Host的主存,有些空間還可以映射到Host的IO空間(如果Host存在IO空間的話)。但新的PCIe設備(區別於Legacy PCIe設備)只支持內存映射,之所以還存在訪問IO空間的TLP,完全是爲了照顧那些老設備。以後IO映射的方式會逐漸取消,爲減輕學習壓力,我們以後看到IO 相關的東西,大可或略之。

所有的配置空間(Configuration)的訪問,都是Host發起的,確切的說是RC發起的,往往只在上電枚舉和配置階段會發起Configuration的訪問,這樣的TLP很重要,但不是常態; Message也是一樣,只有有中斷,或者有錯誤等情況下,纔會有Message TLP,屬非主流。PCIe線上主流傳輸的是Memory訪問相關的TLP,Host與device,或者device與device之間,數據都是在彼此的Memory之間(拋掉IO)交互,因此,這種TLP是我們最常見的。

這四種請求,如果需要對方響應的,我們叫做Non-Posted的TLP;如果不期望對方給響應的,我們稱之爲Posted TLP。Post,有”郵政”的意思,我們只管把信投到郵箱,能不能到達對方,就取決於郵遞員了。Posted TLP,就是不指望對方回覆(信能不能收到都是個問題);Non-Posted TLP,就是要求對方務必回覆。

哪些TLP是Posted,哪些又是non-posted的呢?像Configuration和IO訪問,無論讀寫,都是Non-posted的,這樣的請求必須得到設備的響應;Message TLP是Posted;Memory Read必須是Non-posted,我讀你數據,你不返回數據(返回數據也是響應),那肯定不行的。所以,Memory Read必須得到響應。而Memory Write是Posted,我數據傳給你,無需回覆,這樣Host或者Device可以不等對方回覆,趁早把下一筆數據寫下去,這樣一定程度上提高了寫的性能。有人會擔心如果沒有得到對方的響應,發送者就沒有辦法知道數據究竟有沒有成功寫入,就有丟數據的風險。雖然這個風險存在(概率很小),但數據鏈路層提供了ACK/NAK機制,一定程度上能保證TLP正確交互,因此能很大程度減小數據寫失敗的可能。

Request Type Non-Posted or Posted
Memory Read Non-Posted
Memory Write Posted
Memory Read Lock Non-Posted
IO Read Non-Posted
IO Write Non-Posted
Configuration Read (Type 0 and Type 1) Non-Posted
Configuration Write (Type 0 and Type 1) Non-Posted
Message Posted

所以,只要記住只有Memory Write和Message兩種TLP是Posted就可以了。

Memory Read Lock是歷史的遺留物,Native PCIe設備已經拋棄了這個,存在的意義完全是爲了兼容Legacy PCIe設備。和IO一樣,我們以後也忽略。能不看的就不看,PCIe東西本來就多,不要被這些過時沒用的東西擋着我們學習的道路。

Configuration一欄,看到有Type 0和Type 1。我們在之前的拓撲結構中,看到除了Endpoint之外,還有Switch,他們都是PCIe設備,但配置種類不同,因此用Type 0和Type 1區分。

Request Type Non-Posted or Posted
Memory Read Non-Posted
Memory Write Posted
Configuration Read (Type 0 and Type 1) Non-Posted
Configuration Write (Type 0 and Type 1) Non-Posted
Message Posted

這樣,Request TLP是不是清爽點?

對Non-Posted的Request,是一定需要對方響應的,對方是通過返回一個Completion TLP來作爲響應的。對Read Request,響應者通過Completion TLP返回請求者所需的數據,這種Completion TLP包含有效數據;對Write Request(現在只有Configuration Write了)來說,響應者通過Completion TLP告訴請求者執行狀態,這樣的Completion TLP不含有效數據。

因此,PCIe裏面所有的TLP = Request TLP + Completion TLP。

TLP Packet Type Abbreviated Name
Memory Read MRd
Memory Write MWr
Configuration Read(Type 0 and Type 1) CfgRd0, CfgRd
Configuration Write(Type 0 and Type 1) CfgWr0,CfgWr1
Message Request with Data MsgD
Message Request without Data Msg
Completion with Data CplD
Completion without Data Cpl

看個Memory Read的例子:

img

例子中,Switch B下面的某個Endpoint想讀Host內存的數據,因此,它在事務層上生成一個Memory Read TLP,該MRd一路向上,翻過B,越過A,最終到達RC。RC收到該Request,就到內存中取該Endpoint所需的數據,RC通過Completion with Data TLP(CplD)返回數據,原路返回,直到Endpoint。

一個TLP,最多隻能攜帶4KB有效數據,因此,上例子,如果Endpoint需要讀16KB的數據,RC必須返回4個CplD給Endpoint。注意,Endpoint只需發1個MRd就可以了。

再看個Memory Write的例子:

在這裏插入圖片描述

該例子中,Host想往某個Endpoint寫入數據,因此RC在其事務層生成一個Memory Write TLP(要寫的數據在該TLP中),翻過A,越過B,直到目的地。前面說過Memory Write TLP是Posted的,因此,Endpoint收到數據後,是不需要返回Completion TLP(如果這個時候返回Completion TLP,反而是畫蛇添足)。

同樣的,由於一個TLP只能攜帶 4KB數據,因此Host想往Endpoint上寫入16KB數據,RC必鬚髮送4個MWr TLP。

老男孩讀PCIe之五:TLP結構

無論Request TLP,還是作爲迴應的Completion TLP,它們模樣都差不多:

在這裏插入圖片描述

TLP主要由三部分組成:Header,Data和CRC。TLP都是生於發送端的事務層(Transaction Layer),終於接收端的事務層。

每個TLP都有一個Header,跟動物一樣,沒有頭就活不了,所以TLP可以沒手沒腳,但不能沒有頭。事務層根據上層請求內容,生成TLP Header。Header內容包括髮送者的相關信息、目標地址(該TLP要發給誰)、TLP類型(前面提到的諸如Memory read,Memory Write之類的)、數據長度(如果有的話)等等。

Data Payload域,用以放有效載荷數據。該域不是必須的,因爲並不是每個TLP都必須攜帶數據的,比如Memory Read TLP,它只是一個請求,數據是由目標設備通過Completion TLP返回的。後面我們會整理哪些TLP需要攜帶數據,哪些TLP不帶數據的。前面也提到,一個TLP最大載重是4KB,數據長度大於4KB的話,就需要分幾個TLP傳輸。

ECRC(End to End CRC)域,它對之前的Header和Data(如果有的話)生成一個CRC,在接收端然後根據收到的TLP,重新生成Header和Data(如果有的話)的CRC,和收到的CRC比較,一樣則說明數據在傳輸過程中沒有出錯,否則就有錯。它也是可選的,可以設置不加CRC。

在這裏插入圖片描述

Data域和CRC域沒有什麼好說的,有花頭的是Header域,我們要深入其中看看。

一個Header大小可以是3DW,也可以是4DW。以4DW的Header爲例,TLP的Header長下面樣子:

在這裏插入圖片描述

紅色區域爲所有TLP Header公共部分,所有Header都有這些;其它則是跟具體的TLP相關。

稍微解釋一下:

Fmt:Format, 表明該TLP是否帶有數據,Header是3DW還是4DW;
Type:TLP類型,上一節提到的,Memory Read, Memory Write, Configuration Read, Configuration Write, Message和Completion,等等;
R: Reserved,爲0;
TC: Traffic
Class,TLP也分三六九等,優先級高的先得到服務。這裏是3比特,說明可以分爲8個等級,0-7,TC默認是0,數字越大,優先級越高;
Attr: Attrbiute, 屬性,前後共三個bit,先不說;
TH: TLP Processing Hints,先不說;
TD: TLP Digest,之前說ECRC可選,如果這個這個bit置起來,說明該TLP包含ECRC,接收端應該做CRC校驗;
EP: Poisoned data, 有毒的數據,遠離,哈哈;
AT: Address Type,地址種類,先不說;
Length: Payload數據長度,10個bit,最大1024,單位DW,所以TLP最大數據長度是4KB; 該長度總是DW的整數倍,如果TLP的數據不是DW的整數倍(不是4Byte的整數倍),則需要用到下面兩個域:Last DW BE 和 1st DW BE。

我覺得,到目前爲止,對於Header,我們只需知道它大概有什麼內容,沒有必要記住每個域是什麼。

這裏重點講講Fmt和Type,看看不同的TLP(精簡版的,Native PCIe設備所有)其Fmt和Type應該怎樣編碼:

TLP Fmt Type Comment
Memory Read Request 000=3DW,no data 001=4DW,no data 0 0000 3DW或者4DW是指Header大小,Memory Read不帶數據
Memory Write Request 010=3DW,with data 011=4DW,with data 0 0000 Memory Write必須帶數據
Configuration Type 0 Read Request 000=3DW,no data 0 0100 讀Endpoint的Configuration,不帶數據,Header總是3DW
Configuration Type 0 Write Request 010=3DW,with data 0 0100 寫Endpoint的Configuration,帶數據,Header總是3DW
Configuration Type 1 Read Request 000=3DW,no data 0 0101 讀Switch的Configuration,不帶數據,Header總是3DW
Configuration Type 1 Write Request 010=3DW,with data 0 0101 寫Switch的Configuration,帶數據,Header總是3DW
Message Request 001 = 4DW, no data 1 0rrr Message的Header總是4DW
Message Request with Data 011 = 4DW, with data 1 0rrr Message的Header總是4DW
Completion 000=3DW,no data 0 1010 Completion的Header總是3DW
Completion with Data 010=3DW,with data 0 1010 Completion的Header總是3DW

從上可以看出,Configuration和Completion 的TLP(以C打頭的TLP),其Header大小總是3字節;Message TLP的Header總是4字節;而Memory相關的TLP取決於地址空間的大小,地址空間小於4GB的,Header大小爲3DW,大於4GB的,Header大小則爲4DW。

上面介紹了幾個TLP Header的通用部分,下面分別介紹具體TLP的Header。

Memory TLP

有兩個重要的東西在前面沒有提到,那就是TLP的源和目標,即,該TLP是哪裏產生的,它要到哪裏去,它們都包含在Header裏面的。因爲不同的TLP類型,尋址方式不同,因此要具體TLP具體來看這兩個東西。

在這裏插入圖片描述

對一個PCIe設備來說,它開放給Host訪問的設備空間首先會映射到Host的內存空間,Host如果想訪問設備的某個空間,TLP Header當中的地址應該設置爲該訪問空間在Host內存的映射地址。如果Host內存空間小於4GB,則Memory讀寫TLP的Header大小爲3DW,大於4GB,則爲4DW。那是因爲,對4GB內存空間,32bit的地址用1DW就可以表示,該地址位於Byte8-11;而4GB以上的內存空間,需要2DW表示地址,該地址位於Byte8-15。

該TLP經過Switch的時候,Switch會根據地址信息,把該TLP轉發到目標設備。之所以能唯一的找到目標設備,那是因爲不同的Endpoint設備空間會映射到Host內存空間的不同位置。

關於TLP路由,後面還會專門講。

Memory TLP的目標是通過內存地址告知的,而源是通過”Requester ID”告知。每個設備在PCIe系統中都有唯一的ID,該ID由總線(Bus)、設備(Device)、功能(Function)三者唯一確定。這個後面也會專門講,這裏只需知道一個PCIe組成有唯一的ID,不管是RC,Switch還是Endpoint。

Configuration TLP

Endpoint和Switch的配置(Configuration)格式不一樣,分別爲Type 0和 Type 1來表示。配置可以認爲是一個Endpoint或者Switch的一個標準空間,這段空間在初始化時也需要映射到Host的內存空間。與設備的其他空間不同,該空間是標準化的,即不管哪個廠家生產的設備,都需要有這麼段空間,而且哪個地方放什麼東西,都是協議規定好的,Host按協議訪問這部分空間。由於每個設備ID唯一,而其Configuration又是固定好的,因此,Host訪問PCIe設備的配置空間,只需指定目標設備的ID就可以了,不需要內存地址。

下面是訪問Endpoint的配置空間的TLP Header (Type 0):

在這裏插入圖片描述

Bus Number + Device + Function就唯一決定了目標設備; Ext Reg Number + Register Number相當於配置空間的偏移。找到了設備,然後指定了配置空間的偏移,就能找到具體想訪問的配置空間的某個位置。

Message TLP
Message TLP用以傳輸中斷、錯誤、電源管理等信息,取代PCI時代的邊帶信號傳輸。Message TLP的Header 大小總是4DW。

在這裏插入圖片描述

Message Code來指定該Message的類型,具體如下:

在這裏插入圖片描述

不同的Message Code,最後兩個DW的意義也不同,這裏不展開。

Completion TLP
有non-posted request TLP,纔有Completion TLP。有因纔有果。前面看到,Requester 的TLP當中都有Requester ID和Tag,來告訴接收者發起者是誰。那麼響應者的目標地址就很簡單,照抄發起者的源地址就可以了。因此,Completion TLP的Header如下:

在這裏插入圖片描述

Completion TLP,一方面,可以返回請求者的數據,比如作爲Memory或者Configuration Read的響應;另一方面,還可以返回該事務(Transaction)的狀態,因此,在Completion TLP的Header裏面有一個Completion Status,用以返回事務狀態:

在這裏插入圖片描述

老男孩讀PCIe之六:配置和地址空間

每個PCIe設備,有這麼一段空間,Host軟件可以讀取它獲得該設備的一些信息,也可以通過它來配置該設備,這段空間就叫做PCIe的配置空間。不同於每個設備的其它空間,PCIe設備的配置空間是協議規定好的,哪個地方放什麼內容,都是有定義的。PCI或者PCI-X時代就有配置空間的概念,那時的配置空間如下:

在這裏插入圖片描述

整個配置空間就是一系列寄存器的集合,其中Type 0是Endpoint的配置,Type 1是Bridge(PCIe時代就是Switch)的配置,都由兩部分組成:64 Bytes的Header+192Bytes的Capability結構,後者是設備告訴Host它有多牛逼,都會什麼絕活。

進入PCIe時代,PCIe能耐更大,192 Bytes不足以羅列它的絕活。爲了保持後向兼容,又要不把絕活落下,怎麼辦?很簡單,我擴展後者的空間,整個配置空間由256 Bytes擴展成4KB,前面256 Bytes保持不變:

在這裏插入圖片描述

PCIe有什麼能耐(Capability)我們不看,我們先挑軟柿子捏,先看看只佔64 Bytes的Configuration Header。

在這裏插入圖片描述

像Device ID,Vendor ID,Class Code和Revision ID,是隻讀寄存器,PCIe設備通過這些寄存器告訴Host軟件,這是哪個廠家的設備、設備ID是多少、以及是什麼類型的(網卡?顯卡?橋?)設備。

其它的我們暫時不看,我們看看重要的BAR(Base Address Register)。

對Endpoint Configuration(Type 0),提供了最多6個BAR,而對Switch(Type 1)來說,只有2個。BAR是幹什麼用的?

每個PCIe設備,都有自己的內部空間,這部分空間如果開放給Host(軟件或者CPU)訪問,那麼Host怎樣才能往這部分空間寫入數據,或者讀數據呢?

我們知道,CPU只能直接訪問Host內存(Memory)空間(或者IO空間,我們不考慮),不對PCIe等外設直接操作。怎麼辦?記得皇帝身邊那個有根的太監嗎?Root Complex,RC。RC可以爲CPU分憂。

解決辦法是:CPU如果想訪問某個設備的空間,由於它不能(或者不屑)親自跟那些PCIe外設打交道,因此叫太監RC去辦。比如,如果CPU想讀PCIe外設的數據,先叫RC通過TLP把數據從PCIe外設讀到Host內存,然後CPU從Host內存讀數據;如果CPU要往外設寫數據,則先把數據在內存中準備好,然後叫RC通過TLP寫入到PCIe設備。完美!

在這裏插入圖片描述

上圖例子中,最左邊虛線的表示CPU要讀Endpoint A的數據,RC則通過TLP(經歷Switch)數據交互獲得數據,並把它寫入到系統內存中,然後CPU從內存中讀取數據(紫色箭頭所示),從而CPU間接完成對PCIe設備數據的讀取。

具體實現就是上電的時候,系統把PCIe設備開放的空間(系統軟件可見)映射到內存空間,CPU要訪問該PCIe設備空間,只需訪問對應的內存空間。RC檢查該內存地址,如果發現該內存空間地址是某個PCIe設備空間的映射,就會觸發其產生TLP,去訪問對應的PCIe設備,讀取或者寫入PCIe設備。

一個PCIe設備,可能有若干個內部空間(屬性可能不一樣,比如有些可預讀,有些不可預讀)需要映射到內存空間,設備出廠時,這些空間的大小和屬性都寫在Configuration BAR寄存器裏面,然後上電後,系統軟件讀取這些BAR,分別爲其分配對應的系統內存空間,並把相應的內存基地址寫回到BAR。(BAR的地址其實是PCI總線域的地址,CPU訪問的是存儲器域的地址,CPU訪問PCIe設備時,需要把總線域地址轉換成存儲器域的地址。)

在這裏插入圖片描述

如上圖例子,一個Native PCIe Endpoint,只支持Memory Map,它有兩個不同屬性的內部空間要開放給系統軟件,因此,它可以分別映射到系統內存空間的兩個地方;還有一個Legacy Endpoint,它既支持Memory Map,還支持IO Map,它也有兩個不同屬性的內部空間,分別映射到系統內存空間和IO空間。

來個例子,看一下一個PCIe設備,系統軟件是如何爲其分配映射空間的。

在這裏插入圖片描述

上電時,系統軟件首先會讀取PCIe設備的BAR0,得到數據:

在這裏插入圖片描述

然後系統軟件往該BAR0寫入全1,得到:

在這裏插入圖片描述

BAR寄存器有些bit是隻讀的,是PCIe設備在出廠前就固定好的bit,寫全1進去,如果值保持不變,就說明這些bit是廠家固化好的,這些固化好的bit提供了這塊內部空間的一些信息:

怎麼解讀?低12沒變,表明該設備空間大小是4KB(2的12次方),然後低4位表明了該存儲空間的一些屬性(IO映射還是內存映射,32bit地址還是64bit地址,能否預取?做過單片機的人可能知道,有些寄存器只要一讀,數據就會清掉,因此,對這樣的空間,是不能預讀的,因爲預讀會改變原來的值),這些都是PCIe設備在出廠前都設置好的,提供給系統軟件的信息。

然後系統軟件根據這些信息,在系統內存空間找到這樣一塊地方來映射這4KB的空間,把分配的基地址寫入到BAR0:

在這裏插入圖片描述

從而最終完成了該PCIe空間的映射。一個PCIe設備可能有若干個內部空間需要開放出來,系統軟件依次讀取BAR1,BAR2。。。,直到BAR5,完成所有內部空間的映射。

上面主要講了Endpoint的BAR,Switch也有兩個BAR,今天不打算講,下節講TLP路由,再回過頭來講。繼續說配置空間。

前面說每個PCIe設備都有一個配置空間,其實這樣說是不準確的,而是每個PCIe設備至少有一個配置空間。一個PCIe設備,它可能具有多個功能(function),比如既能當硬盤,還能當網卡。每個功能對應一個配置空間。

在一個PCIe拓撲結構裏,一條總線下面可以掛幾個設備,而每個設備可以具有幾個功能,如下所示:

在這裏插入圖片描述

因此,在整個PCIe系統中,只要知道了Bus+Device+Function,就能找到對應的Function。尋址基本單元是功能(function),它的ID就由Bus+Device+Function組成 (BDF)。一個PCIe系統,可以最多有256條Bus,每條Bus上可以掛最多32個Device,而每個Device最多又能實現8個Function,而每個Function對應着4KB的配置空間。上電的時候,這些配置空間都是需要映射到Host的內存空間,因此,需要佔用內存空間是:256328*4KB =256MB。在這個動輒4GB、8GB內存的時代,256MB算不了什麼。

系統軟件是如何讀取Configuration空間呢?不能通過BAR中的地址,爲什麼?別忘了BAR是在Configuration中的,你首先要讀取Configuration,才能得到BAR。前面不是系統爲所有可能的Configuration預留了256MB內存空間嗎?系統軟件想訪問哪個Configuration,只需指定相應Function對應的內存空間地址,RC發現這個地址是Configuration映射空間,就會產生相應的Configuration Read TLP去獲得相應Function的Configuration。

再回想一下前面介紹的Configuration Read TLP的Header格式:

在這裏插入圖片描述

Bus Number + Device + Function就唯一決定了目標設備; Ext Reg Number + Register Number相當於配置空間的偏移。找到了設備,然後指定了配置空間的偏移,就能找到具體想訪問的配置空間的某個位置。

結束前,強調一下,只有RC才能發起Configuration的訪問請求,其他設備是不允許對別的設備進行Configuration讀寫的。

老男孩讀PCIe之七:TLP的路由

一個TLP,是怎樣經歷千山萬水,最後順利抵達目的地呢?

在這裏插入圖片描述

今天就以上圖的簡單拓撲結構爲例,討論一個TLP是怎樣從發起者到達接收者,即TLP路由問題。

PCIe共有三種路由方式:基於地址(Address)路由,基於設備ID(Bus number + Device number + Function Number)路由,還有就是隱式(Implicit)路由。

不同類型的TLP,其尋址方式也不同,下表總結了每種TLP對應的路由方式:

TLP類型 路由方式
Memory Read/Write TLP 地址路由
Configuration Read/Write TLP ID路由
Completion TLP ID路由
Message TLP 地址路由或者ID路由或者隱式路由

下面分別講講這幾種路由方式。

地址路由

前面提到,Switch負責路由和TLP的轉發,而路由信息是存儲在Switch的Configuration空間的,因此,很有必要先理解Switch的Configuration。

在這裏插入圖片描述

BAR0和BAR1沒有什麼好說,跟前一節講的Endpoint的BAR意義一樣,存放Switch內部空間在Host內存空間映射基址。

Switch有一個上游端口(靠近RC)和若干個下游端口,每個端口其實是一個Bridge,都是有一個Configuration的,每個Configuration描述了其下面連接設備空間映射的範圍,分別由Memory Base和Memory Limit來表示。對上游端口,其Configuration描述的地址範圍是它下游所有設備的映射空間範圍,而對每個下游端口的Configuration,描述了連接它端口設備的映射空間範圍。大家看看下面這張圖,理解一下我剛纔說的。(Range由Memory Base和Memory Limit限定)

在這裏插入圖片描述

前面我們看到,Memory Read 或者Memory Write TLP的Header裏面都有一個地址信息,該地址是PCIe設備內部空間在內存中的映射地址。

在這裏插入圖片描述

當一個Endpoint收到一個Memory Read或者Memory Write TLP,它會把TLP Header中的地址跟它Configuration當中的所有BAR寄存器比較,如果TLP Header中的地址落在這些BAR的地址空間,那麼它就認爲該TLP是發給它的,於是接收該TLP,否則就忽略。

在這裏插入圖片描述

當一個Switch上游端口收到一個Memory Read或者Memory Write TLP,它首先把TLP Header中的地址跟它自己Configuration當中的所有BAR寄存器比較,如果TLP Header當中的地址落在這些BAR的地址空間,那麼它就認爲該TLP是發給它的,於是接收該TLP(這個過程與Endpoint的處理方式一樣);如果不是,然後看這個地址是否落在其下游設備的地址範圍內(是否在memory base 和memory limit之間),如果是,說明該TLP是發給它下游設備的,因此它要完成路由轉發;如果地址不落在下游設備的地方範圍內,說明該TLP不是發給它下面設備的,因此不接受該TLP。

在這裏插入圖片描述

剛纔的描述是針對TLP從Upstream流到Downstream的路由。如果是TLP從下游往上走呢?

它首先把TLP Header中的地址跟它自己Configuration當中的所有BAR寄存器比較,如果TLP Header當中的地址落在這些BAR的地址空間,那麼它就認爲該TLP是發給它的,於是接收該TLP(跟前面描述一樣);如果不是,然後看這個地址是否落在其下游設備的地址範圍內(是否在memory base 和memory limit之間),如果是,這個時候不是接受,而是拒絕;相反,如果地址不落在下游設備的地方範圍內,Switch則把該TLP傳上去。

ID路由

在一個PCIe拓撲結構中,由ID = Bus number+Device number+Function Number(BDF)能唯一找到某個設備的某個功能。這種按設備ID號來尋址的方式叫做ID路由。Configuration TLP和Completion TLP(以C打頭的TLP)按ID路由,Message在某些情況下也是ID路由。

使用ID路由的TLP,其TLP Header中含有BDF信息:
在這裏插入圖片描述

當一個Endpoint收到一個這樣的TLP,它用自己的ID和收到TLP Header中的BDF比較,如果是給自己的,就收下TLP,否則就拒絕。

如果是一個Switch收到這樣的一個TLP,怎麼處理?我們再回頭去看看Switch的Configuration Header。

在這裏插入圖片描述

看三個寄存器:Subordinate Bus Number,Secondary Bus Number和Primary Bus Number,看下圖就知道這幾個寄存器是什麼意思:

在這裏插入圖片描述

對一個Switch來說,每個Port靠近RC(上游)的那根Bus叫做Primary Bus,其Number寫在其Configuration Header中的Primary Bus Number寄存器;每個Port下面的那根Bus叫做Secondary Bus,其Number寫在其Configuration Header中的Secondary Bus Number寄存器;對上游端口,Subordinate Bus是其下游所有端口連接的Bus 編號最大的那個Bus,Subordinate Bus Number寫在每個Port的Configuration Header中的Subordinate Bus Number寄存器。

當一個Switch收到一個基於ID尋址的TLP,首先檢查TLP中的BDF是否與自己的ID匹配,如匹配,說明該TLP是給自己的,收下;否則,則檢查該TLP中的Bus Number是否落在Secondary Bus Number和Subordinate Bus Number之間,如果是,說明該TLP是發給其下游設備的,然後轉發到對應的下游端口;如果其他情況,則拒絕這些TLP。

在這裏插入圖片描述

隱式路由

只有Message TLP才支持隱式路由。在PCIe總線中,有些Message是與RC通信的,RC是該TLP的發送者或者接收者,因此沒有必要明明白白的指定地址或者ID,而是採用”你懂的”的方式進行路由,這種路由方式爲隱式路由。Message TLP還支持地址路由和ID路由,但以隱式路由爲主。

Message TLP的Header總是4DW,如下圖所示:

在這裏插入圖片描述

Type字段,低三位,用rrr表示的,指明該Message的路由方式,具體如下:

在這裏插入圖片描述

當一個Endpoint收到一個Message TLP,檢查TLP Header,如果是RC的廣播Message(011b)或者該Message終結於它(100b),它就接受該Message。

當一個Switch收到一個Message TLP,檢查TLP Header,如果是RC的廣播Message(011b),則往它每個下游端口複製該Message然後轉發;如果該Message終結於它(100b),則接受該TLP;如果下游端口收到發給RC的message,往上游端口轉發便是。

上面說的是Message使用隱式路由的情況。如果是地址路由或者ID路由,Message TLP的路由跟別的TLP一樣,不贅述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章