S32K3学习笔记—链接文件及启动代码相关
一.链接(ld)文件
链接文件解释了不同section怎样合并并生成可执行文件,还包括了数据段和代码段的地址和长度等信息。主要作用于链接过程。
关键词及其作用
1.ENTRY
此命令用于设置可执行文件elf文件。这是MCU复位后执行的第一行代码。
ENTRY(Reset_Handler)
2.MEMORY
此命令用于描述flash的不同段的划分及其的起始地址和大小
MEMORY
{
int_pflash : ORIGIN = 0x00400000, LENGTH = 0x003D4000 /* 4096KB - 176KB (sBAF + HSE)*/
int_dflash : ORIGIN = 0x10000000, LENGTH = 0x00020000 /* 128KB */
int_itcm : ORIGIN = 0x00000000, LENGTH = 0x00008000 /* 32KB */
int_dtcm : ORIGIN = 0x20000000, LENGTH = 0x0000F000 /* 60KB */
int_stack_dtcm : ORIGIN = 0x2000F000, LENGTH = 0x00001000 /* 4KB */
int_sram : ORIGIN = 0x20400000, LENGTH = 0x0002FF00 /* 192KB, needs to include int_sram_fls_rsv */
int_sram_fls_rsv : ORIGIN = 0x2042FF00, LENGTH = 0x00000100
int_sram_no_cacheable : ORIGIN = 0x20430000, LENGTH = 0x0000FF00 /* 64KB, needs to include int_sram_results */
int_sram_results : ORIGIN = 0x2043FF00, LENGTH = 0x00000100
int_sram_shareable : ORIGIN = 0x20440000, LENGTH = 0x00004000 /* 16KB */
ram_rsvd2 : ORIGIN = 0x20444000, LENGTH = 0 /* End of SRAM */
}
3.SECTIONS
此命令主要是告诉编译器如何将输入sections映射到输出sections,以及怎么在内存中放置输出sections.
有四种典型的section:
text – code
rodata – read-only data
data – read-write initialized data
bss – read-write zero initialized data
此外还有两个概念:VMA & LMA
VMA: Virtual Memory Address or runtime address,
LMA : Load Memory Address or ROM address.
对于具有初始值的全局变量(数据部分),这些初始值存储在闪存中,并通过启动代码加载到RAM。因此,ROM地址是LMA,RAM地址是VMA。bss部分没有LMA。
.sram_bss (NOLOAD) :/*there is no LMA */
{
...
*(.bss)
...
} > int_sram /*VMA is in int_sram */
.sram_data : AT(__sram_data_rom) /*LMA is sram_data_rom */
{
...
*(.data)
...
} > int_sram /*VMA is in int_sram */
4.Wildcard character (*)
告诉编译器合并所有输入的sections
*(.text) /*merge the .text section of all the Input file*/
5.Location counter (.)
链接器会自动用位置(地址)信息更新此符号。它可以用来跟踪和定义不同部分的边界。此位置计数器也可以设置为任何特定值。
_text_end = .; /*the symbol _text_end equals to currentaddress*/
. = ALIGN(4);/*increment the current address to the next 4-bytes aligned address*/
. += 0x100;/*increment the current address by 0x100*/
. = 0x20401000; /*locate the current address to 0x20401000*/
6.ALIGN
此指令是用于对齐且对齐的地址必须是2的幂。
. = ALIGN(2048); /*increment the current address to the next 2048-bytes aligned address*/
. = ALIGN(10);/*It is wrong. 10 is not power of 2*/
7.AT
此指令表示LMA of section
.sram_data : AT(_sram_data_rom) /*LMA is _sram_data_rom */
{
...
*(.data)
...
} > int_sram /*VMA is in int_sram */
...
8.KEEP
此指令是标记不被编译器删除的部分
KEEP(*(.boot_header)) /*keep the .boot_header section not be eliminated */
9.Linker script sysbol
sysbol是指地址的名称。类似于C语言中的声明,大多用sysbol定义sections的边界并会在内存初始化的启动代码所引用。
__INT_SRAM_START = ORIGIN(int_sram); /*symbol _INT_SRAM_START equals the origin address ofmemory region int_ sram*/
__INT_ITCM_END = ORIGIN(int_itcm) + LENGTH(int_itcm); /*symbol _INT_ITCM_END equals theorigin address of memory region int_itcm plus its length*/
_ sram bss_ start = .; /*symbol _sram_bss_start equals current address*/
__BSS_SRAM_SIZE = __sram_bss_end - __sram_bss_start; /*symbol _BSS_SRAM SIZE equals the end of address of .sram_bss section minus the start address of .sram_bss section */
二.启动流程
MCU的启动过程指从上电到Main 函数中间的过程。芯片复位后,最先进入SBAF,即启动固件,这是芯片出厂就已经固化在芯片内部的,所有复位方式复位后都会运行。BAF初始化完成之后,就从Reset_Handler()执行到main,下图显示了启动代码的整个过程
三.启动代码
启动代码是根据启动流程来实现的。
1.boot_header
boot_header包含启动配置、核心启动地址、生命周期等信息,且只能储存在5个固定的地址:0x00400000、0x00500000、0x00600000、0x00700000、0x10000000。详细信息请参考芯片手册
.section ".boot_header","ax"
.long SBAF_BOOT_MARKER /* IVT marker */
.long (CM7_0_ENABLE << CM7_0_ENABLE_SHIFT) | (CM7_1_ENABLE << CM7_1_ENABLE_SHIFT) | (CM7_2_ENABLE << CM7_2_ENABLE_SHIFT) /* Boot configuration word */
.long 0 /* Reserved */
.long CM7_0_VTOR_ADDR /* CM7_0 Start address */
.long 0 /* Reserved */
.long CM7_1_VTOR_ADDR /* CM7_1 Start address */
.long 0 /* Reserved */
.long CM7_2_VTOR_ADDR /* CM7_2 Start address */
.long 0 /* Reserved */
.long XRDC_CONFIG_ADDR /* XRDC configuration pointer */
.long LF_CONFIG_ADDR /* Lifecycle configuration pointer */
.long 0 /* Reserved */
2. Disable global
S32K3在开始时会失能全局中断和清除R0-R7寄存器
/*****************************************************/
/* Skip normal entry point as nothing is initialized */
/*****************************************************/
cpsid i
mov r0, #0
mov r1, #0
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
3. Enable MSCM Clock
#ifndef NO_MSCM_CLOCK_INIT
InitMSCMClock:
/* If the MSCM clock is enabled, skip this sequence */
ldr r0, =MCME_PRTN1_COFB0_STAT
ldr r1, [r0]
ldr r2, =MCME_MSCM_REQ
and r1, r1, r2
cmp r1, 0
bne SetVTOR
/* Enable clock in PRTN1 */
ldr r0, =MCME_PRTN1_COFB0_CLKEN
ldr r1, [r0]
ldr r2, =MCME_MSCM_REQ
orr r1, r2
str r1, [r0]
/* Set PUPD field */
ldr r0, =MCME_PRTN1_PUPD
ldr r1, [r0]
ldr r2, =1
orr r1, r2
str r1, [r0]
/* Trigger update */
ldr r0, =MCME_CTL_KEY
ldr r1, =MCME_KEY
str r1, [r0]
ldr r1, =MCME_INV_KEY
str r1, [r0]
#endif
/* Check MSCM clock in PRTN1 */
#ifndef SIM_TYPE_VDK
WaitForClock:
ldr r0, =MCME_PRTN1_COFB0_STAT
ldr r1, [r0]
ldr r2, =MCME_MSCM_REQ
and r1, r1, r2
cmp r1, 0
beq WaitForClock
#endif
4. Relocate vector table to Ram
将中断向量表(intc_vector)复制到SRAM。设置中断向量表的基地址到VTOR(向量表偏移寄存器),这样core就可以根据不同的向量表数跳转到不同的ISR。将向量表定义到SRAM访问速率会比在Flash中快并且能满足客户修改ISR的名称。
SetVTOR:
/* relocate vector table to RAM */
ldr r0, =VTOR_REG
ldr r1, =__RAM_INTERRUPT_START
str r1,[r0]
5.Initialize CPU Core Stack
初始化堆栈指针MSP
/* Enable TCM and Disable RETEN bit */
LDR r1, =CM7_DTCMCR
LDR r0, [r1]
bic r0, r0, #0x4
orr r0, r0, #0x1
str r0, [r1]
/* Enable TCM and Disable RETEN bit */
LDR r1, =CM7_ITCMCR
LDR r0, [r1]
bic r0, r0, #0x4
orr r0, r0, #0x1
str r0, [r1]
/* set up stack; r13 SP*/
ldr r0, =__Stack_dtcm_start
msr MSP, r0
/*GetCoreID*/
ldr r0, =0x40260004
ldr r1,[r0]
ldr r0, =MAIN_CORE
cmp r1,r0
beq DisableSWT0
#ifdef RAM_DATA_INIT_ON_ALL_CORES
b RamInit
#else
b DTCM_Init /* SWT1 clock is disabled at startup */
#endif
6.Disable SWT watchdog
软件看门狗计时器默认启用,在启动的过程中需要关闭
/* Note from manual: For any operation to be performed on an SWT */
/* instance, its respective core must be enabled. */
DisableSWT0:
ldr r0, =0x40270010
ldr r1, =0xC520
str r1, [r0]
ldr r1, =0xD928
str r1, [r0]
ldr r0, =0x40270000
ldr r1, =0xFF000040
str r1, [r0]
b RamInit
DisableSWT1:
ldr r0, =0x4046C010
ldr r1, =0xC520
str r1, [r0]
ldr r1, =0xD928
str r1, [r0]
ldr r0, =0x4046C000
ldr r1, =0xFF000040
str r1, [r0]
b RamInit
7. RAM ECC initialization
芯片复位后,在使用RAM之前必须对其进行初始化,避免ECC错误。注意程序大小必须2 word对齐,对于SRAM、DTCM、ITCM也是同样的,这是启动中最耗时间的地方。
RamInit:
/* Initialize SRAM ECC */
ldr r0, =__RAM_INIT
cmp r0, 0
/* Skip if __RAM_INIT is not set */
beq SRAM_LOOP_END
ldr r0, =MCRGM_DES
ldr r1, [r0]
ldr r2, =MCRGM_DES_F_POR
and r1, r1, r2
cmp r1, 0
beq NO_INIT_STANDBY_REGION
ldr r1, =__INT_SRAM_START
ldr r2, =__INT_SRAM_END
b ZERO_64B_RAM
NO_INIT_STANDBY_REGION:
#if defined(S32K310)||defined(S32M274)
ldr r1, =__BSS_SRAM_NC_START
#else
ldr r1, =__BSS_SRAM_START
#endif
ldr r2, =__INT_SRAM_END
ZERO_64B_RAM:
subs r2, r1
subs r2, #1
ble SRAM_LOOP_END
movs r0, 0
movs r3, 0
SRAM_LOOP:
stm r1!, {r0,r3}
subs r2, 8
bge SRAM_LOOP
SRAM_LOOP_END:
DTCM_Init:
/* Initialize DTCM ECC */
ldr r0, =__DTCM_INIT
cmp r0, 0
/* Skip if __DTCM_INIT is not set */
beq DTCM_LOOP_END
ldr r1, =__INT_DTCM_START
ldr r2, =__INT_DTCM_END
subs r2, r1
subs r2, #1
ble DTCM_LOOP_END
movs r0, 0
movs r3, 0
DTCM_LOOP:
stm r1!, {r0,r3}
subs r2, #8
bge DTCM_LOOP
DTCM_LOOP_END:
ITCM_Init:
/* Initialize ITCM ECC */
ldr r0, =__ITCM_INIT
cmp r0, 0
/* Skip if __TCM_INIT is not set */
beq ITCM_LOOP_END
ldr r1, =__INT_ITCM_START
ldr r2, =__INT_ITCM_END
subs r2, r1
subs r2, #1
ble ITCM_LOOP_END
movs r0, 0
movs r3, 0
ITCM_LOOP:
stm r1!, {r0,r3}
subs r2, #8
bge ITCM_LOOP
ITCM_LOOP_END:
8.Wait for debugger to hole core
当在debug的时候需要保持住,调试器可以写一个固定的值0x5A5A5A5A到RESET_CATCH_CORE
DebuggerHeldCoreLoop:
ldr r0, =RESET_CATCH_CORE
ldr r0, [r0]
ldr r1, =0x5A5A5A5A
cmp r0, r1
beq DebuggerHeldCoreLoop
9.RAM data copy
主要是将存在Flash中的数据copy到RAM中,主要包括以下几种情况:
a.将vector table 、initialized data(.data)、code从ROM复制到RAM
b.清除zero_initialized(.bss) section的数据
/* Table for copying and zeroing */
.section ".init_table", "a"
.long 6
.long __RAM_CACHEABLE_START
.long __ROM_CACHEABLE_START
.long __ROM_CACHEABLE_END
.long __RAM_NO_CACHEABLE_START
.long __ROM_NO_CACHEABLE_START
.long __ROM_NO_CACHEABLE_END
.long __RAM_SHAREABLE_START
.long __ROM_SHAREABLE_START
.long __ROM_SHAREABLE_END
.long __RAM_INTERRUPT_START
.long __INIT_INTERRUPT_START
.long __INIT_INTERRUPT_END
.long __RAM_ITCM_START
.long __ROM_ITCM_START
.long __ROM_ITCM_END
.long __RAM_DTCM_DATA_START
.long __ROM_DTCM_DATA_START
.long __ROM_DTCM_END
对于bss段,由于没有初始值存在flash,因此只有两个section: RAM_START、RAM_END
.section ".zero_table", "a"
.long 3
.long __BSS_SRAM_SH_START
.long __BSS_SRAM_SH_END
.long __BSS_SRAM_NC_START
.long __BSS_SRAM_NC_END
.long __BSS_SRAM_START
.long __BSS_SRAM_END
init_data_bss 通过写入0来初始化zero_table
_INIT_DATA_BSS:
bl init_data_bss
10.System Initialization
系统初始化主要包括:
a.获取核ID,使能对应核的中断请求
b.使能FPU
c.使能MPU
d.使能 I/D cache
__SYSTEM_INIT:
bl SystemInit
11. 跳转main
这是启动代码的最后一部,会启动CPU全局的中断,如果是由AUTOSAR启动将会切换用户模式,最终跳转到main,之后与启动就无关
_MAIN:
cpsie i
bl startup_go_to_user_mode
bl main
四.编译过程
主要是指把源代码编译成可执行代码,编译过程主要几个过程: 预处理、编译、汇编、链接
1.预处理
头文件展开:将#include的内容展开到当前位置,并删除#include
宏展开:展开所有的宏定义,并删除#define
展开条件编译:根据条件展开分支,其余代码删除
删除注释:删掉所有的注释
添加行号及文件名标识:编译过程中根据许需要显示这些信息,如警告,错误等
保留#pragma命令:供编译器使用
2.编译
编译器会做词法分析、语法分析、语义分析等操作,在检查没有错误信息后,会把代码翻译成汇编语句。
3.汇编
将汇编代码翻译成二进制文件
4.链接
编译器会根据链接器将目标文件以及所依赖的库文件进行链接、重定位组装成可执行文件
五.展望
在了解S32K3xx启动相关的知识之后,后续打算实操,后续将基于S32K3xx实例修改一份启动代码和linker文件。