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的协同模式,我总结为三种:
- PS控制PL寄存器 — 驱动通过AXI_GP读写PL中的控制/状态寄存器,适合低速控制场景
- PL主动访问PS内存 — PL通过AXI_HP直接读写DDR,适合高速数据采集
- 中断协同 — PL产生中断通知PS,PS响应后处理数据,适合事件驱动场景
我的经验:新手最容易犯的错误是把所有数据都走AXI_GP。GP端口带宽有限,一旦数据量上来,系统就卡死了。大块数据一定要走HP端口,配合DMA使用。
1.3 Linux在Zynq上的角色
Linux在Zynq上跑在PS的ARM核里。它负责管理硬件资源、调度任务、提供文件系统和网络协议栈。驱动开发,说白了就是让Linux内核认识并控制Zynq上的各种硬件模块。
Linux在Zynq上的典型分层是这样的:
驱动开发主要关注中间两层:内核层的设备驱动模型,以及硬件抽象层的设备树和中断管理。你写的驱动,最终会被用户空间的应用程序通过系统调用接口来使用。
注意: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驱动开发流程是这样的:
- 硬件设计 — 在Vivado中搭建PS和PL,生成bitstream和XSA文件
- 导出硬件描述 — 用Vivado导出XSA,包含PS配置和PL地址映射
- 构建启动镜像 — PetaLinux根据XSA生成U-Boot、内核、设备树、根文件系统
- 编写驱动 — 在Linux内核源码树外或树内编写驱动模块
- 交叉编译 — 用ARM交叉编译器编译驱动模块(.ko文件)
- 部署调试 — 通过NFS或scp将驱动模块传到开发板,insmod加载测试
我的建议:新手别一上来就用PetaLinux的自动构建。先手动走一遍交叉编译内核、生成设备树、制作根文件系统的流程。虽然麻烦,但能让你真正理解每个环节在干什么。我当年就是图省事用一键脚本,结果出了问题根本不知道从哪里查起。
嗯,这一章的内容就到这里。Zynq的架构、PS与PL的协同方式、Linux在其中的角色,以及开发环境的搭建思路,都是后续每一章的基础。你把这些概念理清了,后面写驱动、调bug的时候,心里就有底了。