1. 为什么一块Zynq7020开发板值得折腾Linux实验
手里这块Zynq7020开发板已经放了小半年,最近终于抽出时间把Linux相关的实验从头到尾跑了一遍。说实话,如果你之前只玩过单片机裸机开发,第一次接触Zynq系列的芯片,大概率会有点懵——它跟传统的ARM开发板不太一样,是ARM+FPGA的异构架构,PS端和PL端各管一摊事,中间还有一堆AXI总线协议要搞清楚。我当初拿到板子的时候,光是理清启动流程和开发环境就花了两三天。
这篇内容主要面向有基本嵌入式基础、想上手Zynq7020 Linux开发的同行。我会把整个实验流程拆开讲清楚,包括开发环境搭建、启动方式选择、SD卡分区制作、设备树配置、外设驱动验证这些核心环节。不管你是学生做课设,还是工程师做原型验证,这些内容都能直接参考。我尽量把踩过的坑和验证过的方案都写出来,让你少走弯路。
Zynq7020这颗芯片的定位很明确:双核Cortex-A9处理器加Artix-7系列FPGA逻辑资源,PS端跑Linux系统负责上层调度和通信,PL端做硬件加速或接口扩展。这种架构在工业控制、图像处理、通信设备里用得很多。但很多教程要么只讲Vivado配置,要么只讲Linux命令,中间衔接的部分一笔带过,导致新手根本串不起来。我下面会按照实际操作的顺序,把每个环节的关键点和原理都补上。
2. 开发环境搭建与工具链选型
2.1 软件工具清单与版本匹配
Zynq开发最让人头疼的就是工具版本兼容性问题。Vivado、PetaLinux、交叉编译器、u-boot、kernel这几样东西的版本必须对得上,否则编译报错能让你怀疑人生。我用的组合是Vivado 2018.3 + PetaLinux 2018.3 + arm-linux-gnueabihf-gcc 7.3.1,这套组合在Zynq7020上验证过很多次,比较稳定。
为什么强调版本匹配?因为PetaLinux里面集成的u-boot和kernel源码是跟Vivado的硬件描述文件(.hdf)绑定的,你用2018.3的Vivado导出硬件,就必须用2018.3的PetaLinux来生成设备树和fsbl,否则寄存器地址映射可能对不上。我试过用2020.1的PetaLinux去处理2018.3的hdf文件,结果设备树里的中断号全乱了,串口都起不来。
如果你不想装PetaLinux这个庞然大物(它动辄几十GB),也可以走手动编译路线:用Vivado导出hdf,然后用SDK生成fsbl,再单独下载u-boot和kernel源码交叉编译。这条路更灵活,但步骤多,适合对Linux编译流程比较熟的人。下面我两种方式都会提到,但重点讲PetaLinux流程,因为它自动化程度高,出错概率低。
2.2 虚拟机与系统环境准备
我是在VMware里装的Ubuntu 16.04.6 LTS,分配了8核CPU、16GB内存、200GB硬盘。为什么用16.04而不是更新的版本?因为PetaLinux 2018.3官方支持的就是Ubuntu 16.04,你在18.04或20.04上装会遇到各种依赖库版本问题,比如libtinfo5、libncurses5这些老库在新系统里默认没有,得手动降级,非常折腾。
安装PetaLinux之前,先装一堆依赖包:
sudo apt-get install tofrodos iproute2 gawk gcc git make net-tools libncurses5-dev tftpd zlib1g-dev libssl-dev flex bison libselinux1 gnupg wget diffstat chrpath socat xterm autoconf libtool tar unzip texinfo zlib1g-dev gcc-multilib build-essential libsdl1.2-dev libglib2.0-dev zlib1g:i386这些包缺一不可,尤其是libsdl1.2-dev和zlib1g:i386,少了它们PetaLinux的图形配置界面起不来。装完之后把PetaLinux安装包解压到/opt目录下,运行安装脚本,一路默认选项就行。安装完成后记得source一下settings.sh,把环境变量加载进来。
注意:PetaLinux安装路径不要有中文和空格,否则后面编译会报奇怪的路径错误。我有个朋友把安装包放在“下载”文件夹里,结果编译到一半报错,查了半天才发现是路径问题。
2.3 串口终端与网络连接配置
板子跟电脑的通信靠串口和网口。串口我用的是CH340 USB转TTL模块,接到板子的UART0上,波特率115200。终端软件用minicom或者picocom都行,我习惯用picocom,命令简单:
sudo picocom -b 115200 /dev/ttyUSB0网口配置稍微麻烦一点。Zynq7020板子上一般有两个网口,一个PS端的千兆网口,一个PL端的。PS端网口在Linux启动后会自动识别为eth0,你需要给它配个IP,让它跟虚拟机在同一网段。我通常这样设置:
ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up然后在虚拟机里也配个同网段的静态IP,比如192.168.1.101。这样两边就能互相ping通,后面用tftp传文件或者nfs挂载根文件系统都靠这个通道。
实操心得:如果你用tftp传文件,记得在虚拟机上装tftpd-hpa,并且把tftp根目录权限设成777,否则板子下载文件时会报“Permission denied”。这个坑我踩过不止一次。
3. 启动方式选择与SD卡分区制作
3.1 Zynq7020的三种启动模式对比
Zynq7020支持三种启动方式:JTAG、QSPI Flash、SD卡。JTAG一般用于调试,每次上电都要重新下载bit流和elf文件,不适合量产。QSPI Flash启动最稳定,但烧写麻烦,而且容量有限(通常16MB或32MB),放不下完整的Linux根文件系统。SD卡启动是最常用的方式,容量大、更换方便,适合开发和原型验证。
SD卡启动的流程是这样的:上电后BootROM先运行,根据MIO引脚的电平判断启动模式,如果是SD卡模式,就去读SD卡第一个分区里的BOOT.BIN文件。BOOT.BIN里面打包了fsbl.elf、bitstream.bit和u-boot.elf三个文件。fsbl负责初始化DDR、时钟、外设,然后加载bit流到PL端,最后把控制权交给u-boot。u-boot再去加载kernel和设备树,挂载根文件系统。
为什么BOOT.BIN要包含bit流?因为Zynq的PL端配置是在启动阶段完成的,如果你在Linux启动后再去加载bit流,虽然也能用,但需要额外的驱动和工具,而且有些外设初始化会来不及。所以标准做法是把bit流打包进BOOT.BIN,让fsbl在启动时就配置好PL。
3.2 SD卡分区方案与格式化操作
SD卡需要分成两个区:第一个区是FAT32格式,用来放BOOT.BIN和image.ub(或者uImage+devicetree);第二个区是ext4格式,用来放根文件系统。第一个区不用太大,给个100MB到500MB就够了,第二个区把剩余空间全部分配。
在Linux下用fdisk分区:
sudo fdisk /dev/sdb依次输入n(新建分区)、p(主分区)、1(分区号)、回车(默认起始扇区)、+500M(分区大小),然后再建第二个分区,类型选主分区,大小默认全给。最后输入w保存。
格式化命令:
sudo mkfs.vfat -F 32 -n BOOT /dev/sdb1 sudo mkfs.ext4 -L rootfs /dev/sdb2注意:分区时一定要确认设备名是/dev/sdb还是/dev/sdc,别把电脑硬盘给格了。我一般先用lsblk看一眼,确认容量对得上再操作。
分完区之后,把BOOT.BIN和image.ub拷贝到第一个分区,把根文件系统解压到第二个分区。根文件系统可以用PetaLinux生成的rootfs.tar.gz,解压命令:
sudo tar -xzf rootfs.tar.gz -C /media/rootfs/解压完记得同步一下再拔卡:
sync3.3 BOOT.BIN生成过程详解
BOOT.BIN的生成有两种方式:用PetaLinux自动生成,或者用SDK手动创建。PetaLinux流程很简单,在petalinux-config里指定hdf文件路径,然后在petalinux-build之后运行petalinux-package --boot命令,它会自动把fsbl、bit流、u-boot打包成BOOT.BIN。
手动创建的话,先在Vivado里导出hdf,然后在SDK里新建fsbl工程,编译生成fsbl.elf。然后用Xilinx SDK的Create Boot Image工具,依次添加fsbl.elf、system.bit、u-boot.elf,输出格式选BIN,点击Create Image就行了。
手动方式的好处是你可以控制每个组件的版本,比如你想用自己编译的u-boot而不是PetaLinux自带的,就得走这条路。但手动方式容易出错,尤其是bit流的路径和fsbl的编译选项,搞错了启动就卡在“Loading bitstream”那一步。
实操心得:如果你用PetaLinux生成BOOT.BIN,默认会把bit流打包进去。但如果你在petalinux-config里没有指定bit流文件,它就不会打包,启动后PL端是空的。检查方法很简单,启动时看串口输出有没有“Loading bitstream”这行字,没有的话就是没打包。
4. 设备树配置与外设驱动验证
4.1 设备树的基本结构与Zynq定制要点
设备树是Linux内核识别硬件的桥梁。Zynq的设备树文件通常叫system-top.dts或者zynq-zed.dts,里面描述了PS端的外设(串口、网口、USB、SD卡控制器等)和PL端的自定义IP。PetaLinux会根据hdf文件自动生成设备树,但自动生成的往往不完整,比如你自定义的AXI IP可能没有对应的节点,需要手动添加。
一个典型的Zynq设备树节点长这样:
&uart0 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart0_default>; };status = "okay"表示启用这个外设,"disabled"表示禁用。pinctrl节点定义了引脚复用配置,Zynq的MIO引脚可以配置成多种功能,比如UART、GPIO、SD卡等,必须在设备树里明确指定。
PL端的IP节点需要根据AXI地址来写。比如你有一个自定义的PWM IP,基地址是0x43C00000,中断号是61,那么设备树节点大概是这样:
pwm_0: pwm@43C00000 { compatible = "my-pwm"; reg = <0x43C00000 0x10000>; interrupt-parent = <&intc>; interrupts = <0 29 4>; clocks = <&clkc 15>; };interrupts里的29是中断号减去32得到的(Zynq的PL中断从32开始编号),4表示高电平触发。这个映射关系很容易搞错,我建议你在Vivado的Address Editor里确认好基地址和中断号,再对照UG585手册里的中断映射表来写。
4.2 串口与网口驱动验证
串口是最基础的外设,Linux启动后应该能看到/dev/ttyPS0这个设备节点。验证方法很简单,用echo往串口发数据:
echo "hello" > /dev/ttyPS0如果电脑端的串口终端能收到"hello",说明串口驱动正常。收不到的话,先检查设备树里uart0的status是不是"okay",再检查pinctrl配置对不对。
网口验证用ifconfig和ping:
ifconfig eth0 192.168.1.100 ping 192.168.1.101能ping通说明网口驱动和PHY都正常。ping不通的话,先看ifconfig里eth0有没有出现,没有的话可能是设备树里gem0节点没启用,或者PHY地址不对。Zynq的PHY地址通常在设备树里通过phy-handle指定,如果你用的板子PHY地址跟默认不一样,需要手动改。
注意:有些板子的网口PHY复位引脚接在PL端的GPIO上,需要在设备树里配置GPIO复位,否则PHY起不来。这个坑很隐蔽,因为串口输出看起来一切正常,就是网口不通。
4.3 GPIO与中断驱动测试
GPIO测试用sysfs接口最方便。先看系统里有没有gpiochip:
ls /sys/class/gpio/如果有gpiochip0、gpiochip1这些目录,说明GPIO驱动加载了。然后导出某个GPIO:
echo 54 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio54/direction echo 1 > /sys/class/gpio/gpio54/value54是GPIO编号,具体值取决于你的硬件连接。Zynq的MIO GPIO编号是0-53,EMIO GPIO编号是54-117。如果你控制的是PL端的LED,通常用的是EMIO,编号从54开始。
中断测试稍微复杂一点,需要写个简单的内核模块或者用gpio-keys驱动。我一般用gpio-keys来验证,在设备树里添加:
gpio-keys { compatible = "gpio-keys"; button { label = "user-button"; gpios = <&gpio0 54 0>; linux,code = <KEY_ENTER>; }; };编译加载后,按按钮就会产生按键事件,用evtest工具能看到。这个方法不用写代码,适合快速验证中断通路。
5. 常见问题排查与避坑经验
5.1 启动失败问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 串口无输出 | 启动模式不对 | 检查MIO引脚电平,确认SD卡启动模式 |
| 卡在"Loading bitstream" | bit流文件损坏或路径错误 | 重新生成BOOT.BIN,确认bit流已打包 |
| 卡在"Starting kernel" | 设备树或kernel镜像不匹配 | 检查image.ub是否包含正确的设备树 |
| 根文件系统挂载失败 | SD卡第二分区格式不对 | 确认分区是ext4,且rootfs解压完整 |
| 网口不通 | PHY复位或地址配置错误 | 检查设备树gem0节点和PHY地址 |
这个表是我实际遇到过的典型问题,基本上覆盖了80%的启动故障。遇到问题先对照这个表排查,能省不少时间。
5.2 编译报错与依赖问题处理
PetaLinux编译报错最常见的就是依赖库缺失。比如报“libtinfo.so.5: cannot open shared object file”,说明你系统里只有libtinfo.so.6,需要装libtinfo5:
sudo apt-get install libtinfo5还有报“python3: command not found”的,是因为PetaLinux 2018.3默认用python2,但你的系统可能只有python3。解决办法是装python2并创建软链接:
sudo apt-get install python2 sudo ln -s /usr/bin/python2 /usr/bin/python实操心得:编译之前先跑一遍petalinux-util --webtalk off,关掉网络统计功能,否则编译过程中会卡在联网检查那一步,等半天没反应。
5.3 设备树调试技巧
设备树改错了导致启动失败,是最让人抓狂的。因为串口可能只输出到“Starting kernel”就没了,根本看不到错误信息。我的经验是先用PetaLinux自带的设备树编译器单独编译dts文件,检查语法错误:
dtc -I dts -O dtb -o test.dtb system-top.dts如果dtc报错,说明语法有问题,根据错误提示改就行。如果dtc编译通过但启动还是失败,那可能是节点地址或中断号写错了。这时候可以在u-boot里用fdt print命令查看设备树内容:
fdt print /soc/uart@e0001000这个命令能打印出指定节点的所有属性,对照手册检查reg和interrupts值是否正确。
另外,Linux内核启动时加“earlyprintk”参数可以把早期启动信息输出到串口,方便定位问题。在u-boot的bootargs里加上:
earlyprintk console=ttyPS0,115200这样即使内核在初始化设备树阶段就挂了,你也能看到一些线索。
5.4 SD卡寿命与数据安全建议
SD卡作为启动介质,频繁读写容易损坏。我建议把根文件系统挂载为只读,或者把日志、临时文件重定向到tmpfs。具体做法是在/etc/fstab里把根分区改成ro,然后加一行:
tmpfs /tmp tmpfs defaults,size=64M 0 0 tmpfs /var/log tmpfs defaults,size=32M 0 0这样能大幅减少SD卡写入次数,延长使用寿命。另外,每次修改完SD卡内容后记得sync再拔卡,否则数据可能没写进去,下次启动就报文件系统错误。
注意:如果你用SD卡启动,不要热插拔。Zynq的SD控制器在Linux运行中热插拔可能导致内核崩溃,因为根文件系统在上面。要换卡必须先断电。
6. 从实验到产品的延伸思考
跑通Linux实验只是第一步,真正把Zynq7020用到产品里,还有几个问题要解决。首先是启动速度优化,默认的PetaLinux启动要十几秒,如果产品要求快速启动,可以裁剪内核、去掉不必要的驱动、用静态IP代替DHCP,能把启动时间压到5秒以内。其次是固件升级方案,SD卡启动的产品升级不方便,可以考虑QSPI+SD双启动,QSPI放恢复系统,SD卡放主系统,升级时只更新SD卡内容。
PL端的IP核开发也是重点。Linux实验里PL端通常只跑个LED闪烁或者PWM,但实际产品里PL端可能要做高速数据采集、图像预处理、协议转换这些任务。这时候就需要写AXI-Lite或AXI-Stream接口的IP核,并在设备树里正确描述,让Linux驱动能访问。这块内容展开讲能写好几篇,后面有机会再单独聊。
我个人在实际操作中的体会是,Zynq7020的Linux开发门槛主要在环境搭建和设备树配置这两块,一旦跑通一次,后面就是复制粘贴改改参数的事。但第一次跑通之前,各种报错和卡死会让你觉得这板子是不是坏了。坚持查手册、看串口输出、对比官方例程,问题总能解决。最后再分享一个小技巧:把每次成功的配置文件和命令都记下来,形成自己的“启动清单”,下次换板子或者重装环境时直接照着做,能省掉大量重复排查的时间。