10.9.2. Using the crc_table() Operator in the MEMORY Directive

Along with generating CRC Tables, the linker can also generate CRCs over memory ranges as well. To do this, instead of using the crc_table() operator in a SECTIONS directive, you use the crc() operator in a MEMORY directive. Within the MEMORY directive, you specify a GROUP of memory regions to have a CRC value computed. The memory ranges in the GROUP must be continuous.

The syntax is as follows:

MEMORY
{
    GROUP(FLASH)
    {
        RANGE1 :...
        RANGE2 :...
    } crc(_table_name, algorithm=xxx)
}

This syntax causes the linker to compute a single CRC over both RANGE1 and RANGE2. The CRC is based on the algorithm specified, taking into account all output sections that have been placed in those ranges. The result is stored in a table in the format described in Interface When Using the crc() Operator. This table is placed in an output section called .TI.memcrc, which is accessible through the table name as a linker symbol.

The algorithm argument for crc() may be any algorithm listed in Restrictions when using the crc_table() Operator. The algorithm is required in the current version, and linking will fail without it. In future releases, the algorithm specification will be optional, and the default is specified. If no algorithm is specified, the default algorithm will be chosen, which is TMS570_CRC64_ISO.

Specifying the GROUP name is optional. For example:

MEMORY
{
    GROUP
    {
        RANGE1 :...
        RANGE2 :...
    } crc(_table_name, algorithm=CRC8_PRIME)
}

When GROUP is used inside a MEMORY block, the syntax options are limited to the functionality described here and in the subsections that follow. The full functionality described in Using GROUP and UNION Statements for GROUP within the SECTIONS directive is not available within the MEMORY directive.

10.9.2.1. Restrictions when Using the crc() Operator

The crc() operator can only be applied to a GROUP within a MEMORY directive. It cannot be applied to individual memory ranges in a MEMORY directive or to groups in the SECTIONS directive.

Along with the restrictions described in Restrictions when using the crc_table() Operator, the following additional restrictions apply:

  • Memory range groups cannot contain any gaps between the ranges.

  • All of the memory ranges must be on the same page.

  • Memory ranges that contain sections that would not otherwise be eligible for CRC table generation cannot have a CRC computed. That is, memory ranges for which a CRC value is generated must correspond only to load addresses of initialized sections.

  • The .TI.memcrc section may not be placed in a range that itself is having a CRC value computed. This would result in a circular reference; the CRC result would depend upon the result of the CRC. See Generate CRC for Most or All of Flash Memory for ways to generate CRCs for most or all of Flash memory without violating this restriction.

10.9.2.2. Using the VFILL Specifier within a GROUP

In addition to specifying the origin and length of a memory range within a GROUP, you can also use the VFILL specifier, as described in Using the VFILL Specifier in the Memory Map, to allow ECC data to be generated for areas of the input memory range that remain uninitialized.

The load image will have gaps between output sections, and how these bits are set depends on your device. Most devices count empty spaces as 0x1 values, but if your device counts empty space as 0x0 values, the result of the CRC will be different. Thus, if the CRC result does not line up, make sure that you specify the empty space byte with the VFILL parameter, as shown in the following example:

MEMORY
{
    GROUP
    {
        FLASH : origin = 0x0000, length = 0x1000,
                VFILL = 0x0 /* Fill gaps with zeroes */
    } crc(_table_name, CRC8_PRIME)
}

If no VFILL parameter is specified, it defaults to 0x1, which fills everything with ones. Remember to update every memory range that has a fill value other than 0x1 for CRCs.

10.9.2.3. Generate CRC for Most or All of Flash Memory

If you are trying to generate a CRC value for the entire FLASH memory, place the table in a separate memory range, which .TI.memcrc will be placed in by default. For example:

MEMORY
{
    /* Carve out a section of FLASH to store the CRC result */
    CRC_PRELUDE : origin=0x0, length=0x10
    GROUP
    {
        FLASH : origin=0x10, length=0xFFFF
    } crc(_flash_crc, algorithm=CRC8_PRIME)
    /* Other memory ranges... */
}
SECTION
{
    .TI.memcrc > CRC_PRELUDE
}

