From 02bedfe7ec5b4f774b2964e980f6af654ba9c36f Mon Sep 17 00:00:00 2001 From: Hiroyuki OYAMA Date: Tue, 18 Jun 2024 01:31:28 +0900 Subject: [PATCH] Additional FreeRTOS samples and limitations. --- LIMITATION.md | 8 +- examples/CMakeLists.txt | 6 ++ examples/freertos_benchmark/CMakeLists.txt | 20 ++++ examples/freertos_benchmark/FreeRTOSConfig.h | 92 +++++++++++++++++++ .../FreeRTOS_Kernel_import.cmake | 61 ++++++++++++ examples/freertos_benchmark/fs_init.c | 59 ++++++++++++ examples/freertos_benchmark/main.c | 87 ++++++++++++++++++ .../freertos_benchmark/pico_sdk_import.cmake | 73 +++++++++++++++ 8 files changed, 403 insertions(+), 3 deletions(-) create mode 100644 examples/freertos_benchmark/CMakeLists.txt create mode 100644 examples/freertos_benchmark/FreeRTOSConfig.h create mode 100644 examples/freertos_benchmark/FreeRTOS_Kernel_import.cmake create mode 100644 examples/freertos_benchmark/fs_init.c create mode 100644 examples/freertos_benchmark/main.c create mode 100644 examples/freertos_benchmark/pico_sdk_import.cmake diff --git a/LIMITATION.md b/LIMITATION.md index ee834b6..0ac7e46 100644 --- a/LIMITATION.md +++ b/LIMITATION.md @@ -4,14 +4,16 @@ When using pico-vfs, there are several limitations and behavior specifics that u ## File System Limitations -1. **Max File Size for FAT**: The maximum single file size of a FAT file system depends on the capacity of the storage medium. Check the size of the SD card used and the type of FAT (FAT16/32/ExFat) automatically assigned. +1. **Multiple Open Instances**: The current implementations of littlefs and FatFs do not support opening the same file multiple times simultaneously[^1][^2]. This limitation can affect scenarios where multiple accesses to the same file are required concurrently. -2. **Multiple Open Instances**: The current implementations of littlefs and FatFs do not support opening the same file multiple times simultaneously[^1][^2]. This limitation can affect scenarios where multiple accesses to the same file are required concurrently. +2. **Access to on-board flash from `core1`**: When operating file systems using the onboard flash block device on `core1`, it is necessary to run the operations from RAM[^3]. -3. **`core1` Usage with Onboard Flash Block Device**: When operating file systems using the onboard flash block device on `core1`, it is necessary to run the operations from RAM[^3]. +3. **Access on-board flash in FreeRTOS**: To access the flash device, the firmware must be stored in RAM or run with `#define configNUMBER_OF_CORES 1`. 4. **Memory maped IO**: `mmap` system call not supported. +5. **Max File Size for FAT**: The maximum single file size of a FAT file system depends on the capacity of the storage medium. Check the size of the SD card used and the type of FAT (FAT16/32/ExFat) automatically assigned. + We recommend reviewing these limitations before designing systems that heavily rely on multicore operations or require high file access availability. ## References diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 796e50c..7e99b5b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -24,3 +24,9 @@ if (PICO_CYW43_SUPPORTED) endif() endif() endif() + +if (NOT DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) + message("Skipping FreeRTOS Benchmark example as support is not available") +else() + add_subdirectory(freertos_benchmark EXCLUDE_FROM_ALL) +endif() diff --git a/examples/freertos_benchmark/CMakeLists.txt b/examples/freertos_benchmark/CMakeLists.txt new file mode 100644 index 0000000..c5a709c --- /dev/null +++ b/examples/freertos_benchmark/CMakeLists.txt @@ -0,0 +1,20 @@ +include(FreeRTOS_Kernel_import.cmake) + +add_executable(freertos_benchmark main.c fs_init.c) +target_compile_options(freertos_benchmark PRIVATE -Os) +target_include_directories(freertos_benchmark PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(freertos_benchmark PRIVATE + FreeRTOS-Kernel + FreeRTOS-Kernel-Heap1 + pico_stdlib + blockdevice_sd + blockdevice_flash + filesystem_fat + filesystem_littlefs + filesystem_vfs +) +target_link_options(freertos_benchmark PRIVATE -Wl,--print-memory-usage) + +#pico_set_binary_type(freertos_benchmark no_flash) +pico_enable_stdio_usb(freertos_benchmark 1) +pico_add_extra_outputs(freertos_benchmark) diff --git a/examples/freertos_benchmark/FreeRTOSConfig.h b/examples/freertos_benchmark/FreeRTOSConfig.h new file mode 100644 index 0000000..fbe1d2c --- /dev/null +++ b/examples/freertos_benchmark/FreeRTOSConfig.h @@ -0,0 +1,92 @@ +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 1 +#define configENABLE_BACKWARD_COMPATIBILITY 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (30*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* SMP port only */ +#define configNUMBER_OF_CORES 1 +#define configTICK_CORE 1 +#define configRUN_MULTIPLE_PRIORITIES 1 +#define configUSE_CORE_AFFINITY 0 +#define configUSE_PASSIVE_IDLE_HOOK 0 + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/examples/freertos_benchmark/FreeRTOS_Kernel_import.cmake b/examples/freertos_benchmark/FreeRTOS_Kernel_import.cmake new file mode 100644 index 0000000..109a54e --- /dev/null +++ b/examples/freertos_benchmark/FreeRTOS_Kernel_import.cmake @@ -0,0 +1,61 @@ +# This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake + +# This can be dropped into an external project to help locate the FreeRTOS kernel +# It should be include()ed prior to project(). Alternatively this file may +# or the CMakeLists.txt in this directory may be included or added via add_subdirectory +# respectively. + +if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) + set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) + message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") +endif () + +set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC/RP2040") +# undo the above +set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") + +if (NOT FREERTOS_KERNEL_PATH) + # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) + get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) + get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + endif() + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") + elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") + set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) + message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") + endif() +endif () + +if (NOT FREERTOS_KERNEL_PATH) + foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) + # check if FreeRTOS-Kernel exists under directory that included us + set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) + if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") + break() + endif() + endforeach() +endif() + +if (NOT FREERTOS_KERNEL_PATH) + message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") +endif() + +set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") + +get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") +endif() +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain an RP2040 port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") +endif() +set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) + +add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) diff --git a/examples/freertos_benchmark/fs_init.c b/examples/freertos_benchmark/fs_init.c new file mode 100644 index 0000000..8765fff --- /dev/null +++ b/examples/freertos_benchmark/fs_init.c @@ -0,0 +1,59 @@ +/* + * Copyright 2024, Hiroyuki OYAMA. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include +#include +#include +#include +#include "blockdevice/flash.h" +#include "blockdevice/sd.h" +#include "filesystem/fat.h" +#include "filesystem/littlefs.h" +#include "filesystem/vfs.h" + +bool fs_init(void) { + printf("mount /sd\n"); + 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, + false); + filesystem_t *fat = filesystem_fat_create(); + int err = fs_mount("/sd", fat, sd); + if (err == -1) { + printf("format /sd with FAT\n"); + err = fs_format(fat, sd); + if (err == -1) { + printf("fs_format error: %s", strerror(errno)); + return false; + } + err = fs_mount("/sd", fat, sd); + if (err == -1) { + printf("fs_mount error: %s", strerror(errno)); + return false; + } + } + + printf("mount /flash\n"); + blockdevice_t *device = blockdevice_flash_create(PICO_FLASH_SIZE_BYTES - PICO_FS_DEFAULT_SIZE, 0); + filesystem_t *fs = filesystem_littlefs_create(500, 16); + err = fs_mount("/flash", fs, device); + if (err == -1) { + printf("format /flash\n"); + err = fs_format(fs, device); + if (err == -1) { + printf("fs_format error: %s", strerror(errno)); + return false; + } + err = fs_mount("/flash", fs, device); + if (err == -1) { + printf("fs_mount error: %s", strerror(errno)); + return false; + } + } + return true; +} diff --git a/examples/freertos_benchmark/main.c b/examples/freertos_benchmark/main.c new file mode 100644 index 0000000..6a50e5c --- /dev/null +++ b/examples/freertos_benchmark/main.c @@ -0,0 +1,87 @@ +#include +#include + +#include +#include +#include +#include +#include +#include "filesystem/vfs.h" + +#define BENCHMARK_SIZE (0.4 * 1024 * 1024) +#define BUFFER_SIZE (512 * 1) + +static uint32_t xor_rand(uint32_t *seed) { + *seed ^= *seed << 13; + *seed ^= *seed >> 17; + *seed ^= *seed << 5; + return *seed; +} + +static uint32_t xor_rand_32bit(uint32_t *seed) { + return xor_rand(seed); +} + +void benchmark_task(void *p) +{ + const char *path = p; + printf("start benchmark %s\n", path); + + uint64_t start_at = get_absolute_time(); + int fd = open(path, O_WRONLY|O_CREAT); + if (fd == -1) { + printf("open error: %s\n", strerror(errno)); + return; + } + + uint32_t counter = 0; + xor_rand(&counter); + uint8_t buffer[BUFFER_SIZE] = {0}; + size_t remind = BENCHMARK_SIZE; + while (remind > 0) { + size_t chunk = remind % sizeof(buffer) ? remind % sizeof(buffer) : sizeof(buffer); + uint32_t *b = (uint32_t *)buffer; + for (size_t j = 0; j < (chunk / sizeof(uint32_t)); j++) { + b[j] = xor_rand_32bit(&counter); + } + + ssize_t write_size = write(fd, buffer, chunk); + if (write_size == -1) { + printf("write: error: %s\n", strerror(errno)); + return; + } + remind = remind - write_size; + } + + int err = close(fd); + if (err == -1) { + printf("close error: %s\n", strerror(errno)); + return; + } + + double duration = (double)absolute_time_diff_us(start_at, get_absolute_time()) / 1000 / 1000; + printf("Finish %s: %.1f KB/s\n", path, (double)(BENCHMARK_SIZE) / duration / 1024); + + while (true) { + ; + } +} + +int main(void) +{ + stdio_init_all(); + fs_init(); + printf("FreeRTOS benchmark\n"); + + xTaskCreate(benchmark_task, "SD Card", 1024*1, "/sd/benchmark", 1, NULL); + + TaskHandle_t task_handle; + xTaskCreate(benchmark_task, "flash", 1024*1, "/flash/benchmark", 1, &(task_handle)); + //vTaskCoreAffinitySet(task_handle, 1); + + vTaskStartScheduler(); + + while (true) + ; +} + diff --git a/examples/freertos_benchmark/pico_sdk_import.cmake b/examples/freertos_benchmark/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/examples/freertos_benchmark/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE})