STM32F3: Implement self-update
This commit is contained in:
parent
636a93390f
commit
8fe4f9e009
8 changed files with 347 additions and 49 deletions
|
|
@ -34,7 +34,7 @@ Not all features are implemented for all MCUs, following is supported MCUs and i
|
|||
| K32L2 | ✔ | ✔ | | | | |
|
||||
| LPC55 | ✔ | ✔ | | | ✔ | |
|
||||
| iMXRT | ✔ | ✔ | ✔ | | ✔ | |
|
||||
| STM32F3 | ✔ | ✔ | | | ✔ | |
|
||||
| STM32F3 | ✔ | ✔ | ✔ | | ✔ | |
|
||||
| STM32F4 | ✔ | ✔ | ✔ | ✔ | ✔ | |
|
||||
|
||||
## Build and Flash
|
||||
|
|
|
|||
|
|
@ -13,5 +13,13 @@ flash-dfu-util: $(BUILD)/$(OUTNAME).bin
|
|||
# Self-update
|
||||
#------------------------------------------
|
||||
|
||||
self-update:
|
||||
@echo "not implemented yet"
|
||||
# direction containing Makefile for building update app
|
||||
SELF_DIR = apps/self_update
|
||||
|
||||
$(SELF_DIR)/bootloader_bin.c: $(BUILD)/$(OUTNAME).bin
|
||||
$(PYTHON3) $(TOP)/lib/uf2/utils/uf2conv.py --carray $^ -o $@
|
||||
|
||||
# remove bootloader_bin.c at the end to force re-generate each time
|
||||
self-update: $(SELF_DIR)/bootloader_bin.c
|
||||
make -C $(SELF_DIR) BOARD=$(BOARD) LOG=$(LOG) LOGGER=$(LOGGER) self-update
|
||||
@rm -f $^
|
||||
|
|
|
|||
25
ports/stm32f3/apps/self_update/Makefile
Normal file
25
ports/stm32f3/apps/self_update/Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
PORT = stm32f3
|
||||
OUTNAME = update-tinyuf2-$(BOARD)
|
||||
|
||||
# skip bootloader src
|
||||
BUILD_APPLICATION = 1
|
||||
|
||||
# skip tinyusb src
|
||||
BUILD_NO_TINYUSB = 1
|
||||
|
||||
CFLAGS += -DTINYUF2_SELF_UPDATE -Wno-error=stringop-overread
|
||||
|
||||
include ../../../make.mk
|
||||
include ../../port.mk
|
||||
|
||||
SRC_C += \
|
||||
apps/self_update/self_update.c \
|
||||
$(CURRENT_PATH)/bootloader_bin.c
|
||||
|
||||
include ../../../rules.mk
|
||||
|
||||
self-update: $(BUILD)/$(OUTNAME).uf2
|
||||
|
||||
$(BUILD)/$(OUTNAME).uf2: $(BUILD)/$(OUTNAME).hex
|
||||
@echo CREATE $@
|
||||
$(PYTHON3) $(TOP)/lib/uf2/utils/uf2conv.py -f $(UF2_FAMILY_ID) -c -o $@ $^
|
||||
|
|
@ -32,24 +32,24 @@
|
|||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
#define FLASH_CACHE_SIZE 512
|
||||
#define FLASH_CACHE_INVALID_ADDR 0xffffffff
|
||||
// no caching
|
||||
// #define FLASH_CACHE_SIZE 512
|
||||
// #define FLASH_CACHE_INVALID_ADDR 0xffffffff
|
||||
|
||||
// define flash space, reserve first 8 sectors for bootloader up to 3FFF
|
||||
#define BOARD_FLASH_SECTORS 64
|
||||
#define BOARD_FIRST_FLASH_SECTOR_TO_ERASE 8
|
||||
#define FLASH_BASE_ADDR 0x08000000UL
|
||||
|
||||
#define APP_LOAD_ADDRESS 0x08004000
|
||||
enum
|
||||
{
|
||||
SECTOR_COUNT = (BOARD_FLASH_SIZE / BOARD_PAGE_SIZE),
|
||||
BOOTLOADER_SECTOR_COUNT = ((BOARD_FLASH_APP_START - FLASH_BASE_ADDR) / BOARD_PAGE_SIZE)
|
||||
};
|
||||
|
||||
/* flash parameters */
|
||||
#define SIZE 2048
|
||||
|
||||
static uint8_t erasedSectors[BOARD_FLASH_SECTORS];
|
||||
static uint8_t erased_sectors[SECTOR_COUNT] = { 0 };
|
||||
|
||||
uint32_t flash_func_sector_size(unsigned sector)
|
||||
{
|
||||
if (sector < BOARD_FLASH_SECTORS) {
|
||||
return SIZE;
|
||||
if (sector < SECTOR_COUNT) {
|
||||
return BOARD_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -71,59 +71,76 @@ static bool is_blank(uint32_t addr, uint32_t size)
|
|||
return true;
|
||||
}
|
||||
|
||||
//ADDR data
|
||||
void flash_write(uint32_t dst, const uint8_t *src, int len)
|
||||
static bool flash_erase(uint32_t addr)
|
||||
{
|
||||
// assume sector 0-7 (bootloader) is same size as sector 1
|
||||
uint32_t addr = APP_LOAD_ADDRESS;
|
||||
// starting address from 0x08000000
|
||||
uint32_t sector_addr = FLASH_BASE_ADDR;
|
||||
bool erased = false;
|
||||
|
||||
uint32_t sector = 0;
|
||||
int erased = false;
|
||||
uint32_t size = 0;
|
||||
|
||||
for ( unsigned i = 0; i < BOARD_FLASH_SECTORS; i++ )
|
||||
for ( uint32_t i = 0; i < SECTOR_COUNT; i++ )
|
||||
{
|
||||
TUF2_ASSERT(sector_addr < FLASH_BASE_ADDR + BOARD_FLASH_SIZE);
|
||||
|
||||
size = flash_func_sector_size(i);
|
||||
if ( addr + size > dst )
|
||||
if ( sector_addr + size > addr )
|
||||
{
|
||||
sector = i + 1;
|
||||
erased = erasedSectors[i];
|
||||
erasedSectors[i] = 1; // don't erase anymore - we will continue writing here!
|
||||
sector = i;
|
||||
erased = erased_sectors[i];
|
||||
erased_sectors[i] = 1; // don't erase anymore - we will continue writing here!
|
||||
break;
|
||||
}
|
||||
addr += size;
|
||||
sector_addr += size;
|
||||
}
|
||||
|
||||
if (sector == 0)
|
||||
(void)sector;
|
||||
|
||||
#ifndef TINYUF2_SELF_UPDATE
|
||||
// skip erasing sector0 if not self-update
|
||||
TUF2_ASSERT(sector);
|
||||
#endif
|
||||
|
||||
if ( !erased && !is_blank(sector_addr, size))
|
||||
{
|
||||
TUF2_LOG1("invalid sector\r\n");
|
||||
}
|
||||
|
||||
HAL_FLASH_Unlock();
|
||||
|
||||
if (!erased && !is_blank(addr, size))
|
||||
{
|
||||
uint32_t SectorError = 0;
|
||||
|
||||
TUF2_LOG1("Erase: %08lX size = %lu\n", addr, size);
|
||||
TUF2_LOG1("Erase: %08lX size = %lu KB ... ", sector_addr, size / 1024);
|
||||
|
||||
FLASH_EraseInitTypeDef EraseInit;
|
||||
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
EraseInit.PageAddress = addr;
|
||||
EraseInit.NbPages = ((0x08040000 - addr)/size);
|
||||
EraseInit.PageAddress = sector_addr;
|
||||
EraseInit.NbPages = ((BOARD_FLASH_APP_START - sector_addr)/size);
|
||||
|
||||
uint32_t SectorError = 0;
|
||||
HAL_FLASHEx_Erase(&EraseInit, &SectorError);
|
||||
FLASH_WaitForLastOperation(HAL_MAX_DELAY);
|
||||
|
||||
if (SectorError != 0xFFFFFFFF)
|
||||
{
|
||||
TUF2_LOG1("failed to erase!\r\n");
|
||||
}
|
||||
TUF2_LOG1("OK\r\n");
|
||||
TUF2_ASSERT( (SectorError != 0xFFFFFFFF) && is_blank(sector_addr, size) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void flash_write(uint32_t dst, const uint8_t *src, int len)
|
||||
{
|
||||
flash_erase(dst);
|
||||
|
||||
TUF2_LOG1("Write flash at address %08lX\r\n", dst);
|
||||
for (int i = 0; i < len; i += 4)
|
||||
{
|
||||
uint32_t data = *( (uint32_t*) ((void*) (src + i)) );
|
||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, dst + i, (uint64_t) data);
|
||||
|
||||
if ( HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, dst + i, (uint64_t) data) != HAL_OK )
|
||||
{
|
||||
TUF2_LOG1("Failed to write flash at address %08lX\r\n", dst + i);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( FLASH_WaitForLastOperation(HAL_MAX_DELAY) != HAL_OK )
|
||||
{
|
||||
TUF2_LOG1("Waiting on last operation failed\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// verify contents
|
||||
|
|
@ -159,7 +176,9 @@ void board_flash_flush(void)
|
|||
void board_flash_write (uint32_t addr, void const *data, uint32_t len)
|
||||
{
|
||||
// TODO skip matching contents
|
||||
HAL_FLASH_Unlock();
|
||||
flash_write(addr, data, len);
|
||||
HAL_FLASH_Lock();
|
||||
}
|
||||
|
||||
void board_flash_erase_app(void)
|
||||
|
|
@ -175,9 +194,61 @@ bool board_flash_protect_bootloader(bool protect)
|
|||
}
|
||||
|
||||
#ifdef TINYUF2_SELF_UPDATE
|
||||
|
||||
bool is_new_bootloader_valid(const uint8_t * bootloader_bin, uint32_t bootloader_len)
|
||||
{
|
||||
// at least larger than vector table
|
||||
if (bootloader_len < 512 ) return false;
|
||||
|
||||
// similar to board_app_valid() check
|
||||
if((((*(uint32_t*)bootloader_bin) - BOARD_RAM_START) <= BOARD_RAM_SIZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void board_self_update(const uint8_t * bootloader_bin, uint32_t bootloader_len)
|
||||
{
|
||||
(void) bootloader_bin;
|
||||
(void) bootloader_len;
|
||||
// check if the bootloader payload is valid
|
||||
if ( is_new_bootloader_valid(bootloader_bin, bootloader_len) )
|
||||
{
|
||||
#if TINYUF2_PROTECT_BOOTLOADER
|
||||
// Note: Don't protect bootloader when done, leave that to the new bootloader
|
||||
// since it may or may not enable protection.
|
||||
board_flash_protect_bootloader(false);
|
||||
#endif
|
||||
|
||||
// keep writing until flash contents matches new bootloader data
|
||||
while( memcmp((const void*) FLASH_BASE_ADDR, bootloader_bin, bootloader_len) )
|
||||
{
|
||||
uint32_t sector_addr = FLASH_BASE_ADDR;
|
||||
const uint8_t * data = bootloader_bin;
|
||||
uint32_t len = bootloader_len;
|
||||
|
||||
for ( uint32_t i = 0; i < BOOTLOADER_SECTOR_COUNT && len > 0; i++ )
|
||||
{
|
||||
uint32_t const size = (flash_func_sector_size(i) < len ? flash_func_sector_size(i) : len);
|
||||
board_flash_write(sector_addr, data, size);
|
||||
|
||||
sector_addr += size;
|
||||
data += size;
|
||||
len -= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// self-destruct: write 0 to first 2 entry of vector table
|
||||
// Note: write bit from 1 to 0 does not need to erase in advance
|
||||
__disable_irq();
|
||||
HAL_FLASH_Unlock();
|
||||
|
||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOARD_FLASH_APP_START , 0);
|
||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOARD_FLASH_APP_START+4, 0);
|
||||
|
||||
HAL_FLASH_Lock();
|
||||
|
||||
// reset to run new bootloader
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
#define BOARD_FLASH_APP_START 0x08004000
|
||||
#endif
|
||||
|
||||
#define BOARD_PAGE_SIZE 0x800
|
||||
|
||||
#define BOARD_RAM_START 0x20000000
|
||||
#define BOARD_RAM_SIZE 0x9FFF
|
||||
|
||||
|
|
|
|||
188
ports/stm32f3/linker/stm32f3_app.ld
Normal file
188
ports/stm32f3/linker/stm32f3_app.ld
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32F303VCTx Device with
|
||||
** 256KByte FLASH, 40KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
|
||||
_board_dfu_dbl_tap = _estack;
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x800;; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x800;; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x8004000, LENGTH = 256K - 16K /* must match BOARD_FLASH_APP_START */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 40K - 4 /* reserve 4 bytes for double tap */
|
||||
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 8K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
|
||||
.ARM : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(4);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
_siccmram = LOADADDR(.ccmram);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.ccmram :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sccmram = .; /* create a global symbol at ccmram start */
|
||||
*(.ccmram)
|
||||
*(.ccmram*)
|
||||
|
||||
. = ALIGN(4);
|
||||
_eccmram = .; /* create a global symbol at ccmram end */
|
||||
} >CCMRAM AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ _Min_Stack_Size = 0x800;; /* required amount of stack */
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 40K - 4
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 40K - 4 /* reserve 4 bytes for double tap */
|
||||
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 8K
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ CFLAGS += \
|
|||
CFLAGS += -Wno-error=cast-align -Wno-error=unused-parameter
|
||||
|
||||
# default linker file
|
||||
LD_FILES ?= $(PORT_DIR)/linker/stm32f3_boot.ld
|
||||
ifdef BUILD_APPLICATION
|
||||
LD_FILES ?= $(PORT_DIR)/linker/stm32f3_app.ld
|
||||
else
|
||||
LD_FILES ?= $(PORT_DIR)/linker/stm32f3_boot.ld
|
||||
endif
|
||||
|
||||
# Port source
|
||||
SRC_C += \
|
||||
|
|
|
|||
Loading…
Reference in a new issue