S32DS中链接文件及启动代码学习
一、链接文件
<Linker Files>文件夹中有linker_flash.ld文件和linker_ram.ld文件。
Linker File称为链接文件,它是作用在链接过程。程序代码(.s 和 .c)源文件会经过预编译、编译、汇编、链接最后生成目标可执行文件;
linker_flash.ld文件功能:Flash和SRAM内存分配,为Flash构建目标分配代码段和数据段;
linker_ram.ld文件功能:SRAM内存分配,为RAM构建目标分配代码段和数据段。
linker_flash.ld文件分析:
二、启动代码
<Startup_Code>文件夹中有以下几个文件:
1.<startup_cm7.s>内主要分为定义部分和程序执行部分;
定义部分:
1)定义部分常量
#define MAIN_CORE 0
#define MCME_CTL_KEY 0x402DC000
#define MCME_PRTN1_PUPD 0x402DC304
#define MCME_PRTN1_STAT 0x402DC308
#define MCME_PRTN1_COFB0_CLKEN 0x402DC330
#define MCME_MSCM_REQ (1 << 24)
#define MCME_KEY 0x5AF0
#define MCME_INV_KEY 0xA50F
#define CM7_ITCMCR 0xE000EF90
#define CM7_DTCMCR 0xE000EF94
#define SBAF_BOOT_MARKER (0x5AA55AA5)
#define CM7_0_ENABLE_SHIFT (0)
#define CM7_1_ENABLE_SHIFT (1)
#define CM7_0_ENABLE (1)
#define CM7_1_ENABLE (0)
#define CM7_0_VTOR_ADDR (__ROM_INTERRUPT_START)
#define CM7_1_VTOR_ADDR (0)
#define XRDC_CONFIG_ADDR (0)
#define LF_CONFIG_ADDR (0)
.syntax unified
.arch armv7-m
2)定义”.init_table”段内容为可分配
/* Table for copying and zeroing */
/* Copy table:
- Table entries count
- entry one ram start
- entry one rom start
- entry one rom end
...
- entry n ram start
- entry n rom start
- entry n rom end
Zero Table:
- Table entries count
- entry one ram start
- entry one ram end
*/
/** 1.定义部分 **/
.section ".init_table", "a" //段内容可分配
.long 4
.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 __ROM_INTERRUPT_START
.long __ROM_INTERRUPT_END
3)定义”.zero_table”段内容为可分配
.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
4)定义”.core_loop”段内容为可分配,可执行
.globl RESET_CATCH_CORE
.globl _core_loop
.section ".core_loop","ax" //段内容可分配,可执行
.thumb
_core_loop:
nop
nop
nop
nop
b _core_loop
5)定义”.boot_header”段内容为可分配,可执行(详细见startup_cm7.s文件)
.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) /* 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 XRDC_CONFIG_ADDR /* XRDC configuration pointer */
.long LF_CONFIG_ADDR /* Lifecycle configuration pointer */
.long 0 /* Reserved */
执行部分:
其中代码执行的顺序是按S32K3xx RTD启动步骤执行的,如下图:
1) Reset_Handler:
.set VTOR_REG, 0xE000ED08
.thumb
.thumb_func
.globl Reset_Handler
.globl _start
_start: /** 2.程序执行部分 **/
Reset_Handler:
/*****************************************************/
/* 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
2) InitMSCMClock:
a) 通过判断 MC_ME 模块 PRTN1_COFB0_STAT 寄存器第 24bit,检查 MSCM 时钟使能情况。
b) 若时钟已使能,跳转到 SetVTOR 执行
c) 若时钟未使能,继续往下执行
d) 通过置位 MC_ME 模块 PRTN1_COFB0_CLKEN 寄存器第 24bit,打开 MSCM 时钟
e) 通过往 MC_ME 模块 PRTN1_PUPD 寄存器写 1,更新时钟操作
f) 通过往 MC_ME 模块 CTL_KEY 写入 KEY 值,使能硬件操作
InitMSCMClock: //
/* 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]
ldr r0, =MCME_PRTN1_PUPD
3) WaitForClock:
a) 循环等到时钟初始化完成
WaitForClock:
ldr r1, [r0]
ldr r2, =1
and r1, r2
cmp r1, #1
bge WaitForClock
4) SetVTOR:(向 CM7 内核设置向量表地址偏移,相对 0x00000000)
a) 将.non_cacheable_data 段的__interrupts_ram_start 地址写入了中断偏移地址
p.s. :_interrupts_ram_start地址的内容从__interrupts_rom_start 地址获得,其内容为”.intc_vector”段,”.intc_vector”内容的定义在 Vector_Table.s 中
b) 获取当前内核 ID, 若 为 main core, 跳转到 SetCore0Stack ,否则跳转到SetCore1Stack。
/* relocate vector table to RAM */
ldr r0, =VTOR_REG
ldr r1, =__RAM_INTERRUPT_START
str r1,[r0]
/*GetCoreID*/
ldr r0, =0x40260004
ldr r1,[r0]
ldr r0, =MAIN_CORE
cmp r1,r0
beq SetCore0Stack
5) SetCore0Stack:
a) 设置 MSP(main stack pointer)为__Stack_start_c0
b) 跳转到 DisableSWT0
SetCore0Stack:
/* set up stack; r13 SP*/
ldr r0, =__Stack_start_c0
msr MSP, r0
b DisableSWT0
6) SetCore1Stack:
a) S32K344 为单主核(另一核不做分析)
7) DisableSWT0:
a) 失能内核 0 看门狗后,跳转到 RamInit
/* 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
8). DisableSWT1:
a) S32K344 为单主核(另一核不做分析)
9). RamInit:(没有清零 SRAM,用户需自己认定 SRAM 数据上电后不可靠)
a) 比较__RAM_INIT 和 0,若相等,跳转到 SRAM_LOOP_END(不成立)
b) _INT_SRAM_EDN-__INT_SRAM_START = 1 则跳转到 SRAM_LOOP_END(不成立)
c) 清空 r0,r3 通用寄存器
/******************************************************************/
/* Autosar Guidance 13 - The start-up code shall initialize a */
/* minimum amount of RAM in order to allow proper execution of */
/* the MCU driver services and the caller of these services. */
/******************************************************************/
RamInit:
/* Initialize SRAM ECC */
ldr r0, =__RAM_INIT
cmp r0, 0
/* Skip if __SRAM_INIT is not set */
beq SRAM_LOOP_END
ldr r1, =__INT_SRAM_START
ldr r2, =__INT_SRAM_END
subs r2, r1
subs r2, #1
ble SRAM_LOOP_END
movs r0, 0
movs r3, 0
10). SRAM_LOOP:
a) r1 的地址值(__INT_SRAM_START)+8 ,将值写给 r0,r3
b) r2 的地址值(__INT_SRAM_END-1)-8,若大于 0,跳转到SRAM_LOOP
SRAM_LOOP:
stm r1!, {r0,r3}
subs r2, 8
bge SRAM_LOOP
11). SRAM_LOOP_END:
无操作
SRAM_LOOP_END:
12). DTCM_Init/ ITCM_Init:
a) 比较__DTCM_INIT 和 0,若相等,跳转到 DTCM_LOOP_END(不成立)
b) 往内核寄存器 DTCMCR 写 1,使能 DTCM
c) _INT_DTCM_END-__INT_DTCM_START = 1 则跳转到DTCM_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
/* Enable TCM */
LDR r1, =CM7_DTCMCR
LDR r0, [r1]
LDR r2, =0x1
ORR r0, r2
STR r0, [r1]
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
13). DTCM_LOOP/ ITCM_LOOP:
参照 SRAM_LOOP
DTCM_LOOP:
stm r1!, {r0,r3}
subs r2, #8
bge DTCM_LOOP
14). DTCM_LOOP_END/ ITCM_LOOP_END:
无操作
DTCM_LOOP_END:
15). DebuggerHeldCoreLoop:
DebuggerHeldCoreLoop:
ldr r0, =RESET_CATCH_CORE
ldr r0, [r0]
ldr r1, =0x5A5A5A5A
cmp r0, r1
beq DebuggerHeldCoreLoop
16). _DATA_INIT:(数据初始化)
a) 判断当前核是否为主核,是则跳转到 _INIT_DATA_BSS ,否则跳转到__SYSTEM_INIT
/************************/
/* Erase ".bss Section" */
/************************/
_DATA_INIT:
/* If this is the primary core, initialize data and bss */
ldr r0, =0x40260004
ldr r1,[r0]
ldr r0, =MAIN_CORE
cmp r1,r0
beq _INIT_DATA_BSS
b _MAIN
17). _INIT_DATA_BSS:
跳转到 startup.c 的 init_data_bss()函数
a) 把”.init_table”段(非 0 的全局静态等变量的 sram 起始地址,flash 起始/终止地址)地址对应的信息,从只能读不能写的片内 flash 拷贝到 SRAM.
b) 清零”.zero_table”段(BSS 段)
_INIT_DATA_BSS:
bl init_data_bss
18). __SYSTEM_INIT:
跳转到 system.c 的 SystemInit()函数
a) 获取内核 ID,使能对应 CM7 内核的所有(240 个)中断请求
b) 使能 FPU
c) 清空 cache,关闭 cache
d) 将内存的地址起始位置,大小,属性(读/写,共享等)写入 CM7 内核 MPU 对应寄存器
e) 使能 MPU,打开 cache
__SYSTEM_INIT:
bl SystemInit
19). _MAIN:
跳转到 main.c 的 main()函数(详细见startup_cm7.s文件)
_MAIN:
cpsie i
bl startup_go_to_user_mode
bl main
2.< startup.c>作用:
1.把”.init_table”段(非 0 的全局静态等变量的 sram 起始地址,flash 起始/终止地址)地址对应的信息,从只能读不能写的片内 flash 拷贝到 SRAM.
2.清零”.zero_table”段(BSS 段)
/*FUNCTION**********************************************************************
*
* Function Name : init_data_bss
* Description : Make necessary initializations for RAM.
* - Copy the vector table from ROM to RAM.
* - Copy initialized data from ROM to RAM.
* - Copy code that should reside in RAM from ROM
* - Clear the zero-initialized data section.
*
* Tool Chains:
* __GNUC__ : GNU Compiler Collection
* __ghs__ : Green Hills ARM Compiler
* __ICCARM__ : IAR ARM Compiler
* __DCC__ : Wind River Diab Compiler
* __ARMCC_VERSION : ARMC Compiler
*
* Implements : init_data_bss_Activity
*END**************************************************************************/
void init_data_bss(void);
void init_data_bss(void)
{
const Sys_CopyLayoutType * copy_layout;
const Sys_ZeroLayoutType * zero_layout;
const uint8 * rom;
uint8 * ram;
uint32 len = 0U;
uint32 size = 0U;
uint32 i = 0U;
uint32 j = 0U;
const uint32 * initTable = (uint32 *)__INIT_TABLE;
const uint32 * zeroTable = (uint32*)__ZERO_TABLE;
/* Copy initialized table */
len = *initTable;
copy_layout = (Sys_CopyLayoutType *)(initTable + 1U);
for(i = 0; i < len; i++)
{
rom = copy_layout[i].rom_start;
ram = copy_layout[i].ram_start;
size = (uint32)copy_layout[i].rom_end - (uint32)copy_layout[i].rom_start;
for(j = 0UL; j < size; j++)
{
ram[j] = rom[j];
}
}
/* Clear zero table */
len = *zeroTable;
zero_layout = (Sys_ZeroLayoutType *)(zeroTable + 1U);
for(i = 0; i < len; i++)
{
ram = zero_layout[i].ram_start;
size = (uint32)zero_layout[i].ram_end - (uint32)zero_layout[i].ram_start;
for(j = 0UL; j < size; j++)
{
ram[j] = 0U;
}
}
}
3.< system.c>作用:
system.c中SystemInit()主要完成按如下顺序完成系统初始化
1.获取内核 ID,使能对应 CM7 内核的所有(240 个)中断请求
2.使能 FPU
3.清空 cache,关闭 cache
4.将内存的地址起始位置,大小,属性(读/写,共享等)写入 CM7 内核 MPU 对应寄存器
5.使能 MPU,打开 cache
主要功能是完成对 CM7 内核 MPU 寄存器的设置。
void SystemInit(void)
{
uint32 i;
uint32 coreMask;
uint8 regionNum = 0U;
uint8 coreId = OsIf_GetCoreID();
switch(coreId)
{
case CM7_0:
coreMask = (1UL << MSCM_IRSPRC_M7_0_SHIFT);
break;
case CM7_1:
coreMask = (1UL << MSCM_IRSPRC_M7_1_SHIFT);
break;
default:
coreMask = 0UL;
break;
}
/* Configure MSCM to enable/disable interrupts routing to Core processor */
for (i = 0; i < MSCM_IRSPRC_COUNT; i++)
{
MSCM->IRSPRC[i] |= coreMask;
}
/**************************************************************************/
/* FPU ENABLE*/
/**************************************************************************/
#ifdef ENABLE_FPU
/* Enable CP10 and CP11 coprocessors */
S32_SCB->CPACR |= (S32_SCB_CPACR_CPx(10U, 3U) | S32_SCB_CPACR_CPx(11U, 3U));
ASM_KEYWORD("dsb");
ASM_KEYWORD("isb");
#endif /* ENABLE_FPU */
/**************************************************************************/
/* DEFAULT MEMORY ENABLE*/
/**************************************************************************/
ASM_KEYWORD("dsb");
ASM_KEYWORD("isb");
/* Set default memory regions */
for (regionNum = 0U; regionNum < CPU_DEFAULT_MEMORY_CNT; regionNum++)
{
S32_MPU->RNR = regionNum;
S32_MPU->RBAR = rbar[regionNum];
S32_MPU->RASR = rasr[regionNum];
}
/* Enable MPU */
S32_MPU->CTRL |= S32_MPU_CTRL_ENABLE_MASK;
ASM_KEYWORD("dsb");
ASM_KEYWORD("isb");
/**************************************************************************/
/* ENABLE CACHE */
/**************************************************************************/
sys_m7_cache_init();
}
三、程序编译链接过程
程序编译链接过程分为四个阶段:预编译->编译->汇编->链接;
1.预编译:
1)删除所有的“#define”,并且展开所有的宏定义;
2)处理所有的条件预编译指令,“#if”、“#ifdef”、“#endif”等;
3)处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置;
4)删除所有的注释;
5)添加行号和文件名标识,以便于编译器产生调试用的符号信息及编译时产生编译错误和警告时显示行号;
6)保留所有的#pragma 编译器指令,因为编译器需要使用它们。
2.编译:
词法分析、语法分析、语义分析,代码优化,汇总符号。
(编译过程往往是整个程序构建的核心部分,也是最复杂的部分之一,编译阶段结束后会生成 .s 文件)
3.汇编:
将汇编指令翻译成二进制格式,生成各个 section,生成符号表。(汇编阶段结束后会生成 .o 文件)
4.链接:
1)合并各个 section,调整 section 的起始位移和段大小,合并符号表,进行符号解析,给符号分配虚拟地址
2)符号重定位
四、名词介绍
1.RAM(Random Access Memory)随机存储器:可读可写,特点是掉电会丢失数据。
RAM又分为SRAM(Static RAM)和DRAM(Dynamic RAM),SRAM是读写速度非常快的存储设备,但价格昂贵。DRAM比ROM速度快,但是比SRAM速度慢,价格低于SRAM,计算机内存使用的就是DRAM。
静态RAM(Static RAM/SRAM),SRAM速度非常快,是目前读写最快的存储设备了;但它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲;掉电数据消失,持续供电时数据一直存在,不需要动态刷新。
动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的;掉电数据丢失,供电时需要刷新电路(因为栅极会漏电)
2.ROM(Read Only Memory) 只读存储器:ROM是只读存储器,掉电时可以保存数据:
只读存储器,在单片机运行时,只能从中读取数据,不能向里面写数据。特点是掉电不丢失数据,在单片机中主要用来存储代码和常量等内容。
ROM种类:
ROM也有很多种,PROM是可编程的ROM,PROM和EPROM(可擦除可编程ROM)两者区别是,PROM是一次性的,也就是软件灌入后无法修改,这种是早期的产品,现在已经不可能使用了;而EPROM是通过紫外光的照射擦除原先的程序,是一种通用的存储器。另外一种EEPROM是通过电子擦出,价格很高,写入时间很长,写入很慢。
3.FLASH存储器:FLASH存储器又称闪存,它结合了ROM和RAM的长处,不仅具备电子可擦除可编程(EEPROM)的性能,还不会断电丢失数据,同时可以快速读取数据(NVRAM的优势),U盘和MP3里用的就是这种存储器。在过去的20年里,嵌入式系统一直使用ROM(EPROM)作为它们的存储设备,然而近年来Flash全面代替了ROM(EPROM)在嵌入式系统中的地位,它用作存储Bootloader以及操作系统或者程序代码,或者直接当硬盘使用(U盘)。
4.RTD:NXP最新一代GPIS芯片:S32K3系列,K3系列的配套驱动软件叫做RTD。
5.BAF(Boot Assist Firmware):BAF是在重启后运行在HSE-B安全核心上的第一个代码,它执行必要的系统初始化,搜索和解析IVT(Image Vector Table),最后停止HSE-B,启用SWT并启动应用程序核心。
6.IVT(Image Vector Table):图像矢量表。
五、关键词功能及使用
1.ENTRY(func)
:定义应用程序代码的程序入口,它是应用程序/引导加载程序重置处理程序。
2.MEMORY{}
:包括几个区域的内存映射分配。
−<region> (attribute): ORIGIN = start_address, LENGTH = size of the region。
3.ORIGIN
:用于分配内存区域的起始地址,可以缩写为org或o。
4.LEGHTH
:用于分配内存区域的长度,它可以缩写为len或l。
5.SECTIONS{}
:包含所有的section放置,可以包含几个section组:
−AT用于分配ROM/Flash上的段存储地址(可选);
−KEEP用于保留未使用的section,如向量表、IVT;
−ALIGN用于内存地址对齐,并添加必要的填充数据字节的连接器;
−NOLOAD用于告诉链接器一个段是不可加载的,当程序运行时,它将不会被加载到内存中(可选)。
6.A <section group>
:具有任何合法名称的<section组>可以包含多个section放置。
7..
:一个点“.”可以用来获取当前内存地址。
8.ORIGIN()
:在MEMORY{}和SECTIONS{}之外,ORIGIN()可以用来分配一个内存区域的起始地址,并且LEGHTH()获取内存区域的长度。
9..text
: 以下是代码段。
10..data
:以下是初始化数据段。
11..bss
:以下是未初始化数据段。
12..long
:定义一个长整型,并为它分配空间,占四字节,0x12345678。
13.align
:例:.align absexpr1,absexpr2;以某种对齐方式,在未使用的存储区域填充值。 第一个值表示对齐方式,4, 8,16或32. 第二个表达式值表示填充的值。
——2022.6.23继续学习继续补充、完善、纠正此内容
原创文章,作者:kirin,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/269817.html