Re-enabled self-updater with SAMD51 support.

This commit is contained in:
Scott Shawcroft 2017-10-24 17:25:17 -07:00
parent ee100b0adc
commit 9de0794dc1
9 changed files with 340 additions and 42 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "lib/uf2"]
path = lib/uf2
url = https://github.com/Microsoft/uf2.git

View file

@ -29,11 +29,13 @@ $(WFLAGS)
ifeq ($(CHIP_FAMILY), samd21) ifeq ($(CHIP_FAMILY), samd21)
LINKER_SCRIPT=./lib/samd21/samd21a/gcc/gcc/samd21j18a_flash.ld LINKER_SCRIPT=./lib/samd21/samd21a/gcc/gcc/samd21j18a_flash.ld
BOOTLOADER_SIZE=8192 BOOTLOADER_SIZE=8192
SELF_LINKER_SCRIPT=scripts/samd21j18a_self.ld
endif endif
ifeq ($(CHIP_FAMILY), samd51) ifeq ($(CHIP_FAMILY), samd51)
LINKER_SCRIPT=./lib/samd51/gcc/gcc/samd51j18a_flash.ld LINKER_SCRIPT=./lib/samd51/gcc/gcc/samd51j18a_flash.ld
BOOTLOADER_SIZE=16384 BOOTLOADER_SIZE=16384
SELF_LINKER_SCRIPT=scripts/samd51j19a_self.ld
endif endif
LDFLAGS= $(COMMON_FLAGS) \ LDFLAGS= $(COMMON_FLAGS) \
@ -80,7 +82,7 @@ NAME=bootloader
EXECUTABLE=$(BUILD_PATH)/$(NAME).bin EXECUTABLE=$(BUILD_PATH)/$(NAME).bin
SELF_EXECUTABLE=$(BUILD_PATH)/update-$(NAME).uf2 SELF_EXECUTABLE=$(BUILD_PATH)/update-$(NAME).uf2
all: dirs $(EXECUTABLE) #$(SELF_EXECUTABLE) all: dirs $(EXECUTABLE) $(SELF_EXECUTABLE)
r: run r: run
b: burn b: burn
@ -117,10 +119,10 @@ $(EXECUTABLE): $(OBJECTS)
$(SELF_EXECUTABLE): $(SELF_OBJECTS) $(SELF_EXECUTABLE): $(SELF_OBJECTS)
$(CC) -L$(BUILD_PATH) $(LDFLAGS) \ $(CC) -L$(BUILD_PATH) $(LDFLAGS) \
-T./scripts/samd21j18a_self.ld \ -T$(SELF_LINKER_SCRIPT) \
-Wl,-Map,$(BUILD_PATH)/update-$(NAME).map -o $(BUILD_PATH)/update-$(NAME).elf $(SELF_OBJECTS) -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 arm-none-eabi-objcopy -O binary $(BUILD_PATH)/update-$(NAME).elf $(BUILD_PATH)/update-$(NAME).bin
node scripts/bin2uf2.js $(BUILD_PATH)/update-$(NAME).bin $@ python 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)/%.o: src/%.c $(wildcard inc/*.h boards/*/*.h)
echo "$<" echo "$<"
@ -130,7 +132,7 @@ $(BUILD_PATH)/%.o: $(BUILD_PATH)/%.c
$(CC) $(CFLAGS) $(BLD_EXTA_FLAGS) $(INCLUDES) $< -o $@ $(CC) $(CFLAGS) $(BLD_EXTA_FLAGS) $(INCLUDES) $< -o $@
$(BUILD_PATH)/selfdata.c: $(EXECUTABLE) scripts/gendata.js src/sketch.cpp $(BUILD_PATH)/selfdata.c: $(EXECUTABLE) scripts/gendata.js src/sketch.cpp
node scripts/gendata.js $(BUILD_PATH) $(NAME).bin python scripts/gendata.py $(BOOTLOADER_SIZE) $(EXECUTABLE)
clean: clean:
rm -rf build rm -rf build

