Skip to content

Commit

Permalink
Merge pull request #47 from oyama/feature/freertos-example
Browse files Browse the repository at this point in the history
Additional FreeRTOS samples and limitations.
  • Loading branch information
oyama authored Jun 17, 2024
2 parents cc725a5 + 02bedfe commit 14acced
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 3 deletions.
8 changes: 5 additions & 3 deletions LIMITATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
20 changes: 20 additions & 0 deletions examples/freertos_benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
92 changes: 92 additions & 0 deletions examples/freertos_benchmark/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
/* 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 */
61 changes: 61 additions & 0 deletions examples/freertos_benchmark/FreeRTOS_Kernel_import.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# This is a copy of <FREERTOS_KERNEL_PATH>/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)
59 changes: 59 additions & 0 deletions examples/freertos_benchmark/fs_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2024, Hiroyuki OYAMA. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include <hardware/clocks.h>
#include <hardware/flash.h>
#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;
}
87 changes: 87 additions & 0 deletions examples/freertos_benchmark/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <FreeRTOS.h>
#include <portable.h>

#include <task.h>
#include <errno.h>
#include <string.h>
#include <pico/stdlib.h>
#include <stdio.h>
#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)
;
}

Loading

0 comments on commit 14acced

Please sign in to comment.