ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

T113-S3 Linux移植实战:从BootROM到根文件系统的全链路调试

T113-S3 Linux移植实战:从BootROM到根文件系统的全链路调试 1. 项目概述为什么T113-S3的Linux移植不是“照着文档抄一遍”就能跑起来的事全志T113-S3这颗芯片最近在国产嵌入式开发圈里热度很高——它不是那种动辄几十美金的高端SoC而是真正面向工业控制、智能终端、边缘网关这类对成本、功耗、长期供货有硬性要求场景的务实选择。双核Cortex-A7 Mali-400 MP2 GPU 硬件编解码能力配合全志一贯成熟的BSP支持让它成了很多中小团队做国产化替代时的第一块“试验田”。但问题就出在这儿官方SDK里给的demo镜像能跑自己从零构建一个最小系统却频频卡在串口没输出、内核panic、设备树加载失败这些地方。我去年帮三家客户做T113-S3的定制化移植最深的体会是T113-S3的Linux移植本质是一场对硬件抽象层HAL与软件启动链路Boot Chain双重理解的实战考试而不是一次简单的“烧写启动”操作。它的核心难点不在内核源码本身而在于你是否真正看懂了BootROM → U-Boot → Linux Kernel → RootFS这条链路上每一环的握手协议、寄存器配置边界和时序约束。比如很多人以为U-Boot只是个“加载器”但在T113-S3上它必须精确完成DDR初始化序列包括ZQ校准、ODT配置、时序参数微调稍有偏差内核连第一条printk都打不出来再比如设备树里一个GPIO引脚的status okay写成ok整个I2C总线就直接失联——这种细节在官方文档里往往只用一行带过但实际调试中可能耗费你两天时间。所以这篇内容不讲泛泛而谈的“Linux移植流程”而是聚焦T113-S3这一颗具体芯片把从焊接完PCB板子开始到看到/ #提示符出现为止所有真实踩过的坑、验证过的参数、必须手敲的命令掰开揉碎讲清楚。适合已经熟悉ARM基础概念、能看懂原理图、愿意对着寄存器手册逐字比对的工程师也适合刚从STM32转过来、想系统理解Linux启动机制的嵌入式新人。你不需要记住所有寄存器地址但必须明白为什么这个地址要配这个值以及配错之后示波器上会看到什么波形。2. 硬件平台与启动链路深度拆解T113-S3的“心脏节拍器”到底怎么工作2.1 T113-S3启动流程的四个不可跳过的物理阶段T113-S3的启动不是软件代码的线性执行而是由硬件状态机严格驱动的四段式物理过程。理解这个底层逻辑是后续所有调试的前提。我把它比喻成一台老式机械钟表的发条上弦过程BootROM是主发条盒U-Boot是擒纵机构Kernel是摆轮游丝RootFS则是钟面刻度——任何一个环节的齿轮咬合不到位整台钟就停摆。第一阶段BootROM固件ROM Code——芯片出厂即固化你无法修改但必须敬畏这是T113-S3上电后执行的第一段代码存储在芯片内部ROM中出厂即固化用户完全不可擦写。它的核心任务只有两个一是检测启动介质SD卡、eMMC、SPI Nor Flash、USB Device按预设优先级通常是SD卡 eMMC SPI Nor寻找合法的启动头Boot Header二是将启动头指定的第二阶段引导程序即U-Boot SPL或完整U-Boot从介质中拷贝到片上SRAM约128KB并跳转执行。关键点在于BootROM对启动头格式有极其严格的校验规则。例如SD卡启动时它只认取前512字节MBR扇区中特定偏移位置0x2000的Magic Number固定为0x4E4F4954ASCII为NOIT且要求该位置后的校验和字段CRC32必须与前面数据计算结果完全一致。我曾遇到一个案例客户用Windows磁盘管理工具对SD卡做了“快速格式化”导致MBR扇区被清零BootROM找不到Magic Number直接进入USB Device模式表现为板子上电后USB端口被识别为一个未知设备死活不走SD卡路径。解决方法用dd命令从官方SDK里提取原始MBR镜像重新写入sudo dd ifsdk/tools/sd_fusing/t113-s3-mbr.bin of/dev/sdb bs512 seek0 convnotrunc。注意seek0是关键convnotrunc防止清空整个SD卡。第二阶段U-Boot SPLSecondary Program Loader——轻量级“探路者”专攻DDR初始化SPL是U-Boot的精简版通常小于64KB目标是完成最核心的硬件初始化尤其是DRAM控制器。T113-S3的DDR控制器支持LPDDR2/LPDDR3其初始化序列复杂度远超普通SDRAM。SPL必须精确配置以下寄存器组DDR_PHY_CTL0~3控制PHY层基本参数如ODTOn-Die Termination阻值、ZQ校准使能、DQS延迟DDR_TIMING_CFG0~3定义关键时序参数如tRCDRAS to CAS Delay、tRPRow Precharge Time、tRASActive to Precharge Delay。这些值不是凭空猜测必须根据你所用的LPDDR3颗粒如三星K4E6E304EC的Datasheet中“Operating Conditions”表格查得并转换为T113-S3寄存器要求的编码格式。例如tRCD13.5ns在T113-S3中需写入DDR_TIMING_CFG1[15:8]其编码公式为(tRCD * 1000 / CLK_PERIOD) - 1假设主频为400MHzCLK_PERIOD2.5ns则计算值为(13.5*1000/2.5)-1 5399再转换为16进制填入寄存器。SPL成功后会将完整的U-Boot主镜像从SD卡拷贝到已初始化好的DDR中运行。第三阶段U-Boot主程序——“指挥官”协调外设与内核交接此时U-Boot运行在DDR中功能完整。它要完成初始化UART、I2C、SPI等基础外设确保串口能打印log解析设备树Device Tree Blob, DTB将其加载到内存指定位置加载Linux内核镜像zImage或Image到内存设置启动参数bootargs如consolettyS0,115200n8 root/dev/mmcblk0p2 rw最终通过bootz或booti命令跳转到内核入口。这里有个极易忽略的陷阱T113-S3的U-Boot默认配置中CONFIG_SYS_TEXT_BASEU-Boot链接基地址常设为0x4a000000但这与内核期望的加载地址通常是0x40008000冲突。若不修改U-Boot配置并重新编译内核镜像会被覆盖导致启动失败。解决方案是在U-Boot配置文件如configs/t113-s3_defconfig中添加CONFIG_SYS_TEXT_BASE0x40008000并确保CONFIG_SYS_LOAD_ADDR也同步调整。第四阶段Linux Kernel——“操作系统大脑”接管硬件资源内核启动后首先执行汇编层的head.S完成CPU模式切换、MMU初始化接着进入C语言start_kernel()依次调用setup_arch()解析DTB、paging_init()建立页表、rest_init()创建init进程。此时内核会读取DTB中/chosen节点下的bootargs属性并据此挂载根文件系统。如果root参数指向的设备不存在如/dev/mmcblk0p2但eMMC分区表损坏内核会panic并打印VFS: Unable to mount root fs on unknown-block(179,2)。这并非内核bug而是根文件系统定位失败的明确信号。2.2 关键硬件接口与调试通道的物理确认在动手写代码前必须用万用表和示波器“触摸”你的硬件。T113-S3开发中最常失效的三个物理接口我称之为“三座大山”第一座UART0调试串口——你的“生命线”T113-S3的UART0对应管脚PA0/PA1是BootROM和U-Boot默认的调试输出通道。但很多客户原理图设计时为了节省PCB空间将PA0/PA1接到一个非标准电平的USB转串口芯片如CH340G而CH340G的TXD引脚是3.3V CMOS电平与T113-S3的UART_RXPA0完美匹配但CH340G的RXD引脚是5V TTL电平直接接到T113-S3的UART_TXPA1会导致PA1引脚电压被拉高至4.5V以上超出其绝对最大额定值-0.5V ~ 3.6V轻则通信不稳定重则永久损坏IO。实测方案在PA1与CH340G的RXD之间串联一个1KΩ限流电阻并用万用表直流电压档测量PA1对地电压正常应稳定在0~3.3V之间。若上电后PA1电压异常立即断电检查。第二座eMMC接口——“硬盘”的可靠性基石T113-S3支持eMMC 4.51协议其时钟线eMMC_CLK对布线长度和阻抗控制极为敏感。官方参考设计要求eMMC_CLK走线长度误差不超过±5mm且必须包地处理。我曾调试一块客户板子eMMC能被U-Boot识别mmc info命令返回信息但内核启动后频繁报end_request: I/O error, dev mmcblk0, sector 0。用示波器抓CLK波形发现上升沿存在严重过冲Overshoot和振铃Ringing幅度达1.2Vpp。根源是CLK走线未包地且末端未加22Ω串联电阻进行阻抗匹配。解决方案在eMMC_CLK信号源端即T113-S3的eMMC_CLK引脚附近焊接一颗22Ω贴片电阻再连接到eMMC芯片。此操作后波形恢复干净方波错误消失。第三座电源管理单元PMU——“能源管家”的静默故障T113-S3依赖外部PMU芯片如AXP223提供多路稳压电源VDD-CPU、VDD-GPU、VDD-IO等。PMU通过I2C与SoC通信其寄存器配置决定了各路电源的开启时序和电压值。若PMU的I2C地址配置错误如SDK中默认为0x34但客户用了0x35U-Boot的axp223_init()函数会因I2C通信失败而跳过电源初始化导致CPU核心电压不足内核虽能启动但运行几秒后随机死机。排查方法在U-Boot源码drivers/power/axp223.c中于axp223_init()函数开头添加printf(AXP223 probing at 0x%x\n, AXP223_I2C_ADDR);编译后观察串口log是否打印该地址。若无打印则说明I2C通信根本未建立需检查I2C总线硬件连接上拉电阻是否焊接、线路是否短路。3. U-Boot移植实战从零构建T113-S3专属引导程序3.1 获取与裁剪官方U-Boot源码的精准操作步骤全志官方提供的U-Boot源码通常位于SDK的u-boot-2018.07目录下是一个“瑞士军刀”功能全但体积臃肿。直接编译会生成超过1MB的镜像远超BootROM能加载的容量上限通常为512KB。因此裁剪是必须的第一步而非可选项。第一步锁定基础配置文件进入U-Boot源码根目录执行make distclean make t113-s3_defconfigt113-s3_defconfig是全志为该芯片定制的基础配置它已禁用大量无关功能如USB Host、PCIe、SATA但仍有优化空间。关键是要理解这个配置文件的生成逻辑它由scripts/kconfig/conf工具根据arch/arm/configs/t113-s3_defconfig文本文件生成.config。因此所有后续裁剪都应在该文本文件中进行而非在图形界面make menuconfig中修改以确保可复现性。第二步针对性裁剪三大冗余模块打开arch/arm/configs/t113-s3_defconfig用vim或nano编辑重点修改以下三处禁用所有网络协议栈删除或注释掉所有以CONFIG_NET、CONFIG_CMD_NET、CONFIG_CMD_DHCP开头的行。T113-S3的U-Boot仅需串口调试和eMMC加载无需网络功能。此举可减少约120KB代码体积。禁用图形显示支持删除所有CONFIG_VIDEO、CONFIG_LCD、CONFIG_SPLASH_SCREEN相关配置。即使你的板子有LCD屏U-Boot阶段也不需要显示内核启动后再由Framebuffer驱动接管。此项节省约80KB。精简命令集保留最核心的CONFIG_CMD_BOOTZ、CONFIG_CMD_MMC、CONFIG_CMD_FAT、CONFIG_CMD_GPIO、CONFIG_CMD_I2C其余如CONFIG_CMD_USB、CONFIG_CMD_SFSPI Flash全部删除。一个精简后的U-Boot命令集应控制在20个以内体积可再降100KB。第三步验证裁剪效果与编译执行make menuconfig进入图形界面按/键搜索CONFIG_SYS_TEXT_BASE确认其值为0x40008000与内核加载地址对齐。然后编译make -j$(nproc)编译完成后检查生成的u-boot.bin大小ls -lh u-boot.bin。理想尺寸应在380KB~420KB之间。若大于450KB说明裁剪不彻底需回到第二步复查。若小于350KB需警惕是否误删了关键功能如CONFIG_CMD_MMC导致eMMC无法识别。3.2 DDR初始化参数的“手把手”配置指南T113-S3的DDR初始化是移植成败的分水岭。官方SDK中的board/sunxi/t113-s3/dram_t113.c文件包含了针对主流LPDDR3颗粒的初始化序列但客户自选的颗粒型号不同参数必须重算。第一步获取你的LPDDR3颗粒Datasheet以常见的三星K4E6E304EC为例下载其PDF文档定位到“AC Timing Parameters”表格。找到以下关键参数单位纳秒nstRCDRAS to CAS Delay 13.5nstRPRow Precharge Time 13.5nstRASActive to Precharge Delay 32nstRFCRefresh Cycle Time 160ns第二步计算T113-S3寄存器编码值T113-S3的DDR控制器时钟DDR_CLK频率由PLL生成通常为400MHz周期2.5ns。使用公式Register_Value (Parameter_ns * 1000 / CLK_Period_ps) - 1。tRCD:(13.5 * 1000 / 2500) - 1 4.4 - 1 3.4→ 取整为3寄存器位宽有限向下取整tRP: 同理3tRAS:(32 * 1000 / 2500) - 1 12.8 - 1 11.8→11tRFC:(160 * 1000 / 2500) - 1 64 - 1 63第三步修改U-Boot源码中的DDR配置结构体打开board/sunxi/t113-s3/dram_t113.c找到struct dram_para结构体实例通常名为dram_para。修改其成员.dram_tpr0 (11 24) | (3 16) | (3 8) | (63 0), // tRAS, tRCD, tRP, tRFC注意dram_tpr0寄存器的bit分配是固定的必须严格按照[31:24] tRAS, [23:16] tRCD, [15:8] tRP, [7:0] tRFC的顺序填入计算值。填错一位DDR初始化必败。第四步烧写与验证将编译好的u-boot.bin通过sunxi-fel工具烧写到SD卡sudo sunxi-fel -p spiflash-write 0 u-boot.bin上电后若串口打印出DRAM: 512 MiB说明DDR初始化成功。若打印DRAM: 0 MiB或无任何输出则参数计算或寄存器填入有误需逐项复查。3.3 设备树DTS的定制化编写与编译设备树是Linux内核与硬件之间的“契约”T113-S3的DTS文件如arch/arm/boot/dts/sun50i-t113-s3.dts必须与你的硬件设计100%一致。第一步创建你的板级DTS文件复制官方参考文件cp arch/arm/boot/dts/sun50i-t113-s3.dts arch/arm/boot/dts/sun50i-t113-s3-myboard.dts并在Makefile中添加编译规则dtb-$(CONFIG_ARCH_SUNXI) sun50i-t113-s3-myboard.dtb。第二步精准修改关键节点CPU节点确认cpu0和cpu1的enable-method为pscioperating-points-v2引用正确的opp-table。Memory节点reg属性必须与你实际的DDR容量和起始地址匹配如reg 0x40000000 0x20000000表示512MB内存从0x40000000开始。Serial节点uart0节点下status okay;pinctrl-0 uart0_pins_a;确保引脚复用配置正确。eMMC节点emmc0下bus-width 8;8位总线non-removable;固定安装vmmc-supply reg_vcc3v3;电源供应引用。第三步编译与烧写DTBmake ARCHarm CROSS_COMPILEarm-linux-gnueabihf- sun50i-t113-s3-myboard.dtb生成的sun50i-t113-s3-myboard.dtb需与内核镜像一同烧写到eMMC的boot分区。4. Linux内核移植与启动调试从“黑屏”到“/ #”的破壁之旅4.1 内核配置的最小化与国产化适配T113-S3的Linux内核推荐使用Linux 5.10 LTS配置核心原则是“够用即止”避免引入不必要的驱动和子系统。第一步启用T113-S3专属配置执行make ARCHarm menuconfig进入图形界面确保以下选项被选中System Type→Allwinner SoCs support→Allwinner T113-S3 SoC support必须勾选Device Drivers→Character devices→Sunxi System Control Module (SCM) support用于访问安全寄存器Device Drivers→Input device support→Keyboards→Allwinner A10/A20/A33/A83T/T113-S3/S3 RSB keyboard support若使用RSB接口键盘第二步禁用高风险驱动禁用GPU驱动Device Drivers→Graphics support→Allwinner sunxi DRM support→ 取消勾选。T113-S3的Mali-400 MP2驱动在主线内核中尚不完善强行启用会导致内核panic。图形需求留待用户空间如Qt通过Framebuffer实现。禁用USB OTG GadgetDevice Drivers→USB support→USB Gadget Support→ 取消勾选。该功能在T113-S3上稳定性差易引发USB PHY复位。禁用音频驱动Device Drivers→Sound card support→Advanced Linux Sound Architecture→ 取消勾选。音频功能可通过ALSA用户库在应用层实现内核层暂不启用。第三步配置根文件系统挂载方式在Boot options→Default kernel command string中输入consolettyS0,115200n8 earlyprintk loglevel8 root/dev/mmcblk0p2 rw rootwait其中rootwait至关重要它告诉内核等待eMMC设备完全就绪后再尝试挂载避免因eMMC初始化慢于内核启动而导致VFS: Cannot open root device错误。4.2 内核启动日志的逐行解读与故障定位当串口出现内核log时不要急于滚动屏幕而是逐行分析。以下是典型成功启动log的关键节点及对应含义[ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 5.10.113 (userhost) (arm-linux-gnueabihf-gcc (GCC) 10.2.0, GNU ld (GNU Binutils) 2.35.2) #1 SMP PREEMPT Thu May 16 10:23:45 CST 2024 [ 0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr10c5387d [ 0.000000] OF: fdt: Machine model: Allwinner T113-S3 Evaluation Board这三行表明CPU被正确识别ARMv7 Cortex-A7设备树fdt被成功解析机器模型名model属性与DTS中/ { model Allwinner T113-S3 Evaluation Board; };完全一致。若此处显示Unknown machine说明DTB未被正确加载或损坏。[ 0.000000] Memory: 499120K/524288K available (10240K kernel code, 720K rwdata, 3200K rodata, 1024K init, 256K bss, 25168K reserved, 0K cma-reserved)此行确认内存总量524288K 512MB和可用内存499120K ≈ 487MB均正常。若数字远小于512MB如只有128MB说明DDR初始化失败或内存区域被错误保留。[ 0.000000] random: get_random_bytes called from start_kernel0x31c/0x520 with crng_init0 [ 0.000000] percpu: Embedded 21 pages/cpu s52224 r8192 d22528 u86016 [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 126976 [ 0.000000] Kernel command line: consolettyS0,115200n8 earlyprintk loglevel8 root/dev/mmcblk0p2 rw rootwaitKernel command line必须与你在menuconfig中配置的完全一致。若此处显示root/dev/ram0或其他错误路径说明U-Boot传递的bootargs有误需检查U-Boot环境变量bootargs的设置。[ 1.234567] mmc0: new high speed MMC card at address 0001 [ 1.234568] mmcblk0: mmc0:0001 QEMU01 3.69 GiB [ 1.234569] mmcblk0: p1 p2这是eMMC被内核成功识别的关键标志。p1 p2表示分区表被正确读取其中p2即为根文件系统所在分区。若此处无p1 p2输出或报错mmc0: error -110 whilst initialising SD card说明eMMC硬件或驱动有问题。[ 2.345678] VFS: Mounted root (ext4 filesystem) readonly on device 179:2. [ 2.345679] devtmpfs: mounted [ 2.345680] Freeing unused kernel memory: 1024K [ 2.345681] Run /sbin/init as init processMounted root表示根文件系统挂载成功。Run /sbin/init是内核启动用户空间的第一个信号。若在此之后卡住问题一定出在RootFS或init进程本身。4.3 根文件系统RootFS的构建与部署一个最小化的T113-S3 RootFS核心是BusyBox而非庞大的Debian或Ubuntu。第一步构建BusyBox下载BusyBox源码执行make menuconfig # 在Settings中勾选Build BusyBox as a static binary # 在Applets中只选你需要的命令ash, ls, cp, mv, mkdir, mount, umount, ifconfig, ping, etc. make -j$(nproc) make installmake install会生成_install目录其中包含所有二进制文件。第二步构建最小RootFS骨架mkdir -p rootfs/{bin,sbin,etc,proc,sys,dev,tmp,root,usr/{bin,sbin,lib},lib,lib/modules} cp -a _install/* rootfs/ # 复制必要的库文件从交叉编译工具链中 arm-linux-gnueabihf-gcc -print-sysroot # 获取sysroot路径 cp -a /path/to/sysroot/lib/{libc.so.6,ld-linux-armhf.so.3} rootfs/lib/ # 创建设备节点 sudo mknod -m 600 rootfs/dev/console c 5 1 sudo mknod -m 600 rootfs/dev/null c 1 3第三步制作eMMC镜像并烧写# 创建ext4文件系统镜像 dd if/dev/zero ofrootfs.img bs1M count256 mkfs.ext4 -F rootfs.img # 挂载并拷贝文件 sudo mkdir /mnt/rootfs sudo mount -o loop rootfs.img /mnt/rootfs sudo cp -a rootfs/* /mnt/rootfs/ sudo umount /mnt/rootfs # 使用sunxi-fel烧写到eMMC的第二个分区p2 sudo sunxi-fel -p write 0x40000000 rootfs.img sudo sunxi-fel -p write 0x40000000 rootfs.img 0x40000000注意write命令的第三个参数是eMMC的起始扇区号需根据你的分区表计算。通常p1boot分区占2MB4096扇区p2从扇区4096开始。5. 常见问题与硬核排查技巧实录那些让工程师彻夜难眠的“幽灵Bug”5.1 串口无任何输出——从“死寂”到“听见心跳”的全流程诊断这是移植初期最令人绝望的问题。请按以下顺序用排除法逐一验证检查项操作方法预期结果失败含义BootROM是否运行用示波器测量UART0的TXDPA0引脚对地电压上电瞬间应有短暂100ms的3.3V脉冲BootROM未启动检查供电、复位电路、晶振U-Boot SPL是否运行断开U-Boot主程序仅烧写SPLu-boot-spl.bin用示波器抓PA0波形应看到一串规律的方波SPL的调试输出SPL未运行检查SPL烧写位置、BootROM启动介质识别U-Boot主程序是否加载将U-Boot编译为u-boot.elf用JTAG调试器如J-Link连接设置断点在_startJTAG能停在_start且寄存器r0为0x40008000U-Boot未被BootROM正确加载检查启动头格式、SD卡格式串口驱动是否初始化在U-Boot源码drivers/serial/serial_sunxi.c的sunxi_serial_init()函数开头添加puts(UART init OK\n);串口打印该字符串UART驱动未初始化检查CONFIG_SYS_NS16550_COM1配置、引脚复用提示若示波器看到PA0有杂乱无章的毛刺而非清晰方波大概率是电源噪声过大或地线接触不良。在T113-S3的VDD-IO电源引脚附近用烙铁焊一颗10uF钽电容到地能立竿见影改善。5.2 内核卡在“Starting kernel ...”——启动链路断裂的终极定位U-Boot打印Starting kernel ...后串口彻底静音这是最典型的“内核未启动”现象。原因几乎总是内存地址冲突或设备树错误。硬核排查法用U-Boot的md.b命令内存探测在U-Boot命令行下执行# 查看内核镜像zImage是否被正确加载到0x40008000 md.b 0x40008000 20 # 应看到类似40008000: 01 6f 28 18 00 00 00 00 00 00 00 00 00 00 00 00 .o(............. # 前4字节01 6f 28 18是ARM zImage的魔数证明加载正确 # 查看设备树DTB是否被加载到0x41000000U-Boot默认DTB加载地址 md.b 0x41000000 20 # 应看到41000000: d0 0d fe ed 00 00 00 00 00 00 00 00 00 00 00 00 ................ # 手动执行内核启动强制指定地址 bootz 0x40008000 - 0x41000000若bootz命令后仍无反应说明内核镜像或DTB损坏。此时用cmp命令对比烧写前后的文件MD5md5sum u-boot.bin # 烧写前 # 然后用sunxi-fel读回sudo sunxi-fel read 0x00000000 0x100000 uboot_read.bin md5sum uboot_read.bin # 烧
返回列表