PCIe知识

BAR

CPU通过地址总线访问空间,编址方式有两种,如下图所示:

PCIe知识

在ARM中,CPU通过不同地址区分内存和外设。在x86中,内存地址和IO地址是不同的地址总线,CPU访问内存地址和访问IO地址使用不同的指令。
由于x86的CPU只有memory指令和IO指令,没有配置指令,因此X86的CPU可以直接访问memory空间和IO空间,但是不能直接访问PCIe配置空间。
BAR(base address registers)就是为了把设备的内部各种资源映射到IO空间(IO BAR)或者memory空间(memory BAR)。
PCIe知识

FPGA访问PC,TLP包里包含地址信息,该地址信息即PC的物理地址。FPGA可以发送合法的PCIe TLP包,并得到PC端的响应。

PC在启动时,BIOS探测所有外设,BIOS检测PCIe板卡有多少个BAR空间,每个空间有多大,然后为这些BAR空间分配对应大小的地址。因此,对PC来说,能访问的只有BAR空间。

DMA模式

Block DMA

PC申请一小片连续物理空间,设置好对应寄存器后启动DMA。如果一次DMA传输的数据比较大,需要申请大片连续物理空间。在Linux中这需要修改内核设置来实现,在Windows中无法实现。

在Windows中的一种处理方法是在PC上申请小片物理空间,然后重复DMA多次。这样做的话,每次DMA都会产生中断,而且每次DMA完成后都需要将数据从物理空间拷走才能释放空间。中断处理和数据拷贝是Block DMA性能进一步提高的障碍。

Scatter Gather DMA

Scatter Gather DMA(SG DMA)可以方便解决Block DMA中的问题。SG DMA要建立链表式的描述符,每个描述符包含PC端的物理地址、传输长度、链表last标志、下一个链表的物理地址等信息,也就是说包含了一次DMA所需要的全部信息。
板卡顺序执行链表中的DMA任务,在完成最后一个描述符后通过一次中断或查询作为操作的结束。

在实现时,PC申请内存中的页表,然后将页表组织成描述符链表并启动SG DMA。板卡根据链表信息将所有页表填满数据。由于非分页内存在各级别均可以使用,用户程序可以直接处理这些数据而不再需要进行数据拷贝。与Block DMA相比,几百次中断处理变为1次,几百次的内存拷贝页没有了,从而提升了传输效率。

Altera的Avalon-MM DMA接口的PCIe IP就是使用SG DMA实现。

Minimum Latency DMA

有的应用需求的数据量不大,但是对于数据进入板卡到PC能处理的延时非常关注。

例如每到板卡32B后需要最短时间内能被应用程序处理。如果采用Block DMA,每收到32B就产生中断并DMA传至内存,首先中断处理是us级别,其次总体数据传输效率非常低,PC负载也很高。采用SG DMA效果也差不多。

对于这种情况,可以借鉴SG DMA,在PC内生成一个环状链表,链表内的信息比SG DMA多一个Stick Bit。板卡每收到32B就组织一个MWr包填充一个描述符指定的空间,同时再发送一个MWr修改这个Stick Bit。软件轮询Stick Bit的修改情况,就可以去取Stick Bit所属描述符的空间了。

由于是环状链表,板卡可以循环执行。要注意,PC端处理的速度需要快过板卡接收的速度,否则会出现数据被覆盖。

这种方式不经过中断,也不去查询板卡信息,仅查询PC本地内存的数据,系统负载以及时延都可以有效控制。

FPGA的PCIe实现

Arria10的PCIe实现

Arria10的PCIe接口有3种解决方案:Avalon-ST接口、Avalon-MM接口、Avalon-MM DMA接口。主要区别如下图所示:

PCIe知识

其中,Gen3 x8 Avalon-MM只支持Root Port模式,Gen3 x8 Avalon-MM DMA只支持Endpoint模式。

