☰
FMQL45T900开发板环境搭建全记录:ARM+FPGA从零到Linux与Qt
2026/9/28 13:50:49 网站建设 项目流程

一块FMQL45T900开发板,加上一套能跑通的ARM+FPGA开发环境,我从拿到板子到点起第一颗LED,用了整整两天,再到Linux系统启动、Qt界面亮起来,又磨了一周多。这中间的坑比我预想的多得多,很多坑不是官方文档写得不够清楚,而是国产化SoC开发环境本身就处在“资料靠摸索、报错靠百度、解决靠经验”的阶段。今天这篇博文,我不写官方手册的复读,只讲自己从零搭建复旦微FMQL45T900开发环境的完整过程和踩坑记录,希望能帮你把最痛苦的“环境磨合期”从一周缩短到两三天。

1. FMQL45T900平台梳理:为什么ARM+FPGA的开发环境这么难搭

1.1 先认识FMQL45T900这块国产异构SoC

FMQL45T900是复旦微电子推出的一款异构SoC,内部同时集成了ARM处理器和FPGA可编程逻辑,整体架构对标国际主流的Zynq-7000系列。其中ARM端通常称为PS(Processing System),负责跑操作系统、应用程序和网络协议栈;FPGA端称为PL(Programmable Logic),负责做并行信号处理、高速接口逻辑、图像采集和自定义外设。

这种单芯片异构方案最大的优势是ARM和FPGA之间通过片内高速总线通信,不再是两块芯片通过外部接口慢慢倒数据,带宽和延迟都有明显改善。它适合的场景集中在工业控制、机器视觉、边缘计算、仪器仪表等领域,本质上是用ARM生态解决复杂逻辑控制,用FPGA解决实时性和并行性要求高的任务。

开发这套平台,和以前单纯玩STM32或者单纯写FPGA完全是两个概念。STM32的痛点在于外设多、寄存器细,但开发环境相对固定;纯FPGA的痛点在于时序约束和逻辑设计,工具链单一;而FMQL45T900把两个痛点叠在了一起,你既要会Linux内核的交叉编译,又要会RTL逻辑设计,还得知道ARM怎么通过总线操作FPGA里的IP核。稍有不慎,环境问题就能消耗掉一整天。

1.2 开发环境复杂在什么地方

先说结论:难,不是难在某一个工具装不上,而是难在多个工具链之间的配合。

第一层是硬件描述工具链。FPGA端需要专用EDA工具来完成代码综合、布局布线、生成比特流文件,版本不同,器件支持库就不同,很多时候还得单独安装板级支持包。有些国产开发板商提供的工具版本也和国际版有差异,照搬网上的Vivado教程很容易踩版本坑。

第二层是ARM Linux交叉编译工具链。内核、U-Boot、应用程序都要用一套交叉编译器,编译器版本和内核源码版本如果不匹配,编译出来的系统可能启动到一半就崩溃。

第三层是文件系统与启动流程。FMQL45T900的启动牵扯到启动镜像、U-Boot环境变量、设备树、根文件系统,任何一个环节配置错误,板子就挂在串口无输出或者内核panic的阶段。

这三层问题叠加在一起,最难排错的是你不知道问题出在哪一层。我见过不少朋友在板子上烧录了一个bit文件,然后又改了U-Boot参数,结果启动失败,第一反应是检查Linux内核,折腾半天发现是FPGA侧的配置覆盖了启动镜像的某个分区,这种跨层联调问题,恰恰是异构SoC开发环境最典型也最折磨人的地方。

1.3 以终为始:列清楚一套完整环境需要哪些组件

在动手安装工具之前,我建议你先列一张环境清单,明确我们要搭的是“主机开发环境 + ARM Linux运行环境 + FPGA开发环境”三件套。按我自己跑通的这套配置,核心组件可以整理成一张简表。

分层组件说明
主机侧Ubuntu 18.04/20.04 64位系统开发宿主,建议至少100GB剩余磁盘
FPGA工具Vivado类EDA工具及对应器件支持用于PL侧综合、布线、生成bit文件
交叉编译器arm-linux-gnueabihf-gcc(GCC 7.5或更高)编译内核、U-Boot、Qt应用
启动组件BOOT.BIN、uImage、设备树dtb、根文件系统四件套缺一不可
运行环境Qt 5.5.10 ARM版、tslib库用于开发板上的GUI显示
调试接口USB转串口、JTAG调试器、网线串口看日志,JTAG下载调试,NFS挂载提高开发效率

