This chapter contains the following sections:
Introduction
Linking Process
Phase 1: Linking
Phase 2: Locating
Linker Optimizations
Linking with Libraries
How the Linker Searches Libraries
Specifying Libraries to the Linker
How the Linker Extracts Objects from the LibraryLibraries
Controlling the Linker with a Script
Purpose of the Linker Script Language
EDE and LSL
Structure of a Linker Script File
The Architecture Definition: Self-Designd Cores
The Derivative Definition: Self-Designd Processors
The Memory Definition: Defining External Memory
The Section Layout Definition: Locating Sections
The Processor Definition: Using Multi-Processor Systems
The linker ltc is a combined linker/locator. The linker phase combines relocatable object files (.o files, generated by the assembler), and libraries into a single relocatable linker object file (.out). The locator phase assigns absolute addresses to the linker object file and creates an absolute object file which you can load into a target processor. From this point the term linker is used for the combined linker/locator.
The linker takes the following files for input and output:
Figure 7-1: ltc Linker
This chapter first describes the linking process. Then it describes how to call the linker and how to use its options. An extensive list of all options and their descriptions is included in section 4.3 , Linker Options, of the Reference Guide.
To gain even more control over the link process, you can write a script for the linker. This chapter shortly describes the purpose and basic principles of the Linker Script Language (LSL) on the basis of an example. A complete description of the LSL is included in Chapter 7 , Linker Script Language, of the Reference Guide.
Finally, a few important features are described such as overlaying and choosing among various output formats.
The linker combines and transforms relocatable object files (.o) into a single absolute object file. This process consists of two phases: the linking phase and the locating phase.
In the first phase the linker combines the supplied relocatable object files and libraries into a single relocatable object file. In the second phase, the linker assigns absolute addresses to the object file so it can actually be loaded into a target.
Term | Definition |
Absolute object file | Object code in which addresses have fixed absolute values, ready to load into a target. |
Address | A specification of a location in an address space. |
Address space | The set of possible addresses in a given space. A core can support multiple spaces, for example in a Harvard architecture the addresses that identify the location of an instruction refer to code space, whereas addresses that identify the location of a data object refer to a data space. |
Architecture | A description of the characteristics of a core that are of interest for the linker. This encompasses the logical address space(s) and the internal bus structure. Given this information the linker can convert logical addresses into physical addresses. |
Copy table |
A section created by the linker. This section contains data that specifies how the startup code initializes the data and BSS sections. For each section the copy table contains the following fields: - action: defines whether a section is copied or zeroed - destination: defines the section's address in RAM. - source: defines the sections address in ROM, zero for BSS sections - length: defines the size of the section in MAUs |
Core | An instance of one or more core architectures. |
Derivative | The design of a processor. A description of one or more core architectures including internal memory and any number of buses. |
Library |
Collection of relocatable object files. Usually each object file in a library contains one symbol definition (for example, a function). |
Logical address | An address as encoded in an instruction word, an address generated by a core (CPU). |
LSL file | The set of linker script files that are passed to the linker. |
MAU | Minimum Addressable Unit. For a given processor the amount of memory loaded between an address and the next address. This is not necessarily a byte or a word. |
Object code | The binary machine language representation of the C source. |
Physical address | An addresses generated by the memory system. |
Processor | An instance of one or more derivatives. Usually implemented as a (custom) chip, but can also be implemented on an FPGA, in which case the derivative can be designed by the developer. |
Relocatable object file | Object code in which addresses are represented by symbols and thus relocatable. |
Relocation | The process of assigning absolute addresses. |
Relocation information | Information about how the linker must modify the machine code instructions when it relocates addresses. |
Section | A group of instructions and/or data objects that occupy a contiguous range of addresses. |
Section attributes | Attributes that define how the section should be linked or located. |
Target | The hardware board on which an application is executing. A board contains at least one processor. However, a complex target may contain multiple processors and external memory and may be shared between processors. |
Unresolved reference | A reference to a symbol for which the linker did not find a definition yet. |
Table 7-1: Glossary of terms
The linker takes one or more relocatable object files and/or libraries as input. A relocatable object file, as generated by the assembler, contains the following information:
The linker's first task is to resolve the external references between the supplied relocatable object files and/or libraries. Its second task is to combine the supplied relocatable object files into a single relocatable linker object file.
The linker starts its task by scanning all specified relocatable object files and libraries. If the linker encounters an unresolved symbol, it remembers its name and continues scanning. The symbol may be defined elsewhere in the same file, or in one of the other files or libraries that you specified to the linker. If the symbol is defined in a library, the linker extracts the object file with the symbol definition from the library. This way the linker collects all definitions and references of all of the symbols.
With this information, the linker combines the object code of all relocatable object files. The linker combines sections with the same section name and attributes into single sections, starting each section at address zero. The linker also substitutes (external) symbol references by (relocatable) numerical addresses where possible. At the end of the linking phase, the linker either writes the results to a file (a single relocatable object file) or keeps the results in memory for further processing during the locating phase.
The resulting file of the linking phase is a single relocatable object file (.out). If this file contains unresolved references, you can link this file with other relocatable object files (.o) or libraries (.a) to resolve the remaining unresolved references.
With the linker command line option --link-only, you can tell the linker to only perform this linking phase and skip the locating phase. The linker complains if any unresolved references are left.
In the locating phase, the linker assigns absolute addresses to the object code, placing each section in a specific part of the target memory. The linker also replaces references to symbols by the actual address of those symbols. The resulting file is an absolute object file which you can actually load into a target memory. Optionally, when the resulting file should be loaded into a ROM device the linker creates a so-called copy table section which is used by the startup code to initialize the data and BSS sections.
When the linker assigns absolute addresses to the object code, it needs to modify this code according to certain rules or relocation expressions to reflect the new addresses. These relocation expressions are stored in the relocatable object file. Consider the following snippet of x86 code that moves the contents of variable a to variable b via the eax register:
A1 3412 0000 mov a,%eax (a defined at 0x1234, byte reversed) A3 0000 0000 mov %eax,b (b is imported so the instruction refers to 0x0000 since its location is unknown)
Now assume that the linker links this code so that the section in which a is located is relocated by 0x10000 bytes, and b turns out to be at 0x9A12. The linker modifies the code to be:
A1 3412 0100 mov a,%eax (0x10000 added to the address) A3 129A 0000 mov %eax,b (0x9A12 patched in for b)
These adjustments affect instructions, but keep in mind that any pointers in the data part of a relocatable object file have to be modified as well.
The linker can produce its output in different file formats. The default ELF/DWARF2 format (.elf) contains an image of the executable code and data, and can contain additional debug information. The Intel-Hex format (.hex) and Motorola S-record format (.sre) only contain an image of the executable code and data. You can specify a format with the options -F (--format) and -c (--chip-format).
Via a so-called linker script file you can gain complete control over the linker. The script language used to describe these features is called the Linker Script Language (LSL). You can define:
Instead of writing your own linker script file, you can
use the standard linker script files with architecture descriptions for the
TriCore.
See also section 7.6
, Controlling the Linker with a Script.
During the linking and locating phase, the linker looks for opportunities to optimize the object code. Both code size and execution speed can be optimized. To enable or disable optimizations:
1. From the Project menu, select Project Options...
2. Expand the Linker entry and select Optimization.
You can enable or disable the optimizations described below. The command line option for each optimization is given in brackets.
See also option
-O (--optimize)
in section 4.3,
Linker Options, in Chapter Tool Options of the TriCore
Reference Guide.
When the physical memory is fragmented or when address spaces are nested it may be possible that a given application cannot be located although the size of available physical memory is larger than the sum of the section sizes. Enable the first-fit-decreasing optimization when this occurs and re-link your application.
The linker's default behavior is to place sections in the order that is specified in the LSL file. This also applies to sections within an unrestricted group. If a memory range is partially filled and a section must be located that is larger than the remainder of this range, then the section and all subsequent sections are placed in a next memory range. As a result of this gaps occur at the end of a memory range.
When the first-fit-decreasing optimization is enabled the linker will first place the largest sections in the smallest memory ranges that can contain the section. Small sections are located last and can likely fit in the remaining gaps.
The startup code initializes the application's data and BSS areas. The information about which memory addresses should be zeroed (bss) and which memory ranges should be copied from ROM to RAM is stored in the copy table.
When this optimization is enabled the linker will try to locate sections in such a way that the copy table is as small as possible thereby reducing the application's ROM image.
EDE uses a makefile to build your entire project. This means that you cannot run only the linker. However, you can set options specific for the linker. After you have build your project, the output files of the linking step are available in your project directory.
To link your program, click either one of the following buttons:
Builds your entire project but only updates files that are out-of-date or have been changed since the previous build, which saves time.
Builds your entire project unconditionally. All steps necessary to obtain the final .elf file are performed.
To get access to the linker options:
1. From the Project menu, select Project Options...
2. Expand the Linker entry. Select the subentries and set the options in the various pages.
The following linker options are available:
EDE options | Command line | ||||||||||||||||||||||||||||||||||||
Output Format | |||||||||||||||||||||||||||||||||||||
Output formats | -Fformat -cformat[:addr_size] | ||||||||||||||||||||||||||||||||||||
Script File | |||||||||||||||||||||||||||||||||||||
Select linker script file |
-dfile
Map File |
|
Generate a map file (.map) |
-M |
Suboptions for the Generate a map file
option |
-mflags |
Libraries |
|
Link default C libraries | Use non-trapping floating point library Use trapping floating point library
-lx | -lfp -lfpt
Rescan libraries to solve unresolved exernals |
--no-rescan |
Libraries |
library files |
Optimization |
|
Use a 'first fit decreasing' algorithm | Emit smart restrictions to reduce copy table size
-Ol/-OL
(= on/off) | -Ot/-OT
Warnings |
|
Report all warnings | Suppress all warnings Suppress specific warnings
no option -w | -w -wnum[,num]...
Treat warnings as errors |
--warnings-as-errors |
Miscellaneous |
|
Include symbolic debug information |
-S (strip debug) |
Print the name of each file as it is processed |
-v |
Link case sensitive (required for C language) |
--case-sensitive |
Dump processor and memory info from LSL file |
--lsl-dump[=file] |
Additional command line options |
options | |
Table 7-2: Linker options
The following options are only available on the command line:
Description | Command line | ||||||||||||||||||||||||
Display invocation syntax | -? | ||||||||||||||||||||||||
Define preprocessor macro | -Dmacro[=def ] | ||||||||||||||||||||||||
Show description of diagnostic(s) | --diag=[fmt:]{all|nr,...} | ||||||||||||||||||||||||
Specify a symbol as unresolved external |
-esymbol
Redirect errors to a file with extension .elk |
--error-file[=file] |
Read options from file |
-f file |
Scan libraries in given order |
--first-library-first |
Search only in -L directories, not in default path |
--ignore-default- | library-path
Keep output files after errors |
-k |
Link only, do not locate |
--link-only
|
Check LSL file(s) and exit |
--lsl-check |
Do not generate ROM copy |
-N |
Locate all ROM sections in RAM |
--non-romable |
Name of the resulting output file |
-o file |
Link incrementally |
-r |
Display version header only |
-V | |
Table 7-3: Linker command line options
The invocation syntax on the command
line is:
When you are linking multiple files (either relocatable object files (.o) or libraries (.a), it is important to specify the files in the right order. This is explained in Section 7.4.1, Specifying Libraries to the Linker
For a complete overview of all options
with extensive description, see section 4.3,
Linker Options, of the Reference Guide.
There are two kinds of libraries: system libraries and user libraries.
The system libraries are installed in subdirectories of the c:\ctc\lib directory. An overview of the system libraries is given in the following table.
Library to link | Description |
libc.a |
C library (With full printf/scanf functionality. Some functions require the floating point library. Also includes the startup code.) |
libcs.a |
C library single precision (compiler option -F) (With full printf/scanf functionality. Some functions require the floating point library. Also includes the startup code.) |
libcs_fpu.a | C library single precision with FPU instructions (compiler option -F and --fpu-present) |
libfp.a | Floating point library (non-trapping) |
libfpt.a |
Floating point library (trapping) (Control program option -fptrap) |
libfp_fpu.a |
Floating point library (non-trapping, with FPU instructions) (Compiler option --fpu-present) |
libfpt_fpu.a |
Floating point library (trapping, with FPU instructions) (Control program option -fptrap, compiler option --fpu-present) |
librt.a | Run-time library |
Table 7-4: Overview of libraries
For more information on these libraries
see section 3.12, Libraries, in Chapter TriCore C Language.
When you want to link system libraries, you must specify this with the option -l. With the option -lc you specify the system library libc.a.
You can also create your own libraries. Section 8.4 , Archiver, in Chapter Using the Utilities, describes how you can use the archiver to create your own library with object modules. To link user libraries, specify their filenames on the command line.
In EDE you can specify both system and user libraries.
To specify to link the default C libraries:
1. From the Project menu, select Project Options...
2. Expand the Linker entry and select Libraries.
3. Select Link default C libraries.
4. Select a floating-point library: non-trapping or trapping.
5. Click OK to accept the linker options.
The invocation syntax on the command
line is for example:
ltc -lc start.o
To specify your own libraries:
1. From the Project menu, select Project Options...
2. Expand the Linker entry and select Libraries.
3. Add your libraries to the Libraries field and click OK to accept the linker options.
The invocation syntax on the command
line is for example:
ltc start.o mylib.a
If the library resides in a subdirectory, specify that directory with the library name:
ltc start.o mylibs\mylib.a
The order in which libraries appear on the command line is important. By default the linker processes object files and libraries in the order in which they appear at the command line. With the option --first-library-first you can tell the linker to scan the libraries from left to right, and extract symbols from the first library where the linker finds it.
Example:
ltc --first-library-first a.a test.o b.a
If the file test.o calls a function which is both present in a.a and b.a, normally the function in b.a would be extracted. With this option the linker first tries to extract the symbol from the first library a.a.
You can specify the location of system libraries (specified with option -l) in several ways. The linker searches the specified locations in the following order:
1. The linker first looks in the directories that are specified in the Directories dialog (-L option). If you specify the -L option without a pathname, the linker stops searching after this step.
2. When the linker did not find the library (because it is not in the specified library directory or because no directory is specified), it looks which paths were set during installation. You can still change these paths if you like.
See environment variables LIBTC1V1_2,
LIBTC1V1_3 and LIBTC2 in section 1.3.2,
Configuring the Command Line Environment, in Chapter Software Installation.
3. When the linker did not find the library, it tries the default ctc\lib directory which was created during installation (or a processor specific sub-directory).
If you use your own library, the linker searches the library in the current directory only.
A library built with artc always contains an index part at the beginning of the library. The linker scans this index while searching for unresolved externals. However, to keep the index as small as possible, only the defined symbols of the library members are recorded in this area.
When the linker finds a symbol that matches an unresolved external, the corresponding object file is extracted from the library and is processed. After processing the object file, the remaining library index is searched. If after a complete search unresolved externals are introduced, the library index will be scanned again.
The -v option shows how libraries have been searched and which objects have been extracted.
If you are linking from libraries, only the objects that contain symbol definition(s) to which you refer, are extracted from the library. This implies that if you invoke the linker like:
ltc mylib.a
nothing is linked and no output file will be produced, because there are no unresolved symbols when the linker searches through mylib.a.
It is possible to force a symbol as external (unresolved symbol) with the option -e:
ltc -e main mylib.a
In this case the linker searches for the symbol main in the library and (if found) extracts the object that contains main. If this module contains new unresolved symbols, the linker looks again in mylib.a. This process repeats until no new unresolved symbols are found.
With the TriCore linker ltc it is possible to link incrementally. Incremental linking means that you link some, but not all .o modules to a relocatable object file .out. In this case the linker does not perform the locating phase. With the second invocation, you specify both new .o files and the .out file you had created with the first invocation.
Incremental linking is only possible on the command line.
ltc -r test1.o -otest.out ltc test2.o test.out
This links the file test1.o and generates the file test.out. This file is used again and linked together with test2.o to create the file task1.elf (the default name if no output filename is given in the default ELF/DWARF 2 format).
With incremental linking it is normal to have unresolved references in the output file until all .o files are linked and the final .out or .elf file has been reached. The option -r for incremental linking also suppresses warnings and errors because of unresolved symbols.
With EDE or the options on the command line you can control the linker to a certain degree. However, when you exactly want to determine where your sections will be located, how much memory is available, which sorts of memory are available, and so on, you can write a script.
The language for the script is called the Linker Script Language, or shortly LSL. You can specify the script file to the linker, which reads it and locates your application exactly as defined in the script. If you do not specify your own script file, the linker always reads a standard script file which is supplied with the toolchain.
The Linker Script Language (LSL) serves three purposes:
1. It provides the linker with a definition of the target's core architecture and its internal memory (this is called the derivative). These definitions are written by Altium and supplied with the toolchain.
2. It provides the linker with a specification of the external memory attached to the target processor. The template extmem.lsl is supplied with the toolchain.
3. It provides the linker with information on how your application should be located in memory. This gives you, for example, the possibility to create overlaying sections.
The linker accepts multiple LSL files. You can use the specifications of the TriCore architectures and derivatives that Altium has supplied in the include.lsl directory. Do not change these files.
If you attached external memory to a derivative you must specify this in a separate LSL file and pass both the LSL file that describes the derivative's architecture and your LSL file that contains the memory specification to the linker. Next you may also want to specify how sections should be located and overlaid.
LSL has its own syntax. In addition, you can use the standard ANSI C preprocessor keywords because the linker sends the script file first to the C preprocessor before it starts interpreting the script.
The complete syntax is described in
Chapter 7,
Linker Script Language, in the Reference Guide.
In EDE you can specify the size of the stack and heap; the physical memory attached to the processor; identify that particular address ranges are reserved; and specify which sections are located where in memory. EDE translates your input into an LSL file that is stored in the project directory under the name project.lsl and passes this file to the linker.
If you want to learn more about LSL you can inspect the generated file project.lsl. To change the LSL settings:
1. From the Project menu, select Project Options...
2. Expand the Linker entry and select Script File.
3. Make your changes.
Each time you close the Project Options dialog the file project.lsl is updated and you can immediately see how your settings are encoded in LSL.
Note that EDE supports ChromaCoding (applying color coding to text) and template expansion when you edit LSL files.
A script file consists of several definitions. The definitions can appear in any order.
In essence an architecture definition describes how the linker should convert virtual addresses into physical addresses for a given type of core. If the core supports multiple address spaces, then for each space the linker must know how to perform this conversion. In this context a physical address is an offset on a given internal or external bus. Additionally the architecture definition contains information about items such as the (hardware) stack and the interrupt vector table.
This specification is normally written by Altium. For each TriCore core architecture, a separate LSL file is provided. These are tc1v1_2.lsl, tc1v1_3.lsl, and tc2.lsl.
The architecture definition of the LSL file should not be changed by you unless you also modify the core's hardware architecture. If the LSL file describes a multi-core system an architecture definition must be available for each different type of core.
The derivative definition describes the configuration of the internal (on-chip) bus and memory system. Basically it tells the linker how to convert offsets on the busses specified in the architecture definition into offsets in internal memory. A derivative definition must be present in an LSL file. Microcontrollers and DSPs often have internal memory and I/O sub-systems apart from one or more cores. The design of such a chip is called a derivative.
Altium provides LSL descriptions of supported derivatives, along with "SFR files", which provide easy access to registers in I/O sub-systems from C and assembly programs. When you build an ASIC or use a derivative that is not (yet) supported by the TASKING tools, you may have to write a derivative definition.
The processor definition describes an instance of a derivative. Typically the processor definition instantiates one derivative only (single-core processor). A processor that contains multiple cores having the same (homogeneous) or different (heterogeneous) architecture can also be described by instantiating multiple derivatives of the same or different types in separate processor definitions.
If for a derivative 'A' no processor is defined in the LSL file, the linker automatically creates a processor named 'A' of derivative 'A'. This is why for single-processor applications it is enough to specify the derivative in the LSL file, for example with -dtc1920a.lsl.
Memory and bus definition are used within the context of a derivative definition to specify internal memory and on-chip busses. In the context of a board specification the memory and bus definitions are used to define external (off-chip) memory and busses. Given the above definitions the linker can convert a logical address into an offset into an on-chip or off-chip memory device.
The processor definition and memory and bus definitions together form a board specification. LSL provides language constructs to easily describe single-core and heterogeneous or homogeneous multi-core systems. The board specification describes all characteristics of your target board's system busses, memory devices, I/O sub-systems, and cores that are of interest to the linker. Based on the information provided in the board specification the linker can for each core:
The optional section layout definition enables you to exactly control where input sections are located. Features are provided such as: the ability to place sections at a given load-address or run-time address, to place sections in a given order, and to overlay code and/or data sections.
A linker script file that defines a derivative "X'" based on the TC1V1.3 architecture, its external memory and how sections are located in memory, may have the following skeleton:
architecture TC1V1.3 { // Specification of the TC1v1.3 core architecture. // Written by Altium. }
derivative X // derivative name is arbitrary { // Specification of the derivative. // Written by Altium. core tc // always specify the core { architecture = TC1V1.3; } bus fpi_bus // internal bus { // maps to fpi_bus in "tc" core } // internal memory }
processor proc1 // processor name is arbitrary { derivative = X; // You can omit this part, except if you use a // multi-core system. }
memory ext_name { // external memory definition }
section_layout proc1:tc:linear // section layout { // section placement statements // sections are located in address space 'linear' // of core 'tc' of processor 'proc1' }
Although you will probably not need to program the architecture definition (unless you are building your own processor core) it helps to understand the Linker Script Language and how the definitions are interrelated.
Within an architecture definition the characteristics of a target processor core that are important for the linking process are defined. These include:
A logical address space is a memory range for which the core has a separate way to encode an address into instructions. For example, the Tricore's 32-bit linear address space encloses 16 24-bit sub-spaces and 16 14-bit sub-spaces. See also the Tricore Architecture Manual sections "Memory Model" and "Addressing Model".
Most microcontrollers and DSPs support multiple address spaces. An address space is a range of addresses starting from zero. Normally, the size of an address space is to 2N, with N the number of bits used to encode the addresses.
The relation of an address space with another address space can be one of the following:
Address spaces (even nested) can have different minimal addressable units (MAU), alignment restrictions, and page sizes.
The best way to program the architecture definition, is to start with a drawing. The following figure shows a part of the TriCore architecture:
Figure 7-2: Scheme of (part of) the TriCore architecture
The figure shows three address spaces called linear, abs24 and pcp_code. The address space abs24 is a subset of the address space linear. All address spaces have attributes like a number that identifies the logical space (id), a MAU and an alignment. In LSL notation the definition of these address spaces looks as follows:
space linear { id = 1; mau = 8; map (src_offset=0x00000000, dest_offset=0x00000000, size=4G, dest=bus:fpi_bus); }
space abs24 { id = 2; mau = 8; map (src_offset=0x00000000, dest_offset=0x00000000, size=2M, dest=space:linear); map (src_offset=0x10000000, dest_offset=0x10000000, size=2M, dest=space:linear); map (src_offset=0x20000000, dest_offset=0x20000000, size=2M, dest=space:linear); //... }
space pcp_code { id = 8; mau = 16; map (src_offset=0x00000000, dest_offset=0, size=0x04000000, dest=bus:pcp_code_bus); }
The keyword map corresponds with the arrows in the drawing. You can map:
Next the two internal busses, named fpi_bus and pcp_code_bus must be defined in LSL:
bus fpi_bus { mau = 8; width = 32; // there are 32 data lines on the bus }
bus pcp_code_bus { mau = 8; width = 8; }
This completes the LSL code in the architecture definition. Note that all code above goes into the architecture definition, thus between:
architecture TC1V1.3
{
All code above goes here.
}
Although you will probably not need to program the derivative definition (unless you are building your own processor) it helps to understand the Linker Script Language and how the definitions are interrelated.
A derivative is the design of a processor, as implemented on a chip. It can be an ASIC or a generic design and comprises one or more cores and on-chip memory. The derivative definition includes:
Each derivative must have a specification of its core architecture in LSL:
core tc { architecture = TC1V1.3; }
Each derivative must contain a bus definition for connecting external memory. In this example, the bus fpi_bus maps to the bus fpi_bus defined in the architecture definition of core tc:
bus fpi_bus { mau = 8; width = 32; map (dest=bus:tc:fpi_bus, dest_offset=0, size=4G); }
Figure 7-3: Internal memory definition for a derivative
According to the drawing, the TriCore contains internal memory called pcode with a size 0x04000 (16k). This is physical memory which is mapped to the internal bus pcp_code_bus and to the fpi_bus, so both the tc unit and the pcp can access the memory:
memory pcode { mau = 8; size = 16k; type = ram; map (dest=bus:tc:fpi_bus, dest_offset=0xF0020000, size=16k); map (dest=bus:tc:pcp_code_bus, size=16k); }
This completes the LSL code in the derivative definition. Note that all code above goes into the derivative definition, thus between:
derivative X // name of derivative
{
All code above goes here.
}
If you want to create a custom derivative and you want
to use EDE to select sections, the core of the derivative must be called
"tc", since EDE uses this name in the generated LSL file. If you want to specify external memory in EDE, the custom derivative must contain a bus named "fpi_bus" for the same reason. In EDE you have to define a target processor as specified in section 5.5
, Specifying a Target Processor, in Chapter Using the Compiler.
Once the processor is defined in LSL, you may want to extend the processor with external (or off-chip) memory. You need to specify the location and size of the physical external memory devices in the target system.
The principle is the same as defining the core's architecture but now you need to fill the memory definition:
memory name { External memory definitions. }
Figure 7-4: Adding external memory to the TriCore architecture
Suppose your embedded system has 16k of external ROM, named code_rom and 2k of external NVRAM, named my_nvsram. (See figure above.) Both memories are connected to the bus fpi_bus. In LSL this looks like follows:
memory code_rom { type = rom; mau = 8; size = 16k; map (dest=bus:X:fpi_bus, dest_offset=0xa0000000, size=16k); }
The memory my_nvsram is connected to the bus with an offset of 0xc0000000:
memory my_nvsram { mau = 8; size = 2k; type = ram; map (dest=bus:X:fpi_bus, dest_offset=0xc0000000, size=2k); }
Once you have defined the internal core architecture and optional external memory, you can actually define where your application must be located in the physical memory.
During compilation, the compiler divides the application into sections. Sections have a name, an indication in which address space it should be located and attributes like writable or read-only.
In the section layout definition you can exactly define how input sections are placed in address spaces, relative to each other, and what their absolute run-time and load-time addresses will be. To illustrate section placement the following example of a C program is used:
To illustrate section placement, the following example of a C program (bat.c) is used. The program prints the number of times it has been executed.
#define BATTERY_BACKUP_TAG 0xa5f0 #include <stdio.h> int uninitialized_data; int initialized_data = 1; #pragma section all "non_volatile" int battery_backup_tag; int battery_backup_invok; #pragma section all
void main (void) { if (battery_backup_tag != BATTERY_BACKUP_TAG ) { // battery back-upped memory area contains invalid data // initialize the memory battery_backup_tag = BATTERY_BACKUP_TAG; battery_backup_invok = 0; } printf( "This application has been invoked %d times\n", battery_backup_invok++); }
The compiler assigns names and attributes to sections. With the #pragma section all "name" the compiler's default section naming convention is overruled and a section with the name non_volatile is defined. In this section the battery back-upped data is stored.
By default the compiler creates the section .zbss.bat. uninitialized_data to store uninitialized data objects. The section prefix ".zbss" tells the linker to locate the section in address space abs18 and that the section content should be filled with zeros at startup.
As a result of the #pragma section all "non_volatile", the data objects between the pragma pair are placed in .zbss.non_volatile. Note that ".zbss" sections are cleared at startup. However, battery back-upped sections should not be cleared and therefore we will change this section attribute using the LSL.
The generated assembly may look like:
.extern printf .name "bat" .extern _START .sdecl ".text.bat.main",CODE .sect ".text.bat.main .align 4 .global main
; Function main main: sub16.a a10,#8 mov.u d15,#42480 ld.w d0,battery_backup_tag jeq d15,d0,L_2 . . . jg printf ; End of function ; End of section
.sdecl ".zbss.bat.uninitialized_data",DATA .sect ".zbss.bat.uninitialized_data" .global uninitialized_data .align 2 uninitialized_data: .space 4 ; End of section
.sdecl ".zdata.bat.initialized_data",DATA .sect ".zdata.bat.initialized_data" .global initialized_data .align 2 initialized_data: .word 1 ; End of section
.sdecl ".zbss.non_volatile",DATA .sect ".zbss.non_volatile" .global battery_backup_tag .align 2 battery_backup_tag: .space 4 .global battery_backup_invok .align 2 battery_backup_invok: .space 4 ; End of section
.sdecl ".rodata.bat..1.ini",DATA,ROM .sect ".rodata.bat..1.ini" .align 2 _1_ini: .byte 84 /* T */ .byte 104 /* h */ ; This application has been invoked %d times\n .byte 10 /* \n */ .byte 0 /* NULL */ ; End of section ; Module end
The number of invocations of the example program should be saved in non-volatile (battery back-upped) memory. This is the memory my_nvsram from the example in the previous section.
To control the locating of sections, you need to write one or more section definitions in the LSL file. At least one for each address space where you want to change the default behavior of the linker. In our example, we need to locate sections in the address space abs18:
section_layout ::abs18 { Section placement statements }
To locate sections, you must create a group in which you select sections from your program. For the battery back-up example, we need to define one group, which contains the section .zbss_non_volatile. All other sections are located using the defaults specified in the architecture
definition. Section .zbss_non_volatile should be placed in non-volatile ram. To achieve this, the run address refers to our non-volatile memory called my_nvsram. Furthermore, the section should not be cleared and therefore the attribute
s (scratch)
is assigned to the group:
group ( ordered, run_addr = mem:my_nvsram, attributes = s ) { select ".zbss.non_volatile"; }
This completes the LSL file for the sample architecture and sample program. You can now call the linker with this file and the sample program to obtain an application that works for this architecture.
For a complete description of the Linker
Script Language, refer to Chapter 7,
Linker Script Language, in the Reference Guide.
The processor definition is only needed when you write an LSL-file for a multi-processor embedded system. In the processor definition you can instantiate one or more cores and couple them to one of the architectures as defined in the architecture definition.
The linker creates labels that you can use to refer to from within the application software. Some of these labels are real labels at the beginning or the end of a section. Other labels have a second function, these labels are used to address generated data in the locating phase. The data is only generated if the label is used.
Linker labels are labels starting with _lc_. The linker assigns addresses to the following labels when they are referenced:
Label | Description | ||||||||||||
_lc_cp | Start of copy table. Same as _lc_ub_table. The copy table gives the source and destination addresses of sections to be copied. This table will be generated by the linker only if this label is used. | ||||||||||||
_lc_bh | Begin of heap. Same as _lc_ub_heap. | ||||||||||||
_lc_eh | End of heap. Same as _lc_ue_heap. | ||||||||||||
_lc_u_name |
User defined label. The label must be defined in the LSL file. For example,
"_lc_u_int_tab" = (INTTAB);
_lc_ub_name
_lc_b_name |
Begin of section name. Also used to mark the begin
of the stack or heap or copy table. |
_lc_ue_name
_lc_e_name |
End of section name. Also used to mark the begin of
the stack or heap. |
_lc_cb_name |
Start address of an overlay section in ROM. |
_lc_ce_name |
End address of an overlay section in ROM. |
_lc_gb_name |
Begin of group name. This label appears in the output
file even if no reference to the label exist in the input file. |
_lc_ge_name |
End of group name. This label appears in the output
file even if no reference to the label exist in the input file. | |
Table 7-5: Linker labels
The linker only allocates space for the stack and/or heap when a reference to either of the section labels exists in one of the input object files.
Suppose in an LSL you have defined a user stack section with the name "ustack" (with the keyword stack). You can refer to the begin and end of the stack from your C source as follows:
#include <stdio.h> extern char *_lc_ub_ustack; extern char *_lc_ue_ustack; int main() { printf( "Size of user stack is %d\n", _lc_ue_ustack - _lc_ub_ustack ); }
From assembly you can refer to the end of the user stack with:
.extern _lc_ue_ustack ; user stack end
The map file is an additional output file that contains information about the location of sections and symbols. With the options in the Map File page of the Linker entry in the Project Options dialog you choose to generate a map file or to skip it (-M option). You can also customize the type of information that should be included in the map file (-m option).
See section 5.2
, Section 5.1, Linker Map File Format, in Chapter List File Formats of the Reference Guide for an explanation
of the format of the map file.
Linker option -M (Generate map file)
Linker option -m (Map file formatting options)
ltc -Mtest.map test.o
With this command the list file test.map is created.
The linker produces error messages of the following types:
After a fatal error the linker immediately aborts the link/locate process.
Errors are reported, but the linker continues linking and locating. No output files are produced unless you have set the linker option --keep-output-files.
Warning messages do not result into an erroneous output file. They are meant to draw your attention to assumptions of the linker for a situation which may not be correct. You can control warnings in the Linker | Warnings page of the Project | Project Options... menu (linker option -w ).
Verbose information messages do not indicate an error but tell something about a process or the state of the linker. To see verbose information, use the linker option -v.
System errors occur when internal consistency checks fail and should never occur. When you still receive the system error message
S6##: message
please report the error number and as many details as possible about the context in which the error occurred. The following helps you to prepare an e-mail using EDE:
1. From the Help menu, select Technical Support -> Prepare Email...
2. Fill out the the form. State the error number and attach relevant files.
3. Click the Copy to Email client button to open your email application.
4. Finish the e-mail and send it.
1. In the Help menu, enable the option Show Help on Tool Errors.
2. In the Build tab of the Output window, double-click on an error or warning message.
ltc --diag=[format:]{all | number,...}
See linker option --diag
in section 4.3,
Linker Options in Chapter Tool Options of the TriCore Reference
Guide.