S32K3学习笔记---链接文件及启动相关实操

S32K3学习笔记—链接文件及启动相关实操

一、常用操作

​ 关于启动文件和链接文件一些基本知识,可以参考上一篇文章:S32K3学习笔记—链接文件及启动代码相关

1.关键字 attribute_

​ 可以用_attribute__关键字将函数或者变量重新定义到某个section,这种比较简单,但是有时候也能解决很多的问题。例如:把一个函数放到 ramcode 段可以更快的执行

extern void __attribute__ ((section(".ramcode")))  XX_MainFunction(void);
void __attribute__ ((section(".ramcode")))  XX_MainFunction(void)  //set XX_MainFunction to ramcode section
{
	...
}

uint8 _attribute_ ((section(".mcal_data_no_cacheable"))) xx_status;//set xx_status to mcal_data_no_cacheable

2.关键字 #pragma GCC section

​ 如果我们有大量的数据或者函数要定义到某个section,一行一行用_attribute__就会比较费劲。那么#pragma GCC section作用就体现了,我们只需要用一次就可以将某部分代码放到某个section。 data、test、bss、rodata都可以用以下方式

//set XX_MainFunction and XXX_MainFunction to ramcode section
#pragma GCC section text "ramcode"
extern void  XX_MainFunction(void);
void XXX_MainFunction(void)
#pragma GCC section text "default"

#pragma GCC section text "ramcode" 
void XX_MainFunction(void)
{
	...
}
void XXX_MainFunction(void)
{
	...
}
#pragma GCC section text "default" 

//set xx_status and xxx_status to mcal_data_no_cacheable section
#pragma GCC section data "mcal_data_no_cacheable"
extern uint8 xx_status;
extern uint8 xxx_status;
#pragma GCC section text "default"

#pragma GCC section data "mcal_data_no_cacheable"
uint8 xx_status = 0;
uint8 xxx_status = 0;
#pragma GCC section data "default"

3.将中断向量表放到DTCM

​ 为了更好的性能,有时堆栈可以从SRAM重定位到DTCM。为了更快的响应中断,我们可以把放到SRAM的中断向量表定义到DTCM中,只需要将之前的中断向量表从SRAM移动到DTCM中就行。需要注意的是中断向量表的起始地址需要对齐4096字节。

/*Divide 4K and use it for the interrupt vector table */
int_dtcm                : ORIGIN = 0x20000000, LENGTH = 0x0010000 - 0x00001000 /* 60KB + int_stack_dtcm_c0*/
int_stack_dtcm_c0          : ORIGIN = 0x2000F000 - 0x00001000, LENGTH = 0x00001000    /* 4KB*/

/*change SRAM to DTCM,int_stack_dtcm_c0 will be assiggned to the MSP in the startup code*/
__Stack_dtcm_end        = ORIGIN(int_stack_dtcm_c0);
__Stack_dtcm_start      = ORIGIN(int_stack_dtcm_c0) + LENGTH(int_stack_dtcm_c0);

/*update the boundary for DTCM end address */
__INT_DTCM_START         = ORIGIN(int_dtcm);
__INT_DTCM_END           = ORIGIN(int_dtcm) + LENGTH(int_dtcm) + LENGTH(int_stack_dtcm_c0);

/*define the section*/
.int_vector :
{
    . = ALIGN(4096);
    __interrupts_ram_start = .;
    . += __interrupts_init_end - __interrupts_init_start;
    . = ALIGN(4);
    __interrupts_ram_end = .;
} > int_dtcm

4.将代码放到ITCM

​ ITCM是零等待内存,CPU访问的时间比flash和SRAM更快,如果要使用ITCM,可以参考如下步骤:

在链接文件在定义ITCM section,其默认就定义了int_itcm,如下,因此我们关键字_attribute__ 将需要放的代码定义到ITCM中

__tcm_code_rom_start = __tcm_data_rom_end;

.itcm_text : AT(__tcm_code_rom_start)
{
    . = ALIGN(4);
    __itcm_start__ = .;
    *(.itcm_text*)
    . = ALIGN(4);
    __itcm_end__ = .;
} > int_itcm

__tcm_code_rom_end = __tcm_code_rom_start + (__itcm_end__ - __itcm_start__);

​ 默认的链接文件中没有定义ARM、EABI、GUN预留section位置,可以将他们添加到pflash中避免ITCM/DTCM或data section重叠

ARM.exidx:
{
    *(.ARM.exidx*)
    *(.gnu.linkonce.armexidx.*)
    *(.glue*)
    *(.vfp11*)
    *(.v4*)
    *(.iplt*)
    *(.rel*)
) > int flash

​ 链接文件在定义从flash加载到ITCM的地址

__RAM_ITCM_START         = __itcm_start__;
__ROM_ITCM_START         = __tcm_code_rom_start;
__ROM_ITCM_END           = __tcm_code_rom_end;

​ 在启动文件中将 _RAM_ITCM_START、_ROM_ITCM_START、_ROM_ITCM_END定义到init_table,将长度增加一个。

.section ".init_table", "a"
  .long  n+1
	...
  .long __RAM_ITCM_START
  .long __ROM_ITCM_START
  .long __ROM_ITCM_END

​ 最后我们只需要用关键字_attribute__ 将需要放的代码定义到itcm_text中

void __attribute__ ((section(".itcm_text"))) XX_MainFunction(void)  //set XXX to itcm_text section
{
	...
}

5.将数据放到DTCM

​ DTCM和ITCM类似,但是专门用于数据。将data存放在DTCM,有以下操作

​ 定义DTCM section,数据段有初始值和没有初始值两种类型,因此定义两个段 .data_tcm_data 和 .bss_tcm_data

__tcm_data_rom = __shareable_data_rom_end;

.data_tcm_data : AT(__tcm_data_rom)
{
    . = ALIGN(4);
    __dtcm_data_start__ = .;
    *(.dtcm_data*)
    . = ALIGN(4);
    __dtcm_data_end__ = .;
} > int_dtcm

__tcm_data_rom_end = __tcm_data_rom + (__dtcm_data_end__ - __dtcm_data_start__);

.bss_tcm_data (NOLOAD) :
{
    . = ALIGN(4);
    __dtcm_bss_start__ = .;
    *(.dtcm_bss*)
    . = ALIGN(4);
    __dtcm_bss_end__ = .;
} > int_dtcm

__tcm_code_rom_start = __tcm_data_rom_end;

​ 在链接文件中定义加载到DTCM的地址

__RAM_DTCM_DATA_START    = __dtcm_data_start__;
__ROM_DTCM_DATA_START    = __tcm_data_rom;
__ROM_DTCM_END           = __tcm_data_rom_end;

__BSS_DTCM_START      	 = __dtcm_bss_start__;
__BSS_DTCM_SIZE       	 = __dtcm_bss_end__ - __dtcm_bss_start__;
__BSS_DTCM_END        	 = __dtcm_bss_end__;

​ 在启动文件中将新增的添加到.init_table 和 .zero_table

.section ".init_table", "a"
  .long n+1
  	...
  .long __RAM_DTCM_DATA_START
  .long __ROM_DTCM_DATA_START
  .long __ROM_DTCM_END
.section ".zero_table", "a"
  .long n+1
	...
  .long __BSS_DTCM_START
  .long __BSS_DTCM_END

​ 最后我们只需要用关键字_attribute__ 将需要放的代码定义到.dtcm_bss 和.dtcm_data中

uint8  _attribute__  ((section(".dtcm_bss"))) gPdxx;
uint8  _attribute__  ((section(".dtcm_data"))) gPdxx = 8;

6.链接二进制文件

​ 某些情况下,我们可能会想把当前已有的二进制文件连接到当前的工程中,比如当我们在装HSE的核心固件的时候

int_pflash1 (R)   : ORIGIN = 0x00600000, LENGTH = 0x00100000  /*Area for storage*/

TARGET(binary) /*指定二进制的格式*/
INPUT(路径...) /*文件的路径*/
OUTPUT_FORMAT(default)/*恢复文件输出格式*/

/*define the section*/
.int_pflash1 :
{
	.= ALIGN(0X4);
	_int_pflash1_start_ = .;
	路径...(.data) 
	. = ALIGN(0X4);
	_int_pflash1_end_ = .;
}

