AXI_DMA_SG模式 例程代码分析—— polling方式

AXI_DMA_SG模式 例程代码分析—— polling方式

分析代码

这段代码的功能和结构:

  1. 代码概述:
    这是一个用于测试Xilinx AXI DMA(直接内存访问)控制器在Scatter-Gather(分散-聚集)模式下工作的示例程序。它通过DMA进行数据传输并验证数据正确性。

  2. 主要功能模块:

  • 主函数(main):初始化DMA并协调整个测试流程
  • 发送设置(TxSetup):配置DMA发送通道
  • 接收设置(RxSetup):配置DMA接收通道
  • 发送数据(SendPacket):准备并发送测试数据包
  • 检查数据(CheckData):验证接收到的数据
  • 检查DMA结果(CheckDmaResult):等待传输完成并清理资源
  1. 关键参数:
  • 内存基地址:0x01000000
  • 测试数据包长度:0x20字节(32字节)
  • 测试起始值:0xC
  • 使用轮询模式(Polling)而非中断
  1. 工作流程:

    1. 初始化DMA控制器
    2. 配置发送和接收通道的BD(Buffer Descriptor)环
    3. 准备测试数据并发送
    4. 等待传输完成
    5. 验证接收数据是否正确
    6. 释放资源并输出结果
  2. 数据结构:

  • 使用BD环管理数据传输
  • 发送和接收缓冲区分别位于不同的内存区域
  • 每个BD包含缓冲区地址、长度和控制信息
  1. 缓存处理:
  • 在数据传输前后调用了缓存刷新(Xil_DCacheFlushRange)和无效化(Xil_DCacheInvalidateRange)操作,确保数据一致性
  1. 输出信息:
  • 成功时会打印"Successfully ran AXI DMA SG Polling Example"
  • 失败时会打印相应的错误信息

这个示例展示了如何在Scatter-Gather模式下使用AXI DMA控制器进行可靠的数据传输,包括初始化、配置、数据传输和验证的全过程。代码结构清晰,包含了必要的错误检查和状态处理。

在哪里有测试包长的设置?

在代码中,测试包长(Packet Length)的设置出现在以下几个地方:

  1. 主要定义位置(文件顶部宏定义):
#define MAX_PKT_LEN		0x20  // 这里定义了最大包长为0x20(即32字节)
  1. 接收设置中使用(RxSetup函数):
Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
        RxRingPtr->MaxTransferLen);  // 设置每个BD的长度
  1. 发送设置中使用(SendPacket函数):
Status = XAxiDma_BdSetLength(BdPtr, MAX_PKT_LEN,
        TxRingPtr->MaxTransferLen);  // 设置发送BD的长度
  1. 缓存操作中使用:
Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN);  // 刷新发送缓存
Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN);  // 刷新接收缓存
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, MAX_PKT_LEN);  // 无效化接收缓存
  1. 数据初始化中使用(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) 模式中,关键设置主要出现在以下几个部分:

  1. SG模式检测(main函数中):
if(!XAxiDma_HasSg(&AxiDma)) {  // 检查是否配置为SG模式
    xil_printf("Device configured as Simple mode \r\n");
    return XST_FAILURE;
}
  1. 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);
  1. 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);
  1. 内存区域定义(文件顶部):
#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)
  1. 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环的工作流程

SoftwareDMA HardwareMemoryDMABD环预分配BD环和缓冲区初始化BD环(BdRingCreate)配置BD参数(地址、长度等)提交BD(BdRingToHw)根据BD读取/写入数据通过状态位/中断通知完成回收BD(BdRingFree)loop[传输过程]SoftwareDMA HardwareMemoryDMABD环

5. 为什么SG模式需要BD环?

  • 解决非连续传输:单个DMA传输只能处理连续内存,而BD环允许将多个不连续的缓冲区组织成一个逻辑传输流。
  • 降低CPU开销:CPU只需初始化BD环,硬件自动按BD列表处理,无需逐笔干预。
  • 高吞吐量:支持批量提交BD,适合高速数据传输场景。

6. 实际应用场景

  • 网络数据包处理:每个包可能分散在不同内存块
  • 磁盘I/O:文件数据可能非连续存储
  • 视频处理:帧缓冲区可能分片存储

