From 4b48311f0deb45ec8dbae7673468ae931e2636e3 Mon Sep 17 00:00:00 2001 From: Hiroyuki OYAMA Date: Tue, 18 Jun 2024 18:26:54 +0900 Subject: [PATCH] Flash exclusive control changed to pico_flash --- CMakeLists.txt | 2 + examples/benchmark/main.c | 4 +- src/blockdevice/flash.c | 71 ++++++-- src/filesystem/vfs.c | 17 ++ tests/multicore/CMakeLists.txt | 9 +- tests/multicore/fs_init.c | 39 +++++ tests/multicore/main.c | 289 +++++---------------------------- 7 files changed, 161 insertions(+), 270 deletions(-) create mode 100644 tests/multicore/fs_init.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fd67c5..02503ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,9 +33,11 @@ target_sources(blockdevice_flash INTERFACE target_compile_options(blockdevice_flash INTERFACE -Werror -Wall -Wextra -Wnull-dereference) target_link_libraries(blockdevice_flash INTERFACE blockdevice + hardware_exception hardware_flash hardware_sync pico_sync + pico_flash ) # Heap memory blockdevice library diff --git a/examples/benchmark/main.c b/examples/benchmark/main.c index 56e46e6..1bd00ff 100644 --- a/examples/benchmark/main.c +++ b/examples/benchmark/main.c @@ -76,7 +76,7 @@ static uint32_t xor_rand_32bit(uint32_t *seed) { static void benchmark_write(void) { const char *label = "Write"; - uint64_t start_at = get_absolute_time(); + absolute_time_t start_at = get_absolute_time(); int fd = open("/benchmark", O_WRONLY|O_CREAT); if (fd == -1) { @@ -116,7 +116,7 @@ static void benchmark_write(void) { static void benchmark_read(void) { const char *label = "Read"; - uint64_t start_at = get_absolute_time(); + absolute_time_t start_at = get_absolute_time(); int fd = open("/benchmark", O_RDONLY); if (fd == -1) { diff --git a/src/blockdevice/flash.c b/src/blockdevice/flash.c index 2b2aa91..9ac2b99 100644 --- a/src/blockdevice/flash.c +++ b/src/blockdevice/flash.c @@ -7,19 +7,48 @@ #include #include #include +#include #include #include #include #include "blockdevice/flash.h" +#define FLASH_SAFE_EXECUTE_TIMEOUT 10 * 1000 + +#define FLASH_BLOCK_DEVICE_ERROR_TIMEOUT -4001 /*!< operation timeout */ +#define FLASH_BLOCK_DEVICE_ERROR_NOT_PERMITTED -4002 /*!< safe execution is not possible */ +#define FLASH_BLOCK_DEVICE_ERROR_INSUFFICIENT_RESOURCES -4003 /*!< method fails due to dynamic resource exhaustion */ + typedef struct { uint32_t start; size_t length; mutex_t _mutex; } blockdevice_flash_config_t; +typedef struct { + bool is_erase; + size_t addr; + size_t size; + void *buffer; +} _safe_flash_update_param_t; + static const char DEVICE_NAME[] = "flash"; +static int _error_remap(int err) { + switch (err) { + case PICO_OK: + return BD_ERROR_OK; + case PICO_ERROR_TIMEOUT: + return FLASH_BLOCK_DEVICE_ERROR_TIMEOUT; + case PICO_ERROR_NOT_PERMITTED: + return FLASH_BLOCK_DEVICE_ERROR_NOT_PERMITTED; + case PICO_ERROR_INSUFFICIENT_RESOURCES: + return FLASH_BLOCK_DEVICE_ERROR_INSUFFICIENT_RESOURCES; + default: + return err; + } +} + static size_t flash_target_offset(blockdevice_t *device) { blockdevice_flash_config_t *config = device->config; return config->start; @@ -52,28 +81,34 @@ static int read(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_si return BD_ERROR_OK; } -static int erase(blockdevice_t *device, bd_size_t addr, bd_size_t size) { - blockdevice_flash_config_t *config = device->config; - - mutex_enter_blocking(&config->_mutex); - uint32_t ints = save_and_disable_interrupts(); - flash_range_erase(flash_target_offset(device) + addr, (size_t)size); - restore_interrupts(ints); - mutex_exit(&config->_mutex); +static void _safe_flash_update(void *param) { + const _safe_flash_update_param_t *args = param; + if (args->is_erase) { + flash_range_erase(args->addr, args->size); + } else { + flash_range_program(args->addr, (const uint8_t *)args->buffer, args->size); + } +} - return BD_ERROR_OK; +static int erase(blockdevice_t *device, bd_size_t addr, bd_size_t size) { + _safe_flash_update_param_t param = { + .is_erase = true, + .addr = flash_target_offset(device) + addr, + .size = (size_t)size, + }; + int err = flash_safe_execute(_safe_flash_update, ¶m, FLASH_SAFE_EXECUTE_TIMEOUT); + return _error_remap(err); } static int program(blockdevice_t *device, const void *buffer, bd_size_t addr, bd_size_t size) { - blockdevice_flash_config_t *config = device->config; - - mutex_enter_blocking(&config->_mutex); - uint32_t ints = save_and_disable_interrupts(); - flash_range_program(flash_target_offset(device) + addr, buffer, (size_t)size); - restore_interrupts(ints); - mutex_exit(&config->_mutex); - - return BD_ERROR_OK; + _safe_flash_update_param_t param = { + .is_erase = false, + .addr = flash_target_offset(device) + addr, + .buffer = (void *)buffer, + .size = (size_t)size, + }; + int err = flash_safe_execute(_safe_flash_update, ¶m, FLASH_SAFE_EXECUTE_TIMEOUT); + return _error_remap(err); } static int trim(blockdevice_t *device, bd_size_t addr, bd_size_t size) { diff --git a/src/filesystem/vfs.c b/src/filesystem/vfs.c index e900290..ebf4a67 100644 --- a/src/filesystem/vfs.c +++ b/src/filesystem/vfs.c @@ -737,6 +737,23 @@ char *fs_strerror(int errnum) { break; } return (char *)str; + } else if (errnum > 4000) { + // On-board flash blockdevice error + const char *str = ""; + switch (errnum) { + case 4001: + str = "operation timeout"; + break; + case 4002: + str = "safe execution is not possible"; + break; + case 4003: + str = "method fails due to dynamic resource exhaustion"; + break; + default: + break; + } + return (char *)str; } else { return strerror(errnum); } diff --git a/tests/multicore/CMakeLists.txt b/tests/multicore/CMakeLists.txt index 0e5db57..e246af3 100644 --- a/tests/multicore/CMakeLists.txt +++ b/tests/multicore/CMakeLists.txt @@ -1,6 +1,10 @@ add_executable(multicore main.c) -target_compile_options(multicore PRIVATE -Werror -Wall -Wextra -Wnull-dereference) +target_compile_options(multicore PRIVATE + -DPICO_FLASH_ASSUME_CORE1_SAFE=1 + -Werror -Wall -Wextra -Wnull-dereference +) target_link_libraries(multicore PRIVATE + pico_flash pico_stdlib pico_multicore blockdevice_flash @@ -12,11 +16,10 @@ target_link_libraries(multicore PRIVATE ) target_link_options(multicore PRIVATE -Wl,--print-memory-usage) pico_add_extra_outputs(multicore) -pico_enable_stdio_uart(multicore 1) pico_enable_stdio_usb(multicore 0) +pico_enable_filesystem(multicore FS_INIT fs_init.c) pico_set_binary_type(multicore copy_to_ram) - find_program(OPENOCD openocd) if(OPENOCD) add_custom_target(run_multicore diff --git a/tests/multicore/fs_init.c b/tests/multicore/fs_init.c new file mode 100644 index 0000000..0a6bc78 --- /dev/null +++ b/tests/multicore/fs_init.c @@ -0,0 +1,39 @@ +#include +#include +#include "blockdevice/flash.h" +#include "blockdevice/flash.h" +#include "blockdevice/sd.h" +#include "filesystem/fat.h" +#include "filesystem/littlefs.h" +#include "filesystem/vfs.h" + +#define FLASH_LENGTH_ALL 0 + +bool fs_init(void) { + blockdevice_t *flash = blockdevice_flash_create(PICO_FLASH_SIZE_BYTES - PICO_FS_DEFAULT_SIZE, + FLASH_LENGTH_ALL); + blockdevice_t *sd = blockdevice_sd_create(spi0, + PICO_DEFAULT_SPI_TX_PIN, + PICO_DEFAULT_SPI_RX_PIN, + PICO_DEFAULT_SPI_SCK_PIN, + PICO_DEFAULT_SPI_CSN_PIN, + 24 * MHZ, + true); + filesystem_t *fat = filesystem_fat_create(); + filesystem_t *littlefs = filesystem_littlefs_create(500, 16); + + int err = fs_format(littlefs, flash); + if (err == -1) + return false; + err = fs_mount("/flash", littlefs, flash); + if (err == -1) + return false; + + fs_format(fat, sd); + if (err == -1) + return false; + err = fs_mount("/sd", fat, sd); + if (err == -1) + return false; + return true; +} diff --git a/tests/multicore/main.c b/tests/multicore/main.c index 73e3895..fdbf5aa 100644 --- a/tests/multicore/main.c +++ b/tests/multicore/main.c @@ -2,73 +2,24 @@ * NOTE: When operating Flash memory from core1, it must be run from RAM. */ #include -#include -#include +#include #include #include #include #include #include #include -#include "blockdevice/flash.h" -#include "blockdevice/heap.h" -#include "blockdevice/sd.h" -#include "filesystem/littlefs.h" -#include "filesystem/fat.h" #include "filesystem/vfs.h" #define COLOR_GREEN(format) ("\e[32m" format "\e[0m") -#define TEST_FILE_SIZE (320 * 1024) +#define TEST_FILE_SIZE (512 * 1024) #define FLASH_START_AT (0.5 * 1024 * 1024) #define FLASH_LENGTH_ALL 0 static uint8_t core1_buffer[1024*16]; static uint8_t core0_buffer[1024*16]; -struct combination_map { - blockdevice_t *device; - filesystem_t *filesystem; - const char *label; -}; - -#define NUM_COMBINATION 4 -static struct combination_map combination[NUM_COMBINATION] = {0}; - -static void init_filesystem_combination(void) { - blockdevice_t *flash = blockdevice_flash_create(PICO_FLASH_SIZE_BYTES - PICO_FS_DEFAULT_SIZE, - FLASH_LENGTH_ALL); - blockdevice_t *sd = blockdevice_sd_create(spi0, - PICO_DEFAULT_SPI_TX_PIN, - PICO_DEFAULT_SPI_RX_PIN, - PICO_DEFAULT_SPI_SCK_PIN, - PICO_DEFAULT_SPI_CSN_PIN, - 24 * MHZ, - true); - filesystem_t *fat = filesystem_fat_create(); - filesystem_t *littlefs = filesystem_littlefs_create(500, 16); - - combination[0] = (struct combination_map){ - .device = flash, .filesystem = littlefs, .label = "littlefs on Flash" - }; - combination[1] = (struct combination_map){ - .device = flash, .filesystem = fat, .label = "FAT on Flash" - }; - combination[2] = (struct combination_map){ - .device = sd, .filesystem = littlefs, .label = "littlefs on SD card" - }; - combination[3] = (struct combination_map){ - .device = sd, .filesystem = fat, .label = "FAT on SD card" - }; -} - -static void cleanup_combination(void) { - filesystem_littlefs_free(combination[0].filesystem); - filesystem_fat_free(combination[1].filesystem); - blockdevice_flash_free(combination[0].device); - blockdevice_sd_free(combination[2].device); -} - static size_t test_printf(const char *format, ...) { va_list args; va_start(args, format); @@ -81,249 +32,93 @@ static size_t test_printf(const char *format, ...) { return (size_t)n; } -static void print_progress(const char *label, size_t current, size_t total) { - int num_dots = (int)((double)current / total * (50 - strlen(label))); - int num_spaces = (50 - strlen(label)) - num_dots; - - printf("\r%s ", label); - for (int i = 0; i < num_dots; i++) { - printf("."); - } - for (int i = 0; i < num_spaces; i++) { - printf(" "); - } - printf(" %zu/%zu bytes", current, total); - fflush(stdout); -} - -static void __not_in_flash_func(test_write_read_two_files_core1)(void) { - int fd = open("/core1", O_WRONLY|O_CREAT); - size_t remind = TEST_FILE_SIZE; - unsigned seed = 1; - while (remind > 0) { - size_t chunk = remind % sizeof(core1_buffer) ? remind % sizeof(core1_buffer) : sizeof(core1_buffer); - for (size_t i = 0; i < (size_t)chunk; i++) { - core1_buffer[i] = rand_r(&seed) & 0xFF; - } - ssize_t write_size = write(fd, core1_buffer, chunk); - assert(write_size != -1); - remind = remind - write_size; - } - int err = close(fd); - assert(err == 0); - - fd = open("/core1", O_RDONLY); - assert(fd != 0); - - seed = 1; - remind = TEST_FILE_SIZE; - while (remind > 0) { - size_t chunk = remind % sizeof(core1_buffer) ? remind % sizeof(core1_buffer) : sizeof(core1_buffer); - ssize_t read_size = read(fd, core1_buffer, chunk); - assert(read_size != -1); - for (size_t i = 0; i < (size_t)read_size; i++) { - volatile uint8_t r = rand_r(&seed) & 0xFF; - assert(core1_buffer[i] == r); - } - remind = remind - read_size; - } - - err = close(fd); - assert(err == 0); - - multicore_fifo_push_blocking(1); // finish - while (1) - tight_loop_contents(); - -} - -static void test_write_read_two_files(void) { - const char *label = "Write then read"; - - /* NOTE: When running with pico-sdk 1.5.1 and openocd, core1 needs to reset and sleep. - * https://forums.raspberrypi.com/viewtopic.php?t=349795 - */ - multicore_reset_core1(); - sleep_ms(100); - multicore_launch_core1(test_write_read_two_files_core1); - - int fd = open("/core0", O_WRONLY|O_CREAT); - size_t remind = TEST_FILE_SIZE; +static void write_read(const char *path, size_t size, uint8_t *buffer, size_t buf_size) { + int fd = open(path, O_WRONLY|O_CREAT); + size_t remind = size; unsigned seed = 0; while (remind > 0) { - size_t chunk = remind % sizeof(core0_buffer) ? remind % sizeof(core0_buffer) : sizeof(core0_buffer); + size_t chunk = remind % buf_size ? remind % buf_size : buf_size; for (size_t i = 0; i < (size_t)chunk; i++) { - core0_buffer[i] = rand_r(&seed) & 0xFF; + buffer[i] = rand_r(&seed) & 0xFF; } - ssize_t write_size = write(fd, core0_buffer, chunk); + ssize_t write_size = write(fd, buffer, chunk); assert(write_size != -1); remind = remind - write_size; - - print_progress(label, TEST_FILE_SIZE - remind, TEST_FILE_SIZE * 2); } int err = close(fd); assert(err == 0); - fd = open("/core0", O_RDONLY); + fd = open(path, O_RDONLY); assert(fd != 0); - seed = 0; - remind = TEST_FILE_SIZE; + remind = size; while (remind > 0) { - size_t chunk = remind % sizeof(core0_buffer) ? remind % sizeof(core0_buffer) : sizeof(core0_buffer); - ssize_t read_size = read(fd, core0_buffer, chunk); + size_t chunk = remind % buf_size ? remind % buf_size : buf_size; + ssize_t read_size = read(fd, buffer, chunk); assert(read_size != -1); for (size_t i = 0; i < (size_t)read_size; i++) { volatile uint8_t r = rand_r(&seed) & 0xFF; - assert(core0_buffer[i] == r); + assert(buffer[i] == r); } remind = remind - read_size; - print_progress(label, TEST_FILE_SIZE * 2 - remind, TEST_FILE_SIZE * 2); } err = close(fd); assert(err == 0); - - uint32_t result = multicore_fifo_pop_blocking(); - assert(result == 1); - - printf(COLOR_GREEN(" ok\n")); } +static void test_sequential_write_read(void) { + test_printf("/flash/core0 then /sd/core0"); + absolute_time_t start_at = get_absolute_time(); -static void __not_in_flash_func(test_write_while_read_two_files_core1)(void) { - int fd = open("/core1", O_WRONLY|O_CREAT); - size_t remind = TEST_FILE_SIZE; - unsigned seed = 1; - while (remind > 0) { - size_t chunk = remind % sizeof(core1_buffer) ? remind % sizeof(core1_buffer) : sizeof(core1_buffer); - for (size_t i = 0; i < (size_t)chunk; i++) { - core1_buffer[i] = rand_r(&seed) & 0xFF; - } - ssize_t write_size = write(fd, core1_buffer, chunk); - assert(write_size != -1); - remind = remind - write_size; - } - int err = close(fd); - assert(err == 0); - multicore_fifo_push_blocking(1); // write finish + write_read("/flash/core0", TEST_FILE_SIZE, core0_buffer, sizeof(core0_buffer)); + write_read("/sd/core0", TEST_FILE_SIZE * 5, core0_buffer, sizeof(core0_buffer)); - fd = open("/core1", O_RDONLY); - assert(fd != 0); - seed = 1; - remind = TEST_FILE_SIZE; - while (remind > 0) { - size_t chunk = remind % sizeof(core1_buffer) ? remind % sizeof(core1_buffer) : sizeof(core1_buffer); - ssize_t read_size = read(fd, core1_buffer, chunk); - assert(read_size != -1); - for (size_t i = 0; i < (size_t)read_size; i++) { - volatile uint8_t r = rand_r(&seed) & 0xFF; - assert(core1_buffer[i] == r); - } - remind = remind - read_size; - } - err = close(fd); - assert(err == 0); + double duration = (double)absolute_time_diff_us(start_at, get_absolute_time()) / 1000 / 1000; + printf(COLOR_GREEN("ok, %.1f seconds\n"), duration); +} - multicore_fifo_push_blocking(1); // read finish +static void sd_card_write_read_task(void) { + flash_safe_execute_core_init(); + write_read("/sd/core1", TEST_FILE_SIZE * 5, core1_buffer, sizeof(core1_buffer)); + + multicore_fifo_push_blocking(1); // finish while (1) tight_loop_contents(); } - -static void test_write_while_read_two_files(void) { - const char *label = "Write while read"; +static void test_parallel_write_read(void) { + test_printf("/flash/core0 with /sd/core1"); /* NOTE: When running with pico-sdk 1.5.1 and openocd, core1 needs to reset and sleep. * https://forums.raspberrypi.com/viewtopic.php?t=349795 */ multicore_reset_core1(); sleep_ms(100); - multicore_launch_core1(test_write_while_read_two_files_core1); - - int fd = open("/core0", O_RDONLY); - assert(fd != 0); - - unsigned seed = 0; - size_t remind = TEST_FILE_SIZE; - while (remind > 0) { - size_t chunk = remind % sizeof(core0_buffer) ? remind % sizeof(core0_buffer) : sizeof(core0_buffer); - ssize_t read_size = read(fd, core0_buffer, chunk); - assert(read_size != -1); - - for (size_t i = 0; i < (size_t)read_size; i++) { - volatile uint8_t r = rand_r(&seed) & 0xFF; - assert(core0_buffer[i] == r); - } - remind = remind - read_size; - print_progress(label, TEST_FILE_SIZE - remind, TEST_FILE_SIZE * 2); - } - int err = close(fd); - assert(err == 0); - - // wait core1 - uint32_t result = multicore_fifo_pop_blocking(); - assert(result == 1); - - fd = open("/core0", O_WRONLY|O_CREAT); - remind = TEST_FILE_SIZE; - seed = 0; - while (remind > 0) { - size_t chunk = remind % sizeof(core0_buffer) ? remind % sizeof(core0_buffer) : sizeof(core0_buffer); - for (size_t i = 0; i < (size_t)chunk; i++) { - core0_buffer[i] = rand_r(&seed) & 0xFF; - } - ssize_t write_size = write(fd, core0_buffer, chunk); - assert(write_size != -1); - remind = remind - write_size; - print_progress(label, TEST_FILE_SIZE * 2 - remind, TEST_FILE_SIZE * 2); - } - err = close(fd); - assert(err == 0); + absolute_time_t start_at = get_absolute_time(); + multicore_launch_core1(sd_card_write_read_task); - // wait core1 - result = multicore_fifo_pop_blocking(); + write_read("/flash/core0", TEST_FILE_SIZE, core0_buffer, sizeof(core0_buffer)); + int result = multicore_fifo_pop_blocking(); assert(result == 1); - printf(COLOR_GREEN(" ok\n")); -} - -static bool setup(filesystem_t *fs, blockdevice_t *device) { - int err = fs_format(fs, device); - if (err == -1 && errno == 5005) - return false; - assert(err == 0); - err = fs_mount("/", fs, device); - assert(err == 0); - return true; + double duration = (double)absolute_time_diff_us(start_at, get_absolute_time()) / 1000 / 1000; + printf(COLOR_GREEN("ok, %.1f seconds\n"), duration); } -static void cleanup() { - int err = fs_unmount("/"); - assert(err == 0); -} int main(void) { stdio_init_all(); - printf("Start multicore tests\n"); - - init_filesystem_combination(); - for (size_t i = 0; i < NUM_COMBINATION; i++) { - struct combination_map *setting = &combination[i]; - printf("%s:\n", setting->label); - - if (!setup(setting->filesystem, setting->device)) { - printf("skip, device not connected\n"); - continue; - } - - test_write_read_two_files(); - test_write_while_read_two_files(); - - cleanup(); + printf("Start write and read tests:\n"); + if (!fs_init()) { + printf("SD card device not found, skip\n"); + } else { + test_sequential_write_read(); + remove("/flash/core0"); + remove("/sd/core0"); + test_parallel_write_read(); } - cleanup_combination(); printf(COLOR_GREEN("All tests are ok\n")); while (1)