boot_param_gcc.S详细解析该文件是ArtInChip D13x在RISC-V 32 位GCC上的启动参数保存例程在Reset_Handler里最早执行把上一阶段BootROM / SPL经寄存器传来的信息固化到 RAM供后续 C 代码通过boot_param.c读取。1. 在启动链中的位置Reset_Handler: .option push .option norelax j save_boot_params save_boot_params_ret:流程可以概括为a0dev, a1boot_argsSPL: ep(dev, boot_arg)Reset_Handlersave_boot_params(boot_param_gcc.S)save_boot_params_retgp/mtvec/cache/...正常启动SPL 跳转应用时见boot_app.cep(dev, boot_arg);按 RISC-V 调用约定寄存器含义设计约定a0启动参数字设备、控制器、原因、镜像 ID 等可编码在低 32 位a1私有数据指针即 SPL 里struct boot_args的地址save_boot_params必须在BSS/数据段初始化、C 运行时、改栈之前执行否则 a0/a1 会被覆盖因此用纯汇编、只写已链接好的.data变量。2. 宏与符号#include rtconfig.h #define LREG lw #define SREG sw #define REGBYTES 4 .extern boot_params_stash #ifndef AIC_BOOTLOADER .extern boot_arg #endif项说明LREG/SREG固定为32 位lw/swD13x E907 类核心REGBYTES4寄存器槽步进boot_params_stash在boot_param.c定义union boot_params88 字节boot_arg256 字节的struct boot_args仅非 SPL固件链接AIC_BOOTLOADER编 SPL 时定义不声明/不拷贝boot_argSPL 自己是上一段不需要从“更上层”拷参数块3. 阶段一保存全部“启动上下文”寄存器save_boot_params: la t0, boot_params_stash SREG a0, REGBYTES * 0(t0) ... SREG ra, REGBYTES * 21(t0)与boot_param.h布局一一对应union boot_params { unsigned long regs[22]; struct { unsigned long a[8]; /* a0 ~ a7 */ unsigned long s[12]; /* s0 ~ s11 */ unsigned long sp; unsigned long ra; } r; };偏移索引寄存器典型用途0a0aic_get_boot_device()等读r.a[0]1a1aic_get_boot_resource()→ 实为 SPL 传来的boot_args源地址拷贝前2–7a2–a7保留完整调用现场8–19s0–s11被调用者保存寄存器20sp进入镜像时的栈指针21ra返回地址若需回 BootROM 等场景可恢复注意这里保存的是跳入Reset_Handler瞬间的寄存器值不是函数内部的临时值因此 a0/a1 仍是 SPL 传入的启动参数。后续 C 侧用法示例boot_param.cboot_params_stash.r.a[0]→get_boot_device()/get_boot_reason()等位域解析boot_params_stash.r.a[1]→ 资源地址与 a1 一致除非运行中aic_set_boot_resource()修改a0位域定义boot_param.h#define get_boot_device(var) ((var)0xF) #define get_boot_controller(var) (((var) 4) 0xF) #define get_boot_reason(var) (((var) 8) 0xF) #define get_boot_image_id(var) (((var) 12) 0xF)SPL 若只传dev到 a0低 4 位仍正确高字段需 SPL/BootROM 打包进 a0 才有意义。4. 阶段二把 SPL 的boot_args拷到 OS 的.data仅非 SPL#ifndef AIC_BOOTLOADER beqz a1, 2f /* copy boot_arg from SPL to OS */ la t0, boot_arg mv t1, a1 /* boot_arg size is 256 bytes */ addi t2, t1, 0xFF 1: LREG t3, (t1) SREG t3, (t0) addi t0, t0, REGBYTES addi t1, t1, REGBYTES bltu t1, t2, 1b 2: #endif4.1 为何拷贝SPL 里的boot_arg常在SPL 专用 RAM跳转到 RT-Thread/内核后该区域可能被复用或不可访问。OS 侧在boot_param.c另有全局boot_arg.data启动时从 a1 指向的源整块复制保证aic_get_boot_args()长期有效。struct boot_args256 字节struct boot_args { char image_version[16]; char reserved[240]; };SPL 在 FIT 加载时写入版本字符串如fitimage.c里strncpy到boot_arg.image_versionOS 通过aic_get_boot_args()打印或传给 USB 显示等。4.2beqz a1, 2fa1 为 0 则跳过拷贝冷启动路径、无私有数据、或上层未设置指针。4.3 拷贝循环边界t1 源地址进入时的a1t2 t1 0xFF→ 末字节地址为start 255循环条件t1 t2每次t1 4共64 次字拷贝 →256 字节。addi t2, t1, 0xFF等价于“拷贝到包含start255的那一字”与 256 字节大小一致。4.4 寄存器复用拷贝段把t0从boot_params_stash改成目标boot_argt1为源指针a1 在函数入口已用于拷贝保存后的boot_params_stash.r.a[1]仍是原始 SPL 指针与 OS 内boot_arg可能不同——读版本应走aic_get_boot_args()不要假设 a1 仍指向有效 SPL 内存。5. 返回启动代码j save_boot_params_ret跳回startup_gcc.S的save_boot_params_ret继续设置gp配合 linker 的__global_pointer$mtvec/mtvt栈、cache、TCM 等此时a0–a7 等仍保持 SPL 传入的值本函数未破坏 caller-saved 以外的逻辑——只写了内存里的 stash但若后续启动代码改 a0应读 stash 而非 live 寄存器。6. 与boot_param.c的分工文件角色boot_param_gcc.S极早、无依赖 C 库寄存器快照 可选 256B 拷贝boot_param.c定义boot_params_stash/boot_arg提供aic_get_boot_*()APIboot_param.h布局、位域宏、enum boot_device等例如enum boot_reason aic_get_boot_reason(void) { enum boot_reason reason; /* SPL use a0 */ reason get_boot_reason(boot_params_stash.r.a[0]); return reason; }void *aic_get_boot_args(void) { return (void *)boot_arg; }7. 编译变体小结配置save_boot_params行为RT-Thread / 内核镜像保存 22 个寄存器 若 a1≠0 则拷贝 256B 到boot_argAIC_BOOTLOADERSPL仅保存寄存器无boot_arg拷贝无上一段传下来的 OS 参数块8. 设计要点读代码时值得记住时机全文件是为“复位向量后第一条实质工作”服务的参数冻结不是普通库函数。双通道传参a0 为紧凑启动字a1 为可变大块元数据此处固定 256B 的boot_args。stash vs 拷贝stash 保留原始 a1SPL 地址拷贝后的boot_arg供 OS 全生命周期使用。与 g73x/d12x 等同族 BSP 同源D13x 通过#ifndef AIC_BOOTLOADER区分 SPL/OS 两套链接。