通过BD环,DMA可以像处理连续内存一样高效处理这些零散数据。

详细介绍本例中数据流

在本例的AXI DMA Scatter-Gather(SG)模式测试代码中,数据流涉及初始化、发送、接收和验证四个主要阶段。以下是完整的数据流分析(以代码实际执行顺序展开):


1. 内存与BD环初始化

定义内存布局
分配TX/RX BD空间
创建发送BD环
创建接收BD环
预绑定接收缓冲区到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. 发送数据流

CPUTX_BD_RingDMALoopback_DeviceTX_BUFFER_BASE写入测试数据(0xC,0xD,...0x2B)分配一个空闲BD配置BD(地址=TX_BUFFER_BASE, 长度=32, SOF/EOF标志)提交BD(BdRingToHw)从TX_BUFFER_BASE读取32字节实际硬件中数据通过Loopback回环CPUTX_BD_RingDMALoopback_DeviceTX_BUFFER_BASE
  • 测试数据生成

    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. 接收数据流

DMARX_BD_RingCPULoopback_DeviceRX_BUFFER_BASE回环数据到达(32字节)写入数据到预绑定的缓冲区标记BD状态为完成轮询检查完成的BD(BdRingFromHw)验证数据DMARX_BD_RingCPULoopback_DeviceRX_BUFFER_BASE
  • 关键接收操作

    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. 关键设计要点

  1. 硬件加速

    • DMA直接通过BD环获取缓冲区信息,无需CPU参与数据传输
    • 每个BD的SOF/EOF标志标识数据包边界
  2. 零拷贝设计

    • 发送/接收缓冲区地址固定,避免内存拷贝
    • BD通过物理地址直接操作内存
  3. 缓存一致性

    • 所有DMA操作前后均调用CacheFlush/Invalidate
    • 确保CPU和DMA看到一致的数据视图
  4. 环状缓冲区管理

    • 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数量
  • DelayCoalesce
    • 中断模式下,这两个参数控制中断触发的频率(本例为轮询模式,未使用中断,但仍需设置)。

3. 执行流程

(1)获取发送BD环指针

TxRingPtr = XAxiDma_GetTxRing(&AxiDma);
  • 作用
    • 从DMA实例中获取发送通道的BD环管理结构体XAxiDma_BdRing)。
  • 返回值
    • 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=0Length=0Ctrl=0)。
  • 关键函数
    • 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. 数据流示意图

TxSetup
分配TX BD环内存
初始化BD默认值
启动BD环
硬件可处理BD

6. 实际应用中的扩展

  • 动态BD分配
    • 在更复杂的系统中,BD可以动态分配(而非固定内存),提高灵活性。
  • 多BD批处理
    • 可以一次提交多个BD(提升吞吐量),而非示例中的单BD提交。
  • 中断模式
    • 如果启用中断,需配置CoalesceDelay以优化中断频率。

7. 常见问题

Q1: 为什么需要BdRingClone

  • 保证所有BD初始状态一致,避免未定义行为。

Q2: BdRingStart之后可以修改BD吗?

  • 可以,但需确保硬件未正在使用该BD,否则可能引发竞争条件。

Q3: 如果BD环满了怎么办?

  • SendPacket中,BdRingAlloc会返回XST_FAILURE,需等待硬件释放BD(通过BdRingFromHw)。

总结

TxSetup是DMA SG模式的核心初始化函数,负责:

  1. 配置BD环内存
  2. 设置BD默认属性
  3. 启动发送通道
    它为后续的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)。
  • 返回值
    • 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_BASE0x01100000),即发送缓冲区的物理地址。

