CRC integrity check of ARM Cortex flash memory

The program and data contained in a micro controller flash memory can become corrupted over time. The integrity can be verified by running a CRC test over the flash. This post describes how to automatically add the correct CRC to a binary file and later let the micro controller perform the check.This has been tested on an stm32f103 device, but should work on all controllers in this series. Other makers of Cortex M3 devices also often include a CRC unit in their devices, but I do not know if this procedure applies to those.

The stm32f CRC unit has a 32 bit interface register, that is, it’s fed a full word at a time. The unit therefore only works for data buffers that are a multiple of four long. This is the case for program and data saved in the flash if properly 4-byte aligned by the linker. But I cannot see how this hardware can be used to check buffers of arbitrary length, e.g. communication messages, if not both the sender and receiver use a common padding pattern to ensure 4-byte alignment while calculating the CRC. I think the CRC module is primarily implemented for flash integrity verification, as mandated by the IEC60730 safety standard for household appliances, see e.g. Using IEC 60730 for Safe and Reliable Operation of Stellaris Devices (AN01272).

Another problem with the CRC unit is the byte order. CRC is usually calculated over a linear byte sequence, but this unit uses a linear word sequence where the most significant byte of the word is consumed first. The Cortex M3 processor is little endian, so this makes the effective byte sequence to be  3,2,1,0,7,6,5,4,…, different from the usual 0,1,2,3,4,5,6,7.

Two stages are involved with the flash memory CRC check:

  1. Calculate the CRC of the binary file to be flashed and append this CRC to the file
  2. Verify the flash during the micro controller startup or periodically during operation

Calculate the CRC

The flash image file length must be a multiple of 4 for this to work, this is the responsibility of the linker script.

A project make file often specifies an elf output file which in addition to the flash image also holds information used by the debugger. The flashing program can use this elf file, but may as well use only the clean binary image file. The image file is produced by this makefile rule, which also appends the CRC

flashprg.bin:flashprg.elf
        $(OBJCOPY)  flashprg.elf -O binary flashprg.bin
        cortex_crc flashprg.bin

OBJCOPY will likely be set to something like arm-none-eabi-objdump.

cortex_crc is a program which reads in the given file, calculates the CRC and appends the CRC to the file. cortex_crc is a standard CRC-calculation program, but with a small modification.

for (idx=0; idx < len; idx++) {
        bp = src  + (idx^0x3);
        crc = ( crc << 8 ) ^ crctab[ ( ( crc >> 24 ) ^ *bp ) & 0xFF ];
}

Instead of going through the buffer in a linear sequence, the XOR operation inverts the two least significant bits of idx to produce the wanted sequence 3,2,1,0,7,6,5,4,11,10,9,8 … In this way cortex_crc calculates the CRC in the same way as the cortex processor. You may download the source code for cortex_crc.

Verify the flash

The linker file must define some global labels that enables the micro controller to find the end of the image.  This snippet from the ST library  STM32_SEC_FLASH.ld illustrates this

.isr_vector :
{
        . = ALIGN(4);
        KEEP(*(.isr_vector))   
        . = ALIGN(4);
} >FLASH
.text :
{
        . = ALIGN(4);
        *(.text)                   
        *(.text.*)                  
        *(.rodata)                
        *(.rodata*)
        *(.glue_7)
        *(.glue_7t)
        . = ALIGN(4);
        _etext = .;
        _sidata = _etext;
} >FLASH
.data  : AT ( _sidata )
{
        . = ALIGN(4);
        _sdata = . ;
        *(.data)
        *(.data.*)
        . = ALIGN(4);
        _edata = . ;
} >RAM

Observe that all segments are 4-byte aligned at the beginning and end. Three segments are defined here: .isr_vector, .text and .data. The program code resides in the .isr_vector and .text segments, initialized variables in .data, all three segments are stored in the flash. The length of the flash image is given by the address of the  end of the program labeled _etext plus the length of the initialized variables range given by the difference of the  _edata and _sdata label addresses. These three labels are used by the micro controller to find what part of the flash to check. The  names of these labels may vary between linker scripts and you may provide your own if you wish. A pointer to such a label is found by using the & operator. Here is a flash check procedure example:

