ZYNQ AXIDMA详解

本文深入解析AXIDMA在ZYNQ中的角色与操作模式,包括Direct Register Mode与Scatter/Gather Mode的区别,以及多通道与循环模式的工作原理,探讨了数据缓存带来的Cache一致性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载出处:

https://www.cnblogs.com/batianhu/p/zynq_axidma_xiangjie1.html

一、基本概念

AXIDMA: 官方解释是为内存与AXI4-Stream外设之间提供高带宽的直接存储访问,其可选的scatter/gather功能可将CPU从数据搬移任务中解放出来。在ZYNQ中,AXIDMA就是FPGA访问DDR3的桥梁,不过该过程受ARM的监控和管理。使用其他的IP(也是AXI4-Stream转AXI4-MM)可以不需要ARM管理,但是在SOC开发时是危险的,这是后话了。

如图1所示,AXIDMA IP有6个接口,S_AXI_LITE是ARM配置dma寄存器的接口,M_AXI_SG是从(往)存储器加载(上传)buffer descriptor的接口,剩下4个构成两对接口,S2MM和MM2S表示数据的方向,AXI是存储器一侧的接口,AXIS是FPGA一侧的接口。AXIDMA IP和ARM自带的DMA是很像的,只不过不具备从存储器到存储器的功能,当然啦如果将S2MM和MM2S的AXIS接口直接连接也是可以实现的。image

图1

 

scatter gather模式:AXIDMA工作模式分为两种,分别是Direct Register Mode和Scatter/Gather Mode。

从图2可以看出,Direct Register Mode具备DMA的基本功能,除了控制寄存器和状态寄存器之外,给出源(目的)地址和传输长度之后就可以开启一次传输了。Direct Register Mode的特点(也是缺点)是配置完一次寄存器之后只能完成存储器连续地址空间的读写,如果有需求往不同地址空间搬运数据的话,那就需要重新配置寄存器开启一次新的传输。

image

图2

 

鉴于Direct Register Mode的不足,发展出了Scatter/Gather Mode,其工作方式要复杂得多。Scatter/Gather Mode把关于传输的基本参数(比如起始地址、传输长度、包信息等)存储在存储器中,一套参数称之为Buffer Descriptor(简称BD),在工作过程中通过上面提到的SG接口来加载BD并且更新BD中的状态。从图3可以看出,Scatter/Gather Mode下的寄存器列表中没有了Address、Length相关的寄存器了,取而代之的是CURDESC、TAILDESC。

image

图3

 

非多通道模式下的BD如图4所示,主要有四部分内容:NXTDESC、BUFFER_ADDRESS、CONTROL、STATUS。NXTDESC指定下一个BD的地址,由此可以构成成一个BD链条,AXIDMA可以顺着该链条依次fetch BD。BUFFER_ADDRESS指定传输的源(目的)地址,CONTROL主要是length和包信息,STATUS反应该BD完成后的状态。

image

图4

 

AXIDMA启动后,首先从CURDESC指定的位置加载BD,完成当前BD的传输任务后根据BD链条找到下一个BD,依次完成BD指定的传输,直到遇到TAILDESC指定的BD才停止。

Multichannel DMA:在Scatter/Gather Mode下S2MM和MM2S都支持多个通道,Direct Register Mode不支持多通道。如图5所示,多通道相比非多通道,BD中增加了TID和TDEST,用来区分不同的通道。

image

image

 

图5

 

多通道模式支持2D-Transfer,如图6所示,从buffer address开始,读写HSIZE后跳过剩余的Stride - HSIZE个地址单元,下一次从buffer address + Stride位置开始,此过程迭代VSIZE次后结束该BD指定的传输。

image

图6

 

如图7所示,Multichannel模式下,S2MM有16个通道,每个通道都有独立的CURDESC和TAILDESC寄存器,而CR和SR则是共用的。令人费解的是,从对称的角度来说,MM2S理应每个通道也要有独立的CURDESC和TAILDESC寄存器,但事实是多个通道共用一份。手册上的说法是:

MM2S is similar to normal AXI DMA operation. When MM2S_CURDESC and
MM2S_TAILDESC are programmed by software, AXI DMA fetches a chain of descriptors and
processes until it reaches tail descriptor. In AXI DMA, TDEST, TID, and TUSER fields are
assumed to remain constant for an entire packet as defined in the descriptors. That is, each
packet transfer across a logical channel defined by (TDEST, TID, TUSER) runs to completion
before the DMA transfers another packet. Although packet transfers for multiple channels
can be interleaved, after started, each must run to completion before another transfer can
occur. It is your responsibility to avoid deadlock scenarios under this assumption. The AXI
DMA does not signal error conditions if the (TDEST, TID, TUSER) fields within the descriptors
do not adhere to these assumptions. It is up to the software to maintain consistency.

从以上文字来看,MM2S端只能等当前包传输完才能开始下一次的传输,我猜测这与CPU不太容易同时操纵多个通道的数据包发送有关系。所以在实际使用时只能先执行一个通道的发送任务再执行另一个通道的发送任务,虽然很不爽,但是还是能用的。

不知道以上理解是否有误,如果哪位同学能提供更好的解释的话,请在评论区留言。

image

图7

 

Cyclic DMA:循环模式是在Scatter/Gather模式下的一种独特工作方式,在Multichannel Mode下不可用。正常情况下的Scatter/Gather模式在遇到Tail BD就应该结束当前的传输,但是如果使能了Cyclic模式的话,在遇到Tail BD时会忽略completed位,并且回到First BD,这一过程会一直持续直到遇到错误或者人为中止,图8比较形象地表示了这一过程。Cyclic模式只需要在开启传输前设置好BD链条,工作之后就再也不需要管了。要做到遇到Tail BD时返回到First BD,手册中提供的方法是:

In this setup the Tail BD points back to the first BD. The Tail Descriptor register does not
serve any purpose and is used only to trigger the DMA engine.

Program the Tail Descriptor register with some value which is not a part of the BD chain. Say
for example 0x50.

image

图8

 

Data Cache:从图9中可以看出,在ZYNQ内部ARM CPU与DDR3之间存在两级缓存区,分别是L1 I/D Cache和L2 Cache,它们都是32-byte line size。Data Cache的使用带来了一个问题,DMA和CPU都与DDR3有数据往来,可CPU的Cache是不知道DMA对DDR3的数据读写过程的,也就是说CPU得到的数据很可能是”假的“,这就是著名的Cache一致性问题。解决该问题的办法是在程序中使用flush函数(invalid函数)及时将Cache的数据写入到DDR3(从DDR3读取数据到Cache),也就是说要避免该问题就只能靠我们自己了。image

图9

### STM32 ADC多通道DMA采样及均值计算 #### 配置环境与初始化 为了实现STM32的ADC多通道DMA采样并计算平均值,首先需要通过STM32CubeMX配置项目。选择目标MCU型号(如STM32F103C8或STM32F407),然后添加ADC外设及其DMA支持[^1]。 在STM32CubeMX界面中,点击`Add`按钮来增加ADC模块对应的DMA流/通道,并将工作模式设定为循环模式(Circular Mode),这能确保数据持续不断地被转移到内存缓冲区直到程序停止该过程[^2]。 #### 编写代码逻辑 完成硬件抽象层(HAL)库的基础设置之后,在应用程序源文件里定义必要的宏常量用于指定参与测量的信道数量以及每条路径上获取样本的数量: ```c #define NUM_CHANNELS 2 /* 被测物理量数目 */ #define NUM_SAMPLES_PER_CHANNEL 100 /* 单次取样次数 */ uint32_t dma_adc_buffer[NUM_CHANNELS * NUM_SAMPLES_PER_CHANNEL]; // DMA接收缓存 uint32_t dma_adc_buffer_av[NUM_CHANNELS]; // 平均值保存位置 ``` 接着编写函数处理接收到的数据,这里提供了一个简单的例子展示如何遍历所有收集到的结果进而求得各路输入信号强度的算术平均数: ```c void CalculateAverage(void){ int i, j; for(i = 0; i < NUM_CHANNELS ; ++i){ // 对每一个通道执行操作 uint32_t sum = 0; for(j = 0; j < NUM_SAMPLES_PER_CHANNEL; ++j){ sum += dma_adc_buffer[i * NUM_SAMPLES_PER_CHANNEL + j]; } dma_adc_buffer_av[i] = (sum / NUM_SAMPLES_PER_CHANNEL); // 计算平均值 } } ``` 此段代码会逐一遍历由DMA传输过来的数据集(`dma_adc_buffer`)中的每一项,累加以获得总后再除以样品总数得到最终结果存储至`dma_adc_buffer_av[]`数组内表示各个传感器输出电压水平的大致情况[^4]。 #### 注意事项 - **稳定性考量**: 实际应用过程中可能会遇到噪声干扰等问题造成读数波动较大;因此建议适当调整`NUM_SAMPLES_PER_CHANNEL`参数大小以平衡响应速度与精度之间的关系。 - **资源占用评估**: 当涉及多个模拟输入端口的同时监测时,请务必确认所选微控制器具备足够的内部RAM空间容纳整个批次的数据记录。 - **校准机制引入**: 如果发现某些特定条件下存在系统误差,则可以在软件层面加入补偿算法修正偏差影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值