5.3.3. Placing regions at fixed addresses

You can use the FIXED attribute in an execution region scatter-loading description file to create root regions that load and execute at fixed addresses.

FIXED is used to create multiple root regions within a single load region and therefore typically a single ROM device. For example, you can use this to place a function or a block of data, such as a constant table or a checksum, at a fixed address in ROM so that it can be accessed easily through pointers.

If you specify, for example, that some initialization code is to be placed at start of ROM and a checksum at the end of ROM, some of the memory contents might be unused. Use the * or .ANY module selector to flood fill the region between the end of the initialization block and the start of the data block.

Note

To make your code easier to maintain and debug, use the minimum amount of placement specifications in scatter-loading description files and leave the detailed placement of functions and data to the linker.

You cannot specify component objects that have been partially linked. For example, if you partially link the objects obj1.o, obj2.o, and obj3.o together to produce obj_all.o, the resulting component object names are discarded in the resulting object. Therefore, you cannot refer to one of the objects by name, for example, obj1.o. You can only refer to the combined object obj_all.o.

Placing functions and data at specific addresses

Normally, the compiler produces RO, RW, and ZI sections from a single source file. These regions contain all the code and data from the source file. To place a single function or data item at a fixed address, you must enable the linker to process the function or data separately from the rest of the input files.

The linker has two methods that enable you to place a section at a specific address:

  • An execution region can be created at the desired address with a section description that selects just one section.

  • For a specially named section the linker can get the placement address from the section name. These specially named sections are called __at sections. See Using __at sections to place sections at a specific address for more information.

To place a function or variable at a specific address it must be placed in its own section. There are several ways to do this:

  • Place the function or data item in its own source file.

  • Use the --split_sections compiler option to produce an object file for each function. See --split_sections in the Compiler Reference Guide.

    This option increases code size slightly for some functions because it reduces the potential for sharing addresses, data, and string literals between functions. However, this can help to reduce the final image size overall by enabling the linker to remove unused functions when you specify armlink --remove.

  • Use __attribute__((section(”name”))) to create multiple named sections. See __attribute__((section)) in the Compiler Reference Guide.

  • Use the AREA directive from assembly language. In assembly code, the smallest locatable unit is an AREA. See the Assembler Guide for more information.

Placing a named section explicitly using scatter-loading

The scatter-loading description file in Example 5.10 places:

  • initialization code at address 0x0 followed by the remainder of the RO code and all of the RO data except for the RO data in the object data.o

  • all global RW variables in RAM at 0x400000

  • a table of RO-DATA from data.o fixed at address 0x1FF00.

Example 5.10. Section placement

LR1 0x0 0x10000
{
    ER1 0x0 0x2000                ; Root Region, containing init code
    {                             ; place init code at exactly 0x0
        init.o (Init, +FIRST)  
        * (+RO)                   ; rest of code and read-only data  
    }
    RAM  0x400000                 ; RW & ZI data to be placed at 0x400000
    {
        * (+RW +ZI)
    }
    DATABLOCK 0x1FF00 FIXED 0xFF  ; execution region fixed at 0x1FF00
    {                             ; maximum space available for table is 0xFF
        data.o(+RO-DATA)          ; place RO data between 0x1FF00 and 0x1FFFF
    }
}

Note

There are some situations where using FIXED and a single load region are not appropriate. Other techniques for specifying fixed locations are:

  • If your loader can handle multiple load regions, place the RO code or data in its own load region.

  • If you do not require the function or data to be at a fixed location in ROM, use ABSOLUTE instead of FIXED. The loader then copies the data from the load region to the specified address in RAM. ABSOLUTE is the default attribute.

  • To place a data structure at the location of memory-mapped I/O, use two load regions and specify UNINIT. UNINIT ensures that the memory locations are not initialized to zero. See Chapter 2 Embedded Software Development in the Developer Guide for more information.

Using __attribute__((section("name")))

Placing a code or data object in its own source file and then placing the object file sections uses standard coding techniques. However, you can also use __attribute__((section("name"))) and a scatter-loading description file to place named sections. Create a module, for example, adder.c and name a section explicitly as shown in Example 5.11.

Example 5.11. Naming a section

int variable __attribute__((section("foo"))) = 10;

Use a scatter-loading description file to specify where the named section is placed, see Example 5.12. If both code and data sections have the same name, the code section is placed first.

Example 5.12. Placing a section

FLASH 0x24000000 0x4000000
{
    ...                                ; rest of code

    ADDER 0x08000000
    {
        adder.o (foo)                  ; select section foo from adder.o
    }
}

Using __at sections to place sections at a specific address

A section can be given a special name that encodes the address at which it must be placed. The name can be specified as follows:

.ARM.__at_address

Where:

address

is the required address of the section. This can be specified in hexadecimal or decimal. Sections in the form of .ARM.__at_address are referred to by the abbreviation __at.

In the compiler, variables can be assigned to __at sections by either explicitly naming the section using the attribute section() or by using the attribute __at which sets up the name of the section for you. For example, the following statements place variable in a section called .ARM.__at_0x8000:

