DMA操作的理解
个人理解DMA是CPU数据搬运的一中机制,在操作硬件平台的时候,DMA一般都是由运行在PC上应用软件控制。使用链式DMA,那么BAR2或BAR3空间配置不能少于256字节。
链式DMA参考设计,其结构允许其传输一大片碎片化的存储器中数据,每块存储器访问时无需再次访问DMA寄存器。因为在每块存储器进行链式DMA传输时,使用了一个描述符表格,该表格中包含了以下一些信息:
l 传输的长度
l 源地址
l 目的地址
l 用于设置应用软件(或BFM驱动)与链式DMA模块之间握手动作的控制位
链式DMA参考设计的仿真测试激励
在通过MegaWizard插件管理器例化PCIe核的时候为自动生成一个链式DMA参考设计,同时包含了该设计仿真时需要的Testbench,具体位于工程目录的子目录_examples/chaining_dma/testbench。该Testbench的顶层命名为_chaining_testbench。
该testbench最多可以仿真PCIex8,而且用户可以选择使用使用PIPE接口或者串行接口来进行仿真,一般PIPE接口的仿真速度更快一些,图1显示了testbench的顶层视图。
图1:链式DMA参考设计仿真测试激励顶层模块(端点设备)
该测试激励的顶层总共例化了四个主要模块:
l _example_chaining_pipen1b - 端点链式DMA参考设计。
l altpcietb_bfm_rp_top_x8_pipen1b - PCIe根端口的BFM模型。
l altpcietb_pipe_phy - 一共例化了8个这样的模块,每个lane例化一个。这些模块完成了根端口和端点中的PIPE MAC层接口的互连。模拟了PIPE PHY层到两侧MAC接口的行为。
l altpcietb_bfm_driver_chaining - 这个模块驱动了到根端口BFM的传输。用户可以修改本模块来改变事物发送到端点参考设计还是用户自己的设计。
当然,除了上述主要模块以外,testbench还有代码来实现其它的一些任务,比如:
l 为端点生成需要的参考时钟
l 在起始阶段提供复位
链式DMA参考设计
所谓的DMA操作是以FPGA作为参考,所以DMA操作分为DMA写操作和DMA读操作,写DMA模块实现了从端点存储器到RC存储器的写操作。读DMA实现了从RC存储器到端点存储器的读操作。板参考设计支持同时DMA读写操作。
前面提到DMA有运行在PC上的应用软件控制,而在进行仿真的时候,testbench提供了一个BFM驱动模块来控制DMA操作。BFM驱动将描述符表写入到BFM共享存储器之中,链式DMA设计引擎就会为DMA读写从该存储器之中连续地收集描述符表格。在输开始时,BFM会编程端点链式DMA控制寄存器。链式DMA控制寄存器包含了描述符表的总数以及第一个描述符表位于BFM共享存储器中的地址。在编程配置完链式DMA控制寄存器后,链式DMA引擎同时为DMA读写连续地从BFM共享存储器中读取描述符,并在随后为每个描述符执行数据传输。如图2所示,为连接了一个外部RC CPU的参考设计的框图。
图2:连接了一个外部RC CPU的参考设计框图
如上图所示,图中主要包括了以下一些单元:
l 端点DMA写和读请求模块。
l 链式DMA与PCIe核之间的Avalon-ST连接接口。
n Avalon-ST RX:从PCIe核接收TLP头和数据信息
n Avalon-ST TX:发送TLP头和数据信息到PCIe核
n Avalon-ST MSI端口:向PCIe核请求MSI中断
n 边带信号:传递诸如配置信息等静态信息的端口
l 位于BFM共享存储器中的DMA读和写描述符表
l 与端点设计对应的RC CPU,使用了一个根端口和一个北/南桥
对于使用硬核来说,可能还会包含其他一些模块,比如ECRC等。下面我们来看看该参考设计的testbench中的应用层实现了哪些目标模块:
l 为设计者显示了如何在用户逻辑中在Avalon-ST模式下与PCIe核连接。
l 提供了一个链式DMA通道用于在PCIe链路上启动存储器读和写事物。
我们说说了这个参考设计的DMA操作模块包括DMA读和DMA写模块,每个DMA模块又包含了以下一些元件:
l 控制寄存器模块 - RC配置控制寄存器(四个DW)从而启动DMA。
l 描述符模块 - DMA引擎从BFM共享存储器获取4DW的描述符。
l 请求模块 - 对于一个给定的描述符,DMA引擎在端点存储器和BFM共享存储器之间执行存储器传输。
参考设计BAR地址映射
基于每个BAR对应的事物,参考设计将收到的存储器事物映射到目标存储器或控制寄存器。表1显示了该参考设计的BAR地址映射。
表1:参考设计的BAR地址映射
BAR存储器 |
映射 |
32-bit BAR0 32-bit BAR1 64-bit BAR1:0 |
映射到32K字节存储器块,使用rc_slave模块访问,并旁路链式DMA |
32-bit BAR2 32-bit BAR3 64-bit BAR3:2 |
映射到DMA读和DMA写控制和状态寄存器,最少256字节 |
32-bit BAR4 32-bit BAR5 64-bit BAR5:4 |
映射到32K字节存储器块,使用rc_slave模块访问,并旁路链式DMA |
扩展ROM BAR |
本参考设计并未实现 |
I/O空间BAR |
本参考设计并未实现 |
链式DMA控制和状态寄存器
应用软件配置位于端点应用设计中的链式DMA控制寄存器,表2描述了控制寄存器的详细内容,其中包括了4DW的DMA写和4DW的DMA读,该寄存器可读可写。
表2:控制寄存器(注:地址为BAR2偏移地址)
地址 |
寄存器名称 |
31..24 |
23..16 |
15..0 |
0x0 |
DMA Wr Cntl DW0 |
控制域 |
描述符表中描述符数量 |
|
0x4 |
DMA Wr Cntl DW1 |
写描述符表的基地址(高比特) |
||
0x8 |
DMA Wr Cntl DW2 |
写描述符表的基地址(低比特) |
||
0xC |
DMA Wr Cntl DW3 |
预留 |
|
RCLAST-ldx中最后一个需要处理的描述符 |
0x10 |
DMA Rd Cntl DW0 |
控制域 |
描述符表中描述符数量 |
|
0x14 |
DMA Rd Cntl DW1 |
读描述符表的基地址(高比特) |
||
0x18 |
DMA Rd Cntl DW2 |
读描述符表的基地址(低比特) |
||
0x1C |
DMA Rd Cntl DW3 |
预留 |
|
RCLAST-ldx中最后一个需要处理的描述符 |
表3则详细描述了表2中的控制域的含义,包括DMA读和DMA写。
表3:控制寄存器中控制域详细描述
比特 |
域 |
描述 |
16 |
预留 |
|
17 |
MSI_ENA |
使能所有描述符的中断。如果为1时,当每个描述符完成时,端点DMA使用MSI向RC申请一个中断。用户应用软件或BFM驱动可以使用这个中断来监测DMA传输的状态。 |
18 |
EPLAST_ENA |
使能端点DMA模块将每个描述符数量写回到描述符表的EPLAST域。 |
24..20 |
MSI Number |
当RC读取端点的MSI容量时,这些寄存器比特会映射到PCIe核的后端MSI信号app_msi_num[4:0]。如果有不止1个MSI存在,且所有MSI可用的时候,默认情况下: l MSI 0 = 读 l MSI 1 = 写 |
30..28 |
MSI Traffic Class |
当RC应用软件读取端点的MSI容量时,被默认赋值为0.这些比特映射到PCIe的后端信号app_msi_tc[2:0] |
31 |
DT RC Last Sync |
如果为0,DMA引擎在最后一个描述符被执行后即停止传输。如果为1,DMA引擎在最后一个描述符被执行后继续从第一个描述符循环操作,要想停止该死循环,将其设置为0即可。 |
以上介绍了控制寄存器,下面我们来看看DMA状态寄存器的定义,这些寄存器都是只读的,如表4所示。
表4:链式DMA状态寄存器定义(注:BAR2地址为BAR2偏移地址)
地址 |
寄存器名称 |
31..24 |
23..16 |
15..0 |
0x20 |
DMA Wr Status Hi |
参考表5定义 |
||
0x24 |
DMA Wr Status Lo |
目标存储器地址位宽 |
写DMA执行计数。(从DMA头配置到最后一个描述符完成的时钟周期,包括获取描述符的时间) |
|
0x28 |
DMA Rd Status Hi |
参考表6定义 |
||
0x2C |
DMA Rd Status Lo |
最大标签(Tag)数量 |
读DMA执行计数。(从DMA头配置到最后一个描述符完成的时钟周期,包括获取描述符的时间) |
|
0x30 |
Error Status |
预留 |
出错计数。由应用层探测到的坏ECRC数量。在ECRC转发被使能的时候才有效 |
表5:DMA写状态高位寄存器比特域定义
比特 |
比特域 |
描述 |
32..28 |
CDMA version |
识别链式DMA设计实例的版本 |
27..26 |
Core type |
识别核的接口。以下定义了编码: l 01:Descriptor/Data接口 l 10:Avalon-ST软核实现 l 00:其它 |
25..24 |
Reserved |
|
23..21 |
Max payload size |
定义了以下编码: l 001:128字节(?) l 001:256字节 l 010: 512字节 l 011:1024字节 l 100:2048字节 |
20..17 |
Reserved |
|
16 |
写DMA描述符FIFO空 |
指示没有写DMA的描述符了 |
15..0 |
写DMA EPLAST |
指示写DMA完成最后一个描述符中的数量 |
表6:DMA读状态高位寄存器比特域定义
比特 |
比特域 |
描述 |
32..25 |
CDMA version |
指示了应用软件使用了那块板子,编码定义如下: l 0:Altera Stratix II GX x1 l 1:Altera Stratix II GX x4 l 2:Altera Stratix II GX x8 l 3:Cyclone II GX x1 l 4:Arria GX x1 l 5:Arria GX x4 l 6:Custom PHY x1 l 7:Custom PHY x4 |
24 |
Reserved |
|
23..21 |
Max payload size |
定义了以下编码: l 001:128字节(?) l 001:256字节 l 010: 512字节 l 011:1024字节 l 100:2048字节 |
20..17 |
Reserved |
|
16 |
读DMA描述符FIFO空 |
指示没有读DMA的描述符了 |
15..0 |
读DMA EPLAST |
指示读DMA完成最后一个描述符中的数量 |
链式DMA描述符表
如表7所示定义了链式DMA描述符表,该表存储在BFM共享存储器之中。改表包含一个4-DW描述符头以及一组连续的个4-DW描述符。端点设备的链式DMA应用访问链式DMA的描述符表是基于以下两个原因:
l 通过迭代方式检索4-DW描述符来启动DMA
l 发送更新状态到端点设备,例如记录已完成描述符数目到描述符头之中
每个后续的描述符包括最少四个双字(4-DW)数据,并对应于一个DMA传输。(一个双字,即DW,等于32比特。)
需要注意的是,链式DMA描述符表不应该超出4K字节的边界。
表7:链式DMA描述符定义
偏移地址 |
描述符类型 |
描述 |
0x0 |
描述符头 |
预留 |
0x4 |
预留 |
|
0x8 |
预留 |
|
0xC |
EPLAST - 当被控制寄存器或描述符中的EPLAST_ENA位使能时,该地址记录由链式DMA模块完成的最后一个描述符的数量。 |
|
0x10 |
描述符0 |
控制域,DMA长度 |
0x14 |
端点地址 |
|
0x18 |
RC地址高位DW(高32位) |
|
0x1C |
RC地址低位DW(低32位) |
|
0x20 |
描述符1 |
控制域,DMA长度 |
0x24 |
端点地址 |
|
0x28 |
RC地址高位DW(高32位) |
|
0x2C |
RC地址低位DW(低32位) |
|
... |
||
0x..0 |
描述符 |
控制域,DMA长度 |
0x..4 |
端点地址 |
|
0x..8 |
RC地址高位DW(高32位) |
|
0x..C |
RC地址低位DW(低32位) |
表8则显示了描述符头后面描述符空间的具体定义
表8:链式DMA描述符格式映射
31..22 |
21..16 |
15..0 |
预留 |
控制域空间(参考表9) |
DMA长度 |
端点地址 |
||
RC地址高32位 |
||
RC地址低32位 |
表9:链式DMA描述符格式映射(控制域空间)
21..18 |
17 |
16 |
预留 |
EPLAST_ENA |
MSI |
每个描述符提供了每次DMA传输的硬件信息,如表10所示详细说明了一个描述符之中各个空间。
表10:链式DMA描述符区域定义
描述符空间 |
端点访问 |
RC访问 |
描述 |
端点地址 |
读 |
读/写 |
指定了端点侧存储器传输的32位基地址 |
RC地址高32位 |
读 |
读/写 |
指定了RC侧存储器传输高32位基地址 |
RC地址低32位 |
读 |
读/写 |
指定了RC侧存储器传输低32位基地址 |
DMA长度 |
读 |
读/写 |
指定了一次DMA传输的双字数量 |
EPLAST_ENA |
读 |
读/写 |
该位和控制寄存器中的EPLAST_ENA位是相或的关系。当该位被设置时,端点DMA模块使用最后完成的描述符编号来更新描述符表中的EPLAST空间,使用的格式<0 - n>,参考表7所示 |
MSI_ENA |
读 |
读/写 |
该位和描述符头中的MSI位是相或的关系。当该位被设置时,端点DMA模块在描述符完成的时候会发送一个中断。 |
测试驱动模块(BFM)
BFM驱动模块是在PCIe核生成阶段一同产生,用于测试链式DMA端点参考设计。BFM驱动模块配置了配置空间寄存器,并且接着测试了参考设计链式DMA通道。BFM测试驱动模块依次执行以下一些步骤:
1. 配置根端口和端点设备的配置空间。BFM驱动模块调用了altpcietb_bfm_configure模块中的ebfm_cfg_rp_ep函数来完成上述配置。
2. 找到一个合适的BAR来访问实例端点设计的控制寄存器空间。BAR2或BAR3必须为一个不小于256字节的存储空间BAR才能完成DMA通道测试。此时调用的是altpcietb_bfm_driver_chaining模块中的find_mem_bar函数。
3. 如果在上一步找到了一个合适的BAR,那么驱动开始执行以下任务:
l DMA读 - 驱动配置链式DMA从BFM共享存储器读取数据到端点存储器。根据描述符控制空间(如表3所示)设定,此时链式DMA完成以下步骤来指示传输完成:
n 链式DMA在完成第一个和最后一个描述符的数据传输后写链式DMA描述符表的EPLAST(如表7所示)位。
n 链式DMA在最后一个描述完成后申请一个MSI中断。
l DMA写 -驱动配置链式DMA将数据从端点存储器写回到BFM共享存储器。表3所示的描述符控制空间此时被指定使得链式DMA通过完成以下步骤来显示传输的完成:
n 链式DMA在完成第一个和最后一个描述符的数据传输后写链式DMA描述符表的EPLAST(如表7所示)位。
n 链式DMA在最后一个描述完成后申请一个MSI中断。
n 写回BFM的数据与之前从BFM读取的数据进行确认。
n 驱动配置链式DMA执行一个测试,该测试演示了下行访问链式DMA端点存储器。
DMA写周期
DMA写操作调用了dma_wr_test函数,具体步骤如下:
1. 配置BFM共享存储器。该配置由三个描述符表完成,分别如表11、表12,表13所示。
表11:写描述符0
|
BFM中的偏移地址 |
值 |
描述 |
DW0 |
0x810 |
82 |
传输长度(双字)和控制位(定义见表3) |
DW1 |
0x814 |
3 |
端点地址 |
DW2 |
0x818 |
0 |
BFM共享存储数据buffer 0高位地址值 |
DW3 |
0x81C |
0x1800 |
BFM共享存储数据buffer 0低位地址值 |
Data Buffer 0 |
0x1800 |
从0x1515_0001开始自增1 |
从地址0x01800到0x1840的BFM共享存储中的数据内容 |
表12:写描述符1
|
BFM中的偏移地址 |
值 |
描述 |
DW0 |
0x820 |
1024 |
传输长度(双字)和控制位(定义见表8) |
DW1 |
0x824 |
0 |
端点地址 |
DW2 |
0x828 |
0 |
BFM共享存储数据buffer 1高位地址值 |
DW3 |
0x82C |
0x2800 |
BFM共享存储数据buffer 1低位地址值 |
Data Buffer 1 |
0x2800 |
从0x2525_0001开始自增1 |
BFM共享存储中从地址0x02800开始的数据内容 |
表13:写描述符3
|
BFM中的偏移地址 |
值 |
描述 |
DW0 |
0x830 |
644 |
传输长度(双字)和控制位(定义见表3) |
DW1 |
0x834 |
0 |
端点地址 |
DW2 |
0x838 |
0 |
BFM共享存储数据buffer 2高位地址值 |
DW3 |
0x83C |
0x57A0 |
BFM共享存储数据buffer 2低位地址值 |
Data Buffer 1 |
0x57A0 |
从0x3535_0001开始自增1 |
BFM共享存储中从地址0x057A0开始的数据内容 |
2. 建立起链式DMA描述符头,并开启从端点存储器到BFM共享存储器的数据传输。该数据传输调用了dma_set_header函数,这个函数写了表14所示四个双字到DMA写寄存器模块。
表14:为DMA写建立的DMA控制寄存器
|
控制寄存器的偏移地址(BAR2) |
值 |
描述 |
DW0 |
0x830 |
3 |
描述符数量和控制位(定义见表2) |
DW1 |
0x834 |
0 |
BFM共享存储描述符表高位地址 |
DW2 |
0x838 |
0x800 |
BFM共享存储描述符表低位地址 |
DW3 |
0x83C |
2 |
最后一个有效描述符 |
3. 通过轮询BFM共享存储器位置0x80C查询并等待DMA写完成,DMA写会更新已完成描述符表到此地址。调用rcmen_poll和msi_poll函数来确定和DMA写传输已经完成。
DMA读周期
使用函数dma_rd_test来完成DMA读,具体步骤如下所示:
1. 通过调用dma_set_rd_desc_data函数来配置BFM共享存储器,该配置主要设置如表15、表16以及表17所示的3个描述符表。
表15:读描述符0
|
BFM中的偏移地址 |
值 |
描述 |
DW0 |
0x910 |
82 |
传输长度(双字)和控制位(定义见表8) |
DW1 |
0x914 |
3 |
端点地址 |
DW2 |
0x918 |
0 |
BFM共享存储数据buffer 0高位地址值 |
DW3 |
0x91C |
0x8DF0 |
BFM共享存储数据buffer 0低位地址值 |
Data Buffer 0 |
0x8DF0 |
从0xAAA0_0001开始自增1 |
从地址0x8DF0开始的BFM共享存储中的数据内容 |
由于受篇幅限制,后面两个描述符表这里不再列出,其实格式以及说明与第一个描述符类似,只是便宜地址不同而已。
2. 调用函数dma_set_header通过写四个DW,即DW0:DW3,如表18所示,到DMA读寄存器模块来设置链式DMA描述符头,并开始从BFM共享存储器到端点存储器传输数据。
表18:DMA写时DMA控制寄存器的设置
|
DMA控制寄存器偏移地址(BAR2) |
值 |
描述 |
DW0 |
0x0 |
3 |
描述符数量及控制位(定义见表2) |
DW1 |
0x14 |
0 |
BFM共享存储器高位地址值 |
DW2 |
0x18 |
0x900 |
BFM共享存储器低位地址值 |
DW3 |
0x1C |
2 |
写最后一个描述符 |
在写完描述符头的最后一个双字(DW3)之后,DMA读开启随后的三个连续的数据传输。
3. 通过轮询BFM共享存储器地址0x90C来等待并确认DMA读是否完成,DMA读引擎会将完成的描述数量值不断地更新到该地址。可以通过调用函数rcmem_poll和msi_poll
来确认DMA何时已经完成。
用户3684765 2016-5-31 17:37
用户610368 2015-12-12 15:07
用户120550 2008-1-20 15:00
真有用。
用户1463907 2007-11-30 12:23
哎,可惜学得太浅了,看不懂
用户1167543 2006-9-22 18:52
用户1053025 2006-9-20 11:26
用户55312 2006-9-19 21:50