diff --git a/extmod/modvfs.c b/extmod/modvfs.c index df422365b75ef..2730e8cbc97d4 100644 --- a/extmod/modvfs.c +++ b/extmod/modvfs.c @@ -38,6 +38,10 @@ #error "MICROPY_PY_VFS requires MICROPY_VFS" #endif +#if MICROPY_VFS_ROM +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_vfs_rom_ioctl_obj, 1, 4, mp_vfs_rom_ioctl); +#endif + static const mp_rom_map_elem_t vfs_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_vfs) }, @@ -53,6 +57,7 @@ static const mp_rom_map_elem_t vfs_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, #endif #if MICROPY_VFS_ROM + { MP_ROM_QSTR(MP_QSTR_rom_ioctl), MP_ROM_PTR(&mp_vfs_rom_ioctl_obj) }, { MP_ROM_QSTR(MP_QSTR_VfsRom), MP_ROM_PTR(&mp_type_vfs_rom) }, #endif #if MICROPY_VFS_POSIX diff --git a/extmod/vfs.h b/extmod/vfs.h index 626e25a351123..543b2a8808f26 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -52,6 +52,13 @@ #define MP_BLOCKDEV_IOCTL_BLOCK_SIZE (5) #define MP_BLOCKDEV_IOCTL_BLOCK_ERASE (6) +// Constants for vfs.rom_ioctl() function. +#define MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS (1) // rom_ioctl(1) +#define MP_VFS_ROM_IOCTL_GET_SEGMENT (2) // rom_ioctl(2, ) +#define MP_VFS_ROM_IOCTL_WRITE_PREPARE (3) // rom_ioctl(3, , ) +#define MP_VFS_ROM_IOCTL_WRITE (4) // rom_ioctl(4, , , ) +#define MP_VFS_ROM_IOCTL_WRITE_COMPLETE (5) // rom_ioctl(5, ) + // At the moment the VFS protocol just has import_stat, but could be extended to other methods typedef struct _mp_vfs_proto_t { mp_import_stat_t (*import_stat)(void *self, const char *path); @@ -122,4 +129,9 @@ MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_rmdir_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_stat_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_vfs_statvfs_obj); +#if MICROPY_VFS_ROM +// A port must define this. +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args); +#endif + #endif // MICROPY_INCLUDED_EXTMOD_VFS_H diff --git a/ports/esp32/esp32_partition.c b/ports/esp32/esp32_partition.c index 55830a285b0b6..485cbca1a71ad 100644 --- a/ports/esp32/esp32_partition.c +++ b/ports/esp32/esp32_partition.c @@ -53,6 +53,20 @@ typedef struct _esp32_partition_obj_t { uint16_t block_size; } esp32_partition_obj_t; +#if MICROPY_VFS_ROM + +static esp32_partition_obj_t esp32_partition_romfs_obj = { + .base = { .type = NULL }, + .part = NULL, + .cache = NULL, + .block_size = NATIVE_BLOCK_SIZE_BYTES, +}; + +static const void *esp32_partition_romfs_ptr = NULL; +static esp_partition_mmap_handle_t esp32_partition_romfs_handle; + +#endif + static esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part, uint16_t block_size) { if (part == NULL) { mp_raise_OSError(MP_ENOENT); @@ -114,6 +128,24 @@ static mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_arg return MP_OBJ_FROM_PTR(esp32_partition_new(part, block_size)); } +#if MICROPY_VFS_ROM +static mp_int_t esp32_partition_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self == &esp32_partition_romfs_obj && flags == MP_BUFFER_READ) { + if (esp32_partition_romfs_ptr == NULL) { + check_esp_err(esp_partition_mmap(self->part, 0, self->part->size, ESP_PARTITION_MMAP_DATA, &esp32_partition_romfs_ptr, &esp32_partition_romfs_handle)); + } + bufinfo->buf = (void *)esp32_partition_romfs_ptr; + bufinfo->len = self->part->size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Unsupported. + return 1; + } +} +#endif + static mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // Parse args enum { ARG_type, ARG_subtype, ARG_label, ARG_block_size }; @@ -284,11 +316,51 @@ static const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(esp32_partition_locals_dict, esp32_partition_locals_dict_table); +#if MICROPY_VFS_ROM +#define ESP32_PARTITION_GET_BUFFER buffer, esp32_partition_get_buffer, +#else +#define ESP32_PARTITION_GET_BUFFER +#endif + MP_DEFINE_CONST_OBJ_TYPE( esp32_partition_type, MP_QSTR_Partition, MP_TYPE_FLAG_NONE, make_new, esp32_partition_make_new, print, esp32_partition_print, + ESP32_PARTITION_GET_BUFFER locals_dict, &esp32_partition_locals_dict ); + +/******************************************************************************/ +// romfs partition + +#if MICROPY_VFS_ROM + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (esp32_partition_romfs_obj.base.type == NULL) { + esp32_partition_romfs_obj.base.type = &esp32_partition_type; + // Get the romfs partition. + // TODO: number of segments ioctl can be used if there is more than one romfs. + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "romfs"); + if (iter != NULL) { + esp32_partition_romfs_obj.part = esp_partition_get(iter); + } + esp_partition_iterator_release(iter); + } + + if (esp32_partition_romfs_obj.part == NULL) { + return MP_OBJ_NEW_SMALL_INT(-MP_ENODEV); + } + + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&esp32_partition_romfs_obj); + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM diff --git a/ports/esp32/modules/_boot.py b/ports/esp32/modules/_boot.py index 96af581f38a5c..8c8b4a24bc5ff 100644 --- a/ports/esp32/modules/_boot.py +++ b/ports/esp32/modules/_boot.py @@ -1,7 +1,14 @@ import gc import vfs +import sys from flashbdev import bdev +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass + try: if bdev: vfs.mount(bdev, "/") @@ -11,3 +18,5 @@ inisetup.setup() gc.collect() + +del sys diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b5b7d63a56333..6488b8f78646f 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -70,6 +70,7 @@ #define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (1) // control over Python builtins #define MICROPY_PY_STR_BYTES_CMP_WARN (1) diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiB.csv index 53f0f167442bc..dd02506e54684 100644 --- a/ports/esp32/partitions-4MiB.csv +++ b/ports/esp32/partitions-4MiB.csv @@ -3,5 +3,6 @@ # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, +factory, app, factory, 0x10000, 0x1D0000, +romfs, data, 0x8f, 0x1E0000, 0x20000, vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp8266/boards/ESP8266_GENERIC/_boot.py b/ports/esp8266/boards/ESP8266_GENERIC/_boot.py index 16da8bc4c3750..048d8a06ec8f7 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/_boot.py +++ b/ports/esp8266/boards/ESP8266_GENERIC/_boot.py @@ -1,6 +1,13 @@ # Minimal _boot.py for the 512kiB variant. Does not set up a block device or # filesystem. Other variants use esp8266/modules/_boot.py. -import gc +import gc, vfs gc.threshold((gc.mem_free() + gc.mem_alloc()) // 4) + +# Try to mount the ROMFS. +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/") +except: + pass + diff --git a/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py b/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py index 15f6cffc3f94a..9abaaa746f7c4 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py +++ b/ports/esp8266/boards/ESP8266_GENERIC/manifest_512kiB.py @@ -1,9 +1,9 @@ module("_boot.py", opt=3) -module("apa102.py", base_path="$(PORT_DIR)/modules", opt=3) -module("port_diag.py", base_path="$(PORT_DIR)/modules", opt=3) -require("ntptime") -require("dht") -require("onewire") -require("ds18x20") -require("webrepl") -require("neopixel") +# module("apa102.py", base_path="$(PORT_DIR)/modules", opt=3) +# module("port_diag.py", base_path="$(PORT_DIR)/modules", opt=3) +# require("ntptime") +# require("dht") +# require("onewire") +# require("ds18x20") +# require("webrepl") +# require("neopixel") diff --git a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h index 1f679961e8786..1c564f52fb3ca 100644 --- a/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h +++ b/ports/esp8266/boards/ESP8266_GENERIC/mpconfigboard.h @@ -3,15 +3,11 @@ #define MICROPY_HW_BOARD_NAME "ESP module" #define MICROPY_HW_MCU_NAME "ESP8266" -#define MICROPY_PERSISTENT_CODE_LOAD (1) #define MICROPY_EMIT_XTENSA (1) #define MICROPY_EMIT_INLINE_XTENSA (1) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) -#define MICROPY_READER_VFS (MICROPY_VFS) -#define MICROPY_VFS (1) - #define MICROPY_PY_CRYPTOLIB (1) #elif defined(MICROPY_ESP8266_1M) @@ -19,16 +15,11 @@ #define MICROPY_HW_BOARD_NAME "ESP module (1M)" #define MICROPY_HW_MCU_NAME "ESP8266" -#define MICROPY_PERSISTENT_CODE_LOAD (1) #define MICROPY_EMIT_XTENSA (1) #define MICROPY_EMIT_INLINE_XTENSA (1) #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) -#define MICROPY_READER_VFS (MICROPY_VFS) -#define MICROPY_VFS (1) - - #define MICROPY_PY_CRYPTOLIB (1) #elif defined(MICROPY_ESP8266_512K) @@ -46,5 +37,18 @@ #define MICROPY_PY_ASYNCIO (0) #define MICROPY_PY_RE_SUB (0) #define MICROPY_PY_FRAMEBUF (0) +#define MICROPY_PY_HEAPQ (0) + +#define MICROPY_VFS_WRITABLE (0) + +#define MICROPY_PY_ESP_FLASH_FUNCS (0) +#define MICROPY_PY_ESP_EXTRA_FUNCS (0) #endif + +// Configuration common to all variants. + +#define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_READER_VFS (MICROPY_VFS) +#define MICROPY_VFS (1) +#define MICROPY_VFS_ROM (1) diff --git a/ports/esp8266/boards/esp8266_1MiB.ld b/ports/esp8266/boards/esp8266_1MiB.ld index 94bc6a229a75b..678013940acdf 100644 --- a/ports/esp8266/boards/esp8266_1MiB.ld +++ b/ports/esp8266/boards/esp8266_1MiB.ld @@ -2,7 +2,8 @@ Flash layout: 0x40200000 36k header + iram/dram init - 0x40209000 572k firmware (irom0) + 0x40209000 556k firmware (irom0) + 0x40294000 16k ROMFS 0x40298000 396k filesystem 0x402fb000 20k SDK parameters */ @@ -12,7 +13,8 @@ MEMORY dport0_0_seg : org = 0x3ff00000, len = 16 dram0_0_seg : org = 0x3ffe8000, len = 80K iram1_0_seg : org = 0x40100000, len = 32K - irom0_0_seg : org = 0x40209000, len = 572K + irom0_0_seg : org = 0x40209000, len = 556K + FLASH_ROMFS : org = 0x40294000, len = 16K } /* define common sections and symbols */ diff --git a/ports/esp8266/boards/esp8266_2MiB.ld b/ports/esp8266/boards/esp8266_2MiB.ld index b5813ce0fb7a4..76cb22de14e37 100644 --- a/ports/esp8266/boards/esp8266_2MiB.ld +++ b/ports/esp8266/boards/esp8266_2MiB.ld @@ -2,7 +2,8 @@ Flash layout: 0x40200000 36k header + iram/dram init - 0x40209000 988k firmware (irom0) + 0x40209000 732k firmware (irom0) + 0x402c0000 256k ROMFS 0x40300000 1M+ filesystem (not memory mapped) */ @@ -11,7 +12,8 @@ MEMORY dport0_0_seg : org = 0x3ff00000, len = 16 dram0_0_seg : org = 0x3ffe8000, len = 80K iram1_0_seg : org = 0x40100000, len = 32K - irom0_0_seg : org = 0x40209000, len = 1M - 36K + irom0_0_seg : org = 0x40209000, len = 1M - 36K - 256K + FLASH_ROMFS : org = 0x402c0000, len = 256K } /* define common sections and symbols */ diff --git a/ports/esp8266/boards/esp8266_512kiB.ld b/ports/esp8266/boards/esp8266_512kiB.ld index c4cc798499df0..24a33fe031c70 100644 --- a/ports/esp8266/boards/esp8266_512kiB.ld +++ b/ports/esp8266/boards/esp8266_512kiB.ld @@ -2,7 +2,8 @@ Flash layout: 0x40200000 36k header + iram/dram init - 0x40209000 456k firmware (irom0) + 0x40209000 448k firmware (irom0) + 0x40279000 8k ROMFS 0x4027b000 20k SDK parameters */ @@ -11,7 +12,8 @@ MEMORY dport0_0_seg : org = 0x3ff00000, len = 16 dram0_0_seg : org = 0x3ffe8000, len = 80K iram1_0_seg : org = 0x40100000, len = 32K - irom0_0_seg : org = 0x40209000, len = 512K - 36K - 20K + irom0_0_seg : org = 0x40209000, len = 512K - 36K - 20K - 8K + FLASH_ROMFS : org = 0x4027a000, len = 8K } /* define common sections and symbols */ diff --git a/ports/esp8266/boards/esp8266_common.ld b/ports/esp8266/boards/esp8266_common.ld index cf4883acc9d20..4c4970045902c 100644 --- a/ports/esp8266/boards/esp8266_common.ld +++ b/ports/esp8266/boards/esp8266_common.ld @@ -3,6 +3,10 @@ /* define the top of RAM */ _heap_end = ORIGIN(dram0_0_seg) + LENGTH(dram0_0_seg); +/* define ROMFS extents */ +_micropy_hw_romfs_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_size = LENGTH(FLASH_ROMFS); + PHDRS { dport0_0_phdr PT_LOAD; diff --git a/ports/esp8266/boards/esp8266_ota.ld b/ports/esp8266/boards/esp8266_ota.ld index 7fdf6abef00e3..c5ea2d58fa1a7 100644 --- a/ports/esp8266/boards/esp8266_ota.ld +++ b/ports/esp8266/boards/esp8266_ota.ld @@ -6,7 +6,8 @@ MEMORY dram0_0_seg : org = 0x3ffe8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 /* 0x3c000 is size of bootloader, 0x9000 is size of packed RAM segments */ - irom0_0_seg : org = 0x40200000 + 0x3c000 + 0x9000, len = 0x8f000 + irom0_0_seg : org = 0x40200000 + 0x3c000 + 0x9000, len = 0x8f000 - 8K + FLASH_ROMFS : org = 0x402d2000, len = 8K } /* define common sections and symbols */ diff --git a/ports/esp8266/modesp.c b/ports/esp8266/modesp.c index f303da1a33289..d35b17532f100 100644 --- a/ports/esp8266/modesp.c +++ b/ports/esp8266/modesp.c @@ -164,9 +164,10 @@ static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); #define IS_OTA_FIRMWARE() ((*(uint32_t *)0x40200000 & 0xff00) == 0x100) extern byte _firmware_size[]; +extern uint8_t _micropy_hw_romfs_size; static mp_obj_t esp_flash_user_start(void) { - return MP_OBJ_NEW_SMALL_INT((uint32_t)_firmware_size); + return MP_OBJ_NEW_SMALL_INT((uint32_t)_firmware_size + (uint32_t)&_micropy_hw_romfs_size); } static MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); @@ -345,21 +346,25 @@ static const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_osdebug), MP_ROM_PTR(&esp_osdebug_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep_type), MP_ROM_PTR(&esp_sleep_type_obj) }, { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&esp_deepsleep_obj) }, + #if MICROPY_PY_ESP_FLASH_FUNCS { MP_ROM_QSTR(MP_QSTR_flash_id), MP_ROM_PTR(&esp_flash_id_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_read), MP_ROM_PTR(&esp_flash_read_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_write), MP_ROM_PTR(&esp_flash_write_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_erase), MP_ROM_PTR(&esp_flash_erase_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_size), MP_ROM_PTR(&esp_flash_size_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_user_start), MP_ROM_PTR(&esp_flash_user_start_obj) }, + #endif #if MICROPY_ESP8266_APA102 { MP_ROM_QSTR(MP_QSTR_apa102_write), MP_ROM_PTR(&esp_apa102_write_obj) }, #endif + #if MICROPY_PY_ESP_EXTRA_FUNCS { MP_ROM_QSTR(MP_QSTR_freemem), MP_ROM_PTR(&esp_freemem_obj) }, { MP_ROM_QSTR(MP_QSTR_meminfo), MP_ROM_PTR(&esp_meminfo_obj) }, { MP_ROM_QSTR(MP_QSTR_check_fw), MP_ROM_PTR(&esp_check_fw_obj) }, { MP_ROM_QSTR(MP_QSTR_malloc), MP_ROM_PTR(&esp_malloc_obj) }, { MP_ROM_QSTR(MP_QSTR_free), MP_ROM_PTR(&esp_free_obj) }, { MP_ROM_QSTR(MP_QSTR_esf_free_bufs), MP_ROM_PTR(&esp_esf_free_bufs_obj) }, + #endif #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA { MP_ROM_QSTR(MP_QSTR_set_native_code_location), MP_ROM_PTR(&esp_set_native_code_location_obj) }, #endif @@ -379,3 +384,66 @@ const mp_obj_module_t esp_module = { }; MP_REGISTER_MODULE(MP_QSTR_esp, esp_module); + +///// + +#if MICROPY_VFS_ROM + +#include "py/objarray.h" +#include "extmod/vfs.h" + +#define FLASH_MEM_BASE (0x40200000) +#define FLASH_PAGE_SIZE (4096) + +#define MICROPY_HW_ROMFS_BASE (uintptr_t)(&_micropy_hw_romfs_start) +#define MICROPY_HW_ROMFS_BYTES (uintptr_t)(&_micropy_hw_romfs_size) + +#define ROMFS_SPI_FLASH_OFFSET (MICROPY_HW_ROMFS_BASE - FLASH_MEM_BASE) + +extern uint8_t _micropy_hw_romfs_start; +extern uint8_t _micropy_hw_romfs_size; + +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&romfs_obj); + + case MP_VFS_ROM_IOCTL_WRITE_PREPARE: { + uint32_t dest = ROMFS_SPI_FLASH_OFFSET; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + while (dest < dest_max) { + ets_loop_iter(); // flash access takes time so run any pending tasks + SpiFlashOpResult res = spi_flash_erase_sector(dest / FLASH_PAGE_SIZE); + ets_loop_iter(); + if (res != SPI_FLASH_RESULT_OK) { + return MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? -MP_ETIMEDOUT : -MP_EIO); + } + dest += FLASH_PAGE_SIZE; + } + return MP_OBJ_NEW_SMALL_INT(4); // minimum write size + } + + case MP_VFS_ROM_IOCTL_WRITE: { + mp_int_t offset = ROMFS_SPI_FLASH_OFFSET + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + ets_loop_iter(); // flash access takes time so run any pending tasks + SpiFlashOpResult res = spi_flash_write(offset, bufinfo.buf, (bufinfo.len + 3) & ~3); + ets_loop_iter(); + if (res == SPI_FLASH_RESULT_OK) { + return MP_OBJ_NEW_SMALL_INT(0); + } + return MP_OBJ_NEW_SMALL_INT(res == SPI_FLASH_RESULT_TIMEOUT ? -MP_ETIMEDOUT : -MP_EIO); + } + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM diff --git a/ports/esp8266/modules/_boot.py b/ports/esp8266/modules/_boot.py index 00a0d6550f945..570dd3139e99f 100644 --- a/ports/esp8266/modules/_boot.py +++ b/ports/esp8266/modules/_boot.py @@ -1,9 +1,17 @@ import gc gc.threshold((gc.mem_free() + gc.mem_alloc()) // 4) -import vfs + +import sys, vfs from flashbdev import bdev +# Try to mount the ROMFS. +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass + if bdev: try: vfs.mount(bdev, "/") diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 83d80a7c963f6..e88986b16b55b 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -111,6 +111,14 @@ #define MICROPY_STREAMS_POSIX_API (1) #define MICROPY_MODULE_FROZEN_LEXER mp_lexer_new_from_str32 +#ifndef MICROPY_PY_ESP_FLASH_FUNCS +#define MICROPY_PY_ESP_FLASH_FUNCS (1) +#endif + +#ifndef MICROPY_PY_ESP_EXTRA_FUNCS +#define MICROPY_PY_ESP_EXTRA_FUNCS (1) +#endif + #define MICROPY_FATFS_ENABLE_LFN (1) #define MICROPY_FATFS_RPATH (2) #define MICROPY_FATFS_MAX_SS (4096) diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8106ab4f3ff03..e3a942e22124f 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -267,6 +267,11 @@ ifeq ($(USE_UF2_BOOTLOADER),1) CFLAGS += -DMICROPY_MACHINE_UF2_BOOTLOADER=1 endif +# Set the default size of the VfsRom file system. Can be changed at board level +ifeq ($(MICROPY_HW_ROMFS_BYTES),) + MICROPY_HW_ROMFS_BYTES ?= 0x40000 +endif + # Add sources for respective board flash type # Add hal/flexspi_nor_flash.c or hal/flashspi_hyper_flash.c respectively SRC_HAL_C += hal/flexspi_$(subst qspi_,,$(FLEXSPI_FLASH_TYPE)).c @@ -450,9 +455,10 @@ LDFLAGS += \ # LDDEFINES are used for link time adaptation of linker scripts, utilizing # the C preprocessor. Therefore keep LDDEFINES separated from LDFLAGS! -LDDEFINES = \ +LDDEFINES += \ -DMICROPY_HW_FLASH_BASE=$(MICROPY_HW_FLASH_BASE) \ - -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) + -DMICROPY_HW_FLASH_SIZE=$(MICROPY_HW_FLASH_SIZE) \ + -DMICROPY_HW_ROMFS_BYTES=$(MICROPY_HW_ROMFS_BYTES) ifdef MICROPY_HW_FLASH_RESERVED LDDEFINES += -DMICROPY_HW_FLASH_RESERVED=$(MICROPY_HW_FLASH_RESERVED) diff --git a/ports/mimxrt/boards/MIMXRT1011.ld b/ports/mimxrt/boards/MIMXRT1011.ld index 0e961a49433f2..b5c5464dd7a65 100644 --- a/ports/mimxrt/boards/MIMXRT1011.ld +++ b/ports/mimxrt/boards/MIMXRT1011.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1015.ld b/ports/mimxrt/boards/MIMXRT1015.ld index 58b88e0fe3084..64ca7bfd3f2fc 100644 --- a/ports/mimxrt/boards/MIMXRT1015.ld +++ b/ports/mimxrt/boards/MIMXRT1015.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00008000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1021.ld b/ports/mimxrt/boards/MIMXRT1021.ld index 78add04c0c26c..7b032788aa8ee 100644 --- a/ports/mimxrt/boards/MIMXRT1021.ld +++ b/ports/mimxrt/boards/MIMXRT1021.ld @@ -18,8 +18,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00010000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1052.ld b/ports/mimxrt/boards/MIMXRT1052.ld index ea034d713e2f6..d1b4d556eba61 100644 --- a/ports/mimxrt/boards/MIMXRT1052.ld +++ b/ports/mimxrt/boards/MIMXRT1052.ld @@ -20,8 +20,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00200000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1062.ld b/ports/mimxrt/boards/MIMXRT1062.ld index 3d7e6d0634196..d5da419f7e309 100644 --- a/ports/mimxrt/boards/MIMXRT1062.ld +++ b/ports/mimxrt/boards/MIMXRT1062.ld @@ -20,8 +20,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1064.ld b/ports/mimxrt/boards/MIMXRT1064.ld index 7c35cb60c750b..a8215bc3bf5c0 100644 --- a/ports/mimxrt/boards/MIMXRT1064.ld +++ b/ports/mimxrt/boards/MIMXRT1064.ld @@ -14,8 +14,10 @@ interrupts_start = flash_start + 0x0000C000; interrupts_size = 0x00000400; text_start = flash_start + 0x0000C400; vfs_start = flash_start + 0x00100000; -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/boards/MIMXRT1176.ld b/ports/mimxrt/boards/MIMXRT1176.ld index 4d114ef96fa4a..11664a752f041 100644 --- a/ports/mimxrt/boards/MIMXRT1176.ld +++ b/ports/mimxrt/boards/MIMXRT1176.ld @@ -30,8 +30,10 @@ m_core1_image_start = vfs_start - 0x00040000; m_core1_image_size = 0x00040000; #endif -text_size = ((vfs_start) - (text_start)); vfs_size = ((flash_end) - (vfs_start)); +vfsrom_start = ((vfs_start) - MICROPY_HW_ROMFS_BYTES); +vfsrom_end = ((vfsrom_start) + MICROPY_HW_ROMFS_BYTES); +text_size = ((vfsrom_start) - (text_start)); itcm_start = 0x00000000; itcm_size = 0x00020000; dtcm_start = 0x20000000; diff --git a/ports/mimxrt/mimxrt_flash.c b/ports/mimxrt/mimxrt_flash.c index fdd48e280bc15..09ce2a4fbd898 100644 --- a/ports/mimxrt/mimxrt_flash.c +++ b/ports/mimxrt/mimxrt_flash.c @@ -5,6 +5,7 @@ * * Copyright (c) 2020-2021 Damien P. George * Copyright (c) 2021-2023 Philipp Ebensberger + * Copyright (c) 2021-2024 Robert Hammelrath * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +30,7 @@ #include "py/runtime.h" #include "extmod/vfs.h" +#include "py/mperrno.h" #include "modmimxrt.h" #include "flash.h" #include BOARD_FLASH_OPS_HEADER_H @@ -60,6 +62,19 @@ static mp_obj_t mimxrt_flash_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(&mimxrt_flash_obj); } +static mp_int_t mimxrt_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mimxrt_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)((uintptr_t)&__flash_start + self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} + // readblocks(block_num, buf, [offset]) // read size of buffer number of bytes from block (with offset) into buffer static mp_obj_t mimxrt_flash_readblocks(size_t n_args, const mp_obj_t *args) { @@ -151,5 +166,39 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, mimxrt_flash_make_new, + buffer, mimxrt_flash_get_buffer, locals_dict, &mimxrt_flash_locals_dict ); + +#if MICROPY_VFS_ROM + +extern uint8_t vfsrom_start; +extern uint8_t vfsrom_end; + +// Put VfsRom file system between the code space and the VFS file system. +// The size is defined in Makefile(s) as linker symbol MICROPY_HW_ROMFS_BYTES. +// For machine.mem32 the absolute address is required, for the flash functions +// erase and write the offset to the flash start address. +#define MICROPY_HW_ROMFS_BASE ((uintptr_t)&vfsrom_start - (uintptr_t)&__flash_start) +#define MICROPY_HW_ROMFS_BYTES ((uintptr_t)&vfsrom_end - (uintptr_t)&vfsrom_start) + +static mimxrt_flash_obj_t mimxrt_flash_romfs_obj = { + .base = { &mimxrt_flash_type }, +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + mimxrt_flash_romfs_obj.flash_base = MICROPY_HW_ROMFS_BASE; + mimxrt_flash_romfs_obj.flash_size = MICROPY_HW_ROMFS_BYTES; + return MP_OBJ_FROM_PTR(&mimxrt_flash_romfs_obj); + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} +#endif // MICROPY_VFS_ROM diff --git a/ports/mimxrt/modules/_boot.py b/ports/mimxrt/modules/_boot.py index fa3b780434a43..b658bf3b9e80f 100644 --- a/ports/mimxrt/modules/_boot.py +++ b/ports/mimxrt/modules/_boot.py @@ -8,6 +8,12 @@ import mimxrt from machine import Pin +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass + bdev = mimxrt.Flash() try: fs = vfs.VfsLfs2(bdev, progsize=256) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 7da85f1aee579..4a4a184fde7ca 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -59,6 +59,9 @@ uint32_t trng_random_u32(void); #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif // Control over Python builtins #define MICROPY_PY_BUILTINS_HELP_TEXT mimxrt_help_text diff --git a/ports/nrf/boards/memory.ld b/ports/nrf/boards/memory.ld index 9c4c9c8bd1267..fac072516158f 100644 --- a/ports/nrf/boards/memory.ld +++ b/ports/nrf/boards/memory.ld @@ -8,10 +8,12 @@ _head_size = DEFINED(_sd_size) ? _sd_size : _bootloader_head_size; _head_ram = DEFINED(_sd_ram) ? _sd_ram : _bootloader_head_ram_size; _sd_size = DEFINED(_sd_size) ? _sd_size : 0; _sd_ram = DEFINED(_sd_ram) ? _sd_ram : 0; +_vfsrom_size = DEFINED(_vfsrom_size) ? _vfsrom_size : 0; _fs_size = DEFINED(_fs_size) ? _fs_size : 64K; /* TODO: set to 0 if not using the filesystem */ -_app_size = _flash_size - _head_size - _fs_size - _bootloader_tail_size; +_app_size = _flash_size - _head_size - _vfsrom_size - _fs_size - _bootloader_tail_size; _app_start = _head_size; -_fs_start = _head_size + _app_size; +_vfsrom_start = _head_size + _app_size; +_fs_start = _vfsrom_start + _vfsrom_size; _fs_end = _fs_start + _fs_size; _app_ram_start = 0x20000000 + _head_ram; _app_ram_size = _ram_size - _head_ram; diff --git a/ports/nrf/boards/nrf51x22_256k_16k.ld b/ports/nrf/boards/nrf51x22_256k_16k.ld index 8a40ae0f17307..7fd978a358f4c 100644 --- a/ports/nrf/boards/nrf51x22_256k_16k.ld +++ b/ports/nrf/boards/nrf51x22_256k_16k.ld @@ -6,6 +6,7 @@ GROUP(-lgcc -lc -lnosys) _flash_size = 256K; _ram_size = 16K; +_vfsrom_size = 12K; /* Default stack size when there is no SoftDevice */ _stack_size = 4K; diff --git a/ports/nrf/boards/nrf51x22_256k_32k.ld b/ports/nrf/boards/nrf51x22_256k_32k.ld index 06c0914035b4a..e7940e2097c9e 100644 --- a/ports/nrf/boards/nrf51x22_256k_32k.ld +++ b/ports/nrf/boards/nrf51x22_256k_32k.ld @@ -6,6 +6,7 @@ GROUP(-lgcc -lc -lnosys) _flash_size = 256K; _ram_size = 32K; +_vfsrom_size = 12K; /* Default stack size when there is no SoftDevice */ _stack_size = 4K; diff --git a/ports/nrf/boards/nrf52832_512k_64k.ld b/ports/nrf/boards/nrf52832_512k_64k.ld index 22804df5cdb5e..dead8f345c6d0 100644 --- a/ports/nrf/boards/nrf52832_512k_64k.ld +++ b/ports/nrf/boards/nrf52832_512k_64k.ld @@ -4,6 +4,7 @@ _flash_size = 512K; _ram_size = 64K; +_vfsrom_size = 128K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 8K; diff --git a/ports/nrf/boards/nrf52840_1M_256k.ld b/ports/nrf/boards/nrf52840_1M_256k.ld index 16d61af6a30b9..2670b88f50d4d 100644 --- a/ports/nrf/boards/nrf52840_1M_256k.ld +++ b/ports/nrf/boards/nrf52840_1M_256k.ld @@ -4,6 +4,7 @@ _flash_size = 1M; _ram_size = 256K; +_vfsrom_size = 128K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 8K; diff --git a/ports/nrf/boards/nrf9160_1M_256k.ld b/ports/nrf/boards/nrf9160_1M_256k.ld index 6347095a899ce..467a0325e25d5 100644 --- a/ports/nrf/boards/nrf9160_1M_256k.ld +++ b/ports/nrf/boards/nrf9160_1M_256k.ld @@ -7,6 +7,7 @@ _ram_size = 256K; _sd_size = 0x00008000; _sd_ram = 0x00020000; _fs_size = 80K; +_vfsrom_size = 128K; /* produce a link error if there is not this amount of RAM for these sections */ _stack_size = 32K; diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 29550bd77a748..17dbe19dfda5d 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -179,22 +179,8 @@ void NORETURN _start(void) { #if MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE flashbdev_init(); - - // Try to mount the flash on "/flash" and chdir to it for the boot-up directory. - mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash); - int ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); - - if ((ret == -MP_ENODEV) || (ret == -MP_EIO)) { - pyexec_frozen_module("_mkfs.py", false); // Frozen script for formatting flash filesystem. - ret = mp_vfs_mount_and_chdir_protected((mp_obj_t)&nrf_flash_obj, mount_point); - } - - if (ret != 0) { - printf("MPY: can't mount flash\n"); - } else { - mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash)); - mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib)); - } + // Execute _boot.py to set up the filesystem. + pyexec_frozen_module("_boot.py", false); #endif #if MICROPY_MBFS diff --git a/ports/nrf/modules/manifest.py b/ports/nrf/modules/manifest.py index 7ba0f80d884d0..631ad19a8db8a 100644 --- a/ports/nrf/modules/manifest.py +++ b/ports/nrf/modules/manifest.py @@ -1,2 +1,2 @@ -module("_mkfs.py", base_path="$(PORT_DIR)/modules/scripts", opt=3) +module("_boot.py", base_path="$(PORT_DIR)/modules/scripts", opt=3) include("$(MPY_DIR)/extmod/asyncio") diff --git a/ports/nrf/modules/nrf/flashbdev.c b/ports/nrf/modules/nrf/flashbdev.c index e490dc53dd677..feacca8746486 100644 --- a/ports/nrf/modules/nrf/flashbdev.c +++ b/ports/nrf/modules/nrf/flashbdev.c @@ -183,12 +183,26 @@ static mp_obj_t nrf_flashbdev_make_new(const mp_obj_type_t *type, size_t n_args, return MP_OBJ_FROM_PTR(self); } +static mp_int_t nrf_flashbdev_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + nrf_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)self->start; + bufinfo->len = self->len; + bufinfo->typecode = 'B'; + return 0; + } else { + // Unsupported. + return 1; + } +} + MP_DEFINE_CONST_OBJ_TYPE( nrf_flashbdev_type, MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, nrf_flashbdev_make_new, print, nrf_flashbdev_print, + buffer, nrf_flashbdev_get_buffer, locals_dict, &nrf_flashbdev_locals_dict ); @@ -202,4 +216,34 @@ void flashbdev_init(void) { nrf_flash_obj.len = num_pages * FLASH_PAGESIZE; } +#if MICROPY_VFS_ROM + +extern byte _vfsrom_start[]; +extern byte _vfsrom_size[]; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)_vfsrom_start) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)_vfsrom_size) + +static nrf_flash_obj_t nrf_flash_romfs_obj = { + .base = { &nrf_flashbdev_type }, + .start = MICROPY_HW_ROMFS_BASE, // Get from MCU-Specific loader script. + .len = MICROPY_HW_ROMFS_BYTES, // Get from MCU-Specific loader script. +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&nrf_flash_romfs_obj); + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM + #endif // MICROPY_PY_NRF && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE diff --git a/ports/nrf/modules/scripts/_boot.py b/ports/nrf/modules/scripts/_boot.py new file mode 100644 index 0000000000000..00cde657dbc9f --- /dev/null +++ b/ports/nrf/modules/scripts/_boot.py @@ -0,0 +1,37 @@ +import gc +import vfs +import sys +import nrf +import os + +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass + +try: + bdev = nrf.Flash() + fs_type = ( + vfs.VfsLfs2 + if hasattr(vfs, "VfsLfs2") + else (vfs.VfsLfs1 if hasattr(vfs, "VfsLfs1") else vfs.VfsFat) + ) + vfs.mount(fs_type(bdev), "/flash") +except AttributeError: + fs_type = None +except: + try: + fs_type.mkfs(bdev) + vfs.mount(fs_type(bdev), "/flash") + except: + pass + +if fs_type is not None: + os.chdir("/flash") + sys.path.append("/flash") + sys.path.append("/flash/lib") + +del os, nrf, vfs, sys, bdev, fs_type +gc.collect() +del gc diff --git a/ports/nrf/modules/scripts/_mkfs.py b/ports/nrf/modules/scripts/_mkfs.py deleted file mode 100644 index 601f9558eb7b5..0000000000000 --- a/ports/nrf/modules/scripts/_mkfs.py +++ /dev/null @@ -1,23 +0,0 @@ -import vfs, nrf - -try: - from vfs import VfsLfs1 - - vfs.VfsLfs1.mkfs(nrf.Flash()) -except ImportError: - try: - from vfs import VfsLfs2 - - vfs.VfsLfs2.mkfs(nrf.Flash()) - except ImportError: - try: - from vfs import VfsFat - - vfs.VfsFat.mkfs(nrf.Flash()) - except ImportError: - pass - except OSError as e: - if e.args[0] == 5: # I/O Error - flashbdev_size = (nrf.Flash.ioctl(4, 0) * nrf.Flash.ioctl(5, 0)) // 1024 - print() - print("Is `FS_SIZE=%iK` enough for FAT filesystem?" % flashbdev_size) diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 7cc8a66d9840c..7d06fe1bd79a6 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -69,6 +69,11 @@ #define MICROPY_VFS (CORE_FEAT) #endif +// VfsROM filesystem +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (CORE_FEAT) +#endif + // micro:bit filesystem #ifndef MICROPY_MBFS #define MICROPY_MBFS (!MICROPY_VFS) diff --git a/ports/qemu/main.c b/ports/qemu/main.c index 75c6fe4cdc0d8..bcb62d51dd057 100644 --- a/ports/qemu/main.c +++ b/ports/qemu/main.c @@ -77,3 +77,7 @@ void nlr_jump_fail(void *val) { mp_printf(&mp_plat_print, "uncaught NLR\n"); exit(1); } + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld index c1fac5106c37e..3b5be1642ec6c 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/ra6m5.ld @@ -6,7 +6,8 @@ MEMORY { FLASH_BOOT (r) : ORIGIN = 0x00000000, LENGTH = 0x00010000 /* 64K */ - FLASH (rx) : ORIGIN = 0x00010000, LENGTH = 0x000f0000 /* 960KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000C0000 /* 768KB/2MB */ + VFSROM_FS (r) : ORIGIN = 0x000D0000, LENGTH = 0x00030000 /* 192KB/2MB */ FLASH_FS (r) : ORIGIN = 0x00100000, LENGTH = 0x00100000 /* 1MB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00080000 /* 512KB */ OSPI_RAM (rwx) : ORIGIN = 0x68000000, LENGTH = 0x00800000 /* 8MB/8MB */ @@ -304,3 +305,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld b/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld index 52f8acf93eb33..e1c1c5b796a61 100644 --- a/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA4M1/ra4m1_ek.ld @@ -3,9 +3,14 @@ */ /* Linker script to configure memory regions. */ +/* + * VFSROM_FS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (2k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00037000 /* 220KB/256KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00034000 /* 208KB/256KB */ + VFSROM_FS (r) : ORIGIN = 0x00034000, LENGTH = 0x00003000 /* 12KB/256KB */ FLASH_FS (r) : ORIGIN = 0x00037000, LENGTH = 0x00009000 /* 36KB/256KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* 32KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +305,5 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h b/ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h index 9eda05f0fdd1b..19477f9df9c3e 100644 --- a/ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h +++ b/ports/renesas-ra/boards/EK_RA4W1/mpconfigboard.h @@ -67,3 +67,5 @@ #define MICROPY_HW_LED_ON(pin) mp_hal_pin_low(pin) #define MICROPY_HW_LED_OFF(pin) mp_hal_pin_high(pin) #define MICROPY_HW_LED_TOGGLE(pin) mp_hal_pin_toggle(pin) + +#define MICROPY_VFS_ROM (1) diff --git a/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld b/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld index 1241b5bc2302b..bdc90d545876d 100644 --- a/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA4W1/ra4w1_ek.ld @@ -3,9 +3,14 @@ */ /* Linker script to configure memory regions. */ +/* + * VFSROM_FS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (2k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00070000 /* 448KB/512KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00060000 /* 384KB/512KB */ + VFSROM_FS (r) : ORIGIN = 0x00060000, LENGTH = 0x00010000 /* 64KB/512KB */ FLASH_FS (r) : ORIGIN = 0x00070000, LENGTH = 0x00010000 /* 64KB/512KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000 /* 96KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +305,5 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld b/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld index c7d85ed3db0a1..c10cdfcbb22f9 100644 --- a/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld +++ b/ports/renesas-ra/boards/EK_RA6M1/ra6m1_ek.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00070000 /* 448KB/512KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00060000 /* 384KB/512KB */ + VFSROM_FS (r) : ORIGIN = 0x00060000, LENGTH = 0x00010000 /* 64KB/512KB */ FLASH_FS (r) : ORIGIN = 0x00070000, LENGTH = 0x00010000 /* 64KB/512KB */ RAM (rwx) : ORIGIN = 0x1FFE0000, LENGTH = 0x00040000 /* 256KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +301,5 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld b/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld index 086f6630c110a..346ef827fc71d 100644 --- a/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld +++ b/ports/renesas-ra/boards/EK_RA6M2/ra6m2_ek.ld @@ -3,9 +3,15 @@ */ /* Linker script to configure memory regions. */ + +/* + * VFSROM_FS and FLASH_FS must start and length must be a multiple + * of the physical sector size of that region (32k). + */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000e0000 /* 896KB/1MB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000e0000 /* 768KB/1MB */ + VFSROM_FS (r) : ORIGIN = 0x000c0000, LENGTH = 0x00020000 /* 128KB/1MB */ FLASH_FS (r) : ORIGIN = 0x000e0000, LENGTH = 0x00020000 /* 128KB/1MB */ RAM (rwx) : ORIGIN = 0x1FFE0000, LENGTH = 0x00060000 /* 384KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00008000 /* 32KB */ @@ -300,3 +306,6 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld b/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld index 52f8acf93eb33..6667a7153387a 100644 --- a/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld +++ b/ports/renesas-ra/boards/RA4M1_CLICKER/ra4m1_clicker.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00037000 /* 220KB/256KB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00034000 /* 208KB/256KB */ + VFSROM_FS (r) : ORIGIN = 0x00034000, LENGTH = 0x00003000 /* 12KB/256KB */ FLASH_FS (r) : ORIGIN = 0x00037000, LENGTH = 0x00009000 /* 36KB/256KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000 /* 32KB */ DATA_FLASH (rx) : ORIGIN = 0x40100000, LENGTH = 0x00002000 /* 8KB */ @@ -300,3 +301,5 @@ _heap_end = __HeapLimit; /* tunable */ _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld b/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld index 8363d2e743f24..42fa6682a884e 100644 --- a/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld +++ b/ports/renesas-ra/boards/VK_RA6M5/vk_ra6m5.ld @@ -5,7 +5,8 @@ /* Linker script to configure memory regions. */ MEMORY { - FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00100000 /* 1MB/2MB */ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x000C0000 /* 768KB/2MB */ + VFSROM_FS (r) : ORIGIN = 0x000C0000, LENGTH = 0x00040000 /* 256KB/2MB */ FLASH_FS (r) : ORIGIN = 0x00100000, LENGTH = 0x00100000 /* 1MB/2MB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00080000 /* 512KB */ OSPI_RAM (rwx) : ORIGIN = 0x68000000, LENGTH = 0x00800000 /* 8MB/8MB */ @@ -306,3 +307,6 @@ _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); _micropy_hw_external_flash_storage_start = ORIGIN(QSPI_FLASH); _micropy_hw_external_flash_storage_end = ORIGIN(QSPI_FLASH) + LENGTH(QSPI_FLASH); + +_micropy_hw_vfsrom_storage_start = ORIGIN(VFSROM_FS); +_micropy_hw_vfsrom_storage_len = LENGTH(VFSROM_FS); diff --git a/ports/renesas-ra/build_all_boards.sh b/ports/renesas-ra/build_all_boards.sh deleted file mode 100644 index 0c192dba93103..0000000000000 --- a/ports/renesas-ra/build_all_boards.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -set -eu -o pipefail -export BOARD="RA4M1_CLICKER" -DT=`date +%Y%m%d%H%M` -make DEBUG=0 BOARD=${BOARD} clean 2>&1 | tee ${BOARD}_build_${DT}.log -make DEBUG=0 BOARD=${BOARD} 2>&1 | tee -a ${BOARD}_build_${DT}.log -# -export BOARD="EK_RA6M2" -DT=`date +%Y%m%d%H%M` -make DEBUG=1 BOARD=${BOARD} clean 2>&1 | tee ${BOARD}_build_${DT}.log -make DEBUG=1 BOARD=${BOARD} 2>&1 | tee -a ${BOARD}_build_${DT}.log -# -export BOARD="EK_RA4M1" -DT=`date +%Y%m%d%H%M` -make DEBUG=0 BOARD=${BOARD} clean 2>&1 | tee ${BOARD}_build_${DT}.log -make DEBUG=0 BOARD=${BOARD} 2>&1 | tee -a ${BOARD}_build_${DT}.log -# -export BOARD="EK_RA4W1" -DT=`date +%Y%m%d%H%M` -make DEBUG=0 BOARD=${BOARD} clean 2>&1 | tee ${BOARD}_build_${DT}.log -make DEBUG=0 BOARD=${BOARD} 2>&1 | tee -a ${BOARD}_build_${DT}.log -# -export BOARD="EK_RA6M1" -DT=`date +%Y%m%d%H%M` -make DEBUG=1 BOARD=${BOARD} clean 2>&1 | tee ${BOARD}_build_${DT}.log -make DEBUG=1 BOARD=${BOARD} 2>&1 | tee -a ${BOARD}_build_${DT}.log diff --git a/ports/renesas-ra/mpconfigboard_common.h b/ports/renesas-ra/mpconfigboard_common.h index 479c9f61d1a68..2bd0eab5611e3 100644 --- a/ports/renesas-ra/mpconfigboard_common.h +++ b/ports/renesas-ra/mpconfigboard_common.h @@ -154,6 +154,10 @@ #define MICROPY_HW_UART_IS_RESERVED(uart_id) (false) #endif +// Whether to support the VFSROM file system +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif /*****************************************************************************/ // General configuration diff --git a/ports/renesas-ra/storage.c b/ports/renesas-ra/storage.c index 12416e4dc7a9f..57ffd957f8d16 100644 --- a/ports/renesas-ra/storage.c +++ b/ports/renesas-ra/storage.c @@ -28,6 +28,7 @@ #include #include +#include "py/objarray.h" #include "py/runtime.h" #include "py/mperrno.h" #include "extmod/vfs_fat.h" @@ -36,6 +37,13 @@ #include "led.h" #include "storage.h" #include "irq.h" +#include "flash.h" + +typedef struct _pyb_flash_obj_t { + mp_obj_base_t base; + uint32_t start; // in bytes + uint32_t len; // in bytes +} pyb_flash_obj_t; #if MICROPY_HW_ENABLE_STORAGE @@ -236,12 +244,6 @@ int storage_readblocks_ext(uint8_t *dest, uint32_t block, uint32_t offset, uint3 } #endif -typedef struct _pyb_flash_obj_t { - mp_obj_base_t base; - uint32_t start; // in bytes - uint32_t len; // in bytes -} pyb_flash_obj_t; - // This Flash object represents the entire available flash, with emulated partition table at start const pyb_flash_obj_t pyb_flash_obj = { { &pyb_flash_type }, @@ -427,3 +429,57 @@ void pyb_flash_init_vfs(fs_user_mount_t *vfs) { } #endif + +#if MICROPY_VFS_ROM + +extern uint32_t _micropy_hw_vfsrom_storage_start, _micropy_hw_vfsrom_storage_len; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)&_micropy_hw_vfsrom_storage_start) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)&_micropy_hw_vfsrom_storage_len) +#define VFSROM_BLOCK_SIZE (2048) + +static const MP_DEFINE_MEMORYVIEW_OBJ(romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&romfs_obj); + + case MP_VFS_ROM_IOCTL_WRITE_PREPARE: { + // Erase sectors in given range. + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = MICROPY_HW_ROMFS_BASE; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + uint32_t sec_size = sector_size(dest); + for (; dest < dest_max; dest += sec_size) { + flash_erase(dest, sec_size); + } + return MP_OBJ_NEW_SMALL_INT(4); // minimum write size + } + + case MP_VFS_ROM_IOCTL_WRITE: { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = MICROPY_HW_ROMFS_BASE + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + flash_write(dest, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(0); + } + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} + +#endif // MICROPY_VFS_ROM diff --git a/ports/rp2/modules/_boot.py b/ports/rp2/modules/_boot.py index 71cb28d6823bb..772287078806c 100644 --- a/ports/rp2/modules/_boot.py +++ b/ports/rp2/modules/_boot.py @@ -1,6 +1,13 @@ import vfs +import sys import machine, rp2 +# Try to mount the ROMFS. +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass # Try to mount the filesystem, and format the flash if it doesn't exist. # Note: the flash requires the programming size to be aligned to 256 bytes. @@ -12,4 +19,4 @@ fs = vfs.VfsLfs2(bdev, progsize=256) vfs.mount(fs, "/") -del vfs, bdev, fs +del vfs, sys, bdev, fs diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index fe8287ba1d6e5..435e0a7ca14d7 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -170,6 +170,7 @@ #define MICROPY_VFS (1) #define MICROPY_VFS_LFS2 (1) #define MICROPY_VFS_FAT (1) +#define MICROPY_VFS_ROM (1) #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index a487fb1633b21..a2a320bad41e0 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -28,6 +28,7 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "py/mperrno.h" #include "extmod/vfs.h" #include "modrp2.h" #include "hardware/flash.h" @@ -44,6 +45,13 @@ static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size mu #define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) #endif +#ifndef MICROPY_HW_ROMFS_BYTES +#define MICROPY_HW_ROMFS_BYTES (128 * 1024) +#endif + +// Put ROMFS at the upper end of the code space. +#define MICROPY_HW_ROMFS_BASE (MICROPY_HW_FLASH_STORAGE_BASE - MICROPY_HW_ROMFS_BYTES) + static_assert(MICROPY_HW_FLASH_STORAGE_BYTES <= PICO_FLASH_SIZE_BYTES, "MICROPY_HW_FLASH_STORAGE_BYTES too big"); static_assert(MICROPY_HW_FLASH_STORAGE_BASE + MICROPY_HW_FLASH_STORAGE_BYTES <= PICO_FLASH_SIZE_BYTES, "MICROPY_HW_FLASH_STORAGE_BYTES too big"); @@ -59,6 +67,14 @@ static rp2_flash_obj_t rp2_flash_obj = { .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, }; +#if MICROPY_HW_ROMFS_BYTES > 0 +static rp2_flash_obj_t rp2_flash_romfs_obj = { + .base = { &rp2_flash_type }, + .flash_base = MICROPY_HW_ROMFS_BASE, + .flash_size = MICROPY_HW_ROMFS_BYTES, +}; +#endif + // Tag the flash drive in the binary as readable/writable (but not reformatable) bi_decl(bi_block_device( BINARY_INFO_TAG_MICROPYTHON, @@ -138,6 +154,19 @@ static mp_obj_t rp2_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz return MP_OBJ_FROM_PTR(self); } +static mp_int_t rp2_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + rp2_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)(XIP_BASE + self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} + static mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) { rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; @@ -218,5 +247,19 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, rp2_flash_make_new, + buffer, rp2_flash_get_buffer, locals_dict, &rp2_flash_locals_dict ); + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + switch (mp_obj_get_int(args[0])) { + #if MICROPY_HW_ROMFS_BYTES > 0 + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&rp2_flash_romfs_obj); + #endif + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 005664f178d3c..e7441a6d94970 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -90,7 +90,7 @@ CFLAGS += $(CFLAGS_EXTRA) CFLAGS += -DMICROPY_HW_CODESIZE=$(strip $(subst K,' ', $(MICROPY_HW_CODESIZE))) LDFLAGS += -nostdlib $(addprefix -T,$(LD_FILES)) -Map=$@.map --cref -LDFLAGS += --defsym=_codesize=$(MICROPY_HW_CODESIZE) +LDFLAGS += --defsym=_codesize=$(MICROPY_HW_CODESIZE) --defsym=_vfsromsize=$(MICROPY_HW_VFSROMSIZE) LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) @@ -103,6 +103,10 @@ CFLAGS += -Os -DNDEBUG LDFLAGS += --gc-sections --print-memory-usage CFLAGS += -fdata-sections -ffunction-sections endif +# Disable VFSROM support if the size was set to 0 +ifeq ($(MICROPY_HW_VFSROMSIZE),0) + CFLAGS += -DMICROPY_VFS_ROM=0 +endif # Flags for optional C++ source code CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk index e2895c7c0c2f9..6ea327a650d40 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk @@ -5,4 +5,4 @@ TEXT0 = 0x2000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk index 6ec2d43dedbd8..73be611309e4a 100644 --- a/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M4_EXPRESS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk index e2895c7c0c2f9..6ea327a650d40 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M0_EXPRESS/mpconfigboard.mk @@ -5,4 +5,4 @@ TEXT0 = 0x2000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk index 740154a6d6825..adc9b1397c3eb 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk index 43ca5a59cc636..709441b833212 100644 --- a/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk +++ b/ports/samd/boards/ADAFRUIT_METRO_M4_EXPRESS/mpconfigboard.mk @@ -7,4 +7,7 @@ TEXT0 = 0x4000 MICROPY_PY_NETWORK ?= 1 MICROPY_PY_NETWORK_NINAW10 ?= 1 -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.mk b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk index 54948627d2b67..eea063fe55433 100644 --- a/ports/samd/boards/MINISAM_M4/mpconfigboard.mk +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk @@ -6,4 +6,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk index cc43c22cea588..4e8fb3d4429fb 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -3,4 +3,4 @@ CMSIS_MCU = SAMD21J18A LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 -MICROPY_HW_CODESIZE ?= 248K +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk index 7bf70ac669566..fd6d4b24516d0 100644 --- a/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk +++ b/ports/samd/boards/SEEED_WIO_TERMINAL/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 496K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# The default for MICROPY_HW_VFSROMSIZE is 64K +MICROPY_HW_CODESIZE ?= 432K diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk index 263e582694495..0af891e263686 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.mk @@ -5,4 +5,7 @@ TEXT0 = 0x4000 # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings -MICROPY_HW_CODESIZE ?= 1008K +# The size of a MCU flash filesystem will be +# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +MICROPY_HW_CODESIZE ?= 752K +MICROPY_HW_VFSROMSIZE ?= 256K diff --git a/ports/samd/boards/samd21x18a.ld b/ports/samd/boards/samd21x18a.ld index 3ab051569fb18..7de761b0f6a84 100644 --- a/ports/samd/boards/samd21x18a.ld +++ b/ports/samd/boards/samd21x18a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _vfsromsize are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_VFSROMSIZE and are set in Makefile. */ _flashsize = 256K; /* The physical flash size */ @@ -21,8 +21,18 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 8K; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs will be 0. +That does not matter since these devices do not use the MCU flash file +system. +*/ + _oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +_sflash_fs = _flashsize - _codesize - _bootloader - _vfsromsize; + +_oflash_vfsrom = ORIGIN(FLASH) + _flashsize - _vfsromsize - _bootloader; +_sflash_vfsrom = _vfsromsize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/boards/samd51x19a.ld b/ports/samd/boards/samd51x19a.ld index 30bc8e33281fb..45d102da8874c 100644 --- a/ports/samd/boards/samd51x19a.ld +++ b/ports/samd/boards/samd51x19a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _vfsromsize are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_VFSROMSIZE and are set in Makefile. */ _flashsize = 512K; /* The physical flash size */ @@ -21,8 +21,18 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 16K; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs might be 0. +That does not matter since these devices do not use the MCU flash file +system. +*/ + _oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +_sflash_fs = _flashsize - _codesize - _bootloader - _vfsromsize; + +_oflash_vfsrom = ORIGIN(FLASH) + _flashsize - _vfsromsize - _bootloader; +_sflash_vfsrom = _vfsromsize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/boards/samd51x20a.ld b/ports/samd/boards/samd51x20a.ld index 472ab316c6fde..e485a173fcdaa 100644 --- a/ports/samd/boards/samd51x20a.ld +++ b/ports/samd/boards/samd51x20a.ld @@ -3,8 +3,8 @@ */ /* -_codesize is defined in mpconfigmcu.mk or mpconfigboard.mk as -MICROPY_HW_CODESIZE and is set in Makefile +_codesize and _vfsromsize are defined in mpconfigmcu.mk or mpconfigboard.mk +as MICROPY_HW_CODESIZE and MICROPY_HW_VFSROMSIZE and are set in Makefile. */ _flashsize = 1024K; /* The physical flash size */ @@ -21,8 +21,18 @@ MEMORY _estack = ORIGIN(RAM) + LENGTH(RAM) - 8; _sstack = _estack - 16K; +/* +The VfsROM file system is placed at the end of the flash. +For device with SPI flash the number for _sflash_fs might be 0. +That does not matter since these devices do not use the MCU flash file +system. +*/ + _oflash_fs = ORIGIN(FLASH) + _codesize; -_sflash_fs = _flashsize - _codesize - _bootloader; +_sflash_fs = _flashsize - _codesize - _bootloader - _vfsromsize; + +_oflash_vfsrom = ORIGIN(FLASH) + _flashsize - _vfsromsize - _bootloader; +_sflash_vfsrom = _vfsromsize; _sheap = _ebss; _eheap = _sstack; diff --git a/ports/samd/mcu/samd21/manifest.py b/ports/samd/mcu/samd21/manifest.py index 2a19a843f8a9b..8ad1e38ba099a 100644 --- a/ports/samd/mcu/samd21/manifest.py +++ b/ports/samd/mcu/samd21/manifest.py @@ -1,5 +1,2 @@ include("$(PORT_DIR)/boards/manifest.py") include("$(MPY_DIR)/extmod/asyncio") -require("onewire") -require("ds18x20") -require("dht") diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index 29965f50f63a8..131ee723a9667 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -2,7 +2,7 @@ #include "samd21.h" #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES) -#if MICROPY_HW_CODESIZE == 248 +#if MICROPY_HW_CODESIZE == 232 #define SAMD21_EXTRA_FEATURES 1 #else #define SAMD21_EXTRA_FEATURES 0 diff --git a/ports/samd/mcu/samd21/mpconfigmcu.mk b/ports/samd/mcu/samd21/mpconfigmcu.mk index 34209775c2565..cc6058562c4b2 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.mk +++ b/ports/samd/mcu/samd21/mpconfigmcu.mk @@ -4,10 +4,13 @@ CFLAGS_MCU += -DCFG_TUSB_MCU=OPT_MCU_SAMD21 MPY_CROSS_MCU_ARCH = armv6m -MICROPY_HW_CODESIZE ?= 184K +MICROPY_HW_CODESIZE ?= 191K -ifeq ($(MICROPY_HW_CODESIZE), 248K) +ifeq ($(MICROPY_HW_CODESIZE), 232K) FROZEN_MANIFEST ?= mcu/$(MCU_SERIES_LOWER)/manifest.py +MICROPY_HW_VFSROMSIZE ?= 16K +else +MICROPY_HW_VFSROMSIZE ?= 15K endif MICROPY_VFS_LFS1 ?= 1 diff --git a/ports/samd/mcu/samd51/manifest.py b/ports/samd/mcu/samd51/manifest.py index 2a19a843f8a9b..8ad1e38ba099a 100644 --- a/ports/samd/mcu/samd51/manifest.py +++ b/ports/samd/mcu/samd51/manifest.py @@ -1,5 +1,2 @@ include("$(PORT_DIR)/boards/manifest.py") include("$(MPY_DIR)/extmod/asyncio") -require("onewire") -require("ds18x20") -require("dht") diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 9a7b8528f3573..19b29612dfa24 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -18,10 +18,10 @@ unsigned long trng_random_u32(void); #define MICROPY_PY_MACHINE_UART_IRQ (1) // fatfs configuration used in ffconf.h -#define MICROPY_FATFS_ENABLE_LFN (1) -#define MICROPY_FATFS_RPATH (2) -#define MICROPY_FATFS_MAX_SS (4096) -#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define VFS_BLOCK_SIZE_BYTES (1536) // diff --git a/ports/samd/mcu/samd51/mpconfigmcu.mk b/ports/samd/mcu/samd51/mpconfigmcu.mk index 9bef5eca166ab..9bb1292434f16 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.mk +++ b/ports/samd/mcu/samd51/mpconfigmcu.mk @@ -5,6 +5,7 @@ CFLAGS_MCU += -DCFG_TUSB_MCU=OPT_MCU_SAMD51 MPY_CROSS_MCU_ARCH = armv7m MICROPY_HW_CODESIZE ?= 368K +MICROPY_HW_VFSROMSIZE ?= 64K MICROPY_VFS_LFS2 ?= 1 MICROPY_VFS_FAT ?= 1 diff --git a/ports/samd/modules/_boot.py b/ports/samd/modules/_boot.py index f4053e2cddf00..88a9c0c4a9504 100644 --- a/ports/samd/modules/_boot.py +++ b/ports/samd/modules/_boot.py @@ -3,6 +3,12 @@ import samd import sys +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2)), "/rom") + sys.path.insert(0, "/rom") +except: + pass + bdev = samd.Flash() # Try to mount the filesystem, and format the flash if it doesn't exist. diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index 0b47500bf7e7c..896acd1a00c0c 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -85,6 +85,9 @@ #define MICROPY_PY_OS_INCLUDEFILE "ports/samd/modos.c" #define MICROPY_READER_VFS (1) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (1) +#endif #ifndef MICROPY_PY_MACHINE_ADC #define MICROPY_PY_MACHINE_ADC (1) #endif diff --git a/ports/samd/samd_flash.c b/ports/samd/samd_flash.c index f68bdf140f490..6e2e07e8d68ef 100644 --- a/ports/samd/samd_flash.c +++ b/ports/samd/samd_flash.c @@ -26,11 +26,13 @@ #include +#include "py/objarray.h" #include "py/runtime.h" #include "extmod/vfs.h" +#include "py/mperrno.h" #include "samd_soc.h" -#if MICROPY_HW_MCUFLASH +#if MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM // ASF 4 #include "hal_flash.h" @@ -45,7 +47,6 @@ #endif static struct flash_descriptor flash_desc; -static mp_int_t BLOCK_SIZE = VFS_BLOCK_SIZE_BYTES; // Board specific: mpconfigboard.h extern const mp_obj_type_t samd_flash_type; typedef struct _samd_flash_obj_t { @@ -54,8 +55,9 @@ typedef struct _samd_flash_obj_t { uint32_t flash_size; } samd_flash_obj_t; +#if MICROPY_HW_MCUFLASH +static mp_int_t BLOCK_SIZE = VFS_BLOCK_SIZE_BYTES; // Board specific: mpconfigboard.h extern uint8_t _oflash_fs, _sflash_fs; - // Build a Flash storage at top. static samd_flash_obj_t samd_flash_obj = { .base = { &samd_flash_type }, @@ -63,9 +65,31 @@ static samd_flash_obj_t samd_flash_obj = { .flash_size = (uint32_t)&_sflash_fs, // Get from MCU-Specific loader script. }; +static mp_obj_t samd_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // No args required. bdev=Flash(). Start Addr & Size defined in samd_flash_obj. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // Return singleton object. + return MP_OBJ_FROM_PTR(&samd_flash_obj); +} + +static mp_int_t samd_flash_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + samd_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (flags == MP_BUFFER_READ) { + bufinfo->buf = (void *)((uintptr_t)self->flash_base); + bufinfo->len = self->flash_size; + bufinfo->typecode = 'B'; + return 0; + } else { + // Write unsupported. + return 1; + } +} +#endif // MICROPY_HW_MCUFLASH + // Flash init (from cctpy) // Method is needed for when MP starts up in _boot.py -static void samd_flash_init(void) { +void samd_flash_init(void) { #ifdef SAMD51 hri_mclk_set_AHBMASK_NVMCTRL_bit(MCLK); #endif @@ -76,27 +100,19 @@ static void samd_flash_init(void) { flash_init(&flash_desc, NVMCTRL); } -static mp_obj_t samd_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - // No args required. bdev=Flash(). Start Addr & Size defined in samd_flash_obj. - mp_arg_check_num(n_args, n_kw, 0, 0, false); - - samd_flash_init(); - - // Return singleton object. - return MP_OBJ_FROM_PTR(&samd_flash_obj); -} - +#if MICROPY_HW_MCUFLASH // Function for ioctl. static mp_obj_t eraseblock(uint32_t sector_in) { // Destination address aligned with page start to be erased. - uint32_t DEST_ADDR = sector_in; // Number of pages to be erased. - mp_int_t PAGE_SIZE = flash_get_page_size(&flash_desc); // adf4 API call + uint32_t dest_addr = sector_in; // Number of pages to be erased. + mp_int_t page_size = flash_get_page_size(&flash_desc); // adf4 API call - flash_erase(&flash_desc, DEST_ADDR, (BLOCK_SIZE / PAGE_SIZE)); + flash_erase(&flash_desc, dest_addr, (BLOCK_SIZE / page_size)); return mp_const_none; } + static mp_obj_t samd_flash_version(void) { return MP_OBJ_NEW_SMALL_INT(flash_get_version()); } @@ -131,7 +147,7 @@ static mp_obj_t samd_flash_writeblocks(size_t n_args, const mp_obj_t *args) { } // Write data to flash (adf4 API) flash_write(&flash_desc, offset, bufinfo.buf, bufinfo.len); - // TODO check return value + return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_flash_writeblocks_obj, 3, 4, samd_flash_writeblocks); @@ -176,7 +192,75 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_Flash, MP_TYPE_FLAG_NONE, make_new, samd_flash_make_new, + buffer, samd_flash_get_buffer, locals_dict, &samd_flash_locals_dict ); +#endif -#endif // MICROPY_HW_MCUFLASH +#if MICROPY_VFS_ROM +// +// Uses object-based capabilities for devices using the internal flash +// for the regular file system and ioctl function for devices with +// external flash. +// +extern uint8_t _oflash_vfsrom, _sflash_vfsrom; + +#define MICROPY_HW_ROMFS_BASE ((uint32_t)&_oflash_vfsrom) +#define MICROPY_HW_ROMFS_BYTES ((uint32_t)&_sflash_vfsrom) + +#if MICROPY_HW_MCUFLASH +static samd_flash_obj_t samd_flash_romfs_obj = { + .base = { &samd_flash_type }, + .flash_base = MICROPY_HW_ROMFS_BASE, // Get from MCU-Specific loader script. + .flash_size = MICROPY_HW_ROMFS_BYTES, // Get from MCU-Specific loader script. +}; +#else +static const MP_DEFINE_MEMORYVIEW_OBJ(samd_flash_romfs_obj, 'B', 0, MICROPY_HW_ROMFS_BYTES, (void *)MICROPY_HW_ROMFS_BASE); +#endif + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + if (MICROPY_HW_ROMFS_BYTES <= 0) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + switch (mp_obj_get_int(args[0])) { + case MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS: + return MP_OBJ_NEW_SMALL_INT(1); + + case MP_VFS_ROM_IOCTL_GET_SEGMENT: + return MP_OBJ_FROM_PTR(&samd_flash_romfs_obj); + + #if !MICROPY_HW_MCUFLASH + + case MP_VFS_ROM_IOCTL_WRITE_PREPARE: { + // Erase sectors in given range. + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest_addr = MICROPY_HW_ROMFS_BASE; + uint32_t bytes_used = mp_obj_get_int(args[2]); + mp_int_t page_size = flash_get_page_size(&flash_desc); // adf4 API call + flash_erase(&flash_desc, dest_addr, (bytes_used + page_size - 1) / page_size); + return MP_OBJ_NEW_SMALL_INT(4); + } + + case MP_VFS_ROM_IOCTL_WRITE: { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest_addr = MICROPY_HW_ROMFS_BASE + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + flash_write(&flash_desc, dest_addr, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(0); + } + + #endif + + default: + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } +} +#endif + +#endif // MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM diff --git a/ports/samd/samd_soc.c b/ports/samd/samd_soc.c index e78032513c216..3ec418ff96e17 100644 --- a/ports/samd/samd_soc.c +++ b/ports/samd/samd_soc.c @@ -39,6 +39,7 @@ #include "tusb.h" extern void machine_rtc_start(bool force); +extern void samd_flash_init(void); static void usb_init(void) { // Init USB clock @@ -120,6 +121,9 @@ void samd_init(void) { mp_hal_ticks_cpu_enable(); #endif machine_rtc_start(false); + #if MICROPY_HW_MCUFLASH || MICROPY_VFS_ROM + samd_flash_init(); + #endif } #if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SPI || MICROPY_PY_MACHINE_UART diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index a9612f1a5792e..d75a898448714 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -287,6 +287,7 @@ SRC_C += \ rtc.c \ flash.c \ flashbdev.c \ + romfs.c \ spibdev.c \ storage.c \ sdcard.c \ diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h index 8f4ce30eff80a..bddef85f764d6 100644 --- a/ports/stm32/boardctrl.h +++ b/ports/stm32/boardctrl.h @@ -61,6 +61,12 @@ #define MICROPY_BOARD_TOP_SOFT_RESET_LOOP boardctrl_top_soft_reset_loop #endif +#ifndef MICROPY_BOARD_FROZEN_BOOT_FILE +#if MICROPY_VFS_ROM +#define MICROPY_BOARD_FROZEN_BOOT_FILE "_boot.py" +#endif +#endif + #ifndef MICROPY_BOARD_RUN_BOOT_PY #define MICROPY_BOARD_RUN_BOOT_PY boardctrl_run_boot_py #endif diff --git a/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h b/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h index c573f942a0a13..f8c426b4d00ae 100644 --- a/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h +++ b/ports/stm32/boards/ADAFRUIT_F405_EXPRESS/mpconfigboard.h @@ -25,6 +25,10 @@ #define MICROPY_HW_RTC_USE_US (0) #define MICROPY_HW_RTC_USE_CALOUT (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // External SPI Flash config #if !MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index fdf3128a3606d..b46d3c4b81097 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -125,8 +130,8 @@ void GIGA_board_low_power(int mode); // QSPI flash #1 for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB at for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld index 793a76b970292..43dda26f07dc3 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,6 +44,10 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h index 505f8b6890169..d1b9f60b23e33 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 3) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -130,8 +135,8 @@ void NICLAV_board_osc_enable(int enable); // QSPI flash for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB at for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld index 6d6ce279f299c..580f51726dc4d 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } _cm4_ram_start = ORIGIN(SRAM4); @@ -46,6 +46,10 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 900bf18673728..e295dcb82cd4e 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -32,6 +32,11 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // Flash storage config #define MICROPY_HW_SPIFLASH_ENABLE_CACHE (1) #define MICROPY_HW_SPIFLASH_SOFT_RESET (1) @@ -129,8 +134,8 @@ void PORTENTA_board_osc_enable(int enable); // QSPI flash #1 for storage #define MICROPY_HW_QSPI_PRESCALER (2) // 100MHz #define MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 (27) -// Reserve 1MiB at the end for compatibility with alternate firmware that places WiFi blob here. -#define MICROPY_HW_SPIFLASH_SIZE_BITS (120 * 1024 * 1024) +// Reserve 4MiB for romfs and 1MiB at for WiFi/BT firmware. +#define MICROPY_HW_SPIFLASH_SIZE_BITS (88 * 1024 * 1024) #define MICROPY_HW_QSPIFLASH_CS (pyb_pin_QSPI2_CS) #define MICROPY_HW_QSPIFLASH_SCK (pyb_pin_QSPI2_CLK) #define MICROPY_HW_QSPIFLASH_IO0 (pyb_pin_QSPI2_D0) diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld index 793a76b970292..43dda26f07dc3 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/stm32h747.ld @@ -13,11 +13,11 @@ MEMORY SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */ FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */ + FLASH_ROMFS (rx) : ORIGIN = 0x90B00000, LENGTH = 4096K /* romfs partition in QSPI flash */ } /* produce a link error if there is not this amount of RAM for these sections */ @@ -44,6 +44,10 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); +/* Location of romfs filesystem */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + /* OpenAMP shared memory region */ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); diff --git a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h index 9878b1533eedb..e038bc2325ea2 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h +++ b/ports/stm32/boards/LEGO_HUB_NO6/mpconfigboard.h @@ -32,6 +32,10 @@ // For 2.7 to 3.6 V, 75 to 100 MHz: 3 wait states. #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // UART buses // Bluetooth HCI #define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS) diff --git a/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld b/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld index d3c43a6948316..5e516c01bf92d 100644 --- a/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld +++ b/ports/stm32/boards/LEGO_HUB_NO6/stm32f413xg.ld @@ -3,11 +3,11 @@ */ /* Specify the memory areas */ -/* FLASH_FS2 is placed before FLASH_TEXT to support 1MB and 1.5MB FLASH with common code in flashbdev.c */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ - FLASH_APP (rx) : ORIGIN = 0x08010000, LENGTH = 976K + FLASH_APP (rx) : ORIGIN = 0x08010000, LENGTH = 448K + FLASH_ROMFS (rx): ORIGIN = 0x08080000, LENGTH = 256K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K /* SRAM1 + SRAM2 */ } @@ -25,3 +25,7 @@ _ram_start = ORIGIN(RAM); _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; + +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); diff --git a/ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h b/ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h index 3615225e51b6b..b07c7cea9171e 100644 --- a/ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h +++ b/ports/stm32/boards/LEGO_HUB_NO7/mpconfigboard.h @@ -32,6 +32,10 @@ // For 2.7 to 3.6 V, 75 to 100 MHz: 3 wait states. #define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_3 +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // UART buses // Bluetooth HCI #define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS) diff --git a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h index 6a9ac02714fb7..00f31b5628168 100644 --- a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.h @@ -6,11 +6,13 @@ #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_INLINE_THUMB (0) #define MICROPY_OPT_COMPUTED_GOTO (0) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) #define MICROPY_PY_BUILTINS_COMPLEX (0) #define MICROPY_PY_SOCKET (0) #define MICROPY_PY_NETWORK (0) #define MICROPY_PY_STM (0) #define MICROPY_PY_PYB_LEGACY (0) +#define MICROPY_PY_MACHINE_PIN_LEGACY (0) #define MICROPY_PY_HEAPQ (0) #define MICROPY_PY_FRAMEBUF (0) @@ -32,6 +34,10 @@ // The board has an external 32kHz crystal #define MICROPY_HW_RTC_USE_LSE (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // UART config #define MICROPY_HW_UART1_TX (pin_B6) #define MICROPY_HW_UART1_RX (pin_B7) diff --git a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk index 193ed793f6fc0..d8187824830f0 100644 --- a/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F091RC/mpconfigboard.mk @@ -9,7 +9,7 @@ MICROPY_VFS_LFS1 ?= 1 MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM = 1 # Don't include default frozen modules because MCU is tight on flash space -FROZEN_MANIFEST ?= +FROZEN_MANIFEST ?= boards/manifest_minimal.py # LTO reduces final binary size, may be slower to build depending on gcc version and hardware LTO ?= 1 diff --git a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld index 70d7f9cc4b8e4..5efefde358cc8 100644 --- a/ports/stm32/boards/PYBD_SF2/f722_qspi.ld +++ b/ports/stm32/boards/PYBD_SF2/f722_qspi.ld @@ -20,7 +20,8 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 480K /* sectors 2-7 */ - FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ + FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 1024K /* external QSPI */ + FLASH_ROMFS (rx): ORIGIN = 0x90100000, LENGTH = 512K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K /* DTCM+SRAM1+SRAM2 */ } @@ -39,6 +40,10 @@ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + /* Define output sections */ SECTIONS { diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index f0ef67e77e604..e8923bf9b66e5 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -64,6 +64,11 @@ void board_sleep(int value); #define MICROPY_HW_RTC_USE_US (1) #define MICROPY_HW_RTC_USE_CALOUT (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // SPI flash #1, for R/W storage #define MICROPY_HW_SOFTQSPI_SCK_LOW(self) (GPIOE->BSRR = (0x10000 << 11)) #define MICROPY_HW_SOFTQSPI_SCK_HIGH(self) (GPIOE->BSRR = (1 << 11)) diff --git a/ports/stm32/boards/PYBD_SF6/f767.ld b/ports/stm32/boards/PYBD_SF6/f767.ld index 4262a48a9962c..e130efd6608fa 100644 --- a/ports/stm32/boards/PYBD_SF6/f767.ld +++ b/ports/stm32/boards/PYBD_SF6/f767.ld @@ -17,8 +17,9 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K - FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 2016K /* sectors 1-11 3x32K 1*128K 7*256K */ + FLASH_APP (rx) : ORIGIN = 0x08008000, LENGTH = 1504K /* sectors 1-9 3x32K 1*128K 5*256K */ FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 2048K /* external QSPI */ + FLASH_ROMFS (rx): ORIGIN = 0x90000000, LENGTH = 1024K /* external QSPI */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* DTCM=128k, SRAM1=368K, SRAM2=16K */ } @@ -37,4 +38,8 @@ _ram_end = ORIGIN(RAM) + LENGTH(RAM); _heap_start = _ebss; /* heap starts just after statically allocated memory */ _heap_end = _sstack; +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); + INCLUDE common_bl.ld diff --git a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h index 8c11d7b2eec96..526a111177987 100644 --- a/ports/stm32/boards/PYBD_SF6/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF6/mpconfigboard.h @@ -45,6 +45,11 @@ #define MICROPY_HW_CLK_PLLQ (6) #define MICROPY_HW_FLASH_LATENCY (FLASH_LATENCY_4) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) +#define MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ (&spi_bdev2.spiflash) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // Extra UART config #define MICROPY_HW_UART7_TX (pyb_pin_W16) #define MICROPY_HW_UART7_RX (pyb_pin_W22B) diff --git a/ports/stm32/boards/PYBV10/mpconfigboard.h b/ports/stm32/boards/PYBV10/mpconfigboard.h index 6219862be4957..b9216c70ff1fa 100644 --- a/ports/stm32/boards/PYBV10/mpconfigboard.h +++ b/ports/stm32/boards/PYBV10/mpconfigboard.h @@ -24,6 +24,10 @@ #define MICROPY_HW_RTC_USE_US (0) #define MICROPY_HW_RTC_USE_CALOUT (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // UART config #define MICROPY_HW_UART1_NAME "XB" #define MICROPY_HW_UART1_TX (pin_B6) diff --git a/ports/stm32/boards/PYBV11/mpconfigboard.h b/ports/stm32/boards/PYBV11/mpconfigboard.h index c8f660c8fbb86..2b96ff974b7f2 100644 --- a/ports/stm32/boards/PYBV11/mpconfigboard.h +++ b/ports/stm32/boards/PYBV11/mpconfigboard.h @@ -24,6 +24,10 @@ #define MICROPY_HW_RTC_USE_US (0) #define MICROPY_HW_RTC_USE_CALOUT (1) +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (1) +#define MICROPY_HW_ROMFS_ENABLE_PART1 (1) + // UART config #define MICROPY_HW_UART1_NAME "XB" #define MICROPY_HW_UART1_TX (pin_B6) diff --git a/ports/stm32/boards/_boot.py b/ports/stm32/boards/_boot.py new file mode 100644 index 0000000000000..be65bff646ad0 --- /dev/null +++ b/ports/stm32/boards/_boot.py @@ -0,0 +1,10 @@ +import sys, vfs + +try: + vfs.mount(vfs.VfsRom(vfs.rom_ioctl(2, 0)), "/rom") + # Note: this won't execute boot.py/main.py because the cwd is still /flash. + sys.path.insert(0, "/rom") +except: + pass + +del sys, vfs diff --git a/ports/stm32/boards/manifest.py b/ports/stm32/boards/manifest.py index 4f38179713ea6..7e4d1821351eb 100644 --- a/ports/stm32/boards/manifest.py +++ b/ports/stm32/boards/manifest.py @@ -1,3 +1,4 @@ +freeze(".", "_boot.py", opt=3) include("$(MPY_DIR)/extmod/asyncio") require("dht") diff --git a/ports/stm32/boards/manifest_minimal.py b/ports/stm32/boards/manifest_minimal.py new file mode 100644 index 0000000000000..ec2408e1074aa --- /dev/null +++ b/ports/stm32/boards/manifest_minimal.py @@ -0,0 +1 @@ +freeze(".", "_boot.py", opt=3) diff --git a/ports/stm32/boards/stm32f091xc.ld b/ports/stm32/boards/stm32f091xc.ld index a8f0d3316ebf6..0f0fedaf3067a 100644 --- a/ports/stm32/boards/stm32f091xc.ld +++ b/ports/stm32/boards/stm32f091xc.ld @@ -5,7 +5,8 @@ /* Specify the memory areas */ MEMORY { - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 236K /* sectors 0-117 */ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 226K /* sectors 0-112 */ + FLASH_ROMFS (rx): ORIGIN = 0x08038800, LENGTH = 10K /* sectors 113-117 */ FLASH_FS (r) : ORIGIN = 0x0803B000, LENGTH = 20K /* sectors 118-127 */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K } @@ -32,3 +33,6 @@ _heap_end = _sstack; _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); diff --git a/ports/stm32/boards/stm32f405.ld b/ports/stm32/boards/stm32f405.ld index 792558b6371e0..98039f3e56d7b 100644 --- a/ports/stm32/boards/stm32f405.ld +++ b/ports/stm32/boards/stm32f405.ld @@ -8,7 +8,8 @@ MEMORY FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* entire flash */ FLASH_START (rx): ORIGIN = 0x08000000, LENGTH = 16K /* sector 0 */ FLASH_FS (rx) : ORIGIN = 0x08004000, LENGTH = 112K /* sectors 1,2,3,4 are for filesystem */ - FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 896K /* sectors 5,6,7,8,9,10,11 */ + FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 640K /* sectors 5,6,7,8,9 */ + FLASH_ROMFS (rx): ORIGIN = 0x080c0000, LENGTH = 256K /* sectors 10,11 */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K } @@ -33,3 +34,7 @@ _micropy_hw_internal_flash_storage_ram_cache_start = ORIGIN(CCMRAM); _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(CCMRAM) + LENGTH(CCMRAM); _micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS); _micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS); + +/* ROMFS location */ +_micropy_hw_romfs_part1_start = ORIGIN(FLASH_ROMFS); +_micropy_hw_romfs_part1_size = LENGTH(FLASH_ROMFS); diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 4ce0a75b87e2b..2a650077f3a45 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -67,6 +67,26 @@ #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1) #endif +// Whether to enable ROMFS on the internal flash. +#ifndef MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH +#define MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH (0) +#endif + +// Whether to enable ROMFS on external QSPI flash. +#ifndef MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0) +#endif + +// Whether to enable ROMFS partition 1. +#ifndef MICROPY_HW_ROMFS_ENABLE_PART1 +#define MICROPY_HW_ROMFS_ENABLE_PART1 (0) +#endif + +// Whether to enable ROMFS partition 2. +#ifndef MICROPY_HW_ROMFS_ENABLE_PART2 +#define MICROPY_HW_ROMFS_ENABLE_PART2 (0) +#endif + // Whether to enable storage on the internal flash of the MCU #ifndef MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE #define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (1) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 2b57446ee8233..da86fa641623a 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -79,6 +79,9 @@ #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_VFS (1) +#ifndef MICROPY_VFS_ROM +#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI) +#endif // control over Python builtins #ifndef MICROPY_PY_BUILTINS_HELP_TEXT diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 98364db41d4fb..34359a1ccf838 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -34,8 +34,6 @@ #if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) -#define QSPI_MAP_ADDR (0x90000000) - #ifndef MICROPY_HW_QSPI_PRESCALER #define MICROPY_HW_QSPI_PRESCALER 3 // F_CLK = F_AHB/3 (72MHz when CPU is 216MHz) #endif diff --git a/ports/stm32/qspi.h b/ports/stm32/qspi.h index c774b12582be6..6fe91168cfdba 100644 --- a/ports/stm32/qspi.h +++ b/ports/stm32/qspi.h @@ -28,9 +28,16 @@ #include "drivers/bus/qspi.h" +#define QSPI_MAP_ADDR (0x90000000) +#define QSPI_MAP_ADDR_MAX (0xa0000000) + extern const mp_qspi_proto_t qspi_proto; void qspi_init(void); void qspi_memory_map(void); +static inline bool qspi_is_valid_addr(uint32_t addr) { + return QSPI_MAP_ADDR <= addr && addr < QSPI_MAP_ADDR_MAX; +} + #endif // MICROPY_INCLUDED_STM32_QSPI_H diff --git a/ports/stm32/romfs.c b/ports/stm32/romfs.c new file mode 100644 index 0000000000000..5bee9f548b6eb --- /dev/null +++ b/ports/stm32/romfs.c @@ -0,0 +1,161 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George + * + * 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 "py/obj.h" +#include "py/objarray.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "drivers/memory/spiflash.h" + +#include "flash.h" +#include "qspi.h" +#include "storage.h" + +#if MICROPY_VFS_ROM + +#if MICROPY_HW_ROMFS_ENABLE_PART1 && !defined(MICROPY_HW_ROMFS_PART1_START) +#define MICROPY_HW_ROMFS_PART1_START (uintptr_t)(&_micropy_hw_romfs_part1_start) +#define MICROPY_HW_ROMFS_PART1_SIZE (uintptr_t)(&_micropy_hw_romfs_part1_size) +extern uint8_t _micropy_hw_romfs_part1_start; +extern uint8_t _micropy_hw_romfs_part1_size; +#endif + +#if MICROPY_HW_ROMFS_ENABLE_PART2 && !defined(MICROPY_HW_ROMFS_PART2_START) +#define MICROPY_HW_ROMFS_PART2_START (uintptr_t)(&_micropy_hw_romfs_part2_start) +#define MICROPY_HW_ROMFS_PART2_SIZE (uintptr_t)(&_micropy_hw_romfs_part2_size) +extern uint8_t _micropy_hw_romfs_part2_start; +extern uint8_t _micropy_hw_romfs_part2_size; +#endif + +#define ROMFS_MEMORYVIEW(base, size) {{&mp_type_memoryview}, 'B', 0, (size), (void *)(base)} + +static const mp_obj_array_t romfs_obj_table[] = { + #if MICROPY_HW_ROMFS_ENABLE_PART1 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART1_START, MICROPY_HW_ROMFS_PART1_SIZE), + #endif + #if MICROPY_HW_ROMFS_ENABLE_PART2 + ROMFS_MEMORYVIEW(MICROPY_HW_ROMFS_PART2_START, MICROPY_HW_ROMFS_PART2_SIZE), + #endif +}; + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + mp_int_t cmd = mp_obj_get_int(args[0]); + if (cmd == MP_VFS_ROM_IOCTL_GET_NUMBER_OF_SEGMENTS) { + return MP_OBJ_NEW_SMALL_INT(MP_ARRAY_SIZE(romfs_obj_table)); + } + + if (n_args < 2) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + mp_int_t romfs_id = mp_obj_get_int(args[1]); + if (!(0 <= romfs_id && romfs_id < MP_ARRAY_SIZE(romfs_obj_table))) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + const mp_obj_array_t *romfs_obj = &romfs_obj_table[romfs_id]; + uintptr_t romfs_base = (uintptr_t)romfs_obj->items; + uintptr_t romfs_len = romfs_obj->len; + + if (cmd == MP_VFS_ROM_IOCTL_GET_SEGMENT) { + // Return the ROMFS memoryview object. + return MP_OBJ_FROM_PTR(romfs_obj); + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE_PREPARE) { + // Erase sectors in given range. + if (n_args < 3) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base; + uint32_t dest_max = dest + mp_obj_get_int(args[2]); + if (dest_max > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + #if MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH + if (flash_is_valid_addr(dest)) { + while (dest < dest_max) { + int ret = flash_erase(dest); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + uint32_t sector_size = 0; + flash_get_sector_info(dest, NULL, §or_size); + dest += sector_size; + } + return MP_OBJ_NEW_SMALL_INT(16); + } + #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI + if (qspi_is_valid_addr(dest)) { + dest -= QSPI_MAP_ADDR; + dest_max -= QSPI_MAP_ADDR; + while (dest < dest_max) { + int ret = mp_spiflash_erase_block(MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ, dest); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + dest += MP_SPIFLASH_ERASE_BLOCK_SIZE; + } + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif + } + + if (cmd == MP_VFS_ROM_IOCTL_WRITE) { + // Write data to flash. + if (n_args < 4) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + uint32_t dest = romfs_base + mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (dest + bufinfo.len > romfs_base + romfs_len) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); + } + + #if MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH + if (flash_is_valid_addr(dest)) { + int ret = flash_write(dest, bufinfo.buf, bufinfo.len / 4); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI + if (qspi_is_valid_addr(dest)) { + dest -= QSPI_MAP_ADDR; + int ret = mp_spiflash_write(MICROPY_HW_ROMFS_QSPI_SPIFLASH_OBJ, dest, bufinfo.len, bufinfo.buf); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif + } + + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} + +#endif // MICROPY_VFS_ROM diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c index a6594fd4d98c9..7625bd223f0cd 100644 --- a/ports/stm32/storage.c +++ b/ports/stm32/storage.c @@ -28,6 +28,7 @@ #include "py/mperrno.h" #include "extmod/vfs_fat.h" +#include "flash.h" #include "systick.h" #include "led.h" #include "storage.h" @@ -289,6 +290,7 @@ static mp_obj_t pyb_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); diff --git a/ports/unix/main.c b/ports/unix/main.c index 58fa3ff589a55..53a5a2309f55b 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -45,6 +45,7 @@ #include "py/gc.h" #include "py/objstr.h" #include "py/cstack.h" +#include "py/mperrno.h" #include "py/mphal.h" #include "py/mpthread.h" #include "extmod/misc.h" @@ -803,3 +804,7 @@ void nlr_jump_fail(void *val) { fprintf(stderr, "FATAL: uncaught NLR %p\n", val); exit(1); } + +mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { + return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); +} diff --git a/py/objarray.h b/py/objarray.h index 4a0e8a983fe77..bb7a514b97913 100644 --- a/py/objarray.h +++ b/py/objarray.h @@ -53,6 +53,10 @@ typedef struct _mp_obj_array_t { } mp_obj_array_t; #if MICROPY_PY_BUILTINS_MEMORYVIEW + +#define MP_DEFINE_MEMORYVIEW_OBJ(obj_name, typecode, offset, len, ptr) \ + mp_obj_array_t obj_name = {{&mp_type_memoryview}, (typecode), (offset), (len), (ptr)} + static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) { self->base.type = &mp_type_memoryview; self->typecode = typecode; @@ -60,6 +64,7 @@ static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, self->len = len; self->items = items; } + #endif #if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index f86befd080349..f03596202421e 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -7,6 +7,7 @@ from .transport import TransportError, stdout_write_bytes from .transport_serial import SerialTransport +from .romfs import make_romfs class CommandError(Exception): @@ -478,3 +479,120 @@ def do_rtc(state, args): state.transport.exec("machine.RTC().datetime({})".format(timetuple)) else: print(state.transport.eval("machine.RTC().datetime()")) + + +def _do_romfs_query(state, args): + state.ensure_raw_repl() + state.did_action() + + # Detect the romfs and get its associated device. + state.transport.exec("import vfs") + num_rom_partitions = state.transport.eval("hasattr(vfs, 'rom_ioctl') and vfs.rom_ioctl(1)") + if num_rom_partitions is False: + print("No ROM partitions available") + return + + for rom_id in range(num_rom_partitions): + state.transport.exec(f"dev=vfs.rom_ioctl(2,{rom_id})") + has_object = state.transport.eval("hasattr(dev,'ioctl')") + if has_object: + rom_block_count = state.transport.eval("dev.ioctl(4,0)") + rom_block_size = state.transport.eval("dev.ioctl(5,0)") + rom_size = rom_block_count * rom_block_size + print( + f"ROM{rom_id} partition has size {rom_size} bytes ({rom_block_count} blocks of {rom_block_size} bytes each)" + ) + else: + rom_size = state.transport.eval("len(dev)") + print(f"ROM{rom_id} partition has size {rom_size} bytes") + print("Contents:", state.transport.eval("bytes(memoryview(dev)[:8]).hex(':')"), "...") + + +def _do_romfs_build(state, args): + state.did_action() + + input_directory = args.path[0] + output_file = input_directory + ".romfs" + romfs = make_romfs(input_directory, mpy_cross=args.mpy) + print(f"Image size is {len(romfs)} bytes") + with open(output_file, "wb") as f: + f.write(romfs) + + +def _do_romfs_deploy(state, args): + rom_id = 0 + + state.ensure_raw_repl() + state.did_action() + + # Read in or create the romfs filesystem. + romfs_filename = args.path[0] + if romfs_filename.endswith(".romfs"): + with open(romfs_filename, "rb") as f: + romfs = f.read() + else: + romfs = make_romfs(romfs_filename, mpy_cross=args.mpy) + print(f"Image size is {len(romfs)} bytes") + + # Detect the romfs and get its associated device. + state.transport.exec(f"import vfs;dev=vfs.rom_ioctl(2,{rom_id})") + has_object = state.transport.eval("hasattr(dev,'ioctl')") + if has_object: + rom_block_count = state.transport.eval("dev.ioctl(4,0)") + rom_block_size = state.transport.eval("dev.ioctl(5,0)") + rom_size = rom_block_count * rom_block_size + print( + f"ROM partition has size {rom_size} bytes ({rom_block_count} blocks of {rom_block_size} bytes each)" + ) + else: + rom_size = state.transport.eval("len(dev)") + print(f"ROM partition has size {rom_size} bytes") + + # Check if romfs will fit on the target device. + if len(romfs) > rom_size: + print("ROMFS partition is too small for image") + sys.exit(1) + + # Prepare ROM for writing. + print("Preparing ROM for writing") + chunk_size = 4096 + if has_object: + for offset in range(0, len(romfs), rom_block_size): + state.transport.exec(f"dev.ioctl(6,{offset // rom_block_size})") + chunk_size = min(chunk_size, rom_block_size) + else: + rom_min_write = state.transport.eval(f"vfs.rom_ioctl(3,{rom_id},{len(romfs)})") + chunk_size = max(chunk_size, rom_min_write) + + # Deploy the romfs filesystem to the device. + state.transport.exec(f"buf=bytearray({chunk_size})") + for offset in range(0, len(romfs), chunk_size): + romfs_chunk = romfs[offset : offset + chunk_size] + state.transport.exec(f"buf[:{len(romfs_chunk)}]=" + repr(romfs_chunk)) + print(f"\rWriting at offset {offset}", end="") + if has_object: + state.transport.exec( + f"dev.writeblocks({offset // rom_block_size},buf,{offset % rom_block_size})" + ) + else: + state.transport.exec(f"vfs.rom_ioctl(4,{rom_id},{offset},buf)") + + # Complete writing. + if has_object: + pass + else: + state.transport.eval(f"vfs.rom_ioctl(5,{rom_id})") + + print() + print("ROMFS image deployed") + + +def do_romfs(state, args): + if args.command[0] == "query": + _do_romfs_query(state, args) + elif args.command[0] == "build": + _do_romfs_build(state, args) + elif args.command[0] == "deploy": + _do_romfs_deploy(state, args) + else: + raise CommandError(f"romfs: '{args.command[0]}' is not a command") diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index e6e397020fe85..6130cb6200614 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -36,6 +36,7 @@ do_resume, do_rtc, do_soft_reset, + do_romfs, ) from .mip import do_mip from .repl import do_repl @@ -228,6 +229,14 @@ def argparse_mip(): return cmd_parser +def argparse_romfs(): + cmd_parser = argparse.ArgumentParser(description="manage romfs") + _bool_flag(cmd_parser, "mpy", "m", True, "download as compiled .mpy files (default)") + cmd_parser.add_argument("command", nargs=1, help="romfs command (e.g. query, build, deploy)") + cmd_parser.add_argument("path", nargs="*", help="path to directory to deploy") + return cmd_parser + + def argparse_none(description): return lambda: argparse.ArgumentParser(description=description) @@ -302,6 +311,15 @@ def argparse_none(description): do_version, argparse_none("print version and exit"), ), + # separate commands for each step: + # manifest -> directory -> romfs -> device + # then can have shortcuts that do everything at once + # can have auto-mpy-cross + # no need to do the first step (manifest) now + "romfs": ( + do_romfs, + argparse_romfs, + ), } # Additional commands aliases. diff --git a/tools/mpremote/mpremote/romfs.py b/tools/mpremote/mpremote/romfs.py new file mode 100644 index 0000000000000..2e848c0f96858 --- /dev/null +++ b/tools/mpremote/mpremote/romfs.py @@ -0,0 +1,170 @@ +# MIT license; Copyright (c) 2022 Damien P. George + +import struct, sys, os + +try: + from mpy_cross import run as mpy_cross_run +except ImportError: + mpy_cross_run = None + + +class VfsRomWriter: + ROMFS_HEADER = b"\xd2\xcd\x31" + + ROMFS_RECORD_KIND_UNUSED = 0 + ROMFS_RECORD_KIND_PADDING = 1 + ROMFS_RECORD_KIND_DATA_VERBATIM = 2 + ROMFS_RECORD_KIND_DATA_POINTER = 3 + ROMFS_RECORD_KIND_DIRECTORY = 4 + ROMFS_RECORD_KIND_FILE = 5 + + def __init__(self): + self._dir_stack = [(None, bytearray())] + + def _encode_uint(self, value): + encoded = [value & 0x7F] + value >>= 7 + while value != 0: + encoded.insert(0, 0x80 | (value & 0x7F)) + value >>= 7 + return bytes(encoded) + + def _pack(self, kind, payload): + return self._encode_uint(kind) + self._encode_uint(len(payload)) + payload + + def _extend(self, data): + buf = self._dir_stack[-1][1] + buf.extend(data) + return len(buf) + + def finalise(self): + _, data = self._dir_stack.pop() + encoded_kind = VfsRomWriter.ROMFS_HEADER + encoded_len = self._encode_uint(len(data)) + if (len(encoded_kind) + len(encoded_len) + len(data)) % 2 == 1: + encoded_len = b"\x80" + encoded_len + data = encoded_kind + encoded_len + data + return data + + def opendir(self, dirname): + self._dir_stack.append((dirname, bytearray())) + + def closedir(self): + dirname, dirdata = self._dir_stack.pop() + dirdata = self._encode_uint(len(dirname)) + bytes(dirname, "ascii") + dirdata + self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DIRECTORY, dirdata)) + + def mkdata(self, data): + assert len(self._dir_stack) == 1 + return self._extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, data)) - len( + data + ) + + def mkfile(self, filename, filedata): + filename = bytes(filename, "ascii") + payload = self._encode_uint(len(filename)) + payload += filename + if isinstance(filedata, tuple): + sub_payload = self._encode_uint(filedata[0]) + sub_payload += self._encode_uint(filedata[1]) + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_POINTER, sub_payload) + else: + payload += self._pack(VfsRomWriter.ROMFS_RECORD_KIND_DATA_VERBATIM, filedata) + self._dir_stack[-1][1].extend(self._pack(VfsRomWriter.ROMFS_RECORD_KIND_FILE, payload)) + + +def copy_recursively(vfs, src_dir, print_prefix, mpy_cross): + assert src_dir.endswith("/") + DIR = 1 << 14 + mpy_cross_missed = 0 + dir_contents = sorted(os.listdir(src_dir)) + for name in dir_contents: + src_name = src_dir + name + st = os.stat(src_name) + + if name == dir_contents[-1]: + # Last entry in the directory listing. + print_entry = "\\--" + print_recurse = " " + else: + # Not the last entry in the directory listing. + print_entry = "|--" + print_recurse = "| " + + if st[0] & DIR: + # A directory, enter it and copy its contents recursively. + print(print_prefix + print_entry, name + "/") + vfs.opendir(name) + mpy_cross_missed += copy_recursively( + vfs, src_name + "/", print_prefix + print_recurse, mpy_cross + ) + vfs.closedir() + else: + # A file. + did_mpy = False + name_extra = "" + if mpy_cross and name.endswith(".py"): + name_mpy = name[:-3] + ".mpy" + src_name_mpy = src_dir + name_mpy + if not os.path.isfile(src_name_mpy): + if mpy_cross_run is not None: + did_mpy = True + proc = mpy_cross_run(src_name) + proc.wait() + else: + mpy_cross_missed += 1 + if did_mpy: + name_extra = " -> .mpy" + print(print_prefix + print_entry, name + name_extra) + if did_mpy: + name = name_mpy + src_name = src_name_mpy + with open(src_name, "rb") as src: + vfs.mkfile(name, src.read()) + if did_mpy: + os.remove(src_name_mpy) + return mpy_cross_missed + + +def make_romfs(src_dir, *, mpy_cross): + if src_dir.endswith(".py"): + sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../..")) + import manifestfile + + VARS = {} + VARS["MPY_LIB_DIR"] = os.path.abspath("../../lib/micropython-lib") + manifest = manifestfile.ManifestFile(manifestfile.MODE_FREEZE, VARS) + try: + manifest.execute(src_dir) + except manifestfile.ManifestFileError as er: + print('freeze error executing "{}": {}'.format(src_dir, er.args[0])) + sys.exit(1) + + manifest_dir_structure = {} + for result in manifest.files(): + components = result.target_path.split("/") + s = manifest_dir_structure + for x in components[:-1]: + s = s.setdefault(x, {}) + s[components[-1]] = result + return + + if not src_dir.endswith("/"): + src_dir += "/" + + vfs = VfsRomWriter() + + # Build the filesystem recursively. + print("Building romfs filesystem, source directory: {}".format(src_dir)) + print("/") + try: + mpy_cross_missed = copy_recursively(vfs, src_dir, "", mpy_cross) + except OSError as er: + print("Error: OSError {}".format(er), file=sys.stderr) + sys.exit(1) + + if mpy_cross_missed: + print("Warning: `mpy_cross` module not found, .py files were not precompiled") + mpy_cross = False + + return vfs.finalise()