在Xilinx ZYNQ系列SoC上进行嵌入式开发,与传统ARM开发最大的区别在于需要同时处理PS(Processing System)和PL(Programmable Logic)两部分的协同工作。我最近完成的一个工业控制器项目就采用了ZYNQ-7020芯片,这里分享下基础开发环境的搭建过程。
首先需要准备Vivado 2020.1开发环境(这个版本在稳定性和资源占用上比较均衡)。安装时务必勾选"SDK"和"Device Drivers"组件,后期开发中会频繁用到硬件描述导出和驱动编译功能。新建工程时芯片型号选择xc7z020clg400-2,这是ZYNQ-7020的工业级封装型号。
注意:Vivado安装路径不要包含中文或空格,否则后期生成BSP时可能出现诡异错误。我曾在某次安装时使用了"D:\Xilinx\Vivado 2020.1"这样的路径,结果在生成FSBL时持续报错,排查了整整两天才发现是路径空格导致的问题。
在Block Design中添加ZYNQ7 Processing System IP核后,需要配置以下关键参数:
特别要注意的是PS-PL接口配置中的AXI HP端口设置。如果项目中需要PL高速访问DDR,建议至少使能两个HP端口,并将数据宽度设为64bit。我在第一个版本中只启用了一个32bit端口,结果DMA传输带宽直接成为系统瓶颈。
即使是最简单的测试工程,PL部分也建议添加以下基础IP:
生成比特流文件前,务必在"Implementation Settings"中将"Strategy"改为Performance_ExplorePostRoutePhysOpt。这个策略组合在我测试过的多个项目中,能在时序收敛和编译时间之间取得较好平衡。
导出硬件到SDK后,按这个顺序创建软件组件:
在bsp配置中有一个隐藏坑点:memcpy/memset的实现要选择"optimized"而非"standalone"。后者虽然代码量小,但在实际测试中,处理大块数据拷贝时性能差异可达5倍以上。
下面这个LED闪烁测试程序包含了ZYNQ开发的几个核心要素:
c复制#include "xparameters.h"
#include "xgpio.h"
#include "xil_printf.h"
#define GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define LED_DELAY 10000000
XGpio Gpio;
int main() {
int Status;
volatile int Delay;
// 初始化GPIO
Status = XGpio_Initialize(&Gpio, GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("GPIO Init Failed\r\n");
return XST_FAILURE;
}
// 设置GPIO方向(1输出,0输入)
XGpio_SetDataDirection(&Gpio, 1, 0x00);
while (1) {
XGpio_DiscreteWrite(&Gpio, 1, 0x0F); // LED全亮
for (Delay = 0; Delay < LED_DELAY; Delay++);
XGpio_DiscreteWrite(&Gpio, 1, 0x00); // LED全灭
for (Delay = 0; Delay < LED_DELAY; Delay++);
}
return XST_SUCCESS;
}
这段代码中容易被忽视的关键点:
当BOOT.bin无法正常启动时,建议按这个顺序排查:
最近遇到一个典型案例:客户反馈板卡偶尔启动失败。最终发现是FSBL中DDR初始化参数与实际使用的DDR芯片型号不匹配。修改ps7_init.c中的EMIF参数后问题解决。
通过AXI HP端口进行DMA传输时,不同配置下的实测带宽对比:
| 配置方案 | 数据宽度 | 突发长度 | 实测带宽(MB/s) |
|---|---|---|---|
| 方案1 | 32bit | 16 | 320 |
| 方案2 | 64bit | 32 | 890 |
| 方案3 | 64bit | 64 | 1200 |
要实现最佳性能,还需要在PL端做以下优化:
在实际项目中,这几个技巧能显著提升开发效率:
tcl复制# 生成比特流
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1
# 导出硬件
write_hw_platform -fixed -include_bit -force -file ${design_name}.xsa
# 启动SDK
set sdk_workspace ${project_dir}/sdk
file mkdir ${sdk_workspace}
setws ${sdk_workspace}
c复制// 进入待机模式
Xil_PM_SetWakeUpHandler(WakeUp_Handler);
Xil_PM_SetSlavePower(XPM_DEV_USB_0, XPM_NOPOWER);
Xil_PM_SelfSuspend(XPM_SUSPEND_MODE_STANDBY);
这些经验都来自实际项目的教训。比如在某次省电模式开发中,没有正确设置唤醒处理函数就直接进入了待机模式,导致设备"睡死",最终只能通过硬件复位恢复。