diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 7ae3f918371b3..e4bafdfedef00 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -4,6 +4,7 @@ BOARD ?= TEENSY40 BOARD_DIR ?= boards/$(BOARD) +BOOTLOADER_DIR ?= mboot BUILD ?= build-$(BOARD) PORT ?= /dev/ttyACM0 CROSS_COMPILE ?= arm-none-eabi- @@ -39,24 +40,28 @@ include $(TOP)/extmod/extmod.mk MCU_DIR = lib/nxp_driver/sdk/devices/$(MCU_SERIES) # Select linker scripts based on MCU_SERIES -LD_FILES = boards/$(MCU_SERIES).ld boards/common.ld +LD_FILES = boards/common.ld # Parameter configurations for generation AF_FILE = boards/$(MCU_SERIES)_af.csv BOARD_PINS = $(BOARD_DIR)/pins.csv PREFIX_FILE = boards/mimxrt_prefix.c +PATCH_BINARY = mboot/tools/patch-binary.py GEN_FLEXRAM_CONFIG_SRC = $(BUILD)/flexram_config.s GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h GEN_PINS_AF_PY = $(BUILD)/pins_af.py GEN_PINS_HDR = $(HEADER_BUILD)/pins.h GEN_PINS_QSTR = $(BUILD)/pins_qstr.h GEN_PINS_SRC = $(BUILD)/pins_gen.c +# Include mboot make definitions +include mboot.mk # ============================================================================= # Includes # ============================================================================= INC += -I$(BOARD_DIR) +INC += -I$(BOOTLOADER_DIR)/shared INC += -I$(BUILD) INC += -I$(TOP) INC += -I$(TOP)/$(MCU_DIR) @@ -147,6 +152,9 @@ endif # C source files SRC_C += \ + $(SRC_ETH_C) \ + $(SRC_HAL_IMX_C) \ + $(SRC_TINYUSB_C) \ board_init.c \ boards/$(MCU_SERIES)_clock_config.c \ dma_manager.c \ @@ -158,6 +166,7 @@ SRC_C += \ extmod/modusocket.c \ extmod/uos_dupterm.c \ fatfs_port.c \ + flash.c \ hal/pwm_backport.c \ led.c \ machine_adc.c \ @@ -200,10 +209,12 @@ SRC_C += \ shared/timeutils/timeutils.c \ systick.c \ ticks.c \ - tusb_port.c \ - $(SRC_TINYUSB_C) \ - $(SRC_HAL_IMX_C) \ - $(SRC_ETH_C) + tusb_port.c + +# Set default values if optional variables not defined +ifndef MICROPY_HW_BOARD_FLASH_FILES + MICROPY_HW_BOARD_FLASH_FILES = 0 +endif # Add sources for respective board flash type ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash qspi_hyper_flash)) @@ -220,6 +231,15 @@ else $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) endif + +# Set default values if optional variables not defined +ifndef SUPPORTS_HARDWARE_FP_DOUBLE + SUPPORTS_HARDWARE_FP_DOUBLE = 0 +endif +ifndef SUPPORTS_HARDWARE_FP_SINGLE + SUPPORTS_HARDWARE_FP_SINGLE = 0 +endif + # Math library source files ifeq ($(MICROPY_FLOAT_IMPL),double) LIBM_SRC_C += $(addprefix lib/libm_dbl/,\ @@ -245,10 +265,6 @@ else endif endif -# Reset variables -SUPPORTS_HARDWARE_FP_SINGLE = 0 -SUPPORTS_HARDWARE_FP_DOUBLE = 0 - # Assembly source files SRC_SS = \ $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S \ @@ -404,7 +420,20 @@ LDFLAGS = \ LDDEFINES = \ -DMICROPY_HW_FLASH_TYPE=$(MICROPY_HW_FLASH_TYPE) \ - -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) + -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ + -DBOARD_LINKER_SCRIPT_H=\"$(MCU_SERIES).ld\" \ + -DBOOTLOADER_ENABLED=1 + +# Start of firmware is dependent on board flash type. Bootloader requires at least 32kB. +# Firmware start has to be aligned to sector size to allow erase of firmware without affecting the bootloader. +# Sector size is different between QPSI flash (4kB) and HyperFlash (256kB) devices! +ifeq ($(MICROPY_HW_FLASH_TYPE),qspi_nor_flash) + LDDEFINES += -DMICROPY_HW_FLASH_FIRMWARE_START_ADDR=0x00008000 +else ifeq ($(MICROPY_HW_FLASH_TYPE),qspi_hyper_flash) + LDDEFINES += -DMICROPY_HW_FLASH_FIRMWARE_START_ADDR=0x00040000 +else + $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) +endif ifdef MICROPY_HW_FLASH_RESERVED LDDEFINES += -DMICROPY_HW_FLASH_RESERVED=$(MICROPY_HW_FLASH_RESERVED) @@ -453,10 +482,23 @@ $(BUILD)/firmware.elf: $(OBJ) $(Q)$(CC) -E -x c $(LDDEFINES) $(LD_FILES) | grep -v '^#' > $(BUILD)/link.ld $(ECHO) "LINK $@" $(Q)$(LD) -T$(BUILD)/link.ld $(LDFLAGS) -o $@ $^ $(LIBS) + $(ECHO) "" + $(ECHO) "UPDATE SECTION .fw_header" + $(Q)$(OBJCOPY) -O binary $@ $(BUILD)/temp_firmware.bin + $(Q)$(PYTHON) $(PATCH_BINARY) $(BUILD)/temp_firmware.bin $(BUILD)/fw_header.bin 1024 $$(($(MICROPY_BOOT_BUFFER_SIZE))) + $(Q)$(OBJCOPY) $@ --update-section .fw_header=$(BUILD)/fw_header.bin + $(ECHO) "$(BUILD)/fw_header.bin ==> .fw_header" + $(ECHO) "" + $(ECHO) "UPDATE SECTION .mboot" + $(Q)$(OBJCOPY) $@ --update-section .mboot=$(BUILD_BL)/mboot.bin + $(ECHO) "$(BUILD_BL)/mboot.bin ==> .mboot" + $(ECHO) "" + $(ECHO) "OUTPUT INFO" $(Q)$(SIZE) $@ + $(ECHO) "" $(BUILD)/firmware.bin: $(BUILD)/firmware.elf - $(Q)$(OBJCOPY) -O binary $^ $@ + $(Q)$(OBJCOPY) -O binary $< $@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf $(Q)$(OBJCOPY) -O ihex -R .eeprom $< $@ @@ -466,7 +508,7 @@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf # any of the objects. The normal dependency generation will deal with the # case when pins.h is modified. But when it doesn't exist, we don't know # which source files might need it. -$(OBJ): | $(GEN_PINS_HDR) $(GEN_FLEXRAM_CONFIG_SRC) +$(OBJ): | $(BUILD_BL)/mboot.bin $(BUILD_BL)/mboot.hex $(GEN_PINS_HDR) $(GEN_FLEXRAM_CONFIG_SRC) # With conditional pins, we may need to regenerate qstrdefs.h when config # options change. @@ -477,7 +519,6 @@ $(GEN_FLEXRAM_CONFIG_SRC): $(Q)$(PYTHON) $(MAKE_FLEXRAM_LD) -d $(TOP)/$(MCU_DIR)/$(MCU_SERIES).h \ -f $(TOP)/$(MCU_DIR)/$(MCU_SERIES)_features.h -l boards/$(MCU_SERIES).ld -c $(MCU_SERIES) > $(GEN_FLEXRAM_CONFIG_SRC) - # Use a pattern rule here so that make will only call make-pins.py once to make # both pins_gen.c and pins.h $(BUILD)/%_gen.c $(HEADER_BUILD)/%.h: $(BOARD_PINS) $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index 12496890ac4a3..0406465b1970c 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -40,9 +40,10 @@ #include CLOCK_CONFIG_H #include "modmachine.h" - const uint8_t dcd_data[] = { 0x00 }; +void usb_phy0_init(uint8_t d_cal, uint8_t txcal45dp, uint8_t txcal45dn); + void board_init(void) { // Init clock BOARD_BootClockRUN(); @@ -59,32 +60,8 @@ void board_init(void) { // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); - // ------------- USB0 ------------- // - // Clock - CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, 480000000U); - CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, 480000000U); - - #ifdef USBPHY1 - USBPHY_Type *usb_phy = USBPHY1; - #else - USBPHY_Type *usb_phy = USBPHY; - #endif - - // Enable PHY support for Low speed device + LS via FS Hub - usb_phy->CTRL |= USBPHY_CTRL_SET_ENUTMILEVEL2_MASK | USBPHY_CTRL_SET_ENUTMILEVEL3_MASK; - - // Enable all power for normal operation - usb_phy->PWD = 0; - - // TX Timing - uint32_t phytx = usb_phy->TX; - phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK); - phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06); - usb_phy->TX = phytx; - - // USB1 - // CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, 480000000U); - // CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, 480000000U); + // USB0 + usb_phy0_init(0b0111, 0b0110, 0b0110); // Configure nominal values for D_CAL and TXCAL45DP/DN // ADC machine_adc_init(); @@ -104,6 +81,28 @@ void board_init(void) { machine_rtc_start(); } +void usb_phy0_init(uint8_t d_cal, uint8_t txcal45dp, uint8_t txcal45dn) { + #ifdef USBPHY1 + USBPHY_Type *usb_phy = USBPHY1; + #else + USBPHY_Type *usb_phy = USBPHY; + #endif + + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, BOARD_XTAL0_CLK_HZ); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, BOARD_XTAL0_CLK_HZ); + + #if defined(CPU_MIMXRT1176_cm7) + usb_phy->TRIM_OVERRIDE_EN = USBPHY_TRIM_OVERRIDE_EN_TRIM_DIV_SEL_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_ENV_TAIL_ADJ_VD_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_D_CAL_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_CAL45DP_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_CAL45DN_OVERRIDE(1); // Enable override for D_CAL and TXCAL45DP/DN + #endif + usb_phy->PWD = 0U; // Set all bits in PWD register to normal operation + usb_phy->TX = ((usb_phy->TX & (~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK))) | + (USBPHY_TX_D_CAL(d_cal) | USBPHY_TX_TXCAL45DP(txcal45dp) | USBPHY_TX_TXCAL45DM(txcal45dn))); // Configure values for D_CAL and TXCAL45DP/DN +} + void USB_OTG1_IRQHandler(void) { tud_int_handler(0); tud_task(); diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index 19db53c3f8710..50824a4a177f3 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -6,6 +6,8 @@ MICROPY_PY_MACHINE_SDCARD = 0 MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x1000000 # 16MB +MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) + JLINK_PATH ?= /media/RT1010-EVK/ JLINK_COMMANDER_SCRIPT = $(BUILD)/script.jlink diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 908eefffd643f..910014ec42cdf 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -4,22 +4,16 @@ reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else -#error Unknown MICROPY_HW_FLASH_TYPE + #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); + +/* MIMXRT1011 requires different offset for flash configuration */ flash_config_start = flash_start + 0x00000400; flash_config_size = 0x00000C00; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; -vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); + itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; @@ -29,12 +23,6 @@ ocrm_size = 0x00010000; /* 20kiB stack. */ __stack_size__ = 0x5000; -_estack = __StackTop; -_sstack = __StackLimit; /* Do not use the traditional C heap. */ __heap_size__ = 0; - -/* Use second OCRAM bank for GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld index 90336a2437127..02ef5d3426fe6 100644 --- a/ports/mimxrt/boards/MIMXRT1015.ld +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -1,25 +1,15 @@ /* Memory configuration */ -#if defined MICROPY_HW_FLASH_RESERVED +#if MICROPY_HW_FLASH_RESERVED reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else -#error Unknown MICROPY_HW_FLASH_TYPE + #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); -flash_config_start = flash_start; -flash_config_size = 0x00001000; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; -vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); + itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; @@ -29,10 +19,3 @@ ocrm_size = 0x00010000; /* 24kiB stack. */ __stack_size__ = 0x5000; -_estack = __StackTop; -_sstack = __StackLimit; - -/* Use second OCRAM bank for GC heap. */ -/* Use all OCRAM for the GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index e8922e4b7abfb..af868ff6d4fcc 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -9,6 +9,7 @@ MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld index bef0c13df5505..28153f76b1de0 100644 --- a/ports/mimxrt/boards/MIMXRT1021.ld +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -1,25 +1,15 @@ /* Memory configuration */ -#if defined MICROPY_HW_FLASH_RESERVED +#if MICROPY_HW_FLASH_RESERVED reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else -#error Unknown MICROPY_HW_FLASH_TYPE + #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); -flash_config_start = flash_start; -flash_config_size = 0x00001000; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; -vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); + itcm_start = 0x00000000; itcm_size = 0x00010000; dtcm_start = 0x20000000; @@ -34,15 +24,3 @@ sdram_size = MICROPY_HW_SDRAM_SIZE; /* 24kiB stack. */ __stack_size__ = 0x6000; -_estack = __StackTop; -_sstack = __StackLimit; - -#if MICROPY_HW_SDRAM_AVAIL -_gc_heap_start = ORIGIN(m_sdram); -_gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); -#else -/* Use second OCRAM bank for GC heap. */ -/* Use all OCRAM for the GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); -#endif diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 00843350494a7..6ca244d6818f0 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -9,6 +9,8 @@ MICROPY_HW_FLASH_SIZE = 0x4000000 # 64MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) + MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/MIMXRT1052.ld b/ports/mimxrt/boards/MIMXRT1052.ld index ca656711a5a77..e1e5525970e53 100644 --- a/ports/mimxrt/boards/MIMXRT1052.ld +++ b/ports/mimxrt/boards/MIMXRT1052.ld @@ -4,24 +4,16 @@ reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else -#error Unknown MICROPY_HW_FLASH_TYPE + #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); -flash_config_start = flash_start; -flash_config_size = 0x00001000; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; + vfs_start = flash_start + 0x00200000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; @@ -36,14 +28,3 @@ sdram_size = MICROPY_HW_SDRAM_SIZE; /* 24kiB stack. */ __stack_size__ = 0x6000; -_estack = __StackTop; -_sstack = __StackLimit; - -#if MICROPY_HW_SDRAM_AVAIL -_gc_heap_start = ORIGIN(m_sdram); -_gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); -#else -/* Use second OCRAM bank for GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); -#endif diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index 96afe276f706d..5a7cbd3407c35 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -9,6 +9,7 @@ MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/MIMXRT1062.ld b/ports/mimxrt/boards/MIMXRT1062.ld index 5b91550d97d60..36c30d1ad6d8f 100644 --- a/ports/mimxrt/boards/MIMXRT1062.ld +++ b/ports/mimxrt/boards/MIMXRT1062.ld @@ -4,24 +4,15 @@ reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); -flash_config_start = flash_start; -flash_config_size = 0x00001000; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; -vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); + itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; @@ -36,14 +27,3 @@ sdram_size = MICROPY_HW_SDRAM_SIZE; /* 32kiB stack. */ __stack_size__ = 0x8000; -_estack = __StackTop; -_sstack = __StackLimit; - -#if MICROPY_HW_SDRAM_AVAIL -_gc_heap_start = ORIGIN(m_sdram); -_gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); -#else -/* Use second OCRAM bank for GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); -#endif diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 1fbc855fa13f5..fd58e9515396e 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -4,26 +4,18 @@ reserved_size = MICROPY_HW_FLASH_RESERVED; #endif #if MICROPY_HW_FLASH_TYPE == qspi_nor_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash -flash_start = 0x60000000; + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; #elif MICROPY_HW_FLASH_TYPE == internal -flash_start = 0x70000000; + flash_start = 0x70000000; + flash_size = MICROPY_HW_FLASH_SIZE; #else -#error Unknown MICROPY_HW_FLASH_TYPE + #error Unknown MICROPY_HW_FLASH_TYPE #endif -flash_size = MICROPY_HW_FLASH_SIZE; -flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); -flash_config_start = flash_start; -flash_config_size = 0x00001000; -ivt_start = flash_start + 0x00001000; -ivt_size = 0x00001000; -interrupts_start = flash_start + 0x00002000; -interrupts_size = 0x00000400; -text_start = flash_start + 0x00002400; -vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); -vfs_size = ((flash_end) - (vfs_start)); + itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; @@ -38,14 +30,3 @@ sdram_size = MICROPY_HW_SDRAM_SIZE; /* 24kiB stack. */ __stack_size__ = 0x6000; -_estack = __StackTop; -_sstack = __StackLimit; - -#if MICROPY_HW_SDRAM_AVAIL -_gc_heap_start = ORIGIN(m_sdram); -_gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); -#else -/* Use second OCRAM bank for GC heap. */ -_gc_heap_start = ORIGIN(m_ocrm); -_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); -#endif diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index ceb1e9796e01b..78af44a6733f1 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -9,6 +9,7 @@ MICROPY_HW_FLASH_SIZE = 0x4000000 # 64MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk index 135c43257c95e..8eb464b19ae8f 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk @@ -7,6 +7,8 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_BOOT_BUFFER_SIZE = (32 * 1024) + CFLAGS += -DMICROPY_HW_FLASH_DQS=kFlexSPIReadSampleClk_LoopbackInternally SRC_C += \ diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index 2312d11311e50..695f815156470 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -9,6 +9,7 @@ MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_SDRAM_AVAIL = 1 MICROPY_HW_SDRAM_SIZE = 0x2000000 # 32MB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 4482c629f7dcc..98cb385140c57 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -7,5 +7,7 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x200000 # 2MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) + deploy: $(BUILD)/firmware.hex teensy_loader_cli --mcu=imxrt1062 -v -w $< diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index 601a2cfe8606c..62b1ebf2b142d 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -7,6 +7,7 @@ MICROPY_HW_FLASH_TYPE = qspi_nor_flash MICROPY_HW_FLASH_SIZE = 0x800000 # 8MB MICROPY_HW_FLASH_RESERVED ?= 0x1000 # 4KB +MICROPY_BOOT_BUFFER_SIZE = (64 * 1024) MICROPY_PY_LWIP = 1 MICROPY_PY_USSL = 1 MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/mimxrt/boards/common.ld b/ports/mimxrt/boards/common.ld index fbc99da3ddb0e..d3095b7190f41 100644 --- a/ports/mimxrt/boards/common.ld +++ b/ports/mimxrt/boards/common.ld @@ -1,7 +1,7 @@ /* ** ################################################################### ** Linker script inspired by NXP linker script for MIMXRT10xx -** +** ** Copyright for original linker script: ** Copyright 2016 Freescale Semiconductor, Inc. ** Copyright 2016-2018 NXP @@ -19,33 +19,87 @@ ** ################################################################### */ +#include BOARD_LINKER_SCRIPT_H + /* Entry Point */ ENTRY(Reset_Handler) + +#if defined MICROPY_HW_FLASH_RESERVED + /* Reserved Area + * Users can create a reserved area at the end of the flash memory via + * 'reserved_size' variable. The size of the reserved area should be a multiple + * of the sector size of the flash memory! + */ + reserved_size = MICROPY_HW_FLASH_RESERVED; + flash_end = flash_start + (flash_size - reserved_size); +#else + reserved_size = 0; + flash_end = flash_start + flash_size; +#endif + +/* Flash configuration is physically used only in non-bootloader builds. + * For bootloader builds this section will be NOLOAD since information is already present in flash memory through bootloader binary. + */ +flash_config_start = DEFINED(flash_config_start) ? flash_config_start : flash_start; +flash_config_size = DEFINED(flash_config_size) ? flash_config_size : 0x00001000; + +mboot_start = flash_config_start; +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + mboot_size = MICROPY_HW_FLASH_FIRMWARE_START_ADDR - (flash_config_start - flash_start); +#elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash + mboot_size = MICROPY_HW_FLASH_FIRMWARE_START_ADDR - (flash_config_start - flash_start); +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif +firmware_start = mboot_start + mboot_size; +firmware_size = flash_end - firmware_start; + +ivt_start = flash_config_start + flash_config_size; +ivt_size = 0x00001000; +fw_header_start = firmware_start; +fw_header_size = 0x00000400; +interrupts_start = (fw_header_start + fw_header_size); + +interrupts_size = 0x00000400; +text_start = (interrupts_start + interrupts_size); + +/* VFS starts at fixed offset of 0x0010'0000 from flash_start */ +vfs_start = DEFINED(vfs_start) ? vfs_start : flash_start + 0x00100000; +text_size = ((vfs_start) - (text_start)); +vfs_size = flash_end - vfs_start; + +_estack = __StackTop; +_sstack = __StackLimit; + +#if MICROPY_HW_SDRAM_AVAIL +_gc_heap_start = ORIGIN(m_sdram); +_gc_heap_end = ORIGIN(m_sdram) + LENGTH(m_sdram); +#else +/* Use second OCRAM bank for GC heap. */ +_gc_heap_start = ORIGIN(m_ocrm); +_gc_heap_end = ORIGIN(m_ocrm) + LENGTH(m_ocrm); +#endif + + HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400; STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400; -/* Reserved Area - * Users can create a reserved area at the end of the flash memory via - * 'reserved_size' variable. The size of the reserved area should be a multiple - * of the sector size of the flash memory! - */ -reserved_size = DEFINED(reserved_size) ? reserved_size : 0k; /* Specify the memory areas */ MEMORY { - m_flash_config (RX) : ORIGIN = flash_config_start, LENGTH = flash_config_size - m_ivt (RX) : ORIGIN = ivt_start, LENGTH = ivt_size + m_mboot (RX) : ORIGIN = flash_config_start, LENGTH = mboot_size + /* --- */ + m_fw_header (RX) : ORIGIN = fw_header_start, LENGTH = fw_header_size m_interrupts (RX) : ORIGIN = interrupts_start, LENGTH = interrupts_size m_text (RX) : ORIGIN = text_start, LENGTH = text_size 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_reserved (RX) : ORIGIN = (vfs_start + vfs_size), LENGTH = reserved_size m_itcm (RX) : ORIGIN = itcm_start, LENGTH = itcm_size m_dtcm (RW) : ORIGIN = dtcm_start, LENGTH = dtcm_size m_ocrm (RW) : ORIGIN = ocrm_start, LENGTH = ocrm_size - #if MICROPY_HW_SDRAM_AVAIL m_sdram (RX) : ORIGIN = sdram_start, LENGTH = sdram_size #endif @@ -55,30 +109,35 @@ MEMORY SECTIONS { __flash_start = flash_start; + __vfs_start = ORIGIN(m_vfs); + __vfs_end = __vfs_start + LENGTH(m_vfs); #if MICROPY_HW_SDRAM_AVAIL __sdram_start = sdram_start; #endif - __vfs_start = ORIGIN(m_vfs); - __vfs_end = __vfs_start + LENGTH(m_vfs); - - .flash_config : + + .mboot : { + FILL(0x00); . = ALIGN(4); __FLASH_BASE = .; KEEP(* (.boot_hdr.conf)) /* flash config section */ - . = ALIGN(4); - } > m_flash_config - - ivt_begin = ORIGIN(m_flash_config) + LENGTH(m_flash_config); - - .ivt : AT(ivt_begin) - { - . = ALIGN(4); + . = ORIGIN(m_mboot) + flash_config_size; KEEP(* (.boot_hdr.ivt)) /* ivt section */ KEEP(* (.boot_hdr.boot_data)) /* boot section */ KEEP(* (.boot_hdr.dcd_data)) /* dcd section */ + . = ORIGIN(m_mboot) + LENGTH(m_mboot) - 1; + BYTE(0x00) + } > m_mboot + + . = ORIGIN(m_fw_header); + .fw_header : + { + FILL(0x00); . = ALIGN(4); - } > m_ivt + KEEP(* (.fw_header)) + . = ORIGIN(m_fw_header) + LENGTH(m_fw_header) - 1; + BYTE(0x00) + } > m_fw_header /* The startup code goes first into internal RAM */ .interrupts : @@ -176,8 +235,17 @@ SECTIONS KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } > m_text - + __etext = .; /* define a global symbol at end of code */ + + .no_init : AT(__etext) + { + __no_init_ram_begin = .; + KEEP(*(._bl_command*)) + __no_init_ram_end = .; + } > m_dtcm + + . = __etext + (__no_init_ram_end - __no_init_ram_begin); __DATA_ROM = .; /* Symbol is used by startup for data initialization */ .data : AT(__DATA_ROM) @@ -205,7 +273,7 @@ SECTIONS *(.text*) . = ALIGN(4); __ram_function_end__ = .; - } > m_itcm + } > m_itcm __NDATA_ROM = __DATA_ROM + (__ram_function_end__ - __data_start__); .ncache.init : AT(__NDATA_ROM) diff --git a/ports/mimxrt/boards/make-pins_new.py b/ports/mimxrt/boards/make-pins_new.py new file mode 100644 index 0000000000000..88a3d5f72dcf3 --- /dev/null +++ b/ports/mimxrt/boards/make-pins_new.py @@ -0,0 +1,433 @@ +#!/usr/bin/env python +"""Creates the pin file for the MIMXRT10xx.""" + +import argparse +import csv +import re +import warnings + +""" +typedef struct gpio_mux_info_t_ { + uint16_t:1 available; # `1` if Pad supports peripheral function + uint16_t:5 mux_mode; # Alternate function index - e.g. 4 for AF4 + uint16_t:4 instance_idx; # Instance number - e.g. `1` for LPSPI1 + uint16_t:6 io; + uint16_t misc; + uint32_t mux_reg; + uint32_t config_reg; +} gpio_mux_info_t; + +typedef struct adc_mux_info_t_ { + uint32_t placeholder; +} adc_mux_info_t; + +typedef struct acmp_mux_info_t_ { + uint32_t placeholder; +} acmp_mux_info_t; + +// Todo: Make mux array dynamic dependent on actually available alternate functions of chip + +typedef struct pin_config_t_ { + gpio_mux_info_t gpio_mux[9]; + adc_mux_info_t adc_mux; + acmp_mux_info_t acmp_mux; +} +""" + +""" +ENET +FLEXIO +FLEXPWM +FLEXSPI +GPIO +GPT +KPP +LPSPI +LPUART +MQS +PIT +SAI +SPDIF +TMR +USB +--- +ADC +ACMP + +""" + +""" +- Erzeugen von separaten mux konfigurationen für ADC und ACMP + - vor allem bei ACMP muss die Kombination von ACMP_IN und ACMP_OUT auf einem GPIO berücksichtigt werden +- Zusammenführung von geparsten informaiton aus der iomux und dem csv. IM CSV müsste igentlich nur infos für ADC und ACMP stehen! +- Eventuell könnte mux und config reg auf weniger Speicherplatz getrimmt werden wenn: + - lookup bei wenigen basisadressen mit index + - offset in 16/8bit mit Speichern der Basis +- Speichern von inputRegister inputDaisy in der mux config // Ausgabe +- besser gekapselter pretty print +- zusammenfügen des parsens von af.csv und iomux um direkt "volle" Pin Objekte zu erzeugen +- mux array abhängig von gewünschten/unterstützten mux modes//AFs -> einbauen für code generierung +- prüfung einbauen ob gleiches peripheral bei einem gpio mehrer mux mides belegt (ACMP bekannte ausnahme die gesondert behandelt werden muss) +- herausfindne wie groß die pin datentypen derzeit sind +""" + + +class PinAf(object): + def __init__(self, **kwargs): + self.peripheral = kwargs.get("peripheral", "") + # mux_mode + mux_mode_temp = kwargs.get("muxMode", "") + mux_mode_temp = mux_mode_temp.strip("U") # Remove unsigned specifier + self.mux_mode = int(mux_mode_temp, base=16) # Create int from hex string + # + self.instance = kwargs.get("instance", "") + self.instance_number = kwargs.get("instance_number", "") + # + self.pin_function = kwargs.get("pin_function", "") + self.mux_register = kwargs.get("muxRegister", "") + self.input_register = kwargs.get("inputRegister", "") + # input_daisy + input_daisy_temp = kwargs.get("inputDaisy", "") + input_daisy_temp = input_daisy_temp.strip("U") # Remove unsigned specifier + self.input_daisy = int(input_daisy_temp, base=16) # Create int from hex string + # + self.config_register = kwargs.get("configRegister", "") + + def __repr__(self): + return f"[{self.mux_mode}] {self.peripheral:10} {self.pin_function}" + + +class PinNew(object): + def __init__(self, pad): + self.pad = pad + self.afs = dict() + + def add_af(self, af): + if af.peripheral in self.afs: + warnings.warn(f"{self.pad} already contains AF for {af.peripheral}!") + self.afs[af.peripheral] = af + + def __hash__(self): + return hash(self.pad) + + def __eq__(self, other): + if hash(other) == hash(self): + return True + else: + return False + + def __lt__(self, other): + return self.pad < other.pad + + def __repr__(self): + return self.pad + + +class Pin(object): + af_pattern = ( + r"(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*)" + ) + + def __init__(self, pad, afs, alt_name=""): + self.pad = pad + self.alt_name = alt_name + + # Split alternate function into: + # FLEXPWM2_PWM1_A + # |________|______| + # | |----> PWM1_A + # |------------> FLEXPWM2 + # |______|_| + # FLEXPWM <------| | + # 2 <----------------| + self.afs = dict() + pattern = re.compile(self.af_pattern) + # + for i, af in enumerate(afs): + match = pattern.match(af) + if match: + af_dict = match.groupdict() + af_dict["af_idx"] = i + self.afs[match.group("peripheral")] = af_dict + + def __hash__(self): + return hash(self.pad) + + def __eq__(self, other): + if hash(other) == hash(self): + return True + else: + return False + + def __lt__(self, other): + return self.pad < other.pad + + def __str__(self): + # Get GPIO info + gpio_af = self.afs["GPIO"] + return f"{self.pad} ({self.alt_name}) - {gpio_af['instance']} {gpio_af['pin_function']}" + + def __repr__(self): + return self.pad + + +def parse_mux_files(mux_file, user_mux_file): + pins_dict = dict() # {"pad": Pin} + + # Parse mux file and generate Pin objects from it + regexes = [ + r"IOMUXC_(?PGPIO_SD_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_EMC_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_SD_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + ] + + with open(mux_file, "r") as mux_file_: + input_str = mux_file_.read() + + # Create iomux configuration tree + # + # From: + # #define IOMUXC_GPIO_AD_B0_00_GPT1_COMPARE1 0x401F80BCU, 0x7U, 0, 0, 0x401F8230U + # #define IOMUXC_GPIO_EMC_41_SPDIF_IN 0x401F80B8U, 0x2U, 0x401F8488U, 0x1U, 0x401F822CU + # + # Generate: + # { + # "GPIO_AD_B0_00": { + # 'pad': 'GPIO_AD_B0_00', + # 'instance': 'GPT1', + # 'peripheral': 'GPT', + # 'instance_number': '1', + # 'pin_function': 'COMPARE1', + # 'muxRegister': '0x401F80BCU', + # 'muxMode': '0x7U', + # 'inputRegister': '0', + # 'inputDaisy': '0', + # 'configRegister': '0x401F8230U' + # } + # } + + for regex in regexes: + matches = re.finditer(regex, input_str, re.MULTILINE) + + for match in matches: + pin_af = PinAf(**match.groupdict()) + + if match.group("pad") not in pins_dict: + pins_dict[match.group("pad")] = PinNew(match.group("pad")) + + pins_dict[match.group("pad")].add_af(pin_af) + + print(len(pins_dict)) + print(pins_dict["GPIO_AD_B1_01"]) + for af in pins_dict["GPIO_AD_B1_01"].afs: + print("\t" + str(af)) + + # Parse user mux file and enricht pin objets with more information + # Return list of pins + pass + + +def parse_af_file(af_file, pad_col, af_start_col): + af_end_col = af_start_col + 12 + available_pins = list() + available_afs = set() + + with open(af_file, "r") as csvfile: + _available_pins = set() + rows = csv.reader(csvfile) + header = next(rows) + for row in rows: + _available_pins.add(Pin(row[pad_col], row[af_start_col:af_end_col])) + + # Parse alternate functions + for af_idx, af in enumerate(row[af_start_col:af_end_col]): + match = re.match(Pin.af_pattern, af) + if match: + available_afs.add(match.group("peripheral")) + available_pins = list(_available_pins) + available_pins.sort() + + return {available_pin.pad: available_pin for available_pin in available_pins}, list( + available_afs + ) + + +def parse_iomux_file(iomux_file): + iomux_pin_configs = dict() + regexes = [ + r"IOMUXC_(?PGPIO_SD_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_EMC_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_B\d_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_AD_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + r"IOMUXC_(?PGPIO_SD_\d\d)_(?P(?P[a-zA-Z]+)(?P\d?))\_?(?P\w*) (?P\w+), (?P\w+), (?P\w+), (?P\w+), (?P\w+)", + ] + + with open(iomux_file, "r") as iomuxfile: + input_str = iomuxfile.read() + + # Create iomux configuration tree + # + # From: + # #define IOMUXC_GPIO_AD_B0_00_GPT1_COMPARE1 0x401F80BCU, 0x7U, 0, 0, 0x401F8230U + # + # Generate: + # { + # "GPIO_AD_B0_00": { + # 'pad': 'GPIO_AD_B0_00', + # 'instance': 'GPT1', + # 'peripheral': 'GPT', + # 'instance_number': '1', + # 'pin_function': 'COMPARE1', + # 'muxRegister': '0x401F80BCU', + # 'muxMode': '0x7U', + # 'inputRegister': '0', + # 'inputDaisy': '0', + # 'configRegister': '0x401F8230U' + # } + # } + + for regex in regexes: + matches = re.finditer(regex, input_str, re.MULTILINE) + + for match in matches: + if match: + iomux_pin_configs[match.group("pad")] = match.groupdict() + return iomux_pin_configs + + +def parse_board_pins_file(board_file): + board_pins = dict() + with open(board_file, "r") as csvfile: + rows = csv.reader(csvfile) + for row in rows: + if len(row) == 0 or row[0].startswith("#"): + # Skip empty lines, and lines starting with "#" + continue + if len(row) != 2: + raise ValueError("Expecting two entries in a row") + + # Todo: Add error when pin is found multiple times + board_pins[row[1]] = row[0] + + # Return dict with {pad: alt_name} + return board_pins + + +def main(): + parser = argparse.ArgumentParser( + prog="make-pins.py", + usage="%(prog)s [options] [command]", + description="Generate board specific pin file", + ) + parser.add_argument( + "-a", + "--af", + dest="af_filename", + help="Specifies the alternate function file for the chip", + default="mimxrt1021_af.csv", + ) + parser.add_argument( + "-i", + "--iomux", + dest="iomux_filename", + help="Specifies the fsl_iomuxc.h file for the chip", + default="fsl_iomuxc.h", + ) + parser.add_argument( + "-b", + "--board", + dest="board_filename", + help="Specifies the board file", + default="MIMXRT1020_EVK/pins.csv", + ) + parser.add_argument( + "-p", + "--prefix", + dest="prefix_filename", + help="Specifies beginning portion of generated pins file", + default="mimxrt_prefix.c", + ) + parser.add_argument( + "-r", + "--hdr", + dest="hdr_filename", + help="Specifies name of generated pin header file", + default="build/pins.h", + ) + + # test code + args = parser.parse_args( + [ + "-a=/home/philipp/Projects/micropython/micropython/ports/mimxrt/boards/MIMXRT1064_af.csv", + "-i=/home/philipp/Projects/micropython/micropython/lib/nxp_driver/sdk/devices/MIMXRT1021/drivers/fsl_iomuxc.h", + ] + ) + # + + parse_mux_files(args.iomux_filename, args.af_filename) + + """ + available_pins, available_afs = parse_af_file(args.af_filename, 0, 1) + iomux_configs = parse_iomux_file(args.iomux_filename) + board_pins = parse_board_pins_file(args.board_filename) + # + available_afs = [ + "ENET", + "FLEXIO", + "FLEXPWM", + "FLEXSPI", + "GPIO", + "GPT", + "KPP", + "LPSPI", + "LPUART", + "MQS", + "PIT", + "SAI", + "SPDIF", + "TMR", + "USB", + "ADC", + ] + # + pins_to_generate = list() + for pad, alt_name in board_pins.items(): + pin = available_pins[pad] + pin.alt_name = alt_name + pins_to_generate.append(pin) + # + def print_pin_config(pin, indent="", end=","): + print(indent + "[" + pin.pad + "] " + "=" + " {") + for alt_fn in available_afs[:-1]: + if alt_fn in pin.afs: + print((indent * 2) + "[" + alt_fn + "]" + " = " + f"GPIO_MUX(1U, {pin.afs[alt_fn]['af_idx']}, {pin.afs[alt_fn]['instance_number']}, {pin.afs['GPIO']['pin_function'].lstrip('IO')}, 0U, {iomux_configs[pin.pad]['muxRegister']}, {iomux_configs[pin.pad]['configRegister']}), ") + else: + print((indent * 2) + "[" + alt_fn + "]" + " = " + f"GPIO_MUX(0U, 0U, 0U, 0U, 0U, 0U, 0U), ") + + if available_afs[-1] in pin.afs: + print((indent * 2) + "[" + available_afs[-1] + "]" + " = " + f"GPIO_MUX(1U, {pin.afs[available_afs[-1]]['af_idx']}, {pin.afs[available_afs[-1]]['instance_number']}, {pin.afs['GPIO']['pin_function'].lstrip('IO')}, 0U, {iomux_configs[pin.pad]['muxRegister']}, {iomux_configs[pin.pad]['configRegister']}), ") + else: + print((indent * 2) + "[" + available_afs[-1] + "]" + " = " + f"GPIO_MUX(0U, 0U, 0U, 0U, 0U, 0U, 0U), ") + + print(indent + "}" + end) + + print("pinconfigs = ") + print("{") + for pin in pins_to_generate[:-1]: + print_pin_config(pin, indent=" ") + print_pin_config(pins_to_generate[-1], indent=" ", end="") + print("};") + """ + + +# .available=1U, .mux_mode=10, .instance_idx=, .io=, .misc=, .mux_reg=, .config_reg= + + +if __name__ == "__main__": + main() diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c new file mode 100644 index 0000000000000..9fccacb583051 --- /dev/null +++ b/ports/mimxrt/flash.c @@ -0,0 +1,127 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "flash.h" + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ +void flash_init(void) { + // Upload the custom flash configuration + // This should be performed by the boot ROM but for some reason it is not. + FLEXSPI_UpdateLUT(FLEXSPI, 0, + qspiflash_config.memConfig.lookupTable, + ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); + + // Configure FLEXSPI IP FIFO access. + FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); + FLEXSPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); + FLEXSPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); + FLEXSPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); + + FLEXSPI_EnableIPParallelMode(FLEXSPI, true); +} + +// flash_erase_block(erase_addr) +// erases the block starting at addr. Block size according to the flash properties. +status_t flash_erase_block(uint32_t erase_addr) { + status_t status = kStatus_Fail; + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + __disable_irq(); + + status = flexspi_nor_flash_erase_block(FLEXSPI, erase_addr); + + __enable_irq(); + SCB_EnableDCache(); + + return status; +} + +// flash_erase_sector(erase_addr_bytes) +// erases the sector starting at addr. Sector size according to the flash properties. +status_t flash_erase_sector(uint32_t erase_addr) { + status_t status = kStatus_Fail; + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + __disable_irq(); + + status = flexspi_nor_flash_erase_sector(FLEXSPI, erase_addr); + + __enable_irq(); + SCB_EnableDCache(); + + return status; +} + +// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) +// writes length_byte data to the destination address +// the vfs driver takes care for erasing the sector if required +status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { + #if FLASH_DISABLE_OP == 1 + return kStatus_Success; + #else + status_t status = kStatus_Fail; + uint32_t size; + uint32_t next_addr; + + if (length == 0) { + status = kStatus_Success; // Nothing to do + } else { + + SCB_CleanInvalidateDCache(); + SCB_DisableDCache(); + + // write data in chunks not crossing a page boundary + while (length > 0) { + next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary + size = next_addr - dest_addr; // maximal chunk length + if (size > length) { // compare against remaining data size + size = length; + } + + __disable_irq(); + + status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr, (uint32_t *)src, size); + + __enable_irq(); + + if (status != kStatus_Success) { + break; + } + length -= size; + src += size; + dest_addr += size; + } + + SCB_EnableDCache(); + + } + return status; + #endif +} diff --git a/ports/mimxrt/flash.h b/ports/mimxrt/flash.h new file mode 100644 index 0000000000000..65e4f6085c4e8 --- /dev/null +++ b/ports/mimxrt/flash.h @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_FLASH_H +#define MICROPY_INCLUDED_MIMXRT_FLASH_H + +#include BOARD_FLASH_OPS_HEADER_H + +// --------------------------------------------------------------------+ +// Defines and Macros +// --------------------------------------------------------------------+ +#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) +#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) +#define BLOCK_SIZE_BYTES (qspiflash_config.blockSize) + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +void flash_init(void); +__attribute__((section(".ram_functions"))) status_t flash_erase_sector(uint32_t erase_addr); +__attribute__((section(".ram_functions"))) status_t flash_erase_block(uint32_t erase_addr); +__attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length); + +#endif // MICROPY_INCLUDED_MIMXRT_FLASH_H diff --git a/ports/mimxrt/hal/flexspi_flash_config.h b/ports/mimxrt/hal/flexspi_flash_config.h index 3c21eb609a77c..0198d8d26ccce 100644 --- a/ports/mimxrt/hal/flexspi_flash_config.h +++ b/ports/mimxrt/hal/flexspi_flash_config.h @@ -222,6 +222,7 @@ typedef struct _FlexSPIConfig #define NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10 #define NOR_CMD_LUT_SEQ_IDX_CHIPERASE 11 #define NOR_CMD_LUT_SEQ_IDX_EXITQPI 12 +#define NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK 13 #define HYPERFLASH_CMD_LUT_SEQ_IDX_READDATA 0 #define HYPERFLASH_CMD_LUT_SEQ_IDX_WRITEDATA 1 diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.c b/ports/mimxrt/hal/flexspi_hyper_flash.c index c7b41658a4988..20c7fb0efec43 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.c +++ b/ports/mimxrt/hal/flexspi_hyper_flash.c @@ -175,6 +175,11 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { return status; } +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) { + return flexspi_nor_flash_erase_sector(base, address); // HyperFlash does not support block erase! +} + status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size) { status_t status; diff --git a/ports/mimxrt/hal/flexspi_hyper_flash.h b/ports/mimxrt/hal/flexspi_hyper_flash.h index dbd028fd6f718..b40fe8d9fe506 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -36,6 +36,7 @@ extern flexspi_nor_config_t qspiflash_config; status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base); void flexspi_nor_update_lut(void); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); static inline uint32_t flexspi_get_frequency(void) { diff --git a/ports/mimxrt/hal/flexspi_nor_flash.c b/ports/mimxrt/hal/flexspi_nor_flash.c index 8c04150d12738..556ad3c72c852 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.c +++ b/ports/mimxrt/hal/flexspi_nor_flash.c @@ -165,6 +165,37 @@ status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address) { return status; } +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) __attribute__((section(".ram_functions"))); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address) { + status_t status; + flexspi_transfer_t flashXfer; + + /* Write enable */ + status = flexspi_nor_write_enable(base, address); + + if (status != kStatus_Success) { + return status; + } + + /* Erase sector */ + flashXfer.deviceAddress = address; + flashXfer.port = kFLEXSPI_PortA1; + flashXfer.cmdType = kFLEXSPI_Command; + flashXfer.SeqNumber = 1; + flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_ERASEBLOCK; + status = FLEXSPI_TransferBlocking(base, &flashXfer); + + if (status != kStatus_Success) { + return status; + } + + status = flexspi_nor_wait_bus_busy(base); + + flexspi_nor_reset(base); + + return status; +} + status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) __attribute__((section(".ram_functions"))); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src, uint32_t size) { status_t status; diff --git a/ports/mimxrt/hal/flexspi_nor_flash.h b/ports/mimxrt/hal/flexspi_nor_flash.h index f8c31488a9883..d4f8445b66469 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -36,6 +36,7 @@ extern flexspi_nor_config_t qspiflash_config; status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); void flexspi_nor_update_lut(void); status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address); +status_t flexspi_nor_flash_erase_block(FLEXSPI_Type *base, uint32_t address); status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t address, const uint32_t *src, uint32_t size); static inline uint32_t flexspi_get_frequency(void) { diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index 469a584b877a4..a6bbd624ed534 100644 --- a/ports/mimxrt/hal/qspi_nor_flash_config.c +++ b/ports/mimxrt/hal/qspi_nor_flash_config.c @@ -129,11 +129,17 @@ const flexspi_nor_config_t qspiflash_config = { FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + + // 13 Erase Block (32k) -> 13 + FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x52, RADDR_SDR, FLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler + FLEXSPI_LUT_SEQ(0, 0, 0, 0, 0, 0), // Filler }, }, .pageSize = 256u, .sectorSize = 4u * 1024u, - .blockSize = 64u * 1024u, + .blockSize = 32u * 1024u, .isUniformBlockSize = false, // .ipcmdSerialClkFreq = kFlexSpiSerialClk_30MHz, }; diff --git a/ports/mimxrt/hal/resethandler_MIMXRT10xx.S b/ports/mimxrt/hal/resethandler_MIMXRT10xx.S index 8fe061103388b..a0ad99fac97a2 100644 --- a/ports/mimxrt/hal/resethandler_MIMXRT10xx.S +++ b/ports/mimxrt/hal/resethandler_MIMXRT10xx.S @@ -51,13 +51,13 @@ Reset_Handler: /* Loop to copy data from read only memory to RAM. The ranges * of copy from/to are specified by following symbols evaluated in * linker script. - * __etext: End of code section, i.e., begin of data sections to copy from. + * __DATA_ROM: End of code section, i.e., begin of data sections to copy from. * __data_start__/__data_end__: RAM address range that data should be * __noncachedata_start__/__noncachedata_end__ : none cachable region * __ram_function_start__/__ram_function_end__ : ramfunction region * copied to. Both must be aligned to 4 bytes boundary. */ - ldr r1, =__etext + ldr r1, =__DATA_ROM ldr r2, =__data_start__ ldr r3, =__data_end__ diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index 82e07868a1d41..87f21f9c68ed3 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -38,6 +38,7 @@ #include "led.h" #include "pendsv.h" #include "modmachine.h" +#include "fw_header.h" #if MICROPY_PY_LWIP #include "lwip/init.h" @@ -48,6 +49,13 @@ extern uint8_t _sstack, _estack, _gc_heap_start, _gc_heap_end; +extern void Reset_Handler(void); + +__attribute__((section(".fw_header"))) const fw_header_t fw_header = { + .magic = FW_HEADER_MAGIC, + .entry_addr = (uintptr_t)Reset_Handler, +}; + void board_init(void); int main(void) { diff --git a/ports/mimxrt/mboot.mk b/ports/mimxrt/mboot.mk new file mode 100644 index 0000000000000..40d2d55a43152 --- /dev/null +++ b/ports/mimxrt/mboot.mk @@ -0,0 +1,246 @@ +BUILD_BL ?= build-$(BOARD)/mboot +LD_FILES_BL = $(BOOTLOADER_DIR)/boards/common.ld + +INC_BL += \ + -I$(BOARD_DIR) \ + -I$(BOOTLOADER_DIR) \ + -I$(BOOTLOADER_DIR)/boards \ + -I$(BOOTLOADER_DIR)/hal \ + -I$(BOOTLOADER_DIR)/shared \ + -I$(TOP)/$(MCU_DIR) \ + -I$(TOP)/$(MCU_DIR)/drivers \ + -I$(TOP)/lib/cmsis/inc \ + -I$(TOP)/lib/tinyusb/hw \ + -I$(TOP)/lib/tinyusb/src \ + -I. \ + -Ihal + +SRC_HAL_IMX_C_BL += \ + $(MCU_DIR)/drivers/fsl_cache.c \ + $(MCU_DIR)/drivers/fsl_clock.c \ + $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_flexram.c \ + $(MCU_DIR)/drivers/fsl_flexspi.c \ + $(MCU_DIR)/drivers/fsl_gpt.c \ + $(MCU_DIR)/system_$(MCU_SERIES).c + +SRC_TINYUSB_C_BL += \ + lib/tinyusb/src/class/dfu/dfu_device.c \ + lib/tinyusb/src/common/tusb_fifo.c \ + lib/tinyusb/src/device/usbd.c \ + lib/tinyusb/src/device/usbd_control.c \ + lib/tinyusb/src/portable/nxp/transdimension/dcd_transdimension.c \ + lib/tinyusb/src/tusb.c + +SRC_C_BL += \ + $(BOOTLOADER_DIR)/boards/flexspi_nor_mboot.c \ + $(BOOTLOADER_DIR)/main.c \ + $(BOOTLOADER_DIR)/mboot_buffer.c \ + $(BOOTLOADER_DIR)/mboot_dfu.c \ + $(BOOTLOADER_DIR)/mboot_upgrade.c \ + $(BOOTLOADER_DIR)/mboot_utils.c \ + $(BOOTLOADER_DIR)/mboot_validate.c \ + $(BOOTLOADER_DIR)/tusb_port.c \ + $(SRC_HAL_IMX_C_BL) \ + $(SRC_TINYUSB_C_BL) \ + boards/$(MCU_SERIES)_clock_config.c \ + flash.c + +# Set default values if optional variables not defined +ifndef MICROPY_HW_BOARD_FLASH_FILES + MICROPY_HW_BOARD_FLASH_FILES = 0 +endif + +# Add sources for respective board flash type +ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash qspi_hyper_flash)) + # Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively + SRC_C_BL += hal/flexspi_$(subst qspi_,,$(MICROPY_HW_FLASH_TYPE)).c + # + # Add custom (board specific) or default configuration + ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) + SRC_C_BL += $(BOARD_DIR)/$(MICROPY_HW_FLASH_TYPE)_config.c + else + SRC_C_BL += hal/$(MICROPY_HW_FLASH_TYPE)_config.c + endif +else + $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) +endif + +SRC_SS_BL = \ + $(MCU_DIR)/gcc/startup_$(MCU_SERIES).S \ + $(BOOTLOADER_DIR)/hal/resethandler_mboot_MIMXRT10xx_RAM.S + +# Flags +CFLAGS_DEFINES_BL += \ + -DMICROPY_BOOT_BUFFER_SIZE='$(MICROPY_BOOT_BUFFER_SIZE)' + +# Tune for Debugging or Optimization +ifeq ($(DEBUG),1) +CFLAGS_DEBUG_BL += -Og -ggdb -DNDEBUG +else +CFLAGS_DEBUG_BL += -Os -DNDEBUG +endif + +CFLAGS_BL += \ + $(CFLAGS_DEBUG_BL) \ + $(CFLAGS_DEFINES_BL) \ + $(INC_BL) \ + -D__START=main \ + -D__STARTUP_CLEAR_BSS \ + -D__STARTUP_INITIALIZE_RAMFUNCTION \ + -DBOARD_$(BOARD) \ + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX \ + -DCLOCK_CONFIG_H='' \ + -DCPU_$(MCU_SERIES) \ + -DCPU_$(MCU_VARIANT) \ + -DCPU_HEADER_H='<$(MCU_SERIES).h>' \ + -DFSL_SDK_ENABLE_DRIVER_CACHE_CONTROL=1 \ + -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ + -DXIP_BOOT_HEADER_ENABLE=1 \ + -DXIP_EXTERNAL_FLASH=1 \ + -fdata-sections \ + -ffunction-sections \ + -mcpu=cortex-m7 \ + -mtune=cortex-m7 \ + -nostdlib \ + -std=c99 \ + -Wall \ + -Werror \ + -Wdouble-promotion \ + -Werror \ + -Wfloat-conversion \ + -Wno-error=unused-parameter + +# Configure respective board flash type +ifeq ($(MICROPY_HW_FLASH_TYPE),$(filter $(MICROPY_HW_FLASH_TYPE),qspi_nor_flash qspi_hyper_flash)) + # Add hal/flexspi_nor_flash.h or hal/flexspi_hyper_flash.h respectively + CFLAGS_BL += -DBOARD_FLASH_OPS_HEADER_H=\"hal/flexspi_$(subst qspi_,,$(MICROPY_HW_FLASH_TYPE)).h\" + # + # Add custom (board specific) or default configuration + ifeq ($(MICROPY_HW_BOARD_FLASH_FILES),1) + CFLAGS_BL += -DBOARD_FLASH_CONFIG_HEADER_H=\"$(BOARD)_flexspi_flash_config.h\" + else + CFLAGS_BL += -DBOARD_FLASH_CONFIG_HEADER_H=\"hal/flexspi_flash_config.h\" + endif +else + $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) +endif + +LDFLAGS_BL += \ + -mcpu=cortex-m7 \ + -mtune=cortex-m7 \ + -specs=nano.specs \ + -specs=nosys.specs \ + -Wl,--cref \ + -Wl,--gc-sections \ + -Wl,--print-memory-usage \ + -Wl,-Map=$@.map \ + -Wl,-N + +# LDDEFINES_BL are used for link time adaptation of linker scripts, utilizing +# the C preprocessor. Therefore keep LDDEFINES_BL separated from LDFLAGS! + +LDDEFINES_BL = \ + -DMICROPY_HW_FLASH_TYPE=$(MICROPY_HW_FLASH_TYPE) \ + -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ + -DBOARD_LINKER_SCRIPT_H=\"$(MCU_SERIES).ld\" + +# Start of firmware is dependent on board flash type. Bootloader requires at least 32kB. +# Firmware start has to be aligned to sector size to allow erase of firmware without affecting the bootloader. +# Sector size is different between QPSI flash (4kB) and HyperFlash (256kB) devices! +ifeq ($(MICROPY_HW_FLASH_TYPE),qspi_nor_flash) + LDDEFINES_BL += -DMICROPY_HW_FLASH_FIRMWARE_START_ADDR=0x00008000 +else ifeq ($(MICROPY_HW_FLASH_TYPE),qspi_hyper_flash) + LDDEFINES_BL += -DMICROPY_HW_FLASH_FIRMWARE_START_ADDR=0x00040000 +else + $(error Error: Unknown board flash type $(MICROPY_HW_FLASH_TYPE)) +endif + +LIBS_BL = $(shell $(CC) $(CFLAGS_BL) -print-libgcc-file-name) +OBJ_BL += $(addprefix $(BUILD_BL)/, $(SRC_C_BL:.c=.o)) +OBJ_BL += $(addprefix $(BUILD_BL)/, $(SRC_SS_BL:.S=.o)) + +define compile_c_bl +$(ECHO) "CC $<" +$(Q)$(CC) $(CFLAGS_BL) -c -MD -o $@ $< +@# The following fixes the dependency file. +@# See http://make.paulandlesley.org/autodep.html for details. +@# Regex adjusted from the above to play better with Windows paths, etc. +@$(CP) $(@:.o=.d) $(@:.o=.P); \ + $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ + $(RM) -f $(@:.o=.d) +endef + +# Targets +mboot_all: $(BUILD_BL)/mboot.bin $(BUILD_BL)/mboot.hex $(BUILD_BL)/mboot.hex mboot_info +.PHONY: mboot_all + +mboot_clean: + $(RM) -rf $(BUILD_BL) +.PHONY: mboot_clean + +OBJ_DIRS_BL = $(sort $(dir $(OBJ_BL))) +$(OBJ_BL): | $(OBJ_DIRS_BL) +$(OBJ_DIRS_BL): + $(MKDIR) -p $@ + +vpath %.S . $(TOP) +$(BUILD_BL)/%.o: %.S + $(ECHO) "CC $<" + $(Q)$(CC) $(CFLAGS_BL) -c -o $@ $< + +vpath %.s . $(TOP) +$(BUILD_BL)/%.o: %.s + $(ECHO) "AS $<" + $(Q)$(AS) -o $@ $< + +vpath %.c . $(TOP) +$(BUILD_BL)/%.o: %.c + $(call compile_c_bl) + +$(BUILD_BL)/%.pp: %.c + $(ECHO) "PreProcess $<" + $(Q)$(CPP) $(CFLAGS_BL) -Wp,-C,-dD,-dI -o $@ $< + +$(BUILD_BL)/mboot.elf: $(OBJ_BL) + $(ECHO) $(LINK_MSG) + $(ECHO) "PREPROCESS LINK $@" + $(Q)$(CC) -E -x c $(LDDEFINES_BL) $(LD_FILES_BL) | grep -v '^#' > $(BUILD_BL)/link.ld + $(ECHO) "LINK $@" + $(Q)$(CC) $(addprefix -T, $(BUILD_BL)/link.ld) $(LDFLAGS_BL) -o $@ $^ $(LIBS_BL) + +$(BUILD_BL)/mboot.bin: $(BUILD_BL)/mboot.elf + $(Q)$(OBJCOPY) -O binary $^ $@ + +$(BUILD_BL)/mboot.hex: $(BUILD_BL)/mboot.elf + $(Q)$(OBJCOPY) -O ihex $^ $@ + +mboot_info: $(BUILD_BL)/mboot.elf $(BUILD_BL)/mboot.bin + $(ECHO) "Bootloader Info" + $(ECHO) "Download buffer $(MICROPY_BOOT_BUFFER_SIZE)" + $(Q)$(SIZE) $(BUILD_BL)/mboot.elf + $(Q)$(SIZE) $(BUILD_BL)/mboot.bin --target binary + +BL_JLINK_COMMANDER_SCRIPT = $(BUILD_BL)/script_mboot.jlink + +ifdef JLINK_IP +BL_JLINK_CONNECTION_SETTINGS = -IP $(JLINK_IP) +else +BL_JLINK_CONNECTION_SETTINGS = -USB +endif + +mboot_deploy_jlink: $(BUILD_BL)/mboot.hex + $(Q)$(TOUCH) $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "ExitOnError 1" > $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "speed auto" >> $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "r" >> $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "st" >> $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "loadfile \"$(realpath $(BUILD_BL)/mboot.hex)\"" >> $(BL_JLINK_COMMANDER_SCRIPT) + $(ECHO) "qc" >> $(BL_JLINK_COMMANDER_SCRIPT) + $(JLINK_PATH)JLinkExe -device $(MCU_VARIANT) -if SWD $(BL_JLINK_CONNECTION_SETTINGS) -CommanderScript $(BL_JLINK_COMMANDER_SCRIPT) + + + +.PHONY: mboot_info +.PHONY: mboot_deploy_jlink diff --git a/ports/mimxrt/mboot/README.md b/ports/mimxrt/mboot/README.md new file mode 100644 index 0000000000000..d8ec8662cc348 --- /dev/null +++ b/ports/mimxrt/mboot/README.md @@ -0,0 +1,36 @@ +# Memory Layout + +## QSPI +``` + +--------------+ +0x6000'0000 | mboot | + | | + +--------------+ +0x6000'8000 | firmware | + | | + | | + +--------------+ +0x6010'0000 | VFS | + | | + / ... / + | | + +--------------+ +``` + +## HyperFlash +``` + +--------------+ +0x6000'0000 | mboot | + | | + +--------------+ +0x6004'0000 | firmware | + | | + | | + +--------------+ +0x6010'0000 | VFS | + | | + / ... / + | | + +--------------+ +``` +Since HyperFlash memory has 256k sectors the firmware has to start at a sector border. diff --git a/ports/mimxrt/mboot/boards/MIMXRT1011.ld b/ports/mimxrt/mboot/boards/MIMXRT1011.ld new file mode 100644 index 0000000000000..6d3c070a89be6 --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1011.ld @@ -0,0 +1,19 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +/* MIMXRT1011 requires different offset for flash configuration */ +flash_config_start = flash_start + 0x00000400; +flash_config_size = 0x00000C00; + +itcm_start = 0x00000000; +itcm_size = 0x00008000; /* 32KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00008000; /* 32KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00010000; /* 64KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/MIMXRT1015.ld b/ports/mimxrt/mboot/boards/MIMXRT1015.ld new file mode 100644 index 0000000000000..fe470531aa535 --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1015.ld @@ -0,0 +1,15 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +itcm_start = 0x00000000; +itcm_size = 0x00008000; /* 32KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00008000; /* 32KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00010000; /* 64KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/MIMXRT1021.ld b/ports/mimxrt/mboot/boards/MIMXRT1021.ld new file mode 100644 index 0000000000000..1eb0f0cfbd86a --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1021.ld @@ -0,0 +1,15 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +itcm_start = 0x00000000; +itcm_size = 0x00010000; /* 64KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00010000; /* 64KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00020000; /* 128KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/MIMXRT1052.ld b/ports/mimxrt/mboot/boards/MIMXRT1052.ld new file mode 100644 index 0000000000000..183a691823679 --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1052.ld @@ -0,0 +1,15 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash || MICROPY_HW_FLASH_TYPE == qspi_hyper_flash + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +itcm_start = 0x00000000; +itcm_size = 0x00020000; /* 128KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00020000; /* 128KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00040000; /* 256KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/MIMXRT1062.ld b/ports/mimxrt/mboot/boards/MIMXRT1062.ld new file mode 100644 index 0000000000000..fbc10fa41d326 --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1062.ld @@ -0,0 +1,15 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +itcm_start = 0x00000000; +itcm_size = 0x00020000; /* 128KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00020000; /* 128KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00040000; /* 256KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/MIMXRT1064.ld b/ports/mimxrt/mboot/boards/MIMXRT1064.ld new file mode 100644 index 0000000000000..efed70edd68d8 --- /dev/null +++ b/ports/mimxrt/mboot/boards/MIMXRT1064.ld @@ -0,0 +1,18 @@ +/* Memory configuration */ +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + flash_start = 0x60000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#elif MICROPY_HW_FLASH_TYPE == internal + flash_start = 0x70000000; + flash_size = MICROPY_HW_FLASH_SIZE; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +itcm_start = 0x00000000; +itcm_size = 0x00020000; /* 128KB */ +dtcm_start = 0x20000000; +dtcm_size = 0x00020000; /* 128KB */ +ocrm_start = 0x20200000; +ocrm_size = 0x00040000; /* 256KB */ +ocrm_end = (ocrm_start + ocrm_size); diff --git a/ports/mimxrt/mboot/boards/common.ld b/ports/mimxrt/mboot/boards/common.ld new file mode 100644 index 0000000000000..30b58a14b9a7d --- /dev/null +++ b/ports/mimxrt/mboot/boards/common.ld @@ -0,0 +1,314 @@ +/* +** ################################################################### +** Linker script inspired by NXP linker script for MIMXRT10xx +** +** Copyright for original linker script: +** Copyright 2016 Freescale Semiconductor, Inc. +** Copyright 2016-2018 NXP +** SPDX-License-Identifier: BSD-3-Clause +** +** http: www.nxp.com +** mail: support@nxp.com +** +** Integrated ideas from CircuitPython: +** SPDX-License-Identifier: The MIT License (MIT) +** SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft (tannewt) +** +** Copyright (c) 2021 Philipp Ebensberger +** +** ################################################################### +*/ + +#include BOARD_LINKER_SCRIPT_H + +/* Entry Point */ +ENTRY(Reset_Handler) + +#if defined MICROPY_HW_FLASH_RESERVED + /* Reserved Area + * Users can create a reserved area at the end of the flash memory via + * 'reserved_size' variable. The size of the reserved area should be a multiple + * of the sector size of the flash memory! + */ + reserved_size = MICROPY_HW_FLASH_RESERVED; +#endif + +flash_end = DEFINED(reserved_size) ? ((flash_start) + (flash_size - reserved_size)) : ((flash_start) + (flash_size)); + +/* Flash configuration is physically used only in non-bootloader builds. + * For bootloader builds this section will be NOLOAD since information is already present in flash memory through bootloader binary. + */ +flash_config_start = DEFINED(flash_config_start) ? flash_config_start : flash_start; +flash_config_size = DEFINED(flash_config_size) ? flash_config_size : 0x00001000; + +#if MICROPY_HW_FLASH_TYPE == qspi_nor_flash + firmware_start = flash_start + MICROPY_HW_FLASH_FIRMWARE_START_ADDR; +#elif MICROPY_HW_FLASH_TYPE == qspi_hyper_flash + firmware_start = flash_start + MICROPY_HW_FLASH_FIRMWARE_START_ADDR; +#else + #error Unknown MICROPY_HW_FLASH_TYPE +#endif + +ivt_start = flash_config_start + flash_config_size; +ivt_size = 0x00000400; +interrupts_start = ivt_start + ivt_size; +interrupts_size = 0x00000400; +text_start = interrupts_start + interrupts_size; +text_size = (firmware_start - text_start); + +firmware_size = flash_end - firmware_start; +mboot_flash_start = flash_start; +mboot_flash_size = firmware_start - mboot_flash_start; + +_estack = __StackTop; +_sstack = __StackLimit; + + +HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400; +STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0400; + +/* Specify the memory areas */ +MEMORY +{ + m_flash_config (RX) : ORIGIN = flash_config_start, LENGTH = flash_config_size + m_ivt (RX) : ORIGIN = ivt_start, LENGTH = ivt_size + m_interrupts (RX) : ORIGIN = interrupts_start, LENGTH = interrupts_size + m_text (RX) : ORIGIN = text_start, LENGTH = text_size + m_firmware (RX) : ORIGIN = firmware_start, LENGTH = firmware_size + m_itcm (RX) : ORIGIN = itcm_start, LENGTH = itcm_size + m_dtcm (RW) : ORIGIN = dtcm_start, LENGTH = dtcm_size + m_ocrm (RW) : ORIGIN = ocrm_start, LENGTH = ocrm_size +} + +/* Define output sections */ +SECTIONS +{ + __flash_start = flash_start; + __flash_config_start = flash_config_start; + __firmware_start = ORIGIN(m_firmware); + __firmware_end = __firmware_start + LENGTH(m_firmware); + + .flash_config : + { + FILL(0x00); + . = ALIGN(4); + KEEP(* (.boot_hdr.conf)) /* flash config section */ + . = ORIGIN(m_flash_config) + LENGTH(m_flash_config) - 1; + BYTE(0x00) + . = ALIGN(4); + } > m_flash_config + + __FLASH_BASE = flash_config_start; + ivt_begin = ORIGIN(m_flash_config) + LENGTH(m_flash_config); + .ivt : AT(ivt_begin) + { + FILL(0x00); + __ivt_begin = .; + . = ALIGN(4); + KEEP(* (.boot_hdr.ivt)) /* ivt section */ + KEEP(* (.boot_hdr.boot_data)) /* boot section */ + KEEP(* (.boot_hdr.dcd_data)) /* dcd section */ + . = ORIGIN(m_ivt) + LENGTH(m_ivt) - 1; + BYTE(0x00) + . = ALIGN(4); + __ivt_end = .; + } > m_ivt + + __interrupts_begin = ivt_begin + LENGTH(m_ivt); + .interrupts : AT(__interrupts_begin) + { + __interrupts_begin = .; + __VECTOR_TABLE = .; + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + __interrupts_rom_end = .; + } > m_interrupts + + __VECTOR_RAM = __VECTOR_TABLE; + __RAM_VECTOR_TABLE_SIZE_BYTES = 0x0; + + __text_begin = __interrupts_begin + (__interrupts_rom_end - __interrupts_begin); + _stext = __text_begin; + .text : AT(__text_begin) + { + __text_begin = .; + . = ALIGN(4); + *(EXCLUDE_FILE(*fsl_flexspi.o) .text*) + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + KEEP (*(.init)) + KEEP (*(.fini)) + . = ALIGN(4); + __text_rom_end = .; + } > m_text + + __extab_begin = __text_begin + (__text_rom_end - __text_begin); + .ARM.extab : AT(__extab_begin) + { + __extab_begin = .; + PROVIDE_HIDDEN (__arm_extab_start = .); + *(.ARM.extab* .gnu.linkonce.armextab.*) + PROVIDE_HIDDEN (__arm_extab_end = .); + __extab_rom_end = .; + } > m_text + + __arm_begin = __extab_begin + (__extab_rom_end - __extab_begin); + .ARM : AT(__arm_begin) + { + __exidx_begin = .; + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + __exidx_rom_end = .; + } > m_text + + __preinit_begin = __arm_begin + (__exidx_rom_end - __exidx_begin); + .preinit_array : AT(__preinit_begin) + { + __preinit_array_begin = .; + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + __preinit_array_rom_end = .; + } > m_text + + __init_array_begin = __preinit_begin + (__preinit_array_rom_end - __preinit_array_begin); + .init_array : AT(__init_array_begin) + { + __init_array_begin = .; + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + __init_array_rom_end = .; + } > m_text + + __fini_array_begin = __init_array_begin + (__init_array_rom_end - __init_array_begin); + .fini_array : AT(__fini_array_begin) + { + __fini_array_begin = .; + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + __fini_array_rom_end = .; + } > m_text + + __etext = __fini_array_begin + (__fini_array_rom_end - __fini_array_begin); + + .no_init : AT(__etext) + { + __no_init_begin = .; + KEEP(*(._bl_command*)) + __no_init_rom_end = .; + } > m_dtcm + + . = __etext + (__no_init_rom_end - __no_init_begin); + + __DATA_ROM = .; + + .data : AT(__DATA_ROM) + { + . = ALIGN(4); + __DATA_RAM = .; + __data_start__ = .; /* create a global symbol at data start */ + *(m_usb_dma_init_data) + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + KEEP(*(.jcr*)) + . = ALIGN(4); + __data_end__ = .; /* define a global symbol at data end */ + } > m_dtcm + + __RAM_FUNCTIONS_ROM = __DATA_ROM + (__data_end__ - __data_start__); + .ram_functions : AT(__RAM_FUNCTIONS_ROM) + { + . = ALIGN(4); + __ram_function_start__ = .; + *(.ram_functions*) + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + __ram_function_end__ = .; + } > m_itcm + + __NDATA_ROM = __DATA_ROM + (__ram_function_end__ - __ram_function_start__); + .ncache.init : AT(__NDATA_ROM) + { + __noncachedata_start__ = .; /* create a global symbol at ncache data start */ + *(NonCacheable.init) + . = ALIGN(4); + __noncachedata_init_end__ = .; /* create a global symbol at initialized ncache data end */ + } > m_dtcm + . = __noncachedata_init_end__; + .ncache : + { + *(NonCacheable) + . = ALIGN(4); + __noncachedata_end__ = .; /* define a global symbol at ncache data end */ + } > m_dtcm + + __DATA_END = __NDATA_ROM + (__noncachedata_end__ - __noncachedata_start__); + text_end = ORIGIN(m_text) + LENGTH(m_text); + ASSERT(__DATA_END <= (mboot_flash_start + mboot_flash_size), "region m_text overflowed with text and data") + + /* Uninitialized data section */ + .bss (NOLOAD): + { + /* This is used by the startup in order to initialize the .bss section */ + . = ALIGN(4); + __START_BSS = .; + __bss_start__ = .; + __bss_section_table = .; + *(m_usb_dma_noninit_data) + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + __END_BSS = .; + } > m_dtcm + + .heap (NOLOAD): + { + . = ALIGN(8); + __end__ = .; + PROVIDE(end = .); + __HeapBase = .; + . += HEAP_SIZE; + __HeapLimit = .; + __heap_limit = .; /* Add for _sbrk */ + } > m_dtcm + + .stack : + { + . = ALIGN(8); + . += STACK_SIZE; + } > m_dtcm + + .fw_buffer (NOLOAD): + { + . = ALIGN(0x400); + *(.fw_buffer); + . = ALIGN(4); + __END_FW_BUFFER = .; + } > m_ocrm + + /* Initializes stack on the end of block */ + __StackTop = ORIGIN(m_dtcm) + LENGTH(m_dtcm); + __StackLimit = __StackTop - STACK_SIZE; + PROVIDE(__stack = __StackTop); + + + boot_data_start_address = flash_config_start; + boot_data_size = __etext - flash_start; + + .ARM.attributes 0 : { *(.ARM.attributes) } + + ASSERT(__StackLimit >= __HeapLimit, "region m_dtcm overflowed with stack and heap") +} + diff --git a/ports/mimxrt/mboot/boards/flexspi_nor_mboot.c b/ports/mimxrt/mboot/boards/flexspi_nor_mboot.c new file mode 100644 index 0000000000000..da5c989720c47 --- /dev/null +++ b/ports/mimxrt/mboot/boards/flexspi_nor_mboot.c @@ -0,0 +1,54 @@ +/* + * Copyright 2017 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "flexspi_nor_mboot.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.xip_device" +#endif + +extern uint32_t __ivt_begin; +extern uint32_t boot_data_start_address; +extern uint32_t boot_data_size; +extern void Reset_Handler(); + +#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1) +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.ivt"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.ivt" +#endif +/************************************* + * IVT Data + *************************************/ +const ivt image_vector_table = { + IVT_HEADER, /* IVT Header */ + (uint32_t)&Reset_Handler, /* Image Entry Function */ + IVT_RSVD, /* Reserved = 0 */ + (uint32_t)0, /* Address where DCD information is stored */ + (uint32_t)BOOT_DATA_ADDRESS, /* Address where BOOT Data Structure is stored */ + (uint32_t)&__ivt_begin, /* Pointer to IVT Self (absolute address */ + (uint32_t)0, /* Address where CSF file is stored */ + IVT_RSVD /* Reserved = 0 */ +}; + +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) || defined(__GNUC__) +__attribute__((section(".boot_hdr.boot_data"))) +#elif defined(__ICCARM__) +#pragma location=".boot_hdr.boot_data" +#endif +/************************************* + * Boot Data + *************************************/ +const BOOT_DATA_T boot_data = { + (uint32_t)&boot_data_start_address, /* boot start location */ + (uint32_t)&boot_data_size, /* size */ + PLUGIN_FLAG, /* Plugin flag*/ + 0xFFFFFFFF /* empty - extra data word */ +}; +#endif diff --git a/ports/mimxrt/mboot/boards/flexspi_nor_mboot.h b/ports/mimxrt/mboot/boards/flexspi_nor_mboot.h new file mode 100644 index 0000000000000..481f97d6ba78f --- /dev/null +++ b/ports/mimxrt/mboot/boards/flexspi_nor_mboot.h @@ -0,0 +1,111 @@ +/* + * Copyright 2017 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __FLEXSPI_NOR_MBOOT_H__ +#define __FLEXSPI_NOR_MBOOT_H__ + +#include +#include "board.h" + +/*! @name Driver version */ +/*@{*/ +/*! @brief XIP_DEVICE driver version 2.0.0. */ +#define FSL_XIP_DEVICE_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) +/*@}*/ + +/************************************* + * IVT Data + *************************************/ +typedef struct _ivt_ { + /** @ref hdr with tag #HAB_TAG_IVT, length and HAB version fields + * (see @ref data) + */ + uint32_t hdr; + /** Absolute address of the first instruction to execute from the + * image + */ + uint32_t entry; + /** Reserved in this version of HAB: should be NULL. */ + uint32_t reserved1; + /** Absolute address of the image DCD: may be NULL. */ + uint32_t dcd; + /** Absolute address of the Boot Data: may be NULL, but not interpreted + * any further by HAB + */ + uint32_t boot_data; + /** Absolute address of the IVT.*/ + uint32_t self; + /** Absolute address of the image CSF.*/ + uint32_t csf; + /** Reserved in this version of HAB: should be zero. */ + uint32_t reserved2; +} ivt; + +#define IVT_MAJOR_VERSION 0x4 +#define IVT_MAJOR_VERSION_SHIFT 0x4 +#define IVT_MAJOR_VERSION_MASK 0xF +#define IVT_MINOR_VERSION 0x1 +#define IVT_MINOR_VERSION_SHIFT 0x0 +#define IVT_MINOR_VERSION_MASK 0xF + +#define IVT_VERSION(major, minor) \ + ((((major) & IVT_MAJOR_VERSION_MASK) << IVT_MAJOR_VERSION_SHIFT) | \ + (((minor) & IVT_MINOR_VERSION_MASK) << IVT_MINOR_VERSION_SHIFT)) + +/* IVT header */ +#define IVT_TAG_HEADER 0xD1 /**< Image Vector Table */ +#define IVT_SIZE 0x2000 +#define IVT_PAR IVT_VERSION(IVT_MAJOR_VERSION, IVT_MINOR_VERSION) +#define IVT_HEADER (IVT_TAG_HEADER | (IVT_SIZE << 8) | (IVT_PAR << 24)) + +/* Set resume entry */ +#if defined(__CC_ARM) || defined(__ARMCC_VERSION) +extern uint32_t __Vectors[]; +extern uint32_t Image$$RW_m_config_text$$Base[]; +#define IMAGE_ENTRY_ADDRESS ((uint32_t)__Vectors) +#define FLASH_BASE ((uint32_t)Image$$RW_m_config_text$$Base) +#elif defined(__MCUXPRESSO) +extern uint32_t __Vectors[]; +extern uint32_t __boot_hdr_start__[]; +#define IMAGE_ENTRY_ADDRESS ((uint32_t)__Vectors) +#define FLASH_BASE ((uint32_t)__boot_hdr_start__) +#elif defined(__ICCARM__) +extern uint32_t __VECTOR_TABLE[]; +extern uint32_t m_boot_hdr_conf_start[]; +#define IMAGE_ENTRY_ADDRESS ((uint32_t)__VECTOR_TABLE) +#define FLASH_BASE ((uint32_t)m_boot_hdr_conf_start) +#elif defined(__GNUC__) +extern uint32_t __VECTOR_TABLE[]; +extern uint32_t __FLASH_BASE[]; +#define IMAGE_ENTRY_ADDRESS ((uint32_t)__VECTOR_TABLE) +#define FLASH_BASE ((uint32_t)__FLASH_BASE) +#endif + +#define DCD_ADDRESS dcd_data +#define BOOT_DATA_ADDRESS &boot_data +#define CSF_ADDRESS 0 +#define IVT_RSVD (uint32_t)(0x00000000) + +/************************************* + * Boot Data + *************************************/ +typedef struct _boot_data_ { + uint32_t start; /* boot start location */ + uint32_t size; /* size */ + uint32_t plugin; /* plugin flag - 1 if downloaded application is plugin */ + uint32_t placeholder; /* placehoder to make even 0x10 size */ +}BOOT_DATA_T; + +extern uint32_t mboot_flash_size; +#define FLASH_SIZE (uint32_t)&mboot_flash_size +#define PLUGIN_FLAG (uint32_t)0 + +/* External Variables */ +const BOOT_DATA_T boot_data; +extern const uint8_t dcd_data[]; + +#endif /* __FLEXSPI_NOR_MBOOT_H__ */ diff --git a/ports/mimxrt/mboot/hal/resethandler_mboot_MIMXRT10xx_RAM.S b/ports/mimxrt/mboot/hal/resethandler_mboot_MIMXRT10xx_RAM.S new file mode 100644 index 0000000000000..4c7c4e4eaaa88 --- /dev/null +++ b/ports/mimxrt/mboot/hal/resethandler_mboot_MIMXRT10xx_RAM.S @@ -0,0 +1,151 @@ + + .syntax unified + .arch armv7-m + +/* Reset Handler */ + + .thumb_func + .align 2 + .globl Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + cpsid i /* Mask interrupts */ + .equ VTOR, 0xE000ED08 + ldr r0, =VTOR + ldr r1, =__isr_vector + str r1, [r0] + ldr r2, [r1] + msr msp, r2 + +#ifndef __NO_SYSTEM_INIT + ldr r0,=SystemInit + blx r0 +#endif +/* Loop to copy data from read only memory to RAM. The ranges + * of copy from/to are specified by following symbols evaluated in + * linker script. + * __DATA_ROM: End of code section, i.e., begin of data sections to copy from. + * __data_start__/__data_end__: RAM address range that data should be + * __ocram_data_start__/__ocram_data_end__ : OCRAM region + * __ram_function_start__/__ram_function_end__ : ramfunction region + * __noncachedata_start__/__noncachedata_end__ : none cachable region + * copied to. Both must be aligned to 4 bytes boundary. */ + + ldr r1, =__DATA_ROM + ldr r2, =__data_start__ + ldr r3, =__data_end__ + +#ifdef __PERFORMANCE_IMPLEMENTATION +/* Here are two copies of loop implementations. First one favors performance + * and the second one favors code size. Default uses the second one. + * Define macro "__PERFORMANCE_IMPLEMENTATION" in project to use the first one */ + subs r3, r2 + ble .LC1 +.LC0: + subs r3, #4 + ldr r0, [r1, r3] + str r0, [r2, r3] + bgt .LC0 +.LC1: +#else /* code size implemenation */ +.LC0: + cmp r2, r3 + ittt lt + ldrlt r0, [r1], #4 + strlt r0, [r2], #4 + blt .LC0 +#endif + +#ifdef __STARTUP_INITIALIZE_RAMFUNCTION + ldr r2, =__ram_function_start__ + ldr r3, =__ram_function_end__ +#ifdef __PERFORMANCE_IMPLEMENTATION +/* Here are two copies of loop implementations. First one favors performance + * and the second one favors code size. Default uses the second one. + * Define macro "__PERFORMANCE_IMPLEMENTATION" in project to use the first one */ + subs r3, r2 + ble .LC_ramfunc_copy_end +.LC_ramfunc_copy_start: + subs r3, #4 + ldr r0, [r1, r3] + str r0, [r2, r3] + bgt .LC_ramfunc_copy_start +.LC_ramfunc_copy_end: +#else /* code size implemenation */ +.LC_ramfunc_copy_start: + cmp r2, r3 + ittt lt + ldrlt r0, [r1], #4 + strlt r0, [r2], #4 + blt .LC_ramfunc_copy_start +#endif +#endif /* __STARTUP_INITIALIZE_RAMFUNCTION */ + +#ifdef __STARTUP_INITIALIZE_NONCACHEDATA + ldr r2, =__noncachedata_start__ + ldr r3, =__noncachedata_init_end__ +#ifdef __PERFORMANCE_IMPLEMENTATION +/* Here are two copies of loop implementations. First one favors performance + * and the second one favors code size. Default uses the second one. + * Define macro "__PERFORMANCE_IMPLEMENTATION" in project to use the first one */ + subs r3, r2 + ble .LC3 +.LC2: + subs r3, #4 + ldr r0, [r1, r3] + str r0, [r2, r3] + bgt .LC2 +.LC3: +#else /* code size implemenation */ +.LC2: + cmp r2, r3 + ittt lt + ldrlt r0, [r1], #4 + strlt r0, [r2], #4 + blt .LC2 +#endif +/* zero inited ncache section initialization */ + ldr r3, =__noncachedata_end__ + movs r0,0 +.LC4: + cmp r2,r3 + itt lt + strlt r0,[r2],#4 + blt .LC4 +#endif /* __STARTUP_INITIALIZE_NONCACHEDATA */ + +#ifdef __STARTUP_CLEAR_BSS +/* This part of work usually is done in C library startup code. Otherwise, + * define this macro to enable it in this startup. + * + * Loop to zero out BSS section, which uses following symbols + * in linker script: + * __bss_start__: start of BSS section. Must align to 4 + * __bss_end__: end of BSS section. Must align to 4 + */ + ldr r1, =__bss_start__ + ldr r2, =__bss_end__ + + movs r0, 0 +.LC5: + cmp r1, r2 + itt lt + strlt r0, [r1], #4 + blt .LC5 +#endif /* __STARTUP_CLEAR_BSS */ + + cpsie i /* Unmask interrupts */ +#ifndef __START +#define __START _start +#endif +#ifndef __ATOLLIC__ + ldr r0,=__START + blx r0 +#else + ldr r0,=__libc_init_array + blx r0 + ldr r0,=main + bx r0 +#endif + .pool + .size Reset_Handler, . - Reset_Handler diff --git a/ports/mimxrt/mboot/main.c b/ports/mimxrt/mboot/main.c new file mode 100644 index 0000000000000..ba803f9dab6f8 --- /dev/null +++ b/ports/mimxrt/mboot/main.c @@ -0,0 +1,154 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +/* + * After device is enumerated in dfu mode run the following commands + * + * To transfer firmware from host to device: + * + * $ dfu-util -D [filename] + * + * To transfer firmware from device to host: + * + * $ dfu-util -U [filename] + * + */ + +#include CPU_HEADER_H +// include fsl_clock.h BEFORE including clock configuration due to bug in NXP HAL. +#include "fsl_clock.h" +#include CLOCK_CONFIG_H +#include +#include "mboot_buffer.h" +#include "mboot_command.h" +#include "mboot_dfu.h" +#include "mboot_memory.h" +#include "mboot_validate.h" +#include "fw_header.h" + +// --------------------------------------------------------------------+ +// Type Definitions +// --------------------------------------------------------------------+ +typedef void (*firmware_entry_func)(void); + +// --------------------------------------------------------------------+ +// Local Variables +// --------------------------------------------------------------------+ +MEM_AT_SECTION("._bl_command") mboot_command_t bl_command; +const uint8_t dcd_data[] = { 0x00 }; + +// --------------------------------------------------------------------+ +// External Variables +// --------------------------------------------------------------------+ +extern uint32_t __firmware_start; // Linker symbol + +// --------------------------------------------------------------------+ +// External Function Declarations +// --------------------------------------------------------------------+ +extern void SystemInit(void); + +// --------------------------------------------------------------------+ +// Local Function Declarations +// --------------------------------------------------------------------+ +static void board_init(void); +static bool reprog_request(void); +static bool valid_fw_available(void); +static inline firmware_entry_func get_entry_func(void); +static inline void fw_start(void) __attribute__ ((noreturn)); + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ +int main(void) { + board_init(); + + if (reprog_request() || !valid_fw_available()) { + mboot_dfu_init(); + mboot_dfu_run(); + // Reset controller after upgrade + NVIC_SystemReset(); + } else { + fw_start(); + } + + // Fatal error occured - wait for power cycle + for (;;) { + __asm__ ("nop"); + } + + return 0; +} + +// --------------------------------------------------------------------+ +// Local Function Definitions +// --------------------------------------------------------------------+ +static bool reprog_request(void) { + bool reprogramming_requested = false; + + if ((bl_command.magic == BOOT_COMMAND_MAGIC) && (bl_command.command == BOOT_COMMAND_RESET_DFU)) { + reprogramming_requested = true; + } else { + reprogramming_requested = false; + } + + // Clear reprogramming header flags immediately to prevent reboot loops + bl_command.magic = 0x0UL; + bl_command.command = 0x0UL; + + return reprogramming_requested; +} + +static bool valid_fw_available(void) { + fw_header_t *fw_header = (fw_header_t *)MEM_GET_SYMBOL_VALUE(__firmware_start); + uintptr_t start_addr = ((uintptr_t)MEM_GET_SYMBOL_VALUE(__firmware_start)) + sizeof(fw_header_t); + mboot_validate_status_t fw_status = mboot_validate_firmware(fw_header, start_addr); + + return fw_status == FWV_STATUS_OK ? true : false; +} + +static inline firmware_entry_func get_entry_func() { + fw_header_t *fw_header = (fw_header_t *)&__firmware_start; + firmware_entry_func entry_func = (firmware_entry_func)fw_header->entry_addr; + return entry_func; +} + +static inline void fw_start(void) { + firmware_entry_func entry_func = get_entry_func(); + + entry_func(); + + for (;;) { + __asm__ ("nop"); + } +} + +static void board_init(void) { + // Init clock + BOARD_BootClockRUN(); + SystemCoreClockUpdate(); + + // Enable IOCON clock + CLOCK_EnableClock(kCLOCK_Iomuxc); +} diff --git a/ports/mimxrt/mboot/mboot_buffer.c b/ports/mimxrt/mboot/mboot_buffer.c new file mode 100644 index 0000000000000..c06c13934456f --- /dev/null +++ b/ports/mimxrt/mboot/mboot_buffer.c @@ -0,0 +1,76 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "mboot_buffer.h" +#include "mboot_memory.h" + +// --------------------------------------------------------------------+ +// Local Variables +// --------------------------------------------------------------------+ +MEM_AT_SECTION(".fw_buffer") uint8_t fw_buffer[MICROPY_BOOT_BUFFER_SIZE]; +static size_t buffer_length; + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ +void mboot_buffer_init(void) { + buffer_length = 0UL; + memset(&fw_buffer[0], 0, MICROPY_BOOT_BUFFER_SIZE); +} + +size_t mboot_buffer_append(const uint8_t *data, size_t length) { + size_t bytes_remaining = (size_t)MICROPY_BOOT_BUFFER_SIZE - buffer_length; + size_t bytes_to_buffer = (bytes_remaining >= length) ? length : bytes_remaining; + + memcpy((void *)&fw_buffer[buffer_length], (void *)data, bytes_to_buffer); + buffer_length += bytes_to_buffer; + + return length - bytes_to_buffer; +} + +void mboot_buffer_pad(void) { + if (buffer_length < MICROPY_BOOT_BUFFER_SIZE) { + memset((void *)&fw_buffer[buffer_length], 0, (MICROPY_BOOT_BUFFER_SIZE - buffer_length)); + buffer_length = MICROPY_BOOT_BUFFER_SIZE; + } +} + +void mboot_buffer_reset(void) { + buffer_length = 0UL; +} + +uint8_t *mboot_buffer_data_ptr(void) { + return &fw_buffer[0]; +} + +bool mboot_buffer_is_full(void) { + return (size_t)buffer_length >= MICROPY_BOOT_BUFFER_SIZE; +} + +size_t mboot_buffer_len(void) { + return (size_t)buffer_length; +} diff --git a/ports/mimxrt/mboot/mboot_buffer.h b/ports/mimxrt/mboot/mboot_buffer.h new file mode 100644 index 0000000000000..ee7e58fb58746 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_buffer.h @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_BUFFER_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_BUFFER_H + +#include +#include +#include + +// --------------------------------------------------------------------+ +// Defines and Macros +// --------------------------------------------------------------------+ +#ifndef MICROPY_BOOT_BUFFER_SIZE +#define MICROPY_BOOT_BUFFER_SIZE (32 * 1024) // buffer size must be multiple of 4k! +#endif + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +void mboot_buffer_init(void); +size_t mboot_buffer_append(const uint8_t *data, size_t length); +void mboot_buffer_pad(void); +void mboot_buffer_reset(void); +uint8_t *mboot_buffer_data_ptr(void); +bool mboot_buffer_is_full(void); +size_t mboot_buffer_len(void); + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_BUFFER_H diff --git a/ports/mimxrt/mboot/mboot_dfu.c b/ports/mimxrt/mboot/mboot_dfu.c new file mode 100644 index 0000000000000..d88ac56db16ad --- /dev/null +++ b/ports/mimxrt/mboot/mboot_dfu.c @@ -0,0 +1,220 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// include fsl_clock.h BEFORE including clock configuration due to bug in NXP HAL. +#include "fsl_clock.h" +#include CLOCK_CONFIG_H +#include "mboot_dfu.h" +#include "mboot_memory.h" +#include "mboot_upgrade.h" +#include "mboot_utils.h" +#include "mboot_validate.h" +#include "tusb.h" +#include "tusb_config.h" + +// --------------------------------------------------------------------+ +// Constants +// --------------------------------------------------------------------+ +const uint32_t dfu_finish_delay_time_ms = 500; + +// --------------------------------------------------------------------+ +// Local Variables +// --------------------------------------------------------------------+ +static bool dfu_finished = false; +static int32_t bytes_to_ignore = 0UL; + +// --------------------------------------------------------------------+ +// External Variables +// --------------------------------------------------------------------+ +extern uint32_t __flash_config_start; // Linker symbol +extern uint32_t __firmware_start; // Linker symbol + +// --------------------------------------------------------------------+ +// Local Function Declarations +// --------------------------------------------------------------------+ + +void usb_init(uint8_t d_cal, uint8_t txcal45dp, uint8_t txcal45dn); + +// --------------------------------------------------------------------+ +// Global Functions +// --------------------------------------------------------------------+ + +void mboot_dfu_init(void) { + usb_init(0b0111, 0b0110, 0b0110); // Configure nominal values for D_CAL and TXCAL45DP/DN + (void)mboot_upgrade_init(); + tusb_init(); + // --- + dfu_finished = false; + bytes_to_ignore = (int32_t)((uint32_t)MEM_GET_SYMBOL_VALUE(__firmware_start)) - ((uint32_t)MEM_GET_SYMBOL_VALUE(__flash_config_start)); +} + +void mboot_dfu_run(void) { + while (dfu_finished == false) { + tud_task(); // tinyusb device task + } +} + +// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST) + + +// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests +void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length); + +// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest) + + +// Invoked when a DFU_DETACH request is received +TU_ATTR_WEAK void tud_dfu_detach_cb(void); + +// --------------------------------------------------------------------+ +// Device callbacks +// --------------------------------------------------------------------+ + +// Invoked when device is mounted +void tud_mount_cb(void) { + return; +} + +// Invoked when device is unmounted +void tud_umount_cb(void) { + (void)mboot_upgrade_deinit(); +} + +// Invoked when usb bus is suspended +// remote_wakeup_en : if host allow us to perform remote wake-up +// Within 7ms, device must draw an average of current less than 2.5 mA from bus +void tud_suspend_cb(bool remote_wakeup_en) { + (void)remote_wakeup_en; + (void)mboot_upgrade_deinit(); +} + +// Invoked when usb bus is resumed +void tud_resume_cb(void) { + (void)mboot_upgrade_deinit(); +} + +// --------------------------------------------------------------------+ +// DFU callbacks +// --------------------------------------------------------------------+ + +uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) { + (void)alt; // Todo: Implement handling of alternative option in the future + return 0; +} + +void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length) { + (void)alt; // Todo: Implement handling of alternative option in the future + + + mboot_upgrade_status_t status = BU_STATUS_UNKNOWN_ERROR; + + if (bytes_to_ignore > length) { + bytes_to_ignore -= length; + status = BU_STATUS_OK; + } else { + length = length - bytes_to_ignore; + status = mboot_upgrade_write_data(block_num, data + bytes_to_ignore, length); + bytes_to_ignore = 0; + } + + if (status == BU_STATUS_OK) { + tud_dfu_finish_flashing(DFU_STATUS_OK); + } else { + mboot_upgrade_deinit(); + tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG); + } +} + +void tud_dfu_manifest_cb(uint8_t alt) { + (void)alt; + + dfu_status_t dfu_status = DFU_STATUS_ERR_UNKNOWN; + mboot_upgrade_status_t status = BU_STATUS_UNKNOWN_ERROR; + + status = mboot_upgrade_finalize(); + + if (status == BU_STATUS_OK) { + status = mboot_upgrade_validate(); + if (status == BU_STATUS_OK) { + dfu_status = DFU_STATUS_OK; + } else { + mboot_upgrade_deinit(); + dfu_status = DFU_STATUS_ERR_VERIFY; + } + } else { + mboot_upgrade_deinit(); + dfu_status = DFU_STATUS_ERR_PROG; + } + + tud_dfu_finish_flashing(dfu_status); +} + +void tud_dfu_abort_cb(uint8_t alt) { + (void)alt; // Todo: Implement handling of alternative option in the future + (void)mboot_upgrade_deinit(); +} + +void tud_dfu_detach_cb(void) { + dfu_finished = true; +} + +uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t *data, uint16_t length) { + (void)alt; + (void)block_num; + (void)data; + (void)length; + return 0UL; +} + +// --------------------------------------------------------------------+ +// Helper functions +// --------------------------------------------------------------------+ +void usb_init(uint8_t d_cal, uint8_t txcal45dp, uint8_t txcal45dn) { + #ifdef USBPHY1 + USBPHY_Type *usb_phy = USBPHY1; + #else + USBPHY_Type *usb_phy = USBPHY; + #endif + + CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, BOARD_XTAL0_CLK_HZ); + CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, BOARD_XTAL0_CLK_HZ); + + #if defined(CPU_MIMXRT1176_cm7) + usb_phy->TRIM_OVERRIDE_EN = USBPHY_TRIM_OVERRIDE_EN_TRIM_DIV_SEL_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_ENV_TAIL_ADJ_VD_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_D_CAL_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_CAL45DP_OVERRIDE(1) | + USBPHY_TRIM_OVERRIDE_EN_TRIM_TX_CAL45DN_OVERRIDE(1); // Enable override for D_CAL and TXCAL45DP/DN + #endif + usb_phy->PWD = 0U; // Set all bits in PWD register to normal operation + usb_phy->TX = ((usb_phy->TX & (~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK))) | + (USBPHY_TX_D_CAL(d_cal) | USBPHY_TX_TXCAL45DP(txcal45dp) | USBPHY_TX_TXCAL45DM(txcal45dn))); // Configure values for D_CAL and TXCAL45DP/DN +} + +void USB_OTG1_IRQHandler(void) { + tud_int_handler(0); + __SEV(); +} diff --git a/ports/mimxrt/mboot/mboot_dfu.h b/ports/mimxrt/mboot/mboot_dfu.h new file mode 100644 index 0000000000000..635337406dff7 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_dfu.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_DFU_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_DFU_H + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +void mboot_dfu_init(void); +void mboot_dfu_run(void); + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_DFU_H diff --git a/ports/mimxrt/mboot/mboot_memory.h b/ports/mimxrt/mboot/mboot_memory.h new file mode 100644 index 0000000000000..8453f1028e991 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_memory.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_MEMORY_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_MEMORY_H + +#define MEM_AT_SECTION(section_name) __attribute__((section(section_name))) +#define MEM_GET_SYMBOL_VALUE(symbol) & symbol + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_MEMORY_H diff --git a/ports/mimxrt/mboot/mboot_upgrade.c b/ports/mimxrt/mboot/mboot_upgrade.c new file mode 100644 index 0000000000000..56fe64adad909 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_upgrade.c @@ -0,0 +1,307 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mboot_buffer.h" +#include "mboot_memory.h" +#include "mboot_upgrade.h" +#include "mboot_validate.h" +#include "flash.h" + +// --------------------------------------------------------------------+ +// Type Definitions +// --------------------------------------------------------------------+ +typedef struct _upgrade_session_t +{ + // Available memory range for session + uintptr_t mem_start_addr; + uintptr_t mem_end_addr; + + // Current addresses of write and erase pointer + uintptr_t write_addr; + uintptr_t erase_addr; + + // Number of blocks received for sessions + uint32_t n_blocks; + + // Number of bytes written to flash by session + uint32_t n_bytes_written; + + // Session block download function + mboot_upgrade_status_t (*write_data_handler)(); + + // Session status + status_t status; + mboot_upgrade_status_t upgrade_status; + mboot_validate_status_t verification_status; +} upgrade_session_t; + +// --------------------------------------------------------------------+ +// Local Variables +// --------------------------------------------------------------------+ +static fw_header_t image_header = {}; +static upgrade_session_t session; + +// --------------------------------------------------------------------+ +// External Variables +// --------------------------------------------------------------------+ +extern uintptr_t __firmware_start; +extern uintptr_t __firmware_end; +extern uintptr_t __flash_start; + +// --------------------------------------------------------------------+ +// Local Function Declarations +// --------------------------------------------------------------------+ +static mboot_upgrade_status_t write_initial_block_handler(void); +static mboot_upgrade_status_t write_block_handler(void); +static mboot_upgrade_status_t write_block(uint8_t *data, uint32_t length, uintptr_t *eaddr, uintptr_t *waddr); + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ + +// Todo: Add configuration structure +mboot_upgrade_status_t mboot_upgrade_init(void) { + flash_init(); + + // Initialize firmware upgrade session + session.n_blocks = 0UL; + session.n_bytes_written = 0UL; + session.status = kStatus_Success; + + session.mem_start_addr = ((uintptr_t)MEM_GET_SYMBOL_VALUE(__firmware_start)) - ((uintptr_t)MEM_GET_SYMBOL_VALUE(__flash_start)); + session.mem_end_addr = ((uintptr_t)MEM_GET_SYMBOL_VALUE(__firmware_end)) - ((uintptr_t)MEM_GET_SYMBOL_VALUE(__flash_start)); + session.write_addr = session.mem_start_addr; + session.erase_addr = session.mem_start_addr; + + session.upgrade_status = BU_STATUS_UNKNOWN_ERROR; + session.verification_status = FWV_STATUS_UNKNOWN_ERROR; + + // Initialize session write data handler + session.write_data_handler = write_initial_block_handler; + + // Initialize session data buffer + mboot_buffer_init(); + mboot_buffer_reset(); + + return BU_STATUS_OK; +} + +mboot_upgrade_status_t mboot_upgrade_deinit(void) { + // De-initialize firmware upgrade session + session.n_blocks = 0UL; + session.n_bytes_written = 0UL; + session.status = kStatus_Success; + + session.mem_start_addr = 0UL; + session.mem_end_addr = 0UL; + session.write_addr = session.mem_start_addr; + session.erase_addr = session.mem_start_addr; + + session.upgrade_status = BU_STATUS_UNKNOWN_ERROR; + session.verification_status = FWV_STATUS_UNKNOWN_ERROR; + + // De-initialize session write data handler + session.write_data_handler = write_initial_block_handler; + + // De-initialize session data buffer + mboot_buffer_reset(); + + return BU_STATUS_OK; +} + +// TODO: Return error when more data is 'written' than available +mboot_upgrade_status_t mboot_upgrade_write_data(uint16_t block_num, const uint8_t *data, uint16_t block_length) { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + session.n_blocks += 1UL; + + do { + // Append data to buffer when not full + if (!mboot_buffer_is_full()) { + uint32_t remaining_data = mboot_buffer_append(data, block_length); + // Update data buffer pointer to point to remaining data and update remaining block length + data += block_length - remaining_data; + block_length = remaining_data; + // --- + ret_status = BU_STATUS_OK; + } + + // Write data to flash when buffer full + if (mboot_buffer_is_full()) { + ret_status = session.write_data_handler(); + + if (ret_status != BU_STATUS_OK) { + break; + } + } + } while (block_length != 0); + + session.upgrade_status = ret_status; + return ret_status; +} + +mboot_upgrade_status_t mboot_upgrade_finalize() { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + + do { + // Pad buffer + mboot_buffer_pad(); + + ret_status = session.write_data_handler(); + if (ret_status != BU_STATUS_OK) { + break; + } + } while (session.write_addr < session.erase_addr); + + session.upgrade_status = ret_status; + return ret_status; +} + +mboot_upgrade_status_t mboot_upgrade_validate() { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + uintptr_t start_addr = ((uintptr_t)MEM_GET_SYMBOL_VALUE(__firmware_start)) + sizeof(fw_header_t); + + do { + mboot_validate_status_t fw_status = mboot_validate_firmware(&image_header, start_addr); + session.verification_status = fw_status; + + // Check if downloaded firmware is valid + if (fw_status != FWV_STATUS_OK) { + ret_status = BU_STATUS_FIRMWARE_INVALID; + break; + } + + // Write image header to flash to validate downloaded firmware + session.status = flash_write_block(session.mem_start_addr, + (uint8_t *)&image_header, + sizeof(image_header)); + if (session.status != kStatus_Success) { + ret_status = BU_STATUS_FLASH_WRITE_ERROR; + break; + } + + // Validation successfull + ret_status = BU_STATUS_OK; + } while (false); + + session.upgrade_status = ret_status; + return ret_status; +} + +// --------------------------------------------------------------------+ +// Local Function Definitions +// --------------------------------------------------------------------+ +static mboot_upgrade_status_t write_initial_block_handler(void) { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + uint8_t *data = mboot_buffer_data_ptr(); + uint32_t length = mboot_buffer_len(); + + do { + // Check received image header before writing any data to flash + // in order to prevent erasing a valid firmware if user tries to + // download an incompatible firmware image. + if (mboot_validate_header((fw_header_t *)data) != FWV_STATUS_OK) { + ret_status = BU_STATUS_RECEIVED_IMAGE_HEADER_INVALID; + break; + } + + // Store image header for later use in validation step of + // downloaded firmware. + memcpy(&image_header, (void *)data, sizeof(fw_header_t)); + + // Increment data pointer to offset image header. + data = (data + sizeof(fw_header_t)); + // Offset write address to skip image header when writing initial block. + session.write_addr += sizeof(fw_header_t); + + ret_status = write_block(data, length - sizeof(fw_header_t), + &session.erase_addr, + &session.write_addr); + if (ret_status != BU_STATUS_OK) { + break; + } + + // Swap session write handler function if + // initial block has been successfully written to flash memory. + session.write_data_handler = write_block_handler; + } while (false); + + mboot_buffer_reset(); + + return ret_status; +} + +static mboot_upgrade_status_t write_block_handler(void) { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + uint8_t *data = mboot_buffer_data_ptr(); + uint32_t length = mboot_buffer_len(); + + ret_status = write_block(data, length, + &session.erase_addr, + &session.write_addr); + + mboot_buffer_reset(); + + return ret_status; +} + +static mboot_upgrade_status_t write_block(uint8_t *data, uint32_t length, uintptr_t *eaddr, uintptr_t *waddr) { + mboot_upgrade_status_t ret_status = BU_STATUS_UNKNOWN_ERROR; + + do { + // Erase block of flash only if write address plus data length + // exceeds already erased area + if (*eaddr < (*waddr + length)) { + do { + session.status = flash_erase_block(*eaddr); + + if (session.status == kStatus_Success) { + *eaddr += BLOCK_SIZE_BYTES; + ret_status = BU_STATUS_OK; + } else { + ret_status = BU_STATUS_FLASH_ERASE_ERROR; + break; // Break write operation in case of erase error + } + } while (*eaddr < (*waddr + length)); + + if (ret_status != BU_STATUS_OK) { + break; // Break write operation in case of erase error + } + } + + // Write block of data to flash + session.status = flash_write_block(*waddr, data, length); + + if (session.status == kStatus_Success) { + *waddr += length; + session.n_bytes_written += length; + ret_status = BU_STATUS_OK; + } else { + ret_status = BU_STATUS_FLASH_WRITE_ERROR; + } + } while (false); + + return ret_status; +} diff --git a/ports/mimxrt/mboot/mboot_upgrade.h b/ports/mimxrt/mboot/mboot_upgrade.h new file mode 100644 index 0000000000000..b7efbc13f8168 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_upgrade.h @@ -0,0 +1,75 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_UPGRADE_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_UPGRADE_H + +#include + +typedef enum _mboot_upgrade_status_t { + BU_STATUS_OK = 1, + BU_STATUS_FLASH_ERASE_ERROR = 10, + BU_STATUS_FLASH_WRITE_ERROR = 11, + BU_STATUS_RECEIVED_IMAGE_HEADER_INVALID = 20, + BU_STATUS_FIRMWARE_INVALID = 30, + BU_STATUS_UNKNOWN_ERROR = 99, +} mboot_upgrade_status_t; + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ + +/** + * Initializes firmware upgrade session base don selected configuration. + */ +mboot_upgrade_status_t mboot_upgrade_init(void); + +/** + * De-initializes firmware upgrade session. + */ +mboot_upgrade_status_t mboot_upgrade_deinit(void); + +/** + * Write block of data to memory. + * Note that data is not immediately written but rather buffered + * and flushed when buffer is full. + **/ +mboot_upgrade_status_t mboot_upgrade_write_data(uint16_t block_num, const uint8_t *data, uint16_t block_length); + +/** + * Finalize firmware upgrade by: + * - flushing remaining data from buffer and write to memory + **/ +mboot_upgrade_status_t mboot_upgrade_finalize(void); + +/** + * Check and validate firmware upgrade: + * - check written data + * - valdiate firmware image if check ok + **/ +mboot_upgrade_status_t mboot_upgrade_validate(void); + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_UPGRADE_H diff --git a/ports/mimxrt/mboot/mboot_utils.c b/ports/mimxrt/mboot/mboot_utils.c new file mode 100644 index 0000000000000..b6d533e67f025 --- /dev/null +++ b/ports/mimxrt/mboot/mboot_utils.c @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mboot_utils.h" +#include CPU_HEADER_H + +void mboot_utils_uid(uint8_t uid[]) { + *(uint32_t *)&uid[0] = OCOTP->CFG0; + *(uint32_t *)&uid[4] = OCOTP->CFG1; +} diff --git a/ports/mimxrt/mboot/mboot_utils.h b/ports/mimxrt/mboot/mboot_utils.h new file mode 100644 index 0000000000000..2f9501dfd9fdb --- /dev/null +++ b/ports/mimxrt/mboot/mboot_utils.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_UTILS_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_UTILS_H + +#include +#include + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +uint32_t mboot_utils_crc32(uint8_t *data, size_t len); +void mboot_utils_uid(uint8_t uid[]); + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_UTILS_H diff --git a/ports/mimxrt/mboot/mboot_validate.c b/ports/mimxrt/mboot/mboot_validate.c new file mode 100644 index 0000000000000..83c03f2a52d3a --- /dev/null +++ b/ports/mimxrt/mboot/mboot_validate.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include "mboot_validate.h" + +// --------------------------------------------------------------------+ +// Local Variables +// --------------------------------------------------------------------+ +const uint32_t crc_polynomial = 0x04C11DB7; +const uint32_t crc_initial = 0xFFFFFFFF; +const uint32_t crc_final_xor = 0xFFFFFFFF; + +// --------------------------------------------------------------------+ +// Local Function Declarations +// --------------------------------------------------------------------+ +static uint8_t reflect8(uint8_t val); +static uint32_t reflect32(uint32_t val); +static uint32_t crc32(const uint8_t *data, size_t len); + +// --------------------------------------------------------------------+ +// Global Function Definitions +// --------------------------------------------------------------------+ +mboot_validate_status_t mboot_validate_firmware(fw_header_t *img_header, uintptr_t start_addr) { + uint32_t crc_result = 0UL; + mboot_validate_status_t header_status = mboot_validate_header(img_header); + + if (header_status == FWV_STATUS_OK) { + crc_result = crc32((uint8_t *)start_addr, img_header->size - FW_HEADER_SIZE); + + if (crc_result == img_header->crc) { + return FWV_STATUS_OK; + } else { + return FWV_STATUS_IMAGE_INVALID_CRC_ERROR; + } + } else { + return header_status; + } +} + +mboot_validate_status_t mboot_validate_header(fw_header_t *img_header) { + uint32_t crc_result = 0UL; + + if (strncmp(img_header->magic, FW_HEADER_MAGIC, FW_HEADER_MAGIC_SIZE) == 0) { + crc_result = crc32((uint8_t *)img_header, FW_HEADER_SIZE - FW_HEADER_CRC_SIZE); + + if (crc_result == img_header->header_crc) { + return FWV_STATUS_OK; + } else { + return FWV_STATUS_HEADER_INVALID_CRC_ERROR; + } + } else { + return FWV_STATUS_HEADER_INVALID_MAGIC_ERROR; + } +} + +// --------------------------------------------------------------------+ +// Local Function Definitions +// --------------------------------------------------------------------+ +static uint8_t reflect8(uint8_t val) { + uint8_t resVal = 0; + + for (int i = 0; i < 8; i++) { + if ((val & (1 << i)) != 0) { + resVal |= (uint8_t)(1 << (7 - i)); + } + } + + return resVal; +} + +static uint32_t reflect32(uint32_t val) { + uint32_t resVal = 0; + + for (int i = 0; i < 32; i++) { + if ((val & (1 << i)) != 0) { + resVal |= (uint32_t)(1 << (31 - i)); + } + } + + return resVal; +} + +uint32_t crc32(const uint8_t *data, size_t len) { + uint32_t crc = crc_initial; + + for (int i = 0; i < len; ++i) { + uint8_t curByte = reflect8(data[i]); + + crc ^= (uint32_t)(curByte << 24); /* move byte into MSB of 32bit CRC */ + + for (int i = 0; i < 8; i++) { + if ((crc & 0x80000000) != 0) { /* test for MSB = bit 31 */ + crc = (uint32_t)((crc << 1) ^ crc_polynomial); + } else { + crc <<= 1; + } + } + } + crc = reflect32(crc); + return (uint32_t)(crc ^ crc_final_xor); +} diff --git a/ports/mimxrt/mboot/mboot_validate.h b/ports/mimxrt/mboot/mboot_validate.h new file mode 100644 index 0000000000000..05efe8c47a79c --- /dev/null +++ b/ports/mimxrt/mboot/mboot_validate.h @@ -0,0 +1,48 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_VALIDATE_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_VALIDATE_H + +#include +#include +#include "fw_header.h" + +typedef enum _mboot_validate_status_t { + FWV_STATUS_OK = 1, + FWV_STATUS_HEADER_INVALID_MAGIC_ERROR = 10, + FWV_STATUS_HEADER_INVALID_CRC_ERROR = 11, + FWV_STATUS_IMAGE_INVALID_CRC_ERROR = 20, + FWV_STATUS_UNKNOWN_ERROR = 99, +} mboot_validate_status_t; + +// --------------------------------------------------------------------+ +// Global Function Declarations +// --------------------------------------------------------------------+ +mboot_validate_status_t mboot_validate_firmware(fw_header_t *img_header, uintptr_t start_addr); +mboot_validate_status_t mboot_validate_header(fw_header_t *img_header); + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_VALIDATE_H diff --git a/ports/mimxrt/mboot/shared/fw_header.h b/ports/mimxrt/mboot/shared/fw_header.h new file mode 100644 index 0000000000000..c498f9954df19 --- /dev/null +++ b/ports/mimxrt/mboot/shared/fw_header.h @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_FW_HEADER_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_FW_HEADER_H + +#include +#include + +#define FW_HEADER_MAGIC "MPFW" +#define FW_HEADER_MAGIC_SIZE (4) +#define FW_HEADER_SIZE (sizeof(fw_header_t)) +#define FW_HEADER_CRC_SIZE (sizeof(fw_header_crc_t)) + +typedef uint32_t fw_header_crc_t; + +typedef struct _fw_header_t +{ + char magic[FW_HEADER_MAGIC_SIZE]; // 'MPFW' + uint8_t reserved0[4]; + fw_header_crc_t crc; + size_t size; + uintptr_t entry_addr; + uint8_t reserved1[8]; + fw_header_crc_t header_crc; +}fw_header_t; + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_FW_HEADER_H diff --git a/ports/mimxrt/mboot/shared/mboot_command.h b/ports/mimxrt/mboot/shared/mboot_command.h new file mode 100644 index 0000000000000..689ec1fb46764 --- /dev/null +++ b/ports/mimxrt/mboot/shared/mboot_command.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Philipp Ebensberger + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_MIMXRT_MBOOT_COMMAND_H +#define MICROPY_INCLUDED_MIMXRT_MBOOT_COMMAND_H + +#define BOOT_COMMAND_MAGIC (0xCAFECAFE) +#define BOOT_COMMAND_RESET_DFU (0xA55A55AA) + +typedef struct _mboot_command_t +{ + uint32_t magic; + uint32_t command; +} mboot_command_t; + +#endif // MICROPY_INCLUDED_MIMXRT_MBOOT_COMMAND_H diff --git a/ports/mimxrt/mboot/tools/patch-binary.py b/ports/mimxrt/mboot/tools/patch-binary.py new file mode 100644 index 0000000000000..5141737a7cc36 --- /dev/null +++ b/ports/mimxrt/mboot/tools/patch-binary.py @@ -0,0 +1,118 @@ +#!/usr/bin/python +import sys +import struct +import argparse +import binascii +from collections import namedtuple +from pprint import pprint + + +FirmwareImageHeader = namedtuple( + "FirmwareImageHeader", + "magic crc size entry_addr header_crc", +) + +format_specifier_header = "<4s4xIII8xI" + + +def convert_size(byte_size): + if byte_size > (1024 * 1024 * 1024): + format_specifier = "GB" + byte_size = byte_size / (1024 * 1024 * 1024) + elif byte_size > (1024 * 1024): + format_specifier = "MB" + byte_size = byte_size / (1024 * 1024) + elif byte_size > 1024: + format_specifier = "KB" + byte_size = byte_size / 1024 + else: + format_specifier = "B" + byte_size = byte_size + + return "{0}{1}".format( + str(round(byte_size, 1) if byte_size % 1 else int(byte_size)), format_specifier + ) + + +def patch_binary(bin_data, offset, fill_to): + format_specifier_header = "<4s4xIII8xI" + format_specifier_header_crc = format_specifier_header[:-1] + header_size = struct.calcsize(format_specifier_header) + # + raw_header = FirmwareImageHeader._make( + struct.unpack(format_specifier_header, bin_data[0:header_size]) + ) + # Set image size attribute + size = len(bin_data) + crc = binascii.crc32(bin_data[header_size:]) & 0xFFFFFFFF + + patched_header = struct.pack( + format_specifier_header_crc, + raw_header.magic, + crc, + size, + raw_header.entry_addr, + ) + header_crc = binascii.crc32(patched_header) + patched_header += struct.pack("= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { + return NULL; + } + if (index == USBD_STR_SERIAL) { + uint8_t uid[8]; + mboot_utils_uid(uid); + // store it as a hex string + for (len = 0; len < 16; len += 2) { + desc_str[1 + len] = hexchr[uid[len / 2] >> 4]; + desc_str[1 + len + 1] = hexchr[uid[len / 2] & 0x0f]; + } + } else { + const char *str = usbd_desc_str[index]; + for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { + desc_str[1 + len] = str[len]; + } + } + } + + // first byte is length (including header), second byte is string type + desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2); + + return desc_str; +} diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index 27ab987ed17f2..05aa3d6f02216 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "extmod/vfs.h" #include "modmimxrt.h" +#include "flash.h" #include BOARD_FLASH_OPS_HEADER_H #define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) @@ -60,52 +61,6 @@ STATIC mimxrt_flash_obj_t mimxrt_flash_obj = { .base = { &mimxrt_flash_type } }; -// flash_erase_block(erase_addr_bytes) -// erases the sector starting at addr. Sector size according to the flash properties. -status_t flash_erase_block(uint32_t erase_addr) __attribute__((section(".ram_functions"))); -status_t flash_erase_block(uint32_t erase_addr) { - status_t status; - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); - __disable_irq(); - status = flexspi_nor_flash_erase_sector(FLEXSPI, erase_addr); - __enable_irq(); - SCB_EnableDCache(); - return status; -} - -// flash_write_block(flash_dest_addr_bytes, data_source, length_bytes) -// writes length_byte data to the destination address -// the vfs driver takes care for erasing the sector if required -status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) __attribute__((section(".ram_functions"))); -status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { - status_t status = 0; - uint32_t size; - uint32_t next_addr; - - SCB_CleanInvalidateDCache(); - SCB_DisableDCache(); - // write data in chunks not crossing a page boundary - while (length > 0) { - next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary - size = next_addr - dest_addr; // maximal chunk length - if (size > length) { // compare against remaining data size - size = length; - } - __disable_irq(); - status = flexspi_nor_flash_page_program(FLEXSPI, dest_addr, (uint32_t *)src, size); - __enable_irq(); - if (status != kStatus_Success) { - break; - } - length -= size; - src += size; - dest_addr += size; - } - SCB_EnableDCache(); - return status; -} - STATIC mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { // Check args. mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -161,7 +116,7 @@ STATIC mp_obj_t mimxrt_flash_writeblocks(size_t n_args, const mp_obj_t *args) { uint32_t offset = mp_obj_get_int(args[1]) * SECTOR_SIZE_BYTES; if (n_args == 3) { - status = flash_erase_block(self->flash_base + offset); + status = flash_erase_sector(self->flash_base + offset); if (status != kStatus_Success) { mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("flash erase command failed with %d"), status); @@ -199,7 +154,7 @@ STATIC mp_obj_t mimxrt_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t a return MP_OBJ_NEW_SMALL_INT(SECTOR_SIZE_BYTES); case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { uint32_t offset = mp_obj_get_int(arg_in) * SECTOR_SIZE_BYTES; - status = flash_erase_block(self->flash_base + offset); + status = flash_erase_sector(self->flash_base + offset); return MP_OBJ_NEW_SMALL_INT(status != kStatus_Success); } default: diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index ad4e4a59412e8..9e421a6a7617d 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -38,6 +38,7 @@ #include "modmachine.h" #include "fsl_clock.h" #include "fsl_wdog.h" +#include "mboot/shared/mboot_command.h" #if MICROPY_PY_MACHINE @@ -51,6 +52,14 @@ typedef enum { MP_SOFT_RESET } reset_reason_t; +__attribute__((section("._bl_command"))) mboot_command_t bl_command; + +void reset_boot(void) { + bl_command.magic = BOOT_COMMAND_MAGIC; + bl_command.command = BOOT_COMMAND_RESET_DFU; + NVIC_SystemReset(); +} + STATIC mp_obj_t machine_unique_id(void) { unsigned char id[8]; mp_hal_get_unique_id(id); @@ -84,6 +93,12 @@ STATIC mp_obj_t machine_reset_cause(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); +STATIC mp_obj_t machine_bootloader(void) { + reset_boot(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_bootloader_obj, machine_bootloader); + STATIC mp_obj_t machine_freq(void) { return MP_OBJ_NEW_SMALL_INT(mp_hal_get_cpu_freq()); } @@ -114,6 +129,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, + { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) }, { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, diff --git a/ports/mimxrt/tusb_config.h b/ports/mimxrt/tusb_config.h index 862fb6c52d2e5..1926e02c68d5a 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/tusb_config.h @@ -33,4 +33,6 @@ #define CFG_TUD_CDC_RX_BUFSIZE (512) #define CFG_TUD_CDC_TX_BUFSIZE (512) +#define CFG_TUD_DFU_RUNTIME (1) + #endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index 55855674c6293..d25c1bddf1e37 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -39,11 +39,16 @@ #define MICROPY_HW_USB_STR_MANUF ("MicroPython") #endif -#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) -#define USBD_MAX_POWER_MA (250) +enum +{ + ITF_NUM_CDC, // needs 2 interfaces + ITF_NUM_CDC_DUMMY, + ITF_NUM_DFU_RT, + ITF_NUM_TOTAL +}; -#define USBD_ITF_CDC (0) // needs 2 interfaces -#define USBD_ITF_MAX (2) +#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_DFU_RT_DESC_LEN) +#define USBD_MAX_POWER_MA (250) #define USBD_CDC_EP_CMD (0x81) #define USBD_CDC_EP_OUT (0x02) @@ -51,11 +56,16 @@ #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE (512) -#define USBD_STR_0 (0x00) -#define USBD_STR_MANUF (0x01) -#define USBD_STR_PRODUCT (0x02) -#define USBD_STR_SERIAL (0x03) -#define USBD_STR_CDC (0x04) +enum { + USBD_STR_0, + USBD_STR_MANUF, + USBD_STR_PRODUCT, + USBD_STR_SERIAL, + USBD_STR_CDC, + USBD_STR_DFU, +}; + +extern void reset_boot(void); // Note: descriptors returned from callbacks must exist long enough for transfer to complete @@ -77,11 +87,14 @@ static const tusb_desc_device_t usbd_desc_device = { }; static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, USBD_STR_0, USBD_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), - TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), + + // Interface number, string index, attributes, detach timeout, transfer size */ + TUD_DFU_RT_DESCRIPTOR(ITF_NUM_DFU_RT, USBD_STR_DFU, 0x0d, 1000, 4096), }; static const char *const usbd_desc_str[] = { @@ -89,6 +102,7 @@ static const char *const usbd_desc_str[] = { [USBD_STR_PRODUCT] = MICROPY_HW_BOARD_NAME, [USBD_STR_SERIAL] = "00000000000000000000", [USBD_STR_CDC] = "Board CDC", + [USBD_STR_DFU] = "Board DFU Runtime" }; const uint8_t *tud_descriptor_device_cb(void) { @@ -136,3 +150,7 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { return desc_str; } + +void tud_dfu_runtime_reboot_to_dfu_cb() { + reset_boot(); +}