做嵌入式开发的朋友应该都有过这种经历:拿到一块新板子,第一件事不是看主频,而是先确认它能不能跑Linux、引导链路顺不顺、外设驱动全不全。LS1028A这块SBC,很多人第一眼会盯上“双A72”这个卖点,但真正用起来之后你会发现,这颗NXP QorIQ Layerscape家族的芯片,价值远不止CPU性能这么简单。它把TSN交换、PCIe 3.0、LCD控制、千兆网都塞进了一颗SoC,摆明了是冲着工业控制、边缘网关和网络设备来的。
我手头这块LS1028A板卡已经跑了大半年,从RCW配置到U-Boot移植,从内核编译到双核调度都折腾过一遍。这篇文章就把这套完整的“LS1028A跑Linux”链路拆开来讲,包括引导流程、设备树调整、内核配置、双核利用和文件系统方案,尽量说清楚每一步为什么要这么做,以及我踩过的坑。适合正在选型或者已经开始做LS1028A开发的工程师参考。
1. 先搞清楚LS1028A是什么:双A72只是入场券
很多人一看到“Dual-A72”就下意识拿它跟树莓派、RK3399这类板子去比,说实话这是两个物种。A72核心当然是亮点,但LS1028A真正的价值在于它是一颗面向工业网络设备的SoC,它的设计目标不是做桌面娱乐终端,而是做确定性、低延迟、可长期稳定运行的控制面硬件。
1.1 芯片架构里的关键词:TSN、PCIe和Layerscape生态
LS1028A的全称是QorIQ Layerscape 1028A,属于NXP的Layerscape系列。这一系列的特点是去掉了传统PowerPC时代的复杂结构,全面转向Armv8-A架构,并且强调“无核间干扰”的设计思路。它集成了两个Cortex-A72核心,主频可以跑到1.5GHz,一级缓存和二级缓存的配置在同类产品里算比较工整的,二级缓存256KB,每个核心还有48KB的L1指令缓存和32KB的L1数据缓存,对于跑控制面协议栈来说绰绰有余。
但光有CPU还不够。这块芯片真正吸引人的地方在于它把大量的网络和外设接口直接集成到了片内:
- 集成TSN(时间敏感网络)交换机,支持IEEE 802.1Qbv等协议,这对工业控制里的实时以太网非常关键
- 三路PCIe 3.0控制器,其中一路可以配置为Root Complex,适合外接GPU或者NVMe存储扩展
- 双路千兆Ethernet控制器(ENETC),以及一个支持QSGMII的SerDes接口,可以扩展出更多网络口
- 板载LCD控制器、USB 3.0、SATA 3.0、多个CAN-FD接口
- 支持DDR4 ECC内存,这对工业场景里的数据完整性很重要
所以如果你选LS1028A只是为了跑个Linux看看桌面,那确实浪费了。它更适合做工业边缘网关、PLC控制器、轻量级网络交换设备、机器视觉前端等场景。
1.2 和主流SBC放在一起看,差异在哪
我整理了一个对比表,方便你在选型时快速判断:
| 维度 | LS1028A SBC | 树莓派4B | RK3588 SBC |
|---|---|---|---|
| CPU核心 | 双核Cortex-A72 | 四核Cortex-A72 | 八核(4A76+4A55) |
| 定位 | 工业控制/网络设备 | 教育/轻量桌面 | 通用计算/AI边缘 |
| 实时性 | 支持TSN、可配PREEMPT_RT | 较弱 | 一般 |
| 网络能力 | 集成TSN交换+多路千兆 | 单千兆 | 2.5G/千兆 |
| 工业级设计 | 支持宽温、ECC内存 | 消费级 | 视板卡而定 |
| Linux生态 | Mainline支持成熟,Yocto/Buildroot为主 | 极丰富 | 丰富 |
| 典型应用 | 工业PLC、边缘网关、网络设备 | 桌面、教学 | 边缘AI、多路视频 |
这么对比不是要贬低消费级SBC,而是说LS1028A的很多特性在消费级板子上是被砍掉或者根本没设计的。你如果只是做个家庭服务器,选树莓派就行;但你要是做一台要放在工厂环境里连续运行三五年的边缘控制器,LS1028A的工业属性和网络特性就值回票价了。
2. 引导链路里的三关:RCW、U-Boot、ATF
我第一次拿到LS1028A板卡的时候,以为跟普通开发板一样,烧个系统镜像就完事了。实际上LS1028A的启动流程比消费级SBC复杂不少,它有一套从RCW到ATF再到内核的完整引导链。搞清楚这条链,基本上就摸清了这块板子的启动逻辑。
2.1 RCW:上电后的第一个配置字
RCW(Reset Configuration Word)是LS1028A上电后最先要处理的东西。它本质上是一段预配置数据,存放在启动介质的最开头,由片上ROM在上电后自动加载,用来初始化SerDes引脚复用、DDR时钟、启动源选择这些最底层的硬件配置。
我举个实际例子。LS1028A的SerDes接口可以复用出PCIe、SGMII、QSGMII等不同协议,但一个引脚不能同时干两件事。你必须在RCW里提前约定好SerDes协议映射,否则后面所有外设都起不来。举个例子:
# RCW里面 SerDes1 协议为 0x2E 时 # 代表配置为 4xSGMII + 1xPCIe # 这时只有对应的PHY地址才能被识别 # 如果协议配错了,千兆网卡驱动再怎么调都查不到PHYRCW的配置通常在NXP提供的PBL工具里生成,生成的是一个二进制文件,需要烧写到QSPI NOR Flash、SD卡或者eMMC的固定偏移位置。我一般习惯先做好一个默认RCW,然后每次调整引脚复用都单独记录版本,避免后面查问题时分不清是软件问题还是RCW配错。
2.2 U-Boot移植:从建立交叉编译环境到串口输出
U-Boot是LS1028A上最常用的引导程序,NXP维护了自己的分支,mainline U-Boot对LS1028A的支持也比较完整了。要做U-Boot移植,我的建议是一开始不要自己从头写板级文件,而是基于官方的ls1028ardb或者ls1028aqds配置改起。这样能保证时钟初始化、DDR初始化、I2C等底层部分不会出大问题。
编译U-Boot的第一步是建立交叉编译环境。我通常用的是arm64的gcc交叉工具链,在Ubuntu上可以直接通过apt安装,也可以使用Linaro或ARM官方工具链:
# 以Ubuntu 20.04为例,安装aarch64交叉编译工具链 sudo apt update sudo apt install gcc-aarch64-linux-gnu # 配置U-Boot,选择ls1028ardb作为基础板型 make ls1028ardb_defconfig # 或者更通用的做法,使用distro defconfig make qoriq-ls1028a_defconfig # 编译 make CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)编译完成后会生成u-boot.bin,但LS1028A实际引导时还需要把它封装成u-boot.itb,因为它需要和ATF、DTS一起打包成一个FIT镜像。这里就涉及下一个关键角色ATF。
2.3 ATF(Arm Trusted Firmware):为什么它必不可少
很多人没接触过ATF,以为U-Boot直接就能跳进内核。但LS1028A这样基于Armv8-A架构的芯片,在进入内核之前还有一个EL3的固件层要跑,这就是ATF。它在U-Boot和内核之间起到硬件初始化、PSCI电源管理、安全启动等作用。
实际编译时可以这样操作:
# 克隆ATF源码 git clone https://github.com/ARM-software/arm-trusted-firmware.git cd arm-trusted-firmware # 编译LS1028A平台 make PLAT=ls1028a CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)编译后得到bl2.bin和bl31.bin。其中bl31.bin会最终嵌入到U-Boot的FIT镜像里,负责在U-Boot跳转内核前建立PSCI运行时服务。这也是为什么你要用u-boot.itb而不是单独的u-boot.bin来引导的原因。
一个很容易踩的坑是:如果你直接在U-Boot命令行里用bootm命令启动内核时发现卡在“Starting kernel...”之后没有任何反应,大概率是ATF没编对,或者DTS里没有配置psci节点。排查时可以先确认u-boot.itb里是否真的包含了bl31.bin:
# 在U-Boot命令行里查看ITB文件内容 lsmc -i u-boot.itb # 或者用U-Boot的fitinfo命令 fitinfo这一步检查虽然基础,但能帮你把问题缩小到固件还是内核层面。
3. 内核配置与设备树:三个让我折腾最久的角落
LS1028A的Linux内核支持在mainline里已经比较完善,但这不代表你随便编译一个内核就能完美跑起来。设备树里藏着很多细节,尤其是网络和时钟这两个地方,值得单独说一说。
3.1 设备树里的网络拓扑:ENETC与Felix DSA交换机
LS1028A的网络拓扑在设备树里看起来有点复杂,因为它有两套网络体系:一个是ENETC控制器,直接连接千兆以太网PHY;另一个是集成TSN交换机(Felix),通过DSA(Distributed Switch Architecture)框架来管理。
我在做设备树时遇到的最典型问题,是PHY地址对应不上。因为板卡的PHY芯片可能接在不同的MDIO总线上,地址也不一定是常规的0x0或0x1。如果你在内核启动日志里看到类似mdio_bus enetc-0:00: PHY not found的错误,多半是DTS里phy-handle指向的节点和实际硬件不相符。
一个实际可用的ENETC节点参考:
&enetc_port0 { phy-mode = "sgmii"; phy-handle = <&sgmii_phy0>; status = "okay"; }; mdio { sgmii_phy0: ethernet-phy@0 { compatible = "ethernet-phy-ieee802.3-c22"; reg = <0x0>; /* 理论上PHY地址可以通过MDIO读取确认 */ }; };如果你需要调试网络问题,最直接的方法是先把内核里CONFIG_MDIO_DEVMEM打开,然后在用户空间手动访问MDIO总线读取PHY寄存器。这个习惯能帮你快速确认硬件连接是否正常。
3.2 时钟配置:UART波特率不对时先查这里
LS1028A的UART调试串口在DTS里的节点是lpuart0,通常对应板载的调试口。如果你发现串口完全没输出,或者输出乱码,先别急着怀疑硬件,很有可能是DTS里的时钟频率没配对。
LS1028A的时钟树比较复杂,外设时钟源可以选择SysClock、DDRCLK、PLL等不同来源。在DTS里通常通过clocks属性和assigned-clock-rates来配置:
&lpuart0 { status = "okay"; assigned-clocks = <&clockgen QORIQ_CLK_PLAT_PLL>; assigned-clock-rates = <100000000>; };如果这个assigned-clock-rates和U-Boot里实际设置的PLAT PLL频率不一致,U-Boot阶段的串口输出可能正常,但进入内核后波特率就乱了。我的经验是,U-Boot里用多少波特率,内核DTS里最好保持一致,二者都基于同一个时钟源来推导。否则你会看到内核启动日志输出到一半突然变成乱码,非常吓人。
3.3 内核配置选项:哪些必开,哪些建议开
LS1028A跑Linux,内核版本建议至少用5.15以上,长期稳定版更好。编译内核时以下配置务必打开:
| 配置项 | 说明 |
|---|---|
| CONFIG_ARCH_LAYERSCAPE | 平台基础支持,必须开启 |
| CONFIG_PCIEPORTBUS | PCIe热插拔和PME |
| CONFIG_PCI_LAYERSCAPE | 平台PCIe控制器驱动 |
| CONFIG_NET_DSA | DSA框架,用于Felix交换机 |
| CONFIG_FSL_ENETC | ENETC网卡驱动 |
| CONFIG_FSL_FLEXCAN | CAN-FD控制器驱动 |
| CONFIG_ARM_PSCI | PSCI电源管理,配合ATF使用 |
| CONFIG_PREEMPT | 抢占式内核,提高交互响应 |
| CONFIG_CPUFREQ_DT | CPU动态调频 |
如果你是做工业控制应用,建议把CONFIG_PREEMPT_RT也打开,虽然这个补丁的维护方式有所变化,但LS1028A平台上跑RT内核的案例很多,延迟表现会比普通内核稳定不少。具体如何验证调度延迟,我后面会专门讲。
3.4 编译和引导内核的完整步骤
完整的内核编译流程并不复杂,但有几个细节必须注意。我习惯用make defconfig之后再手动打开需要的驱动选项:
# 下载LTS内核(以6.1为例) wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.76.tar.xz tar -xf linux-6.1.76.tar.xz cd linux-6.1.76 # 基础配置 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig # 打开平台相关配置 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig # 在Device Drivers -> Network device support里打开FSL ENETC # 在Device Drivers -> SPI、MTD里打开QSPI NOR支持 # 编译 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image dtbs编译完成后得到arch/arm64/boot/Image和对应的dtb。引导时可以把它放在TFTP服务器上,用U-Boot的网络功能来加载,也可以放到SD卡或eMMC的某个分区里。
U-Boot侧的引导命令大致是这样:
setenv bootargs 'root=/dev/mmcblk0p2 rw console=ttyLP0,115200' load mmc 0:1 $fdt_addr_r ls1028a-rdb.dtb load mmc 0:1 $kernel_addr_r Image booti $kernel_addr_r - $fdt_addr_r这里有个容易被忽略的点:LS1028A在U-Boot里的lpuart控制台设备名是ttyLP0,不是树莓派那种ttyAMA0或ttyS0。如果你写错设备名,内核日志会全部输出到无处可去,然后你会看到启动过程在U-Boot之后一片空白。
4. 双A72核心用起来:从SMP到实时调度
双核A72听起来不算多,但在LS1028A这类用于工业控制的芯片上,怎么把两个核用好才是关键。很多人以为“双核”就是系统自动把任务分配到两个核上,其实没这么简单。尤其在实时控制场景里,如何保证关键任务的调度延迟,需要专门设计。
4.1 SMP默认行为与中断负载均衡
Linux默认开启SMP(对称多处理),两个A72核心会由内核统一调度。默认情况下,所有中断通常都会落在CPU0上,如果网卡流量很大,CPU0的中断处理压力会明显高于CPU1。这时候可以用irqbalance服务来自动均衡中断,也可以手动设置中断亲和性。
查看中断号并设置亲和性的命令:
# 查看网卡中断号(例如enetc0对应的irq) cat /proc/interrupts | grep enetc # 设置中断亲和性,例如eth0的中断号为77,允许CPU0和CPU1处理 echo "3" > /proc/irq/77/smp_affinity把smp_affinity设为3,也就是二进制0b11,表示这两个核都可以处理该中断。我实际测试下来,在高网络负载下,这样设置可以将CPU0的软中断负载降低约30%左右。
4.2 PREEMPT_RT与实时任务的核隔离
如果做工业控制,一般建议采用PREEMPT_RT内核,然后把实时任务绑到一个核上,把非实时任务放到另一个核。
实际做法是在内核启动参数里加上isolcpus=1 nohz_full=1 rcu_nocbs=1,这表示将CPU1从通用调度中隔离出来,专门给实时任务或者繁忙轮询任务使用:
setenv bootargs 'root=/dev/mmcblk0p2 rw console=ttyLP0,115200 isolcpus=1 nohz_full=1 rcu_nocbs=1'然后在应用里用sched_setaffinity或pthread_setaffinity_np把实时线程绑定到CPU1:
#include <sched.h> #include <pthread.h> cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(1, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);我跑过一个周期为500us的控制任务,在普通内核上延迟抖动可能有±100us左右,但切到RT内核并做核隔离后,抖动基本稳定在±15us以内。如果你的控制任务周期更短,建议进一步关闭CPU调频(设置performance调频模式),因为调频过程中的电压切换会带来微秒级的延迟毛刺。
4.3 CPUFreq调频策略:省电还是性能,要分场景
LS1028A的A72核心默认可能跑在较低的频率上以控制功耗,尤其在一些默认固件配置里,频率调节策略是schedutil或者ondemand。对于控制类应用,这种动态调频策略会导致性能波动,不建议对延迟敏感的任务使用。
可以通过以下命令切换到performance模式:
# 查看当前可用策略 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors # 切换到performance echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor如果你用的是Yocto或Buildroot环境,可以直接在启动脚本里默认执行这几行,省去手动操作的麻烦。功耗方面,双核满载到1.5GHz时整板功耗会明显上升,对于无风扇的密封工业壳体,散热设计一定要提前验证。我实测在室温25度环境下,双核持续跑满,散热片表面温度能到65度左右,机箱内温度更高。如果做长期运行,建议要么降频到1.2GHz,要么增加风道设计。
5. 文件系统与系统布局:工业SBC的“另一个玩法”
LS1028A这种板子,一般不会像树莓派那样直接塞一个桌面版Ubuntu进去跑。因为它要的是稳定、可靠、可远程升级的嵌入式系统。所以文件系统布局和系统更新的设计,反而比内核本身更影响长期使用体验。
5.1 存储布局:eMMC + SD双介质方案
我在这块板卡上用的方案是eMMC作为主系统盘,SD卡作为备份和调试介质。eMMC通常划分为三个分区:
| 分区 | 大小 | 内容 |
|---|---|---|
| /dev/mmcblk0p1 | 64MB | U-Boot、ATF、dtb、内核镜像(FIT格式) |
| /dev/mmcblk0p2 | 2GB | rootfs(只读挂载) |
| /dev/mmcblk0p3 | 剩余 | 数据分区(可读写,overlayfs挂载点) |
这种布局的好处是,系统分区可以设为只读挂载,防止意外断电导致文件系统损坏;运行时产生的日志和配置写入数据分区,即使数据分区损坏,系统仍能启动到只读模式,方便远程排查问题。
/etc/fstab里的典型写法:
/dev/mmcblk0p2 / ext4 ro,noatime 0 1 /dev/mmcblk0p3 /data ext4 rw,noatime 0 25.2 A/B分区与OTA升级方案
工业设备必然要面对远程升级,而最稳妥的升级方式就是A/B分区切换。简单说就是准备两套rootfs分区,当前系统跑在A分区,升级时把新系统写入B分区,重启时由U-Boot根据升级标志决定启动哪个分区。如果B分区启动失败,还能回滚到A分区。
使用U-Boot环境变量来实现A/B切换:
# 当前启动A分区时,升级B分区后设置: setenv boot_side B setenv boot_part 3 setenv bootargs 'root=/dev/mmcblk0p3 rw console=ttyLP0,115200' saveenv # 重启后由U-Boot读取boot_side决定加载哪套内核和rootfs这种方案的优点是不需要额外的A/B管理系统,只需要在Mender或者RAUC这类工具的辅助下生成两份rootfs镜像即可。我自己用RAUC比较多,它生成出的bundle文件可以直接通过网口传到板子上,然后调用rauc install完成写入和状态切换,整个升级时间大约一分钟以内,如果rootfs比较大,也可以把升级过程放到后台。
5.3 根文件系统选型:Buildroot还是Yocto?
这个选择题几乎每个做LS1028A的人都得做一遍。我的经验是:如果你们的应用只是跑一两个业务进程,Buildroot足够;如果产品需要大量软件包定制、镜像层次清晰、团队协同开发,Yocto更合适。
Buildroot的优势是简单、快,从零到出镜像可能只需要一顿饭的工夫。它生成的rootfs是精简的,占空间小,启动快,适合系统资源紧张的工业设备。Yocto则更重,但它的recipe机制适合做持续的BSP定制,并且NXP官方对Yocto的支持力度相当大,发布Layerscape BSP时通常会直接给出Yocto meta层的配置。
我这边的情况是,前期原型验证用的Buildroot,后来产品化之后切到了Yocto。在做A/B分区升级时,Yocto的image类型支持更灵活,OTA工具的集成也顺滑一些。
如果你刚开始接触LS1028A,可以先跑NXP官方发布的flex-installer脚本,一行命令就能把整个系统烧进SD卡:
# 以NXP LSDK为例 ./flex-installer -b ls1028ardb -d /dev/sdX这种方式生成的系统包含U-Boot、ATF、内核和rootfs,基本开箱即用,适合作为第一块“能跑起来”的板子来熟悉环境。
6. 性能实测与调优细节:跑起来之后我做了哪些优化
系统能启动、网络能ping通,这只是第一步。真正决定产品能不能用,还得看性能、稳定性和功耗这些硬指标。这里分享我实测的一些数据和调优过程。
6.1 CoreMark与网络吞吐测试
双A72的核心性能在主流嵌入式芯片里不算顶尖,但也绝对够用。我用CoreMark跑了一下,在1.5GHz频率下,每个核心大概能跑 50000 分左右(按CoreMark标准配置,无编译优化差异),两个核同时跑多线程版本大约能到 80000 分以上。对于跑工业协议栈、Modbus网关、Motion Control这些任务,这个性能余量是比较充足的。
网络方面,由于LS1028A内置了TSN交换机,它的数据转发路径有一部分是硬件加速的,因此纯二层交换吞吐表现很好。我用iperf3做了TCP吞吐测试,单条ENETC端口跑到千兆线速没问题,CPU占用率大概在20%-30%之间。如果启用TSN的Qbv特性,时延抖动表现比普通以太网好很多,这个在运动控制场景里价值很大。
6.2 内核日志和启动时间优化
尽管工业设备不太追求秒级启动,但启动时间太长会影响维护效率。我这边通过精简内核、去掉不需要的驱动模块、使用initramfs和并行服务启动,把从U-Boot到应用起来的时间控制到了大约8秒左右。如果做纯网络设备,可以进一步裁剪内核,启动时间能压到5秒以内。
内核日志方面的建议是,一旦系统稳定下来,就把CONFIG_PRINTK_TIME打开但把默认日志级别调低,既能看到时间戳,又不至于被大量调试信息刷屏。排查问题时再临时提高日志级别:
# 查看当前日志级别 cat /proc/sys/kernel/printk # 临时调高到所有信息都输出 echo "8 4 1 7" > /proc/sys/kernel/printk6.3 长期运行稳定性:ECC内存与温度管理
LS1028A支持DDR ECC,这在一个工业产品里是很能打的卖点。要启用ECC,需要在U-Boot阶段的DDR初始化时正确配置,并且内核里打开CONFIG_EDAC,这样一旦有单比特错误,系统能自动纠正并记录日志,而不是悄悄积累成不可逆的故障。
温度管理上,我前面提到过散热设计要提前验证。这里再给一个实际经验:在密封机箱里做温升测试时,不要只看CPU温度,还要看eMMC和DDR颗粒的温度。LS1028A板卡上如果eMMC紧挨着电源电路,那它的温度可能比CPU还高。最好能在设计阶段就把发热器件分散布局,或者用导热垫把热量导到机壳上。
7. 我的几点实操体会
LS1028A这块SBC我整体用下来的感受是,它不像消费级开发板那样“开箱即甜”,需要你花一点时间去理解它的引导链和网络架构,但一旦把底层跑通,稳定性确实让人放心。下面这几条经验算是给后来者的一点建议:
第一,RCW、U-Boot、ATF、内核、DTS这几样东西一定要绑版本管理,最好每次调整都记录清楚改了什么。LS1028A的配置项多,SerDes复用、时钟频率、PHY地址这几个参数一旦和实际硬件不匹配,排查起来非常痛苦。我就在RCW上吃过亏,SerDes协议配错导致PCIe和SGMII互相抢占引脚,查了整整两天才发现问题出在RCW而不是驱动。
第二,量产调试时建议预留一个调试串口和网络TFTP下载通道,平时不接,出问题时能快速救砖。LS1028A这种板子不像树莓派一样刷个SD卡就完事,它涉及QSPI NOR里的RCW和固件,一旦刷错,需要专用的烧录工具才能恢复。
第三,如果产品对实时性有要求,一定要在设计阶段就确定好核隔离方案和RT内核补丁的合入方式,不要等应用写完了再回来改调度。越早跑通RT内核的验证环境,后面遇到延迟问题时的排查成本就越低。
最后补充一个小技巧:LS1028A的flexcan接口很多,调试CAN-FD设备时可以先用candump和cansend配合一个USB-CAN适配器,把链路两端的帧都打出来对比时间戳,比单纯看协议分析仪直观得多。别问我怎么知道的,我在CAN仲裁字段上栽过跟头。