基于Zynq FPGA的VDMA视频测试系统:从TPG生成到Linux显示全流程实践
1. 项目概述:构建一个完整的视频测试与传输系统
最近在调试一块基于Zynq的FPGA开发板上的视频输出接口,遇到了一个典型问题:屏幕上要么没信号,要么图像错乱,排查起来非常麻烦。传统的做法是用一个现成的视频源(比如电脑HDMI输出)来测试,但这又需要额外的设备,而且无法精确控制视频时序和像素内容来定位问题。于是,我决定在PL(可编程逻辑)侧自己动手实现一个Test Pattern Generator,并通过VDMA将生成的测试图像流送到PS(处理器系统)侧,最终在Petalinux系统下通过Frame Buffer显示出来。整个项目基于最新的Vivado和Petalinux 2025.2工具链,这不仅是一次对AXI4-Stream和VDMA的深入实践,更是构建一个自包含、可复用的视频系统验证平台的绝佳案例。
这个项目非常适合正在学习Zynq/Zynq MPSoC视频处理的工程师或爱好者。无论你是想验证自己的视频输出IP(如HDMI TX、MIPI D-PHY)是否工作正常,还是想深入理解VDMA如何作为PL和PS之间高速数据搬运的“桥梁”,亦或是想在Petalinux下编写一个简单的帧缓冲显示应用,这个项目都能提供一个清晰的路径。通过它,你不仅能得到一个实用的测试工具,更能打通从FPGA逻辑设计到Linux驱动和应用的全流程,理解其中每一个环节的关键配置和潜在陷阱。
2. 系统架构与核心组件选型
2.1 整体数据流设计思路
整个系统的核心目标是在PL侧生成可编程的测试图像,并通过高效的数据通道在PS侧的Linux用户空间显示出来。数据流需要跨越时钟域(PL的像素时钟 vs PS的AXI总线时钟)和内存域(PL的BRAM/寄存器 vs PS的DDR),因此架构设计至关重要。
我设计的核心数据流路径如下:
- TPG (Test Pattern Generator): 位于PL侧,作为一个纯逻辑的IP核。它根据配置的时序参数(如分辨率、帧率)和图案类型(颜色条、渐变、移动方块等),持续产生AXI4-Stream格式的视频像素流。
- VDMA (Video Direct Memory Access): 这是连接PL和PS的关键枢纽。它负责将TPG产生的流数据通过AXI4总线写入PS侧DDR内存中的指定缓冲区,同时也支持从DDR中读取数据流回PL(本项目中主要用于显示通路)。VDMA内部包含读写通道、帧缓冲管理和调度器,能高效处理视频帧数据。
- AXI Interconnect: 提供PS的AXI Master端口(如HP或ACP)与VDMA、其他IP之间的互联通路。
- PS (Processing System): 运行Petalinux。其作用包括:通过AXI-Lite总线配置TPG和VDMA的寄存器;在DDR中开辟帧缓冲区;加载并运行VDMA的Linux驱动;提供Frame Buffer设备供用户空间访问。
- Linux Frame Buffer & 用户空间应用: Petalinux系统启动后,VDMA驱动会注册一个Frame Buffer设备(如
/dev/fb0)。用户可以通过标准的Frame Buffer接口(如mmap)直接访问DDR中的图像数据,或者编写简单的应用将图像显示出来。
选择TPG+VDMA的组合,而不是用PS端的处理器通过AXI总线直接向PL发送像素数据,主要基于性能和实时性考虑。视频数据量巨大(以1920x1080 @ 30fps RGB888为例,带宽约~180MB/s),由PL侧的专用逻辑生成并流式传输,效率远高于PS软件模拟。VDMA则提供了专业、稳定的DMA传输通道,减轻了CPU负担,并简化了驱动开发。
2.2 关键IP核与接口详解
1. Test Pattern Generator (TPG) IP:在Vivado 2025.2中,Xilinx提供了官方的Video Test Pattern Generator IP核(位于Video & Image Processing子目录下)。这个IP功能强大,我主要使用了它的以下特性:
- 多种图案: 支持颜色条(Color Bar)、渐变(Ramp)、移动方块(Moving Box)、纯色等。颜色条图案对于验证色彩深度和通道顺序至关重要。
- 可动态配置: 通过AXI4-Lite接口,可以在运行时从PS修改图案类型、颜色、移动速度等参数,无需重新综合。
- 标准视频时序输出: 生成标准的AXI4-Stream Video协议信号,包括
TDATA(像素数据)、TVALID、TREADY、TUSER(帧起始)、TLAST(行结束)。这保证了与下游IP(如VDMA)的无缝对接。 - 分辨率与帧率可调: 需要在IP配置界面输入像素时钟频率、水平/垂直总数、同步脉冲宽度等参数,以匹配你的显示设备。
注意: 如果Vivado IP Catalog中没有找到,可能需要安装
Vivado Design Suite - Video and Imaging相关的扩展包。也可以自己用HDL编写一个简单的TPG,但使用官方IP能节省大量时间并保证时序正确性。
2. AXI Video Direct Memory Access (VDMA) IP:这是整个项目的核心。VDMA IP有几个关键配置选项,直接影响系统行为和驱动兼容性:
- Number of Read/Write Channels: 本项目主要使用写通道(将TPG数据写入DDR)。但为了架构完整性和未来扩展(例如从摄像头采集),我通常使能一个写通道和一个读通道。
- Enable Frame Buffers: 必须使能。我设置为3个帧缓冲区(Frame Buffer)。双缓冲(2个)是最低要求,用于乒乓操作避免撕裂;3个缓冲区可以提供更宽松的调度余量,防止因DDR访问延迟导致的帧丢失。
- Memory Map Data Width & Stream Data Width: 这两个宽度必须匹配你的视频数据位宽。对于RGB888格式(24位),通常设置为32位(
MM2S和S2MM的TDATA宽度均设为32)。VDMA会在内部处理数据打包,32位总线能有效利用带宽。 - Line Buffer Depth: 行缓冲深度。对于高清视频,建议设置为最大行长度(如1920像素)以上,以确保流畅传输。通常设为2048。
- GenLock Mode: 生成锁模式。在简单的TPG到内存的写入场景中,
S2MM(Stream to Memory-Map,即写通道)通常设置为Master模式,因为它由TPG的时序驱动。MM2S(读通道)可以设置为Slave模式,但本例中读通道未使用,可先关闭相关功能或设为自由运行。
3. AXI Interconnect 与 Clocking Wizard:
- 时钟网络: TPG工作在像素时钟(例如148.5 MHz for 1080p60)下,而VDMA的AXI4内存映射接口和PS的HP端口通常运行在更高的频率(如150 MHz或200 MHz)。需要使用Clocking Wizard生成这两个时钟,并确保它们同源(例如都来自PS的PL时钟输出)。VDMA内部的异步FIFO会处理这两个时钟域之间的数据传递。
- 互联: 将PS的
S_AXI_HP0接口(高性能从端口)连接到AXI Interconnect,再将VDMA的M_AXI_MM2S和M_AXI_S2MM接口(内存映射主接口)连接到该Interconnect。同时,将PS的M_AXI_GP0(通用主端口)连接到另一个AXI Interconnect,用于通过AXI-Lite配置TPG、VDMA等IP的寄存器。
3. Vivado 硬件平台搭建实操
3.1 IP集成与Block Design绘制
启动Vivado 2025.2,创建RTL项目,选择对应的Zynq器件。在Block Design中,操作步骤如下:
添加并配置Zynq Processing System: 双击Zynq IP,根据开发板型号进行预设。关键步骤是启用PS-PL接口:
- 在
PS-PL Configuration > HP Slave AXI Interface中,使能S AXI HP0接口。这将提供PS到PL的高性能数据通道。 - 在
PS-PL Configuration > General > Enable Clock Resets中,使能FCLK_CLK0(例如100MHz),作为PL侧部分逻辑和AXI互联的参考时钟。 - 在
Clock Configuration > PL Fabric Clocks中,使能FCLK_CLK1,并设置一个频率(例如150MHz),作为VDMA AXI总线时钟。 - 在
MIO Configuration中,根据开发板确保UART等基础外设已正确配置,用于后续Linux调试输出。
- 在
添加并配置Clocking Wizard:
- 添加Clocking Wizard IP。设置一个主输入时钟(例如
FCLK_CLK0的100MHz)。 - 输出两个时钟:
clk_out1(150MHz)提供给VDMA的axi_aclk和AXI Interconnect;clk_out2(148.5MHz)作为像素时钟,提供给TPG和可能的后续显示IP。务必勾选reset和locked端口。
- 添加Clocking Wizard IP。设置一个主输入时钟(例如
添加并配置Video Test Pattern Generator:
- 添加
Video Test Pattern GeneratorIP。 - 在
Basic标签页,设置Samples Per Clock为1(每个时钟一个像素),Maximum Number of Columns和Rows至少大于你的目标分辨率。 - 在
Video Format标签页,选择Video Format为RGB,Component Width通常为8(即每通道8位)。Total Columns/Rows、Active Columns/Rows、Sync Widths等参数需要根据你的目标视频模式精确计算填写。例如,对于1920x1080p60,常用参数如下表所示:
- 添加
| 参数 | 值 | 说明 |
|---|---|---|
| Clock Frequency (MHz) | 148.5 | 像素时钟频率 |
| Total Columns (Pixels) | 2200 | 水平总像素数 |
| Active Columns (Pixels) | 1920 | 水平有效像素数 |
| H Front Porch | 88 | 水平前肩 |
| H Sync Width | 44 | 水平同步脉宽 |
| H Back Porch | 148 | 水平后肩 |
| Total Rows (Lines) | 1125 | 垂直总行数 |
| Active Rows (Lines) | 1080 | 垂直有效行数 |
| V Front Porch | 4 | 垂直前肩 |
| V Sync Width | 5 | 垂直同步脉宽 |
| V Back Porch | 36 | 垂直后肩 |
* 在`Interface`标签页,确保`Interface Mode`为`Native`(使用AXI4-Stream信号),并勾选`Enable s_axi_ctrl interface`以允许PS动态配置。添加并配置AXI VDMA:
- 添加
AXI Video Direct Memory AccessIP。 Basic标签页:使能Read Channel和Write Channel。Number of Frame Buffers设为3。Memory Map Data Width和Stream Data Width均设为32。Advanced标签页:Line Buffer Depth设为2048。在S2MM Options下,设置GenLock Mode为Master。Stream to Memory Map的Transfer Type保持为Increment。- 其他选项可暂时保持默认。
- 添加
添加AXI Interconnect和Processor System Reset:
- 添加两个
AXI InterconnectIP。一个用于高性能数据路径(连接HP0和VDMA的内存映射接口),另一个用于低速控制路径(连接GP0和各个IP的AXI-Lite接口)。 - 添加
Processor System ResetIP,用于生成各个时钟域对应的复位信号。将Clocking Wizard的locked信号连接到其aux_reset_in,可以确保时钟稳定后才释放复位。
- 添加两个
连接Block Design: 这是最需要细心的一步。连接关系可概括为下表:
| 信号组 | 源 | 目的 | 说明 |
|---|---|---|---|
| 时钟 | FCLK_CLK0 | Clocking Wizardclk_in1 | 主时钟输入 |
Clocking Wizardclk_out1(150MHz) | VDMAs_axi_lite_aclk,m_axi_mm2s_aclk,m_axi_s2mm_aclk, AXI Interconnect (HP)ACLK等 | AXI总线时钟域 | |
Clocking Wizardclk_out2(148.5MHz) | TPGap_clk | 像素时钟域 | |
| 复位 | Processor System Reset 输出的各时钟域复位 | 对应IP的复位端口 | 异步复位,同步释放 |
| 数据流 | TPGm_axis_video | VDMAS_AXIS_S2MM | AXI4-Stream视频流 |
| 内存映射 | VDMAM_AXI_MM2S和M_AXI_S2MM | AXI Interconnect (HP)S00_AXI,S01_AXI | 数据通道到DDR |
AXI Interconnect (HP)M00_AXI | ZynqS_AXI_HP0 | ||
| 控制通路 | ZynqM_AXI_GP0 | AXI Interconnect (Lite)S00_AXI | PS配置IP寄存器 |
AXI Interconnect (Lite)M00_AXI | TPGs_axi_ctrl | ||
AXI Interconnect (Lite)M01_AXI | VDMAS_AXI_LITE | ||
| 中断 | VDMAmm2s_introut和s2mm_introut | ZynqIRQ_F2P[1:0] | 可选,用于驱动中通知帧完成 |
连接完成后,使用`Validate Design`功能检查,确保无错误。3.2 约束、综合与实现
- 创建顶层HDL包装器: 在Block Design上右键,选择
Create HDL Wrapper,让Vivado自动生成顶层文件。 - 引脚约束: 本项目主要验证内部数据流,暂时无需外部视频输出引脚。但需要为系统时钟、复位、UART等基础引脚添加约束。如果你的开发板有外部时钟输入,需要创建
.xdc文件约束sys_clk等引脚。 - 综合与实现: 直接运行
Generate Bitstream。Vivado会自动执行综合、布局布线。这个过程耗时较长,取决于设计复杂度。 - 导出硬件: 生成Bitstream后,在
File > Export > Export Hardware中,选择Include bitstream,导出.xsa文件。这个文件包含了硬件描述、IP配置和比特流,是后续Petalinux工程的基础。
实操心得: 在连接Block Design时,经常容易混淆时钟域。一个简单的检查方法是:所有连接到同一个AXI Interconnect的
ACLK信号,必须来自同一个时钟源(这里是150MHz)。而TPG的ap_clk和VDMA的s_axis_s2mm_aclk必须连接像素时钟(148.5MHz)。混接时钟是导致VDMA无法正常传输数据的最常见原因之一。
4. Petalinux 系统配置与驱动开发
4.1 创建Petalinux工程与导入硬件
# 假设工作目录为 ~/project source /opt/pkg/petalinux/2025.2/settings.sh # 设置Petalinux环境变量 petalinux-create -t project -n video_project --template zynq cd video_project petalinux-config --get-hw-description=/path/to/your/exported.xsa执行petalinux-config命令时,会启动配置菜单。关键配置位于:
Subsystem AUTO Hardware Settings > Memory Settings: 确认DDR容量配置正确。Image Packaging Configuration > Root filesystem type: 选择INITRAMFS或EXT4 (SD card),取决于你的启动方式。DTG Settings: 内核设备树在此生成。我们后续需要手动修改。
4.2 配置内核与设备树
配置Linux内核:
petalinux-config -c kernel需要确保以下驱动被编译(通常已默认内置或编译为模块):
Device Drivers > Character devices > Xilinx DMA engines support: 这是VDMA和AXI DMA驱动的依赖。Device Drivers > Graphics support > Frame buffer Devices > Support for frame buffer devices: 使能Frame Buffer。Device Drivers > Graphics support > Xilinx framebuffer driver: 这是Xilinx提供的基于DMA的Frame Buffer驱动,非常适合VDMA。- 在
Device Drivers > Staging drivers下,找到Xilinx Video IP Core support,确保Xilinx Video Test Pattern Generator被启用(作为模块m)。
修改设备树: Petalinux自动生成的设备树可能不包含我们的TPG和VDMA节点,需要手动添加。
- 找到
project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi文件。 - 在其中添加TPG和VDMA的设备节点。节点的地址(
reg属性)可以在Vivado中Address Editor标签页找到。
/ { amba_pl: amba_pl { #address-cells = <1>; #size-cells = <1>; compatible = "simple-bus"; ranges; // VDMA 节点 vdma@a0000000 { compatible = "xlnx,axi-vdma-6.3", "xlnx,axi-vdma-1.00.a"; reg = <0xa0000000 0x10000>; #dma-cells = <1>; clocks = <&clkc 15>, <&clkc 15>, <&clkc 16>; // 第一个是s_axi_lite_aclk, 第二个是m_axi_mm2s_aclk,第三个是m_axi_s2mm_aclk?需要根据实际时钟索引调整 clock-names = "s_axi_lite_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk"; xlnx,num-fstores = <3>; dma-channel@a0000000 { compatible = "xlnx,axi-vdma-mm2s-channel"; interrupts = <0 31 4>; // 中断号需要根据PL到PS的连接调整 xlnx,datawidth = <0x20>; xlnx,genlock-mode = <0>; // 读通道模式 }; dma-channel@a0000030 { compatible = "xlnx,axi-vdma-s2mm-channel"; interrupts = <0 32 4>; xlnx,datawidth = <0x20>; xlnx,genlock-mode = <1>; // 写通道为Master模式 }; }; // TPG 节点 tpg@a0010000 { compatible = "xlnx,v-tpg-5.0", "xlnx,v-tpg"; reg = <0xa0010000 0x10000>; clocks = <&clkc 16>; // 像素时钟 clock-names = "ap_clk"; xlnx,max-width = <1920>; xlnx,max-height = <1080>; port { tpg_out: endpoint { remote-endpoint = <&vdma_in>; // 与VDMA的stream端口连接 }; }; }; }; };- 同时,需要在
amba节点下,为VDMA的stream接口和TPG的stream接口添加连接关系(在system-user.dtsi的amba块内或同级添加):
&vdma_s2mm { dma-channel@a0000030 { xlnx,video-format = <0>; // 例如,0代表RGB xlnx,video-width = <32>; // 数据位宽 port { vdma_in: endpoint { remote-endpoint = <&tpg_out>; }; }; }; };注意: 设备树中的时钟索引(
&clkc 15)、中断号(<0 31 4>)、寄存器地址必须与Vivado设计完全一致。最可靠的方法是参考Vivado生成的*.sysdef文件或pl.dtsi文件中的内容进行修改。错误的设备树是导致驱动加载失败的主要原因。- 找到
4.3 构建与启动系统
petalinux-build petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot images/linux/u-boot.elf将生成的BOOT.BIN和image.ub拷贝到SD卡FAT分区,启动开发板。
系统启动后,通过串口登录。首先检查驱动是否加载成功:
dmesg | grep -E “vdma|tpg|xilinx”你应该能看到类似xilinx-vdma a0000000.vdma: Xilinx AXI VDMA driver probe successful和xilinx-vdma a0000000.vdma: Please ensure that AXIS and AXI MM interface clocks are synchronous(这是一条警告,在我们的异步时钟设计下是预期的)的信息。
然后检查Frame Buffer设备:
ls -l /dev/fb* cat /sys/class/graphics/fb0/name如果一切正常,/dev/fb0应该存在,并且name属性可能显示为xilinx-fb。
5. 用户空间应用与测试验证
5.1 编写简单的Frame Buffer测试程序
驱动加载成功后,VDMA会在/dev/fb0创建一个Frame Buffer设备。我们可以编写一个简单的C程序来验证图像是否正确写入。
这个程序不需要绘制复杂图形,因为它只是映射出VDMA从TPG写入DDR的缓冲区。程序的主要作用是打开设备,映射内存,然后可以读取前几个像素值来验证数据是否在变化(对于移动图案),或者直接使用ioctl获取固定颜色条图像。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <linux/fb.h> int main() { int fbfd = 0; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; long int screensize = 0; char *fbp = 0; // 打开Frame Buffer设备 fbfd = open(“/dev/fb0”, O_RDWR); if (fbfd == -1) { perror(“Error: cannot open framebuffer device”); exit(1); } printf(“The framebuffer device was opened successfully.\n”); // 获取固定屏幕信息 if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) { perror(“Error reading fixed information”); exit(2); } // 获取可变屏幕信息(分辨率、位深等) if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) { perror(“Error reading variable information”); exit(3); } printf(“%dx%d, %dbpp\n”, vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); printf(“Line length: %d bytes\n”, finfo.line_length); // 计算映射内存的大小 screensize = finfo.line_length * vinfo.yres; printf(“Screensize: %ld bytes\n”, screensize); // 将Frame Buffer内存映射到用户空间 fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); if ((void*)fbp == MAP_FAILED) { perror(“Error: failed to map framebuffer device to memory”); exit(4); } printf(“The framebuffer device was mapped to memory successfully.\n”); // 此时,fbp指向DDR中VDMA写入的帧缓冲区。 // 对于TPG生成的动态图案(如移动方块),我们可以定期读取特定位置的值来观察变化。 int x = 100, y = 100; // 观察坐标(100, 100)的像素 long int location = 0; unsigned int pixel_value = 0; for (int i = 0; i < 10; ++i) { location = (x + vinfo.xoffset) * (vinfo.bits_per_pixel / 8) + (y + vinfo.yoffset) * finfo.line_length; pixel_value = *((unsigned int*)(fbp + location)); // 假设32bpp printf(“Frame %d: Pixel at (%d, %d) = 0x%08X\n”, i, x, y, pixel_value); sleep(1); // 等待1秒,观察TPG图案变化 } // 清理 munmap(fbp, screensize); close(fbfd); return 0; }将此程序交叉编译(使用petalinux-build -c myapp -x do_compile或在Petalinux工程中创建应用),并拷贝到开发板运行。如果TPG配置为移动方块图案,程序输出的像素值应该会周期性变化。
5.2 使用现有工具直接显示
更简单的方法是使用Linux下现有的Frame Buffer工具。
fbset: 查看和修改Frame Buffer显示模式。fbset -i可以查看详细信息。cat或dd: 理论上,你可以将图像文件(如原始的RGB数据文件)直接写入/dev/fb0来显示。但我们的fb0内容由VDMA从TPG持续写入,直接写入会被覆盖。- 使用
ioctl切换缓冲区: 更高级的用法是,利用VDMA多缓冲区的特性,通过驱动提供的ioctl命令,让显示端切换到一个由TPG写入完毕的缓冲区,而TPG继续写入另一个缓冲区,实现无撕裂显示。这需要对VDMA驱动有更深入的了解,并可能编写更复杂的应用。
对于快速验证,最直观的方法是连接一个显示器。如果你的PL部分还连接了HDMI TX或DisplayPort IP,并且正确驱动,那么系统启动后,屏幕上就应该直接显示TPG生成的测试图案。这是最直接的验证方式。
6. 调试技巧与常见问题排查
在实际操作中,几乎不可能一次成功。以下是我在调试过程中遇到的一些典型问题及解决方法:
问题1:Vivado实现时报错,时序不满足。
- 现象: 在
Implementation阶段,出现Setup/Hold时间违例。 - 排查:
- 检查时钟约束: 确保所有时钟(特别是像素时钟和VDMA的AXI时钟)都创建了正确的约束(
create_clock)。使用Report Clock Networks和Report Clock Interaction检查时钟关系。 - 降低时钟频率: 初次尝试时,可以先将像素时钟和AXI时钟频率设低(例如降到50MHz),验证功能,再逐步提高。
- 优化流水线: 检查TPG到VDMA之间的路径。可以在它们之间插入
AXI4-Stream Register SliceIP来打拍,提高时序性能。
- 检查时钟约束: 确保所有时钟(特别是像素时钟和VDMA的AXI时钟)都创建了正确的约束(
问题2:Petalinux启动后,/dev/fb0不存在或驱动加载失败。
- 现象:
dmesg中无VDMA或Frame Buffer相关成功信息,甚至报错。 - 排查:
- 检查设备树: 这是最常见的原因。使用
petalinux-build --sdk生成SDK,用其中的设备树编译器(dtc)检查你的system-user.dtsi语法是否正确:dtc -I dts -O dtb -o test.dtb system-user.dtsi。 - 核对地址与中断: 仔细核对设备树中的
reg地址、clocks索引、interrupts号是否与Vivado Address Editor和Zynq配置中完全一致。中断号错误会导致驱动probe失败。 - 检查内核配置: 确认
Xilinx DMA engines,Xilinx framebuffer driver等必要驱动已编译进内核(*)或作为模块(M)。如果是模块,需要手动modprobe。 - 查看完整内核日志:
dmesg | grep -i error或dmesg | grep -i fail寻找更具体的错误信息。
- 检查设备树: 这是最常见的原因。使用
问题3:程序读取/dev/fb0,但像素数据不变或全为0。
- 现象: Frame Buffer设备存在,但映射出来的内存区域是静态的或全是0。
- 排查:
- 确认TPG是否启动: 在用户空间,可以通过
devmem工具或编写一个小程序,通过AXI-Lite总线访问TPG的寄存器(地址在设备树中定义),检查其控制寄存器,确保它没有被禁用(例如,ap_start位是否被置1)。 - 确认VDMA是否启动: 同样,检查VDMA的
S2MM_DMACR寄存器,确保RS(运行状态)位和RUN_STOP位被置1。VDMA需要被正确初始化并启动传输。 - 检查时钟和复位: 确认PL侧的像素时钟和AXI时钟是否真的在运行。可以在Vivado中生成ILA(集成逻辑分析仪)核,插入到TPG和VDMA的时钟、复位、流接口信号上,烧录后通过Vivado Hardware Manager观察实际信号。这是最强大的调试手段。
- 检查数据流路径: 使用ILA抓取TPG输出的
TDATA、TVALID、TREADY信号。如果TVALID和TREADY从未同时有效,说明流通道未建立。检查VDMA的S2MM通道是否已启动并准备好接收数据。
- 确认TPG是否启动: 在用户空间,可以通过
问题4:图像显示撕裂或不完整。
- 现象: 连接显示器后,图像有撕裂感、闪烁或只有部分屏幕有内容。
- 排查:
- 缓冲区数量: 检查VDMA配置的帧缓冲区数量。至少需要2个(双缓冲)。如果只有一个,当TPG写入速度与显示器读取速度不同步时,必然撕裂。建议设置为3。
- 时序参数: 仔细核对TPG中配置的视频时序参数(总像素/行、有效区域、同步脉宽等)是否与你的显示器模式严格匹配。一个错误的参数就可能导致图像错位。
- DDR带宽与延迟: 如果系统负载重,DDR访问延迟可能导致VDMA无法及时写入/读取数据。尝试降低帧率或分辨率,看问题是否改善。也可以尝试使用PS的ACP端口(如果支持),它缓存一致性更好,延迟更低。
这个项目从硬件设计到软件驱动的完整流程,涵盖了Zynq混合系统开发的关键环节。最大的收获不是最终屏幕上显示的彩色条纹,而是在解决上述一个个问题的过程中,对AXI总线协议、VDMA工作机制、Linux设备驱动模型以及硬件调试方法有了更深刻的理解。当你第一次看到自己生成的测试图案稳定地显示在屏幕上时,那种对系统级掌控感是无可替代的。后续,你可以基于这个平台,轻松地将TPG替换为真实的图像传感器接口IP,或者将显示通路连接到更复杂的视频处理管线,从而构建更复杂的视频应用系统。