用大白话讲,这套环境的完整落地过程就是:先在主机侧用交叉编译器搞出一套能在ARM上跑的系统镜像,再通过PS端加载FPGA配置,最后应用层程序通过总线访问FPGA逻辑。后面四个章节就沿着这个顺序,从硬件检查、FPGA工具链、Linux系统、软硬件协同四个维度逐步展开。

2. 从零搭建第一步:硬件平台与FPGA开发工具链

2.1 动手之前,先把硬件连接和板级信息核清楚

这一节是最容易被忽略但又最影响效率的部分。拿到FMQL45T900开发板,不要急着上电,先把硬件连接理清楚。

供电方面,确认开发板输入电源的电压和电流规格,通常这类板卡用12V直流供电,电流建议不要小于2A,供电不足最典型的症状就是FPGA加载到一半失败或者ARM启动时频繁重启。串口方面,FMQL45T900开发板一般会引出UART调试串口,需要配一根USB转TTL串口线,注意TX和RX要交叉连接,板卡上的丝印通常会标注,接反了开机没日志,很多人第一反应是板子坏了,实际就是交叉线接错了。

JTAG方面,确认调试器与板上JTAG排针的接线顺序,不同开发板引脚定义可能不同,以板卡原理图为准。网线方面,如果计划使用NFS挂载方式加快开发,要保证开发板和主机在同一个网段,网口驱动在系统启动后需要单独确认是否已编译进内核。

第一次上电之前,建议用万用表量一下关键电源轨的对地阻抗,确认没有短路再上电。这一步能避免后续一堆莫名其妙的故障,尤其是FPGA的IO电源和内核电源接反时,损坏往往是瞬间的。

2.2 安装FPGA开发工具链:版本与器件库是头号大坑

FMQL45T900的PL侧开发,官方支持的是Vivado类EDA工具,但由于器件属于国产化平台,通常需要额外安装对应的器件描述文件,或者直接使用开发板厂商提供的定制版本。

我的建议是:第一选择,直接用开发板厂商随附的安装包,别急着去官网下最新版。第二选择,如果厂商提供了器件支持插件,按照官方README要求安装到指定目录。第三选择,手里的安装包和网上教程版本不一致时,记住一个原则——版本号差异造成的综合结果不一致,远比你想的常见,如果后续编译报错,先怀疑版本,再去怀疑代码。

安装过程中有几个需要注意的坑。坑一是安装路径不要包含中文和空格,有些EDA工具对路径非常敏感,路径一长就报奇怪的错误。坑二是License配置,尤其是浮动License,需要正确设置环境变量,建议启动工具前先用lmutil查看License状态,确认能检出对应功能。坑三是磁盘空间,完整安装通常需要几十GB,别装在系统盘C盘或家目录下,建议单独挂载一块大容量数据盘,后续编译工程要缓存大量中间文件。

装完工具链之后,第一步不是写代码,而是先新建一个空工程,确认能在工程里搜到FMQL45T900对应的芯片型号。如果型号列表中找不到,说明器件库没装好,这时候回头检查安装步骤比硬着头皮编译源码有效得多。

2.3 第一个PL工程:点亮一颗LED并下载bit文件

工具链就绪后,我建议用一个最简单的外设——LED,来跑通整个PL流程。为什么要选LED?因为它是唯一一个不依赖任何复杂外设、不依赖Linux驱动、不看串口协议就能直接验证FPGA逻辑是否生效的硬件。

新建工程时,器件型号选择FMQL45T900对应的封装。创建Verilog顶层文件,写一个简单的时钟分频逻辑,让LED按固定频率闪烁。示例代码如下:

