From ed6566f170e854ab4d10b48d5eed62e3586af106 Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 24 Nov 2023 10:47:37 -0500 Subject: [PATCH] Write IMXRT10xx image to flash --- .../flash_image.c | 2 +- .../flexspi.h | 54 +++- .../mongoose_custom.h | 8 + .../flexspi.h | 45 ++- .../rt1060-evk-make-baremetal-builtin/main.c | 32 ++- .../mongoose_custom.h | 8 + mongoose.c | 261 +++++++++++++++++- mongoose.h | 9 + src/device.h | 8 + src/device_flash.c | 21 +- src/device_imxrt.c | 124 +++++++++ src/ota.h | 1 + src/ota_flash.c | 112 +++++++- 13 files changed, 647 insertions(+), 38 deletions(-) create mode 100644 src/device_imxrt.c 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/flexspi.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/flexspi.h index 602d0c9248f..ebac8754dd1 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/flexspi.h +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/flexspi.h @@ -8,12 +8,19 @@ #include #include -typedef struct _lut_sequence -{ - uint8_t seqNum; //!< Sequence Number, valid number: 1-16 - uint8_t seqId; //!< Sequence Index, valid number: 0-15 - uint16_t reserved; -} flexspi_lut_seq_t; +#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 typedef struct _FlexSPIConfig { @@ -30,12 +37,12 @@ typedef struct _FlexSPIConfig //! Generic configuration, etc. uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for //! DPI/QPI/OPI switch or reset command - flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + uint32_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt //! sequence number, [31:16] Reserved uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe - flexspi_lut_seq_t + uint32_t configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands @@ -64,7 +71,7 @@ typedef struct _FlexSPIConfig uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - //! busy flag is 0 when flash device is busy uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences - flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use } flexspi_mem_config_t; @@ -120,3 +127,32 @@ typedef struct _flexspi_nor_config [4 * 9 + 1] = FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0x0),\ [4 * 11 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x60, STOP, FLEXSPI_1PAD, 0x0),\ } + +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; + +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 \ No newline at end of file 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..dbca23d2086 100644 --- a/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose_custom.h @@ -3,6 +3,14 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#ifdef RUNINFLASH +#define MG_OTA MG_OTA_FLASH +#else +#define MG_OTA MG_OTA_FLASH_FROM_RAM +#endif + +#define MG_DEVICE MG_DEVICE_RT1020 + #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 #define MG_IO_SIZE 256 diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/flexspi.h b/examples/nxp/rt1060-evk-make-baremetal-builtin/flexspi.h index 602d0c9248f..a05d91b3503 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/flexspi.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/flexspi.h @@ -8,13 +8,6 @@ #include #include -typedef struct _lut_sequence -{ - uint8_t seqNum; //!< Sequence Number, valid number: 1-16 - uint8_t seqId; //!< Sequence Index, valid number: 0-15 - uint16_t reserved; -} flexspi_lut_seq_t; - typedef struct _FlexSPIConfig { uint32_t tag; //!< [0x000-0x003] Tag, fixed value 0x42464346UL @@ -30,12 +23,12 @@ typedef struct _FlexSPIConfig //! Generic configuration, etc. uint16_t waitTimeCfgCommands; //!< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for //! DPI/QPI/OPI switch or reset command - flexspi_lut_seq_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt + uint32_t deviceModeSeq; //!< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] - LUt //! sequence number, [31:16] Reserved uint32_t deviceModeArg; //!< [0x018-0x01b] Argument/Parameter for device configuration uint8_t configCmdEnable; //!< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable uint8_t configModeType[3]; //!< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe - flexspi_lut_seq_t + uint32_t configCmdSeqs[3]; //!< [0x020-0x02b] Sequence info for Device Configuration command, similar as deviceModeSeq uint32_t reserved1; //!< [0x02c-0x02f] Reserved for future use uint32_t configCmdArgs[3]; //!< [0x030-0x03b] Arguments/Parameters for device Configuration commands @@ -64,7 +57,7 @@ typedef struct _FlexSPIConfig uint16_t busyBitPolarity; //!< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 - //! busy flag is 0 when flash device is busy uint32_t lookupTable[64]; //!< [0x080-0x17f] Lookup table holds Flash command sequences - flexspi_lut_seq_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences + uint32_t lutCustomSeq[12]; //!< [0x180-0x1af] Customizable LUT Sequences uint32_t reserved4[4]; //!< [0x1b0-0x1bf] Reserved for future use } flexspi_mem_config_t; @@ -120,3 +113,35 @@ typedef struct _flexspi_nor_config [4 * 9 + 1] = FLEXSPI_LUT_SEQ(WRITE_SDR, FLEXSPI_1PAD, 0x04, STOP, FLEXSPI_1PAD, 0x0),\ [4 * 11 + 0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0x60, STOP, FLEXSPI_1PAD, 0x0),\ } + +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; + +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 \ No newline at end of file diff --git a/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c b/examples/nxp/rt1060-evk-make-baremetal-builtin/main.c index 3fd164e5152..f3a18338085 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 @@ -32,15 +33,42 @@ static void timer_fn(void *arg) { } 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 + + #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 = DEFAULT_ROOT_TOPIC; + #endif + 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(); // Call bootloader: continue to load, or boot another FW + +#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 // 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 +93,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..1badf3b3928 100644 --- a/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h +++ b/examples/nxp/rt1060-evk-make-baremetal-builtin/mongoose_custom.h @@ -3,6 +3,14 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_NEWLIB +#ifdef RUNINFLASH +#define MG_OTA MG_OTA_FLASH +#else +#define MG_OTA MG_OTA_FLASH_FROM_RAM +#endif + +#define MG_DEVICE MG_DEVICE_RT1060 + #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT 1 #define MG_IO_SIZE 256 diff --git a/mongoose.c b/mongoose.c index f76fc4545c4..c5aab59dbcc 100644 --- a/mongoose.c +++ b/mongoose.c @@ -235,7 +235,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. @@ -268,10 +269,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 (s == 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) { @@ -303,7 +305,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)); @@ -314,7 +317,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 @@ -343,13 +346,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) { 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) { @@ -405,6 +412,134 @@ 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 +#define FLEXSPI_NOR_INSTANCE 0 + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 8 * 1024 * 1024; +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +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; +} + +#if MG_DEVICE == MG_DEVICE_RT1020 +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}; + memcpy(config, &default_config, sizeof(flexspi_nor_config_t)); + return 0; +} +#else +static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + uint32_t options[] = {0xc0000008, 0x00}; + uint32_t status = flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options); + 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); +} + +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; + } + bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) addr, + mg_flash_sector_size()) == 0); + MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +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; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = + flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) dst, src); + 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")); + return ok; +} + +void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} + +#if MG_DEVICE == MG_DEVICE_RT1060 +bool mg_flash_erase_all(void) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) return false; + bool ok = (flexspi_nor->erase_all(FLEXSPI_NOR_INSTANCE, &config) == 0); + MG_DEBUG(("Chip erase: %s", ok ? "Success" : "Failure")); + return ok; +} +#endif + +#endif + #ifdef MG_ENABLE_LINES #line 1 "src/device_stm32h5.c" #endif @@ -5677,7 +5812,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}; @@ -5795,6 +5930,116 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_FLASH_FROM_RAM + +#define MG_OTADATA_KEY 0xb07afed0 + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC + MG_DEBUG(("%#x %p %lu -> %d", s_addr, buf, len, ok)); + s_addr += len; + } + return ok; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("Saving fw key")); + 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}; + uint32_t key = MG_OTADATA_KEY + MG_FIRMWARE_CURRENT; + ok = mg_flash_save(NULL, key, &od, sizeof(od)); + } + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index 08a9bdffa2c..7d3dca7229f 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1694,6 +1694,7 @@ void mg_rpc_list(struct mg_rpc_req *r); #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_FLASH_FROM_RAM 2 // OTA write to external flash from RAM #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA @@ -1737,9 +1738,17 @@ 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 +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 +#define MG_FLASH_OFFSET 0x60000000 // Offset to memory mapped flash +#else +#define MG_FLASH_OFFSET 0 +#endif + #ifndef MG_DEVICE #define MG_DEVICE MG_DEVICE_NONE #endif diff --git a/src/device.h b/src/device.h index 6215fde8c1c..791116750a0 100644 --- a/src/device.h +++ b/src/device.h @@ -8,9 +8,17 @@ #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 +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 +#define MG_FLASH_OFFSET 0x60000000 // Offset to memory mapped flash +#else +#define MG_FLASH_OFFSET 0 +#endif + #ifndef MG_DEVICE #define MG_DEVICE MG_DEVICE_NONE #endif diff --git a/src/device_flash.c b/src/device_flash.c index c7de46e15b5..01158e2d119 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 (s == 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) { 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..16e7b47aac1 --- /dev/null +++ b/src/device_imxrt.c @@ -0,0 +1,124 @@ +#include "device.h" +#include "log.h" + +#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060 + +#include +#define FLEXSPI_NOR_INSTANCE 0 + +void *mg_flash_start(void) { + return (void *) 0x0; +} +size_t mg_flash_size(void) { + return 8 * 1024 * 1024; +} +size_t mg_flash_sector_size(void) { + return 4 * 1024; // 4k +} +size_t mg_flash_write_align(void) { + return 256; +} +int mg_flash_bank(void) { + return 0; +} + +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; +} + +#if MG_DEVICE == MG_DEVICE_RT1020 +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}; + memcpy(config, &default_config, sizeof(flexspi_nor_config_t)); + return 0; +} +#else +static int flexspi_nor_get_config(flexspi_nor_config_t *config) { + uint32_t options[] = {0xc0000008, 0x00}; + uint32_t status = flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options); + 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); +} + +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; + } + bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) addr, + mg_flash_sector_size()) == 0); + MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); + return ok; +} + +bool mg_flash_swap_bank() { + return false; +} + +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; + while (ok && src < end) { + if (flash_page_start(dst) && mg_flash_erase(dst) == false) break; + uint32_t status = + flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) dst, src); + 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")); + return ok; +} + +void mg_device_reset(void) { + MG_DEBUG(("Resetting device...")); + *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; +} + +#if MG_DEVICE == MG_DEVICE_RT1060 +bool mg_flash_erase_all(void) { + flexspi_nor_config_t config; + if (flexspi_nor_get_config(&config) != 0) return false; + bool ok = (flexspi_nor->erase_all(FLEXSPI_NOR_INSTANCE, &config) == 0); + MG_DEBUG(("Chip erase: %s", ok ? "Success" : "Failure")); + return ok; +} +#endif + +#endif diff --git a/src/ota.h b/src/ota.h index 7eae2a496a0..70dd420e243 100644 --- a/src/ota.h +++ b/src/ota.h @@ -7,6 +7,7 @@ #define MG_OTA_NONE 0 // No OTA support #define MG_OTA_FLASH 1 // OTA via an internal flash +#define MG_OTA_FLASH_FROM_RAM 2 // OTA write to external flash from RAM #define MG_OTA_CUSTOM 100 // Custom implementation #ifndef MG_OTA diff --git a/src/ota_flash.c b/src/ota_flash.c index d5fe87b05b5..639d70d0c33 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}; @@ -192,4 +192,114 @@ MG_IRAM void mg_ota_boot(void) { mg_device_reset(); } } + +#elif MG_OTA == MG_OTA_FLASH_FROM_RAM + +#define MG_OTADATA_KEY 0xb07afed0 + +static char *s_addr; // Current address to write to +static size_t s_size; // Firmware size to flash. In-progress indicator +static uint32_t s_crc32; // Firmware checksum + +struct mg_otadata { + uint32_t crc32, size, timestamp, status; +}; + +bool mg_ota_begin(size_t new_firmware_size) { + bool ok = false; + if (s_size) { + MG_ERROR(("OTA already in progress. Call mg_ota_end()")); + } else { + if (new_firmware_size > mg_flash_size()) { + MG_ERROR(("Firmware exceeds flash size")); + return false; + } + s_size = new_firmware_size; + uint32_t flash_init_status = mg_flash_init(); + if (flash_init_status) { + MG_ERROR(("Error initialising the flash structure")); + return false; + } else { + ok = true; + } + } + return ok; +} + +bool mg_ota_write(const void *buf, size_t len) { + bool ok = false; + if (s_size == 0) { + MG_ERROR(("OTA is not started, call mg_ota_begin()")); + } else { + size_t align = mg_flash_write_align(); + size_t len_aligned_down = MG_ROUND_DOWN(len, align); + if (len_aligned_down) ok = mg_flash_write(s_addr, buf, len_aligned_down); + if (len_aligned_down < len) { + size_t left = len - len_aligned_down; + char tmp[align]; + memset(tmp, 0xff, sizeof(tmp)); + memcpy(tmp, (char *) buf + len_aligned_down, left); + ok = mg_flash_write(s_addr + len_aligned_down, tmp, sizeof(tmp)); + } + s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC + MG_DEBUG(("%#x %p %lu -> %d", s_addr, buf, len, ok)); + s_addr += len; + } + return ok; +} + +bool mg_ota_end(void) { + bool ok = true; + size_t size = (size_t) s_addr; + uint32_t crc32 = mg_crc32(0, (char *) 0x60000000, size); + MG_DEBUG(("Saving fw key")); + 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}; + uint32_t key = MG_OTADATA_KEY + MG_FIRMWARE_CURRENT; + ok = mg_flash_save(NULL, key, &od, sizeof(od)); + } + MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, + size, s_crc32 == crc32 ? "ok" : "fail")); + s_addr = 0; + s_size = 0; + MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); + return ok; +} + +static struct mg_otadata mg_otadata(int fw) { + (void) fw; + struct mg_otadata od = {}; + return od; +} + +int mg_ota_status(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.status; +} + +uint32_t mg_ota_crc32(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.crc32; +} + +uint32_t mg_ota_timestamp(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.timestamp; +} + +size_t mg_ota_size(int fw) { + struct mg_otadata od = mg_otadata(fw); + return od.size; +} + +bool mg_ota_commit(void) { + return false; +} + +bool mg_ota_rollback(void) { + MG_DEBUG(("Rolling firmware not supported yet")); + return false; +} + #endif