描述符通过三个部分组成,事务ID,属性域,TC域。如下图

由上可以知道,事务ID是以一个16位的的requester ID和一个8位的tag组成。 这是由请求者的BDF,Bus Number[7:0]、Device Number[4:0]、Function Number[2:0]组成的信息。 对于post请求,仅发送bdf作为requester id即可,对于non-posted请求,还需要tag字段,
这里之前有提到,对于attr[2]和attr[1:0]定义如下

对于bit1和bit2,有四种模式为,1.pci强排序模式,2.轻松排序模式,3.基于ID的排序模式,4.轻松排序或ID排序,如下

对于bit0,选择是否cache一致性。0是默认一致,1可不一致,如下图

tc作为流量控制,之前也提到过,000为默认不控制,001-111按照优先级做流控

tlp有三种方式路由,Address方式,ID方式和implicit(隐式) address和id是常用路由方式,implicit仅用作message request的type tlp包的路由,这里描述一下address和id。
address 路由用作memory和io类型,如果是64位地址则header是4dword,32位地址则是3dword,如下

对于地址路由时,header的1dword的at字段在memory read/write和atomicOp请求时有效,其他tlp类型时,at字段保留。如下

也就是:00b=未转换的地址,01b=地址转换请求,10b=已转换的地址,11b=预留
如果存在转换的地址,则地址映射关系,体现在header域的byte8-byte15上,如下图所示

值得注意的是,对于4g以下的地址,请求必须以32位的格式。
id路由通常作为配置请求,ID Routed Message和Completions。对于header中,需要定义Vendor_Defined Messages作为ID。
id路由通过BDF(Bus,Device,Function Numbers)来明确TLP的目的地址
对于ari和非ari设备,id路由的header域值如下

如果是4dword的tlp header,id路由的header布局如下.(不同字节的header由tlp的类型决定)

如果是3dword的tlp header,id路由的header布局如下

这里可以从byte8和byte9来确定BDF值作为id路由的依据
对于header的byte7,有两个字段,last dw/1st dw。它与byte2和byte3的length[9:0]有一定关系:
事务层主要处理生成和接收TLP包,以及其他如交换流控信息,支持软件和硬件启动的电源管理,这里主要说TLP包。

主要包括四个类型,内存,IO,配置,消息。如下图

TLP包有前缀,头,数据,摘要四个部分组成,如下图

详细的TLP包格式如下图

对于TLP的包的前缀包含如下字段。

前缀默认只有1dword,也就是上述字段。这里注意的是prefix的fmt一定是100b,其中type位的4位代表前缀的类型。对于local tlp prefix的type字段如下

组合起来如下
对于end-end tlp prefix 的type字段如下

组合起来如下
这里VendPrefix均是Vendor定义的TLP前缀字段
和prefix一样,header的第一个dword也是固定格式

但是对于fmt字段,不是prefix的固定的100,而是如下定义

这里fmt的值代表整个header包含多少个dword,
而接下来的type字段确定tlp的类型,故fmt和type的对于tlp包的类型如下图


这里还有如下字段解释
关于length域,主要如下

对于data payload,需要注意如下

对于数据 76543210,如果非AtomicOp Request和AtomicOp Completion,则0x100存放0,0x107存放7(第二点) 如果是AtomicOp Request和AtomicOp Completion,如果目标内存架构是小端,则100存放0,107存放7(小端数据高位存在低地址)。如果是大端,则100存放7,107存放0.
如上述可知,TD位如果是1,则tlp址出digest,并且digest域存放ecrc的值,当然,如果tlp本身不支持ecrc校验,则应该主动忽略digest域的内容
这里ECRC的意思是,end to end crc,端到端crc。ecrc与lcrc不同的点是,数据的内部转发不会对lcrc值进行校验,如果转发过程中出现错误,则可以通过ecrc来进行判断。
pcie主要有rc,ep,和switch以及pcie to pci/pci-x bridge组成,如下图所示

rc设备通常直接对接cpu和memory,通常也是pcie设备的根设备,也就是bus域的根设备
ep设备是终端设备,也就是具体实现功能的设备,它有如下特殊的ep设备:
switch设备可以提供多个虚拟的pci-pci的bridge设备,如下图所示

