天天看點

通路PCIe配置空間using Intel Chipsets通路PCIe配置空間using Intel Chipsets

通路PCIe配置空間using Intel Chipsets

     目前用于通路PCIe配置空間寄存器的方法需要追溯到原始的PCI規範。為了發起PCI總線配置周期,Intel實作的PCI規範使用IO空間的CF8h和CFCh來分别作為索引和資料寄存器,這種方法可以通路所有PCI裝置的255 bytes配置寄存器。Intel Chipsets目前仍然支援這種通路PCI配置空間的方法。

    PCIe規範在PCI規範的基礎上,将配置空間擴充到4K bytes,至于為什麼擴充到4K,具體可以參考PCIe規範,這些功能都需要配置空間。原來的CF8/CFC方法仍然可以通路所有PCIe裝置配置空間的頭255 bytes,但是該方法通路不了剩下的(4K-255)配置空間。怎麼辦呢?Intel提供了另外一種PCIe配置空間通路方法。Intel Chipset通過将配置空間映射到記憶體位址空間,PCIe配置空間可以像對映射範圍内的記憶體進行read/write一樣來通路了。這種映射是由北橋晶片來完成的,但是不同晶片的映射方式也是不同的。

1、CF8h/CFCH Method

    Intel Chipsets使用IO空間的CF8h/CFCh位址來通路PCI裝置的配置寄存器,該方法同樣可以通路PCIe裝置的頭255配置寄存器。

通路PCIe配置空間using Intel Chipsets通路PCIe配置空間using Intel Chipsets

    為了對已知PCI裝置發起一個PCI總線配置周期,軟體必須執行以下步驟:

  1. PCI裝置的總線号必須被填寫到IO位址CF8h的[23:16] bits
  2. PCI裝置的裝置号必須被填寫到IO位址CF8h的[15:11] bits
  3. PCI裝置的功能号必須被填寫到IO位址CF8h的[10:8] bits
  4. 需要通路的寄存器雙字位址必須被填寫到IO位址CF8h的[7:2] bits
  5. CF8h的最高位為配置位,該位必須設定為1
  6. 對于寫操作,将裝置的特定資訊組合成一個雙字(4bytes)後,寫到CFCh位址
  7. 對于讀操作,将裝置的特定資訊組合成一個雙字後,把資料從CFCh讀回來

    當執行6或者7步驟時,相應的PCI配置read/write cycle被Created by Intel Chipset,并在需要時傳遞到整個系統。在步驟4配置需要讀寫的寄存器位址時,該空間隻有6位,也就說隻有64個位址可寫,但是PCI配置空間不是256嗎?别急,記得是雙字位址,一個Dword=4 bytes,也就是說4 * 64 = 256,剛好,不是嗎?

2、Memory Mapped Method

    PCIe規範為每個PCIe裝置添加了更多的配置寄存器,空間為4K,盡管CF8h/CFCh方法仍然能夠通路lower 255 bytes,但是必須提供另外一種方法來通路剩下的4K range寄存器。Intel的解決方案是使用了預留256MB記憶體位址空間,對這段記憶體的任何通路都會發起PCI 配置cycle。但是為什麼是256MB???聽我慢慢解釋給大家聽:猶豫4K的配置空間是directly mapped to memory的,那麼PCIe規範必須保證所有的PCIe裝置的配置空間占用不同的記憶體位址,按照PCIe規範,支援最多256個buses,每個Bus支援最多32個PCI devices,每個device支援最多8個function,也就是說:占用記憶體的最大值為:256 * 32 * 8 * 4K = 256MB。

    這段256MB的記憶體區将根據intel chipset的不同,可以映射到系統記憶體映射範圍内的任何位置,一般北橋晶片都會有一個寄存器來指明PCI配置空間的記憶體映射位址,它叫PCIe Configuration Register Base Address Register (BAR),如下圖:

通路PCIe配置空間using Intel Chipsets通路PCIe配置空間using Intel Chipsets

    當軟體通路指定PCIe裝置的配置寄存器時,必須正确計算該寄存器映射到記憶體的具體位址,那麼怎麼計算呢,參考上圖我們可以知道,busNo=0,deviceNo=0,funcNo=0的位址剛好是BAR,一條總線占用的最大空間計算如下:

    SIZE_PER_BUS = 4K * 32 * 8 = 256K = 1M = 100000h

    SIZE_PER_DEVICE = 4K * 8 = 8000h

    SIZE_PER_FUNC = 4K = 1000h

    通路總線号為busNo,裝置号為DevNo,功能号為funcNo的offset寄存器的計算公式是:

    Memory Address = PCIe Configuration Register Base Address Register (BAR)

                                    + busNo * SIZE_PER_BUS

                                    + devNo * SIZE_PER_DEVICE

                                    + funcNo * SIZE_PER_FUNC

                                    + offset

    For example, to access the following configuration register:

    • PCI Express Configuration Register F0000000h

    • Bus Number 15h

    • Device Number 00h

    • Function Number 05h

    • Register Offset 84h

