初始化栈空间 和执行地址0处的程序谁先执行?为什么

stm32 的启动文件中分配栈空间等指令和上电后就从地址0 开始执行函数这两个之间是如何协调的。
启动文件的执行地址0 处的指令是在哪啊
是这里吗__Vectors DCD __initial_sp ; Top of Stack

img

img

普通程序最先做的就是初始化栈,0地址处的内容就是栈,并不是指令。这个启动逻辑是硬件决定的,楼上说得很清楚。
至于第一个函数,还要等一系列的初始化完成之后才会调用。

在 STM32 的启动过程中,启动文件的执行地址是从地址0开始的。因为在芯片上电后,处理器会自动跳转到地址0处执行代码。而在启动文件中,通常会定义一些向量表(Vector Table),用于存储处理器的中断向量和一些重要的系统信息。这些向量表通常会放置在地址0处,以便处理器在上电后能够正确地初始化和配置系统状态。

在启动文件中,一般会定义 __Vectors 这样的符号,用于指向向量表的起始地址。在向量表中,会包含一个 __initial_sp 符号,用于指定栈空间的起始地址。这个地址通常会在启动文件中定义,以便在启动过程中正确地分配和使用栈空间。例如:

.section .isr_vector,"a",%progbits
  .word  _estack               /* 堆栈起始地址 */
  .word  Reset_Handler        /* 复位中断向量 */
  .word  NMI_Handler          /* NMI中断向量 */
  .word  HardFault_Handler    /*在 STM32 的启动过程中,启动文件的执行地址是从地址0开始的。这是因为在芯片上电后,处理器会自动跳转到地址0处执行代码。而在启动文件中,我们通常会定义一些向量表(Vector Table),用于存储处理器的中断向量和一些重要的系统信息。这些向量表通常会放置在地址0处,以便处理器在上电后能够正确地初始化和配置系统状态。

在启动文件中,一般会定义 __Vectors 这样的符号,用于指向向量表的起始地址。在向量表中,会包含一个 __initial_sp 符号,用于指定栈空间的起始地址。这个地址通常会在启动文件中定义,以便在启动过程中正确地分配和使用栈空间。例如:

```assembly
.section .isr_vector,"a",%progbits
  .word  _estack               /* 堆栈起始地址 */
  .word  Reset_Handler        /* 复位中断向量 */
  .word  NMI_Handler          /* NMI中断向量 */
  .word  HardFault_Handler    /*在 STM32 的启动过程中,启动文件的执行地址是从地址0开始的。这是因为在芯片上电后,处理器会自动跳转到地址0处执行代码。而在启动文件中,我们通常会定义一些向量表(Vector Table),用于存储处理器的中断向量和一些重要的系统信息。这些向量表通常会放置在地址0处,以便处理器在上电后能够正确地初始化和配置系统状态。

在启动文件中,一般会定义 __Vectors 这样的符号,用于指向向量表的起始地址。在向量表中,会包含一个 __initial_sp 符号,用于指定栈空间的起始地址。这个地址通常会在启动文件中定义,以便在启动过程中正确地分配和使用栈空间。例如:

```assembly
.section .isr_vector,"a",%progbits
  .word  _estack               /* 堆栈起始地址 */
  .word  Reset_Handler        /* 复位中断向量 */
  .word  NMI_Handler          /* NMI中断向量 */
  .word  HardFault_Handler    /*在上面的代码中,__Vectors 符号指向了向量表的起始地址,而 __initial_sp 符号指定了栈空间的起始地址,它们都是存储在向量表中的。在向量表中,第一个字是栈空间的起始地址,因此在这里 __initial_sp 符号指向了 __Vectors 符号所指向的地址。这样,当处理器从地址0开始执行代码时,首先会初始化栈指针,然后跳转到 Reset_Handler 函数执行。

在启动文件中,通常会使用一些汇编语句来定义向量表和其他重要的系统信息,如栈指针、堆空间等。这些汇编语句会被编译成二进制代码,然后存储在芯片的闪存中。当芯片上电后,处理器会自动从地址0开始执行这些指令,初始化系统状态和配置硬件环境。

需要注意的是,在启动文件中定义的栈空间大小必须足够应对实际的程序运行需求,否则可能会出现栈溢出等问题。在 STM32 中,栈空间通常是在堆栈段(Stack Section)中分配的。在启动文件中,可以通过定义 .stack 段和指定栈空间大小来分配栈空间。*/