View file

@ -6,9 +6,9 @@ the USB MSC (mass storage).
[![Build Status](https://travis-ci.org/Microsoft/uf2-samd21.svg?branch=master)](https://travis-ci.org/Microsoft/uf2-samd21) [![Build Status](https://travis-ci.org/Microsoft/uf2-samd21.svg?branch=master)](https://travis-ci.org/Microsoft/uf2-samd21)
## UF2 ## UF2
**UF2 (USB Flashing Format)** is a name of a file format, developed by Microsoft, that is particularly **UF2 (USB Flashing Format)** is a name of a file format, developed by Microsoft, that is particularly
suitable for flashing devices over MSC devices. The file consists suitable for flashing devices over MSC devices. The file consists
of 512 byte blocks, each of which is self-contained and independent of 512 byte blocks, each of which is self-contained and independent
of others. of others.
@ -27,7 +27,7 @@ a UF2 file is written and immediately write it to flash.
## Features ## Features
* USB CDC (Serial emulation) monitor mode compatible with Arduino * USB CDC (Serial emulation) monitor mode compatible with Arduino
(including XYZ commands) and BOSSA flashing tool (including XYZ commands) and BOSSA flashing tool
* USB MSC interface for writing UF2 files * USB MSC interface for writing UF2 files
* reading of the contests of the flash as an UF2 file via USB MSC * reading of the contests of the flash as an UF2 file via USB MSC
@ -84,12 +84,14 @@ sketch. You can copy&paste it into Arduino IDE and upload it to the device.
## Fuses ## Fuses
### SAMD21
The SAMD21 supports a `BOOTPROT` fuse, which write-protects the flash area of The SAMD21 supports a `BOOTPROT` fuse, which write-protects the flash area of
the bootloader. Changes to this fuse only take effect after device reset. the bootloader. Changes to this fuse only take effect after device reset.
OpenOCD exposes `at91samd bootloader` command to set this fuse. **This command is buggy.** OpenOCD exposes `at91samd bootloader` command to set this fuse. **This command is buggy.**
It seems to reset both fuse words to `0xffffffff`, which prevents the device It seems to reset both fuse words to `0xffffffff`, which prevents the device
from operating correctly (it seems to reboot very frequently). from operating correctly (it seems to reboot very frequently).
In `scripts/fuses.tcl` there is an OpenOCD script In `scripts/fuses.tcl` there is an OpenOCD script
which correctly sets the fuse. It's invoked by `dbgtool.js fuses`. It can be also which correctly sets the fuse. It's invoked by `dbgtool.js fuses`. It can be also
used to reset the fuses to sane values - just look at the comment at the top. used to reset the fuses to sane values - just look at the comment at the top.
@ -98,6 +100,17 @@ The bootloader update programs (both the `.uf2` file and the Arduino sketch)
clear the `BOOTPROT` (i.e., set it to `0x7`) before trying to flash anything. clear the `BOOTPROT` (i.e., set it to `0x7`) before trying to flash anything.
After flashing is done, they set `BOOTPROT` to 8 kilobyte bootloader size (i.e, `0x2`). After flashing is done, they set `BOOTPROT` to 8 kilobyte bootloader size (i.e, `0x2`).
### SAMD51
The SAMD51s bootloader protection can be temporarily disabled through an NVM
command rather than a full erase and write of the AUX page. The boot protection
will be checked and set by the self updaters.
So, if you've used self-updaters but want to load it directly, then you'll need
to temporarily turn off the protection. In gdb the command is:
`set ((Nvmctrl *)0x41004000UL)->CTRLB.reg = (0xA5 << 8) | 0x1a`
## Build ## Build
### Requirements ### Requirements
@ -132,7 +145,7 @@ The default board is `zero`. You can build a different one using:
make BOARD=metro make BOARD=metro
``` ```
If you're working on different board, it's best to create `Makefile.local` If you're working on different board, it's best to create `Makefile.local`
with say `BOARD=metro` to change the default. with say `BOARD=metro` to change the default.
The names `zero` and `metro` refer to subdirectories of `boards/`. The names `zero` and `metro` refer to subdirectories of `boards/`.
@ -153,22 +166,22 @@ make r
There is a number of configuration parameters at the top of `uf2.h` file. There is a number of configuration parameters at the top of `uf2.h` file.
Adjust them to your liking. Adjust them to your liking.
By default, you cannot enable all the features, as the bootloader would exceed By default, you cannot enable all the features, as the bootloader would exceed
the 8k allocated to it by Arduino etc. It will assert on startup that it's not bigger the 8k allocated to it by Arduino etc. It will assert on startup that it's not bigger
than 8k. Also, the linker script will not allow it. than 8k. Also, the linker script will not allow it.
Three typical configurations are: Three typical configurations are:
* HID, WebUSB, MSC, plus flash reading via FAT; UART and CDC disabled; * HID, WebUSB, MSC, plus flash reading via FAT; UART and CDC disabled;
logging optional; **recommended** logging optional; **recommended**
* USB CDC and MSC, plus flash reading via FAT; UART disabled; * USB CDC and MSC, plus flash reading via FAT; UART disabled;
logging optional; this may have Windows driver problems logging optional; this may have Windows driver problems
* USB CDC and MSC, no flash reading via FAT (or at least `index.htm` disabled); UART enabled; * USB CDC and MSC, no flash reading via FAT (or at least `index.htm` disabled); UART enabled;
logging disabled; no handover; no HID; logging disabled; no handover; no HID;
only this one if you need the UART support in bootloader for whatever reason only this one if you need the UART support in bootloader for whatever reason
CDC and MSC together will work on Linux and Mac with no drivers. CDC and MSC together will work on Linux and Mac with no drivers.
On Windows, if you have drivers installed for the USB ID chosen, On Windows, if you have drivers installed for the USB ID chosen,
then CDC might work and MSC will not work; then CDC might work and MSC will not work;
otherwise, if you have no drivers, MSC will work, and CDC will work on Windows 10 only. otherwise, if you have no drivers, MSC will work, and CDC will work on Windows 10 only.
Thus, it's best to set the USB ID to one for which there are no drivers. Thus, it's best to set the USB ID to one for which there are no drivers.

1
lib/uf2 Submodule

@ -0,0 +1 @@
Subproject commit d7ab98ee50bb42bb1c2ff600324e3a155a3cc4ad

51
scripts/gendata.py Normal file
View file

@ -0,0 +1,51 @@
import sys
import os
def update_crc(new_byte, current_crc):
crc = current_crc ^ new_byte << 8
for cmpt in range(8):
if crc & 0x8000:
crc = crc << 1 ^ 0x1021
else:
crc = crc << 1
crc &= 0xffff
return crc
# Load the bootloader file
bootloader_size = int(sys.argv[1])
bin_name = sys.argv[2]
bootloader = bytearray()
with open(bin_name, "rb") as bootloader_bin:
bootloader.extend(bootloader_bin.read())
# Fill the remaining space with 0xff.
bootloader.extend([0xff] * (bootloader_size - len(bootloader)))
# Output the bootloader binary data into C code to use in the self updater.
with open(os.path.dirname(bin_name) + "/selfdata.c", "w") as output:
output.write("#include <stdint.h>\n")
output.write("const uint8_t bootloader[{}] ".format(bootloader_size) +
"__attribute__ ((aligned (4))) = {")
crcs = []
crc = 0
for row in range(bootloader_size / 16):
# Save the crc every 1k.
if row % (1024 / 16) == 0 and row > 0:
crcs.append(crc)
crc = 0
start_index = row * 16
row_bytes = bootloader[start_index:start_index+16]
formatted_bytes = ["0x{:02x}".format(x) for x in row_bytes]
output.write(", ".join(formatted_bytes) + ",\n")
# Update the crc
for b in row_bytes:
crc = update_crc(b, crc)
crcs.append(crc) # Add the last crc
output.write("\n};\n")
crcs = ["0x{:04x}".format(x) for x in crcs]
output.write("const uint16_t bootloader_crcs[] = {" +
" ,".join(crcs) + "};\n")

163
scripts/samd51j19a_self.ld Normal file
View file

@ -0,0 +1,163 @@
/**
* \file
*
* \brief Linker script for running in internal FLASH on the SAMD51J19A
*
* Copyright (c) 2017 Microchip Technology Inc.
*
* \asf_license_start
*
* \page License
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the Licence at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* \asf_license_stop
*
*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
SEARCH_DIR(.)
/* Memory Spaces Definitions */
MEMORY
{
rom (rx) : ORIGIN = 0x00004000, LENGTH = 0x00080000 - 0x4000
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00030000
bkupram (rwx) : ORIGIN = 0x47000000, LENGTH = 0x00002000
qspi (rwx) : ORIGIN = 0x04000000, LENGTH = 0x01000000
}
/* The stack size used by the application. NOTE: you need to adjust according to your application. */
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
/* Section Definitions */
SECTIONS
{
.text :
{
. = ALIGN(4);
_sfixed = .;
KEEP(*(.vectors .vectors.*))
*(.text .text.* .gnu.linkonce.t.*)
*(.glue_7t) *(.glue_7)
*(.rodata .rodata* .gnu.linkonce.r.*)
*(.ARM.extab* .gnu.linkonce.armextab.*)
/* Support C constructors, and C destructors in both user code
and the C library. This also provides support for C++ code. */
. = ALIGN(4);
KEEP(*(.init))
. = ALIGN(4);
__preinit_array_start = .;
KEEP (*(.preinit_array))
__preinit_array_end = .;
. = ALIGN(4);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
. = ALIGN(4);
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*crtend.o(.ctors))
. = ALIGN(4);
KEEP(*(.fini))
. = ALIGN(4);
__fini_array_start = .;
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
__fini_array_end = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*crtend.o(.dtors))
. = ALIGN(4);
_efixed = .; /* End of text section */
} > rom
/* .ARM.exidx is sorted, so has to go in its own output section. */
PROVIDE_HIDDEN (__exidx_start = .);
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > rom
PROVIDE_HIDDEN (__exidx_end = .);
. = ALIGN(4);
_etext = .;
.relocate : AT (_etext)
{
. = ALIGN(4);
_srelocate = .;
*(.ramfunc .ramfunc.*);
*(.data .data.*);
. = ALIGN(4);
_erelocate = .;
} > ram
.bkupram (NOLOAD):
{
. = ALIGN(8);
_sbkupram = .;
*(.bkupram .bkupram.*);
. = ALIGN(8);
_ebkupram = .;
} > bkupram
.qspi (NOLOAD):
{
. = ALIGN(8);
_sqspi = .;
*(.qspi .qspi.*);
. = ALIGN(8);
_eqspi = .;
} > qspi
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
. = ALIGN(4);
_sbss = . ;
_szero = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = . ;
_ezero = .;
} > ram
/* stack section */
.stack (NOLOAD):
{
. = ALIGN(8);
_sstack = .;
. = . + STACK_SIZE;
. = ALIGN(8);
_estack = .;
} > ram
. = ALIGN(4);
_end = . ;
}

