☰
FMQL45T900核心板实战:ARM+FPGA异构工控平台搭建与BSP配置指南
2026/9/28 1:56:54 网站建设 项目流程

最近公司接了个工业数据采集的活儿,要求平台关键器件用国产型号,我选来选去最终定了复旦微FMQL45T900核心板。板子到手后,从零开始把BSP配置、内核裁剪、设备树调整、信号处理逻辑全部跑通,前前后后踩了不少坑。这篇笔记不是产品介绍,完全是实战记录,适用于任何正在评估FMQL45T900或者在做国产化工控平台的朋友。先说说这板子解决什么问题:工控现场信号种类多、干扰大、实时性要求高,MCU往往算力不够,纯FPGA做复杂应用又太费劲,FMQL45T900这种ARM+FPGA异构方案刚好卡在中间——FPGA负责高速采集和信号预处理,ARM负责跑Linux做控制、通信和上位机交互,两边通过片内总线交换数据,整体可靠性和开发效率都能兼顾。

1. 项目整体设计与选型思路

1.1 FMQL45T900核心板到底是一块什么样的板子

FMQL45T900不是普通单片机开发板,它属于可编程SoC。芯片内部集成了ARM处理器子系统(PS)和FPGA可编程逻辑(PL),封装里既有硬核处理器,又有可重新配置的逻辑阵列。PS端有两个ARM Cortex-A9核心,主频能跑到900MHz上下,带DDR控制、中断控制器、DMA以及UART、SPI、CAN、USB、Ethernet等常用外设;PL端则对应型号里的“45T”,大致是几十万逻辑单元规模,还有不少DSP Slice和Block RAM,做并行信号处理绰绰有余。

我拿到的这块核心板,板上已经配好了一片DDR3、一片QSPI Flash、电源管理电路和晶振,用户只需要把电源和必要的启动配置接上就能跑起来。官方把这种形态叫做核心板,是因为它把BSP里最容易出问题的DDR初始化、电源时序、Flash分区这些事都提前收敛了,用户在外面做底板时,不用再面对几百个BGA引脚和复杂的DDR走线。对工控项目来说,这个特性非常友好,因为硬件工程师可以把精力集中在信号前端和外设接口上。

1.2 选型时对比过哪些方案

工控信号处理平台常见的路线有这么几条:MCU+ADC、DSP+FPGA、纯FPGA、ARM+FPGA SoC。我列个表给大家参考。

方案优点缺点适合场景
MCU+ADC开发简单,成本低高速多通道同步采集吃力,实时性不够低速监控、单通道采集
DSP+FPGA算法能力强,时序控制灵活器件多,板级设计复杂,联调周期长雷达、音频等高算力算法
纯FPGA并行性极强,延时可控跑协议栈、界面和复杂控制太痛苦接口转换、高速数据流处理
ARM+FPGA SoC软硬件兼顾,单芯片集成度高入门门槛比MCU高,BSP和Linux需要投入时间工控信号处理、机器视觉、运动控制

我最后选FMQL45T900,核心原因有三点:一是平台集成度高,一个芯片把ARM、FPGA、DDR控制器和大量外设都包了,底板上不用再串联一颗FPGA和一颗ARM处理器,可靠性好很多;二是硬实时任务和软实时任务能分开,高速采样、数字滤波、触发检测放在PL,参数配置、网络通信、数据上传放在PS,两边并行跑互不拖累;三是这颗芯片的资料和BSP已经比早期国产型号完善很多,还有一个看得见摸得着的社区生态,遇到问题不是完全抓瞎。

1.3 整体架构:软硬件怎么分工

设计整个平台之前,我先在纸上把数据流画了一遍,这个习惯帮了大忙。现场传感器信号经过隔离放大和抗混叠滤波后,进入ADC变成数字量;ADC输出的并行或串行数据送到PL,PL里做通道切换、数字滤波、触发判断、数据打包,再通过AXI总线写入DDR;PS端的Linux应用从DDR里取数据,完成算法分析、界面显示和网络上传。反过来,ARM也可以下发配置参数给PL,比如滤波器系数、采样率、触发阈值,这样整套系统就是双向可控的。

这种架构的好处是,PL不必关心应用层,ARM也不必逐字节地去抓高速ADC数据,两边通过DMA交换数据,CPU占用率可以压得很低。实测下来,8通道100kSPS的16位采集,PS端CPU占用率只剩下不到百分之十,剩下算力全部留给协议解析和业务逻辑,这在纯MCU方案里很难做到。

2. 核心板到系统:硬件平台搭建的关键细节

2.1 供电、时钟、复位与启动模式

FMQL45T900核心板虽然集成了大部分电源,但底板上仍然要提供输入电源和启动配置。我这边底板输入用的12V直流,板上DCDC转换成5V后再给核心板供电;核心板内部需要的0.95V内核、1.8V IO、1.5V DDR等电源都是核心板自己产生的,外部不用管。但是有一点必须注意,输入电源的纹波要控制在合理范围内,工控现场电源干扰大,输入端最好加共模电感和TVS管,否则DDR初始化偶尔会失败,现象就是Uboot起来后莫名其妙重启。

时钟方面,核心板PS端已经配置好了系统时钟,外部不需要额外提供。但如果你在PL里做了高速接口,比如千兆以太网、HDMI或者高速ADC,那最好在底板上预留一个独立的差分时钟源,不要直接靠内部PLL从低速时钟倍频上去,抖动会比较大。复位设计我推荐用专门的复位芯片,一个按键加一个电源监控,比单纯RC复位靠谱,尤其在低温环境下。

启动模式这一块要重点检查。FMQL45T900支持从QSPI Flash、SD卡和JTAG启动,具体是几个启动引脚的电平组合决定的。我习惯在底板上做一个三位拨码开关,把启动模式引出来,调试时拨到JTAG,固化后拨到QSPI Flash,这样就不用反复改电路。注意拨码开关状态必须在复位前确定,否则可能进入错误的启动流程。

2.2 信号前端与ADC接口设计

既然是信号处理平台,信号链路的设计质量直接决定整个系统的性能。这一路我选用了一款8通道同步采样ADC,分辨率为16位,最大采样率为200kSPS,接口是SPI模式,带8个独立的模拟输入通道。选同步采样是因为工控现场经常要对比多路相位关系,如果通道间有采样延时差,计算出来的相位信息就是错的。

ADC前端的调理电路我用了一个两级结构:第一级是输入保护和分压衰减,第二级是二阶低通滤波。比如现场信号是±10V,ADC输入范围是±5V,那前端就需要2:1的衰减;如果信号很弱,只有0到100mV,反而要加放大,运放选择低噪声精密运放。滤波器的截止频率要按采样定理设计,我当时把采样率定在100kSPS,信号最高频率按5kHz考虑,低通截止设在10kHz附近,这样既不会因为带外噪声混叠,也不会把有用信号滤掉。

ADC和FPGA之间的电气连接也要看仔细。我们选的这颗ADC输出是3.3V CMOS电平,直接接到PL的普通IO bank即可。但要注意给这个bank供电为3.3V,并且在PCB布局时让ADC尽量靠近核心板连接器,走线保持等长,避免高速采样时出现数据错位。如果用的是高速LVDS接口ADC,还要检查PL bank是否支持LVDS标准,通常需要接终端电阻。

2.3 通信接口与电平匹配

工控设备免不了和PLC、上位机、传感器通信,所以底板上要预留足够的通信接口。FMQL45T900的PS端资源比较丰富,我这边使用了以下几种接口:一路RS485走UART外接485收发器,两路CAN走PS端CAN控制器,一路RS232走调试串口,一路千兆以太网走PS端RGMII接口外接PHY芯片。大部分工控协议,比如Modbus RTU、Modbus TCP、CANopen,都能直接在这套硬件上实现。

接口设计中容易忽略的是电平转换和隔离。RS485和CAN都是差分信号,收发器本身就有电平转换作用,但供电要匹配:目前常用的收发器很多支持3.3V供电,可以直接和PS端IO连接。以太网PHY则要注意RGMII接口的电压和时序,FMQL45T900的PS端RGMII电平是1.8V还是3.3V,务必查清楚,不能想当然接3.3V,否则会烧IO或不稳定。我这边PHY供电是3.3V,但接口电平通过VCCIO配置到2.5V,中间加了电平转换电路才解决。

工控现场还经常要求隔离,RS485和CAN我都在收发器后级加了隔离模块,电源也要隔离,不然地环路会把现场干扰串进核心板。隔离后波形边沿会有一些变缓,但用在1Mbps以下的现场总线完全没问题,实测通信非常稳定。

3. BSP配置指南:从零构建Linux运行环境

3.1 BSP包组成和使用前的准备

很多第一次接触核心板的人看到“BSP”三个字母就头大,其实BSP就是板级支持包,是一整套让Linux在指定硬件上跑起来的软件集合。复旦微FMQL45T900的BSP包里通常包含:交叉编译工具链、U-Boot源码、Linux内核源码、设备树源码(DTS)、第一级引导程序FSBL源码、驱动程序、根文件系统构建脚本以及各种自动化编译脚本。它不是“一个驱动”或者“一个补丁”,而是从上电到应用跑起来的全链路支持。

