From a5629f205e6a12752bcb9a941d371e52179fddf4 Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 24 Nov 2023 10:47:37 -0500 Subject: [PATCH] RT1020 and RT1060 OTA --- Makefile | 3 +- .../Makefile | 13 +- .../flash_image.c | 2 +- .../rt1020-evk-make-baremetal-builtin/link.ld | 6 +- .../rt1020-evk-make-baremetal-builtin/main.c | 64 ++-- .../mongoose_custom.h | 2 + .../Makefile | 12 +- .../rt1060-evk-make-baremetal-builtin/link.ld | 6 +- .../rt1060-evk-make-baremetal-builtin/main.c | 48 ++- .../mongoose_custom.h | 2 + mongoose.c | 313 +++++++++++++++++- mongoose.h | 9 + src/device.h | 9 + src/device_flash.c | 23 +- src/device_imxrt.c | 278 ++++++++++++++++ src/ota_flash.c | 8 +- 16 files changed, 719 insertions(+), 79 deletions(-) create mode 100644 src/device_imxrt.c diff --git a/Makefile b/Makefile index 818a42ccb67..c804f6b8df5 100644 --- a/Makefile +++ b/Makefile @@ -185,5 +185,4 @@ clean: clean_examples clean_embedded #find examples -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C clean_embedded: - for X in $(EXAMPLES_EMBEDDED); do test -f $$X/Makefile || continue; $(MAKE) -C $$X clean || exit 1; done - + for X in $(EXAMPLES_EMBEDDED); do test -f $$X/Makefile || continue; $(MAKE) -C $$X clean || exit 1; done \ No newline at end of file diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile b/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile index 130cdadbe32..fc9f09d5c1f 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile @@ -3,15 +3,15 @@ CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_mcu/devices/MIMXRT1021 #-DCPU_MIMXRT1021DAG5A CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA) -LDSCRIPT = link_ram.ld -LDFLAGS ?= -T$(LDSCRIPT) -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map +LDSCRIPT = link.ld +LDFLAGS ?= -T$(LDSCRIPT) -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map SOURCES = main.c syscalls.c sysinit.c SOURCES += cmsis_mcu/devices/MIMXRT1021/gcc/startup_MIMXRT1021.S # NXP startup file. Compiler-dependent! CFLAGS += -D__ATOLLIC__ -D__STARTUP_CLEAR_BSS # Make startup code work as expected # Mongoose options are defined in mongoose_custom.h -SOURCES += mongoose.c +SOURCES += mongoose.c flash_image.c net.c packed_fs.c # Example specific build options. See README.md CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\" @@ -24,11 +24,6 @@ endif all build example: firmware.bin -image: LDSCRIPT = link.ld -image: SOURCES += flash_image.c net.c packed_fs.c # no room to run dashboard in RAM w/ default config -image: CFLAGS += -DRUNINFLASH -image: firmware.bin - firmware.bin: firmware.elf arm-none-eabi-objcopy -O binary $< $@ @@ -71,7 +66,7 @@ test: update curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt grep 'READY, IP:' /tmp/output.txt # Check for network init -testimage: image +testimage: firmware.bin clean: $(RM) firmware.* *.su cmsis_core cmsis_mcu mbedtls *.zip diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/flash_image.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/flash_image.c index e83a9f79e3f..8ed5a4f3da0 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/flash_image.c +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/flash_image.c @@ -1,6 +1,6 @@ #include "dcd.h" // pin settings for MIMXRT1020-EVK board -#include "flexspi.h" // peripheral structures #include "hal.h" +#include "flexspi.h" // peripheral structures extern uint32_t __isr_vector[]; diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld b/examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld index d3f5c6f1490..16cb844822a 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld @@ -2,7 +2,7 @@ ENTRY(Reset_Handler); MEMORY { flash_hdr(rx) : ORIGIN = 0x60000000, LENGTH = 8k flash_irq(rx) : ORIGIN = 0x60002000, LENGTH = 1k - flash_code(rx) : ORIGIN = 0x60002400, LENGTH = 8183k + flash_code(rx) : ORIGIN = 0x60002400, LENGTH = 8179k itcram(rx) : ORIGIN = 0x00000000, LENGTH = 64k dtcram(rw) : ORIGIN = 0x20000000, LENGTH = 64k @@ -15,8 +15,8 @@ SECTIONS { KEEP(*(.dat)) . = 0x1030 ; KEEP(*(.dcd)) . = 0x2000 ;} >flash_hdr .irq : { KEEP(*(.isr_vector)) } > flash_irq .text : { *(.text* .text.*) *(.rodata*) ; } > flash_code - .data : { __data_start__ = .; *(.data SORT(.data.*)) __data_end__ = .; } > dtcram AT > flash_code + .data : { __data_start__ = .; *(.data SORT(.data.*)) *(.iram) __data_end__ = .; } > itcram AT > flash_code __etext = LOADADDR(.data); - .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > dtcram + .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > itcram _end = .; } diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c index e41e5cd0fce..921e7efa7e2 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c @@ -31,28 +31,18 @@ static void timer_fn(void *arg) { ifp->ndrop, ifp->nerr)); } -#ifndef RUNINFLASH -static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) fn_data; - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - if (mg_http_match_uri(hm, "/api/hello")) { // Request to /api/hello - mg_http_reply(c, 200, "", "{%m:%u,%m:%u,%m:%u,%m:%u,%m:%u}\n", - MG_ESC("eth"), ifp->state, MG_ESC("frames_received"), - ifp->nrecv, MG_ESC("frames_sent"), ifp->nsent, - MG_ESC("frames_dropped"), ifp->ndrop, - MG_ESC("interface_errors"), ifp->nerr); - } else if (mg_http_match_uri(hm, "/")) { // Index page - mg_http_reply( - c, 200, "", "%s", - "" - "

