From a3f3b9045cd515022b13a96baf371d40e0f9faad Mon Sep 17 00:00:00 2001 From: Philipp Ebensberger Date: Mon, 18 Jul 2022 20:51:43 +0200 Subject: [PATCH] mimxrt/flash: Separate low level driver code from flash object. Separate low level flash access from mimxrt flash driver object. Allows better abstraction from hardware for testing and reuse in other areas (e.g. bootloader). Signed-off-by: Philipp Ebensberger --- ports/mimxrt/Makefile | 1 + ports/mimxrt/flash.c | 123 +++++++++++++++++++++++ ports/mimxrt/flash.h | 58 +++++++++++ ports/mimxrt/hal/flexspi_flash_config.h | 1 + ports/mimxrt/hal/flexspi_hyper_flash.c | 5 + ports/mimxrt/hal/flexspi_hyper_flash.h | 1 + ports/mimxrt/hal/flexspi_nor_flash.c | 31 ++++++ ports/mimxrt/hal/flexspi_nor_flash.h | 1 + ports/mimxrt/hal/qspi_nor_flash_config.c | 8 +- ports/mimxrt/mimxrt_flash.c | 83 ++------------- 10 files changed, 235 insertions(+), 77 deletions(-) create mode 100644 ports/mimxrt/flash.c create mode 100644 ports/mimxrt/flash.h 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: