首页 / 资讯中心 / 文章详情

ADSP-SC589平台Linux引导链路:U-Boot与内核编译及烧录指南

ADSP-SC589平台Linux引导链路:U-Boot与内核编译及烧录指南 ★ FEATURED ARTICLE
1. 动手之前先把SC589这套引导链路看清楚1.1 这颗芯片的特殊之处拿到一块ADSP-SC589的开发板大多数团队的第一件事都是把Linux跑起来。这颗芯片把一颗ARM Cortex-A5和两颗SHARC DSP装在了同一个封装里ARM侧跑Linux做控制、网络协议和数据搬运DSP侧做实时信号算法是音频处理、工业检测、医疗信号采集领域很常见的异构计算方案。你负责的板级软件工作核心就是把ARM这一侧的Linux引导链路打通。这颗ARM核属于ARMv7-A架构带硬件浮点完全可以跑标准Linux内核。但要注意它和单片机不一样上电后代码不是直接从Flash里执行而是经过“片内BootROM → U-Boot → 内核”这样的接力引导。BootROM是芯片出厂固化的我们改不了也基本不用管U-Boot负责初始化DDR、串口、存储介质把内核镜像和设备树加载到内存最后跳转执行内核。内核启动后再挂载根文件系统交付一个完整的Linux环境。整个链路上U-Boot和内核就是你能掌控的两道关。1.2 U-Boot、内核、设备树分别管什么先把角色理清楚后面操作才不会乱U-Boot引导加载程序相当于开机时的“门卫”。它要完成DDR初始化、时钟配置、启动介质识别然后把内核请进门。编译产物烧录在启动介质SD卡、SPI Flash等的固定位置BootROM上电后负责找到它。Linux内核操作系统本体管进程调度、内存管理、设备驱动。我们编译出来的zImage或uImage就是内核压缩镜像。设备树DTB一块板子的硬件“接线图”。同样一颗SC589不同板卡的DDR容量、外设引脚、PHY地址可能都不一样内核不把这些信息写死而是在设备树里描述。同一份内核镜像换一个DTB就能适配不同板卡。所以整个编译工作可以拆成三大块交叉工具链、U-Boot、内核设备树。这三块互相依赖建议按顺序来先把工具链装好再编U-Boot最后编内核。很多人一上来就急着编译内核结果U-Boot还没着落板子上没有任何引导程序内核编得再好也跑不起来。2. 搭建交叉编译环境版本矩阵别乱搭2.1 为什么必须用交叉编译SC589的ARM核是跑Linux的但你不可能在板子上直接编译内核——芯片的资源主要用于产品功能不是用来做开发的。常规做法是在一台x86架构的PC上安装针对ARM架构的交叉工具链编译出目标平台可执行的二进制。交叉编译的关键就是工具链前缀。我们要用的是arm-linux-gnueabihf-这一类其中arm是目标架构gnueabihf表示使用glibc并且支持硬浮点。Cortex-A5自带VFPv4硬件浮点单元所以必须选hf版本如果用软浮点的arm-linux-gnueabi内核编译能过但性能会明显下降某些浮点运算库也可能跑出问题。2.2 工具链安装与环境变量在PC端Linux发行版里可以直接用包管理器安装sudo apt-get install gcc-arm-linux-gnueabihf libssl-dev u-boot-tools device-tree-compiler这四个包分别是交叉编译器、内核编译时需要的SSL开发库、mkimage工具生成uImage必需、设备树编译器。缺一不可后面编译报错有一半原因就是少装了其中某个。装完验证一下arm-linux-gnueabihf-gcc -v能打印出版本信息就说明基本可用。接下来每次编译前都要先导出三个环境变量或者直接写进~/.bashrcexport ARCHarm export CROSS_COMPILEarm-linux-gnueabihf-ARCH告诉构建系统目标CPU架构是ARMCROSS_COMPILE指定交叉编译器前缀。这样U-Boot和内核的Makefile才会自动调用arm-linux-gnueabihf-gcc而不是PC本机的gcc。2.3 工具链版本的选择逻辑这里有个很多人踩过的坑不是gcc越新越好。芯片厂商发布的U-Boot和内核源码通常是在某个gcc版本上验证过的。如果厂商文档里明确写了推荐工具链版本最好用那个版本如果没写优先用和源码发布时间接近的gcc。实测中用太新的gcc比如gcc 12以上编译老版本内核经常会出现“unknown option”、“error: implicit declaration of function”这类报错有些是内核代码本身依赖了旧编译器行为硬改代码风险很大。判断标准很简单先按默认工具链试编如果U-Boot和内核都能干净通过就不动版本一旦出现看起来“莫名其妙的语法报错”三四行代码内找不到原因别硬扛先换工具链版本试试。我在实际操作中遇到过编译内核时gcc报一堆Werror级别错误的情况换了个gcc版本后一行代码没改就过了。3. 编译U-Boot让门卫先进场3.1 获取源码和确认defconfigU-Boot源码优先用芯片厂商维护的分支不要自己去mainline社区拉最新版。芯片厂商会把SC589的板级配置、DDR初始化代码、驱动补丁都合在维护分支里主线的U-Boot对这颗芯片的支持往往不完整。拿到源码后先看configs目录下有哪些SC589相关的defconfigls configs | grep -i sc589通常能找到类似sc589_defconfig或带板卡名字前缀的配置项。量产板如果用是自研硬件依然建议先选官方评估板的defconfig编译跑通后再根据自己的板卡差异做裁剪。这一步的原则是先求通再优化。3.2 配置与编译流程进入U-Boot源码目录先做一次彻底清理避免之前尝试留下的配置污染make distclean make sc589_defconfig make -j$(nproc)make distclean很关键。很多编译问题都是因为反复切换defconfig后旧的config残留导致的。每次换配置之前都clean一次能省掉大量莫名其妙的链接错误。编译完成后目录下会生成u-boot.bin。有些SC589分支还会生成u-boot.img或SPL具体用哪个看BootROM的要求。这里的核心经验是BootROM对镜像格式和烧录偏移位置有严格要求这不是U-Boot本身能决定的而是芯片BootROM出厂时就定死的。所以拿到板卡后第一件事就是去芯片手册的BootROM章节查启动介质加载偏移或者看厂商评估板的烧写脚本。以SD卡启动为例常见做法是把U-Boot写入SD卡的扇区偏移位置而不是从块0开始。很多SoC的BootROM会跳过前几个扇区因为要留空间给分区表和MBRU-Boot镜像通常从1号扇区或64号扇区开始写不同芯片差异很大。稳妥做法如下sudo dd ifu-boot.bin of/dev/sdX bs512 seek1 convfsync注意/dev/sdX是你的SD卡设备名千万别写错否则会毁掉PC的硬盘数据。seek参数就是BootROM要求的分区偏移。如果厂商还提供了SPL则需要先烧SPL再烧U-Boot主体。3.3 U-Boot环境变量与启动参数U-Boot启动内核靠的是环境变量。环境变量就像U-Boot的“启动脚本”存在存储介质里上电后U-Boot会按bootcmd定义的动作去找内核。调试阶段最推荐SD卡FAT分区的方式把uImage和.dtb文件放在SD卡第一个分区FAT格式内核和根文件系统放在后面。进入U-Boot命令行后这样设置setenv bootargs consolettySC0,115200 root/dev/mmcblk0p2 rw rootwait setenv bootcmd fatload mmc 0:1 0x80008000 uImage; fatload mmc 0:1 0x81000000 sc589-ezkit.dtb; bootm 0x80008000 - 0x81000000 saveenv这里bootargs是传给内核的启动参数console指定内核日志输出到哪个串口。ttySC0是我用的内核里注册的串口名不同厂商内核可能叫ttyAMA0或者ttyS0以你自己的内核dmesg输出为准。把root/dev/mmcblk0p2理解为“根文件系统在SD卡的第二个分区”rootwait告诉内核挂载根文件系统前等待设备就绪这个参数对SD卡几乎是必须的没有它内核可能在设备还没ready时就尝试挂载然后panic。0x80008000和0x81000000这两个内存地址也有讲究前者是内核镜像的加载地址后者是设备树的加载地址。中间留出足够间距避免内核解压和DTB互相覆盖。bootm 0x80008000 - 0x81000000中间那个-表示“没有ramdisk”直接用设备树启动。4. 编译Linux内核和设备树4.1 内核源码与defconfig内核源码同样建议用芯片厂商维护的分支。SC589的内核树通常基于某个Linux主线版本常见的是4.x或5.x系列并合入了对这颗芯片的板级支持、外设驱动和补丁。不要用主线内核直接编译因为SC589的很多驱动尤其涉及到与DSP通信的驱动只在厂商分支里。拿到源码后make distclean make sc589_defconfig make menuconfig厂商defconfig默认会打开SC589评估板所需的大部分驱动包括网络、串口、MMC、USB等。第一次编译不建议大改配置先整体过一次能启动后再按需裁剪。make menuconfig打开图形配置界面本质是帮你生成真正的.config文件。如果你对某些驱动选项不确定宁可保持默认也别乱禁用。我见过有人为了精简内核把“device mapper”模块关了结果根文件系统挂不上排查了半天最后发现是内核选项缺失。4.2 编译uImage和设备树内核编译和U-Boot不同的是要明确指定镜像格式和加载地址。执行make -j$(nproc) uImage LOADADDR0x80008000 make dtbs make modulesLOADADDR0x80008000和U-Boot里fatload的加载地址必须一致uImage头里会把加载地址信息写进去bootm读取时如果发现镜像头部的加载地址和你实际加载位置不一致会出现Wrong Image Format或者解压后跳转到错误地址的问题。make dtbs会生成设备树二进制文件产物在arch/arm/boot/dts/目录下文件名形如sc589-ezkit.dtb。如果你的板卡是自己画的需要改arch/arm/boot/dts/下对应的.dts源文件比如调整DDR容量、修改PHY地址、改LED引脚等改完再单独重新编译对应的dtbmake xxx-board.dtb内核模块也要一并编译因为SC589很多驱动尤其是网络、USB里的协议栈部分是模块方式加载的。编译完后可以指定一个临时目录安装模块方便后面打包进rootfsmake modules_install INSTALL_MOD_PATH./modroot这样模块会安装到./modroot/lib/modules/下把这个目录并入根文件系统即可。4.3 组装SD卡启动介质内核、设备树、模块都齐了下一步是把它们摆到正确的位置。SD卡规划如下分区文件系统内容分区1FAT16/FAT32uImage、sc589-ezkit.dtb分区2ext4根文件系统、内核模块用fdisk或gparted完成分区后第一个分区拷入镜像文件sudo mount /dev/mmcblk0p1 /mnt/boot sudo cp uImage sc589-ezkit.dtb /mnt/boot/ sudo umount /mnt/boot第二个分区解压根文件系统。调试阶段也可以先用NFS或者ramdisk启动但SD卡方案最接近量产形态而且串口拔掉后也能独立跑。第一次上电前确认U-Boot的bootcmd环境变量里引用的文件名、分区号、内存地址和实际一致。我反复强调这三点是因为实际出问题最多的就是它们要么分区号写错要么dtb文件名大小写不对FAT文件系统不区分大小写但U-Boot的fatload命令对大小写敏感要么LOADADDR不一致。5. 常见问题与排查实录5.1 编译阶段的典型报错U-Boot和内核编译期的问题绝大多数集中在工具链和依赖库上。我整理了一张速查表按实际遇到的频率排序报错现象根本原因解决办法arm-linux-gnueabihf-gcc: command not found工具链没装或不在PATH里重新安装交叉编译器确认CROSS_COMPILE已导出mkimage: command not found缺少u-boot-tools安装u-boot-tools包scripts/extract-cert.c: 致命错误openssl/...缺少openssl开发头文件安装libssl-dev老版本内核还需opensslError: /usr/bin/arm-linux-gnueabihf-gcc: 命令不存在环境变量前缀错误检查CROSS_COMPILE是否以-结尾gcc: error: unrecognized command line option工具链版本过新换用厂商推荐的gcc版本dtc: command not found缺少设备树编译器安装device-tree-compiler其中工具链版本和openssl这两个问题是最隐蔽的。我曾经为了编一个老版本内核被scripts/extract-cert.c的报错卡了两个小时一开始以为是源码问题后来发现纯粹是本机libssl版本太新内核里的证书解析代码和它不匹配。当时解决办法不是去改源码而是装了老版本libssl-dev才过的。所以遇到编译报错先系统性检查编译环境再怀疑源码。5.2 启动阶段的问题定位顺序板子上电后如果什么都没有不要慌按这个顺序排查第一看串口有没有U-Boot输出。如果串口完全没动静很可能BootROM就没找到U-Boot问题出在启动模式引脚或镜像烧写位置。对照芯片手册检查Boot Mode拨码开关确认选择了SD卡启动同时回头检查dd命令的seek偏移。有条件的话用逻辑分析仪或者示波器抓一下SD卡CLK上有没有read动作能快速区分是BootROM没工作还是U-Boot崩了。第二U-Boot有输出但内核起不来。常见表现是打印到Starting kernel ...后就没有后续。这种情况优先怀疑设备树和内核不匹配或者加载地址不对。重新检查fatload地址和bootm参数是否一致以及uImage编译时指定的LOADADDR是否和U-Boot加载地址相同。第三内核已经开始启动但卡在某个驱动或挂载根文件系统失败。看最后一条日志如果是VFS: Cannot open root device绝大部分是bootargs里的root设备名错误或者rootfs没有真正放到那个分区。我在SC589平台上遇到过一种更隐蔽的情况内核启动后mmcblk0设备注册顺序变了本来的mmcblk0p2变成了mmcblk1p2加rootwait也没用。最终解决办法是在/dev里通过分区UUID来指定根设备root/dev/disk/by-uuid/xxxx彻底绕开设备名顺序问题。5.3 串口只有“花屏”或乱码的排查串口输出乱码或者全是0x00在SC589这类芯片上首先要检查波特率是否匹配。很多板卡的BootROM阶段波特率可能是固定的但U-Boot初始化串口后会把波特率切成环境变量里的值。如果baudrate变量设成了115200而终端软件开着57600就会看到一片乱码。另一个容易忽略的是串口电平。Cortex-A5的调试串口一般是TTL电平如果你的USB转串口模块是RS232电平轻则通信不稳重则烧掉串口引脚。板上调试一定先确认电平标准再去怀疑代码。5.4 网络启动调试的小技巧如果每次改内核都要插拔SD卡效率太低。U-Boot支持TFTP网络启动调试阶段强烈建议用这个方案把uImage和dtb放在PC的TFTP服务器目录里U-Boot通过网线直接拉取。setenv ipaddr 192.168.1.10 setenv serverip 192.168.1.1 setenv bootcmd tftp 0x80008000 uImage; tftp 0x81000000 sc589-ezkit.dtb; bootm 0x80008000 - 0x81000000 saveenvipaddr是板子的IPserverip是你PC的IP。这样每次编译完内核只需拷贝到TFTP目录再复位板子即可省掉烧录SD卡的重复劳动。我第一次在SC589上调通网络启动后后续所有的内核配置迭代都走TFTP只有最终定型才去烧SD卡。6. 串起完整流程后的几点体会整套配置编译流程走通之后回头看最核心的其实不是哪条命令而是对引导链路的理解。整条链路上任何一个环节的地址、格式、偏移对不上系统就跑不到你预期的状态。我建议每次动手前都在纸上把下面这些信息写清楚BootROM从哪个介质找U-Boot、U-Boot从哪个分区加载内核和DTB、内核的LOADADDR是多少、根文件系统挂在哪个设备。四个信息对齐剩下的编译和烧录就是体力活。最后分享一个个人习惯每次编译都从make distclean开始每次烧录前都校验镜像文件大小是否正确。看似多余但恰恰是这两步能避免95%的“明明步骤都对但就是跑不起来”的灵异问题。U-Boot和内核编译不像应用层开发错误往往发生在晦涩的底层细节里把基础动作做成肌肉记忆才是效率最高的做法。SC589这套流程跑通后再遇到同系列其他芯片你会发现整个链路完全通用剩下的只是换defconfig和DTS文件而已。
阅读完成 · 觉得有帮助?
咨询建站