10. Analysis of the .lsl file (What is a Linker Script file?)

Toby
4 min readSep 16, 2023

--

Hello,

Today, we will explore the Lcf_Tasking_Tricore_Tc.lsl file.

Before we dive into the file, let’s understand how our previously created Driver_Stm.c file is read by the CPU and transformed into executable instructions.

Upon navigating to the Debug folder, you’ll find similar files.

Firstly, the compiler transforms the Driver_Stm.c file into an object file named Driver_Stm.o.

This file is then unified into a single file using a linker.

From this process, we get files that can be downloaded to the MCU, examples being .elf and .hex files.

So, what’s the difference between .elf and .hex files?

The .elf file includes additional Debug Symbol information, which I will elaborate on later.

In essence, the compiler initially turns all .c files into .o (object files), and during the process of merging these, a Linker Script file becomes necessary.

The memory allocation for functions created in the .c file is determined by the Linker Script file.

Linker Script files differ in syntax depending on the compiler, but the general flow remains the same. The most crucial section involves memory allocation.

You can view this memory allocation through a .map file. Observing the .map file of our previously created code, we can see that the .text.start code is located at 0x80000020. In other words, when deciding the memory location of our code, we can modify the linker script file and verify it through the .map file.

Now, let’s open the Linker Script file. Our current AURIX-Studio utilizes the Tasking compiler. Hence, it follows the syntax of the Tasking compiler.

//Specifies the size of the stack to be used by Core0.
//As I mentioned before, the Tasking compiler stack is divided into USTACK, ISTACK, and CSA.
#define LCF_CSA0_SIZE 8k
#define LCF_USTACK0_SIZE 2k
#define LCF_ISTACK0_SIZE 1k
// This is the stack size that Core1 will use.
#define LCF_CSA1_SIZE 8k
#define LCF_USTACK1_SIZE 2k
#define LCF_ISTACK1_SIZE 1k
// This is the stack size that Core2 will use.
#define LCF_CSA2_SIZE 8k
#define LCF_USTACK2_SIZE 2k
#define LCF_ISTACK2_SIZE 1k
// Heap area to be dynamically allocated.
#define LCF_HEAP_SIZE 4k

#define LCF_CPU0 0
#define LCF_CPU1 1
#define LCF_CPU2 2

/*Un comment one of the below statements to enable CpuX DMI RAM to hold global variables*/
/*#define LCF_DEFAULT_HOST LCF_CPU0*/
#define LCF_DEFAULT_HOST LCF_CPU1
/*#define LCF_DEFAULT_HOST LCF_CPU2*/

//This part shows the starting address and size of the RAM area.
#define LCF_DSPR2_START 0x50000000
#define LCF_DSPR2_SIZE 120k

#define LCF_DSPR1_START 0x60000000
#define LCF_DSPR1_SIZE 120k

#define LCF_DSPR0_START 0x70000000
#define LCF_DSPR0_SIZE 112k

//Stack allocates and uses a portion of RAM.
// So, this is the part that calculates the OFFSET value and specifies the starting address of the stack.

#define LCF_CSA2_OFFSET (LCF_DSPR2_SIZE - 1k - LCF_CSA2_SIZE)
#define LCF_ISTACK2_OFFSET (LCF_CSA2_OFFSET - 256 - LCF_ISTACK2_SIZE)
#define LCF_USTACK2_OFFSET (LCF_ISTACK2_OFFSET - 256 - LCF_USTACK2_SIZE)

#define LCF_CSA1_OFFSET (LCF_DSPR1_SIZE - 1k - LCF_CSA1_SIZE)
#define LCF_ISTACK1_OFFSET (LCF_CSA1_OFFSET - 256 - LCF_ISTACK1_SIZE)
#define LCF_USTACK1_OFFSET (LCF_ISTACK1_OFFSET - 256 - LCF_USTACK1_SIZE)

#define LCF_CSA0_OFFSET (LCF_DSPR0_SIZE - 1k - LCF_CSA0_SIZE)
#define LCF_ISTACK0_OFFSET (LCF_CSA0_OFFSET - 256 - LCF_ISTACK0_SIZE)
#define LCF_USTACK0_OFFSET (LCF_ISTACK0_OFFSET - 256 - LCF_USTACK0_SIZE)

//This is the beginning of Heap.
#define LCF_HEAP0_OFFSET (LCF_USTACK0_OFFSET - LCF_HEAP_SIZE)
#define LCF_HEAP1_OFFSET (LCF_USTACK1_OFFSET - LCF_HEAP_SIZE)
#define LCF_HEAP2_OFFSET (LCF_USTACK2_OFFSET - LCF_HEAP_SIZE)

//This is the starting address of the interrupt table when an interrupt occurs.
//For example, the table is located starting from the 0x801F4000 area.
#define LCF_INTVEC0_START 0x801F4000
#define LCF_INTVEC1_START 0x801F5000
#define LCF_INTVEC2_START 0x801F3000
//This is the starting memory allocation for Trap.
#define LCF_TRAPVEC0_START 0x80000100
#define LCF_TRAPVEC1_START 0x801F6200
#define LCF_TRAPVEC2_START 0x801F6000

#define INTTAB0 (LCF_INTVEC0_START)
#define INTTAB1 (LCF_INTVEC1_START)
#define INTTAB2 (LCF_INTVEC2_START)

#define TRAPTAB0 (LCF_TRAPVEC0_START)
#define TRAPTAB1 (LCF_TRAPVEC1_START)
#define TRAPTAB2 (LCF_TRAPVEC2_START)

//And the address at the beginning of the Start Code is assigned like this.
#define RESET 0x80000020

This is a brief overview of the Linker Script file.

We will discuss this in detail later.

To summarize, memory has been allocated in this manner.

Knowing this level of detail will be beneficial.

For instance, for Stack allocation, RAM is used, while Flash is employed for Trap and Interrupt Table allocations.

For Core0, RAM is allocated at 0x70000000 and Flash at 0x80000000.

This isn’t randomly allocated but is aligned with the MCU’s memory map.

If you refer to the reference manual, you’ll notice that memory addresses differentiate RAM and Flash regions.

0x70000000 corresponds to the DRAM used by CPU0, and 0x80000000 refers to the PF0 Program Flash0 region.

When creating a project, manually creating a Linker Script file would be very challenging.

Hence, Infineon provides a default Linker Script file, which most users utilize as a base.

However, this base file is well-designed to fit the MCU. In the future, we may need to modify this Linker Script file to fit our requirements, positioning our custom functions at our desired locations.

Understanding the broader picture will suffice for now.

If you need the code, please leave a comment on the post.

--

--