int variable __attribute__((section(".ARM.__at_0x8000"))) = 10;
int variable __attribute__((__at(0x8000))) = 10;

See Variable attributes in the Compiler Reference Guide.

Restrictions
  • __at section address ranges must not overlap, unless the overlapping sections are placed in different overlay regions

  • __at sections are not permitted in position independent execution regions

  • you must not reference the linker-defined symbols $$Base, $$Limit and $$Length of an __at section

  • __at sections must not be used in System V and BPABI executables and BPABI DLLs

  • __at sections must have an address that is a multiple of their alignment

  • __at sections ignore any +FIRST or +LAST ordering constraints.

Manual placement

The standard scatter-loading rules are used to determine which execution regions are used to place __at sections.

Example 5.13 shows the placement of read-only sections .ARM.__at_0x2000 and the read-write section .ARM.__at_0x4000. Load and execution regions are not created automatically in manual mode. An error is produced if an __at section cannot be placed in an execution region.

Example 5.13. Manual placement of __at sections in a scatter file

LR1 0x0
{
    ER_RO 0x0 0x2000
    {
        *(+RO)               ; .ARM.__at_0x0 is selected by +RO
    }
    ER_RO2 0x2000
    {
        *(.ARM.__at_0x2000)  ; .ARM.__at_0x2000 is selected by .ARM.__at_0x2000
    }
    ER2 0x4000
    {
        *(+RW +ZI)           ; .ARM.__at_0x4000 is selected by +RW
    }
}

Automatic placement

This mode is enabled by using the linker command-line option, --autoat. See Specifying memory map information for the image for more information.

When linking with the --autoat option, the __at sections are not placed by the scatter-loading selectors. Instead, the linker places the __at section in a compatible region. If no compatible region is found, the linker creates a load and execution region for the __at section.

All linker --autoat created execution regions have the UNINIT scatter-loading attribute. If you require a ZI __at section to be zero-initialized then it must be placed within a compatible region.

A compatible region is one where:

  • The __at address lies within the execution region base and limit, where limit is the base address + maximum size of execution region. If no maximum size is set, the limit is assumed to be infinite.

  • The execution region meets at least one of the following conditions:

    • it has a selector that matches the __at section by the standard scatter-loading rules

    • it has at least one section of the same type (RO, RW or ZI) as the __at section

    • it does not have the EMPTY attribute.

      Note

      The linker considers an __at section with type RW compatible with RO.

Example 5.14 show the sections .ARM.__at_0x0 type RO, .ARM.__at_0x2000 type RW, .ARM.__at_0x4000 type ZI and .ARM.__at_0x8000 type ZI.

Example 5.14.  Automatic placement of __at sections

LR1 0x0
{
	ER_RO 0x0 0x2000
	{
		*(+RO)			; .ARM.__at_0x0 lies within the bounds of ER_RO
	}
	ER_RW 0x2000 0x2000
	{
		*(+RW)			; .ARM.__at_0x2000 lies within the bounds of ER_RW
	}
	ER_ZI 0x4000 0x2000
	{
		*(+ZI)			; .ARM.__at_0x4000 lies within the bounds of ER_ZI
	}
}

; the linker creates a load and execution region for the __at section
; .ARM.__at_0x8000 because it lies outside all candidate regions.

Placing a key in flash memory

Some flash devices require a key to be written to an address to activate certain features. An __at section provides a simple method of writing a value to a specific address.

Assuming a device has flash memory from 0x8000 to 0x10000 and a key is required in address 0x9000. To do this with an __at section, you must define a section .ARM.__at_0x9000 that contains the value of the key. For example:

int key __attribute__((__at(0x9000))) = 10;

Example 5.15 shows a scatter file with manual placement of the flash execution region.

Example 5.15.  Manual placement of flash execution regions

ER_FLASH 0x8000 0x2000
{
    *(+RO)                     ; other code, read-only data, and padding if reqd
    *(.ARM.__at_0x9000)        ; key
}

Example 5.16 shows a scatter file with automatic placement of the flash execution region. Use the linker command-line option --autoat to enable automatic placement.

Example 5.16.  Automatic placement of flash execution regions

ER_FLASH 0x8000 0x2000
{
    *(+RO)               ; other code and read-only data, the
                         ; __at section is automatically selected
}

Mapping a structure over a peripheral register

To place an uninitialized variable over a peripheral register, a ZI __at section can be used. Assuming a register is available for use at 0x10000000. Define a ZI __at section called .ARM.__at_0x10000000.

Example 5.17 shows the a scatter file with the manual placement of the ZI __at section.

Example 5.17.  Manual placement of ZI __at sections

ER_PERIPHERAL 0x10000000 UNINIT
{
    *(.ARM.__at_10000000)
}

Using automatic placement, assuming that there is no other execution region near 0x10000000, the linker automatically creates a region with the UNINIT attribute at 0x10000000.

Copyright © 2002-2007 ARM Limited. All rights reserved.ARM DUI 0206H
Non-Confidential