module led_blinky( input wire clk, // 板载时钟,通常为50MHz或100MHz input wire rst_n, // 复位信号,低有效 output reg led // LED输出 ); reg [31:0] counter; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin counter <= 32'd0; led <= 1'b0; end else if (counter >= 32'd24_999_999) begin counter <= 32'd0; led <= ~led; end else begin counter <= counter + 1'b1; end end endmodule

这里假设板载时钟是50MHz,计数器计到24,999,999正好产生一秒翻转,LED每秒闪一次。实际频率需要根据开发板原理图确认,不同板子可能用100MHz晶振,计数上限就要改成49,999,999。这类参数错误不会导致编译失败,但现象上LED完全不闪或者闪得特别快,容易误导排查方向。

约束文件(XDC)里需要把时钟、复位、LED的物理引脚做约束。给一个模板:

create_clock -period 20.000 -name sys_clk [get_ports clk] set_property PACKAGE_PIN U18 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property PACKAGE_PIN T17 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] set_property PACKAGE_PIN M14 [get_ports led] set_property IOSTANDARD LVCMOS33 [get_ports led]

注意具体引脚编号必须从开发板原理图中查,我这边给的是示意值,不能直接照抄。约束文件写错最典型的表现是bit文件能生成,下载之后没有任何反应,回读检查IO也未必报错,但信号根本没到想要的引脚上。

综合、实现、生成bit文件,这几步按图形界面默认流程走就可以。生成完成后,通过JTAG把bit文件下载到开发板。FMQL45T900支持多种加载方式,JTAG直接下载只对当前上电状态生效,板子断电后FPGA配置就丢了。如果要固化,需要把bit文件合并进Boot Image,再烧写到SPI Flash或SD卡,这部分在第四章展开。

第一次下载成功后,你会看到LED按照预期频率闪烁,这说明PL侧的基础链路已经通了。下一步就是返回ARM侧,把Linux系统跑起来。

3. ARM Linux侧环境:交叉编译、Qt与NFS挂载

3.1 交叉编译工具链:选型与避坑

ARM端的Linux系统,需要在一台x86主机上编译出ARM架构的二进制文件,这个流程叫交叉编译。工具链的选型直接影响后面所有环节的稳定性。

很多STM32出身的朋友会习惯性地找ARM Compiler 5.06、Keil自带的AC5编译器,这在裸机开发场景没问题,但到了Linux内核和Qt应用层面就不适用了。Linux内核和标准动态库需要的是Linux GCC工具链,最常见的是arm-linux-gnueabihf-系列。我使用的是GCC 7.5版本的Linaro工具链,命名规则是arm-linux-gnueabihf-,其中“gnueabihf”表示使用glibc和硬件浮点,FMQL45T900这颗ARM核支持硬件浮点运算,必须选带hf的版本,否则未来跑图像处理、浮点密集任务时性能损失很大。

安装流程不复杂:

# 解压工具链到/opt目录 sudo tar -xjf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.bz2 -C /opt # 配置环境变量,建议写入 ~/.bashrc export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- # 验证工具链是否可用 arm-linux-gnueabihf-gcc -v

写完环境变量后重新打开终端再验证一次。我遇到过不少情况是环境变量临时生效没问题,重启终端就失效,原因就是没写进.bashrc,只在当前终端export了一下。验证时看到“gcc version 7.5.0”字样就说明工具链正常。

一个特别容易被坑到的点是:交叉编译出的程序依赖于目标板上的动态库存根文件系统,如果根文件系统用的是精简BusyBox,而没有拷贝对应的动态库,编译时明明成功了,程序放到板子上却报“No such file or directory”,这往往不是文件不存在,而是动态库加载路径不对。所以第一课就是搞清楚两个概念——宿主机的编译环境和目标板的运行环境,它们是两套东西。

3.2 U-Boot与内核编译要点:从源码到启动四件套

启动四件套指的是BOOT.BIN、uImage或zImage、设备树dtb文件、根文件系统rootfs。这四样东西任何一个对不上,板子都进不了Linux。

先编译U-Boot。不同开发板厂商通常会提供一份U-Boot源码和默认配置,比如使用NDA厂商的定制版,先在源码目录下执行make xxx_defconfig加载默认配置,再执行交叉编译:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- <厂商默认配置>_defconfig make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

编译完成后会生成u-boot.elf,用厂商提供的脚本把它和FSBL(First Stage Boot Loader)以及FPGA bit文件打包成BOOT.BIN。打包过程的注意事项:bit文件不是独立加载的,而是集成进BOOT.BIN由启动流程统一加载,所以要先准备好PL侧的bit文件,再生成BOOT.BIN,顺序反了就得反复重打。