(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 字段作用
    BufAddrTX_BUFFER_BASE (0x01100000)数据缓冲区物理地址
    LengthMAX_PKT_LEN (0x20)传输长度 = 32 字节
    Ctrl`TXSOFTXEOF`
    IdPacket 地址用于调试或跟踪 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 中的 BufAddrLength,从内存中搬移数据到目标设备(本例为 Loopback 回环设备)。

4. 数据流示意图

CPUTX_BD_RingDMAMemoryLoopback写入测试数据到 TX_BUFFER_BASE (0xC,0xD...)分配BD (BdRingAlloc)配置BD (地址、长度、控制位)提交BD (BdRingToHw)从 TX_BUFFER_BASE 读取32字节发送数据(实际硬件回环到接收端)CPUTX_BD_RingDMAMemoryLoopback

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 数据传输的核心函数,其关键步骤包括:

  1. 准备数据 → 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)。
  • 返回值
    • 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=0Length=0Ctrl=0)。
  • 关键函数
    • 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. 数据流示意图

DMARX_BD_RingMemory监听数据输入(Loopback回环)将数据写入预绑定的RX_BUFFER_BASE标记BD状态为“完成”硬件自动切换至下一个BDDMARX_BD_RingMemory

5. 关键点总结

操作作用
创建BD环RX_BD_SPACE_BASE 初始化BD环结构。
预绑定接收缓冲区将每个BD的缓冲区地址指向 RX_BUFFER_BASE,形成连续的接收区域。
提交BD到硬件使DMA硬件能够自动将数据写入内存。
启动接收通道激活DMA接收功能,开始监听数据输入。

6. 实际应用扩展

  • 动态缓冲区管理
    • 在更复杂的系统中,可以动态分配接收缓冲区(而非固定地址),提高灵活性。
  • 多BD环交替使用
    • 使用双BD环实现“乒乓缓冲”,避免处理延迟导致的数据丢失。
  • 中断模式优化
    • 如果启用中断,可调整 CoalesceDelay 以减少中断频率。

7. 常见问题

Q1: 为什么需要预绑定接收缓冲区?

  • DMA硬件需提前知道将数据写入何处。预绑定BD后,硬件会自动按BD描述的位置存储数据,无需CPU干预。

Q2: 如果接收缓冲区不够怎么办?

  • CheckDmaResult 中回收已使用的BD,并重新绑定新缓冲区(动态管理)。

Q3: 接收BD需要设置 SOF/EOF 吗?

  • 不需要。接收BD的包边界由硬件自动识别(发送端设置的 SOF/EOF 会被硬件解析)。

总结

RxSetup() 是DMA接收功能的核心初始化函数,其关键步骤包括:

  1. 创建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. 数据流示意图

CPUDMATX_BD_RingRX_BD_RingMemory轮询TX完成 (BdRingFromHw)返回已发送的BD释放BD (BdRingFree)轮询RX完成 (BdRingFromHw)数据已写入RX_BUFFER_BASE验证数据 (CheckData)释放并重新提交BDCPUDMATX_BD_RingRX_BD_RingMemory

5. 关键点总结

操作作用
BdRingFromHw轮询获取已完成的BD(发送/接收)
BdRingFree释放BD资源,避免BD环耗尽
CheckData验证接收数据是否符合预期
BdRingAlloc + BdRingToHw重新绑定接收缓冲区,准备下一次传输

6. 实际应用扩展

  • 超时机制
    • 在轮询 BdRingFromHw 时增加超时检测,避免硬件挂起导致死循环。
  • 批处理优化
    • 一次处理多个BD(而非单个),提升吞吐量。
  • 中断模式改造
    • 用中断替代轮询,降低CPU占用率(需修改 CoalesceDelay 参数)。

7. 常见问题

Q1: 如果数据验证失败怎么办?

  • 可能是DMA传输错误或硬件问题,需检查:
    • 缓存一致性(是否漏了 Xil_DCacheInvalidateRange?)
    • 硬件连接(Loopback是否正常?)

Q2: 为什么需要重新提交接收BD?

  • SG模式要求 显式回收并提交BD,硬件不会自动复用BD。这是与Simple模式的关键区别。

Q3: 如何扩展为持续传输?

  • CheckDmaResult 放入循环中,配合动态缓冲区管理实现连续传输。

总结

CheckDmaResult() 是DMA SG模式中 确保传输可靠性的核心函数,其职责包括:

  1. 轮询完成状态 → 2. 验证数据 → 3. 回收资源 → 4. 准备下一次传输
    它体现了SG模式的核心思想:通过BD环的主动管理,实现高效、可控的数据流
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值