Introduction

Erasing and reprogramming the flash of the STM32 causes it to wear out over time. During the lifetime of a product, even a lot of software upgrades are a negligible wear on flash memory. But during software development you may reprogram the entire flash every few minutes to test your changes. Over time these writes can wear out the flash and ultimatively make it unwritable, rendering the entire chip useless. To reduce wear on flash the program can be run directly from SRAM.

The Linker

The usual linker script for STM32 may look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
MEMORY
{
	flash(rx) : ORIGIN = 0x08000000, LENGTH = 512K
	sram(rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}

SECTIONS
{
	.vectors : {
		*(isr_vectors)
	} > flash
	
	.text : {
		*(.text*)
	} > flash

	.rodata : {
		*(.rodata*)
	} > flash
	
	.data : {
		__data_start__ = . ;
		*(.data*)
		__data_end__ = . ;
	} > sram AT > flash

	__data_flash__ = LOADADDR(.data);

	.bss : {
		__bss_start__ = . ;
		*(.bss)
		__bss_end__ = . ;
	} > sram
}

Read only data like code and constant variables are put in flash. We can adjust the sections .vectors .text and .rodata to put all data into SRAM. For Cortex-M processors it is important that the vector table is placed at 0x0. Though SRAM starts at 0x20000000, by setting the boot pins BOOT0 and BOOT1 high on the STM32F4 we can alias the SRAM memory region to 0x0. The modified linker script looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
MEMORY
{
	flash(rx) : ORIGIN = 0x08000000, LENGTH = 512K
	sram(rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}

SECTIONS
{
	.vectors : {
		*(isr_vectors)
	} > sram
	
	.text : {
		*(.text*)
	} > sram

	.rodata : {
		*(.rodata*)
	} > sram
	
	.data : {
		__data_start__ = . ;
		*(.data*)
		__data_end__ = . ;
	} > sram

	.bss : {
		__bss_start__ = . ;
		*(.bss)
		__bss_end__ = . ;
	} > sram
}

Additionally, because the .data section is already in SRAM from the start, we don’t need to copy it from FLASH to SRAM in the startup code.

Loading the Program

The executable can be loaded onto the chip with gdb:

Connect to OpenOCD:

(gdb) target remote localhost:3333
Remote debugging using localhost:3333

The load the ELF binary:

(gdb) load main.elf
Loading section .vectors, size 0x194 lma 0x20000000
Loading section .text, size 0x234 lma 0x20000194
Start address 0x2000039c, load size 968
Transfer rate: 135 KB/sec, 484 bytes/write.

Finally reset the CPU (this won’t clear SRAM): 1

(gdb) monitor reset halt
adapter speed: 2000 kHz
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x2000039c msp: 0x20018000

  1. If you’re using the STM32F4x1 Black Pill BOOT1 (PB2) must be connected to Vdd and BOOT0 must be held every time the chip is reset (either through gdb or hardware reset) otherwise SRAM won’t be aliased to 0x0 ↩︎