再编译内核。从厂商提供的Linux源码开始,配置内核基本选项:

make ARCH=arm <厂商默认配置> make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- uImage LOADADDR=0x8000 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs

编译完成后会生成uImage和对应的设备树dtb文件。设备树文件是描述板级硬件信息的文本编译产物,GPIO对应的引脚号、串口地址、网口控制器都定义在里面。如果板子上的外设没有正确写入设备树,Linux启动后内核日志里找不到对应设备,这就是最常见的“硬件明明在那里但系统认不到”的原因。

最后是根文件系统。第一种办法是使用厂商提供的预编译rootfs,启动最快,适合先跑通流程。第二种是自己用Buildroot或Debian的rootfs工具构建,灵活但费时。大多数情况下我建议先用厂商预编译的rootfs跑通,后续熟练了再定制。

3.3 开发板挂载Ubuntu主机目录:NFS的配置与排错

开发Linux应用时,如果每次修改代码都要重新拷贝文件到SD卡、重新插卡重启,效率太低。常见的做法是在主机Ubuntu上开启NFS服务,把编译出的应用程序放到一个共享目录,开发板通过网络直接挂载这个目录。

主机端配置流程:

# 安装NFS服务 sudo apt update sudo apt install nfs-kernel-server # 编辑导出配置,把/home/share/work目录共享出去 sudo vim /etc/exports # 添加一行: # /home/share/work *(rw,sync,no_subtree_check,no_root_squash) # 重启NFS服务 sudo systemctl restart nfs-kernel-server

开发板端挂载命令:

mount -t nfs -o nolock 192.168.1.100:/home/share/work /mnt/work

这里有几处值得解释的选项。nolock表示不使用NFS锁,目的是避免开发板上的文件系统服务不完整时锁定报错。rw表示可读写。no_root_squash表示允许root用户直接操作共享目录,如果缺了这个选项,开发板上用root身份写入的文件在主机端会变成nobody用户,权限错乱非常难受。

最常见的挂载失败原因有三个。第一,开发板和主机不在同一网段,互相ping不通,这个最基础但最容易忽视。第二,主机防火墙拦截了NFS端口,开发板挂载时卡在RPC超时,建议临时关闭ufw或者放行nfs相关端口。第三,/etc/exports写错了路径或者选项格式,服务端启动时报错。

我用NFS踩过的最深的一个坑是:挂载成功之后,在开发板上运行一个Qt程序,程序反复退出,Kernel日志里发现SFTP锁文件被拒绝访问,排查了很久,最后发现是把no_root_squash漏了,导致进程以nobody身份运行,没有权限写临时目录。所以那种“程序拿过去就崩,但根本不知道哪里崩了”的诡异问题,先怀疑权限,再去怀疑代码。

3.4 在开发板上跑起Qt 5.5.10:交叉编译与运行

很多行业应用需要在开发板上跑图形界面,这就要把Qt库交叉编译成ARM版本。FMQL45T900常用的嵌入式Qt版本是5.5.10,搭配tslib触摸屏库。

交叉编译Qt的步骤比较繁琐,但核心逻辑很简单:用arm-linux-gnueabihf-gcc去编译Qt源码中与硬件无关的部分,让生成的库文件运行在ARM上。关键配置命令示例:

./configure -prefix /opt/qt-5.5.10-arm \ -opensource \ -confirm-license \ -xplatform linux-arm-gnueabihf-g++ \ -no-opengl \ -no-gtk \ -nomake examples \ -qt-zlib \ -qt-libpng \ -qt-libjpeg \ -tslib \ -I/opt/tslib-arm/include \ -L/opt/tslib-arm/lib

配置时要格外注意几个参数。no-opengl表示板端不启用OpenGL硬件加速,很多国产板卡不带GPU或者驱动不完善,这个选项能避免Link阶段报出一堆GL库相关的错误。tslib选项只有在事先交叉编译了tslib并安装到指定路径时才需要加,如果没有触摸屏可以不加。xplatform指定的是Qt源码里预定义的交叉平台文件,有些定制工具链需要自己创建qmake.conf,比如指定编译器路径为/opt/gcc-linaro.../arm-linux-gnueabihf-g++。

