AXI_DMA_SG模式 例程代码分析—— polling方式
分析代码
这段代码的功能和结构:
-
代码概述:
这是一个用于测试Xilinx AXI DMA(直接内存访问)控制器在Scatter-Gather(分散-聚集)模式下工作的示例程序。它通过DMA进行数据传输并验证数据正确性。 -
主要功能模块:
- 主函数(main):初始化DMA并协调整个测试流程
- 发送设置(TxSetup):配置DMA发送通道
- 接收设置(RxSetup):配置DMA接收通道
- 发送数据(SendPacket):准备并发送测试数据包
- 检查数据(CheckData):验证接收到的数据
- 检查DMA结果(CheckDmaResult):等待传输完成并清理资源
- 关键参数:
- 内存基地址:0x01000000
- 测试数据包长度:0x20字节(32字节)
- 测试起始值:0xC
- 使用轮询模式(Polling)而非中断
-
工作流程:
- 初始化DMA控制器
- 配置发送和接收通道的BD(Buffer Descriptor)环
- 准备测试数据并发送
- 等待传输完成
- 验证接收数据是否正确
- 释放资源并输出结果
-
数据结构:
- 使用BD环管理数据传输
- 发送和接收缓冲区分别位于不同的内存区域
- 每个BD包含缓冲区地址、长度和控制信息
- 缓存处理:
- 在数据传输前后调用了缓存刷新(Xil_DCacheFlushRange)和无效化(Xil_DCacheInvalidateRange)操作,确保数据一致性
- 输出信息:
- 成功时会打印"Successfully ran AXI DMA SG Polling Example"
- 失败时会打印相应的错误信息
这个示例展示了如何在Scatter-Gather模式下使用AXI DMA控制器进行可靠的数据传输,包括初始化、配置、数据传输和验证的全过程。代码结构清晰,包含了必要的错误检查和状态处理。
在哪里有测试包长的设置?
在代码中,测试包长(Packet Length)的设置出现在以下几个地方:
- 主要定义位置(文件顶部宏定义):
#define MAX_PKT_LEN 0x20 // 这里定义了最大包长为0x20(即32字节)
- 接收设置中使用(RxSetup函数):
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
RxRingPtr->MaxTransferLen); // 设置每个BD的长度
- 发送设置中使用(SendPacket函数):
Status = XAxiDma_BdSetLength(BdPtr, MAX_PKT_LEN,
TxRingPtr->MaxTransferLen); // 设置发送BD的长度
- 缓存操作中使用:
Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN); // 刷新发送缓存
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN); // 刷新接收缓存
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, MAX_PKT_LEN); // 无效化接收缓存
- 数据初始化中使用(SendPacket函数):
for(Index = 0; Index < MAX_PKT_LEN; Index ++) { // 按照包长初始化测试数据
TxPacket[Index] = Value;
Value = (Value + 1) & 0xFF;
}
如果要修改测试包长,只需要修改顶部的宏定义 MAX_PKT_LEN
即可,所有相关操作都会自动使用新的长度值。当前设置为0x20(32字节),这是相对较小的测试包长,适合基础功能验证。
在SG模式代码中什么地方在设置?
在Scatter-Gather (SG) 模式中,关键设置主要出现在以下几个部分:
- SG模式检测(main函数中):
if(!XAxiDma_HasSg(&AxiDma)) { // 检查是否配置为SG模式
xil_printf("Device configured as Simple mode \r\n");
return XST_FAILURE;
}
- BD环创建(TxSetup/RxSetup函数中):
// 发送通道BD环创建
Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE,
TX_BD_SPACE_BASE,
XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
// 接收通道BD环创建
Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,
RX_BD_SPACE_BASE,
XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
- BD描述符设置(关键SG配置):
// 在RxSetup函数中:
Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr); // 设置缓冲区地址
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN, // 设置传输长度
RxRingPtr->MaxTransferLen);
XAxiDma_BdSetCtrl(BdCurPtr, 0); // 设置控制字
XAxiDma_BdSetId(BdCurPtr, RxBufferPtr); // 设置BD ID
// 在SendPacket函数中:
XAxiDma_BdSetCtrl(BdPtr, XAXIDMA_BD_CTRL_TXEOF_MASK | // 设置开始/结束标志
XAXIDMA_BD_CTRL_TXSOF_MASK);
- 内存区域定义(文件顶部):
#define TX_BD_SPACE_BASE (MEM_BASE_ADDR) // 发送BD空间起始
#define TX_BD_SPACE_HIGH (MEM_BASE_ADDR + 0x00000FFF)
#define RX_BD_SPACE_BASE (MEM_BASE_ADDR + 0x00001000) // 接收BD空间起始
#define RX_BD_SPACE_HIGH (MEM_BASE_ADDR + 0x00001FFF)
- SG模式特有操作:
// BD环提交硬件(关键SG操作)
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
// 从硬件获取已处理的BD
ProcessedBdCount = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
SG模式的核心是通过BD(Buffer Descriptor)环来管理多个不连续的内存缓冲区传输。每个BD包含:
- 数据缓冲区地址
- 传输长度
- 控制标志(如SOF/EOF)
- 状态信息
代码中通过XAxiDma_BdRing
系列API来管理这些BD,实现了真正的Scatter-Gather功能。
什么是DB环
BD环(Buffer Descriptor Ring)是DMA Scatter-Gather(SG)模式的核心数据结构,用于高效管理多个不连续的内存缓冲区传输。以下是详细解释:
1. BD(Buffer Descriptor)是什么?
BD是一个硬件数据结构,每个BD描述一个数据缓冲区的传输参数,通常包含以下字段(具体硬件相关):
- 缓冲区物理地址:数据在内存中的位置
- 传输长度:本次传输的字节数
- 控制标志:如传输开始(SOF)、结束(EOF)、中断使能等
- 状态标志:由硬件更新,如传输完成、错误状态等
- 下一个BD指针(可选):形成链表或环状结构
2. 为什么需要"环"(Ring)结构?
- 高效复用:BD环是预分配的BD组成的环形队列,硬件按顺序消费BD,软件回收后重复使用,避免频繁内存分配。
- 流水线支持:允许同时存在多个进行中的传输(硬件处理当前BD时,软件可准备后续BD)。
3. 代码中的BD环关键操作
在示例代码中,BD环的管理流程如下:
(1) 创建BD环(初始化)
// 计算BD数量(基于内存区域大小和对齐要求)
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
// 在指定内存区域创建BD环
Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,
RX_BD_SPACE_BASE, XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
(2) 配置BD参数(每个缓冲区)
// 设置缓冲区地址和长度
XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN, RxRingPtr->MaxTransferLen);
// 设置控制标志(如SOF/EOF)
XAxiDma_BdSetCtrl(BdPtr, XAXIDMA_BD_CTRL_TXEOF_MASK | XAXIDMA_BD_CTRL_TXSOF_MASK);
(3) 提交BD给硬件(启动传输)
// 将一组BD提交给DMA硬件
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
(4) 回收已完成的BD
// 从硬件获取已处理的BD
ProcessedBdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
// 回收BD供后续重用
Status = XAxiDma_BdRingFree(RxRingPtr, ProcessedBdCount, BdPtr);
4. BD环的工作流程
5. 为什么SG模式需要BD环?
- 解决非连续传输:单个DMA传输只能处理连续内存,而BD环允许将多个不连续的缓冲区组织成一个逻辑传输流。
- 降低CPU开销:CPU只需初始化BD环,硬件自动按BD列表处理,无需逐笔干预。
- 高吞吐量:支持批量提交BD,适合高速数据传输场景。
6. 实际应用场景
- 网络数据包处理:每个包可能分散在不同内存块
- 磁盘I/O:文件数据可能非连续存储
- 视频处理:帧缓冲区可能分片存储
通过BD环,DMA可以像处理连续内存一样高效处理这些零散数据。
详细介绍本例中数据流
在本例的AXI DMA Scatter-Gather(SG)模式测试代码中,数据流涉及初始化、发送、接收和验证四个主要阶段。以下是完整的数据流分析(以代码实际执行顺序展开):
1. 内存与BD环初始化
-
关键地址定义:
#define TX_BD_SPACE_BASE 0x01000000 // 发送BD环起始地址 #define RX_BD_SPACE_BASE 0x01001000 // 接收BD环起始地址 #define TX_BUFFER_BASE 0x01100000 // 发送数据缓冲区 #define RX_BUFFER_BASE 0x01300000 // 接收数据缓冲区
-
接收BD预绑定:
for (Index = 0; Index < FreeBdCount; Index++) { XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr); // 绑定缓冲区地址 XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN); // 设置包长32字节 RxBufferPtr += MAX_PKT_LEN; // 移动缓冲区指针 }
2. 发送数据流
-
测试数据生成:
for(Index = 0; Index < MAX_PKT_LEN; Index++) { TxPacket[Index] = Value; // 数据内容:0xC,0xD,0xE...递增 Value = (Value + 1) & 0xFF; }
-
缓存一致性处理:
Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN); // 确保数据写入物理内存
3. 接收数据流
-
关键接收操作:
while ((ProcessedBdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr)) == 0) { // 轮询等待接收完成 }
-
数据验证前缓存处理:
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, MAX_PKT_LEN); // 确保读取最新数据
4. 数据验证
-
逐字节比对:
for(Index = 0; Index < MAX_PKT_LEN; Index++) { if (RxPacket[Index] != Value) { // 预期值:0xC,0xD,0xE... xil_printf("Data error at %d: %x/%x\r\n", Index, RxPacket[Index], Value); return XST_FAILURE; } Value = (Value + 1) & 0xFF; }
-
成功输出:
Successfully ran AXI DMA SG Polling Example
5. 关键设计要点
-
硬件加速:
- DMA直接通过BD环获取缓冲区信息,无需CPU参与数据传输
- 每个BD的
SOF/EOF
标志标识数据包边界
-
零拷贝设计:
- 发送/接收缓冲区地址固定,避免内存拷贝
- BD通过物理地址直接操作内存
-
缓存一致性:
- 所有DMA操作前后均调用
CacheFlush/Invalidate
- 确保CPU和DMA看到一致的数据视图
- 所有DMA操作前后均调用
-
环状缓冲区管理:
- BD环空间预分配,避免运行时内存分配
- 通过
BdRingFree
回收BD实现资源复用
6. 性能优化启示
- BD批处理:示例中每次提交1个BD,实际应用可批量提交
- 双缓冲技术:可扩展为交替使用两组BD环提升吞吐量
- 中断模式:当前为轮询,实际系统可改用中断通知传输完成
这个示例完整展示了SG模式下DMA数据传输的全生命周期,是理解Xilinx AXI DMA核心操作的经典案例。
TxSetup函数详解
TxSetup
函数负责初始化DMA发送通道(TX Channel)的BD环(Buffer Descriptor Ring),为后续的数据传输做好准备。以下是逐部分的详细解析:
1. 函数原型
static int TxSetup(XAxiDma *AxiDmaInstPtr)
- 参数:
AxiDmaInstPtr
:指向DMA实例的指针(本例中为全局变量AxiDma
)。
- 返回值:
XST_SUCCESS
:初始化成功XST_FAILURE
:初始化失败
2. 关键变量
XAxiDma_BdRing *TxRingPtr; // 发送BD环指针
XAxiDma_Bd BdTemplate; // BD模板(用于初始化BD)
int Delay = 0; // 延迟计数(用于中断触发)
int Coalesce = 1; // 中断聚合阈值(每完成1个BD触发中断)
int Status; // 操作状态
u32 BdCount; // BD环的BD数量
Delay
和Coalesce
:- 在中断模式下,这两个参数控制中断触发的频率(本例为轮询模式,未使用中断,但仍需设置)。
3. 执行流程
(1)获取发送BD环指针
TxRingPtr = XAxiDma_GetTxRing(&AxiDma);
- 作用:
- 从DMA实例中获取发送通道的BD环管理结构体(
XAxiDma_BdRing
)。
- 从DMA实例中获取发送通道的BD环管理结构体(
- 返回值:
TxRingPtr
:指向TX BD环的指针,后续所有BD操作都基于此指针。
(2)禁用发送中断
XAxiDma_BdRingIntDisable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
- 作用:
- 由于本例使用轮询模式(Polling),禁用所有发送中断(避免硬件触发不必要的中断)。
- 关键宏:
XAXIDMA_IRQ_ALL_MASK
:禁用所有中断类型(完成、错误等)。
(3)设置BD环的延迟和聚合
XAxiDma_BdRingSetCoalesce(TxRingPtr, Coalesce, Delay);
- 作用:
- 配置BD环的中断触发条件(尽管本例未使用中断,但仍需设置默认值)。
- 参数:
Coalesce = 1
:每完成1个BD后触发中断(最小单位)。Delay = 0
:无额外延迟。
(4)计算BD环的容量
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
TX_BD_SPACE_HIGH - TX_BD_SPACE_BASE + 1);
- 作用:
- 计算TX BD环可容纳的最大BD数量。
- 关键参数:
XAXIDMA_BD_MINIMUM_ALIGNMENT
:BD的对齐要求(通常为32字节或64字节)。TX_BD_SPACE_HIGH - TX_BD_SPACE_BASE + 1
:TX BD环的总内存大小(本例为0x1000
字节,即4KB)。
(5)创建BD环
Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE,
TX_BD_SPACE_BASE, XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
- 作用:
- 在指定的内存区域(
TX_BD_SPACE_BASE
)初始化BD环。
- 在指定的内存区域(
- 参数:
TxRingPtr
:BD环管理结构体。TX_BD_SPACE_BASE
:BD环的物理基地址(0x01000000
)。XAXIDMA_BD_MINIMUM_ALIGNMENT
:BD对齐要求。BdCount
:BD数量(由BdRingCntCalc
计算得出)。
- 返回值:
XST_SUCCESS
:创建成功。XST_FAILURE
:创建失败(如内存不足或对齐错误)。
(6)初始化BD模板
XAxiDma_BdClear(&BdTemplate); // 清空BD模板
Status = XAxiDma_BdRingClone(TxRingPtr, &BdTemplate); // 复制模板到BD环
- 作用:
- 所有BD初始化为相同的默认值(如
BufferAddr=0
,Length=0
,Ctrl=0
)。
- 所有BD初始化为相同的默认值(如
- 关键函数:
XAxiDma_BdClear
:清零BD结构体。XAxiDma_BdRingClone
:将模板应用到整个BD环。
(7)启动发送BD环
Status = XAxiDma_BdRingStart(TxRingPtr);
- 作用:
- 激活发送通道,使DMA硬件可以开始处理BD环中的描述符。
- 返回值:
XST_SUCCESS
:启动成功。XST_FAILURE
:启动失败(如硬件错误)。
4. 关键点总结
操作 | 作用 |
---|---|
XAxiDma_GetTxRing | 获取发送BD环指针 |
BdRingIntDisable | 禁用中断(轮询模式) |
BdRingSetCoalesce | 设置中断触发条件(本例未使用中断,但仍需设置) |
BdRingCntCalc | 计算BD环容量 |
BdRingCreate | 在指定内存区域创建BD环 |
BdClear + BdRingClone | 初始化所有BD为默认值 |
BdRingStart | 启动BD环,使DMA硬件可处理BD |
5. 数据流示意图
6. 实际应用中的扩展
- 动态BD分配:
- 在更复杂的系统中,BD可以动态分配(而非固定内存),提高灵活性。
- 多BD批处理:
- 可以一次提交多个BD(提升吞吐量),而非示例中的单BD提交。
- 中断模式:
- 如果启用中断,需配置
Coalesce
和Delay
以优化中断频率。
- 如果启用中断,需配置
7. 常见问题
Q1: 为什么需要BdRingClone
?
- 保证所有BD初始状态一致,避免未定义行为。
Q2: BdRingStart
之后可以修改BD吗?
- 可以,但需确保硬件未正在使用该BD,否则可能引发竞争条件。
Q3: 如果BD环满了怎么办?
- 在
SendPacket
中,BdRingAlloc
会返回XST_FAILURE
,需等待硬件释放BD(通过BdRingFromHw
)。
总结
TxSetup
是DMA SG模式的核心初始化函数,负责:
- 配置BD环内存
- 设置BD默认属性
- 启动发送通道
它为后续的SendPacket
提供了可用的BD环结构,确保DMA能正确识别和管理发送缓冲区。
SendPacket() 函数深度分析
SendPacket()
是 AXI DMA Scatter-Gather (SG) 模式中触发数据传输的关键函数,负责准备数据并提交到 DMA 发送通道。以下是逐部分的详细解析:
1. 函数原型
static int SendPacket(XAxiDma *AxiDmaInstPtr)
- 参数:
AxiDmaInstPtr
:指向 DMA 实例的指针(本例中为全局变量AxiDma
)。
- 返回值:
XST_SUCCESS
:数据提交成功XST_FAILURE
:提交失败(如 BD 分配失败或参数错误)
2. 关键变量
XAxiDma_BdRing *TxRingPtr; // 发送BD环指针
u8 *TxPacket; // 发送数据缓冲区指针
u8 Value; // 测试数据初始值(0xC)
XAxiDma_Bd *BdPtr; // 当前操作的BD指针
int Status; // 操作状态
int Index; // 循环索引
3. 执行流程
(1)获取发送BD环指针
TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
- 作用:
- 从 DMA 实例中获取发送通道的 BD 环管理结构体(
XAxiDma_BdRing
)。
- 从 DMA 实例中获取发送通道的 BD 环管理结构体(
- 返回值:
TxRingPtr
:指向 TX BD 环的指针,用于后续 BD 操作。
(2)初始化测试数据
TxPacket = (u8 *)Packet; // 指向发送缓冲区(TX_BUFFER_BASE)
Value = TEST_START_VALUE; // 初始值 = 0xC
for (Index = 0; Index < MAX_PKT_LEN; Index++) {
TxPacket[Index] = Value; // 填充数据:0xC, 0xD, 0xE...
Value = (Value + 1) & 0xFF; // 递增并限制为8位
}
- 数据模式:
- 缓冲区内容为连续递增值(
0xC, 0xD, 0xE,..., 0x2B
),共 32 字节(MAX_PKT_LEN=0x20
)。
- 缓冲区内容为连续递增值(
- 内存布局:
Packet
指向TX_BUFFER_BASE
(0x01100000
),即发送缓冲区的物理地址。
(3)缓存一致性处理
Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN);
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN);
- 作用:
- 刷新 CPU 缓存,确保 DMA 能读取到最新的数据。
- 在启用数据缓存(DCache)的系统中,CPU 写入的数据可能暂存在缓存中,需手动刷到内存供 DMA 访问。
- 关键点:
- 刷新发送缓冲区(
TxPacket
)和接收缓冲区(RX_BUFFER_BASE
),避免后续验证时读取旧数据。
- 刷新发送缓冲区(
(4)分配一个空闲 BD
Status = XAxiDma_BdRingAlloc(TxRingPtr, 1, &BdPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE; // BD 分配失败
}
- 作用:
- 从 TX BD 环中分配 1 个空闲 BD,用于描述本次传输。
- 参数:
TxRingPtr
:发送 BD 环指针。1
:请求分配的 BD 数量。&BdPtr
:返回分配的 BD 指针。
- 失败场景:
- BD 环已满(需等待硬件释放 BD)。
(5)配置 BD 参数
// 设置缓冲区地址
Status = XAxiDma_BdSetBufAddr(BdPtr, (UINTPTR)Packet);
if (Status != XST_SUCCESS) {
xil_printf("Tx set buffer addr failed %d\r\n", Status);
return XST_FAILURE;
}
// 设置传输长度
Status = XAxiDma_BdSetLength(BdPtr, MAX_PKT_LEN, TxRingPtr->MaxTransferLen);
if (Status != XST_SUCCESS) {
xil_printf("Tx set length failed %d\r\n", Status);
return XST_FAILURE;
}
// 设置控制标志(SOF + EOF)
XAxiDma_BdSetCtrl(BdPtr, XAXIDMA_BD_CTRL_TXEOF_MASK | XAXIDMA_BD_CTRL_TXSOF_MASK);
// 设置 BD 标识(可选,用于调试)
XAxiDma_BdSetId(BdPtr, (UINTPTR)Packet);
-
关键字段:
BD 字段 值 作用 BufAddr
TX_BUFFER_BASE
(0x01100000)数据缓冲区物理地址 Length
MAX_PKT_LEN
(0x20)传输长度 = 32 字节 Ctrl
`TXSOF TXEOF` Id
Packet
地址用于调试或跟踪 BD
(6)提交 BD 到硬件
Status = XAxiDma_BdRingToHw(TxRingPtr, 1, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Submit BD to hardware failed %d\r\n", Status);
return XST_FAILURE;
}
- 作用:
- 将配置好的 BD 提交给 DMA 硬件,触发数据传输。
- 参数:
TxRingPtr
:发送 BD 环指针。1
:提交的 BD 数量。BdPtr
:要提交的 BD 指针。
- 硬件行为:
- DMA 读取 BD 中的
BufAddr
和Length
,从内存中搬移数据到目标设备(本例为 Loopback 回环设备)。
- DMA 读取 BD 中的
4. 数据流示意图
5. 关键点总结
操作 | 作用 |
---|---|
数据初始化 | 填充测试模式(0xC,0xD,…)到发送缓冲区 |
缓存刷新 | 确保 DMA 能读取最新数据(Xil_DCacheFlushRange ) |
BD 分配 | 从 BD 环中获取一个空闲 BD(BdRingAlloc ) |
BD 配置 | 设置缓冲区地址、长度、控制标志(BdSetBufAddr , BdSetLength , BdSetCtrl ) |
BD 提交 | 触发 DMA 硬件启动传输(BdRingToHw ) |
6. 实际应用扩展
- 多 BD 批处理:
- 可一次分配并提交多个 BD,提升吞吐量(需配合更大 BD 环)。
- 零拷贝优化:
- 直接让 BD 指向应用层缓冲区,避免内存拷贝(需确保缓存一致性)。
- 错误恢复:
- 检查
BdRingToHw
的返回值,处理硬件错误(如 DMA 引擎故障)。
- 检查
7. 常见问题
Q1: 如果 BD 环满了怎么办?
BdRingAlloc
会返回XST_FAILURE
,需等待硬件完成传输并调用BdRingFree
回收 BD。
Q2: 为什么需要设置 SOF/EOF
?
- 在 SG 模式中,
SOF
(Start of Frame)和EOF
(End of Frame)标识数据包的边界,确保 DMA 正确处理不连续的缓冲区。
Q3: 缓存刷新是否可以省略?
- 如果系统禁用缓存或使用一致性缓存(如
ACP
端口),可省略。但默认情况下必须刷新,否则 DMA 可能读取到旧数据。
总结
SendPacket()
是 DMA 数据传输的核心函数,其关键步骤包括:
- 准备数据 → 2. 分配 BD → 3. 配置 BD → 4. 提交硬件
它通过 BD 环机制实现了高效的零拷贝数据传输,是理解 Xilinx AXI DMA SG 模式的关键。
RxSetup() 函数深度解析
RxSetup()
负责 初始化DMA接收通道(RX Channel)的BD环(Buffer Descriptor Ring),并预绑定接收缓冲区,使DMA硬件能够自动将接收到的数据写入指定内存区域。以下是逐部分的详细解析:
1. 函数原型
static int RxSetup(XAxiDma *AxiDmaInstPtr)
- 参数:
AxiDmaInstPtr
:指向DMA实例的指针(本例中为全局变量AxiDma
)。
- 返回值:
XST_SUCCESS
:初始化成功XST_FAILURE
:初始化失败
2. 关键变量
XAxiDma_BdRing *RxRingPtr; // 接收BD环指针
int Delay = 0; // 中断延迟(轮询模式下未使用)
int Coalesce = 1; // 中断聚合阈值(每完成1个BD触发中断)
XAxiDma_Bd BdTemplate; // BD模板(用于初始化BD)
XAxiDma_Bd *BdPtr; // BD指针(批量分配时使用)
XAxiDma_Bd *BdCurPtr; // 当前操作的BD指针
u32 BdCount; // BD环的总容量
u32 FreeBdCount; // 当前空闲BD数量
UINTPTR RxBufferPtr; // 接收缓冲区指针
int Index; // 循环索引
3. 执行流程
(1)获取接收BD环指针
RxRingPtr = XAxiDma_GetRxRing(&AxiDma);
- 作用:
- 从DMA实例中获取接收通道的BD环管理结构体(
XAxiDma_BdRing
)。
- 从DMA实例中获取接收通道的BD环管理结构体(
- 返回值:
RxRingPtr
:指向RX BD环的指针,后续所有BD操作基于此指针。
(2)禁用接收中断
XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
- 作用:
- 本例使用 轮询模式(Polling),禁用所有接收中断(避免硬件触发不必要的中断)。
- 关键宏:
XAXIDMA_IRQ_ALL_MASK
:禁用所有中断类型(完成、错误等)。
(3)设置BD环的延迟和聚合
XAxiDma_BdRingSetCoalesce(RxRingPtr, Coalesce, Delay);
- 作用:
- 配置BD环的中断触发条件(尽管本例未使用中断,但仍需设置默认值)。
- 参数:
Coalesce = 1
:每完成 1个BD 后触发中断(最小单位)。Delay = 0
:无额外延迟。
(4)计算BD环的容量
BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
- 作用:
- 计算RX BD环可容纳的 最大BD数量。
- 关键参数:
XAXIDMA_BD_MINIMUM_ALIGNMENT
:BD的对齐要求(通常为32字节或64字节)。RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1
:RX BD环的总内存大小(本例为0x1000
字节,即4KB)。
(5)创建BD环
Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,
RX_BD_SPACE_BASE, XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
- 作用:
- 在指定内存区域(
RX_BD_SPACE_BASE
)初始化BD环。
- 在指定内存区域(
- 参数:
RxRingPtr
:BD环管理结构体。RX_BD_SPACE_BASE
:BD环的物理基地址(0x01001000
)。XAXIDMA_BD_MINIMUM_ALIGNMENT
:BD对齐要求。BdCount
:BD数量(由BdRingCntCalc
计算得出)。
- 返回值:
XST_SUCCESS
:创建成功。XST_FAILURE
:创建失败(如内存不足或对齐错误)。
(6)初始化BD模板
XAxiDma_BdClear(&BdTemplate); // 清空BD模板
Status = XAxiDma_BdRingClone(RxRingPtr, &BdTemplate); // 复制模板到BD环
- 作用:
- 所有BD初始化为相同的默认值(如
BufferAddr=0
,Length=0
,Ctrl=0
)。
- 所有BD初始化为相同的默认值(如
- 关键函数:
XAxiDma_BdClear
:清零BD结构体。XAxiDma_BdRingClone
:将模板应用到整个BD环。
(7)绑定接收缓冲区到BD
FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr); // 获取空闲BD数量
Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr); // 分配所有空闲BD
BdCurPtr = BdPtr;
RxBufferPtr = RX_BUFFER_BASE; // 接收缓冲区起始地址(0x01300000)
for (Index = 0; Index < FreeBdCount; Index++) {
// 设置BD的缓冲区地址
Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
if (Status != XST_SUCCESS) {
xil_printf("Set buffer addr failed on BD %x\r\n", (UINTPTR)BdCurPtr);
return XST_FAILURE;
}
// 设置BD的传输长度
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN, RxRingPtr->MaxTransferLen);
if (Status != XST_SUCCESS) {
xil_printf("Set length failed on BD %x\r\n", (UINTPTR)BdCurPtr);
return XST_FAILURE;
}
// 设置BD控制字(接收BD通常不需要SOF/EOF)
XAxiDma_BdSetCtrl(BdCurPtr, 0);
// 设置BD标识(可选,用于调试)
XAxiDma_BdSetId(BdCurPtr, RxBufferPtr);
RxBufferPtr += MAX_PKT_LEN; // 移动缓冲区指针
BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr); // 获取下一个BD
}
-
关键操作:
步骤 作用 BdRingGetFreeCnt
获取当前空闲的BD数量(本例中为BD环的全部容量)。 BdRingAlloc
分配所有空闲BD,用于预绑定接收缓冲区。 BdSetBufAddr
将BD的缓冲区地址指向 RX_BUFFER_BASE
(物理地址)。BdSetLength
设置每个BD的传输长度为 MAX_PKT_LEN
(32字节)。BdSetCtrl
接收BD通常不需要设置控制标志(硬件自动处理SOF/EOF)。 BdSetId
可选操作,用于调试或跟踪BD与缓冲区的关联关系。
(8)清空接收缓冲区
memset((void *)RX_BUFFER_BASE, 0, MAX_PKT_LEN);
- 作用:
- 将接收缓冲区初始化为0,确保后续数据验证时能检测到DMA写入的新数据。
(9)提交BD到硬件
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Submit BD to hardware failed %d\r\n", Status);
return XST_FAILURE;
}
- 作用:
- 将所有配置好的BD 提交给DMA硬件,使硬件能够将接收到的数据写入预绑定的缓冲区。
- 参数:
RxRingPtr
:接收BD环指针。FreeBdCount
:提交的BD数量(本例为全部空闲BD)。BdPtr
:要提交的BD指针。
(10)启动接收通道
Status = XAxiDma_BdRingStart(RxRingPtr);
if (Status != XST_SUCCESS) {
xil_printf("Start RX BD ring failed %d\r\n", Status);
return XST_FAILURE;
}
- 作用:
- 激活接收通道,使DMA硬件开始监听数据输入并写入预绑定的缓冲区。
- 返回值:
XST_SUCCESS
:启动成功。XST_FAILURE
:启动失败(如硬件错误)。
4. 数据流示意图
5. 关键点总结
操作 | 作用 |
---|---|
创建BD环 | 在 RX_BD_SPACE_BASE 初始化BD环结构。 |
预绑定接收缓冲区 | 将每个BD的缓冲区地址指向 RX_BUFFER_BASE ,形成连续的接收区域。 |
提交BD到硬件 | 使DMA硬件能够自动将数据写入内存。 |
启动接收通道 | 激活DMA接收功能,开始监听数据输入。 |
6. 实际应用扩展
- 动态缓冲区管理:
- 在更复杂的系统中,可以动态分配接收缓冲区(而非固定地址),提高灵活性。
- 多BD环交替使用:
- 使用双BD环实现“乒乓缓冲”,避免处理延迟导致的数据丢失。
- 中断模式优化:
- 如果启用中断,可调整
Coalesce
和Delay
以减少中断频率。
- 如果启用中断,可调整
7. 常见问题
Q1: 为什么需要预绑定接收缓冲区?
- DMA硬件需提前知道将数据写入何处。预绑定BD后,硬件会自动按BD描述的位置存储数据,无需CPU干预。
Q2: 如果接收缓冲区不够怎么办?
- 在
CheckDmaResult
中回收已使用的BD,并重新绑定新缓冲区(动态管理)。
Q3: 接收BD需要设置 SOF/EOF
吗?
- 不需要。接收BD的包边界由硬件自动识别(发送端设置的
SOF/EOF
会被硬件解析)。
总结
RxSetup()
是DMA接收功能的核心初始化函数,其关键步骤包括:
- 创建BD环 → 2. 预绑定缓冲区 → 3. 提交BD → 4. 启动通道
它为DMA硬件提供了 自动存储接收数据的能力,是实现高效零拷贝接收的关键。
CheckDmaResult() 函数深度解析
CheckDmaResult()
是 DMA Scatter-Gather (SG) 模式中 轮询检查传输结果、验证数据并回收资源的核心函数。它确保数据完整传输,并清理BD环以供后续使用。以下是逐部分的详细解析:
1. 函数原型
static int CheckDmaResult(XAxiDma *AxiDmaInstPtr)
- 参数:
AxiDmaInstPtr
:指向DMA实例的指针(本例中为全局变量AxiDma
)。
- 返回值:
XST_SUCCESS
:数据传输成功且数据正确XST_FAILURE
:传输失败或数据错误
2. 关键变量
XAxiDma_BdRing *TxRingPtr; // 发送BD环指针
XAxiDma_BdRing *RxRingPtr; // 接收BD环指针
XAxiDma_Bd *BdPtr; // 当前处理的BD指针
int ProcessedBdCount; // 已完成的BD数量
int FreeBdCount; // 当前空闲BD数量
int Status; // 操作状态
3. 执行流程
(1)获取发送/接收BD环指针
TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
RxRingPtr = XAxiDma_GetRxRing(AxiDmaInstPtr);
- 作用:
- 获取发送和接收通道的BD环管理结构体,用于后续轮询和资源回收。
(2)检查发送完成(TX Channel)
while ((ProcessedBdCount = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr)) == 0) {
// 轮询等待发送完成
}
- 关键函数:
XAxiDma_BdRingFromHw
:从硬件获取已完成的BD。XAXIDMA_ALL_BDS
:请求返回所有已完成的BD。&BdPtr
:返回完成的BD链表头指针。
- 行为:
- 如果返回
ProcessedBdCount = 0
,表示无BD完成,继续轮询;否则退出循环。
- 如果返回
(3)释放发送BD
Status = XAxiDma_BdRingFree(TxRingPtr, ProcessedBdCount, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed to free %d TX BDs: %d\r\n", ProcessedBdCount, Status);
return XST_FAILURE;
}
- 作用:
- 将已使用的发送BD 归还到BD环的空闲队列,供后续传输复用。
- 关键点:
- 必须显式释放BD,否则BD环会耗尽,导致后续传输失败。
(4)检查接收完成(RX Channel)
while ((ProcessedBdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr)) == 0) {
// 轮询等待接收完成
}
- 行为:
- 轮询直到DMA硬件将数据写入接收缓冲区,并标记BD为完成状态。
(5)验证接收数据
if (CheckData() != XST_SUCCESS) {
return XST_FAILURE; // 数据校验失败
}
-
CheckData()
内部逻辑:u8 *RxPacket = (u8 *)RX_BUFFER_BASE; for (int i = 0; i < MAX_PKT_LEN; i++) { if (RxPacket[i] != (TEST_START_VALUE + i) % 256) { xil_printf("Data mismatch at byte %d\r\n", i); return XST_FAILURE; } }
- 作用:
- 检查接收缓冲区数据是否与发送的递增序列(
0xC, 0xD,...
)匹配。
- 检查接收缓冲区数据是否与发送的递增序列(
- 作用:
(6)释放接收BD
Status = XAxiDma_BdRingFree(RxRingPtr, ProcessedBdCount, BdPtr);
if (Status != XST_SUCCESS) {
xil_printf("Failed to free %d RX BDs: %d\r\n", ProcessedBdCount, Status);
return XST_FAILURE;
}
- 作用:
- 回收已使用的接收BD,避免BD环耗尽。
(7)重新提交接收BD(准备下次传输)
FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr); // 获取空闲BD数量
Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr); // 分配所有空闲BD
if (Status != XST_SUCCESS) {
xil_printf("Failed to alloc RX BDs: %d\r\n", Status);
return XST_FAILURE;
}
Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr); // 提交BD到硬件
if (Status != XST_SUCCESS) {
xil_printf("Failed to submit RX BDs: %d\r\n", Status);
return XST_FAILURE;
}
- 作用:
- 重新绑定接收缓冲区并提交BD,使DMA能继续接收新数据。
- 关键点:
- 此操作是 SG模式的核心,通过循环使用BD环实现持续数据传输。
4. 数据流示意图
5. 关键点总结
操作 | 作用 |
---|---|
BdRingFromHw | 轮询获取已完成的BD(发送/接收) |
BdRingFree | 释放BD资源,避免BD环耗尽 |
CheckData | 验证接收数据是否符合预期 |
BdRingAlloc + BdRingToHw | 重新绑定接收缓冲区,准备下一次传输 |
6. 实际应用扩展
- 超时机制:
- 在轮询
BdRingFromHw
时增加超时检测,避免硬件挂起导致死循环。
- 在轮询
- 批处理优化:
- 一次处理多个BD(而非单个),提升吞吐量。
- 中断模式改造:
- 用中断替代轮询,降低CPU占用率(需修改
Coalesce
和Delay
参数)。
- 用中断替代轮询,降低CPU占用率(需修改
7. 常见问题
Q1: 如果数据验证失败怎么办?
- 可能是DMA传输错误或硬件问题,需检查:
- 缓存一致性(是否漏了
Xil_DCacheInvalidateRange
?) - 硬件连接(Loopback是否正常?)
- 缓存一致性(是否漏了
Q2: 为什么需要重新提交接收BD?
- SG模式要求 显式回收并提交BD,硬件不会自动复用BD。这是与Simple模式的关键区别。
Q3: 如何扩展为持续传输?
- 将
CheckDmaResult
放入循环中,配合动态缓冲区管理实现连续传输。
总结
CheckDmaResult()
是DMA SG模式中 确保传输可靠性的核心函数,其职责包括:
- 轮询完成状态 → 2. 验证数据 → 3. 回收资源 → 4. 准备下一次传输
它体现了SG模式的核心思想:通过BD环的主动管理,实现高效、可控的数据流。