In the above example, a small section of flash has been cut out of the whole, to allow the .TI.memcrc section to reside there, while everything else that is eligible for CRC generation is placed in FLASH. This avoids placing the CRC result in the CRC range.

In some cases, you may want to generate a CRC for all of Flash memory and read back the CRC result via the linker-generated map file (see Create a Map File (--map_file Option)). However, there is no memory location to place the CRC result for the memory range covering all of Flash memory. If you place it in Flash, then you violate the rule that the result cannot be placed within the input range. Thus, if there’s no good place to put the CRC result, you can mark the .TI.memcrc section as a COPY section like so:

.TI.memcrc : type=COPY

This prevents the CRC result for a memory range from being placed anywhere. Marking .TI.memcrc as a DSECT section has the same result.

10.9.2.4. Computing CRCs for Both Memory Ranges and Sections

You can run a CRC on both memory ranges and output sections together. In the following example, a CRC is computed over the memory range FLASH2, which is used only by the .text section. A CRC table is also generated for only the .text output section, which does not include the rest of the memory range.

MEMORY
{
    FLASH1 : origin = 0x0000, length = 0x1000
    GROUP
    {
        FLASH2 : origin = 0x1000, length=0x1000
    } crc(_memrange_flash_crc, algorithm=CRC8_PRIME)
}
SECTION
{
    .TI.memcrc > CRC_PRELUDE
    .text > FLASH2, crc_table(_crc_table, algorithm=CRC8_PRIME)
}

10.9.2.5. Example Specifying Memory Range CRCs

Here is a full linker command file that uses the crc() operator to generate a memory range CRC:

-c              /* Use C linking conventions: auto-init vars at runtime */
-stack 0x1400   /* Stack size */
-heap  0x0c00   /* Heap size */

MEMORY
{
    GROUP(FLASH)
    {
        MEM(RW)       : origin = 0x1200, length = 0x9DE0, VFILL = 0x0
    } crc(_ext_memrange_crc, algorithm=CRC32_PRIME)

    MEM2              : origin = 0xAFE0, length = 0x5000
    VECTORS(R)        : origin = 0xFFE0, length = 0x001E
    RESET             : origin = 0xFFFE, length = 0x0002
}

SECTIONS
{
    .intvecs          : {} > VECTORS

    /* These sections are uninitialized */
    .bss              : {} > MEM2
    .sysmem           : {} > MEM2
    .stack            : {} > MEM2

    /* These sections will be CRC'd */
    .text             : {} > MEM
    .const            : {} > MEM
    .rodata           : {} > MEM
    .cinit            : {} > MEM
    .switch           : {} > MEM

    .reset            : > RESET
}

10.9.2.6. Interface When Using the crc() Operator

CRCs over memory ranges are stored in a table format similar to that shown in Interface When Using the crc_table() Operator for the CRCs over sections. However, the table format is different than that of CRC tables.

The following figure shows the storage format for CRCs over memory ranges with example values:

../../../_images/crc_table_memrange.png

The table header stores the record count and size, as well as the algorithm type and the CRC result. Each table entry encodes the start address and length of a memory range that was used to compute the CRC.

The following header file excerpt shows the C structures the linker creates to manage the CRC information:

typedef struct memrange_crc_record {
    uintptr_t           addr;        /* Starting address */
#if defined(__LARGE_CODE_MODEL__) || defined(__LARGE_DATA_MODEL__)
    uint32_t            size;        /* size of data in 8-bit addressable units */
#else
    uint16_t            size;        /* size of data in 8-bit addressable units */
#endif
} MEMRANGE_CRC_RECORD;
typedef struct memrange_crc_table {
    uint16_t            rec_size;    /* 8-bit addressable units */
    uint16_t            num_recs;    /* how many records are in the table */
    uint16_t            crc_alg_ID;  /* CRC algorithm ID */
    uint32_t            crc_value;   /* result of crc */
    MEMRANGE_CRC_RECORD recs[1];
} MEMRANGE_CRC_TABLE;