1. 程式人生 > >怎樣訪問pcie整個4k的配置空間

怎樣訪問pcie整個4k的配置空間

ips bottom per str nal block top 2個 內存

眼下用於訪問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)

}

怎樣訪問pcie整個4k的配置空間