From ab795350fbe920ac86d34bccb9ac354c6d212c92 Mon Sep 17 00:00:00 2001 From: sheinz Date: Mon, 27 Jun 2016 18:06:06 +0300 Subject: [PATCH 01/19] Draft implementation of SPIFFS integration --- .gitmodules | 3 + examples/spiffs/Makefile | 9 ++ examples/spiffs/spiffs_example.c | 180 +++++++++++++++++++++ extras/spiffs/component.mk | 17 ++ extras/spiffs/esp_spiffs.c | 187 ++++++++++++++++++++++ extras/spiffs/esp_spiffs.h | 28 ++++ extras/spiffs/spiffs | 1 + extras/spiffs/spiffs_config.h | 263 +++++++++++++++++++++++++++++++ 8 files changed, 688 insertions(+) create mode 100644 examples/spiffs/Makefile create mode 100644 examples/spiffs/spiffs_example.c create mode 100644 extras/spiffs/component.mk create mode 100644 extras/spiffs/esp_spiffs.c create mode 100644 extras/spiffs/esp_spiffs.h create mode 160000 extras/spiffs/spiffs create mode 100644 extras/spiffs/spiffs_config.h diff --git a/.gitmodules b/.gitmodules index 7a3370e..00dadd8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ path = bootloader/rboot url = https://github.com/raburton/rboot.git +[submodule "extras/spiffs/spiffs"] + path = extras/spiffs/spiffs + url = https://github.com/pellepl/spiffs.git diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile new file mode 100644 index 0000000..b342df7 --- /dev/null +++ b/examples/spiffs/Makefile @@ -0,0 +1,9 @@ +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c new file mode 100644 index 0000000..226ee51 --- /dev/null +++ b/examples/spiffs/spiffs_example.c @@ -0,0 +1,180 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" + +#include "spiffs.h" +#include "esp_spiffs.h" + + +#define TEST_FILE_NAME_LEN 16 +#define TEST_FILES 32 +#define TEST_FILE_MAX_SIZE 8192 + +typedef struct { + char name[TEST_FILE_NAME_LEN]; + uint16_t size; + uint8_t first_data_byte; +} TestFile; + +static TestFile test_files[TEST_FILES]; + +inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte) +{ + while (size--) { + *src++ = first_byte++; + } +} + +static bool write_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + sprintf(test_files[i].name, "file_%d.dat", i); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, + SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + test_files[i].size = rand() % TEST_FILE_MAX_SIZE; + test_files[i].first_data_byte = rand() % 256; + fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte); + + printf("Writing file %s size=%d\n", test_files[i].name, + test_files[i].size); + int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size); + if (written != test_files[i].size) { + printf("Write file operation failed, written=%d\n", written); + result = false; + break; + } + SPIFFS_close(&fs, f); + } + free(buf); + return result; +} + +inline static bool verify_test_data(uint8_t *data, uint16_t size, + uint8_t first_byte) +{ + while (size--) { + if (*data++ != first_byte++) { + return false; + } + } + return true; +} + +static bool verify_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Verifying file %s\n", test_files[i].name); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + + int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size); + if (n != test_files[i].size) { + printf("Read file operation failed\n"); + result = false; + break; + } + + if (!verify_test_data(buf, test_files[i].size, + test_files[i].first_data_byte)) { + printf("Data verification failed\n"); + result = false; + break; + } + + SPIFFS_close(&fs, f); + } + + free(buf); + return result; +} + +static bool cleanup_test_files() +{ + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Removing file %s\n", test_files[i].name); + if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) { + printf("Remove file operation failed\n"); + result = false; + break; + } + } + return result; +} + +inline static void print_info() +{ + uint32_t total, used; + + SPIFFS_info(&fs, &total, &used); + + printf("FS total=%d bytes, used=%d bytes\n", total, used); + printf("FS %d %% used\n", 100 * used/total); + + // File system structure visualisation + // SPIFFS_vis(&fs); +} + +void test_task(void *pvParameters) +{ + bool result = true; + + esp_spiffs_mount(); + esp_spiffs_unmount(); // FS must be unmounted before formating + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Format failed\n"); + } + esp_spiffs_mount(); + + while (1) { + vTaskDelay(5000 / portTICK_RATE_MS); + + result = write_test_files(); + + if (result) { + result = verify_test_files(); + } + + print_info(); + + if (result) { + result = cleanup_test_files(); + } + + if (result) { + printf("Test passed!\n"); + } else { + printf("Test failed!\n"); + while (1) { + vTaskDelay(1); + } + } + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + + xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); +} diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk new file mode 100644 index 0000000..fcd5572 --- /dev/null +++ b/extras/spiffs/component.mk @@ -0,0 +1,17 @@ +# Component makefile for extras/spiffs + +SPIFFS_BASE_ADDR ?= 0x300000 +SPIFFS_SIZE ?= 0x100000 + +INC_DIRS += $(spiffs_ROOT) +INC_DIRS += $(spiffs_ROOT)spiffs/src + +# args for passing into compile rule generation +spiffs_SRC_DIR = $(spiffs_ROOT)spiffs/src +spiffs_SRC_DIR += $(spiffs_ROOT) + +spiffs_CFLAGS = $(CFLAGS) +spiffs_CFLAGS += -DSPIFFS_BASE_ADDR=$(SPIFFS_BASE_ADDR) +spiffs_CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +$(eval $(call component_compile_rules,spiffs)) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c new file mode 100644 index 0000000..d6ec7d6 --- /dev/null +++ b/extras/spiffs/esp_spiffs.c @@ -0,0 +1,187 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#include "esp_spiffs.h" +#include "spiffs.h" +#include +#include + +spiffs fs; + +static void *work_buf = 0; +static void *fds_buf = 0; +static void *cache_buf = 0; + +/* + * Flash addresses and size alignment is a rip-off of Arduino implementation. + */ + +static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) +{ + uint32_t result = SPIFFS_OK; + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t nb = alignedBegin - addr; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + memcpy(dst, &tmp + 4 - nb, nb); + } + + if (alignedEnd != alignedBegin) { + if (sdk_spi_flash_read(alignedBegin, + (uint32_t*) (dst + alignedBegin - addr), + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(dst + size - nb, &tmp, nb); + } + + return result; +} + +static const int UNALIGNED_WRITE_BUFFER_SIZE = 512; + +static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) +{ + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t ofs = alignedBegin - addr; + uint32_t nb = (size < ofs) ? size : ofs; + uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; + memcpy(tmp + 4 - ofs, src, nb); + if (sdk_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (alignedEnd != alignedBegin) { + uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); + uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; + if (!srcAlign) { + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + else { + uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE]; + for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) { + size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); + memcpy(buf, srcLeftover, willCopy); + + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + sizeLeft -= willCopy; + srcLeftover += willCopy; + alignedBegin += willCopy; + } + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp = 0xffffffff; + memcpy(&tmp, src + size - nb, nb); + + if (sdk_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +static s32_t esp_spiffs_erase(u32_t addr, u32_t size) +{ + if (addr % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase addr=%x\n", addr); + } + if (size % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase size=%d\n", size); + } + + const uint32_t sector = addr / SPI_FLASH_SEC_SIZE; + const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; + + for (uint32_t i = 0; i < sectorCount; ++i) { + sdk_spi_flash_erase_sector(sector + i); + } + return SPIFFS_OK; +} + +int32_t esp_spiffs_mount() +{ + spiffs_config config = {0}; + + config.hal_read_f = esp_spiffs_read; + config.hal_write_f = esp_spiffs_write; + config.hal_erase_f = esp_spiffs_erase; + + size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); + size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + + work_buf = malloc(workBufSize); + fds_buf = malloc(fdsBufSize); + cache_buf = malloc(cacheBufSize); + printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + workBufSize, fdsBufSize, cacheBufSize); + + int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, + cache_buf, cacheBufSize, 0); + + if (err != SPIFFS_OK) { + printf("Error spiffs mount: %d\n", err); + } + + return err; +} + +void esp_spiffs_unmount() +{ + SPIFFS_unmount(&fs); + + free(work_buf); + free(fds_buf); + free(cache_buf); + + work_buf = 0; + fds_buf = 0; + cache_buf = 0; +} diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h new file mode 100644 index 0000000..3022d51 --- /dev/null +++ b/extras/spiffs/esp_spiffs.h @@ -0,0 +1,28 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#ifndef __ESP_SPIFFS_H__ +#define __ESP_SPIFFS_H__ + +#include "spiffs.h" + +extern spiffs fs; + +/** + * Provide SPIFFS with all necessary configuration, allocate memory buffers + * and mount SPIFFS. + * + * Return SPIFFS return code. + */ +int32_t esp_spiffs_mount(); + +/** + * Unmount SPIFFS and free all allocated buffers. + */ +void esp_spiffs_unmount(); + +#endif // __ESP_SPIFFS_H__ diff --git a/extras/spiffs/spiffs b/extras/spiffs/spiffs new file mode 160000 index 0000000..c6e94fd --- /dev/null +++ b/extras/spiffs/spiffs @@ -0,0 +1 @@ +Subproject commit c6e94fdca5c1601b90c027167f8d453c48e482c4 diff --git a/extras/spiffs/spiffs_config.h b/extras/spiffs/spiffs_config.h new file mode 100644 index 0000000..7b8c1a7 --- /dev/null +++ b/extras/spiffs/spiffs_config.h @@ -0,0 +1,263 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +#include +#include +#include +#include +#include +#include +// #include // for vPortEnterCritical/vPortExitCritical +// ----------- >8 ------------ + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 1 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 3 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (64) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (0) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) // vPortEnterCritical() +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) // vPortExitCritical() +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 1 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (SPIFFS_SIZE) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (SPIFFS_BASE_ADDR) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4*1024) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 1 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ From 33b63d46a549dbafb4028b86ccc76008d879ca42 Mon Sep 17 00:00:00 2001 From: sheinz Date: Wed, 29 Jun 2016 00:47:14 +0300 Subject: [PATCH 02/19] Reverse engineered some spi_flash functions --- extras/spiffs/esp_spiffs.c | 157 +++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 8 deletions(-) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index d6ec7d6..a2c1dd1 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -9,6 +9,9 @@ #include "spiffs.h" #include #include +#include "common_macros.h" +#include "FreeRTOS.h" +#include "esp/rom.h" spiffs fs; @@ -16,6 +19,144 @@ static void *work_buf = 0; static void *fds_buf = 0; static void *cache_buf = 0; + +// ROM functions +uint32_t SPI_read_data(sdk_flashchip_t *p, uint32_t dest_addr, void *src, + uint32_t size); +uint32_t SPI_page_program(sdk_flashchip_t *p, uint32_t dest_addr, void *dst, + uint32_t size); +uint32_t SPI_write_enable(sdk_flashchip_t *p); +uint32_t SPI_sector_erase(sdk_flashchip_t *p, uint32_t sector_addr); + + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIRead + */ +uint32_t IRAM spi_read(uint32_t dest_addr, void *src, uint32_t size) +{ + if (SPI_read_data(&sdk_flashchip, dest_addr, src, size)) { + return 1; + } else { + return 0; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_read + */ +uint32_t IRAM spi_flash_read(uint32_t dest_addr, void *src, uint32_t size) +{ + if (src) { + vPortEnterCritical(); + Cache_Read_Disable(); + uint32_t result = spi_read(dest_addr, src, size); + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + return result; + } else { + return 1; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIWrite + */ +uint32_t IRAM spi_write(uint32_t dest_addr, void *dst, uint32_t size) +{ + if (sdk_flashchip.chip_size < (dest_addr + size)) { + return 1; + } + + uint32_t write_bytes_to_page = sdk_flashchip.page_size - + (dest_addr % sdk_flashchip.page_size); + + if (size < write_bytes_to_page) { + if (SPI_page_program(&sdk_flashchip, dest_addr, dst, size)) { + return 1; + } else { + return 0; + } + } + + if (SPI_page_program(&sdk_flashchip, dest_addr, dst, write_bytes_to_page)) { + return 1; + } + + uint32_t offset = write_bytes_to_page; + uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; + for (uint8_t i = 0; i != pages_to_write; i++) { + if (SPI_page_program(&sdk_flashchip, dest_addr + offset, + dst + ((offset>>2)<<2), sdk_flashchip.page_size)) { + return 1; + } + offset += sdk_flashchip.page_size; + } + + if (SPI_page_program(&sdk_flashchip, dest_addr + offset, + dst + ((offset>>2)<<2), size - offset)) { + return 1; + } else { + return 0; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_write + */ +uint32_t IRAM spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size) +{ + if (dst) { + if (size & 0b11) { // not 4-byte aligned + size = size >> 2; + size = (size << 2) + 1; + } + vPortEnterCritical(); + Cache_Read_Disable(); + uint32_t result = spi_write(dest_addr, dst, size); + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + return result; + } else { + return 1; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIEraseSector + */ +uint32_t IRAM spi_erase_sector(uint32_t sector) +{ + if (sector >= (sdk_flashchip.chip_size / sdk_flashchip.sector_size)) { + return 1; + } + + if (SPI_write_enable(&sdk_flashchip)) { + return 1; + } + + if (SPI_sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sector)) { + return 1; + } + return 0; +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_erase_sector + */ +uint32_t IRAM spi_flash_erase_sector(uint32_t sector) +{ + vPortEnterCritical(); + Cache_Read_Disable(); + + uint32_t result = spi_erase_sector(sector); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + + return result; +} + + /* * Flash addresses and size alignment is a rip-off of Arduino implementation. */ @@ -32,7 +173,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) if (addr < alignedBegin) { uint32_t nb = alignedBegin - addr; uint32_t tmp; - if (sdk_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -40,7 +181,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) } if (alignedEnd != alignedBegin) { - if (sdk_spi_flash_read(alignedBegin, + if (spi_flash_read(alignedBegin, (uint32_t*) (dst + alignedBegin - addr), alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); @@ -51,7 +192,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) if (addr + size > alignedEnd) { uint32_t nb = addr + size - alignedEnd; uint32_t tmp; - if (sdk_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -77,7 +218,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t nb = (size < ofs) ? size : ofs; uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; memcpy(tmp + 4 - ofs, src, nb); - if (sdk_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) + if (spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -88,7 +229,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; if (!srcAlign) { - if (sdk_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, + if (spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -100,7 +241,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); memcpy(buf, srcLeftover, willCopy); - if (sdk_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) + if (spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -118,7 +259,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t tmp = 0xffffffff; memcpy(&tmp, src + size - nb, nb); - if (sdk_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -140,7 +281,7 @@ static s32_t esp_spiffs_erase(u32_t addr, u32_t size) const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; for (uint32_t i = 0; i < sectorCount; ++i) { - sdk_spi_flash_erase_sector(sector + i); + spi_flash_erase_sector(sector + i); } return SPIFFS_OK; } From b71a7ad23758fd3bf23628ed687e5e0aabee7a26 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 30 Jun 2016 17:38:05 +0300 Subject: [PATCH 03/19] Use SPIFFS for POSIX file access. Draft. Not tested. --- .gitmodules | 3 ++ core/newlib_syscalls.c | 20 ++++++++--- examples/posix_fs/Makefile | 11 ++++++ examples/posix_fs/fs-test | 1 + examples/posix_fs/posix_fs_example.c | 41 ++++++++++++++++++++++ extras/spiffs/esp_spiffs.c | 51 ++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 examples/posix_fs/Makefile create mode 160000 examples/posix_fs/fs-test create mode 100644 examples/posix_fs/posix_fs_example.c diff --git a/.gitmodules b/.gitmodules index 00dadd8..d3fc285 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ [submodule "extras/spiffs/spiffs"] path = extras/spiffs/spiffs url = https://github.com/pellepl/spiffs.git +[submodule "examples/posix_fs/fs-test"] + path = examples/posix_fs/fs-test + url = https://github.com/sheinz/fs-test diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index 023872c..c8104a9 100644 --- a/core/newlib_syscalls.c +++ b/core/newlib_syscalls.c @@ -41,7 +41,7 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr) } /* syscall implementation for stdio write to UART */ -long _write_r(struct _reent *r, int fd, const char *ptr, int len ) +__attribute__((weak)) long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { r->_errno = EBADF; @@ -79,10 +79,20 @@ __attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len /* Stub syscall implementations follow, to allow compiling newlib functions that pull these in via various codepaths */ -__attribute__((alias("syscall_returns_enosys"))) int _open_r(struct _reent *r, const char *pathname, int flags, int mode); -__attribute__((alias("syscall_returns_enosys"))) int _fstat_r(struct _reent *r, int fd, void *buf); -__attribute__((alias("syscall_returns_enosys"))) int _close_r(struct _reent *r, int fd); -__attribute__((alias("syscall_returns_enosys"))) off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence); +__attribute__((weak, alias("syscall_returns_enosys"))) +int _open_r(struct _reent *r, const char *pathname, int flags, int mode); + +__attribute__((weak, alias("syscall_returns_enosys"))) +int _close_r(struct _reent *r, int fd); + +__attribute__((weak, alias("syscall_returns_enosys"))) +int _unlink_r(struct _reent *r, const char *path); + +__attribute__((alias("syscall_returns_enosys"))) +int _fstat_r(struct _reent *r, int fd, void *buf); + +__attribute__((alias("syscall_returns_enosys"))) +off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence); /* Generic stub for any newlib syscall that fails with errno ENOSYS ("Function not implemented") and a return value equivalent to diff --git a/examples/posix_fs/Makefile b/examples/posix_fs/Makefile new file mode 100644 index 0000000..bf45ed7 --- /dev/null +++ b/examples/posix_fs/Makefile @@ -0,0 +1,11 @@ +PROGRAM=posix_fs_example +PROGRAM_EXTRA_SRC_FILES=./fs-test/fs_test.c + +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test new file mode 160000 index 0000000..983ed83 --- /dev/null +++ b/examples/posix_fs/fs-test @@ -0,0 +1 @@ +Subproject commit 983ed830a8d2bd1a3eaa586ed608530f9d29201e diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c new file mode 100644 index 0000000..7e3ad8f --- /dev/null +++ b/examples/posix_fs/posix_fs_example.c @@ -0,0 +1,41 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" +#include + +#include "esp_spiffs.h" +#include "spiffs.h" + +#include "fs-test/fs_test.h" + + +void test_task(void *pvParameters) +{ + esp_spiffs_mount(); + esp_spiffs_unmount(); // FS must be unmounted before formating + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Format failed\n"); + } + esp_spiffs_mount(); + + while (1) { + vTaskDelay(5000 / portTICK_RATE_MS); + + if (fs_test_run(10000)) { + printf("PASS\n"); + } else { + printf("FAIL\n"); + } + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + + xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); +} diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index d6ec7d6..194d697 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -9,6 +9,7 @@ #include "spiffs.h" #include #include +#include spiffs fs; @@ -185,3 +186,53 @@ void esp_spiffs_unmount() fds_buf = 0; cache_buf = 0; } + +/* syscall implementation for stdio write to UART */ +long _write_r(struct _reent *r, int fd, const char *ptr, int len ) +{ + if(fd != r->_stdout->_file) { + return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len); + } + for(int i = 0; i < len; i++) { + /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ + if(ptr[i] == '\r') + continue; + if(ptr[i] == '\n') + uart_putc(0, '\r'); + uart_putc(0, ptr[i]); + } + return len; +} + +/* syscall implementation for stdio read from UART */ +long _read_r( struct _reent *r, int fd, char *ptr, int len ) +{ + int ch, i; + + if(fd != r->_stdin->_file) { + return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); + } + uart_rxfifo_wait(0, 1); + for(i = 0; i < len; i++) { + ch = uart_getc_nowait(0); + if (ch < 0) break; + ptr[i] = ch; + } + return i; +} + +/* syscall implementation for stdio write to UART */ +int _open_r(struct _reent *r, const char *pathname, int flags, int mode) +{ + return SPIFFS_open(&fs, pathname, flags, mode); +} + +int _close_r(struct _reent *r, int fd) +{ + return SPIFFS_close(&fs, (spiffs_file)fd); +} + +int _unlink_r(struct _reent *r, const char *path) +{ + return SPIFFS_remove(&fs, path); +} From 0f9d991ba7fe84655de9f2a57c98bb89492533c5 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 30 Jun 2016 22:18:07 +0300 Subject: [PATCH 04/19] Fixed libc and SPIFFS integration. Test passes on ESP-12E module. --- examples/posix_fs/fs-test | 2 +- examples/posix_fs/posix_fs_example.c | 2 +- extras/spiffs/esp_spiffs.c | 31 +++++++++++++++++++++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test index 983ed83..218c523 160000 --- a/examples/posix_fs/fs-test +++ b/examples/posix_fs/fs-test @@ -1 +1 @@ -Subproject commit 983ed830a8d2bd1a3eaa586ed608530f9d29201e +Subproject commit 218c5235584429f407d619e5e35f90732ad505f3 diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index 7e3ad8f..d84be5e 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -25,7 +25,7 @@ void test_task(void *pvParameters) while (1) { vTaskDelay(5000 / portTICK_RATE_MS); - if (fs_test_run(10000)) { + if (fs_test_run(1000)) { printf("PASS\n"); } else { printf("FAIL\n"); diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 194d697..4b5ef77 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -10,6 +10,7 @@ #include #include #include +#include spiffs fs; @@ -187,11 +188,15 @@ void esp_spiffs_unmount() cache_buf = 0; } -/* syscall implementation for stdio write to UART */ +#define FD_OFFSET 3 + +// This implementation replaces implementation in core/newlib_syscals.c long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { - return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len); + long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), + (char*)ptr, len); + return ret; } for(int i = 0; i < len; i++) { /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ @@ -204,13 +209,14 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len ) return len; } -/* syscall implementation for stdio read from UART */ +// This implementation replaces implementation in core/newlib_syscals.c long _read_r( struct _reent *r, int fd, char *ptr, int len ) { int ch, i; if(fd != r->_stdin->_file) { - return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); + long ret = SPIFFS_read(&fs, (spiffs_file)(fd - FD_OFFSET), ptr, len); + return ret; } uart_rxfifo_wait(0, 1); for(i = 0; i < len; i++) { @@ -221,15 +227,26 @@ long _read_r( struct _reent *r, int fd, char *ptr, int len ) return i; } -/* syscall implementation for stdio write to UART */ int _open_r(struct _reent *r, const char *pathname, int flags, int mode) { - return SPIFFS_open(&fs, pathname, flags, mode); + uint32_t spiffs_flags = SPIFFS_RDONLY; + + if (flags & O_CREAT) spiffs_flags |= SPIFFS_CREAT; + if (flags & O_APPEND) spiffs_flags |= SPIFFS_APPEND; + if (flags & O_TRUNC) spiffs_flags |= SPIFFS_TRUNC; + if (flags & O_RDONLY) spiffs_flags |= SPIFFS_RDONLY; + if (flags & O_WRONLY) spiffs_flags |= SPIFFS_WRONLY; + + int ret = SPIFFS_open(&fs, pathname, spiffs_flags, mode); + if (ret > 0) { + return ret + FD_OFFSET; + } + return ret; } int _close_r(struct _reent *r, int fd) { - return SPIFFS_close(&fs, (spiffs_file)fd); + return SPIFFS_close(&fs, (spiffs_file)(fd - FD_OFFSET)); } int _unlink_r(struct _reent *r, const char *path) From 5c12b7c7e92e2317c89f618d1280f8aa042aeb1b Mon Sep 17 00:00:00 2001 From: sheinz Date: Mon, 27 Jun 2016 18:06:06 +0300 Subject: [PATCH 05/19] Draft implementation of SPIFFS integration --- .gitmodules | 3 + examples/spiffs/Makefile | 9 ++ examples/spiffs/spiffs_example.c | 180 +++++++++++++++++++++ extras/spiffs/component.mk | 17 ++ extras/spiffs/esp_spiffs.c | 187 ++++++++++++++++++++++ extras/spiffs/esp_spiffs.h | 28 ++++ extras/spiffs/spiffs | 1 + extras/spiffs/spiffs_config.h | 263 +++++++++++++++++++++++++++++++ 8 files changed, 688 insertions(+) create mode 100644 examples/spiffs/Makefile create mode 100644 examples/spiffs/spiffs_example.c create mode 100644 extras/spiffs/component.mk create mode 100644 extras/spiffs/esp_spiffs.c create mode 100644 extras/spiffs/esp_spiffs.h create mode 160000 extras/spiffs/spiffs create mode 100644 extras/spiffs/spiffs_config.h diff --git a/.gitmodules b/.gitmodules index 7a3370e..00dadd8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ path = bootloader/rboot url = https://github.com/raburton/rboot.git +[submodule "extras/spiffs/spiffs"] + path = extras/spiffs/spiffs + url = https://github.com/pellepl/spiffs.git diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile new file mode 100644 index 0000000..b342df7 --- /dev/null +++ b/examples/spiffs/Makefile @@ -0,0 +1,9 @@ +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +# spiffs configuration +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c new file mode 100644 index 0000000..226ee51 --- /dev/null +++ b/examples/spiffs/spiffs_example.c @@ -0,0 +1,180 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include "FreeRTOS.h" +#include "task.h" +#include "esp8266.h" + +#include "spiffs.h" +#include "esp_spiffs.h" + + +#define TEST_FILE_NAME_LEN 16 +#define TEST_FILES 32 +#define TEST_FILE_MAX_SIZE 8192 + +typedef struct { + char name[TEST_FILE_NAME_LEN]; + uint16_t size; + uint8_t first_data_byte; +} TestFile; + +static TestFile test_files[TEST_FILES]; + +inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte) +{ + while (size--) { + *src++ = first_byte++; + } +} + +static bool write_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + sprintf(test_files[i].name, "file_%d.dat", i); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, + SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + test_files[i].size = rand() % TEST_FILE_MAX_SIZE; + test_files[i].first_data_byte = rand() % 256; + fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte); + + printf("Writing file %s size=%d\n", test_files[i].name, + test_files[i].size); + int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size); + if (written != test_files[i].size) { + printf("Write file operation failed, written=%d\n", written); + result = false; + break; + } + SPIFFS_close(&fs, f); + } + free(buf); + return result; +} + +inline static bool verify_test_data(uint8_t *data, uint16_t size, + uint8_t first_byte) +{ + while (size--) { + if (*data++ != first_byte++) { + return false; + } + } + return true; +} + +static bool verify_test_files() +{ + uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Verifying file %s\n", test_files[i].name); + spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0); + if (f < 0) { + printf("Open file operation failed\n"); + result = false; + break; + } + + int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size); + if (n != test_files[i].size) { + printf("Read file operation failed\n"); + result = false; + break; + } + + if (!verify_test_data(buf, test_files[i].size, + test_files[i].first_data_byte)) { + printf("Data verification failed\n"); + result = false; + break; + } + + SPIFFS_close(&fs, f); + } + + free(buf); + return result; +} + +static bool cleanup_test_files() +{ + bool result = true; + + for (uint8_t i = 0; i < TEST_FILES; i++) { + printf("Removing file %s\n", test_files[i].name); + if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) { + printf("Remove file operation failed\n"); + result = false; + break; + } + } + return result; +} + +inline static void print_info() +{ + uint32_t total, used; + + SPIFFS_info(&fs, &total, &used); + + printf("FS total=%d bytes, used=%d bytes\n", total, used); + printf("FS %d %% used\n", 100 * used/total); + + // File system structure visualisation + // SPIFFS_vis(&fs); +} + +void test_task(void *pvParameters) +{ + bool result = true; + + esp_spiffs_mount(); + esp_spiffs_unmount(); // FS must be unmounted before formating + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Format failed\n"); + } + esp_spiffs_mount(); + + while (1) { + vTaskDelay(5000 / portTICK_RATE_MS); + + result = write_test_files(); + + if (result) { + result = verify_test_files(); + } + + print_info(); + + if (result) { + result = cleanup_test_files(); + } + + if (result) { + printf("Test passed!\n"); + } else { + printf("Test failed!\n"); + while (1) { + vTaskDelay(1); + } + } + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + + xTaskCreate(test_task, (signed char *)"test_task", 1024, NULL, 2, NULL); +} diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk new file mode 100644 index 0000000..fcd5572 --- /dev/null +++ b/extras/spiffs/component.mk @@ -0,0 +1,17 @@ +# Component makefile for extras/spiffs + +SPIFFS_BASE_ADDR ?= 0x300000 +SPIFFS_SIZE ?= 0x100000 + +INC_DIRS += $(spiffs_ROOT) +INC_DIRS += $(spiffs_ROOT)spiffs/src + +# args for passing into compile rule generation +spiffs_SRC_DIR = $(spiffs_ROOT)spiffs/src +spiffs_SRC_DIR += $(spiffs_ROOT) + +spiffs_CFLAGS = $(CFLAGS) +spiffs_CFLAGS += -DSPIFFS_BASE_ADDR=$(SPIFFS_BASE_ADDR) +spiffs_CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +$(eval $(call component_compile_rules,spiffs)) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c new file mode 100644 index 0000000..d6ec7d6 --- /dev/null +++ b/extras/spiffs/esp_spiffs.c @@ -0,0 +1,187 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#include "esp_spiffs.h" +#include "spiffs.h" +#include +#include + +spiffs fs; + +static void *work_buf = 0; +static void *fds_buf = 0; +static void *cache_buf = 0; + +/* + * Flash addresses and size alignment is a rip-off of Arduino implementation. + */ + +static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) +{ + uint32_t result = SPIFFS_OK; + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t nb = alignedBegin - addr; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + memcpy(dst, &tmp + 4 - nb, nb); + } + + if (alignedEnd != alignedBegin) { + if (sdk_spi_flash_read(alignedBegin, + (uint32_t*) (dst + alignedBegin - addr), + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp; + if (sdk_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_read failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + memcpy(dst + size - nb, &tmp, nb); + } + + return result; +} + +static const int UNALIGNED_WRITE_BUFFER_SIZE = 512; + +static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) +{ + uint32_t alignedBegin = (addr + 3) & (~3); + uint32_t alignedEnd = (addr + size) & (~3); + if (alignedEnd < alignedBegin) { + alignedEnd = alignedBegin; + } + + if (addr < alignedBegin) { + uint32_t ofs = alignedBegin - addr; + uint32_t nb = (size < ofs) ? size : ofs; + uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; + memcpy(tmp + 4 - ofs, src, nb); + if (sdk_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + if (alignedEnd != alignedBegin) { + uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); + uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; + if (!srcAlign) { + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, + alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + else { + uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE]; + for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) { + size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); + memcpy(buf, srcLeftover, willCopy); + + if (sdk_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) + != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + + sizeLeft -= willCopy; + srcLeftover += willCopy; + alignedBegin += willCopy; + } + } + } + + if (addr + size > alignedEnd) { + uint32_t nb = addr + size - alignedEnd; + uint32_t tmp = 0xffffffff; + memcpy(&tmp, src + size - nb, nb); + + if (sdk_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + printf("spi_flash_write failed\n"); + return SPIFFS_ERR_INTERNAL; + } + } + + return SPIFFS_OK; +} + +static s32_t esp_spiffs_erase(u32_t addr, u32_t size) +{ + if (addr % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase addr=%x\n", addr); + } + if (size % SPI_FLASH_SEC_SIZE) { + printf("Unaligned erase size=%d\n", size); + } + + const uint32_t sector = addr / SPI_FLASH_SEC_SIZE; + const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; + + for (uint32_t i = 0; i < sectorCount; ++i) { + sdk_spi_flash_erase_sector(sector + i); + } + return SPIFFS_OK; +} + +int32_t esp_spiffs_mount() +{ + spiffs_config config = {0}; + + config.hal_read_f = esp_spiffs_read; + config.hal_write_f = esp_spiffs_write; + config.hal_erase_f = esp_spiffs_erase; + + size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); + size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + + work_buf = malloc(workBufSize); + fds_buf = malloc(fdsBufSize); + cache_buf = malloc(cacheBufSize); + printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + workBufSize, fdsBufSize, cacheBufSize); + + int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, + cache_buf, cacheBufSize, 0); + + if (err != SPIFFS_OK) { + printf("Error spiffs mount: %d\n", err); + } + + return err; +} + +void esp_spiffs_unmount() +{ + SPIFFS_unmount(&fs); + + free(work_buf); + free(fds_buf); + free(cache_buf); + + work_buf = 0; + fds_buf = 0; + cache_buf = 0; +} diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h new file mode 100644 index 0000000..3022d51 --- /dev/null +++ b/extras/spiffs/esp_spiffs.h @@ -0,0 +1,28 @@ +/** + * ESP8266 SPIFFS HAL configuration. + * + * Part of esp-open-rtos + * Copyright (c) 2016 sheinz https://github.com/sheinz + * MIT License + */ +#ifndef __ESP_SPIFFS_H__ +#define __ESP_SPIFFS_H__ + +#include "spiffs.h" + +extern spiffs fs; + +/** + * Provide SPIFFS with all necessary configuration, allocate memory buffers + * and mount SPIFFS. + * + * Return SPIFFS return code. + */ +int32_t esp_spiffs_mount(); + +/** + * Unmount SPIFFS and free all allocated buffers. + */ +void esp_spiffs_unmount(); + +#endif // __ESP_SPIFFS_H__ diff --git a/extras/spiffs/spiffs b/extras/spiffs/spiffs new file mode 160000 index 0000000..c6e94fd --- /dev/null +++ b/extras/spiffs/spiffs @@ -0,0 +1 @@ +Subproject commit c6e94fdca5c1601b90c027167f8d453c48e482c4 diff --git a/extras/spiffs/spiffs_config.h b/extras/spiffs/spiffs_config.h new file mode 100644 index 0000000..7b8c1a7 --- /dev/null +++ b/extras/spiffs/spiffs_config.h @@ -0,0 +1,263 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +#include +#include +#include +#include +#include +#include +// #include // for vPortEnterCritical/vPortExitCritical +// ----------- >8 ------------ + +typedef signed int s32_t; +typedef unsigned int u32_t; +typedef signed short s16_t; +typedef unsigned short u16_t; +typedef signed char s8_t; +typedef unsigned char u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //printf(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //printf(__VA_ARGS__) +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 1 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 3 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (64) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (0) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) // vPortEnterCritical() +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) // vPortExitCritical() +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 1 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (SPIFFS_SIZE) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (SPIFFS_BASE_ADDR) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4*1024) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 1 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) printf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ From d25b8b2a55be217034f2063bf7943a752e86b6e5 Mon Sep 17 00:00:00 2001 From: sheinz Date: Sun, 10 Jul 2016 01:33:23 +0300 Subject: [PATCH 06/19] Create SPIFFS image and flash it. mkspiffs is added to create SPIFFS image from directory with files. Build process changed to flash SPIFFS image if necessary --- common.mk | 3 +- examples/spiffs/Makefile | 2 + examples/spiffs/files/test.txt | 1 + extras/spiffs/component.mk | 33 ++++ extras/spiffs/mkspiffs/Makefile | 37 +++++ extras/spiffs/mkspiffs/README.md | 34 +++++ extras/spiffs/mkspiffs/mkspiffs.c | 243 ++++++++++++++++++++++++++++++ 7 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 examples/spiffs/files/test.txt create mode 100644 extras/spiffs/mkspiffs/Makefile create mode 100644 extras/spiffs/mkspiffs/README.md create mode 100644 extras/spiffs/mkspiffs/mkspiffs.c diff --git a/common.mk b/common.mk index 172fbe2..f4bfe06 100644 --- a/common.mk +++ b/common.mk @@ -210,7 +210,8 @@ $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR) $(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE) flash: $(FW_FILE) - $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) + $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) \ + 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(SPIFFS_ESPTOOL_ARGS) erase_flash: $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile index b342df7..b0be23f 100644 --- a/examples/spiffs/Makefile +++ b/examples/spiffs/Makefile @@ -7,3 +7,5 @@ SPIFFS_BASE_ADDR = 0x200000 SPIFFS_SIZE = 0x100000 include ../../common.mk + +$(eval $(call make_spiffs_image,files)) diff --git a/examples/spiffs/files/test.txt b/examples/spiffs/files/test.txt new file mode 100644 index 0000000..c86fb0c --- /dev/null +++ b/examples/spiffs/files/test.txt @@ -0,0 +1 @@ +This file will go to SPIFFS image. diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk index fcd5572..cf0a5a0 100644 --- a/extras/spiffs/component.mk +++ b/extras/spiffs/component.mk @@ -14,4 +14,37 @@ spiffs_CFLAGS = $(CFLAGS) spiffs_CFLAGS += -DSPIFFS_BASE_ADDR=$(SPIFFS_BASE_ADDR) spiffs_CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +# Create an SPIFFS image of specified directory and flash it with +# the rest of the firmware. +# +# Argumens: +# $(1) - directory with files which go into spiffs image +# +# Example: +# $(eval $(call make_spiffs_image,files)) +define make_spiffs_image +SPIFFS_IMAGE = $(addprefix $(FIRMWARE_DIR),spiffs.bin) +MKSPIFFS_DIR = $(ROOT)/extras/spiffs/mkspiffs +MKSPIFFS = $$(MKSPIFFS_DIR)/mkspiffs + +all: $$(SPIFFS_IMAGE) + +clean: clean_spiffs_img clean_mkspiffs + +$$(SPIFFS_IMAGE): $$(MKSPIFFS) $(1) + $$< $(1) $$@ + +$$(MKSPIFFS): + $$(MAKE) -C $$(MKSPIFFS_DIR) SPIFFS_SIZE=$(SPIFFS_SIZE) + +clean_spiffs_img: + $$(Q) rm -f spiffs.img + +clean_mkspiffs: + $$(Q) $$(MAKE) -C $$(MKSPIFFS_DIR) clean + +SPIFFS_ESPTOOL_ARGS = $(SPIFFS_BASE_ADDR) $$(SPIFFS_IMAGE) +endef + $(eval $(call component_compile_rules,spiffs)) diff --git a/extras/spiffs/mkspiffs/Makefile b/extras/spiffs/mkspiffs/Makefile new file mode 100644 index 0000000..7517456 --- /dev/null +++ b/extras/spiffs/mkspiffs/Makefile @@ -0,0 +1,37 @@ +# Check if SPIFFS_SIZE defined only if not cleaning +ifneq ($(MAKECMDGOALS),clean) +ifndef SPIFFS_SIZE +define ERROR_MSG +Variable SPIFFS_SIZE is not defined. +Cannot build mkspiffs without SPIFFS_SIZE. +Please specify it in your application Makefile. + +endef +$(error $(ERROR_MSG)) +endif +endif + +SOURCES := spiffs_hydrogen.c +SOURCES += spiffs_cache.c +SOURCES += spiffs_gc.c +SOURCES += spiffs_check.c +SOURCES += spiffs_nucleus.c +SOURCES += mkspiffs.c + +OBJECTS := $(SOURCES:.c=.o) + +VPATH = ../spiffs/src + +CFLAGS += -I.. +CFLAGS += -DSPIFFS_BASE_ADDR=0 # for image base addr is start of the image +CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) + +all: mkspiffs + +mkspiffs: $(OBJECTS) + +clean: + @rm -f mkspiffs + @rm -f *.o + +.PHONY: all clean diff --git a/extras/spiffs/mkspiffs/README.md b/extras/spiffs/mkspiffs/README.md new file mode 100644 index 0000000..5f6943d --- /dev/null +++ b/extras/spiffs/mkspiffs/README.md @@ -0,0 +1,34 @@ +# mkspiffs Create spiffs image + +mkspiffs is a command line utility to create an image of SPIFFS in order +to write to flash. + +## Usage + +mkspiffs will be built automatically if you include the following line in your +makefile: + +``` +$(eval $(call make_spiffs_image,files)) +``` + +where *files* is the directory with files that should go into SPIFFS image. + +Or you can build mkspiffs manually with: + +``` +make SPIFFS_SIZE=0x100000 +``` + +mkspiffs cannot be built without specifying SPIFFS size because it uses the +same SPIFFS sources as the firmware. And for the firmware SPIFFS size is +compile time defined. + +Please note that if you change SPIFFS_SIZE you need to rebuild mkspiffs. +The easiest way is to run `make clean` for you project. + +To manually generate SPIFFS image from directory, run: + +``` +mkspiffs DIRECTORY IMAGE_NAME +``` diff --git a/extras/spiffs/mkspiffs/mkspiffs.c b/extras/spiffs/mkspiffs/mkspiffs.c new file mode 100644 index 0000000..9f231a8 --- /dev/null +++ b/extras/spiffs/mkspiffs/mkspiffs.c @@ -0,0 +1,243 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spiffs_config.h" +#include "../spiffs/src/spiffs.h" + +static spiffs fs; +static void *image = 0; +static void *work_buf = 0; +static void *fds_buf = 0; +static void *cache_buf = 0; + +static void print_usage(const char *prog_name, const char *error_msg) +{ + if (error_msg) { + printf("Error: %s\n", error_msg); + } + printf("Usage: "); + printf("\t%s DIRECTORY IMAGE_NAME\n\n", prog_name); + printf("Example:\n"); + printf("\t%s ./my_files spiffs.img\n\n", prog_name); +} + +static s32_t _read_data(u32_t addr, u32_t size, u8_t *dst) +{ + memcpy(dst, (uint8_t*)image + addr, size); + return SPIFFS_OK; +} + +static s32_t _write_data(u32_t addr, u32_t size, u8_t *src) +{ + memcpy((uint8_t*)image + addr, src, size); + return SPIFFS_OK; +} + +static s32_t _erase_data(u32_t addr, u32_t size) +{ + memset((uint8_t*)image + addr, 0xFF, size); + return SPIFFS_OK; +} + +static bool init_spiffs(bool allocate_mem) +{ + spiffs_config config = {0}; + printf("Initializing SPIFFS, size=%d\n", SPIFFS_SIZE); + + config.hal_read_f = _read_data; + config.hal_write_f = _write_data; + config.hal_erase_f = _erase_data; + + int workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + int fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); + int cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + + if (allocate_mem) { + image = malloc(SPIFFS_SIZE); + work_buf = malloc(workBufSize); + fds_buf = malloc(fdsBufSize); + cache_buf = malloc(cacheBufSize); + printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + workBufSize, fdsBufSize, cacheBufSize); + } + + int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, + cache_buf, cacheBufSize, 0); + + if (err != SPIFFS_OK) { + printf("Error spiffs mount: %d\n", err); + return false; + } + + return true; +} + +static bool format_spiffs() +{ + SPIFFS_unmount(&fs); + + if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); + } else { + printf("Failed to format SPIFFS\n"); + return false; + } + + if (!init_spiffs(false)) { + printf("Failed to mount SPIFFS\n"); + return false; + } + return true; +} + +static void spiffs_free() +{ + free(image); + image = NULL; + + free(work_buf); + work_buf = NULL; + + free(fds_buf); + fds_buf = NULL; + + free(cache_buf); + cache_buf = NULL; +} + +static bool process_file(const char *src_file, const char *dst_file) +{ + int fd; + const int buf_size = 256; + uint8_t buf[buf_size]; + int data_len; + + fd = open(src_file, O_RDONLY); + if (fd < 0) { + printf("Error openning file: %s\n", src_file); + } + + spiffs_file out_fd = SPIFFS_open(&fs, dst_file, + SPIFFS_O_CREAT | SPIFFS_O_WRONLY, 0); + while ((data_len = read(fd, buf, buf_size)) != 0) { + if (SPIFFS_write(&fs, out_fd, buf, data_len) != data_len) { + printf("Error writing to SPIFFS file\n"); + break; + } + } + SPIFFS_close(&fs, out_fd); + close(fd); + return true; +} + +static bool process_directory(const char *direcotry) +{ + DIR *dp; + struct dirent *ep; + char path[256]; + + dp = opendir(direcotry); + if (dp != NULL) { + while ((ep = readdir(dp)) != 0) { + if (!strcmp(ep->d_name, ".") || + !strcmp(ep->d_name, "..")) { + continue; + } + if (ep->d_type != DT_REG) { + continue; // not a regular file + } + sprintf(path, "%s/%s", direcotry, ep->d_name); + printf("Processing file %s\n", path); + if (!process_file(path, ep->d_name)) { + printf("Error processing file\n"); + break; + } + } + closedir(dp); + } else { + printf("Error reading direcotry: %s\n", direcotry); + } + return true; +} + +static bool write_image(const char *out_file) +{ + int fd; + int size = SPIFFS_SIZE; + uint8_t *p = (uint8_t*)image; + fd = open(out_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + printf("Error creating file %s\n", out_file); + return false; + } + + printf("Writing image to file: %s\n", out_file); + + while (size != 0) { + write(fd, p, SPIFFS_CFG_LOG_PAGE_SZ()); + p += SPIFFS_CFG_LOG_PAGE_SZ(); + size -= SPIFFS_CFG_LOG_PAGE_SZ(); + } + + close(fd); + return true; +} + +int main(int argc, char *argv[]) +{ + int result = 0; + + if (argc != 3) { + print_usage(argv[0], NULL); + return -1; + } + + if (init_spiffs(/*allocate_mem=*/true)) { + if (format_spiffs()) { + if (process_directory(argv[1])) { + if (!write_image(argv[2])) { + printf("Error writing image\n"); + } + } else { + printf("Error processing direcotry\n"); + } + } else { + printf("Error formating spiffs\n"); + } + } else { + printf("Error initialising SPIFFS\n"); + } + + spiffs_free(); + return result; +} From bfa20af8555719344db9b0a478445f4d218e5fec Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 14 Jul 2016 15:44:02 +0300 Subject: [PATCH 07/19] Fix branch merging. Changes in esp_spiffs.c recovered. --- extras/spiffs/esp_spiffs.c | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index d6ec7d6..4b5ef77 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -9,6 +9,8 @@ #include "spiffs.h" #include #include +#include +#include spiffs fs; @@ -185,3 +187,69 @@ void esp_spiffs_unmount() fds_buf = 0; cache_buf = 0; } + +#define FD_OFFSET 3 + +// This implementation replaces implementation in core/newlib_syscals.c +long _write_r(struct _reent *r, int fd, const char *ptr, int len ) +{ + if(fd != r->_stdout->_file) { + long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), + (char*)ptr, len); + return ret; + } + for(int i = 0; i < len; i++) { + /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ + if(ptr[i] == '\r') + continue; + if(ptr[i] == '\n') + uart_putc(0, '\r'); + uart_putc(0, ptr[i]); + } + return len; +} + +// This implementation replaces implementation in core/newlib_syscals.c +long _read_r( struct _reent *r, int fd, char *ptr, int len ) +{ + int ch, i; + + if(fd != r->_stdin->_file) { + long ret = SPIFFS_read(&fs, (spiffs_file)(fd - FD_OFFSET), ptr, len); + return ret; + } + uart_rxfifo_wait(0, 1); + for(i = 0; i < len; i++) { + ch = uart_getc_nowait(0); + if (ch < 0) break; + ptr[i] = ch; + } + return i; +} + +int _open_r(struct _reent *r, const char *pathname, int flags, int mode) +{ + uint32_t spiffs_flags = SPIFFS_RDONLY; + + if (flags & O_CREAT) spiffs_flags |= SPIFFS_CREAT; + if (flags & O_APPEND) spiffs_flags |= SPIFFS_APPEND; + if (flags & O_TRUNC) spiffs_flags |= SPIFFS_TRUNC; + if (flags & O_RDONLY) spiffs_flags |= SPIFFS_RDONLY; + if (flags & O_WRONLY) spiffs_flags |= SPIFFS_WRONLY; + + int ret = SPIFFS_open(&fs, pathname, spiffs_flags, mode); + if (ret > 0) { + return ret + FD_OFFSET; + } + return ret; +} + +int _close_r(struct _reent *r, int fd) +{ + return SPIFFS_close(&fs, (spiffs_file)(fd - FD_OFFSET)); +} + +int _unlink_r(struct _reent *r, const char *path) +{ + return SPIFFS_remove(&fs, path); +} From 22654a4de71a8a4411b2e217f51e88448f619c5c Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 14 Jul 2016 16:13:03 +0300 Subject: [PATCH 08/19] SPIFFS: Support lseek, stat, fstat Support for lseek, stat, fstat added. Test extended to covert those functions. --- core/newlib_syscalls.c | 7 +++++-- examples/posix_fs/fs-test | 2 +- extras/spiffs/esp_spiffs.c | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/core/newlib_syscalls.c b/core/newlib_syscalls.c index c8104a9..577ecda 100644 --- a/core/newlib_syscalls.c +++ b/core/newlib_syscalls.c @@ -88,10 +88,13 @@ int _close_r(struct _reent *r, int fd); __attribute__((weak, alias("syscall_returns_enosys"))) int _unlink_r(struct _reent *r, const char *path); -__attribute__((alias("syscall_returns_enosys"))) +__attribute__((weak, alias("syscall_returns_enosys"))) int _fstat_r(struct _reent *r, int fd, void *buf); -__attribute__((alias("syscall_returns_enosys"))) +__attribute__((weak, alias("syscall_returns_enosys"))) +int _stat_r(struct _reent *r, const char *pathname, void *buf); + +__attribute__((weak, alias("syscall_returns_enosys"))) off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence); /* Generic stub for any newlib syscall that fails with errno ENOSYS diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test index 218c523..12b1023 160000 --- a/examples/posix_fs/fs-test +++ b/examples/posix_fs/fs-test @@ -1 +1 @@ -Subproject commit 218c5235584429f407d619e5e35f90732ad505f3 +Subproject commit 12b10230cc56970857e6890bdd5663fbae74c4c3 diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 4b5ef77..72419a1 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -253,3 +253,30 @@ int _unlink_r(struct _reent *r, const char *path) { return SPIFFS_remove(&fs, path); } + +int _fstat_r(struct _reent *r, int fd, void *buf) +{ + spiffs_stat s; + struct stat *sb = (struct stat*)buf; + + int result = SPIFFS_fstat(&fs, (spiffs_file)(fd - FD_OFFSET), &s); + sb->st_size = s.size; + + return result; +} + +int _stat_r(struct _reent *r, const char *pathname, void *buf) +{ + spiffs_stat s; + struct stat *sb = (struct stat*)buf; + + int result = SPIFFS_stat(&fs, pathname, &s); + sb->st_size = s.size; + + return result; +} + +off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence) +{ + return SPIFFS_lseek(&fs, (spiffs_file)(fd - FD_OFFSET), offset, whence); +} From 1db953e0c3e096f9e7ff442f6c50dece002df6e1 Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 00:08:34 +0300 Subject: [PATCH 09/19] SPIFFS: Add speed test. --- examples/posix_fs/fs-test | 2 +- examples/posix_fs/posix_fs_example.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/examples/posix_fs/fs-test b/examples/posix_fs/fs-test index 12b1023..2ad547a 160000 --- a/examples/posix_fs/fs-test +++ b/examples/posix_fs/fs-test @@ -1 +1 @@ -Subproject commit 12b10230cc56970857e6890bdd5663fbae74c4c3 +Subproject commit 2ad547adc5f725594b3c6752f036ff4401b221fc diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index d84be5e..4525be3 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -1,5 +1,6 @@ #include "espressif/esp_common.h" #include "esp/uart.h" +#include "esp/timer.h" #include "FreeRTOS.h" #include "task.h" #include "esp8266.h" @@ -10,6 +11,10 @@ #include "fs-test/fs_test.h" +static fs_time_t get_current_time() +{ + return timer_get_count(FRC2) / 5000; // to get roughly 1ms resolution +} void test_task(void *pvParameters) { @@ -24,12 +29,20 @@ void test_task(void *pvParameters) while (1) { vTaskDelay(5000 / portTICK_RATE_MS); - - if (fs_test_run(1000)) { + if (fs_load_test_run(100)) { printf("PASS\n"); } else { printf("FAIL\n"); } + + vTaskDelay(5000 / portTICK_RATE_MS); + float write_rate, read_rate; + if (fs_speed_test_run(get_current_time, &write_rate, &read_rate)) { + printf("Read speed: %.0f bytes/s\n", read_rate * 1000); + printf("Write speed: %.0f bytes/s\n", write_rate * 1000); + } else { + printf("FAIL\n"); + } } } From df796947bdfe3b452d93700fd68cf870f2d66f0a Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 00:41:29 +0300 Subject: [PATCH 10/19] SPIFFS: Update README.md --- examples/posix_fs/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 examples/posix_fs/README.md diff --git a/examples/posix_fs/README.md b/examples/posix_fs/README.md new file mode 100644 index 0000000..7f05bd4 --- /dev/null +++ b/examples/posix_fs/README.md @@ -0,0 +1,10 @@ +# POSIX file access example + +This example runs several file system tests on ESP8266. +It uses fs-test library to perform file operations test. fs-test library uses +only POSIX file functions so can be run on host system as well. + +Currently included tests: + * File system load test. Perform multiple file operations in random order. + * File system speed test. Measures files read/write speed. + From 66610c56cb03398c9af035ba57f0f19368f56de4 Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 01:21:32 +0300 Subject: [PATCH 11/19] SPIFFS: Improve SPIFFS image build Rebuild SPIFFS image if files change. Rebuild mkspiffs if SPIFFS_SIZE is changed in Makefile. --- extras/spiffs/component.mk | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk index cf0a5a0..b0b34c1 100644 --- a/extras/spiffs/component.mk +++ b/extras/spiffs/component.mk @@ -20,26 +20,29 @@ spiffs_CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) # # Argumens: # $(1) - directory with files which go into spiffs image -# +# # Example: # $(eval $(call make_spiffs_image,files)) define make_spiffs_image SPIFFS_IMAGE = $(addprefix $(FIRMWARE_DIR),spiffs.bin) MKSPIFFS_DIR = $(ROOT)/extras/spiffs/mkspiffs MKSPIFFS = $$(MKSPIFFS_DIR)/mkspiffs +SPIFFS_FILE_LIST = $(shell find $(1)) all: $$(SPIFFS_IMAGE) clean: clean_spiffs_img clean_mkspiffs -$$(SPIFFS_IMAGE): $$(MKSPIFFS) $(1) +$$(SPIFFS_IMAGE): $$(MKSPIFFS) $$(SPIFFS_FILE_LIST) $$< $(1) $$@ -$$(MKSPIFFS): +# if SPIFFS_SIZE in Makefile is changed rebuild mkspiffs +$$(MKSPIFFS): Makefile + $$(MAKE) -C $$(MKSPIFFS_DIR) clean $$(MAKE) -C $$(MKSPIFFS_DIR) SPIFFS_SIZE=$(SPIFFS_SIZE) clean_spiffs_img: - $$(Q) rm -f spiffs.img + $$(Q) rm -f $$(SPIFFS_IMAGE) clean_mkspiffs: $$(Q) $$(MAKE) -C $$(MKSPIFFS_DIR) clean From 924860a78f389a8ff4c912d3eb8f6e5181d1059c Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 15:22:03 +0300 Subject: [PATCH 12/19] SPIFFS: Update example, README.md Separate method to initialize SPIFFS memory buffers. REDME.md for spiffs component. Simplify spiffs example. --- examples/posix_fs/posix_fs_example.c | 3 +- examples/spiffs/Makefile | 2 +- examples/spiffs/spiffs_example.c | 194 ++++++++------------------- extras/spiffs/README.md | 153 +++++++++++++++++++++ extras/spiffs/esp_spiffs.c | 71 ++++++---- extras/spiffs/esp_spiffs.h | 25 +++- 6 files changed, 277 insertions(+), 171 deletions(-) create mode 100644 extras/spiffs/README.md diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index 4525be3..2f5ad14 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -18,8 +18,9 @@ static fs_time_t get_current_time() void test_task(void *pvParameters) { + esp_spiffs_init(); esp_spiffs_mount(); - esp_spiffs_unmount(); // FS must be unmounted before formating + SPIFFS_unmount(&fs); // FS must be unmounted before formating if (SPIFFS_format(&fs) == SPIFFS_OK) { printf("Format complete\n"); } else { diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile index b0be23f..7327707 100644 --- a/examples/spiffs/Makefile +++ b/examples/spiffs/Makefile @@ -4,7 +4,7 @@ FLASH_SIZE = 32 # spiffs configuration SPIFFS_BASE_ADDR = 0x200000 -SPIFFS_SIZE = 0x100000 +SPIFFS_SIZE = 0x010000 include ../../common.mk diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c index 226ee51..98decaf 100644 --- a/examples/spiffs/spiffs_example.c +++ b/examples/spiffs/spiffs_example.c @@ -4,171 +4,95 @@ #include "task.h" #include "esp8266.h" +#include "fcntl.h" +#include "unistd.h" + #include "spiffs.h" #include "esp_spiffs.h" -#define TEST_FILE_NAME_LEN 16 -#define TEST_FILES 32 -#define TEST_FILE_MAX_SIZE 8192 - -typedef struct { - char name[TEST_FILE_NAME_LEN]; - uint16_t size; - uint8_t first_data_byte; -} TestFile; - -static TestFile test_files[TEST_FILES]; - -inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte) +static void example_read_file_posix() { - while (size--) { - *src++ = first_byte++; - } -} + const int buf_size = 0xFF; + uint8_t buf[buf_size]; -static bool write_test_files() -{ - uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); - bool result = true; - - for (uint8_t i = 0; i < TEST_FILES; i++) { - sprintf(test_files[i].name, "file_%d.dat", i); - spiffs_file f = SPIFFS_open(&fs, test_files[i].name, - SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0); - if (f < 0) { - printf("Open file operation failed\n"); - result = false; - break; - } - test_files[i].size = rand() % TEST_FILE_MAX_SIZE; - test_files[i].first_data_byte = rand() % 256; - fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte); - - printf("Writing file %s size=%d\n", test_files[i].name, - test_files[i].size); - int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size); - if (written != test_files[i].size) { - printf("Write file operation failed, written=%d\n", written); - result = false; - break; - } - SPIFFS_close(&fs, f); - } - free(buf); - return result; -} - -inline static bool verify_test_data(uint8_t *data, uint16_t size, - uint8_t first_byte) -{ - while (size--) { - if (*data++ != first_byte++) { - return false; - } - } - return true; -} - -static bool verify_test_files() -{ - uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); - bool result = true; - - for (uint8_t i = 0; i < TEST_FILES; i++) { - printf("Verifying file %s\n", test_files[i].name); - spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0); - if (f < 0) { - printf("Open file operation failed\n"); - result = false; - break; - } - - int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size); - if (n != test_files[i].size) { - printf("Read file operation failed\n"); - result = false; - break; - } - - if (!verify_test_data(buf, test_files[i].size, - test_files[i].first_data_byte)) { - printf("Data verification failed\n"); - result = false; - break; - } - - SPIFFS_close(&fs, f); + int fd = open("test.txt", O_RDONLY); + if (fd < 0) { + printf("Error opening file\n"); + return; } - free(buf); - return result; + int read_bytes = read(fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + close(fd); } -static bool cleanup_test_files() +static void example_read_file_spiffs() { - bool result = true; + const int buf_size = 0xFF; + uint8_t buf[buf_size]; - for (uint8_t i = 0; i < TEST_FILES; i++) { - printf("Removing file %s\n", test_files[i].name); - if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) { - printf("Remove file operation failed\n"); - result = false; - break; - } + spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; } - return result; + + int read_bytes = SPIFFS_read(&fs, fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + SPIFFS_close(&fs, fd); } -inline static void print_info() +static void example_write_file() +{ + uint8_t buf[] = "Example data, written by ESP8266"; + + int fd = open("other.txt", O_WRONLY|O_CREAT, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; + } + + int written = write(fd, buf, sizeof(buf)); + printf("Written %d bytes\n", written); + + close(fd); +} + +static void example_fs_info() { uint32_t total, used; - SPIFFS_info(&fs, &total, &used); - - printf("FS total=%d bytes, used=%d bytes\n", total, used); - printf("FS %d %% used\n", 100 * used/total); - - // File system structure visualisation - // SPIFFS_vis(&fs); + printf("Total: %d bytes, used: %d bytes", total, used); } void test_task(void *pvParameters) { - bool result = true; - - esp_spiffs_mount(); - esp_spiffs_unmount(); // FS must be unmounted before formating - if (SPIFFS_format(&fs) == SPIFFS_OK) { - printf("Format complete\n"); - } else { - printf("Format failed\n"); + esp_spiffs_init(); + if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); } - esp_spiffs_mount(); while (1) { - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_RATE_MS); - result = write_test_files(); + example_write_file(); - if (result) { - result = verify_test_files(); - } + example_read_file_posix(); - print_info(); + example_read_file_spiffs(); - if (result) { - result = cleanup_test_files(); - } + example_fs_info(); - if (result) { - printf("Test passed!\n"); - } else { - printf("Test failed!\n"); - while (1) { - vTaskDelay(1); - } - } + printf("\n\n"); } } diff --git a/extras/spiffs/README.md b/extras/spiffs/README.md new file mode 100644 index 0000000..76d2081 --- /dev/null +++ b/extras/spiffs/README.md @@ -0,0 +1,153 @@ +# SPIFFS ESP8266 File system + +This component adds file system support for ESP8266. File system of choice +for ESP8266 is [SPIFFS](https://github.com/pellepl/spiffs). +It was specifically designed to use with SPI NOR flash on embedded systems. +The main advantage of SPIFFS is wear leveling, which prolongs life time +of a flash memory. + +## Features + + * SPIFFS - embedded file system for NOR flash memory. + * POSIX file operations. + * Static files upload to ESP8266 file system within build process. + * SPIFFS singleton configuration. Only one instance of FS on a device. + +## Usage + +In order to use file system in a project the following steps should be made: + * Add SPIFFS component in a project Makefile `EXTRA_COMPONENTS = extras/spiffs` + * Specify your flash size in the Makefile `FLASH_SIZE = 32` + * Specify the start address of file system region on the flash memory +`SPIFFS_BASE_ADDR = 0x200000` + * If you want to upload files to a file system during flash process specify +the directory with files `$(eval $(call make_spiffs_image,files))` + +In the end the Makefile should look like: + +``` +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk + +$(eval $(call make_spiffs_image,files)) +``` + +Note: Macro call to prepare SPIFFS image for flashing should go after +`include common.mk` + +### Files upload + +To upload files to a file system during flash process the following macro is +used: + +``` +$(eval $(call make_spiffs_image,files)) +``` + +It enables the build of a helper utility **mkspiffs**. This utility creates +an SPIFFS image with files in the specified directory. + +The SPIFFS image is created during build stage, after `make` is run. +The image is flashed into the device along with firmware during flash stage, +after `make flash` is run. + +**mkspiffs** utility uses the same SPIFFS source code and the same +configuration as ESP8266. So the created image should always be compatible +with SPIFFS on a device. + +The build process will catch any changes in files directory and rebuild the +image each time `make` is run. +The build process will handle SPIFFS_SIZE change and rebuild **mkspiffs** +utility and the image. + +## Example + +### Mount + +``` +esp_spiffs_init(); // allocate memory buffers +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mounting SPIFFS\n"); +} +``` + +### Format + +Formatting SPIFFS is a little bit awkward. Before formatting SPIFFS must be +mounted and unmounted. +``` +esp_spiffs_init(); +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); +} +SPIFFS_unmount(&fs); // FS must be unmounted before formating +if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); +} else { + printf("Format failed\n"); +} +esp_spiffs_mount(); +``` + +### POSIX read + +Nothing special here. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +int fd = open("test.txt", O_RDONLY); +if (fd < 0) { + printf("Error opening file\n"); +} + +read(fd, buf, buf_size); +printf("Data: %s\n", buf); + +close(fd); +``` + +### SPIFFS read + +SPIFFS interface is intended to be as close to POSIX as possible. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +SPIFFS_read(&fs, fd, buf, buf_size); +printf("Data: %s\n", buf); + +SPIFFS_close(&fs, fd); +``` + +### POSIX write + +``` +uint8_t buf[] = "Example data, written by ESP8266"; + +int fd = open("other.txt", O_WRONLY|O_CREAT, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +write(fd, buf, sizeof(buf)); + +close(fd); +``` + +## Resources + +[SPIFFS](https://github.com/pellepl/spiffs) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 72419a1..11a84d5 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -14,9 +14,21 @@ spiffs fs; -static void *work_buf = 0; -static void *fds_buf = 0; -static void *cache_buf = 0; +typedef struct { + void *buf; + uint32_t size; +} fs_buf_t; + +static fs_buf_t work_buf = {0}; +static fs_buf_t fds_buf = {0}; +static fs_buf_t cache_buf = {0}; + +/** + * Number of file descriptors opened at the same time + */ +#define ESP_SPIFFS_FD_NUMBER 5 + +#define ESP_SPIFFS_CACHE_PAGES 5 /* * Flash addresses and size alignment is a rip-off of Arduino implementation. @@ -147,6 +159,29 @@ static s32_t esp_spiffs_erase(u32_t addr, u32_t size) return SPIFFS_OK; } +void esp_spiffs_init() +{ + work_buf.size = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + fds_buf.size = SPIFFS_buffer_bytes_for_filedescs(&fs, ESP_SPIFFS_FD_NUMBER); + cache_buf.size= SPIFFS_buffer_bytes_for_cache(&fs, ESP_SPIFFS_CACHE_PAGES); + + work_buf.buf = malloc(work_buf.size); + fds_buf.buf = malloc(fds_buf.size); + cache_buf.buf = malloc(cache_buf.size); +} + +void esp_spiffs_deinit() +{ + free(work_buf.buf); + work_buf.buf = 0; + + free(fds_buf.buf); + fds_buf.buf = 0; + + free(cache_buf.buf); + cache_buf.buf = 0; +} + int32_t esp_spiffs_mount() { spiffs_config config = {0}; @@ -155,18 +190,13 @@ int32_t esp_spiffs_mount() config.hal_write_f = esp_spiffs_write; config.hal_erase_f = esp_spiffs_erase; - size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); - size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); - size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + printf("SPIFFS size: %d\n", SPIFFS_SIZE); + printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + work_buf.size, fds_buf.size, cache_buf.size); - work_buf = malloc(workBufSize); - fds_buf = malloc(fdsBufSize); - cache_buf = malloc(cacheBufSize); - printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", - workBufSize, fdsBufSize, cacheBufSize); - - int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, - cache_buf, cacheBufSize, 0); + int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, + (uint8_t*)fds_buf.buf, fds_buf.size, + cache_buf.buf, cache_buf.size, 0); if (err != SPIFFS_OK) { printf("Error spiffs mount: %d\n", err); @@ -175,19 +205,6 @@ int32_t esp_spiffs_mount() return err; } -void esp_spiffs_unmount() -{ - SPIFFS_unmount(&fs); - - free(work_buf); - free(fds_buf); - free(cache_buf); - - work_buf = 0; - fds_buf = 0; - cache_buf = 0; -} - #define FD_OFFSET 3 // This implementation replaces implementation in core/newlib_syscals.c diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h index 3022d51..f074017 100644 --- a/extras/spiffs/esp_spiffs.h +++ b/extras/spiffs/esp_spiffs.h @@ -13,16 +13,27 @@ extern spiffs fs; /** - * Provide SPIFFS with all necessary configuration, allocate memory buffers - * and mount SPIFFS. + * Prepare for SPIFFS mount. + * + * The function allocates all the necessary buffers. + */ +void esp_spiffs_init(); + +/** + * Free all memory buffers that were used by SPIFFS. + * + * The function should be called after SPIFFS unmount if the file system is not + * going to need any more. + */ +void esp_spiffs_deinit(); + +/** + * Mount SPIFFS. + * + * esp_spiffs_init must be called first. * * Return SPIFFS return code. */ int32_t esp_spiffs_mount(); -/** - * Unmount SPIFFS and free all allocated buffers. - */ -void esp_spiffs_unmount(); - #endif // __ESP_SPIFFS_H__ From 55b7d29767bc78f6575ecc41ab4537adbbe66bc8 Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 15:44:22 +0300 Subject: [PATCH 13/19] SPIFFS: Fix SPIFFS rebuild if SIZE is changed. --- extras/spiffs/component.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk index b0b34c1..a0a8928 100644 --- a/extras/spiffs/component.mk +++ b/extras/spiffs/component.mk @@ -36,6 +36,10 @@ clean: clean_spiffs_img clean_mkspiffs $$(SPIFFS_IMAGE): $$(MKSPIFFS) $$(SPIFFS_FILE_LIST) $$< $(1) $$@ +# Rebuild SPIFFS if Makefile is changed, where SPIFF_SIZE is defined +$$(spiffs_ROOT)spiffs_config.h: Makefile + $$(Q) touch $$@ + # if SPIFFS_SIZE in Makefile is changed rebuild mkspiffs $$(MKSPIFFS): Makefile $$(MAKE) -C $$(MKSPIFFS_DIR) clean From 0a98f4390272508487fd6a34c3ee917353dec5ab Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 19:01:17 +0300 Subject: [PATCH 14/19] SPIFFS: Fixed linux build and travis build mkspiffs utility explicitly use gcc to compile --- extras/spiffs/mkspiffs/Makefile | 4 ++++ extras/spiffs/mkspiffs/mkspiffs.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extras/spiffs/mkspiffs/Makefile b/extras/spiffs/mkspiffs/Makefile index 7517456..a9a9722 100644 --- a/extras/spiffs/mkspiffs/Makefile +++ b/extras/spiffs/mkspiffs/Makefile @@ -11,6 +11,10 @@ $(error $(ERROR_MSG)) endif endif +# explicitly use gcc as in xtensa build environment it might be set to +# cross compiler +CC = gcc + SOURCES := spiffs_hydrogen.c SOURCES += spiffs_cache.c SOURCES += spiffs_gc.c diff --git a/extras/spiffs/mkspiffs/mkspiffs.c b/extras/spiffs/mkspiffs/mkspiffs.c index 9f231a8..65b90e0 100644 --- a/extras/spiffs/mkspiffs/mkspiffs.c +++ b/extras/spiffs/mkspiffs/mkspiffs.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include From 5d5f28a22fcbcdf25ccf057b0d5afdfdec29a398 Mon Sep 17 00:00:00 2001 From: sheinz Date: Sat, 16 Jul 2016 00:22:33 +0300 Subject: [PATCH 15/19] SPIFFS: Bypass SDK and bootrom for flash access. Accessing SPI flash using reversed engineered functions. --- extras/spiffs/esp_spi_flash.c | 336 ++++++++++++++++++++++++++++++++++ extras/spiffs/esp_spi_flash.h | 11 ++ extras/spiffs/esp_spiffs.c | 157 +--------------- 3 files changed, 356 insertions(+), 148 deletions(-) create mode 100644 extras/spiffs/esp_spi_flash.c create mode 100644 extras/spiffs/esp_spi_flash.h diff --git a/extras/spiffs/esp_spi_flash.c b/extras/spiffs/esp_spi_flash.c new file mode 100644 index 0000000..74f8d98 --- /dev/null +++ b/extras/spiffs/esp_spi_flash.c @@ -0,0 +1,336 @@ +#include "esp_spi_flash.h" +#include "flashchip.h" +#include "espressif/spi_flash.h" +#include "FreeRTOS.h" +#include "esp/rom.h" +#include "esp/spi_regs.h" + + +static uint32_t IRAM read_status(sdk_flashchip_t *flashchip, uint32_t *status) +{ + uint32_t _status; + + do { + SPI(0).RSTATUS = 0; + SPI(0).CMD = SPI_CMD_READ_SR; + while (SPI(0).CMD) {} + _status = SPI(0).RSTATUS & flashchip->status_mask; + } while ( _status & 0b1); + + *status = _status; + + return 0; +} + +static uint32_t IRAM wait_idle(sdk_flashchip_t *flashchip) +{ + while (DPORT.SPI_READY & DPORT_SPI_READY_IDLE) {} + uint32_t a3; + return read_status(flashchip, &a3); +} + +static uint32_t IRAM write_enable(sdk_flashchip_t *flashchip) +{ + uint32_t local0 = 0; + + wait_idle(flashchip); + + SPI(0).CMD = SPI_CMD_WRITE_ENABLE; + while (SPI(0).CMD) {} + + if (!(local0 & 0b1)) { + do { + read_status(flashchip, &local0); + } while (!(local0 & (1<<1))); + } + return 0; +} + +static uint32_t IRAM page_program(sdk_flashchip_t *flashchip, uint32_t dest_addr, + uint32_t *buf, uint32_t size) +{ + if (size & 0b11) { + return 1; + } + + // check if block to write doesn't cross page boundary + if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) { + return 1; + } + wait_idle(flashchip); + if (size < 1) { + return 0; + } + + // a12 = 0x60000200 + // a0 = 0x00FFFFFF + // a6 = (dest_addr & 0x00FFFFFF) | 0x20000000 + while (size >= 32) { + SPI(0).ADDR = (dest_addr & 0x00FFFFFF) | 0x20000000; + // a4 - loop variable += 4 + // a5 = buf[0] + for (uint8_t i = 0; i != 8; i++) { + SPI(0).W[i] = buf[i]; + } + size -= 32; + dest_addr += 32; + buf += 8; + if (write_enable(flashchip)) { + return 1; + } + SPI(0).CMD = SPI_CMD_PP; + while (SPI(0).CMD) {} // wait for reg->cmd to be 0 + wait_idle(flashchip); + // a0 = 0x00FFFFFF + if (size < 1) { + return 0; + } + } + // a7 = 0x00FFFFFF & dest_addr + // a4 = size << 24; + // a4 = a7 | a4 + SPI(0).ADDR = (size << 24) | (0x00FFFFFF & dest_addr); + // a6 = 0b11 & size + // a3 = size >> 2; + // a5 = a3 + 1 + uint32_t words = size >> 2; + if (0b11 & size) { + words += 1; + } + words = words & 0xFF; + if (words != 0) { + // a4 = 0 + uint8_t i = 0; + + if (words & 0b1) { // bit 0 is set in a3 + SPI(0).W[0] = buf[0]; + i++; + } + // a6 = a3 >> 1; + if (words >> 1) { + // a6 = 0x600000200 + // buff[0] + for (; i != words; i++) { + SPI(0).W[i] = buf[i]; + } + } + } + + if (write_enable(flashchip)) { + return 1; + } + SPI(0).CMD = SPI_CMD_PP; + while (SPI(0).CMD) {} // wait for reg->cmd to be 0 + wait_idle(flashchip); + // a0 = 0x00FFFFFF + return 0; +} + +static uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, + uint32_t *dst, uint32_t size) +{ + // a12 = dst + if ((addr + size) > flashchip->chip_size) { + return 1; + } + + // a14 = addr + // a13 = size + wait_idle(flashchip); + if (size < 1) { + return 0; + } + // SPI(0).CMD + while (size >= 32) { + // a8 = addr | 0x20000000; + SPI(0).ADDR = addr | 0x20000000; + SPI(0).CMD = SPI_CMD_READ; + while (SPI(0).CMD) {}; + for (uint32_t a2 = 0; a2 < 8; a2++) { + *dst = SPI(0).W[a2]; + dst++; + } + size -= 32; + addr += 32; + } + + if (size >= 1) { + // a7 = size << 24; + // a7 = addr | a7 + SPI(0).ADDR = addr | (size << 24); + SPI(0).CMD = SPI_CMD_READ; + while (SPI(0).CMD) {}; + // a10 = size & 0b11 + uint8_t a7 = size >> 2; + // a9 = a7 + 1 + if (size & 0b11) { + // a7 = a7 + 1 + a7++; + } + // a7 = a7 & 0xFF + if (!a7) { + return 0; + } + uint8_t a2 = 0; + if (a7 & 0b1) { + a2 = 1; + // a11 = SPI(0).W0 + *dst = SPI(0).W[0]; + dst += 1; + } + size = a7 >> 1; + if (!size) { + return 0; + } + for (; a2 != a7; a2++) { + *dst = SPI(0).W[a2]; + dst += 1; + } + } + + return 0; +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIRead + */ +static uint32_t IRAM spi_read(uint32_t dest_addr, void *src, uint32_t size) +{ + if (read_data(&sdk_flashchip, dest_addr, (uint32_t*)src, size)) { + return 1; + } else { + return 0; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_read + */ +uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size) +{ + if (src) { + vPortEnterCritical(); + Cache_Read_Disable(); + uint32_t result = spi_read(dest_addr, src, size); + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + return result; + } else { + return 1; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIWrite + */ +static uint32_t IRAM spi_write(uint32_t dest_addr, void *dst, uint32_t size) +{ + if (sdk_flashchip.chip_size < (dest_addr + size)) { + return 1; + } + + uint32_t write_bytes_to_page = sdk_flashchip.page_size - + (dest_addr % sdk_flashchip.page_size); + + if (size < write_bytes_to_page) { + if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, size)) { + return 1; + } else { + return 0; + } + } + + if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, write_bytes_to_page)) { + return 1; + } + + uint32_t offset = write_bytes_to_page; + uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; + for (uint8_t i = 0; i != pages_to_write; i++) { + if (page_program(&sdk_flashchip, dest_addr + offset, + dst + ((offset>>2)<<2), sdk_flashchip.page_size)) { + return 1; + } + offset += sdk_flashchip.page_size; + } + + if (page_program(&sdk_flashchip, dest_addr + offset, + dst + ((offset>>2)<<2), size - offset)) { + return 1; + } else { + return 0; + } +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_write + */ +uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size) +{ + if (dst) { + if (size & 0b11) { // not 4-byte aligned + size = size >> 2; + size = (size << 2) + 1; + } + vPortEnterCritical(); + Cache_Read_Disable(); + uint32_t result = spi_write(dest_addr, dst, size); + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + return result; + } else { + return 1; + } +} + +static uint32_t IRAM sector_erase(sdk_flashchip_t *chip, uint32_t addr) +{ + // a12 -> addr + // a0 = addr & 0xFFF + if (addr & 0xFFF) { + return 1; + } + + wait_idle(chip); + SPI(0).ADDR = addr & 0x00FFFFFF; + SPI(0).CMD = SPI_CMD_SE; + while (SPI(0).CMD) {}; + wait_idle(chip); + + return 0; +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_SPIEraseSector + */ +static uint32_t IRAM spi_erase_sector(uint32_t sector) +{ + if (sector >= (sdk_flashchip.chip_size / sdk_flashchip.sector_size)) { + return 1; + } + + if (write_enable(&sdk_flashchip)) { + return 1; + } + + if (sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sector)) { + return 1; + } + return 0; +} + +/** + * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_erase_sector + */ +uint32_t IRAM esp_spi_flash_erase(uint32_t sector) +{ + vPortEnterCritical(); + Cache_Read_Disable(); + + uint32_t result = spi_erase_sector(sector); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + + return result; +} diff --git a/extras/spiffs/esp_spi_flash.h b/extras/spiffs/esp_spi_flash.h new file mode 100644 index 0000000..6bf43e9 --- /dev/null +++ b/extras/spiffs/esp_spi_flash.h @@ -0,0 +1,11 @@ +#ifndef __ESP_SPI_FLASH_H__ +#define __ESP_SPI_FLASH_H__ + +#include +#include "common_macros.h" + +uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size); +uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size); +uint32_t IRAM esp_spi_flash_erase(uint32_t sector); + +#endif // __ESP_SPI_FLASH_H__ diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 9b095c2..a3c6b12 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -9,11 +9,9 @@ #include "spiffs.h" #include #include -#include "common_macros.h" -#include "FreeRTOS.h" -#include "esp/rom.h" #include #include +#include "esp_spi_flash.h" spiffs fs; @@ -34,143 +32,6 @@ static fs_buf_t cache_buf = {0}; #define ESP_SPIFFS_CACHE_PAGES 5 -// ROM functions -uint32_t SPI_read_data(sdk_flashchip_t *p, uint32_t dest_addr, void *src, - uint32_t size); -uint32_t SPI_page_program(sdk_flashchip_t *p, uint32_t dest_addr, void *dst, - uint32_t size); -uint32_t SPI_write_enable(sdk_flashchip_t *p); -uint32_t SPI_sector_erase(sdk_flashchip_t *p, uint32_t sector_addr); - - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIRead - */ -uint32_t IRAM spi_read(uint32_t dest_addr, void *src, uint32_t size) -{ - if (SPI_read_data(&sdk_flashchip, dest_addr, src, size)) { - return 1; - } else { - return 0; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_read - */ -uint32_t IRAM spi_flash_read(uint32_t dest_addr, void *src, uint32_t size) -{ - if (src) { - vPortEnterCritical(); - Cache_Read_Disable(); - uint32_t result = spi_read(dest_addr, src, size); - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - return result; - } else { - return 1; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIWrite - */ -uint32_t IRAM spi_write(uint32_t dest_addr, void *dst, uint32_t size) -{ - if (sdk_flashchip.chip_size < (dest_addr + size)) { - return 1; - } - - uint32_t write_bytes_to_page = sdk_flashchip.page_size - - (dest_addr % sdk_flashchip.page_size); - - if (size < write_bytes_to_page) { - if (SPI_page_program(&sdk_flashchip, dest_addr, dst, size)) { - return 1; - } else { - return 0; - } - } - - if (SPI_page_program(&sdk_flashchip, dest_addr, dst, write_bytes_to_page)) { - return 1; - } - - uint32_t offset = write_bytes_to_page; - uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; - for (uint8_t i = 0; i != pages_to_write; i++) { - if (SPI_page_program(&sdk_flashchip, dest_addr + offset, - dst + ((offset>>2)<<2), sdk_flashchip.page_size)) { - return 1; - } - offset += sdk_flashchip.page_size; - } - - if (SPI_page_program(&sdk_flashchip, dest_addr + offset, - dst + ((offset>>2)<<2), size - offset)) { - return 1; - } else { - return 0; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_write - */ -uint32_t IRAM spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size) -{ - if (dst) { - if (size & 0b11) { // not 4-byte aligned - size = size >> 2; - size = (size << 2) + 1; - } - vPortEnterCritical(); - Cache_Read_Disable(); - uint32_t result = spi_write(dest_addr, dst, size); - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - return result; - } else { - return 1; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIEraseSector - */ -uint32_t IRAM spi_erase_sector(uint32_t sector) -{ - if (sector >= (sdk_flashchip.chip_size / sdk_flashchip.sector_size)) { - return 1; - } - - if (SPI_write_enable(&sdk_flashchip)) { - return 1; - } - - if (SPI_sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sector)) { - return 1; - } - return 0; -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_erase_sector - */ -uint32_t IRAM spi_flash_erase_sector(uint32_t sector) -{ - vPortEnterCritical(); - Cache_Read_Disable(); - - uint32_t result = spi_erase_sector(sector); - - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - - return result; -} - - /* * Flash addresses and size alignment is a rip-off of Arduino implementation. */ @@ -187,7 +48,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) if (addr < alignedBegin) { uint32_t nb = alignedBegin - addr; uint32_t tmp; - if (spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (esp_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -195,7 +56,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) } if (alignedEnd != alignedBegin) { - if (spi_flash_read(alignedBegin, + if (esp_spi_flash_read(alignedBegin, (uint32_t*) (dst + alignedBegin - addr), alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); @@ -206,7 +67,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) if (addr + size > alignedEnd) { uint32_t nb = addr + size - alignedEnd; uint32_t tmp; - if (spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (esp_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_read failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -232,7 +93,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t nb = (size < ofs) ? size : ofs; uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; memcpy(tmp + 4 - ofs, src, nb); - if (spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) + if (esp_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -243,7 +104,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; if (!srcAlign) { - if (spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, + if (esp_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -255,7 +116,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); memcpy(buf, srcLeftover, willCopy); - if (spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) + if (esp_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; @@ -273,7 +134,7 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) uint32_t tmp = 0xffffffff; memcpy(&tmp, src + size - nb, nb); - if (spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { + if (esp_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { printf("spi_flash_write failed\n"); return SPIFFS_ERR_INTERNAL; } @@ -295,7 +156,7 @@ static s32_t esp_spiffs_erase(u32_t addr, u32_t size) const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; for (uint32_t i = 0; i < sectorCount; ++i) { - spi_flash_erase_sector(sector + i); + esp_spi_flash_erase(sector + i); } return SPIFFS_OK; } From 4b1568cbb90eaaa9fb138f5cac22ea60ed7e7136 Mon Sep 17 00:00:00 2001 From: sheinz Date: Mon, 18 Jul 2016 13:12:21 +0300 Subject: [PATCH 16/19] SPIFFS: flash access refactoring. --- extras/spiffs/esp_spi_flash.c | 336 ------------------------------- extras/spiffs/esp_spi_flash.h | 11 - extras/spiffs/esp_spiffs.c | 132 ++---------- extras/spiffs/esp_spiffs.h | 2 +- extras/spiffs/esp_spiffs_flash.c | 250 +++++++++++++++++++++++ extras/spiffs/esp_spiffs_flash.h | 64 ++++++ 6 files changed, 332 insertions(+), 463 deletions(-) delete mode 100644 extras/spiffs/esp_spi_flash.c delete mode 100644 extras/spiffs/esp_spi_flash.h create mode 100644 extras/spiffs/esp_spiffs_flash.c create mode 100644 extras/spiffs/esp_spiffs_flash.h diff --git a/extras/spiffs/esp_spi_flash.c b/extras/spiffs/esp_spi_flash.c deleted file mode 100644 index 74f8d98..0000000 --- a/extras/spiffs/esp_spi_flash.c +++ /dev/null @@ -1,336 +0,0 @@ -#include "esp_spi_flash.h" -#include "flashchip.h" -#include "espressif/spi_flash.h" -#include "FreeRTOS.h" -#include "esp/rom.h" -#include "esp/spi_regs.h" - - -static uint32_t IRAM read_status(sdk_flashchip_t *flashchip, uint32_t *status) -{ - uint32_t _status; - - do { - SPI(0).RSTATUS = 0; - SPI(0).CMD = SPI_CMD_READ_SR; - while (SPI(0).CMD) {} - _status = SPI(0).RSTATUS & flashchip->status_mask; - } while ( _status & 0b1); - - *status = _status; - - return 0; -} - -static uint32_t IRAM wait_idle(sdk_flashchip_t *flashchip) -{ - while (DPORT.SPI_READY & DPORT_SPI_READY_IDLE) {} - uint32_t a3; - return read_status(flashchip, &a3); -} - -static uint32_t IRAM write_enable(sdk_flashchip_t *flashchip) -{ - uint32_t local0 = 0; - - wait_idle(flashchip); - - SPI(0).CMD = SPI_CMD_WRITE_ENABLE; - while (SPI(0).CMD) {} - - if (!(local0 & 0b1)) { - do { - read_status(flashchip, &local0); - } while (!(local0 & (1<<1))); - } - return 0; -} - -static uint32_t IRAM page_program(sdk_flashchip_t *flashchip, uint32_t dest_addr, - uint32_t *buf, uint32_t size) -{ - if (size & 0b11) { - return 1; - } - - // check if block to write doesn't cross page boundary - if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) { - return 1; - } - wait_idle(flashchip); - if (size < 1) { - return 0; - } - - // a12 = 0x60000200 - // a0 = 0x00FFFFFF - // a6 = (dest_addr & 0x00FFFFFF) | 0x20000000 - while (size >= 32) { - SPI(0).ADDR = (dest_addr & 0x00FFFFFF) | 0x20000000; - // a4 - loop variable += 4 - // a5 = buf[0] - for (uint8_t i = 0; i != 8; i++) { - SPI(0).W[i] = buf[i]; - } - size -= 32; - dest_addr += 32; - buf += 8; - if (write_enable(flashchip)) { - return 1; - } - SPI(0).CMD = SPI_CMD_PP; - while (SPI(0).CMD) {} // wait for reg->cmd to be 0 - wait_idle(flashchip); - // a0 = 0x00FFFFFF - if (size < 1) { - return 0; - } - } - // a7 = 0x00FFFFFF & dest_addr - // a4 = size << 24; - // a4 = a7 | a4 - SPI(0).ADDR = (size << 24) | (0x00FFFFFF & dest_addr); - // a6 = 0b11 & size - // a3 = size >> 2; - // a5 = a3 + 1 - uint32_t words = size >> 2; - if (0b11 & size) { - words += 1; - } - words = words & 0xFF; - if (words != 0) { - // a4 = 0 - uint8_t i = 0; - - if (words & 0b1) { // bit 0 is set in a3 - SPI(0).W[0] = buf[0]; - i++; - } - // a6 = a3 >> 1; - if (words >> 1) { - // a6 = 0x600000200 - // buff[0] - for (; i != words; i++) { - SPI(0).W[i] = buf[i]; - } - } - } - - if (write_enable(flashchip)) { - return 1; - } - SPI(0).CMD = SPI_CMD_PP; - while (SPI(0).CMD) {} // wait for reg->cmd to be 0 - wait_idle(flashchip); - // a0 = 0x00FFFFFF - return 0; -} - -static uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, - uint32_t *dst, uint32_t size) -{ - // a12 = dst - if ((addr + size) > flashchip->chip_size) { - return 1; - } - - // a14 = addr - // a13 = size - wait_idle(flashchip); - if (size < 1) { - return 0; - } - // SPI(0).CMD - while (size >= 32) { - // a8 = addr | 0x20000000; - SPI(0).ADDR = addr | 0x20000000; - SPI(0).CMD = SPI_CMD_READ; - while (SPI(0).CMD) {}; - for (uint32_t a2 = 0; a2 < 8; a2++) { - *dst = SPI(0).W[a2]; - dst++; - } - size -= 32; - addr += 32; - } - - if (size >= 1) { - // a7 = size << 24; - // a7 = addr | a7 - SPI(0).ADDR = addr | (size << 24); - SPI(0).CMD = SPI_CMD_READ; - while (SPI(0).CMD) {}; - // a10 = size & 0b11 - uint8_t a7 = size >> 2; - // a9 = a7 + 1 - if (size & 0b11) { - // a7 = a7 + 1 - a7++; - } - // a7 = a7 & 0xFF - if (!a7) { - return 0; - } - uint8_t a2 = 0; - if (a7 & 0b1) { - a2 = 1; - // a11 = SPI(0).W0 - *dst = SPI(0).W[0]; - dst += 1; - } - size = a7 >> 1; - if (!size) { - return 0; - } - for (; a2 != a7; a2++) { - *dst = SPI(0).W[a2]; - dst += 1; - } - } - - return 0; -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIRead - */ -static uint32_t IRAM spi_read(uint32_t dest_addr, void *src, uint32_t size) -{ - if (read_data(&sdk_flashchip, dest_addr, (uint32_t*)src, size)) { - return 1; - } else { - return 0; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_read - */ -uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size) -{ - if (src) { - vPortEnterCritical(); - Cache_Read_Disable(); - uint32_t result = spi_read(dest_addr, src, size); - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - return result; - } else { - return 1; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIWrite - */ -static uint32_t IRAM spi_write(uint32_t dest_addr, void *dst, uint32_t size) -{ - if (sdk_flashchip.chip_size < (dest_addr + size)) { - return 1; - } - - uint32_t write_bytes_to_page = sdk_flashchip.page_size - - (dest_addr % sdk_flashchip.page_size); - - if (size < write_bytes_to_page) { - if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, size)) { - return 1; - } else { - return 0; - } - } - - if (page_program(&sdk_flashchip, dest_addr, (uint32_t*)dst, write_bytes_to_page)) { - return 1; - } - - uint32_t offset = write_bytes_to_page; - uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; - for (uint8_t i = 0; i != pages_to_write; i++) { - if (page_program(&sdk_flashchip, dest_addr + offset, - dst + ((offset>>2)<<2), sdk_flashchip.page_size)) { - return 1; - } - offset += sdk_flashchip.page_size; - } - - if (page_program(&sdk_flashchip, dest_addr + offset, - dst + ((offset>>2)<<2), size - offset)) { - return 1; - } else { - return 0; - } -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_write - */ -uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size) -{ - if (dst) { - if (size & 0b11) { // not 4-byte aligned - size = size >> 2; - size = (size << 2) + 1; - } - vPortEnterCritical(); - Cache_Read_Disable(); - uint32_t result = spi_write(dest_addr, dst, size); - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - return result; - } else { - return 1; - } -} - -static uint32_t IRAM sector_erase(sdk_flashchip_t *chip, uint32_t addr) -{ - // a12 -> addr - // a0 = addr & 0xFFF - if (addr & 0xFFF) { - return 1; - } - - wait_idle(chip); - SPI(0).ADDR = addr & 0x00FFFFFF; - SPI(0).CMD = SPI_CMD_SE; - while (SPI(0).CMD) {}; - wait_idle(chip); - - return 0; -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_SPIEraseSector - */ -static uint32_t IRAM spi_erase_sector(uint32_t sector) -{ - if (sector >= (sdk_flashchip.chip_size / sdk_flashchip.sector_size)) { - return 1; - } - - if (write_enable(&sdk_flashchip)) { - return 1; - } - - if (sector_erase(&sdk_flashchip, sdk_flashchip.sector_size * sector)) { - return 1; - } - return 0; -} - -/** - * Reverse engineered implementation of spi_flash.o:sdk_spi_flash_erase_sector - */ -uint32_t IRAM esp_spi_flash_erase(uint32_t sector) -{ - vPortEnterCritical(); - Cache_Read_Disable(); - - uint32_t result = spi_erase_sector(sector); - - Cache_Read_Enable(0, 0, 1); - vPortExitCritical(); - - return result; -} diff --git a/extras/spiffs/esp_spi_flash.h b/extras/spiffs/esp_spi_flash.h deleted file mode 100644 index 6bf43e9..0000000 --- a/extras/spiffs/esp_spi_flash.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __ESP_SPI_FLASH_H__ -#define __ESP_SPI_FLASH_H__ - -#include -#include "common_macros.h" - -uint32_t IRAM esp_spi_flash_read(uint32_t dest_addr, void *src, uint32_t size); -uint32_t IRAM esp_spi_flash_write(uint32_t dest_addr, void *dst, uint32_t size); -uint32_t IRAM esp_spi_flash_erase(uint32_t sector); - -#endif // __ESP_SPI_FLASH_H__ diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index a3c6b12..1574be2 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -11,7 +11,7 @@ #include #include #include -#include "esp_spi_flash.h" +#include "esp_spiffs_flash.h" spiffs fs; @@ -31,113 +31,19 @@ static fs_buf_t cache_buf = {0}; #define ESP_SPIFFS_CACHE_PAGES 5 - -/* - * Flash addresses and size alignment is a rip-off of Arduino implementation. - */ - static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { - uint32_t result = SPIFFS_OK; - uint32_t alignedBegin = (addr + 3) & (~3); - uint32_t alignedEnd = (addr + size) & (~3); - if (alignedEnd < alignedBegin) { - alignedEnd = alignedBegin; + if (esp_spiffs_flash_read(addr, dst, size) == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; } - if (addr < alignedBegin) { - uint32_t nb = alignedBegin - addr; - uint32_t tmp; - if (esp_spi_flash_read(alignedEnd - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) { - printf("spi_flash_read failed\n"); - return SPIFFS_ERR_INTERNAL; - } - memcpy(dst, &tmp + 4 - nb, nb); - } - - if (alignedEnd != alignedBegin) { - if (esp_spi_flash_read(alignedBegin, - (uint32_t*) (dst + alignedBegin - addr), - alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { - printf("spi_flash_read failed\n"); - return SPIFFS_ERR_INTERNAL; - } - } - - if (addr + size > alignedEnd) { - uint32_t nb = addr + size - alignedEnd; - uint32_t tmp; - if (esp_spi_flash_read(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { - printf("spi_flash_read failed\n"); - return SPIFFS_ERR_INTERNAL; - } - - memcpy(dst + size - nb, &tmp, nb); - } - - return result; + return SPIFFS_OK; } -static const int UNALIGNED_WRITE_BUFFER_SIZE = 512; - static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) { - uint32_t alignedBegin = (addr + 3) & (~3); - uint32_t alignedEnd = (addr + size) & (~3); - if (alignedEnd < alignedBegin) { - alignedEnd = alignedBegin; - } - - if (addr < alignedBegin) { - uint32_t ofs = alignedBegin - addr; - uint32_t nb = (size < ofs) ? size : ofs; - uint8_t tmp[4] __attribute__((aligned(4))) = {0xff, 0xff, 0xff, 0xff}; - memcpy(tmp + 4 - ofs, src, nb); - if (esp_spi_flash_write(alignedBegin - 4, (uint32_t*) tmp, 4) - != SPI_FLASH_RESULT_OK) { - printf("spi_flash_write failed\n"); - return SPIFFS_ERR_INTERNAL; - } - } - - if (alignedEnd != alignedBegin) { - uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr); - uint32_t srcAlign = ((uint32_t) srcLeftover) & 3; - if (!srcAlign) { - if (esp_spi_flash_write(alignedBegin, (uint32_t*) srcLeftover, - alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) { - printf("spi_flash_write failed\n"); - return SPIFFS_ERR_INTERNAL; - } - } - else { - uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE]; - for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) { - size_t willCopy = sizeLeft < sizeof(buf) ? sizeLeft : sizeof(buf); - memcpy(buf, srcLeftover, willCopy); - - if (esp_spi_flash_write(alignedBegin, (uint32_t*) buf, willCopy) - != SPI_FLASH_RESULT_OK) { - printf("spi_flash_write failed\n"); - return SPIFFS_ERR_INTERNAL; - } - - sizeLeft -= willCopy; - srcLeftover += willCopy; - alignedBegin += willCopy; - } - } - } - - if (addr + size > alignedEnd) { - uint32_t nb = addr + size - alignedEnd; - uint32_t tmp = 0xffffffff; - memcpy(&tmp, src + size - nb, nb); - - if (esp_spi_flash_write(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) { - printf("spi_flash_write failed\n"); - return SPIFFS_ERR_INTERNAL; - } + if (esp_spiffs_flash_write(addr, src, size) == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; } return SPIFFS_OK; @@ -145,19 +51,15 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) static s32_t esp_spiffs_erase(u32_t addr, u32_t size) { - if (addr % SPI_FLASH_SEC_SIZE) { - printf("Unaligned erase addr=%x\n", addr); - } - if (size % SPI_FLASH_SEC_SIZE) { - printf("Unaligned erase size=%d\n", size); + uint32_t sectors = size / SPI_FLASH_SEC_SIZE; + + for (uint32_t i = 0; i < sectors; i++) { + if (esp_spiffs_flash_erase_sector(addr + (SPI_FLASH_SEC_SIZE * i)) + == ESP_SPIFFS_FLASH_ERROR) { + return SPIFFS_ERR_INTERNAL; + } } - const uint32_t sector = addr / SPI_FLASH_SEC_SIZE; - const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE; - - for (uint32_t i = 0; i < sectorCount; ++i) { - esp_spi_flash_erase(sector + i); - } return SPIFFS_OK; } @@ -196,8 +98,8 @@ int32_t esp_spiffs_mount() printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", work_buf.size, fds_buf.size, cache_buf.size); - int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, - (uint8_t*)fds_buf.buf, fds_buf.size, + int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, + (uint8_t*)fds_buf.buf, fds_buf.size, cache_buf.buf, cache_buf.size, 0); if (err != SPIFFS_OK) { @@ -213,7 +115,7 @@ int32_t esp_spiffs_mount() long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { - long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), + long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), (char*)ptr, len); return ret; } @@ -296,6 +198,6 @@ int _stat_r(struct _reent *r, const char *pathname, void *buf) } off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence) -{ +{ return SPIFFS_lseek(&fs, (spiffs_file)(fd - FD_OFFSET), offset, whence); } diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h index f074017..60dc7f0 100644 --- a/extras/spiffs/esp_spiffs.h +++ b/extras/spiffs/esp_spiffs.h @@ -14,7 +14,7 @@ extern spiffs fs; /** * Prepare for SPIFFS mount. - * + * * The function allocates all the necessary buffers. */ void esp_spiffs_init(); diff --git a/extras/spiffs/esp_spiffs_flash.c b/extras/spiffs/esp_spiffs_flash.c new file mode 100644 index 0000000..1098551 --- /dev/null +++ b/extras/spiffs/esp_spiffs_flash.c @@ -0,0 +1,250 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * 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 "esp_spiffs_flash.h" +#include "flashchip.h" +#include "espressif/spi_flash.h" +#include "FreeRTOS.h" +#include "esp/rom.h" +#include "esp/spi_regs.h" + + +#define SPI_WRITE_MAX_SIZE 32 +#define SPI_READ_MAX_SIZE 32 + +/** + * Low level SPI flash write. Write block of data up to SPI_WRITE_MAX_SIZE. + */ +static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, + uint8_t *buf, uint32_t size) +{ + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + + uint32_t data = 0; + // Copy more than size, in order not to handle unaligned size. + // The exact size will be written to flash + for (uint32_t i = 0; i != SPI_WRITE_MAX_SIZE; i++) { data >>= 8; + data |= (uint32_t)buf[i] << 24; + + if (i & 0b11) { + SPI(0).W[i >> 2] = data; + } + } + + if (SPI_write_enable(chip)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + SPI(0).CMD = SPI_CMD_PP; + while (SPI(0).CMD) {} + Wait_SPI_Idle(chip); + + return ESP_SPIFFS_FLASH_OK; +} + +/** + * Write a page of flash. Data block should bot cross page boundary. + */ +static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr, + uint8_t *buf, uint32_t size) +{ + // check if block to write doesn't cross page boundary + if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + + Wait_SPI_Idle(flashchip); + + while (size >= SPI_WRITE_MAX_SIZE) { + if (spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + size -= SPI_WRITE_MAX_SIZE; + dest_addr += SPI_WRITE_MAX_SIZE; + buf += SPI_WRITE_MAX_SIZE; + + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + } + + if (spi_write_data(flashchip, dest_addr, buf, size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + return ESP_SPIFFS_FLASH_OK; +} + +/** + * Split block of data into pages and write pages. + */ +static uint32_t IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size) +{ + if (sdk_flashchip.chip_size < (addr + size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + uint32_t write_bytes_to_page = sdk_flashchip.page_size - + (addr % sdk_flashchip.page_size); // TODO: place for optimization + + if (size < write_bytes_to_page) { + if (spi_write_page(&sdk_flashchip, addr, dst, size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + return ESP_SPIFFS_FLASH_OK; + } + + if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + uint32_t offset = write_bytes_to_page; + uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; + for (uint8_t i = 0; i != pages_to_write; i++) { + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, sdk_flashchip.page_size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + offset += sdk_flashchip.page_size; + } + + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, size - offset)) { + return ESP_SPIFFS_FLASH_ERROR; + } + return ESP_SPIFFS_FLASH_OK; +} + +uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) +{ + uint32_t result = ESP_SPIFFS_FLASH_ERROR; + + if (buf) { + vPortEnterCritical(); + Cache_Read_Disable(); + + result = spi_write(addr, buf, size); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + } + + return result; +} + +/** + * Read SPI flash up to SPI_READ_MAX_SIZE size. + */ +static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr, + uint8_t *buf, uint32_t size) +{ + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + SPI(0).CMD = SPI_CMD_READ; + while (SPI(0).CMD) {}; + uint32_t data = 0; + for (uint32_t i = 0; i < size; i++) { + if (!(i & 0b11)) { + data = SPI(0).W[i>>2]; + } + buf[i] = 0xFF & data; + data >>= 8; + } +} + +/** + * Read SPI flash data. Data region doesn't need to be page aligned. + */ +static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, + uint8_t *dst, uint32_t size) +{ + if (size < 1) { + return ESP_SPIFFS_FLASH_OK; + } + + if ((addr + size) > flashchip->chip_size) { + return ESP_SPIFFS_FLASH_ERROR; + } + + Wait_SPI_Idle(flashchip); + + while (size >= SPI_READ_MAX_SIZE) { + read_block(flashchip, addr, dst, SPI_READ_MAX_SIZE); + dst += SPI_READ_MAX_SIZE; + size -= SPI_READ_MAX_SIZE; + addr += SPI_READ_MAX_SIZE; + } + + if (size > 0) { + read_block(flashchip, addr, dst, size); + } + + return ESP_SPIFFS_FLASH_OK; +} + +uint32_t IRAM esp_spiffs_flash_read(uint32_t dest_addr, uint8_t *buf, uint32_t size) +{ + uint32_t result = ESP_SPIFFS_FLASH_ERROR; + + if (buf) { + vPortEnterCritical(); + Cache_Read_Disable(); + + result = read_data(&sdk_flashchip, dest_addr, buf, size); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + } + + return result; +} + +uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr) +{ + if ((addr + sdk_flashchip.sector_size) > sdk_flashchip.chip_size) { + return ESP_SPIFFS_FLASH_ERROR; + } + + if (addr & 0xFFF) { + return ESP_SPIFFS_FLASH_ERROR; + } + + vPortEnterCritical(); + Cache_Read_Disable(); + + SPI_write_enable(&sdk_flashchip); + + Wait_SPI_Idle(&sdk_flashchip); + SPI(0).ADDR = addr & 0x00FFFFFF; + SPI(0).CMD = SPI_CMD_SE; + while (SPI(0).CMD) {}; + Wait_SPI_Idle(&sdk_flashchip); + + Cache_Read_Enable(0, 0, 1); + vPortExitCritical(); + + return ESP_SPIFFS_FLASH_OK; +} diff --git a/extras/spiffs/esp_spiffs_flash.h b/extras/spiffs/esp_spiffs_flash.h new file mode 100644 index 0000000..abfe864 --- /dev/null +++ b/extras/spiffs/esp_spiffs_flash.h @@ -0,0 +1,64 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __ESP_SPIFFS_FLASH_H__ +#define __ESP_SPIFFS_FLASH_H__ + +#include +#include "common_macros.h" + +#define ESP_SPIFFS_FLASH_OK 0 +#define ESP_SPIFFS_FLASH_ERROR 1 + +/** + * Read data from SPI flash. + * + * @param addr Address to read from. Can be not aligned. + * @param buf Buffer to read to. Doesn't have to be aligned. + * @param size Size of data to read. Buffer size must be >= than data size. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_read(uint32_t addr, uint8_t *buf, uint32_t size); + +/** + * Write data to SPI flash. + * + * @param addr Address to write to. Can be not aligned. + * @param buf Buffer of data to write to flash. Doesn't have to be aligned. + * @param size Size of data to write. Buffer size must be >= than data size. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size); + +/** + * Erase a sector. + * + * @param addr Address of sector to erase. Must be sector aligned. + * + * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR + */ +uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr); + +#endif // __ESP_SPIFFS_FLASH_H__ From 281faa2cacac65b12a7732b0cc9ea5ac556d7ee6 Mon Sep 17 00:00:00 2001 From: sheinz Date: Tue, 19 Jul 2016 17:38:21 +0300 Subject: [PATCH 17/19] SPIFFS: Wait SPI idle optimization. --- extras/spiffs/esp_spiffs_flash.c | 79 ++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/extras/spiffs/esp_spiffs_flash.c b/extras/spiffs/esp_spiffs_flash.c index 1098551..fafd167 100644 --- a/extras/spiffs/esp_spiffs_flash.c +++ b/extras/spiffs/esp_spiffs_flash.c @@ -27,25 +27,45 @@ #include "FreeRTOS.h" #include "esp/rom.h" #include "esp/spi_regs.h" +#include +/** + * Note about Wait_SPI_Idle. + * + * Each write/erase flash operation sets BUSY bit in flash status register. + * If attempt to access flash while BUSY bit is set operation will fail. + * Function Wait_SPI_Idle loops until this bit is not cleared. + * + * The approach in the following code is that each write function that is + * accessible from the outside should leave flash in Idle state. + * The read operations doesn't set BUSY bit in a flash. So they do not wait. + * They relay that previous operation is completely finished. + * + * This approach is different from ESP8266 bootrom where Wait_SPI_Idle is + * called where it needed and not. + */ #define SPI_WRITE_MAX_SIZE 32 #define SPI_READ_MAX_SIZE 32 /** - * Low level SPI flash write. Write block of data up to SPI_WRITE_MAX_SIZE. + * Low level SPI flash write. Write block of data up to 64 bytes. */ static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, uint8_t *buf, uint32_t size) { + Wait_SPI_Idle(chip); // wait for previous write to finish + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + uint32_t words = size >> 2; + if (size & 0b11) { + words++; + } uint32_t data = 0; - // Copy more than size, in order not to handle unaligned size. - // The exact size will be written to flash - for (uint32_t i = 0; i != SPI_WRITE_MAX_SIZE; i++) { data >>= 8; + for (uint32_t i = 0; i < (words << 2); i++) { + data >>= 8; data |= (uint32_t)buf[i] << 24; - if (i & 0b11) { SPI(0).W[i >> 2] = data; } @@ -57,13 +77,12 @@ static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, SPI(0).CMD = SPI_CMD_PP; while (SPI(0).CMD) {} - Wait_SPI_Idle(chip); return ESP_SPIFFS_FLASH_OK; } /** - * Write a page of flash. Data block should bot cross page boundary. + * Write a page of flash. Data block should not cross page boundary. */ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr, uint8_t *buf, uint32_t size) @@ -77,8 +96,6 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad return ESP_SPIFFS_FLASH_OK; } - Wait_SPI_Idle(flashchip); - while (size >= SPI_WRITE_MAX_SIZE) { if (spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE)) { return ESP_SPIFFS_FLASH_ERROR; @@ -96,6 +113,7 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad if (spi_write_data(flashchip, dest_addr, buf, size)) { return ESP_SPIFFS_FLASH_ERROR; } + return ESP_SPIFFS_FLASH_OK; } @@ -115,27 +133,27 @@ static uint32_t IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size) if (spi_write_page(&sdk_flashchip, addr, dst, size)) { return ESP_SPIFFS_FLASH_ERROR; } - return ESP_SPIFFS_FLASH_OK; - } - - if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { - return ESP_SPIFFS_FLASH_ERROR; - } - - uint32_t offset = write_bytes_to_page; - uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; - for (uint8_t i = 0; i != pages_to_write; i++) { - if (spi_write_page(&sdk_flashchip, addr + offset, - dst + offset, sdk_flashchip.page_size)) { + } else { + if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { + return ESP_SPIFFS_FLASH_ERROR; + } + + uint32_t offset = write_bytes_to_page; + uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; + for (uint8_t i = 0; i != pages_to_write; i++) { + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, sdk_flashchip.page_size)) { + return ESP_SPIFFS_FLASH_ERROR; + } + offset += sdk_flashchip.page_size; + } + + if (spi_write_page(&sdk_flashchip, addr + offset, + dst + offset, size - offset)) { return ESP_SPIFFS_FLASH_ERROR; } - offset += sdk_flashchip.page_size; } - if (spi_write_page(&sdk_flashchip, addr + offset, - dst + offset, size - offset)) { - return ESP_SPIFFS_FLASH_ERROR; - } return ESP_SPIFFS_FLASH_OK; } @@ -149,6 +167,9 @@ uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) result = spi_write(addr, buf, size); + // make sure all write operations is finished before exiting + Wait_SPI_Idle(&sdk_flashchip); + Cache_Read_Enable(0, 0, 1); vPortExitCritical(); } @@ -157,7 +178,7 @@ uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) } /** - * Read SPI flash up to SPI_READ_MAX_SIZE size. + * Read SPI flash up to 64 bytes. */ static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr, uint8_t *buf, uint32_t size) @@ -189,8 +210,6 @@ static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, return ESP_SPIFFS_FLASH_ERROR; } - Wait_SPI_Idle(flashchip); - while (size >= SPI_READ_MAX_SIZE) { read_block(flashchip, addr, dst, SPI_READ_MAX_SIZE); dst += SPI_READ_MAX_SIZE; @@ -237,10 +256,10 @@ uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr) SPI_write_enable(&sdk_flashchip); - Wait_SPI_Idle(&sdk_flashchip); SPI(0).ADDR = addr & 0x00FFFFFF; SPI(0).CMD = SPI_CMD_SE; while (SPI(0).CMD) {}; + Wait_SPI_Idle(&sdk_flashchip); Cache_Read_Enable(0, 0, 1); From 38cccbd456182c6d30fad2e031fc1aeb8293fa75 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 21 Jul 2016 16:36:55 +0300 Subject: [PATCH 18/19] SPIFFS: Optimized SPI data read/write. Unaligned read/write from/to SPI data registers is rewritten in assembler to improve performance. --- extras/spiffs/esp_spiffs_flash.c | 72 ++++++++++---------- extras/spiffs/unaligned_memcpy.S | 112 +++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 extras/spiffs/unaligned_memcpy.S diff --git a/extras/spiffs/esp_spiffs_flash.c b/extras/spiffs/esp_spiffs_flash.c index fafd167..f0a6049 100644 --- a/extras/spiffs/esp_spiffs_flash.c +++ b/extras/spiffs/esp_spiffs_flash.c @@ -45,40 +45,53 @@ * called where it needed and not. */ -#define SPI_WRITE_MAX_SIZE 32 -#define SPI_READ_MAX_SIZE 32 +#define SPI_WRITE_MAX_SIZE 64 + +// 64 bytes read causes hang +// http://bbs.espressif.com/viewtopic.php?f=6&t=2439 +#define SPI_READ_MAX_SIZE 60 + + +/** + * Copy unaligned data to 4-byte aligned destination buffer. + * + * @param words Number of 4-byte words to write. + * + * @see unaligned_memcpy.S + */ +void memcpy_unaligned_src(volatile uint32_t *dst, uint8_t *src, uint8_t words); + +/** + * Copy 4-byte aligned source data to unaligned destination buffer. + * + * @param bytes Number of byte to copy to dst. + * + * @see unaligned_memcpy.S + */ +void memcpy_unaligned_dst(uint8_t *dst, volatile uint32_t *src, uint8_t bytes); + /** * Low level SPI flash write. Write block of data up to 64 bytes. */ -static inline uint32_t IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, +static inline void IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, uint8_t *buf, uint32_t size) { - Wait_SPI_Idle(chip); // wait for previous write to finish - - SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); - uint32_t words = size >> 2; if (size & 0b11) { words++; } - uint32_t data = 0; - for (uint32_t i = 0; i < (words << 2); i++) { - data >>= 8; - data |= (uint32_t)buf[i] << 24; - if (i & 0b11) { - SPI(0).W[i >> 2] = data; - } - } - if (SPI_write_enable(chip)) { - return ESP_SPIFFS_FLASH_ERROR; - } + Wait_SPI_Idle(chip); // wait for previous write to finish + + SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); + + memcpy_unaligned_src(SPI(0).W, buf, words); + + SPI_write_enable(chip); SPI(0).CMD = SPI_CMD_PP; while (SPI(0).CMD) {} - - return ESP_SPIFFS_FLASH_OK; } /** @@ -97,9 +110,7 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad } while (size >= SPI_WRITE_MAX_SIZE) { - if (spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE)) { - return ESP_SPIFFS_FLASH_ERROR; - } + spi_write_data(flashchip, dest_addr, buf, SPI_WRITE_MAX_SIZE); size -= SPI_WRITE_MAX_SIZE; dest_addr += SPI_WRITE_MAX_SIZE; @@ -110,9 +121,7 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad } } - if (spi_write_data(flashchip, dest_addr, buf, size)) { - return ESP_SPIFFS_FLASH_ERROR; - } + spi_write_data(flashchip, dest_addr, buf, size); return ESP_SPIFFS_FLASH_OK; } @@ -185,15 +194,10 @@ static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr, { SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); SPI(0).CMD = SPI_CMD_READ; + while (SPI(0).CMD) {}; - uint32_t data = 0; - for (uint32_t i = 0; i < size; i++) { - if (!(i & 0b11)) { - data = SPI(0).W[i>>2]; - } - buf[i] = 0xFF & data; - data >>= 8; - } + + memcpy_unaligned_dst(buf, SPI(0).W, size); } /** diff --git a/extras/spiffs/unaligned_memcpy.S b/extras/spiffs/unaligned_memcpy.S new file mode 100644 index 0000000..b96c92c --- /dev/null +++ b/extras/spiffs/unaligned_memcpy.S @@ -0,0 +1,112 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * 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. + */ + + .text + .section .iram1.text, "x" + .literal_position + +/** + * Copy unaligned data to 4-byte aligned buffer. + */ + .align 4 + .global memcpy_unaligned_src + .type memcpy_unaligned_src, @function +memcpy_unaligned_src: +/* a2: dst, a3: src, a4: size */ + ssa8l a3 + srli a3, a3, 2 + slli a3, a3, 2 + beqz a4, u_src_end + l32i a6, a3, 0 +u_src_loop: + l32i a7, a3, 4 + src a8, a7, a6 + memw + s32i a8, a2, 0 + mov a6, a7 + addi a3, a3, 4 + addi a2, a2, 4 + addi a4, a4, -1 + bnez a4, u_src_loop +u_src_end: + movi a2, 0 + ret.n + + +/** + * Copy data from 4-byte aligned source to unaligned destination buffer. + */ + .align 4 + .global memcpy_unaligned_dst + .type memcpy_unaligned_dst, @function +memcpy_unaligned_dst: +/* a2: dst, a3: src, a4: size */ + beqz.n a4, u_dst_end + extui a5, a4, 0, 2 + beqz.n a5, aligned_dst_loop +u_dst_loop: + /* Load data word */ + memw + l32i.n a5, a3, 0 + + /* Save byte number 0 */ + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 1 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 2 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + beqz a4, u_dst_end + addi.n a2, a2, 1 + + /* Shift and save byte number 3 */ + srli a5, a5, 8 + s8i a5, a2, 0 + addi.n a4, a4, -1 + addi.n a2, a2, 1 + + /* Next word */ + addi.n a3, a3, 4 + bnez.n a4, u_dst_loop + ret.n +aligned_dst_loop: + memw + l32i a5, a3, 0 + s32i a5, a2, 0 + addi.n a3, a3, 4 + addi.n a2, a2, 4 + addi.n a4, a4, -4 + bnez.n a4, aligned_dst_loop +u_dst_end: ret.n + From d69b8390d43e851c84d294f585d5534ef54402b4 Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 22 Jul 2016 14:09:50 +0300 Subject: [PATCH 19/19] SPIFFS: Fix PR review comments. * Enable SPIFFS_USE_MAGIC * Enable SPIFFS_USE_MAGIC_LENGTH * Enable SPIFFS_FILEHDL_OFFSET * Rebuild mkspiffs if spiffs_config.h is changed * Emulate NOR flash in mkspiffs * Build spiffs image in 'flash' and 'test' targets --- common.mk | 4 ++-- extras/spiffs/component.mk | 8 ++++++++ extras/spiffs/esp_spiffs.c | 25 +++++++++-------------- extras/spiffs/mkspiffs/Makefile | 4 ++++ extras/spiffs/mkspiffs/mkspiffs.c | 34 +++++++++++++++---------------- extras/spiffs/spiffs_config.h | 8 ++++---- 6 files changed, 44 insertions(+), 39 deletions(-) diff --git a/common.mk b/common.mk index f4bfe06..c3923c8 100644 --- a/common.mk +++ b/common.mk @@ -68,7 +68,7 @@ Q := @ vecho := @echo endif -.PHONY: all clean flash erase_flash +.PHONY: all clean flash erase_flash test size rebuild all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE) @@ -209,7 +209,7 @@ $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR) $(vecho) "FW $@" $(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE) -flash: $(FW_FILE) +flash: all $(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) \ 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE) $(SPIFFS_ESPTOOL_ARGS) diff --git a/extras/spiffs/component.mk b/extras/spiffs/component.mk index a0a8928..f7f5a49 100644 --- a/extras/spiffs/component.mk +++ b/extras/spiffs/component.mk @@ -40,6 +40,9 @@ $$(SPIFFS_IMAGE): $$(MKSPIFFS) $$(SPIFFS_FILE_LIST) $$(spiffs_ROOT)spiffs_config.h: Makefile $$(Q) touch $$@ +$$(MKSPIFFS)_MAKE: + $$(MAKE) -C $$(MKSPIFFS_DIR) SPIFFS_SIZE=$(SPIFFS_SIZE) + # if SPIFFS_SIZE in Makefile is changed rebuild mkspiffs $$(MKSPIFFS): Makefile $$(MAKE) -C $$(MKSPIFFS_DIR) clean @@ -51,6 +54,11 @@ clean_spiffs_img: clean_mkspiffs: $$(Q) $$(MAKE) -C $$(MKSPIFFS_DIR) clean +# run make for mkspiffs always +all: $$(MKSPIFFS)_MAKE + +.PHONY: $$(MKSPIFFS)_MAKE + SPIFFS_ESPTOOL_ARGS = $(SPIFFS_BASE_ADDR) $$(SPIFFS_IMAGE) endef diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 1574be2..9ae4dea 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -94,6 +94,8 @@ int32_t esp_spiffs_mount() config.hal_write_f = esp_spiffs_write; config.hal_erase_f = esp_spiffs_erase; + config.fh_ix_offset = 3; + printf("SPIFFS size: %d\n", SPIFFS_SIZE); printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", work_buf.size, fds_buf.size, cache_buf.size); @@ -109,15 +111,11 @@ int32_t esp_spiffs_mount() return err; } -#define FD_OFFSET 3 - // This implementation replaces implementation in core/newlib_syscals.c long _write_r(struct _reent *r, int fd, const char *ptr, int len ) { if(fd != r->_stdout->_file) { - long ret = SPIFFS_write(&fs, (spiffs_file)(fd - FD_OFFSET), - (char*)ptr, len); - return ret; + return SPIFFS_write(&fs, (spiffs_file)fd, (char*)ptr, len); } for(int i = 0; i < len; i++) { /* Auto convert CR to CRLF, ignore other LFs (compatible with Espressif SDK behaviour) */ @@ -136,8 +134,7 @@ long _read_r( struct _reent *r, int fd, char *ptr, int len ) int ch, i; if(fd != r->_stdin->_file) { - long ret = SPIFFS_read(&fs, (spiffs_file)(fd - FD_OFFSET), ptr, len); - return ret; + return SPIFFS_read(&fs, (spiffs_file)fd, ptr, len); } uart_rxfifo_wait(0, 1); for(i = 0; i < len; i++) { @@ -157,17 +154,15 @@ int _open_r(struct _reent *r, const char *pathname, int flags, int mode) if (flags & O_TRUNC) spiffs_flags |= SPIFFS_TRUNC; if (flags & O_RDONLY) spiffs_flags |= SPIFFS_RDONLY; if (flags & O_WRONLY) spiffs_flags |= SPIFFS_WRONLY; + if (flags & O_EXCL) spiffs_flags |= SPIFFS_EXCL; + /* if (flags & O_DIRECT) spiffs_flags |= SPIFFS_DIRECT; no support in newlib */ - int ret = SPIFFS_open(&fs, pathname, spiffs_flags, mode); - if (ret > 0) { - return ret + FD_OFFSET; - } - return ret; + return SPIFFS_open(&fs, pathname, spiffs_flags, mode); } int _close_r(struct _reent *r, int fd) { - return SPIFFS_close(&fs, (spiffs_file)(fd - FD_OFFSET)); + return SPIFFS_close(&fs, (spiffs_file)fd); } int _unlink_r(struct _reent *r, const char *path) @@ -180,7 +175,7 @@ int _fstat_r(struct _reent *r, int fd, void *buf) spiffs_stat s; struct stat *sb = (struct stat*)buf; - int result = SPIFFS_fstat(&fs, (spiffs_file)(fd - FD_OFFSET), &s); + int result = SPIFFS_fstat(&fs, (spiffs_file)fd, &s); sb->st_size = s.size; return result; @@ -199,5 +194,5 @@ int _stat_r(struct _reent *r, const char *pathname, void *buf) off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence) { - return SPIFFS_lseek(&fs, (spiffs_file)(fd - FD_OFFSET), offset, whence); + return SPIFFS_lseek(&fs, (spiffs_file)fd, offset, whence); } diff --git a/extras/spiffs/mkspiffs/Makefile b/extras/spiffs/mkspiffs/Makefile index a9a9722..0b8b49e 100644 --- a/extras/spiffs/mkspiffs/Makefile +++ b/extras/spiffs/mkspiffs/Makefile @@ -32,6 +32,10 @@ CFLAGS += -DSPIFFS_SIZE=$(SPIFFS_SIZE) all: mkspiffs +$(OBJECTS): $(SOURCES) + +$(OBJECTS): ../spiffs_config.h + mkspiffs: $(OBJECTS) clean: diff --git a/extras/spiffs/mkspiffs/mkspiffs.c b/extras/spiffs/mkspiffs/mkspiffs.c index 65b90e0..a1a0513 100644 --- a/extras/spiffs/mkspiffs/mkspiffs.c +++ b/extras/spiffs/mkspiffs/mkspiffs.c @@ -59,7 +59,12 @@ static s32_t _read_data(u32_t addr, u32_t size, u8_t *dst) static s32_t _write_data(u32_t addr, u32_t size, u8_t *src) { - memcpy((uint8_t*)image + addr, src, size); + uint32_t i; + uint8_t *dst = image + addr; + + for (i = 0; i < size; i++) { + dst[i] &= src[i]; // mimic NOR flash, flip only 1 to 0 + } return SPIFFS_OK; } @@ -94,12 +99,7 @@ static bool init_spiffs(bool allocate_mem) int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, cache_buf, cacheBufSize, 0); - if (err != SPIFFS_OK) { - printf("Error spiffs mount: %d\n", err); - return false; - } - - return true; + return err == SPIFFS_OK; } static bool format_spiffs() @@ -222,20 +222,18 @@ int main(int argc, char *argv[]) return -1; } - if (init_spiffs(/*allocate_mem=*/true)) { - if (format_spiffs()) { - if (process_directory(argv[1])) { - if (!write_image(argv[2])) { - printf("Error writing image\n"); - } - } else { - printf("Error processing direcotry\n"); - } + init_spiffs(/*allocate_mem=*/true); + + if (format_spiffs()) { + if (process_directory(argv[1])) { + if (!write_image(argv[2])) { + printf("Error writing image\n"); + } } else { - printf("Error formating spiffs\n"); + printf("Error processing direcotry\n"); } } else { - printf("Error initialising SPIFFS\n"); + printf("Error formating spiffs\n"); } spiffs_free(); diff --git a/extras/spiffs/spiffs_config.h b/extras/spiffs/spiffs_config.h index 7b8c1a7..b782d48 100644 --- a/extras/spiffs/spiffs_config.h +++ b/extras/spiffs/spiffs_config.h @@ -125,7 +125,7 @@ typedef unsigned char u8_t; // not on mount point. If not, SPIFFS_format must be called prior to mounting // again. #ifndef SPIFFS_USE_MAGIC -#define SPIFFS_USE_MAGIC (0) +#define SPIFFS_USE_MAGIC (1) #endif #if SPIFFS_USE_MAGIC @@ -135,7 +135,7 @@ typedef unsigned char u8_t; // be accepted for mounting with a configuration defining the filesystem as 2 // megabytes. #ifndef SPIFFS_USE_MAGIC_LENGTH -#define SPIFFS_USE_MAGIC_LENGTH (0) +#define SPIFFS_USE_MAGIC_LENGTH (1) #endif #endif @@ -174,7 +174,7 @@ typedef unsigned char u8_t; #define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) #endif #ifndef SPIFFS_CFG_LOG_BLOCK_SZ -#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4*1024) +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (8*1024) #endif #endif @@ -195,7 +195,7 @@ typedef unsigned char u8_t; // NB: This adds config field fh_ix_offset in the configuration struct when // mounting, which must be defined. #ifndef SPIFFS_FILEHDL_OFFSET -#define SPIFFS_FILEHDL_OFFSET 0 +#define SPIFFS_FILEHDL_OFFSET 1 #endif // Enable this to compile a read only version of spiffs.