1. Zynq架构概览:PS与PL的划分、AXI总线基础、Zynq启动流程与调试入口

好,咱们开始第一讲。Zynq这个芯片,说白了就是一颗ARM处理器和一个FPGA坐在同一块硅片上。很多人刚接触时容易懵——它到底算ARM开发还是FPGA开发?我的答案是:两者都是,而且你得学会让它们配合。

我个人习惯把Zynq看作一个「双核团队」:PS(Processing System)是项目经理,负责跑Linux、做控制、处理网络协议;PL(Programmable Logic)是执行团队,专门干那些高吞吐、低延迟的脏活累活。你想想看,如果让ARM去处理1Gbps的视频流,光是中断就能把它淹死。但交给PL,流水线一搭,数据直接流过,延迟能控制在微秒级。

核心认知: Zynq不是ARM+FPGA的简单拼凑,而是通过AXI总线把两者粘合成一个异构计算平台。调试的难点,恰恰就出在这个「粘合」上。

1.1 PS与PL的划分

先看一张我手绘的架构简图,帮你建立整体印象:

Zynq 架构概览:PS 与 PL 的划分 PS (Processing System) ARM Cortex-A9 双核 / 32KB L1 / 512KB L2 存储控制器 DDR3 / OCM / QSPI 高速外设 USB / GbE / SD 低速外设 UART / SPI / I2C / GPIO GIC 中断控制器 共享外设中断 DMA 控制器 PL 专用通道 内部互联 (OCM / L2 Cache / Snoop Control) PL (Programmable Logic) 逻辑资源 LUT / FF / DSP48 BRAM 36Kb 块 / 真双口 高速收发器 GTP / GTX / PCIe I/O 资源 HP / HR Bank AXI 总线 桥接 调试入口 PS: JTAG / 串口 / GDB PL: ILA / VIO / ChipScope 交叉: AXI Monitor / Trace

从图上你能看到,PS和PL是两块完全独立的「领地」。PS这边跑的是ARM硬核,有自己的一级二级缓存、内存控制器和各种外设。PL那边就是FPGA fabric,LUT、FF、DSP、BRAM这些资源随便你用。

但真正有意思的是中间那条紫色的AXI总线。它就像一座桥,让两边可以高速交换数据。我在项目中遇到过不少新手,把PS和PL当成两个独立芯片来调试,结果两边数据对不上,查了三天才发现是AXI握手时序出了问题。

我的建议: 从一开始就把PS和PL看作一个整体系统。调试时,先确认AXI总线能正常工作,再分别排查两侧的逻辑。

1.2 AXI总线基础

AXI是ARM公司推出的高性能总线协议,Zynq里主要用了三种:

AXI 类型 特点 典型用途 我的经验
AXI4 高性能内存映射,支持突发传输 DDR访问、大数据块搬运 适合一次传几百字节以上的场景
AXI4-Lite 简化版,单次传输,无突发 配置寄存器、状态查询 我习惯用它做控制通道,简单可靠
AXI4-Stream 无地址,连续数据流 视频流、DMA、FIFO 延迟最低,适合高速流水线

为什么要有三种?你想想看,如果你只是想读一个状态寄存器,用AXI4搞个16拍的突发传输,那不是杀鸡用牛刀吗?反过来,如果你要传一帧1080p的视频,用AXI4-Lite一次传4字节,CPU得等到天荒地老。

AXI的握手信号其实不复杂,核心就是五个通道:

  • 读地址通道(AR):主机告诉从机「我要读哪个地址」
  • 读数据通道(R):从机把数据返回给主机
  • 写地址通道(AW):主机告诉从机「我要写哪个地址」
  • 写数据通道(W):主机把数据发给从机
  • 写响应通道(B):从机告诉主机「写完了,没问题」

每个通道都用VALID和READY信号做握手。VALID表示发送方数据有效,READY表示接收方准备好了。两者同时为高时,一个传输完成。

避坑指南: 我曾经在一个项目中,PL端的AXI从机没有正确处理READY信号,导致PS端一直等不到响应,整个系统卡死。后来用ILA抓了波形才发现,从机的READY拉高了一个周期就撤回了,而主机只在那一个周期采样。记住:VALID和READY必须同时为高才算一次握手成功。

1.3 Zynq启动流程

Zynq的启动流程,说白了就是「谁先醒,谁后醒」的问题。PS是老大,它先启动,然后通过PCAP接口把PL的bitstream加载进去。整个过程分三个阶段:

  1. BootROM(阶段0):芯片上电后,ARM核先执行片内的BootROM代码。它检查启动模式引脚(MIO[2:0]),决定从QSPI、NAND、SD卡还是JTAG加载。
  2. FSBL(阶段1):BootROM把FSBL(First Stage Boot Loader)加载到OCM(On-Chip Memory)并执行。FSBL负责初始化PS的基本外设(时钟、DDR、MIO),然后加载PL的bitstream。
  3. SSBL / 用户程序(阶段2):FSBL加载完bitstream后,再加载SSBL(比如U-Boot)或者直接启动裸机程序。SSBL再加载操作系统内核。

这里有个关键点:PL的bitstream是在FSBL阶段加载的。也就是说,在PS跑Linux之前,PL就已经配置好了。如果你想让PL在Linux启动后再动态重配置,那就得用PCAP驱动或者DMA方式,那是进阶话题了。

调试入口: 启动流程的每个阶段都有对应的调试手段。BootROM阶段只能用JTAG;FSBL阶段可以用Xilinx SDK的调试器单步执行;Linux启动后,可以用GDB或者printk。

1.4 调试入口

调试Zynq,你得准备好几把「钥匙」:

调试对象 工具 接口 适用场景
PS 软件 Xilinx SDK / Vitis JTAG + 串口 单步调试、断点、变量查看
PL 逻辑 Vivado + ILA / VIO JTAG 波形抓取、内部信号观察
PS-PL 交互 AXI Monitor / Trace JTAG 总线事务分析、性能统计
Linux 内核 KGDB / printk / ftrace 串口 / 网络 驱动调试、性能分析

我个人最常用的组合是:ILA抓PL波形 + SDK调试PS代码 + 串口打印日志。三管齐下,大部分问题都能定位。

一个小技巧: 在PS代码里加一个GPIO控制LED,在PL里也加一个GPIO控制另一个LED。调试时,两边各闪一下,你就能直观地看到「PS跑到这里了」和「PL跑到那里了」。这比看波形直观多了。