View file

@ -42,7 +42,6 @@ void flash_write_words(uint32_t *dst, uint32_t *src, uint32_t n_words) {
uint32_t len = 4 < n_words ? 4 : n_words; uint32_t len = 4 < n_words ? 4 : n_words;
wait_ready(); wait_ready();
// Fill a quad word so it triggers the auto-write.
for (uint32_t i = 0; i < 4; i++) { for (uint32_t i = 0; i < 4; i++) {
if (i < len) { if (i < len) {
dst[i] = src[i]; dst[i] = src[i];
@ -78,6 +77,10 @@ bool row_same[FLASH_SIZE / NVMCTRL_BLOCK_SIZE][NVMCTRL_BLOCK_SIZE / FLASH_ROW_SI
#define QUICK_FLASH 1 #define QUICK_FLASH 1
void flash_write_row(uint32_t *dst, uint32_t *src) { void flash_write_row(uint32_t *dst, uint32_t *src) {
// The cache in Rev A isn't reliable when reading and writing to the NVM.
NVMCTRL->CTRLA.bit.CACHEDIS0 = true;
NVMCTRL->CTRLA.bit.CACHEDIS1 = true;
uint32_t block = ((uint32_t) dst) / NVMCTRL_BLOCK_SIZE; uint32_t block = ((uint32_t) dst) / NVMCTRL_BLOCK_SIZE;
uint8_t row = (((uint32_t) dst) % NVMCTRL_BLOCK_SIZE) / FLASH_ROW_SIZE; uint8_t row = (((uint32_t) dst) % NVMCTRL_BLOCK_SIZE) / FLASH_ROW_SIZE;
#if QUICK_FLASH #if QUICK_FLASH
@ -99,6 +102,7 @@ void flash_write_row(uint32_t *dst, uint32_t *src) {
if (!block_erased[block]) { if (!block_erased[block]) {
uint8_t rows_per_block = NVMCTRL_BLOCK_SIZE / FLASH_ROW_SIZE; uint8_t rows_per_block = NVMCTRL_BLOCK_SIZE / FLASH_ROW_SIZE;
uint8_t* block_address = block * NVMCTRL_BLOCK_SIZE;
bool some_rows_same = false; bool some_rows_same = false;
for (uint8_t i = 0; i < rows_per_block; i++) { for (uint8_t i = 0; i < rows_per_block; i++) {
@ -108,7 +112,9 @@ void flash_write_row(uint32_t *dst, uint32_t *src) {
if (some_rows_same) { if (some_rows_same) {
for (uint8_t i = 0; i < rows_per_block; i++) { for (uint8_t i = 0; i < rows_per_block; i++) {
if(row_same[block][i]) { if(row_same[block][i]) {
memcpy(row_cache[i], dst + i * FLASH_ROW_SIZE, FLASH_ROW_SIZE); // dst is a uint32_t pointer so we add the number of words,
// not bytes.
memcpy(row_cache[i], block_address + i * (FLASH_ROW_SIZE / 4), FLASH_ROW_SIZE);
} }
} }
} }
@ -117,11 +123,17 @@ void flash_write_row(uint32_t *dst, uint32_t *src) {
if (some_rows_same) { if (some_rows_same) {
for (uint8_t i = 0; i < rows_per_block; i++) { for (uint8_t i = 0; i < rows_per_block; i++) {
if(row_same[block][i]) { if(row_same[block][i]) {
flash_write_words(dst + i * FLASH_ROW_SIZE, row_cache[i], FLASH_ROW_SIZE / 4); // dst is a uint32_t pointer so we add the number of words,
// not bytes.
flash_write_words(block_address + i * (FLASH_ROW_SIZE / 4), row_cache[i], FLASH_ROW_SIZE / 4);
} }
} }
} }
} }
flash_write_words(dst, src, FLASH_ROW_SIZE / 4); flash_write_words(dst, src, FLASH_ROW_SIZE / 4);
// Don't return until we're done writing in case something after us causes
// a reset.
wait_ready();
} }

View file

@ -193,7 +193,7 @@ int main(void) {
// not enumerated yet // not enumerated yet
RGBLED_set_color(COLOR_START); RGBLED_set_color(COLOR_START);
led_tick_step = 0; led_tick_step = 10;
/* Wait for a complete enum on usb or a '#' char on serial line */ /* Wait for a complete enum on usb or a '#' char on serial line */
while (1) { while (1) {
@ -204,8 +204,7 @@ int main(void) {
resetHorizon = 0; resetHorizon = 0;
#endif #endif
RGBLED_set_color(COLOR_USB); RGBLED_set_color(COLOR_USB);
if (!led_tick_step) led_tick_step = 1;
led_tick_step = 1;
} }
main_b_cdc_enable = true; main_b_cdc_enable = true;

View file

@ -1,29 +1,51 @@
#include "uf2.h" #include "uf2.h"
#include "sam.h"
#ifdef SAMD21
#define BOOTLOADER_K 8 #define BOOTLOADER_K 8
#endif
#ifdef SAMD51
#define BOOTLOADER_K 16
#endif
extern const uint8_t bootloader[]; extern const uint8_t bootloader[];
extern const uint16_t bootloader_crcs[]; extern const uint16_t bootloader_crcs[];
uint8_t pageBuf[FLASH_ROW_SIZE]; uint8_t pageBuf[FLASH_ROW_SIZE];
#define exec_cmd(cmd) \ #ifdef SAMD21
do { \ #define NVM_FUSE_ADDR NVMCTRL_AUX0_ADDRESS
NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; \ #define exec_cmd(cmd) \
NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER / 2; \ do { \
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd; \ NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; \
while (NVMCTRL->INTFLAG.bit.READY == 0) \ NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER / 2; \
; \ NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd; \
while (NVMCTRL->INTFLAG.bit.READY == 0) {} \
} while (0) } while (0)
#endif
#ifdef SAMD51
#define NVM_FUSE_ADDR NVMCTRL_FUSES_BOOTPROT_ADDR
#define exec_cmd(cmd) \
do { \
NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER; \
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | cmd; \
while (NVMCTRL->STATUS.bit.READY == 0) {} \
} while (0)
#endif
void setBootProt(int v) { void setBootProt(int v) {
uint32_t fuses[2]; uint32_t fuses[2];
while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)) #ifdef SAMD21
; while (!(NVMCTRL->INTFLAG.reg & NVMCTRL_INTFLAG_READY)) {}
#endif
#ifdef SAMD51
while (NVMCTRL->STATUS.bit.READY == 0) {}
#endif
fuses[0] = *((uint32_t *)NVMCTRL_AUX0_ADDRESS); fuses[0] = *((uint32_t *)NVM_FUSE_ADDR);
fuses[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1); fuses[1] = *(((uint32_t *)NVM_FUSE_ADDR) + 1);
uint32_t bootprot = (fuses[0] & NVMCTRL_FUSES_BOOTPROT_Msk) >> NVMCTRL_FUSES_BOOTPROT_Pos; uint32_t bootprot = (fuses[0] & NVMCTRL_FUSES_BOOTPROT_Msk) >> NVMCTRL_FUSES_BOOTPROT_Pos;
@ -37,15 +59,28 @@ void setBootProt(int v) {
fuses[0] = (fuses[0] & ~NVMCTRL_FUSES_BOOTPROT_Msk) | (v << NVMCTRL_FUSES_BOOTPROT_Pos); fuses[0] = (fuses[0] & ~NVMCTRL_FUSES_BOOTPROT_Msk) | (v << NVMCTRL_FUSES_BOOTPROT_Pos);
#ifdef SAMD21
NVMCTRL->CTRLB.reg = NVMCTRL->CTRLB.reg | NVMCTRL_CTRLB_CACHEDIS | NVMCTRL_CTRLB_MANW; NVMCTRL->CTRLB.reg = NVMCTRL->CTRLB.reg | NVMCTRL_CTRLB_CACHEDIS | NVMCTRL_CTRLB_MANW;
exec_cmd(NVMCTRL_CTRLA_CMD_EAR); exec_cmd(NVMCTRL_CTRLA_CMD_EAR);
exec_cmd(NVMCTRL_CTRLA_CMD_PBC); exec_cmd(NVMCTRL_CTRLA_CMD_PBC);
#endif
#ifdef SAMD51
NVMCTRL->CTRLA.bit.WMODE = NVMCTRL_CTRLA_WMODE_MAN;
*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = fuses[0]; exec_cmd(NVMCTRL_CTRLB_CMD_EP);
*(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = fuses[1]; exec_cmd(NVMCTRL_CTRLB_CMD_PBC);
#endif
*((uint32_t *)NVM_FUSE_ADDR) = fuses[0];
*(((uint32_t *)NVM_FUSE_ADDR) + 1) = fuses[1];
#ifdef SAMD21
exec_cmd(NVMCTRL_CTRLA_CMD_WAP); exec_cmd(NVMCTRL_CTRLA_CMD_WAP);
#endif
#ifdef SAMD51
exec_cmd(NVMCTRL_CTRLB_CMD_WQW);
#endif
resetIntoApp(); resetIntoApp();
} }
@ -55,7 +90,7 @@ int main(void) {
logmsg("Start"); logmsg("Start");
assert(8 << NVMCTRL->PARAM.bit.PSZ == FLASH_PAGE_SIZE); assert((8 << NVMCTRL->PARAM.bit.PSZ) == FLASH_PAGE_SIZE);
// assert(FLASH_PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP == FLASH_SIZE); // assert(FLASH_PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP == FLASH_SIZE);
/* We have determined we should stay in the monitor. */ /* We have determined we should stay in the monitor. */
@ -66,7 +101,19 @@ int main(void) {
logmsg("Before main loop"); logmsg("Before main loop");
#ifdef SAMD21
setBootProt(7); // 0k setBootProt(7); // 0k
#endif
#ifdef SAMD51
// We only need to set the BOOTPROT once on the SAMD51. For updates, we can
// temporarily turn the protection off instead.
if (NVMCTRL->STATUS.bit.BOOTPROT != 13) {
setBootProt(13); // 16k
}
exec_cmd(NVMCTRL_CTRLB_CMD_SBPDIS);
NVMCTRL->CTRLA.bit.CACHEDIS0 = true;
NVMCTRL->CTRLA.bit.CACHEDIS1 = true;
#endif
const uint8_t *ptr = bootloader; const uint8_t *ptr = bootloader;
int i; int i;
@ -89,22 +136,29 @@ int main(void) {
logmsg("Update successful!"); logmsg("Update successful!");
// re-base int vector back to bootloader, so that the flash erase below doesn't write over the
// vectors
SCB->VTOR = 0;
// Write zeros to the stack location and reset handler location so the
// bootloader doesn't run us a second time. We don't need to erase to write
// zeros. The remainder of the write unit will be set to 1s which should
// preserve the existing values but its not critical.
uint32_t zeros[2] = {0, 0};
flash_write_words((void *)(BOOTLOADER_K * 1024), zeros, 2);
for (i = 0; i < 20; ++i) { for (i = 0; i < 20; ++i) {
LED_MSC_TGL(); LED_MSC_TGL();
delay(1000); delay(1000);
} }
// re-base int vector back to bootloader, so that the flash erase below doesn't write over the
// vectors
SCB->VTOR = 0;
// erase first row of this updater app, so the bootloader doesn't start us again
flash_erase_row((void *)(BOOTLOADER_K * 1024));
LED_MSC_OFF(); LED_MSC_OFF();
#ifdef SAMD21
setBootProt(2); // 8k setBootProt(2); // 8k
#endif
// For the SAMD51, the boot protection will automatically be re-enabled on
// reset.
resetIntoBootloader(); resetIntoBootloader();