`startup_gcc.S` 详细介绍(D13x):从复位到内核的完整路径 startup_gcc.S详细介绍D13xArtInChip D13x在GCC 工具链下的RISC-V 启动汇编典型为平头哥E907内核负责中断向量表、复位入口、早期硬件与 C 运行时准备最后跳入内核或main。它与链接脚本、vectors.S、boot_param_gcc.S、system.c共同构成“上电 → 跑 RT-Thread/裸机”的第一段路径。1. 在工程中的位置角色说明链接入口gcc_aic.ld中ENTRY(Reset_Handler)复位后 CPU 从Reset_Handler开始执行代码段KEEP(*startup_gcc.o(*.text*))放在.text最前保证复位代码不被优化掉向量表KEEP(*startup_gcc.o(*.vectors*))放在.data段与__global_pointer$等同在 SRAM配套文件boot_param_gcc.S保存启动参数、vectors.S默认/陷阱处理、system.cSystemInit上电后的调用链可概括为RT-Thread/FreeRTOS其它硬件复位Reset_Handlersave_boot_paramssave_boot_params_retmtvec/mtvt/gp/sp/缓存/TCM等BSS清零 reloc_private_paramsSystemInit内核类型entry → rtthread_startup等main2. 文件结构概览文件分三块.section .vectors—__Vectors中断向量表100 项Reset_Handler— 真正的启动逻辑__exit— 死循环兜底正常不应到达3. 中断向量表__Vectors第 11–118 行.section .vectors, aw, progbits .align 6 .globl __Vectors .type __Vectors, object __Vectors: .long Default_Handler /* 0 */ ... .long PendSV_Handler /* 3 */ ... .long Default_IRQHandler /* 7 */ ... /* External interrupts */ .long Default_IRQHandler /* 16 */ ...3.1 表项含义0–15系统类异常/内部事件入口多数先落到Default_Handler再由vectors.S区分 trap / NMI。3PendSV_Handler— 在KERNEL_BAREMETAL下由vectors.S实现最终进trapRTOS 场景下由内核提供真实实现。7固定为Default_IRQHandler与其它外部 IRQ 共用向量中断入口风格。16–99外部中断默认均为Default_IRQHandler。表长100 × 4 字节与 D13x CLIC 向量中断模型配合使用具体 IRQ 号与芯片手册、aic_drv_irq.c中drv_irq_vectors_init一致。3.2 与mtvt的关系后面Reset_Handler会执行la a0, __Vectors csrw mtvt, a0这是T-Head 扩展 CSRmtvt向量表基址。发生向量中断时硬件按 IRQ 号从__Vectors取处理函数地址。实际业务 IRQ 还会在SystemInit→drv_irq_vectors_init()里写入g_irqvector[]与Default_IRQHandler里的分发逻辑配合。3.3 链接放置向量表在.data可写 RAM不是只读 Flash。Bootloader/SPL 把镜像拷到 SRAM 后表在运行地址上有效若需要运行时改向量也可写本工程主要靠g_irqvector做二次分发。4.Reset_Handler逐步解析4.1 保存 Boot 参数第 129–133 行Reset_Handler: .option push .option norelax j save_boot_params save_boot_params_ret:先j save_boot_params实现在boot_param_gcc.S。SPL/Bootloader 跳转 OS 时会把a0–a7、s0–s11、sp、ra等写入boot_params_stash。若a1 ≠ 0还会把256 字节boot_arg从 SPL 拷到 OS 的boot_argDDR/资源等启动信息。完成后j save_boot_params_ret回到本文件继续。这样 OS 能保留上一阶段的启动上下文供aic_get_boot_resource()、reloc_private_params等使用。4.2 全局指针与中断控制器初配第 134–140 行la gp, __global_pointer$ .option pop la a0, Default_Handler ori a0, a0, 3 csrw mtvec, a0 la a0, __Vectors csrw mtvt, a0步骤作用la gp, __global_pointer$初始化 RISC-Vgp访问小数据/.sdata.option norelax避免早期la被错误松弛mtvec Default_Handler | 3向量模式低 2 位为 mode未走mtvt的路径先进Default_Handlermtvt __Vectors绑定向量表外部 IRQ 按表跳转Default_Handler在vectors.S里会检查mcause若为NMI(24)走.NMI_Handler否则j trap进统一异常/系统调用路径。4.3 栈指针第 142–148 行la sp, g_top_irqstack #ifdef CONFIG_THEAD_EXT_SPSWAPEN csrw mscratch, sp #endif #ifdef KERNEL_BAREMETAL la sp, g_top_normalstack #endif默认sp g_top_irqstackvectors.S里g_base_irqstackAIC_INTERRUPTSTACK_SIZE的栈顶。CONFIG_THEAD_EXT_SPSWAPEN把该栈顶写入mscratch配合 T-HeadSPSWAPEN在中断里换栈。裸机KERNEL_BAREMETAL再切到g_top_normalstack中断与主流程栈分离。4.4 使能 I/D Cache第 150–153 行la a5, icache_enable jalr a5 la a5, dcache_enable jalr a5调用system.c中的icache_enable()/dcache_enable()内部走aicos_*。SystemInit()里还会再次csi_icache_enable()/csi_dcache_enable()属于“尽早开缓存 正式初始化再确认”的常见做法。4.5 芯片相关早期硬件第 155–198 行#ifndef QEMU_RUN仅在真实芯片编译QEMU 仿真跳过。宏行为AIC_TCM_EN写mitcmcr/mdtcmcr使能 ITCM/DTCM开 SYSCFG 时钟SYSCFG 寄存器使能 TCMAIC_SRAM1_EN配置 SYSCFG 中 SRAM1 大小/使能位AIC_SRAM_S1_REG_SIZEPSRAM_UNCACHED_EN写SYSMAP_ADDR6_ATTR把 PSRAM 标为non-cacheable地址注释在源码中如 ITCM0x30040000、DTCM0x30060000、CMU/SYSCFG 基址与aic_soc.h一致。4.6 QEMU 下的.data拷贝第 201–215 行#if defined(QEMU_RUN) la a0, __erodata la a1, __data_start__ la a2, __data_end__ ... la t0, dcache_clean jalr t0 #endifQEMU 启动时可能没有 Bootloader 做LMA→VMA拷贝因此在启动代码里把已初始化数据从 rodata 末尾加载地址拷到__data_start____data_end__再dcache_clean。真机通常由 SPL 完成故用QEMU_RUN隔开。4.7 BSS 清零第 217–225 行标准 C 运行时对__bss_start____bss_end__按字写 0保证未初始化全局/静态变量为 0。4.8 私有参数重定位第 227–229 行la a5, reloc_private_params jalr a5实现在bsp/common/private_param/private_param.c从 boot 资源里解析PDAT/私有配置如 DDR 参数等拷入private_params_stash并aic_set_boot_resource()供后续驱动/内存初始化读取。4.9SystemInit第 231–234 行#ifndef __NO_SYSTEM_INIT la a5, SystemInit jalr a5 #endifsystem.c中主要做FPU、MXSTATUSTHEADISAEE、MM、MEXSTATUSSPUSHEN/SPSWAPENCLIC配置、清 pending、设向量中断属性再次开 cache、使能Machine Software用于上下文切换/软中断drv_irq_vectors_init()、aic_gtc_enable()、时钟低功耗、CoreT/SysTick非 baremetal4.10 进入内核或应用第 236–242 行#if (defined(KERNEL_RTTHREAD) || defined(KERNEL_FREERTOS)) la a5, entry jalr a5 #else la a5, main jalr a5 #endifRT-Threadentry()→rtthread_startup()kernel/rt-thread/src/components.c再建主线程跑main()。FreeRTOSentry()建任务并vTaskStartScheduler()。裸机/其它直接main()。entry/main返回后不会继续执行有用代码文件末尾__exit: j __exit为死循环。5. 与vectors.S的分工符号所在文件职责__Vectorsstartup_gcc.S硬件向量表指针数组Default_IRQHandlervectors.S外部 IRQipush、保存 FPU/上下文、查g_irqvector分发Default_Handlervectors.Smtvec入口NMI 或trapPendSV_Handlervectors.Sbaremetal软件 PendSV →trapg_top_irqstackvectors.S中断栈顶startup_gcc.S只建表、不配具体外设 ISR外设驱动通过drv_irq_register()等改g_irqvectoraic_drv_irq.c。6. 关键编译宏小结宏影响QEMU_RUN跳过 TCM/SRAM1/PSRAM 硬件初始化增加.data拷贝AIC_TCM_EN/AIC_SRAM1_EN/PSRAM_UNCACHED_EN内存子系统早期配置KERNEL_BAREMETAL使用g_top_normalstackPendSV_Handler在vectors.SKERNEL_RTTHREAD/KERNEL_FREERTOS跳转entryCONFIG_THEAD_EXT_SPSWAPEN设置mscratch__NO_SYSTEM_INIT跳过SystemInit特殊镜像/调试7. 与标准 RISC-V 启动的差异便于对照学习双 CSRmtvec带 mode 3mtvt向量表属 T-Head/E907 常见组合。Boot 参数链先save_boot_params再 C 环境适配 SPL → OS 跳转。向量表在 RAM.data且项数固定 100。SoC 寄存器初始化写在汇编里TCM/SYSMAP而不是全部放在SystemInit。内核入口统一为entry与 ARM 上-eentry习惯类似。8. 调试时可关注的点若卡在复位查 SPL 是否已拷.data、BSS 范围是否与gcc_aic.ld一致。若 IRQ 全进默认处理查mtvt是否指向__Vectors、SystemInit是否执行、drv_irq_vectors_init是否注册。若栈溢出确认AIC_INTERRUPTSTACK_SIZE与g_top_irqstack/mscratch配置。QEMU 与真机构建选项不同注意是否定义QEMU_RUN。