Re-enabled self-updater with SAMD51 support.
This commit is contained in:
parent
ee100b0adc
commit
9de0794dc1
9 changed files with 340 additions and 42 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "lib/uf2"]
|
||||
path = lib/uf2
|
||||
url = https://github.com/Microsoft/uf2.git
|
||||
10
Makefile
10
Makefile
|
|
@ -29,11 +29,13 @@ $(WFLAGS)
|
|||
ifeq ($(CHIP_FAMILY), samd21)
|
||||
LINKER_SCRIPT=./lib/samd21/samd21a/gcc/gcc/samd21j18a_flash.ld
|
||||
BOOTLOADER_SIZE=8192
|
||||
SELF_LINKER_SCRIPT=scripts/samd21j18a_self.ld
|
||||
endif
|
||||
|
||||
ifeq ($(CHIP_FAMILY), samd51)
|
||||
LINKER_SCRIPT=./lib/samd51/gcc/gcc/samd51j18a_flash.ld
|
||||
BOOTLOADER_SIZE=16384
|
||||
SELF_LINKER_SCRIPT=scripts/samd51j19a_self.ld
|
||||
endif
|
||||
|
||||
LDFLAGS= $(COMMON_FLAGS) \
|
||||
|
|
@ -80,7 +82,7 @@ NAME=bootloader
|
|||
EXECUTABLE=$(BUILD_PATH)/$(NAME).bin
|
||||
SELF_EXECUTABLE=$(BUILD_PATH)/update-$(NAME).uf2
|
||||
|
||||
all: dirs $(EXECUTABLE) #$(SELF_EXECUTABLE)
|
||||
all: dirs $(EXECUTABLE) $(SELF_EXECUTABLE)
|
||||
|
||||
r: run
|
||||
b: burn
|
||||
|
|
@ -117,10 +119,10 @@ $(EXECUTABLE): $(OBJECTS)
|
|||
|
||||
$(SELF_EXECUTABLE): $(SELF_OBJECTS)
|
||||
$(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)
|
||||
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)
|
||||
echo "$<"
|
||||
|
|
@ -130,7 +132,7 @@ $(BUILD_PATH)/%.o: $(BUILD_PATH)/%.c
|
|||
$(CC) $(CFLAGS) $(BLD_EXTA_FLAGS) $(INCLUDES) $< -o $@
|
||||
|
||||
$(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:
|
||||
rm -rf build
|
||||
|
|
|
|||
33
README.md
33
README.md
|
|
@ -6,9 +6,9 @@ the USB MSC (mass storage).
|
|||
|
||||
[](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
|
||||
of 512 byte blocks, each of which is self-contained and independent
|
||||
of others.
|
||||
|
|
@ -27,7 +27,7 @@ a UF2 file is written and immediately write it to flash.
|
|||
|
||||
## 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
|
||||
* USB MSC interface for writing UF2 files
|
||||
* 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
|
||||
|
||||
### SAMD21
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
|
@ -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.
|
||||
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
|
||||
|
||||
### Requirements
|
||||
|
|
@ -132,7 +145,7 @@ The default board is `zero`. You can build a different one using:
|
|||
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.
|
||||
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.
|
||||
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
|
||||
than 8k. Also, the linker script will not allow it.
|
||||
|
||||
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**
|
||||
* 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
|
||||
* 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;
|
||||
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.
|
||||
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;
|
||||
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.
|
||||
|
|
|
|||
1
lib/uf2
Submodule
1
lib/uf2
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d7ab98ee50bb42bb1c2ff600324e3a155a3cc4ad
|
||||
51
scripts/gendata.py
Normal file
51
scripts/gendata.py
Normal 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
163
scripts/samd51j19a_self.ld
Normal 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 = . ;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
wait_ready();
|
||||
// Fill a quad word so it triggers the auto-write.
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
if (i < len) {
|
||||
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
|
||||
|
||||
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;
|
||||
uint8_t row = (((uint32_t) dst) % NVMCTRL_BLOCK_SIZE) / FLASH_ROW_SIZE;
|
||||
#if QUICK_FLASH
|
||||
|
|
@ -99,6 +102,7 @@ void flash_write_row(uint32_t *dst, uint32_t *src) {
|
|||
|
||||
if (!block_erased[block]) {
|
||||
uint8_t rows_per_block = NVMCTRL_BLOCK_SIZE / FLASH_ROW_SIZE;
|
||||
uint8_t* block_address = block * NVMCTRL_BLOCK_SIZE;
|
||||
|
||||
bool some_rows_same = false;
|
||||
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) {
|
||||
for (uint8_t i = 0; i < rows_per_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) {
|
||||
for (uint8_t i = 0; i < rows_per_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);
|
||||
|
||||
// Don't return until we're done writing in case something after us causes
|
||||
// a reset.
|
||||
wait_ready();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ int main(void) {
|
|||
|
||||
// not enumerated yet
|
||||
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 */
|
||||
while (1) {
|
||||
|
|
@ -204,8 +204,7 @@ int main(void) {
|
|||
resetHorizon = 0;
|
||||
#endif
|
||||
RGBLED_set_color(COLOR_USB);
|
||||
if (!led_tick_step)
|
||||
led_tick_step = 1;
|
||||
led_tick_step = 1;
|
||||
}
|
||||
|
||||
main_b_cdc_enable = true;
|
||||
|
|
|
|||
|
|
@ -1,29 +1,51 @@
|
|||
#include "uf2.h"
|
||||
|
||||
#include "sam.h"
|
||||
|
||||
#ifdef SAMD21
|
||||
#define BOOTLOADER_K 8
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
#define BOOTLOADER_K 16
|
||||
#endif
|
||||
|
||||
extern const uint8_t bootloader[];
|
||||
extern const uint16_t bootloader_crcs[];
|
||||
|
||||
uint8_t pageBuf[FLASH_ROW_SIZE];
|
||||
|
||||
#define exec_cmd(cmd) \
|
||||
do { \
|
||||
NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; \
|
||||
NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER / 2; \
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd; \
|
||||
while (NVMCTRL->INTFLAG.bit.READY == 0) \
|
||||
; \
|
||||
#ifdef SAMD21
|
||||
#define NVM_FUSE_ADDR NVMCTRL_AUX0_ADDRESS
|
||||
#define exec_cmd(cmd) \
|
||||
do { \
|
||||
NVMCTRL->STATUS.reg |= NVMCTRL_STATUS_MASK; \
|
||||
NVMCTRL->ADDR.reg = (uint32_t)NVMCTRL_USER / 2; \
|
||||
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | cmd; \
|
||||
while (NVMCTRL->INTFLAG.bit.READY == 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) {
|
||||
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[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);
|
||||
fuses[0] = *((uint32_t *)NVM_FUSE_ADDR);
|
||||
fuses[1] = *(((uint32_t *)NVM_FUSE_ADDR) + 1);
|
||||
|
||||
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);
|
||||
|
||||
#ifdef SAMD21
|
||||
NVMCTRL->CTRLB.reg = NVMCTRL->CTRLB.reg | NVMCTRL_CTRLB_CACHEDIS | NVMCTRL_CTRLB_MANW;
|
||||
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_EAR);
|
||||
exec_cmd(NVMCTRL_CTRLA_CMD_PBC);
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
NVMCTRL->CTRLA.bit.WMODE = NVMCTRL_CTRLA_WMODE_MAN;
|
||||
|
||||
*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = fuses[0];
|
||||
*(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = fuses[1];
|
||||
exec_cmd(NVMCTRL_CTRLB_CMD_EP);
|
||||
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);
|
||||
#endif
|
||||
#ifdef SAMD51
|
||||
exec_cmd(NVMCTRL_CTRLB_CMD_WQW);
|
||||
#endif
|
||||
|
||||
resetIntoApp();
|
||||
}
|
||||
|
|
@ -55,7 +90,7 @@ int main(void) {
|
|||
|
||||
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);
|
||||
|
||||
/* We have determined we should stay in the monitor. */
|
||||
|
|
@ -66,7 +101,19 @@ int main(void) {
|
|||
|
||||
logmsg("Before main loop");
|
||||
|
||||
#ifdef SAMD21
|
||||
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;
|
||||
int i;
|
||||
|
|
@ -89,22 +136,29 @@ int main(void) {
|
|||
|
||||
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) {
|
||||
LED_MSC_TGL();
|
||||
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();
|
||||
|
||||
#ifdef SAMD21
|
||||
setBootProt(2); // 8k
|
||||
|
||||
#endif
|
||||
// For the SAMD51, the boot protection will automatically be re-enabled on
|
||||
// reset.
|
||||
|
||||
resetIntoBootloader();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue