跳转至

CDK 启动代码精简

阅读信息
阅读时间:7 分钟 字符数:3124 有效代码行数:88

本文记录使用 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 运行时初始化且不启用中断的专用程序。普通固件不应直接套用。

标准启动流程

标准启动流程为:

Reset_Handler
    -> 设置 PSR
    -> 设置 VBR
    -> 设置 SP
    -> 调用 __main
    -> 初始化 .bss/.data 和 C 运行时
    -> 调用 main

原始汇编入口:

.import __main

lrw a0, __main
jsr a0

链接结果中,Obj/__rt_entry.o 占用 240 字节代码空间。

原始向量表包含完整的内核异常、保留项和外部中断入口,共占用 188 字节。

直接调用 main()

修改导入符号

修改前:

.import __main
.import SystemInit

修改后:

.import SystemInit
.import main

SystemInit 当前未调用,保留导入声明不会生成代码。

修改复位入口

修改前:

lrw a0, __main
jsr a0

修改后:

lrw a0, main
jsr a0

精简后的启动流程:

Reset_Handler
    -> 设置 PSR
    -> 设置 VBR
    -> 设置 SP
    -> 直接调用 main

工程已启用链接垃圾回收:

-Wl,--gc-sections

由于没有代码继续引用 __main,链接器会移除 __rt_entry.o 的 240 字节代码。

最新 map 显示:

Removing .text(Obj/__rt_entry.o), (240 bytes)
Obj/__rt_entry.o: Code = 0

适用限制

绕过 __main 后,不再自动执行:

  • .bss 清零;
  • .data 从加载区复制到运行区;
  • C/C++ 构造函数;
  • 其他 C 运行库初始化。

当前 CRC 程序可以这样处理,因为 map 中普通 RW Data 和 ZI Data 均为 0,CRC 表位于只读区域,运行参数通过 CKLINK 在启动前写入固定 SRAM 地址。

后续若增加以下代码,必须恢复 __main,或自行实现初始化:

static uint32_t counter;
uint32_t state = 1U;

第一项依赖 .bss 清零,第二项依赖 .data 复制。

精简向量表

原始向量表

原始启动文件包含:

  • Reset 向量;
  • 内核同步异常向量;
  • 保留向量;
  • 外部中断向量;
  • 若干芯片相关固定值。

原始 .exp_table 大小为:

0xBC = 188 字节

精简后的向量表

当前程序由 CKLINK 加载和启动,运行期间不启用中断,并由外部保证读取地址有效且四字节对齐。因此只保留 Reset 向量:

$d:
.align 10
ckcpu_vsr_table:
    .long Reset_Handler

$t:

精简后的 .exp_table 大小为:

0x04 = 4 字节

相比原始向量表节省:

188 - 4 = 184 字节

适用条件

精简向量表要求:

  • 通过 CKLINK 加载并启动程序;
  • 不依赖 BootROM 解析完整向量表;
  • 不开启普通中断或快速中断;
  • 不触发未对齐、非法指令或访问错误等同步异常;
  • CRC 的 START、END 和长度始终合法。

若发生异常或中断,CPU 将无法从精简向量表取得有效处理入口,程序行为不可预期。

后续需要中断或异常保护时,必须恢复对应向量项及处理函数。

Mapping symbol

$d: 和 $t: 是 C-SKY 工具链使用的 mapping symbol(映射符号):

$d:    /* 后续内容为数据 */
$t:    /* 后续内容为代码 */

它们用于帮助反汇编器和调试器区分向量数据与机器指令。

Mapping symbol 具有以下特点:

  • 不生成机器指令;
  • 不占用 BIN 空间;
  • 不影响执行速度;
  • 注释后不会减小代码;
  • 建议保留。

栈与堆

当前启动文件配置:

#define StackSize 0x100
#define HeapSize  0x10

内存布局:

区域 地址范围 大小
Stack 0x20007C00 - 0x20007CFF 256 字节
Heap 0x20007D00 - 0x20007D0F 16 字节

当前 CRC 最大正常调用链的栈占用约为:

main               20 字节
crc_update_byte    16 字节
reverse_nibble      0 字节
合计约             36 字节

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() 内部包含无限循环,正常情况下不会返回;末尾死循环用于处理意外返回。

代码尺寸对比

精简前:

ER_ROM1:       724 字节(0x2D4)
向量表:        188 字节
__rt_entry.o:  240 字节

精简后:

ER_ROM1:       296 字节(0x128)
向量表:          4 字节
__rt_entry.o:    0 字节

减少量:

724 - 296 = 428 字节
减少约 59.1%

精简后组成:

组成 大小
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 运行时初始化时,将导入和调用改回:

.import __main

lrw a0, __main
jsr a0

需要启用中断或异常处理时,还应恢复完整向量表、相关 .global/.weak 声明以及对应处理函数。

结论

在当前使用条件下,绕过 __main 并仅保留 Reset 向量,可将 ER_ROM1 从 724 字节缩减到 296 字节,减少约 59.1%。一旦程序开始依赖 .bss、.data、构造函数、中断或异常处理,就必须恢复相应的运行时初始化和向量入口。

相关内容