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

原創內容,轉載請注明:  [http://www.od-music.com]  謝謝!

每個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的內存空間,因此,需要占用內存空間是:256*32*8*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讀寫的。

?

?

?

引用

?

分類目錄 存儲, 技術文章.
掃一掃二維碼或者微信搜索公眾號ssdfans關注(添加朋友->點最下面的公眾號->搜索ssdfans),可以經??吹絊SD技術和產業的文章(SSD Fans只推送干貨)。
ssdfans微信群介紹
技術討論群 覆蓋2000多位中國和世界華人圈SSD以及存儲技術精英
固件、軟件、測試群 固件、軟件和測試技術討論
異構計算群 討論人工智能和GPU、FPGA、CPU異構計算
ASIC-FPGA群 芯片和FPGA硬件技術討論群
閃存器件群 NAND、3D XPoint等固態存儲介質技術討論
企業級 企業級SSD、企業級存儲
銷售群 全國SSD供應商都在這里,砍砍價,會比某東便宜20%
工作求職群 存儲行業換工作,發招聘,要關注各大公司招聘信息,趕快來
高管群 各大SSD相關存儲公司高管和創始人、投資人

想加入這些群,請微信掃描下面二維碼,或搜索nanoarchplus,加阿呆為微信好友,介紹你的昵稱-單位-職務,注明群名,拉你進群。SSD業界需要什么幫助,也可以找阿呆聊。