第三章 VDMA基础:架构、寄存器与回环测试

各位同学,今天我们来聊聊VDMA。说实话,VDMA是图像加速里最基础也最关键的模块之一。我刚开始接触Xilinx的VDMA时,也被那一堆寄存器搞得头大。但后来发现,只要理解了它的核心架构,配置起来其实就那么几件事。

3.1 VDMA架构:它到底在干什么?

VDMA,全称Video Direct Memory Access。说白了,就是专门为视频流设计的高速DMA。它跟普通DMA最大的区别是什么?普通DMA搬完一块数据就停了,VDMA可以一直搬、循环搬、同步搬。

我画了一张架构图,你看一眼就明白了:

DDR 内存 帧缓冲区 (多帧缓存) AXI4 (读/写) VDMA 核心 寄存器配置 帧同步控制 地址生成 AXI4-Stream 视频处理 (用户逻辑) AXI4-Lite 图:VDMA 系统架构示意图

VDMA内部有三个关键部分:

  • AXI4主接口:负责跟DDR内存打交道,读写帧数据
  • AXI4-Stream接口:跟你的视频处理逻辑对接,数据流式进出
  • 寄存器控制模块:通过AXI4-Lite配置,决定怎么搬、搬多少、同步方式

嗯,这里要注意:VDMA其实有两个数据通道——读通道和写通道。你可以只用一个,也可以两个都用。回环测试时,我们两个都用。

3.2 寄存器配置:别怕,就这几个关键寄存器

VDMA的寄存器有几十个,但实际项目中常用的不超过10个。我列个表给你看:

偏移地址 寄存器名称 功能描述 我常用的值
0x00 VDMA_CR 控制寄存器,启动/停止、复位 0x00000003
0x04 VDMA_SR 状态寄存器,查错误、查忙闲 只读
0x0C VDMA_PARK 帧同步模式选择 0x00000000
0x30 VDMA_VSIZE 垂直尺寸(行数) 0x00000438 (1080p)
0x34 VDMA_HSIZE 水平尺寸(每行字节数) 0x00002800 (1920*4)
0x38 VDMA_FRMDLY_STRIDE 行跨度(Stride) 0x00002800
0xAC VDMA_START_ADDR_1 帧缓冲区起始地址 0x10000000

配置顺序我建议这样:先配帧尺寸和跨度,再配地址,最后启动。为什么?因为VDMA在启动时会立即检查这些参数,顺序错了它可能罢工。

关键点: HSIZE 是每行的字节数,不是像素数。如果你用RGB888,一个像素4字节,1920像素就是7680字节(0x1E00)。这个坑我踩过,当时图像一直错位,查了半天才发现是HSIZE算错了。

3.3 同步模式:三种模式怎么选?

VDMA有三种同步模式,我直接说人话:

  1. 自动同步模式(Auto Sync):VDMA自己管帧同步,你只管发数据。适合简单的图像传输。
  2. 动态同步模式(Dynamic Sync):通过外部信号控制帧切换。适合需要精确帧控制的场景。
  3. 非同步模式(Non-Sync):VDMA不管帧边界,一直搬数据。适合连续流。

我个人习惯用自动同步模式做回环测试。为什么?省事。你想想看,回环测试的目的是验证通路,不是验证同步逻辑。自动模式下,VDMA内部会处理好帧切换,你只需要关注数据对不对。

小技巧: 在自动同步模式下,VDMA_PARK寄存器的bit[0]要设为0。如果设为1,VDMA会停在当前帧不更新,图像就卡住了。

3.4 实战:VDMA回环测试

好了,理论说完了,咱们直接上手。回环测试的思路很简单:

  • VDMA从DDR读一帧数据 → 送到输出Stream
  • 输入Stream接收数据 → 写回DDR的另一块区域
  • 比较两块区域的数据,看是否一致

我曾经在一个项目中用这个方法来验证VDMA的配置是否正确。那次板子刚上电,图像全是雪花,我一度以为是硬件坏了。后来用回环测试一跑,发现是Stride配错了——图像宽度和行跨度不一致,导致数据错位。

下面是一个简单的配置代码示例(C语言风格):

// 1. 复位VDMA
Xil_Out32(VDMA_BASE + 0x00, 0x00000004);  // 复位
usleep(100);
Xil_Out32(VDMA_BASE + 0x00, 0x00000000);  // 释放复位

// 2. 配置帧参数(1920x1080, RGB888)
Xil_Out32(VDMA_BASE + 0x34, 0x00001E00);  // HSIZE = 7680字节
Xil_Out32(VDMA_BASE + 0x38, 0x00001E00);  // STRIDE = 7680
Xil_Out32(VDMA_BASE + 0x30, 0x00000438);  // VSIZE = 1080行

// 3. 设置帧缓冲区地址
Xil_Out32(VDMA_BASE + 0xAC, 0x10000000);  // 读帧地址
Xil_Out32(VDMA_BASE + 0xB0, 0x20000000);  // 写帧地址

// 4. 启动VDMA(自动同步模式)
Xil_Out32(VDMA_BASE + 0x00, 0x00000003);  // 同时启动读写通道
注意: 回环测试时,读地址和写地址不能重叠!否则VDMA读到的数据可能是自己刚写进去的,造成数据混乱。我建议读地址和写地址至少相隔一帧的大小。

跑完回环测试后,怎么判断结果?两个方法:

  • 软件比对:用CPU把两块内存的数据读出来,逐字节比较。慢但可靠。
  • 硬件校验:在FPGA里加一个CRC校验模块,实时比较。快但需要额外逻辑。

我个人偏好先用软件比对做一次全面检查,确认没问题后,再用硬件校验做在线监控。这样既保证了正确性,又不影响性能。

3.5 避坑指南

最后,分享几个我踩过的坑:

  • Stride必须对齐:AXI总线要求地址对齐到64字节。如果Stride不是64的倍数,VDMA会报错。我曾经因为Stride设成7679(少1字节),折腾了一下午。
  • 帧缓冲区大小要够:一帧的大小 = VSIZE * STRIDE。如果分配的内存不够,VDMA会写到别人的地盘上,后果嘛...系统崩溃。
  • 时钟域问题:VDMA的AXI4接口和Stream接口可以在不同时钟域。但如果你两个时钟频率差太多,建议加FIFO做异步处理。

好了,VDMA的基础就讲到这里。回环测试是验证VDMA配置最直接的方法,建议你在每个新板子上都跑一遍。下一节我们会讲VDMA的中断处理和性能优化,到时候再聊。


可可精品资料库