Memory Address = F0000000h + 15h * 100000h + 00h * 8000h  + 05h * 1000h + 84h

                        = F1505084h

    現在我們可以從已知的busNo,devNo,funcNo和offset來計算映射後的記憶體位址,那麼反過來,給定的記憶體位址,我們想知道這個位址的busNo, devNo, funcNo和offset資訊,可以嗎?當然可以,計算公式如下:

    busNo = (Memory Address - BAR) / SIZE_PER_BUS;

    devNo = (Memory Address - BAR - busNo * SIZE_PER_BUS) / SIZE_PER_DEVICE;

    funcNo = (Memory Address - BAR - busNo * SIZE_PER_BUS 

                    - devNo * SIZE_PER_DEVICE) / SIZE _PER_FUNC;

    offset = Memory Address - BAR - busNo * SIZE_PER_BUS - devNo * SIZE_PER_DEVICE

                    - funcNo * SIZE_PER_FUNC;

    又或offset = Memory Address & 0x0FFFh;(為什麼是0x0FFFh?自己想想啦)

    想起來了麼?是以PCIe的配置空間大小就是4K啊。

3、晶片組的異同

    上面說的BAR,也就是PCI配置空間寄存器映射到記憶體的基位址寄存器,在intel chipset中的實作方式也千差萬别。在前期的intel chipset中,該寄存器被包含在晶片組(MCH ,GMCH)的記憶體控制器部分。

    另外,由于被PCIe配置空間占用的256M記憶體空間會屏蔽掉DRAM使用該段記憶體區,大部分的Intel Chipset允許BIOS來配置該空間大小,是以在實際應用中,一般就應用前面幾個總線号,BIOS通過檢測PCIe總線的擴充深度來動态設定該映射記憶體區的大小,比如PM965晶片組,如果配置軟體檢測系統使用不大于64的總線号,那麼該軟體将程式設計記憶體映射大小為64M,剩下的(256M-64M = 192M)留給DRAM。

4、PCIe配置空間的記憶體映射對32bit系統的影響

    由于PCIe配置空間占用了256M記憶體空間,而且該被占用空間對DRAM來說是不可用的,這意味着256M空間消失于系統記憶體,這在32bit系統中更為明顯。

    比如,在32 bit WINxp中,理論上可以通路到的記憶體是4G,如果4G空間都被DRAM給占用,由于PCIe的存在,被PCIe占用的那部分記憶體空間對OS來說是不可用的,莫名的消失了最多256M記憶體,這也是大部分Intel Chipset允許BIOS來配置該空間大小的原因。

    在64 bit 系統中,不存在這個問題,因為系統可以通路超過4G的記憶體空間,Intel Chipset會包含控制邏輯把該PCIe的記憶體映射到above 4G,這樣跟DRAM就沒有沖突。在64bit系統中,不可能使用2的64次方的記憶體吧。哈哈,總會沒有使用到的記憶體空間。

5、通路PCIe配置空間的C轉換代碼

//**********************************************************************

unsigned long PCIeBase = 0xF0000000UL;

unsigned long FinalAddress;

unsigned long Bus = 0;

unsigned long Device = 0;

unsigned long Function = 0;

unsigned long Register = 0;

//**********************************************************************

void Convert_to_Memory()

{

    FinalAddress = PCIeBase +

                        (Bus*0x100000UL) +

                        (Device*0x8000) +

                        (Function*0x1000) +

                        Register;

}

//**********************************************************************

void Convert_to_Register()

{

    Bus = (FinalAddress-PCIeBase) / (0x100000UL);

    Device = (FinalAddress-PCIeBase - (Bus*0x100000UL)) / (0x8000);

    Function = (FinalAddress-PCIeBase - (Bus*0x100000UL) -

    (Device*0x8000)) / (0x1000);

    Register = (FinalAddress) & (0x00000FFF)

}

以上是參考Intel文檔chipsets-pcie-config-reg-paper.pdf并根據自己的了解記錄下來的,若有錯誤的地方,請各位原諒。

繼續閱讀