Avalon-MM DMA中,PC是主设备,FPGA是从设备,通过SG DMA的方式实现主机DMA访问FPGA。
Avalon-MM中,FPGA是主设备,PC是从设备,IP替用户实现了Avalon-MM到TLP包之间的转换。
Avalon-ST接口是最基本的PCIe硬核,输入输出数据为TLP包数据,需要用户自行解包和组包。

Avalon-ST接口IP由IP Catalog生成,Avalon-MM和Avalon-MM DMA由Platform Designer生成。

Xilinx的PCIe实现

在最新的Xilinx系列器件中,PCIe IP不再使用ST接口,而是改为了AXI接口。

PCIe IP的用户侧有4路AXI接口(不含中断):m_axis_cq、s_axis_cc、s_axis_rq、m_axis_rc。
这4路接口命名的后缀含义如下:

  • cq:Completer Request
  • cc:Completer Completion
  • rq:Requester Request
  • rc:Requester Completion

由于DMA控制器是在FPGA端,因此Xilinx将FPGA端视为请求端Requester,PC端视为完成端Completer。

接口的主从是从PCIe IP的角度来划分:

  • cq:完成端发起请求,PC主FPGA从
  • cc:向完成端发送完成包,FPGA主PC从
  • rq:请求端发起请求,FPGA主PC从
  • rc:向请求端发送完成包,PC主FPGA从

TLP协议

TLP概念

PCIe设备为分层结构,有事务层、数据链路层和物理层组成。在FPGA中如果使用PCIe硬核,则不需要关心数据链路层和物理层,这两层由IP负责处理,用户只需要处理TLP层。

TLP由三部分组成,包头、数据、摘要(ECRC)。TLP包头长3或4DW,只有携带数据的TLP才有数据,摘要由IP填充。因此,PCIe的处理在用户层表现为处理TLP包的包头和数据。

TLP包请求有5种:Memory、IO、Configuration、Message、Completion。

  • Memory:用于访问存储空间,是目前最常见的TLP包。
  • IO:用于访问IO空间,现在基本已经很少使用,可以忽略。
  • Configuration:用于访问配置空间,由Host发起,通常只在上电枚举和配置阶段会发起Configuration访问,不是常态,可以忽略。
  • Message:只有有中断或者有错误等情况下才会有Message TLP,属于非主流,可以忽略。
  • Completion:完成包,对于non-posted的TLP包都需要返回Completion包。

TLP包根据是否需要返回完成信息,又可以分为posted和non-posted两类。MWr是posted类型,不需要返回完成包。MRd是non-posted类型,必须返回完成包。

MRd的读数据是通过带数据的完成包返回的。

Arria10的PCIe常用TLP包格式

64bit地址访存读请求包格式如下:

PCIe知识

64bit地址访存写请求包格式如下:

PCIe知识

带数据完成包格式如下:

PCIe知识

TLP包字段和参数说明

Fmt和Type

Fmt和Type字段联合确定当前TLP的事务类型。
Fmt用于标识包头长度和是否携带数据,格式如下:

Fmt[1:0] 描述
00 包头长度3DW,不携带数据
01 包头长度4DW,不携带数据
10 包头长度3DW,携带数据
11 包头长度4DW,携带数据

其中,包头长度为3DW则地址为32bit,包头长度为4DW则地址为64bit。

常用的TLP类型的Fmt和Type如下:

TLP类型 Fmt[1:0] Type[3:0] 描述
Memory读 00/01 0_0000 访存读请求,包头为3DW或4DW
Memory写 10/11 0_0000 访存写请求,包头为3DW或4DW
带数据完成包 10 0_1010 带数据完成包,包头为3DW

在Xilinx器件的IP中,Type的含义为其自定义的传输类型,TLP包中不含Fmt。

TC

TC字段表示当前TLP的传输类型,PCIe总线规定了8种传输类型TC0~TC7,一般只用默认值TC0,即TC=3’d0。

TD、EP、AT

TD位表示TLP种的TLP Digest是否有效,1为有效,0为无效。即是否使用ECRC。

EP(Poisoned Request)位表示TLP中的数据是否有效,1为无效,0为有效。

