用C手搓ARM启动代码

/ 0评 / 0

最近淘到一个瑞萨的板子,MCU是R7FA6M5BH,官方推荐用e2studio或rasc,用rasc生成的Keil等工程每次编译都要调用rasc不知道干嘛,发布时候也特别麻烦,决定手戳一个最小启动代码.

完整项目 https://github.com/nickfox-taterli/embedfire-ra6m5

首先在LD文件指定入口点是复位.这是ARM定义好的.

ENTRY(Reset_Handler)

大家都见过STM32的启动代码是汇编写的,用C可以不可以呢,答案是肯定可以,但是有限制,比如一开始时不能传递参数(毕竟堆栈还没准备好).

// 复位处理函数
void Reset_Handler(void)
{
    /* 初始化系统 */
    SystemInit();

    /* 调用用户主函数 */
    extern void main(void);
    main();

    /* 主函数不应返回,如果返回则进入死循环 */
    while (1) {}
}

定义中断向量表,因为瑞萨的用户中断都是自定义映射,所以只写ARM内置中断,第一个是栈指针,手册上写了,因为栈是往小地址生长,所以我设置为0x400是栈顶,就是有0x400这么大的堆栈了.

// 主栈空间定义(1KB大小,8字节对齐,放在stack段)
static uint8_t g_main_stack[0x400] __attribute__((aligned(8))) __attribute__((section("stack"))) __attribute__((__used__));

__attribute__((used)) const exc_ptr_t __Vectors[] __attribute__((section(".fixed_vectors"))) __attribute__((__used__)) =
{
    (exc_ptr_t) (&g_main_stack[0] + 0x400),  // 初始栈指针
    Reset_Handler,                           // 复位处理函数
    NMI_Handler,                             // NMI中断处理函数
    HardFault_Handler,                       // 硬件错误处理函数
    MemManage_Handler,                       // 内存管理错误处理函数
    BusFault_Handler,                        // 总线错误处理函数
    UsageFault_Handler,                      // 用法错误处理函数
    SecureFault_Handler,                     // 安全错误处理函数
    0,                                       // 保留
    0,                                       // 保留
    0,                                       // 保留
    SVC_Handler,                             // SVC调用处理函数
    DebugMon_Handler,                        // 调试监控处理函数
    0,                                       // 保留
    PendSV_Handler,                          // PendSV处理函数
    SysTick_Handler,                         // SysTick处理函数
};

然后进入后就初始化堆栈,填充bss区域,调用各种C/C++构造函数.


// 系统初始化函数
void SystemInit(void)
{
    /* 启用FPU */
    SCB->CPACR = (uint32_t)(0xFU << 20);

    /* 设置向量表偏移寄存器 */
    SCB->VTOR = (uint32_t)&__Vectors;

    /* 设置主栈指针限制 */
    __set_MSPLIM((uint32_t)&g_main_stack[0]);

    /* 清零.bss段(未初始化的全局变量区) */
    extern uint32_t __bss_start__;
    extern uint32_t __bss_end__;

    uint32_t *bss_start = &__bss_start__;
    uint32_t *bss_end = &__bss_end__;
    uint32_t bss_size = (uint32_t)bss_end - (uint32_t)bss_start;

    for(uint32_t i = 0; i < bss_size; i++) {
        *((uint8_t*)bss_start + i) = 0;
    }

    /* 初始化.data段(已初始化的全局变量区) */
    extern uint32_t _sidata;
    extern uint32_t _sdata;
    extern uint32_t _edata;

    uint32_t *src = &_sidata;  /* Flash中的数据源地址 */
    uint32_t *dest = &_sdata;  /* RAM中的目标地址 */
    uint32_t size = (uint32_t)&_edata - (uint32_t)&_sdata;

    for (uint32_t i = 0; i < size; i += 4) {
        *dest++ = *src++;
    }

    /* 调用全局构造函数 */
    extern void (*__init_array_start[])(void);
    extern void (*__init_array_end[])(void);

    int32_t count = __init_array_end - __init_array_start;
    for (int32_t i = 0; i < count; i++) {
        __init_array_start[i]();
    }
}

这些段定义都在LD文件写着的,下面是一个完整例子.

/* 程序入口点 */
ENTRY(Reset_Handler)

/* 存储器定义 */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K    /* RAM:可执行/可读/可写,起始地址0x20000000,长度128KB */
  FLASH  (rx)     : ORIGIN = 0x00000000,   LENGTH = 2M      /* FLASH:只读/可执行,起始地址0x00000000,长度2MB */
}

/* 段(Section)定义 */
SECTIONS
{
  /* 启动代码存放在FLASH中 */
  .isr_vector :
  {
    . = ALIGN(4);                                          /* 4字节对齐 */
    KEEP(*(.fixed_vectors*))                               /* 固定向量表 */
    . = ALIGN(4);
  } >FLASH

  /* 程序代码和其他数据存放在FLASH中 */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* 代码段 */
    *(.text*)          /* 其他代码段 */
    *(.glue_7)         /* ARM/Thumb代码粘合段 */
    *(.glue_7t)        /* Thumb/ARM代码粘合段 */
    *(.eh_frame)       /* 异常处理帧信息 */

    KEEP (*(.init))    /* 保留初始化代码 */
    KEEP (*(.fini))    /* 保留终止代码 */

    . = ALIGN(4);
    _etext = .;        /* 定义代码结束的全局符号 */
  } >FLASH

  /* 常量数据存放在FLASH中 */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* 只读数据段(常量、字符串等) */
    *(.rodata*)        /* 其他只读数据段 */
    . = ALIGN(4);
  } >FLASH

  /* ARM异常处理表 */
  .ARM.extab () :
  {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH

  /* ARM索引表 */
  .ARM () :
  {
    . = ALIGN(4);
    __exidx_start = .;  /* 定义索引表开始 */
    *(.ARM.exidx*)
    __exidx_end = .;    /* 定义索引表结束 */
    . = ALIGN(4);
  } >FLASH

  /* 初始化数组 */
  .init_array () :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH

  /* 供启动代码初始化数据使用的符号 */
  _sidata = LOADADDR(.data);

  /* 初始化数据段(加载到FLASH但运行在RAM) */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* 定义数据段开始的全局符号 */
    *(.data)           /* 数据段 */
    *(.data*)          /* 其他数据段 */

    . = ALIGN(4);
    _edata = .;        /* 定义数据段结束的全局符号 */
  } >RAM AT> FLASH

  /* 未初始化数据段(BSS) */
  . = ALIGN(4);
  .bss :
  {
    _sbss = .;         /* 定义BSS段开始的全局符号 */
    __bss_start__ = _sbss;
    *(.bss)            /* BSS段 */
    *(.bss*)           /* 其他BSS段 */
    *(COMMON)          /* 公共符号 */

    . = ALIGN(4);
    _ebss = .;         /* 定义BSS段结束的全局符号 */
    __bss_end__ = _ebss;
  } >RAM

  /* 堆栈段(不加载) */
  .stack_dummy (NOLOAD):
  {
    . = ALIGN(8);
    /* 主堆栈 */
    KEEP(*(.stack))
  } > RAM

  /* 丢弃的段 */
  /DISCARD/ :
  {
    libc.a ( * )      /* 丢弃标准库 */
    libm.a ( * )      /* 丢弃数学库 */
    libgcc.a ( * )    /* 丢弃GCC库 */
  }

  /* ARM属性段 */
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注