英飞凌(Infineon)TC397链接文件解析
本篇文章首先介绍了链接文件在整个代码编译过程中所起到的作用,然后根据TC397芯片对应的链接文件涉及的链接命令,参照官网的示例给出了常见的使用方法,最后分为MEMORY与SECTIONS两个部分以脚本命令走读的形式解析了TC397芯片的链接文件,并对照代码帮助读者理解链接的整体过程。
目录
背景知识
Memory Layout
SECTIONS
背景知识
我们将由C语言编写成的源程序文件(.c&.h)最终构建成可执行程序的过程,按顺序简单分为以下四个步骤。
下面我们以GCC编译过程为例,简单的介绍一下各个步骤的作用。
- 预处理(Preprocessing):预处理器把.c文件生成.i文件,它会提前处理由"#"开头的预处理器指令,如#define,#include等。命令如下:
gcc -E *.c -o *.i
- 编译(Compilation):编译器将.i文件生成.s文件,它将程序转换为特定平台的汇编语言,命令如下:
gcc -S *.i -o *.s
- 汇编(Assemble):汇编过程将上一步的汇编代码转换成机器码,这一步产生的文件叫做目标文件,是二进制格式。编译的命令如下:
gcc -c *.s -o *.o
- 链接(Linking):链接器将所有目标文件与其他库文件、启动文件等链接起来生成可执行文件。命令格式如下:
gcc *.o -o *
链接器使用链接脚本将这些输入文件(.o)文件映射为一个输出文件的,链接脚本定义了输出文件的存储空间布局。下图由左到右为一个简单的链接过程,它把不同文件的里的不同内容按类别重新组合,并放置到对应的存储空间中。
下面我们会从TC397芯片实际的链接文件举例,来分析链接是怎么描述输出文件的存储空间布局,在分析之前,我们先了解以下几个相关的链接命令:
- MEMORY:MEMORY命令描述目标中内存块的位置和大小。通过谨慎使用它,可以描述链接器可以使用哪些内存区域,以及必须避免哪些内存区域。链接器将sections移动到正确的区域,并在区域变得太满时发出错误。下面是它的一个例子:
MEMORY { name (attr) : ORIGIN = origin, LENGTH = len ... }
-
SECTIONS:SECTIONS命令精确控制输入部分在输出部分中的放置位置、它们在输出文件中的顺序以及它们被分配到哪些输出部分。
-
Section Definitions:SECTIONS 命令中最常用的语句是 Section 定义,它指定输出 Section 的属性:其位置、对齐方式、内容、填充模式和目标内存区域。
SECTIONS { ... secname : { contents } ... }
-
Section Placement:在Section Definitions中,可以通过列出特定的输入文件、列出特定的输入文件Section或两者的组合来指定输出Section的内容。您还可以在Section中放置任意数据,并定义相对于Section开头的符号。下面的示例演示如何使用通配符对文件进行分区。所有.text 部分都放在.text 中,所有.bss 部分都放在.bss 中。对于所有以大写字符开头的文件,.data部分将放在.DTAT,对于所有其他的.data 部分放在.data 中。
SECTIONS { .text : { *(.text) } .DATA : { [A-Z]*(.data) } .data : { *(.data) } .bss : { *(.bss) } }
-
Section Data Expressions:您还可以将数据直接放置在链接命令脚本的输出部分中。下面的例子将数据0x5A5A5A5A放到了0x0202固定的位置。
.UserDataSec 0x0202 : { UserData= .; LONG (0x5A5A5A5A); }
-
Optional Section Attributes:以下是节定义的完整语法,包括所有可选部分,有兴趣的读者可以自行研究。
SECTIONS { ... secname start BLOCK(align) (NOLOAD) : AT ( ldadr ) { contents } >region :phdr =fill ... }
-
-
OUTPUT_FORMAT:指定了输出格式,OUTPUT_FORMAT("elf32-tricore")代表输出的是tricore32位的elf文件。
-
OUTPUT_ARCH:指定了输出的体系结构,OUTPUT_ARCH(tricore)代表为tricore。
-
ENTRY:设置程序入口地址,ENTRY(_crt0_reset)设置符号_crt0_reset为入口。
-
REGION_ALIAS:对存储区域取别名,方便后边表意使用。
Memory Layout
下图是TC397的功能模块图,通过此图可以清晰的看到各部分存储器的位置。DLMU为每个core各自拥有,Core自身可以快速访问;LMU为共有,只能通过SRI总线访问。DAM为共有,只能通过SRI总线访问。PSPR,DSPR都为每个Core自带RAM,DSPR主要用来放置数据,PSPR可用来放置数据及代码段(使函数在RAM上运行)。pcache、dcache主要用于加速指令和数据的访问。
我们可以在《Infineon-AURIX_TC3xx_Part1-UserManual-v02_00-EN.pdf》文件中2.3.2.1节找到TC397的内存布局,如下图所示。
它主要包含了以下几个部分:
-
ucb_bmhd_orig&ucb_bmhd_copy:BMHD的原始区地址与copy区地址。
-
int_flash(x):PFlash(存储代码的Flash空间)。
-
pspr_cpu(x):程序片上便签存储器。
-
dspr_cpu(x):数据片上便签存储器。
-
dlmu_cpu(x):每个核心私有的ram存储器。
-
lmu0_cached(x):公用的ram存储器。
下面的链接文件是针对上述主要存储布局定义,布局根据芯片手册规定而来。链接文件中有注释帮助理解各区含义。
OUTPUT_FORMAT("elf32-tricore")
OUTPUT_ARCH(tricore)
ENTRY(_crt0_reset)
/* ================================================================================================
* TC39XB MEMORY REGIONS
* ==============================================================================================*/
MEMORY
{
/* User configuration block - BMHD headers only */
ucb_bmhd_orig (rx): org = 0xaf400000, len = 2K
ucb_bmhd_copy (rx): org = 0xaf401000, len = 2K
/* Program Flash memory - cached region */
int_flash0 (rx): org = 0x80000000, len = 3M
int_flash1 (rx): org = 0x80300000, len = 3M
int_flash2 (rx): org = 0x80600000, len = 3M
int_flash3 (rx): org = 0x80900000, len = 3M
int_flash4 (rx): org = 0x80C00000, len = 3M
int_flash5 (rx): org = 0x80F00000, len = 1M
/* Program scratchpad memories */
pspr_cpu0 (rx): org = 0x70100000, len = 64K
pspr_cpu1 (rx): org = 0x60100000, len = 64K
pspr_cpu2 (rx): org = 0x50100000, len = 64K
pspr_cpu3 (rx): org = 0x40100000, len = 64K
pspr_cpu4 (rx): org = 0x30100000, len = 64K
pspr_cpu5 (rx): org = 0x10100000, len = 64K
/* Data scratchpad memories */
dspr_cpu0 (w!x): org = 0x70000000, len = 240K
dspr_cpu1 (w!x): org = 0x60000000, len = 240K
dspr_cpu2 (w!x): org = 0x50000000, len = 96K
dspr_cpu3 (w!x): org = 0x40000000, len = 96K
dspr_cpu4 (w!x): org = 0x30000000, len = 96K
dspr_cpu5 (w!x): org = 0x10000000, len = 96K
/* Distributed LMU RAM - Non-Cached regions selected
* Local core access is always non-cached */
dlmu_cpu0 (wx): org = 0xB0000000, len = 64K
dlmu_cpu1 (wx): org = 0xB0010000, len = 64K
dlmu_cpu2 (wx): org = 0xB0020000, len = 64K
dlmu_cpu3 (wx): org = 0xB0030000, len = 64K
dlmu_cpu4 (wx): org = 0xB0100000, len = 64K
dlmu_cpu5 (wx): org = 0xB0110000, len = 64K
/* Global LMU - Local memory Unit */
lmu0_cached (wx): org = 0x90040000, len = 256K
lmu1_cached (wx): org = 0x90080000, len = 256K
lmu2_cached (wx): org = 0x900C0000, len = 256K
lmu0_noncached (wx): org = 0xB0040000, len = 256K
lmu1_noncached (wx): org = 0xB0080000, len = 256K
lmu2_noncached (wx): org = 0xB00C0000, len = 256K
/* Periphery memory space region */
periphery_base : org = 0xF0000000, len = 0
periphery_end : org = 0xFFFF0000, len = 0
}
/* memory mirrors describe same physical memory accessible by different addresses */
REGION_MIRROR("lmu0_cached", "lmu0_noncached")
REGION_MIRROR("lmu1_cached", "lmu1_noncached")
REGION_MIRROR("lmu2_cached", "lmu2_noncached")
/* ================================================================================================
* MEMORY REGION SYMBOLS
* ==============================================================================================*/
/* Internal Flash memory */
INT_FLASH_MEMORY_BASE = ORIGIN(int_flash0);
INT_FLASH_MEMORY_SIZE = 16M;
/* ================================================================================================
* INCLUDE OF APPLICATION LINKER FILE
* ==============================================================================================*/
SECTIONS
在定义section之前,我们先预定义输出部分到内存区域别名,别名允许我们更好的表意和更快的更改。
/* ================================================================================================
* PREDEFINED OUTPUT SECTIONS TO MEMORY REGION ALIASES
* Aliases allow a quick change in memory placement
* ==============================================================================================*/
/* BMHD sections */
REGION_ALIAS("BMHD_ORIG", ucb_bmhd_orig)
REGION_ALIAS("BMHD_COPY", ucb_bmhd_copy)
/* CRT0 Boot Code Start */
REGION_ALIAS("BOOT", int_flash0)
/* BSP Interrupt handler table in RAM */
REGION_ALIAS("BSP_ISR_HANDLERS_CPU0_", pspr_cpu0)
REGION_ALIAS("BSP_ISR_HANDLERS_CPU1_", pspr_cpu1)
REGION_ALIAS("BSP_ISR_HANDLERS_CPU2_", pspr_cpu2)
REGION_ALIAS("BSP_ISR_HANDLERS_CPU3_", pspr_cpu3)
REGION_ALIAS("BSP_ISR_HANDLERS_CPU4_", pspr_cpu4)
REGION_ALIAS("BSP_ISR_HANDLERS_CPU5_", pspr_cpu5)
/* Core0: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU0_", int_flash0)
REGION_ALIAS("RODATA_CPU0_", int_flash0)
REGION_ALIAS("RAMCODE_CPU0_", pspr_cpu0)
REGION_ALIAS("DATA_DSPR_CPU0_", dspr_cpu0)
REGION_ALIAS("DATA_DLMU_CPU0_", dlmu_cpu0)
/* Core1: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU1_", int_flash1)
REGION_ALIAS("RODATA_CPU1_", int_flash1)
REGION_ALIAS("RAMCODE_CPU1_", pspr_cpu1)
REGION_ALIAS("DATA_DSPR_CPU1_", dspr_cpu1)
REGION_ALIAS("DATA_DLMU_CPU1_", dlmu_cpu1)
/* Core2: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU2_", int_flash2)
REGION_ALIAS("RODATA_CPU2_", int_flash2)
REGION_ALIAS("RAMCODE_CPU2_", pspr_cpu2)
REGION_ALIAS("DATA_DSPR_CPU2_", dspr_cpu2)
REGION_ALIAS("DATA_DLMU_CPU2_", dlmu_cpu2)
/* Core3: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU3_", int_flash3)
REGION_ALIAS("RODATA_CPU3_", int_flash3)
REGION_ALIAS("RAMCODE_CPU3_", pspr_cpu3)
REGION_ALIAS("DATA_DSPR_CPU3_", dspr_cpu3)
REGION_ALIAS("DATA_DLMU_CPU3_", dlmu_cpu3)
/* Core4: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU4_", int_flash4)
REGION_ALIAS("RODATA_CPU4_", int_flash4)
REGION_ALIAS("RAMCODE_CPU4_", pspr_cpu4)
REGION_ALIAS("DATA_DSPR_CPU4_", dspr_cpu4)
REGION_ALIAS("DATA_DLMU_CPU4_", dlmu_cpu4)
/* Core5: Output sections to memory region mapping */
REGION_ALIAS("CODE_CPU5_", int_flash5)
REGION_ALIAS("RODATA_CPU5_", int_flash5)
REGION_ALIAS("RAMCODE_CPU5_", pspr_cpu5)
REGION_ALIAS("DATA_DSPR_CPU5_", dspr_cpu5)
REGION_ALIAS("DATA_DLMU_CPU5_", dlmu_cpu5)
/* Common code and data */
REGION_ALIAS("CODE", int_flash0)
REGION_ALIAS("RODATA", int_flash0)
REGION_ALIAS("DATA", lmu0_noncached)
REGION_ALIAS("RAM_CODE", lmu0_noncached)
我们还需要提前定义一些常量,方便我们直接修改。
/* ================================================================================================
* PREDEFINED CONSTANTS
* ==============================================================================================*/
/* Shared Global Stack as defined by AURIX architecture (PSW.IS = 1)
* the User stack is left on eventual User or RTOS control (PSW.IS = 0)
*/
SHARED_STACK_SIZE = 4K;
/* CSA dedicated memory region size defined as
* (Number of CSA regions x CSA entry size) where 256 entries is Maximum
*/
CSA_REGIONS = 256;
CSA_SIZE = CSA_REGIONS * 64;
/* General HEAP is case one uses certain stdlib functions */
__HEAP_SIZE = DEFINED (__HEAP_SIZE) ? __HEAP_SIZE : 4K;
准备工作做完之后,我们第一步把bmhd的内容放置到对应的存储器地址中。
/* ================================================================================================
* SECTIONS: BMHD headers
* ==============================================================================================*/
SECTIONS
{
/* Boot Mode Header - original values */
.bmhd_0_orig 0xaf400000 : { KEEP (*(.bmhd_0_orig)) } > BMHD_ORIG
.bmhd_1_orig 0xaf400200 : { KEEP (*(.bmhd_1_orig)) } > BMHD_ORIG
.bmhd_2_orig 0xaf400400 : { KEEP (*(.bmhd_2_orig)) } > BMHD_ORIG
.bmhd_3_orig 0xaf400600 : { KEEP (*(.bmhd_3_orig)) } > BMHD_ORIG
/* Boot Mode Header - copy values */
.bmhd_0_copy 0xaf401000 : { KEEP (*(.bmhd_0_copy)) } > BMHD_COPY
.bmhd_1_copy 0xaf401200 : { KEEP (*(.bmhd_1_copy)) } > BMHD_COPY
.bmhd_2_copy 0xaf401400 : { KEEP (*(.bmhd_2_copy)) } > BMHD_COPY
.bmhd_3_copy 0xaf401600 : { KEEP (*(.bmhd_3_copy)) } > BMHD_COPY
}
这样定义好之后,我们就可以在代码中定义BMHD内容。
/* Boot Mode Header Structure type */
typedef struct
{
uint16_t bmi; /**< \brief Boot Mode Index (BMI)*/
uint16_t bmhdid; /**< \brief Boot Mode Header ID (CODE) = B359H*/
uint32_t stad; /**< \brief User Code start address*/
uint32_t crc; /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
uint32_t crcInv; /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
uint32_t reserved[120]; /**< \brief Reserved area till the offset 0x1F0*/
uint32_t confirmation; /**< \brief 32-bit CODE, (always same)*/
} IfxCpu_Bmhd;
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = OFF
*/
#pragma section ".bmhd_0_orig" a
const IfxCpu_Bmhd bmhd_0_orig=
{
.bmi= 0x00FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0x31795570, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0xCE86AA8F, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = OFF
*/
#pragma section ".bmhd_1_orig" a
const IfxCpu_Bmhd bmhd_1_orig=
{
.bmi= 0x00FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0x31795570, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0xCE86AA8F, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = ON
*/
#pragma section ".bmhd_2_orig" a
const IfxCpu_Bmhd bmhd_2_orig=
{
.bmi= 0x01FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0xFA2586D5, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0x05DA792A, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = ON
*/
#pragma section ".bmhd_3_orig" a
const IfxCpu_Bmhd bmhd_3_orig=
{
.bmi= 0x01FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0xFA2586D5, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0x05DA792A, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = OFF
*/
#pragma section ".bmhd_0_copy" a
const IfxCpu_Bmhd bmhd_0_copy=
{
.bmi= 0x00FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0x31795570, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0xCE86AA8F, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = OFF
*/
#pragma section ".bmhd_1_copy" a
const IfxCpu_Bmhd bmhd_1_copy=
{
.bmi= 0x00FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0x31795570, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0xCE86AA8F, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = ON
*/
#pragma section ".bmhd_2_copy" a
const IfxCpu_Bmhd bmhd_2_copy=
{
.bmi= 0x01FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0xFA2586D5, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0x05DA792A, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
/* BMHD Setting
* Core[0-3] = LOCKSTEP ON
* Core[4-5] = LOCKSTEP OFF
* LBIST = ON
*/
#pragma section ".bmhd_3_copy" a
const IfxCpu_Bmhd bmhd_3_copy=
{
.bmi= 0x01FE, /**< \brief Boot Mode Index (BMI)*/
.bmhdid= 0xB359, /**< \brief Boot Mode Header ID (CODE) = B359H*/
.stad= 0xA0000000, /**< \brief User Code start address*/
.crc= 0xFA2586D5, /**< \brief Check Result for the BMI Header (offset 000H - 007H)*/
.crcInv= 0x05DA792A, /**< \brief Inverted Check Result for the BMI Header (offset 000H - 007H)*/
.confirmation= 0x43211234, /**< \brief 32-bit CODE, (always same)*/
};
#pragma section
第二步我们把启动代码放置到bmhd定义好的地址处。
/* ================================================================================================
* SECTIONS: Application BOOT code
* Address pointed by BMHD start address value
* ==============================================================================================*/
SECTIONS
{
.crt0_boot : { KEEP (*(.crt0_boot.code)) } > BOOT
}
对应的启动代码如下。
/* ================================================================================================
* CRT0 RESET VECTOR
*
* Here execution starts after the Reset.
* The first duty is to force eventual address segment change in Aurix core
* from non-cached memory to a cacheable one
* ==============================================================================================*/
.section .crt0_boot.code, "ax"
_crt0_reset:
movh.a %a15, hi:_start
lea %a15, [%a15] lo:_start
ji %a15
第三步我们将Trap的中断向量表和处理函数放在0x80000100处。
/* ================================================================================================
* SECTIONS: Reset Default TRAP handlers provided by BSP
* the TRAP table is shared across cores
* BTV register value after reset is 0xA0000100 (0x80000100)
* ==============================================================================================*/
SECTIONS
{
.bsp_trap_reset 0x80000100 :
{
KEEP (*(.bsp_trap_vector_table))
KEEP (*(.bsp_trap_handlers))
} > CODE_CPU0_
}
我们在代码中的中断向量表和中断处理函数如下。
#pragma section ".bsp_trap_handlers" ax 2
/* ------------------------------------------------------------------------------------------------
* Trap class 0 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_0_Handler( void )
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Virtual Address Fill Trap occurred
case 0:
break;
// Virtual Address Protection Trap occurred
case 1:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 1 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_1_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Privilege Instruction Trap occurred
case 1:
break;
// Memory Protection Read Trap occurred
case 2:
break;
// Memory Protection Write Trap occurred
case 3:
break;
// Memory Protection Execution Trap occurred
case 4:
break;
// Memory Protection Peripheral Access Trap occurred
case 5:
break;
// Memory Protection Null Address Trap occurred
case 6:
break;
// Global Register Write Protection Trap occurred
case 7:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 2 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_2_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Illegal Opcode Trap occurred
case 1:
break;
// Unimplemented Opcode Trap occurred
case 2:
break;
// Invalid Operand Specification Trap occurred
case 3:
break;
// Data Address Alignment Trap occurred
case 4:
break;
// Invalid Local Memory Address Trap occurred
case 5:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 3 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_3_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Free context list depletion trap
case 1:
break;
// Call depth overflow trap occurred
case 2:
break;
// Call depth underflow trap occurred
case 3:
break;
// Free context list underflow trap
case 4:
break;
// Call stack underflow trap
case 5:
break;
// Context type trap
case 6:
break;
// Nesting error -> good to initiate reset
case 7:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 4 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_4_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Program Fetch Synchronous Error Trap occurred
case 1:
break;
// Data Access Synchronous Error Trap occurred
case 2:
break;
// Data Access Asynchronous Error Trap occurred
case 3:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 5 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_5_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// TIN=1: Arithemetic Overflow Trap occurred
// TIN=2: Sticky Arithemetic Overflow Trap occurred
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 6 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_6_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// System Call Trap occured
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
/* ------------------------------------------------------------------------------------------------
* Trap class 7 handler.
* ------------------------------------------------------------------------------------------------
*/
void Trap_7_Handler(void)
{
uint32_t tin;
// read TIN number stored in %d15 register to a variable
BSP_GET_TIN(tin);
// different reasons
switch(tin)
{
// Non-Maskable Interrupt Trap occurred
case 0:
break;
default:
break;
}
// stop in debug mode
__asm("debug");
// hard execution stop
while (1);
/* in case of return - restore lower context and return from exception
__asm ("rslcx");
__asm ("rfe");
*/
}
#pragma section /* #pragma section ".bsp_trap_handlers" ax 2 */
/* ================================================================================================
* BSP Trap Vectors
* In their specific code section (for linker placement)
* ==============================================================================================*/
/* TRAP Vector table definition macro */
#define BSP_TRAP_VECTOR_TABLE() \
__asm (".section .bsp_trap_vector_table , \"ax\", @progbits"); \
__asm (".align 8"); \
__asm (".global BSP_TRAP_VECTOR_TABLE"); \
__asm ("BSP_TRAP_VECTOR_TABLE:")
/* TRAP Vector X */
#define BSP_TRAP_HANDLER(i, handler) \
__asm (".align 5"); \
__asm ("debug"); \
__asm ("svlcx"); \
__asm ("movh.a %a14, hi:" #handler); \
__asm ("lea %a14, [%a14]lo:" #handler); \
__asm ("ji %a14")
/* TRAP Vector Table section definition */
BSP_TRAP_VECTOR_TABLE();
/* TRAP Vector Table */
BSP_TRAP_HANDLER(0, Trap_0_Handler);
BSP_TRAP_HANDLER(1, Trap_1_Handler);
BSP_TRAP_HANDLER(2, Trap_2_Handler);
BSP_TRAP_HANDLER(3, Trap_3_Handler);
BSP_TRAP_HANDLER(4, Trap_4_Handler);
BSP_TRAP_HANDLER(5, Trap_5_Handler);
BSP_TRAP_HANDLER(6, Trap_6_Handler);
BSP_TRAP_HANDLER(7, Trap_7_Handler);
第三步我们可以把涉及到的中断向量以及处理函数向后继续排布。
/* ================================================================================================
* SECTIONS: BSP Interrupt tables
* BSP provided Interrupt vector table and ISR Handler tables for each core
* BSP code set BIV register to core corresponding Base address
* ==============================================================================================*/
SECTIONS
{
.CPU0.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu0)) } > CODE_CPU0_
.CPU1.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu1)) } > CODE_CPU1_
.CPU2.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu2)) } > CODE_CPU2_
.CPU3.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu3)) } > CODE_CPU3_
.CPU4.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu4)) } > CODE_CPU4_
.CPU5.bsp_isr_vector_table : { KEEP (*(.bsp_isr_vector_table_cpu5)) } > CODE_CPU5_
}
SECTIONS
{
.CPU0.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu0) } > BSP_ISR_HANDLERS_CPU0_
.CPU1.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu1) } > BSP_ISR_HANDLERS_CPU1_
.CPU2.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu2) } > BSP_ISR_HANDLERS_CPU2_
.CPU3.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu3) } > BSP_ISR_HANDLERS_CPU3_
.CPU4.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu4) } > BSP_ISR_HANDLERS_CPU4_
.CPU5.bsp_isr_ram_table (NOLOAD) : { *(.BspIsrRamTable_Cpu5) } > BSP_ISR_HANDLERS_CPU5_
}
第四步在每个核心的DSPR预留出CSA(上下位)空间。
/* ================================================================================================
* SECTIONS: Cores' CSA regions
* Each core has its own CSA region list
* ==============================================================================================*/
SECTIONS
{
.CPU0.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU0_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU0_
.CPU1.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU1_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU1_
.CPU2.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU2_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU2_
.CPU3.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU3_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU3_
.CPU4.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU4_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU4_
.CPU5.csa (NOLOAD) : { . = ALIGN(64); __CSA_BASE_CPU5_ = .; . += CSA_SIZE; } > DATA_DSPR_CPU5_
}
第五步预留栈空间。
/* ================================================================================================
* SECTIONS: Cores' Shared Stacks
* Each core has its own Shared stack area (PSW.IS = 1)
* ==============================================================================================*/
SECTIONS
{
.CPU0.stack : { . = ALIGN(8); __STACK_BASE_CPU0_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU0_
.CPU1.stack : { . = ALIGN(8); __STACK_BASE_CPU1_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU1_
.CPU2.stack : { . = ALIGN(8); __STACK_BASE_CPU2_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU2_
.CPU3.stack : { . = ALIGN(8); __STACK_BASE_CPU3_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU3_
.CPU4.stack : { . = ALIGN(8); __STACK_BASE_CPU4_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU4_
.CPU5.stack : { . = ALIGN(8); __STACK_BASE_CPU5_ = .; . += SHARED_STACK_SIZE; } > DATA_DSPR_CPU5_
}
第六步我们针对每个核心定义一些空的Section,用来放我们的用户代码,ram代码, 只读数据,普通数据以及没有初值的全局变量。
/* ================================================================================================
* SECTIONS: CORE 0 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU0.code : { } > CODE_CPU0_
.CPU0.ramcode : { } > RAMCODE_CPU0_ AT > RODATA_CPU0_
.CPU0.rodata : { } > RODATA_CPU0_
.CPU0.data : { } > DATA_DSPR_CPU0_ AT > RODATA_CPU0_
.CPU0.bss : { } > DATA_DSPR_CPU0_
}
/* ================================================================================================
* SECTIONS: CORE 1 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU1.code : { } > CODE_CPU1_
.CPU1.ramcode : { } > RAMCODE_CPU1_ AT > RODATA_CPU1_
.CPU1.rodata : { } > RODATA_CPU1_
.CPU1.data : { } > DATA_DSPR_CPU1_ AT > RODATA_CPU1_
.CPU1.bss : { } > DATA_DSPR_CPU1_
}
/* ================================================================================================
* SECTIONS: CORE 2 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU2.code : { } > CODE_CPU2_
.CPU2.ramcode : { } > RAMCODE_CPU2_ AT > RODATA_CPU2_
.CPU2.rodata : { } > RODATA_CPU2_
.CPU2.data : { } > DATA_DSPR_CPU2_ AT > RODATA_CPU2_
.CPU2.bss : { } > DATA_DSPR_CPU2_
}
/* ================================================================================================
* SECTIONS: CORE 3 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU3.code : { } > CODE_CPU3_
.CPU3.ramcode : { } > RAMCODE_CPU3_ AT > RODATA_CPU3_
.CPU3.rodata : { } > RODATA_CPU3_
.CPU3.data : { } > DATA_DSPR_CPU3_ AT > RODATA_CPU3_
.CPU3.bss : { } > DATA_DSPR_CPU3_
}
/* ================================================================================================
* SECTIONS: CORE 4 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU4.code : { } > CODE_CPU4_
.CPU4.ramcode : { } > RAMCODE_CPU4_ AT > RODATA_CPU4_
.CPU4.rodata : { } > RODATA_CPU4_
.CPU4.data : { } > DATA_DSPR_CPU4_ AT > RODATA_CPU4_
.CPU4.bss : { } > DATA_DSPR_CPU4_
}
/* ================================================================================================
* SECTIONS: CORE 5 dedicated sections
* In the example they are Empty, but user might used them to play
* with core specific placement
* ==============================================================================================*/
SECTIONS
{
.CPU5.code : { } > CODE_CPU5_
.CPU5.ramcode : { } > RAMCODE_CPU5_ AT > RODATA_CPU5_
.CPU5.rodata : { } > RODATA_CPU5_
.CPU5.data : { } > DATA_DSPR_CPU5_ AT > RODATA_CPU5_
.CPU5.bss : { } > DATA_DSPR_CPU5_
}
第七步我们定义默认的分区,用来承接代码中没有特别定义位置的代码与数据。
SECTIONS
{
/* Code sections */
.code :
{
*(.text*)
} > CODE
/* code to execute from RAM */
.ramcode :
{
*(.ramcode*)
} > RAM_CODE AT > RODATA
/* read-only data, constants */
.rodata :
{
*(.rodata*)
} > RODATA
/* C++ constructors */
.ctors : ALIGN(4) FLAGS(arl)
{
__CTOR_LIST__ = .;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
KEEP (*(.ctors));
LONG(0);
__CTOR_END__ = .;
} > RODATA
/* Short-addressable read-only data */
.sdata2 :
{
*(.sdata2*)
} > RODATA
/* Short-addresable read-write data - initialized */
.sdata :
{
*(.sdata*)
} > DATA AT > RODATA
/* Short-addresable read-write data - non-initialized (zeroed) */
.sbss :
{
*(.sbss*)
} > DATA
/* read-write data - initialized */
.data :
{
*(.data*)
} > DATA AT > RODATA
/* read-write data - non-initialized */
.bss :
{
*(.bss*);
*(COMMON);
} > DATA
/* HEAP area for stdlib functions */
.heap : ALIGN(8)
{
. += __HEAP_SIZE;
} > DATA
}
第八步我们定义各个核心CLEAR & COPY两个操作对应的地址列表。
/* ================================================================================================
* SECTIONS: CLEAR & COPY TABLES with END delimiter to support crt0 init
* clear_table:
* data memory ranges to clear to zero
* copy_table:
* data memory ranges that needs to be value initialized
* (init values are stored in FLASH and copied to RAM)
* Each core has its own table to process during its init to allow multicore execution.
* Shared resources are inserted to Core[0] tables (the RESET core)
* ==============================================================================================*/
SECTIONS
{
/*-------------------------------------------------------------------------------------------*/
.CPU0.clear_sec :
{
LONG(ADDR(.CPU0.bss)); LONG(SIZEOF(.CPU0.bss));
LONG(ADDR(.sbss)); LONG(SIZEOF(.sbss));
LONG(ADDR(.bss)); LONG(SIZEOF(.bss));
LONG(ADDR(.heap)); LONG(SIZEOF(.heap));
LONG(-1); LONG(-1);
} > RODATA_CPU0_
.CPU0.copy_sec :
{
LONG(LOADADDR(.CPU0.ramcode)); LONG(ADDR(.CPU0.ramcode)); LONG(SIZEOF(.CPU0.ramcode));
LONG(LOADADDR(.CPU0.data)); LONG(ADDR(.CPU0.data)); LONG(SIZEOF(.CPU0.data));
LONG(LOADADDR(.ramcode)); LONG(ADDR(.ramcode)); LONG(SIZEOF(.ramcode));
LONG(LOADADDR(.sdata)); LONG(ADDR(.sdata)); LONG(SIZEOF(.sdata));
LONG(LOADADDR(.data)); LONG(ADDR(.data)); LONG(SIZEOF(.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU0_
/*-------------------------------------------------------------------------------------------*/
.CPU1.clear_sec :
{
LONG(ADDR(.CPU1.bss)); LONG(SIZEOF(.CPU1.bss));
LONG(-1); LONG(-1);
} > RODATA_CPU1_
.CPU1.copy_sec :
{
LONG(LOADADDR(.CPU1.ramcode)); LONG(0 + ADDR(.CPU1.ramcode)); LONG(SIZEOF(.CPU1.ramcode));
LONG(LOADADDR(.CPU1.data)); LONG(ADDR(.CPU1.data)); LONG(SIZEOF(.CPU1.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU1_
/*-------------------------------------------------------------------------------------------*/
.CPU2.clear_sec :
{
LONG(ADDR(.CPU2.bss)); LONG(SIZEOF(.CPU2.bss));
LONG(-1); LONG(-1);
} > RODATA_CPU2_
.CPU2.copy_sec :
{
LONG(LOADADDR(.CPU2.ramcode)); LONG(0 + ADDR(.CPU2.ramcode)); LONG(SIZEOF(.CPU2.ramcode));
LONG(LOADADDR(.CPU2.data)); LONG(0 + ADDR(.CPU2.data)); LONG(SIZEOF(.CPU2.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU2_
/*-------------------------------------------------------------------------------------------*/
.CPU3.clear_sec :
{
LONG(ADDR(.CPU3.bss)); LONG(SIZEOF(.CPU3.bss));
LONG(-1); LONG(-1);
} > RODATA_CPU3_
.CPU3.copy_sec :
{
LONG(LOADADDR(.CPU3.ramcode)); LONG(0 + ADDR(.CPU3.ramcode)); LONG(SIZEOF(.CPU3.ramcode));
LONG(LOADADDR(.CPU3.data)); LONG(0 + ADDR(.CPU3.data)); LONG(SIZEOF(.CPU3.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU3_
/*-------------------------------------------------------------------------------------------*/
.CPU4.clear_sec :
{
LONG(ADDR(.CPU4.bss)); LONG(SIZEOF(.CPU4.bss));
LONG(-1); LONG(-1);
} > RODATA_CPU4_
.CPU4.copy_sec :
{
LONG(LOADADDR(.CPU4.ramcode)); LONG(0 + ADDR(.CPU4.ramcode)); LONG(SIZEOF(.CPU4.ramcode));
LONG(LOADADDR(.CPU4.data)); LONG(0 + ADDR(.CPU4.data)); LONG(SIZEOF(.CPU4.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU4_
/*-------------------------------------------------------------------------------------------*/
.CPU5.clear_sec :
{
LONG(ADDR(.CPU5.bss)); LONG(SIZEOF(.CPU5.bss));
LONG(-1); LONG(-1);
} > RODATA_CPU5_
.CPU5.copy_sec :
{
LONG(LOADADDR(.CPU5.ramcode)); LONG(0 + ADDR(.CPU5.ramcode)); LONG(SIZEOF(.CPU5.ramcode));
LONG(LOADADDR(.CPU5.data)); LONG(0 + ADDR(.CPU5.data)); LONG(SIZEOF(.CPU5.data));
LONG(-1); LONG(-1); LONG(-1);
} > RODATA_CPU5_
}
第九步我们定义一些后边要用的符号。
/* ================================================================================================
* Linker Symbols
* ==============================================================================================*/
/* Read only small address pointer */
_SMALL_DATA2_ = ADDR(.sdata2) + 0x8000;
/* Volatile data short address pointer (.sdata before .sbss) */
_SMALL_DATA_ = ADDR(.sdata) + 0x8000;
/* Expected HEAP SYMBOLS */
__HEAP = ADDR(.heap);
__HEAP_END = SIZEOF(.heap);
第十步我们为代码提供__crt0_config符号,代码可以使用这个符号获得crt0_config对应的地址,从而完成core0的启动配置。
/* ================================================================================================
* CRT0 CONFIG STRUCTURE
* Initialization structure for uC Cores used in crt0 startup code
* Each core has itw one table entry.
* ==============================================================================================*/
SECTIONS
{
.crt0_config : ALIGN(4)
{
PROVIDE(__crt0_config = .); /* base of the crt0 config table */
/* Core[0] */
LONG(ADDR(.CPU0.stack)); /* STACK address */
LONG(SIZEOF(.CPU0.stack)); /* STACK size */
LONG(ADDR(.CPU0.csa)); /* CSA address */
LONG(SIZEOF(.CPU0.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA2 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU0.clear_sec)); /* CLEAR table */
LONG(ADDR(.CPU0.copy_sec)); /* COPY table */
LONG(__CTOR_LIST__); /* CTOR table */
/* Core[1] */
LONG(ADDR(.CPU1.stack)); /* STACK address */
LONG(SIZEOF(.CPU1.stack)); /* STACK size */
LONG(ADDR(.CPU1.csa)); /* CSA address */
LONG(SIZEOF(.CPU1.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA2 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU1.clear_sec) ); /* CLEAR table */
LONG(ADDR(.CPU1.copy_sec)); /* COPY table */
LONG(0); /* CTOR table */
/* Core[2] */
LONG(ADDR(.CPU2.stack)); /* STACK address */
LONG(SIZEOF(.CPU2.stack)); /* STACK size */
LONG(ADDR(.CPU2.csa)); /* CSA address */
LONG(SIZEOF(.CPU2.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA2 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU2.clear_sec)); /* CLEAR table */
LONG(ADDR(.CPU2.copy_sec)); /* COPY table */
LONG(0); /* CTOR table */
/* Core[3] */
LONG(ADDR(.CPU3.stack)); /* STACK address */
LONG(SIZEOF(.CPU3.stack)); /* STACK size */
LONG(ADDR(.CPU3.csa)); /* CSA address */
LONG(SIZEOF(.CPU3.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA3 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU3.clear_sec)); /* CLEAR table */
LONG(ADDR(.CPU3.copy_sec)); /* COPY table */
LONG(0); /* CTOR table */
/* Core[4] */
LONG(ADDR(.CPU4.stack)); /* STACK address */
LONG(SIZEOF(.CPU4.stack)); /* STACK size */
LONG(ADDR(.CPU4.csa)); /* CSA address */
LONG(SIZEOF(.CPU4.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA3 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU4.clear_sec)); /* CLEAR table */
LONG(ADDR(.CPU4.copy_sec)); /* COPY table */
LONG(0); /* CTOR table */
/* Dummy entry due to CoreId index gap in TC39x */
LONG(0); /* STACK address */
LONG(0); /* STACK size */
LONG(0); /* CSA address */
LONG(0); /* CSA size */
LONG(0); /* SDATA address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(0); /* CLEAR table */
LONG(0); /* COPY table */
LONG(0); /* CTOR table */
/* Core[5] */
LONG(ADDR(.CPU5.stack)); /* STACK address */
LONG(SIZEOF(.CPU5.stack)); /* STACK size */
LONG(ADDR(.CPU5.csa)); /* CSA address */
LONG(SIZEOF(.CPU5.csa)); /* CSA size */
LONG(_SMALL_DATA_); /* SDATA address */
LONG(_SMALL_DATA2_); /* SDATA3 address */
LONG(0); /* SDATA3 address */
LONG(0); /* SDATA4 address */
LONG(ADDR(.CPU5.clear_sec)); /* CLEAR table */
LONG(ADDR(.CPU5.copy_sec)); /* COPY table */
LONG(0); /* CTOR table */
} > RODATA
}
具体对应的启动代码可以根据预先定义的排布获得需要配置到核心的地址,诸如中断向量表等,代码如下。
.section .text, "ax"
_start:
/* ------------------------------------------------------------------------------------------------
* CRT0 CONFIG TABLE BASE POINTER SETUP
*
* Config table contains parameters controlling crt0 startup execution.
* It is prepared by the linker file with the knowledge of the final placement.
*
* Registers used by the crt0 startup code
* 'A14` : is used as Core Entry Base Pointer in crt0 configuration structure
* throughout the statup asm code.
*
* The A14 register value is saved by Aurix core in upper context during subroutine calls.
* ----------------------------------------------------------------------------------------------*/
movh.a %a14, hi:__crt0_config
lea %a14, [%a14]lo:__crt0_config
mfcr %d15, $core_id /* read CoreID(0xfe1c) SFR register */
and %d15, 7 /* mask the lowest 3 bits */
mul %d15, %d15, CPUINIT_SIZE /* get the core entry base address */
addsc.a %a14, %a14, %d15, 0
/* ------------------------------------------------------------------------------------------------
* SMALL ADDRESS REGISTERS INIT
*
* Values given by crt0 configuration structure from the linker file.
*
* Four dedicated registers, if they are used
* a0 - usually small data (rw)
* a1 - usually small const data (r)
* a8 - usually OS / application specific
* a9 - usually OS / application specific
* ----------------------------------------------------------------------------------------------*/
ld.w %d15, [%a14] SDATA
mov.a %a0, %d15
ld.w %d15, [%a14] SDATA2
mov.a %a1, %d15
ld.w %d15, [%a14] SDATA3
mov.a %a8, %d15
ld.w %d15, [%a14] SDATA4
mov.a %a9, %d15
/* ------------------------------------------------------------------------------------------------
* CSA CONTEXT AREA INIT
*
* Linked list initialization of CSA entries (TriCore specific feature) used to save
* function context during standard 'C' function calls.
*
* CSA entry and linked list has fixed structure given by AURIX architecture.
* Number of CSA entries (max 256 entries) is part of crt0 configuration in the linker file.
* ----------------------------------------------------------------------------------------------*/
mov %d4, 0
mtcr $pcxi, %d4 /* clear pcxi SFR */
ld.w %d4, [%a14] CSA_SIZE /* load number of CSA entries to initialize */
sh %d4, %d4, LW_OFFSET_SHIFT /* get number of entries : SIZE / 64, where 64 = CSA entry size */
mov.a %a4, %d4 /* a4 <= loop counter = number of CSA entries */
add.a %a4, -1 /* decrement to take into account value 0 in loop */
mov %d4, CSA_ENTRY_SIZE
mov.a %a3, %d4 /* a3 <= 64 (CSA Entry Size) */
ld.w %d4, [%a14] CSA /* load content pointed by %a14+CSA to data register */
mov.a %a15, %d4 /* and store it in %a15 address register for later use */
movh %d5, 0x000F /* prepare mask for segment in Link Word */
mov.d %d15, %a15 /* store CSA Base address to Data register for next data operations */
sh %d15, %d15, -12 /* move Segment bits to right position in Link Word */
and %d15, %d5 /* d15 <= masked segment bits of Link Word */
mov.u %d5, 0xFFFF /* prepare mask for CSA offset in Link Word */
mov.d %d4, %a15 /* store CSA base address to data register for next data operations */
sh %d4, %d4, LW_OFFSET_SHIFT /* shift offset 6 bits to right to right Link Word offset position */
and %d4, %d5 /* mask offset bits, zero the others */
or %d4, %d15 /* d4 <= Link Word (Segment + Offset) */
mtcr $fcx, %d4 /* store first CSA entry in FXC SFR register */
loop_csa:
add %d4, 1 /* prepare address of next CSA entry */
st.w [%a15], %d4 /* store it in current link word to point to next CSA entry in list */
add.a %a15, %a3 /* move address pointer to next CSA entry in the list */
loop %a4, loop_csa /* repeat as many time as we have entries configured in the linker file */
mtcr $lcx, %d4 /* store last link word content to LCX SFR register */
/* ------------------------------------------------------------------------------------------------
* STACK INIT
*
* Mandatory operation before calling any 'C' function
*
* Two things to do
* 1. correct ECC checksum syndroms for complete Stack area by writing with required
* data size instructions
* 2. Stack pointer init used by 'C' code
*
* Startup code initialize both TriCore stack pointers, User and Interrupt, to the same area.
* - the code runs with 'PSW.IS = 1' after the reset -> shared stack
* - the separation of User and Interrupt stack is left on Application (usually OS)
* later on.
* ----------------------------------------------------------------------------------------------*/
ld.w %d4, [%a14] STACK /* Load Stack bottom address value */
mov.a %a4, %d4 /* and store it to address register %a4 */
ld.w %d4, [%a14] STACK_SIZE /* Load stack size */
mov.d %d1, %a4 /* Store start address into the data reg */
add %d1, %d1, %d4 /* End address of region is base stack add. */
mov.a %sp, %d1 /* Set stack register A[10] */
mtcr $isp, %d1 /* Set stack register ISP */
call clear_exec /* Call Clear routine */
/* ------------------------------------------------------------------------------------------------
* CRT0 PRE-INIT 'C' USER CODE
*
* Chance for user to execute HW init at the very beginning, before longer operations
* take place, like memory clear and copy of init data from Flash to RAM.
*
* In case of CORE dependent Hook execution,
* the application must read it ourselves (physical CoreID might not correspond
* to a consecutive sequence needed for array operations).
*
* Pre-init code MUST rely only on Stack variables only !
* ----------------------------------------------------------------------------------------------*/
call Crt0PreInit
/* ------------------------------------------------------------------------------------------------
* CLEAR .BSS SECTIONS
*
* Areas to clear are given in the __clear_table config entry.
* The crt0 function is of WEAK type to allow the user implementation in the application
* by for example by 'C' specific routine
* ----------------------------------------------------------------------------------------------*/
ld.w %d4, [%a14] CLEAR_TABLE
mov.a %a4, %d4
call Crt0BssInit
/* ------------------------------------------------------------------------------------------------
* COPY INITIALIZED DATA
*
* Initialization of data regions provided in __copy table in crt0 configuration structure.
* The crt0 function is of WEAK type to allow the user implementation in the application.
* ----------------------------------------------------------------------------------------------*/
ld.w %d4, [%a14] COPY_TABLE
mov.a %a4, %d4
call Crt0DataInit
/* ------------------------------------------------------------------------------------------------
* C++ GLOBAL OBJECT INITIALIZATION
*
* The ctor table (constructors to call) is provided as one of the crt0_configr structure entry.
* Each core can have its own ctor table array, if implemented in the linker file
* (not in BSP case)
* ----------------------------------------------------------------------------------------------*/
ld.w %d4, [%a14] CTOR_TABLE
mov.a %a4, %d4
call Crt0CtorInit
/* ------------------------------------------------------------------------------------------------
* CRT0 POST-INIT 'C' USER CODE
*
* Chance for user to execute specific code before jump to application entry,
* 'shared main()' in case of BSP
*
* In case of core dependent Hook execution,
* the application must read it ourselves (physical CoreID might not correspond
* to a consecutive sequence needed for array operations).
* ----------------------------------------------------------------------------------------------*/
call Crt0PostInit
/* -----------------------------------------------------------------------------------------------
* CRT0 END : ENTRY TO APPLICATION
*
* Jump to the application entry point, shared across all cores in case of BSP examples
*
* In case of core dependent Hook execution, the application must read it ourselves,
* physical CoreID might not correspond to a consecutive sequence needed for array operations
*
* The return from the application is not expected, hard to say what the embedded system
* shall do here
* ----------------------------------------------------------------------------------------------*/
call shared_main
_exit:
debug /* debug stop in case of active debugging process,
otherwise 'nop' instruction */
j . /* infinitive loop, waiting for eventual timeout watchdog */
/* ================================================================================================
* CRT0 'DEFAULT' FUNCTIONS
*
* Implemented as WEAK to allow their replacement by the user versions in the application
* ==============================================================================================*/
/* ------------------------------------------------------------------------------------------------
* FUNCTION: Crt0PreInit
*
* User hook before 'C' runtime initialization. Empty routine in case of crt0 startup code.
* ----------------------------------------------------------------------------------------------*/
.weak Crt0PreInit
.type Crt0PreInit, %function
Crt0PreInit:
ret
/* ------------------------------------------------------------------------------------------------
* FUNCTION: Crt0PostInit
*
* User hook after 'C' runtime initialization. Empty routine in case of crt0 startup code.
* ----------------------------------------------------------------------------------------------*/
.weak Crt0PostInit
.type Crt0PostInit, %function
Crt0PostInit:
ret
/* ------------------------------------------------------------------------------------------------
* FUNCTION: Crt0BssInit
*
* Default Crt0 BSS clear function. It goes through clear_table entries and calls the clear
* operation for each of them
*
* Input
* A[4] : core's clear_table base pointer
* ----------------------------------------------------------------------------------------------*/
.weak Crt0BssInit
.type Crt0BssInit, %function
Crt0BssInit:
mov.aa %a13, %a4 /* Local pointer for clear_table entry */
mov.a %a12, CLEAR_TABLE_OFFSET /* Clear_table next entry offset */
_table_bss_clear_loop:
ld.w %d15, [%a13] CLEAR_TABLE_DST /* Base address of the area to clear */
jeq %d15, -1, _table_bss_clear_loop_end /* Checks table termination value -1, */
mov.a %a4, %d15 /* Prepare area start pointer for clear routine */
ld.w %d4, [%a13] CLEAR_TABLE_SIZE /* Get size of the area*/
call clear_exec /* Call Clear routine with saving Upper Context */
add.a %a13,%a12 /* Next row from BSS clear table */
j _table_bss_clear_loop
_table_bss_clear_loop_end:
ret
/* ------------------------------------------------------------------------------------------------
* FUNCTION: Crt0DataInit
*
* Default Crt0 DATA init function. It goes through copy_table entries and calls
* copy operation for each of them.
*
* Input
* A[4] : core's copy_table pointer
* ----------------------------------------------------------------------------------------------*/
.weak Crt0DataInit
.type Crt0DataInit, %function
Crt0DataInit:
mov.aa %a13, %a4 /* Local Pointer for copy table */
mov.a %a12, COPY_TABLE_OFFSET /* Copy table item offset in bytes */
_table_data_copy_loop:
ld.w %d15, [%a13]COPY_TABLE_DST /* Start address of the destination copy area */
jeq %d15, -1, _table_data_copy_loop_end /* Checks table termination value -1, */
mov.a %a4, %d15
ld.w %d4, [%a13]COPY_TABLE_SRC /* First Address of the source copy table */
mov.a %a5, %d4 /* store it into address register %a5 */
ld.w %d4, [%a13]COPY_TABLE_SIZE /* Get size of the area*/
call copy_exec /* Call Copy routine */
add.a %a13,%a12 /* Next row from BSS copy table */
j _table_data_copy_loop
_table_data_copy_loop_end:
ret
/* ------------------------------------------------------------------------------------------------
* FUNCTION: Crt0CtorInit
*
* Default global C++ object initialization. It goes through ctor table and calls
* global constructors.
*
* Input
* A[4] : CTOR table base address
* ----------------------------------------------------------------------------------------------*/
.weak Crt0CtorInit
.type Crt0CtorInit, %function
Crt0CtorInit:
jz.a %a4, _ctor_exec_end /* check against no table present */
ld.w %d4, [%a4+]4 /* get number of entries */
mov.a %a15, %d4 /* and store it into address register %a15 */
jz.a %a15, _ctor_exec_end /* check against no entry (size = 0) */
add.a %a15, -1 /* consider always one 'loop' execution */
_ctor_exec_loop:
ld.w %d4, [%a4+]4 /* read the function pointer */
mov.a %a13, %d4 /* and store it into the address register %a13 */
calli %a13 /* call the function */
loop %a15,_ctor_exec_loop /* go through all functions */
_ctor_exec_end:
ret
/* ================================================================================================
* MODULE LOCAL ROUTINES
*
* Used only within this module
* ==============================================================================================*/
/* ------------------------------------------------------------------------------------------------
* FUNCTION: clear_exec
*
* Executes the erase loop from start address for specified number of bytes.
* It uses 64bit Store instruction
*
* Input
* A[4] : start address
* D[4] : size in bytes
* ----------------------------------------------------------------------------------------------*/
clear_exec:
jz %d4,_clear_exec_end /* Return if size is zero */
add %d4,-1 /* decrement to take into account always one loop execution */
sh %d4,-3 /* adjustment of the clear loop for the double word write instruction */
mov.a %a15,%d4 /* init loop counter */
mov %e14,0 /* Zero value */
_clear_exec_loop:
st.d [%a4+]8,%e14 /* Store 64bit value */
loop %a15,_clear_exec_loop /* execution loop until zero */
_clear_exec_end:
ret
/* ------------------------------------------------------------------------------------------------
* FUNCTION: copy_exec
*
* Executes the copy loop from start address to end address.
* Routine is simple Byte copy without any optimization.
*
* Input
* A[4] : start write address
* A[5] : start read address
* D[4] : size in bytes
* ----------------------------------------------------------------------------------------------*/
copy_exec:
mov %d15,%d4
jz %d15,_copy_exec_end /* Return if size is zero */
add %d15,-1 /* decrement to take into account value 0 in loop */
mov.a %a15,%d15
_copy_exec_loop:
ld.b %d15, [%a5+]1
st.b [%a4+]1, %d15
loop %a15,_copy_exec_loop
_copy_exec_end:
ret
最后,我们完成一些调试和其他功能用到的section定义。
/* ================================================================================================
* DWARF debug sections and others
* Symbols in the DWARF debugging sections are relative to the
* beginning of the section, so we begin them at 0.
* ==============================================================================================*/
SECTIONS
{
/* DWARF 1 */
.comment 0 : { *(.comment) }
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF 2 control flow extension */
.debug_control_flow 0 : { *(.debug_control_flow) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.version_info 0 : { *(.version_info) }
}
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。