1
0
mirror of https://github.com/rfivet/stm32bringup.git synced 2024-12-20 15:58:44 -05:00
stm32bringup/docs/38_crc32.html

334 lines
9.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>3.8 CRC-32 Code Validation</title>
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
<h1>3.8 CRC-32 Code Validation</h1
The STM32F030 family comes with a CRC calculation unit. It can be used
during startup to validate the integrity of the code in memory.
<p>
Cyclic Redundancy Check is a way to do error detection and correction. I
have already met CRC when dealing with the DS18B20 sensor where CRC-8 is
used during the scratchpad 9 bytes transmission.
<p>
The STM32 CRC calculation unit has the following default characteristic:
<ul>
<li> POLY32 is 0x04C11DB7.
<li> Initialisation 0xFFFFFFFF.
<li> High bit first (left shift).
<li> 32 bit word input, little endian.
</ul>
I don't plan to write a self-signing executable, so on top of the STM32
startup code validation, I will also write a sign32 command to sign
binary files during build.
<h2>Implementation Steps</h2>
<ol>
<li> Update <b>stm32f030xx.h</b> with the CRC calculation unit definitions.
<li> Update startup with `check_flash()` to be tested before `init()` is
called.
<li> Update <b>generic.ld</b> with a new section used as placeholder for the CRC
sum at the end of the flashable content.
<li> Write `sign32` command to sign a binary file.
<li> Update <b>Makefile</b> to sign the binary executable and create an intel
hex version out of it.
</ol>
<h3>1. stm32f030xx.h</h3>
The CRC calculation unit is on the AHB bus and its clock need to be
enabled before use.
<pre>
#define RCC_AHBENR_CRCEN (1 << 6) /* 6: CRC clock enable */
</pre>
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.
<pre>
#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]
</pre>
<h3>2. startup.crc.c</h3>
I make a copy of <b>startup.ram.c</b> into <b>startup.crc.c</b>.
<p>
I use conditional compilation, the build option `CRC32SIGN` will be
defined in the Makefile.
<p>
The constant variable `crcsum` is a placeholder with the hexadecimal value
<code>DEADC0DE</code> 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.
<p>
`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.
<pre>
#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 ;
}
#endif
</pre>
Flash content is checked before calling `init()`. This means the check
is done using the default clock setup of HSI 8 MHz.
<pre>
if(
#ifdef CRC32SIGN
check_flash() &&
#endif
init() == 0)
main() ;
</pre>
<h3>3. generic.ld</h3>
I add a new section to hold the CRC value placeholder. This needs to be
DWORD aligned and at the end of the used FLASH area.
<pre>
.crc __etext + SIZEOF(.data) :
{
KEEP(*(.crc_chk))
} > FLASH
</pre>
<h3>4. sign32</h3>
The command `sign32` creates a <b>signed.bin</b> file from it's input file
specified as parameter.
<pre>
$ touch empty.bin
$ ./sign32 empty.bin
FFFFFFFF empty.bin: 0, signed.bin: 4
</pre>
If the input file is already signed, the output signed.bin is identical
to the input.
<pre>
$ mv signed.bin FFFFFFFF.bin
$ ./sign32 FFFFFFFF.bin
00000000 FFFFFFFF.bin: 4, signed.bin: 4
</pre>
Padding with null is done on the input to insure the calculation is
DWORD aligned.
<pre>
$ 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 |........|
00000008
</pre>
Calculation stops when the placeholder DEADC0DE is found or the end of
the input file is reached.
<p>
I create a folder <b>crc32/</b> for <b>sign32.c</b> and its <b>Makefile</b>.
<p>
The core of sign32.c is customizable to do CRC calculation bitwise,
unrolled bitwise, tablewise or to generate the CRC table.
<h3>5. Makefile</h3>
The build option `CRC32SIGN` controls the signature of the binary file
and the generation of the intel hex version from the signed binary using
`objcopy`.
<pre>
# 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 $@
$(OBJCOPY) --change-address=$(BINLOC) -I binary -O ihex $< $@
endif
</pre>
<h2>Building and testing</h2>
If I build an executable, I can see that the binary file is CRC-32
signed. In the example below, the CRC-32 signature is 0xBC689506 and the
total binary image is 2680 bytes long.
<pre>
$ make
f030f4.elf
Memory region Used Size Region Size %age Used
FLASH: 2680 B 16 KB 16.36%
RAM: 24 B 4 KB 0.59%
text data bss dec hex filename
2673 4 20 2697 a89 f030f4.elf
f030f4.0x08000000.bin
crc32/sign32 f030f4.0x08000000.bin
BC689506 f030f4.0x08000000.bin: 2676, signed.bin: 2680
mv signed.bin f030f4.0x08000000.bin
f030f4.hex
</pre>
I can double check that the value at the end of the binary file matches.
<pre>
$ hexdump -C f030f4.0x08000000.bin | tail
000009f0 01 46 63 46 52 41 5b 10 10 46 01 d3 40 42 00 2b |.FcFRA[..F..@B.+|
00000a00 00 d5 49 42 70 47 63 46 5b 10 00 d3 40 42 01 b5 |..IBpGcF[...@B..|
00000a10 00 20 00 f0 05 f8 02 bd 00 29 f8 d0 16 e7 70 47 |. .......)....pG|
00000a20 70 47 c0 46 50 4c 4c 48 53 49 0a 00 20 25 64 20 |pG.FPLLHSI.. %d |
00000a30 25 73 25 73 00 75 70 00 77 65 65 6b 00 64 61 79 |%s%s.up.week.day|
00000a40 00 68 6f 75 72 00 6d 69 6e 75 74 65 00 73 65 63 |.hour.minute.sec|
00000a50 6f 6e 64 00 30 31 32 33 34 35 36 37 38 39 41 42 |ond.0123456789AB|
00000a60 43 44 45 46 00 00 20 2b 2b 10 0a 02 08 00 00 00 |CDEF.. ++.......|
00000a70 ef 00 00 00 06 95 68 bc |......h.|
00000a78
</pre>
I can flash the resulting intel hex file and see that it executes.
<pre>
$ stm32flash -x f030f4.hex COM3
stm32flash 0.6-patch-hex
http://stm32flash.sourceforge.net/
Using Parser : Intel HEX
Location : 0x8000000
Size : 2680
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 0x08000a78 (100.00%) Done.
Starting execution at address 0x08000000... done.
</pre>
I can use stm32flash to compute the CRC-32 checksum on the first 2680
bytes of FLASH, the result is 0 as this covers both the payload AND
the CRC-32 checksum value.
<pre>
$ stm32flash -C -S 0x08000000:2680 COM3
stm32flash 0.6-patch-hex
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 0x08000a78 (100.00%) Done.
CRC(0x08000000-0x08000a78) = 0x00000000
</pre>
If I ask stm32flash to compute the CRC-32 checksum on the first 2676
bytes (payload excluding CRC-32 checksum value), it returns 0xbc689506,
which is the value computed at build time.
<pre>
$ stm32flash -C -S 0x08000000:2676 COM3
stm32flash 0.6-patch-hex
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 0x08000a74 (100.00%) Done.
CRC(0x08000000-0x08000a74) = 0xbc689506
</pre>
Because 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.
<h2>Checkpoint</h2>
There is variation in the functionality of the CRC calculation unit
among different STM32 chipset family. The <code>check_flash()</code>
implementation I just made relying on the default settings for polynomial,
initial value, polynomial length and shift direction should be common.
<p>
<a href="39_resistor.html">Next</a>, I will use the ADC to read a resistor
value.
<hr>© 2020-2024 Renaud Fivet
</body>
</html>