1. Zynq平台与嵌入式Linux概述

做Zynq平台驱动开发,第一件事不是写代码,而是搞清楚你手里这块芯片到底长什么样。我刚开始接触Zynq时,也被它那种「既像ARM单片机、又像FPGA」的混合体搞晕过。说白了,Zynq就是一颗芯片里同时塞了一个ARM处理器和一个FPGA逻辑阵列。它们之间怎么配合,就是我们这章要聊的核心。

1.1 Zynq架构简介

Zynq芯片内部可以分成两大块:PS(Processing System,处理系统)和PL(Programmable Logic,可编程逻辑)。PS就是那个ARM硬核处理器,PL就是那片FPGA逻辑。

核心要点:PS是固定的硬核,PL是灵活的可编程逻辑。驱动开发主要跑在PS上,但经常需要和PL交互。

PS部分包含以下关键组件:

  • 双核/单核ARM Cortex-A9 — 主频最高1GHz,带NEON SIMD引擎和浮点单元
  • 存储器接口 — DDR3/DDR2控制器、NAND Flash控制器、QSPI控制器
  • 外设接口 — GigE、USB 2.0、CAN、UART、I2C、SPI、SD/SDIO
  • 内部互联 — AXI总线矩阵,连接PS内部各模块以及PS与PL
  • 中断控制器 — GIC(Generic Interrupt Controller),支持SPI、PPI、SGI

PL部分则是一张白纸。你可以用它实现自定义外设、硬件加速器、高速数据通路,甚至软核处理器。我在一个工业相机项目里,就用PL实现了图像传感器的时序控制和像素数据预处理,把CPU从繁重的位操作中解放了出来。

1.2 PS与PL协同

PS和PL之间通过AXI总线通信。AXI是ARM公司推出的高性能总线协议,在Zynq上主要有三种接口:

接口类型 位宽 典型用途 我见过的坑
AXI_HP(高性能端口) 32/64位 大数据量传输,如DMA、视频帧 时钟域同步没做好,数据会丢
AXI_GP(通用端口) 32位 寄存器读写、控制命令 带宽有限,别拿它传大块数据
AXI_ACP(加速一致性端口) 64位 缓存一致性加速 配置复杂,新手慎用

PS和PL的协同模式,我总结为三种:

  1. PS控制PL寄存器 — 驱动通过AXI_GP读写PL中的控制/状态寄存器,适合低速控制场景
  2. PL主动访问PS内存 — PL通过AXI_HP直接读写DDR,适合高速数据采集
  3. 中断协同 — PL产生中断通知PS,PS响应后处理数据,适合事件驱动场景

我的经验:新手最容易犯的错误是把所有数据都走AXI_GP。GP端口带宽有限,一旦数据量上来,系统就卡死了。大块数据一定要走HP端口,配合DMA使用。

1.3 Linux在Zynq上的角色

Linux在Zynq上跑在PS的ARM核里。它负责管理硬件资源、调度任务、提供文件系统和网络协议栈。驱动开发,说白了就是让Linux内核认识并控制Zynq上的各种硬件模块。

Linux在Zynq上的典型分层是这样的:

Zynq平台Linux软件栈 用户空间应用程序 Qt界面 / 网络服务 / 数据处理 / 控制逻辑 系统调用接口(open/read/write/ioctl) Linux内核 进程调度 / 内存管理 / 文件系统 / 网络协议栈 设备驱动模型(platform驱动 / 字符设备 / DMA引擎) 硬件抽象层(时钟 / GPIO / 中断 / 定时器) Bootloader(U-Boot) + 设备树(DTB) Zynq硬件平台(PS + PL) ARM Cortex-A9 / DDR / 外设 / FPGA逻辑 / AXI互联 驱动开发关注区域

驱动开发主要关注中间两层:内核层的设备驱动模型,以及硬件抽象层的设备树和中断管理。你写的驱动,最终会被用户空间的应用程序通过系统调用接口来使用。

注意:Zynq上的Linux不是标准PC Linux。它没有BIOS,没有ACPI,硬件信息全靠设备树描述。设备树写错了,驱动根本跑不起来。我曾经因为设备树里一个中断号写错,排查了整整两天。

1.4 开发环境概览

做Zynq驱动开发,你需要搭一套完整的工具链。我个人的习惯是分三层来准备:

1.4.1 硬件环境

  • Zynq开发板 — 比如ZedBoard、Pynq-Z2、或者自己画的板子
  • JTAG调试器 — 推荐Xilinx Platform Cable USB II,或者更便宜的Digilent JTAG-HS3
  • 串口线 — USB转TTL,用于内核启动日志和调试输出
  • 网线 — 用于NFS挂载根文件系统和网络调试

1.4.2 软件工具链

工具 用途 版本建议
Vivado 硬件设计、PL逻辑开发、生成硬件描述文件(XSA) 2018.3 或 2020.1(稳定)
PetaLinux 构建Linux内核、设备树、根文件系统、U-Boot 与Vivado版本匹配
ARM交叉编译器 编译内核和驱动模块 gcc-arm-linux-gnueabihf
串口终端 minicom / putty / screen 任意
TFTP/NFS服务器 网络启动内核和挂载文件系统 Linux主机自带

1.4.3 开发流程

一个典型的Zynq驱动开发流程是这样的:

  1. 硬件设计 — 在Vivado中搭建PS和PL,生成bitstream和XSA文件
  2. 导出硬件描述 — 用Vivado导出XSA,包含PS配置和PL地址映射
  3. 构建启动镜像 — PetaLinux根据XSA生成U-Boot、内核、设备树、根文件系统
  4. 编写驱动 — 在Linux内核源码树外或树内编写驱动模块
  5. 交叉编译 — 用ARM交叉编译器编译驱动模块(.ko文件)
  6. 部署调试 — 通过NFS或scp将驱动模块传到开发板,insmod加载测试

我的建议:新手别一上来就用PetaLinux的自动构建。先手动走一遍交叉编译内核、生成设备树、制作根文件系统的流程。虽然麻烦,但能让你真正理解每个环节在干什么。我当年就是图省事用一键脚本,结果出了问题根本不知道从哪里查起。

嗯,这一章的内容就到这里。Zynq的架构、PS与PL的协同方式、Linux在其中的角色,以及开发环境的搭建思路,都是后续每一章的基础。你把这些概念理清了,后面写驱动、调bug的时候,心里就有底了。