_int_pflash1_START_ = ORIGIN(int_pflash1);
_int_pflash1_SIZE_  = _int_pflash1_end_ - _int_pflash1_start_;

7.将数据储存在Dflash

​ S32K3x4 有128K的dflash,但HSE用最后的40KB,因此我们有88K可使用。

int_dflash              : ORIGIN = 0x10000000, LENGTH = 0x00016000    /* 128KB - 40KB = 88K*/

/*define the section*/
.data_flash :
{
	. = ALIGN(4);
	*(.dflash)
} > int_dflash

uint8 __attribute__ ((section(".dflash"))) test_dflash[] = "dflash test" /*use the __attribute__ to relocate he variable in dflash*/

8.将数据放到 standby RAM

​ 在standby模式下,只有standby RAM中的数据可以保留,S32K324有32KB的standby RAM。当前的链接文件将其作为普通的SRAM使用,如果我们需要使用,就需要新建一个单独的区域。此过程比较复杂,需要修改的地方比较多,链接文件和启动文件需要修改,此外,基于该段的特殊性,ECC初始化那块也需要保留数据也需要做一些修改,对于.bss和.data处理也会有差异,其初始化也需要重新做,工作量很大,等遇到实际需求再去研究这块。

二、实操

​ 本文是基于S32K324官网demo修改一个双核双中断向量表单elf的工程,由于项目需要,要求每个核有自己的中断向量表且每个核的中断优先级是可以重复的,可能就会出现我们需要将S32K3双中断向量表的形式,以下基于RTD3.0做的简单修改。

1.链接文件的修改

​ 根据上述的几种常用的操作,为了更快的响应中断,我们将中断向量表放在DTCM中。由于是想做两个中断向量表,将DTCM分出2个4K用于存放中断向量表,具体操作如下:

int_dtcm                : ORIGIN = 0x20000000, LENGTH = 0x0000E000    /* 56KB */   /*64K include int_dtcm、int_stack_dtcm_c0、int_stack_dtcm_c1*/
int_stack_dtcm_c0       : ORIGIN = 0x2000E000, LENGTH = 0x00001000    /* 4KB */
int_stack_dtcm_c1       : ORIGIN = 0x2000F000, LENGTH = 0x00001000    /* 4KB */

​ 相应的pflah section也需要做简单的修改

.pflash :
{
    KEEP(*(.boot_header))
    . = ALIGN(4096);
    __interrupts_rom_start_c0 = .;
    KEEP(*(.intc_vector_c0))  
    . = ALIGN(4);
    __interrupts_rom_end_c0 = .;
    . = ALIGN(4096);
    __interrupts_rom_start_c1 = .;
    KEEP(*(.intc_vector_c1))
    . = ALIGN(4);
    __interrupts_rom_end_c1 = .;
    
    ...
}

​ 还需要定义一个section 加载中断向量表,需要注意的是中断向量表的起始地址需要对齐4096字节

.int_vector :
{
    . = ALIGN(4096);
    __interrupts_ram_start_c0 = .;
    . += __interrupts_rom_end_c0 - __interrupts_rom_start_c0;    
    . = ALIGN(4);
    __interrupts_ram_end_c0 = .;
    
    . = ALIGN(4096);
    __interrupts_ram_start_c1 = .;
    . += __interrupts_rom_end_c1 - __interrupts_rom_start_c1;    
    . = ALIGN(4);
    __interrupts_ram_end_c1 = .;
} > int_dtcm

​ 将SRAM更改为DTCM,int_stack_dtcm_c0、int_stack_dtcm_c1将在启动代码中分配给MSP,且重新定义DTCM的边界

/*change SRAM to DTCM,int_stack_dtcm_c0 and int_stack_dtcm_c1 will be assiggned to the MSP in the startup code*/
__Stack_end_c0           = ORIGIN(int_stack_dtcm_c0);
__Stack_start_c0         = ORIGIN(int_stack_dtcm_c0) + LENGTH(int_stack_dtcm_c0);
__Stack_end_c1           = ORIGIN(int_stack_dtcm_c1);
__Stack_start_c1         = ORIGIN(int_stack_dtcm_c1) + LENGTH(int_stack_dtcm_c1);