例如:

```assembly
.stack  0x200         ; 栈空间大小为 512 字节

在向量表中,__initial_sp 符号通常会指向栈空间的末尾地址,以便在程序运行过程中正确地分配和使用栈空间。

在启动文件中,除了定义栈空间和向量表之外,还会包含一些初始化代码和系统配置代码,用于初始化处理器、配置时钟、设置中断向量表等。这些代码通常会在 Reset_Handler 函数中执行。当处理器从地址0开始执行时,首先会初始化栈指针,然后跳转到 Reset_Handler 函数执行。在 Reset_Handler 函数中,会执行一些初始化和配置代码,最终跳转到 main 函数开始执行程序。

在启动过程中,初始化栈空间和执行地址 0 处的程序都是必须的步骤。实际上,在 STM32 的启动文件中,这两个部分已经被写好了,只需要按照格式进行修改即可。

在 ARM Cortex-M 系列处理器中,启动时会首先加载堆栈指针 (SP) 寄存器的初值,然后跳转到执行地址 0 处开始执行。因此,初始化栈空间代码应该位于向量表的第一个位置,即 SP 的初值应该是向量表的第一个地址,而执行地址 0 处的程序则应该是向量表中 Reset_Handler 对应的地址。

在 STM32 的启动文件中,初始化栈空间的代码通常是以下这行指令:

_initial_sp EQU 0x20001000 // 设置栈顶地址为 0x20001000
该语句将栈顶地址设置为 0x20001000,表示堆栈从该地址向下生长。接着,在向量表中将栈顶地址的初值设置为 _initial_sp 即可:

DCD _initial_sp ; Top of Stack
而执行地址 0 处的程序则由 Reset_Handler 函数来实现,其定义如下:

Reset_Handler:
  /* ...其他代码 */
  B .
它会在启动时被执行,并通过 B . 跳转到当前位置(即执行地址 0 处)继续执行其他操作。

因此,初始化栈空间和执行地址 0 处的程序都是必要的步骤,并且它们顺序是固定的:首先初始化堆栈指针,然后跳转到执行地址 0 处开始执行 Reset_Handler。其它的中断处理函数也在向量表中定义了相应的位置,可以通过修改向量表来实现对应的中断功能。

注意观察栈的内存区域,寄存器SP和BP的变化。出栈的时候要遵守先进后出,后进先出的原则,当然你也可以不遵守,按ax,bx,cx,dx的顺序出栈,但是这样可能会导致程序的最终执行结果并不是你想要的。因为我们现在所写的程序已经是没有了操作系统的限制,所以你想做怎么都可以,如果是基于操作系统上这样写程序是肯定不行的,普通用户程序无法直接访问内存,必须借助操作系统才能访问。

STM32的启动过程中,启动文件的执行地址是设定为地址0,会将各种寄存器的初始值和栈空间分配在指定的位置,并跳转到主函数的位置,完成整个启动过程。

结合ChatGPT部分内容参考:在STM32的启动文件中,分配栈空间等指令和从地址0开始执行函数之间的协调是通过向量表实现的。向量表是一个存储器区域,其中包含了处理器的中断向量和重置向量等信息。在STM32中,向量表通常位于Flash的起始地址处,也就是地址0处。

在启动文件中,__Vectors DCD __initial_sp ; Top of Stack这一行代码定义了向量表的起始地址,也就是栈顶的地址。__initial_sp是一个符号,表示栈顶的地址,DCD表示将该地址定义为一个双字(32位)的数据。因此,这一行代码的作用是将栈顶的地址存储在向量表的第一个位置,以便后续的程序可以使用该地址作为栈顶。

在向量表中,第一个向量是重置向量,也就是处理器上电后从地址0开始执行的第一条指令。因此,启动文件中的代码会将重置向量设置为一个跳转指令,跳转到程序的入口函数。程序的入口函数通常是由应用程序开发者编写的,用于初始化系统和应用程序,并开始执行应用程序的主要功能。

因此,在STM32的启动过程中,启动文件会先设置向量表的起始地址和重置向量,然后跳转到程序的入口函数,由入口函数开始执行应用程序的主要功能。同时,启动文件还会分配栈空间等指令,以确保程序能够正常运行。