CDK 启动代码精简¶
本文记录使用 CDK 精简 C-SKY 启动代码的背景、修改方法、适用限制和验证结果。目标是在不依赖完整 C 运行时和中断向量表的前提下,减少 SRAM 中的代码占用。
背景与目标¶
CRC 程序通过 CKLINK 加载并运行,只执行固定的 CRC 扫描,不使用中断、普通全局变量初始化或完整 C 运行时。
通过精简 crt0.S,删除不需要的 C 运行时入口和中断向量,可以降低最终链接代码大小。
相关文件:
<工程根目录>/Drivers/CK801/crt0.S<工程根目录>/Drivers/GC13XX_Driver/Src/main.c<工程根目录>/CDK/Lst/GC13XX_Template.map
适用范围
本方案仅适用于通过 CKLINK 加载、无需 C 运行时初始化且不启用中断的专用程序。普通固件不应直接套用。
标准启动流程¶
标准启动流程为:
原始汇编入口:
链接结果中,Obj/__rt_entry.o 占用 240 字节代码空间。
原始向量表包含完整的内核异常、保留项和外部中断入口,共占用 188 字节。
直接调用 main()¶
修改导入符号¶
修改前:
修改后:
SystemInit 当前未调用,保留导入声明不会生成代码。
修改复位入口¶
修改前:
修改后:
精简后的启动流程:
工程已启用链接垃圾回收:
由于没有代码继续引用 __main,链接器会移除 __rt_entry.o 的 240 字节代码。
最新 map 显示:
适用限制¶
绕过 __main 后,不再自动执行:
.bss清零;.data从加载区复制到运行区;- C/C++ 构造函数;
- 其他 C 运行库初始化。
当前 CRC 程序可以这样处理,因为 map 中普通 RW Data 和 ZI Data 均为 0,CRC 表位于只读区域,运行参数通过 CKLINK 在启动前写入固定 SRAM 地址。
后续若增加以下代码,必须恢复 __main,或自行实现初始化:
第一项依赖 .bss 清零,第二项依赖 .data 复制。
精简向量表¶
原始向量表¶
原始启动文件包含:
- Reset 向量;
- 内核同步异常向量;
- 保留向量;
- 外部中断向量;
- 若干芯片相关固定值。
原始 .exp_table 大小为:
精简后的向量表¶
当前程序由 CKLINK 加载和启动,运行期间不启用中断,并由外部保证读取地址有效且四字节对齐。因此只保留 Reset 向量:
精简后的 .exp_table 大小为:
相比原始向量表节省:
适用条件¶
精简向量表要求:
- 通过 CKLINK 加载并启动程序;
- 不依赖 BootROM 解析完整向量表;
- 不开启普通中断或快速中断;
- 不触发未对齐、非法指令或访问错误等同步异常;
- CRC 的 START、END 和长度始终合法。
若发生异常或中断,CPU 将无法从精简向量表取得有效处理入口,程序行为不可预期。
后续需要中断或异常保护时,必须恢复对应向量项及处理函数。
Mapping symbol¶
$d: 和 $t: 是 C-SKY 工具链使用的 mapping symbol(映射符号):
它们用于帮助反汇编器和调试器区分向量数据与机器指令。
Mapping symbol 具有以下特点:
- 不生成机器指令;
- 不占用 BIN 空间;
- 不影响执行速度;
- 注释后不会减小代码;
- 建议保留。
栈与堆¶
当前启动文件配置:
内存布局:
| 区域 | 地址范围 | 大小 |
|---|---|---|
| Stack | 0x20007C00 - 0x20007CFF |
256 字节 |
| Heap | 0x20007D00 - 0x20007D0F |
16 字节 |
当前 CRC 最大正常调用链的栈占用约为:
256 字节栈对当前程序具有足够余量。
栈和堆属于 NOBITS 区域,只增加 RAM/BSS 占用,不增加 BIN 代码大小。
最终复位入口¶
当前关键启动代码如下:
Reset_Handler:
/* 设置 PSR,允许异常并关闭普通中断和快速中断。 */
lrw a3, 0x80000100
mtcr a3, psr
/* 设置向量表基地址。 */
lrw a3, ckcpu_vsr_table
mtcr a3, vbr
/* 设置普通栈指针。 */
lrw a3, __initial_sp
mov sp, a3
#ifdef FAST_INTERRUPT_ENABLE
psrset af
lrw r6, __initial_fast_sp
mov sp, r6
psrclr af
#endif
/* 当前程序不执行 SystemInit,也不进入 __main。 */
lrw a0, main
jsr a0
1:
br 1b
main() 内部包含无限循环,正常情况下不会返回;末尾死循环用于处理意外返回。
代码尺寸对比¶
精简前:
精简后:
减少量:
精简后组成:
| 组成 | 大小 |
|---|---|
| Reset 向量 | 4 字节 |
main |
92 字节 |
| CRC 查找表 | 64 字节 |
crc_update_byte |
60 字节 |
| Reset 启动代码 | 44 字节 |
reverse_nibble |
32 字节 |
| 总计 | 296 字节 |
构建与验证¶
修改 crt0.S 后必须执行 Clean/Rebuild,避免继续使用旧对象文件。
检查 map 文件:
Image Entry point = Reset_Handler
ER_ROM1 Size = 0x128
.exp_table = 0x04
__rt_entry.o = 0
Stack_Mem = 0x20007C00
__initial_sp = 0x20007D00
__heap_start = 0x20007D00
__heap_end = 0x20007D10
当前 map 中的关键结果:
Image Entry point : 0x200078DC
Region ER_ROM1 : Size 0x00000128
.exp_table : 0x20007800, Size 0x00000004
Reset_Handler : 0x200078DC
构建生成的封装 BIN 可能大于 296 字节。实际链接代码占用应以 map 中 ER_ROM1 的 Size 为准。
恢复标准启动¶
需要恢复 C 运行时初始化时,将导入和调用改回:
需要启用中断或异常处理时,还应恢复完整向量表、相关 .global/.weak 声明以及对应处理函数。
结论¶
在当前使用条件下,绕过 __main 并仅保留 Reset 向量,可将 ER_ROM1 从 724 字节缩减到 296 字节,减少约 59.1%。一旦程序开始依赖 .bss、.data、构造函数、中断或异常处理,就必须恢复相应的运行时初始化和向量入口。