From 9fa8b64e81755add9ab988b8da490a404fdd033d Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Sat, 12 Mar 2022 04:07:46 +0100 Subject: [PATCH 1/9] Add bootloader for A/B update support --- CMakeLists.txt | 72 ++++++-- STM32F103RCTx_FLASH_a.ld | 168 ++++++++++++++++++ STM32F103RCTx_FLASH_b.ld | 168 ++++++++++++++++++ .../STM32F103RCTx_FLASH_boot.ld | 11 +- ab_boot/ab_boot.c | 31 ++++ ab_boot/ab_boot.h | 15 ++ system_stm32f1xx.c | 4 +- 7 files changed, 446 insertions(+), 23 deletions(-) create mode 100644 STM32F103RCTx_FLASH_a.ld create mode 100644 STM32F103RCTx_FLASH_b.ld rename STM32F103RCTx_FLASH.ld => ab_boot/STM32F103RCTx_FLASH_boot.ld (97%) create mode 100644 ab_boot/ab_boot.c create mode 100644 ab_boot/ab_boot.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c76dd7..e339fa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,21 @@ set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}") set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) -set(COMMON_LINKER_FLAGS "-specs=nosys.specs -T${CMAKE_SOURCE_DIR}/STM32F103RCTx_FLASH.ld -lc -lm -lnosys -lstdc++ -Wl,--gc-sections -Wl,-Map=${CMAKE_BINARY_DIR}/hover.map,--cref") -set(CMAKE_EXE_LINKER_FLAGS "${COMMON_LINKER_FLAGS}") -set(CMAKE_SHARED_LINKER_FLAGS "${COMMON_LINKER_FLAGS}") +set(A_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/STM32F103RCTx_FLASH_a.ld") +set(B_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/STM32F103RCTx_FLASH_b.ld") +set(AB_BOOT_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/ab_boot/STM32F103RCTx_FLASH_boot.ld") +set(COMMON_LINKER_FLAGS -specs=nosys.specs -lc -lm -lnosys -lstdc++ -Wl,--gc-sections "-Wl,-Map=${CMAKE_BINARY_DIR}/hover.map,--cref") + +set(A_FIRMWARE_APP_BASE 0x08002000) +set(B_FIRMWARE_APP_BASE 0x08020800) + +get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) +if(IS_IN_TRY_COMPILE) + set(CMAKE_EXE_LINKER_FLAGS ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) + list(JOIN CMAKE_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS) + set(CMAKE_SHARED_LINKER_FLAGS ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) + list(JOIN CMAKE_SHARED_LINKER_FLAGS " " CMAKE_SHARED_LINKER_FLAGS) +endif(IS_IN_TRY_COMPILE) project(bobbycar-controller-firmware ASM C CXX) @@ -52,7 +64,6 @@ add_library(stm32_hal STATIC STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c STM32CubeF1/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_can.c startup_stm32f103xe.s - system_stm32f1xx.c ) add_library(emanuel_foc_model STATIC @@ -66,12 +77,18 @@ add_library(bobbycar-protocol INTERFACE ) - +set(COMMON_SOURCES + config.h + defines.h + main.cpp + system_stm32f1xx.c +) # # motor test # -add_executable(motortest.elf config.h defines.h main.cpp) +add_executable(motortest.elf ${COMMON_SOURCES}) +target_link_options(motortest.elf PRIVATE ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) target_link_libraries(motortest.elf stm32_hal emanuel_foc_model bobbycar-protocol) target_compile_options(motortest.elf PRIVATE -DMOTOR_TEST @@ -88,16 +105,19 @@ target_compile_options(motortest.elf PRIVATE # -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK + + -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT motortest.hex COMMAND arm-none-eabi-objcopy -O ihex motortest.elf motortest.hex DEPENDS motortest.elf) add_custom_command(OUTPUT motortest.bin COMMAND arm-none-eabi-objcopy -O binary -S motortest.elf motortest.bin DEPENDS motortest.elf) add_custom_target(motortest ALL SOURCES motortest.hex motortest.bin) -add_custom_target(flash-motortest COMMAND st-flash --reset write motortest.bin 0x8000000 SOURCES motortest.bin DEPENDS motortest.bin) +add_custom_target(flash-motortest COMMAND st-flash --reset write motortest.bin ${A_FIRMWARE_APP_BASE} SOURCES motortest.bin DEPENDS motortest.bin) # # motor test peter # -add_executable(motortest_peter.elf config.h defines.h main.cpp) +add_executable(motortest_peter.elf ${COMMON_SOURCES}) +target_link_options(motortest_peter.elf PRIVATE ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) target_link_libraries(motortest_peter.elf stm32_hal emanuel_foc_model bobbycar-protocol) target_compile_options(motortest_peter.elf PRIVATE #-DMOTOR_TEST @@ -115,17 +135,20 @@ target_compile_options(motortest_peter.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR -DIS_BACK + + -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT motortest_peter.hex COMMAND arm-none-eabi-objcopy -O ihex motortest_peter.elf motortest_peter.hex DEPENDS motortest_peter.elf) add_custom_command(OUTPUT motortest_peter.bin COMMAND arm-none-eabi-objcopy -O binary -S motortest_peter.elf motortest_peter.bin DEPENDS motortest_peter.elf) add_custom_target(motortest_peter ALL SOURCES motortest_peter.hex motortest_peter.bin) -add_custom_target(flash-motortest_peter COMMAND st-flash --reset write motortest_peter.bin 0x8000000 SOURCES motortest_peter.bin DEPENDS motortest_peter.bin) +add_custom_target(flash-motortest_peter COMMAND st-flash --reset write motortest_peter.bin ${A_FIRMWARE_APP_BASE} SOURCES motortest_peter.bin DEPENDS motortest_peter.bin) # # feedc0de front # -add_executable(feedcode-front.elf config.h defines.h main.cpp) +add_executable(feedcode-front.elf ${COMMON_SOURCES}) +target_link_options(feedcode-front.elf PRIVATE ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) target_link_libraries(feedcode-front.elf stm32_hal emanuel_foc_model bobbycar-protocol) target_compile_options(feedcode-front.elf PRIVATE # -DMOTOR_TEST @@ -142,16 +165,19 @@ target_compile_options(feedcode-front.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK + + -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT feedcode-front.hex COMMAND arm-none-eabi-objcopy -O ihex feedcode-front.elf feedcode-front.hex DEPENDS feedcode-front.elf) add_custom_command(OUTPUT feedcode-front.bin COMMAND arm-none-eabi-objcopy -O binary -S feedcode-front.elf feedcode-front.bin DEPENDS feedcode-front.elf) add_custom_target(feedcode-front ALL SOURCES feedcode-front.hex feedcode-front.bin) -add_custom_target(flash-feedcode-front COMMAND st-flash --reset write feedcode-front.bin 0x8000000 SOURCES feedcode-front.bin DEPENDS feedcode-front.bin) +add_custom_target(flash-feedcode-front COMMAND st-flash --reset write feedcode-front.bin ${A_FIRMWARE_APP_BASE} SOURCES feedcode-front.bin DEPENDS feedcode-front.bin) # # feedc0de back # -add_executable(feedcode-back.elf config.h defines.h main.cpp) +add_executable(feedcode-back.elf ${COMMON_SOURCES}) +target_link_options(feedcode-back.elf PRIVATE ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) target_link_libraries(feedcode-back.elf stm32_hal emanuel_foc_model bobbycar-protocol) target_compile_options(feedcode-back.elf PRIVATE # -DMOTOR_TEST @@ -168,16 +194,19 @@ target_compile_options(feedcode-back.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR -DIS_BACK + + -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT feedcode-back.hex COMMAND arm-none-eabi-objcopy -O ihex feedcode-back.elf feedcode-back.hex DEPENDS feedcode-back.elf) add_custom_command(OUTPUT feedcode-back.bin COMMAND arm-none-eabi-objcopy -O binary -S feedcode-back.elf feedcode-back.bin DEPENDS feedcode-back.elf) add_custom_target(feedcode-back ALL SOURCES feedcode-back.hex feedcode-back.bin) -add_custom_target(flash-feedcode-back COMMAND st-flash --reset write feedcode-back.bin 0x8000000 SOURCES feedcode-back.bin DEPENDS feedcode-back.bin) +add_custom_target(flash-feedcode-back COMMAND st-flash --reset write feedcode-back.bin ${A_FIRMWARE_APP_BASE} SOURCES feedcode-back.bin DEPENDS feedcode-back.bin) # # greyhash # -add_executable(greyhash.elf config.h defines.h main.cpp) +add_executable(greyhash.elf ${COMMON_SOURCES}) +target_link_options(greyhash.elf PRIVATE ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) target_link_libraries(greyhash.elf stm32_hal emanuel_foc_model bobbycar-protocol) target_compile_options(greyhash.elf PRIVATE # -DMOTOR_TEST @@ -194,14 +223,25 @@ target_compile_options(greyhash.elf PRIVATE # -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK + + -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT greyhash.hex COMMAND arm-none-eabi-objcopy -O ihex greyhash.elf greyhash.hex DEPENDS greyhash.elf) add_custom_command(OUTPUT greyhash.bin COMMAND arm-none-eabi-objcopy -O binary -S greyhash.elf greyhash.bin DEPENDS greyhash.elf) add_custom_target(greyhash ALL SOURCES greyhash.hex greyhash.bin) -add_custom_target(flash-greyhash COMMAND st-flash --reset write greyhash.bin 0x8000000 SOURCES greyhash.bin DEPENDS greyhash.bin) - +add_custom_target(flash-greyhash COMMAND st-flash --reset write greyhash.bin ${A_FIRMWARE_APP_BASE} SOURCES greyhash.bin DEPENDS greyhash.bin) +add_executable(ab_boot.elf ab_boot/ab_boot.c system_stm32f1xx.c) +target_link_options(ab_boot.elf PRIVATE ${COMMON_LINKER_FLAGS} ${AB_BOOT_FIRMWARE_LINKER_FLAGS}) +target_link_libraries(ab_boot.elf stm32_hal) +target_compile_options(ab_boot.elf PRIVATE + -DVECT_TAB_OFFSET=0x00000000 +) +add_custom_command(OUTPUT ab_boot.hex COMMAND arm-none-eabi-objcopy -O ihex ab_boot.elf ab_boot.hex DEPENDS ab_boot.elf) +add_custom_command(OUTPUT ab_boot.bin COMMAND arm-none-eabi-objcopy -O binary -S ab_boot.elf ab_boot.bin DEPENDS ab_boot.elf) +add_custom_target(ab_boot ALL SOURCES ab_boot.hex ab_boot.bin) +add_custom_target(flash-boot COMMAND st-flash --reset write ab_boot.bin 0x08000000 SOURCES ab_boot.bin DEPENDS ab_boot.bin) # util targets diff --git a/STM32F103RCTx_FLASH_a.ld b/STM32F103RCTx_FLASH_a.ld new file mode 100644 index 0000000..8e5abe1 --- /dev/null +++ b/STM32F103RCTx_FLASH_a.ld @@ -0,0 +1,168 @@ +/* +***************************************************************************** +** + +** File : LinkerScript.ld +** +** Abstract : Linker script for STM32F103RCTx Device with +** 256KByte FLASH, 48KByte 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 = 0x2000C000; /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K +FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 122K +CONFIG (r) : ORIGIN = 0x0803f800, LENGTH = 2K +} + +/* 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 + + + /* 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) } +} diff --git a/STM32F103RCTx_FLASH_b.ld b/STM32F103RCTx_FLASH_b.ld new file mode 100644 index 0000000..f98c166 --- /dev/null +++ b/STM32F103RCTx_FLASH_b.ld @@ -0,0 +1,168 @@ +/* +***************************************************************************** +** + +** File : LinkerScript.ld +** +** Abstract : Linker script for STM32F103RCTx Device with +** 256KByte FLASH, 48KByte 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 = 0x2000C000; /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K +FLASH (rx) : ORIGIN = 0x08020800, LENGTH = 122K +CONFIG (r) : ORIGIN = 0x0803f800, LENGTH = 2K +} + +/* 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 + + + /* 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) } +} diff --git a/STM32F103RCTx_FLASH.ld b/ab_boot/STM32F103RCTx_FLASH_boot.ld similarity index 97% rename from STM32F103RCTx_FLASH.ld rename to ab_boot/STM32F103RCTx_FLASH_boot.ld index b77c831..3c33e67 100644 --- a/STM32F103RCTx_FLASH.ld +++ b/ab_boot/STM32F103RCTx_FLASH_boot.ld @@ -42,7 +42,8 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K -FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 8K +CONFIG(r) : ORIGIN = 0x0803f800, LENGTH = 2K } /* Define output sections */ @@ -114,7 +115,7 @@ SECTIONS _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : + .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ @@ -125,7 +126,7 @@ SECTIONS _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH - + /* Uninitialized data section */ . = ALIGN(4); .bss : @@ -153,7 +154,7 @@ SECTIONS . = ALIGN(8); } >RAM - + /* Remove information from the standard libraries */ /DISCARD/ : @@ -165,5 +166,3 @@ SECTIONS .ARM.attributes 0 : { *(.ARM.attributes) } } - - diff --git a/ab_boot/ab_boot.c b/ab_boot/ab_boot.c new file mode 100644 index 0000000..ec60d6e --- /dev/null +++ b/ab_boot/ab_boot.c @@ -0,0 +1,31 @@ +#include + +#include "stm32f1xx_hal.h" +#include "ab_boot.h" + +int main() +{ + struct ab_boot_config *config = (struct ab_boot_config *)CONFIG_START; + + uint32_t *bootp; + if (config->boot_partition >= FLASH_START && + config->boot_partition < FLASH_END && + (config->boot_partition & 3) == 0) + { + bootp = (uint32_t *)config->boot_partition; + } + else + { + bootp = (uint32_t *)APP_A_START; + } + + uint32_t sp = bootp[0]; + uint32_t entry = bootp[1]; + asm volatile( + " mov sp, %0 \n" + " bx %1 \n" + "1: b 1b \n" + : + : "r" (sp), "r" (entry) + ); +} diff --git a/ab_boot/ab_boot.h b/ab_boot/ab_boot.h new file mode 100644 index 0000000..d1a26c7 --- /dev/null +++ b/ab_boot/ab_boot.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define CONFIG_START 0x0803f800 +#define FLASH_START 0x08000000 +#define FLASH_END 0x08040000 + +#define APP_A_START 0x08002000 +#define APP_B_START 0x08020800 + +struct ab_boot_config +{ + uint32_t boot_partition; +}; diff --git a/system_stm32f1xx.c b/system_stm32f1xx.c index bb1a3ab..7671c97 100644 --- a/system_stm32f1xx.c +++ b/system_stm32f1xx.c @@ -110,9 +110,11 @@ /*!< Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */ /* #define VECT_TAB_SRAM */ +#if !defined(VECT_TAB_OFFSET) +#warning VECT_TAB_OFFSET is not defined, using default of 0x00000000U #define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. \ This value must be a multiple of 0x200. */ - +#endif /* VECT_TAB_OFFSET */ /** * @} -- 2.50.1 From 4376dceb2344a4b8ce1b89ea5c8ebb903944f03d Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 03:43:51 +0200 Subject: [PATCH 2/9] Organize defines in ab_boot.h better --- ab_boot/ab_boot.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ab_boot/ab_boot.h b/ab_boot/ab_boot.h index d1a26c7..38d8fb0 100644 --- a/ab_boot/ab_boot.h +++ b/ab_boot/ab_boot.h @@ -2,12 +2,18 @@ #include -#define CONFIG_START 0x0803f800 #define FLASH_START 0x08000000 -#define FLASH_END 0x08040000 +#define AB_BOOT_SIZE 0x00002000 #define APP_A_START 0x08002000 #define APP_B_START 0x08020800 + // 122 KiB +#define APP_SIZE 0x1e800 + +#define CONFIG_START 0x0803f800 +#define CONFIG_SIZE 0x800 + +#define FLASH_END 0x08040000 struct ab_boot_config { -- 2.50.1 From c0401ffd3297fce345c92ddf060f8a6a9b82b77d Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 03:44:34 +0200 Subject: [PATCH 3/9] Stop trying to please CMake --- CMakeLists.txt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e339fa5..7cdbf05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,13 +24,7 @@ set(COMMON_LINKER_FLAGS -specs=nosys.specs -lc -lm -lnosys -lstdc++ -Wl,--gc-sec set(A_FIRMWARE_APP_BASE 0x08002000) set(B_FIRMWARE_APP_BASE 0x08020800) -get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) -if(IS_IN_TRY_COMPILE) - set(CMAKE_EXE_LINKER_FLAGS ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) - list(JOIN CMAKE_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS) - set(CMAKE_SHARED_LINKER_FLAGS ${COMMON_LINKER_FLAGS} ${A_FIRMWARE_LINKER_FLAGS}) - list(JOIN CMAKE_SHARED_LINKER_FLAGS " " CMAKE_SHARED_LINKER_FLAGS) -endif(IS_IN_TRY_COMPILE) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(bobbycar-controller-firmware ASM C CXX) -- 2.50.1 From 62967fe497524f12b7e9abb1bb8f9b35286da2cb Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 03:46:42 +0200 Subject: [PATCH 4/9] Make bobbycar-protocol library less useless --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cdbf05..e35daf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,6 @@ include_directories( STM32CubeF1/Drivers/CMSIS/Device/ST/STM32F1xx/Include STM32CubeF1/Drivers/CMSIS/Include bobbycar-foc-model - bobbycar-protocol ) add_library(stm32_hal STATIC @@ -67,8 +66,8 @@ add_library(emanuel_foc_model STATIC bobbycar-foc-model/rtwtypes.h ) -add_library(bobbycar-protocol INTERFACE -) +add_library(bobbycar-protocol INTERFACE) +target_include_directories(bobbycar-protocol INTERFACE bobbycar-protocol) set(COMMON_SOURCES -- 2.50.1 From 6fae8ef918e08747b02cbb7e055f5a14ba101050 Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 03:48:29 +0200 Subject: [PATCH 5/9] Resolve location of interrupt vector table at link time --- CMakeLists.txt | 14 +------------- system_stm32f1xx.c | 6 ++---- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e35daf8..f672f8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,8 +98,6 @@ target_compile_options(motortest.elf PRIVATE # -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK - - -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT motortest.hex COMMAND arm-none-eabi-objcopy -O ihex motortest.elf motortest.hex DEPENDS motortest.elf) add_custom_command(OUTPUT motortest.bin COMMAND arm-none-eabi-objcopy -O binary -S motortest.elf motortest.bin DEPENDS motortest.elf) @@ -128,8 +126,6 @@ target_compile_options(motortest_peter.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR -DIS_BACK - - -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT motortest_peter.hex COMMAND arm-none-eabi-objcopy -O ihex motortest_peter.elf motortest_peter.hex DEPENDS motortest_peter.elf) add_custom_command(OUTPUT motortest_peter.bin COMMAND arm-none-eabi-objcopy -O binary -S motortest_peter.elf motortest_peter.bin DEPENDS motortest_peter.elf) @@ -158,8 +154,6 @@ target_compile_options(feedcode-front.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK - - -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT feedcode-front.hex COMMAND arm-none-eabi-objcopy -O ihex feedcode-front.elf feedcode-front.hex DEPENDS feedcode-front.elf) add_custom_command(OUTPUT feedcode-front.bin COMMAND arm-none-eabi-objcopy -O binary -S feedcode-front.elf feedcode-front.bin DEPENDS feedcode-front.elf) @@ -187,8 +181,6 @@ target_compile_options(feedcode-back.elf PRIVATE -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR -DIS_BACK - - -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT feedcode-back.hex COMMAND arm-none-eabi-objcopy -O ihex feedcode-back.elf feedcode-back.hex DEPENDS feedcode-back.elf) add_custom_command(OUTPUT feedcode-back.bin COMMAND arm-none-eabi-objcopy -O binary -S feedcode-back.elf feedcode-back.bin DEPENDS feedcode-back.elf) @@ -216,8 +208,6 @@ target_compile_options(greyhash.elf PRIVATE # -DFEATURE_CAN # -DCAN_LOG_UNKNOWN_ADDR # -DIS_BACK - - -DVECT_TAB_OFFSET=${A_FIRMWARE_APP_BASE} ) add_custom_command(OUTPUT greyhash.hex COMMAND arm-none-eabi-objcopy -O ihex greyhash.elf greyhash.hex DEPENDS greyhash.elf) add_custom_command(OUTPUT greyhash.bin COMMAND arm-none-eabi-objcopy -O binary -S greyhash.elf greyhash.bin DEPENDS greyhash.elf) @@ -228,9 +218,7 @@ add_custom_target(flash-greyhash COMMAND st-flash --reset write greyhash.bin ${A add_executable(ab_boot.elf ab_boot/ab_boot.c system_stm32f1xx.c) target_link_options(ab_boot.elf PRIVATE ${COMMON_LINKER_FLAGS} ${AB_BOOT_FIRMWARE_LINKER_FLAGS}) target_link_libraries(ab_boot.elf stm32_hal) -target_compile_options(ab_boot.elf PRIVATE - -DVECT_TAB_OFFSET=0x00000000 -) +target_compile_options(ab_boot.elf PRIVATE ) add_custom_command(OUTPUT ab_boot.hex COMMAND arm-none-eabi-objcopy -O ihex ab_boot.elf ab_boot.hex DEPENDS ab_boot.elf) add_custom_command(OUTPUT ab_boot.bin COMMAND arm-none-eabi-objcopy -O binary -S ab_boot.elf ab_boot.bin DEPENDS ab_boot.elf) add_custom_target(ab_boot ALL SOURCES ab_boot.hex ab_boot.bin) diff --git a/system_stm32f1xx.c b/system_stm32f1xx.c index 7671c97..2034671 100644 --- a/system_stm32f1xx.c +++ b/system_stm32f1xx.c @@ -110,11 +110,9 @@ /*!< Uncomment the following line if you need to relocate your vector Table in Internal SRAM. */ /* #define VECT_TAB_SRAM */ -#if !defined(VECT_TAB_OFFSET) -#warning VECT_TAB_OFFSET is not defined, using default of 0x00000000U -#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. \ +extern unsigned int g_pfnVectors[]; +#define VECT_TAB_OFFSET ((unsigned int)g_pfnVectors) /*!< Vector Table base offset field. \ This value must be a multiple of 0x200. */ -#endif /* VECT_TAB_OFFSET */ /** * @} -- 2.50.1 From f29c4d182eec71ffbf3542d20468ef798595f52c Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 21:01:33 +0200 Subject: [PATCH 6/9] Deduplicate and improve linker scripts --- CMakeLists.txt | 9 +- STM32F103RCTx_FLASH_a.ld | 168 ------------------ STM32F103RCTx_FLASH_b.ld | 168 ------------------ .../STM32F103RCTx_FLASH.ld | 7 +- ld/ab_boot.ld | 2 + ld/app_a.ld | 2 + ld/app_b.ld | 2 + 7 files changed, 14 insertions(+), 344 deletions(-) delete mode 100644 STM32F103RCTx_FLASH_a.ld delete mode 100644 STM32F103RCTx_FLASH_b.ld rename ab_boot/STM32F103RCTx_FLASH_boot.ld => ld/STM32F103RCTx_FLASH.ld (96%) create mode 100644 ld/ab_boot.ld create mode 100644 ld/app_a.ld create mode 100644 ld/app_b.ld diff --git a/CMakeLists.txt b/CMakeLists.txt index f672f8d..9964d79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,10 +16,11 @@ set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}") set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) -set(A_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/STM32F103RCTx_FLASH_a.ld") -set(B_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/STM32F103RCTx_FLASH_b.ld") -set(AB_BOOT_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/ab_boot/STM32F103RCTx_FLASH_boot.ld") -set(COMMON_LINKER_FLAGS -specs=nosys.specs -lc -lm -lnosys -lstdc++ -Wl,--gc-sections "-Wl,-Map=${CMAKE_BINARY_DIR}/hover.map,--cref") +set(A_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/ld/app_a.ld") +set(B_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/ld/app_b.ld") +set(AB_BOOT_FIRMWARE_LINKER_FLAGS "-T${CMAKE_SOURCE_DIR}/ld/ab_boot.ld") +set(COMMON_LINKER_FLAGS -specs=nosys.specs -lc -lm -lnosys -lstdc++ -Wl,--gc-sections "-Wl,-Map=${CMAKE_BINARY_DIR}/hover.map,--cref" + -T${CMAKE_SOURCE_DIR}/ld/STM32F103RCTx_FLASH.ld) set(A_FIRMWARE_APP_BASE 0x08002000) set(B_FIRMWARE_APP_BASE 0x08020800) diff --git a/STM32F103RCTx_FLASH_a.ld b/STM32F103RCTx_FLASH_a.ld deleted file mode 100644 index 8e5abe1..0000000 --- a/STM32F103RCTx_FLASH_a.ld +++ /dev/null @@ -1,168 +0,0 @@ -/* -***************************************************************************** -** - -** File : LinkerScript.ld -** -** Abstract : Linker script for STM32F103RCTx Device with -** 256KByte FLASH, 48KByte 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 = 0x2000C000; /* end of RAM */ -/* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x200; /* required amount of heap */ -_Min_Stack_Size = 0x400; /* required amount of stack */ - -/* Specify the memory areas */ -MEMORY -{ -RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K -FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 122K -CONFIG (r) : ORIGIN = 0x0803f800, LENGTH = 2K -} - -/* 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 - - - /* 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) } -} diff --git a/STM32F103RCTx_FLASH_b.ld b/STM32F103RCTx_FLASH_b.ld deleted file mode 100644 index f98c166..0000000 --- a/STM32F103RCTx_FLASH_b.ld +++ /dev/null @@ -1,168 +0,0 @@ -/* -***************************************************************************** -** - -** File : LinkerScript.ld -** -** Abstract : Linker script for STM32F103RCTx Device with -** 256KByte FLASH, 48KByte 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 = 0x2000C000; /* end of RAM */ -/* Generate a link error if heap and stack don't fit into RAM */ -_Min_Heap_Size = 0x200; /* required amount of heap */ -_Min_Stack_Size = 0x400; /* required amount of stack */ - -/* Specify the memory areas */ -MEMORY -{ -RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K -FLASH (rx) : ORIGIN = 0x08020800, LENGTH = 122K -CONFIG (r) : ORIGIN = 0x0803f800, LENGTH = 2K -} - -/* 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 - - - /* 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) } -} diff --git a/ab_boot/STM32F103RCTx_FLASH_boot.ld b/ld/STM32F103RCTx_FLASH.ld similarity index 96% rename from ab_boot/STM32F103RCTx_FLASH_boot.ld rename to ld/STM32F103RCTx_FLASH.ld index 3c33e67..46c70d4 100644 --- a/ab_boot/STM32F103RCTx_FLASH_boot.ld +++ b/ld/STM32F103RCTx_FLASH.ld @@ -32,8 +32,6 @@ /* Entry Point */ ENTRY(Reset_Handler) -/* Highest address of the user mode stack */ -_estack = 0x2000C000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x200; /* required amount of heap */ _Min_Stack_Size = 0x400; /* required amount of stack */ @@ -42,7 +40,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K -FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 8K +FLASH (rx) : ORIGIN = _App_Base, LENGTH = _App_Length CONFIG(r) : ORIGIN = 0x0803f800, LENGTH = 2K } @@ -154,7 +152,8 @@ SECTIONS . = ALIGN(8); } >RAM - + /* Highest address of the user mode stack */ + _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM area */ /* Remove information from the standard libraries */ /DISCARD/ : diff --git a/ld/ab_boot.ld b/ld/ab_boot.ld new file mode 100644 index 0000000..0fc03c2 --- /dev/null +++ b/ld/ab_boot.ld @@ -0,0 +1,2 @@ +_App_Base = 0x08000000; +_App_Length = 8K; diff --git a/ld/app_a.ld b/ld/app_a.ld new file mode 100644 index 0000000..b918ad2 --- /dev/null +++ b/ld/app_a.ld @@ -0,0 +1,2 @@ +_App_Base = 0x08002000; +_App_Length = 122K; diff --git a/ld/app_b.ld b/ld/app_b.ld new file mode 100644 index 0000000..dc6e8cc --- /dev/null +++ b/ld/app_b.ld @@ -0,0 +1,2 @@ +_App_Base = 0x08020800; +_App_Length = 122K; -- 2.50.1 From bda583b94c67bda3b5342073fd180619d3e745ed Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 21:05:09 +0200 Subject: [PATCH 7/9] Add persist RAM area for passing data across resets; implement booting from a partition specified in the persist RAM area --- CMakeLists.txt | 7 ++++- ab_boot/ab_boot.c | 58 ++++++++++++++++++++++++++------------- ab_boot/ab_boot.h | 2 +- ld/STM32F103RCTx_FLASH.ld | 11 +++++++- main.cpp | 26 ++++++++++++++---- persist/persist.c | 10 +++++++ persist/persist.h | 39 ++++++++++++++++++++++++++ 7 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 persist/persist.c create mode 100644 persist/persist.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9964d79..288508b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ set(COMMON_SOURCES defines.h main.cpp system_stm32f1xx.c + persist/persist.c ) # @@ -216,7 +217,11 @@ add_custom_target(greyhash ALL SOURCES greyhash.hex greyhash.bin) add_custom_target(flash-greyhash COMMAND st-flash --reset write greyhash.bin ${A_FIRMWARE_APP_BASE} SOURCES greyhash.bin DEPENDS greyhash.bin) -add_executable(ab_boot.elf ab_boot/ab_boot.c system_stm32f1xx.c) +add_executable(ab_boot.elf + ab_boot/ab_boot.c + system_stm32f1xx.c + persist/persist.c +) target_link_options(ab_boot.elf PRIVATE ${COMMON_LINKER_FLAGS} ${AB_BOOT_FIRMWARE_LINKER_FLAGS}) target_link_libraries(ab_boot.elf stm32_hal) target_compile_options(ab_boot.elf PRIVATE ) diff --git a/ab_boot/ab_boot.c b/ab_boot/ab_boot.c index ec60d6e..bbe1d66 100644 --- a/ab_boot/ab_boot.c +++ b/ab_boot/ab_boot.c @@ -1,31 +1,51 @@ #include +#include #include "stm32f1xx_hal.h" #include "ab_boot.h" +#include "persist/persist.h" -int main() -{ - struct ab_boot_config *config = (struct ab_boot_config *)CONFIG_START; - - uint32_t *bootp; - if (config->boot_partition >= FLASH_START && - config->boot_partition < FLASH_END && - (config->boot_partition & 3) == 0) - { - bootp = (uint32_t *)config->boot_partition; - } - else - { - bootp = (uint32_t *)APP_A_START; - } - - uint32_t sp = bootp[0]; - uint32_t entry = bootp[1]; +static inline void __attribute__((noreturn)) boot_image(uint32_t sp, uint32_t entry) { asm volatile( " mov sp, %0 \n" - " bx %1 \n" + " blx %1 \n" "1: b 1b \n" : : "r" (sp), "r" (entry) ); + __builtin_unreachable(); +} + +static bool is_valid_boot_address(uint32_t *bootp) +{ + return (uint32_t)bootp >= FLASH_START && + (uint32_t)bootp < FLASH_END && + ((uint32_t)bootp & 3) == 0; +} + +int main() +{ + struct ab_boot_config *flash_config = (struct ab_boot_config *)CONFIG_START; + struct ab_boot_config *config = NULL; + if (is_persist_valid() && + is_valid_boot_address(persist.ab_boot_config.boot_partition)) + { + // Invalidate persist + persist.checksum = 0; + config = &persist.ab_boot_config; + } + else if (is_valid_boot_address(flash_config->boot_partition)) + { + config = flash_config; + } + + uint32_t *bootp; + if (config) + bootp = config->boot_partition; + else + bootp = (uint32_t *)APP_A_START; + + uint32_t sp = bootp[0]; + uint32_t entry = bootp[1]; + boot_image(sp, entry); } diff --git a/ab_boot/ab_boot.h b/ab_boot/ab_boot.h index 38d8fb0..7d2bd9b 100644 --- a/ab_boot/ab_boot.h +++ b/ab_boot/ab_boot.h @@ -17,5 +17,5 @@ struct ab_boot_config { - uint32_t boot_partition; + uint32_t *boot_partition; }; diff --git a/ld/STM32F103RCTx_FLASH.ld b/ld/STM32F103RCTx_FLASH.ld index 46c70d4..0801d36 100644 --- a/ld/STM32F103RCTx_FLASH.ld +++ b/ld/STM32F103RCTx_FLASH.ld @@ -39,7 +39,8 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ /* Specify the memory areas */ MEMORY { -RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K +RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 47K +PERSIST (rw) : ORIGIN = 0x2000BC00, LENGTH = 1K FLASH (rx) : ORIGIN = _App_Base, LENGTH = _App_Length CONFIG(r) : ORIGIN = 0x0803f800, LENGTH = 2K } @@ -155,6 +156,14 @@ SECTIONS /* Highest address of the user mode stack */ _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM area */ + .persist : + { + . = ALIGN(4); + persist = .; + . += LENGTH(PERSIST); + _epersist = .; + } > PERSIST + /* Remove information from the standard libraries */ /DISCARD/ : { diff --git a/main.cpp b/main.cpp index daaef2d..4cdd03d 100644 --- a/main.cpp +++ b/main.cpp @@ -27,6 +27,8 @@ #include #include "stm32f1xx_hal.h" +#include "ab_boot/ab_boot.h" +#include "persist/persist.h" #include "defines.h" #include "config.h" @@ -1364,22 +1366,36 @@ void MX_ADC2_Init() __HAL_ADC_ENABLE(&hadc2); } -#ifdef FEATURE_BUTTON -void poweroff() +void shutdown() { -// if (abs(speed) < 20) { // wait for the speed to drop, then shut down -> this is commented out for SAFETY reasons buzzer.pattern = 0; left.enable = false; - right.enable = 0; + right.enable = false; for (int i = 0; i < 8; i++) { buzzer.freq = (uint8_t)i; HAL_Delay(50); } +} + +#ifdef FEATURE_BUTTON +void poweroff() +{ + shutdown(); + HAL_GPIO_WritePin(OFF_PORT, OFF_PIN, GPIO_PIN_RESET); for (int i = 0; i < 5; i++) HAL_Delay(1000); -// } +} +#endif + +#ifdef FEATURE_CAN +void reboot_new_image(uint32_t *bootp) +{ + shutdown(); + + request_boot_image(bootp); + HAL_NVIC_SystemReset(); } #endif diff --git a/persist/persist.c b/persist/persist.c new file mode 100644 index 0000000..097f1c4 --- /dev/null +++ b/persist/persist.c @@ -0,0 +1,10 @@ +#include + +#include "persist.h" +#include "ab_boot/ab_boot.h" + +// keep this in sync with the linker script +#define PERSIST_SIZE 1024 + +extern struct persist_data persist; +static_assert(sizeof(persist) < PERSIST_SIZE); diff --git a/persist/persist.h b/persist/persist.h new file mode 100644 index 0000000..a1a1d25 --- /dev/null +++ b/persist/persist.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "ab_boot/ab_boot.h" + +struct persist_data { + struct ab_boot_config ab_boot_config; + uint32_t checksum; +}; + +extern struct persist_data persist; + +static inline uint32_t calculate_persist_checksum() { + uint32_t checksum = 0; + uint32_t *pd = (uint32_t *)&persist; + for (int i = 0; i < (sizeof(persist) - 4) / 4; i++) { + checksum += pd[i]; + checksum = (checksum >> 3) | (checksum << 29); + } + + checksum = ~checksum; + + return checksum; +} + +static inline void update_persist_checksum() { + persist.checksum = calculate_persist_checksum(); +} + +static inline bool is_persist_valid() { + return calculate_persist_checksum() == persist.checksum; +} + +static inline void request_boot_image(uint32_t *bootp) { + persist.ab_boot_config.boot_partition = bootp; + update_persist_checksum(); +} -- 2.50.1 From fc6fbd083dd6526717582fa356ac4eed040c4185 Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Thu, 28 Apr 2022 21:39:02 +0200 Subject: [PATCH 8/9] WIP CAN flashing support --- config_partition.h | 5 + flasher/CANFlasher.hpp | 96 +++++++++++++++++++ flasher/Flasher.hpp | 203 +++++++++++++++++++++++++++++++++++++++++ main.cpp | 58 +++++++++++- 4 files changed, 359 insertions(+), 3 deletions(-) create mode 100644 config_partition.h create mode 100644 flasher/CANFlasher.hpp create mode 100644 flasher/Flasher.hpp diff --git a/config_partition.h b/config_partition.h new file mode 100644 index 0000000..fd3496d --- /dev/null +++ b/config_partition.h @@ -0,0 +1,5 @@ +#include "ab_boot/ab_boot.h" + +struct config_partition { + struct ab_boot_config ab_boot_config; +}; diff --git a/flasher/CANFlasher.hpp b/flasher/CANFlasher.hpp new file mode 100644 index 0000000..2d6fe72 --- /dev/null +++ b/flasher/CANFlasher.hpp @@ -0,0 +1,96 @@ +#include +#include + +#include "Flasher.hpp" + +namespace can_flasher { + namespace { + flasher::Result handle_start(uint8_t *data, size_t length) { + uintptr_t address; + if (length != sizeof(address)) + return flasher::Result::InvalidParameter; + + memcpy(&address, data, sizeof(address)); + return flasher::start(address); + } + + flasher::Result handle_write(uint8_t *data, size_t length) { + return flasher::write(data, length); + } + } + + void handle(uint8_t *data, size_t length) { + using enum flasher::State; + + if (length < 1) { + flasher::flasher_state_callback(flasher::get_state(), flasher::Result::InvalidParameter, 0); + return; + } + + flasher::State target_state = (flasher::State)*data; + data += 1; + length -= 1; + + flasher::Result result; + switch (target_state) { + case Idle: + flasher::init(); + result = flasher::Result::Success; + break; + case Erasing: + result = handle_start(data, length); + break; + case Writing: + result = handle_write(data, length); + break; + default: + result = flasher::Result::InvalidParameter; + } + + flasher::flasher_state_callback(flasher::get_state(), result, 0); + } + + constexpr size_t FeedbackSize = + sizeof(flasher::State) + sizeof(flasher::Result) + sizeof(uint32_t); + static_assert(FeedbackSize <= 8); + + struct { + uint32_t last_sent; + std::atomic updated; + bool valid; + uint8_t data[FeedbackSize]; + } feedback; + + void generate_feedback(flasher::State state, flasher::Result result, uint32_t arg) { + uint8_t *ptr = feedback.data; + std::memcpy(ptr, &state, sizeof(state)); + ptr += sizeof(state); + std::memcpy(ptr, &result, sizeof(result)); + ptr += sizeof(result); + std::memcpy(ptr, &arg, sizeof(arg)); + // this works because poll_feedback can't interrupt us + feedback.updated.store(true); + } + + bool poll_feedback(uint32_t now, uint8_t *out) { + if (feedback.updated.load() || + (feedback.valid && now - feedback.last_sent >= 500)) { + feedback.valid = true; + do { + feedback.updated.store(false); + std::memcpy(out, feedback.data, sizeof(feedback.data)); + // this works because we cannot interrupt generate_feedback + } while (feedback.updated.load()); + feedback.last_sent = now; + return true; + } + + return false; + } +} + +namespace flasher { +void flasher_state_callback(flasher::State state, flasher::Result result, uint32_t arg) { + can_flasher::generate_feedback(state, result, arg); +} +} diff --git a/flasher/Flasher.hpp b/flasher/Flasher.hpp new file mode 100644 index 0000000..a8188c1 --- /dev/null +++ b/flasher/Flasher.hpp @@ -0,0 +1,203 @@ +#pragma once + +#include +#include + +#include "stm32f1xx_hal.h" + +#include "ab_boot/ab_boot.h" +#include "stm32f1xx_hal_def.h" +#include "stm32f1xx_hal_flash.h" +#include "stm32f1xx_hal_flash_ex.h" + + +namespace flasher { + enum class State : uint8_t { + Idle, + Erasing, + Waiting, + Writing, + Error + }; + + namespace { + enum class FlashRegion { + Invalid, + Bootloader, + AppA, + AppB, + Config + }; + + FlashRegion region_for_address(uintptr_t address) { + using enum FlashRegion; + + if (address >= FLASH_START && address < APP_A_START) { + return Bootloader; + } else if (address >= APP_A_START && address < (APP_A_START + APP_SIZE)) { + return AppA; + } else if (address >= APP_B_START && address < (APP_B_START + APP_SIZE)) { + return AppB; + } else if (address >= CONFIG_START && address < (CONFIG_START + CONFIG_SIZE)) { + return Config; + } + + return Invalid; + } + + size_t region_size(FlashRegion region) { + using enum FlashRegion; + + switch (region) { + case Bootloader: + return AB_BOOT_SIZE; + case AppA: + case AppB: + return APP_SIZE; + case Config: + return CONFIG_SIZE; + default: + return 0; + } + } + + bool is_valid_start_address(uint32_t address) { + return address == FLASH_START || address == APP_A_START || + address == APP_B_START || address == CONFIG_START; + } + + static State state_; + static FlashRegion region_; + static uintptr_t address_; + static uint8_t write_size_; + } + + enum class Result : uint8_t { + Success, + RegionNotAllowed, + InvalidParameter, + InvalidState, + WriteError, + InProgress + }; + + void flasher_state_callback(State state, Result result, uint32_t arg); + + void init() { + state_ = State::Idle; + region_ = FlashRegion::Invalid; + address_ = 0; + write_size_ = 0; + HAL_FLASH_Lock(); + } + + Result start(uintptr_t address) { + if (state_ != State::Idle) + return Result::InvalidState; + + if (!is_valid_start_address(address)) { + return Result::InvalidParameter; + } + + FlashRegion flashed_region = region_for_address(address); + FlashRegion running_region = region_for_address((uintptr_t)&start); + + if (flashed_region == FlashRegion::Invalid) { + return Result::InvalidParameter; + } + + if (flashed_region == running_region) { + // prohibit flashing the currently running app + return Result::RegionNotAllowed; + } + + size_t size = region_size(flashed_region); + FLASH_EraseInitTypeDef ferase = { + .TypeErase = FLASH_TYPEERASE_PAGES, + .PageAddress = address, + .NbPages = size / FLASH_PAGE_SIZE + }; + + HAL_FLASH_Unlock(); + if (HAL_FLASHEx_Erase_IT(&ferase) != HAL_OK) { + return Result::WriteError; + } + + state_ = State::Erasing; + region_ = flashed_region; + address_ = address; + + return Result::InProgress; + } + + Result write(uint8_t *data, uint8_t length) { + if (state_ != State::Waiting) + return Result::InvalidState; + + uint32_t program_type; + switch (length) { + case 2: + program_type = FLASH_PROC_PROGRAMHALFWORD; + break; + case 4: + program_type = FLASH_PROC_PROGRAMWORD; + break; + case 8: + program_type = FLASH_PROC_PROGRAMDOUBLEWORD; + break; + default: + return Result::InvalidParameter; + } + + uint64_t data_int = 0; + memcpy(&data_int, data, length); + + if (HAL_FLASH_Program_IT(program_type, address_, data_int) != HAL_OK) + return Result::WriteError; + + state_ = State::Writing; + write_size_ = length; + + return Result::InProgress; + } + + State get_state() { + return state_; + } + + void flash_callback(bool success) { + using enum State; + + // Ignore if we are in Idle state, could be the result of + // a cancelled operation. + if (state_ == Idle) + return; + + if (success) { + switch (state_) { + case Writing: + case Erasing: + address_ += write_size_; + flasher_state_callback(state_, Result::Success, address_); + state_ = Waiting; + write_size_ = 0; + break; + default: + // Spurious callback + HAL_FLASH_Lock(); + state_ = Error; + } + } else { + switch (state_) { + case Writing: + case Erasing: + flasher_state_callback(state_, Result::WriteError, address_); + [[fallthrough]]; + default: + // Spurious callback + HAL_FLASH_Lock(); + state_ = Error; + } + } + } +} diff --git a/main.cpp b/main.cpp index 4cdd03d..fd18e75 100644 --- a/main.cpp +++ b/main.cpp @@ -39,6 +39,8 @@ #endif #ifdef FEATURE_CAN #include "bobbycar-can.h" +#include "flasher/Flasher.hpp" +#include "flasher/CANFlasher.hpp" #endif extern "C" { @@ -112,7 +114,7 @@ CAN_HandleTypeDef CanHandle; #define CANx_TX_IRQn USB_HP_CAN1_TX_IRQn #define CANx_TX_IRQHandler USB_HP_CAN1_TX_IRQHandler -constexpr bool doDelayWithCanPoll = false; +constexpr bool doDelayWithCanPoll = true; #endif #ifdef LOG_TO_SERIAL @@ -249,6 +251,7 @@ void sendFeedback(); void parseCanCommand(); void applyIncomingCanMessage(); void sendCanFeedback(); +void sendFlasherFeedback(); #endif #ifdef FEATURE_BUTTON @@ -379,6 +382,7 @@ int main() while ((HAL_GetTick() - tickstart) < wait) { applyIncomingCanMessage(); + sendFlasherFeedback(); } }; @@ -406,6 +410,7 @@ int main() parseCanCommand(); sendCanFeedback(); + sendFlasherFeedback(); #endif #ifdef FEATURE_BUTTON @@ -450,6 +455,7 @@ void updateMotors() if (offsetcount < 2000) // calibrate ADC offsets { offsetcount++; + // TODO this is not an average offsetrl1 = (adc_buffer.rl1 + offsetrl1) / 2; offsetrl2 = (adc_buffer.rl2 + offsetrl2) / 2; offsetrr1 = (adc_buffer.rr1 + offsetrr1) / 2; @@ -1404,8 +1410,8 @@ void communicationTimeout() { applyDefaultSettings(); - buzzer.freq = 24; - buzzer.pattern = 1; + //buzzer.freq = 24; + //buzzer.pattern = 1; HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET); } @@ -1767,6 +1773,31 @@ void sendCanFeedback() arr[whichToSend++](); } + +void sendFlasherFeedback() { + using bobbycar::protocol::can::MotorController; + static CAN_TxHeaderTypeDef header = { + .StdId = MotorController::Command::FlasherCtrl, + .ExtId = 0, + .IDE = CAN_ID_STD, + .RTR = CAN_RTR_DATA, + .DLC = can_flasher::FeedbackSize, + .TransmitGlobalTime = DISABLE + }; + + if (HAL_CAN_GetTxMailboxesFreeLevel(&CanHandle) == 0) + return; + + uint8_t buf[8]; + if (!can_flasher::poll_feedback(HAL_GetTick(), buf)) + return; + + uint32_t TxMailbox; + if (const auto result = HAL_CAN_AddTxMessage(&CanHandle, &header, buf, &TxMailbox); result != HAL_OK) { + myPrintf("HAL_CAN_AddTxMessage() failed with %i", result); + //while (true); + } +} #endif #ifdef FEATURE_BUTTON @@ -2061,4 +2092,25 @@ extern "C" void CANx_TX_IRQHandler(void) using namespace bobbycar::controller; HAL_CAN_IRQHandler(&CanHandle); } + +// CAN flasher stuff +extern "C" void FLASH_IRQHandler(void) +{ + HAL_FLASH_IRQHandler(); +} + +extern "C" void HAL_FLASH_EndOfOperationCallback(uint32_t ReturnValue) +{ + (void)ReturnValue; + + flasher::flash_callback(true); +} + +extern "C" void HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue) +{ + (void)ReturnValue; + + flasher::flash_callback(false); +} + #endif -- 2.50.1 From f3603870fa8b51bad08c1105c30aee242b0f0e10 Mon Sep 17 00:00:00 2001 From: Michael Ehrenreich Date: Mon, 27 Jun 2022 01:28:59 +0200 Subject: [PATCH 9/9] Implement reboot to new image --- main.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/main.cpp b/main.cpp index fd18e75..4f0c560 100644 --- a/main.cpp +++ b/main.cpp @@ -178,6 +178,8 @@ protocol::serial::Feedback feedback; #ifdef FEATURE_CAN std::atomic timeoutCntLeft = 0; std::atomic timeoutCntRight = 0; + +uint32_t *reboot_request_address = 0; #endif uint32_t main_loop_counter; @@ -252,6 +254,7 @@ void parseCanCommand(); void applyIncomingCanMessage(); void sendCanFeedback(); void sendFlasherFeedback(); +void handleFlasher(); #endif #ifdef FEATURE_BUTTON @@ -410,7 +413,7 @@ int main() parseCanCommand(); sendCanFeedback(); - sendFlasherFeedback(); + handleFlasher(); #endif #ifdef FEATURE_BUTTON @@ -1670,11 +1673,21 @@ void applyIncomingCanMessage() break; case MotorController::Command::Poweroff: case MotorController::Command::Poweroff: + { + if (header.DLC >= 2) + { + // Reboot selected image + reboot_request_address = (uint32_t *)((*(uint8_t *)buf == 0) ? APP_A_START : APP_B_START); + } + else + { #ifdef FEATURE_BUTTON - if (*((bool*)buf)) - poweroff(); + if (*((bool*)buf)) + poweroff(); #endif - break; + } + break; + } default: #ifndef CAN_LOG_UNKNOWN_ADDR if constexpr (false) @@ -1798,6 +1811,16 @@ void sendFlasherFeedback() { //while (true); } } + +void handleFlasher() +{ + sendFlasherFeedback(); + + if (reboot_request_address) + { + reboot_new_image(reboot_request_address); + } +} #endif #ifdef FEATURE_BUTTON -- 2.50.1