尧图精选

AXI VDMA 图像通路设计:从原理到实战的完整指南

🕒 发布时间:2026/10/1 15:07:14 📁 来源:尧图网络
AXI VDMA 这个东西刚接触 Zynq 或者 MicroBlaze 软核做视频/图像通路的朋友十有八九会在它身上卡一阵子。我第一次用 VDMA 做摄像头采集到 HDMI 显示的时候脑子里全是问号为什么帧缓存要开三个S2MM 和 MM2S 到底谁进谁出帧同步信号没接对图像直接撕裂成两半。后来把 AXI VDMA 的寄存器手册翻了几遍又拿 ILA 抓了好几轮波形才算把它的脾气摸清楚。这篇就按我自己的理解路径把 AXI VDMA 的工作原理和设计思路从头捋一遍尽量说人话让刚上手 FPGA 图像处理的朋友少走点弯路。1. 先搞清楚 VDMA 到底在系统里扮演什么角色1.1 从“数据搬运”这件事说起FPGA 做图像处理绕不开一个核心矛盾图像数据量太大而片上 BRAM 太小。哪怕你用的是 1080P、30 帧、RGB888 的视频流一帧就是 1920×1080×3 字节差不多 6MB。Zynq-7000 系列里 BRAM 资源多的型号也就几百 KB 到一两 MB 级别根本存不下一帧完整图像。所以图像数据必须放到外部 DDR 里处理的时候再搬进搬出。那谁来搬你可以自己写一个 AXI4 Master 的读写逻辑手动控制地址递增、突发长度、握手信号。但这样做的代价是你得自己管理帧地址切换、自己处理跨时钟域、自己保证带宽。一旦视频分辨率或者帧率变了逻辑还得跟着改。AXI VDMA 就是 Xilinx 官方给出的“专业搬运工”它把 AXI4 读写通道、地址生成、帧缓存管理、同步信号处理全部封装好你只需要配置几个寄存器它就能自动把数据从 DDR 搬到 PL 端或者从 PL 端搬回 DDR。用一句话概括VDMA 是连接 AXI4-Stream 视频流和 AXI4 内存映射DDR之间的高速数据通道控制器。它不处理图像内容只负责搬运但搬运这件事它做得比你自己写逻辑要稳得多。1.2 VDMA 和 DMA、CDMA 的区别在哪很多人一开始会混淆 DMA、CDMA 和 VDMA。普通的 AXI DMA 适合做“一次性”的大块数据传输比如你把一段音频数据从 DDR 搬到外设传完就完事。CDMA 更偏向于内存到内存的搬运。而 VDMA 的“V”代表 Video它的设计目标就是持续不断的视频流。视频流的特点是数据是连续不断的帧与帧之间有固定的时间间隔而且通常需要多帧缓存来避免撕裂。VDMA 针对这些特点做了专门优化比如支持最多 32 个帧缓存Frame Buffer、支持帧同步信号Frame Sync、支持循环模式Circular Mode自动循环读写。这些功能普通 DMA 要么没有要么需要你自己在软件层面去补。还有一个关键区别VDMA 的 AXI4-Stream 接口是专门为视频流设计的支持 AXI4-Stream Video 协议里的 TUSER 信号作为帧起始标志SOFTLAST 作为行结束标志EOL。这些信号和视频时序直接对应用起来比裸 AXI4-Stream 要顺手得多。1.3 什么场景下你必须用 VDMA不是所有 FPGA 图像项目都需要 VDMA。如果你只是做一个简单的直通显示摄像头数据直接进 HDMI 输出中间不做任何处理那确实不需要 DDR 缓存也就不需要 VDMA。但只要你涉及以下任意一种情况VDMA 基本就是标配图像处理算法需要多帧参考比如帧间差分、运动检测、3D 降噪这些算法需要同时访问当前帧和前一帧甚至前几帧的数据。分辨率或帧率不匹配摄像头输出 1080P30fps但你的处理流水线只能跑 720P60fps中间需要缓存来做格式转换。需要做图像缩放或旋转这类操作通常需要先写入 DDR再从 DDR 读出因为行缓存不够大。多路视频流切换或叠加比如画中画、多画面分割需要把多路视频先存到 DDR再按需读取混合。我个人的经验是只要你的图像处理链路里出现了“帧”这个概念并且需要对整帧进行操作VDMA 就值得上。它带来的稳定性提升远超过你手写 AXI Master 省下来的那点逻辑资源。2. VDMA 内部到底怎么运转的2.1 读写通道的独立架构AXI VDMA 内部有两个完全独立的通道MM2SMemory Map to Stream和S2MMStream to Memory Map。这两个通道各自有独立的寄存器组、独立的地址生成器、独立的 AXI4 接口。你可以只用一个也可以两个同时用。MM2S 负责从 DDR 读数据转成 AXI4-Stream 输出给 PL 端。S2MM 负责从 PL 端接收 AXI4-Stream 数据写入 DDR。两个通道之间没有直接的硬件连接它们通过共享 DDR 来交换数据。这种架构的好处是灵活你可以做纯采集只用 S2MM、纯显示只用 MM2S也可以做回环S2MM 写入MM2S 读出。每个通道内部又分为数据搬运路径和地址生成路径。数据搬运路径就是 AXI4 读写通道负责实际的数据传输。地址生成路径则根据配置的帧缓存地址、帧长度、行数等参数自动生成每一拍传输的地址。这两条路径是并行工作的地址生成器提前算好下一个突发传输的地址数据通道只管搬效率很高。2.2 帧缓存与循环模式的工作机制VDMA 最核心的概念就是帧缓存Frame Buffer。你可以把它理解为一组环形排列的“停车位”每个车位能停一帧图像。VDMA 支持最多 32 个帧缓存但实际项目中一般用 3 个就够了。为什么是 3 个这是视频处理里的经典“三缓冲”策略。假设你用 2 个缓存写入端正在写第 1 帧读出端同时在读第 0 帧。当写入端写完第 1 帧准备写第 0 帧时如果读出端还没读完第 0 帧就会冲突。3 个缓存就解决了这个问题写入端写第 2 帧时读出端可以读第 0 帧或第 1 帧总有至少一个缓存是空闲的。VDMA 的循环模式Circular Mode就是让地址生成器在多个帧缓存之间自动循环。你只需要在初始化时把 3 个帧缓存的基地址写进寄存器VDMA 就会按照 Park 指针的顺序依次使用它们。Park 指针的切换时机由帧同步信号或者软件写寄存器来控制。这里有个容易踩的坑帧缓存的地址必须对齐到 AXI 突发传输的边界。AXI4 的突发传输最大是 256 拍每拍 8 字节64 位数据宽度所以一次突发最多传 2048 字节。如果你的帧缓存基地址没有对齐到 2048 字节VDMA 可能会在跨帧边界时产生错误的地址计算。我一般会把帧缓存地址对齐到 4096 字节图个安心。2.3 帧同步信号VDMA 的“节拍器”帧同步信号是 VDMA 最容易被忽视、也最容易出问题的地方。VDMA 支持两种同步模式内部同步Internal Sync和外部同步External Sync。内部同步模式下VDMA 自己生成帧同步信号按照你配置的帧长度和行数来计数。这种模式适合数据源和 VDMA 在同一个时钟域、且时序完全可控的场景。比如你用 FPGA 内部逻辑生成测试图案就可以用内部同步。外部同步模式下VDMA 等待外部输入的帧同步信号比如摄像头的 VSYNC来触发帧切换。这种模式适合数据源是异步的、或者需要和外部设备严格同步的场景。摄像头采集基本都用外部同步。外部同步又分为主模式Master和从模式Slave。主模式下VDMA 输出同步信号给外部设备从模式下VDMA 接收外部设备的同步信号。做摄像头采集时通常把 S2MM 配置成从模式接收摄像头的 VSYNC 作为帧同步把 MM2S 配置成主模式输出同步信号给显示控制器。注意帧同步信号的极性、宽度、与有效数据之间的延迟关系必须和你的视频源严格匹配。我见过有人因为 VSYNC 极性设反了图像上下颠倒排查了一整天。2.4 AXI4-Stream 接口的信号细节VDMA 的 AXI4-Stream 接口不是裸的 AXI4-Stream而是遵循AXI4-Stream Video 协议。这个协议在标准 AXI4-Stream 的基础上定义了 TUSER 和 TLAST 的特定含义TUSER[0]帧起始标志SOF在每帧第一个像素的第一个 beat 拉高。TLAST行结束标志EOL在每行最后一个像素的最后一个 beat 拉高。TDATA像素数据可以是 8/16/24/32 位取决于你配置的数据宽度。TREADY/TVALID标准的 AXI4-Stream 握手信号。这里有个细节VDMA 的 S2MM 通道在接收数据时会根据 TLAST 来判断一行是否结束根据 TUSER 来判断一帧是否开始。如果你自己写的视频源没有正确产生这些信号VDMA 就会把整帧数据当成一行来处理结果就是图像完全错乱。我建议在调试阶段用 ILA 抓一下 TUSER 和 TLAST确认它们和视频时序对齐。3. 从零搭建一个 VDMA 图像通路的设计思路3.1 系统框图与数据流向一个典型的 VDMA 图像处理系统包含以下几个部分视频源摄像头、HDMI 输入、或者 FPGA 内部生成的测试图案。VDMA连接视频源和 DDR 的桥梁。DDR 控制器Zynq 的 PS 端 DDR 控制器或者 MicroBlaze 系统的 MIG。视频输出HDMI 输出、LCD 控制器、或者回到 PL 做进一步处理。控制接口PS 端的 AXI-Lite 接口用来配置 VDMA 寄存器。数据流向是这样的摄像头输出 AXI4-Stream 视频流进入 VDMA 的 S2MM 通道VDMA 把数据写入 DDR 的帧缓存。然后 MM2S 通道从 DDR 读出数据输出给显示控制器。PS 端通过 AXI-Lite 配置 VDMA 的寄存器控制帧缓存的地址、数量、同步模式等。这个框图看起来简单但实际搭建时有很多细节要注意。比如时钟域摄像头通常有自己的像素时钟DDR 控制器跑的是 AXI 时钟VDMA 需要跨时钟域处理。Xilinx 的 VDMA IP 内部有异步 FIFO 来处理这个问题但你需要在 IP 配置里正确设置时钟频率否则 FIFO 深度不够会导致数据丢失。3.2 帧缓存数量的选择与地址规划前面说了三缓冲是经典方案但具体用几个缓存还要看你的应用场景。如果你做的是实时显示三缓冲足够。如果你做的是多帧降噪可能需要 4 到 5 个缓存因为算法要同时访问多帧历史数据。如果你做的是高帧率慢处理比如 240fps 采集但只处理 30fps那缓存数量要按帧率比例来算。地址规划方面我一般会在 DDR 里划出一块连续的区域专门给 VDMA 用。比如 DDR 从 0x00000000 开始系统和其他应用占了前 256MB那我就从 0x10000000 开始划 64MB 给 VDMA。每个帧缓存的大小 分辨率 × 每像素字节数 × 对齐系数。以 1080P RGB888 为例一帧是 1920×1080×3 6220800 字节约 5.93MB。对齐到 4MB 边界的话每个缓存占 8MB三个缓存就是 24MB。64MB 的空间绰绰有余。这里有个经验帧缓存地址最好在 DDR 的物理地址空间里连续。虽然 VDMA 支持分散的帧缓存地址但连续地址可以让 DDR 控制器的行缓冲命中率更高带宽利用率更好。我在一个项目里试过把三个帧缓存分散到 DDR 的不同区域结果带宽掉了将近 20%。3.3 寄存器配置的关键参数VDMA 的寄存器不少但核心的就那么几个。我按初始化顺序列一下复位 VDMA写 0x00000004 到控制寄存器然后清掉。配置帧缓存地址MM2S 和 S2MM 各有自己的帧缓存地址寄存器组从 0x00000000 偏移开始每个缓存占 4 字节。配置帧延迟这个参数决定 VDMA 在切换帧缓存之前等待多少个同步信号。一般设为缓存数量减一。配置 Park 指针决定 VDMA 当前使用哪个帧缓存。初始化时设为 0。配置同步模式设置内部/外部同步、主/从模式、同步信号极性等。配置数据宽度和行数设置每行的字节数、每帧的行数、行跨度Stride。启动通道写 1 到运行控制寄存器。这些参数里行跨度Stride是最容易搞错的。Stride 是指 DDR 里一行数据占用的字节数它不一定等于图像的有效宽度。比如你做 1080P 显示但 DDR 里每行后面留了一些空白用于对齐那 Stride 就要设成 1920×3 对齐字节。如果 Stride 设错了图像会斜着显示或者出现周期性错位。3.4 中断与状态监控VDMA 支持中断输出可以在帧完成、错误发生时通知 PS 端。我一般会开启帧完成中断在中断服务程序里更新帧缓存的 Park 指针或者统计帧率。错误中断也要开比如 AXI 响应错误、同步信号丢失等这些错误在调试阶段非常有用。状态寄存器里可以读到当前 VDMA 的运行状态比如是否空闲、当前 Park 指针位置、已经传输了多少帧。调试的时候我会在 PS 端定期打印这些状态确认 VDMA 是否在正常工作。如果发现帧计数不增加那多半是同步信号没接对或者 AXI-Stream 握手卡住了。4. 调试 VDMA 时那些让人抓狂的瞬间4.1 图像撕裂帧缓存切换时机不对图像撕裂是 VDMA 调试中最常见的问题。现象是画面上出现一条水平线线上下的图像来自不同的帧。根本原因是读出端正在读某个帧缓存时写入端已经开始覆盖这个缓存了。解决思路有两个方向。一是增加帧缓存数量让读写端有足够的缓冲空间。二是调整帧延迟参数让 VDMA 在切换缓存之前多等几个同步信号。我一般会先用 3 个缓存如果还有撕裂就加到 4 个同时把帧延迟设为 2。实测下来1080P60fps 的场景下3 缓存加帧延迟 1 基本不会撕裂。还有一个隐藏原因同步信号的相位关系。如果写入端的帧同步和读出端的帧同步没有对齐即使缓存数量够也可能出现撕裂。这时候需要检查两个通道的同步模式配置确保它们使用同一个同步源或者有固定的相位关系。4.2 带宽不足DDR 访问冲突VDMA 对 DDR 带宽的占用很大。以 1080P60fps RGB888 为例写入带宽 1920×1080×3×60 ≈ 373MB/s读出带宽也是 373MB/s加起来接近 750MB/s。如果 DDR 控制器还要同时服务 CPU 和其他外设带宽就可能不够。带宽不足的表现是图像出现周期性卡顿、帧率下降、或者 VDMA 报 AXI 响应错误。排查方法是先用 ILA 抓 AXI 接口的握手信号看 TREADY 是否经常拉低。如果 TREADY 长时间为低说明 DDR 控制器忙不过来。优化手段有几个一是提高 DDR 控制器的时钟频率比如从 533MHz 提到 800MHz。二是优化 AXI 突发长度把突发长度设到最大256 拍减少地址切换开销。三是调整 DDR 控制器的仲裁优先级给 VDMA 更高的优先级。我在一个项目里把 VDMA 的 AXI 接口优先级调到最高带宽立刻从 600MB/s 提升到 900MB/s。4.3 同步信号丢失外部同步的坑用外部同步模式时如果同步信号没有正确连接到 VDMA或者极性设反了VDMA 会一直等待同步信号通道永远不启动。这时候状态寄存器会显示通道处于“等待同步”状态。排查步骤先用示波器或者 ILA 确认同步信号确实存在并且频率和视频帧率一致。然后检查 VDMA 的同步信号极性配置确认是上升沿有效还是下降沿有效。最后检查同步信号是否跨时钟域正确传递如果同步信号来自异步时钟域需要先做同步处理再送给 VDMA。我遇到过一次奇葩情况摄像头的 VSYNC 信号在复位期间是低电平复位结束后才拉高。但 VDMA 在复位期间就已经开始等待同步信号了结果把复位期间的低电平当成了一个有效的同步脉冲导致帧计数错乱。解决办法是在复位期间屏蔽同步信号等 VDMA 初始化完成后再放开。4.4 数据错位Stride 和像素格式不匹配数据错位的表现是图像整体偏移、或者出现重复的竖条纹。原因通常是 Stride 设置和实际数据排列不一致。比如你的图像是 1920 宽但 DDR 里每行数据后面有 64 字节的填充那 Stride 应该是 1920×3 64 5824 字节。如果你设成了 5760每行就会少读 64 字节下一行的数据就会错位。另一个常见原因是像素格式不匹配。VDMA 配置的数据宽度是 24 位但视频源输出的是 32 位带 Alpha 通道那 VDMA 会把每 4 个字节当成一个像素图像就会花屏。这时候需要检查视频源的像素格式确保和 VDMA 配置一致。提示调试 Stride 问题时可以先把图像分辨率设小一点比如 320×240这样一帧数据量小用 ILA 抓完整帧的地址变化比较容易。确认小分辨率没问题后再逐步提高到目标分辨率。5. 几个让 VDMA 跑得更稳的实战技巧5.1 用 AXI Performance Monitor 量化带宽Xilinx 提供了一个叫 AXI Performance Monitor 的 IP可以实时统计 AXI 接口的读写带宽、事务数量、延迟等指标。我在调试 VDMA 带宽问题时会把它挂在 VDMA 的 AXI 接口上实时观察带宽变化。如果发现带宽远低于理论值就说明有瓶颈。这个 IP 的使用很简单在 Vivado 的 Block Design 里把它加到 VDMA 和 DDR 控制器之间配置好要监控的接口然后在 PS 端通过 AXI-Lite 读取统计寄存器。我一般会监控读写通道的“有效带宽”和“空闲周期数”这两个指标最能反映问题。5.2 帧缓存地址对齐到 4KB 边界前面提过地址对齐的重要性这里再强调一下。AXI4 的突发传输不能跨 4KB 边界如果 VDMA 的帧缓存地址没有对齐到 4KB一次突发传输可能会被拆成两次效率降低。更严重的是如果地址计算出现偏差可能会访问到错误的 DDR 区域导致数据损坏。我的做法是在分配帧缓存地址时直接按 4KB 对齐。比如 1080P RGB888 一帧是 6220800 字节向上对齐到 4KB 就是 6221824 字节。三个缓存就是 18665472 字节约 17.8MB。在 DDR 里划 32MB 给 VDMA绰绰有余。5.3 用 GPIO 或 ILA 做帧同步调试调试帧同步问题时光看寄存器状态有时候不够直观。我会在 PL 端加一个 GPIO把 VDMA 的帧同步信号引出来用示波器观察它的频率和相位。或者在 Vivado 里加 ILA抓同步信号和 AXI-Stream 的 TUSER/TLAST看它们是否对齐。ILA 的触发条件可以设成“TUSER 拉高时”这样每次帧起始都会触发一次抓取。通过对比 TUSER 和同步信号的时间关系可以判断同步是否正常。如果 TUSER 比同步信号晚了太多说明视频源的时序有问题需要调整。5.4 软件端的帧缓存管理策略PS 端的软件不仅要初始化 VDMA还要在运行过程中管理帧缓存。比如在帧完成中断里你需要知道当前 VDMA 正在写哪个缓存、读哪个缓存然后决定是否要切换 Park 指针。我一般会在软件里维护一个帧缓存状态数组记录每个缓存的“写入帧号”和“读出帧号”。在中断里更新这些状态然后根据状态决定是否要调整 Park 指针。如果发现某个缓存被写入后很久没有被读出说明读出端可能卡住了需要检查显示控制器的状态。还有一个技巧在切换 Park 指针之前先确认目标缓存已经完成传输。VDMA 的状态寄存器里有“帧完成”标志可以查询这个标志来判断缓存是否空闲。如果直接切换 Park 指针而不检查状态可能会导致 VDMA 在缓存还没写完时就切换造成数据丢失。5.5 不同分辨率切换时的重配置流程很多项目需要支持多种分辨率比如 720P 和 1080P 切换。这时候不能直接改 VDMA 的寄存器因为 VDMA 可能正在传输数据。正确的流程是停止 VDMA 通道写 0 到运行控制寄存器。等待通道真正停止查询状态寄存器确认空闲。重新配置帧缓存地址、Stride、行数等参数。重新启动通道。这个流程看起来简单但实际做的时候要注意停止通道后VDMA 可能还有未完成的 AXI 事务在飞。需要等待这些事务完成才能安全地重新配置。我一般会在停止通道后加一个延时或者查询 AXI 接口的空闲状态确认没有未完成的事务后再继续。6. 从 VDMA 延伸到更复杂的视频通路设计6.1 多路 VDMA 的协同工作当你的系统需要处理多路视频时比如四路摄像头输入、一路 HDMI 输出就需要多个 VDMA 协同工作。每个摄像头配一个 VDMA 做采集输出端配一个 VDMA 做显示。多路 VDMA 共享 DDR 带宽这时候带宽分配就成了关键问题。我的做法是给每路 VDMA 分配独立的 AXI 接口通过 AXI Interconnect 连接到 DDR 控制器。然后在 AXI Interconnect 里配置 QoS服务质量参数给显示通道更高的优先级保证显示不卡顿。采集通道可以适当降低优先级因为采集数据可以先缓存在 FIFO 里晚一点写入 DDR 也没关系。6.2 VDMA 与图像处理 IP 的配合VDMA 本身不处理图像但它是图像处理流水线的“传送带”。典型的配合方式是VDMA 读出图像数据送给图像处理 IP比如缩放、色彩空间转换、边缘检测处理完的数据再通过另一个 VDMA 写回 DDR或者直接输出到显示。这里要注意数据流的连续性。图像处理 IP 通常有固定的处理延迟如果 VDMA 的读出速率和处理 IP 的消耗速率不匹配就会出现 FIFO 溢出或下溢。解决办法是在 VDMA 和图像处理 IP 之间加一个 AXI-Stream FIFO做速率匹配和缓冲。FIFO 的深度要根据处理延迟和时钟频率差来计算一般留 2 到 3 行的缓冲就够了。6.3 什么时候该考虑放弃 VDMA虽然 VDMA 很强大但也不是万能的。如果你的视频分辨率很低比如 640×480 以下帧率也不高那用 VDMA 可能有点“杀鸡用牛刀”。这时候可以考虑用普通的 AXI DMA或者直接用 BRAM 做行缓存省去 DDR 访问的开销。另外如果你的图像处理算法是逐像素流式的不需要整帧缓存那也可以不用 VDMA。比如简单的 Sobel 边缘检测只需要 3 行缓存就能完成用 BRAM 实现行缓存比 VDMA 更简单、延迟更低。我个人的判断标准是如果一帧图像的大小超过片上 BRAM 容量的 1/3或者算法需要访问多帧数据那就用 VDMA否则优先考虑行缓存方案。这个标准不一定绝对但能帮你快速做决策。6.4 基于 VDMA 的典型项目结构参考最后给一个我常用的项目结构供参考PS 端跑 Linux 或裸机程序负责 VDMA 初始化、中断处理、帧缓存管理。PL 端VDMA IP、视频输入 IP如 MIPI CSI-2 接收、视频输出 IP如 HDMI 发送、图像处理 IP。时钟视频像素时钟、AXI 时钟、DDR 时钟三个时钟域通过 VDMA 内部的异步 FIFO 隔离。复位统一的复位控制器确保 VDMA 在视频源和 DDR 控制器都就绪后才启动。调试ILA 抓 AXI-Stream 和 AXI4 接口AXI Performance Monitor 监控带宽GPIO 引出同步信号。这个结构我在多个项目中复用稳定性不错。当然具体项目还要根据分辨率、帧率、处理算法做调整。关键是理解 VDMA 的工作原理知道每个配置参数背后的含义这样遇到问题时才能快速定位。我在实际使用中发现VDMA 的很多问题其实不是 VDMA 本身的问题而是视频时序、时钟域、DDR 带宽这些周边因素导致的。把整个数据通路理顺了VDMA 自然就跑得稳。希望这篇内容能帮你少踩几个坑顺利把图像通路跑起来。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →