mimxrt: Add support for a UF2 bootloader.

Allowing to use e.g. the Adafruit bootloaders with MicroPython.  The .uf2
file is created in addition to the .bin and .hex files allowing to use the
latter ones without the bootloader for debugging and testing.

Changes:

- Set the location of the ISR Vector and .text segment to 0x6000C000 and
  0x6000C400.
- Reserve an area at the start of ITCM for a copy of the interrupt vector
  table and copy the table on reset to this place.
- Extend `machine.bootloader()` by setting the magic number to enable the
  bootloader on reset.
- Create a .uf2 file which skips the segments below 0x6000C000.

The bootloader has to be installed as a preparation step using the board
specific methods, but then the firmware's .uf2 file version can be
installed using the bootloader.  The bootloader can be invoked with:

- double reset
- calling machine.bootloader()
- Using the touch1200 method

Double reset is hard to achieve on MIMXRT boards, since there is no clean
reset pin.  Some MIMXRT boards provide it by switching the power.

Some boards are excluded from the .uf2 build:

- MIMXRT1050_EVK: The uf2 bootloader is built for the QSPI version of the
  board.  MicroPython supports the Hyperflash version.
- MIMXRT1176_EVK: No support for this board yet, but it should be possible.

Signed-off-by: robert-hh <robert@hammelrath.com>
This commit is contained in:
robert-hh
2024-10-03 09:07:48 +02:00
committed by Damien George
parent b2ce9b6fb0
commit e574f68820
22 changed files with 87 additions and 20 deletions

View File