拿到BSP后第一件事不是马上编译,而是看README和版本发布说明。不同批次的核心板,DDR容量、PHY型号、Flash大小可能不一样,BSP版本和板级配置必须匹配。我第一次就是用了旧BSP,导致U-Boot起来后DDR检测失败,白白折腾了一天。厂家通常会提供一个“出厂默认方案”,用这个方案能最大概率复现官方硬件环境。先把这个默认方案编译、烧录、跑起来,再在这个基础上去改自己的设备树和驱动,效率会高得多。

3.2 交叉编译工具链和SDK装好之后做什么

在PC端Linux环境下编译ARM平台程序,需要交叉编译工具链。FMQL45T900的PS是Cortex-A9,因此需要使用ARM 32位工具链,通常是arm-linux-gnueabihf。把官方提供的工具链解压到/opt目录后,要手动把它的bin目录加入PATH环境变量。我当时用export方式临时加入,每次开终端都要重新设,后来干脆写进.bashrc,省心很多。加入后执行arm-linux-gnueabihf-gcc -v,看到版本信息就说明工具链可用。

工具链就绪后,进入BSP目录,通常会有顶层Makefile或脚本,一次性完成U-Boot、内核、设备树的编译。如果你不熟悉,建议先执行make help,它会列出所有可用的目标。我以一个常见的BSP包为例,一条命令可以编译出fsbl、u-boot.bin、uImage、dtb等文件。这个过程第一次会比较慢,我在笔记本上编译了大约十几分钟,属于正常现象。

这里要说一个重要思路:BSP不是一次编译就能一劳永逸,随着你的硬件设计调整,设备树要随时修改,内核模块也要重编。所以强烈建议在开始前建立一个工作目录,固定BSP版本,记录所有修改点,避免后面找不到自己改了哪里。

3.3 U-Boot、内核和设备树联动配置

FMQL45T900的上电启动流程是:片上BootROM先加载FSBL,FSBL主要完成DDR初始化和最基本的串口、时钟初始化,然后跳转到U-Boot;U-Boot负责加载Linux内核和设备树,举交给内核启动。整个过程中,FSBL做硬件初始化,U-Boot做引导,内核负责操作系统层面的管理,设备树告诉内核硬件长什么样。三者缺一不可。

设备树是这套流程里最容易出问题的地方。它用DTS文件描述硬件的地址、中断、GPIO、时钟、外设引脚复用等属性。比如我要在PS端启用UART1,就需要在dts里添加或确认对应的节点,包含寄存器地址、中断号、时钟引用等。如果外设挂在PL侧,还需要在dts里定义“reg”和“compatible”,保证驱动能正确找到设备。

下面是我设备树里一个SPI外设节点的简化示例:

&spi0 { status = "okay"; num-cs = <1>; adc_spi: ad7606@0 { compatible = "somevendor,ad7606"; reg = <0>; spi-max-frequency = <1000000>; interrupt-parent = <&gpio0>; interrupts = <54 2>; }; };

这个节点的含义是:SPI0控制器使能,在片选0上挂了一颗ADC,SPI时钟最高1MHz,用GPIO0的第54号作为触发中断,中断触发类型是下降沿。编译设备树时,BSP通常提供命令直接生成dtb文件。修改后记得把dtb拷到启动分区,否则内核不会感知新硬件。

3.4 根文件系统制作与固化

内核起来后必须有一个根文件系统,否则Linux跑不完启动流程。为了快速验证,我用的是厂商预编译的buildroot根文件系统,里面已经带好了busybox、SSH和基本工控组件。先把SD卡分成两个区,第一个分区放BOOT.BIN、uImage和dtb,第二个分区放根文件系统,格式化成ext4,然后把buildroot根文件系统解压进去。

相关启动参数是U-Boot环境变量里的bootargs,一个经典设置如下:

console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait

含义是调试串口为ttyPS0,波特率115200,根文件系统在SD卡第二个分区。如果要从QSPI Flash启动,则需要改成root=/dev/mtdblock3或其他分区名,并且要把内核、dtb、根文件系统都写入Flash。固化时注意,烧写过程断电会变砖,但可以重新进入JTAG模式启动后再次烧写,所以先不要着急焊死启动模式选择,至少留出JTAG接口。

4. 信号处理平台从逻辑到应用的核心实现

4.1 PL侧的数据采集逻辑

在我的设计里,ADC输出通过SPI接口连接到PL,PL侧用一段状态机控制采样周期。状态机首先启动一次SPI传输,然后把采样值写入一个环形FIFO,FIFO另一端接到AXI-DMA的写通道,由DMA自动把数据搬到DDR。为什么非要多一个FIFO?因为ADC时钟域频率和AXI总线时钟域并不相同,跨时钟域处理不能直接抓信号,FIFO是解决数据同步最稳妥的办法。

下面是一段简化后的Verilog采集控制模块,它主要实现SPI的主模式和FIFO写使能控制:

module adc_spi_master ( input clk, input rst_n, output spi_cs_n, output spi_sclk, input spi_miso, output spi_mosi, input [31:0] tx_data, input start, output reg [15:0] rx_data, output reg done ); reg [5:0] bit_cnt; reg [15:0] shift_tx; reg [15:0] shift_rx; reg cs_n_r; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin bit_cnt <= 0; cs_n_r <= 1; done <= 0; end else if (start) begin shift_tx <= tx_data; bit_cnt <= 16; cs_n_r <= 0; done <= 0; end else if (bit_cnt > 0) begin spi_sclk <= bit_cnt[0]; // 简化时钟生成 if (bit_cnt[0]) shift_rx <= {shift_rx[14:0], spi_miso}; bit_cnt <= bit_cnt - 1; end else begin cs_n_r <= 1; rx_data <= shift_rx; done <= 1; end end assign spi_cs_n = cs_n_r; assign spi_mosi = shift_tx[15]; endmodule

这段代码只能展示核心思路,真正工程上会用更高性能的SPI控制器或直接使用Xilinx/复旦微的IP核。核心理解是:PL完成那些“周期严苛”的事情,比如按精确采样率启动转换、把数据接连不断地搬到内存,这些事如果交给ARM,很难保证持续稳定。

4.2 PS侧驱动与应用程序怎么配合

PL把数据搬进DDR后,PS端需要一个驱动或应用把数据从内存中读出来。最快捷的方法是使用UIO(Userspace I/O)或者直接mmap一段物理内存。我在项目里用了/mem接口,先通过open打开设备,再mmap到用户空间,然后启动DMA搬运,最后在循环里采集数据。这样省去写内核模块的时间,原型验证阶段非常快。

下面是一个简单的读取采样数据并做FFT分析的C语言骨架:

#define MMAP_PATH "/dev/mem" #define DATA_BASE 0x20000000 // PL侧DMA缓冲区地址 #define DATA_SIZE (8 * 1024 * 1024) unsigned char *map_base; int fd = open(MMAP_PATH, O_RDWR | O_SYNC); map_base = mmap(NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, DATA_BASE); while (running) { // 等待DMA传输完成,可以通过中断或轮询标志位 process_samples((short *)map_base, sample_count); // 这里可以做滤波、FFT、阈值判断等 } munmap(map_base, DATA_SIZE); close(fd);

这种方式的优点是直观、调试方便,缺点是没有在中断层面做精准同步。如果后续对时序要求更高,应该写一个简单的字符设备驱动,注册中断,在中断处理函数里唤醒等待队列,让应用层更准时地拿到新数据。我当时先用mmap把算法跑通,后来又封装了一层RingBuffer,在用户空间自己维护读写指针,配合DMA传输完成标志,实际效果已经很稳定。

4.3 关键的参数计算与性能验证

系统性能设计不是拍脑袋,参数都要算一遍。以我这套平台为例,8通道采集,每通道100kSPS,分辨率16位,总数据量是8乘以100000再乘以2字节,约1.6MB/s。对于DDR和DMA来说,这个速率很低,带宽根本不是瓶颈;但如果要实时处理算法,比如128点实数FFT,每帧处理时间就得控制在10ms以内。按ARM Cortex-A9跑400MHz的频率估算,128点FFT大概几十微秒到一百多微秒,完全来得及。

滤波方面,我用了一个32阶FIR低通滤波器,截止频率5kHz,系数由MATLAB用窗函数法算出来,导出成C数组后放到PS端运算。这个滤波器在100kSPS采样率下,每点运算量是32次乘加,8通道加起来也就是不到1000次乘加每秒,压力不大。如果换到高速ADC采样率几十MSPS,乘法量就会指数上升,那种场景必须把FIR放到PL里做,用DSP Slice完全不是问题。

性能验证我用的是信号发生器输入1kHz正弦波,幅度4V偏置2V,用这套平台采集后做FFT,基波峰值出现位置正好在1000Hz,信噪比在70dB以上,说明整套信号链路从硬件到逻辑都没有明显问题。再对比原始ADC输入和FPGA输出波形的延时,大概30微秒左右,完全满足工控系统对实时性的要求。