Welcome to Mongoose

" - "See /api/hello for REST example" - ""); - } else { // All other URIs - mg_http_reply(c, 404, "", "Not Found\n"); - } - } +#ifdef MQTT_DASHBOARD +bool hal_gpio_write(int pin, bool status) { // For MQTT dashboard HAL + return (pin != status); +} +bool hal_gpio_read(int pin) { // For MQTT dashboard HAL + return (pin != 0); +} +int hal_led_pin(void) { + return (int) 0; +} +uint64_t mg_now(void) { + return mg_millis(); } #endif @@ -60,12 +50,38 @@ int main(void) { gpio_output(LED); // Setup blue LED uart_init(UART_DEBUG, 115200); // Initialise debug printf ethernet_init(); // Initialise ethernet pins + MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); struct mg_mgr mgr; // Initialise mg_mgr_init(&mgr); // Mongoose event manager mg_log_set(MG_LL_DEBUG); // Set log level + mg_ota_boot(); + +#ifdef MQTT_DASHBOARD + // User can customise the MQTT url, device ID or the root topic below +#define DEVICE_ID "RT1020" + g_url = MQTT_SERVER_URL; + g_device_id = DEVICE_ID; + g_root_topic = MQTT_ROOT_TOPIC; +#endif + + #if MG_OTA == MG_OTA_FLASH + // Demonstrate the use of mg_flash_{load/save} functions for keeping device + // configuration data on flash. Increment boot count on every boot. + struct deviceconfig { + uint32_t boot_count; + char some_other_data[40]; + }; + uint32_t key = 0x12345678; // A unique key, one per data type + struct deviceconfig dc = {}; // Initialise to some default values + mg_flash_load(NULL, key, &dc, sizeof(dc)); // Load from flash + dc.boot_count++; // Increment boot count + mg_flash_save(NULL, key, &dc, sizeof(dc)); // And save back + MG_INFO(("Boot count: %u", dc.boot_count)); +#endif + // Initialise Mongoose network stack struct mg_tcpip_driver_imxrt_data driver_data = {.mdc_cr = 24, .phy_addr = 2}; struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(), @@ -84,11 +100,7 @@ int main(void) { } MG_INFO(("Initialising application...")); -#ifdef RUNINFLASH web_init(&mgr); -#else - mg_http_listen(&mgr, "http://0.0.0.0:80", fn, &mif); -#endif MG_INFO(("Starting event loop")); for (;;) { diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h index 450587c69a2..9918b8f665b 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h @@ -2,6 +2,8 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#define MG_OTA MG_OTA_FLASH +#define MG_DEVICE MG_DEVICE_RT1020 #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/Makefile b/examples/nxp/rt1060-evk-make-baremetal-builtin/Makefile index b7e4ca57639..046c9967715 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/Makefile +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/Makefile @@ -3,15 +3,15 @@ CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_mcu/devices/MIMXRT1062 #-DCPU_MIMXRT1062DVL6B CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA) -LDSCRIPT = link_ram.ld -LDFLAGS ?= -T$(LDSCRIPT) -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map +LDSCRIPT = link.ld +LDFLAGS ?= -T$(LDSCRIPT) -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map SOURCES = main.c syscalls.c sysinit.c SOURCES += cmsis_mcu/devices/MIMXRT1062/gcc/startup_MIMXRT1062.S # NXP startup file. Compiler-dependent! CFLAGS += -D__ATOLLIC__ -D__STARTUP_CLEAR_BSS # Make startup code work as expected # Mongoose options are defined in mongoose_custom.h -SOURCES += mongoose.c net.c packed_fs.c +SOURCES += mongoose.c flash_image.c net.c packed_fs.c # Example specific build options. See README.md CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\" @@ -24,10 +24,6 @@ endif all build example: firmware.bin -image: LDSCRIPT = link.ld -image: SOURCES += flash_image.c -image: firmware.bin - firmware.bin: firmware.elf arm-none-eabi-objcopy -O binary $< $@ @@ -70,7 +66,7 @@ test: update curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt grep 'READY, IP:' /tmp/output.txt # Check for network init -testimage: image +testimage: firmware.bin clean: $(RM) firmware.* *.su cmsis_core cmsis_mcu mbedtls *.zip diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/link.ld b/examples/nxp/rt1060-evk-make-baremetal-builtin/link.ld index a12d04c9878..2af764767a3 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/link.ld +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/link.ld @@ -2,7 +2,7 @@ ENTRY(Reset_Handler); MEMORY { flash_hdr(rx) : ORIGIN = 0x60000000, LENGTH = 8k flash_irq(rx) : ORIGIN = 0x60002000, LENGTH = 1k - flash_code(rx) : ORIGIN = 0x60002400, LENGTH = 8183k + flash_code(rx) : ORIGIN = 0x60002400, LENGTH = 8179k itcram(rx) : ORIGIN = 0x00000000, LENGTH = 128k dtcram(rw) : ORIGIN = 0x20000000, LENGTH = 128k @@ -15,8 +15,8 @@ SECTIONS { KEEP(*(.dat)) . = 0x1030 ; KEEP(*(.dcd)) . = 0x2000 ;} >flash_hdr .irq : { KEEP(*(.isr_vector)) } > flash_irq .text : { *(.text* .text.*) *(.rodata*) ; } > flash_code - .data : { __data_start__ = .; *(.data SORT(.data.*)) __data_end__ = .; } > dtcram AT > flash_code + .data : { __data_start__ = .; *(.data SORT(.data.*)) *(.iram) __data_end__ = .; } > itcram AT > flash_code __etext = LOADADDR(.data); - .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > dtcram + .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > itcram _end = .; } diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c index 3fd164e5152..5de830711c2 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c @@ -22,6 +22,7 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); } } + static void timer_fn(void *arg) { gpio_toggle(LED); // Blink LED struct mg_tcpip_if *ifp = arg; // And show @@ -31,16 +32,57 @@ static void timer_fn(void *arg) { ifp->ndrop, ifp->nerr)); } +#ifdef MQTT_DASHBOARD +bool hal_gpio_write(int pin, bool status) { // For MQTT dashboard HAL + return (pin != status); +} +bool hal_gpio_read(int pin) { // For MQTT dashboard HAL + return (pin != 0); +} +int hal_led_pin(void) { + return (int) 0; +} +uint64_t mg_now(void) { + return mg_millis(); +} +#endif + int main(void) { - gpio_output(LED); // Setup blue LED + gpio_output(LED); // Setup green LED uart_init(UART_DEBUG, 115200); // Initialise debug printf ethernet_init(); // Initialise ethernet pins - MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); struct mg_mgr mgr; // Initialise mg_mgr_init(&mgr); // Mongoose event manager mg_log_set(MG_LL_DEBUG); // Set log level + MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); + + mg_ota_boot(); + +#ifdef MQTT_DASHBOARD + // User can customise the MQTT url, device ID or the root topic below +#define DEVICE_ID "RT1060" + g_url = MQTT_SERVER_URL; + g_device_id = DEVICE_ID; + g_root_topic = MQTT_ROOT_TOPIC; +#endif + + #if MG_OTA == MG_OTA_FLASH + // Demonstrate the use of mg_flash_{load/save} functions for keeping device + // configuration data on flash. Increment boot count on every boot. + struct deviceconfig { + uint32_t boot_count; + char some_other_data[40]; + }; + uint32_t key = 0x12345678; // A unique key, one per data type + struct deviceconfig dc = {}; // Initialise to some default values + mg_flash_load(NULL, key, &dc, sizeof(dc)); // Load from flash + dc.boot_count++; // Increment boot count + mg_flash_save(NULL, key, &dc, sizeof(dc)); // And save back + MG_INFO(("Boot count: %u", dc.boot_count)); +#endif + // Initialise Mongoose network stack struct mg_tcpip_driver_imxrt_data driver_data = {.mdc_cr = 24, .phy_addr = 2}; struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(), @@ -65,6 +107,6 @@ int main(void) { for (;;) { mg_mgr_poll(&mgr, 0); } - return 0; } + diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h index 450587c69a2..55503eaa336 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h @@ -2,6 +2,8 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#define MG_OTA MG_OTA_FLASH +#define MG_DEVICE MG_DEVICE_RT1060 #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 diff --git a/mongoose.c b/mongoose.c index b892772c5e5..771f4a06b3a 100644 --- a/mongoose.c +++ b/mongoose.c @@ -239,7 +239,8 @@ void mg_device_reset(void) { #endif -#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 +#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 || \ + MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 // Flash can be written only if it is erased. Erased flash is 0xff (all bits 1) // Writes must be mg_flash_write_align() - aligned. Thus if we want to save an // object, we pad it at the end for alignment. @@ -272,10 +273,11 @@ static char *flash_last_sector(void) { // Find a saved object with a given key bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) { - char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL; + char *base = (char *) mg_flash_start() + MG_FLASH_OFFSET; + char *s = (char *) sector, *res = NULL; size_t ss = mg_flash_sector_size(), ofs = 0, n, sz; bool ok = false; - if (s == NULL) s = flash_last_sector(); + if (sector == NULL) s = flash_last_sector() + MG_FLASH_OFFSET; if (s < base || s >= base + mg_flash_size()) { MG_ERROR(("%p is outsize of flash", sector)); } else if (((s - base) % ss) != 0) { @@ -307,7 +309,8 @@ static void mg_flash_sector_cleanup(char *sector) { uint32_t key; // Traverse all objects MG_DEBUG(("Cleaning up sector %p", sector)); - while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) { + while ((n = mg_flash_next(sector + ofs + MG_FLASH_OFFSET, + sector + ss + MG_FLASH_OFFSET, &key, &size)) > 0) { // Delete an old copy of this object in the cache for (size_t o = 0; o < io.len; o += size2 + hs) { uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t)); @@ -318,7 +321,7 @@ static void mg_flash_sector_cleanup(char *sector) { } } // And add the new copy - mg_iobuf_add(&io, io.len, sector + ofs, size + hs); + mg_iobuf_add(&io, io.len, sector + ofs + MG_FLASH_OFFSET, size + hs); ofs += n; } // All objects are cached in RAM now @@ -347,13 +350,17 @@ bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) { uint32_t hdr[2] = {(uint32_t) len, key}; size_t needed = sizeof(hdr) + len; size_t needed_aligned = MG_ROUND_UP(needed, sizeof(ab)); - while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n; + while ((n = mg_flash_next(s + ofs + MG_FLASH_OFFSET, + s + ss + MG_FLASH_OFFSET, NULL, NULL)) > 0) + ofs += n; // If there is not enough space left, cleanup sector and re-eval ofs - if (ofs + needed_aligned > ss) { + if (ofs + needed_aligned >= ss) { mg_flash_sector_cleanup(s); ofs = 0; - while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n; + while ((n = mg_flash_next(s + ofs + MG_FLASH_OFFSET, + s + ss + MG_FLASH_OFFSET, NULL, NULL)) > 0) + ofs += n; } if (ofs + needed_aligned <= ss) { @@ -409,6 +416,288 @@ bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) { } #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/device_imxrt.c" +#endif + + + +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 + +#include /**************************************************************************************************************************/ +// identical for both 1020 and 1060, they have the same controller. This should be part of Mongoose, not read from the example + + +// these are defined in the device CMSIS .h file, so examples will have their own, we should use a different name or #undef +// hence, the example flexspi.h does not have these macros, that is NXP code carrying their license +// Mongoose does not include any CMSIS file but these would go into mongoose.h and the user including mongoose.h and the device .h both definitions would clash +#ifndef FLEXSPI_LUT_OPERAND0 +#define FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU) +#define FLEXSPI_LUT_NUM_PADS0(x) \ + (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U) +#define FLEXSPI_LUT_OPCODE0(x) \ + (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U) +#define FLEXSPI_LUT_OPERAND1(x) \ + (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U) +#define FLEXSPI_LUT_NUM_PADS1(x) \ + (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U) +#define FLEXSPI_LUT_OPCODE1(x) \ + (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U) +#endif + +#define FLEXSPI_NOR_INSTANCE 0 + +#if MG_DEVICE == MG_DEVICE_RT1020 +typedef struct { + uint32_t version; + int (*init)(uint32_t instance, flexspi_nor_config_t *config); + int (*program)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t dst_addr, const uint32_t *src); + uint32_t reserved; + int (*erase)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t start, uint32_t lengthInBytes); + uint32_t reserved2; + int (*update_lut)(uint32_t instance, uint32_t seqIndex, + const uint32_t *lutBase, uint32_t seqNumber); + int (*xfer)(uint32_t instance, char *xfer); + void (*clear_cache)(uint32_t instance); +} flexspi_nor_driver_interface_t; +#elif MG_DEVICE == MG_DEVICE_RT1060 +typedef struct { + uint32_t version; + int (*init)(uint32_t instance, flexspi_nor_config_t *config); + int (*program)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t dst_addr, const uint32_t *src); + int (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + int (*erase)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t start, uint32_t lengthInBytes); + int (*read)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + int (*xfer)(uint32_t instance, char *xfer); + int (*update_lut)(uint32_t instance, uint32_t seqIndex, + const uint32_t *lutBase, uint32_t seqNumber); + int (*get_config)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t *option); +} flexspi_nor_driver_interface_t; +#endif + +typedef struct { + const uint32_t version; // Bootloader version number + const char *copyright; // Bootloader Copyright + void (*runBootloader)( + void *arg); // Function to start the bootloader executing + const uint32_t *reserved0; // Reserved + const flexspi_nor_driver_interface_t + *flexSpiNorDriver; // FlexSPI NOR Flash API + const uint32_t *reserved1[2]; // Reserved +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t **) (0x0020001c)) +#define flexspi_nor bootloader->flexSpiNorDriver + + +static bool s_flash_irq_disabled; + +MG_IRAM void *mg_flash_start(void) { + return (void *) 0x0; +} +MG_IRAM size_t mg_flash_size(void) { + return 8 * 1024 * 1024; +} +MG_IRAM size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +MG_IRAM size_t mg_flash_write_align(void) { + return 256; +} +MG_IRAM int mg_flash_bank(void) { + return 0; +} + +MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +// Note: the get_config function below works both for RT1020 and 1060 +#if MG_DEVICE == MG_DEVICE_RT1020 +MG_IRAM static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + flexspi_nor_config_t default_config = { + .memConfig = {.tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad + .csHoldTime = 3, + .csSetupTime = 3, + .controllerMiscOption = MG_BIT(4), + .deviceType = 1, // serial NOR + .sflashPadType = 4, + .serialClkFreq = 7, // 133MHz + .sflashA1Size = 8 * 1024 * 1024, + .lookupTable = __FLEXSPI_QSPI_LUT}, + .pageSize = 256, + .sectorSize = 4 * 1024, + .ipcmdSerialClkFreq = 1, + .blockSize = 64 * 1024, + .isUniformBlockSize = false}; + + *config = default_config; + return 0; +} +#else +MG_IRAM static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + uint32_t options[] = {0xc0000000, 0x00}; + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + uint32_t status = + flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options); + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + if (status) { + MG_ERROR(("Failed to extract flash configuration: status %u", status)); + } + return status; +} +#endif + +uint32_t mg_flash_init() { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + return flexspi_nor->init(FLEXSPI_NOR_INSTANCE, &config); +} + +MG_IRAM bool mg_flash_erase(void *addr) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + return false; + } + // Note: Interrupts must be disabled before any call to the ROM API on RT1020 + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) addr, + mg_flash_sector_size()) == 0); + if (!s_flash_irq_disabled) { + asm("cpsie i"); // // Reenable them after the call + } + MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); + return ok; +} + +MG_IRAM bool mg_flash_swap_bank() { + return true; +} + +static inline void spin(volatile uint32_t count) { + while (count--) (void) 0; +} + +static inline void flash_wait(void) { + while ((*((volatile uint32_t *)(0x402A8000 + 0xE0)) & MG_BIT(1)) == 0) spin(1); +} + +MG_IRAM bool mg_flash_write(void *addr, const void *buf, size_t len) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + + // Note: If we overwrite the flash irq section of the image, we must also + // make sure interrupts are disabled and are not reenabled until we write + // this sector with another irq table. + if ((char *) addr == (char *) MG_FLASH_CODE_OFFSET) { + s_flash_irq_disabled = true; + asm("cpsid i"); + } + + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) { + break; + } + uint32_t status; + if ((char *) buf >= (char *) MG_FLASH_OFFSET) { + // If we copy from FLASH to FLASH, then we first need to copy the source + // to RAM + size_t tmp_buf_size = mg_flash_write_align() / sizeof(uint32_t); + uint32_t tmp[tmp_buf_size]; + + for (size_t i = 0; i < tmp_buf_size; i++) { + flash_wait(); + tmp[i] = src[i]; + } + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, + (uint32_t) dst, tmp); + } else { + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, + (uint32_t) dst, src); + } + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) { + ok = false; + } + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); + + if ((char *) addr == (char *) MG_FLASH_CODE_OFFSET) { + s_flash_irq_disabled = false; + asm("cpsie i"); + } + + return ok; +} + +MG_IRAM void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} + +#if MG_DEVICE == MG_DEVICE_RT1060 +MG_IRAM bool mg_flash_erase_all(void) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + bool ok = (flexspi_nor->erase_all(FLEXSPI_NOR_INSTANCE, &config) == 0); + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + MG_DEBUG(("Chip erase: %s", ok ? "Success" : "Failure")); + return ok; +} +#endif + +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/device_stm32h5.c" #endif @@ -5706,7 +5995,7 @@ bool mg_ota_end(void) { bool ok = false; if (s_size) { size_t size = s_addr - base; - uint32_t crc32 = mg_crc32(0, base, s_size); + uint32_t crc32 = mg_crc32(0, base + MG_FLASH_OFFSET, s_size); if (size == s_size && crc32 == s_crc32) { uint32_t now = (uint32_t) (mg_now() / 1000); struct mg_otadata od = {crc32, size, now, MG_OTA_FIRST_BOOT}; @@ -5819,11 +6108,11 @@ MG_IRAM void mg_ota_boot(void) { (void) tmpsector; for (ofs = 0; ofs < max; ofs += ss) { // mg_flash_erase(tmpsector); - mg_flash_write(tmpsector, partition1 + ofs, ss); + mg_flash_write(tmpsector, partition1 + ofs + MG_FLASH_OFFSET, ss); // mg_flash_erase(partition1 + ofs); - mg_flash_write(partition1 + ofs, partition2 + ofs, ss); + mg_flash_write(partition1 + ofs, partition2 + ofs + MG_FLASH_OFFSET, ss); // mg_flash_erase(partition2 + ofs); - mg_flash_write(partition2 + ofs, tmpsector, ss); + mg_flash_write(partition2 + ofs, tmpsector + MG_FLASH_OFFSET, ss); } mg_device_reset(); } diff --git a/mongoose.h b/mongoose.h index ca54a243618..df67ef87c55 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2674,6 +2674,8 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1020 3 // IMXRT1020 +#define MG_DEVICE_RT1060 4 // IMXRT1060 #define MG_DEVICE_CH32V307 100 // WCH CH32V307 #define MG_DEVICE_CUSTOM 1000 // Custom implementation @@ -2681,6 +2683,13 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function #define MG_DEVICE MG_DEVICE_NONE #endif +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 +#define MG_FLASH_OFFSET 0x60000000 // Offset to memory mapped flash +#define MG_FLASH_CODE_OFFSET 0x2000 // Offset to code from start of flash +#else +#define MG_FLASH_OFFSET 0 +#endif + // Flash information void *mg_flash_start(void); // Return flash start address size_t mg_flash_size(void); // Return flash size diff --git a/src/device.h b/src/device.h index 6215fde8c1c..30531b636d3 100644 --- a/src/device.h +++ b/src/device.h @@ -8,6 +8,8 @@ #define MG_DEVICE_NONE 0 // Dummy system #define MG_DEVICE_STM32H5 1 // STM32 H5 #define MG_DEVICE_STM32H7 2 // STM32 H7 +#define MG_DEVICE_RT1020 3 // IMXRT1020 +#define MG_DEVICE_RT1060 4 // IMXRT1060 #define MG_DEVICE_CH32V307 100 // WCH CH32V307 #define MG_DEVICE_CUSTOM 1000 // Custom implementation @@ -15,6 +17,13 @@ #define MG_DEVICE MG_DEVICE_NONE #endif +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 +#define MG_FLASH_OFFSET 0x60000000 // Offset to memory mapped flash +#define MG_FLASH_CODE_OFFSET 0x2000 // Offset to code from start of flash +#else +#define MG_FLASH_OFFSET 0 +#endif + // Flash information void *mg_flash_start(void); // Return flash start address size_t mg_flash_size(void); // Return flash size diff --git a/src/device_flash.c b/src/device_flash.c index c7de46e15b5..3943a953b8d 100644 --- a/src/device_flash.c +++ b/src/device_flash.c @@ -1,6 +1,7 @@ #include "device.h" -#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 +#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 || \ + MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 // Flash can be written only if it is erased. Erased flash is 0xff (all bits 1) // Writes must be mg_flash_write_align() - aligned. Thus if we want to save an // object, we pad it at the end for alignment. @@ -33,10 +34,11 @@ static char *flash_last_sector(void) { // Find a saved object with a given key bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) { - char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL; + char *base = (char *) mg_flash_start() + MG_FLASH_OFFSET; + char *s = (char *) sector, *res = NULL; size_t ss = mg_flash_sector_size(), ofs = 0, n, sz; bool ok = false; - if (s == NULL) s = flash_last_sector(); + if (sector == NULL) s = flash_last_sector() + MG_FLASH_OFFSET; if (s < base || s >= base + mg_flash_size()) { MG_ERROR(("%p is outsize of flash", sector)); } else if (((s - base) % ss) != 0) { @@ -68,7 +70,8 @@ static void mg_flash_sector_cleanup(char *sector) { uint32_t key; // Traverse all objects MG_DEBUG(("Cleaning up sector %p", sector)); - while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) { + while ((n = mg_flash_next(sector + ofs + MG_FLASH_OFFSET, + sector + ss + MG_FLASH_OFFSET, &key, &size)) > 0) { // Delete an old copy of this object in the cache for (size_t o = 0; o < io.len; o += size2 + hs) { uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t)); @@ -79,7 +82,7 @@ static void mg_flash_sector_cleanup(char *sector) { } } // And add the new copy - mg_iobuf_add(&io, io.len, sector + ofs, size + hs); + mg_iobuf_add(&io, io.len, sector + ofs + MG_FLASH_OFFSET, size + hs); ofs += n; } // All objects are cached in RAM now @@ -108,13 +111,17 @@ bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) { uint32_t hdr[2] = {(uint32_t) len, key}; size_t needed = sizeof(hdr) + len; size_t needed_aligned = MG_ROUND_UP(needed, sizeof(ab)); - while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n; + while ((n = mg_flash_next(s + ofs + MG_FLASH_OFFSET, + s + ss + MG_FLASH_OFFSET, NULL, NULL)) > 0) + ofs += n; // If there is not enough space left, cleanup sector and re-eval ofs - if (ofs + needed_aligned > ss) { + if (ofs + needed_aligned >= ss) { mg_flash_sector_cleanup(s); ofs = 0; - while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n; + while ((n = mg_flash_next(s + ofs + MG_FLASH_OFFSET, + s + ss + MG_FLASH_OFFSET, NULL, NULL)) > 0) + ofs += n; } if (ofs + needed_aligned <= ss) { diff --git a/src/device_imxrt.c b/src/device_imxrt.c new file mode 100644 index 00000000000..1304374daab --- /dev/null +++ b/src/device_imxrt.c @@ -0,0 +1,278 @@ +#include "device.h" +#include "log.h" + +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 + +#include /**************************************************************************************************************************/ +// identical for both 1020 and 1060, they have the same controller. This should be part of Mongoose, not read from the example + + +// these are defined in the device CMSIS .h file, so examples will have their own, we should use a different name or #undef +// hence, the example flexspi.h does not have these macros, that is NXP code carrying their license +// Mongoose does not include any CMSIS file but these would go into mongoose.h and the user including mongoose.h and the device .h both definitions would clash +#ifndef FLEXSPI_LUT_OPERAND0 +#define FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU) +#define FLEXSPI_LUT_NUM_PADS0(x) \ + (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U) +#define FLEXSPI_LUT_OPCODE0(x) \ + (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U) +#define FLEXSPI_LUT_OPERAND1(x) \ + (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U) +#define FLEXSPI_LUT_NUM_PADS1(x) \ + (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U) +#define FLEXSPI_LUT_OPCODE1(x) \ + (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U) +#endif + +#define FLEXSPI_NOR_INSTANCE 0 + +#if MG_DEVICE == MG_DEVICE_RT1020 +typedef struct { + uint32_t version; + int (*init)(uint32_t instance, flexspi_nor_config_t *config); + int (*program)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t dst_addr, const uint32_t *src); + uint32_t reserved; + int (*erase)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t start, uint32_t lengthInBytes); + uint32_t reserved2; + int (*update_lut)(uint32_t instance, uint32_t seqIndex, + const uint32_t *lutBase, uint32_t seqNumber); + int (*xfer)(uint32_t instance, char *xfer); + void (*clear_cache)(uint32_t instance); +} flexspi_nor_driver_interface_t; +#elif MG_DEVICE == MG_DEVICE_RT1060 +typedef struct { + uint32_t version; + int (*init)(uint32_t instance, flexspi_nor_config_t *config); + int (*program)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t dst_addr, const uint32_t *src); + int (*erase_all)(uint32_t instance, flexspi_nor_config_t *config); + int (*erase)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t start, uint32_t lengthInBytes); + int (*read)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); + void (*clear_cache)(uint32_t instance); + int (*xfer)(uint32_t instance, char *xfer); + int (*update_lut)(uint32_t instance, uint32_t seqIndex, + const uint32_t *lutBase, uint32_t seqNumber); + int (*get_config)(uint32_t instance, flexspi_nor_config_t *config, + uint32_t *option); +} flexspi_nor_driver_interface_t; +#endif + +typedef struct { + const uint32_t version; // Bootloader version number + const char *copyright; // Bootloader Copyright + void (*runBootloader)( + void *arg); // Function to start the bootloader executing + const uint32_t *reserved0; // Reserved + const flexspi_nor_driver_interface_t + *flexSpiNorDriver; // FlexSPI NOR Flash API + const uint32_t *reserved1[2]; // Reserved +} bootloader_api_entry_t; + +#define bootloader (*(bootloader_api_entry_t **) (0x0020001c)) +#define flexspi_nor bootloader->flexSpiNorDriver + + +static bool s_flash_irq_disabled; + +MG_IRAM void *mg_flash_start(void) { + return (void *) 0x0; +} +MG_IRAM size_t mg_flash_size(void) { + return 8 * 1024 * 1024; +} +MG_IRAM size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +MG_IRAM size_t mg_flash_write_align(void) { + return 256; +} +MG_IRAM int mg_flash_bank(void) { + return 0; +} + +MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { + char *base = (char *) mg_flash_start(), *end = base + mg_flash_size(); + volatile char *p = (char *) dst; + return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0; +} + +// Note: the get_config function below works both for RT1020 and 1060 +#if MG_DEVICE == MG_DEVICE_RT1020 +MG_IRAM static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + flexspi_nor_config_t default_config = { + .memConfig = {.tag = FLEXSPI_CFG_BLK_TAG, + .version = FLEXSPI_CFG_BLK_VERSION, + .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad + .csHoldTime = 3, + .csSetupTime = 3, + .controllerMiscOption = MG_BIT(4), + .deviceType = 1, // serial NOR + .sflashPadType = 4, + .serialClkFreq = 7, // 133MHz + .sflashA1Size = 8 * 1024 * 1024, + .lookupTable = __FLEXSPI_QSPI_LUT}, + .pageSize = 256, + .sectorSize = 4 * 1024, + .ipcmdSerialClkFreq = 1, + .blockSize = 64 * 1024, + .isUniformBlockSize = false}; + + *config = default_config; + return 0; +} +#else +MG_IRAM static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + uint32_t options[] = {0xc0000000, 0x00}; + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + uint32_t status = + flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options); + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + if (status) { + MG_ERROR(("Failed to extract flash configuration: status %u", status)); + } + return status; +} +#endif + +uint32_t mg_flash_init() { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + return flexspi_nor->init(FLEXSPI_NOR_INSTANCE, &config); +} + +MG_IRAM bool mg_flash_erase(void *addr) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if (flash_page_start(addr) == false) { + MG_ERROR(("%p is not on a sector boundary", addr)); + return false; + } + // Note: Interrupts must be disabled before any call to the ROM API on RT1020 + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) addr, + mg_flash_sector_size()) == 0); + if (!s_flash_irq_disabled) { + asm("cpsie i"); // // Reenable them after the call + } + MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); + return ok; +} + +MG_IRAM bool mg_flash_swap_bank() { + return true; +} + +static inline void spin(volatile uint32_t count) { + while (count--) (void) 0; +} + +static inline void flash_wait(void) { + while ((*((volatile uint32_t *)(0x402A8000 + 0xE0)) & MG_BIT(1)) == 0) spin(1); +} + +MG_IRAM bool mg_flash_write(void *addr, const void *buf, size_t len) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if ((len % mg_flash_write_align()) != 0) { + MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align())); + return false; + } + uint32_t *dst = (uint32_t *) addr; + uint32_t *src = (uint32_t *) buf; + uint32_t *end = (uint32_t *) ((char *) buf + len); + bool ok = true; + + // Note: If we overwrite the flash irq section of the image, we must also + // make sure interrupts are disabled and are not reenabled until we write + // this sector with another irq table. + if ((char *) addr == (char *) MG_FLASH_CODE_OFFSET) { + s_flash_irq_disabled = true; + asm("cpsid i"); + } + + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) { + break; + } + uint32_t status; + if ((char *) buf >= (char *) MG_FLASH_OFFSET) { + // If we copy from FLASH to FLASH, then we first need to copy the source + // to RAM + size_t tmp_buf_size = mg_flash_write_align() / sizeof(uint32_t); + uint32_t tmp[tmp_buf_size]; + + for (size_t i = 0; i < tmp_buf_size; i++) { + flash_wait(); + tmp[i] = src[i]; + } + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, + (uint32_t) dst, tmp); + } else { + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, + (uint32_t) dst, src); + } + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + src = (uint32_t *) ((char *) src + mg_flash_write_align()); + dst = (uint32_t *) ((char *) dst + mg_flash_write_align()); + if (status != 0) { + ok = false; + } + } + MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); + + if ((char *) addr == (char *) MG_FLASH_CODE_OFFSET) { + s_flash_irq_disabled = false; + asm("cpsie i"); + } + + return ok; +} + +MG_IRAM void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} + +#if MG_DEVICE == MG_DEVICE_RT1060 +MG_IRAM bool mg_flash_erase_all(void) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) { + return false; + } + if (!s_flash_irq_disabled) { + asm("cpsid i"); + } + bool ok = (flexspi_nor->erase_all(FLEXSPI_NOR_INSTANCE, &config) == 0); + if (!s_flash_irq_disabled) { + asm("cpsie i"); + } + MG_DEBUG(("Chip erase: %s", ok ? "Success" : "Failure")); + return ok; +} +#endif + +#endif diff --git a/src/ota_flash.c b/src/ota_flash.c index d52d90c3369..d4844255433 100644 --- a/src/ota_flash.c +++ b/src/ota_flash.c @@ -74,7 +74,7 @@ bool mg_ota_end(void) { bool ok = false; if (s_size) { size_t size = s_addr - base; - uint32_t crc32 = mg_crc32(0, base, s_size); + uint32_t crc32 = mg_crc32(0, base + MG_FLASH_OFFSET, s_size); if (size == s_size && crc32 == s_crc32) { uint32_t now = (uint32_t) (mg_now() / 1000); struct mg_otadata od = {crc32, size, now, MG_OTA_FIRST_BOOT}; @@ -187,11 +187,11 @@ MG_IRAM void mg_ota_boot(void) { (void) tmpsector; for (ofs = 0; ofs < max; ofs += ss) { // mg_flash_erase(tmpsector); - mg_flash_write(tmpsector, partition1 + ofs, ss); + mg_flash_write(tmpsector, partition1 + ofs + MG_FLASH_OFFSET, ss); // mg_flash_erase(partition1 + ofs); - mg_flash_write(partition1 + ofs, partition2 + ofs, ss); + mg_flash_write(partition1 + ofs, partition2 + ofs + MG_FLASH_OFFSET, ss); // mg_flash_erase(partition2 + ofs); - mg_flash_write(partition2 + ofs, tmpsector, ss); + mg_flash_write(partition2 + ofs, tmpsector + MG_FLASH_OFFSET, ss); } mg_device_reset(); }