1. 程式人生 > >PCIe實踐之路:BAR空間和TLP

PCIe實踐之路:BAR空間和TLP

上一篇文章中寫到每個PCIe的function都有自己的configuration space,其實就是配置暫存器了(這個當然是要有的了,不然軟體要怎麼玩?只不過PCIe的配置暫存器要通過tlp才能去訪問)。其實PCIe裝置是有自己獨立的一套內部空間,不僅僅是配置空間,包括每個裝置提供哪些I/O地址,memory地址。而BAR(Base Address Register)就是用來表徵這些地址空間的。

一、BAR暫存器和PCIe內部空間

關於地址相關的問題,搞清楚這三個地址之間的關係就可以了:

  1. 儲存器地址,就是CPU,DMA等裝置直接讀寫的地址。
  2. TLP中的地址。
  3. BAR空間地址。

如果兩兩組合的話,能夠形成三種關係,但是事實上,這三者之間的關係其實就兩部分:

  1. 儲存器地址和TLP地址欄位的關係。
  2. TLP地址欄位和BAR空間地址的關係。

解決這兩個問題,地址相關的問題就應該都清楚了。

1.1 BAR暫存器

首先要知道BAR有什麼用?通過BAR暫存器,我們首先知道這個基址對應的空間屬性,然後給這段空間分配一個基址(這個基址只是用來路由定址用的,不能和儲存器空間的地址搞混,很多軟體實現上會把兩個地址設定成一樣,但是本質上沒有任何關係,只是TLP定址的時候用的!)。這樣的話,TLP就能根據地址被路由到對應裝置的BAR空間中去。比如說現在有一個mem read request,如果路由地址(地址資訊包含在TLP中)是0x71000000,而有一個裝置func0的mem空間範圍是0x70000000~0x80000000,那麼這個TLP就會被這個func處理。從func0的0x71000000對應的地址讀取相應資料。

這就是TLP中的地址欄位和BAR空間的地址之間的關係。還有一個問題是關於儲存器地址和TLP地址欄位的關係,有個硬體單元非常重要,那就是ATU,見第二節。這裡詳細介紹一下BAR的配置問題。

BAR位置:

對於EP來講,配置空間的對映是這樣的:

2017-07-02 19-33-12螢幕截圖.png

從上圖中看到,BAR是從配置空間0x10到0x24的連續6個32位暫存器。關於BAR每個欄位的解釋可以參考DWC_pcie_reference[1]

BAR配置過程:

  1. 通過cfg write request向BAR地址寫入全1。
  2. 通過cfg read request讀取BAR。
  3. 根據讀取的BAR值進行如下判斷:

從高位開始讀取連續的1,說明這些位元位是可寫的,表徵該space的size,譬如讀到的BAR為0x11100000,那麼這個space的size為0x100000 bytes,同時由於第0位為0,表示memory BAR,否則為I/O BAR。bits[2:1]和bits[3]的含義如下圖所示

2017-07-02 19-46-10螢幕截圖.png

1.2 ATU(Address Translation Unit)

TLP中的地址哪裡來?ATU轉換過來的。這個問題就是這麼的簡單。ATU是什麼?是一個地址轉換單元,負責將一段儲存器域的地址轉換到PCIe匯流排域地址,除了地址轉換外,還能提供訪問型別等資訊,這些資訊都是ATU根據總線上的訊號自己做的,資料都打包到TLP中,不用軟體參與。軟體需要做的是配置ATU,所以如果ATU配置完成,並且能正常工作,那麼CPU訪問PCIe空間就和訪問本地儲存器空間方法是一樣的,只要讀寫即可。

這就解釋了儲存器地址和TLP地址欄位的關係了。至此,地址相關的問題就解決了。

ATU配置舉例:以kernel 4.4中designware PCIe host驅動為例

static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
        int type, u64 cpu_addr, u64 pci_addr, u32 size)
{
    // 使用哪個ATU
    dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
              PCIE_ATU_VIEWPORT);
    // source地址(儲存器域)的低32位
    dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
    dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE);
    // space size
    dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
              PCIE_ATU_LIMIT);
    // 目標地址空間(PCIe匯流排地址)
    dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET);
    dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
    // 空間型別(mem or IO)
    dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
    // 使能ATU
    dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
}

二、TLP

TLP(Transaction Layer Packet)應該算是PCIe中最重要的概念了。可以說TLP是使用者程式和PCIe裝置互動的唯一渠道(edma和MSI本質上還是通過TLP)。TLP的構成如下圖所示,具體每個欄位的含義參見PCI_Express_Base_Specification_Revision_4.0.Ver.0.3第2.2節

2017-07-02 20-04-35螢幕截圖.png

因為軟體不需要顯示的配置TLP,所以這裡就沒有TLP的配置了,取而代之的是相關硬體的配置(譬如ATU)。這裡有個瞭解就行,等到除錯的時候就需要仔細的瞭解了。

這篇文章總結了PCIe裝置地址空間的知識,搞明白了這個之後應該能上手開始寫PCIe驅動了,至少能枚舉了。下篇就詳細介紹PCIe裝置列舉過程,也是對這些知識的一個應用。

下篇預告: PCIe裝置列舉

參考文獻:

[1] DWC_pcie_reference
[2] PCI_Express_Base_Specification_Revision_4.0.Ver.0.3