5. 常见问题与排查技巧实录

5.1 启动失败、DDR不稳定

现象是接上调试串口后没有任何输出,或者U-Boot反复重启。我遇到这个问题时,先检查电源:用示波器看各轨电压上电顺序,确认3.3V、1.8V、0.95V不是同时瞬变,而是按核心板手册要求的顺序建立。如果电压正常,再查启动模式配置,确认拨码开关位置对应的是QSPI还是SD卡,有时候单纯是拨码没拨对。

如果U-Boot启动后DDR自检不过,重点检查VTT电压和DDR终端电阻。核心板上的DDR初始化本来是工厂测好的,但底板如果影响了核心板的供电洁净度,DDR也会不稳定。此时可以用JTAG挂FSBL,单步执行到DDR初始化函数,观察打印信息是卡在哪个地址。还有一个土办法,把系统降频到较低频率,比如ARM降一半,DDR降333MHz,如果此时稳定,说明时序裕量不够,要去检查电源纹波和PCB走线。

5.2 设备树和驱动加载错误

Linux起来后,发现某个外设驱动加载失败,第一反应不是查驱动代码,而是查设备树。设备树就是内核看硬件的“说明书”,说明书里描述的资源地址、中断号、引脚复用只要有一个对不上,驱动就会报No such device或忙等。我在调试SPI ADC时遇到过中断注册失败,最后发现是我在设备树里写的中断号54和FSBL里配置的GPIO编号不一致,两者差了一个偏移。

排查这类问题,建议仔细看dmesg的输出。内核启动时会打印设备树解析、驱动申请资源等关键信息,凡是带fail、error、timeout的行都值得多看几遍。另外,/sys/bus/platform/devices目录下列出的device,如果驱动成功绑定,通常会在对应设备节点下出现driver链接。设备树修改后要重新编译dtb,并且确认启动时加载的是这个新dtb,而不是还留在旧分区里的老文件。

5.3 PL时序收敛问题

PL逻辑如果比较复杂,时序收敛会变成一个反复折腾的过程。我第一次把8通道数字滤波和DMA控制器都放在一个500MHz的时钟域下综合,结果时序报告里setup违例一堆。后来把设计拆成两个时钟域:ADC采样时钟100MHz,AXI总线时钟150MHz,中间用FIFO隔离,问题立刻缓解了很多。

时序不收敛的原因通常是组合逻辑路径太长,或者没有写时钟约束。不要指望工具帮你自动推算出所有约束,至少要在XDC或SDC文件里把主时钟、生成时钟和输入输出延时都声明清楚。比如输入ADC时钟是100MHz,就要写create_clock -period 10.000 -name adc_clk,然后对ADC输入引脚做set_input_delay约束。Vivado或复旦微配套工具时序报告里的WNS(最差负裕量)如果为正,说明路径能收敛;如果为负,就要考虑把跨时钟域信号打拍、插入流水寄存器,或者直接降频调整约束。

5.4 用工具说话:一套高效排查流程

一个人闷头查问题很容易钻牛角尖,后来我总结了一套“工具箱式”排查流程。硬件层用示波器和万用表,涉及高速信号时用逻辑分析仪抓关键引脚波形;软件层用串口终端、dmesg、/proc和sysfs节点;逻辑层用Vivado或配套的调试工具做在线逻辑分析。这套组合拳基本能覆盖九成以上问题。

举个例子,一次ADC读回来的数据全是0xFF,我第一反应是SPI时序问题,于是用逻辑分析仪抓CS、SCLK、MISO信号,结果发现MISO根本没有波形,一路查下去才发现是底板上的ADC电源没焊好,VCC悬空导致输出引脚电平被拉死。这种问题如果不用逻辑分析仪,光看数据根本猜不到根因。排查时切记一次只改一个变量,不要同时改设备树、驱动和FPGA逻辑,否则问题定位会变得非常困难。

项目复盘时我有一个很深的体会:FMQL45T900这类异构SoC,难点不在单点技术,而在“软硬件协同”的节奏。BSP配置阶段最忌跳步骤,老老实实按工具链、FSBL、U-Boot、内核、设备树、根文件系统的顺序走,每一步都能验证再进入下一步,后面才省心。信号处理部分,尽量把实时数据处理放到PL,ARM只做控制和通信,能少掉很多意外。最后再分享一个小技巧:拿到核心板之后先把厂商提供的出厂镜像完整烧录跑一遍,确认板卡硬件没有问题,同时保留一份原始BSP包和工程备份,后面哪怕改乱了也能快速恢复。这个习惯帮我省了至少两天时间。

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

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

立即咨询