电子说
在高速互连领域中,使用高速差分总线替代并行总线是大势所趋。与单端并行信号(PCI总线)相比,高速差分信号(PCIe总线)可以使用更高的时钟频率,从而使用更少的信号线,完成之前需要许多单端并行数据信号才能达到的总线带宽。
PCIe协议基础知识
PCI总线使用并行总线结构,在同一条总线上的所有外部设备共享总线带宽,而PCIe总线使用了高速差分总线,并采用端到端的连接方式,因此在每一条PCIe链路中只能连接两个设备。这使得PCIe与PCI总线采用的拓扑结构有所不同。PCIe总线除了在连接方式上与PCI总线不同之外,还使用了一些在网络通信中使用的技术,如支持多种数据路由方式,基于多通路的数据传递方式,和基于报文的数据传送方式,并充分考虑了在数据传送中出现服务质量QoS (Quality of Service)问题。
与PCI总线不同,PCIe总线使用端到端的连接方式,在一条PCIe链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端。PCIe链路可以由多条Lane组成,目前PCIe链路×1、×2、×4、×8、×16和×32宽度的PCIe链路,还有几乎不使用的×12链路。
在PCIe总线中,使用GT(Gigatransfer)计算PCIe链路的峰值带宽。GT是在PCIe链路上传递的峰值带宽,其计算公式为 总线频率×数据位宽×2。
按照常理新一代的带宽要比上一代翻倍,PCIe3.0的原始数据传输带宽应该是10GT/s才对而实际却只有8.0GT/s。我们知道,在1.0,2.0标准中,采用的是8b/10b的编码方式,也就是说,每传输8比特有效数据,要附带两比特的校验位,实际要传输10比特数据。因此,有效带宽=原始数据传输带宽*80%。而3.0标准中,使用了更为有效的128b/130b编码方案从而避免20%带宽损失,3.0的浪费带宽仅为1.538%,基本可以忽略不计,因此8GT/s的信号不再仅仅是一个理论数值,它将是一个实在的传输值。
PCIe总线采用了串行连接方式,并使用数据包(Packet)进行数据传输,采用这种结构有效去除了在PCI总线中存在的一些边带信号,如INTx和PME#等信号。在PCIe总线中,数据报文在接收和发送过程中,需要通过多个层次,包括事务层、数据链路层和物理层。PCIe总线的层次结构如下。
PCIe总线的层次组成结构与网络中的层次结构有类似之处,但是PCIe总线的各个层次都是使用硬件逻辑实现的。在PCIe体系结构中,数据报文首先在设备的核心层(Device Core)中产生,然后再经过该设备的事务层(TransactionLayer)、数据链路层(Data Link Layer)和物理层(Physical Layer),最终发送出去。而接收端的数据也需要通过物理层、数据链路和事务层,并最终到达Device Core。
事务层(Transaction Layer)
在PCIe总线层次结构中,事务层最易理解,同时也与系统软件直接相关。
TLP由帧头、数据、摘要组成,7系列FPGA 开始,使用标准的 AXI4 总线协议进行通信,因此 PCIe的TLP采用AXI4-S接口协议进行传输,数据的传输以大端方式对齐(高位放在低地址)。
头标: 长度为3或4个DW(double word),格 式和内容随事务类型变化 数据: 若该 TLP 不携带数据,那该段为空摘要: 是基于头标、数据而计算出来的CRC, 称为 ECRC,一般该段由 IP核填充,所 以用户只需处理TLP中头标和数据段
Fmt[1:0]段 是关于头标长度和该 TLP 是否有数据在的信息:
Type与 Fmt字段:一起用于规定事务类型、头标长度和是否有数据载荷
Non-Posted命令:若设备发出一个Non-Posted请求,一段时间后,接收端需回复一个完成包,若不回复则可能遇到异常
Posted命令:不需要回复完成包给发送端
例如:发送的数据为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:
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
全部0条评论
快来发表一下你的评论吧 !