PCIe 学习笔记
一、PCIe概况
随着现代处理器技术的发展,使用高速差分总线替代并行总线已是大势所趋。与单端并行信号相比,高速差分信号可以使用更高的时钟频率,从而可以使用更少的信号线达到更高的通讯速度。PCIe总线解决了PCI总线的不足,它的发展将取代PCI成为新型的数据总线,其提供了更加完善的性能,更多的功能,更强的可扩展性和更低的成本 。
在PCIE中有两种数据传输方式:
1>DMA(Direct Memory Access)模式:直接内存访问,该模式下数据传送不是由CPU负责处理,而是由一个DMA控制器来完成,占用较少的CPU资源
2>PIO(Programmed Input-Output)模式:可编程IO,该模式下数据传送由CPU执行I/O端口指令来处理,占用大量的CPU资源,数据传输速度也大大低于DMA模式
使用DMA模式时,计算机的运行速度会比使用PIO模式快很多
二、PCIe层次结构
PCIe 规范对于设备的设计采用分层的结构,主要分为事务层、数据链路层和物理层,在设备的发送时,首先根据来自FPGA的信息,在事务层形成事务层包(TLP),储存在发送缓冲器里,等待传给下层;数据链路层中,在 TLP 包上串接一些附加信息成为数据链路层包(DLLP),这些信息是接收方接受 TLP 包时进行错误检查要用到的;物理层中,对DLLP包进行编码成物理层包(PLP),占用链路中的可用通道发送出去。TLP、DLLP、PLP产生于各自所在层,最后通过电或光等介质和另一方通讯。这其中DLLP、PLP平常不需要关心,在IP核中封装好了,在FPGA上做PCIe的功能,就变成对TLP的处理。
事务层(Transaction Layer):
TLP由帧头、数据、摘要组成,7系列FPGA 开始,使用标准的 AXI4 总线协议进行通信,因此 PCIe的TLP采用AXI4-S接口协议进行传输,数据的传输以大端方式对齐(高位放在低地址)
头标: 长度为3或4个DW(double word),格式和内容随事务类型变化
数据: (若该 TLP 不携带数据,那该段为空)
摘要: 是基于头标、数据而计算出来的CRC,称为 ECRC,一般该段由 IP核填充,所以用户只需处理TLP中头标和数据段
Fmt[1:0]段 是关于头标长度和该 TLP 是否有数据在的信息:
Fmt[1:0] | 头标长度 | 有无数据 |
---|---|---|
00 | 3DW | 无数据 |
01 | 4DW | 无数据 |
10 | 3DW | 有数据 |
11 | 4DW | 有数据 |
Type与 Fmt字段:一起用于规定事务类型、头标长度和是否有数据载荷
Non-Posted命令:若设备发出一个Non-Posted请求,一段时间后,接收端需回复一个完成包,若不回复则可能遇到异常
Posted命令:不需要回复完成包给发送端
TYPE [4:0] | FMT[1:0] | TLP | Posted? |
---|---|---|---|
00000 | 00/01 (无数据) | 存储器读请求(MRd) | Non- |
00000 | 10/11 (写数据) | 存储器写请求(MWr) | Posted |
00001 | 00/01 (无数据) | 锁定存储器读请求(MRdLk) | Non- |
00010 | 00 (无数据) | IO 读请求(IORd) | Non- |
00010 | 10 (写数据) | IO 写请求(IOWr) | Non- |
00100 | 00 (无数据) | Type0 配置读请求(CfgRd0) | Non- |
00100 | 10 (写数据) | Type0 配置写请求(CfgWr0) | Non- |
00101 | 00 (无数据) | Type1 配置读请求(CfgRd1) | Non- |
00101 | 10 (写数据) | Type1 配置写请求(CfgWr1) | Non- |
10xxx | 01 (无数据) | 消息请求(Msg) | Posted |
10xxx | 11 (写数据) | 带数据的消息请求(MsgD) | |
01010 | 00 (无数据) | 完成(Cpl) | |
01010 | 10 (写数据) | 带数据的完成(CplD) | |
01011 | 00 (无数据) | 锁定完成(CplLk) | |
01011 | 10 (写数据) | 带数据的锁定完成(CplDLk) |
例如:发送的数据为0x4a00_0001_01a0_0004,0x01a0_0a10_0403_0201
则:Fmt是2’b10,Type 是5’b01010,判断为 3DW 带数据的完成包,0x4a00_0001_01a0_0004_01a0_0a10是头标,0x0403_0201是所带的数据
Length字段: 在读存储器请求报文中,表示需要从目标设备数据区域读取的数据长度;在写存储器请求报文中,表示当前报文的DataPayload长度,长度单位为DW
last/1st DW BE字段: PCIe总线以字节为基本单位进行数据传递,但是Length字段以DW为最小单位,该字段用于规定第一个和最后一个的有效字节的位置
Requester ID : 该TLP包的产生设备,的总线号(Bus Number)、设备号(Device Number)、功能号(Function Number)等
Tag: Requester ID、Tag合起来组成Transaction ID,在同一时间段内,PCIe设备发出的每一个Non-Posted数据请求TLP,其Transaction ID必须唯一,即Tag必须唯一
读写TLP包的格式:
上图中的两个格式,前者是针对64位地址的读写包,后者则是针对32位地址的读写包
完成包的格式:
Completer ID: 该完成包的产生设备的ID号
Byte Count: 记录源设备还需要从目标设备中获得多少字节的数据才能完成全部数据传递
Lower Address: 接收端必须使用存储器读写完成TLP的Low Address 字段,识别该TLP中包含有效数据的起始地址
三、事务层空间
PCI配置空间:主要用于向系统提供设备自身的基本信息,并接受系统对设备全局状态的控制和查询(设备只有在系统软件初始化配置空间之后,才能够被其他主设备访问,当配置空间被初值化后,该设备在当前的PCI总线树上将拥有一个独立的BAR空间)
I/O空间:主要包括设备的控制状态寄存器,一般用于控制查询设备的工作状态及少量数据交换
存储器空间:一般用于大量数据的交换(内存、显存、扩展ROM、设备缓冲区等)
消息空间 : 传递消息的空间
PCIe通讯是靠发送TLP包,读写包里都会有地址信息,若FPGA向PC发送TLP 包,例如 MWr 包,那么地址信息就是PC的物理地址;若发送的是 MRd 包,那PC收到后会回复一个完成包,FPGA从完成包提取出数据即可
PC 如何读写板卡的数据:
PC启动时,BIOS探测PCIe设备有多少个BAR空间,每个空间有多大,然后对应为这些空间分配地址
以上图为例,BAR2的空间大小为0x1000,PC上的起始地址为0xFDEFF000,若想通过PC访问BAR2的0x40地址,则在PC上直接访问0xFDEFF040即可,起始地址在不同的PC上是不一样的(但是偏移地址是相同的),在 FPGA 中,BAR 空间的设置,是根据用户需求在IP核里定义大小的
四、发送中断
PCIe可以发出两种中断:虚拟INTx信号线(PCI的信号)和MSI(消息)
虚拟INTx信号线:
发送的数据为:0x3400_0000_0100_0020, 0x0000_0000_0000_0000
则:Fmt为 2’b01,Type 为5’b10100,判断为消息请求包,Message Code 为0x20(8’b0010_0000),判断为中断(INTx)消息
发送的数据为:0x3400_0000_0100_0024, 0x0000_0000_0000_0000时,
则:Fmt 为2’b01,Type 为 5’b10100,判断为消息请求包,Message Code 为0x24 (8’b0010_0100),判断为中断(INTx)撤销消息
这个之后有需要的话,可以做实验测试,现阶段就先只找到这两个信号线(如图直接搜索int_(x))
MSI中断:
是基于消息机制的,PC启动后会为 PCIe 板卡分配消息地址,板卡发送中断的话,只需向对应的地址发送消息即可(消息内容中包含消息号,每个消息号对应在PC 端的某一地址)
注:在Xilinx平台上,中断和其他包是分开的,中断发送是非常简单的,只需要简单操作几条信号线,PCIe 核就可以自己组织需要的中断包向外发送
五、PCIe的IP核
使用环境:VIVADO 2017.4
IP核版本:7 Series FPGAs Integrated Block for PCI Express v3.3
官方文档:pg054
IP核概览图:
IP核接口定义:
System Interface:
信号 | I/O | 功能 |
---|---|---|
sys_clk | I | 有3个可选频率,输入时钟要和IP配置的值一致 |
sys_rst_n | I | (异步复位、低有效),至少1500ns |
PCI Express Interface:
上图为4X模式下的,外部引脚的接口,共4组,每组都有收发信号,且收发信号线均为差分线
Configuration Interface:
这类信号名称一般以cfg_开头,主要用于检测PCIE终端的configuration space状态,详情见手册,此类信号在IP核上有很多很多,比如:
其中的中断接口信号一般以cfg_interrupt开头
其中的异常报告信号一般以cfg_err_开头
Physical Layer Interface:
此类信号一般以pl_开头,详情见手册,用于控制和检测PCIE物理层,可以改变速度、位宽等,一般不使用
Dynamic Reconfiguration Port Interface:
DRP接口,此类信号一般以pcie_drp_开头,详情见手册,用于动态配置PCIE核的寄存器,用于调试
Debug Interface:
user_和fc_开头的信号,输出系统工作状态,用于调试
AXI4-S Interface:
以m_axis_rx_、s_axis_tx_、tx_、rx_开头,用于传输数据,详情见手册
六、DMA传输模式XAPP1052官方例程解析
xapp1052是xilinx官方给出的一个有关DMA数据传输的样例,用于PC端和FPGA端之间的DMA数据传输,虽然xapp1052并不是一个完整的DMA数据传输的终端硬件设计,但是还是有很大参考价值的
文件层次:
主要包括PCIe硬核和应用逻辑,硬核由软件生成,应用逻辑主要包括发送引擎、接收引擎和存储器访问模块
RX_ENGINE: 用于解析IP核的数据包,负责DMA读接收数据包
TX_ENGINE: 负责DMA写发送数据包和中断控制
BMD_EP_MEM_ACCESS: 存储访问模块,包含DMA状态及控制寄存器用以控制DMA读写,这里的寄存器是以PIO的方式写入配置,在RC中设置的TLP长度、TLP数量信息等会写入到这些寄存器中
BMD_GEN2、BMD_RD_THROTTLE、BMD_TO_CTRL、BMD_CFG_CTRL:BMD的一些相关的配置和控制信号的处理模块
axi_trn_top:负责axi协议和trn协议的相互转换
DMA写流程:
一次DMA写的过程是由FPGA的数据写入RC端的存储器中的过程,具体步骤为:
0>在RC端申请一块物理地址连续的内存,EP端准备好写数据后向RC端发送中断
1>在RC端分析中断,并向BAR0空间设置本次DMA写的TLP大小、TLP数量、写地址等,(通过PIO的方式,将带有上述信息的TLP包发送给EP端,写入FPGA的DMA控制状态寄存器中),并且启动DMA
2>根据DMA状态控制寄存器的内容,在收到DMA写启动命令后,TX引擎开始从FPGA中读取数据并按第一步设置的DMA大小数量来组装TLP包然后发送到PCIe核
3>FPGA发送完数据后通过中断等形式通知主机DMA完成,主机读取 BAR0 空间状态寄存器判断中断类型做出相应判断,同时主机从内存读出数据
状态机:
BMD_64_TX_RST_STATE:初始的复位状态,在该状态判断该进入发送完成包、存储器写和存储器读 的其中一个状态
发送完成包:
BMD_64_TX_CPLD_QW1:发送完成包 — 返回存储器地址和指定数据
BMD_64_TX_CPLD_WIT:发送完成包 — 等待完成
存储器写:
BMD_64_TX_MWR_QW1:DMA存储器写请求(32bit地址),发送3DW长度的头+1DW数据
BMD_64_TX_MWR64_QW1:DMA存储器写请求(64bit地址),发送4DW长度的头
BMD_64_TX_MWR_QWN:发送剩余的数据到RC端
存储器读:
BMD_64_TX_MRD_QW1:DMA存储器读请求(然后在RX引擎接收相应数据)
DMA读流程:
一次DMA读的过程是将RC端存储空间的数据读入到FPGA中的过程,具体步骤为:
0>在RC端申请一块物理地址连续的内存,并向该内存写入数据,EP端准备好读数据后向RC端发送中断
1>在RC端分析中断,并向BAR0空间设置本次DMA读的TLP大小、TLP数量、读地址等,(通过PIO的方式,将带有上述信息的TLP包发送给EP端,并写入DMA控制状态寄存器中),并启动DMA
2>根据DMA状态与控制寄存器的内容,在收到DMA读启动命令后,在TX引擎中组装存储器读TLP包后,发送给PCIe核,RC端根据收到的存储器读包,在指定的地址读取数据后形成带数据的完成包(CPLD)返回给FPGA,FPGA在RX引擎中接收数据
3>FPGA接收完数据后通过中断形式通知主机DMA读完成,主机读取 BAR0 空间状态寄存器判断中断类型做出相应判断
状态机:
BMD_64_RX_RST:根据trn_rd[62:56]来判断包的类型:
32位地址读请求 (BMD_MEM_RD32_FMT_TYPE)
32位地址写请求 (BMD_MEM_WR32_FMT_TYPE)
不带数据的完成包 (BMD_CPL_FMT_TYPE)
带数据的完成包 (BMD_CPLD_FMT_TYPE)
32位地址读请求:
BMD_64_RX_MEM_RD32_QW1:解析RC端的读TLP包 — 通知TX引擎发送完成包
BMD_64_RX_MEM_RD32_WT:解析RC端的读TLP包 — 等待完成包发送完毕
32位地址写请求
BMD_64_RX_MEM_WR32_QW1:解析RC端的写TLP包 — 写入寄存器
BMD_64_RX_MEM_WR32_WT:解析RC端的写TLP包 — 等待写寄存器完毕
不带数据的完成包
BMD_64_RX_CPL_QW1:解析出完成包的tag,送至MEM模块
带数据的完成包
BMD_64_RX_CPLD_QW1:解析RC端的完成包 — 获得数据
BMD_64_RX_CPLD_QWN:解析RC端的完成包 — 直至完成
axi-trn互转:
由于7系列的PCIe核的数据是通过AXI-S协议传输的,但是XAPP1052中的信号的相关处理是对trn_信号进行处理,所以会有一个协议转换的模块
以接收类信号为例,发送类信号类比:
trn_rsrc_rdy: 表示RC端(接收的源)准备就绪
trn_rdst_rdy: 表示EP端(接收的目的)准备就绪
trn_rsrc_dsc: 表示RC端(接收的源)将当前包丢掉
trn_rsof: 接收帧开始标志,(仅在trn_rsrc_rdy低时有效)
trn_reof: 接收帧结束标志,(仅在trn_rsrc_rdy低时有效)
trn_rd: 接收到的数据,(仅在trn_rsrc_rdy低时有效)
trn_rrem: 接收数据余数,为0表示数据在trn_rd[63:0],为1表示数据在trn_rd[63:32](仅在trn_reof 、trn_rsrc_rdy、trn_rdst_rdy同时低时有效)
trn_rbar_hit[6:0]: 表示当前包在哪个BAR空间,低有效(仅在trn_rsof、trn_reof低时有效)
trn_rbar_hit[0]——>BAR0
trn_rbar_hit[1]——>BAR1
trn_rbar_hit[2]——>BAR2
trn_rbar_hit[3]——>BAR3
trn_rbar_hit[4]——>BAR4
trn_rbar_hit[5]——>BAR5
trn_rbar_hit[6]——>Expansion ROM Addres
本文转载自: https://blog.csdn.net/long_fly/article/details/79150820