配置完成后,依次执行:

make -j8 make install

编译耗时会比较长,建议放在晚上跑。装完之后,在主机上编写一个最简单的Qt Widgets程序,用Qt自带的qmake工具生成Makefile并交叉编译:

/opt/qt-5.5.10-arm/bin/qmake hello.pro make

把生成的hello可执行文件通过NFS或SD卡拷贝到开发板,运行前先导出环境变量:

export QTDIR=/opt/qt-5.5.10-arm export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORM=linuxfb ./hello

QT_QPA_PLATFORM设置为linuxfb,表示直接通过Linux的帧缓冲设备显示界面,不依赖窗口管理器,这是嵌入式板卡上最简单最通用的显示方案。如果开发板带触摸屏,还需要配置TSLIB_TSDEVICE和QWS_MOUSE_PROTO等变量,没有触摸屏就先别折腾这些。

Qt跑通之后,你的开发环境已经从“能启动Linux”进化到“能运行GUI程序”,很多基于Qt的工控上位机界面,在这一步之后就能真正做起来了。

4. 软硬件协同:bit文件加载、AXI通信与FPGA设计两坑

4.1 bit文件加载的三种方式:请先搞懂各自的适用场景

FPGA的bit文件,本质是一个描述FPGA内部逻辑配置的数据流。FMQL45T900支持多种加载方式,每种方式的适用场景完全不同。

第一种是JTAG直接下载,也就是在Vivado的Hardware Manager界面里选中芯片,把bit文件直接download进去。这种方式只修改当前FPGA的配置,不写入任何非易失存储,断电即丢失。它最适合调试PL逻辑,比如验证RTL代码行为是否正确,改完代码重新下载,不用编译整个启动镜像。

第二种是烧写到QSPI Flash,bit文件先被转换成可下载的格式,通过JTAG烧入SPI Flash,上电后由U-Boot的FSBL负责从Flash取出并配置FPGA。这种方式适合“系统交付”场景,板卡一上电就自动加载FPGA逻辑,不需要连接JTAG。

第三种是集成进BOOT.BIN,从SD卡启动时由启动流程一并加载。这种方式适合开发和验证阶段,因为SD卡上的启动镜像可以随时用读卡器更新,不用反复插拔JTAG线。

三种方式的优先级建议:调试PL逻辑用JTAG;开发Linux应用需要板端运行FPGA功能时,用SD卡启动的BOOT.BIN;产品化固化时再考虑QSPI。特别提醒,每次切换加载方式后,U-Boot的环境变量可能需要同步调整,比如启动设备是sd还是qspi,这个参数写错会导致“明明烧进去了但启动失败”的尴尬。

4.2 通过AXI GPIO实现ARM读取FPGA状态

FMQL45T900的ARM和FPGA之间,最常用的通信通道是AXI总线。最简单也最好理解的方式是在FPGA侧挂一个AXI GPIO IP核,ARM端把GPIO映射成寄存器的读写地址,这样ARM想监控FPGA内部的任何状态,都可以通过GPIO引脚点对点连接。

在PL侧,用Vivado的Block Design添加ARM核和AXI GPIO,具体流程是:创建Block Design,添加ZYNQ Processing System并配置DDR和UART;添加AXI GPIO IP,配置为输入输出双向通道;用自动连接功能把AXI GPIO接到PS的GPIO Master接口上,然后生成比特流,导出硬件描述文件(hdf)。

导出hdf文件后,编写ARM侧程序需要用到设备树或直接在Linux下操作内存映射地址。最简单的实验是:把AXI GPIO的某个输出引脚接到开发板LED上,然后在Linux命令行通过devmem工具向寄存器写值:

# 假设AXI GPIO的基地址为0x40000000,数据寄存器偏移为0 devmem 0x40000000 32 0x00000001

如果LED亮了,说明ARM到FPGA的数据通路是通的。反过来,如果FPGA内部有某路传感器信号,可以把它接到AXI GPIO的输入引脚,ARM端读寄存器值就能拿到状态。

