diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index d9ab4c859b..dd053b3dc1 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -183,6 +183,7 @@ SRC_C += \ drivers/dht/dht.c \ eth.c \ fatfs_port.c \ + flash.c \ hal/pwm_backport.c \ led.c \ machine_adc.c \ diff --git a/ports/mimxrt/flash.c b/ports/mimxrt/flash.c new file mode 100644 index 0000000000..3a18f8f51b --- /dev/null +++ b/ports/mimxrt/flash.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 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" + +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(BOARD_FLEX_SPI, 0, + qspiflash_config.memConfig.lookupTable, + ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); + + // Configure FLEXSPI IP FIFO access. + BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); + BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); + BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); + BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); +} + +// flash_erase_block(erase_addr) +// erases the block starting at addr. Block size according to the flash properties. +__attribute__((section(".ram_functions"))) 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(BOARD_FLEX_SPI, 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. +__attribute__((section(".ram_functions"))) 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(BOARD_FLEX_SPI, 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 +__attribute__((section(".ram_functions"))) status_t flash_write_block(uint32_t dest_addr, const uint8_t *src, uint32_t length) { + status_t status = kStatus_Fail; + uint32_t write_length; + 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 + do { + next_addr = dest_addr - (dest_addr % PAGE_SIZE_BYTES) + PAGE_SIZE_BYTES; // next page boundary + write_length = next_addr - dest_addr; // calculate write length based on destination address and subsequent page boundary. + if (write_length > length) { // compare possible write_length against remaining data length + write_length = length; + } + + __disable_irq(); + status = flexspi_nor_flash_page_program(BOARD_FLEX_SPI, dest_addr, (uint32_t *)src, write_length); + __enable_irq(); + + // Update remaining data length + length -= write_length; + + // Move source and destination pointer + src += write_length; + dest_addr += write_length; + } while ((length > 0) && (status == kStatus_Success)); + + SCB_EnableDCache(); + + } + return status; +} + +// flash_read_block(flash_src_addr_bytes, data_dest, length_bytes) +// read length_byte data to the source address +// It is just a shim to provide the same structure for read_block and write_block. +__attribute__((section(".ram_functions"))) void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length) { + memcpy(dest, (const uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + src_addr), length); +} diff --git a/ports/mimxrt/flash.h b/ports/mimxrt/flash.h new file mode 100644 index 0000000000..ad010767c8 --- /dev/null +++ b/ports/mimxrt/flash.h @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 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 + +#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) +#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) +#define BLOCK_SIZE_BYTES (qspiflash_config.blockSize) + +#define SECTOR_SIZE_BYTES (qspiflash_config.sectorSize) +#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) + +#ifndef MICROPY_HW_FLASH_STORAGE_BYTES +#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start)) +#endif + +#ifndef MICROPY_HW_FLASH_STORAGE_BASE +#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start)) +#endif + +// Linker symbols +extern uint8_t __vfs_start; +extern uint8_t __vfs_end; +extern uint8_t __flash_start; + +void flash_init(void); +status_t flash_erase_sector(uint32_t erase_addr); +status_t flash_erase_block(uint32_t erase_addr); +void flash_read_block(uint32_t src_addr, uint8_t *dest, uint32_t length); +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 7eaf3192b5..5320231a14 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 a9f17f2d1c..5e5d87166d 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 3c016bc78e..a6454a1c9a 100644 --- a/ports/mimxrt/hal/flexspi_hyper_flash.h +++ b/ports/mimxrt/hal/flexspi_hyper_flash.h @@ -49,6 +49,7 @@ status_t flexspi_nor_hyperflash_cfi(FLEXSPI_Type *base); void flexspi_hyper_flash_init(void); 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 48792ec487..956fb657db 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 edc81e37f1..6526142af2 100644 --- a/ports/mimxrt/hal/flexspi_nor_flash.h +++ b/ports/mimxrt/hal/flexspi_nor_flash.h @@ -48,6 +48,7 @@ status_t flexspi_nor_get_vendor_id(FLEXSPI_Type *base, uint8_t *vendorId); status_t flexspi_nor_init(void); 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); #endif // MICROPY_INCLUDED_MIMXRT_HAL_FLEXSPI_NOR_FLASH_H diff --git a/ports/mimxrt/hal/qspi_nor_flash_config.c b/ports/mimxrt/hal/qspi_nor_flash_config.c index 469a584b87..a6bbd624ed 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/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index 536fb60981..d229367e81 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2020-2021 Damien P. George - * Copyright (c) 2021 Philipp Ebensberger + * Copyright (c) 2021-2023 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 @@ -30,23 +30,9 @@ #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) -#define PAGE_SIZE_BYTES (qspiflash_config.pageSize) - -#ifndef MICROPY_HW_FLASH_STORAGE_BYTES -#define MICROPY_HW_FLASH_STORAGE_BYTES (((uint32_t)&__vfs_end) - ((uint32_t)&__vfs_start)) -#endif - -#ifndef MICROPY_HW_FLASH_STORAGE_BASE -#define MICROPY_HW_FLASH_STORAGE_BASE (((uint32_t)&__vfs_start) - ((uint32_t)&__flash_start)) -#endif - -// Linker symbols -extern uint8_t __vfs_start; -extern uint8_t __vfs_end; -extern uint8_t __flash_start; extern flexspi_nor_config_t qspiflash_config; @@ -60,67 +46,11 @@ 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(BOARD_FLEX_SPI, 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(BOARD_FLEX_SPI, 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); - // Upload the custom flash configuration - // This should be performed by the boot ROM but for some reason it is not. - FLEXSPI_UpdateLUT(BOARD_FLEX_SPI, 0, - qspiflash_config.memConfig.lookupTable, - ARRAY_SIZE(qspiflash_config.memConfig.lookupTable)); - - // Configure BOARD_FLEX_SPI IP FIFO access. - BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ARDFEN_MASK); - BOARD_FLEX_SPI->MCR0 &= ~(FLEXSPI_MCR0_ATDFEN_MASK); - BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ARDFEN(0); - BOARD_FLEX_SPI->MCR0 |= FLEXSPI_MCR0_ATDFEN(0); + flash_init(); // Update information based on linker symbols. mimxrt_flash_obj.flash_base = MICROPY_HW_FLASH_STORAGE_BASE; @@ -142,7 +72,8 @@ STATIC mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) { if (n_args == 4) { offset += mp_obj_get_int(args[3]); } - memcpy(bufinfo.buf, (uint8_t *)(BOARD_FLEX_SPI_ADDR_BASE + self->flash_base + offset), bufinfo.len); + + flash_read_block((self->flash_base + offset), (uint8_t *)bufinfo.buf, (uint32_t)bufinfo.len); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mimxrt_flash_readblocks_obj, 3, 4, mimxrt_flash_readblocks); @@ -161,7 +92,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 +130,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: