FPGA 专题 01:Zynq PS/PL 协同——分工、启动、软件
本文是
fpga/专家层第 12 篇。入门见进阶 Zynq SoC 入门,接口/数据搬运见 Zynq 数据交互,MPSoC/Versal 见 UltraScale+。
一、PS/PL 分工

判断标准:每秒发生几十万次以上且流程固定 → PL;偶尔发生且逻辑复杂/要协议栈 → PS。
二、接口一览
| 接口 | 方向/用途 | 特点 |
|---|---|---|
| AXI-GP | PS 主→PL 寄存器 | 低带宽控制面 |
| AXI-HP | PL 主→PS DDR | 高带宽数据流(非一致性,软件管理) |
| ACP | PL↔PS cache 一致 | 紧耦合同享、低延迟 |
| AXI-Stream+DMA | 流式数据 | 经 DMA 进 DDR |
| IRQ F2F | PL→PS 中断 | 事件通知(别过密) |
| EMIO | PS 外设脚→PL | I2C/UART 等扩展 |
三、启动链
BootROM(固化, 读 boot 引脚)
→ FSBL(初始化 PS/时钟/DDR, 配 PL bitstream)
→ u-boot/SSBL
→ Linux + 设备树 + rootfs (或裸机应用)
bitstream: 打包进 boot.bin 开机配, 或 Linux 运行时动态加载四、Linux 访问 PL
| 方式 | 场景 |
|---|---|
| /dev/mem mmap | 调试最快,直接读写寄存器 |
| UIO (/dev/uioX) | 用户态 mmap+中断,免内核模块 |
| platform driver + 设备树 | 生产标准,DMA 用 dmaengine |
| VFIO | 用户态低延迟轮询、功能透传 |
纪律:寄存器映射规范化、DMA buffer 一致性(非缓存/dma_alloc)、中断聚合、设备树与 block design 地址一致。
五、软硬分工方法论:用频率、复杂度和确定性划边界
PS/PL 分工不是“FPGA 工程师做 PL、软件工程师做 PS”的组织划分,而是一个由三个量化判据决定的工程问题。第一判据是频率:每秒发生的次数在几十万以下、每次允许软件开销的,放 PS;每秒百万次以上、必须逐次响应的(每个采样点、每个网络小包、每条总线事务),放 PL 硬件或 DMA,CPU 经中断/描述符批量介入。一个实用换算:一个 A9/A53 核处理一次中断加缓存行搬运的全成本常在数百纳秒到微秒级,每秒十万次中断已占可观负载,百万级直接把系统拖入抖动深渊。第二判据是控制复杂度:分支多、状态多、要解析文本/协议/文件系统/人机交互的,软件成本远低于硬件;操作固定、数据通路规则、算术密集的,PL 成本更低。TCP 握手、Web 配置页、日志压缩放 PS,固定格式帧的线速过滤、FIR/FFT、时序发生放 PL,是自然分界。第三判据是确定性:要求抖动在微秒甚至纳秒级、响应必须可证明有上界的任务(保护逻辑、实时控制总线、精确时间戳),Linux 用户态不是合格执行者;它们属于 PL,或属于 R5 这类实时核(见 专题 02)。
边界设计有几条经验法则。数据面与控制面分离:数据块在 PL 内或经 HP 口流动,控制面只传“开始/停止/配置参数/状态/错误”小寄存器;理想系统稳态运行时 CPU 占用接近于零。事件聚合:PL 不逐数据中断,而在帧/块边界、FIFO 水位、错误事件时中断,配合硬件计数器让软件读趋势。慢路径上送:PL 处理规则流量,罕见/异常/协商类流量经队列上送 CPU,CPU 的决策再写回 PL 表项(这与网络卸载的快/慢路径同构,见 专题 01 第九节)。可演进性:预计频繁变更的策略放软件,稳定的原语放硬件。评审分工时,最能暴露问题的问题是:“这条路径上 CPU 每秒被要求做几次事?最坏情况下多少次?”如果答案让人不安,边界就划错了。还要避免反向错误:把已经稳定的数据通路用软件“先实现以后再硬件化”,往往因为软件版本已带着时序 workaround 部署而永远还不了技术债;架构期就该定路径。
六、启动链深论:BootROM、FSBL、比特流与安全启动
Zynq 的启动是 PS 主导的,这一点决定了很多“PL 先上电”的直觉都是错的。上电后芯片内固化 BootROM 首先执行:采样启动模式引脚选择 JTAG/SD/QSPI/NAND,做最小初始化,从启动介质读镜像头(含校验、地址、长度),把 FSBL 搬进片内 OCM 并校验后跳转。FSBL 由平台工具生成、可加用户钩子,负责 PS 的实质初始化:PLL/时钟树、DDR 训练(这一步失败的症状与 专家 06 一致)、MIO/引脚、电源域,然后处理镜像中的后续成分——比特流(配置 PL)、SSBL(u-boot)或裸机 ELF;比特流可在 FSBL 阶段静态配置(boot.bin 打包),也可推迟到 u-boot/Linux 运行时加载(/lib/firmware 经 FPGA manager 接口)。理解这个顺序能解释几类怪象:FSBL 挂死多为 DDR/启动介质/镜像头问题而非 PL 设计;PL 在 Linux 启动早期不可见是因为选择了延迟加载;JTAG 下载时软件流程与冷启动流程不同,“JTAG 能跑、flash 启动不行”要查 FSBL 钩子和镜像打包。
MPSoC 启动链更复杂:增加 **PMU 固件(PMUFW)**负责电源/复位/安全服务,FSBL 在其之后运行,之后是 ARM Trusted Firmware(ATF/BL31,EL3 安全监控)、可选的 u-boot(BL33)、A53 上的 Linux,R5 可在极早阶段启动(RTU/lock-step 应用甚至先于 Linux);镜像格式变成 BOOT.BIN 含多个分区,由 BIF 配置文件描述。安全启动是高价值产品必须规划的:启用 BootROM 哈希/RSA(MPSoC 为 RSA-4096/SHA3,可加 AES 加密镜像)后,FSBL/ATF/u-boot/内核/设备树/比特流每一级验签下一级,形成信任链,未签名或被篡改的比特流无法加载——这防止了“换掉 flash 里的比特流就接管设备”的攻击(完整安全话题见 专家 24)。工程建议:启动各阶段耗时要测量并写入启动时间预算(FSBL/DDR 训练是毫秒级大头);双分区 A/B 镜像 + u-boot 回滚是现场可靠升级的基本模式;保留强制 JTAG/恢复启动的硬件手段(拨码或隐藏按钮),否则一次坏镜像可能让设备变砖。把启动链当作一个需要架构设计的子系统,而不是工具默认点几下,是产品级 Zynq 开发与教学实验的重要区别。
七、Linux 与 PL 的接口工程:设备树、驱动与内存策略
生产系统中软件如何正确地“看见”PL,决定了后期维护成本。**设备树(device tree)**是硬件描述的单一事实源:PL 中每个寄存器块/DMA/中断节点给出 compatible 字符串、reg(从块设计地址表导出的物理地址和长度)、interrupts(GIC SPI 号与触发类型)、时钟、DMA 通道引用;内核启动时 platform bus 据此 probe 驱动。设备树与 bitstream/block design 地址不一致是最高频的联调 bug:设备树里 reg 差一位表现为驱动 probe 成功但读写无响应或读到邻居寄存器,必须在发布流程里自动从硬件工程导出地址(或人工评审双签)。驱动层次选择应按成熟度递进:原型期 /dev/mem mmap 最快但绕开内存保护、生产风险高(指针算错可以踩垮整个内核);自研简单设备用 UIO(用户态驱动框架,mmap 寄存器 + read/poll 收中断),把硬实时/轮询逻辑放用户态;正式产品写 platform_driver:probe 中 ioremap 寄存器、request_irq、用 dma_alloc_coherent/dma_map_sg 管理共享内存(与 专家 08 同一套 DMA 语义);Xilinx DMA/VDMA 走 dmaengine 子系统;需要多进程隔离/安全边界时走 VFIO。用户态框架(DPDK 风格的轮询、hugepage)用于低延迟数据面。
内存策略是正确性核心。三类共享内存各有归属:描述符环、控制结构体——小而频繁,用 coherent 内存(或 ACP/HPC),双方免维护;大帧缓冲/数据流——大而高带宽,用非缓存映射或流式 map/unmap + 显式 cache 同步,避免一致性流量吃掉 DDR 带宽(详见 专家 13);寄存器窗口——设备内存属性(device memory),杜绝推测访问和写合并。中断设计:PL→PS 每核可用的私有/共享中断数量有限,多个事件源应在 PL 内用中断聚合器(status/mask/ack 寄存器)合并为少量线;中断处理只做“确认+唤醒下半部”,重处理放线程化中断或工作队列;配合事件计数寄存器,软件能区分“没事件”和“中断丢了”。调试纪律:寄存器块永远返回读响应(AXI 响应规则见 专家 10),PL 时钟/复位由 PS 可控并暴露状态;提供版本寄存器(硬件版本/步进)让驱动启动时校验兼容,防止驱动与比特流版本错配。把这些接口约定文档化为“PS-PL 接口控制文档”,与 RTL、设备树、驱动头文件一起版本化,是多人团队避免扯皮的关键。
八、三种软件形态的选择:裸机、RTOS 与 Linux 的真实边界
“跑什么系统”应由延迟目标、生态需求和可靠性模型共同决定。裸机(standalone/BSP):单任务超循环加中断,启动到应用毫秒级,无调度抖动,资源完全可控;适合启动早期、安全监控、极简仪器、以及 R5 上的实时任务。局限是一切都要自己实现:网络栈(lwIP 可用但生态弱)、文件系统、USB、多设备管理,软件复杂度一旦超过“控制+采集+上报”就力不从心。FreeRTOS/RTOS:多任务、队列、信号量、确定优先级抢占,成熟端口带网络/文件系统组件;适合中等复杂度且要响应上界的设备,A9 上可跑,MPSoC 上更推荐跑在 R5 以获得 TCM 和确定性中断。实时性不是“用了 RTOS 就有”:要配置抢占、绑定中断优先级、控制长临界区、避免驱动里关中断过久,并实测最坏响应。Linux(PetaLinux/Yocto 构建):完整网络栈、文件系统、语言运行时、远程管理、多用户和丰富驱动,是有人机交互、联网存储、复杂协议设备的唯一现实选择;代价是启动秒级、调度抖动微秒到毫秒级、实时行为需要 PREEMPT_RT 补丁、隔离 CPU 和 carefully tuned 驱动才能压到几十微秒。
典型的成熟架构是混合部署:MPSoC 上 A53 跑 Linux 做管理面/网络/UI,R5 跑 FreeRTOS 或裸机做实时控制和安全监控,PL 做数据面和硬件时间基;三核通过共享内存(带 cache 同步)、核间消息(OpenAMP/Remoteproc/RPMsg)和中断通信。这要求定义清楚生命期:Linux 重启不应影响 R5 的安全功能(看门狗、保护输出由 R5 持有),R5 可先于 Linux 启动并独立降级运行。7000 单 A9 时代的折中是 Linux 加 PL 承担全部硬实时。选型上的反模式包括:在 Linux 用户态轮询 GPIO 冒充实时控制;为了一个 UART 控制协议上完整 Linux 再抱怨启动慢;在裸机里手写商业级 TCP/IP 栈和 TLS。工程交付时应给出“任务—执行位置—周期/截止期—抖动预算”表,每个软件任务都能在表里找到位置,选型争论就会变成参数核对。
九、联合调试与可观测性:PS+PL 系统怎么定位问题
异构系统最难调试的是“跨越处理器与逻辑边界的故障”,方法论是让两边都可观测、用统一时间相关联。基础分层:PS 侧用 JTAG 调试器做内核/裸机源码调试、串口日志、perf/ftrace、top/iostat 和内核 tracing;PL 侧用 ILA/VIO、硬件寄存器计数器;二者各看各的只能定位单边问题。联合手段:其一是共享事件日志——PL 在一块环形内存(或 BRAM/URAM 窗口)按硬件时间戳写事件,PS 周期性读出并与软件日志合并,这比 ILA 抓一次几秒的波形更能抓偶发问题;其二是统一时间基——PL 维护主计数器/硬件时钟,PS 启动时测量两边时钟偏移(或经 PTP 类机制校时),所有日志带同一时间域,才能回答“中断到的时候 FIFO 水位是多少”;其三是故障快照寄存器组——PL 检测到错误时锁存状态(通道、地址、计数、温度、链路状态),PS 读走并持久化(遥测体系见 专家 45)。
常见联合故障的定位路径:PL 无响应先确认比特流已加载(版本寄存器可读是最有效的心跳)、AXI 时钟/复位、GP 口地址;数据偶发错误先按缓存一致性假设排查(用全非缓存内存复测,见 专家 13),再查跨时钟域和字节序;性能不稳同时记录 PS CPU 占用、中断频率、HP 口计数器和 FIFO 水位,区分软件供给不足、DDR 到顶、PL 反压;启动偶发失败查 FSBL/DDR 训练/电源时序与比特流加载完成中断。还要建设“软件可控的 PL 测试模式”:环回、流量注入、错误注入开关,产线和现场无需示波器即可完成大部分自检(BIST 思路见 专家 39)。最后保留量产构建的调试边界:调试寄存器、环回口、未用中断在出厂镜像中按安全策略关闭或加锁,避免成为攻击入口(见 专家 24)。
十、PS/PL 协同设计清单与常见组织反模式
把方法论收成评审清单。需求与分工:任务频率/截止期表、数据面与控制面框图、CPU 稳态占用预算;接口:寄存器规范(版本/控制/状态/中断/测试,含字段保留策略)、GP/HP/ACP 端口选择与带宽核算、AXI 响应不缺失、中断聚合方案、跨域 FIFO;内存:共享区类型(coherent/非缓存/设备)、大小与分配方式、所有权切换与 cache 同步点、地址三视图(物理/bus/虚拟);启动:启动介质与引脚、FSBL/PMUFW/ATF 来源、比特流加载阶段、A/B 升级与回滚、安全启动需求;软件:设备树来源与评审、驱动层次、核间通信(如用 R5)、日志时间对齐;验证:PL VIP/仿真、PS-PL 联调用例(启动、加载、错误注入、长稳 72 小时、升级回滚)、产线自检;发布:比特流/FSBL/设备树/驱动/固件版本联动(发布纪律见 20 比特流烧录与四方版本一致 manifest/门禁)。
组织反模式值得点名。其一,先做硬件再扔给软件:PL 团队定完寄存器和缓冲格式,软件只能接受,结果控制面设计难用、每帧中断、没有状态寄存器,软件用十倍复杂度兜底。正确做法是接口在第一天由双方共同评审。其二,软件需求频繁变更冲垮 PL:产品策略每周改却要求改 RTL,应把策略层留在软件、PL 只做稳定原语。其三,性能问题互相归因:没有统一计数器,“DMA 慢”在软件、DDR、PL 间踢皮球;计数器和水位日志是协作的共同语言。其四,忽视生命周期与升级:比特流与驱动各自发布、无版本握手,现场组合爆炸;必须启动握手校验、联动发布。其五,让 Linux 承担安全底线:保护功能依赖用户态进程存活,进程被 kill 或系统挂起时设备失去保护;安全底线放 PL 或 R5。把这张清单和五个反模式纳入架构评审,Zynq 这类异构平台的主要系统性风险就能在设计早期被看见。
十一、寄存器接口设计:PS/PL 契约的最小完整集
控制面设计的质量几乎决定了联调效率,一个“可产品化”的寄存器块应包含以下固定栏目。身份与版本:魔数、IP 版本、补丁号、硬件步进,驱动 probe 时首先校验,不匹配直接拒绝运行;这一条在多代产品并存的现场价值极大。控制与状态分离:控制寄存器写触发动作(带写脉冲,不允许“写 0 也改变状态”),状态寄存器只读反映硬件,二者不共用地址,避免软件读改写误伤。中断块:status(硬件写一表示事件)、mask、ack(写一清零)三个寄存器成套,ack 与新事件同拍到达时的“保留新事件”语义要在 RTL 保证;额外提供原始事件计数,软件可发现丢中断。DMA/缓冲管理:门铃(提交计数)、消费计数、错误状态、当前活动描述符索引,全部支持 32/64 位原子读(64 位计数器要么单次总线访问,要么提供高低字稳定快照机制)。测试与可观测:环回使能、流量注入、错误注入、计数器冻结/快照,供产线 BIST 和现场诊断(方法见 专家 39)。地址按 4KB 块组织,字段只追加不移动,废弃字段保留并文档化,这是跨版本兼容的基本纪律。寄存器规范应从单一来源(CSV/IP-XACT/SystemVerilog 包)生成 C 头、文档和 RTL 解码,杜绝手工三处维护。
软件访问模式也要定规范:批量配置用一段连续寄存器加“硬件应用位”(写完所有配置后置位,硬件在边界加载),避免中间态生效;状态轮询必须带超时并在超时后读错误寄存器;严禁用 /dev/mem 在生产代码里散写魔地址。控制面延迟预算要评估:GP 口一次 Lite 事务在 Linux 用户态完成“系统调用+驱动+AXI”的成本可达微秒级以上,需要微秒级响应的控制应放在 R5/PL 本地,A53 Linux 只下发参数。把寄存器块当作正式 API 做文档和 code review,PS/PL 的边界纠纷会大幅减少。
十二、功耗、热与成本:异构分工之外的系统账
PS/PL 分工讨论常忽略物理账:同一个功能放哪个域,功耗和 BOM 成本可能差一个数量级。A9/A53 处理控制协议时功耗低,但一旦让 CPU 软件搬运/轮询数据流,满载核心的持续功耗、内存访问和必需的 Linux 级供电散热会显著抬高系统成本;PL 数据通路在高吞吐下每比特功耗通常远优于软件,但空载静态功耗、配置存储器和更高级别器件的价格是固定成本。架构早期应做粗粒度功耗模型:各域利用率(CPU 负载百分比、PL 翻转率、收发器线速、DDR 占空比)输入厂商功耗工具,比较“软件方案用低档器件”与“硬件方案用高档器件”的总功耗/成本拐点(方法见 专家 23)。热设计还要考虑结温对 PS DDR、收发器裕量的连锁影响(专家 04)。
MPSoC 的电源域能力还提供了系统级省电策略:空闲时由 PMU 挂起 A53、关部分 PS 域甚至 PL 电源,R5/唤醒源保持低功耗值守,事件触发再恢复——无人值守的电池设备应在架构期设计这条唤醒链(唤醒源、恢复时间预算、上下文保存位置)。成本账还包括许可证(某些硬核/IP 按器件收费)、PCB 层数与电源相数、量产测试时间。结论是:PS/PL 的“最佳分工”不是纯技术问题,而是在性能、确定性、功耗、BOM、供应链约束下求最优;把这些约束写进架构输入,分工讨论才有共同前提。
十三、典型失败案例复盘与一页纸总结
复盘几个跨项目反复出现的失败模式。案例一,逐样本中断:采集口每来一个采样点中断一次 A9,CPU 100%、丢样、用户界面卡死;修复是 PL 聚合为块、完成中断加环形缓冲。案例二,缓存踩内存:驱动把内核指针写进 DMA 描述符,系统升级内存布局后偶发踩踏文件系统;根因是地址域不分,修复为统一 dma API(专家 13)。案例三,启动依赖倒置:Linux 启动后才加载比特流,但 u-boot 阶段的某外设管理假定 PL 已就绪,冷启动偶发失败;修复为比特流进 FSBL 分区并校验完成状态。案例四,安全洞:调试用 /dev/mem 和未鉴权的网络配置接口留在量产镜像,现场可改写任意外设;修复为正式驱动+最小权限+关闭调试口(专家 24)。案例五,版本错配:驱动更新了寄存器布局而现场比特流未更新,写控制寄存器命中状态位;修复为启动版本握手和联动发布。每个案例的共同点都是边界(频率边界、地址边界、启动边界、信任边界、版本边界)失守,而非某个算法写错。
一页纸总结:PS 做慢而复杂、PL 做快而确定,边界按事件频率划在块/帧级;控制走 GP、数据走 HP/ACP/Stream、事件走聚合中断;启动按 BootROM→FSBL/PMUFW→ATF→u-boot→Linux 编排并规划安全启动与 A/B 回滚;软件访问 PL 按 /dev/mem(原型)→UIO→platform driver(生产)→VFIO(低延迟)选型;共享内存按 coherent/非缓存/显式同步/一致性口四种模式之一处理,单所有权+屏障;接口契约(寄存器、地址、中断、版本)单一来源、三方会签;联合可观测性依赖统一时间戳和跨域事件日志;安全底线放 PL/R5。按这份清单执行,Zynq 类异构系统的绝大多数系统性风险都能在设计阶段被关闭。
十四、团队工作流:让软硬协同真正并行
PS/PL 协同最大的浪费不是技术难度,而是串行等待:硬件等需求、软件等板子、联调等接口稳定。成熟团队用三件套打破串行。接口先行:项目第一周冻结寄存器映射、共享内存布局、中断列表、DMA 描述符格式和启动顺序,以单一来源生成 C 头、SystemVerilog 包、文档和设备树片段;之后任何变更走带影响评估的评审,接口稳定后软件可以在 QEMU/单板模型、RTL 可以在测试台上并行开发。双端桩件:PL 侧提供一个“仿真从设备”固件(固定响应寄存器、回环 DMA),软件在真实比特流前就能开发驱动;PS 侧提供测试用的内存模式和流量发生器,RTL 团队不必等 Linux 启动就能压测数据面。桩件不是临时脚手架,而是长期资产——CI 每次提交都在桩件上跑协议合规测试(专家 46 的 FPGA CI 实践)。联调节奏:垂直切片在项目极早期打通(哪怕只传一帧数据),之后每两周保持可演示状态;集成问题被切成小批次持续暴露,而不是末期一次性爆炸。
职责界面也要明确:比特流、FSBL/PMUFW、设备树、内核驱动、用户态库、启动镜像各自有明确 owner 和版本库;发布物是一个组合(manifest),现场设备上可读出版本组合并与测试矩阵对照。问题管理上,建立“跨域 bug”标签和三方值班机制:凡是单方无法在约定时间内定位的问题,自动升级为硬件、FPGA、软件三方联合,用统一时间基的日志(第九节)会审,避免长期踢皮球。度量上除了功能进度,还跟踪两类风险指标:接口变更次数(频繁变更说明前期架构不稳)、跨域 bug 平均关闭时间(反映可观测性水平)。把协同当成需要设计的工程过程,而不是“开会沟通”,是 Zynq 项目按期交付与陷入集成泥潭团队之间真正的区别。
常见坑对照
| 坑 | 后果 | 正确做法 |
|---|---|---|
| CPU 进每帧/每包路径 | 中断风暴卡死 | 数据面留 PL,控制才中断 |
| HP 口读缓存脏数据 | 数据不一致 | flush/非缓存内存,或用 ACP |
| 设备树地址错 | 驱动 probe 失败 | 与 block design 一致 |
| bitstream 没配就跑 | PL 无响应 | FSBL 打包或运行时加载确认 |
| 中断每个数据一次 | 系统抖动 | 批量/帧级中断+阈值 |
| 数据面走 GP 口 | 带宽不足 | 数据走 HP,GP 只控制 |
相关与延伸
入门:Zynq SoC 入门、块设计 IPI。 数据:Zynq 数据交互深水区、DMA 搬运、嵌入式软件。
一句话总结
Zynq 协同的核心是把系统切成控制面和数据面:PS(A9/A53+R5)跑慢而复杂的控制、协议栈、文件系统、UI、异常处理(Linux/FreeRTOS/裸机),PL 跑快而固定的高吞吐数据通路、确定性时序和线速处理;二者间控制走 AXI-GP 口配寄存器、数据走 AXI-HP(高带宽、一致性软件管理)或 ACP(cache 一致、紧耦合)、事件走 F2F 中断。铁律是绝不让 CPU 进入每帧/每包的数据路径(中断频率过高会拖死系统),数据大块经 HP/DMA 进出 DDR、PS 只在启停和异常时介入。启动链是 BootROM→FSBL(初始化 PS 并可配置 PL bitstream)→u-boot→Linux+设备树;Linux 下访问 PL 从调试用 /dev/mem、开发用 UIO、生产用 platform driver+设备树(与 block design 地址一致)、低延迟用 VFIO,大块 DMA 注意缓存一致性。