AT字段与PCIe总线的地址转换有关,只有在支持IOMMU技术的处理器系统中才使用此字段。

  • RX方向:EP、TD、AT均不需要解析。
  • TX方向:TD=0,EP=0,AT字段保留。

Attr

Attr为2bit,Xilinx中为3bit,但一般只用低2bit。字段说明如下:

Attr[1:0] 描述
bit[1] 0 默认值,PCI强序模型
bit[1] 1 PCI-X Relaxed Ordering
bit[0] 0 默认值,硬件强制执行Cache共享一致性处理
bit[0] 1 TLP不存在处理器Cache一致性问题,不需要监听处理器Cache
  • RX方向:不需要解析。
  • TX方向:通常为0或1,需要软件驱动开发人员决定是否需要处理Cache共享一致性。

Length

对于读请求,Length为从目标设备EP的数据区读取的数据长度。
对于写请求,Length为当前TLP包的数据长度。

Length为10bit,当Length=0时表示长度1024DW,当Length非0时DW长度为Length。

Requester ID、TAG、Completer ID

Requester ID字段(16bit)包含生成这个TLP报文的PCIe设备的Bus Number[7:0]、Device Number[4:0]、Function Number[2:0]。

TAG字段(8bit)用来标识由请求者发出的未完成的非转发事务的请求序号,以便相应的完成返回时带有与原请求者相同的序号,请求者将事务包与相应挂起的请求对号,解除对应的挂起请求。

Completer ID字段存放发送完成TLP的PCIe设备的ID号,即EP的设备ID。

Last BE、First BE

这两个字段均为4bit。
Last BE的每一位对应数据Payload最后一个DW的Byte使能。
First BE的每一位对应数据Payload第一个DW的Byte使能。

如果传输数据长度不超过1个DW,则Last DW为4’b0000,First DW的对应位置置1。
如果传输数据长度超过1个DW,则First BE字段和Last BE字段都至少要有1bit使能。
如果传输数据长度大于2DW,则First BE字段和Last BE字段不允许出现不连续的置1位。
如果传输数据小于等于2DW,则First BE字段和Last BE字段允许有不连续的置1位。

另外,PCIe总线支持一种特殊的读操作,即"Zero-Length"读请求。此时Length=1,而First BE字段和Last BE字段都为4’b0000,即所有字节都无效。
0长度读请求TLP对应的读完成TLP中不包含有效数据。

0长度读请求用于确保之前posted类型传输的数据到达最终目的地。主设备发起访存写请求后并不知道是否已写完成,有可能数据还在从设备的FIFO中,没有真正写到目的地址。于是主设备可以发起1次0长度读请求,目的设备在收到0长度读请求后确认数据是否已写入完毕,在写入完毕后才返回0长度读请求的完成包。

Byte Count

当前完成包中携带的数据字节数量。Byte Count根据当前完成包对应的First BE字段和Last BE字段确定。
为了方便设计,通常将数据按64B对齐,于是Memory读写操作的First BE字段和Last BE字段均为4’b1111,Byte Count即为当前TLP包中携带数据的Dword_Count×4。

Status、BCM

Status字段保存当前完成TLP的完成状态。

Status[2:0] 描述
000 SC,正常结束
001 UR,不支持的数据请求
010 CRS,要求数据请求方进行重试
100 CA,数据夭折
其他 保留

BCM字段由PCI-X设备设置,该字段处理方法为:保留,设为0。

Max_Payload_Size参数

PCIe总线TLP包中有效数据负载最大值为4KB,但是PCIe设备不一定能够发送最大值的数据报文。
PCIe设备含有"Max_Payload_Size"和"Max_Payload_SizeSupported"参数。这两个参数分别在Device Capability寄存器和Device Control寄存器中定义。

  • Max_Payload_SizeSupported为PCIe设备支持的TLP有效负载的最大值,由硬件决定,系统软件不能修改此参数。
  • Max_Payload_Size为PCIe设备进行数据传输时实际使用的TLP有效负载的最大值,由PCIe链路两端设备协商决定。即选用PCIe链路两端最小的Max_Payload_SizeSupported参数来初始化Max_Payload_Size。

