最近想自己搭建一个PCIE DMA系统,验证一下最近所学。本设计全部采用xilinx的IP核,PCIE部分采用axi bridge for pcie gen3 subsystem。DMA系统使用前面学习过得AXI Central Memory Access(CDMA)。
关于CDMA可以参考我之前得一篇文章。
XAPP1171和AXI-CDMA使用仿真_xilinx cdma-CSDN博客
在学习初期,搞不懂如何去搭建一个DMA系统,搞不懂这些IP分别有什么作用,搞不懂里面复杂的地址分配问题。以及如何在驱动层次去控制我的这个系统。当然本方案暂时不考虑效率的问题。如下图所示就说使用axi_pcie搭建的一个DMA系统。系统的核心是axi bridge for pcie IP核。其次就是使用了xilinx的AXI Central Memory Access(CDMA)这个IP去完成数据的搬移。其实这部分类容并不复杂,重要的是大家都不想去花费时间去看xilinx提供的文档。为了能够理解我还是把需要参考的文档发在下面。前面部分是本次设计需要参考的部分文档。
pg194-axi-bridge-pcie-gen3-en-us-3.0.pdf
pg034-axi-cdma.pdf
xapp1171-pcie-central-dma-subsystem.pdf
Xilinx_Answer_65062_AXI_PCIe_Address_Mapping.pdf
ds820_axi_pcie.pdf
关于pcie的相关基础知识和架构,可以参考下面的文档。
pg054-7series-pcie.pdf
整体框图
整体思路如上图所示。我的思路是通过axi bridge for pcie gen3 subsystem IP核的M_AXI接口分成三路,一路去控制AXI Central Memory Access(CDMA)的寄存器,即(DMA控制器寄存器),一路预留bram空间,方便用户自己做寄存器解析。另外一路去控制axi bridge for pcie gen3 subsystem IP核的寄存器空间,这里面主要就是控制其中的AXI Base Address Translation Configuration用于对应AXI基地址转换配置寄存器(就是你DMA操作对应的内存地址)。
在看另外一路,通过对axi_cdma的配置,现在可以启动DMA操作了,关于CDMA就是一个数据搬运的过程,他可以将数据从A搬移到B,这点在前面的文章中已经介绍过了。本次设计只使用最基础的数据搬运,不涉及SG端口(SG端口支持描述符,如果将描述符通过预存机制写入FPGA可以增加DMA的执行效率,这是为什么呢?看完下面的类容你或许会理解)
看连接部分,CDMA的M_AXI部分通过AXI interconnect分为了两部分,一路连接缓存,一路连接axi bridge for pcie gen3 subsystem IP核的S_AXI端口。通过配置CDMA的寄存器空间,实现缓存和pcie之间的数据交换。
IP介绍
axi bridge for pcie gen3 subsystem
关于此IP,他完成了pcie和axi协议之间的转换。即对AXI的突发操作转换为通过pcie发送带数据的TLP包操作。
关于IP的特性在pg194-axi-bridge-pcie-gen3-en-us-3.0.pdf上的P5有相关介绍。
该IP支持最大512字节的有效载荷,支持通过内存映射AXI4接口访问PCIe地址空间,支持PCIe访问内存映射AXI4地址空间。即通过AXI总线读写可以直接转换成对PCIE地址空间的读写,对PCIE的地址空间读写直接转换成对AXI地址空间的读写。作为EP设备时支持6个32位基地址寄存器和3个64位基地址寄存器。
Xilinx有两个IP支持pcie axi bridge,一个就是AXI Bridge for PCI Express Gen3 Subsystem,另外一个是DMA/Bridge Subsystem for PCI Express in AXI Bridge mode即XDMA。
架构框图
关于AXI Bridge for PCI Express Gen3 Subsystem的架构。
从上图结构可以看出来。AXI Bridge for PCI Express Gen3 Subsystem支持三种AXI总线,S_AXI_CTL是对IP本身的寄存器空间的配置。S_AXI从桥处理来自AXI的读写请求,转换为PCIE的读写TLP。M_AXI作为主设备连接到AXI,处理PCIE生成的读写TLP。
关于上面部分的详细解释。
寄存器模块,S_AXI_CTL是配置IP的寄存器,其中最主要的地方是配置axi to pcie translation将AXI内存映射到PCIE地址空间。
从桥slave bridge,对从桥的写事务会根据配置的Max Payload Size(最大载荷尺寸)被转换为一个或多个MemWr TLP(传输层数据包),并传递给PCI Express集成模块。最多支持8个活跃的AXI写请求。当远端AXI主设备发起读事务时,从桥会捕获读地址及控制信号,生成MemRd请求TLP,并启动完成超时计时器。接收到完成TLP后,数据将返回给AXI主设备。最多支持8个活跃AXI读请求。
对于主桥master bridge,主桥处理来自PCI Express集成模块的MemWr和MemRd请求TLP,实现PCIe地址域到AXI4内存映射地址域的转换。每个MemWr请求TLP头部会生成AXI4总线地址和控制信号,并将数据传递给目标AXI4从设备。最多支持8个活跃的PCIe MemWr请求TLP。
Memory map
这里只列举几个重点得寄存器。
bridge register memory map
AXI Base Address Translation Configuration:AXI基地址转换配置寄存器
地址范围:0x208 - 0x234
offset | bits | register |
0x208 | 31-0 |
AXIBAR2PCIEBAR_0U
|
0x20c | 31-0 |
AXIBAR2PCIEBAR_0L
|
0x210 | 31-0 |
AXIBAR2PCIEBAR_1U
|
0x214 | 31-0 |
AXIBAR2PCIEBAR_1L
|
0x218 | 31-0 |
AXIBAR2PCIEBAR_2U
|
0x21c | 31-0 |
AXIBAR2PCIEBAR_2L
|
0x220 | 31-0 | AXIBAR2PCIEBAR_3U |
... | ||
0x234 | 31-0 |
AXIBAR2PCIEBAR_5L
|
AXI基地址转换配置寄存器位定义。
bits | name | core access | result value | description |
31-0 |
Lower Address
|
R/W
|
C_AXIBAR2PCIEBAR_0(31 to 0)
| 生成PCIe地址时--该值将被替换为AXI地址的「最低有效32位」。 |
31-0 |
Upper Address
| R/W |
if (C_AXIBAR2PCIEBAR_0 = 64 bits),
then reset value =
C_AXIBAR2PCIEBAR_0(63 to 32)
if (C_AXIBAR2PCIEBAR_0 = 32 bits),
then reset value = 0x00000000
| 生成PCIe地址时--该值将被替换为AXI地址的「最高有效32位」。 |
... |
AXI transactions for pcie
AXI4 memory-mapped Transactions to AXI4-Stream pcie TLPs
AXI4 Memory-Mapped Transaction
|
AXI4-Stream PCIe TLPs
|
INCR Burst Read of AXIBAR
|
MemRd 32 (3DW)
|
INCR Burst Write to AXIBAR
|
MemWr 32 (3DW)
|
INCR Burst Read of AXIBAR
|
MemRd 64 (4DW)
|
INCR Burst Write to AXIBAR
|
MemWr 64 (4DW)
|
AXI4-Stream pcie TLPs to AXI4 memory-mapped Transactions
AXI4 Memory-Mapped Transaction
|
AXI4-Stream PCIe TLPs
|
MemRd 32 (3DW) of PcieBar
|
INCR Burst Read
|
MemWr 32 (3DW) of PcieBar
|
INCR Burst Write
|
MemRd 64 (4DW) of PcieBar
|
INCR Burst Read
|
MemWr 64 (4DW) of PcieBar
|
INCR Burst Write
|
关于此IP的处理规定,当处理长度超过一个双字(Dword)的Pcie请求时,主AXI接口上的数据突发传输(burst)长度将始终等于AXI数据总线宽度,即使来自PCIe链路的请求长度短于AXI总线宽度。
从桥AXI写选通(wstrb)信号可用于实现数据对齐地址边界。在有效数据周期开始时,写选通信号可为0,并根据该信号计算地址偏移。从桥接口发起的所有事务将由IP核进行必要调整和流量控制。该接口遵循AXI4协议规范,支持最大4KB的突发传输。IP核将根据PCIe最大读请求尺寸(MRRS)、最大有效载荷尺寸(MPS)及读完成边界(RCB)自动拆分事务。因此,AXI域的单次请求可能在PCIe域生成多个请求,IP核会动态调整PCIe请求数量以避免完成缓冲区溢出。
上述详细说明在pg194-axi-bridge-pcie-gen3-en-us-3.0.pdf文档的P65页。
PCIe和AXI总线之间的地址转换和配置
BAR配置与地址转换
BAR寻址机制
- Aperture_Base_Address_n(3.0版本上未开放给用户)
表示GUI中第n个BAR的基地址,在地址转换时视为偏移量0x0。 - Aperture_High_Address_n(3.0版本上未开放给用户)
表示GUI中第n个BAR的最高有效字节地址(地址转换规则详见下文)。 - AXI_to_PCIe_Translation_n
定义第n个BAR的AXI→PCIe地址映射关系。
AXI BAR大小由Aperture_Base_Address_n
与Aperture_High_Address_n
差值决定,需满足,必须为2的幂次方,最小为4K。
发往桥核的PCIE数据包必须落在对应的BAR的基地址与最高地址范围内。如果超出范围,将阻止进入IP。
Address Translation地址转换
支持两种配置方式。
通过IP 界面配置。
寄存器方式配置。
附件cpm4-bridge-v2-1-register.csv下载地址
这里看一下官方给的例子
32位PCIE地址映射配置示例
本示例显示了设置三个独立的AXI BAR和将AXI地址转换为PCIe的远程32位地址空间的通用设置。
AXI BAR0:
Aperture_Base_Address_0:0x00000000_12340000。
Aperture_High_Address_0:0x00000000_1234FFFF。(64K)
AXI_to_PCIe_Translation_0:0x00000000_56710000。
实例向AXI AWADDR写地址通道写入地址0x00000000_12340ABC。这个地址在BASE地址范围内,中间地址为0x0ABC在加上AXI_to_PCIe_Translation_0:0x00000000_56710000。最后PCIE的地址为0x56710ABC。
AXI BAR1:
Aperture_Base_Address_0:0x00000000_ABCDE000。
Aperture_High_Address_0:0x00000000_ABCDFFFF。(8K)
AXI_to_PCIe_Translation_0:0x00000000_FEDCD000。
实例向AXI AWADDR写地址通道写入地址0x00000000_ABCDF123。这个地址在BASE地址范围内,中间地址为0x0ABC在加上AXI_to_PCIe_Translation_0:0x00000000_FEDCD000。最后PCIE的地址为0xFEDC1123。
PCIe将地址转换到远程AXI地址空间
设置两个的64位PCIE的BAR。
PCIe BAR0基地址:0x20000000_ABCD8000。(64位启用)
C_PCIEBAR2AXIBAR_0,PCIE to AXI Translation:0x00000000_12340000,高16位(Bits 63-48)必须为0(AXI地址48位约束),Bits 14-0必须为0,非零值视为无效。
转换流程,输入pcie地址,0x20000000_ABCDFFF4
(访问BAR0),匹配BAR的范围,0x20000000_ABCD8000,32KB到0x20000000_ABCDFFFF。
偏移量 = PCIe地址 - BAR基地址
= 0xABCDFFF4 - 0xABCD8000 = 0x7FF4
AXI地址 = C_PCIEBAR2AXIBAR_0 + 偏移量
= 0x12340000 + 0x7FF4 = 0x0000_12347FF4
最后输出的AXI地址应该是0x0000_12347FF4 。
官方的图片可能存在笔误。详情见pg194-axi-bridge-pcie-gen3-en-us-3.0.pdf文档的P72页。
好了关于这个IP的重点功能这里已经介绍完了,IP实现了PCIE和AXI之间的地址数据转换。用户可以像操作AXI总线去操作PCIE。特别需要注意的地方是PCIE和AXI的地址转换规则。
AXI Central Memory Access(CDMA)
这里使用CDMA来进行数据搬移,将FPGA内部缓存的数据(DDR或者BRAM)通过PCIE发送上位机的内存上。或者将上位机内存中的数据通过PCIE下发到FPGA内部缓存中。实现DMA数据搬移的IP有几种。
AXI Central Memory AccessCDMA:提供高带宽的直接存存储访问,连接内部的源地址和目的地址。
AXI Datamover:AXI Datamover是一个重要的基础IP,Xilinx 所有的DMA IP基本都包含这个模块,该模块可以将AXIS与AXI格式的数据进行转换。
AXI DMA Controller:AXI DMA Controller为AXIS和AXI4接口的转换(数据存储)提供了一个可由软件控制(通过AXI Lite接口实现)的简单方式。
AXI Multichannel DMA:没用过这个,多通道AXI DMA版本。
这里我们使用之前熟悉过的AXI Central Memory AccessCDMA这个IP。
架构框图
从框图中可以看到IP支持三种端口。
M_AXI_SG端口:这个端口可以提高效率。CPU需要执行的操作可以提前做成描述符的形式写入到内存中,SG端口去读取内存中的描述符并控制CDMA寄存器。描述符中会配置当前搬移的源地址和目的地址,本次搬移的数据量。以及下一个描述符的地址。一次搬移完成后会更新链表状态。本次设计不使用描述符。感兴趣的可以去尝试使用描述符提升DMA效率,缓解CPU的压力。
M_AXI端口:通过该端口完成数据搬移。核心就是通过DATA moverIP实现。
S_AXI_LITE:通过这个端口配置CDMA的寄存器。
Register Space
Address Space
Offset
|
Name
|
Description
|
00h |
CDMACR
| CDMA控制寄存器 |
04h |
CDMASR
| CDMA状态寄存器 |
08h |
CURDESC_PNTR
| 当前描述符指针(低32位) |
0ch |
CURDESC_PNTR_MSB
| 当前描述符指针(高32位) |
10h |
TAILDESC_PNTR
| 尾部描述符指针(低32位) |
14h |
TAILDESC_PNTR_MSB
| 尾部描述符指针(高32位) |
18h |
SA
| 源地址(低32位) |
1ch |
SA_MSB
| 源地址(高32位) |
20h |
DA
| 目标地址(低32位) |
24h |
DA_MSB
| 目标地址(高32位) |
28h |
BTT
| 传输字节数寄存器 |
SA (CDMA Source Address – Offset 18h)
此寄存器为AXI CDMA的简单DMA传输提供源地址。
DA (CDMA Destination Address – Offset 20h)
此寄存器为通过AXI CDMA进行简单DMA传输提供目标地址。
BTT (CDMA Bytes to Transfer – Offset 28h)
这个寄存器用于设置AXI CDMA简单DMA(Simple DMA)传输的字节数,写入该寄存器值将自动启动传输。
最多支持67108863Bytes传输。
IP配置
AXI Bridge for Pcie Gen3 Subsystem
第一页,配置PCIE的模式,引脚时钟,AXI时钟。
第二页,配置PCIE的设备号,Vendor ID和Device ID以及Class Code。
第三页,配置PCIE的BAR,设置BAR的大小。以及PCIE to AXI Translation ,PCIE地址转换到AXI总线的地址。这里只使用了BAR0。
第四页,配置PCIE的中断相关的类容。这个默认即可。
第五页,配置AXI BARs。设置AXI to PCIE translations,这里也只设置一个AXI BAR,这里配置可以通过lite总线修改(地址范围:0x208 - 0x234),即将AXI突发的地址转换到PCIE的地址。
第六页,配置请求数量。IP默认参数无法修改。
第七页,配置调试接口,暂时不需要。
AXI Central Memory Access(CDMA)
CDMA配置界面。不使用SG端口。
方案测试
方案测试目前没有暂时没找到合适的硬件验证。详细的测试过程,以及驱动编写再后续继续更新。
使用WinDriver配置CDMA寄存器。这里只使用简单的配置过程。
18:配置源地址。
20:目标地址。
28:突发传输字节数。
还需要配置AXI_PCIE的寄存器。
0x208,20C:AXI和PCIE之间的地址转换。AXI Base Address Translation,这里对应你上位机分配的内存空间,如果是EP与EP之间传输即对应PCIE设备BAR空间(其实也是一段内存)。
启动流程
DMA写入。即从上位机传输数据到PCIE设备。驱动分配一段内存,用于存放数据,比如说创建连续数。一次配置本次分配内存地址到208,20C。配置本次传输的源地址和目标地址。这里是写入,即源地址为AXI_PCIE。目的地址为BRAM空间。配置完成后写入本次突发传输的长度。即28寄存器。开始本次传输。
DMA读出。即从FPGA传输数据到上位机。还是一样的,驱动需要先分配一段内存,用于存放数据。FPGA中数据存入BRAM中,达到设置个数后中断告知上位机。上位机获取到后,启动DMA读取数据,配置源地址和目标地址。这次源地址为BRAM,目标地址为AXI_PCIE。配置本次分配的内存地址208和20C。配置完成后写入本次突发传输的数据长度。开始本次传输。
PTP传输。在一些设备中,需要借用PCIE完成数据大规模传输。在两个EP设备之间高速传输数据,数据流不通过上位机的参与,直接传输到另外一个设备。
启动流程。上位机获取A设备的BAR空间对应的内存地址,上位机获取B设备的BAR空间对应的内存地址。数据流A-B。A数据缓存在BRAM中,达到阈值,启动中断告知上位机。配置本次传输的源地址和目的地址,BRAM到AXI_PCIE。配置B设备分配的内存空间地址到208和20C。配置完成后写入本次突发传输的数据长度。开始本次传输。
一些测试过程的ila信息。
后续总结
本次方案全程使用xilinx提供的IP完成本次DMA操作。需要重点理解其中”PCIe和AXI总线之间的地址转换和配置“这部分内容。另外需要看懂本次设计的DMA传输过程。即本次设计的逻辑框图。理解DMA传输的方式。
本次未涉及驱动部分的代码,驱动部分代码由windriver软件自动生成。详细使用说明可以看我之前的文章。
寄存器配置思路即”启动流程“中描述的过程。
不足的地方,未尝试使用CDMA这个IP的SG模式,导致本次设计方案的PCIE总线效率上不去。详细测试对于GEN3X8效率只能达到百分之十左右,由于博主的驱动编写能力不足。这里就不展示驱动代码了。
提高PCIE总线效率的思路,使用CDMA的SG模式。