Using stm32flash I can request the bootloader to transfer execution to the code in flash memory.
stm32flash -g 0 COM6With my current code, this works fine as far as I don’t use interrupt subroutine. ledon and blink both work, but ledtick will reset once the
SysTick_Handler()
interrupt routine is triggered
for the first time. This is due to the fact that the system memory is still
mapped at address 0x0 where my interrupt subroutine vector should be. To
fix this, I need to insure the flash is mapped at address 0x0 before I
enable interrupts.
The memory mapping is managed through the System Configuration
controller SYSCFG, so I need to activate it and reconfigure the mapping
before my SysTick initialization code in init()
.
/* Make sure FLASH Memory is mapped at 0x0 before enabling interrupts */ RCC_APB2ENR |= RCC_APB2ENR_SYSCFGEN ; /* Enable SYSCFG */ SYSCFG_CFGR1 &= ~3 ; /* Map FLASH at 0x0 */and add the SYSCFG peripheral description.
#define RCC_APB2ENR_SYSCFGEN 0x00000001 /* 1: SYSCFG clock enable */ #define SYSCFG ((volatile long *) 0x40010000) #define SYSCFG_CFGR1 SYSCFG[ 0]With this in place, I can now switch easily from bootloader to flash code by sending a go command via stm32flash.
The answer is in the application note AN2606 STM32 microcontroller system memory boot mode. Section 5 covers STM32F03xx4/6 devices bootloader and it states in 5.1 Bootloader Configuration:
2 Kbyte starting from address 0x20000000 are used by the bootloader firmware.
I am using a STM32F030F4P6, which has 4KB RAM and the bootloader firmware is using the first 2KB. That means I have only 2KB left to use starting from address 0x20000800.
Actually, I have only 2KB left to use until the bootloader firmware transfer execution to my code in RAM. Once my code executes, I can reclaim the first 2KB. This is exactly what I have to tell the linker.
I just create a new linker script f030f4.ram.ld by copying f030f4.ld and changing the memory configuration.
/* FLASH means code, read only data and data initialization */ FLASH (rx) : ORIGIN = 0x20000800, LENGTH = 2K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 2KI can build ledon or blink with that new linker script and check the resulting f030f4.map file.
stm32flash -w blink.bin -S 0x20000800 COM6 stm32flash -g 0x20000800 COM6This work just fine but of course the executable of ledon or blink doesn’t use interrupt routines.
isr_vector[]
to the beginning of RAM in the
space reserved by the linker.
/* Make sure SRAM Memory is mapped at 0x0 before enabling interrupts */ RCC_APB2ENR |= RCC_APB2ENR_SYSCFGEN ; /* Enable SYSCFG */ SYSCFG_CFGR1 |= 3 ; /* Map RAM at 0x0 */The ISR vector will have at most 16 + 32 entries for STM32F030xx, that means 192 bytes need to be reserved. I add a new section before .data in the link script.
.isrdata : { ram_vector = . ; . = . + 192 ; } > RAM .data : AT (__etext) { ...In the startup code, I add the code to copy the
isr_vector[]
to
the location reserved at the beginning of RAM.
#define ISRV_SIZE (sizeof isr_vector / sizeof *isr_vector) extern isr_p ram_vector[] ; /* Copy isr vector to beginning of RAM */ for( unsigned i = 0 ; i < ISRV_SIZE ; i++) ram_vector[ i] = isr_vector[ i] ;RAM initialization now consists of
ram_vector[]
.
.isrdata 0x20000000 0xc0 0x20000000 ram_vector = . 0x200000c0 . = (. + 0xc0) *fill* 0x20000000 0xc0 .data 0x200000c0 0x0 load address 0x20000c88 0x200000c0 __data_start__ = .I can now use stm32flash to write those executables in RAM and request execution.
| Model | ISRV Location | Load address (word aligned) | |---------|--------------------|-----------------------------------------| |BOOTFLASH| Beginning of FLASH | Beginning of FLASH | | BOOTRAM | Beginning of RAM | Beginning of RAM | | GOFLASH | Beginning of RAM | In FLASH | | GORAM | Beginning of RAM | In RAM, after bootloader reserved space |
MEMORY { /* FLASH means code, read only data and data initialization */ FLASH (rx) : ORIGIN = DEFINED(FLASHSTART) ? FLASHSTART : 0x08000000, LENGTH = DEFINED(FLASHSIZE) ? FLASHSIZE : 16K RAM (rwx) : ORIGIN = DEFINED(RAMSTART) ? RAMSTART : 0x20000000, LENGTH = DEFINED(RAMSIZE) ? RAMSIZE : 4K }The Makefile will provide the necessary addresses and sizes information by passing parameters to the linker:
FLASHSTART
,
FLASHSIZE
, RAMSTART
, RAMSIZE
.
/* In RAM isr vector reserved space at beginning of RAM */ .isrdata (NOLOAD): { KEEP(*(.ram_vector)) } > RAMThe startup code will allocate
ram_vector[]
in .ram_vector
section if needed.
#if RAMISRV == 2 # define ISRV_SIZE (sizeof isr_vector / sizeof *isr_vector) isr_p ram_vector[ ISRV_SIZE] __attribute__((section(".ram_vector"))) ; #endif int main( void) ; void Reset_Handler( void) { const long *f ; /* from, source constant data from FLASH */ long *t ; /* to, destination in RAM */ #if RAMISRV == 2 /* Copy isr vector to beginning of RAM */ for( unsigned i = 0 ; i < ISRV_SIZE ; i++) ram_vector[ i] = isr_vector[ i] ; #endif /* Assume: ** __bss_start__ == __data_end__ ** All sections are 4 bytes aligned */ f = __etext ; for( t = __data_start__ ; t < __bss_start__ ; t += 1) *t = *f++ ; while( t < &__bss_end__) *t++ = 0 ; /* Make sure active isr vector is mapped at 0x0 before enabling interrupts */ RCC_APB2ENR |= RCC_APB2ENR_SYSCFGEN ; /* Enable SYSCFG */ #if RAMISRV SYSCFG_CFGR1 |= 3 ; /* Map RAM at 0x0 */ #else SYSCFG_CFGR1 &= ~3 ; /* Map FLASH at 0x0 */ #endif if( init() == 0) main() ; for( ;;) __asm( "WFI") ; /* Wait for interrupt */ }The SYSCFG controller definition is now included through a chipset specific header file. This way I can maintain all the chipset controllers and peripherals in one place.
#include "stm32f030xx.h"
### Memory Models # By default we use the memory mapping from linker script # In RAM Execution, load and start by USART bootloader # Bootloader uses first 2K of RAM, execution from bootloader #FLASHSTART = 0x20000800 #FLASHSIZE = 2K #RAMSTART = 0x20000000 #RAMSIZE = 2K # In RAM Execution, load and start via SWD # 4K RAM available, execution via SWD #FLASHSTART = 0x20000000 #FLASHSIZE = 3K #RAMSTART = 0x20000C00 #RAMSIZE = 1K # In Flash Execution # if FLASHSTART is not at beginning of FLASH: execution via bootloader or SWD #FLASHSTART = 0x08000000 #FLASHSIZE = 16K #RAMSTART = 0x20000000 #RAMSIZE = 4K # ISR vector copied and mapped to RAM when FLASHSTART != 0x08000000 ifdef FLASHSTART ifneq ($(FLASHSTART),0x08000000) ifeq ($(FLASHSTART),0x20000000) # Map isr vector in RAM RAMISRV := 1 else # Copy and map isr vector in RAM RAMISRV := 2 endif endif BINLOC = $(FLASHSTART) else BINLOC = 0x08000000 endifCompiler and linker have different syntax for defining symbols through command line parameters.
CPU = -mthumb -mcpu=cortex-m0 --specs=nano.specs ifdef RAMISRV CDEFINES = -DRAMISRV=$(RAMISRV) endif WARNINGS=-pedantic -Wall -Wextra -Wstrict-prototypes CFLAGS = $(CPU) -g $(WARNINGS) -Os $(CDEFINES) LD_SCRIPT = generic.ld ifdef FLASHSTART LDOPTS =--defsym FLASHSTART=$(FLASHSTART) --defsym FLASHSIZE=$(FLASHSIZE) LDOPTS +=--defsym RAMSTART=$(RAMSTART) --defsym RAMSIZE=$(RAMSIZE) endif LDOPTS +=-Map=$(subst .elf,.map,$@) -cref --print-memory-usage comma :=, space :=$() # one space before the comment LDFLAGS =-Wl,$(subst $(space),$(comma),$(LDOPTS))As I am revising the compilation flags, I have increased the level of warnings by adding -pedantic, -Wstrict-prototypes.
Build rules updated with new symbols for the linker.
$(PROJECT).elf: $(OBJS) libstm32.a boot.elf: boot.o ledon.elf: ledon.o blink.elf: blink.o ledtick.elf: ledtick.o cstartup.elf: cstartup.o %.elf: @echo $@ $(CC) $(CPU) -T$(LD_SCRIPT) $(LDFLAGS) -nostartfiles -o $@ $+ $(SIZE) $@ $(OBJDUMP) -hS $@ > $(subst .elf,.lst,$@)The projects composition need to be updated to use the new startup.
SRCS = startup.ram.c txeie.c uptime.1.cFinally, to keep track of the memory model and the load location, I put the load address in the name of the binary file generated.
all: $(PROJECT).$(BINLOC).bin $(PROJECT).hexThis way if I build uptime prototype in GORAM memory model
$ make f030f4.elf text data bss dec hex filename 1164 0 20 1184 4a0 f030f4.elf f030f4.hex f030f4.0x20000800.binThe name of the file will remind me where to load the code.
$ stm32flash -w f030f4.0x20000800.bin -S 0x20000800 COM6 $ stm32flash -g 0x20000800
Until v0.7 is out, I am using my own patched version of stm32flash or the binary files when I need to test GOFLASH and GORAM memory models.
As I branched off my own patched version of stm32flash, I added a
-x
option to write and execute an intel hex file:
stm32flash -x file.hex COM#
Using the USART bootloader, I validate BOOTFLASH, GOFLASH and GORAM with stm32flash and STM32 Cube Programmer.
Using the SWD interface, I validate BOOTFLASH, GOFLASH, BOOTRAM and GORAM with STM32 Cube Programmer.