相关资料
现在基本上所有设备都使用PCIE,但PCIE是PCI的拓展,所以基础的PCI知识仍然必要,这里重点强调两者共同部分。
参考资料:
- PCIe扫盲系列博文连载目录篇
 - 深入PCI与PCIe之二:软件篇
 - 《Linux 那些事之我是PCI》
 - 《PCI+Express体系结构》
 - 《Linux设备开发详解》 第11章
 - 《PCI Express Base_r5_1》
 - PCI-SIG
 
相关书籍下载方式:
链接:https://pan.baidu.com/s/1zzWWt9ujVTr9oJSaJNP_mA
提取码:ahax 
测试工具
windows测试工具:
Linux测试工具
- lspci
 
Inbound:PCI域訪问存储器域 Outbound:存储器域訪问PCI域
RC訪问EP: RC存储器域->outbound->RC PCI域->EP PCI域->inbound->EP存储器域 EP訪问RC:EP存储器域->outbound->EP PCI域->RC PCI域->inbound->RC存储器域
Out即出去,发起訪问的一側,须要进行outbound,去訪问对端 In即进来,被訪问的一側,须要进行inbound,使得对端能够訪问
EP訪问RC演示样例(蓝色箭头): (1)首先,EP须要配置outbound,RC须要inbound(一般RC端不用配),这样就建立了EP端0x20000000到RC端0x50000000的映射 (2)在RC端改动0x50000000的内容,EP端能够看到对应的变化。从EP端读/写0x20000000和从RC端读/写0x50000000,结果是一样的
RC訪问EP演示样例(黑色箭头): (1)首先,RC端须要配置outbound(一般内核中配好),EP端须要inbound(0x5b000000 inbound到BAR2),这样就建立了RC端0x20100000(BAR2)到EP端0x5b000000的映射 (2)在EP端改动0x5b000000内存的内容,在RC端0x20100000能够看到对应的变化,从RC端读/写0x20100000和从EP端读/写0x5b000000,结果是一样的
PCI配置空间(软件重点)
因为PCI配置空间0x00-0x3F是必须的有的,0x40-0xFF可选支持,所以这里重点介绍0x00-0x3F的信息
PCI Agent(Type0)配置空间
PCIE设备通常将配置信息存储在EEPROM中,上电后会将EEPROM配置导入到配置空间;
驱动软件是如何读取配置空间
// Linux中pci读写配置空间系列函数#include <linux/pci.h>inline int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);inline int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);inline int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);inline int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);inline int pci_write_config_word(struct pci_dev *dev, int where, u16 val);inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);// 注:跟踪这部分代码,实际上调用的是对应总线的读写操作接口,对应的BDF寻址;int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn,int where, u8 *val);int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn,int where, u16 *val);int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn,int where, u32 *val);int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn,int where, u8 val);int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn,int where, u16 val);int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn,int where, u32 val);// 实际调用IO端口进行访问,详情参考《PCI代码导读》baiy@test:~$ sudo cat /proc/ioports | grep "PCI conf"0cf8-0cff : PCI conf1// arch/x86/direct.c中#define PCI_CONF1_ADDRESS(bus, devfn, reg) \(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \| (devfn << 8) | (reg & 0xFC))outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); // 配置地址u32 value = inl(0xCFC); // 读取配置
Linux下通过lspci查看pci详细信息
baiy:workspace$ sudo lspci -s 00:02.0 -vvv[sudo] password for baiy:00:02.0 VGA compatible controller: Intel Corporation Device 3ea5 (rev 01) (prog-if 00 [VGA controller])Subsystem: Intel Corporation Device 2074Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-Latency: 0, Cache Line Size: 64 bytesInterrupt: pin A routed to IRQ 132Region 0: Memory at 90000000 (64-bit, non-prefetchable) [size=16M]Region 2: Memory at 80000000 (64-bit, prefetchable) [size=256M]baiy:workspace$ sudo lspci -s 00:02.0 -xxx00:02.0 VGA compatible controller: Intel Corporation Device 3ea5 (rev 01)00: 86 80 a5 3e 07 04 10 00 01 00 00 03 10 00 00 0010: 04 00 00 90 00 00 00 00 0c 00 00 80 00 00 00 0020: 01 30 00 00 00 00 00 00 00 00 00 00 86 80 74 2030: 00 00 00 00 40 00 00 00 00 00 00 00 00 01 00 00...
ID相关(只读)
Device ID和Vendor ID: 没啥说的,厂家编号,系统软件只读,FPGA工程可配置。0xFFFF代表ID无效;
RevisionID:PCI设备的版本号;
SubsystemID和Subsystem VendorID:用户可在同一个FPGA上配置不同功能,使用这部分ID进行区分;
ClassCode 设备类型
ClassCode[23:0]由三部分组成:
- Base ClassCode:设备分类
 - Sub ClassCode:设备类型细分
 - Interface ClassCode:编程接口
 
PCI BAR0~5空间详解(重点)
基础部分:PCIe扫盲——基地址寄存器(BAR)详解
在FPGA例化时,会将使用的BAR空间地址映射到真实的PCI设备的物理内存上; 
每一个设备最多有 6组32位bar空间/3组64位bar空间 +1组option Rom空间,用户可选用哪些空间可用;
注:需要特别注意的是,软件对BAR的检测与操作(Evaluating)必须是顺序执行的,即先BAR0,然后BAR1,……,直到BAR5。当软件检测到那些被硬件设置为全0的BAR,则认为这个BAR没有被使用。
如何判断PCI/PCIE使用了32位bar空间,还是64位bar空间?
参考:PCI bar空间详解
bit0:表示设备寄存器是映射到memory(0)还是IO(1)空间。
bit1: reserved 0
bit2: 在base adress register for Memory 中0表示32位地址空间,1表示64位地址空间。
bit3:在memory BAR中用来表示该设备是否允许prefetch,1表示可以预取,0表示不可以预取。
其余的bit用来表示设备需要占用的地址空间大小。
// 内核解析 bar 空间的函数static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar){u32 mem_type;unsigned long flags;if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) {flags = bar & ~PCI_BASE_ADDRESS_IO_MASK;flags |= IORESOURCE_IO;return flags;}flags = bar & ~PCI_BASE_ADDRESS_MEM_MASK;flags |= IORESOURCE_MEM;if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)flags |= IORESOURCE_PREFETCH;mem_type = bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK;switch (mem_type) {case PCI_BASE_ADDRESS_MEM_TYPE_32:break;case PCI_BASE_ADDRESS_MEM_TYPE_1M:/* 1M mem BAR treated as 32-bit BAR */break;case PCI_BASE_ADDRESS_MEM_TYPE_64:flags |= IORESOURCE_MEM_64;break;default:/* mem unknown type treated as 32-bit BAR */break;}return flags;}
所以我们软件在开发时,需要读取下该配置空间的bit[2]是否为1。如果为1,那么当前设备使用64位地址空间,这也就是上边 lspci 看到的:
Region 0: Memory at 90000000 (64-bit, non-prefetchable) [size=16M]Region 2: Memory at 80000000 (64-bit, prefetchable) [size=256M]10: 04 00 00 90 00 00 00 00 0c 00 00 80 00 00 00 00# 0x90000004,然后判断04的低4为是0b0100,那么使用64位 内存空间,且不支持预期# 0x8000000c,然后判断0c的低4位时0b1100,那么使用64位 内存空间,且支持预期
32-bit Memory Address Space Request
Step1:如图中(1)所示,未初始化的BAR的低比特(11~4)都是0,高比特(31~12)都是不确定的值。所谓初始化,就是系统(软件)向整个BAR都写1,来确定BAR的可操作的最低位是哪一位。当前可操作的最低位为12,因此当前BAR可申请的(最小)地址空间大小为4KB(2^12)。如果可操作的最低位为20,则该BAR可申请的(最小)地址空间大小为1MB(2^20)。
Step2:完成初始化(写1操作)之后,软件便开始读取BAR的值,来确定每一个BAR对应的地址空间大小和类型。其中操作的类型一般由最低四位所决定,具体如上图右侧部分所示。
Step3:最后一步是,软件向BAR的高比特写入地址空间的起始地址(Start Address)。如图中所示,为0xF9000000。
当然,这三部分由操作系统启动后执行深度优先扫描算法去检测的时候,来获取PCI的资源信息。
64-bit Memory Address Space Request
64MB P-MMIO地址空间的例子,由于采用的是64-bit的地址,因此需要两个BAR
IO Address Space Request
每一组BAR空间存放了:PCI设备映射的物理内存对应的PCI总线地址,资源信息,长度信息;  
    比如:00 00 00 90  ,则总线地址是90000000,flag是0x04、0,