extern uint32_t _edata[], _etext[], _sdata[];
uint32_t CheckCrc32(void)
{
        uint32_t end;
        uint32_t crc;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
        /* end is what the linker thinks is the end of the image. 
            We later add one word to include the crc. */
        end= (uint32_t)((uint32_t)&_etext + (uint32_t)&_edata - 
                (uint32_t)&_sdata);
        CRC_ResetDR();
        crc= CRC_CalcBlockCRC((uint32_t*)0, end/4+1);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, DISABLE);
        return crc ;
}

This function calculates the end of the image and feeds the CRC unit with words starting at address 0 and ending at the CRC address. By including the CRC in the calculation the returned value is zero if and only if the flash is correct. RCC_AHBPeriphClockCmd enables and disables the clock for the CRC unit.

1 Response to “CRC integrity check of ARM Cortex flash memory”


  • I found the cortex_crc code would not correctly on Windows using open, read, write, close. It would error on not getting a number of bytes divisible by 4 even when my firmware image size was such. So I changed the code to use fopen family of calls as follows and this works:

    /* cortex_crc — calculate crc of file and append at end
    crctab and the crc formula is taken from Linux coreutils cksum.c and is
    Copyright (C) 1992, 1995-2006, 2008-2010 Free Software Foundation, Inc.
    Written by Q. Frank Xia, qx@math.columbia.edu.
    Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.

    How to calculate crc the way the ST cortex processors do was described by http://www.st.com MySTForum
    post at 8/31/2009 9:11 PM by brian.d.myers

    The rest of this source is Copyright (C) 2010 Odd Arild Olsen.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see .

    to compile: gcc cortex_crc.c -ocortex_crc

    Usage: cortex_crc [file…]

    */

    #include
    #include
    #include

    uint32_t crc;
    uint32_t buf[256];

    //—————————————————————————————————————-
    //
    static const uint32_t crctab[256] =
    {
    0x00000000,
    0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
    0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
    0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
    0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
    0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
    0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
    0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
    0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
    0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
    0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
    0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
    0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
    0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
    0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
    0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
    0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
    0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
    0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
    0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
    0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
    0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
    0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
    0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
    0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
    0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
    0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
    0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
    0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
    0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
    0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
    0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
    0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
    0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
    0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
    0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
    0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
    0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
    0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
    0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
    0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
    0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
    0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
    0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
    0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
    0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
    0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
    0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
    0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
    0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
    0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
    0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
    };

    void CrcInit(void)
    {
    crc = 0xffffffff;
    }

    uint32_t CortexCrc(uint8_t *src, int len )
    {
    uint8_t *bp;
    int idx;
    for (idx=0; idx < len; idx++) {
    bp = src + (idx^0x3);
    crc = ( crc <> 24 ) ^ *bp ) & 0xFF ];
    }
    return crc;
    }

    int main(int argc, char *argv[])
    {
    int n;
    FILE* fp;
    char space= ‘ ‘;
    if (argc != 2)
    {
    printf(“Use: cortex_crc filename\n”);
    return 1;
    }
    fp = fopen(argv[1], “rb”);
    if(fp == NULL)
    {
    printf(“Can’t open file %s for reading\n”, argv[1]);
    return 2;
    }
    CrcInit();
    while(1)
    {
    n= fread(buf, 1, sizeof(buf), fp);
    if(n%4 != 0)
    {
    printf(“File length must be multiple of 4 bytes “);
    return 3;
    }
    CortexCrc((uint8_t *)buf, n);
    if(n != sizeof(buf)) break;
    }
    fclose(fp);
    if(crc==0)
    {
    printf(“crc is 0, so the crc has already been appended\n”);
    return 4;
    }

    fp= fopen(argv[1], “ab”);
    if(fp == NULL)
    {
    printf(“Can’t open file %s for writing\n”, argv[1]);
    return 5;
    }
    fwrite(&crc, 1, 4, fp);
    fclose(fp);
    printf(“crc appended to file: %x\n”, crc);
    return 0;
    }

Leave a Reply