訪問PCIe配置空間using Intel Chipsets

http://blog.csdn.net/htyang725/article/details/8523205


訪問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配置寄存器。

    爲了對已知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設備的配置寄存器時,必須正確計算該寄存器映射到內存的具體地址,那麼怎麼計算呢,參考上圖我們可以知道,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並根據自己的理解記錄下來的,若有錯誤的地方,請各位原諒。


發佈了9 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章