这里有一个非常容易踩的坑:AXI GPIO的基地址不是随意的,它必须在设备树里正确描述,并且与FPGA综合时分配的地址一致。如果地址对不上,devmem读写不会报错,但读出来的永远是0或者写进去没反应,这个问题耗费了我一个晚上才定位到。排查方法是在内核启动日志里搜“gpio”相关节点,确认注册成功的设备地址和FPGA工程里的地址一致。

4.3 复位信号亚稳态:新手中最容易被忽略的时序问题

FPGA逻辑设计里有个经典问题叫复位亚稳态。简单说,当复位信号释放的时间点刚好处于时钟沿附近时,触发器采到的信号既可能是0也可能是1,输出不稳定,可能传播到后续所有逻辑,导致系统随机性死机。

很多入门教程只教你怎么写复位,没教你怎么处理复位释放。正确的做法是使用“异步复位、同步释放”的复位同步器结构。示例代码如下:

reg rst_n_r1, rst_n_r2; always @(posedge clk or negedge rstn_async) begin if (!rstn_async) begin rst_n_r1 <= 1'b0; rst_n_r2 <= 1'b0; end else begin rst_n_r1 <= 1'b1; rst_n_r2 <= rst_n_r1; end end assign rst_n_sync = rst_n_r2;

这段代码的作用是:外部异步复位信号先经过两级触发器打拍,再输出一个与时钟同步的复位释放信号。为什么要两级?因为第一级仍然可能采到亚稳态,但亚稳态会在一个时钟周期内稳定下来,第二级再采样时已经是在稳定电平基础上采样,大大降低了亚稳态传递到后续逻辑的概率。

还有一个常见问题是复位信号的同步必须和同一时钟域匹配。如果你逻辑里用了多个时钟域,每个时钟域都要单独做复位同步,不能拿一个时钟域的同步复位直接驱动另一个时钟域的逻辑。跨时钟域的复位处理,属于同步设计里的深水区,至少要知道它的存在,并尽量在模块接口处用同步器隔离。

4.4 布局和布线的区别:为什么你的工程老是时序不收敛

FPGA工程编译或实现时,工具会先后执行综合、布局(Place)、布线(Route)三个阶段。布局是决定每个逻辑单元(LUT、FF、DSP等)放在芯片物理位置的哪个具体坐标,布线是决定这些逻辑单元之间的信号线走哪些物理连线资源。

为什么这两个阶段要分开说?因为它们的执行逻辑和难度完全不同。布局解决的是“把电路安排在芯片上”的全局优化问题,布线解决的是“让信号都连得通、走得快”的局部实现问题。一份RTL代码综合后的资源占用率看着只有50%,但布线报告却出现大量时序违例,原因往往不是资源不够,而是逻辑单元之间的相对位置太分散,信号线绕了远路。

在FMQL45T900这类中大规模FPGA上,最容易遇到时序问题的场景是跨时钟域信号没做同步,或者组合逻辑层级太长。解决组合逻辑层级太长的方法一般是插入流水寄存器。比如一个运算链路中包含多级乘法和加法,如果单周期内完成时间不够,就拆成两三个时钟周期,每级插入寄存器,这种做法的代价是延迟增加,但最高运行频率能明显提升。

布局和布线阶段还有一个实践建议:尽量合理添加物理区域约束(Pblock),把相关的逻辑模块约束在相邻区域内,减少远距离布线延迟。但首次开发不建议动手加Pblock,先让工具自动布局布线,等出现时序违例后再分析高扇出路径的位置关系。一上来就手动物理约束,容易把自己绕进更深的坑。

5. 疑难问题速查表与排查心法

5.1 高频问题速查:从症状到根因再到解决办法

搭建这套环境过程中,我踩过的问题罗列在下面这张表里,每条都是实际经历过并验证过解决思路的,可以直接当排查手册用。

