diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80ccb6c9..3e217a23 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,9 +4,9 @@ name: CI # events but only for the master branch on: push: - branches: [ master,dev/develop,dev/migration_gcc_desktop_build] + branches: [master, dev/develop, dev/littlefs_impl] pull_request: - branches: [ master,dev/develop] + branches: [master, dev/develop] # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: @@ -18,105 +18,103 @@ jobs: buildDir: '${{ github.workspace }}\build' # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - with: - submodules: 'true' - - - name: Setup python - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - - name: Install conan - uses: BSFishy/pip-action@v1 - with: - packages: | - conan - - #Install the latest cmake - - name : Get the latest CMake version - uses: lukka/get-cmake@latest - - - name: Run build for MSVC compiler - run: | - cmake -G"Visual Studio 16 2019" -Ax64 -S"${{ github.workspace }}/Firmware/" -B"${{ env.buildDir }}" -DPACKAGE_TESTS=ON -DTARGET_PLATFORM:STRING="FIRMWARE_SIMULATOR" -DCMAKE_BUILD_TYPE=Release - cd ${{ env.buildDir }} - cmake --build . --config Release - - name : Run firmware testing - run: | - cd ${{ env.buildDir }}/Release - .\FirmwareTesting.exe + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + with: + submodules: "true" + + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install conan + uses: BSFishy/pip-action@v1 + with: + packages: | + conan + + #Install the latest cmake + - name: Get the latest CMake version + uses: lukka/get-cmake@latest + + - name: Run build for MSVC compiler + run: | + cmake -G"Visual Studio 17 2022" -Ax64 -S"${{ github.workspace }}/Firmware/" -B"${{ env.buildDir }}" -DPACKAGE_TESTS=ON -DTARGET_PLATFORM:STRING="FIRMWARE_SIMULATOR" -DCMAKE_BUILD_TYPE=Release + cd ${{ env.buildDir }} + cmake --build . --config Release + - name: Run firmware testing + run: | + cd ${{ env.buildDir }}/Release + .\FirmwareTesting.exe build-arm: - runs-on: ubuntu-latest steps: - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - with: - submodules: 'true' - - #Install the latest cmake - - name : Get the latest CMake version - uses: lukka/get-cmake@latest - - # - name: Get the latest arm-none-eabi-gcc - # uses: fiam/arm-none-eabi-gcc@v1 - # with: - # release: '10-2020-q2' - # directory: ${{ github.workspace }}/toolchain - - name: Cache GCC toolchain - id: cache-gcc-toolchain - uses: actions/cache@v2 - with: - path: ${{ github.workspace }}/gcc_toolchain_10_3 - key: ${{runner.os}}-toolchain-v10_3 - - - name: Get the latest arm-gcc-none-eabi-gcc - uses: wei/wget@v1 - if: steps.cache-gcc-toolchain.outputs.cache-hit != 'true' - id: download-arm-gcc - with: - args: 'https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2' - - - name: Unpack arm-gcc-compiler - if: steps.cache-gcc-toolchain.outputs.cache-hit != 'true' - run: | - mkdir gcc_toolchain_10_3 - tar -xjf gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2 --directory ${{ github.workspace }}/gcc_toolchain_10_3 - - - name: Cache NordicSDK - id: cache-nordic-sdk - uses: actions/cache@v2 - with: - path: ${{ github.workspace }}/Nrf52SDKv16_0 - key: ${{runner.os}}-nordic-sdk-v16-0 - - - name: Get NRF52 16.0 SDK - uses: wei/wget@v1 - id: download-nrf52sdk - if: steps.cache-nordic-sdk.outputs.cache-hit != 'true' - with: - args: 'https://www.nordicsemi.com/-/media/Software-and-other-downloads/SDKs/nRF5/Binaries/nRF5SDK160098a08e2.zip' - - - name: Unpack NordicSDK - if: steps.cache-nordic-sdk.outputs.cache-hit != 'true' - run: | - mkdir Nrf52SDKv16_0 - unzip nRF5SDK160098a08e2.zip -d Nrf52SDKv16_0 - mv Nrf52SDKv16_0/components/boards/pca10040.h Nrf52SDKv16_0/components/boards/pca10040_1.h - - - name: Run CMakeLists generation for GCC compiler - uses: lukka/run-cmake@v2 - with: - cmakeGenerator: Ninja - cmakeListsOrSettingsJson: CMakeListsTxtAdvanced - cmakeListsTxtPath: ${{ github.workspace }}/Firmware/CMakeLists.txt - cmakeBuildType: Debug - cmakeAppendedArgs : '-DTARGET_PLATFORM:STRING="ARM_CORTEX" -DEXECUTE_MCU_FLASHING=OFF -DCMAKE_BUILD_TYPE:STRING=Debug -DREDUCE_LVGL_BINARY_SIZE=ON -DPACKAGE_TESTS=OFF -DNRF5_SDK_PATH=${{ github.workspace }}/Nrf52SDKv16_0 -DARM_NONE_EABI_TOOLCHAIN_PATH:PATH=${{ github.workspace }}/gcc_toolchain_10_3/gcc-arm-none-eabi-10.3-2021.07' - buildWithCMake: true + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + with: + submodules: "true" + + #Install the latest cmake + - name: Get the latest CMake version + uses: lukka/get-cmake@latest + + # - name: Get the latest arm-none-eabi-gcc + # uses: fiam/arm-none-eabi-gcc@v1 + # with: + # release: '10-2020-q2' + # directory: ${{ github.workspace }}/toolchain + - name: Cache GCC toolchain + id: cache-gcc-toolchain + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/gcc_toolchain_11_2 + key: ${{runner.os}}-toolchain-v11_2 + + - name: Get the latest arm-gcc-none-eabi-gcc + uses: wei/wget@v1 + if: steps.cache-gcc-toolchain.outputs.cache-hit != 'true' + id: download-arm-gcc + with: + args: "https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-arm-none-eabi.tar.xz" + + - name: Unpack arm-gcc-compiler + if: steps.cache-gcc-toolchain.outputs.cache-hit != 'true' + run: | + mkdir gcc_toolchain_11_2 + tar -xJf gcc-arm-11.2-2022.02-x86_64-arm-none-eabi.tar.xz --directory ${{ github.workspace }}/gcc_toolchain_11_2 + + - name: Cache NordicSDK + id: cache-nordic-sdk + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/Nrf52SDKv16_0 + key: ${{runner.os}}-nordic-sdk-v16-0 + + - name: Get NRF52 16.0 SDK + uses: wei/wget@v1 + id: download-nrf52sdk + if: steps.cache-nordic-sdk.outputs.cache-hit != 'true' + with: + args: "https://www.nordicsemi.com/-/media/Software-and-other-downloads/SDKs/nRF5/Binaries/nRF5SDK160098a08e2.zip" + + - name: Unpack NordicSDK + if: steps.cache-nordic-sdk.outputs.cache-hit != 'true' + run: | + mkdir Nrf52SDKv16_0 + unzip nRF5SDK160098a08e2.zip -d Nrf52SDKv16_0 + mv Nrf52SDKv16_0/components/boards/pca10040.h Nrf52SDKv16_0/components/boards/pca10040_1.h + + - name: Run CMakeLists generation for GCC compiler + uses: lukka/run-cmake@v2 + with: + cmakeGenerator: Ninja + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: ${{ github.workspace }}/Firmware/CMakeLists.txt + cmakeBuildType: Debug + cmakeAppendedArgs: '-DTARGET_PLATFORM:STRING="ARM_CORTEX" -DEXECUTE_MCU_FLASHING=OFF -DCMAKE_BUILD_TYPE:STRING=Debug -DREDUCE_LVGL_BINARY_SIZE=ON -DPACKAGE_TESTS=OFF -DNRF5_SDK_PATH=${{ github.workspace }}/Nrf52SDKv16_0 -DARM_NONE_EABI_TOOLCHAIN_PATH:PATH=${{ github.workspace }}/gcc_toolchain_11_2/gcc-arm-11.2-2022.02-x86_64-arm-none-eabi' + buildWithCMake: true # # Runs a set of commands using the runners shell # - name: Run a multi-line script @@ -126,51 +124,50 @@ jobs: build-linux-desktop-simulator: runs-on: ubuntu-latest env: - buildDir: '${{ github.workspace }}/build' + buildDir: "${{ github.workspace }}/build" steps: - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - with: - submodules: 'true' - - - name: Setup python - uses: actions/setup-python@v2 - with: - python-version: '3.8' - - - name: Install conan - uses: BSFishy/pip-action@v1 - with: - packages: | - conan - - - name: Intall system required packages - run: | - sudo apt-get update - sudo apt-get install -y libegl-dev - #Install the latest cmake - - name : Get the latest CMake version - uses: lukka/get-cmake@latest - - - name: Set up GCC 11 - uses: egor-tensin/setup-gcc@v1 - with: - version: 11 - platform: x64 - - - name: Run CMakeLists generation for GCC compiler - uses: lukka/run-cmake@v2 - with: - cmakeGenerator: Unix Makefiles - cmakeListsOrSettingsJson: CMakeListsTxtAdvanced - cmakeListsTxtPath: ${{ github.workspace }}/Firmware/CMakeLists.txt - cmakeBuildType: Debug - cmakeAppendedArgs : '-DTARGET_PLATFORM:STRING="FIRMWARE_SIMULATOR" -DCMAKE_BUILD_TYPE:STRING=Debug -DREDUCE_LVGL_BINARY_SIZE=OFF -DPACKAGE_TESTS=ON -DENABLE_SANITIZE_BUILD=ON' - buildDirectory: ${{ env.buildDir }} - buildWithCMake: true - - - name : Run firmware testing - run: | - cd ${{ env.buildDir }} - ./FirmwareTesting \ No newline at end of file + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + with: + submodules: "true" + + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install conan + uses: BSFishy/pip-action@v1 + with: + packages: | + conan + + - name: Intall system required packages + run: | + sudo apt-get update + sudo apt-get install -y libegl-dev + #Install the latest cmake + - name: Get the latest CMake version + uses: lukka/get-cmake@latest + + - name: Set up GCC 11 + uses: egor-tensin/setup-gcc@v1 + with: + version: 11 + platform: x64 + + - name: Run CMakeLists generation for GCC compiler + uses: lukka/run-cmake@v2 + with: + cmakeGenerator: Unix Makefiles + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: ${{ github.workspace }}/Firmware/CMakeLists.txt + cmakeBuildType: Debug + cmakeAppendedArgs: '-DTARGET_PLATFORM:STRING="FIRMWARE_SIMULATOR" -DCMAKE_BUILD_TYPE:STRING=Debug -DREDUCE_LVGL_BINARY_SIZE=OFF -DPACKAGE_TESTS=ON -DENABLE_SANITIZE_BUILD=ON' + buildDirectory: ${{ env.buildDir }} + buildWithCMake: true + + - name: Run firmware testing + run: | + cd ${{ env.buildDir }} + ./FirmwareTesting diff --git a/.gitignore b/.gitignore index dc41d0d8..9abef84a 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ _deps build/ out/ build_simulator/ - +Firmware/build_firmware #ignore vs temp folders -.vs/ \ No newline at end of file +.vs/ +#Ignore kicad backups +*.sch-bak \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 25d679c3..9de77fe9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "Schematic/KiCadLibrariesRoot"] path = Schematic/KiCadLibrariesRoot url = https://github.com/ValentiWorkLearning/KiCadLibrariesRoot.git +[submodule "Firmware/3rdparty/littlefs_lib/littlefs"] + path = Firmware/3rdparty/littlefs_lib/littlefs + url = https://github.com/ValentiWorkLearning/littlefs.git diff --git a/Firmware/3rdparty/CMakeLists.txt b/Firmware/3rdparty/CMakeLists.txt index 5ec4309d..a14455b4 100644 --- a/Firmware/3rdparty/CMakeLists.txt +++ b/Firmware/3rdparty/CMakeLists.txt @@ -5,13 +5,14 @@ if( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) set(CMAKE_CXX_STANDARD 20) #add_subdirectory( cppcoro_lib ) elseif( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set( 3RD_PARTY_DIR CACHE STRING ${CMAKE_CURRENT_SOURCE_DIR} )# Libs root path -add_subdirectory( etl ) -add_subdirectory( fmt ) +add_subdirectory(etl) +add_subdirectory(fmt) +add_subdirectory(littlefs_lib) target_compile_definitions(fmt PUBLIC @@ -20,4 +21,4 @@ target_compile_definitions(fmt FMT_USE_DOUBLE=0 FMT_USE_LONG_DOUBLE=0 FMT_REDUCE_INT_INSTANTIATIONS=0 -) \ No newline at end of file +) diff --git a/Firmware/3rdparty/littlefs_lib/CMakeLists.txt b/Firmware/3rdparty/littlefs_lib/CMakeLists.txt new file mode 100644 index 00000000..be3a0235 --- /dev/null +++ b/Firmware/3rdparty/littlefs_lib/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.14) + +add_library(littlefs) + +target_sources(littlefs PRIVATE + littlefs/lfs.cpp + littlefs/lfs_util.cpp + "lfs_util_desktop.h") + +target_include_directories(littlefs PUBLIC littlefs +) + +target_link_libraries(littlefs PUBLIC UtilsLibrary ) + +target_compile_definitions(littlefs PUBLIC LFS_YES_TRACE ) + +target_link_options( + littlefs + PUBLIC + ${CPU_FLAGS} +) + +target_compile_options( + littlefs + PUBLIC + ${COMMON_FLAGS} +) + +target_compile_options( + littlefs + PUBLIC + $<$:-Os> +) + +target_include_directories(littlefs PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) + target_precompile_headers(littlefs PRIVATE ${CMAKE_CURRENT_LIST_DIR}/lfs_util_segger.h) + target_link_libraries(littlefs PRIVATE NordicSDK::Common) +elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") + target_precompile_headers(littlefs PRIVATE ${CMAKE_CURRENT_LIST_DIR}/lfs_util_desktop.h) + target_link_libraries(littlefs PUBLIC logger_service) +endif() diff --git a/Firmware/3rdparty/littlefs_lib/lfs_util_desktop.h b/Firmware/3rdparty/littlefs_lib/lfs_util_desktop.h new file mode 100644 index 00000000..664d1c66 --- /dev/null +++ b/Firmware/3rdparty/littlefs_lib/lfs_util_desktop.h @@ -0,0 +1,236 @@ +/* + * lfs utility functions + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef LFS_UTIL_H +#define LFS_UTIL_H + +// Users can override lfs_util.h with their own configuration by defining +// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). +// +// If LFS_CONFIG is used, none of the default utils will be emitted and must be +// provided by the config file. To start, I would suggest copying lfs_util.h +// and modifying as needed. +#ifdef LFS_CONFIG +#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) +#define LFS_STRINGIZE2(x) #x +#include LFS_STRINGIZE(LFS_CONFIG) +#else + +// System includes +#include +#include +#include +#include +#include +#ifndef LFS_NO_MALLOC +#include +#endif +#ifndef LFS_NO_ASSERT +#include +#endif +#if !defined(LFS_NO_DEBUG) || \ + !defined(LFS_NO_WARN) || \ + !defined(LFS_NO_ERROR) || \ + defined(LFS_YES_TRACE) +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + +// Macros, may be replaced by system specific wrappers. Arguments to these +// macros must not have side-effects as the macros can be removed for a smaller +// code footprint + +// Logging functions +#ifndef LFS_TRACE +#ifdef LFS_YES_TRACE +#define LFS_TRACE(fmt,...) printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, "") +#else +#define LFS_TRACE(...) +#endif +#endif + +#ifndef LFS_DEBUG +#ifndef LFS_NO_DEBUG +#define LFS_DEBUG(fmt,...) printf("%s:%d:debug: " fmt "%s\n", __VA_ARGS__, "") +#else +#define LFS_DEBUG(...) +#endif +#endif + +#ifndef LFS_WARN +#ifndef LFS_NO_WARN +#define LFS_WARN(fmt,...) printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, "") +#else +#define LFS_WARN(...) +#endif +#endif + +#ifndef LFS_ERROR +#ifndef LFS_NO_ERROR +#define LFS_ERROR(fmt,...) printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, "") +#else +#define LFS_ERROR(...) +#endif +#endif + +// Runtime assertions +#ifndef LFS_ASSERT +#ifndef LFS_NO_ASSERT +#define LFS_ASSERT(test) assert(test) +#else +#define LFS_ASSERT(test) +#endif +#endif + + +// Builtin functions, these may be replaced by more efficient +// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more +// expensive basic C implementation for debugging purposes + +// Min/max functions for unsigned 32-bit numbers +static inline uint32_t lfs_max(uint32_t a, uint32_t b) { + return (a > b) ? a : b; +} + +static inline uint32_t lfs_min(uint32_t a, uint32_t b) { + return (a < b) ? a : b; +} + +// Align to nearest multiple of a size +static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { + return a - (a % alignment); +} + +static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { + return lfs_aligndown(a + alignment-1, alignment); +} + +// Find the smallest power of 2 greater than or equal to a +static inline uint32_t lfs_npw2(uint32_t a) { +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return 32 - __builtin_clz(a-1); +#else + uint32_t r = 0; + uint32_t s; + a -= 1; + s = (a > 0xffff) << 4; a >>= s; r |= s; + s = (a > 0xff ) << 3; a >>= s; r |= s; + s = (a > 0xf ) << 2; a >>= s; r |= s; + s = (a > 0x3 ) << 1; a >>= s; r |= s; + return (r | (a >> 1)) + 1; +#endif +} + +// Count the number of trailing binary zeros in a +// lfs_ctz(0) may be undefined +static inline uint32_t lfs_ctz(uint32_t a) { +#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) + return __builtin_ctz(a); +#else + return lfs_npw2((a & -a) + 1) - 1; +#endif +} + +// Count the number of binary ones in a +static inline uint32_t lfs_popc(uint32_t a) { +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return __builtin_popcount(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; +#endif +} + +// Find the sequence comparison of a and b, this is the distance +// between a and b ignoring overflow +static inline int lfs_scmp(uint32_t a, uint32_t b) { + return (int)(unsigned)(a - b); +} + +// Convert between 32-bit little-endian and native order +static inline uint32_t lfs_fromle32(uint32_t a) { +#if !defined(LFS_NO_INTRINSICS) && ( \ + (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ + (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return a; +#elif !defined(LFS_NO_INTRINSICS) && ( \ + (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ + (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return __builtin_bswap32(a); +#else + return (((uint8_t*)&a)[0] << 0) | + (((uint8_t*)&a)[1] << 8) | + (((uint8_t*)&a)[2] << 16) | + (((uint8_t*)&a)[3] << 24); +#endif +} + +static inline uint32_t lfs_tole32(uint32_t a) { + return lfs_fromle32(a); +} + +// Convert between 32-bit big-endian and native order +static inline uint32_t lfs_frombe32(uint32_t a) { +#if !defined(LFS_NO_INTRINSICS) && ( \ + (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ + (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return __builtin_bswap32(a); +#elif !defined(LFS_NO_INTRINSICS) && ( \ + (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ + (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return a; +#else + return (((uint8_t*)&a)[0] << 24) | + (((uint8_t*)&a)[1] << 16) | + (((uint8_t*)&a)[2] << 8) | + (((uint8_t*)&a)[3] << 0); +#endif +} + +static inline uint32_t lfs_tobe32(uint32_t a) { + return lfs_frombe32(a); +} + +// Calculate CRC-32 with polynomial = 0x04c11db7 +uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); + +// Allocate memory, only used if buffers are not provided to littlefs +// Note, memory must be 64-bit aligned +static inline void *lfs_malloc(size_t size) { +#ifndef LFS_NO_MALLOC + return malloc(size); +#else + (void)size; + return NULL; +#endif +} + +// Deallocate memory, only used if buffers are not provided to littlefs +static inline void lfs_free(void *p) { +#ifndef LFS_NO_MALLOC + free(p); +#else + (void)p; +#endif +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/Firmware/3rdparty/littlefs_lib/lfs_util_segger.h b/Firmware/3rdparty/littlefs_lib/lfs_util_segger.h new file mode 100644 index 00000000..93f51bc2 --- /dev/null +++ b/Firmware/3rdparty/littlefs_lib/lfs_util_segger.h @@ -0,0 +1,268 @@ +/* + * lfs utility functions + * + * Copyright (c) 2017, Arm Limited. All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef LFS_UTIL_H +#define LFS_UTIL_H + +// Users can override lfs_util.h with their own configuration by defining +// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h). +// +// If LFS_CONFIG is used, none of the default utils will be emitted and must be +// provided by the config file. To start, I would suggest copying lfs_util.h +// and modifying as needed. +#ifdef LFS_CONFIG +#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) +#define LFS_STRINGIZE2(x) #x +#include LFS_STRINGIZE(LFS_CONFIG) +#else + +// System includes +#include "SEGGER_RTT.h" +#include +#include +#include +#include +#include +#ifndef LFS_NO_MALLOC +#include +#endif +#ifndef LFS_NO_ASSERT +#include +#endif +#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \ + defined(LFS_YES_TRACE) +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Macros, may be replaced by system specific wrappers. Arguments to these +// macros must not have side-effects as the macros can be removed for a smaller +// code footprint + +// Logging functions +#ifndef LFS_TRACE +#ifdef LFS_YES_TRACE +#define LFS_TRACE(fmt, ...) \ + SEGGER_RTT_printf(0, "%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, ""); \ + SEGGER_RTT_printf(0, "\r\n") +#else +#define LFS_TRACE(...) +#endif +#endif + +#ifndef LFS_DEBUG +#ifndef LFS_NO_DEBUG +#define LFS_DEBUG(fmt, ...) \ + SEGGER_RTT_printf(0, "%s:%d:debug: " fmt "%s\n", __VA_ARGS__, ""); \ + SEGGER_RTT_printf(0, "\r\n") +#else +#define LFS_DEBUG(...) +#endif +#endif + +#ifndef LFS_WARN +#ifndef LFS_NO_WARN +#define LFS_WARN(fmt, ...) \ + SEGGER_RTT_printf(0, "%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, ""); \ + SEGGER_RTT_printf(0, "\r\n") +#else +#define LFS_WARN(...) +#endif +#endif + +#ifndef LFS_ERROR +#ifndef LFS_NO_ERROR +#define LFS_ERROR(fmt, ...) \ + SEGGER_RTT_printf(0, "%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__, ""); \ + SEGGER_RTT_printf(0, "\r\n"); +#else +#define LFS_ERROR(...) +#endif +#endif + +// Runtime assertions +#ifndef LFS_ASSERT +#ifndef LFS_NO_ASSERT +#define LFS_ASSERT(test) \ + SEGGER_RTT_printf(0, "%s assert expr: %s", RTT_CTRL_TEXT_YELLOW, #test "\n"); \ + assert(test) +#else +#define LFS_ASSERT(test) +#endif +#endif + + // Builtin functions, these may be replaced by more efficient + // toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more + // expensive basic C implementation for debugging purposes + + // Min/max functions for unsigned 32-bit numbers + static inline uint32_t lfs_max(uint32_t a, uint32_t b) + { + return (a > b) ? a : b; + } + + static inline uint32_t lfs_min(uint32_t a, uint32_t b) + { + return (a < b) ? a : b; + } + + // Align to nearest multiple of a size + static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) + { + return a - (a % alignment); + } + + static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) + { + return lfs_aligndown(a + alignment - 1, alignment); + } + + // Find the smallest power of 2 greater than or equal to a + static inline uint32_t lfs_npw2(uint32_t a) + { +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return 32 - __builtin_clz(a - 1); +#else + uint32_t r = 0; + uint32_t s; + a -= 1; + s = (a > 0xffff) << 4; + a >>= s; + r |= s; + s = (a > 0xff) << 3; + a >>= s; + r |= s; + s = (a > 0xf) << 2; + a >>= s; + r |= s; + s = (a > 0x3) << 1; + a >>= s; + r |= s; + return (r | (a >> 1)) + 1; +#endif + } + + // Count the number of trailing binary zeros in a + // lfs_ctz(0) may be undefined + static inline uint32_t lfs_ctz(uint32_t a) + { +#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) + return __builtin_ctz(a); +#else + return lfs_npw2((a & -a) + 1) - 1; +#endif + } + + // Count the number of binary ones in a + static inline uint32_t lfs_popc(uint32_t a) + { +#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) + return __builtin_popcount(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; +#endif + } + + // Find the sequence comparison of a and b, this is the distance + // between a and b ignoring overflow + static inline int lfs_scmp(uint32_t a, uint32_t b) + { + return (int)(unsigned)(a - b); + } + + // Convert between 32-bit little-endian and native order + static inline uint32_t lfs_fromle32(uint32_t a) + { +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return a; +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return __builtin_bswap32(a); +#else + return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) | + (((uint8_t*)&a)[3] << 24); +#endif + } + + static inline uint32_t lfs_tole32(uint32_t a) + { + return lfs_fromle32(a); + } + + // Convert between 32-bit big-endian and native order + static inline uint32_t lfs_frombe32(uint32_t a) + { +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + return __builtin_bswap32(a); +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + return a; +#else + return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) | + (((uint8_t*)&a)[3] << 0); +#endif + } + + static inline uint32_t lfs_tobe32(uint32_t a) + { + return lfs_frombe32(a); + } + + // Calculate CRC-32 with polynomial = 0x04c11db7 + uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size); + + // Allocate memory, only used if buffers are not provided to littlefs + // Note, memory must be 64-bit aligned + static inline void* lfs_malloc(size_t size) + { +#ifndef LFS_NO_MALLOC + return malloc(size); +#else + (void)size; + return NULL; +#endif + } + + // Deallocate memory, only used if buffers are not provided to littlefs + static inline void lfs_free(void* p) + { +#ifndef LFS_NO_MALLOC + free(p); +#else + (void)p; +#endif + } + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/Firmware/3rdparty/littlefs_lib/littlefs b/Firmware/3rdparty/littlefs_lib/littlefs new file mode 160000 index 00000000..e56e369d --- /dev/null +++ b/Firmware/3rdparty/littlefs_lib/littlefs @@ -0,0 +1 @@ +Subproject commit e56e369d9ea4f48302525a08ba4679a72aca472e diff --git a/Firmware/CMakeLists.txt b/Firmware/CMakeLists.txt index 8fae51f7..1de1a743 100644 --- a/Firmware/CMakeLists.txt +++ b/Firmware/CMakeLists.txt @@ -13,7 +13,7 @@ if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) -MP -MD -mthumb -mabi=aapcs -Wall -g3 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -fno-exceptions ${CPU_FLAGS} ) - set( CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/arm-gcc-toolchain.cmake ) + set( CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/arm-gcc-toolchain.cmake ) endif() project(nordic-template C CXX ASM) @@ -23,7 +23,7 @@ if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) set( NRF_TARGET "nrf52" ) set( NRF52SOFTDEVICE "S112" ) - set( SDK_DEPENDENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdk_dependent") + set( SDK_DEPENDENT_DIR "${CMAKE_CURRENT_LIST_DIR}/sdk_dependent") #https://stackoverflow.com/questions/31355692/how-do-i-enable-link-time-optimization-lto-with-cmake set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) @@ -32,9 +32,9 @@ if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) elseif(${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") - file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.16.1/conan.cmake" + file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/release/0.17/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake" - EXPECTED_HASH SHA256=396e16d0f5eabdc6a14afddbcfff62a54a7ee75c6da23f32f7a31bc85db23484 + EXPECTED_HASH SHA256=3bef79da16c2e031dc429e1dac87a08b9226418b300ce004cc125a82687baeef TLS_VERIFY ON) endif() @@ -43,12 +43,12 @@ elseif(${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") include(${CMAKE_BINARY_DIR}/conan.cmake) - conan_cmake_configure(REQUIRES sdl/2.0.16 gtest/1.11.0 + conan_cmake_configure(REQUIRES sdl/2.0.16 gtest/1.11.0 spdlog/1.9.2 GENERATORS cmake_find_package IMPORTS "bin, *.dll -> ${CMAKE_CURRENT_BINARY_DIR}" IMPORTS "lib, *.dylib* -> ${CMAKE_CURRENT_BINARY_DIR}" OPTIONS sdl:shared=True - OPTIONS fmt:shared=True + OPTIONS spdlog:header_only=True ) conan_cmake_autodetect(settings) @@ -62,23 +62,18 @@ elseif(${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") endif() -set (3RDPARTY_DIR "3rdparty") -add_subdirectory( ${3RDPARTY_DIR} ${CMAKE_BINARY_DIR}/3rdparty ) - +add_subdirectory(3rdparty) add_subdirectory(drivers) add_subdirectory(utils) add_subdirectory(logger) add_subdirectory(graphics) add_subdirectory(service_providers) +add_subdirectory(filesystem) set (NORDIC_TARGET theOpenWatch) -add_executable(${NORDIC_TARGET} ${PROJECT_SOURCES}) - -target_sources( - ${NORDIC_TARGET} - PRIVATE - main.cpp - ap_application.cpp +add_executable(${NORDIC_TARGET} + ${CMAKE_CURRENT_LIST_DIR}/main.cpp + ${CMAKE_CURRENT_LIST_DIR}/ap_application.cpp ) target_link_libraries( diff --git a/Firmware/ap_application.cpp b/Firmware/ap_application.cpp index 7eb04f55..088f6992 100644 --- a/Firmware/ap_application.cpp +++ b/Firmware/ap_application.cpp @@ -17,6 +17,8 @@ #include +namespace GsEvents = Graphics::Events; + namespace EventConvert { Graphics::Events::TButtonsEvents toButtonEvent(Buttons::ButtonState _buttonToConvert) @@ -42,8 +44,7 @@ Graphics::Events::TButtonsEvents toButtonEvent(Buttons::ButtonState _buttonToCon Application::Application() noexcept { - initBoard(); - initPeripheral(); + LOG_INFO("Application start"); initServices(); initGraphicsStack(); initBleStack(); @@ -52,21 +53,18 @@ Application::Application() noexcept Application::~Application() noexcept = default; -void Application::initBoard() noexcept -{ - m_pBoardImpl = WatchBoard::createBoard(); -} - void Application::initServices() noexcept { - m_fakeServiceProvider = ServiceProviders::getFakeServiceCreator(); - m_batteryLevelService = m_fakeServiceProvider->getBatteryService(); - m_heartrateService = m_fakeServiceProvider->getHeartrateService(); - m_dateTimeService = m_fakeServiceProvider->getDateTimeService(); + auto bleServiceProvider = ServiceProviders::getFakeServiceCreator(); + m_batteryLevelService = bleServiceProvider->getBatteryService(); + m_heartrateService = bleServiceProvider->getHeartrateService(); + m_dateTimeService = bleServiceProvider->getDateTimeService(); } -void Application::initPeripheral() noexcept +Application& Application::Instance() { + static Application app; + return app; } void Application::initBleStack() noexcept @@ -88,23 +86,23 @@ void Application::initBleStack() noexcept auto& pMainWindow = m_graphicsService->getMainWindow(); m_bleStackKeeper->onConnected.connect([&pMainWindow] { pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::BleDevice, - Graphics::Events::TBleClientEvents::DeviceConnected, + {GsEvents::EventGroup::BleDevice, + GsEvents::to_underlying(GsEvents::TBleClientEvents::DeviceConnected), std::nullopt}); }); m_bleStackKeeper->onDisconnected.connect([&pMainWindow] { pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::BleDevice, - Graphics::Events::TBleClientEvents::DeviceDisconnected, + {GsEvents::EventGroup::BleDevice, + GsEvents::to_underlying(GsEvents::TBleClientEvents::DeviceDisconnected), std::nullopt}); }); auto& dateTimeService = m_bleStackKeeper->getDateTimeService(); dateTimeService.onDateTimeDiscovered.connect([&pMainWindow](const TimeWrapper& _newBleTime) { pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + {GsEvents::EventGroup::DateTime, + GsEvents::to_underlying(GsEvents::TDateTimeEvents::DateTimeChanged), _newBleTime}); }); } @@ -118,15 +116,15 @@ void Application::initGraphicsStack() noexcept m_batteryLevelService->onBatteryLevelChangedSig.connect( [&pMainWindow](std::uint8_t _newBatteryValue) { pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::Battery, - Graphics::Events::TBatteryEvents::BatteryLevelChanged, + {GsEvents::EventGroup::Battery, + GsEvents::to_underlying(GsEvents::TBatteryEvents::BatteryLevelChanged), _newBatteryValue}); }); m_dateTimeService->onDateTimeChanged.connect([&pMainWindow](const TimeWrapper& _newTime) { pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + {GsEvents::EventGroup::DateTime, + GsEvents::to_underlying(GsEvents::TDateTimeEvents::DateTimeChanged), _newTime}); }); } @@ -135,17 +133,15 @@ void Application::connectBoardSpecificEvents() noexcept { auto& pMainWindow = m_graphicsService->getMainWindow(); - m_pBoardImpl->getButtonsDriver()->onButtonEvent.connect( - [&pMainWindow](Buttons::ButtonEvent _buttonEvent) { - Graphics::Events::HardwareButtonId graphicsButton{ - Graphics::Events::enumConvert( - _buttonEvent.buttonId)}; + m_boardImpl.getButtonsDriver()->onButtonEvent.connect([&pMainWindow]( + Buttons::ButtonEvent _buttonEvent) { + GsEvents::HardwareButtonId graphicsButton{GsEvents::to_underlying(_buttonEvent.buttonId)}; - pMainWindow.getEventDispatcher().postEvent( - {Graphics::Events::EventGroup::Buttons, - EventConvert::toButtonEvent(_buttonEvent.buttonEvent), - graphicsButton}); - }); + pMainWindow.getEventDispatcher().postEvent( + {GsEvents::EventGroup::Buttons, + GsEvents::to_underlying(EventConvert::toButtonEvent(_buttonEvent.buttonEvent)), + graphicsButton}); + }); } void Application::runApplicationLoop() noexcept @@ -154,7 +150,7 @@ void Application::runApplicationLoop() noexcept m_batteryLevelService->startBatteryMeasure(); m_dateTimeService->launchService(); - m_pBoardImpl->ledToggle(); + m_boardImpl.ledToggle(); while (true) { diff --git a/Firmware/ap_application.hpp b/Firmware/ap_application.hpp index 24049ac2..a297cfbe 100644 --- a/Firmware/ap_application.hpp +++ b/Firmware/ap_application.hpp @@ -20,33 +20,29 @@ class Application : public Utils::noncopyable { public: - Application() noexcept; - - ~Application() noexcept; + static Application& Instance(); -public: void runApplicationLoop() noexcept; private: - void initBoard() noexcept; - void initServices() noexcept; - void initPeripheral() noexcept; - void initBleStack() noexcept; void initGraphicsStack() noexcept; void connectBoardSpecificEvents() noexcept; +private: + Application() noexcept; + ~Application() noexcept; + private: Ble::Stack::TSoftDevicePtr m_bleStackKeeper; - std::unique_ptr m_fakeServiceProvider; std::unique_ptr m_batteryLevelService; std::unique_ptr m_heartrateService; std::unique_ptr m_dateTimeService; std::unique_ptr m_graphicsService; - WatchBoard::TBoardPtr m_pBoardImpl; + WatchBoard::Board m_boardImpl; }; diff --git a/Firmware/cmake/base.cmake b/Firmware/cmake/base.cmake index 41335db0..01097c0c 100644 --- a/Firmware/cmake/base.cmake +++ b/Firmware/cmake/base.cmake @@ -1,6 +1,6 @@ if( NOT ARM_NONE_EABI_TOOLCHAIN_PATH ) #set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_none_eabi_9_2_1") - set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_arm_none_eabi_10_3") + set(ARM_NONE_EABI_TOOLCHAIN_PATH "C:/gcc_arm_none_eabi_11_2") message(STATUS "No ARM_NONE_EABI_TOOLCHAIN_PATH specified, using default: " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) else() message(STATUS " ARM_NONE_EABI_TOOLCHAIN_PATH specified using: " ${ARM_NONE_EABI_TOOLCHAIN_PATH}) diff --git a/Firmware/drivers/ble/ble_datetime_service.cpp b/Firmware/drivers/ble/ble_datetime_service.cpp index e0ccdc66..8c64bca4 100644 --- a/Firmware/drivers/ble/ble_datetime_service.cpp +++ b/Firmware/drivers/ble/ble_datetime_service.cpp @@ -54,14 +54,14 @@ void DateTimeServiceNordic::serviceEventHandler( switch (_pEvent->evt_type) { case BLE_CTS_C_EVT_DISCOVERY_COMPLETE: - LOG_DEBUG_ENDL("Current Time Service discovered on server."); + LOG_DEBUG("Current Time Service discovered on server."); errorCode = ble_cts_c_handles_assign( &m_currentTimeService, _pEvent->conn_handle, &_pEvent->params.char_handles); APP_ERROR_CHECK(errorCode); break; case BLE_CTS_C_EVT_DISCOVERY_FAILED: - LOG_DEBUG_ENDL("Current Time Service not found on server. "); + LOG_DEBUG("Current Time Service not found on server. "); // CTS not found in this case we just disconnect. There is no reason to stay // in the connection for this simple app since it all wants is to interact with CT // if (_pEvent->conn_handle != BLE_CONN_HANDLE_INVALID) @@ -73,16 +73,16 @@ void DateTimeServiceNordic::serviceEventHandler( break; case BLE_CTS_C_EVT_DISCONN_COMPLETE: - LOG_DEBUG_ENDL("Disconnect Complete."); + LOG_DEBUG("Disconnect Complete."); break; case BLE_CTS_C_EVT_CURRENT_TIME: - LOG_DEBUG_ENDL("Current Time received."); + LOG_DEBUG("Current Time received."); // current_time_print(_pEvent); break; case BLE_CTS_C_EVT_INVALID_TIME: - LOG_DEBUG_ENDL("Invalid Time received."); + LOG_DEBUG("Invalid Time received."); break; default: diff --git a/Firmware/drivers/ble/ble_softdevice.cpp b/Firmware/drivers/ble/ble_softdevice.cpp index 0b699240..ff361927 100644 --- a/Firmware/drivers/ble/ble_softdevice.cpp +++ b/Firmware/drivers/ble/ble_softdevice.cpp @@ -125,7 +125,7 @@ void BleStackKeeper::gattEventHandler( if (_pEvent->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED) { - LOG_DEBUG_ENDL("GATT ATT MTU on connection 0x%x changed to %d."); + LOG_DEBUG("GATT ATT MTU on connection 0x%x changed to %d."); onConnected.emitLater(); // NRF_LOG_INFO("GATT ATT MTU on connection 0x%x changed to %d.", @@ -175,7 +175,7 @@ void BleStackKeeper::bleEventHandler(ble_evt_t const* _pBleEvent) noexcept switch (_pBleEvent->header.evt_id) { case BLE_GAP_EVT_DISCONNECTED: - LOG_DEBUG_ENDL("Disconnected."); + LOG_DEBUG("Disconnected."); m_isConnected = false; m_connectionHandle = BLE_CONN_HANDLE_INVALID; @@ -183,7 +183,7 @@ void BleStackKeeper::bleEventHandler(ble_evt_t const* _pBleEvent) noexcept break; case BLE_GAP_EVT_CONNECTED: - LOG_DEBUG_ENDL("Connected"); + LOG_DEBUG("Connected"); m_isConnected = true; // NRF_LOG_INFO("Connected."); // errCode = bsp_indication_set( BSP_INDICATE_CONNECTED ); @@ -196,7 +196,7 @@ void BleStackKeeper::bleEventHandler(ble_evt_t const* _pBleEvent) noexcept break; case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { - LOG_DEBUG_ENDL("PHY update request."); + LOG_DEBUG("PHY update request."); ble_gap_phys_t const phys = { .tx_phys = BLE_GAP_PHY_AUTO, @@ -210,7 +210,7 @@ void BleStackKeeper::bleEventHandler(ble_evt_t const* _pBleEvent) noexcept case BLE_GATTC_EVT_TIMEOUT: // Disconnect on GATT Client timeout event. - LOG_DEBUG_ENDL("GATT Client Timeout."); + LOG_DEBUG("GATT Client Timeout."); errCode = sd_ble_gap_disconnect( _pBleEvent->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); @@ -270,7 +270,7 @@ void BleStackKeeper::advertisingEventHandler(ble_adv_evt_t _pAdvertisingEvent) n switch (_pAdvertisingEvent) { case BLE_ADV_EVT_FAST: - LOG_DEBUG_ENDL("Fast advertising."); + LOG_DEBUG("Fast advertising."); if (!m_isConnected) onDisconnected.emitLater(); // errorCode = bsp_indication_set(BSP_INDICATE_ADVERTISING); @@ -282,7 +282,7 @@ void BleStackKeeper::advertisingEventHandler(ble_adv_evt_t _pAdvertisingEvent) n break; case BLE_ADV_EVT_SLOW_WHITELIST: - LOG_DEBUG_ENDL("Slow advertising with WhiteList"); + LOG_DEBUG("Slow advertising with WhiteList"); // errorCode = bsp_indication_set( BSP_INDICATE_ADVERTISING_WHITELIST ); // APP_ERROR_CHECK( errorCode ); errorCode = ble_advertising_restart_without_whitelist(&m_advertising); @@ -302,7 +302,7 @@ void BleStackKeeper::advertisingEventHandler(ble_adv_evt_t _pAdvertisingEvent) n pm_whitelist_get(whiteListAddrs.data(), &AddrCount, whiteListIrks.data(), &IrkCount); APP_ERROR_CHECK(errorCode); - LOG_DEBUG_ENDL("pm_whitelist_get returns %d addr in whitelist and %d irk whitelist"); + LOG_DEBUG("pm_whitelist_get returns %d addr in whitelist and %d irk whitelist"); // Apply the whitelist. errorCode = ble_advertising_whitelist_reply( @@ -419,7 +419,7 @@ void BleStackKeeper::peerManagerEventHandler(pm_evt_t const* _pPeerEvent) noexce switch (_pPeerEvent->evt_id) { case PM_EVT_BONDED_PEER_CONNECTED: { - LOG_DEBUG_ENDL("Connected to a previously bonded device."); + LOG_DEBUG("Connected to a previously bonded device."); } break; @@ -497,7 +497,7 @@ void BleStackKeeper::peerManagerEventHandler(pm_evt_t const* _pPeerEvent) noexce if (IsUpdateSucceeded) { - LOG_DEBUG_ENDL("New Bond, add the peer to the whitelist if possible"); + LOG_DEBUG("New Bond, add the peer to the whitelist if possible"); if (m_whiteListPeerCount < BLE_GAP_WHITELIST_ADDR_MAX_COUNT) { @@ -568,7 +568,7 @@ void BleStackKeeper::deleteBonds() noexcept { ret_code_t errCode{}; - LOG_DEBUG_ENDL("Erase bonds!"); + LOG_DEBUG("Erase bonds!"); errCode = pm_peers_delete(); APP_ERROR_CHECK(errCode); diff --git a/Firmware/drivers/board/CMakeLists.txt b/Firmware/drivers/board/CMakeLists.txt index 2194ea33..146b9b18 100644 --- a/Firmware/drivers/board/CMakeLists.txt +++ b/Firmware/drivers/board/CMakeLists.txt @@ -16,6 +16,7 @@ target_link_libraries( buttons_driver spi windbond_spi_flash_driver + watch_filesystem PRIVATE UtilsLibrary logger_service diff --git a/Firmware/drivers/board/inc/board/hardware_usings.hpp b/Firmware/drivers/board/inc/board/hardware_usings.hpp index 4b92cf1f..51070db7 100644 --- a/Firmware/drivers/board/inc/board/hardware_usings.hpp +++ b/Firmware/drivers/board/inc/board/hardware_usings.hpp @@ -4,14 +4,21 @@ #ifndef USE_DEVICE_SPECIFIC #include #include +#include +#include +#include #else #include #include +#include +#include #endif #include #include +#include + namespace Hal { @@ -20,6 +27,15 @@ using BoardTimer = Buttons::FirmwareSimulatorTimerBackend; using ButtonsBackend = Buttons::FirmwareSimulatorButtonsBackend; using TFlashDriver = ExternalFlash::WinbondFlashDriver< Interface::SpiTemplated::SpiBus>; + +using TLogHeapBlockDevice = + Filesystem::BlockDevice::LogAdaptorBlockDevice>; + +using TFilesystem = Platform::Fs::Holder; #else using BoardTimer = Buttons::NordicTimerBackend; using ButtonsBackend = Buttons::NordicButtonsBackend; @@ -27,6 +43,20 @@ using TFlashDriver = ExternalFlash::WinbondFlashDriver< Interface::SpiTemplated::SpiBus>>; + +struct BoardSpiFlashDescriptor +{ + static constexpr inline std::size_t kBlockSize = 256; + static constexpr inline std::size_t kSectorsCount = 65'536; + static constexpr inline std::size_t kReadSize = 256; + static constexpr inline std::size_t kEraseSize = 4096; +}; + +using TSpiFlashBlockDevice = Filesystem::BlockDevice::LogAdaptorBlockDevice< + Filesystem::BlockDevice::SpiFlashBlockDevice>; + +using TFilesystem = Platform::Fs::Holder; + #endif using ButtonsDriver = Buttons::ButtonsDriverTemplate; diff --git a/Firmware/drivers/board/inc/board/watchboard.hpp b/Firmware/drivers/board/inc/board/watchboard.hpp index 25cc5ef7..4d5151f4 100644 --- a/Firmware/drivers/board/inc/board/watchboard.hpp +++ b/Firmware/drivers/board/inc/board/watchboard.hpp @@ -1,11 +1,14 @@ #pragma once -#include "utils/FastPimpl.hpp" -#include "utils/Platform.hpp" +#include +#include -#include "hardware_usings.hpp" #include +#include +#include +#include + namespace WatchBoard { @@ -35,11 +38,9 @@ class Board : private Utils::noncopyable private: Hal::ButtonsDriver m_buttonsDriver; using TFlashDriverPtr = std::unique_ptr; + using TFilesystemPtr = std::unique_ptr; + TFilesystemPtr m_filesystem; TFlashDriverPtr m_pFlashDriver; }; - -using TBoardPtr = std::unique_ptr; -TBoardPtr createBoard() noexcept; - } // namespace WatchBoard diff --git a/Firmware/drivers/board/watchboard.cpp b/Firmware/drivers/board/watchboard.cpp index 5817185e..70a9b240 100644 --- a/Firmware/drivers/board/watchboard.cpp +++ b/Firmware/drivers/board/watchboard.cpp @@ -15,13 +15,13 @@ APP_TIMER_DEF(m_ledDriverTimer); #endif -#include "utils/CallbackConnector.hpp" -#include "utils/CoroUtils.hpp" +#include +#include -#include "delay/delay_provider.hpp" -#include "logger/logger_service.hpp" +#include +#include -#if defined (USE_DEVICE_SPECIFIC) +#if defined(USE_DEVICE_SPECIFIC) #define FMT_HEADER_ONLY #endif @@ -89,12 +89,13 @@ void Board::initBoard() noexcept /* Configure board. */ bsp_board_init(BSP_INIT_LEDS); - LOG_DEBUG_ENDL("Hello from E73 Mod Board!"); + LOG_INFO("Hello from E73 Mod Board!"); ret_code_t errorCode{}; errorCode = app_timer_init(); - APP_ERROR_CHECK(errorCode); + // LOG_DEBUG(fmt::format("app_timer_init code() {}", errorCode)); + // APP_ERROR_CHECK(errorCode); #endif m_buttonsDriver.initializeHalDependent(); initBoardSpiFlash(); @@ -107,23 +108,36 @@ void Board::initBoardTimer() noexcept errorCode = app_timer_create(&m_ledDriverTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerExpiredCallback); APP_ERROR_CHECK(errorCode); - LOG_DEBUG("LED timer create code is:"); - LOG_DEBUG_ENDL(errorCode); + LOG_DEBUG(fmt::format("LED timer create code is {}", errorCode)); #endif } void Board::initBoardSpiFlash() noexcept { - m_pFlashDriver = std::make_unique(); - if (m_pFlashDriver) - { - const std::uint32_t JedecId = co_await m_pFlashDriver->requestJEDEDCId(); - LOG_DEBUG("Jedec Id is:"); - LOG_DEBUG_ENDL(fmt::format("{:#04x}", JedecId)); + // LOG_DEBUG("creation of flash driver started"); + // m_pFlashDriver = std::make_unique(); - const std::span DeviceId = co_await m_pFlashDriver->requestDeviceId(); - LOG_DEBUG_ENDL(fmt::format("{:02X}", fmt::join(DeviceId, ""))); - } + // const std::uint32_t JedecId = co_await m_pFlashDriver->requestJEDEDCId(); + // LOG_DEBUG(fmt::format("Jedec Id is {:#04x}", JedecId)); + + // const std::span DeviceId = co_await m_pFlashDriver->requestDeviceId(); + // LOG_DEBUG(fmt::format("Device id is {:02X}", fmt::join(DeviceId, ""))); + + // LOG_DEBUG("m_pFlashDriver->requestBlockWrite"); + // auto blockTest = std::array{1, 2, 3, 4, 5, 6, 7, 8}; + // co_await m_pFlashDriver->pageWrite(0x00, blockTest); + // LOG_DEBUG("m_pFlashDriver->compltetedBlockWrite"); + + // LOG_DEBUG("m_pFlashDriver->requestReadBlock"); + // auto readResult = co_await m_pFlashDriver->requestReadBlock(0x00, blockTest.size()); + // LOG_DEBUG(fmt::format("Got read block {}", readResult)); + + LOG_INFO("Started filesystem creation"); + m_filesystem = std::make_unique(); + + LOG_INFO("Completed filesystem creation"); + co_await m_filesystem->initializeFs(); + LOG_INFO("Filesystem is ready"); } Board::Board() noexcept @@ -139,7 +153,6 @@ void Board::ledToggle() noexcept while (true) { co_await 300ms; - // LOG_DEBUG_ENDL("LED TIMER EXPIRED"); bsp_board_led_invert(0); co_await 100ms; bsp_board_led_invert(0); @@ -164,9 +177,4 @@ Hal::ButtonsDriver* Board::getButtonsDriver() noexcept return &m_buttonsDriver; } -TBoardPtr createBoard() noexcept -{ - return std::make_unique(); -} - }; // namespace WatchBoard diff --git a/Firmware/drivers/buttons/bt_nordic_hardware_backend.cpp b/Firmware/drivers/buttons/bt_nordic_hardware_backend.cpp index 20a961fa..a78f1c0d 100644 --- a/Firmware/drivers/buttons/bt_nordic_hardware_backend.cpp +++ b/Firmware/drivers/buttons/bt_nordic_hardware_backend.cpp @@ -32,7 +32,7 @@ void NordicTimerBackend::initialize() auto timerExpiredCallback = cbc::obtain_connector([this](void* _pContext) { m_isTimerEllapsed = true; onTimerExpired.emit(); - LOG_DEBUG_ENDL("m_isTimerEllapsed = true;"); + LOG_DEBUG("m_isTimerEllapsed = true;"); }); errorCode = @@ -113,12 +113,12 @@ void NordicButtonsBackend::handleHardwareButtonEvent( auto toLocalEvent = [](std::uint8_t _buttonEvent) { if (_buttonEvent == APP_BUTTON_PUSH) { - LOG_DEBUG_ENDL("ButtonBackendEvent::kPressed"); + LOG_DEBUG("ButtonBackendEvent::kPressed"); return ButtonBackendEvent::kPressed; } else if (_buttonEvent == APP_BUTTON_RELEASE) { - LOG_DEBUG_ENDL("ButtonBackendEvent::kReleased"); + LOG_DEBUG("ButtonBackendEvent::kReleased"); return ButtonBackendEvent::kReleased; } else @@ -131,19 +131,19 @@ void NordicButtonsBackend::handleHardwareButtonEvent( switch (_pinNumber) { case BUTTON_1: - LOG_DEBUG_ENDL("BUTTON_1"); + LOG_DEBUG("BUTTON_1"); onButtonEvent.emit(Buttons::ButtonId::kLeftButtonTop, toLocalEvent(_buttonEvent)); break; case BUTTON_2: - LOG_DEBUG_ENDL("BUTTON_2"); + LOG_DEBUG("BUTTON_2"); onButtonEvent.emit(Buttons::ButtonId::kLeftButtonMedium, toLocalEvent(_buttonEvent)); break; case BUTTON_3: - LOG_DEBUG_ENDL("BUTTON_3"); + LOG_DEBUG("BUTTON_3"); onButtonEvent.emit(Buttons::ButtonId::kLeftButtonBottom, toLocalEvent(_buttonEvent)); break; case BUTTON_4: - LOG_DEBUG_ENDL("BUTTON_4"); + LOG_DEBUG("BUTTON_4"); onButtonEvent.emit(Buttons::ButtonId::kRightButtonTop, toLocalEvent(_buttonEvent)); break; default: diff --git a/Firmware/drivers/factory_impl/ble_services_stub.cpp b/Firmware/drivers/factory_impl/ble_services_stub.cpp index 5e92f81f..d6d805b9 100644 --- a/Firmware/drivers/factory_impl/ble_services_stub.cpp +++ b/Firmware/drivers/factory_impl/ble_services_stub.cpp @@ -8,13 +8,13 @@ namespace Ble::BatteryService StubBatteryService::StubBatteryService() noexcept { - LOG_DEBUG_ENDL("Initialized DesktopStubBatteryService"); + LOG_DEBUG("Initialized DesktopStubBatteryService"); } void StubBatteryService::onBatteryLevelChanged(std::uint8_t _newBatteryLevel) noexcept { LOG_DEBUG("New battery level is:"); - LOG_DEBUG_ENDL(_newBatteryLevel); + LOG_DEBUG(_newBatteryLevel); } } // namespace Ble::BatteryService @@ -24,7 +24,7 @@ namespace Ble::DateTimeService StubDateTimeService::StubDateTimeService() noexcept { - LOG_DEBUG_ENDL("Initialized StubDateTimeService"); + LOG_DEBUG("Initialized StubDateTimeService"); } void StubDateTimeService::handleDiscoveryEvent(const std::any& _pBleDbDiscoveryEvent) noexcept diff --git a/Firmware/drivers/gpio/gpio_pin.cpp b/Firmware/drivers/gpio/gpio_pin.cpp index 0a10c1bd..b54a5bc2 100644 --- a/Firmware/drivers/gpio/gpio_pin.cpp +++ b/Firmware/drivers/gpio/gpio_pin.cpp @@ -37,13 +37,13 @@ GpioPin::GpioPin() noexcept = default; template void GpioPin::set() noexcept { - LOG_DEBUG_ENDL("Gpio Set called"); + LOG_DEBUG("Gpio Set called"); } template void GpioPin::reset() noexcept { - LOG_DEBUG_ENDL("Gpio Reset called"); + LOG_DEBUG("Gpio Reset called"); } #endif diff --git a/Firmware/drivers/headers/ih/drivers/ih_button_driver.hpp b/Firmware/drivers/headers/ih/drivers/ih_button_driver.hpp index 57e092d7..79749f5e 100644 --- a/Firmware/drivers/headers/ih/drivers/ih_button_driver.hpp +++ b/Firmware/drivers/headers/ih/drivers/ih_button_driver.hpp @@ -12,7 +12,7 @@ namespace Buttons { -enum class ButtonState +enum class ButtonState : std::uint8_t { kButtonUp, kButtonDown, @@ -22,7 +22,7 @@ enum class ButtonState Undefined }; -enum class ButtonId +enum class ButtonId : std::uint8_t { kLeftButtonTop, kLeftButtonMedium, @@ -93,12 +93,12 @@ template class ButtonsDriverTemplate switch (buttonDescriptor.pressCount) { case 1: - LOG_DEBUG_ENDL( + LOG_DEBUG( "onButtonEvent.emit({ m_lastPressedId, ButtonState::kButtonClick });"); onButtonEvent.emit({buttonDescriptor.id, ButtonState::kButtonClick}); break; case 2: - LOG_DEBUG_ENDL( + LOG_DEBUG( "onButtonEvent.emit({ _buttonId, ButtonState::kButtonDblClick });"); onButtonEvent.emit({buttonDescriptor.id, ButtonState::kButtonDblClick}); break; @@ -139,7 +139,7 @@ template class ButtonsDriverTemplate { m_buttons[arrayIndex].pressCount = 0; m_buttons[arrayIndex].longPressTimeoutExpired = false; - LOG_DEBUG_ENDL( + LOG_DEBUG( "onButtonEvent.emit({ m_lastPressedId, ButtonState::kButtonLongPress });"); onButtonEvent.emit({m_buttons[arrayIndex].id, ButtonState::kButtonLongPress}); } diff --git a/Firmware/drivers/i2c/i2c_test.cpp b/Firmware/drivers/i2c/i2c_test.cpp index c9522fd2..79d197ad 100644 --- a/Firmware/drivers/i2c/i2c_test.cpp +++ b/Firmware/drivers/i2c/i2c_test.cpp @@ -40,14 +40,14 @@ void scanI2CSensors() errorCode = nrf_drv_twi_rx(&m_twi, Mpu9250Addr, &SampleData, sizeof(SampleData)); if (errorCode == NRF_SUCCESS) - LOG_DEBUG_ENDL("MPU9250 detected on address 0x69"); + LOG_DEBUG("MPU9250 detected on address 0x69"); constexpr std::uint8_t Max30102Addr = 0x57; errorCode = nrf_drv_twi_rx(&m_twi, Max30102Addr, &SampleData, sizeof(SampleData)); if (errorCode == NRF_SUCCESS) - LOG_DEBUG_ENDL("Max30102 detected on address 0x57"); + LOG_DEBUG("Max30102 detected on address 0x57"); #endif } diff --git a/Firmware/drivers/spi/CMakeLists.txt b/Firmware/drivers/spi/CMakeLists.txt index 971da45d..5b09c00e 100644 --- a/Firmware/drivers/spi/CMakeLists.txt +++ b/Firmware/drivers/spi/CMakeLists.txt @@ -5,8 +5,6 @@ add_library( if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) target_link_libraries( spi INTERFACE NordicSDK::Common ) -elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR") - target_link_libraries( spi INTERFACE fmt::fmt ) endif() target_compile_options( diff --git a/Firmware/drivers/spi/inc/backends/spi_backend_desktop.hpp b/Firmware/drivers/spi/inc/backends/spi_backend_desktop.hpp index 45b922bb..5ce556ea 100644 --- a/Firmware/drivers/spi/inc/backends/spi_backend_desktop.hpp +++ b/Firmware/drivers/spi/inc/backends/spi_backend_desktop.hpp @@ -15,6 +15,7 @@ #include #include +#include #define USE_THREADING_ASYNC_BACKEND @@ -41,7 +42,7 @@ class SpiBusDesktopBackend using namespace std::chrono_literals; std::unique_lock(m_transactionBufferGuard); - fmt::print("[Desktop SPI simultator]{}\n", m_dataBuffer); + LOG_DEBUG(fmt::format("[Desktop SPI simultator]{}\n", m_dataBuffer)); m_newDataArrived.store(false); m_transactionCompleted(); diff --git a/Firmware/drivers/winbondflash/inc/windbondflash/winbond_commandset.hpp b/Firmware/drivers/winbondflash/inc/windbondflash/winbond_commandset.hpp index a27b2118..d8da2aa5 100644 --- a/Firmware/drivers/winbondflash/inc/windbondflash/winbond_commandset.hpp +++ b/Firmware/drivers/winbondflash/inc/windbondflash/winbond_commandset.hpp @@ -2,42 +2,46 @@ #include - +namespace WindbondCommandSet::Masks +{ +inline constexpr std::uint8_t EraseWriteInProgress = 1 << 0; +} namespace WindbondCommandSet { - constexpr std::uint8_t WriteEnable = 0x06; - constexpr std::uint8_t WriteDisable = 0x04; +inline constexpr std::uint8_t WriteEnable = 0x06; +inline constexpr std::uint8_t WriteDisable = 0x04; - constexpr std::uint8_t ReadStatusRegister1 = 0x05; - constexpr std::uint8_t ReadStatusRegister2 = 0x35; +inline constexpr std::uint8_t ReadStatusRegister1 = 0x05; +inline constexpr std::uint8_t ReadStatusRegister2 = 0x35; +inline constexpr std::uint8_t ReadStatusRegister3 = 0x15; - constexpr std::uint8_t WriteStatusRegister = 0x01; +inline constexpr std::uint8_t WriteStatusRegister = 0x01; - constexpr std::uint8_t ReadData = 0x03; - constexpr std::uint8_t FastRead = 0x0b; - constexpr std::uint8_t PageProgram = 0x02; +inline constexpr std::uint8_t ReadData = 0x03; +inline constexpr std::uint8_t FastRead = 0x0b; +inline constexpr std::uint8_t PageProgram = 0x02; - constexpr std::uint8_t SectorErase4KB = 0x20; - constexpr std::uint8_t BlockErase32KB = 0x52; - constexpr std::uint8_t BlockErase64KB = 0xd8; +inline constexpr std::uint8_t SectorErase4KB = 0x20; +inline constexpr std::uint8_t BlockErase32KB = 0x52; +inline constexpr std::uint8_t BlockErase64KB = 0xd8; - constexpr std::uint8_t ChipErase = 0xc7; - constexpr std::uint8_t ChipEraseSimilar = 0x60; +inline constexpr std::uint8_t ChipErase = 0xc7; +inline constexpr std::uint8_t ChipEraseSimilar = 0x60; - constexpr std::uint8_t EraseSuspend = 0x75; - constexpr std::uint8_t EraseResume = 0x7A; +inline constexpr std::uint8_t EraseSuspend = 0x75; +inline constexpr std::uint8_t EraseResume = 0x7A; - constexpr std::uint8_t PowerDownMode = 0xb9; - constexpr std::uint8_t ResumePowerDownMode = 0xab; +inline constexpr std::uint8_t PowerDownMode = 0xb9; +inline constexpr std::uint8_t ResumePowerDownMode = 0xab; - constexpr std::uint8_t DeviceIdFromPowedDownRelease = 0xab; - constexpr std::uint8_t ReadManufactureId = 0x90; +inline constexpr std::uint8_t DeviceIdFromPowedDownRelease = 0xab; +inline constexpr std::uint8_t ReadManufactureId = 0x90; - constexpr std::uint8_t JedecIdLength = 3; - constexpr std::uint8_t ReadJedecId = 0x9F; +inline constexpr std::uint8_t JedecIdLength = 3; +inline constexpr std::uint8_t ReadJedecId = 0x9F; - constexpr std::uint8_t UniqueIdLength = 8; // 64 bits - constexpr std::uint8_t ReadUniqueId = 0x4B; +inline constexpr std::uint8_t UniqueIdLength = 8; // 64 bits +inline constexpr std::uint8_t ReadUniqueId = 0x4B; - constexpr std::uint8_t DummyByte = 0x00; -} +inline constexpr std::uint8_t DummyByte = 0x00; +} // namespace WindbondCommandSet diff --git a/Firmware/drivers/winbondflash/inc/windbondflash/winbond_flash_templated.hpp b/Firmware/drivers/winbondflash/inc/windbondflash/winbond_flash_templated.hpp index db92b31a..5080d33e 100644 --- a/Firmware/drivers/winbondflash/inc/windbondflash/winbond_flash_templated.hpp +++ b/Firmware/drivers/winbondflash/inc/windbondflash/winbond_flash_templated.hpp @@ -4,8 +4,10 @@ #include #include +#include #include +#include #include #include @@ -16,100 +18,138 @@ template class WinbondFlashDriver { using This_t = WinbondFlashDriver; + struct ChipSelectGuard + { + ChipSelectGuard(This_t* pThis) : m_pThis{pThis} + { + m_pThis->getSpiBus()->setCsPinLow(); + } + + ~ChipSelectGuard() + { + m_pThis->getSpiBus()->setCsPinHigh(); + } + + This_t* m_pThis; + }; + public: CoroUtils::VoidTask pageWrite( - const std::uint32_t _address, + std::uint32_t _address, std::span _blockData) noexcept { + + LOG_TRACE("WinbondFlashDriver pageWrite"); constexpr std::uint16_t PageSize = 256; - assert(_blockData.size() < PageSize); + assert(_blockData.size() <= PageSize); + + { + ChipSelectGuard csGuard{this}; + co_await prepareXferTransaction(std::forward_as_tuple(WindbondCommandSet::WriteEnable)); + } + while (co_await checkIsBusy()) + { + LOG_TRACE("WinbondFlashDriver wait for write completion"); + co_yield CoroUtils::CoroQueueMainLoop::GetInstance(); + } + + co_await eraseSector(_address); - using TTupleProgram = decltype(std::forward_as_tuple( - WindbondCommandSet::PageProgram, - static_cast(_address & 0x00'FF'00'00 >> 16), - static_cast(_address & 0x00'00'FF'00 >> 8), - static_cast(_address & 0x00'00'00'FF))); - constexpr bool ManageSpiTransactions = false; + LOG_TRACE("WinbondFlashDriver XFER WE"); - getSpiBus()->setCsPinLow(); + { + ChipSelectGuard csGuard{this}; - using TTupleWe = decltype(std::forward_as_tuple(WindbondCommandSet::WriteEnable)); - co_await prepareXferTransaction( - std::forward_as_tuple(WindbondCommandSet::WriteEnable)); + LOG_TRACE("WinbondFlashDriver XFER PageProgram"); + co_await prepareXferTransaction(std::forward_as_tuple( + WindbondCommandSet::PageProgram, + static_cast((_address & 0x00'FF'00'00) >> 16), + static_cast((_address & 0x00'00'FF'00) >> 8), + static_cast(_address & 0x00'00'00'FF))); - co_await prepareXferTransaction(std::forward_as_tuple( - WindbondCommandSet::PageProgram, - static_cast(_address & 0x00'FF'00'00 >> 16), - static_cast(_address & 0x00'00'FF'00 >> 8), - static_cast(_address & 0x00'00'00'FF))); + LOG_TRACE("WinbondFlashDriver XFER blockData"); + co_await transmitChunk(_blockData); + } - co_await transmitChunk(std::span(_blockData.data(), _blockData.size())); + while (co_await checkIsBusy()) + { + LOG_TRACE("WinbondFlashDriver wait for write completion"); + co_yield CoroUtils::CoroQueueMainLoop::GetInstance(); + } - getSpiBus()->setCsPinHigh(); + LOG_TRACE("WinbondFlashDriver XFER completed write"); } CoroUtils::Task> requestReadBlock( - const std::uint32_t _address, - const std::uint16_t _blockSize) noexcept + std::uint32_t _address, + std::uint16_t _blockSize) noexcept { constexpr std::uint16_t PageSize = 256; - assert(_blockSize < PageSize); + LOG_TRACE(fmt::format("WinbondFlashDriver requestReadBlock: {},{}", _blockSize, PageSize)); + assert(_blockSize <= PageSize); - using TTupleRead = decltype(std::forward_as_tuple( - WindbondCommandSet::ReadData, - static_cast(_address & 0x00'FF'00'00 >> 16), - static_cast(_address & 0x00'00'FF'00 >> 8), - static_cast(_address & 0x00'00'00'FF))); - constexpr bool ManageSpiTransactions = false; + while (co_await checkIsBusy()) + { + LOG_TRACE("WinbondFlashDriver READ wait for read completion"); + co_yield CoroUtils::CoroQueueMainLoop::GetInstance(); + } - getSpiBus()->setCsPinLow(); + std::span receivedData{}; - co_await prepareXferTransaction(std::forward_as_tuple( - WindbondCommandSet::ReadData, - static_cast((_address & 0x00'FF'00'00) >> 16), - static_cast((_address & 0x00'00'FF'00) >> 8), - static_cast(_address & 0x00'00'00'FF))); + { + ChipSelectGuard csGuard{this}; - auto& transmitBuffer = getSpiBus()->getDmaBufferTransmit(); - auto& receiveBuffer = getSpiBus()->getDmaBufferReceive(); + LOG_TRACE("WinbondFlashDriver prepareXferTransaction BEGIN"); + co_await prepareXferTransaction(std::forward_as_tuple( + WindbondCommandSet::ReadData, + static_cast((_address & 0x00'FF'00'00) >> 16), + static_cast((_address & 0x00'00'FF'00) >> 8), + static_cast(_address & 0x00'00'00'FF))); - std::fill_n( - transmitBuffer.begin(), - _blockSize, - WindbondCommandSet::DummyByte); + LOG_TRACE("WinbondFlashDriver prepareXferTransaction COMPLETION"); + auto& transmitBuffer = getSpiBus()->getDmaBufferTransmit(); + auto& receiveBuffer = getSpiBus()->getDmaBufferReceive(); + + std::fill_n(transmitBuffer.begin(), _blockSize, WindbondCommandSet::DummyByte); - auto receivedData = co_await xferTransaction( - std::span(transmitBuffer.data(), _blockSize), - std::span(receiveBuffer.data(), _blockSize)); + receivedData = co_await xferTransaction( + std::span(transmitBuffer.data(), _blockSize), + std::span(receiveBuffer.data(), _blockSize)); - getSpiBus()->setCsPinHigh(); + LOG_TRACE("WinbondFlashDriver xferTransaction receive"); + } + LOG_TRACE("WinbondFlashDriver return back"); co_return receivedData; } - void requestFastReadBlock(const std::uint32_t _address, const std::uint8_t _blockSize) noexcept + void requestFastReadBlock(std::uint32_t _address, const std::uint8_t _blockSize) noexcept { } CoroUtils::VoidTask requestChipErase() noexcept { + ChipSelectGuard csGuard{this}; co_await prepareXferTransaction(std::forward_as_tuple(WindbondCommandSet::ChipErase)); } CoroUtils::VoidTask requestPowerDown() noexcept { + ChipSelectGuard csGuard{this}; co_await prepareXferTransaction(std::forward_as_tuple(WindbondCommandSet::PowerDownMode)); } CoroUtils::VoidTask resumePowerDown() noexcept { + ChipSelectGuard csGuard{this}; co_await prepareXferTransaction( std::forward_as_tuple(WindbondCommandSet::ResumePowerDownMode)); } CoroUtils::Task> requestDeviceId() noexcept { + ChipSelectGuard csGuard{this}; auto receivedData = co_await prepareXferTransaction( std::forward_as_tuple( WindbondCommandSet::ReadUniqueId, @@ -129,6 +169,7 @@ template class WinbondFlashDriver CoroUtils::Task requestJEDEDCId() noexcept { + ChipSelectGuard csGuard{this}; auto receivedData = co_await prepareXferTransaction( std::forward_as_tuple(WindbondCommandSet::ReadJedecId), WindbondCommandSet::DummyByte, @@ -144,6 +185,20 @@ template class WinbondFlashDriver co_return JedecDeviceId; } + CoroUtils::Task checkIsBusy() noexcept + { + ChipSelectGuard csGuard{this}; + + LOG_TRACE("WinbondFlashDriver checkIsBusy"); + + auto busyStatusRegister = co_await prepareXferTransaction( + std::forward_as_tuple(WindbondCommandSet::ReadStatusRegister1), + WindbondCommandSet::DummyByte); + + LOG_TRACE("WinbondFlashDriver checkIsBusy completion"); + co_return busyStatusRegister[0] & WindbondCommandSet::Masks::EraseWriteInProgress; + } + const auto getSpiBus() const noexcept { return &m_spiBus; @@ -154,8 +209,25 @@ template class WinbondFlashDriver return &m_spiBus; } + CoroUtils::VoidTask eraseSector(std::uint32_t _address) + { + { + ChipSelectGuard guard{this}; + co_await prepareXferTransaction(std::forward_as_tuple( + WindbondCommandSet::SectorErase4KB, + static_cast((_address & 0x00'FF'00'00) >> 16), + static_cast((_address & 0x00'00'FF'00) >> 8), + static_cast(_address & 0x00'00'00'FF))); + } + while (co_await checkIsBusy()) + { + LOG_TRACE("WinbondFlashDriver wait for the eraseSector completion"); + co_yield CoroUtils::CoroQueueMainLoop::GetInstance(); + } + } + private: - template + template CoroUtils::Task> prepareXferTransaction( TCommand&& _command, Args&&... _argList) @@ -163,9 +235,6 @@ template class WinbondFlashDriver auto& transmitBuffer = getSpiBus()->getDmaBufferTransmit(); auto& receiveBuffer = getSpiBus()->getDmaBufferReceive(); - if constexpr (manageCsPin) - getSpiBus()->setCsPinLow(); - processTransmitBuffer(transmitBuffer, std::forward(_command)); constexpr std::size_t CommandSize = std::tuple_size_v; @@ -180,29 +249,17 @@ template class WinbondFlashDriver std::span(transmitBuffer.data(), DummyListSize), std::span(receiveBuffer.data(), DummyListSize)); - if constexpr (manageCsPin) - getSpiBus()->setCsPinHigh(); - co_return std::span(receiveBuffer.data(), DummyListSize); } - template - CoroUtils::VoidTask prepareXferTransaction(TTuple&& _command) + template CoroUtils::VoidTask prepareXferTransaction(TTuple&& _command) { auto& transmitBuffer = getSpiBus()->getDmaBufferTransmit(); - auto& receiveBuffer = getSpiBus()->getDmaBufferReceive(); - - if constexpr (manageCsPin) - getSpiBus()->setCsPinLow(); - processTransmitBuffer(transmitBuffer, std::forward(_command)); constexpr std::size_t CommandSize = std::tuple_size_v; co_await transmitChunk(std::span(transmitBuffer.data(), CommandSize)); - - if constexpr (manageCsPin) - getSpiBus()->setCsPinHigh(); } CoroUtils::Task> xferTransaction( @@ -274,9 +331,7 @@ template class WinbondFlashDriver void await_suspend(std::coroutine_handle<> thisCoroutine) const { pThis->getSpiBus()->transmitBuffer( - pTransmitBuffer, - thisCoroutine.address(), - restoreInSpiCtx); + pTransmitBuffer, thisCoroutine.address(), restoreInSpiCtx); } }; @@ -285,7 +340,7 @@ template class WinbondFlashDriver std::span _pReceiveBuffer) noexcept { return XferAwaiter{ - .restoreInSpiCtx = true, + .restoreInSpiCtx = false, .pTransmitBuffer = _pTrasnmitBuffer, .pReceiveBuffer = _pReceiveBuffer, .pThis = this}; @@ -294,7 +349,7 @@ template class WinbondFlashDriver auto transmitChunk(std::span _pTrasnmitBuffer) noexcept { return TransmitAwaiter{ - .restoreInSpiCtx = true, .pTransmitBuffer = _pTrasnmitBuffer, .pThis = this}; + .restoreInSpiCtx = false, .pTransmitBuffer = _pTrasnmitBuffer, .pThis = this}; } private: diff --git a/Firmware/filesystem/CMakeLists.txt b/Firmware/filesystem/CMakeLists.txt new file mode 100644 index 00000000..6b25ded4 --- /dev/null +++ b/Firmware/filesystem/CMakeLists.txt @@ -0,0 +1,37 @@ +add_library(watch_filesystem INTERFACE + + filesystem/filesystem_holder.hpp + filesystem/block_device_wrapper/combiner_block_device.hpp +) + +target_sources(watch_filesystem INTERFACE + filesystem/block_device_wrapper/ih_block_device.hpp + filesystem/block_device_wrapper/heap_block_device.hpp + filesystem/block_device_wrapper/adaptor_block_device.hpp + filesystem/block_device_wrapper/spi_block_device.hpp +) + +target_include_directories(watch_filesystem INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) + +target_link_libraries(watch_filesystem INTERFACE + littlefs +) + +target_link_options( + watch_filesystem + INTERFACE + ${CPU_FLAGS} +) + +target_compile_options( + watch_filesystem + INTERFACE + ${COMMON_FLAGS} +) + +target_link_libraries( + watch_filesystem INTERFACE + drivers_ih + windbond_spi_flash_driver + logger_service +) \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/block_device_wrapper/adaptor_block_device.hpp b/Firmware/filesystem/filesystem/block_device_wrapper/adaptor_block_device.hpp new file mode 100644 index 00000000..558a78ee --- /dev/null +++ b/Firmware/filesystem/filesystem/block_device_wrapper/adaptor_block_device.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include "ih_block_device.hpp" +#include + +#include +#include + +namespace Filesystem::BlockDevice +{ + +template +class LogAdaptorBlockDevice : public BlockDeviceEntity> +{ + +public: + constexpr std::uint32_t getBlockSize() const noexcept + { + return m_wrappedNode.getBlockSize(); + } + constexpr std::uint32_t getBlocksCount() const noexcept + { + return m_wrappedNode.getBlocksCount(); + } + constexpr std::uint32_t getReadSize() const noexcept + { + return m_wrappedNode.getReadSize(); + } + constexpr std::uint32_t getProgSize() const noexcept + { + return m_wrappedNode.getProgSize(); + } + constexpr std::uint32_t getEraseSize() const noexcept + { + return m_wrappedNode.getEraseSize(); + } + + CoroUtils::VoidTask write( + std::uint32_t _address, + const std::uint8_t* _blockData, + std::uint32_t _blockSize) noexcept + { + LOG_DEBUG(fmt::format( + "Block Device WRITE to address: {address} blockData: {span_data:f} ", + fmt::arg("address", _address), + fmt::arg("span_data", std::span(_blockData, _blockSize)))); + + co_await m_wrappedNode.write(_address, _blockData, _blockSize); + } + CoroUtils::VoidTask read( + std::uint8_t* _pBlockOut, + std::uint32_t _address, + std::uint32_t _blockSize) noexcept + { + co_await m_wrappedNode.read(_pBlockOut, _address, _blockSize); + + LOG_DEBUG(fmt::format( + "Block Device READ from address: {address} blockData: {span_data:f} ", + fmt::arg("address", _address), + fmt::arg("span_data", std::span(static_cast(_pBlockOut), _blockSize)))); + } + +private: + TWrappee m_wrappedNode; +}; +} // namespace Filesystem::BlockDevice \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/block_device_wrapper/combiner_block_device.hpp b/Firmware/filesystem/filesystem/block_device_wrapper/combiner_block_device.hpp new file mode 100644 index 00000000..585e4e30 --- /dev/null +++ b/Firmware/filesystem/filesystem/block_device_wrapper/combiner_block_device.hpp @@ -0,0 +1,79 @@ +#pragma once +#include +#include + +namespace Filesystem::BlockDevice +{ +template class CombinerBlockDevice +{ +public: + using TCombinedBlocksStore = std::tuple; + + constexpr std::uint32_t getBlockSize() const noexcept + { + return getFirstBlock()->getBlockSize(); + } + constexpr std::uint32_t getBlocksCount() const noexcept + { + return getFirstBlock()->getBlocksCount(); + } + constexpr std::uint32_t getReadSize() const noexcept + { + return getFirstBlock()->getReadSize(); + } + constexpr std::uint32_t getProgSize() const noexcept + { + return getFirstBlock()->getProgSize(); + } + constexpr std::uint32_t getEraseSize() const noexcept + { + return getFirstBlock()->getEraseSize(); + } + + CoroUtils::VoidTask write( + std::uint32_t _address, + const std::uint8_t* _blockData, + std::uint32_t _blockSize) noexcept + { + co_await launchAllWrite( + _address, + _blockData, + _blockSize, + std::make_integer_sequence{}); + } + + CoroUtils::VoidTask read( + std::uint8_t* _pBlockOut, + std::uint32_t _address, + std::uint32_t _blockSize) noexcept + { + // TODO add comarison of the differrent block device reads + co_await getFirstBlock()->read(_pBlockOut, _address, _blockSize); + } + +private: + template + CoroUtils::VoidTask launchAllWrite( + std::uint32_t _address, + const std::uint8_t* _blockData, + std::uint32_t _blockSize, + std::integer_sequence) noexcept + { + (co_await std::get(m_combinedBlocks).write(_address, _blockData, _blockSize), ...); + } + + decltype(auto) getFirstBlock() noexcept + { + return &std::get<0>(m_combinedBlocks); + } + + decltype(auto) getFirstBlock() const noexcept + { + return &std::get<0>(m_combinedBlocks); + } + +private: + TCombinedBlocksStore m_combinedBlocks; +}; + +} // namespace Filesystem::BlockDevice \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/block_device_wrapper/heap_block_device.hpp b/Firmware/filesystem/filesystem/block_device_wrapper/heap_block_device.hpp new file mode 100644 index 00000000..50c950d5 --- /dev/null +++ b/Firmware/filesystem/filesystem/block_device_wrapper/heap_block_device.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "ih_block_device.hpp" +#include + +namespace Filesystem::BlockDevice +{ +inline constexpr std::size_t kBlockSize = 256; +inline constexpr std::size_t kSectorsCount = 65'536; +inline constexpr std::size_t kReadSize = 256; +inline constexpr std::size_t kEraseSize = 4096; + +template < + std::size_t BlockSize = kBlockSize, + std::size_t SectorsCount = kSectorsCount, + std::size_t ReadSize = kReadSize, + std::size_t EraseSize = kEraseSize, + std::size_t ProgSize = kReadSize> +class HeapBlockDevice + : public BlockDeviceEntity< + HeapBlockDevice> +{ +public: + constexpr HeapBlockDevice() + { + m_blockStorage.resize(SectorsCount); + std::ranges::for_each( + m_blockStorage, [](auto& storageBlock) { std::ranges::fill(storageBlock, 0xFF); }); + } + + constexpr std::uint32_t getBlockSize() const noexcept + { + return BlockSize; + } + constexpr std::uint32_t getBlocksCount() const noexcept + { + return SectorsCount; + } + constexpr std::uint32_t getReadSize() const noexcept + { + return ReadSize; + } + constexpr std::uint32_t getProgSize() const noexcept + { + return ProgSize; + } + constexpr std::uint32_t getEraseSize() const noexcept + { + return EraseSize; + } + + CoroUtils::VoidTask write( + std::uint32_t _address, + const std::uint8_t* _blockData, + std::uint32_t _blockSize) noexcept + { + std::uint32_t requestSize = _blockSize; + const std::uint8_t* pBlockRequest = _blockData; + std::uint32_t blockAddress{_address}; + while (requestSize > 0) + { + std::uint32_t highPart = blockAddress / getBlockSize(); + std::uint32_t lowPart = blockAddress % getBlockSize(); + + auto& pBlock = m_blockStorage[highPart]; + memcpy(pBlock.data() + lowPart, pBlockRequest, getProgSize()); + + blockAddress += getProgSize(); + pBlockRequest += getProgSize(); + requestSize -= getProgSize(); + } + co_return; + } + CoroUtils::VoidTask read( + std::uint8_t* _pBlockOut, + std::uint32_t _address, + std::uint32_t _blockSize) noexcept + { + std::uint32_t blockSize{_blockSize}; + std::uint8_t* pReadBuffer{_pBlockOut}; + std::uint32_t blockAddress{_address}; + + while (blockSize > 0) + { + std::uint32_t hi = blockAddress / getBlockSize(); + std::uint32_t lo = blockAddress % getBlockSize(); + + memcpy(pReadBuffer, m_blockStorage[hi].data() + lo, getReadSize()); + blockAddress += getReadSize(); + pReadBuffer += getReadSize(); + blockSize -= getReadSize(); + } + co_return; + } + +private: + static constexpr std::uint32_t kDefRequestSize = 1; + +private: + using TBlocksStorage = std::vector>; + TBlocksStorage m_blockStorage; +}; +} // namespace Wrapper \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/block_device_wrapper/ih_block_device.hpp b/Firmware/filesystem/filesystem/block_device_wrapper/ih_block_device.hpp new file mode 100644 index 00000000..ffeb0c0e --- /dev/null +++ b/Firmware/filesystem/filesystem/block_device_wrapper/ih_block_device.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +namespace Filesystem::BlockDevice +{ +template class BlockDeviceEntity +{ +public: + constexpr std::uint32_t getBlockSize() const noexcept + { + return offspring()->getBlockSize(); + } + constexpr std::uint32_t getBlocksCount() const noexcept + { + return offspring()->getBlocksCount(); + } + constexpr std::uint32_t getReadSize() const noexcept + { + return offspring()->getReadSize(); + } + constexpr std::uint32_t getProgSize() const noexcept + { + return offspring()->getProgSize(); + } + constexpr std::uint32_t getEraseSize() const noexcept + { + return offspring()->getEraseSize(); + } + + CoroUtils::VoidTask write(std::uint32_t _address, const std::uint8_t* _blockData, std::uint32_t _blockSize) noexcept + { + co_await offspring()->write(_address, _blockData, _blockSize); + } + CoroUtils::VoidTask read(std::uint8_t* pblockOut, std::uint32_t _address, std::uint32_t _blockSize) noexcept + { + co_await offspring()->read(pblockOut, _address, _blockSize); + } + +private: + constexpr auto offspring() + { + return static_cast(this); + } +}; +} // namespace Wrapper \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/block_device_wrapper/spi_block_device.hpp b/Firmware/filesystem/filesystem/block_device_wrapper/spi_block_device.hpp new file mode 100644 index 00000000..5330dda6 --- /dev/null +++ b/Firmware/filesystem/filesystem/block_device_wrapper/spi_block_device.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "ih_block_device.hpp" + +namespace Filesystem::BlockDevice +{ + +struct FlashBlockDeviceDescriptor +{ + static constexpr inline std::size_t kBlockSize = 256; + static constexpr inline std::size_t kSectorsCount = 65'536; + static constexpr inline std::size_t kReadSize = 256; + static constexpr inline std::size_t kEraseSize = 4096; +}; + +template + +class SpiFlashBlockDevice + : public BlockDeviceEntity> +{ +public: + constexpr std::uint32_t getBlockSize() const noexcept + { + return TBlockDeviceDescriptor::kBlockSize; + } + constexpr std::uint32_t getBlocksCount() const noexcept + { + return TBlockDeviceDescriptor::kSectorsCount; + } + constexpr std::uint32_t getReadSize() const noexcept + { + return TBlockDeviceDescriptor::kReadSize; + } + constexpr std::uint32_t getProgSize() const noexcept + { + return TBlockDeviceDescriptor::kBlockSize; + } + constexpr std::uint32_t getEraseSize() const noexcept + { + return TBlockDeviceDescriptor::kEraseSize; + } + +public: + CoroUtils::VoidTask write( + std::uint32_t _address, + const std::uint8_t* _blockData, + std::uint32_t _blockSize) noexcept + { + std::uint32_t requestSize = _blockSize; + const std::uint8_t* pBlockRequest = _blockData; + std::uint32_t blockAddress{_address}; + while (requestSize > 0) + { + co_await m_currentFlashDriver.pageWrite( + blockAddress, std::span(pBlockRequest, getProgSize())); + + blockAddress += getProgSize(); + pBlockRequest += getProgSize(); + requestSize -= getProgSize(); + } + co_return; + } + + CoroUtils::VoidTask read( + std::uint8_t* _pBlockOut, + std::uint32_t _address, + std::uint32_t _blockSize) noexcept + { + std::uint32_t blockSize{_blockSize}; + std::uint8_t* pReadBuffer{_pBlockOut}; + std::uint32_t blockAddress{_address}; + + while (blockSize > 0) + { + auto resultBuffer = + co_await m_currentFlashDriver.requestReadBlock(blockAddress, getReadSize()); + memcpy(pReadBuffer, resultBuffer.data(), getReadSize()); + + blockAddress += getReadSize(); + pReadBuffer += getReadSize(); + blockSize -= getReadSize(); + } + co_return; + } + +private: + TFlashDriver m_currentFlashDriver; +}; + +template +using SpiBlockDeviceDefaultDevice = SpiFlashBlockDevice; + +}; // namespace Filesystem::BlockDevice \ No newline at end of file diff --git a/Firmware/filesystem/filesystem/filesystem_holder.hpp b/Firmware/filesystem/filesystem/filesystem_holder.hpp new file mode 100644 index 00000000..b74ec342 --- /dev/null +++ b/Firmware/filesystem/filesystem/filesystem_holder.hpp @@ -0,0 +1,216 @@ +#pragma once +#include "block_device_wrapper/ih_block_device.hpp" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace Platform::Fs +{ +template class File; + +template class Holder; + +class FilesystemPasskey +{ + template friend class File; + + template friend class Holder; + +private: + FilesystemPasskey() + { + } + FilesystemPasskey(const FilesystemPasskey&) = default; + FilesystemPasskey& operator=(const FilesystemPasskey&) = delete; +}; +template class Holder +{ +public: + using This_t = Holder; + +public: + Holder() noexcept + { + LOG_DEBUG("FS: holder c-tor"); + m_fsConfig = createLfsConfig(); + } + + CoroUtils::VoidTask initializeFs() + { + LOG_DEBUG("FS: to initializeFs"); + + auto error = co_await lfs_mount(&m_fsInstance, &m_fsConfig); + + LOG_DEBUG(fmt::format("lfs_mount error is {}", error)); + if (error) + { + co_await lfs_format(&m_fsInstance, &m_fsConfig); + co_await lfs_mount(&m_fsInstance, &m_fsConfig); + } + } + + constexpr decltype(auto) fsInstance() noexcept + { + return &m_fsInstance; + } + CoroUtils::Task> openFile(std::string_view path) noexcept + { + assert(!path.empty()); + File retFile{&m_fsInstance}; + auto error = co_await lfs_file_open( + &m_fsInstance, + retFile.nativeHandle(FilesystemPasskey{}), + path.data(), + LFS_O_RDWR | LFS_O_CREAT); + + co_return retFile; + } + + constexpr TBlockDeviceEntity& getBlockDevice() noexcept + { + return m_blockDeviceHolder; + } + +private: +private: + static CoroUtils::Task readCall( + const lfs_config* c, + lfs_block_t block, + lfs_off_t off, + void* buffer, + lfs_size_t size) noexcept + { + auto pThis = reinterpret_cast(c->context); + co_await pThis->getBlockDevice().read( + static_cast(buffer), block * c->block_size + off, size); + co_return LFS_ERR_OK; + } + + static CoroUtils::Task progCall( + const lfs_config* c, + lfs_block_t block, + lfs_off_t off, + const void* buffer, + lfs_size_t size) noexcept + { + auto pThis = reinterpret_cast(c->context); + co_await pThis->getBlockDevice().write( + (block * c->block_size + off), reinterpret_cast(buffer), size); + + co_return LFS_ERR_OK; + } + + static CoroUtils::Task eraseCall(const lfs_config* c, lfs_block_t block) noexcept + { + auto pThis = reinterpret_cast(c->context); + co_return LFS_ERR_OK; + } + + static CoroUtils::Task syncCall(const lfs_config* c) noexcept + { + auto pThis = reinterpret_cast(c->context); + co_return LFS_ERR_OK; + } + +private: + constexpr lfs_config createLfsConfig() noexcept + { + constexpr std::size_t kBlockCycles = 500; + return lfs_config{ + .context = this, + .read = readCall, + .prog = progCall, + .erase = eraseCall, + .sync = syncCall, + + .read_size = m_blockDeviceHolder.getReadSize(), + .prog_size = m_blockDeviceHolder.getProgSize(), + + .block_size = m_blockDeviceHolder.getBlockSize(), + .block_count = m_blockDeviceHolder.getBlocksCount(), + + .block_cycles = kBlockCycles, + .cache_size = m_blockDeviceHolder.getBlockSize(), + .lookahead_size = m_blockDeviceHolder.getBlockSize(), + + .read_buffer = readBuffer.data(), + .prog_buffer = writeBuffer.data(), + .lookahead_buffer = lookaheadBuffer.data()}; + } + +private: + static constexpr inline std::size_t FsArraySize = 512; + using TDataHolder = std::array; + +private: + lfs_config m_fsConfig; + lfs_t m_fsInstance; + TBlockDeviceEntity m_blockDeviceHolder; + TDataHolder readBuffer; + TDataHolder writeBuffer; + TDataHolder lookaheadBuffer; +}; + +template class File +{ +public: + constexpr File(lfs_t* pFsHolder) noexcept + : m_pFileHandle{std::make_unique()}, m_pFsHolder{pFsHolder} + { + } + + constexpr File(File&& otherHandle) noexcept + : m_pFileHandle{std::move(otherHandle.m_pFileHandle)} + , m_pFsHolder{std::exchange(otherHandle.m_pFsHolder, nullptr)} + { + } + + ~File() + { + if (!m_pFsHolder && !m_pFileHandle) + return; + + CoroUtils::syncWait(lfs_file_close(m_pFsHolder, m_pFileHandle.get())); + LOG_DEBUG("~File::File()"); + } + + CoroUtils::VoidTask write(std::span dataHolder) noexcept + { + co_await lfs_file_write( + m_pFsHolder, + m_pFileHandle.get(), + dataHolder.data(), + static_cast(dataHolder.size())); + co_return; + } + + CoroUtils::Task> read(std::span outBuffer) noexcept + { + co_await lfs_file_read( + m_pFsHolder, + m_pFileHandle.get(), + outBuffer.data(), + static_cast(outBuffer.size())); + co_return outBuffer; + } + + lfs_file_t* nativeHandle(const FilesystemPasskey& passkey) noexcept + { + Meta::UnuseVar(passkey); + return m_pFileHandle.get(); + } + +private: + std::unique_ptr m_pFileHandle; + lfs_t* m_pFsHolder; +}; +} // namespace Platform::Fs diff --git a/Firmware/filesystem/filesystem/platform_filesystem.hpp b/Firmware/filesystem/filesystem/platform_filesystem.hpp new file mode 100644 index 00000000..4da8628c --- /dev/null +++ b/Firmware/filesystem/filesystem/platform_filesystem.hpp @@ -0,0 +1,2 @@ +#pragma once +#include "filesystem_holder.hpp" \ No newline at end of file diff --git a/Firmware/firmware_tests/CMakeLists.txt b/Firmware/firmware_tests/CMakeLists.txt index 1d52f398..380e6de6 100644 --- a/Firmware/firmware_tests/CMakeLists.txt +++ b/Firmware/firmware_tests/CMakeLists.txt @@ -10,10 +10,10 @@ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) enable_testing() -set (3RDPARTY_DIR "../3rdparty") find_package(GTest REQUIRED) add_subdirectory(coroutine) add_subdirectory(article_example) +add_subdirectory(testing_common) add_executable( ${PROJECT_NAME} @@ -35,7 +35,9 @@ add_executable( drivers/windond_flash/flash_fixture.hpp drivers/spi/mock_gpio.hpp drivers/spi/mock_spi.hpp -) + + filesystem/filesystem_fixture.hpp + filesystem/filesystem_in_memory_test.cpp ) mark_as_advanced( BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS @@ -63,9 +65,10 @@ target_link_libraries( watch_display lvgl_lib drivers_ih - + watch_filesystem buttons_driver windbond_spi_flash_driver + testing_common ) target_compile_features( diff --git a/Firmware/firmware_tests/buttons_driver/buttons_fixture.hpp b/Firmware/firmware_tests/buttons_driver/buttons_fixture.hpp index 6abed442..fbdfa27d 100644 --- a/Firmware/firmware_tests/buttons_driver/buttons_fixture.hpp +++ b/Firmware/firmware_tests/buttons_driver/buttons_fixture.hpp @@ -38,7 +38,7 @@ class ButtonsDriverTest : public ::testing::Test m_pButtonsDriver.onButtonEvent.connect([this](const Buttons::ButtonEvent& _buttonEvent) { m_pEventDispatcher->postEvent( {Graphics::Events::EventGroup::Buttons, - toUiEvent(_buttonEvent.buttonEvent), + Graphics::Events::to_underlying(toUiEvent(_buttonEvent.buttonEvent)), _buttonEvent.buttonId}); }); } diff --git a/Firmware/firmware_tests/clock_page_handler/clock_page_handler_fixture.hpp b/Firmware/firmware_tests/clock_page_handler/clock_page_handler_fixture.hpp index 31a58e8a..64726329 100644 --- a/Firmware/firmware_tests/clock_page_handler/clock_page_handler_fixture.hpp +++ b/Firmware/firmware_tests/clock_page_handler/clock_page_handler_fixture.hpp @@ -27,7 +27,7 @@ class ClockPageHandlerTest : public ::testing::Test pageWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/22 14:24:43", '/', ':')}); } @@ -45,7 +45,7 @@ class ClockPageHandlerTest : public ::testing::Test pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/22 14:24:43", '/', ':')}); } diff --git a/Firmware/firmware_tests/clock_page_handler/clock_page_view_handler_test.cpp b/Firmware/firmware_tests/clock_page_handler/clock_page_view_handler_test.cpp index 2fc1fb92..8c25e991 100644 --- a/Firmware/firmware_tests/clock_page_handler/clock_page_view_handler_test.cpp +++ b/Firmware/firmware_tests/clock_page_handler/clock_page_view_handler_test.cpp @@ -35,7 +35,7 @@ TEST_F(ClockPageHandlerTest, ViewStaysUnchangedWhenInviisible) // Only hours were changed pageWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/22 14:24:43", '/', ':')}); EXPECT_EQ(fakeView.getHours(), "14"); } @@ -53,7 +53,7 @@ TEST_F(ClockPageHandlerTest, OnlyHoursLabelIsRefreshedWhenNewDateTimeEventOccurs // Only hours were changed pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying( Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/22 15:24:43", '/', ':')}); } @@ -70,7 +70,7 @@ TEST_F(ClockPageHandlerTest, FulldateRefreshedWhenWeekdayWasChanged) // Only weekday was changed pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/29 14:24:43", '/', ':')}); } @@ -87,7 +87,7 @@ TEST_F(ClockPageHandlerTest, FulldateRefreshedWhenMonthWasChanged) // Only month was changed pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/07/13 14:24:43", '/', ':')}); } @@ -104,7 +104,7 @@ TEST_F(ClockPageHandlerTest, FulldateRefreshedWhenYearWasChanged) // Only year was changed pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2021/06/14 14:24:43", '/', ':')}); } @@ -121,7 +121,7 @@ TEST_F(ClockPageHandlerTest, WeekdayChangeHandledCorrectly) pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/23 14:24:43", '/', ':')}); } TEST_F(ClockPageHandlerTest, WeekdayChangeFromSundayToMondayHandledCorrectly) @@ -137,7 +137,7 @@ TEST_F(ClockPageHandlerTest, WeekdayChangeFromSundayToMondayHandledCorrectly) pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/28 14:24:43", '/', ':')}); EXPECT_CALL(fakePageMock, setWeekday(std::string_view("MON"))).Times(1); @@ -145,7 +145,7 @@ TEST_F(ClockPageHandlerTest, WeekdayChangeFromSundayToMondayHandledCorrectly) pageMockWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying( Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/29 14:24:43", '/', ':')}); } @@ -159,7 +159,7 @@ TEST_F(ClockPageHandlerTest, FormattingDateWorkCorrectlyForDigitsWithoutLeadingZ pageWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/22 15:24:43", '/', ':')}); EXPECT_EQ(fakeView.getFullDate(), "JUN/22/2020"); } @@ -174,7 +174,7 @@ TEST_F(ClockPageHandlerTest, FormattingDateWorkCorrectlyForDigitsWithLeadingZero pageWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/2 15:24:43", '/', ':')}); EXPECT_EQ(fakeView.getFullDate(), "JUN/02/2020"); } @@ -189,7 +189,7 @@ TEST_F(ClockPageHandlerTest, FormattingDateWorkCorrectlyForDigitsWithTrailingZer pageWatchHandler->handleEvent( {Graphics::Events::EventGroup::DateTime, - Graphics::Events::TDateTimeEvents::DateTimeChanged, + Graphics::Events::to_underlying(Graphics::Events::TDateTimeEvents::DateTimeChanged), TimeWrapper("2020/06/20 15:24:43", '/', ':')}); EXPECT_EQ(fakeView.getFullDate(), "JUN/20/2020"); } \ No newline at end of file diff --git a/Firmware/firmware_tests/coroutine/CMakeLists.txt b/Firmware/firmware_tests/coroutine/CMakeLists.txt index f3dd090c..ff35633c 100644 --- a/Firmware/firmware_tests/coroutine/CMakeLists.txt +++ b/Firmware/firmware_tests/coroutine/CMakeLists.txt @@ -16,18 +16,27 @@ target_sources( st7789_draft.hpp ) +target_include_directories( + coroutine_experiments_app + PRIVATE + fs_ideas + wrapper +) target_compile_features( coroutine_experiments_app PRIVATE cxx_std_20 ) -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /fsanitize=address") -else() - set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + if( ${ENABLE_SANITIZE_BUILD} ) + + if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /fsanitize=address") + else() + set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + endif() endif() target_link_libraries( @@ -37,7 +46,8 @@ target_link_libraries( watch_display UtilsLibrary logger_service + watch_filesystem ) if(UNIX) target_link_libraries( coroutine_experiments_app PUBLIC Threads::Threads) -endif() +endif() \ No newline at end of file diff --git a/Firmware/firmware_tests/coroutine/coroutine_thoughts.cpp b/Firmware/firmware_tests/coroutine/coroutine_thoughts.cpp index b6fe4d01..875a8bb7 100644 --- a/Firmware/firmware_tests/coroutine/coroutine_thoughts.cpp +++ b/Firmware/firmware_tests/coroutine/coroutine_thoughts.cpp @@ -12,36 +12,132 @@ #include #include #include +#include +#include #include #include #include "st7789_draft.hpp" +#include +#include +#include +#include +#include -CoroUtils::Task coroutineTask() +#include + +#include + +using TSpiBus = Interface::SpiTemplated::SpiBus; +using TFlashDriver = ExternalFlash::WinbondFlashDriver; + +using TSpiFlashBlockDevice = Filesystem::BlockDevice::SpiBlockDeviceDefaultDevice; + +using TDisplayDriver = DisplayDriver::GC9A01Compact; + +using TLogHeapBlockDevice = + Filesystem::BlockDevice::LogAdaptorBlockDevice>; + +using TCombinedBlockDevice = + Filesystem::BlockDevice::CombinerBlockDevice; + +using TFilesystem = Platform::Fs::Holder; + +using TFile = Platform::Fs::File; + +CoroUtils::VoidTask simpleRwTest( + TFilesystem& filesystem, + std::string_view fileName, + std::string_view fileData) { - int b = 32; + LOG_WARN("simpleRwTest begin"); + auto lfs = filesystem.fsInstance(); + { + LOG_WARN("FILE open begin"); + auto filename = std::move(co_await filesystem.openFile(fileName)); + co_await filename.write( + std::span(reinterpret_cast(fileData.data()), fileData.size())); + LOG_WARN("FILE open finalize"); + } - co_return b; -} + std::vector readFrom; + readFrom.resize(fileData.size()); + + { + LOG_WARN("FILE read begin"); + auto holdedFile = std::move(co_await filesystem.openFile(fileName)); + auto resultRead = co_await holdedFile.read(std::span(readFrom.data(), fileData.size())); + LOG_WARN("FILE read finalize"); + } -void showFlashDeviceId() + auto kCompareStringView{ + std::string_view{reinterpret_cast(readFrom.data()), readFrom.size()}}; + assert(fileData == kCompareStringView); + LOG_WARN("simpleRwTest finalize"); +} +CoroUtils::VoidTask fileTest(TFilesystem& filesystem) { - CoroUtils::Task task = coroutineTask(); - int testValue = co_await task; + constexpr auto kFileData = std::string_view("Hello world!"); + simpleRwTest(filesystem, "helloworld.txt", kFileData); + + auto kNmeaDataExample = std::string_view{ + "$GPGSA,A,1,,,,,,,,,,,,,,,* 1E\n" + "$GPGSV,1,1,0,,,,,,,,,,,,,,,,* 49\n" + "$GPRMC,114811.00,A,5548.2054190,N,03732.8518300,E,0.22,0.00,210618,0.0,E,A * 30\n" + "$GPGGA,114811.00,5548.2054190,N,03732.8518300,E,1,06,1.0,179.534,M,14.753,M,0.0,* 45\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,30,222,32,25,41,166,35,26,36,303,34,1 * 67\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,33,,,,,,,,,,,,,7 * 4A\n" + "$GPRMC,114812.00,A,5548.2052305,N,03732.8513810,E,0.17,0.00,210618,0.0,E,A * 3C\n" + "$GPGGA,114812.00,5548.2052305,N,03732.8513810,E,1,06,1.0,178.149,M,14.753,M,0.0,* 40\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,30,222,31,25,41,166,36,26,36,303,34,1 * 67\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + "$GPRMC,114813.00,A,5548.2048307,N,03732.8514121,E,0.28,0.00,210618,0.0,E,A * 34\n" + "$GPGGA,114813.00,5548.2048307,N,03732.8514121,E,1,06,1.0,172.326,M,14.753,M,0.0,* 45\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,31,222,32,25,41,166,36,26,36,303,34,1 * 65\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + "$GPRMC,114814.00,A,5548.2047440,N,03732.8516481,E,0.19,0.00,210618,0.0,E,A * 37\n" + "$GPGGA,114814.00,5548.2047440,N,03732.8516481,E,1,06,1.0,172.488,M,14.753,M,0.0,* 47\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,31,222,32,25,41,166,35,26,36,303,34,1 * 66\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + + }; + co_await simpleRwTest(filesystem, "nmeaData.txt", kNmeaDataExample); + + co_return; } int main() { - using TSpiBus = Interface::SpiTemplated::SpiBus; - using TDisplayDriver = DisplayDriver::GC9A01Compact; + TFilesystem platformFilesystem; + CoroUtils::syncWait(platformFilesystem.initializeFs()); + + CoroUtils::syncWait(fileTest(platformFilesystem)); + /*Display display{}; display.fillRectangle(0, 0, 220, 220, nullptr);*/ TDisplayDriver compactGc9A01; compactGc9A01.fillRectangle(0, 0, 0, 0, nullptr); compactGc9A01.initialize(); + // ST7789Coroutine displayCoro{ // Interface::Spi::createSpiBusAsync() // , 240 @@ -49,8 +145,6 @@ int main() //}; // displayCoro.fillRectangle(0, 0, 0, 0, nullptr); - showFlashDeviceId(); - while (true) { using namespace std::chrono_literals; diff --git a/Firmware/firmware_tests/coroutine/st7789_draft.hpp b/Firmware/firmware_tests/coroutine/st7789_draft.hpp index 93cbf020..7a23903c 100644 --- a/Firmware/firmware_tests/coroutine/st7789_draft.hpp +++ b/Firmware/firmware_tests/coroutine/st7789_draft.hpp @@ -264,7 +264,7 @@ // std::tuple_size::value> {} // ); // -// LOG_DEBUG_ENDL("Display initialized"); +// LOG_DEBUG("Display initialized"); // constexpr bool pushToMainQueue = true; // m_displayInitialized.set(pushToMainQueue); // } @@ -313,7 +313,7 @@ // , std::uint16_t _height // )noexcept // { -// LOG_DEBUG_ENDL("void initColumnRow"); +// LOG_DEBUG("void initColumnRow"); // } // // void setAddrWindow( diff --git a/Firmware/firmware_tests/coroutine/thoughts.hpp b/Firmware/firmware_tests/coroutine/thoughts.hpp index f0a6fd90..5b8d70a4 100644 --- a/Firmware/firmware_tests/coroutine/thoughts.hpp +++ b/Firmware/firmware_tests/coroutine/thoughts.hpp @@ -249,7 +249,7 @@ class ST7789CppCoro : public DisplayDriver::BaseSpiDisplayCoroutine : DisplayDriver::BaseSpiDisplayCoroutine(std::move(_busPtr), _width, _height) { initDisplay(); - LOG_DEBUG_ENDL("Display initialized"); + LOG_DEBUG("Display initialized"); } ~ST7789CppCoro() noexcept override diff --git a/Firmware/firmware_tests/drivers/spi/spi_fake_backend.hpp b/Firmware/firmware_tests/drivers/spi/spi_fake_backend.hpp index 3c4052d3..27de6a73 100644 --- a/Firmware/firmware_tests/drivers/spi/spi_fake_backend.hpp +++ b/Firmware/firmware_tests/drivers/spi/spi_fake_backend.hpp @@ -20,7 +20,8 @@ class SpiBusBackendStub void sendChunk(const std::uint8_t* _pBuffer, const size_t _bufferSize) noexcept { BusTransactionsTransmit.emplace_back(_pBuffer, _bufferSize); - m_spiMocker.sentData(std::span(_pBuffer,_bufferSize)); + auto kChunk{std::span(_pBuffer, _bufferSize)}; + m_spiMocker.sentData(kChunk); m_completedTransaction(); } diff --git a/Firmware/firmware_tests/drivers/windond_flash/flash_driver_test_suite.cpp b/Firmware/firmware_tests/drivers/windond_flash/flash_driver_test_suite.cpp index 9e4f7160..1291bf77 100644 --- a/Firmware/firmware_tests/drivers/windond_flash/flash_driver_test_suite.cpp +++ b/Firmware/firmware_tests/drivers/windond_flash/flash_driver_test_suite.cpp @@ -9,8 +9,44 @@ #include #include -using ::testing::Return; using ::testing::ContainerEq; +using ::testing::Return; + +namespace Testing::FlashCommandStubs +{ +constexpr std::size_t WriteEnableCommandLength = 1; +constexpr const std::array writeEnableCommand{ + WindbondCommandSet::WriteEnable}; + +constexpr std::size_t ReadStatusCommandLength = 1; +constexpr const std::array readStatusRegisterCommand{ + WindbondCommandSet::ReadStatusRegister1}; + +constexpr std::uint32_t address{0x10'00}; + +constexpr std::size_t ProgramPageCommandLength = 4; + +constexpr const std::array pageProgramCommand{ + WindbondCommandSet::PageProgram, + static_cast((address & 0x00'FF'00'00) >> 16), + static_cast((address & 0x00'00'FF'00) >> 8), + static_cast(address & 0x00'00'00'FF)}; + +constexpr const std::array erase4KSectorCommand{ + WindbondCommandSet::SectorErase4KB, + static_cast((address & 0x00'FF'00'00) >> 16), + static_cast((address & 0x00'00'FF'00) >> 8), + static_cast(address & 0x00'00'00'FF)}; +} // namespace Testing::FlashCommandStubs + +namespace Testing::Utils +{ +template +constexpr std::span make_span(const TByteSequence& toSpan) +{ + return std::span(reinterpret_cast(toSpan.data()), toSpan.size()); +} +} // namespace Testing::Utils TEST_F(FlashDriverTest, RequestJedecId) { @@ -21,7 +57,8 @@ TEST_F(FlashDriverTest, RequestJedecId) EXPECT_CALL(spiMockAccess(), receivedData) .Times(1) - .WillOnce(Return(std::span(reinterpret_cast(ExpectedStream.data()), ExpectedStream.size()))); + .WillOnce(Return(std::span( + reinterpret_cast(ExpectedStream.data()), ExpectedStream.size()))); auto fToJedecId = [](const TDataStream& _jedecId) { std::uint32_t result{}; @@ -44,65 +81,93 @@ MATCHER_P(SpanChecker, spanItem, "Span content equals") TEST_F(FlashDriverTest, RequestWriteBlock) { - EXPECT_CALL(getMockGpio(), setGpioLow()).Times(1); - EXPECT_CALL(getMockGpio(), setGpioHigh()).Times(1); + EXPECT_CALL(getMockGpio(), setGpioLow()).Times(6); + EXPECT_CALL(getMockGpio(), setGpioHigh()).Times(6); auto TransmitData{std::array{0xEF, 0xFF, 0x18, 0x19, 0x20, 0x21, 0x22}}; - constexpr std::uint32_t address{0x10'00}; + testing::Sequence sequence; - constexpr std::size_t WriteEnableCommandLength = 1; - constexpr const std::array writeEnableCommand {WindbondCommandSet::WriteEnable}; + // https://gist.github.com/cppengineer/f1b6bc0f04ac7c29e963364f2c564a5e - constexpr std::size_t ProgramPageCommandLength = 4; - constexpr const std::array pageProgramCommand{ - WindbondCommandSet::PageProgram, - static_cast(address & 0x00'FF'00'00 >> 16), - static_cast(address & 0x00'00'FF'00 >> 8), - static_cast(address & 0x00'00'00'FF) + const auto writeEnableSpan = + Testing::Utils::make_span(Testing::FlashCommandStubs::writeEnableCommand); + + TDataStream ExpectedStream{std::byte(0xEF), std::byte(0x40), std::byte(0x18)}; + TDataStream kDummyByte{std::byte(0x00)}; + + EXPECT_CALL(spiMockAccess(), receivedData) + .Times(3) + .WillOnce(Return(Testing::Utils::make_span(kDummyByte))); + + const auto kEraseSectorCommandSpan = + Testing::Utils::make_span(Testing::FlashCommandStubs::erase4KSectorCommand); + + auto fIsBusyChecker = [this, &sequence, kDummyByte] { + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::FlashCommandStubs::readStatusRegisterCommand)) + .Times(1) + .InSequence(sequence); + + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::Utils::make_span(kDummyByte))) + .Times(1) + .InSequence(sequence); }; - testing::Sequence sequence; + { - // https://gist.github.com/cppengineer/f1b6bc0f04ac7c29e963364f2c564a5e + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(writeEnableSpan)) + .Times(1) + .InSequence(sequence); + fIsBusyChecker(); + } - const auto writeEnableSpan = - std::span(writeEnableCommand.data(), writeEnableCommand.size()); + { + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(kEraseSectorCommandSpan)) + .Times(1) + .InSequence(sequence); + fIsBusyChecker(); + } - const auto ProgramCommandSpan = std::span(pageProgramCommand.data(), pageProgramCommand.size()); - EXPECT_CALL(spiMockAccess(), sentData) - .With(SpanChecker(writeEnableSpan)) - .Times(1) - .InSequence(sequence); + { - EXPECT_CALL(spiMockAccess(), sentData) - .With(SpanChecker(ProgramCommandSpan)) - .Times(1) - .InSequence(sequence); - EXPECT_CALL(spiMockAccess(), sentData) - .With(SpanChecker(std::span(TransmitData.data(), TransmitData.size()))) - .Times(1) - .InSequence(sequence); + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::FlashCommandStubs::pageProgramCommand)) + .Times(1) + .InSequence(sequence); - auto task = flashDriver.pageWrite(address, std::span(TransmitData.data(), TransmitData.size())); + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::Utils::make_span(TransmitData))) + .Times(1) + .InSequence(sequence); + + fIsBusyChecker(); + } + + auto task = flashDriver.pageWrite( + Testing::FlashCommandStubs::address, Testing::Utils::make_span(TransmitData)); CoroUtils::syncWait(task); } TEST_F(FlashDriverTest, RequestReadBlock) { - EXPECT_CALL(getMockGpio(), setGpioLow()).Times(1); - EXPECT_CALL(getMockGpio(), setGpioHigh()).Times(1); + EXPECT_CALL(getMockGpio(), setGpioLow()).Times(2); + EXPECT_CALL(getMockGpio(), setGpioHigh()).Times(2); using TStream = std::array; - auto ReceiveData{ - TStream{0xEF, 0xFF, 0x18, 0x19, 0x20, 0x21, 0x22}}; + auto ReceiveData{TStream{0xEF, 0xFF, 0x18, 0x19, 0x20, 0x21, 0x22}}; auto Dummy{TStream{}}; + TDataStream kDummyByte{std::byte(0x00)}; + EXPECT_CALL(spiMockAccess(), receivedData) - .Times(1) + .WillOnce(Return(Testing::Utils::make_span(kDummyByte))) .WillOnce(Return(std::span( reinterpret_cast(ReceiveData.data()), ReceiveData.size()))); @@ -121,20 +186,30 @@ TEST_F(FlashDriverTest, RequestReadBlock) const auto DummySpan = std::span(Dummy.data(), Dummy.size()); - const auto ReadDataSpan = - std::span(readCommand.data(), readCommand.size()); + const auto ReadDataSpan = std::span(readCommand.data(), readCommand.size()); + + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::FlashCommandStubs::readStatusRegisterCommand)) + .Times(1) + .InSequence(sequence); + + EXPECT_CALL(spiMockAccess(), sentData) + .With(SpanChecker(Testing::Utils::make_span(kDummyByte))) + .Times(1) + .InSequence(sequence); EXPECT_CALL(spiMockAccess(), sentData) .With(SpanChecker(ReadDataSpan)) .Times(1) .InSequence(sequence); + EXPECT_CALL(spiMockAccess(), sentData) .With(SpanChecker(std::span(DummySpan.data(), DummySpan.size()))) .Times(1) .InSequence(sequence); auto task = flashDriver.requestReadBlock(address, ReceiveData.size()); - auto readSpan = CoroUtils::syncWait(task); + auto readSpan{CoroUtils::syncWait(task)}; EXPECT_TRUE(std::ranges::equal(readSpan, ReceiveData)); } \ No newline at end of file diff --git a/Firmware/firmware_tests/drivers/windond_flash/flash_fixture.hpp b/Firmware/firmware_tests/drivers/windond_flash/flash_fixture.hpp index 6eb8c691..870049f1 100644 --- a/Firmware/firmware_tests/drivers/windond_flash/flash_fixture.hpp +++ b/Firmware/firmware_tests/drivers/windond_flash/flash_fixture.hpp @@ -13,6 +13,7 @@ // TODO Fix include more clearly #include "../spi/spi_fake_backend.hpp" #include "../spi/mock_gpio.hpp" +#include class FlashDriverTest : public ::testing::Test { @@ -25,8 +26,15 @@ class FlashDriverTest : public ::testing::Test protected: void SetUp() override { + m_coroLoopExecutor.startCoroLoop(); } + void TearDown() override + { + m_coroLoopExecutor.stopCoroutineLoop(); + } + + decltype(auto) getMockGpio() { return flashDriver.getSpiBus()->getBackendImpl().accesToCsPin(); @@ -44,4 +52,7 @@ class FlashDriverTest : public ::testing::Test protected: TFlashTestDriver flashDriver; + +private: + Testing::Common::CoroutineLoopExecutor m_coroLoopExecutor; }; diff --git a/Firmware/firmware_tests/filesystem/filesystem_fixture.hpp b/Firmware/firmware_tests/filesystem/filesystem_fixture.hpp new file mode 100644 index 00000000..c1effa56 --- /dev/null +++ b/Firmware/firmware_tests/filesystem/filesystem_fixture.hpp @@ -0,0 +1,89 @@ +#pragma once +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +// TODO Fix include more clearly +#include "../drivers/spi/mock_gpio.hpp" +#include "../drivers/spi/spi_fake_backend.hpp" +#include "filesystem_test_data.hpp" + +#include + +using TFlashTestDriver = ExternalFlash::WinbondFlashDriver< + Interface::SpiTemplated::SpiBus>; + +using TSpiFlashBlockDevice = Filesystem::BlockDevice::SpiBlockDeviceDefaultDevice; + +using TLogHeapBlockDevice = + Filesystem::BlockDevice::LogAdaptorBlockDevice>; + +using TCombinedBlockDevice = + Filesystem::BlockDevice::CombinerBlockDevice; + +using TWrappedFilesystem = Platform::Fs::Holder; + +using THeapFilesystem = Platform::Fs::Holder< + Filesystem::BlockDevice::LogAdaptorBlockDevice>>; + +template struct TestParamPlaceholder +{ + using THoldedFilesystem = TFilesystem; + std::vector testData; +}; + +static std::tuple, TestParamPlaceholder> + kFilesystemTestParams{ + {{HelloWorldData, NmeaData}}, + {{HelloWorldData, NmeaData}}, + }; + +// http://www.ashermancinelli.com/gtest-type-val-param +template class FilesystemTopLevelTestFixture : public ::testing::Test +{ + +public: + FilesystemTopLevelTestFixture() + : m_params{std::get>(kFilesystemTestParams)} + + { + } + +protected: + void SetUp() override + { + m_coroLoopExecutor.startCoroLoop(); + CoroUtils::syncWait(m_testFilesystem.initializeFs()); + } + + void TearDown() override + { + m_coroLoopExecutor.stopCoroutineLoop(); + } + +protected: + TestParamPlaceholder m_params; + Filesystem m_testFilesystem; + +private: + Testing::Common::CoroutineLoopExecutor m_coroLoopExecutor; +}; \ No newline at end of file diff --git a/Firmware/firmware_tests/filesystem/filesystem_in_memory_test.cpp b/Firmware/firmware_tests/filesystem/filesystem_in_memory_test.cpp new file mode 100644 index 00000000..9099efb7 --- /dev/null +++ b/Firmware/firmware_tests/filesystem/filesystem_in_memory_test.cpp @@ -0,0 +1,47 @@ +#include "filesystem_fixture.hpp" + +#include +#include +#include + + +//https://stackoverflow.com/questions/56115790/gtest-parametrized-tests-for-different-types +// http://www.ashermancinelli.com/gtest-type-val-param + +TYPED_TEST_SUITE_P(FilesystemTopLevelTestFixture); + + +TYPED_TEST_P(FilesystemTopLevelTestFixture, CheckFileReadWriteProcedure) +{ + for (auto const& testParamData : this->m_params.testData) + { + auto& filesystemInstance = this->m_testFilesystem; + auto lfs = filesystemInstance.fsInstance(); + { + auto filePath = testParamData.filename; + auto filename = std::move(CoroUtils::syncWait(filesystemInstance.openFile(filePath))); + CoroUtils::syncWait(filename.write(std::span( + reinterpret_cast(testParamData.fileData.data()), + testParamData.fileData.size()))); + } + + std::vector readFrom; + readFrom.resize(testParamData.fileData.size()); + + { + auto holdedFile = std::move(CoroUtils::syncWait(filesystemInstance.openFile(testParamData.filename))); + auto resultRead = CoroUtils::syncWait( + holdedFile.read(std::span(readFrom.data(), testParamData.fileData.size()))); + } + + auto kCompareStringView{ + std::string_view{reinterpret_cast(readFrom.data()), readFrom.size()}}; + EXPECT_EQ(kCompareStringView, testParamData.fileData); + } +} + + +REGISTER_TYPED_TEST_SUITE_P(FilesystemTopLevelTestFixture, CheckFileReadWriteProcedure); + +using TTestFilesystems = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(FilesystemTests, FilesystemTopLevelTestFixture, TTestFilesystems); diff --git a/Firmware/firmware_tests/filesystem/filesystem_test_data.hpp b/Firmware/firmware_tests/filesystem/filesystem_test_data.hpp new file mode 100644 index 00000000..3f8e4f0b --- /dev/null +++ b/Firmware/firmware_tests/filesystem/filesystem_test_data.hpp @@ -0,0 +1,47 @@ +#pragma once + +struct FilesystemParamInterfaceData +{ + std::string_view fileData; + std::string_view filename; +}; + +inline constexpr auto HelloWorldData = + FilesystemParamInterfaceData{.fileData = "Hello world!", .filename = "helloworld.txt"}; + +constexpr auto kNmeaDataExample = std::string_view{ + "$GPGSA,A,1,,,,,,,,,,,,,,,* 1E\n" + "$GPGSV,1,1,0,,,,,,,,,,,,,,,,* 49\n" + "$GPRMC,114811.00,A,5548.2054190,N,03732.8518300,E,0.22,0.00,210618,0.0,E,A * 30\n" + "$GPGGA,114811.00,5548.2054190,N,03732.8518300,E,1,06,1.0,179.534,M,14.753,M,0.0,* 45\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,30,222,32,25,41,166,35,26,36,303,34,1 * 67\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,33,,,,,,,,,,,,,7 * 4A\n" + "$GPRMC,114812.00,A,5548.2052305,N,03732.8513810,E,0.17,0.00,210618,0.0,E,A * 3C\n" + "$GPGGA,114812.00,5548.2052305,N,03732.8513810,E,1,06,1.0,178.149,M,14.753,M,0.0,* 40\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,30,222,31,25,41,166,36,26,36,303,34,1 * 67\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + "$GPRMC,114813.00,A,5548.2048307,N,03732.8514121,E,0.28,0.00,210618,0.0,E,A * 34\n" + "$GPGGA,114813.00,5548.2048307,N,03732.8514121,E,1,06,1.0,172.326,M,14.753,M,0.0,* 45\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,31,222,32,25,41,166,36,26,36,303,34,1 * 65\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + "$GPRMC,114814.00,A,5548.2047440,N,03732.8516481,E,0.19,0.00,210618,0.0,E,A * 37\n" + "$GPGGA,114814.00,5548.2047440,N,03732.8516481,E,1,06,1.0,172.488,M,14.753,M,0.0,* 47\n" + "$GPGSA,A,3,02,21,25,26,31,,,,,,,,5.4,1.2,5.3,1 * 2B\n" + "$GAGSA,A,3,30,,,,,,,,,,,,0.0,0.0,0.0,3 * 3F\n" + "$GPGSV,2,1,05,02,23,059,28,21,31,222,32,25,41,166,35,26,36,303,34,1 * 66\n" + "$GPGSV,2,2,05,31,27,254,34,,,,,,,,,,,,,1 * 52\n" + "$GAGSV,1,1,01,30,23,183,32,,,,,,,,,,,,,7 * 4B\n" + +}; + +inline constexpr auto NmeaData = + FilesystemParamInterfaceData{.fileData = kNmeaDataExample, .filename = "nmea_data.txt"}; diff --git a/Firmware/firmware_tests/main_window/mainwindow_model_test.cpp b/Firmware/firmware_tests/main_window/mainwindow_model_test.cpp index 2f62d1c4..292179e8 100644 --- a/Firmware/firmware_tests/main_window/mainwindow_model_test.cpp +++ b/Firmware/firmware_tests/main_window/mainwindow_model_test.cpp @@ -28,7 +28,7 @@ TEST_F(MainWindowTest, HandleNavigateToNextPage_ButtonClickEvent) { m_pMainWindow->getEventDispatcher().postEvent( {Graphics::Events::EventGroup::Buttons, - Graphics::Events::TButtonsEvents::ButtonClicked, + Graphics::Events::to_underlying(Graphics::Events::TButtonsEvents::ButtonClicked), Graphics::Events::HardwareButtonId::kLeftButtonTop}); m_pMainWindow->getEventDispatcher().processEventQueue(); @@ -44,7 +44,7 @@ TEST_F(MainWindowTest, HandleNavigateNextFromLastPage_ButtonClickEvent) m_pMainWindow->getEventDispatcher().postEvent( {Graphics::Events::EventGroup::Buttons, - Graphics::Events::TButtonsEvents::ButtonClicked, + Graphics::Events::to_underlying(Graphics::Events::TButtonsEvents::ButtonClicked), Graphics::Events::HardwareButtonId::kLeftButtonTop}); m_pMainWindow->getEventDispatcher().processEventQueue(); @@ -58,7 +58,7 @@ TEST_F(MainWindowTest, HandleNavigateToPreviousPageFromInitialState_ButtonClickE { m_pMainWindow->getEventDispatcher().postEvent( {Graphics::Events::EventGroup::Buttons, - Graphics::Events::TButtonsEvents::ButtonClicked, + Graphics::Events::to_underlying(Graphics::Events::TButtonsEvents::ButtonClicked), Graphics::Events::HardwareButtonId::kLeftButtonBottom}); m_pMainWindow->getEventDispatcher().processEventQueue(); diff --git a/Firmware/firmware_tests/testing_common/CMakeLists.txt b/Firmware/firmware_tests/testing_common/CMakeLists.txt new file mode 100644 index 00000000..17807932 --- /dev/null +++ b/Firmware/firmware_tests/testing_common/CMakeLists.txt @@ -0,0 +1,5 @@ + +add_library( testing_common INTERFACE ) + +target_link_libraries(testing_common INTERFACE UtilsLibrary) +target_include_directories(testing_common INTERFACE ${CMAKE_CURRENT_LIST_DIR}/inc) diff --git a/Firmware/firmware_tests/testing_common/inc/testing_common/coroutine_loop_executor.hpp b/Firmware/firmware_tests/testing_common/inc/testing_common/coroutine_loop_executor.hpp new file mode 100644 index 00000000..d89174c7 --- /dev/null +++ b/Firmware/firmware_tests/testing_common/inc/testing_common/coroutine_loop_executor.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +namespace Testing::Common +{ +class CoroutineLoopExecutor +{ +public: + void startCoroLoop() + { + + m_coroLoopWorker = std::make_unique([stopSource = m_stopSource.get_token()] { + while (!stopSource.stop_requested()) + { + CoroUtils::CoroQueueMainLoop::GetInstance().processQueue(); + } + }); + } + + void stopCoroutineLoop() + { + m_stopSource.request_stop(); + } + +private: + std::stop_source m_stopSource; + std::unique_ptr m_coroLoopWorker; + +}; + +} \ No newline at end of file diff --git a/Firmware/graphics/gs_event_dispatcher.hpp b/Firmware/graphics/gs_event_dispatcher.hpp index 7c8bd80d..c224f315 100644 --- a/Firmware/graphics/gs_event_dispatcher.hpp +++ b/Firmware/graphics/gs_event_dispatcher.hpp @@ -33,7 +33,7 @@ class EventDispatcher : private Utils::noncopyable private: static constexpr inline int EventsCount = - Events::enumConvert(EventGroup::EventGroupEnd); + Events::to_underlying(EventGroup::EventGroupEnd); static constexpr inline int EventPoolSize = 16; using TEventsMap = etl::vector, EventsCount>; diff --git a/Firmware/graphics/gs_lvgl_service.cpp b/Firmware/graphics/gs_lvgl_service.cpp index 103433c4..f7048f8d 100644 --- a/Firmware/graphics/gs_lvgl_service.cpp +++ b/Firmware/graphics/gs_lvgl_service.cpp @@ -71,9 +71,8 @@ class LvglGraphicsService::GSLvglServiceImpl private: void initLvglLogger() noexcept { - auto lvglLoggerCallback = cbc::obtain_connector([](const char* loggerMessage) { - LOG_DEBUG_ENDL(loggerMessage); - }); + auto lvglLoggerCallback = + cbc::obtain_connector([](const char* loggerMessage) { LOG_DEBUG(loggerMessage); }); #if LV_USE_LOG == 1 lv_log_register_print_cb(lvglLoggerCallback); #endif @@ -92,10 +91,7 @@ class LvglGraphicsService::GSLvglServiceImpl auto monitorCallback = cbc::obtain_connector([](lv_disp_drv_t* disp_drv, uint32_t time, uint32_t px) { - LOG_DEBUG("Refresh time:"); - LOG_DEBUG_ENDL(time); - LOG_DEBUG("Refreshed pixels:"); - LOG_DEBUG_ENDL(px); + LOG_DEBUG(fmt::format("Refresh time: {}, pixels: {}", time, px)); }); m_glDisplayDriver.monitor_cb = monitorCallback; diff --git a/Firmware/graphics/ih/gs_events.hpp b/Firmware/graphics/ih/gs_events.hpp index 85146ecf..e24d6451 100644 --- a/Firmware/graphics/ih/gs_events.hpp +++ b/Firmware/graphics/ih/gs_events.hpp @@ -1,12 +1,13 @@ #pragma once #include +#include #include namespace Graphics::Events { -enum class EventGroup +enum class EventGroup : std::uint8_t { Battery, Heartrate, @@ -22,7 +23,7 @@ enum class EventGroup struct TEvent { EventGroup eventGroup; - std::any eventType; + std::uint8_t eventType; std::any eventData; }; @@ -31,54 +32,53 @@ enum class TEventKind EventBegin }; -template -constexpr auto enumConvert(TToConvert _valueToConvert) +template constexpr auto to_underlying(TToConvert _valueToConvert) { - return static_cast::type>(_valueToConvert); + return static_cast::type>(_valueToConvert); } -enum class TBatteryEvents +enum class TBatteryEvents : std::uint8_t { - BatteryEventsStart = enumConvert(TEventKind::EventBegin), + BatteryEventsStart = to_underlying(TEventKind::EventBegin), BatteryLevelChanged, BatteryEventsEnd }; -enum class THeartRateEvents +enum class THeartRateEvents : std::uint8_t { - HeartRateEventsBegin = enumConvert(TBatteryEvents::BatteryEventsEnd), + HeartRateEventsBegin = to_underlying(TBatteryEvents::BatteryEventsEnd), MeasureStarted, MeasureCompleted, MeasureFailed, HeartRateEventsEnd }; -enum class TDateTimeEvents +enum class TDateTimeEvents : std::uint8_t { - DateTimeEventsBegin = enumConvert(THeartRateEvents::HeartRateEventsEnd), + DateTimeEventsBegin = to_underlying(THeartRateEvents::HeartRateEventsEnd), DateTimeChanged, DateTimeEventsEnd }; -enum class TBleClientEvents +enum class TBleClientEvents : std::uint8_t { - BleClientEventsBegin = enumConvert(TDateTimeEvents::DateTimeEventsEnd), + BleClientEventsBegin = to_underlying(TDateTimeEvents::DateTimeEventsEnd), DeviceConnected, DeviceDisconnected, BleClientEventsEnd }; -enum class TGraphicsEvents +enum class TGraphicsEvents : std::uint8_t { - GraphicsEventsBegin = enumConvert(TBleClientEvents::BleClientEventsEnd), + GraphicsEventsBegin = to_underlying(TBleClientEvents::BleClientEventsEnd), PageHiding, PageActivated, GraphicsEventsEnd }; -enum class TButtonsEvents +enum class TButtonsEvents : std::uint8_t { - TButtonsEventsBegin = enumConvert(TGraphicsEvents::GraphicsEventsEnd), + TButtonsEventsBegin = to_underlying(TGraphicsEvents::GraphicsEventsEnd), ButtonClicked, ButtonPressed, ButtonReleased, @@ -87,7 +87,7 @@ enum class TButtonsEvents ButtonEventsEnd }; -enum class HardwareButtonId +enum class HardwareButtonId : std::uint8_t { kLeftButtonTop, kLeftButtonMedium, diff --git a/Firmware/graphics/lvgl_lib/lvgl_library/lv_conf.h b/Firmware/graphics/lvgl_lib/lvgl_library/lv_conf.h index ad79f40d..98ab177a 100644 --- a/Firmware/graphics/lvgl_lib/lvgl_library/lv_conf.h +++ b/Firmware/graphics/lvgl_lib/lvgl_library/lv_conf.h @@ -48,7 +48,7 @@ #define LV_MEM_CUSTOM 0 #if LV_MEM_CUSTOM == 0 /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ -# define LV_MEM_SIZE (17U * 1024U) /*[bytes]*/ +# define LV_MEM_SIZE (18U * 1024U) /*[bytes]*/ /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ # define LV_MEM_ADR 0 /*0: unused*/ diff --git a/Firmware/graphics/platform/gs_platform_layer.cpp b/Firmware/graphics/platform/gs_platform_layer.cpp index 4aa6d314..be7ea2b7 100644 --- a/Firmware/graphics/platform/gs_platform_layer.cpp +++ b/Firmware/graphics/platform/gs_platform_layer.cpp @@ -87,8 +87,6 @@ void PlatformBackend::executeLvTaskHandler() noexcept #include #include -#include - namespace Graphics { @@ -125,7 +123,7 @@ void PlatformBackend::memoryMonitor(lv_timer_t* _param) noexcept lv_mem_monitor_t moninor{}; lv_mem_monitor(&moninor); - LOG_DEBUG_ENDL(fmt::format( + LOG_DEBUG(fmt::format( "Used: {} , {}% fragmentation: {}, biggest free: {}", static_cast(moninor.total_size - moninor.free_size), static_cast(moninor.used_pct), diff --git a/Firmware/graphics/widgets_layer/gs_event_handler_base.hpp b/Firmware/graphics/widgets_layer/gs_event_handler_base.hpp index 2cfc20a1..18852a9f 100644 --- a/Firmware/graphics/widgets_layer/gs_event_handler_base.hpp +++ b/Firmware/graphics/widgets_layer/gs_event_handler_base.hpp @@ -37,41 +37,41 @@ class EventHandler case Events::EventGroup::Battery: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; case Events::EventGroup::Heartrate: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; case Events::EventGroup::BleDevice: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; case Events::EventGroup::DateTime: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; case Events::EventGroup::GraphicsEvents: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; case Events::EventGroup::Buttons: if constexpr (Meta::HasType::value) this->handleEventImpl( - std::any_cast(genericEvent.eventType), + static_cast(genericEvent.eventType), genericEvent.eventData); break; default: diff --git a/Firmware/logger/CMakeLists.txt b/Firmware/logger/CMakeLists.txt index 67c9ca06..c678c795 100644 --- a/Firmware/logger/CMakeLists.txt +++ b/Firmware/logger/CMakeLists.txt @@ -20,9 +20,10 @@ target_link_libraries( logger_service PUBLIC UtilsLibrary - PRIVATE + fmt::fmt ) + if( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) target_compile_definitions( @@ -41,9 +42,4 @@ elseif( ${TARGET_PLATFORM} STREQUAL "FIRMWARE_SIMULATOR" ) PRIVATE LoggerDesktop ) - target_link_libraries( - logger_service - PRIVATE - fmt::fmt - ) endif() \ No newline at end of file diff --git a/Firmware/logger/inc/logger/logger_service.hpp b/Firmware/logger/inc/logger/logger_service.hpp index 695e50bd..941110ea 100644 --- a/Firmware/logger/inc/logger/logger_service.hpp +++ b/Firmware/logger/inc/logger/logger_service.hpp @@ -8,25 +8,73 @@ #include #include +#if defined(USE_DEVICE_SPECIFIC) +#define FMT_HEADER_ONLY +#endif +#include +#include + +enum class LogSeverity +{ + Trace, + Debug, + Info, + Warn, + Error, + None +}; + #define ENABLE_DEBUG_LOGGING #ifdef ENABLE_DEBUG_LOGGING -#define LOG_DEBUG(ARGS) (Logger::Instance().logDebug(ARGS)) -#define LOG_DEBUG_ENDL(ARGS) (Logger::Instance().logDebugEndl(ARGS)) +#define LOG_TRACE(ARGS) (Logger::Instance().logDebug(ARGS)) +#define LOG_DEBUG(ARGS) (Logger::Instance().logDebug(ARGS)) +#define LOG_INFO(ARGS) (Logger::Instance().logDebug(ARGS)) +#define LOG_WARN(ARGS) (Logger::Instance().logDebug(ARGS)) +#define LOG_ERROR(ARGS) (Logger::Instance().logDebug(ARGS)) + #else +#define LOG_TRACE(ARGS) #define LOG_DEBUG(ARGS) -#define LOG_DEBUG_ENDL(ARGS) +#define LOG_INFO(ARGS) +#define LOG_WARN(ARGS) +#define LOG_ERROR(ARGS) #endif +template <> struct fmt::formatter> +{ + + constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) + { + + auto it = ctx.begin(), end = ctx.end(); + + if (it != end && *it == 'f') + { + ++it; + if (it != end && *it != '}') + std::terminate(); + } + return it; + } + + template + auto format(const std::span& p, FormatContext& ctx) -> decltype(ctx.out()) + { + + auto tempFormatHolder = std::string_view{ + reinterpret_cast(p.data()), + reinterpret_cast(p.data()) + p.size()}; + + return fmt::format_to(ctx.out(), "{}", tempFormatHolder); + } +}; + class Logger { public: static Logger& Instance() noexcept; - void logDebugEndl(std::string_view _toLog) noexcept; - - void logDebug(std::string_view _toLog) noexcept; - template static constexpr bool IsStringType() noexcept { using TClearType = typename std::decay::type; @@ -47,7 +95,7 @@ class Logger return isCharType; } - template void logDebugEndl(const TToLog& _toLog) noexcept + template void logDebug(const TToLog& _toLog) noexcept { constexpr bool isString = IsStringType(); @@ -57,64 +105,39 @@ class Logger if (auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), _toLog); ec == std::errc()) { - logDebugEndl(std::string_view(str.data(), p - str.data())); + logDebugChecked(std::string_view(str.data(), p - str.data())); } } else { if constexpr (IsCharType()) - logDebugEndl(static_cast(_toLog)); + logDebugChecked(fmt::format("{}", _toLog)); else - logDebugEndl(static_cast(_toLog)); + logDebugChecked(static_cast(_toLog)); } } - template void logDebug(const TToLog& _toLog) noexcept + template constexpr void logDebugChecked(std::string_view _toLog) noexcept { - constexpr bool isString = IsStringType(); - if constexpr (!isString) - { - std::array str{}; - if (auto [p, ec] = std::to_chars(str.data(), str.data() + str.size(), _toLog); - ec == std::errc()) - { - logDebug(std::string_view(str.data(), p - str.data())); - } - } - else - { - if constexpr (IsCharType()) - logDebug(static_cast(_toLog)); - else - logDebug(static_cast(_toLog)); - } - } - - template - void logDebug(const std::array& _arrayToLog) noexcept - { - for (auto arrayItem : _arrayToLog) + if constexpr ( + static_cast>(severity) >= + static_cast>(kCurrentLogLevel)) { - logDebug('['); - logDebug(arrayItem); - logDebug(']'); + logDebugImpl(severity, _toLog); } } - template - void logDebugEndl(const std::array& _arrayToLog) noexcept - { - logDebug(_arrayToLog); - logDebug('\n'); - } - private: Logger() noexcept; ~Logger(); +private: + void logDebugImpl(LogSeverity severity, std::string_view _toLog) noexcept; + private: static constexpr inline std::size_t kImplSize = Platform::LogerImplSize; static constexpr inline std::size_t kImplAlignment = Platform::LogerImplAlignment; + static constexpr inline LogSeverity kCurrentLogLevel = LogSeverity::Debug; private: mutable std::atomic_flag m_loggerReady; diff --git a/Firmware/logger/logger_service_impl.cpp b/Firmware/logger/logger_service_impl.cpp index 5958a325..74e33923 100644 --- a/Firmware/logger/logger_service_impl.cpp +++ b/Firmware/logger/logger_service_impl.cpp @@ -21,8 +21,8 @@ #include "SEGGER_RTT.h" #elif defined(LoggerDesktop) #include -#include #include +#include #if defined USE_MSVC_DEBUG_OUT #include #endif @@ -32,7 +32,27 @@ namespace { std::string_view CaretReset = "\r\n"; + +constexpr const char* const severityToString(LogSeverity severity) +{ + switch (severity) + { + + case LogSeverity::Trace: + return "[TRACE]"; + case LogSeverity::Debug: + return "[DEBUG]"; + case LogSeverity::Info: + return "[INFO]"; + case LogSeverity::Warn: + return "[WARN]"; + case LogSeverity::Error: + return "[ERROR]"; + default: + return ""; + } } +} // namespace #if defined(LoggerUart) @@ -129,10 +149,91 @@ class Logger::LoggerImpl { public: - void logString(std::string_view _toLog) const noexcept + LoggerImpl() + { + SEGGER_RTT_ConfigUpBuffer( + 0, + "SEGGER_MAIN_BUFFER", + seggerBuffer.data(), + kSeggerBufferSize, + SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL); + } + + auto formatMessage(std::span formatBuffer, LogSeverity severity, std::string_view _toLog) + const noexcept { - SEGGER_RTT_WriteString(0, _toLog.data()); + switch (severity) + { + case LogSeverity::Trace: + return fmt::format_to_n( + formatBuffer.data(), + formatBuffer.size(), + "{} {} {}\n", + RTT_CTRL_TEXT_WHITE, + severityToString(severity), + _toLog.data()); + break; + case LogSeverity::Debug: + return fmt::format_to_n( + formatBuffer.data(), + formatBuffer.size(), + "{} {} {}\n", + RTT_CTRL_TEXT_BRIGHT_WHITE, + severityToString(severity), + _toLog.data()); + break; + case LogSeverity::Info: + return fmt::format_to_n( + formatBuffer.data(), + formatBuffer.size(), + "{} {} {}\n", + RTT_CTRL_TEXT_MAGENTA, + severityToString(severity), + _toLog.data()); + break; + case LogSeverity::Warn: + return fmt::format_to_n( + formatBuffer.data(), + formatBuffer.size(), + "{} {} {}\n", + RTT_CTRL_TEXT_BRIGHT_YELLOW, + severityToString(severity), + _toLog.data()); + break; + case LogSeverity::Error: + return fmt::format_to_n( + formatBuffer.data(), + formatBuffer.size(), + "{} {} {}\n", + RTT_CTRL_TEXT_BRIGHT_RED, + severityToString(severity), + _toLog.data()); + break; + case LogSeverity::None: + break; + default: + break; + } + + std::terminate(); } + void logString(LogSeverity severity, std::string_view _toLog) const noexcept + { + std::array formatBuffer{}; + auto formatResult = formatMessage( + std::span(formatBuffer.data(), formatBuffer.size() - 1), severity, _toLog); + formatBuffer[formatResult.size + 1] = '\0'; + auto formatString = + std::string_view(formatBuffer.data(), formatBuffer.data() + formatResult.size + 1); + + SEGGER_RTT_Write(0, formatString.data(), formatString.size()); + } + + void completeMessage() + { + } + static constexpr inline std::uint16_t kSeggerBufferSize = 512; + static inline std::array seggerBuffer{}; }; #endif @@ -141,9 +242,33 @@ class Logger::LoggerImpl { public: - void logString(std::string_view _toLog) const noexcept + void logString(LogSeverity severity, std::string_view _toLog) const noexcept { - fmt::print(fg(fmt::color::steel_blue),"{}",_toLog.data()); + + switch (severity) + { + case LogSeverity::Trace: + fmt::print( + fg(fmt::color::white_smoke), "{} {}\n", severityToString(severity), _toLog.data()); + break; + case LogSeverity::Debug: + fmt::print(fg(fmt::color::white), "{} {}\n", severityToString(severity), _toLog.data()); + break; + case LogSeverity::Info: + fmt::print(fg(fmt::color::blue), "{} {}\n", severityToString(severity), _toLog.data()); + break; + case LogSeverity::Warn: + fmt::print( + fg(fmt::color::yellow), "{} {}\n", severityToString(severity), _toLog.data()); + break; + case LogSeverity::Error: + fmt::print(fg(fmt::color::red), "{} {}\n", severityToString(severity), _toLog.data()); + break; + case LogSeverity::None: + break; + default: + break; + } #if defined USE_MSVC_DEBUG_OUT OutputDebugString(_toLog.data()); #endif @@ -161,13 +286,7 @@ Logger& Logger::Instance() noexcept return intance; } -void Logger::logDebugEndl(std::string_view _toLog) noexcept -{ - m_pLoggerImpl->logString(_toLog); - m_pLoggerImpl->logString(CaretReset); -} - -void Logger::logDebug(std::string_view _toLog) noexcept +void Logger::logDebugImpl(LogSeverity severity, std::string_view _toLog) noexcept { - m_pLoggerImpl->logString(_toLog); + m_pLoggerImpl->logString(severity, _toLog); } \ No newline at end of file diff --git a/Firmware/main.cpp b/Firmware/main.cpp index fb84bd5b..ac52bd74 100644 --- a/Firmware/main.cpp +++ b/Firmware/main.cpp @@ -2,10 +2,6 @@ int main(void) { - - Application mainApp; - - mainApp.runApplicationLoop(); - + Application::Instance().runApplicationLoop(); return 0; } \ No newline at end of file diff --git a/Firmware/utils/CMakeLists.txt b/Firmware/utils/CMakeLists.txt index fb776297..38a45ce5 100644 --- a/Firmware/utils/CMakeLists.txt +++ b/Firmware/utils/CMakeLists.txt @@ -28,6 +28,11 @@ elseif( ${TARGET_PLATFORM} STREQUAL "ARM_CORTEX" ) INTERFACE -fcoroutines ) + target_compile_definitions( + UtilsLibrary + INTERFACE + BAREMETAL_PLATFORM + ) endif() target_link_libraries( diff --git a/Firmware/utils/inc/utils/coroutine/BlockingEventBaremetal.hpp b/Firmware/utils/inc/utils/coroutine/BlockingEventBaremetal.hpp new file mode 100644 index 00000000..f0eae150 --- /dev/null +++ b/Firmware/utils/inc/utils/coroutine/BlockingEventBaremetal.hpp @@ -0,0 +1,23 @@ +#include + +namespace CoroUtils +{ +class BlockingEvent +{ +public: + void wait() + { + while (!m_isSet) + { + } + } + + void set() + { + m_isSet.store(true); + } + +private: + std::atomic_bool m_isSet = false; +}; +} // namespace CoroUtils \ No newline at end of file diff --git a/Firmware/utils/inc/utils/coroutine/BlockingEventCondvar.hpp b/Firmware/utils/inc/utils/coroutine/BlockingEventCondvar.hpp new file mode 100644 index 00000000..08409957 --- /dev/null +++ b/Firmware/utils/inc/utils/coroutine/BlockingEventCondvar.hpp @@ -0,0 +1,28 @@ +// Detailed research from cppcoro library +#include +#include +#include + +namespace CoroUtils +{ +class BlockingEvent +{ +public: + void wait() + { + std::unique_lock lock(mutex); + condEvent.wait(lock, [this] { return m_isSet.load(std::memory_order_acquire); }); + } + + void set() + { + m_isSet.store(true, std::memory_order_release); + condEvent.notify_all(); + } + +private: + std::atomic_bool m_isSet = false; + std::mutex mutex; + std::condition_variable condEvent; +}; +} \ No newline at end of file diff --git a/Firmware/utils/inc/utils/coroutine/ExecutionQueueCoro.hpp b/Firmware/utils/inc/utils/coroutine/ExecutionQueueCoro.hpp index 9d9e82f1..7fe14cbc 100644 --- a/Firmware/utils/inc/utils/coroutine/ExecutionQueueCoro.hpp +++ b/Firmware/utils/inc/utils/coroutine/ExecutionQueueCoro.hpp @@ -1,5 +1,6 @@ #pragma once #include "Common.hpp" +#include namespace CoroUtils { @@ -27,6 +28,7 @@ struct CoroQueueMainLoop } } +private: template using TQueueStorageType = etl::queue_spsc_atomic; diff --git a/Firmware/utils/inc/utils/coroutine/SyncWait.hpp b/Firmware/utils/inc/utils/coroutine/SyncWait.hpp index 48b11b31..1b3173aa 100644 --- a/Firmware/utils/inc/utils/coroutine/SyncWait.hpp +++ b/Firmware/utils/inc/utils/coroutine/SyncWait.hpp @@ -1,10 +1,12 @@ #pragma once #include "Event.hpp" #include "Task.hpp" +#if defined USE_DESKTOP_SIMULATOR +#include "BlockingEventCondvar.hpp" +#else +#include "BlockingEventBaremetal.hpp" +#endif -// Detailed research from cppcoro library -#include -#include namespace CoroUtils { @@ -37,27 +39,6 @@ auto awaiterGetter(TAwaiter&& _awaiter) -> decltype(awaiterGetterImpl(_awaiter,0 } -class BlockingEvent -{ -public: - void wait() - { - std::unique_lock lock(mutex); - condEvent.wait(lock, [this] { return m_isSet.load(std::memory_order_acquire); }); - } - - void set() - { - m_isSet.store(true, std::memory_order_release); - condEvent.notify_all(); - } - -private: - std::atomic_bool m_isSet = false; - std::mutex mutex; - std::condition_variable condEvent; -}; - template struct AwaitResultGetter { diff --git a/Firmware/utils/inc/utils/coroutine/Task.hpp b/Firmware/utils/inc/utils/coroutine/Task.hpp index 20451c15..c9e27801 100644 --- a/Firmware/utils/inc/utils/coroutine/Task.hpp +++ b/Firmware/utils/inc/utils/coroutine/Task.hpp @@ -1,5 +1,7 @@ #pragma once #include "Common.hpp" +#include "ExecutionQueueCoro.hpp" +#include namespace CoroUtils { @@ -28,9 +30,9 @@ template struct Task return stdcoro::suspend_always{}; } - void return_value(const TResult& _value) noexcept + template void return_value(TResultType&& _value) noexcept { - m_coroutineResult = _value; + m_coroutineResult.emplace(std::forward(_value)); } void set_continuation(stdcoro::coroutine_handle<> continuation) @@ -40,7 +42,7 @@ template struct Task TResult& result() { - return m_coroutineResult; + return *m_coroutineResult; } struct final_awaitable @@ -62,12 +64,34 @@ template struct Task } }; + auto yield_value(CoroQueueMainLoop& loop) const noexcept + { + struct ScheduleForExecution + { + CoroQueueMainLoop& loop; + + constexpr bool await_ready() const noexcept + { + return false; + } + void await_suspend(std::coroutine_handle<> thisCoroutine) + { + loop.pushToLater(thisCoroutine); + } + constexpr void await_resume() + { + } + }; + + return ScheduleForExecution{loop}; + } + auto final_suspend() noexcept { return final_awaitable{}; } - TResult m_coroutineResult; + std::optional m_coroutineResult; stdcoro::coroutine_handle<> m_continuation; }; @@ -145,6 +169,28 @@ struct VoidTask return VoidTask{stdcoro::coroutine_handle::from_promise(*this)}; } + auto yield_value(CoroQueueMainLoop& loop) const noexcept + { + struct ScheduleForExecution + { + CoroQueueMainLoop& loop; + + constexpr bool await_ready() const noexcept + { + return false; + } + void await_suspend(std::coroutine_handle<> thisCoroutine) + { + loop.pushToLater(thisCoroutine); + } + constexpr void await_resume() + { + } + }; + + return ScheduleForExecution{loop}; + } + auto initial_suspend() noexcept { return stdcoro::suspend_always{};