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)
|
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
|
||||||
|
|
|
||||||
33
README.md
33
README.md
|
|
@ -6,9 +6,9 @@ the USB MSC (mass storage).
|
||||||
|
|
||||||
[](https://travis-ci.org/Microsoft/uf2-samd21)
|
[](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
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;
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue