Stuck on how to load application from custom ARM bootloader - SAMD51 - Page 1
Forum section: electronics / general

author: Annakin4
author_rank: Contributor
timestamp: October 24, 2019, 11:16:57 pm
content: Hi All,This is a bit of a noob question, Im sure anyone who works with Microchip micros can answer this easily.I am writing a bootloader for over the air updates based off the Microsoft UF2 bootloader for the ATSAMD51 on a ItsyBitsy with a 2MB flash IC. I have almost all the code written to prove the concept, but am having a hard time with the last step. I can read and write to the flash IC over QSPI, as well as load a blink program compiled by the Arduino IDE on the external flash, and using GDB I can see my code is loading the BIN from the external flash to the internal program memory at 0x4000. So the bootloader is at 0x0, and the BIN file Arduino made is starting at 0x4000. When I run the bootloader after loading the new application into program memory, the issue is in the following code-Code:static void check_start_application(void) {uint32_t app_start_address;/* Load the Reset Handler address of the application */app_start_address = *(uint32_t *)(APP_START_ADDRESS + 4); // APP_START_ADDRESS is 0x4000/*** Test reset vector of application @APP_START_ADDRESS+4* Sanity check on the Reset_Handler address*/if (app_start_address < APP_START_ADDRESS || app_start_address > FLASH_SIZE) { // This check is passed/* Stay in bootloader */return;}if (RESET_CONTROLLER->RCAUSE.bit.POR) {*DBL_TAP_PTR = 0;}else if (*DBL_TAP_PTR == DBL_TAP_MAGIC) {*DBL_TAP_PTR = 0;return; // stay in bootloader // This is to double tab to go into SAMBA programing mode}else {if (*DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) {*DBL_TAP_PTR = DBL_TAP_MAGIC;delay(500);}*DBL_TAP_PTR = 0;}LED_MSC_OFF();RGBLED_set_color(COLOR_LEAVE);/* Rebase the Stack Pointer */__set_MSP(*(uint32_t *)APP_START_ADDRESS);/* Rebase the vector table base address */SCB->VTOR = ((uint32_t)APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);/* Jump to application Reset Handler in the application */asm("bx %0" ::"r"(app_start_address)); // This is where we leave off}So after we go through the checks, we jump to the application reset handler, but it just hangs and GDB has no idea of the processor state and I get a double lockup error. Am I missing something basic?Thanks,Samuel

author: ataradov
author_rank: Super Contributor
timestamp: October 24, 2019, 11:42:54 pm
content: Look at the disassembly around the last few lines. Do you have optimization enabled?You want to call "__set_MSP" right before the branch. If any of the code following it uses SP, which is possible with not optimized code, then you may get into trouble.Otherwise it looks pretty standard.

author: Annakin4
author_rank: Contributor
timestamp: October 24, 2019, 11:56:45 pm
content: Sorry for removing first reply than posting this, I dont use EEVBlog much, I need to.Thanks for the quick reply!It's not in front of me atm, but would me setting the app_start_address to volatile cause the issue? I did this to print out the value with GDB since it cannot inspect optimized code, it was not copied here since I copied from my master branch and not the one I'm currently working on. __set_MSP(*(uint32_t *)APP_START_ADDRESS); is being called and is in the code posted so I assume I'm good on that front.

author: ataradov
author_rank: Super Contributor
timestamp: October 25, 2019, 12:00:53 am
content: Yes, but any code after __set_MSP() runs with a new SP. If any of the code following this call actually uses the old SP, it will be broken. You have to look at the disassembly around this place and check that this does not happen. This is the first step in debugging.The next step is to look at the actual state of the registers right at the "bx" instruction.

author: brucehoult
author_rank: Super Contributor
timestamp: October 25, 2019, 04:47:16 am
content: Quote from: ataradov on October 24, 2019, 11:42:54 pmI'd say to just declare a __attribute__((noreturn)) void jump_to_app(void *start_adr) and write the darn thing in assembly language and don't try to trick C into doing what you want.

author: Annakin4
author_rank: Contributor
timestamp: October 25, 2019, 05:31:19 am
content: I mean, this is Microsoft's code, it's running on all Adafruit M0 and M4 devices so don't blame me, I'm just wondering why it isnt working and it appears its because I made a variable volatile. If you could explain your points than thanks for the feedback I will improve it.

author: ataradov
author_rank: Super Contributor
timestamp: October 25, 2019, 05:35:24 am
content: What variable? You really need to provide more information. The build options are as important as the code itself.

author: Annakin4
author_rank: Contributor
timestamp: October 25, 2019, 05:47:44 am
content: Ah, okay. I was thinking it was adding volatile to the app_start_address since I know with it not there, this part of the bootloader works as it's what all Adafruit M0 and M4 devices run, I simply added QSPI and my own update code to the open source bootloader so I assumed it was that. What build options do you need? Makefile posted, I removed the warnings as errors option due to some unused update functions I will remove later.Code:BOARD=itsybitsy_m4-include Makefile.userinclude boards/$(BOARD)/board.mkCC=arm-none-eabi-gccifeq ($(CHIP_FAMILY), samd21)COMMON_FLAGS = -mthumb -mcpu=cortex-m0plus -Os -g -DSAMD21endififeq ($(CHIP_FAMILY), samd51)COMMON_FLAGS = -mthumb -mcpu=cortex-m4 -O2 -g -DSAMD51endifWFLAGS = \-Werror -Wall -Wstrict-prototypes \-Werror-implicit-function-declaration -Wpointer-arith -std=gnu99 \-ffunction-sections -fdata-sections -Wchar-subscripts -Wcomment -Wformat=2 \-Wimplicit-int -Wmain -Wparentheses -Wsequence-point -Wreturn-type -Wswitch \-Wtrigraphs -Wunused -Wuninitialized -Wunknown-pragmas -Wfloat-equal -Wno-undef \-Wbad-function-cast -Wwrite-strings -Waggregate-return \-Wformat -Wmissing-format-attribute \-Wno-deprecated-declarations -Wpacked -Wredundant-decls -Wnested-externs \-Wlong-long -Wunreachable-code -Wcast-align \-Wno-missing-braces -Wno-overflow -Wno-shadow -Wno-attributes -Wno-packed -Wno-pointer-signCFLAGS = $(COMMON_FLAGS) \-x c -c -pipe -nostdlib \--param max-inline-insns-single=500 \-fno-strict-aliasing -fdata-sections -ffunction-sections \-D__$(CHIP_VARIANT)__ \$(WFLAGS)UF2_VERSION_BASE = $(shell git describe --dirty --always --tags)ifeq ($(CHIP_FAMILY), samd21)LINKER_SCRIPT=scripts/samd21j18a.ldBOOTLOADER_SIZE=8192SELF_LINKER_SCRIPT=scripts/samd21j18a_self.ldendififeq ($(CHIP_FAMILY), samd51)LINKER_SCRIPT=scripts/samd51j19a.ldBOOTLOADER_SIZE=16384SELF_LINKER_SCRIPT=scripts/samd51j19a_self.ldendifLDFLAGS= $(COMMON_FLAGS) \-Wall -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common \-Wl,--warn-section-align \-save-temps -nostartfiles \--specs=nano.specs --specs=nosys.specsBUILD_PATH=build/$(BOARD)INCLUDES = -I. -I./inc -I./inc/preprocessorINCLUDES += -I./boards/$(BOARD) -Ilib/cmsis/CMSIS/Include -Ilib/usb_mscINCLUDES += -I$(BUILD_PATH)ifeq ($(CHIP_FAMILY), samd21)INCLUDES += -Ilib/samd21/samd21a/include/endififeq ($(CHIP_FAMILY), samd51)ifeq ($(findstring SAME54,$(CHIP_VARIANT)),SAME54)INCLUDES += -Ilib/same54/include/elseINCLUDES += -Ilib/samd51/include/endifendifCOMMON_SRC = \src/flash_$(CHIP_FAMILY).c \src/init_$(CHIP_FAMILY).c \src/startup_$(CHIP_FAMILY).c \src/usart_sam_ba.c \src/screen.c \src/images.c \src/utils.cSOURCES = $(COMMON_SRC) \src/cdc_enumerate.c \src/fat.c \src/main.c \src/msc.c \src/sam_ba_monitor.c \src/uart_driver.c \src/hid.c \SELF_SOURCES = $(COMMON_SRC) \src/selfmain.cOBJECTS = $(patsubst src/%.c,$(BUILD_PATH)/%.o,$(SOURCES))SELF_OBJECTS = $(patsubst src/%.c,$(BUILD_PATH)/%.o,$(SELF_SOURCES)) $(BUILD_PATH)/selfdata.oNAME=bootloader-$(BOARD)-$(UF2_VERSION_BASE)EXECUTABLE=$(BUILD_PATH)/$(NAME).binSELF_EXECUTABLE=$(BUILD_PATH)/update-$(NAME).uf2SELF_EXECUTABLE_INO=$(BUILD_PATH)/update-$(NAME).inoSUBMODULES = lib/uf2/README.mdall: $(SUBMODULES) dirs $(EXECUTABLE) $(SELF_EXECUTABLE)r: runb: burnl: logsburn: allnode scripts/dbgtool.js fusesnode scripts/dbgtool.js $(BUILD_PATH)/$(NAME).binrun: burn wait logs# This currently only works on macOS with a BMP debugger attached.# It's meant to flash the bootloader in a loop.BMP = $(shell ls -1 /dev/cu.usbmodem* | head -1)BMP_ARGS = --nx -ex "set mem inaccessible-by-default off" -ex "set confirm off" -ex "target extended-remote $(BMP)" -ex "mon tpwr enable" -ex "mon swdp_scan" -ex "attach 1"GDB = arm-none-eabi-gdbbmp-flash: $(BUILD_PATH)/$(NAME).bin@test "X$(BMP)" != "X"$(GDB) $(BMP_ARGS) -ex "load" -ex "quit" $(BUILD_PATH)/$(NAME).elf | tee build/flash.log@grep -q "Transfer rate" build/flash.logbmp-flashone:while : ; do $(MAKE) bmp-flash && exit 0 ; sleep 1 ; doneafplay /System/Library/PrivateFrameworks/ScreenReader.framework/Versions/A/Resources/Sounds/Error.aiffbmp-loop:while : ; do $(MAKE) bmp-flashone ; sleep 5 ; donebmp-gdb: $(BUILD_PATH)/$(NAME).bin$(GDB) $(BMP_ARGS) $(BUILD_PATH)/$(NAME).elf$(BUILD_PATH)/flash.jlink: $(BUILD_PATH)/$(NAME).binecho " \n\r \n\h \n\loadbin \"$(BUILD_PATH)/$(NAME).bin\", 0x0 \n\verifybin \"$(BUILD_PATH)/$(NAME).bin\", 0x0 \n\r \n\qc \n\" > $(BUILD_PATH)/flash.jlinkjlink-flash: $(BUILD_PATH)/$(NAME).bin $(BUILD_PATH)/flash.jlinkjlinkexe -if swd -device AT$(CHIP_VARIANT) -speed 4000 -CommanderScript $(BUILD_PATH)/flash.jlinkwait:sleep 5logs:node scripts/dbgtool.js $(BUILD_PATH)/$(NAME).mapselflogs:node scripts/dbgtool.js $(BUILD_PATH)/update-$(NAME).mapdirs:@echo "Building $(BOARD)"-@mkdir -p $(BUILD_PATH)$(EXECUTABLE): $(OBJECTS)$(CC) -L$(BUILD_PATH) $(LDFLAGS) \-T$(LINKER_SCRIPT) \-Wl,-Map,$(BUILD_PATH)/$(NAME).map -o $(BUILD_PATH)/$(NAME).elf $(OBJECTS)arm-none-eabi-objcopy -O binary $(BUILD_PATH)/$(NAME).elf $@@echo-@arm-none-eabi-size $(BUILD_PATH)/$(NAME).elf | awk '{ s=$$1+$$2; print } END { print ""; print "Space left: " ($(BOOTLOADER_SIZE)-s) }'@echo$(BUILD_PATH)/uf2_version.h: Makefileecho "#define UF2_VERSION_BASE \"$(UF2_VERSION_BASE)\""> $@$(SELF_EXECUTABLE): $(SELF_OBJECTS)$(CC) -L$(BUILD_PATH) $(LDFLAGS) \-T$(SELF_LINKER_SCRIPT) \-Wl,-Map,$(BUILD_PATH)/update-$(NAME).map -o $(BUILD_PATH)/update-$(NAME).elf $(SELF_OBJECTS)arm-none-eabi-objcopy -O binary $(BUILD_PATH)/update-$(NAME).elf $(BUILD_PATH)/update-$(NAME).binpython2 lib/uf2/utils/uf2conv.py -b $(BOOTLOADER_SIZE) -c -o $@ $(BUILD_PATH)/update-$(NAME).bin$(BUILD_PATH)/%.o: src/%.c $(wildcard inc/*.h boards/*/*.h) $(BUILD_PATH)/uf2_version.hecho "$<"$(CC) $(CFLAGS) $(BLD_EXTA_FLAGS) $(INCLUDES) $< -o $@$(BUILD_PATH)/%.o: $(BUILD_PATH)/%.c$(CC) $(CFLAGS) $(BLD_EXTA_FLAGS) $(INCLUDES) $< -o $@$(BUILD_PATH)/selfdata.c: $(EXECUTABLE) scripts/gendata.py src/sketch.cpppython2 scripts/gendata.py $(BOOTLOADER_SIZE) $(EXECUTABLE)clean:rm -rf buildgdb:arm-none-eabi-gdb $(BUILD_PATH)/$(NAME).elftui:arm-none-eabi-gdb -tui $(BUILD_PATH)/$(NAME).elf%.asmdump: %.oarm-none-eabi-objdump -d $< > $@applet0: $(BUILD_PATH)/flash.asmdumpnode scripts/genapplet.js $< flash_writeapplet1: $(BUILD_PATH)/utils.asmdumpnode scripts/genapplet.js $< resetIntoAppdrop-board: all@echo "*** Copy files for $(BOARD)"mkdir -p build/droprm -rf build/drop/$(BOARD)mkdir -p build/drop/$(BOARD)cp $(SELF_EXECUTABLE) build/drop/$(BOARD)/cp $(EXECUTABLE) build/drop/$(BOARD)/cp $(SELF_EXECUTABLE_INO) build/drop/$(BOARD)/cp boards/$(BOARD)/board_config.h build/drop/$(BOARD)/drop-pkg:mv build/drop build/uf2-samd21-$(UF2_VERSION_BASE)cp bin-README.md build/uf2-samd21-$(UF2_VERSION_BASE)/README.mdcd build; 7z a uf2-samd21-$(UF2_VERSION_BASE).zip uf2-samd21-$(UF2_VERSION_BASE)rm -rf build/uf2-samd21-$(UF2_VERSION_BASE)all-boards:for f in `cd boards; ls` ; do "$(MAKE)" BOARD=$$f drop-board || break; donedrop: all-boards drop-pkg$(SUBMODULES):git submodule update --init --recursiveI will try removing the volatile if I don't hear a "Thats really not it" back, let me know if you want any other details.

author: ataradov
author_rank: Super Contributor
timestamp: October 25, 2019, 06:04:43 am
content: Adding 'volatile' there is probably a bad idea. The generated code will be more complex and will use SP, which is already changed earlier.There is no need for volatile there, just leave this code alone.

author: Annakin4
author_rank: Contributor
timestamp: October 25, 2019, 05:16:24 pm
content: You were 100% on the money, I used SAMBA with the bootloader and it didnt work (It did before I modified check_start_application()) so I removed the volatile and now the bootloader works again normally. While investigating I found my code was not perfectly copying the code into PROGMEM so now I'm going to fix that and it should be good to go.

author: Annakin4
author_rank: Contributor
timestamp: October 25, 2019, 06:46:25 pm
content: Thank you ataradov for the help, the hint towards the volatile variable was a big help and I can now run the application after copying from the QSPI flash to PROGMEM.

author: Mr_Mxyzptlk
author_rank: Newbie
timestamp: December 15, 2019, 05:25:55 pm
content: I'm working on a similar project for the Metro M4 Express. Is your source code available to interested parties?