@@ -21,6 +21,7 @@ BUILD ?= build-$(BOARD)
PORT ?= /dev/ttyACM0
CROSS_COMPILE ?= arm-none-eabi-
GIT_SUBMODULES += lib/tinyusb lib/nxp_driver
UF2CONV ?= $(TOP)/tools/uf2conv.py
# MicroPython feature configurations
MICROPY_VFS_LFS2 ?= 1
@@ -162,6 +163,13 @@ SRC_HAL_IMX_C += \
$(MCU_DIR)/drivers/fsl_romapi.c
endif
# If not empty, then it is 10xx.
ifneq ($(findstring MIMXRT10, $(MCU_SERIES)),)
APPLICATION_ADDR := 0x6000C000
else
APPLICATION_ADDR := 0x3000C000
endif
ifeq ($(MCU_SERIES), MIMXRT1176)
INC += -I$(TOP)/$(MCU_DIR)/drivers/cm7
@@ -253,6 +261,11 @@ else
$(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE))
endif
# Set a flag if the UF2 bootloader is used
ifeq ($(USE_UF2_BOOTLOADER),1)
CFLAGS += -DMICROPY_MACHINE_UF2_BOOTLOADER=1
endif
# Add sources for respective board flash type
# Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively
SRC_HAL_C += hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).c
@@ -470,7 +483,11 @@ $(BUILD)/lib/tinyusb/src/device/usbd.o: CFLAGS += -Wno-missing-braces
# Build targets
# =============================================================================
ifeq ($(USE_UF2_BOOTLOADER),1)
all: $(BUILD)/firmware.hex $(BUILD)/firmware.bin $(BUILD)/firmware.uf2
else
all: $(BUILD)/firmware.hex $(BUILD)/firmware.bin
endif
# Process linker scripts with C preprocessor to exchange LDDEFINES and
# aggregate output of preprocessor in a single linker script `link.ld`
@@ -487,6 +504,10 @@ $(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
$(Q)$(OBJCOPY) -O ihex -R .eeprom $< $@
$(BUILD)/firmware.uf2: $(BUILD)/firmware.elf
$(Q)$(OBJCOPY) -O binary -R .stack -R .ivt -R .flash_config $^ $@-binpart
$(Q)$(PYTHON) $(UF2CONV) -b $(APPLICATION_ADDR) -f MIMXRT10XX -c -o $@ $@-binpart
# Making OBJ use an order-only dependency on the generated pins.h file
# has the side effect of making the pins.h file before we actually compile
# any of the objects. The normal dependency generation will deal with the

View File

@@ -8,3 +8,5 @@ MICROPY_HW_FLASH_SIZE ?= 0x800000 # 8MB
MICROPY_PY_NETWORK_NINAW10 ?= 1
MICROPY_PY_SSL ?= 1
MICROPY_SSL_MBEDTLS ?= 1
USE_UF2_BOOTLOADER = 1

View File

@@ -5,6 +5,8 @@ MICROPY_FLOAT_IMPL = single
MICROPY_HW_FLASH_TYPE = qspi_nor_flash
MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB
USE_UF2_BOOTLOADER = 1
JLINK_PATH ?= /media/RT1010-EVK/
JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink

View File

@@ -14,9 +14,9 @@ flash_config_start = flash_start + 0x00000400;
flash_config_size = 0x00000C00;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -14,9 +14,9 @@ flash_config_start = flash_start;
flash_config_size = 0x00001000;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -6,3 +6,5 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash
MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB
MICROPY_BOOT_BUFFER_SIZE = (32 * 1024)
USE_UF2_BOOTLOADER = 1

View File

@@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
USE_UF2_BOOTLOADER = 1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
JLINK_PATH ?= /media/RT1020-EVK/

View File

@@ -14,9 +14,9 @@ flash_config_start = flash_start;
flash_config_size = 0x00001000;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -16,9 +16,9 @@ flash_config_start = flash_start;
flash_config_size = 0x00001000;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00200000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
USE_UF2_BOOTLOADER = 1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
JLINK_PATH ?= /media/RT1060-EVK/

View File

@@ -16,9 +16,9 @@ flash_config_start = flash_start;
flash_config_size = 0x00001000;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -10,9 +10,9 @@ flash_config_start = flash_start;
flash_config_size = 0x00001000;
ivt_start = flash_start + 0x00001000;
ivt_size = 0x00001000;
interrupts_start = flash_start + 0x00002000;
interrupts_start = flash_start + 0x0000C000;
interrupts_size = 0x00000400;
text_start = flash_start + 0x00002400;
text_start = flash_start + 0x0000C400;
vfs_start = flash_start + 0x00100000;
text_size = ((vfs_start) - (text_start));
vfs_size = ((flash_end) - (vfs_start));

View File

@@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
USE_UF2_BOOTLOADER = 1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
JLINK_PATH ?= /media/RT1064-EVK/

View File

@@ -6,6 +6,8 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash
MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB
MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB
USE_UF2_BOOTLOADER = 1
CFLAGS += -DMICROPY_HW_FLASH_DQS=kFlexSPIReadSampleClk_LoopbackInternally
SRC_C += \

View File

@@ -12,6 +12,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
USE_UF2_BOOTLOADER = 1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
CFLAGS += -DSPI_RETRY_TIMES=1000000

View File

@@ -6,5 +6,7 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash
MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB
MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB
USE_UF2_BOOTLOADER = 1
deploy: $(BUILD)/firmware.hex
teensy_loader_cli --mcu=imxrt1062 -v -w $<

View File

@@ -10,6 +10,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
USE_UF2_BOOTLOADER = 1
FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
deploy: $(BUILD)/firmware.hex

View File

@@ -42,7 +42,8 @@ MEMORY
m_vfs (RX) : ORIGIN = vfs_start, LENGTH = vfs_size
/* Teensy uses the last bit of flash for recovery. */
m_reserved (RX) : ORIGIN = (vfs_start + vfs_size), LENGTH = reserved_size
m_itcm (RX) : ORIGIN = itcm_start, LENGTH = itcm_size
m_isr (RX) : ORIGIN = itcm_start, LENGTH = 0x400
m_itcm (RX) : ORIGIN = itcm_start + 0x400, LENGTH = itcm_size - 0x400
m_dtcm (RW) : ORIGIN = dtcm_start, LENGTH = dtcm_size
m_ocrm (RW) : ORIGIN = ocrm_start, LENGTH = ocrm_size
@@ -80,7 +81,8 @@ SECTIONS
. = ALIGN(4);
} > m_ivt
/* The startup code goes first into internal RAM */
/* ISR Vector table in flash. Copied to ITCM by ResetHandler(). */
.interrupts :
{
__Vectors = .;
@@ -90,10 +92,9 @@ SECTIONS
. = ALIGN(4);
} > m_interrupts
__VECTOR_RAM = __Vectors;
__RAM_VECTOR_TABLE_SIZE_BYTES = 0x0;
__Vectors_RAM = ORIGIN(m_isr);
/* The program code and other data goes into internal RAM */
/* Some program code and other data goes into internal RAM */
.text :
{
. = ALIGN(4);

View File

@@ -64,6 +64,19 @@ Reset_Handler:
* __ram_function_start__/__ram_function_end__ : ramfunction region
* copied to. Both must be aligned to 4 bytes boundary. */
/* Copy the ISR Vector table to the start of ITCM to be available when the
.uf2 bootloader is used */
ldr r1, = __Vectors
ldr r2, = __Vectors_RAM
movs r3, 1024
.LC_ISR:
subs r3, #4
ldr r0, [r1, r3]
str r0, [r2, r3]
bgt .LC_ISR
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__data_end__

View File

@@ -76,6 +76,11 @@ typedef enum {
MP_SOFT_RESET
} reset_reason_t;
// Copied from inc/uf2.h in https://github.com/Microsoft/uf2-samd21
#define DBL_TAP_REG SNVS->LPGPR[3]
#define DBL_TAP_MAGIC 0xf01669ef // Randomly selected, adjusted to have first and last bit set
#define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef
static mp_obj_t mp_machine_unique_id(void) {
unsigned char id[8];
mp_hal_get_unique_id(id);
@@ -158,12 +163,17 @@ NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
// If a board has a custom bootloader, call it first.
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
#elif FSL_ROM_HAS_RUNBOOTLOADER_API
#elif FSL_ROM_HAS_RUNBOOTLOADER_API && !MICROPY_MACHINE_UF2_BOOTLOADER
// If not, enter ROM bootloader in serial downloader / USB mode.
// Skip that in case of the UF2 bootloader being available.
uint32_t arg = 0xEB110000;
ROM_RunBootloader(&arg);
#else
// No custom bootloader, or run bootloader API, then just reset.
// No custom bootloader, or run bootloader API, the set
// the flag for the UF2 bootloader
// Pretend to be the first of the two reset presses needed to enter the
// bootloader. That way one reset will end in the bootloader.
DBL_TAP_REG = DBL_TAP_MAGIC;
WDOG_TriggerSystemSoftwareReset(WDOG1);
#endif
while (1) {

View File

@@ -115,6 +115,7 @@ uint32_t trng_random_u32(void);
#define MICROPY_PY_MACHINE_UART_SENDBREAK (1)
#define MICROPY_PY_MACHINE_UART_IRQ (1)
#define MICROPY_PY_ONEWIRE (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
// fatfs configuration used in ffconf.h
#define MICROPY_FATFS_ENABLE_LFN (2)
@@ -151,6 +152,7 @@ uint32_t trng_random_u32(void);
#define MICROPY_HW_ENABLE_USBDEV (1)
#define MICROPY_HW_USB_CDC (1)
#define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1)
// Hooks to add builtins

View File

@@ -99,7 +99,7 @@ function build_esp8266_boards {
}
function build_mimxrt_boards {
build_boards modmimxrt.c $1 $2 bin hex
build_boards modmimxrt.c $1 $2 bin hex uf2
}
function build_nrf_boards {