如果获取长度信息?写入0xFFFFFFFF,然后读取即可获取;
pci_write_config_dword(struct pci_dev *dev, int where, 0xFFFFFFFF);pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
注:64位需要相邻的两个BAR地址和长度进行拼接,详情参考 《PCI代码导读》
系统软件在Linux系统启动后,有两个空间:PCI总线空间 和 存储器域空间,且 只有映射到存储器域的空间 才可以被系统软件访问到。
混淆点:
pci_read_dconfig() 获取到的是pci总线域的物理地址;
pci_reasource_start()获取到的是BAR空间在存储器域的物理地址;
ioremap(存储器域物理地址,SIZE)
所以系统软件常用:
void  vaddr = ioremap(pci_resource_start(), SIZE);  获取虚拟地址后,像访问内存一样访问PCI存储域空间;*
OptionRom/Expansion Rom
在运行操作系统之前,有些设备有些 预初始化代码,只有在执行后才能使用;比如键盘鼠标,显卡等设备。OptionRom记录了PCIE设备存储这段ROM代码的地址;详情参考《PCIE OptionRom》;
capabilities pointer
PCI设备可选,PCIE设备必须,存访拓展功能信息;PCIE章节会详解;
Command寄存器


// bit[1:0] 在使能设备时打开,也可通过sysfs下enable直接使能int pci_enable_device(struct pci_dev *dev){return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO);}// bit[2] pci_set_master使用static void __pci_set_master(struct pci_dev *dev, bool enable){u16 old_cmd, cmd;pci_read_config_word(dev, PCI_COMMAND, &old_cmd);if (enable)cmd = old_cmd | PCI_COMMAND_MASTER;elsecmd = old_cmd & ~PCI_COMMAND_MASTER;...}// bit[10] 使能中断void pci_intx(struct pci_dev *pdev, int enable){u16 pci_command, new;pci_read_config_word(pdev, PCI_COMMAND, &pci_command);if (enable)new = pci_command & ~PCI_COMMAND_INTX_DISABLE;elsenew = pci_command | PCI_COMMAND_INTX_DISABLE;pci_write_config_word(pdev, PCI_COMMAND, new);}EXPORT_SYMBOL_GPL(pci_intx);
status



BDF寻址和分配过程
基础-BDF与配置空间
基础-PCI和PCIE的扫描
PCIe总线中的每一个功能(Function)都有一个唯一的标识符与之对应。这个标识符就是BDF(Bus,Device,Function)  
详情参考:《PCI+Express体系结构》 P57
BDF描述:16bit = BUS Number 8 bit + Device Number 5bit +Func Number + 3bit
每个PCIE设备支持1-8个功能(至少有一个),其中每个功能都有独立的配置空间。
由于一个pcie 最多有256 bus 最大32个device,每个device最多8个function。每个funciton 又占用4KB的空间,因此上电的时候需要为每个pcie准备256×32×8×4KB=256MB。这即使PCIe著名的256MB的算法

详情参考《PCI代码导读》