问题现象可能的根因解决思路
串口无任何输出串口线TX/RX接反,或波特率与U-Boot设置不一致确认接线和波特率,常见115200/8/N/1
JTAG连不上芯片调试器驱动未安装,或板卡未上电,或JTAG排针接触不良重新安装驱动,确认板卡供电,检查排针焊接
bit文件下载后无现象引脚约束错误,或PLL没有锁定,或复位一直生效核对原理图引脚,查看IO报告,确认复位电平极性
内核启动卡在Starting kernel设备树与内核不匹配,或bootargs参数错误重新编译dtbs,确认console=ttyPS0参数
Qt程序运行段错误动态库版本不匹配,或QTDIR/LD_LIBRARY_PATH未设置用ldd检查库依赖,确认根文件系统拷入了对应Qt库
NFS挂载超时网段不通,防火墙拦截,或exports配置错误先ping通主机,再关闭防火墙或加白名单
FPGA时序不收敛组合逻辑层级过长,或时钟约束缺失增加输入输出延迟约束,插入流水寄存器
devmem写寄存器无反应设备树地址与AXI地址不一致核对设备树reg属性与FPGA工程地址
系统上电反复重启供电不足,或DDR初始化参数错误更换更大功率电源,检查DDR配置代码
编译内核时报环境变量错ARCH/CROSS_COMPILE未设置检查Makefile顶层变量,确认来源于.bashrc

5.2 排查问题的通用思路:日志优先,逐层隔离

针对异芯片环境,排查问题时我推荐一个“日志优先、逐层隔离”的思路。

第一步永远是看日志。开发板的串口输出是启动过程的“黑匣子记录仪”,U-Boot阶段的每一行信息、内核启动阶段的每个Initcall都是线索。很多人遇到系统起不来,第一反应是盲改编译参数,这是效率最低的做法。正确的顺序是先去看串口打印停在哪一行,停在哪个阶段,再根据阶段定位问题。

第二步是逐层隔离。判断问题在PL侧还是PS侧,最简单的方法是分别冻结一侧。例如Linux起不来,先临时移除FPGA bit文件,看纯ARM端的启动是否正常;如果纯ARM端正常,说明问题出在PL侧的配置或AXI通信;如果纯ARM端也不正常,再回头查内核、设备树和启动参数。这种隔离法能把一个跨层大问题拆成若干个局部小问题。

第三步是复位到已知良好状态。环境搭建期间,如果改了一堆配置后系统全乱了,不要试图逐一找回,直接把之前验证过的SD卡镜像恢复,重新走增量调试流程。开发板不像软件系统可以随时回滚,但这更说明增量修改、逐步验证的重要性,每做完一个改动就应跑一次完整的启动验证,而不是一口气改所有内容再一起测。

5.3 独家避坑经验:版本锁定与文档记录

再分享两个可能对你有长期价值的经验。

第一个是版本锁定。复旦微FMQL45T900牵扯的软件组件很多:FPGA工具版本、交叉编译器版本、内核源码版本、U-Boot版本、Qt版本、设备树版本。这些组件存在版本依赖关系,比如用新版工具生成的FSBL未必兼容旧版U-Boot,旧版内核可能不认识新版工具生成的设备树。建议把整套环境的版本号记下来,甚至可以用文本文件保存一份环境清单。后续无论是重装系统还是传给同事,照着清单才能复现,而不是靠“记得当时装过什么”来拼凑。

第二个是保留原始交付资料。开发板厂商一般会提供参考设计、出厂镜像和原理图,这些资料在你开发过程中会反复用到。建议在主机上建立一个专门的开发目录,按硬件资料、出厂镜像、参考设计、自制工程四类归档,定期备份。自制工程中建议至少保留两个命名清晰的版本:一个是“原始能跑版”,一个是“当前开发版”,这样一旦开发版改坏了,还能迅速回退到能跑的版本重建。

6. 写在最后:板子还在跑,但环境已经懂了

这套环境跑通之后,我最深的体会是:国产化ARM+FPGA开发板的门槛,并不比国际主流平台高多少,区别主要在于资料密度和踩坑成本。国际大厂的开发板随便搜一下就有几百篇教程,而FMQL45T900这类平台更多要靠自己啃源码、看日志、做实验。但换个角度看,正是在这种“逼着你弄懂底层机制”的过程中,我对启动流程、设备树、AXI总线和交叉编译的理解,反而比之前‘照着教程抄’时扎实得多。

最后再分享一个小技巧:开发期间建议把串口日志默认保存到文件,别只在屏幕上滚动。很多问题发生的时候你未必注意到,但日志文件里已经留下了完整的线索,排查时翻一翻,往往能少走很多弯路。这套环境搭好只是起点,后面的功能开发还长,希望大家都能顺利绕开那些我踩过的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询