在多数x86处理器系统中,Max_Payload_SizeSupported为128B,在PowerPC处理器系统中,此参数大多为256B。

因此,FPGA端的Max_Payload_Size实际取决于PC端的Max_Payload_SizeSupported。

信用与流控(FC)

基本概念

PCIe的流控缓冲区对posted、non-posted、cpl三类TLP包分别有存储空间进行缓存,其中每一类又分成包头和数据分别缓存。
因此,在流控缓冲区一共有6个缓冲buf:PH、PD、NPH、NPD、CPLH、CPLD。
FC计数器对6个buf大小计数,并发送给对端。
发送端在每次发送TLP包时必须保证接收端的相应buf空间足够。

对于不同类型的包头和数据,1个信用单元大小的定义是不同的:

  • 完成包包头:4DW
  • 请求包包头:5DW
  • 数据:4DW

接收端将FC计数结果通过Flow Control update DLLPs发送给发送端。格式如下:

PCIe知识

可见,包头FC为8bit,数据FC为12bit。

为了提升系统性能,在FPGA中用户层的信用计算与流控需要自行设计实现。

信用计算与流控

相关定义

信用的一些相关定义如下:

名称 位置 描述
CL 发送端 信用限额
CC 发送端 信用消耗,即已使用的信用
PTLP 发送端 待发送的TLP
CrAl 接收端 已分配的信用
CrRcv 接收端 收到的信用

信用空间足够时的信用计算与流控

假设流控缓冲区大小为2KB,初始化后发送第一个请求包,计算请求包包头的信用方法如下:

  1. 计算流控单元数量:2KB/5DW=102,即CL=102,CrAl=102。
  2. 已使用的信用CC=0,待发送的信用PTLP=1。
  3. 发送端计算总的需要的信用:CC+PTLP=1。
  4. 发送端计算剩余信用:CL-(CC+PTLP)=101。
  5. 计算按照8bit无符号数进行计算,也就是对 2 8 2^8 28取模,然后与 2 8 2^8 28/2比较。即:101 ≤ \leq 128。
  6. 信用空间足够,可以发送此TLP。
  7. TLP发送至链接层时,CC+1。
  8. TLP到达接收端时,将包头存入流控缓冲区,将CrRcv+1。注意CrAl不变。

信用空间不足时的信用计算与流控

假设上述流控缓冲区已满,需要发送一个请求包,计算请求包包头的信用方法如下:

  1. 已使用的信用CC=102,待发送的信用PTLP=1。
  2. 发送端计算总的需要的信用:CC+PTLP=103。
  3. 发送端计算剩余信用:CL-(CC+PTLP)=-1。计算按照8bit无符号数进行计算,因此剩余信用计算结果为255。
  4. 255 ≥ \geq 128,信用空间不足,不可以发送此TLP。
  5. 等待接收端从流控缓冲区移出一个包头,移出后CrAl+1,CrAl=103。
  6. 接收端将CrAl发送给发送端作为新的CL,发送端CL=103。
  7. 发送端重新计算剩余信用,CL-(CC+PTLP)=0。
  8. 0 ≤ \leq 128,信用空间足够,可以发送此TLP。

CrAl溢出时的信用计算与流控

由于CrAl为8bit无符号数,递增到255后,下一次增加会变为0。假设CrAl=255,CC=248,接收端移出一个包头后,计算请求包包头的信用方法如下:

  1. 接收端移出一个包头后,CrAl=0。
  2. 接收端将CrAl发送给发送端作为新的CL,发送端CL=0。
  3. 发送端计算总的需要的信用:CC+PTLP=249。
  4. 发送端计算剩余信用:CL-(CC+PTLP)=-249。计算按照8bit无符号数进行计算,因此剩余信用计算结果为7。
  5. 7 ≤ \leq 128,信用空间足够,可以发送此TLP。

相关文章: