6f3a561c8c1b0b1dc2b79c7a927d3bd63badadc0
Chip-cn-dat/Espressif-dat/ESP32-S3-DAT/ESP32-S3-SDK-dat/ESP32-S3-SDK-dat.md
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | - [[ESP32-S3-dat]] |
| 5 | 5 | |
| 6 | -- [[ESP-SDK-dat]] |
|
| 6 | +- [[ESP-SDK-dat]] - [[ESP32-SDK-dat]] |
|
| 7 | 7 | |
| 8 | 8 | - [[micropython-dat]] - [[circuitpython-dat]] - [[circuitpython-ESP32-s3-dat]] |
| 9 | 9 |
SDK-dat/ESP-SDK-dat/ESP32-SDK-dat/ESP32-SDK-dat.md
| ... | ... | @@ -8,7 +8,11 @@ |
| 8 | 8 | |
| 9 | 9 | - [[esptool-dat]] |
| 10 | 10 | |
| 11 | -- [[data-storage-dat]] - [[USB-MSC-dat]] |
|
| 11 | +- [[data-storage-dat]] - [[USB-MSC-dat]] - [[SPIFF-dat]] - [[memory-dat]] |
|
| 12 | + |
|
| 13 | +- [[partition-table-dat]] |
|
| 14 | + |
|
| 15 | + |
|
| 12 | 16 | |
| 13 | 17 | |
| 14 | 18 | ## other reference |
| ... | ... | @@ -18,6 +22,9 @@ |
| 18 | 22 | |
| 19 | 23 | |
| 20 | 24 | |
| 25 | +## arduino |
|
| 26 | + |
|
| 27 | +- C:\Users\Administrator\AppData\Local\Arduino15\packages\esp32\hardware\esp32 |
|
| 21 | 28 | |
| 22 | 29 | |
| 23 | 30 |
Tech-dat/memory-dat/SPIFF-dat/SPIFF-dat.md
| ... | ... | @@ -1,6 +1,37 @@ |
| 1 | 1 | |
| 2 | 2 | # SPIFF-dat |
| 3 | 3 | |
| 4 | +- [[spiffsgen-dat]] |
|
| 5 | + |
|
| 6 | + |
|
| 7 | + |
|
| 8 | +## arduino read and print partition table |
|
| 9 | + |
|
| 10 | + |
|
| 11 | +code |
|
| 12 | + |
|
| 13 | + void printPartitionTable() { |
|
| 14 | + Serial.println("\n--- Partition Table ---"); |
|
| 15 | + esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); |
|
| 16 | + |
|
| 17 | + while (it != NULL) { |
|
| 18 | + const esp_partition_t* part = esp_partition_get(it); |
|
| 19 | + Serial.printf("Label: %-10s Type: 0x%02x SubType: 0x%02x Address: 0x%06x Size: 0x%06x\n", |
|
| 20 | + part->label, part->type, part->subtype, part->address, part->size); |
|
| 21 | + it = esp_partition_next(it); |
|
| 22 | + } |
|
| 23 | + esp_partition_iterator_release(it); |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | +output |
|
| 27 | + |
|
| 28 | + --- Partition Table --- |
|
| 29 | + Label: nvs Type: 0x01 SubType: 0x02 Address: 0x009000 Size: 0x005000 |
|
| 30 | + Label: otadata Type: 0x01 SubType: 0x00 Address: 0x00e000 Size: 0x002000 |
|
| 31 | + Label: app0 Type: 0x00 SubType: 0x10 Address: 0x010000 Size: 0x140000 |
|
| 32 | + Label: app1 Type: 0x00 SubType: 0x11 Address: 0x150000 Size: 0x140000 |
|
| 33 | + Label: spiffs Type: 0x01 SubType: 0x82 Address: 0x290000 Size: 0x160000 |
|
| 34 | + Label: coredump Type: 0x01 SubType: 0x03 Address: 0x3f0000 Size: 0x010000 |
|
| 4 | 35 | |
| 5 | 36 | |
| 6 | 37 |
Tech-dat/memory-dat/SPIFF-dat/spiffsgen-dat/spiffs_files_in/test.txt
| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | +123 |
|
| 2 | +321 |
|
| 3 | +abc |
|
| ... | ... | \ No newline at end of file |
Tech-dat/memory-dat/SPIFF-dat/spiffsgen-dat/spiffs_image.bin
| ... | ... | Binary files /dev/null and b/Tech-dat/memory-dat/SPIFF-dat/spiffsgen-dat/spiffs_image.bin differ |
Tech-dat/memory-dat/SPIFF-dat/spiffsgen-dat/spiffsgen-dat.md
| ... | ... | @@ -0,0 +1,122 @@ |
| 1 | + |
|
| 2 | +# spiffsgen-dat.md |
|
| 3 | + |
|
| 4 | +https://github.com/espressif/esp-idf/blob/v5.5.1/components/spiffs/spiffsgen.py |
|
| 5 | + |
|
| 6 | + |
|
| 7 | + python spiffsgen.py <image_size> <base_dir> <output_file> |
|
| 8 | + |
|
| 9 | +Use an image size that is a multiple of the block size (commonly 4096, 40960, 524288, etc.). |
|
| 10 | + |
|
| 11 | +Example (for 1MB image, block size 4096): |
|
| 12 | + |
|
| 13 | + python spiffsgen.py 1048576 . spiffs_image.bin |
|
| 14 | + |
|
| 15 | +1048576 / 4096 (4 KB) = 256 blocks |
|
| 16 | + |
|
| 17 | + python spiffsgen.py 40960 test.txt spiffs_image.bin |
|
| 18 | + |
|
| 19 | +To include multiple files in the SPIFFS image, specify a directory (not individual files) as the <base_dir> argument. All files and subdirectories inside that directory will be included in the image. |
|
| 20 | + |
|
| 21 | +Example: |
|
| 22 | + |
|
| 23 | +Suppose you have a folder called spiffs_files containing several files: |
|
| 24 | + |
|
| 25 | + spiffs_files/ |
|
| 26 | + file1.txt |
|
| 27 | + file2.txt |
|
| 28 | + config.json |
|
| 29 | + |
|
| 30 | + |
|
| 31 | + |
|
| 32 | +run: python spiffsgen.py 40960 ./spiffs_files_in spiffs_image.bin |
|
| 33 | + |
|
| 34 | + python spiffsgen.py 0x160000 ./spiffs_files_in spiffs_image.bin |
|
| 35 | + |
|
| 36 | +## extract (not working ) |
|
| 37 | + |
|
| 38 | + python spiffsgen.py 40960 ./spiffs_files spiffs_image.bin --extract <output_dir> |
|
| 39 | + |
|
| 40 | + python spiffsgen.py 40960 dummy_dir spiffs_image.bin --extract extracted_files |
|
| 41 | + |
|
| 42 | + python spiffsgen.py 40960 ./spiffs_files spiffs_image.bin --extract spiffs_files_out |
|
| 43 | + |
|
| 44 | + python spiffsgen.py 40960 ./spiffs_files spiffs_image.bin --extract spiffs_files_out |
|
| 45 | + |
|
| 46 | + |
|
| 47 | +## flash it |
|
| 48 | + |
|
| 49 | +- [[ESPtool-dat]] |
|
| 50 | + |
|
| 51 | + python esptool.py --chip esp32 --port [port] --baud [baud] write_flash -z 0x110000 spiffs.bin |
|
| 52 | + |
|
| 53 | + esptool --chip esp32-s3 --port COM16 --baud 921600 write_flash -z 0x290000 spiffs_image.bin |
|
| 54 | + |
|
| 55 | + |
|
| 56 | +## test result |
|
| 57 | + |
|
| 58 | +``` |
|
| 59 | + ESP32 SPIFFS Reader |
|
| 60 | + |
|
| 61 | + --- Partition Table --- |
|
| 62 | + Label: nvs Type: 0x01 SubType: 0x02 Address: 0x009000 Size: 0x005000 |
|
| 63 | + Label: otadata Type: 0x01 SubType: 0x00 Address: 0x00e000 Size: 0x002000 |
|
| 64 | + Label: app0 Type: 0x00 SubType: 0x10 Address: 0x010000 Size: 0x140000 |
|
| 65 | + Label: app1 Type: 0x00 SubType: 0x11 Address: 0x150000 Size: 0x140000 |
|
| 66 | + Label: spiffs Type: 0x01 SubType: 0x82 Address: 0x290000 Size: 0x160000 |
|
| 67 | + Label: coredump Type: 0x01 SubType: 0x03 Address: 0x3f0000 Size: 0x010000 |
|
| 68 | + SPIFFS partition found at: 0x290000, size: 0x160000 |
|
| 69 | + SPIFFS mounted successfully |
|
| 70 | + SPIFFS Total: 1318001 bytes, Used: 502 bytes |
|
| 71 | + |
|
| 72 | + --- File and Folder List --- |
|
| 73 | + Listing directory: / |
|
| 74 | + FILE: test.txt SIZE: 13 |
|
| 75 | + |
|
| 76 | + --- Reading test.txt --- |
|
| 77 | + Reading file: /test.txt |
|
| 78 | + - read from file: |
|
| 79 | + 123 |
|
| 80 | + 321 |
|
| 81 | + abc |
|
| 82 | + Done reading SPIFFS image. |
|
| 83 | + |
|
| 84 | +``` |
|
| 85 | + |
|
| 86 | + |
|
| 87 | + |
|
| 88 | +## options: |
|
| 89 | + |
|
| 90 | +** |
|
| 91 | + -h, --help show this help message and exit |
|
| 92 | + --page-size PAGE_SIZE |
|
| 93 | + Logical page size. Set to value same as CONFIG_SPIFFS_PAGE_SIZE. (default: 256) |
|
| 94 | + --block-size BLOCK_SIZE |
|
| 95 | + Logical block size. Set to the same value as the flash chip's sector size |
|
| 96 | + (g_rom_flashchip.sector_size). (default: 4096) |
|
| 97 | + --obj-name-len OBJ_NAME_LEN |
|
| 98 | + File full path maximum length. Set to value same as CONFIG_SPIFFS_OBJ_NAME_LEN. (default: 32) |
|
| 99 | + --meta-len META_LEN File metadata length. Set to value same as CONFIG_SPIFFS_META_LENGTH. (default: 4) |
|
| 100 | + --use-magic Use magic number to create an identifiable SPIFFS image. Specify if CONFIG_SPIFFS_USE_MAGIC. |
|
| 101 | + (default: True) |
|
| 102 | + --no-magic Inverse of --use-magic (default: --use-magic is enabled) |
|
| 103 | + --use-magic-len Use position in memory to create different magic numbers for each block. Specify if |
|
| 104 | + CONFIG_SPIFFS_USE_MAGIC_LENGTH. (default: True) |
|
| 105 | + --no-magic-len Inverse of --use-magic-len (default: --use-magic-len is enabled) |
|
| 106 | + --follow-symlinks Take into account symbolic links during partition image creation. (default: False) |
|
| 107 | + --big-endian Specify if the target architecture is big-endian. If not specified, little-endian is assumed. |
|
| 108 | + (default: False) |
|
| 109 | + --aligned-obj-ix-tables |
|
| 110 | + Use aligned object index tables. Specify if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES is set. |
|
| 111 | + (default: False) |
|
| 112 | + |
|
| 113 | + |
|
| 114 | + |
|
| 115 | + |
|
| 116 | + |
|
| 117 | + |
|
| 118 | +## ref |
|
| 119 | + |
|
| 120 | +- [[SPIFF-dat]] |
|
| 121 | + |
|
| 122 | +- [[SPIFF]] - [[memory]] |
|
| ... | ... | \ No newline at end of file |
Tech-dat/memory-dat/SPIFF-dat/spiffsgen-dat/spiffsgen.py
| ... | ... | @@ -0,0 +1,590 @@ |
| 1 | +#!/usr/bin/env python |
|
| 2 | +# |
|
| 3 | +# spiffsgen is a tool used to generate a spiffs image from a directory |
|
| 4 | +# |
|
| 5 | +# SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD |
|
| 6 | +# SPDX-License-Identifier: Apache-2.0 |
|
| 7 | +import argparse |
|
| 8 | +import io |
|
| 9 | +import math |
|
| 10 | +import os |
|
| 11 | +import struct |
|
| 12 | + |
|
| 13 | +try: |
|
| 14 | + import typing |
|
| 15 | + |
|
| 16 | + TSP = typing.TypeVar('TSP', bound='SpiffsObjPageWithIdx') |
|
| 17 | + ObjIdsItem = typing.Tuple[int, typing.Type[TSP]] |
|
| 18 | +except ImportError: |
|
| 19 | + pass |
|
| 20 | + |
|
| 21 | + |
|
| 22 | +SPIFFS_PH_FLAG_USED_FINAL_INDEX = 0xF8 |
|
| 23 | +SPIFFS_PH_FLAG_USED_FINAL = 0xFC |
|
| 24 | + |
|
| 25 | +SPIFFS_PH_FLAG_LEN = 1 |
|
| 26 | +SPIFFS_PH_IX_SIZE_LEN = 4 |
|
| 27 | +SPIFFS_PH_IX_OBJ_TYPE_LEN = 1 |
|
| 28 | +SPIFFS_TYPE_FILE = 1 |
|
| 29 | + |
|
| 30 | +# Based on typedefs under spiffs_config.h |
|
| 31 | +SPIFFS_OBJ_ID_LEN = 2 # spiffs_obj_id |
|
| 32 | +SPIFFS_SPAN_IX_LEN = 2 # spiffs_span_ix |
|
| 33 | +SPIFFS_PAGE_IX_LEN = 2 # spiffs_page_ix |
|
| 34 | +SPIFFS_BLOCK_IX_LEN = 2 # spiffs_block_ix |
|
| 35 | + |
|
| 36 | + |
|
| 37 | +class SpiffsBuildConfig(object): |
|
| 38 | + def __init__(self, |
|
| 39 | + page_size, # type: int |
|
| 40 | + page_ix_len, # type: int |
|
| 41 | + block_size, # type: int |
|
| 42 | + block_ix_len, # type: int |
|
| 43 | + meta_len, # type: int |
|
| 44 | + obj_name_len, # type: int |
|
| 45 | + obj_id_len, # type: int |
|
| 46 | + span_ix_len, # type: int |
|
| 47 | + packed, # type: bool |
|
| 48 | + aligned, # type: bool |
|
| 49 | + endianness, # type: str |
|
| 50 | + use_magic, # type: bool |
|
| 51 | + use_magic_len, # type: bool |
|
| 52 | + aligned_obj_ix_tables # type: bool |
|
| 53 | + ): |
|
| 54 | + if block_size % page_size != 0: |
|
| 55 | + raise RuntimeError('block size should be a multiple of page size') |
|
| 56 | + |
|
| 57 | + self.page_size = page_size |
|
| 58 | + self.block_size = block_size |
|
| 59 | + self.obj_id_len = obj_id_len |
|
| 60 | + self.span_ix_len = span_ix_len |
|
| 61 | + self.packed = packed |
|
| 62 | + self.aligned = aligned |
|
| 63 | + self.obj_name_len = obj_name_len |
|
| 64 | + self.meta_len = meta_len |
|
| 65 | + self.page_ix_len = page_ix_len |
|
| 66 | + self.block_ix_len = block_ix_len |
|
| 67 | + self.endianness = endianness |
|
| 68 | + self.use_magic = use_magic |
|
| 69 | + self.use_magic_len = use_magic_len |
|
| 70 | + self.aligned_obj_ix_tables = aligned_obj_ix_tables |
|
| 71 | + |
|
| 72 | + self.PAGES_PER_BLOCK = self.block_size // self.page_size |
|
| 73 | + self.OBJ_LU_PAGES_PER_BLOCK = int(math.ceil(self.block_size / self.page_size * self.obj_id_len / self.page_size)) |
|
| 74 | + self.OBJ_USABLE_PAGES_PER_BLOCK = self.PAGES_PER_BLOCK - self.OBJ_LU_PAGES_PER_BLOCK |
|
| 75 | + |
|
| 76 | + self.OBJ_LU_PAGES_OBJ_IDS_LIM = self.page_size // self.obj_id_len |
|
| 77 | + |
|
| 78 | + self.OBJ_DATA_PAGE_HEADER_LEN = self.obj_id_len + self.span_ix_len + SPIFFS_PH_FLAG_LEN |
|
| 79 | + |
|
| 80 | + pad = 4 - (4 if self.OBJ_DATA_PAGE_HEADER_LEN % 4 == 0 else self.OBJ_DATA_PAGE_HEADER_LEN % 4) |
|
| 81 | + |
|
| 82 | + self.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED = self.OBJ_DATA_PAGE_HEADER_LEN + pad |
|
| 83 | + self.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD = pad |
|
| 84 | + self.OBJ_DATA_PAGE_CONTENT_LEN = self.page_size - self.OBJ_DATA_PAGE_HEADER_LEN |
|
| 85 | + |
|
| 86 | + self.OBJ_INDEX_PAGES_HEADER_LEN = (self.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED + SPIFFS_PH_IX_SIZE_LEN + |
|
| 87 | + SPIFFS_PH_IX_OBJ_TYPE_LEN + self.obj_name_len + self.meta_len) |
|
| 88 | + if aligned_obj_ix_tables: |
|
| 89 | + self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED = (self.OBJ_INDEX_PAGES_HEADER_LEN + SPIFFS_PAGE_IX_LEN - 1) & ~(SPIFFS_PAGE_IX_LEN - 1) |
|
| 90 | + self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD = self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED - self.OBJ_INDEX_PAGES_HEADER_LEN |
|
| 91 | + else: |
|
| 92 | + self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED = self.OBJ_INDEX_PAGES_HEADER_LEN |
|
| 93 | + self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD = 0 |
|
| 94 | + |
|
| 95 | + self.OBJ_INDEX_PAGES_OBJ_IDS_HEAD_LIM = (self.page_size - self.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED) // self.block_ix_len |
|
| 96 | + self.OBJ_INDEX_PAGES_OBJ_IDS_LIM = (self.page_size - self.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED) // self.block_ix_len |
|
| 97 | + |
|
| 98 | + |
|
| 99 | +class SpiffsFullError(RuntimeError): |
|
| 100 | + pass |
|
| 101 | + |
|
| 102 | + |
|
| 103 | +class SpiffsPage(object): |
|
| 104 | + _endianness_dict = { |
|
| 105 | + 'little': '<', |
|
| 106 | + 'big': '>' |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + _len_dict = { |
|
| 110 | + 1: 'B', |
|
| 111 | + 2: 'H', |
|
| 112 | + 4: 'I', |
|
| 113 | + 8: 'Q' |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + def __init__(self, bix, build_config): # type: (int, SpiffsBuildConfig) -> None |
|
| 117 | + self.build_config = build_config |
|
| 118 | + self.bix = bix |
|
| 119 | + |
|
| 120 | + def to_binary(self): # type: () -> bytes |
|
| 121 | + raise NotImplementedError() |
|
| 122 | + |
|
| 123 | + |
|
| 124 | +class SpiffsObjPageWithIdx(SpiffsPage): |
|
| 125 | + def __init__(self, obj_id, build_config): # type: (int, SpiffsBuildConfig) -> None |
|
| 126 | + super(SpiffsObjPageWithIdx, self).__init__(0, build_config) |
|
| 127 | + self.obj_id = obj_id |
|
| 128 | + |
|
| 129 | + def to_binary(self): # type: () -> bytes |
|
| 130 | + raise NotImplementedError() |
|
| 131 | + |
|
| 132 | + |
|
| 133 | +class SpiffsObjLuPage(SpiffsPage): |
|
| 134 | + def __init__(self, bix, build_config): # type: (int, SpiffsBuildConfig) -> None |
|
| 135 | + SpiffsPage.__init__(self, bix, build_config) |
|
| 136 | + |
|
| 137 | + self.obj_ids_limit = self.build_config.OBJ_LU_PAGES_OBJ_IDS_LIM |
|
| 138 | + self.obj_ids = list() # type: typing.List[ObjIdsItem] |
|
| 139 | + |
|
| 140 | + def _calc_magic(self, blocks_lim): # type: (int) -> int |
|
| 141 | + # Calculate the magic value mirroring computation done by the macro SPIFFS_MAGIC defined in |
|
| 142 | + # spiffs_nucleus.h |
|
| 143 | + magic = 0x20140529 ^ self.build_config.page_size |
|
| 144 | + if self.build_config.use_magic_len: |
|
| 145 | + magic = magic ^ (blocks_lim - self.bix) |
|
| 146 | + # narrow the result to build_config.obj_id_len bytes |
|
| 147 | + mask = (2 << (8 * self.build_config.obj_id_len)) - 1 |
|
| 148 | + return magic & mask |
|
| 149 | + |
|
| 150 | + def register_page(self, page): # type: (TSP) -> None |
|
| 151 | + if not self.obj_ids_limit > 0: |
|
| 152 | + raise SpiffsFullError() |
|
| 153 | + |
|
| 154 | + obj_id = (page.obj_id, page.__class__) |
|
| 155 | + self.obj_ids.append(obj_id) |
|
| 156 | + self.obj_ids_limit -= 1 |
|
| 157 | + |
|
| 158 | + def to_binary(self): # type: () -> bytes |
|
| 159 | + img = b'' |
|
| 160 | + |
|
| 161 | + for (obj_id, page_type) in self.obj_ids: |
|
| 162 | + if page_type == SpiffsObjIndexPage: |
|
| 163 | + obj_id ^= (1 << ((self.build_config.obj_id_len * 8) - 1)) |
|
| 164 | + img += struct.pack(SpiffsPage._endianness_dict[self.build_config.endianness] + |
|
| 165 | + SpiffsPage._len_dict[self.build_config.obj_id_len], obj_id) |
|
| 166 | + |
|
| 167 | + assert len(img) <= self.build_config.page_size |
|
| 168 | + |
|
| 169 | + img += b'\xFF' * (self.build_config.page_size - len(img)) |
|
| 170 | + |
|
| 171 | + return img |
|
| 172 | + |
|
| 173 | + def magicfy(self, blocks_lim): # type: (int) -> None |
|
| 174 | + # Only use magic value if no valid obj id has been written to the spot, which is the |
|
| 175 | + # spot taken up by the last obj id on last lookup page. The parent is responsible |
|
| 176 | + # for determining which is the last lookup page and calling this function. |
|
| 177 | + remaining = self.obj_ids_limit |
|
| 178 | + empty_obj_id_dict = { |
|
| 179 | + 1: 0xFF, |
|
| 180 | + 2: 0xFFFF, |
|
| 181 | + 4: 0xFFFFFFFF, |
|
| 182 | + 8: 0xFFFFFFFFFFFFFFFF |
|
| 183 | + } |
|
| 184 | + if remaining >= 2: |
|
| 185 | + for i in range(remaining): |
|
| 186 | + if i == remaining - 2: |
|
| 187 | + self.obj_ids.append((self._calc_magic(blocks_lim), SpiffsObjDataPage)) |
|
| 188 | + break |
|
| 189 | + else: |
|
| 190 | + self.obj_ids.append((empty_obj_id_dict[self.build_config.obj_id_len], SpiffsObjDataPage)) |
|
| 191 | + self.obj_ids_limit -= 1 |
|
| 192 | + |
|
| 193 | + |
|
| 194 | +class SpiffsObjIndexPage(SpiffsObjPageWithIdx): |
|
| 195 | + def __init__(self, obj_id, span_ix, size, name, build_config |
|
| 196 | + ): # type: (int, int, int, str, SpiffsBuildConfig) -> None |
|
| 197 | + super(SpiffsObjIndexPage, self).__init__(obj_id, build_config) |
|
| 198 | + self.span_ix = span_ix |
|
| 199 | + self.name = name |
|
| 200 | + self.size = size |
|
| 201 | + |
|
| 202 | + if self.span_ix == 0: |
|
| 203 | + self.pages_lim = self.build_config.OBJ_INDEX_PAGES_OBJ_IDS_HEAD_LIM |
|
| 204 | + else: |
|
| 205 | + self.pages_lim = self.build_config.OBJ_INDEX_PAGES_OBJ_IDS_LIM |
|
| 206 | + |
|
| 207 | + self.pages = list() # type: typing.List[int] |
|
| 208 | + |
|
| 209 | + def register_page(self, page): # type: (SpiffsObjDataPage) -> None |
|
| 210 | + if not self.pages_lim > 0: |
|
| 211 | + raise SpiffsFullError |
|
| 212 | + |
|
| 213 | + self.pages.append(page.offset) |
|
| 214 | + self.pages_lim -= 1 |
|
| 215 | + |
|
| 216 | + def to_binary(self): # type: () -> bytes |
|
| 217 | + obj_id = self.obj_id ^ (1 << ((self.build_config.obj_id_len * 8) - 1)) |
|
| 218 | + img = struct.pack(SpiffsPage._endianness_dict[self.build_config.endianness] + |
|
| 219 | + SpiffsPage._len_dict[self.build_config.obj_id_len] + |
|
| 220 | + SpiffsPage._len_dict[self.build_config.span_ix_len] + |
|
| 221 | + SpiffsPage._len_dict[SPIFFS_PH_FLAG_LEN], |
|
| 222 | + obj_id, |
|
| 223 | + self.span_ix, |
|
| 224 | + SPIFFS_PH_FLAG_USED_FINAL_INDEX) |
|
| 225 | + |
|
| 226 | + # Add padding before the object index page specific information |
|
| 227 | + img += b'\xFF' * self.build_config.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD |
|
| 228 | + |
|
| 229 | + # If this is the first object index page for the object, add filename, type |
|
| 230 | + # and size information |
|
| 231 | + if self.span_ix == 0: |
|
| 232 | + img += struct.pack(SpiffsPage._endianness_dict[self.build_config.endianness] + |
|
| 233 | + SpiffsPage._len_dict[SPIFFS_PH_IX_SIZE_LEN] + |
|
| 234 | + SpiffsPage._len_dict[SPIFFS_PH_FLAG_LEN], |
|
| 235 | + self.size, |
|
| 236 | + SPIFFS_TYPE_FILE) |
|
| 237 | + |
|
| 238 | + img += self.name.encode() + (b'\x00' * ( |
|
| 239 | + (self.build_config.obj_name_len - len(self.name)) |
|
| 240 | + + self.build_config.meta_len |
|
| 241 | + + self.build_config.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD)) |
|
| 242 | + |
|
| 243 | + # Finally, add the page index of data pages |
|
| 244 | + for page in self.pages: |
|
| 245 | + page = page >> int(math.log(self.build_config.page_size, 2)) |
|
| 246 | + img += struct.pack(SpiffsPage._endianness_dict[self.build_config.endianness] + |
|
| 247 | + SpiffsPage._len_dict[self.build_config.page_ix_len], page) |
|
| 248 | + |
|
| 249 | + assert len(img) <= self.build_config.page_size |
|
| 250 | + |
|
| 251 | + img += b'\xFF' * (self.build_config.page_size - len(img)) |
|
| 252 | + |
|
| 253 | + return img |
|
| 254 | + |
|
| 255 | + |
|
| 256 | +class SpiffsObjDataPage(SpiffsObjPageWithIdx): |
|
| 257 | + def __init__(self, offset, obj_id, span_ix, contents, build_config |
|
| 258 | + ): # type: (int, int, int, bytes, SpiffsBuildConfig) -> None |
|
| 259 | + super(SpiffsObjDataPage, self).__init__(obj_id, build_config) |
|
| 260 | + self.span_ix = span_ix |
|
| 261 | + self.contents = contents |
|
| 262 | + self.offset = offset |
|
| 263 | + |
|
| 264 | + def to_binary(self): # type: () -> bytes |
|
| 265 | + img = struct.pack(SpiffsPage._endianness_dict[self.build_config.endianness] + |
|
| 266 | + SpiffsPage._len_dict[self.build_config.obj_id_len] + |
|
| 267 | + SpiffsPage._len_dict[self.build_config.span_ix_len] + |
|
| 268 | + SpiffsPage._len_dict[SPIFFS_PH_FLAG_LEN], |
|
| 269 | + self.obj_id, |
|
| 270 | + self.span_ix, |
|
| 271 | + SPIFFS_PH_FLAG_USED_FINAL) |
|
| 272 | + |
|
| 273 | + img += self.contents |
|
| 274 | + |
|
| 275 | + assert len(img) <= self.build_config.page_size |
|
| 276 | + |
|
| 277 | + img += b'\xFF' * (self.build_config.page_size - len(img)) |
|
| 278 | + |
|
| 279 | + return img |
|
| 280 | + |
|
| 281 | + |
|
| 282 | +class SpiffsBlock(object): |
|
| 283 | + def _reset(self): # type: () -> None |
|
| 284 | + self.cur_obj_index_span_ix = 0 |
|
| 285 | + self.cur_obj_data_span_ix = 0 |
|
| 286 | + self.cur_obj_id = 0 |
|
| 287 | + self.cur_obj_idx_page = None # type: typing.Optional[SpiffsObjIndexPage] |
|
| 288 | + |
|
| 289 | + def __init__(self, bix, build_config): # type: (int, SpiffsBuildConfig) -> None |
|
| 290 | + self.build_config = build_config |
|
| 291 | + self.offset = bix * self.build_config.block_size |
|
| 292 | + self.remaining_pages = self.build_config.OBJ_USABLE_PAGES_PER_BLOCK |
|
| 293 | + self.pages = list() # type: typing.List[SpiffsPage] |
|
| 294 | + self.bix = bix |
|
| 295 | + |
|
| 296 | + lu_pages = list() |
|
| 297 | + for i in range(self.build_config.OBJ_LU_PAGES_PER_BLOCK): |
|
| 298 | + page = SpiffsObjLuPage(self.bix, self.build_config) |
|
| 299 | + lu_pages.append(page) |
|
| 300 | + |
|
| 301 | + self.pages.extend(lu_pages) |
|
| 302 | + |
|
| 303 | + self.lu_page_iter = iter(lu_pages) |
|
| 304 | + self.lu_page = next(self.lu_page_iter) |
|
| 305 | + |
|
| 306 | + self._reset() |
|
| 307 | + |
|
| 308 | + def _register_page(self, page): # type: (TSP) -> None |
|
| 309 | + if isinstance(page, SpiffsObjDataPage): |
|
| 310 | + assert self.cur_obj_idx_page is not None |
|
| 311 | + self.cur_obj_idx_page.register_page(page) # can raise SpiffsFullError |
|
| 312 | + |
|
| 313 | + try: |
|
| 314 | + self.lu_page.register_page(page) |
|
| 315 | + except SpiffsFullError: |
|
| 316 | + self.lu_page = next(self.lu_page_iter) |
|
| 317 | + try: |
|
| 318 | + self.lu_page.register_page(page) |
|
| 319 | + except AttributeError: # no next lookup page |
|
| 320 | + # Since the amount of lookup pages is pre-computed at every block instance, |
|
| 321 | + # this should never occur |
|
| 322 | + raise RuntimeError('invalid attempt to add page to a block when there is no more space in lookup') |
|
| 323 | + |
|
| 324 | + self.pages.append(page) |
|
| 325 | + |
|
| 326 | + def begin_obj(self, obj_id, size, name, obj_index_span_ix=0, obj_data_span_ix=0 |
|
| 327 | + ): # type: (int, int, str, int, int) -> None |
|
| 328 | + if not self.remaining_pages > 0: |
|
| 329 | + raise SpiffsFullError() |
|
| 330 | + self._reset() |
|
| 331 | + |
|
| 332 | + self.cur_obj_id = obj_id |
|
| 333 | + self.cur_obj_index_span_ix = obj_index_span_ix |
|
| 334 | + self.cur_obj_data_span_ix = obj_data_span_ix |
|
| 335 | + |
|
| 336 | + page = SpiffsObjIndexPage(obj_id, self.cur_obj_index_span_ix, size, name, self.build_config) |
|
| 337 | + self._register_page(page) |
|
| 338 | + |
|
| 339 | + self.cur_obj_idx_page = page |
|
| 340 | + |
|
| 341 | + self.remaining_pages -= 1 |
|
| 342 | + self.cur_obj_index_span_ix += 1 |
|
| 343 | + |
|
| 344 | + def update_obj(self, contents): # type: (bytes) -> None |
|
| 345 | + if not self.remaining_pages > 0: |
|
| 346 | + raise SpiffsFullError() |
|
| 347 | + page = SpiffsObjDataPage(self.offset + (len(self.pages) * self.build_config.page_size), |
|
| 348 | + self.cur_obj_id, self.cur_obj_data_span_ix, contents, self.build_config) |
|
| 349 | + |
|
| 350 | + self._register_page(page) |
|
| 351 | + |
|
| 352 | + self.cur_obj_data_span_ix += 1 |
|
| 353 | + self.remaining_pages -= 1 |
|
| 354 | + |
|
| 355 | + def end_obj(self): # type: () -> None |
|
| 356 | + self._reset() |
|
| 357 | + |
|
| 358 | + def is_full(self): # type: () -> bool |
|
| 359 | + return self.remaining_pages <= 0 |
|
| 360 | + |
|
| 361 | + def to_binary(self, blocks_lim): # type: (int) -> bytes |
|
| 362 | + img = b'' |
|
| 363 | + |
|
| 364 | + if self.build_config.use_magic: |
|
| 365 | + for (idx, page) in enumerate(self.pages): |
|
| 366 | + if idx == self.build_config.OBJ_LU_PAGES_PER_BLOCK - 1: |
|
| 367 | + assert isinstance(page, SpiffsObjLuPage) |
|
| 368 | + page.magicfy(blocks_lim) |
|
| 369 | + img += page.to_binary() |
|
| 370 | + else: |
|
| 371 | + for page in self.pages: |
|
| 372 | + img += page.to_binary() |
|
| 373 | + |
|
| 374 | + assert len(img) <= self.build_config.block_size |
|
| 375 | + |
|
| 376 | + img += b'\xFF' * (self.build_config.block_size - len(img)) |
|
| 377 | + return img |
|
| 378 | + |
|
| 379 | + |
|
| 380 | +class SpiffsFS(object): |
|
| 381 | + def __init__(self, img_size, build_config): # type: (int, SpiffsBuildConfig) -> None |
|
| 382 | + if img_size % build_config.block_size != 0: |
|
| 383 | + raise RuntimeError('image size should be a multiple of block size') |
|
| 384 | + |
|
| 385 | + self.img_size = img_size |
|
| 386 | + self.build_config = build_config |
|
| 387 | + |
|
| 388 | + self.blocks = list() # type: typing.List[SpiffsBlock] |
|
| 389 | + self.blocks_lim = self.img_size // self.build_config.block_size |
|
| 390 | + self.remaining_blocks = self.blocks_lim |
|
| 391 | + self.cur_obj_id = 1 # starting object id |
|
| 392 | + |
|
| 393 | + def _create_block(self): # type: () -> SpiffsBlock |
|
| 394 | + if self.is_full(): |
|
| 395 | + raise SpiffsFullError('the image size has been exceeded') |
|
| 396 | + |
|
| 397 | + block = SpiffsBlock(len(self.blocks), self.build_config) |
|
| 398 | + self.blocks.append(block) |
|
| 399 | + self.remaining_blocks -= 1 |
|
| 400 | + return block |
|
| 401 | + |
|
| 402 | + def is_full(self): # type: () -> bool |
|
| 403 | + return self.remaining_blocks <= 0 |
|
| 404 | + |
|
| 405 | + def create_file(self, img_path, file_path): # type: (str, str) -> None |
|
| 406 | + if len(img_path) > self.build_config.obj_name_len: |
|
| 407 | + raise RuntimeError("object name '%s' too long" % img_path) |
|
| 408 | + |
|
| 409 | + name = img_path |
|
| 410 | + |
|
| 411 | + with open(file_path, 'rb') as obj: |
|
| 412 | + contents = obj.read() |
|
| 413 | + |
|
| 414 | + stream = io.BytesIO(contents) |
|
| 415 | + |
|
| 416 | + try: |
|
| 417 | + block = self.blocks[-1] |
|
| 418 | + block.begin_obj(self.cur_obj_id, len(contents), name) |
|
| 419 | + except (IndexError, SpiffsFullError): |
|
| 420 | + block = self._create_block() |
|
| 421 | + block.begin_obj(self.cur_obj_id, len(contents), name) |
|
| 422 | + |
|
| 423 | + contents_chunk = stream.read(self.build_config.OBJ_DATA_PAGE_CONTENT_LEN) |
|
| 424 | + |
|
| 425 | + while contents_chunk: |
|
| 426 | + try: |
|
| 427 | + block = self.blocks[-1] |
|
| 428 | + try: |
|
| 429 | + # This can fail because either (1) all the pages in block have been |
|
| 430 | + # used or (2) object index has been exhausted. |
|
| 431 | + block.update_obj(contents_chunk) |
|
| 432 | + except SpiffsFullError: |
|
| 433 | + # If its (1), use the outer exception handler |
|
| 434 | + if block.is_full(): |
|
| 435 | + raise SpiffsFullError |
|
| 436 | + # If its (2), write another object index page |
|
| 437 | + block.begin_obj(self.cur_obj_id, len(contents), name, |
|
| 438 | + obj_index_span_ix=block.cur_obj_index_span_ix, |
|
| 439 | + obj_data_span_ix=block.cur_obj_data_span_ix) |
|
| 440 | + continue |
|
| 441 | + except (IndexError, SpiffsFullError): |
|
| 442 | + # All pages in the block have been exhausted. Create a new block, copying |
|
| 443 | + # the previous state of the block to a new one for the continuation of the |
|
| 444 | + # current object |
|
| 445 | + prev_block = block |
|
| 446 | + block = self._create_block() |
|
| 447 | + block.cur_obj_id = prev_block.cur_obj_id |
|
| 448 | + block.cur_obj_idx_page = prev_block.cur_obj_idx_page |
|
| 449 | + block.cur_obj_data_span_ix = prev_block.cur_obj_data_span_ix |
|
| 450 | + block.cur_obj_index_span_ix = prev_block.cur_obj_index_span_ix |
|
| 451 | + continue |
|
| 452 | + |
|
| 453 | + contents_chunk = stream.read(self.build_config.OBJ_DATA_PAGE_CONTENT_LEN) |
|
| 454 | + |
|
| 455 | + block.end_obj() |
|
| 456 | + |
|
| 457 | + self.cur_obj_id += 1 |
|
| 458 | + |
|
| 459 | + def to_binary(self): # type: () -> bytes |
|
| 460 | + img = b'' |
|
| 461 | + all_blocks = [] |
|
| 462 | + for block in self.blocks: |
|
| 463 | + all_blocks.append(block.to_binary(self.blocks_lim)) |
|
| 464 | + bix = len(self.blocks) |
|
| 465 | + if self.build_config.use_magic: |
|
| 466 | + # Create empty blocks with magic numbers |
|
| 467 | + while self.remaining_blocks > 0: |
|
| 468 | + block = SpiffsBlock(bix, self.build_config) |
|
| 469 | + all_blocks.append(block.to_binary(self.blocks_lim)) |
|
| 470 | + self.remaining_blocks -= 1 |
|
| 471 | + bix += 1 |
|
| 472 | + else: |
|
| 473 | + # Just fill remaining spaces FF's |
|
| 474 | + all_blocks.append(b'\xFF' * (self.img_size - len(all_blocks) * self.build_config.block_size)) |
|
| 475 | + img += b''.join([blk for blk in all_blocks]) |
|
| 476 | + return img |
|
| 477 | + |
|
| 478 | + |
|
| 479 | +class CustomHelpFormatter(argparse.HelpFormatter): |
|
| 480 | + """ |
|
| 481 | + Similar to argparse.ArgumentDefaultsHelpFormatter, except it |
|
| 482 | + doesn't add the default value if "(default:" is already present. |
|
| 483 | + This helps in the case of options with action="store_false", like |
|
| 484 | + --no-magic or --no-magic-len. |
|
| 485 | + """ |
|
| 486 | + def _get_help_string(self, action): # type: (argparse.Action) -> str |
|
| 487 | + if action.help is None: |
|
| 488 | + return '' |
|
| 489 | + if '%(default)' not in action.help and '(default:' not in action.help: |
|
| 490 | + if action.default is not argparse.SUPPRESS: |
|
| 491 | + defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE] |
|
| 492 | + if action.option_strings or action.nargs in defaulting_nargs: |
|
| 493 | + return action.help + ' (default: %(default)s)' |
|
| 494 | + return action.help |
|
| 495 | + |
|
| 496 | + |
|
| 497 | +def main(): # type: () -> None |
|
| 498 | + parser = argparse.ArgumentParser(description='SPIFFS Image Generator', |
|
| 499 | + formatter_class=CustomHelpFormatter) |
|
| 500 | + |
|
| 501 | + parser.add_argument('image_size', |
|
| 502 | + help='Size of the created image') |
|
| 503 | + |
|
| 504 | + parser.add_argument('base_dir', |
|
| 505 | + help='Path to directory from which the image will be created') |
|
| 506 | + |
|
| 507 | + parser.add_argument('output_file', |
|
| 508 | + help='Created image output file path') |
|
| 509 | + |
|
| 510 | + parser.add_argument('--page-size', |
|
| 511 | + help='Logical page size. Set to value same as CONFIG_SPIFFS_PAGE_SIZE.', |
|
| 512 | + type=int, |
|
| 513 | + default=256) |
|
| 514 | + |
|
| 515 | + parser.add_argument('--block-size', |
|
| 516 | + help="Logical block size. Set to the same value as the flash chip's sector size (g_rom_flashchip.sector_size).", |
|
| 517 | + type=int, |
|
| 518 | + default=4096) |
|
| 519 | + |
|
| 520 | + parser.add_argument('--obj-name-len', |
|
| 521 | + help='File full path maximum length. Set to value same as CONFIG_SPIFFS_OBJ_NAME_LEN.', |
|
| 522 | + type=int, |
|
| 523 | + default=32) |
|
| 524 | + |
|
| 525 | + parser.add_argument('--meta-len', |
|
| 526 | + help='File metadata length. Set to value same as CONFIG_SPIFFS_META_LENGTH.', |
|
| 527 | + type=int, |
|
| 528 | + default=4) |
|
| 529 | + |
|
| 530 | + parser.add_argument('--use-magic', |
|
| 531 | + dest='use_magic', |
|
| 532 | + help='Use magic number to create an identifiable SPIFFS image. Specify if CONFIG_SPIFFS_USE_MAGIC.', |
|
| 533 | + action='store_true') |
|
| 534 | + |
|
| 535 | + parser.add_argument('--no-magic', |
|
| 536 | + dest='use_magic', |
|
| 537 | + help='Inverse of --use-magic (default: --use-magic is enabled)', |
|
| 538 | + action='store_false') |
|
| 539 | + |
|
| 540 | + parser.add_argument('--use-magic-len', |
|
| 541 | + dest='use_magic_len', |
|
| 542 | + help='Use position in memory to create different magic numbers for each block. Specify if CONFIG_SPIFFS_USE_MAGIC_LENGTH.', |
|
| 543 | + action='store_true') |
|
| 544 | + |
|
| 545 | + parser.add_argument('--no-magic-len', |
|
| 546 | + dest='use_magic_len', |
|
| 547 | + help='Inverse of --use-magic-len (default: --use-magic-len is enabled)', |
|
| 548 | + action='store_false') |
|
| 549 | + |
|
| 550 | + parser.add_argument('--follow-symlinks', |
|
| 551 | + help='Take into account symbolic links during partition image creation.', |
|
| 552 | + action='store_true') |
|
| 553 | + |
|
| 554 | + parser.add_argument('--big-endian', |
|
| 555 | + help='Specify if the target architecture is big-endian. If not specified, little-endian is assumed.', |
|
| 556 | + action='store_true') |
|
| 557 | + |
|
| 558 | + parser.add_argument('--aligned-obj-ix-tables', |
|
| 559 | + action='store_true', |
|
| 560 | + help='Use aligned object index tables. Specify if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES is set.') |
|
| 561 | + |
|
| 562 | + parser.set_defaults(use_magic=True, use_magic_len=True) |
|
| 563 | + |
|
| 564 | + args = parser.parse_args() |
|
| 565 | + |
|
| 566 | + if not os.path.exists(args.base_dir): |
|
| 567 | + raise RuntimeError('given base directory %s does not exist' % args.base_dir) |
|
| 568 | + |
|
| 569 | + with open(args.output_file, 'wb') as image_file: |
|
| 570 | + image_size = int(args.image_size, 0) |
|
| 571 | + spiffs_build_default = SpiffsBuildConfig(args.page_size, SPIFFS_PAGE_IX_LEN, |
|
| 572 | + args.block_size, SPIFFS_BLOCK_IX_LEN, args.meta_len, |
|
| 573 | + args.obj_name_len, SPIFFS_OBJ_ID_LEN, SPIFFS_SPAN_IX_LEN, |
|
| 574 | + True, True, 'big' if args.big_endian else 'little', |
|
| 575 | + args.use_magic, args.use_magic_len, args.aligned_obj_ix_tables) |
|
| 576 | + |
|
| 577 | + spiffs = SpiffsFS(image_size, spiffs_build_default) |
|
| 578 | + |
|
| 579 | + for root, dirs, files in os.walk(args.base_dir, followlinks=args.follow_symlinks): |
|
| 580 | + for f in files: |
|
| 581 | + full_path = os.path.join(root, f) |
|
| 582 | + spiffs.create_file('/' + os.path.relpath(full_path, args.base_dir).replace('\\', '/'), full_path) |
|
| 583 | + |
|
| 584 | + image = spiffs.to_binary() |
|
| 585 | + |
|
| 586 | + image_file.write(image) |
|
| 587 | + |
|
| 588 | + |
|
| 589 | +if __name__ == '__main__': |
|
| 590 | + main() |
Tech-dat/memory-dat/partition-table-dat/partition-table-dat.md
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +# partition-table-dat |
|
| 2 | + |
|
| 3 | +https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/partition-tables.html |
|
| 4 | + |
|
| 5 | +Here is the summary printed for the "Factory app, two OTA definitions" configuration: |
|
| 6 | + |
|
| 7 | + |
|
| 8 | +| Name | Type | SubType | Offset | Size | Flags | |
|
| 9 | +|----------|------|---------|-----------|---------|-------| |
|
| 10 | +| nvs | data | nvs | 0x9000 | 0x4000 | | |
|
| 11 | +| otadata | data | ota | 0xd000 | 0x2000 | | |
|
| 12 | +| phy_init | data | phy | 0xf000 | 0x1000 | | |
|
| 13 | +| factory | app | factory | 0x10000 | 1M | | |
|
| 14 | +| ota_0 | app | ota_0 | 0x110000 | 1M | | |
|
| 15 | +| ota_1 | app | ota_1 | 0x210000 | 1M | | |
|
| 16 | + |
|
| 17 | + |
|
| 18 | + |