The STM32 CRC calculation unit has the following default characteristic:
#define RCC_AHBENR_CRCEN (1 << 6) /* 6: CRC clock enable */I will make use of the default setup so I only need to refer to the Data Register and the Control Register. I create all register definitions as there is a gap in the memory layout.
#define CRC ((volatile unsigned *) 0x40023000) #define CRC_DR CRC[ 0] #define CRC_IDR CRC[ 1] #define CRC_CR CRC[ 2] #define CRC_INIT CRC[ 4]
I use conditional compilation, the build option `CRC32SIGN` will be defined in the Makefile.
The constant variable `crcsum` is a placeholder with the hexadecimal value
DEADC0DE
in byte order. This value will be overriden by the
computed CRC value during build. The linker will put `crcsum` at the end of the
used FLASH.
`check_flash()` use the CRC calculation unit to compute the CRC value from beginning of FLASH `isr_vector` to end of FLASH `crcsum`. If `crcsum` value is the correct CRC, the computed result will be 0.
#ifdef CRC32SIGN const unsigned crcsum __attribute__((section(".crc_chk"))) = 0xDEC0ADDE ; static int check_flash( void) { int ret = 0 ; /* Flash CRC validation */ RCC_AHBENR |= RCC_AHBENR_CRCEN ; /* Enable CRC periph */ CRC_CR = 1 ; /* Reset */ if( CRC_DR == 0xFFFFFFFF) { /* CRC periph is alive and resetted */ const unsigned *wp = (const unsigned *) isr_vector ; while( wp <= &crcsum) CRC_DR = *wp++ ; ret = CRC_DR == 0 ; } RCC_AHBENR &= ~RCC_AHBENR_CRCEN ; /* Disable CRC periph */ return ret ; } #endifFlash content is checked before calling `init()`. This means the check is done using the default clock setup of HSI 8 MHz.
if( #ifdef CRC32SIGN check_flash() && #endif init() == 0) main() ;
.crc __etext + SIZEOF(.data) : { KEEP(*(.crc_chk)) } > FLASH
$ touch empty.bin $ ./sign32 empty.bin FFFFFFFF empty.bin: 0, signed.bin: 4If the input file is already signed, the output signed.bin is identical to the input.
$ mv signed.bin FFFFFFFF.bin $ ./sign32 FFFFFFFF.bin 00000000 FFFFFFFF.bin: 4, signed.bin: 4Padding with null is done on the input to insure the calculation is DWORD aligned.
$ echo > nl.bin $ ./sign32 nl.bin E88E0BAD nl.bin: 1, signed.bin: 8 $ hexdump -C signed.bin 00000000 0a 00 00 00 ad 0b 8e e8 |........| 00000008Calculation stops when the placeholder DEADC0DE is found or the end of the input file is reached.
I create a folder crc32/ for sign32.c and its Makefile.
The core of sign32.c is customizable to do CRC calculation bitwise, unrolled bitwise, tablewise or to generate the CRC table.
# build options CRC32SIGN := 1 ifdef CRC32SIGN CDEFINES += -DCRC32SIGN=$(CRC32SIGN) endif %.$(BINLOC).bin: %.elf @echo $@ $(OBJCOPY) -O binary $< $@ ifdef CRC32SIGN crc32/sign32 $@ mv signed.bin $@ %.hex: %.$(BINLOC).bin @echo $@ from $< $(OBJCOPY) --change-address=$(BINLOC) -I binary -O ihex $< $@ else %.hex: %.elf @echo $@ from $< $(OBJCOPY) -O ihex $< $@ endif
$ make f030f4.elf from startup.crc.o txeie.o uptime.o libstm32.a Memory region Used Size Region Size %age Used FLASH: 1728 B 16 KB 10.55% RAM: 16 B 4 KB 0.39% text data bss dec hex filename 1725 0 16 1741 6cd f030f4.elf f030f4.0x08000000.bin crc32/sign32 f030f4.0x08000000.bin DEAD68BE f030f4.0x08000000.bin: 1724, signed.bin: 1728 mv signed.bin f030f4.0x08000000.bin f030f4.hex from f030f4.0x08000000.binI can double check that the value at the end of the binary file matches.
$ hexdump -C f030f4.0x08000000.bin | tail 00000630 8b 42 01 d3 cb 00 c0 1a 52 41 83 08 8b 42 01 d3 |.B......RA...B..| 00000640 8b 00 c0 1a 52 41 43 08 8b 42 01 d3 4b 00 c0 1a |....RAC..B..K...| 00000650 52 41 41 1a 00 d2 01 46 52 41 10 46 70 47 ff e7 |RAA....FRA.FpG..| 00000660 01 b5 00 20 00 f0 06 f8 02 bd c0 46 00 29 f7 d0 |... .......F.)..| 00000670 76 e7 70 47 70 47 c0 46 50 4c 4c 48 53 45 0a 00 |v.pGpG.FPLLHSE..| 00000680 20 25 64 20 25 73 25 73 00 75 70 00 77 65 65 6b | %d %s%s.up.week| 00000690 00 64 61 79 00 68 6f 75 72 00 6d 69 6e 75 74 65 |.day.hour.minute| 000006a0 00 73 65 63 6f 6e 64 00 30 31 32 33 34 35 36 37 |.second.01234567| 000006b0 38 39 41 42 43 44 45 46 00 00 00 00 be 68 ad de |89ABCDEF.....h..| 000006c0I can flash the resulting intel hex file and see that it executes.
$ stm32flash -x f030f4.hex COM6 stm32flash 0.7-patch-exe http://stm32flash.sourceforge.net/ Using Parser : Intel HEX Location : 0x8000000 Size : 1728 Interface serial_w32: 57600 8E1 Version : 0x31 Option 1 : 0x00 Option 2 : 0x00 Device ID : 0x0444 (STM32F03xx4/6) - RAM : Up to 4KiB (2048b reserved by bootloader) - Flash : Up to 32KiB (size first sector: 4x1024) - Option RAM : 16b - System RAM : 3KiB Write to memory Erasing memory Wrote address 0x080006c0 (100.00%) Done. Starting execution at address 0x08000000... done.I can use stm32flash to compute the CRC-32 checksum on the first 1728 bytes of FLASH, the result is 0 as this covers both the payload AND the CRC-32 checksum value.
$ stm32flash -C -S 0x08000000:1728 COM6 stm32flash 0.7-patch-exe http://stm32flash.sourceforge.net/ Interface serial_w32: 57600 8E1 Version : 0x31 Option 1 : 0x00 Option 2 : 0x00 Device ID : 0x0444 (STM32F03xx4/6) - RAM : Up to 4KiB (2048b reserved by bootloader) - Flash : Up to 32KiB (size first sector: 4x1024) - Option RAM : 16b - System RAM : 3KiB CRC computation CRC address 0x080006c0 (100.00%) Done. CRC(0x08000000-0x080006c0) = 0x00000000If I ask stm32flash to compute the CRC-32 checksum on the first 1724 bytes (payload excluding CRC-32 checksum value), it returns 0xdead68be, which is the value computed at build time.
$ stm32flash -C -S 0x08000000:1724 COM6 stm32flash 0.7-patch-exe http://stm32flash.sourceforge.net/ Interface serial_w32: 57600 8E1 Version : 0x31 Option 1 : 0x00 Option 2 : 0x00 Device ID : 0x0444 (STM32F03xx4/6) - RAM : Up to 4KiB (2048b reserved by bootloader) - Flash : Up to 32KiB (size first sector: 4x1024) - Option RAM : 16b - System RAM : 3KiB CRC computation CRC address 0x080006bc (100.00%) Done. CRC(0x08000000-0x080006bc) = 0xdead68beBecause STM32F030 USART bootloader is v3.1, it doesn't implement the CRC checksum command included in v3.3. This means that in this case stm32flash computes the CRC checksum value on its own. You can check the sources of stm32flash for its implementation of the CRC-32 calculation.
check_flash()
implementation I just made relies on the default settings for polynomial,
initial value, polynomial length and shift direction. This should be common to
most chipset.
Next, I will use the ADC to read a resistor value.