/*update the boundary for DTCM end address for ECC initialization */
__INT_DTCM_START         = ORIGIN(int_dtcm);
__INT_DTCM_END           = ORIGIN(int_dtcm) + LENGTH(int_dtcm) + LENGTH(int_stack_dtcm_c0) 											 + LENGTH(int_stack_dtcm_c1);

​ 重新定义sections的地址,在内存初始化的启动代码所引用

__RAM_INTERRUPT_START_C0    = __interrupts_ram_start_c0;
__ROM_INTERRUPT_START_C0    = __interrupts_rom_start_c0;
__ROM_INTERRUPT_END_C0      = __interrupts_rom_end_c0;

__RAM_INTERRUPT_START_C1    = __interrupts_ram_start_c1;
__ROM_INTERRUPT_START_C1    = __interrupts_rom_start_c1;
__ROM_INTERRUPT_END_C1      = __interrupts_rom_end_c1;

__ENTRY_VTABLE_C0           = __ROM_INTERRUPT_START_C0;
__ENTRY_VTABLE_C1           = __ROM_INTERRUPT_START_C1;

__CORE0_VTOR             = __interrupts_rom_start_c0;
__CORE1_VTOR             = __interrupts_rom_start_c1;
__CORE2_VTOR             = __interrupts_rom_start_c1;/*for s32K324, it's a pseudo value*/

2.中断向量表的修改

​ 其实中断都是用的一套,按照正常的理解,多核每个都有一个自己的SysTick_Handler,但是ARM决定了只有一个 SysTick_Handler,为了区分不同核用不同的SysTick_Handler,因此只能用两个中断向量表修改如下:

/* Vector_Table_C0 */
.section  ".intc_vector_c0","ax"
.align 2
.thumb
.globl undefined_handler
.globl undefined_handler
.globl VTABLE_C0
.globl _core_loop
.globl __Stack_start_c0          /* Top of Stack for Initial Stack Pointer */
.globl Reset_Handler             /* Reset Handler */
.globl NMI_Handler               /* NMI Handler */
.globl HardFault_Handler         /* Hard Fault Handler */
.globl MemManage_Handler         /* Reserved */
.globl BusFault_Handler          /* Bus Fault Handler */
.globl UsageFault_Handler        /* Usage Fault Handler */
.globl SVC_Handler               /* SVCall Handler */
.globl DebugMon_Handler          /* Debug Monitor Handler */
.globl PendSV_Handler            /* PendSV Handler */
.globl SysTick_Handler           /* SysTick Handler */ /* 15*/

VTABLE_C0:
.long __Stack_start_c0          /* Top of Stack for Initial Stack Pointer */
  ...
.size VTABLE_C0, . - VTABLE_C0

/* Vector_Table_C1 */
.section  ".intc_vector_c1","ax"
.align 2
.thumb
.globl undefined_handler
.globl undefined_handler
.globl VTABLE_C1
.globl _core_loop
.globl __Stack_start_c1          /* Top of Stack for Initial Stack Pointer */
.globl Reset_Handler             /* Reset Handler */
.globl NMI_Handler               /* NMI Handler */
.globl HardFault_Handler         /* Hard Fault Handler */
.globl MemManage_Handler         /* Reserved */
.globl BusFault_Handler          /* Bus Fault Handler */
.globl UsageFault_Handler        /* Usage Fault Handler */
.globl SVC_Handler               /* SVCall Handler */
.globl DebugMon_Handler          /* Debug Monitor Handler */
.globl PendSV_Handler            /* PendSV Handler */
.globl SysTick_Handler           /* SysTick Handler */ /* 15*/

VTABLE_C1:
.long __Stack_start_c1          /* Top of Stack for Initial Stack Pointer */
	...
.size VTABLE_C1, . - VTABLE_C1

​ 通过观察发现两个向量表都是一样的,感觉不是特殊的要求一般也不会选择加一个向量表,虽然这种比较直观,能比较直观的区分两个核,但是比较浪费内存,当然这些都需要看需求

