Author Topic: Stuck on how to load application from custom ARM bootloader - SAMD51  (Read 3225 times)

0 Members and 1 Guest are viewing this topic.

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
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: [Select]
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

 
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 12009
  • Country: us
    • Personal site
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #1 on: October 24, 2019, 11:42:54 pm »
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.
Alex
 

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #2 on: October 24, 2019, 11:56:45 pm »
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.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 12009
  • Country: us
    • Personal site
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #3 on: October 25, 2019, 12:00:53 am »
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.
Alex
 
The following users thanked this post: Annakin4

Online brucehoult

  • Super Contributor
  • ***
  • Posts: 5058
  • Country: nz
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #4 on: October 25, 2019, 04:47:16 am »
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.

I'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.
 

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #5 on: October 25, 2019, 05:31:19 am »
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.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 12009
  • Country: us
    • Personal site
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #6 on: October 25, 2019, 05:35:24 am »
What variable? You really need to provide more information. The build options are as important as the code itself.
Alex
 

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #7 on: October 25, 2019, 05:47:44 am »
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: [Select]
BOARD=itsybitsy_m4
-include Makefile.user
include boards/$(BOARD)/board.mk
CC=arm-none-eabi-gcc
ifeq ($(CHIP_FAMILY), samd21)
COMMON_FLAGS = -mthumb -mcpu=cortex-m0plus -Os -g -DSAMD21
endif
ifeq ($(CHIP_FAMILY), samd51)
COMMON_FLAGS = -mthumb -mcpu=cortex-m4 -O2 -g -DSAMD51
endif
WFLAGS = \
-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-sign
CFLAGS = $(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.ld
BOOTLOADER_SIZE=8192
SELF_LINKER_SCRIPT=scripts/samd21j18a_self.ld
endif

ifeq ($(CHIP_FAMILY), samd51)
LINKER_SCRIPT=scripts/samd51j19a.ld
BOOTLOADER_SIZE=16384
SELF_LINKER_SCRIPT=scripts/samd51j19a_self.ld
endif

LDFLAGS= $(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.specs
BUILD_PATH=build/$(BOARD)
INCLUDES = -I. -I./inc -I./inc/preprocessor
INCLUDES += -I./boards/$(BOARD) -Ilib/cmsis/CMSIS/Include -Ilib/usb_msc
INCLUDES += -I$(BUILD_PATH)


ifeq ($(CHIP_FAMILY), samd21)
INCLUDES += -Ilib/samd21/samd21a/include/
endif

ifeq ($(CHIP_FAMILY), samd51)
ifeq ($(findstring SAME54,$(CHIP_VARIANT)),SAME54)
INCLUDES += -Ilib/same54/include/
else
INCLUDES += -Ilib/samd51/include/
endif
endif

COMMON_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.c

SOURCES = $(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.c

OBJECTS = $(patsubst src/%.c,$(BUILD_PATH)/%.o,$(SOURCES))
SELF_OBJECTS = $(patsubst src/%.c,$(BUILD_PATH)/%.o,$(SELF_SOURCES)) $(BUILD_PATH)/selfdata.o

NAME=bootloader-$(BOARD)-$(UF2_VERSION_BASE)
EXECUTABLE=$(BUILD_PATH)/$(NAME).bin
SELF_EXECUTABLE=$(BUILD_PATH)/update-$(NAME).uf2
SELF_EXECUTABLE_INO=$(BUILD_PATH)/update-$(NAME).ino

SUBMODULES = lib/uf2/README.md

all: $(SUBMODULES) dirs $(EXECUTABLE) $(SELF_EXECUTABLE)

r: run
b: burn
l: logs

burn: all
node scripts/dbgtool.js fuses
node scripts/dbgtool.js $(BUILD_PATH)/$(NAME).bin

run: 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-gdb

bmp-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.log

bmp-flashone:
while : ; do $(MAKE) bmp-flash && exit 0 ; sleep 1 ; done
afplay /System/Library/PrivateFrameworks/ScreenReader.framework/Versions/A/Resources/Sounds/Error.aiff

bmp-loop:
while : ; do $(MAKE) bmp-flashone ; sleep 5 ; done

bmp-gdb: $(BUILD_PATH)/$(NAME).bin
$(GDB) $(BMP_ARGS) $(BUILD_PATH)/$(NAME).elf

$(BUILD_PATH)/flash.jlink: $(BUILD_PATH)/$(NAME).bin
echo " \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.jlink

jlink-flash: $(BUILD_PATH)/$(NAME).bin $(BUILD_PATH)/flash.jlink
jlinkexe -if swd -device AT$(CHIP_VARIANT) -speed 4000 -CommanderScript $(BUILD_PATH)/flash.jlink

wait:
sleep 5

logs:
node scripts/dbgtool.js $(BUILD_PATH)/$(NAME).map

selflogs:
node scripts/dbgtool.js $(BUILD_PATH)/update-$(NAME).map

dirs:
@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: Makefile
echo "#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).bin
python2 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.h
echo "$<"
$(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.cpp
python2 scripts/gendata.py $(BOOTLOADER_SIZE) $(EXECUTABLE)

clean:
rm -rf build

gdb:
arm-none-eabi-gdb $(BUILD_PATH)/$(NAME).elf

tui:
arm-none-eabi-gdb -tui $(BUILD_PATH)/$(NAME).elf

%.asmdump: %.o
arm-none-eabi-objdump -d $< > $@

applet0: $(BUILD_PATH)/flash.asmdump
node scripts/genapplet.js $< flash_write

applet1: $(BUILD_PATH)/utils.asmdump
node scripts/genapplet.js $< resetIntoApp

drop-board: all
@echo "*** Copy files for $(BOARD)"
mkdir -p build/drop
rm -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.md
cd 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; done

drop: all-boards drop-pkg

$(SUBMODULES):
git submodule update --init --recursive


I 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.
 

Offline ataradov

  • Super Contributor
  • ***
  • Posts: 12009
  • Country: us
    • Personal site
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #8 on: October 25, 2019, 06:04:43 am »
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.
Alex
 
The following users thanked this post: Annakin4

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #9 on: October 25, 2019, 05:16:24 pm »
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.
 

Offline Annakin4Topic starter

  • Contributor
  • Posts: 26
  • Country: us
  • If it's fixed, I'll break it!
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #10 on: October 25, 2019, 06:46:25 pm »
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. :clap: :clap: :clap: :clap: :clap: :clap: :clap:
 

Offline Mr_Mxyzptlk

  • Newbie
  • Posts: 2
  • Country: us
Re: Stuck on how to load application from custom ARM bootloader - SAMD51
« Reply #11 on: December 15, 2019, 05:25:55 pm »
I'm working on a similar project for the Metro M4 Express. Is your source code available to interested parties?
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf