10.5.4. The MEMORY Directive

The linker determines where output sections are allocated into memory; it must have a model of target memory to accomplish this. The MEMORY directive allows you to specify a model of target memory so that you can define the types of memory your system contains and the address ranges they occupy. The linker maintains the model as it allocates output sections and uses it to determine which memory locations can be used for object code.

The memory configurations of Arm systems differ from application to application. The MEMORY directive allows you to specify a variety of configurations. After you use MEMORY to define a memory model, you can use the SECTIONS directive to allocate output sections into defined memory.

For more information, see How the Linker Handles Sections.

10.5.4.1. Default Memory Model

If you do not use the MEMORY directive, the linker uses a default memory model that is based on the Arm architecture. This model assumes that the full 32-bit address space (232 locations) is present in the system and available for use. For more information about the default memory model, see Default Placement Algorithm.

10.5.4.2. MEMORY Directive Syntax

The MEMORY directive identifies ranges of memory that are physically present in the target system and can be used by a program. Each range has several characteristics:

  • Name

  • Starting address

  • Length

  • Optional set of attributes

  • Optional fill specification

The MEMORY directive also allows you to use the GROUP keyword to create logical groups of memory ranges for use with Cyclic Redundancy Checks (CRC). See Using the crc_table() Operator in the MEMORY Directive for how to compute CRCs over memory ranges using the GROUP syntax.

When you use the MEMORY directive, be sure to identify all memory ranges that are available for the program to access at run time. Memory defined by the MEMORY directive is configured; any memory that you do not explicitly account for with MEMORY is unconfigured. The linker does not place any part of a program into unconfigured memory. You can represent nonexistent memory spaces by simply not including an address range in a MEMORY directive statement.

The MEMORY directive is specified in a command file by the word MEMORY (uppercase), followed by a list of memory range specifications enclosed in braces. The MEMORY directive in the following example defines a system that has 4K bytes of fast external memory at address 0x0000 0000, 2K bytes of slow external memory at address 0x0000 1000 and 4K bytes of slow external memory at address 0x1000 0000. It also demonstrates the use of memory range expressions as well as start/end/size address operators (see Expressions and Address Operators).

/********************************************************/
/* Sample command file with MEMORY directive            */
/********************************************************/
file1.c.o  file2.c.o         /* Input files */
--output_file=prog.out       /* Options     */

MEMORY
{
    FAST_MEM (RX): origin = 0x00000000 length = 0x00001000
    SLOW_MEM (RW): origin = 0x00001000 length = 0x00000800
    EXT_MEM (RX): origin = 0x10000000 length = 0x00001000

The general syntax for the MEMORY directive is:

MEMORY
{
    name_1 [( attr )] : origin = expr, length = expr [, fill = constant] [LAST( sym )]
    ...
    ...
    name_n [( attr )] : origin = expr, length = expr [, fill = constant] [LAST( sym )]
}
  • name names a memory range. A memory name can be one to 64 characters; valid characters include A-Z, a-z, $, ., and _. The names have no special significance to the linker; they simply identify memory ranges. Memory range names are internal to the linker and are not retained in the output file or in the symbol table. All memory ranges must have unique names and must not overlap.

  • attr specifies one to four attributes associated with the named range. Attributes are optional; when used, they must be enclosed in parentheses. Attributes restrict the allocation of output sections into certain memory ranges. If you do not use any attributes, you can allocate any output section into any range with no restrictions. Any memory for which no attributes are specified (including all memory in the default model) has all four attributes. Valid attributes are:

    • R specifies that the memory can be read.

    • W specifies that the memory can be written to.

    • X specifies that the memory can contain executable code.

    • I specifies that the memory can be initialized.

  • origin specifies the starting address of a memory range; enter as origin, org, or o. The value, specified in bytes, is a 32-bit integer constant expression, which can be decimal, octal, or hexadecimal.

  • length specifies the length of a memory range; enter as length, len, or l. The value, specified in bytes, is a 32-bit integer constant expression, which can be decimal, octal, or hexadecimal.

  • fill specifies a fill character for the memory range; enter as fill or f. Fills are optional. The value is an integer constant and can be decimal, octal, or hexadecimal. The fill value is used to fill areas of the memory range that are not allocated to a section. (See Using the VFILL Specifier in the Memory Map for virtual filling of memory ranges when using Error Correcting Code (ECC).)

  • LAST optionally specifies a symbol that can be used at run-time to find the address of the last allocated byte in the memory range. See LAST Operator.

Note

Filling Memory Ranges

If you specify fill values for large memory ranges, your output file will be very large because filling a memory range (even with 0s) causes raw data to be generated for all unallocated blocks of memory in the range.

The following example specifies a memory range with the R and W attributes and a fill constant of 0FFFFFFFFh:

MEMORY
{
   RFILE (RW) : o = 0x0020, l = 0x1000, f = 0xFFFF
}

You normally use the MEMORY directive in conjunction with the SECTIONS directive to control placement of output sections. For more information about the SECTIONS directive, see The SECTIONS Directive.

10.5.4.3. Expressions and Address Operators

Memory range origin and length can use expressions of integer constants with the following operators:

Type

Operators

Binary operators:

*  /  %  +  -  <<  >>  ==    = <  <=  >  >=  &  |  &&  ||

Unary operators:

-  ~  !

Expressions are evaluated using standard C operator precedence rules.

No checking is done for overflow or underflow, however, expressions are evaluated using a larger integer type.

Preprocess directive #define constants can be used in place of integer constants. Global symbols cannot be used in Memory Directive expressions.

Three address operators reference memory range properties from prior memory range entries:

Operators

Description

START(MR)

Returns start address for previously defined memory range MR.

SIZE(MR)

Returns size of previously defined memory range MR.

END(MR)

Returns end address for previously defined memory range MR.

The following example uses an expression to specify an origin and a length:

/********************************************************/
/* Sample command file with MEMORY directive            */
/********************************************************/
file1.c.o file2.c.o                  /* Input files */
--output_file=prog.out               /* Options */
#define ORIGIN 0x00000000
#define BUFFER 0x00000200
#define CACHE 0x0001000

MEMORY
{
    FAST_MEM (RX): origin = ORIGIN + CACHE length = 0x00001000 + BUFFER
    SLOW_MEM (RW): origin = end(FAST_MEM)  length = 0x00001800 - size(FAST_MEM)
    EXT_MEM (RX):  origin = 0x10000000     length = size(FAST_MEM) - CACHE
}

10.5.4.4. The ALIAS Statement

Certain devices, such as the MSP432 Cortex M4, have a region of RAM that can be addressed by two different memory buses--a system bus and an instruction bus. This RAM region, which is located in the DATA region of the memory map (usually at 0x20000000), is internally aliased to the CODE region (usually at 0x01000000). This aliasing takes advantage of the instruction bus to fetch code from RAM while freeing the other system buses. On such devices, your linker command file should use the ALIAS statement so that placements to CODE and DATA are made with no collisions.

In order to use the above capability, the linker must be aware of the two addresses that point to the same memory. Use the following syntax within a MEMORY directive to create an ALIAS for a memory range. ALIAS regions must have the same length.

MEMORY
{
    ...
    ALIAS
    {
        SRAM_CODE (RWX) : origin = 0x01000000
        SRAM_DATA (RW)  : origin = 0x20000000
    } length = 0x0001000
    ...
}