3.启动代码的修改

​ 在启动文件中将变化的添加到.init_table

.section ".init_table", "a"
.long 7
	...
.long __RAM_INTERRUPT_START_C0
.long __ROM_INTERRUPT_START_C0
.long __ROM_INTERRUPT_END_C0
.long __RAM_INTERRUPT_START_C1
.long __ROM_INTERRUPT_START_C1
.long __ROM_INTERRUPT_END_C1
	...

​ 加载中断向量表到RAM

.globl VTABLE_C0    /* interrupt vector table of Core0 */
.globl VTABLE_C1    /* interrupt vector table of Core1 */


SetVTOR:
  /*GetCoreID*/
  ldr  r0, =0x40260004
  ldr  r1,[r0]

  ldr  r0, =MAIN_CORE
  cmp  r1,r0
  beq	 SetVTOR_C0
  b SetVTOR_C1

SetVTOR_C0:
  /* relocate vector table to RAM */
  ldr  r0, =VTOR_REG
  ldr  r1, =__RAM_INTERRUPT_START_C0
  str  r1,[r0]

SetVTOR_C1:
  /* relocate vector table to RAM */
  ldr  r0, =VTOR_REG
  ldr  r1, =__RAM_INTERRUPT_START_C1
  str  r1,[r0]

​ 将两个中断向量表分别给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]
  
  /*GetCoreID*/
  ldr  r0, =0x40260004
  ldr  r1,[r0]
  ldr  r0, =MAIN_CORE
  cmp  r1,r0
  beq	 SetCore0Stack
  b SetCore1Stack

SetCore0Stack:
  /* set up stack; r13 SP*/
  ldr  r0, =__Stack_start_c0
  msr MSP, r0
  b DisableSWT0

SetCore1Stack:
  /* set up stack; r13 SP*/
  ldr  r0, =__Stack_start_c1
  msr MSP, r0
#ifdef RAM_DATA_INIT_ON_ALL_CORES
  b RamInit
#else
  b DTCM_Init /* SWT1 clock is disabled at startup */
#endif

​ 根据核ID进入不同的main_cx

_MAIN:
  cpsie i
  bl startup_go_to_user_mode
_GoMain:
  /*GetCoreID*/
  ldr  r0, =0x40260004
  ldr  r1,[r0]
  ldr  r0, =MAIN_CORE
  cmp  r1,r0
  beq	 main_c0
  b main_c1

​ 总结下,其实startup_cm7.s文件中修改的比较简单,都是根据ID来做区分,之后将中断向量表分别加载,且分别给到MSP,最后进入不同的main。上面一些常用操作支持我们干很多事情了,后续有需要再继续学习。

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
S32K3x4EVB-Q257是一款基于ARM Cortex-M内核的汽车电子控制系统的评估板。以下是入门指南: 1. 硬件准备:首先,确保你有一块S32K3x4EVB-Q257评估板、一台电脑和必要的USB连接线。 2. 软件安装:在电脑上安装相关的开发工具和软件。这可能包括S32 Design Studio集成开发环境(IDE)、S32 SDK软件开发套件、S32K Power Software Examples等。 3. 连接电源:将评估板插入电脑上的USB接口,并连接电源线到评估板上。 4. 下载示例代码:在S32 Design Studio中创建一个新项目,并下载示例代码到评估板上。这些示例代码将帮助你了解基本的功能和操作。 5. 硬件连线:根据需要,使用连接线将评估板与其他外部设备(例如传感器、显示屏等)连接起来。评估板上的引脚和接口可以通过手册查阅获得。 6. 运行代码:编译和下载代码到评估板上,并运行它。通过调试器或串口等方式,观察代码的执行情况和输出结果。 7. 进一步学习:通过阅读相关文档、手册和参考资料,了解评估板的更多功能和特性。你可以尝试修改示例代码或编写自己的代码,以实现特定的功能。 注意事项:在使用评估板时,务必遵循相关的安全操作规程,不要超过硬件的额定电压和电流,并保持设备的正确连接和操作。 以上是关于S32K3x4EVB-Q257入门的简要指南。希望这些信息对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值