under construction!#
Reset_Handler.sx
#
.globl Reset_Handler .text .syntax unified .align 4 .type Reset_Handler, %function .func Reset_HandlerReset_Handler: // Some bootloaders do not reset the stack pointer back to the VTOR entry ldr r0,=__main_stack_top mov sp,r0 // Enable FPU early so the compiler may use FPU registers for copying // SCB->CPACR = ((3UL << 10*2) | (3UL << 11*2)); ldr r0,=0xe000ed88 ldr r1,=0x00f00000 str r1, [r0, #0] bl __modm_initialize_platform bl modm_initialize_platform b __modm_startup .endfunc
以上就是启动文件 reset_handler.sx
.
Reset_Handler
是程序启动时硬件自动跳转到的入口点.globl Reset_Handler
: 声明Reset_Handler
是全局符号.text
: 将后续代码放入.text
段 (程序代码的存储位置).syntax unified
: 指定使用 ARM 的统一汇编语法.align 4
: 确保Reset_Handler
起始地址对齐到 4 字节 (32 位).type Reset_Handler, %function
: 告诉编译器和调试器Reset_Handler
是一个函数.func Reset_Handler
: 指定Reset_Handler
是一个函数的开始
ldr r0,=__main_stack_topmov sp,r0
- 设置堆栈指针
- 一些 bootloader 可能会改变堆栈指针, 所以这里需要显式地重新初始化它
ldr r0,=__main_stack_top
: 加载堆栈顶部地址 (__main_stack_top
的值, 在链接文件中定义) 到寄存器r0
mov sp,r0
: 将寄存器r0
的值赋给堆栈指针sp
, 设置堆栈的初始位置
ldr r0,=0xe000ed88ldr r1,=0x00f00000str r1, [r0, #0]
- 启用浮点运算单元 (FPU)
bl __modm_initialize_platformbl modm_initialize_platform
- 调用初始化函数
bl
是带链接的分支指令, 跳转到函数入口并保存返回地址到lr
b __modm_startup
- 跳转到主程序入口
.endfunc
- 标志函数结束
linkerscript.ld
#
一个典型的 STM32 链接脚本大致可以分为以下几个部分:
ENTRY
: 程序入口点定义MEMORY
: 内存区域定义 , 描述 STM32 的内存布局SECTION
: 段定义, 指定各个段的数据如何布局在内存中- 一些符号定义
下面是一个简单示例:
ENTRY(Reset_Handler)
MEMORY{ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K}
_stack_top = ORIGIN(RAM) + LENGTH(RAM);_heap_start = _ebss;_heap_end = _stack_top;
SECTIONS{ .text : { KEEP(*(.isr_vector)) *(.text*) *(.rodata*) _etext = .; } > FLASH
.data : { _sdata = .; *(.data*) _edata = .; } > RAM AT > FLASH
.bss : { _sbss = .; *(.bss*) *(COMMON) _ebss = .; } > RAM}
.text
段: 存放程序代码和只读数据 (如常量)KEEP(*(.isr_vector))
: 保留中断向量表, 避免被链接器优化掉*(.text*)
: 匹配所有.text
开头的段 (通常是代码)*(.rodata*)
:匹配所有只读数据段_etext = .;
: 定义符号_etext
, 表示.text
段的结束地址> FLASH
: 将.text
段放入 Flash
.data
段: 存放初始化的全局变量_sdata = .;
: 定义符号_sdata
, 表示.data
段的起始地址*(.data*)
: 匹配所有.data
段_edata = .;
: 定义符号_edata
, 表示.data
段的结束地址> RAM AT > FLASH
: 表示.data
段运行时在 RAM, 但初始化数据存放在 Flash
.bss
段: 存放未初始化的全局变量_sbss = .;
: 定义符号_sbss
, 表示.bss
段的起始地址*(.bss*)
: 匹配所有.bss
段*(COMMON)
: 匹配未显式初始化的全局变量_ebss = .;
: 定义符号_ebss
, 表示.bss
段的结束地址> RAM
: 将.bss
段放入 RAM
实际的 linkerscript.ld
太长了, 就不放了, 但有几个注意的点:
- STM32G4 的 RAM 分为 CCM, SRAM1, SRAM2, CCM 速度快, 但无法使用 dma
- 它将许多有用的变量写在了
.rodata
段, 如LONG(__vector_table_ram_start)
- 定义了一些有用的段, 如
.hardware_init.order_*
, DWT 早启动就是以这种方式实现的 - 中断向量表分为
.vector_rom
和.vector_ram
, 运行时可以改变SCB->VTOR
的值让中断向量表可以更改 .stack
段是ALIGN(8)
MAIN_STACK_SIZE
, 不是越大越好, DMA 缓冲区, 全局变量, heap 也会占用 RAM
startup#
根据官网的介绍, 启动流程如下:
After reset, the ARM Cortex-M hardware jumps to the Reset_Handler()
, which is implemented as follows:
- The main stack pointer (MSP) is initialized by software.
- Call
__modm_initialize_platform()
to initialize the device hardware. - Call
modm_initialize_platform()
to initialize the custom device hardware. - Copy data to internal RAM.
- Zero sections in internal RAM.
- Initialize ARM Cortex-M core: enable FPU, caches and relocate vector table.
- Execute shared hardware initialization functions.
- Copy data to external RAM.
- Zero sections in external RAM.
- Initialize heap via
__modm_initialize_memory()
(implemented by themodm:platform:heap
module). - Call static constructors.
- Call
main()
application entry point. - If
main()
returns, assert onmain.exit
(only in debug profile). - Reboot if assertion returns.
其中对应的启动部分的代码就是:
// ----------------------------------------------------------------------------// Called by Reset_Handler in reset_handler.svoid __modm_startup(void){ // Copy and zero all internal memory table_copy(__table_copy_intern_start, __table_copy_intern_end); table_zero(__table_zero_intern_start, __table_zero_intern_end);
// Set the vector table location SCB->VTOR = (uint32_t)__vector_table_ram_start;
// Enable trapping of divide by zero for UDIV/SDIV instructions. SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
// Call all hardware initialize hooks table_call(__hardware_init_start, __hardware_init_end);
// Copy and zero all external memory table_copy(__table_copy_extern_start, __table_copy_extern_end); table_zero(__table_zero_extern_start, __table_zero_extern_end);
// Initialize heap as implemented by the heap option __modm_initialize_memory();
// Call all constructors of static objects table_call(__init_array_start, __init_array_end);
// Call the application's entry point main();
// If main exits, assert here in debug mode (void) modm_assert_continue_fail_debug(0, "main.exit", "The main() function returned!"); // Otherwise reboot NVIC_SystemReset();}