所以switch具有如下两个特性
对于switch包含设备
pcie开始通过配置空间来读取pcie基本信息,改信息通过上层lspci能够正常解析,客户反馈自己的xilinx设备bar0只有512k,需要我们确定一下,遂确定如下
For device Functions with Type 0 headers (all types of Endpoints) For device Functions with Type 1 headers (Root Ports, Switches and Bridges)
如何查看设备是type0还是type1 headers,可以通过配置空间的0x1e的值来确定,如下

For Functions that implement a Type 0 Configuration Space header the encoding 000 0000b must be used. For Functions that implement a Type 1 Configuration Space header the encoding 000 0001b must be used
也就是说,对于0x0e的值,如果是0x1则是type1 headers型设备,通常是rc或bridges,如果是0x0则是type0 headers型设备,通常是ep。
root@kylin:~# lspci -x 00:00.0 PCI bridge: Fuzhou Rockchip Electronics Co., Ltd Device 3588 (rev 01) 00: 87 1d 88 35 07 05 10 00 01 00 04 06 00 00 01 00 10: 00 00 00 00 00 00 00 00 00 01 ff 00 f0 00 00 00 20: 00 f0 00 f0 f1 ff 01 00 00 00 00 00 00 00 00 00 30: 00 00 00 00 40 00 00 00 00 00 00 00 70 01 02 00 01:00.0 Memory controller: Xilinx Corporation Device 7014 00: ee 10 14 70 00 00 10 00 00 00 80 05 00 00 00 00 10: 00 00 f8 ff 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 ee 10 07 00 30: 00 00 00 00 80 00 00 00 00 00 00 00 00 01 00 00
lspci -x可以读到0xe的值,这里可以看到00:00.0 是type1 headers,而01:00.0则是type0 headers
通常的配置空间布局如下图所示
type0的配置空间布局如下图所示
type1的配置空间布局如下图所示

这里可以知道,无论哪种type设备,bar0的寄存器都在0x10处,这里聚焦0x10处的信息
对于0x10的值,默认是linux设置的映射地址,用作pcie域的读写操作。如下可以确定
root@kylin:~# lspci -x -s 01:00.0 01:00.0 Memory controller: Xilinx Corporation Device 7014 00: ee 10 14 70 00 00 10 00 00 00 80 05 00 00 00 00 10: 00 00 00 f0 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 ee 10 07 00 30: 00 00 00 00 80 00 00 00 00 00 00 00 ff 01 00 00
这里可以知道0x10的值为f0000000,这个0xf0000000是linux内通过设备树映射的可访问的地址。
对于bar0的大小lspci已经读取出来了是512k,如下
root@kylin:~# lspci -s 01:00.0 -v 01:00.0 Memory controller: Xilinx Corporation Device 7014 Subsystem: Xilinx Corporation Device 0007 Flags: fast devsel, IRQ 255 Memory at f0200000 (32-bit, non-prefetchable) [disabled] [size=512K] Capabilities: [80] Power Management version 3 Capabilities: [90] MSI: Enable- Count=1/1 Maskable- 64bit+ Capabilities: [c0] Express Endpoint, MSI 00 Capabilities: [100] Advanced Error Reporting lspci: Unable to load libkmod resources: error -2
setpci --dumpregs setpci -s 01:00.0 0x10.L=0xffffffff root@kylin:~# lspci -s 01:00.0 -x 01:00.0 Memory controller: Xilinx Corporation Device 7014 00: ee 10 14 70 00 00 10 00 00 00 80 05 00 00 00 00 10: 00 00 f8 ff 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 ee 10 07 00 30: 00 00 00 00 80 00 00 00 00 00 00 00 ff 01 00 00
这时候读出来是0xfff80000,取最低有效位0x80000,则正好是512k
io -4 0xf0000000 -r -l 64 io -4 -w 0xf0000010 0xffffffff io -4 0xf0000010 -r
这里读出来仍是0xfff80000,换算也是512k
内核主要代码实现在如下两个函数
主要函数如下:
__pci_read_base pci_read_config_dword(dev, pos, &l); pci_write_config_dword(dev, pos, l | mask); pci_read_config_dword(dev, pos, &sz); pci_size u64 size = mask & maxbase; size = size & ~(size-1);