这两年国产化替代的项目一个接一个,我上手复旦微FMQL45T900这块芯片的第一感觉,可以说既惊喜又熟悉。惊喜的是它的架构几乎死磕Xilinx ZYNQ的路子——PS端四核ARM Cortex-A7,PL端可编程逻辑,内部AXI高速互联;熟悉是因为如果你之前调过ZYNQ,那么整个迁移过程就是一套从硬件管脚到启动流程再到驱动适配的翻新工程。这篇文章就是我从项目实战角度整理的完整迁移笔记,覆盖方案选型、工具链切换、FSBL/U-Boot启动移植、PS与PL协同调试,以及图像采集与高速接口落地时踩过的坑。想在这类国产化ARM+FPGA平台做开发的工程师,建议直接收藏。
1. 迁移前的方案评估:FMQL45T900到底对标谁
1.1 复旦微FMQL45T900的核心架构速览
我用的这颗FMQL45T900,单从架构上说,完全可以理解为“国产ZYNQ”:PS端是四核ARM Cortex-A7,PL端是复旦微的FPGA逻辑资源,PL与PS之间通过AXI总线桥接,配套的DDR控制器、UART、GEM以太网、SD/SDIO、QSPI、I2C、SPI、USB这些外设也全部集成在PS内部。
在这颗芯片上跑Linux和裸机程序,PC寄存器跳转、中断控制器GIC、定时器这些和Cortex-A7原生的使用方法是一样的。和Xilinx Zynq-7045这类器件相比,单看资源数量和接口规格,FMQL45T900的PL端处于同一量级,做图像采集、信号处理、接口转换这些中规模设计完全够用。
需要注意的一点是,虽然架构相似,但FMQL45T900并不是每一处寄存器地址都和Zynq一致。片上的UART、QSPI、SD、GPIO等外设基地址,和Zynq手册里的地址有差异。这一点后面移植驱动时最坑。
1.2 迁移之前必须想清楚的三个问题
第一,确认移植的目标是“硬件兼容替换”还是“重新设计”。所谓硬件兼容替换,是希望把原来Zynq板卡上的型号直接换成FMQL45T900,保持管脚和板卡基本不变;这种模式省板卡改版成本,但对封装的引脚定义要求很高。重新设计则是利用FMQL45T900的资源,从原理图开始调整,灵活度最高,工作量也最大。
第二,评估PL资源占用。迁移之前,把你原来Vivado工程里LUT、FF、Block RAM、DSP的数量统计出来,再到FMQL45T900对应的器件手册里核对。如果原设计里的DSP使用量逼近上限,就要提前考虑把部分算法挪到PS端用ARM算,或者优化算法架构。
第三,摸清外设依赖。如果原设计用了Zynq PS内置的MIG DDR控制器、GEM网口、USB控制器这些硬核外设,迁移时就要对比FMQL45T900的PS外设实现方式。比如DDR部分,时序参数要根据板卡实际内存颗粒重新配置,不是简单地Vivado里改一改芯片型号就行的。
2. 开发环境重构:从Vivado到Procise/FMSDK
2.1 工具链选型与版本选择
复旦微FPGA配套的开发环境叫Procise,负责PL端的工程管理、综合、布局布线、约束、比特流生成;PS端嵌入式软件开发用FMSDK,负责FSBL、U-Boot、应用代码的编译与调试。
第一次打开Procise的时候,熟悉感很强。工程树、约束文件、时序报告、逻辑利用率这些模块布局和Vivado的交互逻辑很相似。实际用下来,综合和布局布线的速度中规中矩,对于中等规模的图像算法工程,一次全编译大概比Vivado慢一些,但不至于耽误开发节奏。
版本建议用复旦微官方比较新的稳定版本。我踩过的一个坑是:旧版Procise对新器件的小批次型号支持不全,结果布局布线阶段报奇怪的单元找不到错误,换新版工具后问题消失。另外,FMSDK基于Eclipse框架,如果你之前用过Xilinx SDK,里面许多快捷键和工程导入导出逻辑可以直接复用。
2.2 工程结构设计与IP移植策略
工程结构上,Procise同样支持Tcl脚本批处理,如果你之前的Vivado工程有比较完善的后仿真、时序约束脚本,可以转换成Procise的Tcl语法保留下来。
关键的是PL端IP核怎么办。分三种情况:
第一种,纯逻辑的IP,比如FIFO、移位寄存器、简单的数学运算,这些在Procise的IP核库里通常有对应或替代型号,直接重新生成替换就好。
第二种,高速接口类IP,比如MIPI RX、LVDS收发、PCIe、Ethernet MAC,这类IP往往和FPGA厂商的硬核高度耦合,迁移时要重点看复旦微有没有对应IP,或者提供开放的原语级方案。我这次做的MIPI输入链路,原先是Xilinx的MIPI CSI-2 IP,迁移到FMQL45T900上是用原语+状态机重构的。
第三种,软核CPU类IP,比如MicroBlaze核,这类没法直接迁移。如果你的原设计里用MicroBlaze做软核控制,可以考虑直接把控制逻辑合并到PS端ARM中,减少PS与PL的交互复杂度。
2.3 交叉编译环境搭建:从armcc到arm-linux-gnueabihf
FMQL45T900的PS端是Cortex-A7,跑Linux的话用arm-linux-gnueabihf-gcc交叉编译器即可,常见的选择是Linaro提供的工具链。
很多朋友在搜索引擎里搜“arm compiler 5.06”这类关键词,这里要多说一句:ARM Compiler 5.06是ARM自家面向Cortex-M系列MCU的编译器(Keil MDK内置的就是它),如果目标是给Cortex-A7上跑Linux做交叉编译,用手册里推荐的Linaro GCC更合适。我之前见过一个项目组用错编译器,编出来的裸机程序能跑,但一接Linux应用就各种段错误,排查半天发现是工具链sysroot不匹配。
搭建交叉编译环境时,我习惯把工具链放在/opt下,并配置系统PATH。最关键的是sysroot,你交叉编译的应用依赖的libc、libstdc++、zlib这些运行库,必须和板卡上rootfs里的版本对应,否则就会出现“运行时找不到GLIBC_2.28”之类的错误。
给一个最基础的编译验证示例:
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH export CROSS_COMPILE=arm-linux-gnueabihf- export ARCH=arm arm-linux-gnueabihf-gcc -o hello hello.c -static编译完成后通过NFS或者tftp把hello传到板卡上执行,打印正常说明环境没问题。
如果项目UI是基于Qt的,比如热搜里常出现的Qt5.5.10 ARM Linux开发,交叉编译时记得加上tslib支持,触摸屏和显示设备通过QPA插件(linuxfb或eglfs)指定。我个人经验,Qt5.5.10版本偏老,很多显示驱动和OpenGL ES适配都是独立patch,迁移到国产ARM平台时要确认对应版本的libQt5Gui、libqlinuxfb等库是否完整。
3. 第一块FSBL:启动流程完整移植
3.1 FMQL45T900启动流程解析
FMQL45T900的启动流程和Zynq大体一致:上电后芯片内部固化的BootROM先接管,根据启动模式引脚的电平状态选择启动介质,把BootROM引导程序(对应Zynq里的FSBL)加载到片上RAM,再由FSBL完成DDR初始化、外设初始化、加载U-Boot或裸机应用。
启动模式常见的有QSPI Flash启动、SD卡启动、JTAG启动。板卡上一般通过拨码开关配置启动模式。
一个需要注意的差异点是启动镜像的格式。Zynq生成的BOOT.bin里包含FSBL、U-Boot、bit流等分区,FMQL45T900虽然概念类似,但镜像头的魔数、分区表格式不一定相同。我在网上找资料的时候,看到不少朋友直接用Zynq的bootgen工具生成镜像写到复旦微板卡里,结果启动失败,基本都是格式不匹配导致的。
3.2 FSBL与DDR初始化的实战
FMQL45T900的FSBL工程在FMSDK里创建,主要的修改点集中在DDR配置上。DDR的时钟频率、行列地址宽度、Bank数量、时序参数,每一项都要和板卡上实际用的DDR颗粒严格对应。
修改的思路是:找到FSBL工程里的DDR配置头文件,对照DDR颗粒的datasheet填写参数。如果板卡DDR频率从1066Mbps调整到1600Mbps,还要检查PS端DDR控制器的时钟源配置,保证PLL输出频率在芯片支持的范围内。
这里分享一个排查DDR问题的经验:FSBL阶段如果DDR配置错误,现象很多样,有的表现为U-Boot直接卡死,有的表现为Linux内核启动早期报“undefined instruction”异常。我习惯用JTAG调试器连接板卡,在FSBL的main函数入口处查看PC指针是否还在预期地址,如果PC值跑飞或者反复触发data abort,优先怀疑DDR参数不对。
3.3 U-Boot适配与调试
U-Boot使用起来比FSBL复杂一些。需要准备的东西包括:对应芯片的defconfig、设备树文件、U-Boot源码中关于复旦微特定外设的驱动。
最初的调试阶段,我建议先用tftp从网络加载内核和rootfs,这样每次修改内核不需要反复烧写存储介质。U-Boot环境变量设置大致如下:
setenv ipaddr 192.168.1.100 setenv serverip 192.168.1.10 setenv bootcmd 'tftp 0x20000000 zImage; tftp 0x21000000 fmsdk.dtb; bootz 0x20000000 - 0x21000000' saveenv如果U-Boot阶段串口没有打印,优先检查串口引脚配置和时钟。FMQL45T900的UART外设引脚分配和Zynq不完全一样,设备树里的uart节点地址也要跟着改,否则内核起来后ttyAMA0不出数。
内核阶段,我建议在设备树里先禁用不用的外设,比如SATA、USB、DisplayPort,减少调试干扰。把网络、串口、SD先跑通,再逐步放开。
4. PS与PL协同的关键细节
4.1 AXI互联与地址映射
PS与PL的数据交互主要靠AXI总线。FMQL45T900内部有多个AXI接口,分别提供不同的带宽和延迟特性。PL端自定义外设通常通过AXI GP接口映射到PS地址空间。
迁移过程里,原来Zynq工程中PL外设的基地址很可能变了,因为PS外设地址空间和Zynq不同。修改时注意设备树里的reg属性、驱动里的ioremap地址、以及U-Boot或裸机程序里访问PL外设的地址,三处要一致。
我实际遇到的一个问题:原驱动里硬编码了0x43C00000这个Zynq默认的AXI外设地址,换到FMQL45T900后该地址被其它外设占用,驱动一访问就直接oops。改成设备树动态获取资源后解决。这类硬编码地址问题在代码审查的时候要多留个心眼。
4.2 复位与时钟设计:先解决亚稳态
PL端的复位信号问题,可以说是FPGA迁移后最容易爆发的一类毛病。很多同学在Xilinx平台上习惯直接使用外部按键复位信号驱动内部逻辑的复位网络,迁移到FMQL45T900后,由于时钟和复位的相位关系改变,复位释放时刻不稳定,就会触发亚稳态。
标准的做法是“异步复位、同步释放”。简单说,就是把外部异步复位经过两级触发器同步后再作为系统复位使用。下面是一个经典的处理代码:
reg sync_rst_n_1, sync_rst_n_2; always @(posedge clk or negedge rst_n) begin if (!rst_n) sync_rst_n_1 <= 1'b0; else sync_rst_n_1 <= 1'b1; end always @(posedge clk or negedge rst_n) begin if (!rst_n) sync_rst_n_2 <= 1'b0; else sync_rst_n_2 <= sync_rst_n_1; end assign sys_rst_n = sync_rst_n_2;复位信号这一条,我在文章里把它单独拎出来,是因为它太容易出问题又太难排查。重启一百次可能挂一次,用示波器又抓不到,最后定位到复位释放时间和时钟沿太近,操作系统起来后在启动早期随机死机。
时钟部分,FMQL45T900上PL端的时钟输入、MMCM/PLL的使用方式与Xilinx 7系列的同架构逻辑基本一致。FPGA内部的时钟约束文件(XDC/SDC)需要根据新的器件名重新做时序约束,原来的时钟周期约束可以沿用,但I/O延迟和管脚位置必须重新填写。
4.3 常见外设踩坑记录:QSPI、GEM、UART
QSPI Flash启动是个高频场景。FMQL45T900的QSPI控制器和Zynq QSPI控制器在寄存器级有较大差异,不要指望Vivado自动生成的QSPI驱动在复旦微平台上直接编译通过。要确认QSPI Flash的读ID、写状态寄存器、擦除、编程指令是否被当前BSP支持。
另一个容易踩坑的地方是Flash写保护。有些Flash默认上电后状态寄存器里写保护位是打开的,直接烧写会报擦除失败或写失败。我们一般用烧写工具先发指令关闭写保护,再烧写BOOT镜像。
GEM以太网部分,FMQL45T900的PS端集成的以太网控制器和外部PHY芯片之间的接口模式可以是RGMII或GMII,需要和设备树里的phy-mode保持一致。我们项目里出现过U-Boot下ping不通、但Linux下网络正常的情况,排查下来发现是U-Boot里PHY驱动没有正确读取芯片地址,改成强制指定PHY地址后解决。
UART打印乱码也是常见问题。串口波特率不对、基准时钟配置错误都会导致乱码。在设备树里检查clocks属性是否指向了正确的时钟源,再量一下板卡上的UART TX波形,确认波特率。一般115200和921600这两种波特率下,波形规格能看出问题。
5. 图像处理与高速接口在FMQL45T900上的落地
5.1 MIPI/ISP图像采集链路搭建
图像采集是FMQL45T900非常典型的一个应用场景,毕竟国产化替代项目中,做红外、可见光、多光谱成像的板卡几乎离不开MIPI和ISP。
MIPI RX的接收链路,原先是Xilinx的MIPI CSI-2 IP核,内部包含D-PHY物理层和协议层解析。迁移到FMQL45T900后,如果IP库中没有对应核,就要用Cmos Sensor输出的RAW数据直接进PL,由PL端逻辑完成D-PHY差分接收、字节对齐、Lane合并、数据包解析,得到RAW图像数据。
ISP处理链路里,去马赛克(Demosaic)是核心模块。Sensor输出的是Bayer格式的RAW图,每个像素只有一个颜色通道,要去马赛克插值成RGB三通道。实现去马赛克有多种算法,最基础的双线性插值,后续可以做边缘自适应、甚至利用卷积神经网络。在FPGA里做这部分处理,复杂度高的是资源与实时性的权衡。
关于FPGA图像处理里的定点数,这里多说一句。很多算法在ARM上用浮点算一遍得到结果,迁移到PL里就习惯性地想用浮点IP,资源开销立刻就上去了。正确的做法是先做定点化分析,确定整数位宽和小数位宽,用定点运算替代浮点运算。和ARM上用IQmath做定点数学运算的思路类似,只是FPGA里位宽可调,灵活度和优化空间更大。
5.2 LVDS、信号发生器与外围驱动电路
除了MIPI,LVDS收发也是图像传输和显示接口里的常客。FPGA的LVDS接收,需要关注的是差分信号的电平标准、串行化因子、以及时钟恢复方式。LVDS接进来后,用IDELAY和ISERDES这类原语做位对齐和字对齐,迁移到复旦微平台时要注意原语的命名和参数可能不同,从7系列同构逻辑来看,多数原语用法可以对照Xilinx的UG471等文档来适配。
有些项目里还会用FPGA做信号发生器,通过DDS原理生成正弦波、方波、扫频信号等。DDS的实现核心是相位累加器和查找表,纯逻辑代码,在FMQL45T900上几乎不用改动就能综合。这个模块比较适合作为迁移后PL设计的“冒烟测试”,综合布线通过、上板后能正常输出波形,说明PL端的时钟和基本逻辑已经跑通。
外围驱动方面,如果PL的IO要驱动继电器、风扇这类大电流器件,中间需要达林顿管、MOS管或专用驱动芯片。这里要注意FPGA IO的电平标准、最大灌电流/拉电流参数,以及上电时序。FMQL45T900的IO上电时序和Xilinx 7系列类似,如果FPGA核心电源和IO电源在板卡上不是同时上电,PL端的配置引脚状态会不确定,有可能导致外部器件在启动瞬间误动作。设计时可以在达林顿管基极加下拉电阻,保证FPGA未配置前输出被拉到确定电平。
6. 迁移适配中的高频问题速查表
下面这组问题,基本覆盖了我这次迁移过程中遇到的高频问题,按“现象-原因-排查方向”整理成表,工程现场可以直接对照参考。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| FSBL阶段PC指针跑飞 | DDR参数不匹配、PLL未正确初始化 | 检查DDR颗粒参数、FSBL中的DDR头文件、时钟配置 |
| U-Boot无串口打印 | 串口管脚复用、时钟源错误、启动介质选择不对 | 核对设备树引脚配置、检查UART基准时钟频率 |
| Linux启动随机死机 | PL复位亚稳态、电源噪声、DDR时序裕量不足 | 先做异步复位同步释放,再检查电源纹波 |
| QSPI烧写失败/写保护报错 | Flash状态寄存器写保护位为1 | 用烧录器发指令解除保护,确认指令时序 |
| 网络ping通但UDP丢包 | GEM驱动中断未正确注册、MAC地址冲突 | 检查设备树中断配置,确认PHY芯片地址 |
| 图像采集花屏/偏色 | MIPI lane对齐失败、ISP Bayer通道顺序错误 | 抓取RAW数据检查lane顺序,确认Bayer排列方式 |
| 温度异常风扇不转 | PWM控制极性错误、IO未配置 | 检查风扇驱动电路电平,改用推挽输出 |
| 交叉编译应用链接报GLIBC版本错 | sysroot和板卡rootfs libc版本不一致 | 统一两边工具链和运行库版本 |
除了表格里的问题,再补充一点关于低功耗调试的经验。搜索里也有人提到“ACPI sleep state suspend disabled”“suspend to arm”这类关键词,在把Linux配置成可挂起/恢复模式时,需要注意板卡在suspend状态下的DDR自刷新、时钟停振等参数是否被正确配置。国产化板卡由于硬件上可能没有完全复刻参考设计,suspend/resume功能经常会出现恢复后网口不通、系统时间漂移等问题。如果不是产品硬性要求,建议先关闭suspend能力,以减少状态机复杂度。
下载器部分,我们用的是常规的JTAG调试器,例如常见的USBN-2A这类CPLD/FPGA下载器。使用这类下载器时要注意驱动版本和软件版本匹配,在FMSDK里如果识别不到芯片IDCODE,大概率是驱动没装好或下载器固件版本太旧。换用官方推荐的新版驱动后,基本能解决。
写在最后
做完整个迁移项目之后,我最大的体会是:国产化替换不是“换个芯片重新编译”,而是一个系统工程。工具链的习惯差异、启动流程的格式差异、外设寄存器的地址差异、PL与PS交互时的时序细节,每一处都可能在移植的半路冒出来咬你一口。就把这篇当作一份踩坑地图吧,能帮你把方向性的问题先绕开,剩下的细节文档,多翻官方手册,多用JTAG实测,比什么都强。
如果你正准备把Zynq项目迁到FMQL45T900,我个人的建议是先跑通最小系统:FSBL能起U-Boot,U-Boot能引导Linux,Linux能稳定跑一天不重启,再往上叠加图像、网络、外设这些业务功能。地基打牢了,上层再复杂也不慌。