diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..1847af861ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.riminfo -whitespace +platforms/*/3rdparty/threadx/ports/** -whitespace diff --git a/CMakeLists.txt b/CMakeLists.txt index c9d893a6a9a..baf4c25ba94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,15 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/posix/bsp/bspEepromDriver/test) add_subdirectory(platforms/posix/bsp/socketCanTransceiver/test) + elseif (OPENBSW_PLATFORM STREQUAL "stm32") + + add_subdirectory(platforms/stm32/unitTest EXCLUDE_FROM_ALL) + + add_subdirectory(platforms/stm32/bsp/bspCan/test) + add_subdirectory(platforms/stm32/bsp/bxCanTransceiver/test) + add_subdirectory(platforms/stm32/bsp/fdCanTransceiver/test) + add_subdirectory(platforms/stm32/safety/safeBspMcuWatchdog/test) + elseif (OPENBSW_PLATFORM STREQUAL "s32k1xx") add_subdirectory(platforms/s32k1xx/unitTest EXCLUDE_FROM_ALL) diff --git a/CMakePresets.json b/CMakePresets.json index ec6272009d2..e94879caf9a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -61,6 +61,112 @@ "OPENBSW_PLATFORM": "s32k1xx" } }, + { + "name": "tests-stm32-debug", + "displayName": "Configure for testing STM32 modules (debug)", + "description": "Configure for testing STM32 modules (debug)", + "inherits": "_config-base", + "binaryDir": "${sourceDir}/build/tests/stm32/Debug", + "cacheVariables": { + "CMAKE_DEFAULT_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug", + "BUILD_EXECUTABLE": "unitTest", + "OPENBSW_PLATFORM": "stm32" + } + }, + { + "name": "tests-stm32-release", + "displayName": "Configure for testing STM32 modules (release)", + "description": "Configure for testing STM32 modules (release)", + "inherits": "_config-base", + "binaryDir": "${sourceDir}/build/tests/stm32/Release", + "cacheVariables": { + "CMAKE_DEFAULT_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release", + "BUILD_EXECUTABLE": "unitTest", + "OPENBSW_PLATFORM": "stm32" + } + }, + { + "name": "nucleo-g474re-freertos-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-G474RE FreeRTOS configuration (GCC)", + "description": "Configure for NUCLEO-G474RE platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_G474RE", + "BUILD_TARGET_RTOS": "FREERTOS", + "STM32_CHIP": "STM32G474RE", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "nucleo-g474re-threadx-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-G474RE ThreadX configuration (GCC)", + "description": "Configure for NUCLEO-G474RE platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_G474RE", + "BUILD_TARGET_RTOS": "THREADX", + "STM32_CHIP": "STM32G474RE", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "nucleo-f413zh-freertos-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-F413ZH FreeRTOS configuration (GCC)", + "description": "Configure for NUCLEO-F413ZH platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_F413ZH", + "BUILD_TARGET_RTOS": "FREERTOS", + "STM32_CHIP": "STM32F413ZH", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "nucleo-f413zh-threadx-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-F413ZH ThreadX configuration (GCC)", + "description": "Configure for NUCLEO-F413ZH platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_F413ZH", + "BUILD_TARGET_RTOS": "THREADX", + "STM32_CHIP": "STM32F413ZH", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, { "name": "posix-freertos", "displayName": "POSIX-FREERTOS compliant configuration", @@ -228,6 +334,44 @@ "configurePreset": "tests-s32k1xx-release", "configuration": "Release" }, + { + "name": "tests-stm32-debug", + "displayName": "Build tests of STM32 modules (debug)", + "description": "Build tests of STM32 modules (debug)", + "configurePreset": "tests-stm32-debug", + "configuration": "Debug" + }, + { + "name": "tests-stm32-release", + "displayName": "Build tests of STM32 modules (release)", + "description": "Build tests of STM32 modules (release)", + "configurePreset": "tests-stm32-release", + "configuration": "Release" + }, + { + "name": "nucleo-g474re-freertos-gcc", + "displayName": "NUCLEO-G474RE FreeRTOS build (GCC)", + "description": "Build reference application for NUCLEO-G474RE platform with GCC", + "configurePreset": "nucleo-g474re-freertos-gcc" + }, + { + "name": "nucleo-g474re-threadx-gcc", + "displayName": "NUCLEO-G474RE ThreadX build (GCC)", + "description": "Build reference application for NUCLEO-G474RE platform with GCC", + "configurePreset": "nucleo-g474re-threadx-gcc" + }, + { + "name": "nucleo-f413zh-freertos-gcc", + "displayName": "NUCLEO-F413ZH FreeRTOS build (GCC)", + "description": "Build reference application for NUCLEO-F413ZH platform with GCC", + "configurePreset": "nucleo-f413zh-freertos-gcc" + }, + { + "name": "nucleo-f413zh-threadx-gcc", + "displayName": "NUCLEO-F413ZH ThreadX build (GCC)", + "description": "Build reference application for NUCLEO-F413ZH platform with GCC", + "configurePreset": "nucleo-f413zh-threadx-gcc" + }, { "name": "posix-freertos", "displayName": "build POSIX-FREERTOS", @@ -305,6 +449,20 @@ "description": "Run tests of S32K1XX modules (release)", "configuration": "Release", "configurePreset": "tests-s32k1xx-release" + }, + { + "name": "tests-stm32-debug", + "displayName": "Run tests of STM32 modules (debug)", + "description": "Run tests of STM32 modules (debug)", + "configuration": "Debug", + "configurePreset": "tests-stm32-debug" + }, + { + "name": "tests-stm32-release", + "displayName": "Run tests of STM32 modules (release)", + "description": "Run tests of STM32 modules (release)", + "configuration": "Release", + "configurePreset": "tests-stm32-release" } ] } diff --git a/NOTICE.md b/NOTICE.md index ca162642261..f7a53d1b849 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -42,11 +42,15 @@ We recommend to read their licenses, as their terms may differ from the terms de | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/posix/3rdparty/freeRtosPosix/LICENSE.md`` | | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/s32k1xx/3rdparty/freertos_cm4_sysTick/LICENSE.md`` | | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``libs/3rdparty/freeRtos/LICENSE.md`` | +| FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md`` | | ThreadX Kernel | 6.4.3 | MIT | ``libs/3rdparty/threadx/LICENSE.md`` | | ThreadX Cortex M4 Port | 6.4.3 | MIT | ``platforms/s32k1xx/3rdparty/threadx/LICENSE.md`` | +| ThreadX Cortex M4 Port | 6.4.3 | MIT | ``platforms/stm32/3rdparty/threadx/LICENSE.md`` | | ThreadX Linux Port | 6.4.3 | MIT | ``platforms/posix/3rdparty/threadx/LICENSE.md`` | | CMSIS | 6.1.0 | Apache v2 | ``libs/3rdparty/cmsis/LICENSE`` | | NXP S32K148 Headers | 1.1a | BSD-3 | ``platforms/s32k1xx/bsp/bspMcu/include/3rdparty/nxp/*.h`` | +| ST STM32F4 Device Headers | 2.6.11 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | +| ST STM32G4 Device Headers | 1.2.6 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | | CodeCoverage | | BSD-3 | ``cmake/modules/CodeCoverage.cmake`` | ## MISRA diff --git a/doc/dev/index.rst b/doc/dev/index.rst index 00d174535f8..da03693a06d 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -178,6 +178,7 @@ Eclipse OpenBSW is a trademark of the Eclipse Foundation. modules/common modules/posix modules/s32k1xx + modules/stm32 modules/executables modules/mocks diff --git a/doc/dev/modules/executables.rst b/doc/dev/modules/executables.rst index 255b965ec3f..c3a8490012d 100644 --- a/doc/dev/modules/executables.rst +++ b/doc/dev/modules/executables.rst @@ -47,6 +47,15 @@ S32K148EVB ../../../executables/referenceApp/platforms/s32k148evb/**/doc/index +STM32 Nucleo +++++++++++++ + +.. toctree:: + :maxdepth: 1 + :glob: + + ../../../executables/referenceApp/platforms/nucleo_*/**/doc/index + Safety ++++++ diff --git a/doc/dev/modules/stm32.rst b/doc/dev/modules/stm32.rst new file mode 100644 index 00000000000..3c7b5eb2891 --- /dev/null +++ b/doc/dev/modules/stm32.rst @@ -0,0 +1,22 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +STM32 +===== + +BSP +--- + +.. toctree:: + :maxdepth: 1 + :glob: + + ../../../platforms/stm32/**/doc/index diff --git a/executables/referenceApp/application/CMakeLists.txt b/executables/referenceApp/application/CMakeLists.txt index dcee58d8b46..9538bb4f81f 100644 --- a/executables/referenceApp/application/CMakeLists.txt +++ b/executables/referenceApp/application/CMakeLists.txt @@ -14,6 +14,17 @@ if (PLATFORM_SUPPORT_TRANSPORT) if (PLATFORM_SUPPORT_UDS) target_sources(app.referenceApp PRIVATE src/systems/UdsSystem.cpp src/uds/ReadIdentifierPot.cpp) + if (PLATFORM_SUPPORT_UDS_DEMO_SERVICES) + target_sources( + app.referenceApp + PRIVATE src/uds/Stm32ClearDtc.cpp + src/uds/Stm32ControlDtcSetting.cpp + src/uds/Stm32DemoRoutine.cpp + src/uds/Stm32DtcManager.cpp + src/uds/Stm32ReadDtcInfo.cpp + src/uds/Stm32SecurityAccess.cpp + src/uds/Stm32WriteVin.cpp) + endif () endif () endif () diff --git a/executables/referenceApp/application/include/systems/UdsSystem.h b/executables/referenceApp/application/include/systems/UdsSystem.h index 4d2a8a8a1cc..bdf5e6e5c1d 100644 --- a/executables/referenceApp/application/include/systems/UdsSystem.h +++ b/executables/referenceApp/application/include/systems/UdsSystem.h @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -22,6 +23,19 @@ #include #include #include + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -101,7 +115,28 @@ class UdsSystem ReadIdentifierFromMemory _read22Cf01; ReadIdentifierPot _read22Cf02; WriteIdentifierToMemory _write2eCf03; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ReadIdentifierFromMemory _readF190; + Stm32WriteVin _writeF190; + ReadIdentifierFromMemory _readF195; + ReadIdentifierFromMemory _readF18C; + ReadIdentifierFromMemory _readF193; + ReadIdentifierFromMemory _readF18A; + ReadIdentifierFromMemory _readF180; +#endif TesterPresent _testerPresent; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ECUReset _ecuReset; + HardReset _hardReset; + Stm32SecurityAccess _securityAccess; + Stm32DtcManager _dtcManager; + Stm32ClearDtc _clearDtc; + Stm32ReadDtcInfo _readDtcInfo; + Stm32ControlDtcSetting _controlDtcSetting; + Stm32DemoRoutine _routineFF00; + Stm32DemoRoutine _routineFF01; + Stm32DemoRoutine _routineFF02; +#endif ::async::ContextType _context; ::async::TimeoutType _timeout; diff --git a/executables/referenceApp/application/include/uds/Stm32ClearDtc.h b/executables/referenceApp/application/include/uds/Stm32ClearDtc.h new file mode 100644 index 00000000000..8abd2b64df4 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ClearDtc.h @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ClearDiagnosticInformation (SID 0x14). + * Accepts 3-byte group-of-DTC. 0xFFFFFF = clear all. + */ +class Stm32ClearDtc : public Service +{ +public: + explicit Stm32ClearDtc(Stm32DtcManager& dtcManager); + +private: + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + Stm32DtcManager& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h b/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h new file mode 100644 index 00000000000..d4533889624 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ControlDTCSetting (SID 0x85). + * Subfunction 0x01 = ON (enable DTC recording) + * Subfunction 0x02 = OFF (disable DTC recording) + */ +class Stm32ControlDtcSetting : public Service +{ +public: + explicit Stm32ControlDtcSetting(Stm32DtcManager& dtcManager); + +private: + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + Stm32DtcManager& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h b/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h new file mode 100644 index 00000000000..bfd1e0b4777 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/jobs/RoutineControlJob.h" + +namespace uds +{ +/** + * Demo routine for the STM32 diagnostic demo. + * Handles start/stop/requestResults for a single routine ID. + * Returns positive response with no data for all subfunctions. + */ +class Stm32DemoRoutine : public RoutineControlJob +{ +public: + explicit Stm32DemoRoutine(uint16_t routineId); + + DiagReturnCode::Type start( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + + DiagReturnCode::Type stop( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + + DiagReturnCode::Type requestResults( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + +private: + uint8_t _implRequest[4]; // SID + subFn + routineId[2] + RoutineControlJobNode _stopNode; + RoutineControlJobNode _resultsNode; + uint8_t _stopImplRequest[4]; + uint8_t _resultsImplRequest[4]; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32DtcManager.h b/executables/referenceApp/application/include/uds/Stm32DtcManager.h new file mode 100644 index 00000000000..9b7a980a1b6 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32DtcManager.h @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "platform/estdint.h" + +#include + +namespace uds +{ +/** + * Minimal DTC manager for the STM32 diagnostic demo. + * In-memory only (no NvM persistence). + */ +class Stm32DtcManager +{ +public: + static uint8_t const MAX_DTCS = 16U; + + // ISO 14229-1 DTC status byte bits + static uint8_t const STATUS_TEST_FAILED = 0x01U; + static uint8_t const STATUS_CONFIRMED = 0x08U; + static uint8_t const STATUS_TEST_NOT_COMPLETE = 0x10U; + + struct DtcEntry + { + uint32_t dtcNumber; // 3-byte DTC (e.g. 0x123456) + uint8_t statusByte; + bool active; + }; + + Stm32DtcManager(); + + void reportFault(uint32_t dtcNumber); + void clearAll(); + void clearByGroup(uint32_t groupOfDtc); + + uint16_t getCountByStatusMask(uint8_t statusMask) const; + uint8_t getDtcsByStatusMask(uint8_t statusMask, uint8_t* buffer, uint16_t bufferSize) const; + uint8_t getSupportedDtcs(uint8_t* buffer, uint16_t bufferSize) const; + + void setDtcSettingEnabled(bool enabled) { _dtcSettingEnabled = enabled; } + + bool isDtcSettingEnabled() const { return _dtcSettingEnabled; } + +private: + DtcEntry* findOrCreate(uint32_t dtcNumber); + + etl::array _dtcs; + uint8_t _dtcCount; + bool _dtcSettingEnabled; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h b/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h new file mode 100644 index 00000000000..7ec64e4a27d --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ReadDTCInformation (SID 0x19). + * + * Subfunctions: + * 0x01 = reportNumberOfDTCByStatusMask + * 0x02 = reportDTCByStatusMask + * 0x0A = reportSupportedDTC + */ +class Stm32ReadDtcInfo : public Service +{ +public: + explicit Stm32ReadDtcInfo(Stm32DtcManager const& dtcManager); + +private: + static uint16_t const DTC_BUFFER_SIZE = 128U; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + DiagReturnCode::Type + handleReportNumberByStatusMask(IncomingDiagConnection& connection, uint8_t statusMask); + DiagReturnCode::Type + handleReportByStatusMask(IncomingDiagConnection& connection, uint8_t statusMask); + DiagReturnCode::Type handleReportSupportedDtc(IncomingDiagConnection& connection); + + Stm32DtcManager const& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h b/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h new file mode 100644 index 00000000000..81972910d4f --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/base/Service.h" + +namespace uds +{ +/** + * SecurityAccess (SID 0x27) implementation for the STM32 diagnostic demo. + * + * Algorithm: + * Seed: LCG (a=1664525, c=1013904223) | 1, returned as four big-endian bytes + * Key: (seed ^ 0xDEADBEEF) truncated to uint16_t + * + * Subfunctions: + * 0x01 = requestSeed -> returns 4-byte seed (or zero seed if already unlocked) + * 0x02 = sendKey -> validates 2-byte key + */ +class Stm32SecurityAccess : public Service +{ +public: + Stm32SecurityAccess(); + +private: + static uint8_t const MAX_FAILED_ATTEMPTS = 3U; + static uint32_t const XOR_SECRET = 0xDEADBEEFU; + static uint32_t const LCG_A = 1664525U; + static uint32_t const LCG_C = 1013904223U; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + DiagReturnCode::Type handleRequestSeed(IncomingDiagConnection& connection); + DiagReturnCode::Type handleSendKey( + IncomingDiagConnection& connection, uint8_t const request[], uint16_t requestLength); + + uint32_t nextSeed(); + + uint32_t _seed; + bool _seedIssued; + bool _unlocked; + uint8_t _failedAttempts; + bool _lockedUntilReset; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32WriteVin.h b/executables/referenceApp/application/include/uds/Stm32WriteVin.h new file mode 100644 index 00000000000..30098c0b3b4 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32WriteVin.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace uds +{ + +/** + * WriteDID handler for VIN (F190) that accepts variable-length payloads. + * + * Unlike WriteIdentifierToMemory (which enforces exact length), this handler + * accepts any payload from 1 to memory.size() bytes. + */ +class Stm32WriteVin : public DataIdentifierJob +{ +public: + Stm32WriteVin(uint16_t identifier, ::etl::span const& memory); + + DiagReturnCode::Type verify(uint8_t const* request, uint16_t requestLength) override; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + +private: + uint8_t _implementedRequest[3]; + ::etl::span _memory; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/src/systems/DoCanSystem.cpp b/executables/referenceApp/application/src/systems/DoCanSystem.cpp index 82a08a890e5..ebd78bc84da 100644 --- a/executables/referenceApp/application/src/systems/DoCanSystem.cpp +++ b/executables/referenceApp/application/src/systems/DoCanSystem.cpp @@ -41,7 +41,11 @@ namespace docan { DoCanSystem::AddressingFilterType::AddressEntryType DoCanSystem::_addresses[] - = {{0x02A, 0x0F0U, 0x0F0U, LOGICAL_ADDRESS, 0, 0}}; +#ifdef PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + = {{0x7E0U, 0x7E8U, 0x7E8U, LOGICAL_ADDRESS, 0, 0}}; +#else + = {{0x02AU, 0x0F0U, 0x0F0U, LOGICAL_ADDRESS, 0, 0}}; +#endif DoCanSystem::DoCanSystem( ::transport::ITransportSystem& transportSystem, diff --git a/executables/referenceApp/application/src/systems/UdsSystem.cpp b/executables/referenceApp/application/src/systems/UdsSystem.cpp index 1404acf3f70..8e743789a8a 100644 --- a/executables/referenceApp/application/src/systems/UdsSystem.cpp +++ b/executables/referenceApp/application/src/systems/UdsSystem.cpp @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -24,6 +25,28 @@ uint8_t const responseData22Cf01[] etl::array storedData2eCf03 = {0}; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +// VIN (DID F190) - 17-byte writable scratchpad. +etl::array vinData + = {'O', 'B', 'S', 'W', 'S', 'T', 'M', '3', '2', '0', '0', '0', '0', '0', '0', '0', '1'}; + +// SW version (DID F195) - read-only. +uint8_t const swVersionData[] = {'1', '.', '0', '.', '0', '-', 's', 't', 'm', '3', '2'}; + +// ECU serial number (DID F18C) +uint8_t const ecuSerialData[] + = {'O', 'B', 'S', 'W', '-', 'S', 'T', 'M', '3', '2', '-', '0', '0', '1'}; + +// HW version (DID F193) +uint8_t const hwVersionData[] = {'H', 'W', '-', '1', '.', '0'}; + +// Supplier ID (DID F18A) +uint8_t const supplierIdData[] = {'E', 'c', 'l', 'i', 'p', 's', 'e', '-', 'O', 'B', 'S', 'W'}; + +// Boot SW version (DID F180) +uint8_t const bootSwVersionData[] = {'B', 'O', 'O', 'T', '-', '1', '.', '0'}; +#endif + UdsSystem::UdsSystem( lifecycle::LifecycleManager& lManager, transport::ITransportSystem& transportSystem, @@ -42,7 +65,11 @@ UdsSystem::UdsSystem( transport::TransportConfiguration::DIAG_PAYLOAD_SIZE, ::busid::SELFDIAG, true, /* activate outgoing pending */ +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + true, /* accept all requests */ +#else false, /* accept all requests */ +#endif true, /* copy functional requests */ context} , _udsDispatcher( @@ -57,7 +84,28 @@ UdsSystem::UdsSystem( , _read22Cf01(0xCF01, responseData22Cf01) , _read22Cf02() , _write2eCf03(0xCF03, storedData2eCf03) +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +, _readF190(0xF190, ::etl::span(vinData.data(), vinData.size())) +, _writeF190(0xF190, ::etl::span(vinData.data(), vinData.size())) +, _readF195(0xF195, swVersionData, sizeof(swVersionData)) +, _readF18C(0xF18C, ecuSerialData, sizeof(ecuSerialData)) +, _readF193(0xF193, hwVersionData, sizeof(hwVersionData)) +, _readF18A(0xF18A, supplierIdData, sizeof(supplierIdData)) +, _readF180(0xF180, bootSwVersionData, sizeof(bootSwVersionData)) +#endif , _testerPresent() +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +, _ecuReset() +, _hardReset(_udsLifecycleConnector, _udsDispatcher) +, _securityAccess() +, _dtcManager() +, _clearDtc(_dtcManager) +, _readDtcInfo(_dtcManager) +, _controlDtcSetting(_dtcManager) +, _routineFF00(0xFF00U) +, _routineFF01(0xFF01U) +, _routineFF02(0xFF02U) +#endif , _context(context) , _timeout() { @@ -133,6 +181,46 @@ void UdsSystem::addDiagJobs() (void)_jobRoot.addAbstractDiagJob(_testerPresent); (void)_jobRoot.addAbstractDiagJob(_diagnosticSessionControl); (void)_jobRoot.addAbstractDiagJob(_communicationControl); + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + // 11 - ECUReset + (void)_jobRoot.addAbstractDiagJob(_ecuReset); + (void)_jobRoot.addAbstractDiagJob(_hardReset); + + // 14 - ClearDiagnosticInformation + (void)_jobRoot.addAbstractDiagJob(_clearDtc); + + // 19 - ReadDTCInformation + (void)_jobRoot.addAbstractDiagJob(_readDtcInfo); + + // 22 - ReadDataByIdentifier (demo DIDs) + (void)_jobRoot.addAbstractDiagJob(_readF190); + (void)_jobRoot.addAbstractDiagJob(_readF195); + (void)_jobRoot.addAbstractDiagJob(_readF18C); + (void)_jobRoot.addAbstractDiagJob(_readF193); + (void)_jobRoot.addAbstractDiagJob(_readF18A); + (void)_jobRoot.addAbstractDiagJob(_readF180); + + // 27 - SecurityAccess + (void)_jobRoot.addAbstractDiagJob(_securityAccess); + + // 2E - WriteDataByIdentifier (demo DIDs) + (void)_jobRoot.addAbstractDiagJob(_writeF190); + + // 31 - Routine Control (demo routines) + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getRequestRoutineResults()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getRequestRoutineResults()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getRequestRoutineResults()); + + // 85 - ControlDTCSetting + (void)_jobRoot.addAbstractDiagJob(_controlDtcSetting); +#endif } void UdsSystem::removeDiagJobs() @@ -156,6 +244,31 @@ void UdsSystem::removeDiagJobs() _jobRoot.removeAbstractDiagJob(_testerPresent); _jobRoot.removeAbstractDiagJob(_diagnosticSessionControl); _jobRoot.removeAbstractDiagJob(_communicationControl); + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + _jobRoot.removeAbstractDiagJob(_ecuReset); + _jobRoot.removeAbstractDiagJob(_hardReset); + _jobRoot.removeAbstractDiagJob(_clearDtc); + _jobRoot.removeAbstractDiagJob(_readDtcInfo); + _jobRoot.removeAbstractDiagJob(_readF190); + _jobRoot.removeAbstractDiagJob(_readF195); + _jobRoot.removeAbstractDiagJob(_readF18C); + _jobRoot.removeAbstractDiagJob(_readF193); + _jobRoot.removeAbstractDiagJob(_readF18A); + _jobRoot.removeAbstractDiagJob(_readF180); + _jobRoot.removeAbstractDiagJob(_securityAccess); + _jobRoot.removeAbstractDiagJob(_writeF190); + _jobRoot.removeAbstractDiagJob(_routineFF00.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF00.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF00.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_controlDtcSetting); +#endif } void UdsSystem::execute() {} diff --git a/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp b/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp new file mode 100644 index 00000000000..a6675e7eae9 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ClearDtc.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_CLEAR_DTC = 0x14U; + +Stm32ClearDtc::Stm32ClearDtc(Stm32DtcManager& dtcManager) +: Service(SID_CLEAR_DTC, 3U, 0U, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ClearDtc::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint32_t const groupOfDtc = (static_cast(request[0]) << 16U) + | (static_cast(request[1]) << 8U) + | static_cast(request[2]); + + _dtcManager.clearByGroup(groupOfDtc); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp b/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp new file mode 100644 index 00000000000..d40fb105201 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ControlDtcSetting.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_CONTROL_DTC_SETTING = 0x85U; +static uint8_t const SF_ON = 0x01U; +static uint8_t const SF_OFF = 0x02U; + +Stm32ControlDtcSetting::Stm32ControlDtcSetting(Stm32DtcManager& dtcManager) +: Service(SID_CONTROL_DTC_SETTING, 1U, 1U, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ControlDtcSetting::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + if (subFunction == SF_ON) + { + _dtcManager.setDtcSettingEnabled(true); + } + else if (subFunction == SF_OFF) + { + _dtcManager.setDtcSettingEnabled(false); + } + else + { + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(subFunction); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp b/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp new file mode 100644 index 00000000000..42d5b2b83a4 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32DemoRoutine.h" + +#include "uds/UdsConstants.h" +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +Stm32DemoRoutine::Stm32DemoRoutine(uint16_t const routineId) +: RoutineControlJob(_implRequest, sizeof(_implRequest), &_stopNode, &_resultsNode) +, _stopNode(_stopImplRequest, *this) +, _resultsNode(_resultsImplRequest, *this) +{ + // Start routine: [0x31, 0x01, routineId_hi, routineId_lo] + _implRequest[0] = ServiceId::ROUTINE_CONTROL; + _implRequest[1] = 0x01U; + _implRequest[2] = static_cast((routineId >> 8U) & 0xFFU); + _implRequest[3] = static_cast(routineId & 0xFFU); + + // Stop routine: [0x31, 0x02, routineId_hi, routineId_lo] + _stopImplRequest[0] = ServiceId::ROUTINE_CONTROL; + _stopImplRequest[1] = 0x02U; + _stopImplRequest[2] = _implRequest[2]; + _stopImplRequest[3] = _implRequest[3]; + + // Request results: [0x31, 0x03, routineId_hi, routineId_lo] + _resultsImplRequest[0] = ServiceId::ROUTINE_CONTROL; + _resultsImplRequest[1] = 0x03U; + _resultsImplRequest[2] = _implRequest[2]; + _resultsImplRequest[3] = _implRequest[3]; + + disableSequenceCheck(); +} + +DiagReturnCode::Type Stm32DemoRoutine::start( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x01U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32DemoRoutine::stop( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x02U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32DemoRoutine::requestResults( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x03U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp b/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp new file mode 100644 index 00000000000..588288579f6 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp @@ -0,0 +1,156 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32DtcManager.h" + +namespace uds +{ + +Stm32DtcManager::Stm32DtcManager() : _dtcs{}, _dtcCount(0U), _dtcSettingEnabled(true) {} + +void Stm32DtcManager::reportFault(uint32_t const dtcNumber) +{ + if (!_dtcSettingEnabled) + { + return; + } + + DtcEntry* entry = findOrCreate(dtcNumber); + if (entry != nullptr) + { + entry->statusByte |= STATUS_TEST_FAILED | STATUS_CONFIRMED; + entry->statusByte &= static_cast(~STATUS_TEST_NOT_COMPLETE); + } +} + +void Stm32DtcManager::clearAll() +{ + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + _dtcs[i].statusByte = 0U; + _dtcs[i].active = false; + } + _dtcCount = 0U; +} + +void Stm32DtcManager::clearByGroup(uint32_t const groupOfDtc) +{ + if (groupOfDtc == 0xFFFFFFU) + { + clearAll(); + return; + } + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && (_dtcs[i].dtcNumber == groupOfDtc)) + { + _dtcs[i].statusByte = 0U; + _dtcs[i].active = false; + } + } +} + +uint16_t Stm32DtcManager::getCountByStatusMask(uint8_t const statusMask) const +{ + uint16_t count = 0U; + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && ((_dtcs[i].statusByte & statusMask) != 0U)) + { + ++count; + } + } + return count; +} + +uint8_t Stm32DtcManager::getDtcsByStatusMask( + uint8_t const statusMask, uint8_t* const buffer, uint16_t const bufferSize) const +{ + uint8_t count = 0U; + uint16_t offset = 0U; + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && ((_dtcs[i].statusByte & statusMask) != 0U)) + { + if ((offset + 4U) > bufferSize) + { + break; + } + // DTC high, mid, low bytes + status byte + buffer[offset] = static_cast((_dtcs[i].dtcNumber >> 16U) & 0xFFU); + buffer[offset + 1] = static_cast((_dtcs[i].dtcNumber >> 8U) & 0xFFU); + buffer[offset + 2] = static_cast(_dtcs[i].dtcNumber & 0xFFU); + buffer[offset + 3] = _dtcs[i].statusByte; + offset += 4U; + ++count; + } + } + return count; +} + +uint8_t Stm32DtcManager::getSupportedDtcs(uint8_t* const buffer, uint16_t const bufferSize) const +{ + uint8_t count = 0U; + uint16_t offset = 0U; + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active) + { + if ((offset + 4U) > bufferSize) + { + break; + } + buffer[offset] = static_cast((_dtcs[i].dtcNumber >> 16U) & 0xFFU); + buffer[offset + 1] = static_cast((_dtcs[i].dtcNumber >> 8U) & 0xFFU); + buffer[offset + 2] = static_cast(_dtcs[i].dtcNumber & 0xFFU); + buffer[offset + 3] = _dtcs[i].statusByte; + offset += 4U; + ++count; + } + } + return count; +} + +Stm32DtcManager::DtcEntry* Stm32DtcManager::findOrCreate(uint32_t const dtcNumber) +{ + DtcEntry* freeSlot = nullptr; + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && (_dtcs[i].dtcNumber == dtcNumber)) + { + return &_dtcs[i]; + } + // Reuse cleared (inactive) slots so fault/clear cycles don't exhaust the array. + if ((!_dtcs[i].active) && (freeSlot == nullptr)) + { + freeSlot = &_dtcs[i]; + } + } + + if ((freeSlot == nullptr) && (_dtcCount < MAX_DTCS)) + { + freeSlot = &_dtcs[_dtcCount]; + ++_dtcCount; + } + + if (freeSlot != nullptr) + { + freeSlot->dtcNumber = dtcNumber; + freeSlot->statusByte = 0U; + freeSlot->active = true; + } + + return freeSlot; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp b/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp new file mode 100644 index 00000000000..a5c73e2c59b --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp @@ -0,0 +1,125 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ReadDtcInfo.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_READ_DTC_INFO = 0x19U; +static uint8_t const SF_REPORT_NUM_BY_MASK = 0x01U; +static uint8_t const SF_REPORT_BY_MASK = 0x02U; +static uint8_t const SF_REPORT_SUPPORTED = 0x0AU; + +// ISO 14229: DTC status availability mask (all bits supported) +static uint8_t const STATUS_AVAILABILITY_MASK = 0xFFU; + +Stm32ReadDtcInfo::Stm32ReadDtcInfo(Stm32DtcManager const& dtcManager) +: Service(SID_READ_DTC_INFO, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ReadDtcInfo::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + switch (subFunction) + { + case SF_REPORT_NUM_BY_MASK: + { + if (requestLength < 2U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + return handleReportNumberByStatusMask(connection, request[1]); + } + case SF_REPORT_BY_MASK: + { + if (requestLength < 2U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + return handleReportByStatusMask(connection, request[1]); + } + case SF_REPORT_SUPPORTED: + { + return handleReportSupportedDtc(connection); + } + default: + { + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + } +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportNumberByStatusMask( + IncomingDiagConnection& connection, uint8_t const statusMask) +{ + uint16_t const count = _dtcManager.getCountByStatusMask(statusMask); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_NUM_BY_MASK); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + // DTC format identifier (ISO 14229-1 format) + (void)response.appendUint8(0x01U); + // DTC count high/low + (void)response.appendUint8(static_cast((count >> 8U) & 0xFFU)); + (void)response.appendUint8(static_cast(count & 0xFFU)); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportByStatusMask( + IncomingDiagConnection& connection, uint8_t const statusMask) +{ + uint8_t dtcBuffer[DTC_BUFFER_SIZE]; + uint8_t const dtcCount + = _dtcManager.getDtcsByStatusMask(statusMask, dtcBuffer, DTC_BUFFER_SIZE); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_BY_MASK); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + + for (uint16_t i = 0U; i < (static_cast(dtcCount) * 4U); ++i) + { + (void)response.appendUint8(dtcBuffer[i]); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportSupportedDtc(IncomingDiagConnection& connection) +{ + uint8_t dtcBuffer[DTC_BUFFER_SIZE]; + uint8_t const dtcCount = _dtcManager.getSupportedDtcs(dtcBuffer, DTC_BUFFER_SIZE); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_SUPPORTED); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + + for (uint16_t i = 0U; i < (static_cast(dtcCount) * 4U); ++i) + { + (void)response.appendUint8(dtcBuffer[i]); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp b/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp new file mode 100644 index 00000000000..9f573d8f682 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp @@ -0,0 +1,136 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32SecurityAccess.h" + +#include "uds/UdsConstants.h" +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +Stm32SecurityAccess::Stm32SecurityAccess() +: Service(ServiceId::SECURITY_ACCESS, DiagSession::ALL_SESSIONS()) +, _seed(0x12345678U) +, _seedIssued(false) +, _unlocked(false) +, _failedAttempts(0U) +, _lockedUntilReset(false) +{} + +uint32_t Stm32SecurityAccess::nextSeed() +{ + _seed = (_seed * LCG_A + LCG_C) | 0x00000001U; + return _seed; +} + +DiagReturnCode::Type Stm32SecurityAccess::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + if (subFunction == 0x01U) + { + return handleRequestSeed(connection); + } + else if (subFunction == 0x02U) + { + return handleSendKey(connection, request, requestLength); + } + + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; +} + +DiagReturnCode::Type Stm32SecurityAccess::handleRequestSeed(IncomingDiagConnection& connection) +{ + if (_lockedUntilReset) + { + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x01U); // echo subfunction + + if (_unlocked) + { + // Already unlocked - return 4-byte zero seed per ISO 14229. + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + } + else + { + uint32_t const seed = nextSeed(); + _seedIssued = true; + // 4-byte big-endian seed. + (void)response.appendUint8(static_cast((seed >> 24U) & 0xFFU)); + (void)response.appendUint8(static_cast((seed >> 16U) & 0xFFU)); + (void)response.appendUint8(static_cast((seed >> 8U) & 0xFFU)); + (void)response.appendUint8(static_cast(seed & 0xFFU)); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32SecurityAccess::handleSendKey( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (_lockedUntilReset) + { + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + if (!_seedIssued) + { + return DiagReturnCode::ISO_REQUEST_SEQUENCE_ERROR; + } + + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint16_t const receivedKey + = (static_cast(request[1]) << 8U) | static_cast(request[2]); + uint16_t const expectedKey = static_cast(_seed ^ XOR_SECRET); + + if (receivedKey == expectedKey) + { + _unlocked = true; + _seedIssued = false; + _failedAttempts = 0U; + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x02U); // echo subfunction + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; + } + + _failedAttempts++; + _seedIssued = false; + + if (_failedAttempts >= MAX_FAILED_ATTEMPTS) + { + _lockedUntilReset = true; + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + return DiagReturnCode::ISO_INVALID_KEY; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp b/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp new file mode 100644 index 00000000000..ad6c9b90911 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32WriteVin.h" + +#include "uds/connection/IncomingDiagConnection.h" + +#include + +namespace uds +{ + +Stm32WriteVin::Stm32WriteVin(uint16_t const identifier, ::etl::span const& memory) +: DataIdentifierJob(_implementedRequest), _memory(memory) +{ + _implementedRequest[0] = 0x2EU; + _implementedRequest[1] = static_cast((identifier >> 8U) & 0xFFU); + _implementedRequest[2] = static_cast(identifier & 0xFFU); +} + +DiagReturnCode::Type +Stm32WriteVin::verify(uint8_t const* const request, uint16_t const requestLength) +{ + // request[0..1] = DID high/low (SID already stripped by parent) + if (!compare(request, getImplementedRequest() + 1U, 2U)) + { + return DiagReturnCode::NOT_RESPONSIBLE; + } + + // Must have at least 2 DID bytes + 1 data byte + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + // Data portion must fit in the memory buffer + uint16_t const dataLen = requestLength - 2U; + if (dataLen > _memory.size()) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32WriteVin::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + // Framework already stripped DID bytes; request is data-only. + for (uint16_t i = 0U; i < requestLength; ++i) + { + _memory[i] = request[i]; + } + // Zero-fill the tail so a short write leaves no stale bytes behind. + std::fill(_memory.begin() + requestLength, _memory.end(), static_cast(0U)); + + (void)connection.releaseRequestGetResponse(); + (void)connection.sendPositiveResponse(*this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/configuration/include/app/appConfig.h b/executables/referenceApp/configuration/include/app/appConfig.h index 79214e3c9be..93b88a9e13c 100644 --- a/executables/referenceApp/configuration/include/app/appConfig.h +++ b/executables/referenceApp/configuration/include/app/appConfig.h @@ -13,7 +13,11 @@ #include #ifdef PLATFORM_SUPPORT_TRANSPORT +#ifdef PLATFORM_SUPPORT_OBD_UDS_ADDRESSING +static constexpr uint16_t LOGICAL_ADDRESS = 0x0600U; +#else static constexpr uint16_t LOGICAL_ADDRESS = 0x002AU; +#endif #endif // PLATFORM_SUPPORT_TRANSPORT #define safety_task_stackSize 2048U diff --git a/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt new file mode 100644 index 00000000000..6976e4fbde5 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt @@ -0,0 +1,15 @@ +# NUCLEO-F413ZH platform for referenceApp + +add_subdirectory(bspConfiguration) +add_subdirectory(safety) +add_subdirectory(main) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(freeRtosCoreConfiguration) + add_library(freeRtosPort ALIAS freeRtosCm4SysTickPort) + add_library(freeRtosPortImpl ALIAS freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(threadXCoreConfiguration) + add_library(threadXPort ALIAS threadXCortexM4Port) + add_library(threadXPortImpl ALIAS threadXCortexM4) +endif () diff --git a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake new file mode 100644 index 00000000000..aacdaa15a62 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake @@ -0,0 +1,58 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# NUCLEO-F413ZH platform options + +set(OPENBSW_PLATFORM + "stm32" + CACHE STRING "" FORCE) +set(STM32_CHIP + "STM32F413ZH" + CACHE STRING "" FORCE) +set(BUILD_TARGET_RTOS + "FREERTOS" + CACHE STRING "Target RTOS: FREERTOS or THREADX") + +set(PLATFORM_SUPPORT_CAN + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_IO + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ETHERNET + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_TRANSPORT + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_PROGRAMMING_SESSION + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_WATCHDOG + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_MPU + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_STORAGE + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ROM_CHECK + OFF + CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..5cb06edc3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(bspConfiguration src/bsp/stdIo/stdIo.cpp + src/bsp/uart/UartConfig.cpp) + +target_include_directories(bspConfiguration PUBLIC include) + +target_link_libraries( + bspConfiguration + PUBLIC bspUart + PRIVATE bspMcu platform) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst new file mode 100644 index 00000000000..d51c3c4a000 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst @@ -0,0 +1,57 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspconfig_nucleo_f413zh: + +bspConfiguration - NUCLEO-F413ZH +================================ + +Overview +-------- + +The ``bspConfiguration`` module contains hardware-specific configuration for the +NUCLEO-F413ZH evaluation board. Driver logic is separated from its configuration +so that users can customise a project for different boards or pin assignments +without modifying driver source code. + +The STM32F413ZH platform exposes a minimal set of BSP peripherals required for +the CAN reference application: + +- **bspUart** - USART3 configuration (ST-LINK Virtual COM Port on PD8/PD9). +- **bspStdIo** - Standard I/O bridge that routes ``putByteToStdout`` / + ``getByteFromStdin`` through the configured UART instance. + +.. note:: + + Unlike the S32K148EVB reference application, this platform does not include + ADC, PWM, or digital I/O configuration modules because the CAN reference + application does not require analogue inputs or GPIO-driven outputs. + These modules can be added following the same configuration pattern if + a future application requires them. + +Hardware Summary +++++++++++++++++ + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32F413ZH (Arm Cortex-M4F, 96 MHz, single-precision FPU)" + "UART peripheral", "USART3 - routed to ST-LINK/V2-1 Virtual COM Port" + "UART TX pin", "PD8 (AF7)" + "UART RX pin", "PD9 (AF7)" + "UART baud rate", "115 200 baud (BRR = 417 at 48 MHz APB1)" + "CAN peripheral", "CAN1 (bxCAN, configured in CanSystem, not bspConfiguration)" + +.. toctree:: + :hidden: + + user/index diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst new file mode 100644 index 00000000000..3805fb8f22a --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst @@ -0,0 +1,34 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_StdIo_F413ZH: + +bspStdIo +======== + +Overview +-------- + +The standard I/O bridge implements the ``putByteToStdout`` and +``getByteFromStdin`` C-linkage functions required by the OpenBSW logging +framework, routing character-level I/O through the UART ``TERMINAL`` instance +configured in :ref:`bspConfig_Uart_F413ZH`. + +``putByteToStdout(uint8_t byte)`` transmits one byte over USART3 TX (PD8), +blocking by polling until the UART TX register is free. + +``getByteFromStdin()`` attempts to read one byte from USART3 RX (PD9) and is +non-blocking: if ``Uart::read()`` reports that no byte was received, it returns +``-1`` (POSIX ``getchar()`` convention); otherwise it returns the byte value +(0-255). + +Both functions resolve the UART instance once into a ``static`` local +reference, avoiding a global constructor ordering dependency. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst new file mode 100644 index 00000000000..9841cd00aed --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_Uart_F413ZH: + +bspUart Configuration +===================== + +Overview +-------- + +The UART configuration defines the hardware parameters for the on-board +ST-LINK Virtual COM Port of the NUCLEO-F413ZH. A single UART instance +(``TERMINAL``) is configured for debug logging and interactive console use. + +``UartConfig.h`` declares the ``Uart::Id`` enumeration of available UART +instances, and ``UartConfig.cpp`` maps each ``Uart::Id`` to its hardware +parameters and provides the singleton accessor +``bsp::Uart::getInstance(Uart::Id)``. + +Configuration +------------- + +.. csv-table:: + :header: "Parameter", "Value" + :widths: 30, 70 + :width: 100% + + "Peripheral", "USART3 (APB1, 48 MHz = 96 MHz SYSCLK / 2)" + "TX pin", "PD8, alternate function AF7" + "RX pin", "PD9, alternate function AF7" + "Baud rate", "115 200 baud" + "BRR", "48 000 000 / 115 200 = 416.67 -> 417" + +The resulting actual baud rate is 48 MHz / 417 = 115 108 baud (0.080 % error), +well within the UART tolerance. + +.. note:: + + The UART driver uses polling mode (no DMA, no interrupts). For high- + throughput or latency-sensitive applications, consider extending the + ``bspUart`` BSP module with an interrupt-driven or DMA-backed backend. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst new file mode 100644 index 00000000000..e2d54e3e7f5 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Detailed documentation for each BSP configuration module on the NUCLEO-F413ZH +platform. + +Modules +------- + +.. toctree:: + :hidden: + + bspUart + bspStdIo + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`bspConfig_Uart_F413ZH`, "USART3 pin mapping, baud-rate register value, and clock-enable bits" + :ref:`bspConfig_StdIo_F413ZH`, "Standard I/O bridge routing printf / scanf to the UART terminal" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h new file mode 100644 index 00000000000..b1f23bee3c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +enum class Uart::Id : size_t +{ + TERMINAL, + INVALID, +}; + +static constexpr size_t NUMBER_OF_UARTS = static_cast(Uart::Id::INVALID); + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec new file mode 100644 index 00000000000..c918431ceaa --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec @@ -0,0 +1,2 @@ +unit_test: false +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp new file mode 100644 index 00000000000..d6b4b5f14fb --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/uart/UartConfig.h" +#include "platform/estdint.h" + +extern "C" void putByteToStdout(uint8_t const byte) +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uart.write(etl::span(&byte, 1U)); +} + +extern "C" int32_t getByteFromStdin() +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uint8_t dataByte = 0; + if (uart.read(etl::span(&dataByte, 1U)) == 0U) + { + return -1; + } + return dataByte; +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp new file mode 100644 index 00000000000..fd9acabe943 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// NUCLEO-F413ZH: USART3 on PD8 (TX) / PD9 (RX), AF7 +// ST-LINK VCP, 115200 baud @ 48 MHz APB1 + +#include +#include +#include + +namespace bsp +{ + +Uart::UartConfig const Uart::_uartConfigs[] = { + { + USART3, // usart + GPIOD, // gpioPort + 8U, // txPin (PD8) + 9U, // rxPin (PD9) + 7U, // af (AF7) + 417U, // brr (48000000 / 115200 = 416.67 -> 417) + RCC_AHB1ENR_GPIODEN, // rccGpioEnBit + RCC_APB1ENR_USART3EN, // rccUsartEnBit + &RCC->AHB1ENR, // rccGpioEnReg + &RCC->APB1ENR, // rccUsartEnReg + }, +}; + +static Uart instances[] = { + Uart(Uart::Id::TERMINAL), +}; + +Uart& Uart::getInstance(Id id) +{ + ETL_ASSERT( + id < Id::INVALID, ETL_ERROR_GENERIC("UartId::INVALID is not a valid Uart identifier")); + static_assert( + NUMBER_OF_UARTS == static_cast(etl::size(instances)), + "Not enough Uart instances defined"); + return instances[static_cast(id)]; +} + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..1e859851fac --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(freeRtosCoreConfiguration INTERFACE) +target_include_directories(freeRtosCoreConfiguration INTERFACE include) +target_link_libraries(freeRtosCoreConfiguration INTERFACE bsp bspMcu) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h new file mode 100644 index 00000000000..6f2786eb840 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h @@ -0,0 +1,122 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +// FreeRTOS device-specific configuration for NUCLEO-F413ZH. +// Included by asyncFreeRtos/freeRtosConfiguration/FreeRTOSConfig.h. + +#pragma once + +#include "bsp/SystemTime.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define configCPU_CLOCK_HZ (96000000UL) + +#undef configMINIMAL_STACK_SIZE +#define configMINIMAL_STACK_SIZE ((unsigned short)512) +#define configMAX_TASK_NAME_LEN (10) +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 30 +#undef configCHECK_FOR_STACK_OVERFLOW +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_FPU 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 + +/* Memory allocation */ +#define configTOTAL_HEAP_SIZE 512 +#define configAPPLICATION_ALLOCATED_HEAP 1 + +/* Co-routine definitions */ +#define configMAX_CO_ROUTINE_PRIORITIES (0) + +/* Software timer definitions */ +#undef configTIMER_QUEUE_LENGTH +#define configTIMER_QUEUE_LENGTH 30 +#undef configTIMER_TASK_STACK_DEPTH +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE) + +/* API function includes */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vResumeFromISR 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_eTaskGetState 1 +#undef INCLUDE_uxTaskGetStackHighWaterMark +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#undef INCLUDE_xTaskGetCurrentTaskHandle +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#undef INCLUDE_xTaskGetIdleTaskHandle +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_xEventGroupSetBitFromISR 1 +#undef INCLUDE_xTimerPendFunctionCall +#define INCLUDE_xTimerPendFunctionCall 1 + +/* Cortex-M specific definitions */ +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 4 +#endif + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0F +// ISRs that call FreeRTOS FromISR APIs must run at numeric priority >= this value. +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x05 + +#ifndef configKERNEL_INTERRUPT_PRIORITY +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +/* Assert */ +#define DEV_ASSERT(x) +#if defined(__GNUC__) && (!defined(__ASSEMBLER__)) +#include "mcu/mcu.h" +#endif +#define configASSERT(x) DEV_ASSERT(x) + +/* Override tick hook to feed IWDG (may be enabled from previous firmware) */ +#undef configUSE_TICK_HOOK +#define configUSE_TICK_HOOK 1 + +/* Tickless idle */ +#define configUSE_TICKLESS_IDLE 0 +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#define configUSE_TICKLESS_IDLE_DECISION_HOOK 0 + +/* Map FreeRTOS port handlers to CMSIS standard names */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler + +/* Run-time stats */ +#define configGENERATE_RUN_TIME_STATS (1) +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() getSystemTimeUs32Bit() + +#define configTIMER_SERVICE_TASK_NAME "TIMER_OS" + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt new file mode 100644 index 00000000000..472f82ef313 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt @@ -0,0 +1,71 @@ +# startUp is an INTERFACE library - it carries no compiled sources, only +# propagates link dependencies (bspMcu, common, hardFaultHandler) and the linker +# script to consumers via INTERFACE properties. +add_library(startUp INTERFACE) + +target_link_libraries(startUp INTERFACE bspMcu common hardFaultHandler) + +add_library( + main + src/os/isr/isr_can.cpp + src/os/isr/isr_sys.cpp + src/systems/CanSystem.cpp + src/lifecycle/StaticBsp.cpp + src/main.cpp) + +target_include_directories(main PUBLIC include) + +target_link_libraries( + main + PUBLIC bspConfiguration + PRIVATE bspClock + bspTimer + bspUart + bxCanTransceiver + configuration + etl + lifecycle + safeSupervisor + startUp) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_link_libraries(main PRIVATE freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_sources(startUp INTERFACE src/bsp/threadx/tx_initialize_low_level.S + src/bsp/threadx/tx_execution_initialize.c) + target_link_libraries(main PRIVATE threadXCortexM4) +endif () + +# --whole-archive forces the linker to include ALL object files from the main +# library, even if no other translation unit references them. This is required +# because isr_can.cpp provides strong ISR definitions (CAN1_RX0_IRQHandler, +# CAN1_TX_IRQHandler) that override the weak default handlers in the startup +# assembly. Without --whole-archive the linker would discard isr_can.o as +# "unreferenced" and the weak stubs would remain, silently swallowing CAN IRQs. +set_target_properties(main PROPERTIES INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE + "") +target_link_options( + main + INTERFACE + "LINKER:--whole-archive" + "$" + "LINKER:--no-whole-archive") + +set_target_properties( + startUp + PROPERTIES PROP_LINKER_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") +set_target_properties( + startUp + PROPERTIES LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_library(osHooks src/osHooks/freertos/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_library(osHooks src/osHooks/threadx/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common threadXCortexM4 asyncThreadX + threadXConfiguration) +endif () +target_include_directories(osHooks PRIVATE include) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst new file mode 100644 index 00000000000..00ec97bb42d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_main: + +main - NUCLEO-F413ZH +==================== + +Overview +-------- + +The ``main`` module contains the platform-specific entry point, startup code, linker +script, and lifecycle systems for the NUCLEO-F413ZH evaluation board: + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32F413ZH - Arm Cortex-M4, single-precision FPU" + "Core clock", "96 MHz (HSI with PLL)" + "Flash", "1.5 MB" + "SRAM", "320 KB (SRAM1 256 KB + SRAM2 64 KB)" + "CAN", "bxCAN CAN1 - PD0 (RX) / PD1 (TX), AF9, 500 kbit/s" + "Debug UART", "USART3 - PD8 TX (AF7), 115200 baud, routed to ST-LINK VCP" + "User LED", "LD1 (green) on PB0 (active-high)" + "Debug interface", "On-board ST-LINK/V2-1 (SWD)" + +See :ref:`bspconfig_nucleo_f413zh` for the UART and standard I/O configuration, and the +user documentation for the sub-modules: + +.. toctree:: + user/index diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst new file mode 100644 index 00000000000..9ca2bc298c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst @@ -0,0 +1,24 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_StaticBsp: + +Static BSP +========== + +Overview +-------- + +The ``StaticBsp`` class aggregates the BSP peripherals that must be operational before +the lifecycle starts ("static"): the system timer (DWT cycle counter based) and the +UART terminal (USART3, see :ref:`bspConfig_Uart_F413ZH`). It is instantiated in +``main.cpp`` and initialized from ``main()`` before ``app_main()``, so timing services +and serial output are available during the early lifecycle phases. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst new file mode 100644 index 00000000000..03faa96dded --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Find the detailed information about each module below: + +.. toctree:: + :hidden: + + startup + main + StaticBsp + systems/CanSystem + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`nucleo_f413zh_startup`, "Startup Code and Reset Handler" + :ref:`nucleo_f413zh_main_entry`, "Main File and boot sequence" + :ref:`nucleo_f413zh_StaticBsp`, "Static BSP - pre-lifecycle peripheral init" + :ref:`nucleo_f413zh_CanSystem`, "CAN System (bxCAN1, 500 kbit/s)" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst new file mode 100644 index 00000000000..663366dc4d7 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_main_entry: + +main File +========= + +The ``main.cpp`` module is the entry point of the NUCLEO-F413ZH reference application. +``SystemInit()``, called from ``Reset_Handler``, configures the PLL to 96 MHz, switches +on the LD1 LED (PB0), and sets up early USART3 TX output for use before the BSP UART +driver is initialized. + +``main()`` constructs the safety supervisor, initializes the static BSP (system timer +and UART terminal, see :ref:`nucleo_f413zh_StaticBsp`) and calls ``app_main()``, which +starts the lifecycle manager and the RTOS scheduler. The IWDG refresh writes in +``main()`` are no-ops until the SafetyManager enables the watchdog at runlevel 1. The +task contexts are defined in ``async/Config.h`` of ``referenceApp/asyncCoreConfiguration``: +``TASK_BACKGROUND``, ``TASK_BSP``, ``TASK_UDS``, ``TASK_DEMO``, ``TASK_ETHERNET``, +``TASK_CAN``, ``TASK_SYSADMIN`` and ``TASK_SAFETY``. + +``platformLifecycleAdd()`` registers the platform's ``CanSystem`` at runlevel 2 in the +``TASK_CAN`` context; the remaining components - including ``RuntimeSystem`` and +``SafetySystem`` at runlevel 1 and, since the platform enables +``PLATFORM_SUPPORT_TRANSPORT`` and ``PLATFORM_SUPPORT_UDS``, the transport, DoCAN and +UDS systems - are registered by the shared application code. + +``setupApplicationsIsr()`` is a no-op; CAN NVIC setup happens in ``CanSystem::run()``. + +``HardFault_Handler`` branches to ``customHardFaultHandler`` from the +``hardFaultHandler`` module, which dumps the registers to RAM and ends in +``HardFault_Handler_Final``, which performs a system reset. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst new file mode 100644 index 00000000000..b286cc96fea --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst @@ -0,0 +1,54 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_startup: + +Startup Code +============ + +The startup code (``platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s``) provides +the vector table and the ``Reset_Handler`` for the STM32F413xx. + +Vector Table +------------ + +The vector table is placed at the base of flash (``0x0800_0000``) in the +``.isr_vector`` section. It contains the initial stack pointer, the Cortex-M4 exception +vectors, and the 102 STM32F413xx interrupt vectors (RM0430). Every handler is a weak +alias of ``Default_Handler`` (an infinite loop) and is overridden by a strong definition +where the application implements one (e.g. ``HardFault_Handler``, +``CAN1_RX0_IRQHandler``). + +Reset Handler +------------- + +``Reset_Handler`` executes the following sequence: + +1. Load the initial stack pointer (``_estack``). +2. Copy the ``.data`` section from flash to SRAM. +3. Zero-fill the ``.bss`` section. +4. Enable the FPU (CP10/CP11 full access in ``CPACR``, followed by ``dsb``/``isb``). + This is required for ThreadX; the FreeRTOS port enables the FPU itself. +5. Call ``SystemInit()`` (PLL and early peripheral setup, defined in ``main.cpp``). +6. Call ``__libc_init_array`` (C++ global/static constructors). +7. Call ``main()``. + +The startup code does not mask or unmask interrupts; interrupt control is left to the +RTOS port once the scheduler starts. + +Memory Layout +------------- + +The linker script places code in 1536 KB flash at ``0x0800_0000`` and data in the +320 KB of contiguous SRAM at ``0x2000_0000`` (SRAM1 256 KB + SRAM2 64 KB). The top +1 KB of SRAM (``0x2004_FC00``-``0x2005_0000``) is excluded from the ``RAM`` region and +reserved for the hard fault handler dump, so it survives reset. The stack grows down +from ``_estack`` at ``0x2004_FC00``. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst new file mode 100644 index 00000000000..6def2fc69fd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst @@ -0,0 +1,77 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_CanSystem: + +CAN System +========== + +Overview +-------- + +The ``CanSystem`` implements the ``ICanSystem`` interface for the NUCLEO-F413ZH using +the STM32F413ZH's bxCAN peripheral on CAN1. It manages a single ``BxCanTransceiver`` +(``CAN_0``), participates in the lifecycle as a ``SingleContextLifecycleComponent`` +running in the ``TASK_CAN`` context, and provides the ``extern "C"`` ISR trampolines. +It is registered as an ``etl::singleton`` so the ISR handlers can reach the instance +without global variables. + +Hardware Configuration +---------------------- + +CAN1 uses PD0 (RX) and PD1 (TX), alternate function AF9. An external CAN transceiver +is required to drive the bus. Bit timing at 48 MHz APB1 clock: prescaler 6 (8 MHz time +quantum), 16 TQ per bit (1 sync + 13 BS1 + 2 BS2), giving 500 kbit/s with a sample +point of 87.5 %. + +Lifecycle +--------- + +``init()`` only signals ``transitionDone()`` - the peripheral is untouched until +``run()``, which: + +1. Enables the ``CanRxRunnable``. +2. Initializes and opens the ``BxCanTransceiver`` (CAN1 register setup, normal mode). +3. Configures the NVIC and enables the CAN1 IRQ lines: + + .. code-block:: c++ + + NVIC_SetPriority(CAN1_RX0_IRQn, 6); + NVIC_SetPriority(CAN1_TX_IRQn, 6); + NVIC_ClearPendingIRQ(CAN1_RX0_IRQn); + NVIC_ClearPendingIRQ(CAN1_TX_IRQn); + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_TX_IRQn); + + The NVIC is configured in ``run()``, not in ``setupApplicationsIsr()``, so the async + framework is fully initialized before any CAN ISR can fire. + +``shutdown()`` closes and shuts down the transceiver and disables the RX runnable. + +Interrupt Handling +------------------ + +bxCAN uses dedicated NVIC lines: ``CAN1_RX0_IRQHandler`` (FIFO 0 message pending, +FMPIE0) routes to ``call_can_isr_RX()`` and ``CAN1_TX_IRQHandler`` (TX mailbox empty) +routes to ``call_can_isr_TX()``. + +``call_can_isr_RX()`` first disables the FMPIE0 interrupt: the bxCAN RX FIFO is only +3 messages deep, and on a heavily loaded bus it can refill before the ISR returns, +causing endless ISR re-entry. It then reads pending frames from the hardware FIFO into +the software queue under lock (``BxCanTransceiver::receiveInterrupt``) and, if frames +were received, dispatches the ``CanRxRunnable`` into the ``TASK_CAN`` context; FMPIE0 +stays disabled until ``receiveTask()`` has drained the software queue. If no frames +were pending, FMPIE0 is re-enabled immediately. + +``call_can_isr_TX()`` forwards the TX-complete event to +``BxCanTransceiver::transmitInterrupt``, which releases the TX mailbox and sends the +next queued frame. The ``CanRxRunnable`` calls ``receiveTask()``, which drains the +software queue and notifies the registered frame listeners. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h b/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h new file mode 100644 index 00000000000..d81d46d8d9e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace platform +{ + +class StaticBsp +{ +public: + void init(); +}; + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h b/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h new file mode 100644 index 00000000000..ff70511ddc2 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h @@ -0,0 +1,67 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace systems +{ + +/** + * CAN system for the NUCLEO-F413ZH: manages a single BxCanTransceiver on CAN1 + * (PD0 RX / PD1 TX, 500 kbit/s). Registered as an etl::singleton so the ISR + * trampolines can reach the instance without global variables. + */ +class CanSystem +: public ::lifecycle::SingleContextLifecycleComponent +, public ::can::ICanSystem +, public ::etl::singleton_base +{ +public: + CanSystem(::async::ContextType context); + + void init() override; + void run() override; + void shutdown() override; + + ::can::ICanTransceiver* getCanTransceiver(uint8_t busId) override; + + /** + * Dispatches CAN RX processing into the async context. + * \note Called from ISR context (call_can_isr_RX). Must be safe for ISR invocation. + */ + void dispatchRxTask(); + +private: + class CanRxRunnable : public ::async::RunnableType + { + public: + explicit CanRxRunnable(CanSystem& parent); + + void execute() override; + + void setEnabled(bool enabled) { _enabled = enabled; } + + private: + CanSystem& _parent; + bool _enabled; + }; + + ::async::ContextType _context; + ::bios::BxCanTransceiver _transceiver0; + CanRxRunnable _canRxRunnable; +}; + +} // namespace systems diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld b/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld new file mode 100644 index 00000000000..7192c02f28d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld @@ -0,0 +1,129 @@ +/* Linker script for STM32F413ZH (NUCLEO-F413ZH) + * + * SPDX-License-Identifier: Apache-2.0 + * + * Flash: 1536 KB at 0x08000000 + * SRAM: 320 KB at 0x20000000 + */ + +ENTRY(Reset_Handler) + +_Min_Heap_Size = 0x200; +_Min_Stack_Size = 0x400; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1536K + /* Top 1 KB (0x2004FC00-0x20050000) is reserved for the hard fault + * handler dump region and survives reset (never initialized here). */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 319K +} + +SECTIONS +{ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + _etext = .; + } > FLASH + + .rodata : + { + . = ALIGN(4); + *(.rodata) + *(.rodata*) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + .preinit_array : + { + PROVIDE_HIDDEN(__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN(__preinit_array_end = .); + } > FLASH + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array*)) + PROVIDE_HIDDEN(__init_array_end = .); + } > FLASH + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array*)) + PROVIDE_HIDDEN(__fini_array_end = .); + } > FLASH + + _sidata = LOADADDR(.data); + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM AT> FLASH + + .bss : + { + . = ALIGN(4); + _sbss = .; + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end__ = _ebss; + } > RAM + + ._user_heap_stack : + { + . = ALIGN(8); + __end__ = .; + _end = .; + PROVIDE(end = .); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } > RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c new file mode 100644 index 00000000000..612501e3939 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/* + * Custom profiling initialization function + * + * Do not remove! This is called by ThreadX core just before its scheduler runs + * if TX_ENABLE_EXECUTION_CHANGE_NOTIFY is defined. + */ +void _tx_execution_initialize() { return; } diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S new file mode 100644 index 00000000000..0d9c1203b4b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S @@ -0,0 +1,96 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ ThreadX low-level initialization for NUCLEO-F413ZH (Cortex-M4, 96 MHz) +@ + +#include "async/Config.h" + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global g_pfnVectors + .global __tx_SysTickHandler + .extern setupApplicationsIsr + +SYSTEM_CLOCK = 96000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / ASYNC_CONFIG_TICK_IN_US) -1) + + .text 32 + .align 4 + .syntax unified + +@ VOID _tx_initialize_low_level(VOID) + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: + + CPSID i + + @ Setup Vector Table Offset Register + MOV r0, #0xE000E000 + LDR r1, =g_pfnVectors + STR r1, [r0, #0xD08] + + @ Set system stack pointer from vector value + LDR r0, =_tx_thread_system_stack_ptr + LDR r1, =g_pfnVectors + LDR r1, [r1] + STR r1, [r0] + + @ Enable the cycle count register (DWT) + LDR r0, =0xE0001000 + LDR r1, [r0] + ORR r1, r1, #1 + STR r1, [r0] + + @ Configure SysTick + MOV r0, #0xE000E000 + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ SysTick Reload Value + MOV r1, #0x7 @ Enable, interrupt, processor clock + STR r1, [r0, #0x10] @ SysTick Control + + @ Configure handler priorities + LDR r1, =0x00000000 @ UsgF, BusF, MemM = 0 + STR r1, [r0, #0xD18] + + LDR r1, =0xFF000000 @ SVCall = 0xFF (lowest) + STR r1, [r0, #0xD1C] + + LDR r1, =0x40FF0000 @ SysTick = 0x40, PendSV = 0xFF + STR r1, [r0, #0xD20] + + @ Configure platform-specific ISRs + PUSH {lr} + BL setupApplicationsIsr + POP {lr} + + BX lr + +@ SysTick handler - redirects to ThreadX timer interrupt + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: + PUSH {r0, lr} + BL _tx_timer_interrupt + POP {r0, lr} + BX LR + + .section .text.SVC_Handler + .global SVC_Handler + .thumb_func +SVC_Handler: + ldr r0, =Default_Handler + bx r0 diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp new file mode 100644 index 00000000000..40d1147666f --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "lifecycle/StaticBsp.h" + +#include "bsp/timer/SystemTimer.h" +#include "bsp/uart/UartConfig.h" + +namespace platform +{ + +void StaticBsp::init() +{ + initSystemTimer(); + bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL).init(); +} + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp new file mode 100644 index 00000000000..b63095f1f12 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "clock/clockConfig.h" +#include "lifecycle/StaticBsp.h" +#include "mcu/mcu.h" +#ifdef PLATFORM_SUPPORT_CAN +#include "systems/CanSystem.h" +#endif + +#include +#include +#include + +extern void app_main(); + +extern "C" +{ +void SystemInit() +{ + configurePll(); + + // LED ON: PB0 = LD1 (green) on NUCLEO-F413ZH + static constexpr uint8_t LED_PIN = 0U; + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; + (void)RCC->AHB1ENR; // Read-back for clock propagation + GPIOB->MODER &= ~(3U << (LED_PIN * 2U)); + GPIOB->MODER |= (1U << (LED_PIN * 2U)); // GPIO output mode + GPIOB->BSRR = (1U << LED_PIN); + + // Early USART3 TX (PD8 AF7, 115200 @ 48 MHz APB1), usable before the BSP + // UART driver is initialized. + // BRR = 48 000 000 / 115 200 = 416.67 -> 417 + static constexpr uint8_t USART_TX_PIN = 8U; + static constexpr uint8_t USART_TX_AF = 7U; // AF7 = USART3_TX on PD8 + static constexpr uint32_t USART_BRR_115200_48MHZ = 417U; + + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; + (void)RCC->AHB1ENR; + RCC->APB1ENR |= RCC_APB1ENR_USART3EN; + (void)RCC->APB1ENR; + + // PD8 = AF7 (USART3_TX) - pin 8 uses AFR[1], index = pin - 8 = 0 + GPIOD->MODER &= ~(3U << (USART_TX_PIN * 2U)); + GPIOD->MODER |= (2U << (USART_TX_PIN * 2U)); // Alternate function mode + GPIOD->AFR[1] &= ~(0xFU << ((USART_TX_PIN - 8U) * 4U)); + GPIOD->AFR[1] |= (static_cast(USART_TX_AF) << ((USART_TX_PIN - 8U) * 4U)); + + USART3->CR1 = 0; + USART3->CR2 = 0; + USART3->CR3 = 0; + USART3->BRR = USART_BRR_115200_48MHZ; + USART3->CR1 = USART_CR1_TE | USART_CR1_UE; + while ((USART3->SR & USART_SR_TC) == 0) {} // Wait for TX complete +} + +// NVIC priorities for peripheral interrupts are configured by the owning +// systems during board bring-up. +void setupApplicationsIsr(void) {} +} // extern "C" + +namespace platform +{ +StaticBsp staticBsp; + +StaticBsp& getStaticBsp() { return staticBsp; } + +#ifdef PLATFORM_SUPPORT_CAN +::etl::typed_storage<::systems::CanSystem> canSystem; +#endif + +void platformLifecycleAdd(::lifecycle::LifecycleManager& lifecycleManager, uint8_t const level) +{ + (void)lifecycleManager; +#ifdef PLATFORM_SUPPORT_CAN + if (level == 2U) + { + lifecycleManager.addComponent("can", canSystem.create(TASK_CAN), level); + } +#endif + (void)level; +} +} // namespace platform + +#ifdef PLATFORM_SUPPORT_CAN +namespace systems +{ +::can::ICanSystem& getCanSystem() { return *::platform::canSystem; } +} // namespace systems +#endif + +int main() +{ + // IWDG refresh - a no-op unless the watchdog is already running (it is + // enabled later by the SafetyManager). + IWDG->KR = 0xAAAAU; + + ::safety::safeSupervisorConstructor.construct(); + ::platform::staticBsp.init(); + IWDG->KR = 0xAAAAU; + + app_main(); + return 1; +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp new file mode 100644 index 00000000000..c3d5bf28539 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +extern "C" +{ +extern void call_can_isr_RX(); +extern void call_can_isr_TX(); + +void CAN1_RX0_IRQHandler(void) { call_can_isr_RX(); } + +void CAN1_TX_IRQHandler(void) { call_can_isr_TX(); } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp new file mode 100644 index 00000000000..58b8505a04d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "reset/softwareSystemReset.h" + +extern "C" +{ +void HardFault_Handler() +{ + // WARNING: + // Do NOT modify this function if possible. Use HardFault_Handler_Final instead. + // Refer to hardFaultHandler documentation for details. +#ifndef UNIT_TEST + asm volatile("b customHardFaultHandler"); +#else + while (true) {} +#endif +} + +void HardFault_Handler_Final() { softwareSystemReset(); } + +} // extern "C" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp new file mode 100644 index 00000000000..232043f997c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp @@ -0,0 +1,25 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FreeRTOS.h" +#include "mcu/mcu.h" +#include "task.h" + +extern "C" +{ +void vApplicationStackOverflowHook(TaskHandle_t /* xTask */, char* /* pcTaskName */) +{ + while (true) {} +} + +void vApplicationMallocFailedHook(void) +{ + while (true) {} +} + +// Feeds the IWDG on every tick. The IWDG may already be running from a +// previous firmware image (it cannot be disabled, only fed). This feed runs +// in addition to the SafetyManager's cyclic watchdog service. +void vApplicationTickHook(void) { IWDG->KR = 0xAAAAU; } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp new file mode 100644 index 00000000000..599f7a09365 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "async/Hook.h" +#include "mcu/mcu.h" +#include "tx_api.h" + +#include + +extern "C" +{ +extern TX_THREAD _tx_timer_thread; + +void _tx_execution_thread_enter() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncEnterTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncEnterTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void _tx_execution_thread_exit() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncLeaveTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncLeaveTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void tx_low_power_enter() +{ + // Feed IWDG before entering low-power mode to prevent watchdog reset. + IWDG->KR = 0xAAAAU; +} + +void tx_low_power_exit() {} + +void vIllegalISR() +{ + printf("vIllegalISR\r\n"); + for (;;) + ; +} +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp new file mode 100644 index 00000000000..68264bb051b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp @@ -0,0 +1,142 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "systems/CanSystem.h" + +#include "async/Config.h" +#include "async/Hook.h" +#include "busid/BusId.h" +#include "mcu/mcu.h" + +namespace +{ +// bxCAN1 configuration for STM32F413ZH +// CAN1: PD0 (RX, AF9), PD1 (TX, AF9), 500 kbit/s @ 48 MHz APB1 +// BTR: SJW=1TQ, BS1=13TQ, BS2=2TQ, prescaler=6 -> 500 kbit/s +::bios::BxCanDevice::Config const can1Config = { + CAN1, // peripheral + 6U, // prescaler (48 MHz / 6 = 8 MHz -> 16 TQ @ 500k) + 13U, // bs1 (13 TQ) + 2U, // bs2 (2 TQ) + 1U, // sjw (1 TQ) + GPIOD, // rxPort + 0U, // rxPin (PD0) + 9U, // rxAf (AF9 = CAN1) + GPIOD, // txPort + 1U, // txPin (PD1) + 9U, // txAf (AF9 = CAN1) +}; +} // namespace + +namespace systems +{ + +CanSystem::CanSystem(::async::ContextType context) +: ::lifecycle::SingleContextLifecycleComponent(context) +, ::etl::singleton_base(*this) +, _context(context) +, _transceiver0(context, ::busid::CAN_0, can1Config) +, _canRxRunnable(*this) +{} + +void CanSystem::init() { transitionDone(); } + +void CanSystem::run() +{ + _canRxRunnable.setEnabled(true); + + (void)_transceiver0.init(); + (void)_transceiver0.open(); + + // Enable CAN IRQs only after the transceiver is open, so the async + // framework is ready before ISRs fire. + // ISR priority must be numerically >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + // to safely use FreeRTOS FromISR APIs. + NVIC_SetPriority(CAN1_RX0_IRQn, 6); + NVIC_SetPriority(CAN1_TX_IRQn, 6); + NVIC_ClearPendingIRQ(CAN1_RX0_IRQn); + NVIC_ClearPendingIRQ(CAN1_TX_IRQn); + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_TX_IRQn); + + transitionDone(); +} + +void CanSystem::shutdown() +{ + (void)_transceiver0.close(); + _transceiver0.shutdown(); + + _canRxRunnable.setEnabled(false); + + transitionDone(); +} + +::can::ICanTransceiver* CanSystem::getCanTransceiver(uint8_t busId) +{ + if (busId == ::busid::CAN_0) + { + return &_transceiver0; + } + return nullptr; +} + +void CanSystem::dispatchRxTask() { ::async::execute(_context, _canRxRunnable); } + +CanSystem::CanRxRunnable::CanRxRunnable(CanSystem& parent) : _parent(parent), _enabled(false) {} + +void CanSystem::CanRxRunnable::execute() +{ + if (_enabled) + { + _parent._transceiver0.receiveTask(); + } +} + +} // namespace systems + +extern "C" +{ +/** + * RX ISR for bxCAN1. Disables the RX FIFO interrupt (FMPIE0) to prevent + * re-entry while the 3-deep hardware FIFO refills, reads pending frames under + * lock, and dispatches the CanRxRunnable. FMPIE0 is re-enabled either here + * (no frames) or in receiveTask() after the software queue is drained. + */ +void call_can_isr_RX() +{ + ::bios::BxCanTransceiver::disableRxInterrupt(::busid::CAN_0); + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + + uint8_t framesReceived; + { + ::async::LockType const lock; + framesReceived = ::bios::BxCanTransceiver::receiveInterrupt(::busid::CAN_0); + } + + if (framesReceived > 0) + { + ::systems::CanSystem::instance().dispatchRxTask(); + } + else + { + ::bios::BxCanTransceiver::enableRxInterrupt(::busid::CAN_0); + } + + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} + +void call_can_isr_TX() +{ + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + ::bios::BxCanTransceiver::transmitInterrupt(::busid::CAN_0); + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt new file mode 100644 index 00000000000..89a1188107d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeLifecycle) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt new file mode 100644 index 00000000000..75799a0bb8c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeLifecycle src/SafetyManager.cpp) + +target_include_directories(safeLifecycle PUBLIC include) + +target_link_libraries(safeLifecycle PRIVATE safeSupervisor safeUtils + safeBspMcuWatchdog) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h new file mode 100644 index 00000000000..1ffe5faba23 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ + +class SafetyManager +{ +public: + SafetyManager(); + void init(); + void run(); + void shutdown(); + void cyclic(); + +private: + uint16_t _counter; + static constexpr uint8_t WATCHDOG_CYCLIC_COUNTER = 8U; ///< Kick every 8 cycles (80ms @ 10ms) +}; + +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp new file mode 100644 index 00000000000..70c07278ca1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "safeLifecycle/SafetyManager.h" + +#include +#include +#include + +namespace safety +{ + +using ::util::logger::Logger; +using ::util::logger::SAFETY; + +SafetyManager::SafetyManager() : _counter(0U) {} + +void SafetyManager::init() +{ + Logger::debug(SAFETY, "SafetyManager initialized"); + + if (bsp::Watchdog::isResetFromWatchdog()) + { + Logger::warn(SAFETY, "Previous reset was caused by IWDG watchdog"); + bsp::Watchdog::clearResetFlag(); + } +} + +void SafetyManager::run() +{ + // Enable IWDG here (not in init) - the cyclic scheduler is already running + // at this point, so the 250ms timeout won't fire before the first kick. + bsp::Watchdog::enableWatchdog(bsp::Watchdog::DEFAULT_TIMEOUT); + Logger::debug(SAFETY, "IWDG watchdog enabled (250ms timeout)"); +} + +void SafetyManager::shutdown() {} + +void SafetyManager::cyclic() +{ + auto& supervisor = SafeSupervisor::getInstance(); + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_ENTER); + + // Kick IWDG every WATCHDOG_CYCLIC_COUNTER cycles (80ms @ 10ms cycle) + _counter++; + if (_counter >= WATCHDOG_CYCLIC_COUNTER) + { + _counter = 0U; + bsp::Watchdog::serviceWatchdog(); + } + + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_LEAVE); +} +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..f0d6bbc1f68 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(threadXCoreConfiguration INTERFACE) + +target_include_directories(threadXCoreConfiguration INTERFACE include) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h new file mode 100644 index 00000000000..d4c03c743be --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +/* ThreadX user configuration for NUCLEO-F413ZH (Cortex-M4, 96 MHz). */ + +#include "async/Config.h" + +#define ROUND_UP_32(x) (((x) + 31U) & ~31U) +#define TX_MAX_PRIORITIES_LIMIT 1024 +#define TX_MIN_PRIORITIES 32 + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES \ + ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) > TX_MAX_PRIORITIES_LIMIT \ + ? TX_MAX_PRIORITIES_LIMIT \ + : ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) < TX_MIN_PRIORITIES \ + ? TX_MIN_PRIORITIES \ + : ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U))) +#endif // TX_MAX_PRIORITIES + +#if (TX_MAX_PRIORITIES < 32) || (TX_MAX_PRIORITIES > 1024) || ((TX_MAX_PRIORITIES % 32) != 0) +#error "TX_MAX_PRIORITIES must be between 32 and 1024 and evenly divisible by 32" +#endif + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK (1024U) +#endif + +#define TX_ENABLE_STACK_CHECKING + +#define TX_NO_FILEX_POINTER + +/* Timer thread stack - the default 1024 is too small when the timer + * callback dispatches multiple runnables. */ +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE (8192U) +#endif + +#define TX_TIMER_TICKS_PER_SECOND (1000000U / ASYNC_CONFIG_TICK_IN_US) + +#define TX_ENABLE_EXECUTION_CHANGE_NOTIFY diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt new file mode 100644 index 00000000000..04843bb3b84 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt @@ -0,0 +1,15 @@ +# NUCLEO-G474RE platform for referenceApp + +add_subdirectory(bspConfiguration) +add_subdirectory(safety) +add_subdirectory(main) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(freeRtosCoreConfiguration) + add_library(freeRtosPort ALIAS freeRtosCm4SysTickPort) + add_library(freeRtosPortImpl ALIAS freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(threadXCoreConfiguration) + add_library(threadXPort ALIAS threadXCortexM4Port) + add_library(threadXPortImpl ALIAS threadXCortexM4) +endif () diff --git a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake new file mode 100644 index 00000000000..0fcbecc817d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake @@ -0,0 +1,55 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(OPENBSW_PLATFORM + "stm32" + CACHE STRING "" FORCE) +set(STM32_CHIP + "STM32G474RE" + CACHE STRING "" FORCE) +set(BUILD_TARGET_RTOS + "FREERTOS" + CACHE STRING "") +set(PLATFORM_SUPPORT_CAN + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_IO + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ETHERNET + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_TRANSPORT + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_PROGRAMMING_SESSION + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_WATCHDOG + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_MPU + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_STORAGE + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ROM_CHECK + OFF + CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..5cb06edc3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(bspConfiguration src/bsp/stdIo/stdIo.cpp + src/bsp/uart/UartConfig.cpp) + +target_include_directories(bspConfiguration PUBLIC include) + +target_link_libraries( + bspConfiguration + PUBLIC bspUart + PRIVATE bspMcu platform) diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst new file mode 100644 index 00000000000..4f9aaab32ef --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst @@ -0,0 +1,57 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspconfig_nucleo_g474re: + +bspConfiguration - NUCLEO-G474RE +================================ + +Overview +-------- + +The ``bspConfiguration`` module contains hardware-specific configuration for the +NUCLEO-G474RE evaluation board. Driver logic is separated from its configuration +so that users can customise a project for different boards or pin assignments +without modifying driver source code. + +The STM32G474RE platform exposes a minimal set of BSP peripherals required for +the CAN reference application: + +- **bspUart** - USART2 configuration (ST-LINK Virtual COM Port on PA2/PA3). +- **bspStdIo** - Standard I/O bridge that routes ``putByteToStdout`` / + ``getByteFromStdin`` through the configured UART instance. + +.. note:: + + Unlike the S32K148EVB reference application, this platform does not include + ADC, PWM, or digital I/O configuration modules because the CAN reference + application does not require analogue inputs or GPIO-driven outputs. + These modules can be added following the same configuration pattern if + a future application requires them. + +Hardware Summary +++++++++++++++++ + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32G474RE (Arm Cortex-M4F, 170 MHz, single-precision FPU)" + "UART peripheral", "USART2 - routed to ST-LINK/V3 Virtual COM Port" + "UART TX pin", "PA2 (AF7)" + "UART RX pin", "PA3 (AF7)" + "UART baud rate", "115 200 baud (BRR = 1476 at 170 MHz APB1)" + "CAN peripheral", "FDCAN1 (configured in CanSystem, not bspConfiguration)" + +.. toctree:: + :hidden: + + user/index diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst new file mode 100644 index 00000000000..c8aa2d42b46 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst @@ -0,0 +1,34 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_StdIo_G474RE: + +bspStdIo +======== + +Overview +-------- + +The standard I/O bridge implements the ``putByteToStdout`` and +``getByteFromStdin`` C-linkage functions required by the OpenBSW logging +framework, routing character-level I/O through the UART ``TERMINAL`` instance +configured in :ref:`bspConfig_Uart_G474RE`. + +``putByteToStdout(uint8_t byte)`` transmits one byte over USART2 TX (PA2), +blocking by polling until the UART TX register is free. + +``getByteFromStdin()`` attempts to read one byte from USART2 RX (PA3) and is +non-blocking: if ``Uart::read()`` reports that no byte was received, it returns +``-1`` (POSIX ``getchar()`` convention); otherwise it returns the byte value +(0-255). + +Both functions resolve the UART instance once into a ``static`` local +reference, avoiding a global constructor ordering dependency. diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst new file mode 100644 index 00000000000..aa8cc595e93 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_Uart_G474RE: + +bspUart Configuration +===================== + +Overview +-------- + +The UART configuration defines the hardware parameters for the on-board +ST-LINK Virtual COM Port of the NUCLEO-G474RE. A single UART instance +(``TERMINAL``) is configured for debug logging and interactive console use. + +``UartConfig.h`` declares the ``Uart::Id`` enumeration of available UART +instances, and ``UartConfig.cpp`` maps each ``Uart::Id`` to its hardware +parameters and provides the singleton accessor +``bsp::Uart::getInstance(Uart::Id)``. + +Configuration +------------- + +.. csv-table:: + :header: "Parameter", "Value" + :widths: 30, 70 + :width: 100% + + "Peripheral", "USART2 (APB1)" + "TX pin", "PA2, alternate function AF7" + "RX pin", "PA3, alternate function AF7" + "Baud rate", "115 200 baud" + "BRR", "170 000 000 / 115 200 = 1475.69 -> 1476" + +The resulting actual baud rate is 170 MHz / 1476 = 115 176 baud (0.021 % error), +well within the UART tolerance. + +.. note:: + + The UART driver uses polling mode (no DMA, no interrupts). For high- + throughput or latency-sensitive applications, consider extending the + ``bspUart`` BSP module with an interrupt-driven or DMA-backed backend. diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst new file mode 100644 index 00000000000..5010a644f16 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Detailed documentation for each BSP configuration module on the NUCLEO-G474RE +platform. + +Modules +------- + +.. toctree:: + :hidden: + + bspUart + bspStdIo + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`bspConfig_Uart_G474RE`, "USART2 pin mapping, baud-rate register value, and clock-enable bits" + :ref:`bspConfig_StdIo_G474RE`, "Standard I/O bridge routing printf / scanf to the UART terminal" diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h new file mode 100644 index 00000000000..b1f23bee3c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +enum class Uart::Id : size_t +{ + TERMINAL, + INVALID, +}; + +static constexpr size_t NUMBER_OF_UARTS = static_cast(Uart::Id::INVALID); + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec new file mode 100644 index 00000000000..c918431ceaa --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec @@ -0,0 +1,2 @@ +unit_test: false +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp new file mode 100644 index 00000000000..d6b4b5f14fb --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/uart/UartConfig.h" +#include "platform/estdint.h" + +extern "C" void putByteToStdout(uint8_t const byte) +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uart.write(etl::span(&byte, 1U)); +} + +extern "C" int32_t getByteFromStdin() +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uint8_t dataByte = 0; + if (uart.read(etl::span(&dataByte, 1U)) == 0U) + { + return -1; + } + return dataByte; +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp new file mode 100644 index 00000000000..5c72b5d6cdc --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// NUCLEO-G474RE: USART2 on PA2 (TX) / PA3 (RX), AF7 +// ST-LINK VCP, 115200 baud @ 170 MHz APB1 + +#include +#include +#include + +namespace bsp +{ + +Uart::UartConfig const Uart::_uartConfigs[] = { + { + USART2, // usart + GPIOA, // gpioPort + 2U, // txPin (PA2) + 3U, // rxPin (PA3) + 7U, // af (AF7) + 1476U, // brr (170000000 / 115200 = 1475.69 -> 1476) + RCC_AHB2ENR_GPIOAEN, // rccGpioEnBit + RCC_APB1ENR1_USART2EN, // rccUsartEnBit + &RCC->AHB2ENR, // rccGpioEnReg + &RCC->APB1ENR1, // rccUsartEnReg + }, +}; + +static Uart instances[] = { + Uart(Uart::Id::TERMINAL), +}; + +Uart& Uart::getInstance(Id id) +{ + ETL_ASSERT( + id < Id::INVALID, ETL_ERROR_GENERIC("UartId::INVALID is not a valid Uart identifier")); + static_assert( + NUMBER_OF_UARTS == static_cast(etl::size(instances)), + "Not enough Uart instances defined"); + return instances[static_cast(id)]; +} + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..1e859851fac --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(freeRtosCoreConfiguration INTERFACE) +target_include_directories(freeRtosCoreConfiguration INTERFACE include) +target_link_libraries(freeRtosCoreConfiguration INTERFACE bsp bspMcu) diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h new file mode 100644 index 00000000000..c55b65c60c8 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h @@ -0,0 +1,118 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +// FreeRTOS device-specific configuration for NUCLEO-G474RE. +// Included by asyncFreeRtos/freeRtosConfiguration/FreeRTOSConfig.h. + +#pragma once + +#include "bsp/SystemTime.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define configCPU_CLOCK_HZ (170000000UL) + +#undef configMINIMAL_STACK_SIZE +#define configMINIMAL_STACK_SIZE ((unsigned short)512) +#define configMAX_TASK_NAME_LEN (10) +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 30 +#undef configCHECK_FOR_STACK_OVERFLOW +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_FPU 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 + +/* Memory allocation */ +#define configTOTAL_HEAP_SIZE 512 +#define configAPPLICATION_ALLOCATED_HEAP 1 + +/* Co-routine definitions */ +#define configMAX_CO_ROUTINE_PRIORITIES (0) + +/* Software timer definitions */ +#undef configTIMER_QUEUE_LENGTH +#define configTIMER_QUEUE_LENGTH 30 +#undef configTIMER_TASK_STACK_DEPTH +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE) + +/* API function includes */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vResumeFromISR 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_eTaskGetState 1 +#undef INCLUDE_uxTaskGetStackHighWaterMark +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#undef INCLUDE_xTaskGetCurrentTaskHandle +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#undef INCLUDE_xTaskGetIdleTaskHandle +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_xEventGroupSetBitFromISR 1 +#undef INCLUDE_xTimerPendFunctionCall +#define INCLUDE_xTimerPendFunctionCall 1 + +/* Cortex-M specific definitions */ +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 4 +#endif + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0F +// ISRs that call FreeRTOS FromISR APIs must run at numeric priority >= this value. +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x06 + +#ifndef configKERNEL_INTERRUPT_PRIORITY +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +/* Assert */ +#define DEV_ASSERT(x) +#if defined(__GNUC__) && (!defined(__ASSEMBLER__)) +#include "mcu/mcu.h" +#endif +#define configASSERT(x) DEV_ASSERT(x) + +/* Tickless idle */ +#define configUSE_TICKLESS_IDLE 0 +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#define configUSE_TICKLESS_IDLE_DECISION_HOOK 0 + +/* Map FreeRTOS port handlers to CMSIS standard names */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler + +/* Run-time stats */ +#define configGENERATE_RUN_TIME_STATS (1) +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() getSystemTimeUs32Bit() + +#define configTIMER_SERVICE_TASK_NAME "TIMER_OS" + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt new file mode 100644 index 00000000000..c6350831ed0 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt @@ -0,0 +1,71 @@ +# startUp is an INTERFACE library - it carries no compiled sources, only +# propagates link dependencies (bspMcu, common, hardFaultHandler) and the linker +# script to consumers via INTERFACE properties. +add_library(startUp INTERFACE) + +target_link_libraries(startUp INTERFACE bspMcu common hardFaultHandler) + +add_library( + main + src/os/isr/isr_can.cpp + src/os/isr/isr_sys.cpp + src/systems/CanSystem.cpp + src/lifecycle/StaticBsp.cpp + src/main.cpp) + +target_include_directories(main PUBLIC include) + +target_link_libraries( + main + PUBLIC bspConfiguration + PRIVATE bspClock + bspTimer + bspUart + fdCanTransceiver + configuration + etl + lifecycle + safeSupervisor + startUp) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_link_libraries(main PRIVATE freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_sources(startUp INTERFACE src/bsp/threadx/tx_initialize_low_level.S + src/bsp/threadx/tx_execution_initialize.c) + target_link_libraries(main PRIVATE threadXCortexM4) +endif () + +# --whole-archive forces the linker to include ALL object files from the main +# library, even if no other translation unit references them. This is required +# because isr_can.cpp provides strong ISR definitions (FDCAN1_IT0_IRQHandler, +# FDCAN1_IT1_IRQHandler) that override the weak default handlers in the startup +# assembly. Without --whole-archive the linker would discard isr_can.o as +# "unreferenced" and the weak stubs would remain, silently swallowing CAN IRQs. +set_target_properties(main PROPERTIES INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE + "") +target_link_options( + main + INTERFACE + "LINKER:--whole-archive" + "$" + "LINKER:--no-whole-archive") + +set_target_properties( + startUp + PROPERTIES PROP_LINKER_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") +set_target_properties( + startUp + PROPERTIES LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_library(osHooks src/osHooks/freertos/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_library(osHooks src/osHooks/threadx/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common threadXCortexM4 asyncThreadX + threadXConfiguration) +endif () +target_include_directories(osHooks PRIVATE include) diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst new file mode 100644 index 00000000000..9fa4523d3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_main: + +main - NUCLEO-G474RE +==================== + +Overview +-------- + +The ``main`` module contains the platform-specific entry point, startup code, linker +script, and lifecycle systems for the NUCLEO-G474RE evaluation board: + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32G474RE - Arm Cortex-M4F, single-precision FPU" + "Core clock", "170 MHz (HSI16 with PLL, boost mode enabled)" + "Flash", "512 KB" + "SRAM", "128 KB (SRAM1 80 KB + SRAM2 16 KB + CCM SRAM 32 KB)" + "CAN", "FDCAN1 - PA11 (RX) / PA12 (TX), AF9, 500 kbit/s" + "Debug UART", "USART2 - PA2 TX (AF7), 115200 baud, routed to ST-LINK VCP" + "User LED", "LD2 on PA5 (active-high)" + "Debug interface", "On-board ST-LINK/V3 (SWD)" + +See :ref:`bspconfig_nucleo_g474re` for the UART and standard I/O configuration, and the +user documentation for the sub-modules: + +.. toctree:: + user/index diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst new file mode 100644 index 00000000000..6bddfae3da2 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst @@ -0,0 +1,24 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_StaticBsp: + +Static BSP +========== + +Overview +-------- + +The ``StaticBsp`` class aggregates the BSP peripherals that must be operational before +the lifecycle starts ("static"): the system timer (DWT cycle counter based) and the +UART terminal (USART2, see :ref:`bspConfig_Uart_G474RE`). It is instantiated in +``main.cpp`` and initialized from ``main()`` before ``app_main()``, so timing services +and serial output are available during the early lifecycle phases. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst new file mode 100644 index 00000000000..e089d3fb166 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Find the detailed information about each module below: + +.. toctree:: + :hidden: + + startup + main + StaticBsp + systems/CanSystem + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`nucleo_g474re_startup`, "Startup Code and Reset Handler" + :ref:`nucleo_g474re_main_entry`, "Main File and boot sequence" + :ref:`nucleo_g474re_StaticBsp`, "Static BSP - pre-lifecycle peripheral init" + :ref:`nucleo_g474re_CanSystem`, "CAN System (FDCAN1, 500 kbit/s)" diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst new file mode 100644 index 00000000000..a41942fd4ca --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst @@ -0,0 +1,35 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_main_entry: + +main File +========= + +The ``main.cpp`` module is the entry point of the NUCLEO-G474RE reference application. +``SystemInit()``, called from ``Reset_Handler``, configures the PLL to 170 MHz, switches +on the LD2 LED (PA5), and sets up early USART2 TX output for use before the BSP UART +driver is initialized. + +``main()`` constructs the safety supervisor, initializes the static BSP (system timer +and UART terminal, see :ref:`nucleo_g474re_StaticBsp`) and calls ``app_main()``, which +starts the lifecycle manager and the FreeRTOS scheduler. The task contexts are defined +in ``async/Config.h`` of ``referenceApp/asyncCoreConfiguration``: ``TASK_BACKGROUND``, +``TASK_BSP``, ``TASK_UDS``, ``TASK_DEMO``, ``TASK_ETHERNET``, ``TASK_CAN``, +``TASK_SYSADMIN`` and ``TASK_SAFETY``. + +``platformLifecycleAdd()`` registers the platform's ``CanSystem`` at runlevel 2 in the +``TASK_CAN`` context; the remaining components - including ``RuntimeSystem`` and +``SafetySystem`` at runlevel 1 - are registered by the shared application code. + +``HardFault_Handler`` branches to ``customHardFaultHandler`` from the +``hardFaultHandler`` module, which dumps the registers to RAM and ends in +``HardFault_Handler_Final``, which performs a system reset. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst new file mode 100644 index 00000000000..fc83573298b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_startup: + +Startup Code +============ + +The startup code (``platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s``) provides +the vector table and the ``Reset_Handler`` for the STM32G474xx. + +Vector Table +------------ + +The vector table is placed at the base of flash (``0x0800_0000``) in the +``.isr_vector`` section. It contains the initial stack pointer, the Cortex-M4 exception +vectors, and the 102 STM32G474xx interrupt vectors. Every handler is a weak alias of +``Default_Handler`` (an infinite loop) and is overridden by a strong definition where +the application implements one (e.g. ``HardFault_Handler``, ``FDCAN1_IT0_IRQHandler``). + +Reset Handler +------------- + +``Reset_Handler`` executes the following sequence: + +1. Load the initial stack pointer (``_estack``). +2. Copy the ``.data`` section from flash to SRAM. +3. Zero-fill the ``.bss`` section. +4. Enable the FPU (CP10/CP11 full access in ``CPACR``, followed by ``dsb``/``isb``). +5. Call ``SystemInit()`` (PLL and early peripheral setup, defined in ``main.cpp``). +6. Call ``__libc_init_array`` (C++ global/static constructors). +7. Call ``main()``. + +The startup code does not mask or unmask interrupts; interrupt control is left to the +FreeRTOS port once the scheduler starts. + +Memory Layout +------------- + +The linker script places code in 512 KB flash at ``0x0800_0000`` and data in 128 KB of +contiguous SRAM at ``0x2000_0000`` (SRAM1 80 KB + SRAM2 16 KB + CCM SRAM 32 KB, which +is aliased at ``0x2001_8000``). The stack grows down from the top of SRAM. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst new file mode 100644 index 00000000000..3d443708a9b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst @@ -0,0 +1,79 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_CanSystem: + +CAN System +========== + +Overview +-------- + +The ``CanSystem`` implements the ``ICanSystem`` interface for the NUCLEO-G474RE using +the STM32G474RE's FDCAN1 peripheral. It manages a single ``FdCanTransceiver`` +(``CAN_0``), participates in the lifecycle as a ``SingleContextLifecycleComponent`` +running in the ``TASK_CAN`` context, and provides the ``extern "C"`` ISR trampolines. +It is registered as an ``etl::singleton`` so the ISR handlers can reach the instance +without global variables. + +Hardware Configuration +---------------------- + +FDCAN1 uses PA11 (RX) and PA12 (TX), alternate function AF9. An external CAN +transceiver is required to drive the bus. Bit timing at 170 MHz kernel clock: +prescaler 20 (8.5 MHz time quantum), 17 TQ per bit (1 sync + 14 seg1 + 2 seg2), +giving 500 kbit/s with a sample point of 88.2 %. + +Lifecycle +--------- + +``init()`` only signals ``transitionDone()`` - the peripheral is untouched until +``run()``, which: + +1. Enables the ``CanRxRunnable`` and sets the accept-all frame filter. +2. Initializes and opens the ``FdCanTransceiver`` (FDCAN1 register setup, normal mode). +3. Enables the RX FIFO 0 interrupt and configures the NVIC: + + .. code-block:: c++ + + FDCAN1->IE = FDCAN_IE_RF0NE; + FDCAN1->ILS = 0U; + FDCAN1->ILE = FDCAN_ILE_EINT0; + + NVIC_SetPriority(FDCAN1_IT0_IRQn, 6); + NVIC_SetPriority(FDCAN1_IT1_IRQn, 6); + NVIC_ClearPendingIRQ(FDCAN1_IT0_IRQn); + NVIC_ClearPendingIRQ(FDCAN1_IT1_IRQn); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + +4. Schedules a periodic 50 ms timer that runs the ``CanTxRunnable``. + +``shutdown()`` closes and shuts down the transceiver and disables the RX runnable. + +Interrupt Handling +------------------ + +With ``ILS = 0`` all FDCAN1 interrupt sources are routed to interrupt line 0 (only +``EINT0`` is enabled), so ``FDCAN1_IT0_IRQHandler`` handles both RX and TX events while +``FDCAN1_IT1_IRQHandler`` / ``call_can_isr_TX()`` are empty. ``call_can_isr_RX()`` +reads ``FDCAN1->IR`` once and: + +- on ``RF0N``, reads pending frames from the hardware FIFO into the software queue + under lock (``FdCanTransceiver::receiveInterrupt``) and, if frames were received, + dispatches the ``CanRxRunnable`` into the ``TASK_CAN`` context; +- on ``TC``, forwards the TX-complete event to ``FdCanTransceiver::transmitInterrupt``. + +The ``CanRxRunnable`` calls ``receiveTask()``, which drains the software queue and +notifies the registered frame listeners. The ``CanTxRunnable`` calls +``FdCanTransceiver::pollTxCallback(CAN_0)``, which invokes the sent listener of +completed transmissions; the 50 ms timer guarantees this poll runs even when a +dispatch from the ISR is dropped by the async dedup logic. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h b/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h new file mode 100644 index 00000000000..d81d46d8d9e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace platform +{ + +class StaticBsp +{ +public: + void init(); +}; + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h b/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h new file mode 100644 index 00000000000..d8f777c6f12 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace systems +{ + +/** + * CAN system for the NUCLEO-G474RE: manages a single FdCanTransceiver on FDCAN1 + * (PA11 RX / PA12 TX, 500 kbit/s). Registered as an etl::singleton so the ISR + * trampolines can reach the instance without global variables. + */ +class CanSystem +: public ::lifecycle::SingleContextLifecycleComponent +, public ::can::ICanSystem +, public ::etl::singleton_base +{ +public: + CanSystem(::async::ContextType context); + + void init() override; + void run() override; + void shutdown() override; + + ::can::ICanTransceiver* getCanTransceiver(uint8_t busId) override; + + /** + * Dispatches CAN RX processing into the async context. + * \note Called from ISR context (call_can_isr_RX). Must be safe for ISR invocation. + */ + void dispatchRxTask(); + + /** Dispatches CAN TX confirmation processing into the async context. */ + void dispatchTxTask(); + +private: + class CanRxRunnable : public ::async::RunnableType + { + public: + explicit CanRxRunnable(CanSystem& parent); + + void execute() override; + + void setEnabled(bool enabled) { _enabled = enabled; } + + private: + CanSystem& _parent; + bool _enabled; + }; + + class CanTxRunnable : public ::async::RunnableType + { + public: + explicit CanTxRunnable(CanSystem& parent) : _parent(parent) {} + + void execute() override; + + private: + CanSystem& _parent; + }; + + ::async::ContextType _context; + ::bios::FdCanTransceiver _transceiver0; + CanRxRunnable _canRxRunnable; + CanTxRunnable _canTxRunnable; + ::async::TimeoutType _canTxTimeout; +}; + +} // namespace systems diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld b/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld new file mode 100644 index 00000000000..949c035248e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld @@ -0,0 +1,129 @@ +/* Linker script for STM32G474RE (NUCLEO-G474RE) + * + * SPDX-License-Identifier: Apache-2.0 + * + * Flash: 512 KB at 0x08000000 + * SRAM: 128 KB at 0x20000000 + */ + +ENTRY(Reset_Handler) + +_Min_Heap_Size = 0x200; +_Min_Stack_Size = 0x400; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + /* Top 1 KB (0x2001FC00-0x20020000) is reserved for the hard fault + * handler dump region and survives reset (never initialized here). */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 127K +} + +SECTIONS +{ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + _etext = .; + } > FLASH + + .rodata : + { + . = ALIGN(4); + *(.rodata) + *(.rodata*) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + .preinit_array : + { + PROVIDE_HIDDEN(__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN(__preinit_array_end = .); + } > FLASH + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array*)) + PROVIDE_HIDDEN(__init_array_end = .); + } > FLASH + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array*)) + PROVIDE_HIDDEN(__fini_array_end = .); + } > FLASH + + _sidata = LOADADDR(.data); + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM AT> FLASH + + .bss : + { + . = ALIGN(4); + _sbss = .; + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end__ = _ebss; + } > RAM + + ._user_heap_stack : + { + . = ALIGN(8); + __end__ = .; + _end = .; + PROVIDE(end = .); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } > RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/module.spec b/executables/referenceApp/platforms/nucleo_g474re/main/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c new file mode 100644 index 00000000000..612501e3939 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/* + * Custom profiling initialization function + * + * Do not remove! This is called by ThreadX core just before its scheduler runs + * if TX_ENABLE_EXECUTION_CHANGE_NOTIFY is defined. + */ +void _tx_execution_initialize() { return; } diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S new file mode 100644 index 00000000000..92d10f6aaa4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S @@ -0,0 +1,96 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ ThreadX low-level initialization for NUCLEO-G474RE (Cortex-M4F, 170 MHz) +@ + +#include "async/Config.h" + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global g_pfnVectors + .global __tx_SysTickHandler + .extern setupApplicationsIsr + +SYSTEM_CLOCK = 170000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / ASYNC_CONFIG_TICK_IN_US) -1) + + .text 32 + .align 4 + .syntax unified + +@ VOID _tx_initialize_low_level(VOID) + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: + + CPSID i + + @ Setup Vector Table Offset Register + MOV r0, #0xE000E000 + LDR r1, =g_pfnVectors + STR r1, [r0, #0xD08] + + @ Set system stack pointer from vector value + LDR r0, =_tx_thread_system_stack_ptr + LDR r1, =g_pfnVectors + LDR r1, [r1] + STR r1, [r0] + + @ Enable the cycle count register (DWT) + LDR r0, =0xE0001000 + LDR r1, [r0] + ORR r1, r1, #1 + STR r1, [r0] + + @ Configure SysTick + MOV r0, #0xE000E000 + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ SysTick Reload Value + MOV r1, #0x7 @ Enable, interrupt, processor clock + STR r1, [r0, #0x10] @ SysTick Control + + @ Configure handler priorities + LDR r1, =0x00000000 @ UsgF, BusF, MemM = 0 + STR r1, [r0, #0xD18] + + LDR r1, =0xFF000000 @ SVCall = 0xFF (lowest) + STR r1, [r0, #0xD1C] + + LDR r1, =0x40FF0000 @ SysTick = 0x40, PendSV = 0xFF + STR r1, [r0, #0xD20] + + @ Configure platform-specific ISRs + PUSH {lr} + BL setupApplicationsIsr + POP {lr} + + BX lr + +@ SysTick handler - redirects to ThreadX timer interrupt + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: + PUSH {r0, lr} + BL _tx_timer_interrupt + POP {r0, lr} + BX LR + + .section .text.SVC_Handler + .global SVC_Handler + .thumb_func +SVC_Handler: + ldr r0, =Default_Handler + bx r0 diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp new file mode 100644 index 00000000000..40d1147666f --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "lifecycle/StaticBsp.h" + +#include "bsp/timer/SystemTimer.h" +#include "bsp/uart/UartConfig.h" + +namespace platform +{ + +void StaticBsp::init() +{ + initSystemTimer(); + bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL).init(); +} + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp new file mode 100644 index 00000000000..80b656ce993 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp @@ -0,0 +1,87 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "clock/clockConfig.h" +#include "lifecycle/StaticBsp.h" +#include "mcu/mcu.h" +#include "systems/CanSystem.h" + +#include +#include +#include + +extern void app_main(); + +extern "C" +{ +void SystemInit() +{ + configurePll(); + + // LED ON: PA5 = LD2 (output push-pull) + static constexpr uint8_t LED_PIN = 5U; + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + GPIOA->MODER &= ~(3U << (LED_PIN * 2U)); + GPIOA->MODER |= (1U << (LED_PIN * 2U)); // GPIO output mode + GPIOA->BSRR = (1U << LED_PIN); + + // Early USART2 TX (PA2 AF7, 115200 @ 170 MHz), usable before the BSP + // UART driver is initialized. + // BRR = 170 000 000 / 115 200 = 1475.69 -> 1476 + static constexpr uint8_t USART_TX_PIN = 2U; + static constexpr uint8_t USART_TX_AF = 7U; // AF7 = USART2_TX on PA2 + static constexpr uint32_t USART_BRR_115200_170MHZ = 1476U; + + RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; + (void)RCC->APB1ENR1; // Read-back for clock propagation + (void)RCC->APB1ENR1; + GPIOA->MODER &= ~(3U << (USART_TX_PIN * 2U)); + GPIOA->MODER |= (2U << (USART_TX_PIN * 2U)); // Alternate function mode + GPIOA->AFR[0] &= ~(0xFU << (USART_TX_PIN * 4U)); + GPIOA->AFR[0] |= (static_cast(USART_TX_AF) << (USART_TX_PIN * 4U)); + USART2->CR1 = 0; + USART2->CR2 = 0; + USART2->CR3 = 0; + USART2->BRR = USART_BRR_115200_170MHZ; + USART2->CR1 = USART_CR1_TE | USART_CR1_UE; + while ((USART2->ISR & USART_ISR_TEACK) == 0) {} // Wait for TX enable ack +} +} // extern "C" + +namespace platform +{ +StaticBsp staticBsp; + +StaticBsp& getStaticBsp() { return staticBsp; } + +::etl::typed_storage<::systems::CanSystem> canSystem; + +void platformLifecycleAdd(::lifecycle::LifecycleManager& lifecycleManager, uint8_t const level) +{ + if (level == 2U) + { + lifecycleManager.addComponent("can", canSystem.create(TASK_CAN), level); + } +} +} // namespace platform + +namespace systems +{ +::can::ICanSystem& getCanSystem() { return *::platform::canSystem; } +} // namespace systems + +int main() +{ + ::safety::safeSupervisorConstructor.construct(); + ::platform::staticBsp.init(); + app_main(); + return 1; +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp new file mode 100644 index 00000000000..e2f4dc34f81 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +extern "C" +{ +extern void call_can_isr_RX(); +extern void call_can_isr_TX(); + +void FDCAN1_IT0_IRQHandler(void) { call_can_isr_RX(); } + +void FDCAN1_IT1_IRQHandler(void) { call_can_isr_TX(); } +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp new file mode 100644 index 00000000000..58b8505a04d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "reset/softwareSystemReset.h" + +extern "C" +{ +void HardFault_Handler() +{ + // WARNING: + // Do NOT modify this function if possible. Use HardFault_Handler_Final instead. + // Refer to hardFaultHandler documentation for details. +#ifndef UNIT_TEST + asm volatile("b customHardFaultHandler"); +#else + while (true) {} +#endif +} + +void HardFault_Handler_Final() { softwareSystemReset(); } + +} // extern "C" diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp new file mode 100644 index 00000000000..0e19b52cc59 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp @@ -0,0 +1,19 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FreeRTOS.h" +#include "task.h" + +extern "C" +{ +void vApplicationStackOverflowHook(TaskHandle_t /* xTask */, char* /* pcTaskName */) +{ + while (true) {} +} + +void vApplicationMallocFailedHook(void) +{ + while (true) {} +} +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp new file mode 100644 index 00000000000..252a436348c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "async/Hook.h" +#include "mcu/mcu.h" +#include "tx_api.h" + +#include + +extern "C" +{ +extern TX_THREAD _tx_timer_thread; + +// Called from tx_initialize_low_level.S. NVIC priorities for peripheral +// interrupts are configured by the owning systems during board bring-up. +void setupApplicationsIsr() {} + +void _tx_execution_thread_enter() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncEnterTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncEnterTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void _tx_execution_thread_exit() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncLeaveTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncLeaveTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void tx_low_power_enter() +{ + // Feed IWDG before entering low-power mode to prevent watchdog reset. + IWDG->KR = 0xAAAAU; +} + +void tx_low_power_exit() {} + +void vIllegalISR() +{ + printf("vIllegalISR\r\n"); + for (;;) + ; +} +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp new file mode 100644 index 00000000000..b1f72b9aabd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp @@ -0,0 +1,169 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "systems/CanSystem.h" + +#include "async/Config.h" +#include "async/Hook.h" +#include "busid/BusId.h" +#include "mcu/mcu.h" + +namespace +{ +// FDCAN1 configuration for STM32G474RE +// FDCAN1: PA11 (RX, AF9), PA12 (TX, AF9), 500 kbit/s @ 170 MHz +// NBTP: NSJW=1TQ, NBS1=14TQ, NBS2=2TQ, NBRP=20 -> 500 kbit/s +::bios::FdCanDevice::Config const fdcan1Config = { + FDCAN1, // peripheral + 20U, // prescaler (170 MHz / 20 = 8.5 MHz -> 17 TQ @ 500k) + 13U, // tseg1 (14 TQ - 1) + 1U, // tseg2 (2 TQ - 1) + 0U, // sjw (1 TQ - 1) + GPIOA, // rxPort + 11U, // rxPin + 9U, // rxAf (AF9 = FDCAN1) + GPIOA, // txPort + 12U, // txPin + 9U, // txAf (AF9 = FDCAN1) +}; +} // namespace + +namespace systems +{ + +CanSystem::CanSystem(::async::ContextType context) +: ::lifecycle::SingleContextLifecycleComponent(context) +, ::etl::singleton_base(*this) +, _context(context) +, _transceiver0(context, ::busid::CAN_0, fdcan1Config) +, _canRxRunnable(*this) +, _canTxRunnable(*this) +{} + +void CanSystem::init() { transitionDone(); } + +void CanSystem::run() +{ + _canRxRunnable.setEnabled(true); + + // Accept all CAN frames (no HW filter). fFilterIds/fFilterCount are + // uninitialized in FdCanDevice - must explicitly set to accept-all. + _transceiver0.fDevice.fFilterIds = nullptr; + _transceiver0.fDevice.fFilterCount = 0U; + + (void)_transceiver0.init(); + (void)_transceiver0.open(); + + // Enable FDCAN IRQs only after the transceiver is open, so the async + // framework is ready before ISRs fire. + // Enable RX interrupt only. TCE is managed by FdCanDevice per-TX + // (enabled before listener TX, disabled by transmitISR). + FDCAN1->IE = FDCAN_IE_RF0NE; + FDCAN1->ILS = 0U; + FDCAN1->ILE = FDCAN_ILE_EINT0; + + // ISR priority must be numerically >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + // to safely use FreeRTOS FromISR APIs. + NVIC_SetPriority(FDCAN1_IT0_IRQn, 6); + NVIC_SetPriority(FDCAN1_IT1_IRQn, 6); + NVIC_ClearPendingIRQ(FDCAN1_IT0_IRQn); + NVIC_ClearPendingIRQ(FDCAN1_IT1_IRQn); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + + // Schedule periodic TX callback poll. The ISR dispatches canTxRunnable + // via async::execute, but dedup drops repeated dispatches when the + // runnable is already queued. The periodic timer ensures pollTxCallback + // runs within 50ms regardless of dedup. + ::async::scheduleAtFixedRate( + _context, _canTxRunnable, _canTxTimeout, 50U, ::async::TimeUnit::MILLISECONDS); + + transitionDone(); +} + +void CanSystem::shutdown() +{ + (void)_transceiver0.close(); + _transceiver0.shutdown(); + + _canRxRunnable.setEnabled(false); + + transitionDone(); +} + +::can::ICanTransceiver* CanSystem::getCanTransceiver(uint8_t busId) +{ + if (busId == ::busid::CAN_0) + { + return &_transceiver0; + } + return nullptr; +} + +void CanSystem::dispatchRxTask() { ::async::execute(_context, _canRxRunnable); } + +void CanSystem::dispatchTxTask() { ::async::execute(_context, _canTxRunnable); } + +void CanSystem::CanTxRunnable::execute() +{ + // Process TX completion in task context. The ISR dispatches here via + // async::execute after TC fires. pollTxCallback checks the TX queue and + // invokes the sent listener of each completed frame. + ::bios::FdCanTransceiver::pollTxCallback(::busid::CAN_0); +} + +CanSystem::CanRxRunnable::CanRxRunnable(CanSystem& parent) : _parent(parent), _enabled(false) {} + +void CanSystem::CanRxRunnable::execute() +{ + if (_enabled) + { + _parent._transceiver0.receiveTask(); + } +} + +} // namespace systems + +extern "C" +{ +/** + * Combined RX + TX ISR - all FDCAN1 interrupts routed to IT0. + * Checks IR register to determine if RX, TX, or both occurred. + */ +void call_can_isr_RX() +{ + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + + uint32_t const ir = FDCAN1->IR; + + if ((ir & FDCAN_IR_RF0N) != 0U) + { + ::async::LockType const lock; + uint8_t framesReceived = ::bios::FdCanTransceiver::receiveInterrupt(::busid::CAN_0); + + if (framesReceived > 0) + { + ::systems::CanSystem::instance().dispatchRxTask(); + } + } + + if ((ir & FDCAN_IR_TC) != 0U) + { + ::bios::FdCanTransceiver::transmitInterrupt(::busid::CAN_0); + } + + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} + +void call_can_isr_TX() +{ + // Not used - all interrupts routed to IT0 +} +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt new file mode 100644 index 00000000000..89a1188107d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeLifecycle) diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt new file mode 100644 index 00000000000..75799a0bb8c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeLifecycle src/SafetyManager.cpp) + +target_include_directories(safeLifecycle PUBLIC include) + +target_link_libraries(safeLifecycle PRIVATE safeSupervisor safeUtils + safeBspMcuWatchdog) diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h new file mode 100644 index 00000000000..1ffe5faba23 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ + +class SafetyManager +{ +public: + SafetyManager(); + void init(); + void run(); + void shutdown(); + void cyclic(); + +private: + uint16_t _counter; + static constexpr uint8_t WATCHDOG_CYCLIC_COUNTER = 8U; ///< Kick every 8 cycles (80ms @ 10ms) +}; + +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp new file mode 100644 index 00000000000..4c3af0c4787 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "safeLifecycle/SafetyManager.h" + +#include +#include +#include + +namespace safety +{ + +using ::util::logger::Logger; +using ::util::logger::SAFETY; + +SafetyManager::SafetyManager() : _counter(0U) {} + +void SafetyManager::init() +{ + Logger::debug(SAFETY, "SafetyManager initialized"); + + if (bsp::Watchdog::isResetFromWatchdog()) + { + Logger::warn(SAFETY, "Previous reset was caused by IWDG watchdog"); + bsp::Watchdog::clearResetFlag(); + } +} + +void SafetyManager::run() +{ + // Enable IWDG here (not in init) - the cyclic scheduler is already running + // at this point, so the 250ms timeout won't fire before the first kick. + bsp::Watchdog::enableWatchdog(bsp::Watchdog::DEFAULT_TIMEOUT); + Logger::debug(SAFETY, "IWDG watchdog enabled (250ms timeout)"); +} + +void SafetyManager::shutdown() {} + +void SafetyManager::cyclic() +{ + auto& supervisor = SafeSupervisor::getInstance(); + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_ENTER); + + _counter++; + if (_counter >= WATCHDOG_CYCLIC_COUNTER) + { + _counter = 0U; + bsp::Watchdog::serviceWatchdog(); + } + + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_LEAVE); +} +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..f0d6bbc1f68 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(threadXCoreConfiguration INTERFACE) + +target_include_directories(threadXCoreConfiguration INTERFACE include) diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h new file mode 100644 index 00000000000..53f6b4692fd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +/* ThreadX user configuration for NUCLEO-G474RE (Cortex-M4F, 170 MHz). */ + +#include "async/Config.h" + +#define ROUND_UP_32(x) (((x) + 31U) & ~31U) +#define TX_MAX_PRIORITIES_LIMIT 1024 +#define TX_MIN_PRIORITIES 32 + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES \ + ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) > TX_MAX_PRIORITIES_LIMIT \ + ? TX_MAX_PRIORITIES_LIMIT \ + : ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) < TX_MIN_PRIORITIES \ + ? TX_MIN_PRIORITIES \ + : ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U))) +#endif // TX_MAX_PRIORITIES + +#if (TX_MAX_PRIORITIES < 32) || (TX_MAX_PRIORITIES > 1024) || ((TX_MAX_PRIORITIES % 32) != 0) +#error "TX_MAX_PRIORITIES must be between 32 and 1024 and evenly divisible by 32" +#endif + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK (1024U) +#endif + +#define TX_ENABLE_STACK_CHECKING + +#define TX_NO_FILEX_POINTER + +/* Timer thread stack - the default 1024 is too small when the timer + * callback dispatches multiple runnables. */ +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE (8192U) +#endif + +#define TX_TIMER_TICKS_PER_SECOND (1000000U / ASYNC_CONFIG_TICK_IN_US) + +#define TX_ENABLE_EXECUTION_CHANGE_NOTIFY diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp b/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp index 9bc0f049da7..106bdde450e 100644 --- a/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp +++ b/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -16,6 +17,38 @@ namespace uds { + +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION +// Application-level programming session. Uses SessionType PROGRAMMING (0x02) +// for the correct response byte, but session index 0x02 instead of +// ProgrammingSession's 0x04 so switchSession()'s equality check against +// PROGRAMMING_SESSION() does not match. +class AppProgrammingSession : public DiagSession +{ +public: + AppProgrammingSession() : DiagSession(PROGRAMMING, 0x02U) {} + + DiagReturnCode::Type isTransitionPossible(SessionType const target) override + { + switch (target) + { + case DEFAULT: + case EXTENDED: + case PROGRAMMING: return DiagReturnCode::OK; + default: return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + } + + DiagSession& getTransitionResult(SessionType target) override; +}; + +static AppProgrammingSession& appProgrammingSession() +{ + static AppProgrammingSession s; + return s; +} +#endif + ApplicationDefaultSession& DiagSession::APPLICATION_DEFAULT_SESSION() { static ApplicationDefaultSession applicationDefaultSession; @@ -62,13 +95,18 @@ ApplicationDefaultSession::isTransitionPossible(DiagSession::SessionType const t { case DiagSession::DEFAULT: case DiagSession::EXTENDED: +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + case DiagSession::PROGRAMMING: +#endif { return DiagReturnCode::OK; } +#ifndef PLATFORM_SUPPORT_PROGRAMMING_SESSION case DiagSession::PROGRAMMING: { return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED_IN_ACTIVE_SESSION; } +#endif default: { return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; @@ -85,6 +123,12 @@ ApplicationDefaultSession::getTransitionResult(DiagSession::SessionType const ta { return DiagSession::APPLICATION_EXTENDED_SESSION(); } +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + case DiagSession::PROGRAMMING: + { + return appProgrammingSession(); + } +#endif default: { return *this; @@ -123,7 +167,11 @@ ApplicationExtendedSession::getTransitionResult(DiagSession::SessionType const t } case DiagSession::PROGRAMMING: { +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + return appProgrammingSession(); +#else return DiagSession::PROGRAMMING_SESSION(); +#endif } default: { @@ -166,4 +214,26 @@ DiagSession& ProgrammingSession::getTransitionResult(DiagSession::SessionType co } } +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION +DiagSession& +AppProgrammingSession::getTransitionResult(DiagSession::SessionType const targetSession) +{ + switch (targetSession) + { + case DiagSession::DEFAULT: + { + return DiagSession::APPLICATION_DEFAULT_SESSION(); + } + case DiagSession::EXTENDED: + { + return DiagSession::APPLICATION_EXTENDED_SESSION(); + } + default: + { + return *this; + } + } +} +#endif + } // namespace uds diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo b/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo new file mode 100644 index 00000000000..089fb228cb7 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo @@ -0,0 +1,15 @@ +f56abc63a845842f780631834d300cda2953f026 + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/FreeRTOS/FreeRTOS-Kernel.git +revision_sha1 : 07055b94bac48f5808f005f22099d9a4337936f3 +target_revision: V10.6.2 +ignores : CMakeLists.txt,LICENSE.md +checksum : 4c1fabc997d04aaca896c03b138e2cb38d71e1d9 +subdir : portable/GCC/ARM_CM4F + +committer_date : 2023-11-29 14:13:55 +0000 + diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt b/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt new file mode 100644 index 00000000000..c932d94c7f5 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(freeRtosCm4SysTickPort INTERFACE) + +target_include_directories(freeRtosCm4SysTickPort INTERFACE .) + +add_library(freeRtosCm4SysTick port.c) + +target_include_directories(freeRtosCm4SysTick PUBLIC .) + +target_link_libraries( + freeRtosCm4SysTick + PUBLIC freeRtos + PRIVATE bspInterrupts) + +target_compile_options(freeRtosCm4SysTick PRIVATE -Wno-unused-but-set-variable) diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md b/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md new file mode 100644 index 00000000000..9cf106272ac --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c b/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c new file mode 100644 index 00000000000..73430b5bfd3 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c @@ -0,0 +1,857 @@ +/* + * FreeRTOS Kernel V10.6.2 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/*----------------------------------------------------------- +* Implementation of functions defined in portable.h for the ARM CM4F port. +*----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __VFP_FP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +/* Constants used to detect a Cortex-M7 r0p1 core, which should use the ARM_CM7 + * r0p1 port. */ +#define portCPUID ( *( ( volatile uint32_t * ) 0xE000ed00 ) ) +#define portCORTEX_M7_r0p1_ID ( 0x410FC271UL ) +#define portCORTEX_M7_r0p0_ID ( 0x410FC270UL ) + +#define portMIN_INTERRUPT_PRIORITY ( 255UL ) +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( *( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0xFFUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* For strict compliance with the Cortex-M spec the task start address should + * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ +#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have + * occurred while the SysTick counter is stopped during tickless idle + * calculations. */ +#define portMISSED_COUNTS_FACTOR ( 94UL ) + +/* Let the user override the default SysTick clock rate. If defined by the + * user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the + * configuration register. */ +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ ) + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + +/* Let the user override the pre-loading of the initial LR with the address of + * prvTaskExitError() in case it messes up unwinding of the stack in the + * debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__( ( naked ) ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__( ( naked ) ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__( ( naked ) ); + +/* + * Function to enable the VFP. + */ +static void vPortEnableVFP( void ) __attribute__( ( naked ) ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* Each task maintains its own interrupt status in the critical nesting + * variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * The number of SysTick increments that make up one tick period. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + * interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + * of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + * own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + volatile uint32_t ulDummy = 0; + + /* A function that implements a task must not exit or attempt to return to + * its caller as there is nothing to return to. If a task wants to exit it + * should instead call vTaskDelete( NULL ). + * + * Artificially force an assert() to be triggered if configASSERT() is + * defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + * started to remove a compiler warning about the function being defined + * but never called. ulDummy is used purely to quieten other warnings + * about code appearing after this function is called - making ulDummy + * volatile makes the compiler think the function could return and + * therefore not output an 'unreachable code' warning for code that appears + * after it. */ + } +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + /* Start the first task. This also clears the bit that indicates the FPU is + * in use in case the FPU was used before the scheduler was started - which + * would otherwise result in the unnecessary leaving of space in the SVC stack + * for lazy saving of FPU registers. */ + __asm volatile ( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + " .ltorg \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* This port can be used on all revisions of the Cortex-M7 core other than + * the r0p1 parts. r0p1 parts should use the port from the + * /source/portable/GCC/ARM_CM7/r0p1 directory. */ + configASSERT( portCPUID != portCORTEX_M7_r0p1_ID ); + configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); + + #if ( configASSERT_DEFINED == 1 ) + { + volatile uint8_t ucOriginalPriority; + volatile uint32_t ulImplementedPrioBits = 0; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ucOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Check that the maximum system call priority is nonzero after + * accounting for the number of priority bits supported by the + * hardware. A priority of 0 is invalid because setting the BASEPRI + * register to 0 unmasks all interrupts, and interrupts with priority 0 + * cannot be masked using BASEPRI. + * See https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( ucMaxSysCallPriority ); + + /* Check that the bits not implemented in hardware are zero in + * configMAX_SYSCALL_INTERRUPT_PRIORITY. */ + configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY & ( ~ucMaxPriorityValue ) ) == 0U ); + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulImplementedPrioBits++; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + if( ulImplementedPrioBits == 8 ) + { + /* When the hardware implements 8 priority bits, there is no way for + * the software to configure PRIGROUP to not have sub-priorities. As + * a result, the least significant bit is always used for sub-priority + * and there are 128 preemption priorities and 2 sub-priorities. + * + * This may cause some confusion in some cases - for example, if + * configMAX_SYSCALL_INTERRUPT_PRIORITY is set to 5, both 5 and 4 + * priority interrupts will be masked in Critical Sections as those + * are at the same preemption priority. This may appear confusing as + * 4 is higher (numerically lower) priority than + * configMAX_SYSCALL_INTERRUPT_PRIORITY and therefore, should not + * have been masked. Instead, if we set configMAX_SYSCALL_INTERRUPT_PRIORITY + * to 4, this confusion does not happen and the behaviour remains the same. + * + * The following assert ensures that the sub-priority bit in the + * configMAX_SYSCALL_INTERRUPT_PRIORITY is clear to avoid the above mentioned + * confusion. */ + configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY & 0x1U ) == 0U ); + ulMaxPRIGROUPValue = 0; + } + else + { + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS - ulImplementedPrioBits; + } + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ucOriginalPriority; + } + #endif /* configASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI; + portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + * here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + * exit error function to prevent compiler warnings about a static function + * not being called in the case that the application writer overrides this + * functionality by defining configTASK_RETURN_ADDRESS. Call + * vTaskSwitchContext() so link time optimisation does not remove the + * symbol. */ + vTaskSwitchContext(); + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + * Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + + /* This is not the interrupt safe version of the enter critical function so + * assert() if it is being called from an interrupt context. Only API + * functions that end in "FromISR" can be used in an interrupt. Only assert if + * the critical nesting count is 1 to protect against recursive calls if the + * assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */ + " it eq \n" + " vstmdbeq r0!, {s16-s31} \n" + " \n" + " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r0, r3} \n" + " mov r0, %0 \n" + " msr basepri, r0 \n" + " dsb \n" + " isb \n" + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r0, r3} \n" + " \n" + " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldr r0, [r1] \n" + " \n" + " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */ + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */ + " it eq \n" + " vldmiaeq r0!, {s16-s31} \n" + " \n" + " msr psp, r0 \n" + " isb \n" + " \n" + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ + #if WORKAROUND_PMU_CM001 == 1 + " push { r14 } \n" + " pop { pc } \n" + #endif + #endif + " \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + * executes all interrupts must be unmasked. There is therefore no need to + * save and then restore the interrupt mask value as its value is already + * known. */ + portDISABLE_INTERRUPTS(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + * the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portENABLE_INTERRUPTS(); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE == 1 ) + + __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + * method as that will mask interrupts that should exit sleep mode. */ + __asm volatile ( "cpsid i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* If a context switch is pending or a task is waiting for the scheduler + * to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Re-enable interrupts - see comments above the cpsid instruction + * above. */ + __asm volatile ( "cpsie i" ::: "memory" ); + } + else + { + /* Stop the SysTick momentarily. The time the SysTick is stopped for + * is accounted for as best it can be, but using the tickless mode will + * inevitably result in some tiny drift of the time maintained by the + * kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* Calculate the reload value required to wait xExpectedIdleTime + * tick periods. -1 is used because this code normally executes part + * way through the first tick period. But if the SysTick IRQ is now + * pending, then clear the IRQ, suppressing the first tick, and correct + * the reload value to reflect that the second tick period is already + * underway. The expected idle time is always at least two ticks. */ + ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + + if( xModifiableIdleTime > 0 ) + { + __asm volatile ( "dsb" ::: "memory" ); + __asm volatile ( "wfi" ); + __asm volatile ( "isb" ); + } + + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Re-enable interrupts to allow the interrupt that brought the MCU + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ + __asm volatile ( "cpsie i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* Disable interrupts again because the clock is about to be stopped + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ + __asm volatile ( "cpsid i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* Disable the SysTick clock without reading the + * portNVIC_SYSTICK_CTRL_REG register to ensure the + * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + * the time the SysTick is stopped for is accounted for as best it can + * be, but using the tickless mode will inevitably result in some tiny + * drift of the time maintained by the kernel with respect to calendar + * time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Determine whether the SysTick has already counted to zero. */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + * underflowed because the post sleep hook did something + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* As the pending tick will be processed as soon as this + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick + * periods (not the ulReload value which accounted for part + * ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, + * then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If + * the SysTick is not using the core clock, temporarily configure it to + * use the core clock. This configuration forces the SysTick to load + * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next + * cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready + * to receive the standard value immediately. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT ) + { + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + #else + { + /* The temporary usage of the core clock has served its purpose, + * as described above. Resume usage of the other clock. */ + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; + + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + /* The partial tick period already ended. Be sure the SysTick + * counts it only once. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ + vTaskStepTick( ulCompleteTickPeriods ); + + /* Exit with interrupts enabled. */ + __asm volatile ( "cpsie i" ::: "memory" ); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if ( configUSE_TICKLESS_IDLE == 1 ) + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +/* This is a naked function. */ +static void vPortEnableVFP( void ) +{ + __asm volatile + ( + " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */ + " ldr r1, [r0] \n" + " \n" + " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ + " str r1, [r0] \n" + " bx r14 \n" + " .ltorg \n" + ); +} +/*-----------------------------------------------------------*/ + +#if ( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile ( "mrs %0, ipsr" : "=r" ( ulCurrentInterrupt )::"memory" ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + * an interrupt that has been assigned a priority above + * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + * function. ISR safe FreeRTOS API functions must *only* be called + * from interrupts that have been assigned a priority at or below + * configMAX_SYSCALL_INTERRUPT_PRIORITY. + * + * Numerically low interrupt priority numbers represent logically high + * interrupt priorities, therefore the priority of the interrupt must + * be set to a value equal to or numerically *higher* than + * configMAX_SYSCALL_INTERRUPT_PRIORITY. + * + * Interrupts that use the FreeRTOS API must not be left at their + * default priority of zero as that is the highest possible priority, + * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + * and therefore also guaranteed to be invalid. + * + * FreeRTOS maintains separate thread and ISR API functions to ensure + * interrupt entry is as fast and simple as possible. + * + * The following links provide detailed information: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html + * https://www.FreeRTOS.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + * that define each interrupt's priority to be split between bits that + * define the interrupt's pre-emption priority bits and bits that define + * the interrupt's sub-priority. For simplicity all bits must be defined + * to be pre-emption priority bits. The following assertion will fail if + * this is not the case (if some bits represent a sub-priority). + * + * If the application only uses CMSIS libraries for interrupt + * configuration then the correct setting can be achieved on all Cortex-M + * devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + * scheduler. Note however that some vendor specific peripheral libraries + * assume a non-zero priority group setting, in which cases using a value + * of zero will result in unpredictable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h b/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h new file mode 100644 index 00000000000..ec9cfc99fcb --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h @@ -0,0 +1,254 @@ +/* + * FreeRTOS Kernel V10.6.2 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + + +#ifndef PORTMACRO_H + #define PORTMACRO_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ + #define portCHAR char + #define portFLOAT float + #define portDOUBLE double + #define portLONG long + #define portSHORT short + #define portSTACK_TYPE uint32_t + #define portBASE_TYPE long + + typedef portSTACK_TYPE StackType_t; + typedef long BaseType_t; + typedef unsigned long UBaseType_t; + + #if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff + #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS ) + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL + +/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do + * not need to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 1 + #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS ) + typedef uint64_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffULL + #else + #error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width. + #endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ + #define portSTACK_GROWTH ( -1 ) + #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) + #define portBYTE_ALIGNMENT 8 + #define portDONT_DISCARD __attribute__( ( used ) ) +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ + #define portYIELD() \ + { \ + /* Set a PendSV to request a context switch. */ \ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ + \ + /* Barriers are normally not required but do ensure the code is completely \ + * within the specified behaviour for the architecture. */ \ + __asm volatile ( "dsb" ::: "memory" ); \ + __asm volatile ( "isb" ); \ + } + + #define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) ) + #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) + #define portEND_SWITCHING_ISR( xSwitchRequired ) do { if( xSwitchRequired != pdFALSE ) portYIELD(); } while( 0 ) + #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ + extern void vPortEnterCritical( void ); + extern void vPortExitCritical( void ); + #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x ) + #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() + #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) + #define portENTER_CRITICAL() vPortEnterCritical() + #define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are + * not necessary for to use this port. They are defined so the common demo files + * (which build with all the ports) will build. */ + #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters ) + #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ + #ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) + #endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ + #ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 + #endif + + #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + +/* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); + + return ucReturn; + } + +/* Check the configuration. */ + #if ( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + +/* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + +/*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + + #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + + #ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() + #endif + +/* portNOP() is not required by this port. */ + #define portNOP() + + #define portINLINE __inline + + #ifndef portFORCE_INLINE + #define portFORCE_INLINE inline __attribute__( ( always_inline ) ) + #endif + + portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void ) + { + uint32_t ulCurrentInterrupt; + BaseType_t xReturn; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile ( "mrs %0, ipsr" : "=r" ( ulCurrentInterrupt )::"memory" ); + + if( ulCurrentInterrupt == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; + } + +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static void vPortRaiseBASEPRI( void ) + { + uint32_t ulNewBASEPRI; + + __asm volatile + ( + " mov %0, %1 \n"\ + " msr basepri, %0 \n"\ + " isb \n"\ + " dsb \n"\ + : "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + } + +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void ) + { + uint32_t ulOriginalBASEPRI, ulNewBASEPRI; + + __asm volatile + ( + " mrs %0, basepri \n"\ + " mov %1, %2 \n"\ + " msr basepri, %1 \n"\ + " isb \n"\ + " dsb \n"\ + : "=r" ( ulOriginalBASEPRI ), "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + + /* This return will not be reached but is necessary to prevent compiler + * warnings. */ + return ulOriginalBASEPRI; + } +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) + { + __asm volatile + ( + " msr basepri, %0 "::"r" ( ulNewMaskValue ) : "memory" + ); + } +/*-----------------------------------------------------------*/ + + #define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" ) + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* PORTMACRO_H */ diff --git a/platforms/stm32/3rdparty/threadx/CMakeLists.txt b/platforms/stm32/3rdparty/threadx/CMakeLists.txt new file mode 100644 index 00000000000..bb368930244 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +project(threadXCortexM4 + LANGUAGES C ASM +) + +add_library(threadXCortexM4) + +add_subdirectory(ports/cortex_m4/gnu) + +add_library(threadXCortexM4Port INTERFACE) + +target_include_directories(threadXCortexM4Port INTERFACE ports/cortex_m4/gnu/inc) + +target_link_libraries(threadXCortexM4 PUBLIC bspInterrupts bspMcu threadX threadXCoreConfiguration) +target_link_libraries(threadXCortexM4Port INTERFACE threadXCoreConfiguration) + +target_compile_definitions(threadXCortexM4Port INTERFACE TX_INCLUDE_USER_DEFINE_FILE) +target_compile_definitions(threadXCortexM4 PRIVATE TX_INCLUDE_USER_DEFINE_FILE) diff --git a/platforms/stm32/3rdparty/threadx/LICENSE.md b/platforms/stm32/3rdparty/threadx/LICENSE.md new file mode 100644 index 00000000000..9b2b9468170 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 - present Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo new file mode 100644 index 00000000000..091ff1a77b0 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo @@ -0,0 +1,15 @@ +e90467f30bb69d7ed878b3a0fe9ee24e761693ec + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/eclipse-threadx/threadx.git +revision_sha1 : c4ad279b85fcf836c0b42d3e140fc32f0554e7b5 +target_revision: v6.4.3.202503_rel +ignores : +checksum : 7060c2b72cae400a4e0c5bf964fac9bcd9c73b78 +subdir : ports/cortex_m4/gnu + +committer_date : 2025-09-28 23:22:13 +0100 + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt new file mode 100644 index 00000000000..71e2d4f2e21 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt @@ -0,0 +1,18 @@ + +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat new file mode 100644 index 00000000000..c8e909cc4b9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat @@ -0,0 +1,228 @@ +del tx.a +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_stack_build.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_schedule.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_system_return.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_context_save.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_context_restore.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_interrupt_control.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_timer_interrupt.S + +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_search.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_set_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_high_level.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_kernel_enter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_kernel_setup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_flush.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_front_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_receive.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_send_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_ceiling_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_put_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_entry_exit_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_identify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_preemption_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_relinquish.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_reset.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_shell_entry.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_sleep.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_analyze.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_error_handler.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_error_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_preempt_check.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_terminate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_time_slice.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_time_slice_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_timeout.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_wait_abort.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_time_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_time_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_expiration_process.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_system_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_system_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_thread_entry.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_buffer_full_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_enable.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_event_filter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_event_unfilter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_disable.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_interrupt_control.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_isr_enter_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_isr_exit_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_object_register.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_object_unregister.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_user_event_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_set_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_flush.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_front_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_receive.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_send_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_ceiling_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_put_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_entry_exit_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_preemption_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_relinquish.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_reset.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_terminate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_time_slice_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_wait_abort.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_info_get.c + +arm-none-eabi-ar -r tx.a tx_thread_stack_build.o tx_thread_schedule.o tx_thread_system_return.o tx_thread_context_save.o tx_thread_context_restore.o tx_timer_interrupt.o tx_thread_interrupt_control.o +arm-none-eabi-ar -r tx.a tx_block_allocate.o tx_block_pool_cleanup.o tx_block_pool_create.o tx_block_pool_delete.o tx_block_pool_info_get.o +arm-none-eabi-ar -r tx.a tx_block_pool_initialize.o tx_block_pool_performance_info_get.o tx_block_pool_performance_system_info_get.o tx_block_pool_prioritize.o +arm-none-eabi-ar -r tx.a tx_block_release.o tx_byte_allocate.o tx_byte_pool_cleanup.o tx_byte_pool_create.o tx_byte_pool_delete.o tx_byte_pool_info_get.o +arm-none-eabi-ar -r tx.a tx_byte_pool_initialize.o tx_byte_pool_performance_info_get.o tx_byte_pool_performance_system_info_get.o tx_byte_pool_prioritize.o +arm-none-eabi-ar -r tx.a tx_byte_pool_search.o tx_byte_release.o tx_event_flags_cleanup.o tx_event_flags_create.o tx_event_flags_delete.o tx_event_flags_get.o +arm-none-eabi-ar -r tx.a tx_event_flags_info_get.o tx_event_flags_initialize.o tx_event_flags_performance_info_get.o tx_event_flags_performance_system_info_get.o +arm-none-eabi-ar -r tx.a tx_event_flags_set.o tx_event_flags_set_notify.o tx_initialize_high_level.o tx_initialize_kernel_enter.o tx_initialize_kernel_setup.o +arm-none-eabi-ar -r tx.a tx_mutex_cleanup.o tx_mutex_create.o tx_mutex_delete.o tx_mutex_get.o tx_mutex_info_get.o tx_mutex_initialize.o tx_mutex_performance_info_get.o +arm-none-eabi-ar -r tx.a tx_mutex_performance_system_info_get.o tx_mutex_prioritize.o tx_mutex_priority_change.o tx_mutex_put.o tx_queue_cleanup.o tx_queue_create.o +arm-none-eabi-ar -r tx.a tx_queue_delete.o tx_queue_flush.o tx_queue_front_send.o tx_queue_info_get.o tx_queue_initialize.o tx_queue_performance_info_get.o +arm-none-eabi-ar -r tx.a tx_queue_performance_system_info_get.o tx_queue_prioritize.o tx_queue_receive.o tx_queue_send.o tx_queue_send_notify.o tx_semaphore_ceiling_put.o +arm-none-eabi-ar -r tx.a tx_semaphore_cleanup.o tx_semaphore_create.o tx_semaphore_delete.o tx_semaphore_get.o tx_semaphore_info_get.o tx_semaphore_initialize.o +arm-none-eabi-ar -r tx.a tx_semaphore_performance_info_get.o tx_semaphore_performance_system_info_get.o tx_semaphore_prioritize.o tx_semaphore_put.o tx_semaphore_put_notify.o +arm-none-eabi-ar -r tx.a tx_thread_create.o tx_thread_delete.o tx_thread_entry_exit_notify.o tx_thread_identify.o tx_thread_info_get.o tx_thread_initialize.o +arm-none-eabi-ar -r tx.a tx_thread_performance_info_get.o tx_thread_performance_system_info_get.o tx_thread_preemption_change.o tx_thread_priority_change.o tx_thread_relinquish.o +arm-none-eabi-ar -r tx.a tx_thread_reset.o tx_thread_resume.o tx_thread_shell_entry.o tx_thread_sleep.o tx_thread_stack_analyze.o tx_thread_stack_error_handler.o +arm-none-eabi-ar -r tx.a tx_thread_stack_error_notify.o tx_thread_suspend.o tx_thread_system_preempt_check.o tx_thread_system_resume.o tx_thread_system_suspend.o +arm-none-eabi-ar -r tx.a tx_thread_terminate.o tx_thread_time_slice.o tx_thread_time_slice_change.o tx_thread_timeout.o tx_thread_wait_abort.o tx_time_get.o +arm-none-eabi-ar -r tx.a tx_time_set.o tx_timer_activate.o tx_timer_change.o tx_timer_create.o tx_timer_deactivate.o tx_timer_delete.o tx_timer_expiration_process.o +arm-none-eabi-ar -r tx.a tx_timer_info_get.o tx_timer_initialize.o tx_timer_performance_info_get.o tx_timer_performance_system_info_get.o tx_timer_system_activate.o +arm-none-eabi-ar -r tx.a tx_timer_system_deactivate.o tx_timer_thread_entry.o tx_trace_enable.o tx_trace_disable.o tx_trace_initialize.o tx_trace_interrupt_control.o +arm-none-eabi-ar -r tx.a tx_trace_isr_enter_insert.o tx_trace_isr_exit_insert.o tx_trace_object_register.o tx_trace_object_unregister.o tx_trace_user_event_insert.o +arm-none-eabi-ar -r tx.a tx_trace_buffer_full_notify.o tx_trace_event_filter.o tx_trace_event_unfilter.o +arm-none-eabi-ar -r tx.a txe_block_allocate.o txe_block_pool_create.o txe_block_pool_delete.o txe_block_pool_info_get.o txe_block_pool_prioritize.o txe_block_release.o +arm-none-eabi-ar -r tx.a txe_byte_allocate.o txe_byte_pool_create.o txe_byte_pool_delete.o txe_byte_pool_info_get.o txe_byte_pool_prioritize.o txe_byte_release.o +arm-none-eabi-ar -r tx.a txe_event_flags_create.o txe_event_flags_delete.o txe_event_flags_get.o txe_event_flags_info_get.o txe_event_flags_set.o +arm-none-eabi-ar -r tx.a txe_event_flags_set_notify.o txe_mutex_create.o txe_mutex_delete.o txe_mutex_get.o txe_mutex_info_get.o txe_mutex_prioritize.o +arm-none-eabi-ar -r tx.a txe_mutex_put.o txe_queue_create.o txe_queue_delete.o txe_queue_flush.o txe_queue_front_send.o txe_queue_info_get.o txe_queue_prioritize.o +arm-none-eabi-ar -r tx.a txe_queue_receive.o txe_queue_send.o txe_queue_send_notify.o txe_semaphore_ceiling_put.o txe_semaphore_create.o txe_semaphore_delete.o +arm-none-eabi-ar -r tx.a txe_semaphore_get.o txe_semaphore_info_get.o txe_semaphore_prioritize.o txe_semaphore_put.o txe_semaphore_put_notify.o txe_thread_create.o +arm-none-eabi-ar -r tx.a txe_thread_delete.o txe_thread_entry_exit_notify.o txe_thread_info_get.o txe_thread_preemption_change.o txe_thread_priority_change.o +arm-none-eabi-ar -r tx.a txe_thread_relinquish.o txe_thread_reset.o txe_thread_resume.o txe_thread_suspend.o txe_thread_terminate.o txe_thread_time_slice_change.o +arm-none-eabi-ar -r tx.a txe_thread_wait_abort.o txe_timer_activate.o txe_timer_change.o txe_timer_create.o txe_timer_deactivate.o txe_timer_delete.o txe_timer_info_get.o diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat new file mode 100644 index 00000000000..2be15702aaf --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat @@ -0,0 +1,5 @@ +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb cortexm4_vectors.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb cortexm4_crt0.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb tx_initialize_low_level.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc sample_threadx.c +arm-none-eabi-gcc -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -T sample_threadx.ld -ereset_handler -nostartfiles -o sample_threadx.out -Wl,-Map=sample_threadx.map cortexm4_vectors.o cortexm4_crt0.o tx_initialize_low_level.o sample_threadx.o tx.a diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S new file mode 100644 index 00000000000..bb530ac5a0c --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S @@ -0,0 +1,119 @@ + .global _start + .extern main + + + .section .init, "ax" + .code 16 + .align 2 + .thumb_func + + +_start: + CPSID i + ldr r1, =__stack_end__ + mov sp, r1 + + /* Copy initialised sections into RAM if required. */ + ldr r0, =__data_load_start__ + ldr r1, =__data_start__ + ldr r2, =__data_end__ + bl crt0_memory_copy + ldr r0, =__text_load_start__ + ldr r1, =__text_start__ + ldr r2, =__text_end__ + bl crt0_memory_copy + ldr r0, =__fast_load_start__ + ldr r1, =__fast_start__ + ldr r2, =__fast_end__ + bl crt0_memory_copy + ldr r0, =__ctors_load_start__ + ldr r1, =__ctors_start__ + ldr r2, =__ctors_end__ + bl crt0_memory_copy + ldr r0, =__dtors_load_start__ + ldr r1, =__dtors_start__ + ldr r2, =__dtors_end__ + bl crt0_memory_copy + ldr r0, =__rodata_load_start__ + ldr r1, =__rodata_start__ + ldr r2, =__rodata_end__ + bl crt0_memory_copy + + + /* Zero bss. */ + ldr r0, =__bss_start__ + ldr r1, =__bss_end__ + mov r2, #0 + bl crt0_memory_set + + /* Setup heap - not recommended for Threadx but here for compatibility reasons */ + ldr r0, = __heap_start__ + ldr r1, = __heap_end__ + sub r1, r1, r0 + mov r2, #0 + str r2, [r0] + add r0, r0, #4 + str r1, [r0] + + /* constructors in case of using C++ */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ +crt0_ctor_loop: + cmp r0, r1 + beq crt0_ctor_end + ldr r2, [r0] + add r0, #4 + push {r0-r1} + blx r2 + pop {r0-r1} + b crt0_ctor_loop +crt0_ctor_end: + + /* Setup call frame for main() */ + mov r0, #0 + mov lr, r0 + mov r12, sp + +start: + /* Jump to main() */ + mov r0, #0 + mov r1, #0 + ldr r2, =main + blx r2 + /* when main returns, loop forever. */ +crt0_exit_loop: + b crt0_exit_loop + + + + /* Startup helper functions. */ + +crt0_memory_copy: + cmp r0, r1 + beq memory_copy_done + sub r2, r2, r1 + beq memory_copy_done +memory_copy_loop: + ldrb r3, [r0] + add r0, r0, #1 + strb r3, [r1] + add r1, r1, #1 + sub r2, r2, #1 + bne memory_copy_loop +memory_copy_done: + bx lr + +crt0_memory_set: + cmp r0, r1 + beq memory_set_done + strb r2, [r0] + add r0, r0, #1 + b crt0_memory_set +memory_set_done: + bx lr + + /* Setup attibutes of stack and heap sections so they don't take up room in the elf file */ + .section .stack, "wa", %nobits + .section .stack_process, "wa", %nobits + .section .heap, "wa", %nobits + \ No newline at end of file diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S new file mode 100644 index 00000000000..6ae558e4dbb --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S @@ -0,0 +1,77 @@ + .global reset_handler + + .global __tx_NMIHandler + .global __tx_BadHandler + .global __tx_SVCallHandler + .global __tx_DBGHandler + .global __tx_PendSVHandler + .global __tx_SysTickHandler + .global __tx_BadHandler + + .syntax unified + .section .vectors, "ax" + .code 16 + .align 0 + .global _vectors + +_vectors: + .word __stack_end__ + .word reset_handler + .word __tx_NMIHandler + .word __tx_HardfaultHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word __tx_SVCallHandler //_SVC_Handler - used by Threadx scheduler // + .word __tx_DBGHandler + .word 0 // Reserved + .word __tx_PendSVHandler + .word __tx_SysTickHandler // Used by Threadx timer functionality + .word __tx_BadHandler // Populate with user Interrupt handler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + + .section .init, "ax" + .thumb_func +reset_handler: + // low level hardware config, such as PLL setup goes here + b _start + + + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c new file mode 100644 index 00000000000..597f373ca09 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld new file mode 100644 index 00000000000..c65a134645a --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld @@ -0,0 +1,125 @@ +MEMORY +{ + RAM (wx) : ORIGIN = 0x20000000, LENGTH = 0x00800000 + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00400000 +} + +__STACKSIZE__ = 1024; +__STACKSIZE_PROCESS__ = 0; +__HEAPSIZE__ = 128; + +SECTIONS +{ + .vectors : + { + KEEP(*(.vectors .vectors.*)) + } > FLASH + + .text : + { + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + __ctors_start__ = ALIGN(4); + *(SORT(.ctors.*)) + *(.ctors) + __ctors_end__ = ALIGN(4); + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + __dtors_start__ = ALIGN(4); + *(SORT(.dtors.*)) + *(.dtors) + __dtors_end__ = ALIGN(4); + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + __data_load_start__ = ALIGN (4); + + .data : AT (__data_load_start__) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + + } > RAM + + .heap (COPY): + { + __heap_start__ = ALIGN(4); + *(.heap) + . = ALIGN(. + __HEAPSIZE__, 4); + __heap_end__ = ALIGN(4); + } > RAM + + .stack ALIGN(4) (NOLOAD) : + { + __stack_start__ = ALIGN(4); + *(.stack) + . = ALIGN(. + __STACKSIZE__, 4); + __stack_end__ = ALIGN(4); + } > RAM + + __RAM_segment_used_end__ = .; +} diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 00000000000..d7e11c06bc2 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,232 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ +@/**************************************************************************/ +@/**************************************************************************/ +@/** */ +@/** ThreadX Component */ +@/** */ +@/** Initialize */ +@/** */ +@/**************************************************************************/ +@/**************************************************************************/ +@ +@ + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global __RAM_segment_used_end__ + .global _tx_timer_interrupt + .global __main + .global __tx_SVCallHandler + .global __tx_PendSVHandler + .global _vectors + .global __tx_NMIHandler @ NMI + .global __tx_BadHandler @ HardFault + .global __tx_SVCallHandler @ SVCall + .global __tx_DBGHandler @ Monitor + .global __tx_PendSVHandler @ PendSV + .global __tx_SysTickHandler @ SysTick + .global __tx_IntHandler @ Int 0 +@ +@ +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + .text 32 + .align 4 + .syntax unified +@/**************************************************************************/ +@/* */ +@/* FUNCTION RELEASE */ +@/* */ +@/* _tx_initialize_low_level Cortex-M4/GNU */ +@/* 6.1 */ +@/* AUTHOR */ +@/* */ +@/* William E. Lamie, Microsoft Corporation */ +@/* */ +@/* DESCRIPTION */ +@/* */ +@/* This function is responsible for any low-level processor */ +@/* initialization, including setting up interrupt vectors, setting */ +@/* up a periodic timer interrupt source, saving the system stack */ +@/* pointer for use in ISR processing later, and finding the first */ +@/* available RAM memory address for tx_application_define. */ +@/* */ +@/* INPUT */ +@/* */ +@/* None */ +@/* */ +@/* OUTPUT */ +@/* */ +@/* None */ +@/* */ +@/* CALLS */ +@/* */ +@/* None */ +@/* */ +@/* CALLED BY */ +@/* */ +@/* _tx_initialize_kernel_enter ThreadX entry function */ +@/* */ +@/* RELEASE HISTORY */ +@/* */ +@/* DATE NAME DESCRIPTION */ +@/* */ +@/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +@/* 09-30-2020 William E. Lamie Modified Comment(s), fixed */ +@/* GNU assembly comment, */ +@/* cleaned up whitespace, */ +@/* resulting in version 6.1 */ +@/* */ +@/**************************************************************************/ +@VOID _tx_initialize_low_level(VOID) +@{ + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: +@ +@ /* Disable interrupts during ThreadX initialization. */ +@ + CPSID i +@ +@ /* Set base of available memory to end of non-initialised RAM area. */ +@ + LDR r0, =_tx_initialize_unused_memory @ Build address of unused memory pointer + LDR r1, =__RAM_segment_used_end__ @ Build first free address + ADD r1, r1, #4 @ + STR r1, [r0] @ Setup first unused memory pointer +@ +@ /* Setup Vector Table Offset Register. */ +@ + MOV r0, #0xE000E000 @ Build address of NVIC registers + LDR r1, =_vectors @ Pickup address of vector table + STR r1, [r0, #0xD08] @ Set vector table address +@ +@ /* Set system stack pointer from vector value. */ +@ + LDR r0, =_tx_thread_system_stack_ptr @ Build address of system stack pointer + LDR r1, =_vectors @ Pickup address of vector table + LDR r1, [r1] @ Pickup reset stack pointer + STR r1, [r0] @ Save system stack pointer +@ +@ /* Enable the cycle count register. */ +@ + LDR r0, =0xE0001000 @ Build address of DWT register + LDR r1, [r0] @ Pickup the current value + ORR r1, r1, #1 @ Set the CYCCNTENA bit + STR r1, [r0] @ Enable the cycle count register +@ +@ /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ +@ + MOV r0, #0xE000E000 @ Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ Setup SysTick Reload Value + MOV r1, #0x7 @ Build SysTick Control Enable Value + STR r1, [r0, #0x10] @ Setup SysTick Control +@ +@ /* Configure handler priorities. */ +@ + LDR r1, =0x00000000 @ Rsrv, UsgF, BusF, MemM + STR r1, [r0, #0xD18] @ Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 @ SVCl, Rsrv, Rsrv, Rsrv + STR r1, [r0, #0xD1C] @ Setup System Handlers 8-11 Priority Registers + @ Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 @ SysT, PnSV, Rsrv, DbgM + STR r1, [r0, #0xD20] @ Setup System Handlers 12-15 Priority Registers + @ Note: PnSV must be lowest priority, which is 0xFF + +@ +@ /* Return to caller. */ +@ + BX lr +@} +@ + +@/* Define shells for each of the unused vectors. */ +@ + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +@ /* added to catch the hardfault */ + + .global __tx_HardfaultHandler + .thumb_func +__tx_HardfaultHandler: + B __tx_HardfaultHandler + + +@ /* added to catch the SVC */ + + .global __tx_SVCallHandler + .thumb_func +__tx_SVCallHandler: + B __tx_SVCallHandler + + +@ /* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +@ VOID InterruptHandler (VOID) +@ { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter @ Call the ISR enter function +#endif + +@ /* Do interrupt handler work here */ +@ /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit @ Call the ISR exit function +#endif + POP {r0, lr} + BX LR +@ } + +@ /* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +@ VOID TimerInterruptHandler (VOID) +@ { +@ + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter @ Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit @ Call the ISR exit function +#endif + POP {r0, lr} + BX LR +@ } + + +@ /* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h new file mode 100644 index 00000000000..3eb8dbffa37 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h @@ -0,0 +1,728 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-M4/GNU */ +/* 6.1.12 */ +/* */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* This file replaces the previous Cortex-M3/M4/M7 files. It unifies */ +/* the ARMv7-M architecture and compilers into one common file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 Scott Larson Modified comments and added */ +/* volatile to registers, */ +/* resulting in version 6.1.11 */ +/* 07-29-2022 Scott Larson Modified comments and */ +/* described BASEPRI usage, */ +/* resulting in version 6.1.12 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm look similar */ +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif +#endif /* __ICCARM__ */ + +#ifdef __ghs__ +#include +#include "tx_ghs.h" +#endif /* __ghs__ */ + + +#if !defined(__GNUC__) && !defined(__CC_ARM) +#define __get_control_value __get_CONTROL +#define __set_control_value __set_CONTROL +#endif + +#ifndef __GNUC__ +#define __get_ipsr_value __get_IPSR +#endif + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + +/* By default, ThreadX for Cortex-M uses the PRIMASK register to enable/disable interrupts. +If using BASEPRI is desired, define the following two symbols for both c and assembly files: +TX_PORT_USE_BASEPRI - This tells ThreadX to use BASEPRI instead of PRIMASK. +TX_PORT_BASEPRI = (priority_mask << (8 - number_priority_bits)) - this defines the maximum priority level to mask. +Any interrupt with a higher priority than priority_mask will not be masked, thus the interrupt will run. +*/ + +/* Define various constants for the ThreadX Cortex-M port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((volatile ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE *((volatile ULONG *) 0xE0001004) +#endif +#else +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif + +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + +#ifdef __ghs__ +/* Define constants for Green Hills EventAnalyzer. */ + +/* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps + represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ + +#define TX_EL_TICKS_PER_SECOND 1000000 + +/* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply + simulate the time-stamp source with a counter. */ + +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower +#endif /* __ghs__ */ + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (0) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#elif defined(__ghs__) +#define TX_THREAD_EXTENSION_2 VOID * tx_thread_eh_globals; \ + int Errno; /* errno. */ \ + char * strtok_saved_pos; /* strtok() position. */ +#else +#define TX_THREAD_EXTENSION_2 +#endif + + +#define TX_THREAD_EXTENSION_3 + + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#if (__VER__ < 8000000) +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = __iar_dlib_perthread_allocate(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION __iar_dlib_perthread_access(0); +#else +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); +#endif +#else +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#endif + +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) + +#ifdef TX_MISRA_ENABLE + +ULONG _tx_misra_control_get(void); +void _tx_misra_control_set(ULONG value); +ULONG _tx_misra_fpccr_get(void); +void _tx_misra_vfp_touch(void); + +#else /* TX_MISRA_ENABLE not defined */ + +/* Define some helper functions (these are intrinsics in some compilers). */ +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline ULONG __get_control_value(void) +{ +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + +__attribute__( ( always_inline ) ) static inline void __set_control_value(ULONG control_value) +{ + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#elif defined(__CC_ARM) /* ARM Compiler 5 */ + +__attribute__( ( always_inline ) ) ULONG __get_control_value(void) +{ +ULONG control_value; + + __asm volatile ("MRS control_value,CONTROL"); + return(control_value); +} + +__attribute__( ( always_inline ) ) void __set_control_value(ULONG control_value) +{ + __asm__ volatile ("MSR CONTROL,control_value"); +} +/* Can't access VFP registers with inline asm, so define this in tx_thread_schedule. */ +void _tx_vfp_access(void); +#define TX_VFP_TOUCH() _tx_vfp_access(); + +#elif defined(__ICCARM__) /* IAR */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); +#endif /* Helper functions for different compilers */ + +#endif /* TX_MISRA_ENABLE */ + + +/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA + in order to ensure no lazy stacking will occur. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } + +#endif + +/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. + If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + the lazy FPU save, then restore the CONTROL.FPCA state. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((volatile ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } \ + } \ + } \ + } +#else + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } +#endif + +#else /* No VFP in use */ + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define the get system state macro. */ + +#ifndef TX_THREAD_GET_SYSTEM_STATE +#ifndef TX_MISRA_ENABLE + +#ifdef __CC_ARM /* ARM Compiler 5 */ + +register unsigned int _ipsr __asm("ipsr"); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _ipsr) + +#elif defined(__GNUC__) /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void) +{ +unsigned int ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_ipsr_value()) + +#elif defined(__ICCARM__) /* IAR */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) + +#endif /* TX_THREAD_GET_SYSTEM_STATE for different compilers */ + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ +ULONG _tx_misra_ipsr_get(VOID); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ + + +/* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value + indicates that _tx_thread_system_return should not be called. This overrides the definition in tx_thread.h + for Cortex-M since so we don't waste time checking the _tx_thread_system_state variable that is always + zero after initialization for Cortex-M ports. */ + +#ifndef TX_THREAD_SYSTEM_RETURN_CHECK +#define TX_THREAD_SYSTEM_RETURN_CHECK(c) (c) = ((ULONG) _tx_thread_preempt_disable); +#endif + +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to + prevent early scheduling on Cortex-M parts. */ + +#define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; + + + + +#ifndef TX_DISABLE_INLINE + +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__CC_ARM) /* AC5 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __clz(__rbit((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#endif + + + +/* Define the interrupt disable/restore macros for each compiler. */ + +#if defined(__GNUC__) || defined(__ICCARM__) + +/*** GCC/AC6 and IAR ***/ + +__attribute__( ( always_inline ) ) static inline unsigned int __get_interrupt_posture(void) +{ +unsigned int posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(unsigned int basepri_value) +{ + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(unsigned int int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); + //__asm__ volatile ("MSR BASEPRI,%0": : "r" (int_posture): "memory"); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif +} + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ +unsigned int int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (__get_ipsr_value() == 0) + { + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif + __restore_interrupt(interrupt_save); + } +} + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/*** End GCC/AC6 and IAR ***/ + +#elif defined(__CC_ARM) + +/*** AC5 ***/ + +static __inline unsigned int __get_interrupt_posture(void) +{ +unsigned int posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS #posture, BASEPRI"); +#else + __asm__ volatile ("MRS #posture, PRIMASK"); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +static __inline void __set_basepri_value(unsigned int basepri_value) +{ + __asm__ volatile ("MSR BASEPRI, #basepri_value"); +} +#endif + +static __inline unsigned int __disable_interrupts(void) +{ +unsigned int int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i"); +#endif + return(int_posture); +} + +static __inline void __restore_interrupt(unsigned int int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK, #int_posture"); +#endif +} + +static void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (_ipsr == 0) + { +#ifdef TX_PORT_USE_BASEPRI + interrupt_save = __get_interrupt_posture(); + __set_basepri_value(0); + __set_basepri_value(interrupt_save); +#else + interrupt_save = __disable_irq(); + __enable_irq(); + if (interrupt_save != 0) + __disable_irq(); +#endif + } +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/*** End AC5 ***/ + +#endif /* Interrupt disable/restore macros for each compiler. */ + +/* Redefine _tx_thread_system_return for improved performance. */ + +#define _tx_thread_system_return _tx_thread_system_return_inline + + +#else /* TX_DISABLE_INLINE is defined */ + +UINT _tx_thread_interrupt_disable(VOID); +VOID _tx_thread_interrupt_restore(UINT previous_posture); + +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif /* TX_DISABLE_INLINE */ + + +/* Define FPU extension for the Cortex-M. Each is assumed to be called in the context of the executing + thread. These are no longer needed, but are preserved for backward compatibility only. */ + +void tx_thread_fpu_enable(void); +void tx_thread_fpu_disable(void); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) 2024 Microsoft Corporation. * ThreadX Cortex-M4/GNU Version 6.4.2 *"; +#else +#ifdef TX_MISRA_ENABLE +extern CHAR _tx_version_id[100]; +#else +extern CHAR _tx_version_id[]; +#endif +#endif + + +#endif diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt new file mode 100644 index 00000000000..d9063d65cc5 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt @@ -0,0 +1,209 @@ + Microsoft's Azure RTOS ThreadX for ARMv7-M + (Cortex-M3, Cortex-M4, Cortex-M7) + Using the GNU Tools + + +1. Building the ThreadX run-time Library + +Navigate to the "example_build" directory. Ensure that +you have setup your path and other environment variables necessary for the ARM +GNU compiler. At this point you may run the build_threadx.bat batch file. +This will build the ThreadX run-time environment in the "example_build" +directory. + +You should observe assembly and compilation of a series of ThreadX source +files. At the end of the batch file, they are all combined into the +run-time library file: tx.a. This file must be linked with your +application in order to use ThreadX. + + +2. Demonstration System + +The ThreadX demonstration is designed to execute on Cortex-M evaluation boards +or on a dedicated simulator. + +Building the demonstration is easy, simply execute the build_threadx_sample.bat +batch file while inside the "example_build" directory. + +You should observe the compilation of sample_threadx.c (which is the demonstration +application) and linking with tx.a. The resulting file sample_threadx.out is a binary +file that can be downloaded and executed on the a simulator, or downloaded to a board. + + +3. System Initialization + +The entry point in ThreadX for the Cortex-M using gnu tools uses the standard GNU +Cortex-M reset sequence. From the reset vector the C runtime will be initialized. + +The ThreadX tx_initialize_low_level.S file is responsible for setting up +various system data structures, the vector area, and a periodic timer interrupt +source. + +In addition, _tx_initialize_low_level determines the first available +address for use by the application, which is supplied as the sole input +parameter to your application definition function, tx_application_define. + + +4. Register Usage and Stack Frames + +The following defines the saved context stack frames for context switches +that occur as a result of interrupt handling or from thread-level API calls. +All suspended threads have the same stack frame in the Cortex-M version of +ThreadX. The top of the suspended thread's stack is pointed to by +tx_thread_stack_ptr in the associated thread control block TX_THREAD. + +Non-FPU Stack Frame: + + Stack Offset Stack Contents + + 0x00 lr Interrupted lr (lr at time of PENDSV) + 0x04 r4 Software stacked GP registers + 0x08 r5 + 0x0C r6 + 0x10 r7 + 0x14 r8 + 0x18 r9 + 0x1C r10 + 0x20 r11 + 0x24 r0 Hardware stacked registers + 0x28 r1 + 0x2C r2 + 0x30 r3 + 0x34 r12 + 0x38 lr + 0x3C pc + 0x40 xPSR + +FPU Stack Frame (only interrupted thread with FPU enabled): + + Stack Offset Stack Contents + + 0x00 lr Interrupted lr (lr at time of PENDSV) + 0x04 s16 Software stacked FPU registers + 0x08 s17 + 0x0C s18 + 0x10 s19 + 0x14 s20 + 0x18 s21 + 0x1C s22 + 0x20 s23 + 0x24 s24 + 0x28 s25 + 0x2C s26 + 0x30 s27 + 0x34 s28 + 0x38 s29 + 0x3C s30 + 0x40 s31 + 0x44 r4 Software stacked registers + 0x48 r5 + 0x4C r6 + 0x50 r7 + 0x54 r8 + 0x58 r9 + 0x5C r10 + 0x60 r11 + 0x64 r0 Hardware stacked registers + 0x68 r1 + 0x6C r2 + 0x70 r3 + 0x74 r12 + 0x78 lr + 0x7C pc + 0x80 xPSR + 0x84 s0 Hardware stacked FPU registers + 0x88 s1 + 0x8C s2 + 0x90 s3 + 0x94 s4 + 0x98 s5 + 0x9C s6 + 0xA0 s7 + 0xA4 s8 + 0xA8 s9 + 0xAC s10 + 0xB0 s11 + 0xB4 s12 + 0xB8 s13 + 0xBC s14 + 0xC0 s15 + 0xC4 fpscr + + +5. Improving Performance + +The distribution version of ThreadX is built without any compiler optimizations. +This makes it easy to debug because you can trace or set breakpoints inside of +ThreadX itself. Of course, this costs some performance. To make it run faster, +you can change the build_threadx.bat file to remove the -g option and enable +all compiler optimizations. + +In addition, you can eliminate the ThreadX basic API error checking by +compiling your application code with the symbol TX_DISABLE_ERROR_CHECKING +defined. + + +6. Interrupt Handling + +ThreadX provides complete and high-performance interrupt handling for Cortex-M +targets. There are a certain set of requirements that are defined in the +following sub-sections: + + +6.1 Vector Area + +The Cortex-M vectors start at the label __tx_vectors or similar. The application may modify +the vector area according to its needs. There is code in tx_initialize_low_level() that will +configure the vector base register. + + +6.2 Managed Interrupts + +A ThreadX managed interrupt is defined below. By following these conventions, the +application ISR is then allowed access to various ThreadX services from the ISR. +Here is the standard template for managed ISRs in ThreadX: + + + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +; VOID InterruptHandler (VOID) +; { + PUSH {r0, lr} + +; /* Do interrupt handler work here */ +; /* BL */ + + POP {r0, lr} + BX lr +; } + + +Note: the Cortex-M requires exception handlers to be thumb labels, this implies bit 0 set. +To accomplish this, the declaration of the label has to be preceded by the assembler directive +.thumb_func to instruct the linker to create thumb labels. The label __tx_IntHandler needs to +be inserted in the correct location in the interrupt vector table. This table is typically +located in either your runtime startup file or in the tx_initialize_low_level.S file. + + +7. FPU Support + +ThreadX for Cortex-M supports automatic ("lazy") VFP support, which means that applications threads +can simply use the VFP and ThreadX automatically maintains the VFP registers as part of the thread +context - no additional setup by the application. + + +8. Revision History + +For generic code revision information, please refer to the readme_threadx_generic.txt +file, which is included in your distribution. The following details the revision +information associated with this specific port of ThreadX: + +06-02-2021 Initial ThreadX version 6.1.7 for Cortex-M using GNU tools. + + +Copyright(c) 1996-2021 Microsoft Corporation + + +https://azure.com/rtos + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S new file mode 100644 index 00000000000..8ac0c629f1a --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S @@ -0,0 +1,722 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX MISRA Compliance */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + #define SHT_PROGBITS 0x1 + + .global __aeabi_memset + .global _tx_thread_current_ptr + .global _tx_thread_interrupt_disable + .global _tx_thread_interrupt_restore + .global _tx_thread_stack_analyze + .global _tx_thread_stack_error_handler + .global _tx_thread_system_state +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_trace_buffer_current_ptr + .global _tx_trace_buffer_end_ptr + .global _tx_trace_buffer_start_ptr + .global _tx_trace_event_enable_bits + .global _tx_trace_full_notify_function + .global _tx_trace_header_ptr +#endif + + .global _tx_misra_always_true + .global _tx_misra_block_pool_to_uchar_pointer_convert + .global _tx_misra_byte_pool_to_uchar_pointer_convert + .global _tx_misra_char_to_uchar_pointer_convert + .global _tx_misra_const_char_to_char_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_entry_to_uchar_pointer_convert +#endif + .global _tx_misra_indirect_void_to_uchar_pointer_convert + .global _tx_misra_memset + .global _tx_misra_message_copy +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_object_to_uchar_pointer_convert +#endif + .global _tx_misra_pointer_to_ulong_convert + .global _tx_misra_status_get + .global _tx_misra_thread_stack_check +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_time_stamp_get +#endif + .global _tx_misra_timer_indirect_to_void_pointer_convert + .global _tx_misra_timer_pointer_add + .global _tx_misra_timer_pointer_dif +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_trace_event_insert +#endif + .global _tx_misra_uchar_pointer_add + .global _tx_misra_uchar_pointer_dif + .global _tx_misra_uchar_pointer_sub + .global _tx_misra_uchar_to_align_type_pointer_convert + .global _tx_misra_uchar_to_block_pool_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_uchar_to_entry_pointer_convert + .global _tx_misra_uchar_to_header_pointer_convert +#endif + .global _tx_misra_uchar_to_indirect_byte_pool_pointer_convert + .global _tx_misra_uchar_to_indirect_uchar_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_uchar_to_object_pointer_convert +#endif + .global _tx_misra_uchar_to_void_pointer_convert + .global _tx_misra_ulong_pointer_add + .global _tx_misra_ulong_pointer_dif + .global _tx_misra_ulong_pointer_sub + .global _tx_misra_ulong_to_pointer_convert + .global _tx_misra_ulong_to_thread_pointer_convert + .global _tx_misra_user_timer_pointer_get + .global _tx_misra_void_to_block_pool_pointer_convert + .global _tx_misra_void_to_byte_pool_pointer_convert + .global _tx_misra_void_to_event_flags_pointer_convert + .global _tx_misra_void_to_indirect_uchar_pointer_convert + .global _tx_misra_void_to_mutex_pointer_convert + .global _tx_misra_void_to_queue_pointer_convert + .global _tx_misra_void_to_semaphore_pointer_convert + .global _tx_misra_void_to_thread_pointer_convert + .global _tx_misra_void_to_uchar_pointer_convert + .global _tx_misra_void_to_ulong_pointer_convert + .global _tx_misra_ipsr_get + .global _tx_misra_control_get + .global _tx_misra_control_set +#ifdef __ARM_FP + .global _tx_misra_fpccr_get + .global _tx_misra_vfp_touch +#endif + + .global _tx_misra_event_flags_group_not_used + .global _tx_misra_event_flags_set_notify_not_used + .global _tx_misra_queue_not_used + .global _tx_misra_queue_send_notify_not_used + .global _tx_misra_semaphore_not_used + .global _tx_misra_semaphore_put_notify_not_used + .global _tx_misra_thread_entry_exit_notify_not_used + .global _tx_misra_thread_not_used + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_memset(VOID *ptr, UINT value, UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified + .thumb_func +_tx_misra_memset: + PUSH {R4,LR} + MOVS R4,R0 + MOVS R0,R2 + MOVS R2,R1 + MOVS R1,R0 + MOVS R0,R4 + BL __aeabi_memset + POP {R4,PC} // return + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_add(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_add: + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_sub(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_sub: + RSBS R1,R1,#+0 + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_uchar_pointer_dif(UCHAR *ptr1, UCHAR *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_dif: + SUBS R0,R0,R1 + BX LR // return + + +/************************************************************************************************************************************/ +/************************************************************************************************************************************/ +/** */ +/** This single function serves all of the below prototypes. */ +/** */ +/** ULONG _tx_misra_pointer_to_ulong_convert(VOID *ptr); */ +/** VOID *_tx_misra_ulong_to_pointer_convert(ULONG input); */ +/** UCHAR **_tx_misra_indirect_void_to_uchar_pointer_convert(VOID **return_ptr); */ +/** UCHAR **_tx_misra_uchar_to_indirect_uchar_pointer_convert(UCHAR *pointer); */ +/** UCHAR *_tx_misra_block_pool_to_uchar_pointer_convert(TX_BLOCK_POOL *pool); */ +/** TX_BLOCK_POOL *_tx_misra_void_to_block_pool_pointer_convert(VOID *pointer); */ +/** UCHAR *_tx_misra_void_to_uchar_pointer_convert(VOID *pointer); */ +/** TX_BLOCK_POOL *_tx_misra_uchar_to_block_pool_pointer_convert(UCHAR *pointer); */ +/** UCHAR **_tx_misra_void_to_indirect_uchar_pointer_convert(VOID *pointer); */ +/** TX_BYTE_POOL *_tx_misra_void_to_byte_pool_pointer_convert(VOID *pointer); */ +/** UCHAR *_tx_misra_byte_pool_to_uchar_pointer_convert(TX_BYTE_POOL *pool); */ +/** ALIGN_TYPE *_tx_misra_uchar_to_align_type_pointer_convert(UCHAR *pointer); */ +/** TX_BYTE_POOL **_tx_misra_uchar_to_indirect_byte_pool_pointer_convert(UCHAR *pointer); */ +/** TX_EVENT_FLAGS_GROUP *_tx_misra_void_to_event_flags_pointer_convert(VOID *pointer); */ +/** ULONG *_tx_misra_void_to_ulong_pointer_convert(VOID *pointer); */ +/** TX_MUTEX *_tx_misra_void_to_mutex_pointer_convert(VOID *pointer); */ +/** TX_QUEUE *_tx_misra_void_to_queue_pointer_convert(VOID *pointer); */ +/** TX_SEMAPHORE *_tx_misra_void_to_semaphore_pointer_convert(VOID *pointer); */ +/** VOID *_tx_misra_uchar_to_void_pointer_convert(UCHAR *pointer); */ +/** TX_THREAD *_tx_misra_ulong_to_thread_pointer_convert(ULONG value); */ +/** VOID *_tx_misra_timer_indirect_to_void_pointer_convert(TX_TIMER_INTERNAL **pointer); */ +/** CHAR *_tx_misra_const_char_to_char_pointer_convert(const char *pointer); */ +/** TX_THREAD *_tx_misra_void_to_thread_pointer_convert(void *pointer); */ +/** UCHAR *_tx_misra_object_to_uchar_pointer_convert(TX_TRACE_OBJECT_ENTRY *pointer); */ +/** TX_TRACE_OBJECT_ENTRY *_tx_misra_uchar_to_object_pointer_convert(UCHAR *pointer); */ +/** TX_TRACE_HEADER *_tx_misra_uchar_to_header_pointer_convert(UCHAR *pointer); */ +/** TX_TRACE_BUFFER_ENTRY *_tx_misra_uchar_to_entry_pointer_convert(UCHAR *pointer); */ +/** UCHAR *_tx_misra_entry_to_uchar_pointer_convert(TX_TRACE_BUFFER_ENTRY *pointer); */ +/** UCHAR *_tx_misra_char_to_uchar_pointer_convert(CHAR *pointer); */ +/** VOID _tx_misra_event_flags_group_not_used(TX_EVENT_FLAGS_GROUP *group_ptr); */ +/** VOID _tx_misra_event_flags_set_notify_not_used(VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *notify_group_ptr)); */ +/** VOID _tx_misra_queue_not_used(TX_QUEUE *queue_ptr); */ +/** VOID _tx_misra_queue_send_notify_not_used(VOID (*queue_send_notify)(TX_QUEUE *notify_queue_ptr)); */ +/** VOID _tx_misra_semaphore_not_used(TX_SEMAPHORE *semaphore_ptr); */ +/** VOID _tx_misra_semaphore_put_notify_not_used(VOID (*semaphore_put_notify)(TX_SEMAPHORE *notify_semaphore_ptr)); */ +/** VOID _tx_misra_thread_not_used(TX_THREAD *thread_ptr); */ +/** VOID _tx_misra_thread_entry_exit_notify_not_used(VOID (*thread_entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT id)); */ +/** */ +/************************************************************************************************************************************/ +/************************************************************************************************************************************/ + .text + .thumb_func +_tx_misra_pointer_to_ulong_convert: +_tx_misra_ulong_to_pointer_convert: +_tx_misra_indirect_void_to_uchar_pointer_convert: +_tx_misra_uchar_to_indirect_uchar_pointer_convert: +_tx_misra_block_pool_to_uchar_pointer_convert: +_tx_misra_void_to_block_pool_pointer_convert: +_tx_misra_void_to_uchar_pointer_convert: +_tx_misra_uchar_to_block_pool_pointer_convert: +_tx_misra_void_to_indirect_uchar_pointer_convert: +_tx_misra_void_to_byte_pool_pointer_convert: +_tx_misra_byte_pool_to_uchar_pointer_convert: +_tx_misra_uchar_to_align_type_pointer_convert: +_tx_misra_uchar_to_indirect_byte_pool_pointer_convert: +_tx_misra_void_to_event_flags_pointer_convert: +_tx_misra_void_to_ulong_pointer_convert: +_tx_misra_void_to_mutex_pointer_convert: +_tx_misra_void_to_queue_pointer_convert: +_tx_misra_void_to_semaphore_pointer_convert: +_tx_misra_uchar_to_void_pointer_convert: +_tx_misra_ulong_to_thread_pointer_convert: +_tx_misra_timer_indirect_to_void_pointer_convert: +_tx_misra_const_char_to_char_pointer_convert: +_tx_misra_void_to_thread_pointer_convert: +#ifdef TX_ENABLE_EVENT_TRACE +_tx_misra_object_to_uchar_pointer_convert: +_tx_misra_uchar_to_object_pointer_convert: +_tx_misra_uchar_to_header_pointer_convert: +_tx_misra_uchar_to_entry_pointer_convert: +_tx_misra_entry_to_uchar_pointer_convert: +#endif +_tx_misra_char_to_uchar_pointer_convert: +_tx_misra_event_flags_group_not_used: +_tx_misra_event_flags_set_notify_not_used: +_tx_misra_queue_not_used: +_tx_misra_queue_send_notify_not_used: +_tx_misra_semaphore_not_used: +_tx_misra_semaphore_put_notify_not_used: +_tx_misra_thread_entry_exit_notify_not_used: +_tx_misra_thread_not_used: + + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_add(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_sub(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_sub: + MVNS R2,#+3 + MULS R1,R2,R1 + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_ulong_pointer_dif(ULONG *ptr1, ULONG *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_message_copy(ULONG **source, ULONG **destination, */ +/** UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_message_copy: + PUSH {R4,R5} + LDR R3,[R0, #+0] + LDR R4,[R1, #+0] + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + CMP R2,#+2 + BCC.N _tx_misra_message_copy_0 + SUBS R2,R2,#+1 + B.N _tx_misra_message_copy_1 +_tx_misra_message_copy_2: + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + SUBS R2,R2,#+1 +_tx_misra_message_copy_1: + CMP R2,#+0 + BNE.N _tx_misra_message_copy_2 +_tx_misra_message_copy_0: + STR R3,[R0, #+0] + STR R4,[R1, #+0] + POP {R4,R5} + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_timer_pointer_dif(TX_TIMER_INTERNAL **ptr1, */ +/** TX_TIMER_INTERNAL **ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_timer_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** TX_TIMER_INTERNAL **_tx_misra_timer_pointer_add(TX_TIMER_INTERNAL */ +/** **ptr1, ULONG size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_timer_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_user_timer_pointer_get(TX_TIMER_INTERNAL */ +/** *internal_timer, TX_TIMER **user_timer); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_user_timer_pointer_get: + SUBS R0,#8 + STR R0,[R1, #+0] + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_thread_stack_check(TX_THREAD *thread_ptr, */ +/** VOID **highest_stack); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_thread_stack_check: + PUSH {R3-R5,LR} + MOVS R4,R0 + MOVS R5,R1 + BL _tx_thread_interrupt_disable + CMP R4,#+0 + BEQ.N _tx_misra_thread_stack_check_0 + LDR R1,[R4, #+0] + LDR R2,=0x54485244 + CMP R1,R2 + BNE.N _tx_misra_thread_stack_check_0 + LDR R1,[R4, #+8] + LDR R2,[R5, #+0] + CMP R1,R2 + BCS.N _tx_misra_thread_stack_check_1 + LDR R1,[R4, #+8] + STR R1,[R5, #+0] +_tx_misra_thread_stack_check_1: + LDR R1,[R4, #+12] + LDR R1,[R1, #+0] + CMP R1,#-269488145 + BNE.N _tx_misra_thread_stack_check_2 + LDR R1,[R4, #+16] + LDR R1,[R1, #+1] + CMP R1,#-269488145 + BNE.N _tx_misra_thread_stack_check_2 + LDR R1,[R5, #+0] + LDR R2,[R4, #+12] + CMP R1,R2 + BCS.N _tx_misra_thread_stack_check_3 +_tx_misra_thread_stack_check_2: + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_error_handler + BL _tx_thread_interrupt_disable +_tx_misra_thread_stack_check_3: + LDR R1,[R5, #+0] + LDR R1,[R1, #-4] + CMP R1,#-269488145 + BEQ.N _tx_misra_thread_stack_check_0 + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_analyze + BL _tx_thread_interrupt_disable +_tx_misra_thread_stack_check_0: + BL _tx_thread_interrupt_restore + POP {R0,R4,R5,PC} // return + +#ifdef TX_ENABLE_EVENT_TRACE + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_trace_event_insert(ULONG event_id, */ +/** VOID *info_field_1, ULONG info_field_2, ULONG info_field_3, */ +/** ULONG info_field_4, ULONG filter, ULONG time_stamp); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_trace_event_insert: + PUSH {R3-R7,LR} + LDR.N R4,DataTable2_1 + LDR R4,[R4, #+0] + CMP R4,#+0 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R5,DataTable2_2 + LDR R5,[R5, #+0] + LDR R6,[SP, #+28] + TST R5,R6 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R5,DataTable2_3 + LDR R5,[R5, #+0] + LDR.N R6,DataTable2_4 + LDR R6,[R6, #+0] + CMP R5,#+0 + BNE.N _tx_misra_trace_event_insert_1 + LDR R5,[R6, #+44] + LDR R7,[R6, #+60] + LSLS R7,R7,#+16 + ORRS R7,R7,#0x80000000 + ORRS R5,R7,R5 + B.N _tx_misra_trace_event_insert_2 +_tx_misra_trace_event_insert_1: + CMP R5,#-252645136 + BCS.N _tx_misra_trace_event_insert_3 + MOVS R5,R6 + MOVS R6,#-1 + B.N _tx_misra_trace_event_insert_2 +_tx_misra_trace_event_insert_3: + MOVS R6,#-252645136 + MOVS R5,#+0 +_tx_misra_trace_event_insert_2: + STR R6,[R4, #+0] + STR R5,[R4, #+4] + STR R0,[R4, #+8] + LDR R0,[SP, #+32] + STR R0,[R4, #+12] + STR R1,[R4, #+16] + STR R2,[R4, #+20] + STR R3,[R4, #+24] + LDR R0,[SP, #+24] + STR R0,[R4, #+28] + ADDS R4,R4,#+32 + LDR.N R0,DataTable2_5 + LDR R0,[R0, #+0] + CMP R4,R0 + BCC.N _tx_misra_trace_event_insert_4 + LDR.N R0,DataTable2_6 + LDR R4,[R0, #+0] + LDR.N R0,DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] + LDR.N R0,DataTable2_8 + LDR R0,[R0, #+0] + CMP R0,#+0 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + LDR.N R1,DataTable2_8 + LDR R1,[R1, #+0] + BLX R1 + B.N _tx_misra_trace_event_insert_0 +_tx_misra_trace_event_insert_4: + LDR.N R0,DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] +_tx_misra_trace_event_insert_0: + POP {R0,R4-R7,PC} // return + + + .data +DataTable2_1: + .word _tx_trace_buffer_current_ptr + + .data +DataTable2_2: + .word _tx_trace_event_enable_bits + + .data +DataTable2_5: + .word _tx_trace_buffer_end_ptr + + .data +DataTable2_6: + .word _tx_trace_buffer_start_ptr + + .data +DataTable2_7: + .word _tx_trace_header_ptr + + .data +DataTable2_8: + .word _tx_trace_full_notify_function + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_time_stamp_get(VOID); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_time_stamp_get: + MOVS R0,#+0 + BX LR // return + +#endif + + .data +DataTable2_3: + .word _tx_thread_system_state + + .data +DataTable2_4: + .word _tx_thread_current_ptr + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_always_true(void); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_always_true: + MOVS R0,#+1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_status_get(UINT status); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_status_get: + MOVS R0,#+0 + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_ipsr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_ipsr_get: + MRS R0, IPSR + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_control_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_control_get: + MRS R0, CONTROL + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_control_set(ULONG value); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_control_set: + MSR CONTROL, R0 + BX LR // return + + +#ifdef __ARM_FP + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_fpccr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_fpccr_get: + LDR r0, =0xE000EF34 // Build FPCCR address + LDR r0, [r0] // Load FPCCR value + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_vfp_touch(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_vfp_touch: + vmov.f32 s0, s0 + BX LR // return + +#endif + + + .data + .word 0 diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000000..af374956516 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_exit +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_exit] Execution profiling ISR exit */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .thumb_func +_tx_thread_context_restore: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR exit function to indicate an ISR is complete. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_exit // Call the ISR exit function + POP {r0, lr} // Recover return address +#endif + + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000000..0728d86e910 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_enter] Execution profiling ISR enter */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .thumb_func +_tx_thread_context_save: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR enter function to indicate an ISR is starting. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_enter // Call the ISR enter function + POP {r0, lr} // Recover return address +#endif + + /* Context is already saved - just return. */ + + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000000..38790a855dc --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .thumb_func +_tx_thread_interrupt_control: +#ifdef TX_PORT_USE_BASEPRI + MRS r1, BASEPRI // Pickup current interrupt posture + MSR BASEPRI, r0 // Apply the new interrupt posture + MOV r0, r1 // Transfer old to return register +#else + MRS r1, PRIMASK // Pickup current interrupt lockout + MSR PRIMASK, r0 // Apply the new interrupt lockout + MOV r0, r1 // Transfer old to return register +#endif + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000000..e0ae359abd8 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts and returning */ +/* the previous interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(VOID) +// { + .global _tx_thread_interrupt_disable + .thumb_func +_tx_thread_interrupt_disable: + /* Return current interrupt lockout posture. */ +#ifdef TX_PORT_USE_BASEPRI + MRS r0, BASEPRI + LDR r1, =TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + MRS r0, PRIMASK + CPSID i +#endif + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000000..32839c40511 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring the previous */ +/* interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* previous_posture Previous interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_interrupt_restore(UINT previous_posture) +// { + .global _tx_thread_interrupt_restore + .thumb_func +_tx_thread_interrupt_restore: + /* Restore previous interrupt lockout posture. */ +#ifdef TX_PORT_USE_BASEPRI + MSR BASEPRI, r0 +#else + MSR PRIMASK, r0 +#endif + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000000..8b283009cb9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S @@ -0,0 +1,330 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .global _tx_thread_current_ptr + .global _tx_thread_execute_ptr + .global _tx_timer_time_slice + .global _tx_execution_thread_enter + .global _tx_execution_thread_exit +#ifdef TX_LOW_POWER + .global tx_low_power_enter + .global tx_low_power_exit +#endif + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 Scott Larson Added BASEPRI support, */ +/* resulting in version 6.1.11 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .thumb_func +_tx_thread_schedule: + + /* This function should only ever be called on Cortex-M + from the first schedule request. Subsequent scheduling occurs + from the PendSV handling routine below. */ + + /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ + + MOV r0, #0 // Build value for TX_FALSE + LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag + STR r0, [r2, #0] // Clear preempt disable flag + + /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ + +#ifdef __ARM_FP + MRS r0, CONTROL // Pickup current CONTROL register + BIC r0, r0, #4 // Clear the FPCA bit + MSR CONTROL, r0 // Setup new CONTROL register +#endif + + /* Enable interrupts */ + CPSIE i + + /* Enter the scheduler for the first time. */ + + MOV r0, #0x10000000 // Load PENDSVSET bit + MOV r1, #0xE000E000 // Load NVIC base + STR r0, [r1, #0xD04] // Set PENDSVBIT in ICSR + DSB // Complete all memory accesses + ISB // Flush pipeline + + /* Wait here for the PendSV to take place. */ + +__tx_wait_here: + B __tx_wait_here // Wait for the PendSV to happen +// } + + /* Generic context switching PendSV handler. */ + + .global PendSV_Handler + .global __tx_PendSVHandler + .syntax unified + .thumb_func +PendSV_Handler: + .thumb_func +__tx_PendSVHandler: + + /* Get current thread value and new thread pointer. */ + +__tx_ts_handler: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread exit function to indicate the thread is no longer executing. */ +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif /* TX_PORT_USE_BASEPRI */ + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, lr} // Recover LR +#ifdef TX_PORT_USE_BASEPRI + MOV r0, 0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r0 +#else + CPSIE i // Enable interrupts +#endif /* TX_PORT_USE_BASEPRI */ +#endif /* EXECUTION PROFILE */ + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + MOV r3, #0 // Build NULL value + LDR r1, [r0] // Pickup current thread pointer + + /* Determine if there is a current thread to finish preserving. */ + + CBZ r1, __tx_ts_new // If NULL, skip preservation + + /* Recover PSP and preserve current thread context. */ + + STR r3, [r0] // Set _tx_thread_current_ptr to NULL + MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) + STMDB r12!, {r4-r11} // Save its remaining registers +#ifdef __ARM_FP + TST LR, #0x10 // Determine if the VFP extended frame is present + BNE _skip_vfp_save + VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers +_skip_vfp_save: +#endif + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + STMDB r12!, {LR} // Save LR on the stack + + /* Determine if time-slice is active. If it isn't, skip time handling processing. */ + + LDR r5, [r4] // Pickup current time-slice + STR r12, [r1, #8] // Save the thread stack pointer + CBZ r5, __tx_ts_new // If not active, skip processing + + /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ + + STR r5, [r1, #24] // Save current time-slice + + /* Clear the global time-slice. */ + + STR r3, [r4] // Clear time-slice + + /* Executing thread is now completely preserved!!! */ + +__tx_ts_new: + + /* Now we are looking for a new thread to execute! */ + +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif + LDR r1, [r2] // Is there another thread ready to execute? + CBZ r1, __tx_ts_wait // No, skip to the wait processing + + /* Yes, another thread is ready for else, make the current thread the new thread. */ + + STR r1, [r0] // Setup the current thread pointer to the new thread +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + + /* Increment the thread run count. */ + +__tx_ts_restore: + LDR r7, [r1, #4] // Pickup the current thread run count + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r1, #24] // Pickup thread's current time-slice + ADD r7, r7, #1 // Increment the thread run count + STR r7, [r1, #4] // Store the new run count + + /* Setup global time-slice with thread's current time-slice. */ + + STR r5, [r4] // Setup global time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread entry function to indicate the thread is executing. */ + PUSH {r0, r1} // Save r0 and r1 + BL _tx_execution_thread_enter // Call the thread execution enter function + POP {r0, r1} // Recover r0 and r1 +#endif + + /* Restore the thread context and PSP. */ + + LDR r12, [r1, #8] // Pickup thread's stack pointer + LDMIA r12!, {LR} // Pickup LR +#ifdef __ARM_FP + TST LR, #0x10 // Determine if the VFP extended frame is present + BNE _skip_vfp_restore // If not, skip VFP restore + VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers +_skip_vfp_restore: +#endif + LDMIA r12!, {r4-r11} // Recover thread's registers + MSR PSP, r12 // Setup the thread's stack pointer + + /* Return to thread. */ + + BX lr // Return to thread! + + /* The following is the idle wait processing... in this case, no threads are ready for execution and the + system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts + are disabled to allow use of WFI for waiting for a thread to arrive. */ + +__tx_ts_wait: +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif + LDR r1, [r2] // Pickup the next thread to execute pointer + STR r1, [r0] // Store it in the current pointer + CBNZ r1, __tx_ts_ready // If non-NULL, a new thread is ready! + +#ifdef TX_LOW_POWER + PUSH {r0-r3} + BL tx_low_power_enter // Possibly enter low power mode + POP {r0-r3} +#endif + +#ifdef TX_ENABLE_WFI + DSB // Ensure no outstanding memory transactions + WFI // Wait for interrupt + ISB // Ensure pipeline is flushed +#endif + +#ifdef TX_LOW_POWER + PUSH {r0-r3} + BL tx_low_power_exit // Exit low power mode + POP {r0-r3} +#endif + +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + B __tx_ts_wait // Loop to continue waiting + + /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are + already in the handler! */ + +__tx_ts_ready: + MOV r7, #0x08000000 // Build clear PendSV value + MOV r8, #0xE000E000 // Build base NVIC address + STR r7, [r8, #0xD04] // Clear any PendSV + + /* Re-enable interrupts and restore new thread. */ +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + B __tx_ts_restore // Restore the thread +// } + +#ifdef __ARM_FP + + .global tx_thread_fpu_enable + .thumb_func +tx_thread_fpu_enable: + .global tx_thread_fpu_disable + .thumb_func +tx_thread_fpu_disable: + + /* Automatic VPF logic is supported, this function is present only for + backward compatibility purposes and therefore simply returns. */ + + BX LR // Return to caller + +#endif diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000000..c62ccf30584 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .thumb_func +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + BIC r2, r2, #0x7 // Align frame for 8-byte alignment + SUB r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOV r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r4 + STR r3, [r2, #8] // Store initial r5 + STR r3, [r2, #12] // Store initial r6 + STR r3, [r2, #16] // Store initial r7 + STR r3, [r2, #20] // Store initial r8 + STR r3, [r2, #24] // Store initial r9 + STR r3, [r2, #28] // Store initial r10 + STR r3, [r2, #32] // Store initial r11 + + /* Hardware stack follows. */ + + STR r3, [r2, #36] // Store initial r0 + STR r3, [r2, #40] // Store initial r1 + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + MOV r3, #0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + MOV r3, #0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's + // control block + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000000..234a2121fb9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .thumb_func + .global _tx_thread_system_return +_tx_thread_system_return: + + /* Return to real scheduler via PendSV. Note that this routine is often + replaced with in-line assembly in tx_port.h to improved performance. */ + + MOV r0, #0x10000000 // Load PENDSVSET bit + MOV r1, #0xE000E000 // Load NVIC base + STR r0, [r1, #0xD04] // Set PENDSVBIT in ICSR + MRS r0, IPSR // Pickup IPSR + CMP r0, #0 // Is it a thread returning? + BNE _isr_context // If ISR, skip interrupt enable +#ifdef TX_PORT_USE_BASEPRI + MRS r1, BASEPRI // Thread context returning, pickup BASEPRI + MOV r0, #0 + MSR BASEPRI, r0 // Enable interrupts + MSR BASEPRI, r1 // Restore original interrupt posture +#else + MRS r1, PRIMASK // Thread context returning, pickup PRIMASK + CPSIE i // Enable interrupts + MSR PRIMASK, r1 // Restore original interrupt posture +#endif +_isr_context: + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000000..4d0c8003e11 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .global _tx_timer_time_slice + .global _tx_timer_system_clock + .global _tx_timer_current_ptr + .global _tx_timer_list_start + .global _tx_timer_list_end + .global _tx_timer_expired_time_slice + .global _tx_timer_expired + .global _tx_thread_time_slice + .global _tx_timer_expiration_process + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* expiration functions are called. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { +#ifndef TX_NO_TIMER + .global _tx_timer_interrupt + .thumb_func +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR r1, =_tx_timer_system_clock // Pickup address of system clock + LDR r0, [r1, #0] // Pickup system clock + ADD r0, r0, #1 // Increment system clock + STR r0, [r1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + // if (_tx_timer_time_slice) + // { + + LDR r3, =_tx_timer_time_slice // Pickup address of time-slice + LDR r2, [r3, #0] // Pickup time-slice + CBZ r2, __tx_timer_no_time_slice // Is it non-active? + // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + // _tx_timer_time_slice--; + + SUB r2, r2, #1 // Decrement the time-slice + STR r2, [r3, #0] // Store new time-slice value + + /* Check for expiration. */ + // if (__tx_timer_time_slice == 0) + + CBNZ r2, __tx_timer_no_time_slice // Has it expired? + // No, skip expiration processing + + /* Set the time-slice expired flag. */ + // _tx_timer_expired_time_slice = TX_TRUE; + + LDR r3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV r0, #1 // Build expired value + STR r0, [r3, #0] // Set time-slice expiration flag + + // } + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR r1, =_tx_timer_current_ptr // Pickup current timer pointer address + LDR r0, [r1, #0] // Pickup current timer + LDR r2, [r0, #0] // Pickup timer list entry + CBZ r2, __tx_timer_no_timer // Is there anything in the list? + // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR r3, =_tx_timer_expired // Pickup expiration flag address + MOV r2, #1 // Build expired value + STR r2, [r3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD r0, r0, #4 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR r3, =_tx_timer_list_end // Pickup addr of timer list end + LDR r2, [r3, #0] // Pickup list end + CMP r0, r2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR r3, =_tx_timer_list_start // Pickup addr of timer list start + LDR r0, [r3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR r0, [r1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR r2, [r3, #0] // Pickup time-slice expired flag + CBNZ r2, __tx_something_expired // Did a time-slice expire? + // If non-zero, time-slice expired + LDR r1, =_tx_timer_expired // Pickup addr of other expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CBZ r0, __tx_timer_nothing_expired // Did a timer expire? + // No, nothing expired + +__tx_something_expired: + + STMDB sp!, {r0, lr} // Save the lr register on the stack + // and save r0 just to keep 8-byte alignment + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR r1, =_tx_timer_expired // Pickup addr of expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CBZ r0, __tx_timer_dont_activate // Check for timer expiration + // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR r2, [r3, #0] // Pickup the actual flag + CBZ r2, __tx_timer_not_ts_expiration // See if the flag is set + // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + LDR r0, =_tx_thread_preempt_disable // Build address of preempt disable flag + LDR r1, [r0] // Is the preempt disable flag set? + CBNZ r1, __tx_timer_skip_time_slice // Yes, skip the PendSV logic + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + LDR r3, [r2] // Pickup the execute thread pointer + LDR r0, =0xE000ED04 // Build address of control register + LDR r2, =0x10000000 // Build value for PendSV bit + CMP r1, r3 // Are they the same? + BEQ __tx_timer_skip_time_slice // If the same, there was no time-slice performed + STR r2, [r0] // Not the same, issue the PendSV for preemption +__tx_timer_skip_time_slice: + + // } + +__tx_timer_not_ts_expiration: + + LDMIA sp!, {r0, lr} // Recover lr register (r0 is just there for + // the 8-byte stack alignment + + // } + +__tx_timer_nothing_expired: + + DSB // Complete all memory access + BX lr // Return to caller +// } +#endif diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt new file mode 100644 index 00000000000..b598ec87b14 --- /dev/null +++ b/platforms/stm32/CMakeLists.txt @@ -0,0 +1,39 @@ +if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") + if (NOT DEFINED STM32_CHIP) + message( + FATAL_ERROR + "STM32_CHIP must be defined (e.g., STM32F413ZH or STM32G474RE)") + endif () + + if (STM32_CHIP STREQUAL "STM32F413ZH") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/stm32f413zh.cmake) + elseif (STM32_CHIP STREQUAL "STM32G474RE") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/stm32g474re.cmake) + else () + message( + FATAL_ERROR + "Unsupported STM32_CHIP: ${STM32_CHIP}. Supported: STM32F413ZH, STM32G474RE" + ) + endif () + + # 3rdparty modules - RTOS + if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(3rdparty/freertos_cm4_sysTick) + elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(3rdparty/threadx) + endif () + + add_subdirectory(bsp) + + # ETL implementation + add_subdirectory(etlImpl) + + # Safety modules + add_subdirectory(safety) + + # Other modules + add_subdirectory(hardFaultHandler) +else () + # Unit-test builds compile the transceivers against mock devices. + add_subdirectory(bsp) +endif () diff --git a/platforms/stm32/bsp/CMakeLists.txt b/platforms/stm32/bsp/CMakeLists.txt new file mode 100644 index 00000000000..47cf2745511 --- /dev/null +++ b/platforms/stm32/bsp/CMakeLists.txt @@ -0,0 +1,35 @@ +if (BUILD_EXECUTABLE STREQUAL "unitTest") + # Unit test builds: only compile transceivers (with mock devices). Skip + # hardware-dependent modules (bspMcu, bspClock, bspCan, etc.). + add_subdirectory(bxCanTransceiver) + add_subdirectory(fdCanTransceiver) + add_subdirectory(bspUart) +else () + add_subdirectory(bspMcu) + add_subdirectory(bspClock) + add_subdirectory(bspInterruptsImpl) + add_subdirectory(bspUart) + add_subdirectory(bspTimer) + add_subdirectory(bspIo) + add_subdirectory(bspAdc) + add_subdirectory(bspEepromDriver) + add_subdirectory(bspCan) + + # CAN transceiver - selected by chip family + if (CAN_TYPE STREQUAL "BXCAN") + add_subdirectory(bxCanTransceiver) + elseif (CAN_TYPE STREQUAL "FDCAN") + add_subdirectory(fdCanTransceiver) + endif () + + # Aggregated BSP target + add_library(socBsp INTERFACE) + target_link_libraries( + socBsp + INTERFACE bspClock + bspInterruptsImpl + bspIo + bspMcu + bspTimer + bspUart) +endif () diff --git a/platforms/stm32/bsp/bspAdc/CMakeLists.txt b/platforms/stm32/bsp/bspAdc/CMakeLists.txt new file mode 100644 index 00000000000..31856b6f58d --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspAdc src/adc/Adc.cpp) +target_include_directories(bspAdc PUBLIC include) +target_link_libraries( + bspAdc + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspAdc/include/adc/Adc.h b/platforms/stm32/bsp/bspAdc/include/adc/Adc.h new file mode 100644 index 00000000000..343d51920b7 --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/include/adc/Adc.h @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace bios +{ + +enum class AdcResolution : uint8_t +{ + BITS_12 = 0U, + BITS_10 = 1U, + BITS_8 = 2U, + BITS_6 = 3U +}; + +struct AdcConfig +{ + ADC_TypeDef* peripheral; + AdcResolution resolution; + uint8_t samplingTime; // SMPR code (0-7) +}; + +class Adc +{ +public: + explicit Adc(AdcConfig const& config); + + void init(); + uint16_t readChannel(uint8_t channel); + uint16_t readTemperature(); + uint16_t readVrefint(); + +private: + AdcConfig const fConfig; + bool fInitialized; + + void enableClock(); + void calibrate(); + void configureChannel(uint8_t channel); + uint16_t startAndRead(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspAdc/module.spec b/platforms/stm32/bsp/bspAdc/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp b/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp new file mode 100644 index 00000000000..a8a65ae661f --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp @@ -0,0 +1,179 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +Adc::Adc(AdcConfig const& config) : fConfig(config), fInitialized(false) {} + +void Adc::enableClock() +{ +#if defined(STM32G474xx) + RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN; + uint32_t volatile dummy = RCC->AHB2ENR; + (void)dummy; + + // Select system clock as ADC clock (ADCSEL = 01 in CCIPR) + RCC->CCIPR = (RCC->CCIPR & ~(3U << 28U)) | (1U << 28U); +#elif defined(STM32F413xx) + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + uint32_t volatile dummy = RCC->APB2ENR; + (void)dummy; +#endif +} + +void Adc::calibrate() +{ +#if defined(STM32G474xx) + ADC_TypeDef* adc = fConfig.peripheral; + + adc->CR &= ~ADC_CR_ADEN; + adc->CR &= ~ADC_CR_DEEPPWD; + adc->CR |= ADC_CR_ADVREGEN; + + // Wait for regulator startup (~20us) + for (uint32_t volatile i = 0U; i < 10000U; i++) {} + + adc->CR &= ~ADC_CR_ADCALDIF; + adc->CR |= ADC_CR_ADCAL; +#if !defined(UNIT_TEST) + while ((adc->CR & ADC_CR_ADCAL) != 0U) {} +#else + adc->CR &= ~ADC_CR_ADCAL; // Simulate calibration complete +#endif +#elif defined(STM32F413xx) + // STM32F4 ADC has no hardware calibration sequence + fConfig.peripheral->CR2 &= ~ADC_CR2_ADON; +#endif +} + +void Adc::init() +{ + enableClock(); + calibrate(); + + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->CFGR = (adc->CFGR & ~ADC_CFGR_RES) | (static_cast(fConfig.resolution) << 3U); + adc->CFGR &= ~(ADC_CFGR_CONT | ADC_CFGR_EXTEN); + adc->CFGR &= ~ADC_CFGR_ALIGN; + + adc->ISR |= ADC_ISR_ADRDY; // Write-1-to-clear ready flag + adc->CR |= ADC_CR_ADEN; + while ((adc->ISR & ADC_ISR_ADRDY) == 0U) {} +#elif defined(STM32F413xx) + adc->CR1 = (adc->CR1 & ~ADC_CR1_RES) | (static_cast(fConfig.resolution) << 24U); + adc->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_ALIGN); + + ADC_Common_TypeDef* common = ADC123_COMMON; + common->CCR = (common->CCR & ~ADC_CCR_ADCPRE) | (1U << 16U); // PCLK2/4 + common->CCR |= ADC_CCR_TSVREFE; + + adc->CR2 |= ADC_CR2_ADON; +#endif + + fInitialized = true; +} + +void Adc::configureChannel(uint8_t channel) +{ + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->SQR1 = (adc->SQR1 & ~(ADC_SQR1_L | ADC_SQR1_SQ1)) + | (static_cast(channel) << 6U); // L=0 (1 conv), SQ1=channel + + if (channel < 10U) + { + uint32_t pos = channel * 3U; + adc->SMPR1 + = (adc->SMPR1 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } + else + { + uint32_t pos = (channel - 10U) * 3U; + adc->SMPR2 + = (adc->SMPR2 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } +#elif defined(STM32F413xx) + adc->SQR1 &= ~ADC_SQR1_L; // L=0 (1 conv) + adc->SQR3 = (adc->SQR3 & ~0x1FU) | (channel & 0x1FU); // SQ1=channel + + // F4: SMPR2 covers channels 0-9, SMPR1 covers 10-18 (reversed vs G4) + if (channel < 10U) + { + uint32_t pos = channel * 3U; + adc->SMPR2 + = (adc->SMPR2 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } + else + { + uint32_t pos = (channel - 10U) * 3U; + adc->SMPR1 + = (adc->SMPR1 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } +#endif +} + +uint16_t Adc::startAndRead() +{ + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->ISR |= ADC_ISR_EOC; // Write-1-to-clear EOC + adc->CR |= ADC_CR_ADSTART; + while ((adc->ISR & ADC_ISR_EOC) == 0U) {} + return static_cast(adc->DR); +#elif defined(STM32F413xx) + adc->SR &= ~ADC_SR_EOC; + adc->CR2 |= ADC_CR2_SWSTART; + while ((adc->SR & ADC_SR_EOC) == 0U) {} + return static_cast(adc->DR); +#else + return 0U; +#endif +} + +uint16_t Adc::readChannel(uint8_t channel) +{ + if (!fInitialized) + { + return 0U; + } + configureChannel(channel); + return startAndRead(); +} + +uint16_t Adc::readTemperature() +{ +#if defined(STM32G474xx) + return readChannel(16U); // VSENSE on ADC1 ch16 +#elif defined(STM32F413xx) + return readChannel(18U); // VSENSE on ADC1 ch18 +#else + return 0U; +#endif +} + +uint16_t Adc::readVrefint() +{ +#if defined(STM32G474xx) + return readChannel(18U); // VREFINT on ADC1 ch18 +#elif defined(STM32F413xx) + return readChannel(17U); // VREFINT on ADC1 ch17 +#else + return 0U; +#endif +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/CMakeLists.txt b/platforms/stm32/bsp/bspCan/CMakeLists.txt new file mode 100644 index 00000000000..17b17e1549b --- /dev/null +++ b/platforms/stm32/bsp/bspCan/CMakeLists.txt @@ -0,0 +1,16 @@ +# Low-level CAN device drivers - chip-specific source selected by CAN_TYPE +if (CAN_TYPE STREQUAL "BXCAN") + add_library(bspCan src/can/BxCanDevice.cpp) + target_include_directories(bspCan PUBLIC include) + target_link_libraries( + bspCan + PUBLIC bspMcu cpp2can etl + PRIVATE platform) +elseif (CAN_TYPE STREQUAL "FDCAN") + add_library(bspCan src/can/FdCanDevice.cpp) + target_include_directories(bspCan PUBLIC include) + target_link_libraries( + bspCan + PUBLIC bspMcu cpp2can etl + PRIVATE platform) +endif () diff --git a/platforms/stm32/bsp/bspCan/doc/index.rst b/platforms/stm32/bsp/bspCan/doc/index.rst new file mode 100644 index 00000000000..8a03bcac5cb --- /dev/null +++ b/platforms/stm32/bsp/bspCan/doc/index.rst @@ -0,0 +1,70 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspCan +====== + +Overview +-------- + +The ``bspCan`` module provides low-level register drivers for the two CAN +peripherals found across the STM32 family: + +- ``BxCanDevice`` -- bare-metal driver for the **bxCAN** controller on STM32F4 + (CAN1/CAN2/CAN3). +- ``FdCanDevice`` -- bare-metal driver for the **FDCAN** controller on STM32G4 + (FDCAN1/FDCAN2/FDCAN3). + +Both classes expose the same logical interface -- ``init()``, ``start()``, +``stop()``, ``transmit()``, ``receiveISR()`` -- so the transceiver layer can +wrap either one behind the OpenBSW ``AbstractCANTransceiver`` API. All CAN +communication is classic CAN at 500 kbps; the FDCAN peripheral is CAN FD +capable but is configured with ``FDOE = 0`` and ``BRSE = 0``. + +BxCanDevice (STM32F4) +--------------------- + +- ``Config`` carries the peripheral base address, bit timing (``prescaler``, + ``bs1``, ``bs2``, ``sjw``, written to ``CAN->BTR`` with ``-1`` encoding) and + the TX/RX GPIO port/pin/alternate-function mapping. +- ``init()`` enables the APB1 clock, configures the GPIO pins, enters init + mode, sets ``ABOM`` and ``TXFP``, programs bit timing and installs an + accept-all filter. ``start()`` leaves init mode and enables ``FMPIE0``. +- TX uses the 3 hardware mailboxes; ``transmit()`` returns ``false`` when all + are full. ``TMEIE`` is enabled while TX is pending and masked again by + ``transmitISR()`` once all mailboxes are idle. +- RX drains hardware FIFO0 (3 deep) into a 32-entry circular software queue, + optionally applying a software bit-field filter. +- Filters: bank 0 in 32-bit mask mode (accept all) or banks 0..13 in 32-bit + identifier-list mode (2 standard IDs per bank, max 28 IDs). + +FdCanDevice (STM32G4) +--------------------- + +- ``Config`` carries the peripheral base address, nominal bit timing + (``prescaler``, ``nts1``, ``nts2``, ``nsjw``, written to ``FDCAN->NBTP``) + and the TX/RX GPIO mapping. The FDCAN kernel clock is set to PCLK1. +- The message RAM layout is fixed in hardware. Each instance owns 212 words + (FDCAN1 at ``SRAMCAN_BASE + 0x000``, FDCAN2 at ``+0x350``, FDCAN3 at + ``+0x6A0``); RX/TX elements are spaced 18 words (72 bytes) apart: + + - Standard ID filters: 28 x 1 word, ``0x000``--``0x06F`` + - Extended ID filters: 8 x 2 words, ``0x070``--``0x0AF`` + - RX FIFO 0: 3 x 18 words, ``0x0B0``--``0x187`` + - RX FIFO 1: 3 x 18 words, ``0x188``--``0x25F`` + - TX event FIFO: 3 x 2 words, ``0x260``--``0x277`` + - TX buffers: 3 x 18 words, ``0x278``--``0x34F`` + +- ``transmit()`` writes the TX element at ``ramBase + 0x278 + putIdx * 72``; + ``receiveISR()`` snapshots the RX FIFO 0 fill level once and drains exactly + that many elements into the same 32-entry software queue as ``BxCanDevice``. +- Filters: accept-all via ``RXGFC`` (``ANFS = 0``, ``ANFE = 0``) or up to 28 + exact-match standard-ID filter elements, rejecting non-matching frames. diff --git a/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h b/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h new file mode 100644 index 00000000000..a87a9a65e8c --- /dev/null +++ b/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h @@ -0,0 +1,215 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace bios +{ + +/** + * \brief Low-level bxCAN hardware abstraction for STM32F4. + * + * Manages a bxCAN peripheral: initialization, bit timing, TX mailboxes, + * RX FIFOs, filter banks, and error counters. Does not implement the + * OpenBSW transceiver interface - that is BxCanTransceiver's job. + */ +class BxCanDevice +{ +public: + struct Config + { + CAN_TypeDef* baseAddress; + uint32_t prescaler; + uint32_t bs1; + uint32_t bs2; + uint32_t sjw; + GPIO_TypeDef* rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + GPIO_TypeDef* txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + /** + * \brief Construct a BxCanDevice from a hardware configuration. + * \param config Peripheral base address, bit-timing, and GPIO pin mapping. + */ + explicit BxCanDevice(Config const& config); + + /** + * \brief Initialise the bxCAN peripheral. + * + * Enables the APB1 clock, configures TX/RX GPIO pins, enters init mode, + * programs bit timing, and installs an accept-all hardware filter. + * Must be called before start(). + * \return true on success, false if hardware timeout. + * \note Thread context only - not safe to call from ISR. + */ + bool init(); + + /** + * \brief Leave init mode and begin normal CAN operation. + * + * Drains any stale FIFO0 frames, clears the overrun flag, and enables + * the FMPIE0 (FIFO-message-pending) RX interrupt. + * \return true on success, false if hardware timeout. + * \note Requires a preceding init() call; returns false if not initialised. + */ + bool start(); + + /** + * \brief Disable CAN interrupts and re-enter init mode. + * + * Masks both FMPIE0 (RX) and TMEIE (TX-mailbox-empty) interrupts, + * then requests hardware init mode. + */ + void stop(); + + /** + * \brief Queue a CAN frame for transmission. + * + * Scans TX mailboxes 0-2 for an empty slot, writes the frame's ID, DLC, + * and payload, then sets TXRQ to trigger hardware transmission. + * Enables TMEIE so that transmitISR() fires on completion. + * + * \param frame The CAN frame to transmit (standard or extended ID). + * \return true if the frame was placed in a mailbox, false if all 3 are full. + * + * \note Thread context - must not be called concurrently with transmitISR() + * without external locking. + */ + bool transmit(::can::CANFrame const& frame); + + /** + * \brief ISR handler: drain hardware RX FIFO0 into the software queue. + * + * Reads all pending frames from FIFO0. Each frame is optionally checked + * against a software bit-field filter before being stored in the circular + * RX queue. If the queue is full the FIFO entry is released without storing. + * + * \param filterBitField Byte array indexed by (CAN-ID / 8); bit (CAN-ID % 8) + * set means "accept". Pass nullptr to accept all. + * \return Number of frames actually enqueued. + * + * \note ISR context - called from CAN1_RX0_IRQHandler. + */ + uint8_t receiveISR(uint8_t const* filterBitField); + + /** + * \brief ISR handler: acknowledge completed transmissions. + * + * Clears RQCP flags for all 3 mailboxes. If every mailbox is now empty, + * disables TMEIE to avoid spurious TX interrupts. + * + * \note ISR context - called from CAN1_TX_IRQHandler. + */ + void transmitISR(); + + /** + * \brief Check whether the CAN controller is in bus-off state. + * \return true if the BOFF bit in CAN->ESR is set. + */ + bool isBusOff() const; + + /** + * \brief Read the transmit error counter from CAN->ESR. + * \return TEC value (0-255). + */ + uint8_t getTxErrorCounter() const; + + /** + * \brief Read the receive error counter from CAN->ESR. + * \return REC value (0-255). + */ + uint8_t getRxErrorCounter() const; + + /** + * \brief Configure filter bank 0 in 32-bit mask mode to accept all frames. + * + * Enters filter init mode, sets mask = 0 / id = 0 (accept everything), + * assigns to FIFO0, and leaves filter init mode. + * + * \note Must be called while the peripheral is in init mode. + */ + void configureAcceptAllFilter(); + + /** + * \brief Configure filter banks in 32-bit identifier-list mode. + * + * Packs up to 2 standard IDs per filter bank (banks 0..13). + * If count is odd the last bank duplicates the final ID. + * + * \param idList Array of standard 11-bit CAN IDs to accept. + * \param count Number of entries in idList (max 28). + * + * \note Must be called while the peripheral is in init mode. + */ + void configureFilterList(uint32_t const* idList, uint8_t count); + + /** + * \brief Mask the FMPIE0 interrupt (FIFO0 message-pending). + * + * Used by the transceiver layer to create a critical section around + * RX queue access so that receiveISR() cannot modify the queue + * concurrently. + * + * \note Thread context - called before reading the RX queue. + */ + void disableRxInterrupt(); + + /** + * \brief Re-enable the FMPIE0 interrupt after queue access is complete. + * \note Thread context - called after reading the RX queue. + */ + void enableRxInterrupt(); + + /** + * \brief Access a received frame by index within the circular queue. + * \param index Zero-based offset from the queue head (0 .. getRxCount()-1). + * \return Const reference to the CANFrame at the given position. + */ + ::can::CANFrame const& getRxFrame(uint8_t index) const; + + /** + * \brief Return the number of unread frames in the software RX queue. + * \return Frame count (0 .. RX_QUEUE_SIZE). + */ + uint8_t getRxCount() const; + + /** + * \brief Advance the queue head past all current frames, resetting count to 0. + * + * \note Must be called with the RX interrupt disabled (disableRxInterrupt()) + * to avoid a race with receiveISR(). + */ + void clearRxQueue(); + +private: + Config const fConfig; + ::can::CANFrame fRxQueue[RX_QUEUE_SIZE]; + uint8_t fRxHead; + uint8_t fRxCount; + bool fInitialized; + + bool enterInitMode(); + bool leaveInitMode(); + void configureBitTiming(); + void configureGpio(); + void enablePeripheralClock(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h b/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h new file mode 100644 index 00000000000..f2328ebb0b9 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h @@ -0,0 +1,215 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace bios +{ + +/** + * \brief Low-level FDCAN hardware abstraction for STM32G4. + * + * Manages an FDCAN peripheral: initialization, bit timing, TX FIFO, + * RX FIFOs, message-RAM filter elements, and error counters. Used in + * classic CAN mode. Does not implement the OpenBSW transceiver interface. + */ +class FdCanDevice +{ +public: + struct Config + { + FDCAN_GlobalTypeDef* baseAddress; + uint32_t prescaler; + uint32_t nts1; + uint32_t nts2; + uint32_t nsjw; + GPIO_TypeDef* rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + GPIO_TypeDef* txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + /// Maximum number of frames buffered in the software RX queue. + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + /// HW filter ID list (set before open/start, nullptr = accept all) + uint32_t const* fFilterIds = nullptr; + uint8_t fFilterCount = 0U; + + /** + * \brief Construct an FdCanDevice from a hardware configuration. + * \param config Peripheral base address, bit timing, and GPIO pin map. + */ + explicit FdCanDevice(Config const& config); + + /** + * \brief Construct with a TX-complete callback delegate (matches FlexCANDevice pattern). + * \param config Hardware configuration. + * \param frameSentCallback Delegate invoked from transmitISR() when a listener TX completes. + */ + FdCanDevice(Config const& config, ::etl::delegate frameSentCallback); + + /** + * \brief Initialise the FDCAN peripheral (clock, GPIO, bit timing, message RAM). + * + * Leaves the peripheral in init mode; call start() to begin bus communication. + * \return true on success, false if hardware timeout (e.g. init mode not entered). + * \note Must be called from thread context before any other method. + */ + bool init(); + + /** + * \brief Configure interrupts and leave init mode to start bus communication. + * + * Enables RF0NE (RX FIFO 0 new element) interrupt at startup. TCE (TX + * complete) is managed per-TX by transmit(frame, true) and disabled by + * transmitISR(), matching S32K's selective interrupt pattern. All + * interrupts route to line 0 (ILS=0). Clears INIT to join the bus. + * \return true on success, false if hardware timeout. + * \note Must be called after init(). + */ + bool start(); + + /** + * \brief Disable CAN interrupts and re-enter init mode, detaching from the bus. + */ + void stop(); + + /** + * \brief Queue a CAN frame for transmission via the hardware TX FIFO. + * \param frame Classic CAN frame (standard or extended ID, up to 8 bytes). + * \return true if the frame was placed in the TX FIFO, false if FIFO is full. + * \note Safe to call from thread context. The actual transmission completes + * asynchronously; transmitISR() clears the completion flag. + */ + bool transmit(::can::CANFrame const& frame); + + /** + * \brief Queue a CAN frame with optional TX-complete interrupt control. + * \param frame Classic CAN frame to transmit. + * \param txInterruptNeeded If true, enable TCE before TX (for listener callback). + * If false, do not enable TCE (fire-and-forget). + * \return true if queued, false if FIFO full. + * \note Matches FlexCANDevice::transmit(frame, bufIdx, txInterruptNeeded) contract. + */ + bool transmit(::can::CANFrame const& frame, bool txInterruptNeeded); + + /** + * \brief Drain RX FIFO 0 into the software queue. Called from the RX ISR. + * + * Takes a snapshot of the current fill level and drains only that many + * elements, preventing an infinite loop on a busy bus. Frames whose + * CAN ID is not set in @p filterBitField are acknowledged but discarded. + * + * \param filterBitField Bit-field indexed by CAN ID; a set bit means + * "accept this ID". Pass nullptr to accept all. + * \return Number of frames actually stored in the software RX queue. + * \note ISR context only. Clears RF0N, RF0F, and RF0L interrupt flags. + */ + uint8_t receiveISR(uint8_t const* filterBitField); + + /** + * \brief Handle TX-complete interrupt: disable TCE, clear TC flag, invoke + * callback delegate. Matches FlexCANDevice::transmitISR() contract. + * \note ISR context only (interrupt line 0, ILS=0). + */ + void transmitISR(); + + /** + * \brief Check whether the FDCAN peripheral is in bus-off state. + * \return true if the BO bit in FDCAN->PSR is set. + */ + bool isBusOff() const; + + /** + * \brief Read the transmit error counter from FDCAN->ECR. + * \return Current TEC value (0-255). + */ + uint8_t getTxErrorCounter() const; + + /** + * \brief Read the receive error counter from FDCAN->ECR. + * \return Current REC value (0-127). + */ + uint8_t getRxErrorCounter() const; + + /** + * \brief Configure the global filter to accept all standard and extended IDs + * into RX FIFO 0 (no hardware filtering). + * \note Must be called while the peripheral is in init mode. + */ + void configureAcceptAllFilter(); + + /** + * \brief Program standard-ID filter elements in message RAM for exact-match + * acceptance, rejecting all non-matching frames. + * \param idList Array of 11-bit standard CAN IDs to accept. + * \param count Number of entries in @p idList (max 28). + * \note Must be called while the peripheral is in init mode. + */ + void configureFilterList(uint32_t const* idList, uint8_t count); + + /** + * \brief Retrieve a received frame from the software RX queue by index. + * \param index Logical index (0 .. getRxCount()-1), relative to fRxHead. + * \return Const reference to the CANFrame at the given queue position. + */ + ::can::CANFrame const& getRxFrame(uint8_t index) const; + + /** + * \brief Return the number of frames currently buffered in the software RX queue. + * \return Frame count (0 .. RX_QUEUE_SIZE). + */ + uint8_t getRxCount() const; + + /** + * \brief Advance the queue head past all currently stored frames and reset count. + * \note Call from thread context after processing all frames returned by getRxFrame(). + */ + void clearRxQueue(); + + /** + * \brief Mask the RF0NE interrupt (RX FIFO 0 new element). + * \note Used to prevent ISR re-entry while the thread drains the queue. + */ + void disableRxInterrupt(); + + /** + * \brief Unmask the RF0NE interrupt (RX FIFO 0 new element). + */ + void enableRxInterrupt(); + + /// Returns the number of pending frames in the hardware RX FIFO0. + uint32_t getHwFifoFillLevel() const; + +private: + Config const fConfig; + ::can::CANFrame fRxQueue[RX_QUEUE_SIZE]; + uint8_t fRxHead; + uint8_t fRxCount; + bool fInitialized; + ::etl::delegate fFrameSentCallback; + + bool enterInitMode(); + bool leaveInitMode(); + void configureBitTiming(); + void configureMessageRam(); + void configureGpio(); + void enablePeripheralClock(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/module.spec b/platforms/stm32/bsp/bspCan/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspCan/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp b/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp new file mode 100644 index 00000000000..578e3287de2 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp @@ -0,0 +1,382 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +BxCanDevice::BxCanDevice(Config const& config) +: fConfig(config), fRxQueue{}, fRxHead(0U), fRxCount(0U), fInitialized(false) +{} + +void BxCanDevice::enablePeripheralClock() +{ + // Enable CAN1 clock on APB1 + RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; + // Small delay for clock stabilization + uint32_t volatile dummy = RCC->APB1ENR; + (void)dummy; +} + +void BxCanDevice::configureGpio() +{ + GPIO_TypeDef* txPort = fConfig.txGpioPort; + GPIO_TypeDef* rxPort = fConfig.rxGpioPort; + + // TX pin: AF mode, push-pull, high speed + txPort->MODER &= ~(3U << (fConfig.txPin * 2U)); + txPort->MODER |= (2U << (fConfig.txPin * 2U)); + txPort->OSPEEDR |= (3U << (fConfig.txPin * 2U)); + if (fConfig.txPin < 8U) + { + txPort->AFR[0] &= ~(0xFU << (fConfig.txPin * 4U)); + txPort->AFR[0] |= (static_cast(fConfig.txAf) << (fConfig.txPin * 4U)); + } + else + { + txPort->AFR[1] &= ~(0xFU << ((fConfig.txPin - 8U) * 4U)); + txPort->AFR[1] |= (static_cast(fConfig.txAf) << ((fConfig.txPin - 8U) * 4U)); + } + + // RX pin: AF mode, pull-up + rxPort->MODER &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->MODER |= (2U << (fConfig.rxPin * 2U)); + rxPort->PUPDR &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->PUPDR |= (1U << (fConfig.rxPin * 2U)); // Pull-up + if (fConfig.rxPin < 8U) + { + rxPort->AFR[0] &= ~(0xFU << (fConfig.rxPin * 4U)); + rxPort->AFR[0] |= (static_cast(fConfig.rxAf) << (fConfig.rxPin * 4U)); + } + else + { + rxPort->AFR[1] &= ~(0xFU << ((fConfig.rxPin - 8U) * 4U)); + rxPort->AFR[1] |= (static_cast(fConfig.rxAf) << ((fConfig.rxPin - 8U) * 4U)); + } +} + +bool BxCanDevice::enterInitMode() +{ + fConfig.baseAddress->MCR |= CAN_MCR_INRQ; +#if !defined(UNIT_TEST) + uint32_t timeout = 100000U; + while ((fConfig.baseAddress->MSR & CAN_MSR_INAK) == 0U) + { + if (--timeout == 0U) + { + return false; + } + } +#else + // In unit tests, MSR doesn't auto-track MCR. Simulate HW behavior. + fConfig.baseAddress->MSR |= CAN_MSR_INAK; +#endif + return true; +} + +bool BxCanDevice::leaveInitMode() +{ + fConfig.baseAddress->MCR &= ~CAN_MCR_INRQ; +#if !defined(UNIT_TEST) + uint32_t timeout = 100000U; + while ((fConfig.baseAddress->MSR & CAN_MSR_INAK) != 0U) + { + if (--timeout == 0U) + { + return false; + } + } +#else + fConfig.baseAddress->MSR &= ~CAN_MSR_INAK; +#endif + return true; +} + +void BxCanDevice::configureBitTiming() +{ + // BTR: SJW | BS2 | BS1 | BRP (prescaler - 1) + fConfig.baseAddress->BTR = ((fConfig.sjw - 1U) << CAN_BTR_SJW_Pos) + | ((fConfig.bs2 - 1U) << CAN_BTR_TS2_Pos) + | ((fConfig.bs1 - 1U) << CAN_BTR_TS1_Pos) | (fConfig.prescaler - 1U); +} + +bool BxCanDevice::init() +{ + enablePeripheralClock(); + configureGpio(); + + if (!enterInitMode()) + { + return false; + } + + // Exit sleep mode + fConfig.baseAddress->MCR &= ~CAN_MCR_SLEEP; + + // Configure: auto bus-off management, auto retransmission, TX FIFO priority + fConfig.baseAddress->MCR |= CAN_MCR_ABOM | CAN_MCR_TXFP; + + configureBitTiming(); + configureAcceptAllFilter(); + + fInitialized = true; + return true; +} + +bool BxCanDevice::start() +{ + if (!fInitialized) + { + return false; + } + + if (!leaveInitMode()) + { + return false; + } + + // Drain any frames that arrived while in init mode +#if !defined(UNIT_TEST) + while ((fConfig.baseAddress->RF0R & CAN_RF0R_FMP0) != 0U) + { + fConfig.baseAddress->RF0R |= CAN_RF0R_RFOM0; + } +#endif + // Clear overrun flag + fConfig.baseAddress->RF0R |= CAN_RF0R_FOVR0; + + fConfig.baseAddress->IER |= CAN_IER_FMPIE0; + return true; +} + +void BxCanDevice::stop() +{ + fConfig.baseAddress->IER &= ~(CAN_IER_FMPIE0 | CAN_IER_TMEIE); + enterInitMode(); +} + +bool BxCanDevice::transmit(::can::CANFrame const& frame) +{ + CAN_TypeDef* can = fConfig.baseAddress; + + // Find an empty TX mailbox + uint8_t mailbox = 0xFFU; + if ((can->TSR & CAN_TSR_TME0) != 0U) + { + mailbox = 0U; + } + else if ((can->TSR & CAN_TSR_TME1) != 0U) + { + mailbox = 1U; + } + else if ((can->TSR & CAN_TSR_TME2) != 0U) + { + mailbox = 2U; + } + else + { + return false; // All mailboxes full + } + + // Set ID (standard 11-bit) + uint32_t id = frame.getId(); + if ((id & 0x80000000U) != 0U) + { + // Extended ID + can->sTxMailBox[mailbox].TIR = ((id & 0x1FFFFFFFU) << CAN_TI0R_EXID_Pos) | CAN_TI0R_IDE; + } + else + { + // Standard ID + can->sTxMailBox[mailbox].TIR = ((id & 0x7FFU) << CAN_TI0R_STID_Pos); + } + + // Set DLC + uint8_t dlc = frame.getPayloadLength(); + can->sTxMailBox[mailbox].TDTR = (dlc & 0xFU); + + // Set data bytes + uint8_t const* data = frame.getPayload(); + can->sTxMailBox[mailbox].TDLR + = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) | (static_cast(data[3]) << 24U); + can->sTxMailBox[mailbox].TDHR + = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) | (static_cast(data[7]) << 24U); + + // Request transmission + can->sTxMailBox[mailbox].TIR |= CAN_TI0R_TXRQ; + + // Enable TX mailbox empty interrupt now that we have pending TX + can->IER |= CAN_IER_TMEIE; + + return true; +} + +uint8_t BxCanDevice::receiveISR(uint8_t const* filterBitField) +{ + CAN_TypeDef* can = fConfig.baseAddress; + uint8_t received = 0U; + + // Snapshot fill level (same pattern as FDCAN receiveISR). + // Prevents infinite loop if HW latches new frames during drain. + uint8_t toDrain = static_cast(can->RF0R & CAN_RF0R_FMP0); + while (toDrain > 0U) + { + toDrain--; + if (fRxCount >= RX_QUEUE_SIZE) + { + // Queue full - release FIFO entry without storing + can->RF0R |= CAN_RF0R_RFOM0; + continue; + } + + // Read ID + uint32_t rir = can->sFIFOMailBox[0].RIR; + uint32_t id; + if ((rir & CAN_RI0R_IDE) != 0U) + { + id = ((rir >> CAN_RI0R_EXID_Pos) & 0x1FFFFFFFU) | 0x80000000U; + } + else + { + id = (rir >> CAN_RI0R_STID_Pos) & 0x7FFU; + } + + // Filter check using BitFieldFilter byte array (if provided) + if (filterBitField != nullptr) + { + uint32_t byteIndex = id / 8U; + uint32_t bitIndex = id % 8U; + if ((filterBitField[byteIndex] & (1U << bitIndex)) == 0U) + { + can->RF0R |= CAN_RF0R_RFOM0; + continue; + } + } + + // Read DLC and data + uint8_t dlc = static_cast(can->sFIFOMailBox[0].RDTR & 0xFU); + uint32_t rdlr = can->sFIFOMailBox[0].RDLR; + uint32_t rdhr = can->sFIFOMailBox[0].RDHR; + + uint8_t data[8]; + data[0] = static_cast(rdlr); + data[1] = static_cast(rdlr >> 8U); + data[2] = static_cast(rdlr >> 16U); + data[3] = static_cast(rdlr >> 24U); + data[4] = static_cast(rdhr); + data[5] = static_cast(rdhr >> 8U); + data[6] = static_cast(rdhr >> 16U); + data[7] = static_cast(rdhr >> 24U); + + // Store in queue + uint8_t idx = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxQueue[idx] = ::can::CANFrame(id, data, dlc); + fRxCount++; + received++; + + // Release FIFO entry + can->RF0R |= CAN_RF0R_RFOM0; + } + + return received; +} + +void BxCanDevice::transmitISR() +{ + // Clear TX request completed flags + fConfig.baseAddress->TSR |= CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2; + + // Disable TMEIE if all mailboxes are now empty (prevents spurious interrupts) + if ((fConfig.baseAddress->TSR & (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)) + == (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)) + { + fConfig.baseAddress->IER &= ~CAN_IER_TMEIE; + } +} + +bool BxCanDevice::isBusOff() const { return (fConfig.baseAddress->ESR & CAN_ESR_BOFF) != 0U; } + +uint8_t BxCanDevice::getTxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ESR >> CAN_ESR_TEC_Pos) & 0xFFU); +} + +uint8_t BxCanDevice::getRxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ESR >> CAN_ESR_REC_Pos) & 0xFFU); +} + +void BxCanDevice::configureAcceptAllFilter() +{ + // Filter bank 0: accept all standard + extended frames into FIFO0 + CAN_TypeDef* can = fConfig.baseAddress; + can->FMR |= CAN_FMR_FINIT; // Enter filter init mode + can->FA1R &= ~(1U << 0U); // Deactivate filter 0 + can->sFilterRegister[0].FR1 = 0U; // ID = 0 (don't care) + can->sFilterRegister[0].FR2 = 0U; // Mask = 0 (accept all) + can->FS1R |= (1U << 0U); // 32-bit scale + can->FM1R &= ~(1U << 0U); // Mask mode (not list) + can->FFA1R &= ~(1U << 0U); // Assign to FIFO0 + can->FA1R |= (1U << 0U); // Activate filter 0 + can->FMR &= ~CAN_FMR_FINIT; // Leave filter init mode +} + +void BxCanDevice::configureFilterList(uint32_t const* idList, uint8_t count) +{ + CAN_TypeDef* can = fConfig.baseAddress; + can->FMR |= CAN_FMR_FINIT; + + // Use filter banks 0..N/2 in 32-bit list mode (2 IDs per bank) + uint8_t bankIdx = 0U; + for (uint8_t i = 0U; i < count && bankIdx < 14U; i += 2U, bankIdx++) + { + can->FA1R &= ~(1U << bankIdx); + can->FS1R |= (1U << bankIdx); // 32-bit scale + can->FM1R |= (1U << bankIdx); // List mode + + // Standard IDs shifted to STID position + can->sFilterRegister[bankIdx].FR1 = (idList[i] << CAN_RI0R_STID_Pos); + if ((i + 1U) < count) + { + can->sFilterRegister[bankIdx].FR2 = (idList[i + 1U] << CAN_RI0R_STID_Pos); + } + else + { + can->sFilterRegister[bankIdx].FR2 = (idList[i] << CAN_RI0R_STID_Pos); + } + + can->FFA1R &= ~(1U << bankIdx); // FIFO0 + can->FA1R |= (1U << bankIdx); // Activate + } + + can->FMR &= ~CAN_FMR_FINIT; +} + +::can::CANFrame const& BxCanDevice::getRxFrame(uint8_t index) const +{ + return fRxQueue[(fRxHead + index) % RX_QUEUE_SIZE]; +} + +uint8_t BxCanDevice::getRxCount() const { return fRxCount; } + +void BxCanDevice::clearRxQueue() +{ + fRxHead = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxCount = 0U; +} + +void BxCanDevice::disableRxInterrupt() { fConfig.baseAddress->IER &= ~CAN_IER_FMPIE0; } + +void BxCanDevice::enableRxInterrupt() { fConfig.baseAddress->IER |= CAN_IER_FMPIE0; } + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp b/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp new file mode 100644 index 00000000000..bfb4021b97f --- /dev/null +++ b/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp @@ -0,0 +1,485 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +// STM32G4 FDCAN message RAM layout (fixed, per instance): +// Each FDCAN instance has 212 words (848 bytes) of message RAM. +// FDCAN1 starts at SRAMCAN_BASE + 0x000 +// FDCAN2 starts at SRAMCAN_BASE + 0x350 +// FDCAN3 starts at SRAMCAN_BASE + 0x6A0 +// +// Layout within each instance (offsets from instance base; RX/TX elements +// are spaced 18 words / 72 bytes apart regardless of configured data size): +// Standard ID filters: 28 elements x 1 word = 28 words (0x000 - 0x06F) +// Extended ID filters: 8 elements x 2 words = 16 words (0x070 - 0x0AF) +// RX FIFO0: 3 elements x 18 words = 54 words (0x0B0 - 0x187) +// RX FIFO1: 3 elements x 18 words = 54 words (0x188 - 0x25F) +// TX Event FIFO: 3 elements x 2 words = 6 words (0x260 - 0x277) +// TX Buffers: 3 elements x 18 words = 54 words (0x278 - 0x34F) + +// uintptr_t so host unit tests with 64-bit fake RAM addresses work; on the +// 32-bit target this is identical to uint32_t. +static uintptr_t getInstanceRamBase(FDCAN_GlobalTypeDef* fdcan) +{ + if (fdcan == FDCAN1) + { + return SRAMCAN_BASE; + } +#if defined(FDCAN2) + if (fdcan == FDCAN2) + { + return SRAMCAN_BASE + 0x350U; + } +#endif +#if defined(FDCAN3) + if (fdcan == FDCAN3) + { + return SRAMCAN_BASE + 0x6A0U; + } +#endif + return SRAMCAN_BASE; +} + +static constexpr uint32_t STD_FILTER_OFFSET = 0x000U; +static constexpr uint32_t RX_FIFO0_OFFSET = 0x0B0U; +// STM32G4 message RAM: TX buffers at 0x278 (not 0x128 as standard M_CAN) +static constexpr uint32_t TX_BUFFER_OFFSET = 0x278U; + +FdCanDevice::FdCanDevice(Config const& config) +: fConfig(config), fRxQueue{}, fRxHead(0U), fRxCount(0U), fInitialized(false), fFrameSentCallback() +{} + +FdCanDevice::FdCanDevice(Config const& config, ::etl::delegate frameSentCallback) +: fConfig(config) +, fRxQueue{} +, fRxHead(0U) +, fRxCount(0U) +, fInitialized(false) +, fFrameSentCallback(frameSentCallback) +{} + +void FdCanDevice::enablePeripheralClock() +{ + // Enable FDCAN clock on APB1 + RCC->APB1ENR1 |= RCC_APB1ENR1_FDCANEN; + uint32_t volatile dummy = RCC->APB1ENR1; + (void)dummy; + + // Select FDCAN kernel clock = PCLK1 (FDCANSEL=10 in CCIPR1[25:24]) + // Default after reset is HSE (00), which may not be enabled. + RCC->CCIPR = (RCC->CCIPR & ~(3U << 24U)) | (2U << 24U); +} + +void FdCanDevice::configureGpio() +{ + GPIO_TypeDef* txPort = fConfig.txGpioPort; + GPIO_TypeDef* rxPort = fConfig.rxGpioPort; + + txPort->MODER &= ~(3U << (fConfig.txPin * 2U)); + txPort->MODER |= (2U << (fConfig.txPin * 2U)); + txPort->OSPEEDR |= (3U << (fConfig.txPin * 2U)); + if (fConfig.txPin < 8U) + { + txPort->AFR[0] &= ~(0xFU << (fConfig.txPin * 4U)); + txPort->AFR[0] |= (static_cast(fConfig.txAf) << (fConfig.txPin * 4U)); + } + else + { + txPort->AFR[1] &= ~(0xFU << ((fConfig.txPin - 8U) * 4U)); + txPort->AFR[1] |= (static_cast(fConfig.txAf) << ((fConfig.txPin - 8U) * 4U)); + } + + rxPort->MODER &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->MODER |= (2U << (fConfig.rxPin * 2U)); + rxPort->PUPDR &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->PUPDR |= (1U << (fConfig.rxPin * 2U)); + if (fConfig.rxPin < 8U) + { + rxPort->AFR[0] &= ~(0xFU << (fConfig.rxPin * 4U)); + rxPort->AFR[0] |= (static_cast(fConfig.rxAf) << (fConfig.rxPin * 4U)); + } + else + { + rxPort->AFR[1] &= ~(0xFU << ((fConfig.rxPin - 8U) * 4U)); + rxPort->AFR[1] |= (static_cast(fConfig.rxAf) << ((fConfig.rxPin - 8U) * 4U)); + } +} + +static constexpr uint32_t INIT_TIMEOUT_CYCLES = 100000U; + +bool FdCanDevice::enterInitMode() +{ + fConfig.baseAddress->CCCR |= FDCAN_CCCR_INIT; + uint32_t timeout = INIT_TIMEOUT_CYCLES; + while ((fConfig.baseAddress->CCCR & FDCAN_CCCR_INIT) == 0U) + { + if (--timeout == 0U) + { + return false; + } + } + // Enable configuration change + fConfig.baseAddress->CCCR |= FDCAN_CCCR_CCE; + return true; +} + +bool FdCanDevice::leaveInitMode() +{ + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_INIT; + uint32_t timeout = INIT_TIMEOUT_CYCLES; + while ((fConfig.baseAddress->CCCR & FDCAN_CCCR_INIT) != 0U) + { + if (--timeout == 0U) + { + return false; + } + } + return true; +} + +void FdCanDevice::configureBitTiming() +{ + // Nominal bit timing for classic CAN mode + // NBTP: NSJW | NBRP | NTSEG1 | NTSEG2 + fConfig.baseAddress->NBTP = ((fConfig.nsjw) << FDCAN_NBTP_NSJW_Pos) + | ((fConfig.prescaler - 1U) << FDCAN_NBTP_NBRP_Pos) + | ((fConfig.nts1) << FDCAN_NBTP_NTSEG1_Pos) + | ((fConfig.nts2) << FDCAN_NBTP_NTSEG2_Pos); +} + +void FdCanDevice::configureMessageRam() +{ + // STM32G4 has fixed message RAM layout - no configuration registers. + // RXGFC configures global filter behavior and list sizes only. + fConfig.baseAddress->RXGFC = (0U << FDCAN_RXGFC_LSS_Pos) | (0U << FDCAN_RXGFC_LSE_Pos); + + // TX buffer: use FIFO/queue mode + fConfig.baseAddress->TXBC = 0U; // FIFO mode (TFQM=0) +} + +bool FdCanDevice::init() +{ + enablePeripheralClock(); + configureGpio(); + + if (!enterInitMode()) + { + return false; + } + + // Classic CAN mode (no FD), auto retransmission disabled + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_FDOE; + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_BRSE; + + configureBitTiming(); + configureMessageRam(); + configureAcceptAllFilter(); + + fInitialized = true; + return true; +} + +bool FdCanDevice::start() +{ + if (!fInitialized) + { + return false; + } + + // Configure HW filter in init mode (before leaveInitMode) + if (fFilterIds != nullptr && fFilterCount > 0U) + { + configureFilterList(fFilterIds, fFilterCount); + } + else + { + configureAcceptAllFilter(); + } + + // Enable RX interrupt only at startup. TCE is managed per-TX by + // transmit(frame, true) and disabled by transmitISR() - matching S32K's + // selective per-buffer interrupt enable/disable pattern. + fConfig.baseAddress->IE = FDCAN_IE_RF0NE; + fConfig.baseAddress->ILS = 0U; + fConfig.baseAddress->ILE = FDCAN_ILE_EINT0; + fConfig.baseAddress->TXBTIE = 0x7U; // Required for TC generation per RM0440 + + if (!leaveInitMode()) + { + return false; + } + + // Write IE again after leaving init mode (persistence workaround). + fConfig.baseAddress->IE = FDCAN_IE_RF0NE; + return true; +} + +void FdCanDevice::stop() +{ + fConfig.baseAddress->IE &= ~(FDCAN_IE_RF0NE | FDCAN_IE_TCE); + enterInitMode(); +} + +bool FdCanDevice::transmit(::can::CANFrame const& frame) +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + + // Check TX FIFO free level + if ((fdcan->TXFQS & FDCAN_TXFQS_TFFL) == 0U) + { + return false; // TX FIFO full + } + + // Get put index + uint32_t putIdx = (fdcan->TXFQS & FDCAN_TXFQS_TFQPI) >> FDCAN_TXFQS_TFQPI_Pos; + + // Calculate TX buffer element address in message RAM + // STM32G4 FDCAN uses 18-word (72-byte) element spacing in message RAM, + // regardless of configured data field size (TXESC). Section boundaries + // are fixed at max element size. + static constexpr uint32_t TX_ELEMENT_SIZE = 72U; // 18 words x 4 bytes + uintptr_t ramBase = getInstanceRamBase(fdcan); + uint32_t* txBuf + = reinterpret_cast(ramBase + TX_BUFFER_OFFSET + (putIdx * TX_ELEMENT_SIZE)); + + // Word 0: ID + uint32_t id = frame.getId(); + if ((id & 0x80000000U) != 0U) + { + txBuf[0] = ((id & 0x1FFFFFFFU)) | (1U << 30U); // XTD bit + } + else + { + txBuf[0] = ((id & 0x7FFU) << 18U); + } + + // Word 1: DLC, no FD, no BRS + uint8_t dlc = frame.getPayloadLength(); + txBuf[1] = (static_cast(dlc) << 16U); + + // Words 2-3: Data + uint8_t const* data = frame.getPayload(); + txBuf[2] = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) | (static_cast(data[3]) << 24U); + txBuf[3] = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) | (static_cast(data[7]) << 24U); + + // Request transmission + fdcan->TXBAR = (1U << putIdx); + + return true; +} + +bool FdCanDevice::transmit(::can::CANFrame const& frame, bool txInterruptNeeded) +{ + if (txInterruptNeeded) + { + // Match S32K enableTransmitInterrupt(): clear flag first, then enable mask. + // FlexCANDevice.h:132-136: + // fpDevice->IFLAG1 = fTxInterruptMask0; // clear flag + // fpDevice->IMASK1 |= fTxInterruptMask0; // enable mask + fConfig.baseAddress->IR = FDCAN_IR_TC; // clear stale TC flag + fConfig.baseAddress->IE |= FDCAN_IE_TCE; // enable TC interrupt + } + // S32K: enableTransmitInterrupt() called BEFORE CODE=TRANSMIT. + // Here: TCE enabled BEFORE TXBAR write (inside transmit()). + return transmit(frame); +} + +} // namespace bios + +namespace bios +{ // reopen + +uint8_t FdCanDevice::receiveISR(uint8_t const* filterBitField) +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + uintptr_t ramBase = getInstanceRamBase(fdcan); + uint8_t received = 0U; + + // NOTE: RF0NE disable moved to CanSystem ISR trampoline (if needed). + // Disabling here is unsafe because receiveTask() may not run if the + // async dispatch is dedup-dropped, permanently blocking all RX. + + // NOTE: RF0L (message lost) is cleared below with RF0N and RF0F. + // Production code could increment an overrun counter here if needed. + + // Clear RF0N BEFORE reading F0FL so late arrivals re-trigger ISR + // (once RF0NE is re-enabled by receiveTask). + fdcan->IR = FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L; + + // Snapshot fill level - drain only what's here now. + uint8_t toDrain + = static_cast((fdcan->RXF0S & FDCAN_RXF0S_F0FL) >> FDCAN_RXF0S_F0FL_Pos); + + while (toDrain > 0U) + { + toDrain--; + + if (fRxCount >= RX_QUEUE_SIZE) + { + // Acknowledge without storing + uint32_t getIdx = (fdcan->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + fdcan->RXF0A = getIdx; + continue; + } + + uint32_t getIdx = (fdcan->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + + // Read RX FIFO element from message RAM + // STM32G4 FDCAN uses 18-word (72-byte) element spacing in message RAM. + static constexpr uint32_t RX_ELEMENT_SIZE = 72U; // 18 words x 4 bytes + uint32_t const* rxBuf = reinterpret_cast( + ramBase + RX_FIFO0_OFFSET + (getIdx * RX_ELEMENT_SIZE)); + + // Word 0: ID + uint32_t r0 = rxBuf[0]; + uint32_t id; + if ((r0 & (1U << 30U)) != 0U) + { + id = (r0 & 0x1FFFFFFFU) | 0x80000000U; // Extended + } + else + { + id = (r0 >> 18U) & 0x7FFU; // Standard + } + + if (filterBitField != nullptr) + { + uint32_t byteIndex = id / 8U; + uint32_t bitIndex = id % 8U; + if ((filterBitField[byteIndex] & (1U << bitIndex)) == 0U) + { + fdcan->RXF0A = getIdx; + continue; + } + } + + // Word 1: DLC + uint32_t r1 = rxBuf[1]; + uint8_t dlc = static_cast((r1 >> 16U) & 0xFU); + + // Words 2-3: Data + uint32_t d0 = rxBuf[2]; + uint32_t d1 = rxBuf[3]; + uint8_t data[8]; + data[0] = static_cast(d0); + data[1] = static_cast(d0 >> 8U); + data[2] = static_cast(d0 >> 16U); + data[3] = static_cast(d0 >> 24U); + data[4] = static_cast(d1); + data[5] = static_cast(d1 >> 8U); + data[6] = static_cast(d1 >> 16U); + data[7] = static_cast(d1 >> 24U); + + // Store in queue + uint8_t idx = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxQueue[idx] = ::can::CANFrame(id, data, dlc); + fRxCount++; + received++; + + // Acknowledge + fdcan->RXF0A = getIdx; + } + + // RF0N was already cleared at entry - no need to clear again here. + // Any frame arriving during the drain loop will re-set RF0N and + // trigger a new ISR after we return. + return received; +} + +void FdCanDevice::transmitISR() +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + + // Disable TCE (match S32K disableTransmitInterrupt pattern) + fdcan->IE &= ~FDCAN_IE_TCE; + + // Clear TC flag + fdcan->IR = FDCAN_IR_TC; + + // Invoke callback delegate if set (match FlexCANDevice::transmitISR) + if (fFrameSentCallback.is_valid()) + { + fFrameSentCallback(); + } +} + +bool FdCanDevice::isBusOff() const { return (fConfig.baseAddress->PSR & FDCAN_PSR_BO) != 0U; } + +uint8_t FdCanDevice::getTxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ECR >> FDCAN_ECR_TEC_Pos) & 0xFFU); +} + +uint8_t FdCanDevice::getRxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ECR >> FDCAN_ECR_REC_Pos) & 0x7FU); +} + +void FdCanDevice::configureAcceptAllFilter() +{ + // Accept all frames: set global filter to accept non-matching into FIFO0 + fConfig.baseAddress->RXGFC + = (0U << FDCAN_RXGFC_ANFS_Pos) // Accept non-matching std into RX FIFO0 + | (0U << FDCAN_RXGFC_ANFE_Pos); // Accept non-matching ext into RX FIFO0 +} + +void FdCanDevice::configureFilterList(uint32_t const* idList, uint8_t count) +{ + uintptr_t ramBase = getInstanceRamBase(fConfig.baseAddress); + + // Reject non-matching, configure standard ID filter elements + fConfig.baseAddress->RXGFC = (2U << FDCAN_RXGFC_ANFS_Pos) // Reject non-matching std + | (2U << FDCAN_RXGFC_ANFE_Pos) // Reject non-matching ext + | (static_cast(count) << FDCAN_RXGFC_LSS_Pos); + + // Write filter elements to message RAM (standard filter area) + uint32_t* filterRam = reinterpret_cast(ramBase + STD_FILTER_OFFSET); + + for (uint8_t i = 0U; i < count && i < 28U; i++) + { + // Standard filter element: classic filter (ID + mask) for exact match + // SFT = 10 (classic), SFEC = 001 (store in RX FIFO0) + // SFID1 = target ID, SFID2 = 0x7FF (all 11 bits must match) + filterRam[i] = (2U << 30U) // SFT = 10 (classic filter) + | (1U << 27U) // SFEC = store in FIFO0 + | ((idList[i] & 0x7FFU) << 16U) // SFID1 = filter ID + | 0x7FFU; // SFID2 = mask (exact match) + } +} + +::can::CANFrame const& FdCanDevice::getRxFrame(uint8_t index) const +{ + return fRxQueue[(fRxHead + index) % RX_QUEUE_SIZE]; +} + +uint8_t FdCanDevice::getRxCount() const { return fRxCount; } + +void FdCanDevice::clearRxQueue() +{ + fRxHead = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxCount = 0U; +} + +void FdCanDevice::disableRxInterrupt() { fConfig.baseAddress->IE &= ~FDCAN_IE_RF0NE; } + +void FdCanDevice::enableRxInterrupt() { fConfig.baseAddress->IE |= FDCAN_IE_RF0NE; } + +uint32_t FdCanDevice::getHwFifoFillLevel() const +{ + return (fConfig.baseAddress->RXF0S & FDCAN_RXF0S_F0FL) >> FDCAN_RXF0S_F0FL_Pos; +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/test/CMakeLists.txt b/platforms/stm32/bsp/bspCan/test/CMakeLists.txt new file mode 100644 index 00000000000..08d6151d36e --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(BxCanDeviceTest src/can/BxCanDeviceTest.cpp) + +target_include_directories(BxCanDeviceTest PRIVATE include ../include ../src) + +target_link_libraries(BxCanDeviceTest PRIVATE cpp2can etl gmock gtest_main) + +gtest_discover_tests(BxCanDeviceTest PROPERTIES LABELS "BxCanDeviceTest") + +add_executable(FdCanDeviceTest src/can/FdCanDeviceTest.cpp) + +target_include_directories(FdCanDeviceTest PRIVATE include ../include ../src) + +target_link_libraries(FdCanDeviceTest PRIVATE cpp2can etl gmock gtest_main) + +gtest_discover_tests(FdCanDeviceTest PROPERTIES LABELS "FdCanDeviceTest") diff --git a/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h b/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h new file mode 100644 index 00000000000..4b86cd649d5 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h @@ -0,0 +1,12 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Fake mcu.h for unit tests - types defined in test file +#pragma once diff --git a/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp b/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp new file mode 100644 index 00000000000..0867d082632 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp @@ -0,0 +1,4110 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test-only hardware fakes - must be defined before any STM32 header inclusion. + +#include +#include +#include +#include +#include + +// Provide __IO as volatile (same as CMSIS) +#ifndef __IO +#define __IO volatile +#endif + +// --- Fake GPIO_TypeDef (matches STM32F4 layout) --- +typedef struct +{ + __IO uint32_t MODER; + __IO uint32_t OTYPER; + __IO uint32_t OSPEEDR; + __IO uint32_t PUPDR; + __IO uint32_t IDR; + __IO uint32_t ODR; + __IO uint32_t BSRR; + __IO uint32_t LCKR; + __IO uint32_t AFR[2]; + __IO uint32_t BRR; +} GPIO_TypeDef; + +// --- Fake RCC_TypeDef (matches STM32F4 layout - need APB1ENR) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t PLLCFGR; + __IO uint32_t CFGR; + __IO uint32_t CIR; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED0; + __IO uint32_t APB1RSTR; + __IO uint32_t APB2RSTR; + uint32_t RESERVED1[2]; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED2; + __IO uint32_t APB1ENR; + __IO uint32_t APB2ENR; + uint32_t RESERVED3[2]; + __IO uint32_t AHB1LPENR; + __IO uint32_t AHB2LPENR; + __IO uint32_t AHB3LPENR; + uint32_t RESERVED4; + __IO uint32_t APB1LPENR; + __IO uint32_t APB2LPENR; + uint32_t RESERVED5[2]; + __IO uint32_t BDCR; + __IO uint32_t CSR; + uint32_t RESERVED6[2]; + __IO uint32_t SSCGR; + __IO uint32_t PLLI2SCFGR; +} RCC_TypeDef; + +// --- Fake CAN TX mailbox --- +typedef struct +{ + __IO uint32_t TIR; + __IO uint32_t TDTR; + __IO uint32_t TDLR; + __IO uint32_t TDHR; +} CAN_TxMailBox_TypeDef; + +// --- Fake CAN FIFO mailbox --- +typedef struct +{ + __IO uint32_t RIR; + __IO uint32_t RDTR; + __IO uint32_t RDLR; + __IO uint32_t RDHR; +} CAN_FIFOMailBox_TypeDef; + +// --- Fake CAN filter register --- +typedef struct +{ + __IO uint32_t FR1; + __IO uint32_t FR2; +} CAN_FilterRegister_TypeDef; + +// --- Fake CAN_TypeDef (matches STM32F4 layout) --- +// MSR.INAK (bit 0) must mirror MCR.INRQ (bit 0) for init mode busy-waits. +// We use a C++ struct with a getter-like mechanism: a helper function +// called before each test ensures MSR tracks MCR. +typedef struct +{ + __IO uint32_t MCR; + __IO uint32_t MSR; + __IO uint32_t TSR; + __IO uint32_t RF0R; + __IO uint32_t RF1R; + __IO uint32_t IER; + __IO uint32_t ESR; + __IO uint32_t BTR; + uint32_t RESERVED0[88]; + CAN_TxMailBox_TypeDef sTxMailBox[3]; + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; + uint32_t RESERVED1[12]; + __IO uint32_t FMR; + __IO uint32_t FM1R; + uint32_t RESERVED2; + __IO uint32_t FS1R; + uint32_t RESERVED3; + __IO uint32_t FFA1R; + uint32_t RESERVED4; + __IO uint32_t FA1R; + uint32_t RESERVED5[8]; + CAN_FilterRegister_TypeDef sFilterRegister[28]; +} CAN_TypeDef; + +// Hook: sync MSR.INAK to match MCR.INRQ after production code writes MCR. +// We patch this into the production code's enterInitMode/leaveInitMode by +// redefining the MSR register read to auto-sync from MCR first. +// CAN_MSR_INAK is bit 0, CAN_MCR_INRQ is bit 0 - same position. +// Override CAN1 macro +#define CAN1 (&fakeCan) + +// --- Static fake peripherals --- +static RCC_TypeDef fakeRcc; +static CAN_TypeDef fakeCan; +static GPIO_TypeDef fakeTxGpio; +static GPIO_TypeDef fakeRxGpio; + +// --- Override hardware macros to point at our fakes --- +#define RCC (&fakeRcc) +#define CAN1 (&fakeCan) + +// --- bxCAN register bit definitions (from stm32f413xx.h / stm32f4xx.h) --- + +// MCR bits +#define CAN_MCR_INRQ_Pos (0U) +#define CAN_MCR_INRQ (0x1UL << CAN_MCR_INRQ_Pos) +#define CAN_MCR_SLEEP_Pos (1U) +#define CAN_MCR_SLEEP (0x1UL << CAN_MCR_SLEEP_Pos) +#define CAN_MCR_TXFP_Pos (2U) +#define CAN_MCR_TXFP (0x1UL << CAN_MCR_TXFP_Pos) +#define CAN_MCR_RFLM_Pos (3U) +#define CAN_MCR_RFLM (0x1UL << CAN_MCR_RFLM_Pos) +#define CAN_MCR_NART_Pos (4U) +#define CAN_MCR_NART (0x1UL << CAN_MCR_NART_Pos) +#define CAN_MCR_AWUM_Pos (5U) +#define CAN_MCR_AWUM (0x1UL << CAN_MCR_AWUM_Pos) +#define CAN_MCR_ABOM_Pos (6U) +#define CAN_MCR_ABOM (0x1UL << CAN_MCR_ABOM_Pos) +#define CAN_MCR_TTCM_Pos (7U) +#define CAN_MCR_TTCM (0x1UL << CAN_MCR_TTCM_Pos) + +// MSR bits +#define CAN_MSR_INAK_Pos (0U) +#define CAN_MSR_INAK (0x1UL << CAN_MSR_INAK_Pos) + +// TSR bits +#define CAN_TSR_RQCP0_Pos (0U) +#define CAN_TSR_RQCP0 (0x1UL << CAN_TSR_RQCP0_Pos) +#define CAN_TSR_RQCP1_Pos (8U) +#define CAN_TSR_RQCP1 (0x1UL << CAN_TSR_RQCP1_Pos) +#define CAN_TSR_RQCP2_Pos (16U) +#define CAN_TSR_RQCP2 (0x1UL << CAN_TSR_RQCP2_Pos) +#define CAN_TSR_TME0_Pos (26U) +#define CAN_TSR_TME0 (0x1UL << CAN_TSR_TME0_Pos) +#define CAN_TSR_TME1_Pos (27U) +#define CAN_TSR_TME1 (0x1UL << CAN_TSR_TME1_Pos) +#define CAN_TSR_TME2_Pos (28U) +#define CAN_TSR_TME2 (0x1UL << CAN_TSR_TME2_Pos) + +// RF0R bits +#define CAN_RF0R_FMP0_Pos (0U) +#define CAN_RF0R_FMP0 (0x3UL << CAN_RF0R_FMP0_Pos) +#define CAN_RF0R_FOVR0_Pos (4U) +#define CAN_RF0R_FOVR0 (0x1UL << CAN_RF0R_FOVR0_Pos) +#define CAN_RF0R_RFOM0_Pos (5U) +#define CAN_RF0R_RFOM0 (0x1UL << CAN_RF0R_RFOM0_Pos) + +// IER bits +#define CAN_IER_TMEIE_Pos (0U) +#define CAN_IER_TMEIE (0x1UL << CAN_IER_TMEIE_Pos) +#define CAN_IER_FMPIE0_Pos (1U) +#define CAN_IER_FMPIE0 (0x1UL << CAN_IER_FMPIE0_Pos) + +// ESR bits +#define CAN_ESR_BOFF_Pos (2U) +#define CAN_ESR_BOFF (0x1UL << CAN_ESR_BOFF_Pos) +#define CAN_ESR_TEC_Pos (16U) +#define CAN_ESR_REC_Pos (24U) + +// BTR bits +#define CAN_BTR_BRP_Pos (0U) +#define CAN_BTR_TS1_Pos (16U) +#define CAN_BTR_TS2_Pos (20U) +#define CAN_BTR_SJW_Pos (24U) +#define CAN_BTR_SILM_Pos (31U) +#define CAN_BTR_SILM (0x1UL << CAN_BTR_SILM_Pos) +#define CAN_BTR_LBKM_Pos (30U) +#define CAN_BTR_LBKM (0x1UL << CAN_BTR_LBKM_Pos) + +// TIR bits (TX mailbox identifier register) +#define CAN_TI0R_TXRQ_Pos (0U) +#define CAN_TI0R_TXRQ (0x1UL << CAN_TI0R_TXRQ_Pos) +#define CAN_TI0R_RTR_Pos (1U) +#define CAN_TI0R_RTR (0x1UL << CAN_TI0R_RTR_Pos) +#define CAN_TI0R_IDE_Pos (2U) +#define CAN_TI0R_IDE (0x1UL << CAN_TI0R_IDE_Pos) +#define CAN_TI0R_EXID_Pos (3U) +#define CAN_TI0R_STID_Pos (21U) + +// RIR bits (RX mailbox identifier register) +#define CAN_RI0R_RTR_Pos (1U) +#define CAN_RI0R_RTR (0x1UL << CAN_RI0R_RTR_Pos) +#define CAN_RI0R_IDE_Pos (2U) +#define CAN_RI0R_IDE (0x1UL << CAN_RI0R_IDE_Pos) +#define CAN_RI0R_EXID_Pos (3U) +#define CAN_RI0R_STID_Pos (21U) + +// FMR bits +#define CAN_FMR_FINIT_Pos (0U) +#define CAN_FMR_FINIT (0x1UL << CAN_FMR_FINIT_Pos) + +// RCC APB1ENR +#define RCC_APB1ENR_CAN1EN_Pos (25U) +#define RCC_APB1ENR_CAN1EN (0x1UL << RCC_APB1ENR_CAN1EN_Pos) + +// Prevent the real mcu.h from being included +#define MCU_MCU_H +#define MCU_TYPEDEFS_H + +// Provide the CANFrame include path directly +#include + +// Now include the driver header - it will see our faked types +#include + +// Include the implementation directly so it compiles with our fakes. +#include + +#include + +class BxCanDeviceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Zero all fake peripherals + memset(&fakeRcc, 0, sizeof(fakeRcc)); + memset(&fakeCan, 0, sizeof(fakeCan)); + memset(&fakeTxGpio, 0, sizeof(fakeTxGpio)); + memset(&fakeRxGpio, 0, sizeof(fakeRxGpio)); + + // The MSR.INAK bit: after setting MCR.INRQ, the hardware sets INAK. + // In our fake, writing to MCR doesn't auto-set MSR.INAK, so we + // pre-set it so enterInitMode's while-loop terminates immediately. + fakeCan.MSR = CAN_MSR_INAK; + } + + bios::BxCanDevice::Config makeDefaultConfig() + { + bios::BxCanDevice::Config cfg{}; + cfg.baseAddress = &fakeCan; + cfg.prescaler = 4U; // BRP = prescaler - 1 = 3 + cfg.bs1 = 13U; // TS1 + cfg.bs2 = 2U; // TS2 + cfg.sjw = 1U; // SJW + cfg.rxGpioPort = &fakeRxGpio; + cfg.rxPin = 11U; // PA11 (high pin, tests AFR[1] path) + cfg.rxAf = 9U; // AF9 + cfg.txGpioPort = &fakeTxGpio; + cfg.txPin = 5U; // PB5 (low pin, tests AFR[0] path) + cfg.txAf = 9U; // AF9 + return cfg; + } + + // Helper: put device into initialized state + std::unique_ptr makeInitedDevice() + { + auto cfg = makeDefaultConfig(); + auto dev = std::make_unique(cfg); + dev->init(); + return dev; + } + + // Helper: put device into initialized + started state + std::unique_ptr makeStartedDevice() + { + auto dev = makeInitedDevice(); + // Clear INAK so leaveInitMode succeeds + fakeCan.MSR &= ~CAN_MSR_INAK; + // Mark all TX mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->start(); + return dev; + } + + // Helper: place a standard frame in RX FIFO0 + void placeRxFrameStd(uint32_t canId, uint8_t dlc, uint8_t const* data) + { + fakeCan.sFIFOMailBox[0].RIR = (canId << CAN_RI0R_STID_Pos); + fakeCan.sFIFOMailBox[0].RDTR = dlc & 0xFU; + if (data != nullptr) + { + fakeCan.sFIFOMailBox[0].RDLR = static_cast(data[0]) + | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + fakeCan.sFIFOMailBox[0].RDHR = static_cast(data[4]) + | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Helper: place an extended frame in RX FIFO0 + void placeRxFrameExt(uint32_t canId, uint8_t dlc, uint8_t const* data) + { + fakeCan.sFIFOMailBox[0].RIR = ((canId & 0x1FFFFFFFU) << CAN_RI0R_EXID_Pos) | CAN_RI0R_IDE; + fakeCan.sFIFOMailBox[0].RDTR = dlc & 0xFU; + if (data != nullptr) + { + fakeCan.sFIFOMailBox[0].RDLR = static_cast(data[0]) + | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + fakeCan.sFIFOMailBox[0].RDHR = static_cast(data[4]) + | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Simulate FIFO with N frames: set FMP0 to N, then on each RFOM write + // decrement. For simplicity, we use a counter pattern: set FMP0 = count, + // and the test hooks RFOM clearing to decrement. Since we can't hook writes + // to volatile, we simulate by setting FMP0 directly and calling receiveISR + // which reads FMP0, processes, sets RFOM (which ORs RF0R). We handle this + // by setting FMP0 = 1 for single-frame tests. + void setFifoCount(uint8_t count) + { + fakeCan.RF0R = (fakeCan.RF0R & ~CAN_RF0R_FMP0) | (count & 0x3U); + } + + // Helper: create a CANFrame with standard ID + ::can::CANFrame makeFrame(uint32_t id, uint8_t const* data, uint8_t dlc) + { + return ::can::CANFrame(id, data, dlc); + } + + // Helper: create a CANFrame with extended ID (sets bit 31) + ::can::CANFrame makeExtFrame(uint32_t rawId, uint8_t const* data, uint8_t dlc) + { + return ::can::CANFrame(rawId | 0x80000000U, data, dlc); + } + + // bxCAN hardware simulation: the driver loops on (RF0R & FMP0) and sets + // RFOM0 to release each frame. In real HW, RFOM0 auto-decrements FMP0. + // Our fake struct can't do this, so we spin a background thread that + // watches for RFOM0 being set and clears FMP0 accordingly. + + // Helper: receive exactly N frames via receiveISR with FIFO simulation. + // Spins a thread that simulates hardware FMP0 decrement on RFOM0 write. + uint8_t receiveFrames( + bios::BxCanDevice& dev, + uint8_t const* filter, + uint8_t const* data, + uint32_t const* ids, + bool const* extended, + uint8_t const* dlcs, + uint8_t totalFrames) + { + // For multi-frame, we'd need complex simulation. + // For simplicity in most tests, we use the single-frame helper. + // This function handles the general case with a background thread. + if (totalFrames == 0U) + { + fakeCan.RF0R = 0U; + return dev.receiveISR(filter); + } + + // We simulate by placing frame 0 and using a thread to decrement FMP0. + // Frame data is loaded from the arrays. + uint8_t frameIdx = 0U; + auto loadFrame = [&](uint8_t idx) + { + if (extended != nullptr && extended[idx]) + { + placeRxFrameExt(ids[idx], dlcs[idx], data + idx * 8U); + } + else + { + placeRxFrameStd(ids[idx], dlcs[idx], data + idx * 8U); + } + }; + + // Load first frame and set FMP0 + loadFrame(0); + fakeCan.RF0R = totalFrames; + + // Launch a thread that watches for RFOM0 and simulates HW behavior + std::atomic done{false}; + std::thread hwSim( + [&]() + { + uint8_t remaining = totalFrames; + while (!done.load(std::memory_order_relaxed)) + { + uint32_t rf0r = fakeCan.RF0R; + if ((rf0r & CAN_RF0R_RFOM0) != 0U) + { + remaining--; + if (remaining > 0U && (frameIdx + 1U) < totalFrames) + { + frameIdx++; + loadFrame(frameIdx); + } + // Clear RFOM0 and set new FMP0 + fakeCan.RF0R = remaining; + if (remaining == 0U) + { + done.store(true, std::memory_order_relaxed); + } + } + } + }); + + uint8_t result = dev.receiveISR(filter); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + return result; + } + + // Simplified single-frame receive helper + uint8_t receiveSingleFrame( + bios::BxCanDevice& dev, + uint32_t id, + bool isExtended, + uint8_t dlc, + uint8_t const* data, + uint8_t const* filter = nullptr) + { + if (isExtended) + { + placeRxFrameExt(id, dlc, data); + } + else + { + placeRxFrameStd(id, dlc, data); + } + fakeCan.RF0R = 1U; // 1 frame pending + + // Thread to simulate HW: when RFOM0 is set, clear FMP0 + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t result = dev.receiveISR(filter); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + return result; + } +}; + +TEST_F(BxCanDeviceTest, initEnablesPeripheralClock) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + + EXPECT_EQ(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioTxAlternateFunctionLowPin) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // TX pin = 5 (low register AFR[0]), AF9 + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); // Alternate function mode + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (cfg.txPin * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + + // Very high speed + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioRxAlternateFunctionHighPin) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // RX pin = 11 (high register AFR[1]), AF9 + uint32_t rxModer = (fakeRxGpio.MODER >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxModer, 2U); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((cfg.rxPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + + // Pull-up for RX + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioRxLowPin) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 4U; + cfg.rxAf = 7U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (cfg.rxPin * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 7U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioTxHighPin) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 12U; + cfg.txAf = 9U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((cfg.txPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(BxCanDeviceTest, initEntersInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // After init(), INRQ should be set (we stay in init mode until start()) + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, initClearsSleepMode) +{ + fakeCan.MCR = CAN_MCR_SLEEP; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_SLEEP, 0U); +} + +TEST_F(BxCanDeviceTest, initEnablesAbomAndTxfp) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, initConfiguresBitTiming) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + uint32_t brp = btr & 0x3FFU; + uint32_t ts1 = (btr >> CAN_BTR_TS1_Pos) & 0xFU; + uint32_t ts2 = (btr >> CAN_BTR_TS2_Pos) & 0x7U; + uint32_t sjw = (btr >> CAN_BTR_SJW_Pos) & 0x3U; + + EXPECT_EQ(brp, cfg.prescaler - 1U); + EXPECT_EQ(ts1, cfg.bs1 - 1U); + EXPECT_EQ(ts2, cfg.bs2 - 1U); + EXPECT_EQ(sjw, cfg.sjw - 1U); +} + +TEST_F(BxCanDeviceTest, initSetsInitializedFlag) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // start() before init() should be a no-op (not initialized) + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + // FMPIE0 should NOT be set since start() returns early + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Now init and start should work + fakeCan.MSR |= CAN_MSR_INAK; + dev.init(); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startLeavesInitMode) +{ + auto dev = makeInitedDevice(); + // leaveInitMode clears INRQ, then busy-waits for INAK=0 + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, startDrainsStaleFifo) +{ + auto dev = makeInitedDevice(); + // Simulate 1 stale frame in FIFO0 + fakeCan.RF0R = 1U; + fakeCan.MSR &= ~CAN_MSR_INAK; + + // start() should drain: while (FMP0 != 0) set RFOM0 + // In our fake, after one iteration RF0R = 1 | RFOM0 = 0x21, FMP0 still 1. + // This would loop forever. For this test, simulate HW clearing. + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + dev->start(); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + + // After drain, FMPIE0 should be enabled + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsOverrunFlag) +{ + auto dev = makeInitedDevice(); + fakeCan.MSR &= ~CAN_MSR_INAK; + fakeCan.RF0R = 0U; // No stale frames + dev->start(); + // FOVR0 should have been set (to clear it - write-1-to-clear) + EXPECT_NE(fakeCan.RF0R & CAN_RF0R_FOVR0, 0U); +} + +TEST_F(BxCanDeviceTest, startEnablesFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesFmpie0AndTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; // Simulate TMEIE was enabled + fakeCan.MSR |= CAN_MSR_INAK; // So enterInitMode completes + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopEntersInitMode) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitFindsEmptyMailbox0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + // Should use mailbox 0 (first empty) + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x7AB, data, 0U); + dev->transmit(frame); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x7ABU); + // IDE bit should NOT be set for standard ID + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitExtendedIdEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + // Extended ID: set bit 31 in CANFrame ID per CanId convention + ::can::CANFrame frame(0x80012345U, data, 0U); + dev->transmit(frame); + + // IDE bit should be set + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + // EXID field + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0x12345U); +} + +TEST_F(BxCanDeviceTest, transmitDlcEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 5U); + dev->transmit(frame); + + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 5U); +} + +TEST_F(BxCanDeviceTest, transmitPayloadBytes) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + // TDLR: bytes 0-3 (LSB first) + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR), 0xAAU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 8U), 0xBBU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 16U), 0xCCU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 24U), 0xDDU); + + // TDHR: bytes 4-7 + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR), 0xEEU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 8U), 0xFFU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 16U), 0x11U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 24U), 0x22U); +} + +TEST_F(BxCanDeviceTest, transmitSetsTxrq) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitEnablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + fakeCan.IER &= ~CAN_IER_TMEIE; // Clear TMEIE first + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + dev->transmit(frame); + + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitAll3FullReturnsFalse) +{ + auto dev = makeStartedDevice(); + // No TME bits set = all mailboxes occupied + fakeCan.TSR = 0U; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(BxCanDeviceTest, transmitMailboxPriority012) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + ::can::CANFrame frame1(0x100, data, 1U); + ::can::CANFrame frame2(0x200, data, 1U); + ::can::CANFrame frame3(0x300, data, 1U); + + // All empty: first TX goes to mailbox 0 + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmit(frame1); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); + + // Only mailbox 1 and 2 empty: next TX goes to mailbox 1 + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmit(frame2); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); + + // Only mailbox 2 empty: next TX goes to mailbox 2 + fakeCan.TSR = CAN_TSR_TME2; + dev->transmit(frame3); + EXPECT_NE(fakeCan.sTxMailBox[2].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitZeroPayload) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 0U); +} + +TEST_F(BxCanDeviceTest, transmitMaxPayload8Bytes) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 8U); +} + +TEST_F(BxCanDeviceTest, transmitUsesMailbox1WhenOnly1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; // Only mailbox 1 empty + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x200, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); + uint32_t stid = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x200U); +} + +TEST_F(BxCanDeviceTest, receiveIsrNoFramesReturnsZero) +{ + auto dev = makeStartedDevice(); + fakeCan.RF0R = 0U; // No frames pending + uint8_t count = dev->receiveISR(nullptr); + EXPECT_EQ(count, 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrDrainsFifo0SingleFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; + uint8_t count = receiveSingleFrame(*dev, 0x123U, false, 8U, data); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrStandardIdFromRir) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x456U, false, 1U, data); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x456U); +} + +TEST_F(BxCanDeviceTest, receiveIsrExtendedIdFromRir) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x1ABCDEFU, true, 1U, data); + + auto const& frame = dev->getRxFrame(0); + // Extended IDs have bit 31 set in the CANFrame ID + EXPECT_EQ(frame.getId(), 0x1ABCDEFU | 0x80000000U); +} + +TEST_F(BxCanDeviceTest, receiveIsrDlcFromRdtr) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 5U, data); + + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 5U); +} + +TEST_F(BxCanDeviceTest, receiveIsrPayloadByteOrder) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + receiveSingleFrame(*dev, 0x100U, false, 8U, data); + + auto const& frame = dev->getRxFrame(0); + uint8_t const* payload = frame.getPayload(); + EXPECT_EQ(payload[0], 0xAAU); + EXPECT_EQ(payload[1], 0xBBU); + EXPECT_EQ(payload[2], 0xCCU); + EXPECT_EQ(payload[3], 0xDDU); + EXPECT_EQ(payload[4], 0xEEU); + EXPECT_EQ(payload[5], 0xFFU); + EXPECT_EQ(payload[6], 0x11U); + EXPECT_EQ(payload[7], 0x22U); +} + +TEST_F(BxCanDeviceTest, receiveIsrReleasesRfom) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + // After receive, RF0R should have been written with RFOM0 + // (our HW sim cleared it, but the driver wrote it) + // We verify by checking that the frame was received successfully + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrFrameCountReturn) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrQueueFullDropsFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + + // Fill the queue to capacity (32 frames) + for (uint8_t i = 0U; i < 32U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // 33rd frame: should be dropped but FIFO still released + // Set up frame and FMP0=1 + placeRxFrameStd(0x200U, 1U, data); + fakeCan.RF0R = 1U; + + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + + // Frame was dropped (queue still 32, not 33) + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(count, 0U); // No frames enqueued +} + +TEST_F(BxCanDeviceTest, receiveIsrNullFilterAcceptsAll) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t count = receiveSingleFrame(*dev, 0x7FFU, false, 1U, data, nullptr); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrBitfieldFilterAccept) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Create filter that accepts ID 0x100 (byte 32, bit 0) + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); // Set bit for ID 0x100 + + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrBitfieldFilterReject) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Create filter that does NOT accept ID 0x100 + uint8_t filter[256] = {}; + // filter[0x100/8] bit for 0x100 is NOT set + + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrMultipleFramesDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data1[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + uint8_t data2[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x02}; + + // Receive first frame + receiveSingleFrame(*dev, 0x100U, false, 8U, data1); + // Receive second frame + receiveSingleFrame(*dev, 0x200U, false, 8U, data2); + + EXPECT_EQ(dev->getRxCount(), 2U); + + auto const& f1 = dev->getRxFrame(0); + EXPECT_EQ(f1.getId(), 0x100U); + EXPECT_EQ(f1.getPayload()[0], 0x11U); + + auto const& f2 = dev->getRxFrame(1); + EXPECT_EQ(f2.getId(), 0x200U); + EXPECT_EQ(f2.getPayload()[0], 0xAAU); +} + +TEST_F(BxCanDeviceTest, receiveIsrStaleFrameDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Receive a frame, then clear, then receive again + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP0; + dev->transmitISR(); + // RQCP0 should be set (write-1-to-clear in real HW, but in fake it ORs) + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP1; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP2; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsAllRqcpFlags) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + // All RQCP flags should be written (ORed in) + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDisablesTmeieWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // All 3 mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieWhenNotAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Only mailbox 0 empty, 1 and 2 still pending + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + // After ORing RQCP flags, TME0 is still set but TME1/TME2 are not + // So (TSR & (TME0|TME1|TME2)) != (TME0|TME1|TME2), TMEIE stays enabled + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueInitiallyZero) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueCorrectFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + receiveSingleFrame(*dev, 0x321U, false, 8U, data); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x321U); + EXPECT_EQ(frame.getPayloadLength(), 8U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[7], 0xBEU); +} + +TEST_F(BxCanDeviceTest, rxQueueCountAfterReceive) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); +} + +TEST_F(BxCanDeviceTest, rxQueueClearResets) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrapAround) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill queue partially, clear, fill again to cause wrap-around + for (uint8_t i = 0U; i < 30U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Now head is at 30. Fill 10 more to wrap around past 32 + for (uint8_t i = 0U; i < 10U; i++) + { + data[0] = i + 1U; + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 10U); + + // Verify first and last frame + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(9).getId(), 0x209U); +} + +TEST_F(BxCanDeviceTest, rxQueueCapacity32) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0U; i < 32U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // Verify all frames are accessible + for (uint8_t i = 0U; i < 32U; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), 0x100U + i); + } +} + +TEST_F(BxCanDeviceTest, disableRxInterruptClearsFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); // Enabled after start + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptSetsFmpie0) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptPreservesOtherBits) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; // Set another bit + dev->disableRxInterrupt(); + // TMEIE should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptPreservesOtherBits) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_TMEIE; // Only TMEIE set, FMPIE0 cleared + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, isBusOffReadsBoffBit) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); + + fakeCan.ESR = CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, isBusOffFalseWhenNotSet) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, getTxErrorCounterReadsEsr) +{ + auto dev = makeStartedDevice(); + // TEC is bits [23:16] of ESR + fakeCan.ESR = (128U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(BxCanDeviceTest, getRxErrorCounterReadsEsr) +{ + auto dev = makeStartedDevice(); + // REC is bits [31:24] of ESR + fakeCan.ESR = (64U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(BxCanDeviceTest, errorCounterMaxValue255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_TEC_Pos) | (255U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); + EXPECT_EQ(dev->getRxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, errorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterEntersFilterInitMode) +{ + auto dev = makeInitedDevice(); + // After init(), configureAcceptAllFilter was called. + // FINIT should be cleared (we left filter init mode) + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0MaskMode) +{ + auto dev = makeInitedDevice(); + // Bank 0 should be in mask mode (FM1R bit 0 = 0) + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank032BitScale) +{ + auto dev = makeInitedDevice(); + // Bank 0 should be 32-bit scale (FS1R bit 0 = 1) + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0AllPass) +{ + auto dev = makeInitedDevice(); + // FR1 = 0 (ID = 0, don't care), FR2 = 0 (mask = 0, accept all) + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0Active) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterAssignedToFifo0) +{ + auto dev = makeInitedDevice(); + // FFA1R bit 0 = 0 means FIFO0 + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListModeConfigures) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U}; + dev->configureFilterList(ids, 4U); + + // FINIT should be cleared after configuration + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); + + // Banks 0 and 1 should be in list mode (FM1R bits set) + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FM1R & (1U << 1U), 0U); + + // Bank 0: FR1 = 0x100 << STID_Pos, FR2 = 0x200 << STID_Pos + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + + // Bank 1: FR1 = 0x300 << STID_Pos, FR2 = 0x400 << STID_Pos + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x300U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x400U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListModeOddCountDuplicatesLast) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U}; + dev->configureFilterList(ids, 3U); + + // Bank 0: FR1 = 0x100, FR2 = 0x200 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + + // Bank 1: FR1 = 0x300, FR2 = 0x300 (duplicated) + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x300U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x300U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, bitTimingPrescalerEncoding) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t brp = fakeCan.BTR & 0x3FFU; + EXPECT_EQ(brp, 7U); // prescaler - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingBs1Encoding) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 15U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 14U); // bs1 - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingBs2Encoding) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 3U); // bs2 - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingSjwEncoding) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 1U); // sjw - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingMinValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.bs1 = 1U; + cfg.bs2 = 1U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 0U); // BRP = 0 + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 0U); // TS1 = 0 + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 0U); // TS2 = 0 + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); // SJW = 0 +} + +TEST_F(BxCanDeviceTest, bitTimingCan500kbpsAt42MHz) +{ + // Typical config: 42 MHz APB1, 500 kbps: prescaler=6, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 6U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 5U); // BRP = 5 + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); // TS1 = 10 + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); // TS2 = 1 + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); // SJW = 0 +} + +TEST_F(BxCanDeviceTest, doubleInitDoesNotCrash) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Second init should work without issues + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, stopRestart) +{ + auto dev = makeStartedDevice(); + + // Stop + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Restart + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitBeforeInitStillTransmits) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // Don't call init() or start() + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + // transmit() does not gate on fInitialized - it only checks TME bits + EXPECT_TRUE(dev.transmit(frame)); +} + +TEST_F(BxCanDeviceTest, startBeforeInitIsNoop) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // start() without init() should return early + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + // FMPIE0 should NOT be set + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, extendedIdFullRange) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + // Max 29-bit extended ID + uint8_t data[8] = {}; + ::can::CANFrame frame(0x9FFFFFFFU, data, 0U); // 0x80000000 | 0x1FFFFFFF + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0x1FFFFFFFU); +} + +TEST_F(BxCanDeviceTest, extendedIdMinRange) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x80000000U, data, 0U); // Extended ID = 0 + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0U); +} + +TEST_F(BxCanDeviceTest, filterIdBoundary0x000) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0] |= 1U; // Accept ID 0 + + uint8_t count = receiveSingleFrame(*dev, 0x000U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0U); +} + +TEST_F(BxCanDeviceTest, filterIdBoundary0x7FF) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0x7FF / 8U] |= (1U << (0x7FF % 8U)); // Accept ID 0x7FF + + uint8_t count = receiveSingleFrame(*dev, 0x7FFU, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x7FFU); +} + +TEST_F(BxCanDeviceTest, multipleReceiveCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Cycle 1: receive and clear + for (uint8_t i = 0U; i < 5U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); + dev->clearRxQueue(); + + // Cycle 2: receive and clear + for (uint8_t i = 0U; i < 3U; i++) + { + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + dev->clearRxQueue(); + + // Cycle 3: verify empty + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, concurrentTxMailboxes) +{ + auto dev = makeStartedDevice(); + uint8_t data1[8] = {0x01}; + uint8_t data2[8] = {0x02}; + uint8_t data3[8] = {0x03}; + + // Fill all 3 mailboxes + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data1, 1U))); + + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x200U, data2, 1U))); + + fakeCan.TSR = CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x300U, data3, 1U))); + + // All full now + fakeCan.TSR = 0U; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x400U, data1, 1U))); + + // Verify each mailbox has correct ID + uint32_t id0 = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + uint32_t id1 = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + uint32_t id2 = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(id0, 0x100U); + EXPECT_EQ(id1, 0x200U); + EXPECT_EQ(id2, 0x300U); + + // Verify each mailbox has correct data byte 0 + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR), 0x01U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[1].TDLR), 0x02U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[2].TDLR), 0x03U); +} + +TEST_F(BxCanDeviceTest, filterListSingleId) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x555U}; + dev->configureFilterList(ids, 1U); + + // Bank 0: FR1 = 0x555 << STID, FR2 = 0x555 << STID (duplicated for odd) + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x555U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x555U << CAN_RI0R_STID_Pos); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); // Activated +} + +TEST_F(BxCanDeviceTest, filterListBanksActivated) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids, 6U); + + // 3 banks should be activated (6 IDs / 2 per bank) + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 1U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListAllAssignedToFifo0) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U}; + dev->configureFilterList(ids, 4U); + + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); + EXPECT_EQ(fakeCan.FFA1R & (1U << 1U), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrExtendedIdRecovery) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + + // Receive extended ID + receiveSingleFrame(*dev, 0x12345U, true, 1U, data); + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x12345U | 0x80000000U); + EXPECT_EQ(frame.getPayload()[0], 0x42U); +} + +TEST_F(BxCanDeviceTest, transmitIsrThenTransmitAgain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Transmit first frame + fakeCan.TSR = CAN_TSR_TME0; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + + // Simulate TX complete: all mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); // TMEIE disabled + + // Transmit again: TMEIE should be re-enabled + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x200U, data, 1U))); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, getRxFrameIndexWraps) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames, clear, then add 2 more (head at 31) + for (uint8_t i = 0U; i < 31U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Head is now at 31. Add 2 frames -> indices 31 and 0 (wrapped) + receiveSingleFrame(*dev, 0x500U, false, 1U, data); + receiveSingleFrame(*dev, 0x501U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x501U); +} + +TEST_F(BxCanDeviceTest, isBusOffWithOtherEsrBitsSet) +{ + auto dev = makeStartedDevice(); + // Set TEC and REC but NOT BOFF + fakeCan.ESR = (100U << CAN_ESR_TEC_Pos) | (50U << CAN_ESR_REC_Pos); + EXPECT_FALSE(dev->isBusOff()); + + // Now set BOFF too + fakeCan.ESR |= CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdZero) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x000U, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0U); + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdMax0x7FF) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x7FFU, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x7FFU); +} + +TEST_F(BxCanDeviceTest, clockEnableIsIdempotent) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t apb1_1 = fakeRcc.APB1ENR; + dev.init(); + uint32_t apb1_2 = fakeRcc.APB1ENR; + // Should still have CAN1EN set, no extra bits + EXPECT_EQ(apb1_1, apb1_2); +} + +TEST_F(BxCanDeviceTest, receiveIsrZeroDlcFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 0U, data); + + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrMaxDlc8Frame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + receiveSingleFrame(*dev, 0x100U, false, 8U, data); + + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 8U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueAdvancesHead) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Receive 5 frames, clear, then receive 3 more + for (uint8_t i = 0; i < 5; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 3; i++) + { + data[0] = 0xA0U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0xA0U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x302U); +} + +TEST_F(BxCanDeviceTest, filterListMaxBanks14) +{ + auto dev = makeInitedDevice(); + // 28 IDs = 14 banks (max for configureFilterList) + uint32_t ids[28]; + for (uint8_t i = 0; i < 28; i++) + { + ids[i] = 0x100U + i; + } + dev->configureFilterList(ids, 28U); + + // All 14 banks should be activated + for (uint8_t b = 0; b < 14; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b << " not active"; + } +} + +TEST_F(BxCanDeviceTest, transmitToMailbox2Only) +{ + auto dev = makeStartedDevice(); + // Only mailbox 2 is empty + fakeCan.TSR = CAN_TSR_TME2; + + uint8_t data[8] = {0xFF}; + ::can::CANFrame frame(0x3FFU, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x3FFU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[2].TDLR), 0xFFU); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFinitToggle) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + + // Before init, FMR should be 0 + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); + + dev.init(); + + // After init (which calls configureAcceptAllFilter), FINIT should be cleared + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, disableEnableRxInterruptCycle) +{ + auto dev = makeStartedDevice(); + + // Initially enabled after start + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Disable-enable-disable cycle + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, errorCounterIndependentTecRec) +{ + auto dev = makeStartedDevice(); + // Set TEC=200, REC=50 + fakeCan.ESR = (200U << CAN_ESR_TEC_Pos) | (50U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 200U); + EXPECT_EQ(dev->getRxErrorCounter(), 50U); + + // Change to TEC=10, REC=250 + fakeCan.ESR = (10U << CAN_ESR_TEC_Pos) | (250U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 10U); + EXPECT_EQ(dev->getRxErrorCounter(), 250U); +} + +TEST_F(BxCanDeviceTest, receiveMultipleThenTransmit) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + + // Receive 3 frames + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + receiveSingleFrame(*dev, 0x102U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 3U); + + // Transmit should still work independently + fakeCan.TSR = CAN_TSR_TME0; + ::can::CANFrame txFrame(0x200U, data, 1U); + EXPECT_TRUE(dev->transmit(txFrame)); + + // RX queue unaffected + EXPECT_EQ(dev->getRxCount(), 3U); +} + +TEST_F(BxCanDeviceTest, initDoubleInitPreservesBtrSettings) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 5U; + cfg.bs1 = 10U; + cfg.bs2 = 3U; + cfg.sjw = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t btr1 = fakeCan.BTR; + dev.init(); + uint32_t btr2 = fakeCan.BTR; + EXPECT_EQ(btr1, btr2); +} + +TEST_F(BxCanDeviceTest, initBtrPrescaler1) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 0U); +} + +TEST_F(BxCanDeviceTest, initBtrPrescaler1024) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 1023U); +} + +TEST_F(BxCanDeviceTest, initBtrBs1Max16) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 16U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 15U); +} + +TEST_F(BxCanDeviceTest, initBtrBs2Max8) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 7U); +} + +TEST_F(BxCanDeviceTest, initBtrSjwMax4) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, initGpioTxPin0LowAf) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 0U; + cfg.txAf = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 4U); + uint32_t txModer = (fakeTxGpio.MODER >> (0U * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(BxCanDeviceTest, initGpioTxPin7BoundaryLow) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 7U; + cfg.txAf = 11U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 11U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPin8BoundaryHigh) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 8U; + cfg.rxAf = 5U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 5U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPin15Max) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 15U; + cfg.rxAf = 9U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(BxCanDeviceTest, initClearsSleepModeWhenAlreadyClear) +{ + fakeCan.MCR = 0U; // SLEEP already clear + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_SLEEP, 0U); +} + +TEST_F(BxCanDeviceTest, initAbomFlagSetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); +} + +TEST_F(BxCanDeviceTest, initTxfpFlagSetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, initClockEnableBitPosition25) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); +} + +TEST_F(BxCanDeviceTest, initClockDoesNotAffectOtherApb1Bits) +{ + fakeRcc.APB1ENR = 0x12345678U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Other bits should still be set + EXPECT_NE(fakeRcc.APB1ENR & 0x12345678U, 0U); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, initInrqSetBeforeLeaveInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // After init we are still in init mode (INRQ set) + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, initGpioTxSpeedVeryHigh) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPullUp) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(BxCanDeviceTest, initBtrFieldsDoNotOverlap) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; + cfg.bs1 = 16U; + cfg.bs2 = 8U; + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + uint32_t brp = btr & 0x3FFU; + uint32_t ts1 = (btr >> CAN_BTR_TS1_Pos) & 0xFU; + uint32_t ts2 = (btr >> CAN_BTR_TS2_Pos) & 0x7U; + uint32_t sjw = (btr >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(brp, 511U); + EXPECT_EQ(ts1, 15U); + EXPECT_EQ(ts2, 7U); + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, initFilterConfiguredAcceptAll) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Accept-all filter should be configured after init + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, startWithoutInitDoesNotSetFmpie0) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, doubleStartDoesNotCrash) +{ + auto dev = makeStartedDevice(); + // Second start: re-enter init mode briefly, then leave again + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenRestartClearsAndReenablesFmpie0) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsInrq) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, stopSetsInrq) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsFovrFlag) +{ + auto dev = makeInitedDevice(); + fakeCan.MSR &= ~CAN_MSR_INAK; + fakeCan.RF0R = 0U; + dev->start(); + // FOVR0 should have been written (write-1-to-clear) + EXPECT_NE(fakeCan.RF0R & CAN_RF0R_FOVR0, 0U); +} + +TEST_F(BxCanDeviceTest, startNoStaleFifoFrames) +{ + auto dev = makeInitedDevice(); + fakeCan.RF0R = 0U; // No stale frames + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenStartTwice) +{ + auto dev = makeStartedDevice(); + + // Stop + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Start again + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Stop again + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Start once more + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startPreservesAbomFlag) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + // ABOM should still be set after leaving init mode + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); +} + +TEST_F(BxCanDeviceTest, startPreservesTxfpFlag) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenInitThenStart) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Re-init + dev->init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startIerOnlyFmpie0Set) +{ + auto dev = makeInitedDevice(); + fakeCan.IER = 0U; + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + // After start, only FMPIE0 should be enabled (not TMEIE) + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopClearsIerCompletely) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & (CAN_IER_FMPIE0 | CAN_IER_TMEIE), 0U); +} + +TEST_F(BxCanDeviceTest, startAfterStopRxQueuePreserved) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // RX queue should still have the frame + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, startSetsMailboxesEmptyForSubsequentTx) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, stopDoesNotAffectBtrRegister) +{ + auto dev = makeStartedDevice(); + uint32_t btrBefore = fakeCan.BTR; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.BTR, btrBefore); +} + +TEST_F(BxCanDeviceTest, startWithFifo2StaleFrames) +{ + auto dev = makeInitedDevice(); + fakeCan.RF0R = 2U; // 2 stale frames + fakeCan.MSR &= ~CAN_MSR_INAK; + + // Background thread to simulate HW draining + std::atomic done{false}; + std::thread hwSim( + [&]() + { + uint8_t remaining = 2U; + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + remaining--; + fakeCan.RF0R = remaining; + if (remaining == 0U) + { + done.store(true, std::memory_order_relaxed); + return; + } + } + } + }); + + dev->start(); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopPreservesFilterConfiguration) +{ + auto dev = makeStartedDevice(); + // Check filter is configured after init/start + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + uint32_t fa1rBefore = fakeCan.FA1R; + + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Filter config should not be touched by stop + EXPECT_EQ(fakeCan.FA1R, fa1rBefore); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptFromZeroIer) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitEnablesTmeieFromCleanIer) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_FMPIE0; // Only FMPIE0 set + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + // FMPIE0 should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDisablesTmeieAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox0Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 0 busy, 1 and 2 empty + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox1Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 1 busy, 0 and 2 empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox2Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 2 busy, 0 and 1 empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieAllBusy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = 0U; // All mailboxes busy + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox0Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox2Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptDoesNotAffectTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + dev->disableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptDoesNotAffectTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_TMEIE; // Only TMEIE set + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDoesNotAffectFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + // FMPIE0 should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, ierBitIsolationFmpie0Only) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + dev->enableRxInterrupt(); + // Only FMPIE0 (bit 1) should be set + EXPECT_EQ(fakeCan.IER, CAN_IER_FMPIE0); +} + +TEST_F(BxCanDeviceTest, ierBitIsolationTmeieFromTransmit) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + // TMEIE (bit 0) should be set + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, multipleTxThenIsrCycleIerState) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // TX -> TMEIE enabled + fakeCan.TSR = CAN_TSR_TME0; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // ISR with all empty -> TMEIE disabled + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // TX again -> TMEIE re-enabled + fakeCan.TSR = CAN_TSR_TME1; + dev->transmit(::can::CANFrame(0x200U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // ISR with all empty again -> TMEIE disabled + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptThenReceiveDoesNotEnqueue) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + // Manually call receiveISR (would normally be called by HW interrupt) + uint8_t data[8] = {}; + // receiveISR can still be called manually, it does not check IER + placeRxFrameStd(0x100U, 1U, data); + fakeCan.RF0R = 1U; + + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + // receiveISR still works even if FMPIE0 disabled (just won't be called by HW) + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, enableDisableRxInterruptRapidToggle) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + } +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFs1rBit0Set) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFm1rBit0Clear) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFfa1rBit0Clear) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFa1rBit0Set) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFinitClearedAfterConfig) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode2Ids) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode1IdDuplicatesInBank) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x7FFU}; + dev->configureFilterList(ids, 1U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x7FFU << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x7FFU << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode3IdsOdd) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U}; + dev->configureFilterList(ids, 3U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x10U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x20U << CAN_RI0R_STID_Pos); + // Bank 1: odd count -> last ID duplicated + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x30U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x30U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode5IdsOdd) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U, 0x40U, 0x50U}; + dev->configureFilterList(ids, 5U); + + // Bank 0: IDs 0 and 1 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x10U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x20U << CAN_RI0R_STID_Pos); + // Bank 1: IDs 2 and 3 + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x30U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x40U << CAN_RI0R_STID_Pos); + // Bank 2: ID 4 duplicated + EXPECT_EQ(fakeCan.sFilterRegister[2].FR1, 0x50U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[2].FR2, 0x50U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode6Ids3Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids, 6U); + + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 1U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode8Ids4Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U, 0x40U, 0x50U, 0x60U, 0x70U, 0x80U}; + dev->configureFilterList(ids, 8U); + + for (uint8_t b = 0; b < 4; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } + EXPECT_EQ(fakeCan.sFilterRegister[3].FR1, 0x70U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[3].FR2, 0x80U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode14Ids7Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[14]; + for (uint8_t i = 0; i < 14; i++) + { + ids[i] = 0x100U + i; + } + dev->configureFilterList(ids, 14U); + + for (uint8_t b = 0; b < 7; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterListMode28IdsMax14Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[28]; + for (uint8_t i = 0; i < 28; i++) + { + ids[i] = 0x10U + i; + } + dev->configureFilterList(ids, 28U); + + for (uint8_t b = 0; b < 14; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + EXPECT_NE(fakeCan.FM1R & (1U << b), 0U) << "Bank " << (int)b << " list mode"; + } +} + +TEST_F(BxCanDeviceTest, filterListFinitClearedAfterConfig) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, filterListFs1r32BitScale) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListFm1rListMode) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListFfa1rFifo0Assignment) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListIdZero) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x000U, 0x000U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x000U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x000U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListIdMax0x7FF) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x7FFU, 0x7FFU}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x7FFU << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x7FFU << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListOverwritesPreviousAcceptAll) +{ + auto dev = makeInitedDevice(); + // After init, accept-all filter is configured (mask mode) + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); // Mask mode + + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + // Now should be list mode + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterOverwritesPreviousListFilter) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + + // Re-configure accept-all + dev->configureAcceptAllFilter(); + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); // Back to mask mode + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); +} + +TEST_F(BxCanDeviceTest, filterListFr1CorrectShift) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x001U, 0x002U}; + dev->configureFilterList(ids, 2U); + // STID_Pos is 21, so 0x001 << 21 = 0x00200000 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x00200000U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x00400000U); +} + +TEST_F(BxCanDeviceTest, filterListBank0And1IndependentFr) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x111U, 0x222U, 0x333U, 0x444U}; + dev->configureFilterList(ids, 4U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x111U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x222U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x333U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x444U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListSequentialIds) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x400U, 0x401U, 0x402U, 0x403U, 0x404U, 0x405U}; + dev->configureFilterList(ids, 6U); + + for (uint8_t b = 0; b < 3; b++) + { + uint32_t expected1 = (0x400U + b * 2U) << CAN_RI0R_STID_Pos; + uint32_t expected2 = (0x401U + b * 2U) << CAN_RI0R_STID_Pos; + EXPECT_EQ(fakeCan.sFilterRegister[b].FR1, expected1) << "Bank " << (int)b; + EXPECT_EQ(fakeCan.sFilterRegister[b].FR2, expected2) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterList10Ids5Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[10]; + for (uint8_t i = 0; i < 10; i++) + { + ids[i] = 0x200U + i; + } + dev->configureFilterList(ids, 10U); + + for (uint8_t b = 0; b < 5; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterList20Ids10Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[20]; + for (uint8_t i = 0; i < 20; i++) + { + ids[i] = 0x300U + i; + } + dev->configureFilterList(ids, 20U); + + for (uint8_t b = 0; b < 10; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterListReconfigureLeavesOldBanksActive) +{ + auto dev = makeInitedDevice(); + + // First configure 6 IDs (3 banks) + uint32_t ids1[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids1, 6U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); + + // Now reconfigure with just 2 IDs (1 bank) + uint32_t ids2[] = {0x700U, 0x701U}; + dev->configureFilterList(ids2, 2U); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + // configureFilterList does NOT deactivate banks beyond the new list - + // bank 2 from the previous configuration remains active + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox0SelectedWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox1SelectedWhen0Full) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox2SelectedWhen01Full) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[2].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txAllFullReturnsFalse) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + uint8_t data[8] = {}; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, txTmeBit000AllFull) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; // TME0=0, TME1=0, TME2=0 + uint8_t data[8] = {}; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, txTmeBit100Only0Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit010Only1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit001Only2Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit110Only01Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + // Should pick mailbox 0 (first empty) + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit101Only02Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit011Only12Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit111AllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTxrqSetInSelectedMailbox) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x200U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txStandardIdNotIde) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, txExtendedIdSetsIde) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x80000100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, txDlc0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 0U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 0U); +} + +TEST_F(BxCanDeviceTest, txDlc1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 1U); +} + +TEST_F(BxCanDeviceTest, txDlc2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB}; + dev->transmit(::can::CANFrame(0x100U, data, 2U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 2U); +} + +TEST_F(BxCanDeviceTest, txDlc3) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC}; + dev->transmit(::can::CANFrame(0x100U, data, 3U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 3U); +} + +TEST_F(BxCanDeviceTest, txDlc4) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD}; + dev->transmit(::can::CANFrame(0x100U, data, 4U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 4U); +} + +TEST_F(BxCanDeviceTest, txDlc5) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; + dev->transmit(::can::CANFrame(0x100U, data, 5U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 5U); +} + +TEST_F(BxCanDeviceTest, txDlc6) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + dev->transmit(::can::CANFrame(0x100U, data, 6U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 6U); +} + +TEST_F(BxCanDeviceTest, txDlc7) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11}; + dev->transmit(::can::CANFrame(0x100U, data, 7U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 7U); +} + +TEST_F(BxCanDeviceTest, txDlc8) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + dev->transmit(::can::CANFrame(0x100U, data, 8U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 8U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme000KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = 0U; // All busy + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme001KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme010KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme011KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme100KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme101KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme110KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme111DisablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrPreservesFmpie0) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrNoTmeieWhenNotPreviouslySet) +{ + auto dev = makeStartedDevice(); + fakeCan.IER &= ~CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrCalledMultipleTimesIdempotent) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrThenTransmitReenablesTmeie) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + + fakeCan.TSR = CAN_TSR_TME0; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRqcpSetEvenWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRqcpSetEvenWhenAllBusy) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWithRqcpAlreadySet) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2 | CAN_TSR_TME0 | CAN_TSR_TME1 + | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRapidCallsDoNotCorruptIer) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + } +} + +TEST_F(BxCanDeviceTest, busOffClearWhenBoffBitClear) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, busOffSetWhenBoffBitSet) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, busOffWithTecMaxAndBoff) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF | (255U << CAN_ESR_TEC_Pos); + EXPECT_TRUE(dev->isBusOff()); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, tecZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, tec128) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (128U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(BxCanDeviceTest, tec255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, tec1) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (1U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 1U); +} + +TEST_F(BxCanDeviceTest, recZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, rec64) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (64U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(BxCanDeviceTest, rec127) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (127U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(BxCanDeviceTest, rec255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, rec1) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (1U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 1U); +} + +TEST_F(BxCanDeviceTest, tecAndRecSimultaneous) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (100U << CAN_ESR_TEC_Pos) | (200U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 100U); + EXPECT_EQ(dev->getRxErrorCounter(), 200U); +} + +TEST_F(BxCanDeviceTest, busOffDoesNotAffectErrorCounters) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF | (50U << CAN_ESR_TEC_Pos) | (75U << CAN_ESR_REC_Pos); + EXPECT_TRUE(dev->isBusOff()); + EXPECT_EQ(dev->getTxErrorCounter(), 50U); + EXPECT_EQ(dev->getRxErrorCounter(), 75U); +} + +TEST_F(BxCanDeviceTest, errorCountersChangeOverTime) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (10U << CAN_ESR_TEC_Pos) | (20U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 10U); + EXPECT_EQ(dev->getRxErrorCounter(), 20U); + + fakeCan.ESR = (150U << CAN_ESR_TEC_Pos) | (200U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 150U); + EXPECT_EQ(dev->getRxErrorCounter(), 200U); + + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, getRxCountInitiallyZero) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfterOneFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfter5Frames) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 5; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfter32Frames) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueSetsCountToZero) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueMultipleTimes) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueOnEmptyQueue) +{ + auto dev = makeStartedDevice(); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrappingWithExactCapacity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to 30 frames, clear, then fill to 32 (wraps around) + for (uint8_t i = 0; i < 30; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 32; i++) + { + data[0] = i; + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x21FU); +} + +TEST_F(BxCanDeviceTest, rxQueueHeadAdvancesOnClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 10; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Head should now be at 10 + receiveSingleFrame(*dev, 0x500U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); +} + +TEST_F(BxCanDeviceTest, rxQueueMultipleCycleFillAndClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int cycle = 0; cycle < 5; cycle++) + { + for (uint8_t i = 0; i < 8; i++) + { + receiveSingleFrame(*dev, 0x100U * (cycle + 1) + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 8U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(BxCanDeviceTest, rxQueueFrameContentAfterWrapping) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 28 frames, clear, then add 8 more (wraps 28+8=36 > 32) + for (uint8_t i = 0; i < 28; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 8; i++) + { + data[0] = 0xA0U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 8U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0xA0U); + EXPECT_EQ(dev->getRxFrame(7).getId(), 0x307U); + EXPECT_EQ(dev->getRxFrame(7).getPayload()[0], 0xA7U); +} + +TEST_F(BxCanDeviceTest, rxQueueFullThen33rdDropped) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // 33rd frame should be dropped + placeRxFrameStd(0x999U, 1U, data); + fakeCan.RF0R = 1U; + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 32U); +} + +TEST_F(BxCanDeviceTest, rxQueueClearThenFillToCapacity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x21FU); +} + +TEST_F(BxCanDeviceTest, rxQueueGetFrameIndex0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + receiveSingleFrame(*dev, 0x123U, false, 1U, data); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x123U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0x42U); +} + +TEST_F(BxCanDeviceTest, rxQueueGetFrameLastIndex) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 10; i++) + { + data[0] = i; + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxFrame(9).getId(), 0x109U); + EXPECT_EQ(dev->getRxFrame(9).getPayload()[0], 9U); +} + +TEST_F(BxCanDeviceTest, rxQueueStandardAndExtendedMixed) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x1ABCDU, true, 1U, data); + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x1ABCDU | 0x80000000U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, rxQueueDifferentDlcValues) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + for (uint8_t dlc = 0; dlc <= 8; dlc++) + { + receiveSingleFrame(*dev, 0x100U + dlc, false, dlc, data); + } + EXPECT_EQ(dev->getRxCount(), 9U); + for (uint8_t dlc = 0; dlc <= 8; dlc++) + { + EXPECT_EQ(dev->getRxFrame(dlc).getPayloadLength(), dlc); + } +} + +TEST_F(BxCanDeviceTest, rxQueueReceiveClearReceive) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrappingPreservesDataIntegrity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames and clear + for (uint8_t i = 0; i < 31; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Fill 5 more (wraps around position 31 -> 0 -> 1 -> 2 -> 3 -> 4) + for (uint8_t i = 0; i < 5; i++) + { + data[0] = 0xF0U + i; + receiveSingleFrame(*dev, 0x400U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); + for (uint8_t i = 0; i < 5; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), 0x400U + i); + EXPECT_EQ(dev->getRxFrame(i).getPayload()[0], 0xF0U + i); + } +} + +TEST_F(BxCanDeviceTest, rxQueueFullExactly32) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 32; i++) + { + data[0] = i; + receiveSingleFrame(*dev, i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + for (uint8_t i = 0; i < 32; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), static_cast(i)); + EXPECT_EQ(dev->getRxFrame(i).getPayload()[0], i); + } +} + +TEST_F(BxCanDeviceTest, rxQueueClearAfterFullThenRefill) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Fill again to capacity + for (uint8_t i = 0; i < 32; i++) + { + data[0] = 0x80U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x31FU); +} + +TEST_F(BxCanDeviceTest, rxQueueFilterRejectDoesNotIncrementCount) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + // Only accept ID 0x100 + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + // Try to receive ID 0x200 (rejected) + uint8_t count = receiveSingleFrame(*dev, 0x200U, false, 1U, data, filter); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Now receive ID 0x100 (accepted) + count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, rxQueueFilterAcceptMultipleIds) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + filter[0x200 / 8U] |= (1U << (0x200 % 8U)); + filter[0x300 / 8U] |= (1U << (0x300 % 8U)); + + receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + receiveSingleFrame(*dev, 0x150U, false, 1U, data, filter); // Rejected + receiveSingleFrame(*dev, 0x200U, false, 1U, data, filter); + receiveSingleFrame(*dev, 0x250U, false, 1U, data, filter); // Rejected + receiveSingleFrame(*dev, 0x300U, false, 1U, data, filter); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x300U); +} + +TEST_F(BxCanDeviceTest, btrBrpField10Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t brp = fakeCan.BTR & 0x3FFU; + EXPECT_EQ(brp, 1023U); +} + +TEST_F(BxCanDeviceTest, btrTs1Field4Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 16U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 15U); +} + +TEST_F(BxCanDeviceTest, btrTs2Field3Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 7U); +} + +TEST_F(BxCanDeviceTest, btrSjwField2Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler2) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 1U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler100) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 100U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 99U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler256) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 256U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 255U); +} + +TEST_F(BxCanDeviceTest, btrCan250kbpsAt42MHz) +{ + // 42 MHz APB1, 250 kbps: prescaler=12, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 12U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 11U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); +} + +TEST_F(BxCanDeviceTest, btrCan1MbpsAt42MHz) +{ + // 42 MHz APB1, 1 Mbps: prescaler=3, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 3U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 2U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); +} + +TEST_F(BxCanDeviceTest, btrCan125kbpsAt36MHz) +{ + // 36 MHz APB1, 125 kbps: prescaler=18, BS1=13, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 18U; + cfg.bs1 = 13U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 17U); + EXPECT_EQ((fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU, 12U); + EXPECT_EQ((fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U, 1U); +} + +TEST_F(BxCanDeviceTest, btrAllFieldsMaxValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + cfg.bs1 = 16U; + cfg.bs2 = 8U; + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 1023U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 15U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 7U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 3U); +} + +TEST_F(BxCanDeviceTest, btrAllFieldsMinValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.bs1 = 1U; + cfg.bs2 = 1U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 0U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 0U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 0U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); +} + +TEST_F(BxCanDeviceTest, btrNoSilentOrLoopbackMode) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // SILM and LBKM should not be set for normal operation + EXPECT_EQ(fakeCan.BTR & CAN_BTR_SILM, 0U); + EXPECT_EQ(fakeCan.BTR & CAN_BTR_LBKM, 0U); +} + +TEST_F(BxCanDeviceTest, btrRewrittenOnDoubleInit) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 3U); + + // Corrupt BTR + fakeCan.BTR = 0xFFFFFFFFU; + + // Re-init should restore correct BTR + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 3U); +} + +TEST_F(BxCanDeviceTest, btrBs1Value8) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 7U); +} + +TEST_F(BxCanDeviceTest, clockEnableSetsApb1enrBit25) +{ + fakeRcc.APB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); +} + +TEST_F(BxCanDeviceTest, clockEnablePreservesOtherBitsInApb1enr) +{ + fakeRcc.APB1ENR = 0x00000001U; // Some other peripheral enabled + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & 0x00000001U, 0U); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableIdempotentMultipleInits) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t v1 = fakeRcc.APB1ENR; + dev.init(); + uint32_t v2 = fakeRcc.APB1ENR; + dev.init(); + uint32_t v3 = fakeRcc.APB1ENR; + EXPECT_EQ(v1, v2); + EXPECT_EQ(v2, v3); +} + +TEST_F(BxCanDeviceTest, clockEnableBeforeGpioConfig) +{ + // After init, both clock and GPIO should be configured + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(BxCanDeviceTest, clockEnableWithAllApb1BitsSet) +{ + fakeRcc.APB1ENR = 0xFFFFFFFFU; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // All bits should still be set + EXPECT_EQ(fakeRcc.APB1ENR, 0xFFFFFFFFU); +} + +TEST_F(BxCanDeviceTest, clockEnableBitExactValue) +{ + fakeRcc.APB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, (1U << 25U)); +} + +TEST_F(BxCanDeviceTest, clockEnableDoesNotTouchApb2enr) +{ + fakeRcc.APB2ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeRcc.APB2ENR, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableDoesNotTouchAhb1enr) +{ + fakeRcc.AHB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Note: GPIO clock enable might set AHB1ENR bits, + // but CAN clock should only touch APB1ENR + // We just verify CAN1EN is in APB1ENR + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableWithNeighborBitsPreserved) +{ + // Set bits 24 and 26 (neighbors of bit 25) + fakeRcc.APB1ENR = (1U << 24U) | (1U << 26U); + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 24U), 0U); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 26U), 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableAfterStopAndReInit) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Re-init + dev->init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, gpioRxPullUpConfigured) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // RX pin should have pull-up (PUPDR = 01) + uint32_t pupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(pupdr, 1U); +} + +TEST_F(BxCanDeviceTest, gpioTxHighSpeedConfigured) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // TX pin should have high speed (OSPEEDR = 11) + uint32_t speed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(speed, 3U); +} + +TEST_F(BxCanDeviceTest, rxQueueCapacityConstant) +{ + EXPECT_EQ(bios::BxCanDevice::RX_QUEUE_SIZE, 32U); +} diff --git a/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h b/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h new file mode 100644 index 00000000000..54151b0e51f --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h @@ -0,0 +1,170 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/** + * \file FakeHwHelpers.h + * \brief Smart fake register helpers for STM32 unit tests. + * + * Solves three problems that prevent tests from running on x86_64 host: + * + * 1. HW busy-wait loops: Production code writes a control bit then polls + * a status bit that hardware would auto-set/clear. These helpers hook + * into the fake register structs to simulate the state transitions. + * + * 2. 64-bit pointer truncation: Provides FAKE_ADDR() macro that safely + * creates a testable address from a static array on any platform. + * + * 3. Message RAM access: Provides a fake message RAM region with proper + * addressing that works on both 32-bit and 64-bit hosts. + */ + +#pragma once + +#include +#include + +// ============================================================================ +// Portable address casting - works on both 32-bit ARM and 64-bit host +// ============================================================================ + +/// Cast a pointer to the address type used by the production code (uint32_t). +/// On 64-bit host this truncates, but the production code will cast it back +/// to a pointer via reinterpret_cast(addr) which restores the full address +/// ONLY if the original pointer is in the low 4GB. We use static arrays which +/// on most x86_64 systems are in the low address space. +/// +/// For tests that need message RAM pointer arithmetic, use FakeMessageRam instead. +static inline uint32_t ptrToU32(void* p) +{ + return static_cast(reinterpret_cast(p)); +} + +// ============================================================================ +// BxCAN state simulation +// ============================================================================ + +/// Call after each write to fake CAN->MCR to simulate MSR tracking. +/// When INRQ is set in MCR, hardware sets INAK in MSR. +/// When INRQ is cleared, hardware clears INAK. +struct BxCanStateTracker +{ + /// Bit positions (must match CAN_MCR_INRQ and CAN_MSR_INAK) + static constexpr uint32_t MCR_INRQ = (1U << 0); + static constexpr uint32_t MSR_INAK = (1U << 0); + + /// Call this after writing to fakeCan.MCR + static void syncMsrFromMcr(uint32_t volatile& mcr, uint32_t volatile& msr) + { + if ((mcr & MCR_INRQ) != 0U) + { + msr |= MSR_INAK; // entering init mode + } + else + { + msr &= ~MSR_INAK; // leaving init mode + } + } +}; + +// ============================================================================ +// FDCAN state simulation +// ============================================================================ + +struct FdCanStateTracker +{ + static constexpr uint32_t CCCR_INIT = (1U << 0); + + /// Call this after writing to fakeFdcan.CCCR + static void syncCccrInit(uint32_t volatile& cccr) + { + // On real HW, writing INIT=1 is reflected immediately. + // Writing INIT=0 clears it after the peripheral finishes. + // In the fake, the write already sets/clears the bit directly + // since we're writing to a RAM variable. No extra action needed. + // The production code's while-loop checks the same variable + // and will see the change immediately. + (void)cccr; + } +}; + +// ============================================================================ +// ADC state simulation +// ============================================================================ + +struct AdcStateTracker +{ + /// After writing ADCAL=1 to CR, hardware clears it when calibration done. + /// We clear it immediately since there's no real calibration to do. + static void clearAdcalAfterWrite(uint32_t volatile& cr, uint32_t adcalBit) { cr &= ~adcalBit; } + + /// After writing ADEN=1 to CR, hardware sets ADRDY in ISR. + static void setAdrdyAfterEnable( + uint32_t volatile& cr, uint32_t volatile& isr, uint32_t adenBit, uint32_t adrdyBit) + { + if ((cr & adenBit) != 0U) + { + isr |= adrdyBit; + } + } + + /// After writing ADSTART=1, hardware sets EOC when conversion done. + static void setEocAfterStart( + uint32_t volatile& cr, uint32_t volatile& isr, uint32_t adstartBit, uint32_t eocBit) + { + if ((cr & adstartBit) != 0U) + { + isr |= eocBit; + } + } +}; + +// ============================================================================ +// Flash state simulation +// ============================================================================ + +struct FlashStateTracker +{ + /// Flash BSY bit is always 0 in fake (operation completes instantly). + /// The production waitForFlash() checks FLASH->SR & BSY - as long as + /// we memset SR to 0 in SetUp(), the loop exits immediately. + /// No action needed if SetUp clears SR. + + /// After erasePage sets STRT, clear it (simulates erase complete). + static void clearAfterErase(uint32_t volatile& cr, uint32_t volatile& sr, uint32_t strtBit) + { + cr &= ~strtBit; + sr = 0U; // BSY=0, no errors + } +}; + +// ============================================================================ +// Fake message RAM for FDCAN (solves 64-bit pointer truncation) +// ============================================================================ + +/// Provides a message RAM region that can be addressed via uint32_t offsets. +/// Instead of using absolute addresses (which get truncated on x86_64), +/// the tests should set SRAMCAN_BASE to 0 and use relative offsets into +/// this array. The production code does: `base + offset` then casts to +/// `uint32_t*`. If base is the actual array address (as uintptr_t cast to +/// uint32_t), it gets truncated. The fix: define SRAMCAN_BASE as 0 and +/// override getInstanceRamBase() to return the real pointer. +/// +/// However, since getInstanceRamBase is a static function inside the .cpp, +/// we can't easily override it. The simplest approach: on 64-bit host, +/// accept that message RAM tests will segfault and skip them with a guard. +/// +/// For a complete fix, the production code should use uintptr_t for RAM +/// addresses, which is an API change requiring upstream approval. + +#if UINTPTR_MAX > UINT32_MAX +#define FAKE_MSG_RAM_64BIT_HOST 1 +#else +#define FAKE_MSG_RAM_64BIT_HOST 0 +#endif diff --git a/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp b/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp new file mode 100644 index 00000000000..5fea5c6b99a --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp @@ -0,0 +1,3657 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test-only hardware fakes - must be defined before any STM32 header inclusion. + +#include +#include + +// Provide __IO as volatile (same as CMSIS) +#ifndef __IO +#define __IO volatile +#endif + +// --- Fake GPIO_TypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t MODER; + __IO uint32_t OTYPER; + __IO uint32_t OSPEEDR; + __IO uint32_t PUPDR; + __IO uint32_t IDR; + __IO uint32_t ODR; + __IO uint32_t BSRR; + __IO uint32_t LCKR; + __IO uint32_t AFR[2]; + __IO uint32_t BRR; +} GPIO_TypeDef; + +// --- Fake RCC_TypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t ICSCR; + __IO uint32_t CFGR; + __IO uint32_t PLLCFGR; + uint32_t RESERVED0; + uint32_t RESERVED1; + __IO uint32_t CIER; + __IO uint32_t CIFR; + __IO uint32_t CICR; + uint32_t RESERVED2; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED3; + __IO uint32_t APB1RSTR1; + __IO uint32_t APB1RSTR2; + __IO uint32_t APB2RSTR; + uint32_t RESERVED4; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED5; + __IO uint32_t APB1ENR1; + __IO uint32_t APB1ENR2; + __IO uint32_t APB2ENR; + uint32_t RESERVED6; + __IO uint32_t AHB1SMENR; + __IO uint32_t AHB2SMENR; + __IO uint32_t AHB3SMENR; + uint32_t RESERVED7; + __IO uint32_t APB1SMENR1; + __IO uint32_t APB1SMENR2; + __IO uint32_t APB2SMENR; + uint32_t RESERVED8; + __IO uint32_t CCIPR; + uint32_t RESERVED9; + __IO uint32_t BDCR; + __IO uint32_t CSR; + __IO uint32_t CRRCR; + __IO uint32_t CCIPR2; +} RCC_TypeDef; + +// --- Fake FDCAN_GlobalTypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CREL; + __IO uint32_t ENDN; + uint32_t RESERVED1; + __IO uint32_t DBTP; + __IO uint32_t TEST; + __IO uint32_t RWD; + __IO uint32_t CCCR; + __IO uint32_t NBTP; + __IO uint32_t TSCC; + __IO uint32_t TSCV; + __IO uint32_t TOCC; + __IO uint32_t TOCV; + uint32_t RESERVED2[4]; + __IO uint32_t ECR; + __IO uint32_t PSR; + __IO uint32_t TDCR; + uint32_t RESERVED3; + __IO uint32_t IR; + __IO uint32_t IE; + __IO uint32_t ILS; + __IO uint32_t ILE; + uint32_t RESERVED4[8]; + __IO uint32_t RXGFC; + __IO uint32_t XIDAM; + __IO uint32_t HPMS; + uint32_t RESERVED5; + __IO uint32_t RXF0S; + __IO uint32_t RXF0A; + __IO uint32_t RXF1S; + __IO uint32_t RXF1A; + uint32_t RESERVED6[8]; + __IO uint32_t TXBC; + __IO uint32_t TXFQS; + __IO uint32_t TXBRP; + __IO uint32_t TXBAR; + __IO uint32_t TXBCR; + __IO uint32_t TXBTO; + __IO uint32_t TXBCF; + __IO uint32_t TXBTIE; + __IO uint32_t TXBCIE; + __IO uint32_t TXEFS; + __IO uint32_t TXEFA; +} FDCAN_GlobalTypeDef; + +// --- Static fake peripherals --- +static RCC_TypeDef fakeRcc; +static FDCAN_GlobalTypeDef fakeFdcan; +static GPIO_TypeDef fakeTxGpio; +static GPIO_TypeDef fakeRxGpio; + +// Fake message RAM (848 bytes = 212 words per FDCAN instance) +static uint32_t fakeMessageRam[212]; + +// --- Override hardware macros to point at our fakes --- +#define RCC (&fakeRcc) +#define FDCAN1 (&fakeFdcan) +#define FDCAN1_BASE (reinterpret_cast(&fakeFdcan)) +#define SRAMCAN_BASE (reinterpret_cast(&fakeMessageRam[0])) +#define PERIPH_BASE 0x40000000UL +#define APB1PERIPH_BASE PERIPH_BASE + +// --- FDCAN register bit definitions (from stm32g474xx.h) --- +#define FDCAN_CCCR_INIT_Pos (0U) +#define FDCAN_CCCR_INIT (0x1UL << FDCAN_CCCR_INIT_Pos) +#define FDCAN_CCCR_CCE_Pos (1U) +#define FDCAN_CCCR_CCE (0x1UL << FDCAN_CCCR_CCE_Pos) +#define FDCAN_CCCR_FDOE_Pos (8U) +#define FDCAN_CCCR_FDOE (0x1UL << FDCAN_CCCR_FDOE_Pos) +#define FDCAN_CCCR_BRSE_Pos (9U) +#define FDCAN_CCCR_BRSE (0x1UL << FDCAN_CCCR_BRSE_Pos) + +#define FDCAN_NBTP_NTSEG2_Pos (0U) +#define FDCAN_NBTP_NTSEG1_Pos (8U) +#define FDCAN_NBTP_NBRP_Pos (16U) +#define FDCAN_NBTP_NSJW_Pos (25U) + +#define FDCAN_ECR_TEC_Pos (0U) +#define FDCAN_ECR_REC_Pos (8U) + +#define FDCAN_PSR_BO_Pos (7U) +#define FDCAN_PSR_BO (0x1UL << FDCAN_PSR_BO_Pos) + +#define FDCAN_IR_RF0N_Pos (0U) +#define FDCAN_IR_RF0N (0x1UL << FDCAN_IR_RF0N_Pos) +#define FDCAN_IR_RF0F_Pos (1U) +#define FDCAN_IR_RF0F (0x1UL << FDCAN_IR_RF0F_Pos) +#define FDCAN_IR_RF0L_Pos (2U) +#define FDCAN_IR_RF0L (0x1UL << FDCAN_IR_RF0L_Pos) +#define FDCAN_IR_TC_Pos (7U) +#define FDCAN_IR_TC (0x1UL << FDCAN_IR_TC_Pos) + +#define FDCAN_IE_RF0NE_Pos (0U) +#define FDCAN_IE_RF0NE (0x1UL << FDCAN_IE_RF0NE_Pos) +#define FDCAN_IE_TCE_Pos (7U) +#define FDCAN_IE_TCE (0x1UL << FDCAN_IE_TCE_Pos) +#define FDCAN_IE_TEFNE_Pos (12U) +#define FDCAN_IE_TEFNE (0x1UL << FDCAN_IE_TEFNE_Pos) + +#define FDCAN_IR_TEFN_Pos (12U) +#define FDCAN_IR_TEFN (0x1UL << FDCAN_IR_TEFN_Pos) + +#define FDCAN_TXEFS_EFFL_Pos (0U) +#define FDCAN_TXEFS_EFFL (0x7UL << FDCAN_TXEFS_EFFL_Pos) +#define FDCAN_TXEFS_EFGI_Pos (8U) +#define FDCAN_TXEFS_EFGI (0x3UL << FDCAN_TXEFS_EFGI_Pos) + +#define FDCAN_DBTP_DSJW_Pos (0U) +#define FDCAN_DBTP_DTSEG2_Pos (4U) +#define FDCAN_DBTP_DTSEG1_Pos (8U) +#define FDCAN_DBTP_DBRP_Pos (16U) +#define FDCAN_DBTP_TDC_Pos (23U) +#define FDCAN_DBTP_TDC (0x1UL << FDCAN_DBTP_TDC_Pos) + +#define FDCAN_TDCR_TDCO_Pos (8U) + +#define FDCAN_ILS_SMSG_Pos (2U) +#define FDCAN_ILS_SMSG (0x1UL << FDCAN_ILS_SMSG_Pos) + +#define FDCAN_ILE_EINT0_Pos (0U) +#define FDCAN_ILE_EINT0 (0x1UL << FDCAN_ILE_EINT0_Pos) +#define FDCAN_ILE_EINT1_Pos (1U) +#define FDCAN_ILE_EINT1 (0x1UL << FDCAN_ILE_EINT1_Pos) + +#define FDCAN_RXGFC_ANFE_Pos (2U) +#define FDCAN_RXGFC_ANFS_Pos (4U) +#define FDCAN_RXGFC_LSS_Pos (16U) +#define FDCAN_RXGFC_LSE_Pos (20U) + +#define FDCAN_RXF0S_F0FL_Pos (0U) +#define FDCAN_RXF0S_F0FL (0xFUL << FDCAN_RXF0S_F0FL_Pos) +#define FDCAN_RXF0S_F0GI_Pos (8U) +#define FDCAN_RXF0S_F0GI (0x3UL << FDCAN_RXF0S_F0GI_Pos) + +#define FDCAN_TXFQS_TFFL_Pos (0U) +#define FDCAN_TXFQS_TFFL (0x7UL << FDCAN_TXFQS_TFFL_Pos) +#define FDCAN_TXFQS_TFQPI_Pos (16U) +#define FDCAN_TXFQS_TFQPI (0x3UL << FDCAN_TXFQS_TFQPI_Pos) + +#define RCC_APB1ENR1_FDCANEN_Pos (25U) +#define RCC_APB1ENR1_FDCANEN (0x1UL << RCC_APB1ENR1_FDCANEN_Pos) + +// Prevent the real mcu.h from being included (it would pull in stm32g474xx.h) +#define MCU_MCU_H +#define MCU_TYPEDEFS_H + +// Provide the CANFrame include path directly +#include + +// Now include the driver header - it will see our faked types +#include + +// Include the implementation directly so it compiles with our fakes. +// The getInstanceRamBase() function compares against FDCAN1 which is now &fakeFdcan. +#include + +#include + +// Message RAM offset constants (must match FdCanDevice.cpp). +static constexpr uint32_t MSG_RAM_STD_FILTER_OFFSET = 0x000U; +static constexpr uint32_t MSG_RAM_RX_FIFO0_OFFSET = 0x0B0U; +static constexpr uint32_t MSG_RAM_TX_BUFFER_OFFSET = 0x278U; // STM32G4 specific + +class FdCanDeviceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Zero all fake peripherals and message RAM + memset(&fakeRcc, 0, sizeof(fakeRcc)); + memset(&fakeFdcan, 0, sizeof(fakeFdcan)); + memset(&fakeTxGpio, 0, sizeof(fakeTxGpio)); + memset(&fakeRxGpio, 0, sizeof(fakeRxGpio)); + memset(fakeMessageRam, 0, sizeof(fakeMessageRam)); + + // The CCCR.INIT bit: after setting INIT, the hardware immediately + // reflects it. In our fake we simulate this by pre-setting the bit + // in the write path (the driver busy-waits until INIT is set). + // We handle this by setting INIT in CCCR before construction so + // enterInitMode's while-loop terminates immediately. + } + + bios::FdCanDevice::Config makeDefaultConfig() + { + bios::FdCanDevice::Config cfg{}; + cfg.baseAddress = &fakeFdcan; + cfg.prescaler = 4U; // BRP = prescaler - 1 = 3 + cfg.nts1 = 13U; // NTSEG1 + cfg.nts2 = 2U; // NTSEG2 + cfg.nsjw = 1U; // NSJW + cfg.rxGpioPort = &fakeRxGpio; + cfg.rxPin = 11U; // PA11 (high pin, tests AFR[1] path) + cfg.rxAf = 9U; // AF9 + cfg.txGpioPort = &fakeTxGpio; + cfg.txPin = 5U; // PB5 (low pin, tests AFR[0] path) + cfg.txAf = 9U; // AF9 + return cfg; + } + + // Helper: simulate hardware behavior where INIT bit reflects immediately + void simulateInitBitReflection() + { + // The driver sets INIT, then busy-waits. Since our fake register + // reflects the write immediately (same memory), the loop exits. + // No extra action needed for stack-based volatile registers. + } + + // Helper: put device into initialized + started state + std::unique_ptr makeInitedDevice() + { + auto cfg = makeDefaultConfig(); + auto dev = std::make_unique(cfg); + dev->init(); + return dev; + } + + std::unique_ptr makeStartedDevice() + { + auto dev = makeInitedDevice(); + dev->start(); + return dev; + } + + // Helper: place a frame in RX FIFO0 message RAM at given index + // Element spacing is 72 bytes (18 words) - must match RX_ELEMENT_SIZE + // in FdCanDevice.cpp. + void + placeRxFrame(uint8_t fifoIndex, uint32_t canId, bool extended, uint8_t dlc, uint8_t const* data) + { + uint32_t* rxBuf = reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_RX_FIFO0_OFFSET + + (fifoIndex * 72U)); + + // Word 0: ID + if (extended) + { + rxBuf[0] = (canId & 0x1FFFFFFFU) | (1U << 30U); // XTD bit + } + else + { + rxBuf[0] = ((canId & 0x7FFU) << 18U); + } + + // Word 1: DLC + rxBuf[1] = (static_cast(dlc) << 16U); + + // Words 2-3: Data + if (data != nullptr) + { + rxBuf[2] = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + rxBuf[3] = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Helper: set RX FIFO0 fill level and get index + void setRxFifoStatus(uint8_t fillLevel, uint8_t getIndex) + { + fakeFdcan.RXF0S = (static_cast(fillLevel) << FDCAN_RXF0S_F0FL_Pos) + | (static_cast(getIndex) << FDCAN_RXF0S_F0GI_Pos); + } + + // Helper: set TX FIFO free level and put index + void setTxFifoStatus(uint8_t freeLevel, uint8_t putIndex) + { + fakeFdcan.TXFQS = (static_cast(freeLevel) << FDCAN_TXFQS_TFFL_Pos) + | (static_cast(putIndex) << FDCAN_TXFQS_TFQPI_Pos); + } + + // Helper: read TX buffer element from message RAM + // Element spacing is 72 bytes (18 words) - must match TX_ELEMENT_SIZE + // in FdCanDevice.cpp. + uint32_t const* getTxBuffer(uint8_t index) + { + return reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_TX_BUFFER_OFFSET + (index * 72U)); + } + + // Helper: read standard filter element from message RAM + uint32_t getStdFilter(uint8_t index) + { + uint32_t const* filterRam = reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_STD_FILTER_OFFSET); + return filterRam[index]; + } +}; + +TEST_F(FdCanDeviceTest, initEnablesPeripheralClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + + EXPECT_EQ(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initSelectsPclk1KernelClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // CCIPR[25:24] should be 10b = PCLK1 + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioTxAlternateFunction) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // TX pin = 5 (low register AFR[0]), AF9 + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); // Alternate function mode + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (cfg.txPin * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + + // Very high speed + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioRxAlternateFunctionHighPin) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // RX pin = 11 (high register AFR[1]), AF9 + uint32_t rxModer = (fakeRxGpio.MODER >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxModer, 2U); // Alternate function mode + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((cfg.rxPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + + // Pull-up for RX + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioRxLowPin) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 4U; // Low pin to test AFR[0] path + cfg.rxAf = 7U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (cfg.rxPin * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 7U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioTxHighPin) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 12U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((cfg.txPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initEntersInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // After init(), INIT bit should still be set (init mode stays until start()) + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + // CCE should be set (configuration change enabled) + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_CCE, 0U); +} + +TEST_F(FdCanDeviceTest, initDisablesFdAndBrs) +{ + auto cfg = makeDefaultConfig(); + // Pre-set FDOE and BRSE to verify init clears them + fakeFdcan.CCCR = FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE; + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_FDOE, 0U); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_BRSE, 0U); +} + +TEST_F(FdCanDeviceTest, initConfiguresBitTiming) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + uint32_t nsjw = (nbtp >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + uint32_t nbrp = (nbtp >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + uint32_t nts1 = (nbtp >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + uint32_t nts2 = (nbtp >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + + EXPECT_EQ(nsjw, cfg.nsjw); + EXPECT_EQ(nbrp, cfg.prescaler - 1U); // prescaler is encoded as (prescaler-1) + EXPECT_EQ(nts1, cfg.nts1); + EXPECT_EQ(nts2, cfg.nts2); +} + +TEST_F(FdCanDeviceTest, initConfiguresMessageRam) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // RXGFC should have LSS=0, LSE=0 initially (accept-all replaces it) + // TXBC should be 0 (FIFO mode) + EXPECT_EQ(fakeFdcan.TXBC, 0U); +} + +TEST_F(FdCanDeviceTest, initConfiguresAcceptAllFilter) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // ANFS=0 (accept non-matching std into FIFO0), ANFE=0 (accept non-matching ext into FIFO0) + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, initSetsInitializedFlag) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // Before init, start should do nothing (tested in startWithoutInitDoesNothing) + dev.init(); + // After init, start should work - verified indirectly via start() tests +} + +TEST_F(FdCanDeviceTest, doubleInitSafe) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // Second init should not crash + dev.init(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startEnablesRxFifoInterrupt) +{ + auto dev = makeInitedDevice(); + dev->start(); + // start() enables only RF0NE (RX FIFO 0 new element). TCE is managed + // per-TX by transmit(frame, true) and disabled by transmitISR(). + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, startSetsILS) +{ + auto dev = makeInitedDevice(); + dev->start(); + // All interrupts routed to line 0 (ILS=0) + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, startEnablesILE) +{ + auto dev = makeInitedDevice(); + dev->start(); + // Only EINT0 enabled (all interrupts on line 0) + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsTXBTIE) +{ + auto dev = makeInitedDevice(); + dev->start(); + // All 3 TX buffers enabled + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, startLeavesInitMode) +{ + auto dev = makeInitedDevice(); + dev->start(); + // INIT bit should be cleared + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startWithoutInitDoesNothing) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // Do NOT call init() + dev.start(); + // IE should remain 0 - start() returned early + EXPECT_EQ(fakeFdcan.IE, 0U); + EXPECT_EQ(fakeFdcan.ILS, 0U); + EXPECT_EQ(fakeFdcan.ILE, 0U); +} + +TEST_F(FdCanDeviceTest, stopDisablesInterrupts) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, stopEntersInitMode) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, transmitReturnsTrueOnSuccess) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); // 3 free slots, put index 0 + + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ::can::CANFrame frame(0x100, data, 8U); + + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitReturnsFalseWhenFifoFull) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); // 0 free slots + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 8U); + + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitSetsCorrectStandardId) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x123, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Standard ID: shifted left by 18, no XTD bit + EXPECT_EQ(txBuf[0], (0x123U << 18U)); +} + +TEST_F(FdCanDeviceTest, transmitSetsExtendedId) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + // Extended ID has bit 31 set (0x80000000) + uint32_t extId = 0x80000000U | 0x12345U; + ::can::CANFrame frame(extId, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Extended: raw 29-bit ID in bits [28:0], XTD bit at [30] + EXPECT_EQ(txBuf[0], (0x12345U) | (1U << 30U)); +} + +TEST_F(FdCanDeviceTest, transmitSetsCorrectDlc) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 5U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 5U); +} + +TEST_F(FdCanDeviceTest, transmitCopiesPayloadBytes) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Word 2: bytes 0-3 in little-endian order + EXPECT_EQ(txBuf[2], 0x04030201U); + // Word 3: bytes 4-7 + EXPECT_EQ(txBuf[3], 0x08070605U); +} + +TEST_F(FdCanDeviceTest, transmitSetsRequestBit) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 1U); // put index = 1 + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + // TXBAR bit 1 should be set + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, transmitUsesCorrectPutIndex) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(2U, 2U); // put index = 2 + + uint8_t data[8] = {0xAA}; + ::can::CANFrame frame(0x200, data, 1U); + dev->transmit(frame); + + // Data should be written at TX buffer index 2 + uint32_t const* txBuf = getTxBuffer(2); + EXPECT_EQ(txBuf[0], (0x200U << 18U)); + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, transmitMultipleFramesUseDifferentBuffers) +{ + auto dev = makeStartedDevice(); + + uint8_t data1[8] = {0x11}; + uint8_t data2[8] = {0x22}; + + // First frame at put index 0 + setTxFifoStatus(3U, 0U); + ::can::CANFrame frame1(0x100, data1, 1U); + dev->transmit(frame1); + + // Second frame at put index 1 + setTxFifoStatus(2U, 1U); + ::can::CANFrame frame2(0x200, data2, 1U); + dev->transmit(frame2); + + uint32_t const* buf0 = getTxBuffer(0); + uint32_t const* buf1 = getTxBuffer(1); + EXPECT_EQ(buf0[0], (0x100U << 18U)); + EXPECT_EQ(buf1[0], (0x200U << 18U)); +} + +TEST_F(FdCanDeviceTest, transmitSingleFrame) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(1U, 0U); + + uint8_t data[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + ::can::CANFrame frame(0x7FF, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + EXPECT_EQ(txBuf[0], (0x7FFU << 18U)); + EXPECT_EQ(static_cast((txBuf[1] >> 16U) & 0xFU), 8U); + EXPECT_EQ(txBuf[2], 0xEFBEADDEU); + EXPECT_EQ(txBuf[3], 0xBEBAFECAU); +} + +TEST_F(FdCanDeviceTest, transmitWithZeroPayload) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 0U); +} + +TEST_F(FdCanDeviceTest, transmitWithMaxPayload) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 8U); + EXPECT_EQ(txBuf[2], 0xFCFDFEFFU); + EXPECT_EQ(txBuf[3], 0xF8F9FAFBU); +} + +TEST_F(FdCanDeviceTest, receiveISRDrainsFifo) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsStandardId) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x321, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x321U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsExtendedId) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x1ABCDU, true, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + // Extended IDs have bit 31 set in the CANFrame representation + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x80000000U | 0x1ABCDU); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsDlc) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 5, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 5U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsPayloadBytes) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + uint8_t const* payload = dev->getRxFrame(0).getPayload(); + EXPECT_EQ(payload[0], 0xAAU); + EXPECT_EQ(payload[1], 0xBBU); + EXPECT_EQ(payload[2], 0xCCU); + EXPECT_EQ(payload[3], 0xDDU); + EXPECT_EQ(payload[4], 0xEEU); + EXPECT_EQ(payload[5], 0xFFU); + EXPECT_EQ(payload[6], 0x11U); + EXPECT_EQ(payload[7], 0x22U); +} + +TEST_F(FdCanDeviceTest, receiveISRAcknowledgesFrame) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + // RXF0A should have been written with the get index (0) + EXPECT_EQ(fakeFdcan.RXF0A, 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRReturnsFrameCount) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + // Place 2 frames (fill level = 2, both at index 0 since fake doesn't auto-advance) + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(2U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 2U); +} + +TEST_F(FdCanDeviceTest, receiveISRDropsWhenQueueFull) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + + // Fill the software RX queue to capacity (32 frames) + for (uint32_t i = 0U; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, static_cast(0x100 + i), false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Now try to receive one more - should be acknowledged but dropped + placeRxFrame(0, 0x999, false, 8, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, receiveISRWithNullFilterAcceptsAll) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x7FF, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, receiveISRWithFilterRejectsUnmatched) +{ + auto dev = makeStartedDevice(); + + // Create a filter bit field where only ID 0x100 is accepted + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + // Place a frame with ID 0x200 (not in filter) + uint8_t data[8] = {}; + placeRxFrame(0, 0x200, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRWithFilterAcceptsMatched) +{ + auto dev = makeStartedDevice(); + + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + uint8_t data[8] = {0x42}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); +} + +TEST_F(FdCanDeviceTest, receiveISRSnapshotsFillLevel) +{ + auto dev = makeStartedDevice(); + + // Set fill level to 2 - the ISR should drain exactly 2, not re-read F0FL + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(2U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 2U); +} + +TEST_F(FdCanDeviceTest, receiveISRClearsInterruptFlags) +{ + auto dev = makeStartedDevice(); + + // Pre-set interrupt flags + fakeFdcan.IR = FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L; + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + // IR should have RF0N|RF0F|RF0L written (write-1-to-clear) + // In our fake, the last write to IR is the clear value + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L); +} + +TEST_F(FdCanDeviceTest, receiveISRWithEmptyFifo) +{ + auto dev = makeStartedDevice(); + setRxFifoStatus(0U, 0U); // F0FL = 0 + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); +} + +TEST_F(FdCanDeviceTest, getRxCountReturnsZeroInitially) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxFrameReturnsCorrectFrame) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0xDE, 0xAD}; + placeRxFrame(0, 0x123, false, 2, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x123U); + EXPECT_EQ(frame.getPayloadLength(), 2U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[1], 0xADU); +} + +TEST_F(FdCanDeviceTest, getRxCountAfterReceive) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueResetsCount) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 1U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, rxQueueWrapsAround) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill queue to capacity + for (uint32_t i = 0U; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Clear and receive more - should wrap around + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Receive 5 more (these wrap into the beginning of the buffer) + for (uint32_t i = 0U; i < 5U; i++) + { + placeRxFrame(0, 0x500U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); + EXPECT_EQ(dev->getRxFrame(4).getId(), 0x504U); +} + +TEST_F(FdCanDeviceTest, rxQueueCapacity) { EXPECT_EQ(bios::FdCanDevice::RX_QUEUE_SIZE, 32U); } + +TEST_F(FdCanDeviceTest, disableRxInterruptClearsRF0NE) +{ + auto dev = makeStartedDevice(); + // After start, RF0NE should be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->disableRxInterrupt(); + // start() only enabled RF0NE, so IE is now empty + EXPECT_EQ(fakeFdcan.IE, 0U); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptSetsRF0NE) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->enableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRClearsTCFlag) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + // Only the TC flag is cleared (write-1-to-clear) + EXPECT_NE(fakeFdcan.IR & FDCAN_IR_TC, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRWithNoPendingTx) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + // Should not crash + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +// NOTE: transmitISR() does not drain the TX Event FIFO - it only disables TCE +// and clears the TC flag (matching the S32K FlexCAN transmitISR pattern). + +TEST_F(FdCanDeviceTest, transmitISRDoesNotDrainTxEventFifo) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + fakeFdcan.TXEFA = 0xDEADBEEFU; // sentinel - should NOT be written + + dev->transmitISR(); + + // TXEFA should be untouched (no drain) + EXPECT_EQ(fakeFdcan.TXEFA, 0xDEADBEEFU); + // TC flag still cleared + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, isBusOffReturnsTrueWhenBOSet) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = FDCAN_PSR_BO; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, isBusOffReturnsFalseNormally) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, getTxErrorCounterReadsECR) +{ + auto dev = makeStartedDevice(); + // TEC is in ECR[7:0] + fakeFdcan.ECR = 42U; + EXPECT_EQ(dev->getTxErrorCounter(), 42U); +} + +TEST_F(FdCanDeviceTest, getTxErrorCounterMaxValue) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0xFFU; // max TEC + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(FdCanDeviceTest, getRxErrorCounterReadsECR) +{ + auto dev = makeStartedDevice(); + // REC is in ECR[14:8] + fakeFdcan.ECR = (96U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 96U); +} + +TEST_F(FdCanDeviceTest, getRxErrorCounterMaxValue) +{ + auto dev = makeStartedDevice(); + // REC max is 127 (7 bits) + fakeFdcan.ECR = (0x7FU << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(FdCanDeviceTest, errorCountersBothReadable) +{ + auto dev = makeStartedDevice(); + // TEC = 100, REC = 50 + fakeFdcan.ECR = 100U | (50U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 100U); + EXPECT_EQ(dev->getRxErrorCounter(), 50U); +} + +TEST_F(FdCanDeviceTest, configureAcceptAllFilterSetsANFS0ANFE0) +{ + auto dev = makeInitedDevice(); + // Set non-zero first to verify it gets overwritten + fakeFdcan.RXGFC = 0xFFFFFFFFU; + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, configureFilterListRejectsNonMatching) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200}; + dev->configureFilterList(idList, 2U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 2U); // Reject non-matching standard + EXPECT_EQ(anfe, 2U); // Reject non-matching extended +} + +TEST_F(FdCanDeviceTest, configureFilterListSetsLSSCount) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200, 0x300}; + dev->configureFilterList(idList, 3U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 3U); +} + +TEST_F(FdCanDeviceTest, configureFilterListWritesFilterElements) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x2AB}; + dev->configureFilterList(idList, 2U); + + // Filter element format: SFT(2=classic)<<30 | SFEC(1)<<27 | SFID1<<16 | SFID2(mask) + uint32_t f0 = getStdFilter(0); + EXPECT_EQ(f0, (2U << 30U) | (1U << 27U) | (0x100U << 16U) | 0x7FFU); + + uint32_t f1 = getStdFilter(1); + EXPECT_EQ(f1, (2U << 30U) | (1U << 27U) | (0x2ABU << 16U) | 0x7FFU); +} + +TEST_F(FdCanDeviceTest, configureFilterListCapsAt28) +{ + auto dev = makeInitedDevice(); + + // Create 30 IDs (exceeds 28 max) + uint32_t idList[30]; + for (uint32_t i = 0; i < 30; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 30U); + + // Only 28 elements should be written; element 28 and 29 should be zero + uint32_t f27 = getStdFilter(27); + EXPECT_NE(f27, 0U); // element 27 should exist + + // Element at index 28 should NOT have been written (stays 0 from memset) + uint32_t f28 = getStdFilter(28); + EXPECT_EQ(f28, 0U); +} + +TEST_F(FdCanDeviceTest, configureFilterListAcceptsConfiguredIds) +{ + // This is more of an integration test: configure filter, then verify + // the RXGFC is set to reject non-matching but LSS covers our list + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200, 0x300, 0x400, 0x500}; + dev->configureFilterList(idList, 5U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 5U); + + // Verify each filter element + for (uint8_t i = 0; i < 5; i++) + { + uint32_t f = getStdFilter(i); + uint32_t sfid = (f >> 16U) & 0x7FFU; + EXPECT_EQ(sfid, idList[i]); + } +} + +TEST_F(FdCanDeviceTest, configureBitTimingWritesNBTP) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + cfg.nts1 = 63U; + cfg.nts2 = 15U; + cfg.nsjw = 7U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + EXPECT_NE(nbtp, 0U); +} + +TEST_F(FdCanDeviceTest, prescalerEncodedCorrectly) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 10U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 9U); // prescaler - 1 +} + +TEST_F(FdCanDeviceTest, tseg1Encoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 47U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 47U); +} + +TEST_F(FdCanDeviceTest, tseg2Encoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 5U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 5U); +} + +TEST_F(FdCanDeviceTest, sjwEncoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 3U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 3U); +} + +TEST_F(FdCanDeviceTest, bitTimingAllFieldsPackedCorrectly) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; // BRP=1 + cfg.nts1 = 10U; + cfg.nts2 = 3U; + cfg.nsjw = 2U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (2U << FDCAN_NBTP_NSJW_Pos) | (1U << FDCAN_NBTP_NBRP_Pos) // prescaler - 1 + | (10U << FDCAN_NBTP_NTSEG1_Pos) | (3U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, constructorZerosRxQueue) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, multipleReceiveCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x01}; + + // Receive 3 frames + for (uint32_t i = 0; i < 3; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 3U); + + // Verify all 3 frames + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x101U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x102U); + + // Clear and receive more + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + placeRxFrame(0, 0x200, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, stopThenRestartSequence) +{ + auto dev = makeStartedDevice(); + + // Stop + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + // Re-start + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, transmitAfterStopAndRestart) +{ + auto dev = makeStartedDevice(); + + dev->stop(); + dev->start(); + + setTxFifoStatus(3U, 0U); + uint8_t data[8] = {0x42}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, filterBitFieldEdgeCases) +{ + auto dev = makeStartedDevice(); + + // Test ID 0 (first bit of first byte) + uint8_t filter[256] = {}; + filter[0] = 0x01U; // Accept ID 0 + + uint8_t data[8] = {}; + placeRxFrame(0, 0, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, filterBitFieldHighId) +{ + auto dev = makeStartedDevice(); + + // Test ID 0x7FF (max standard ID) + uint8_t filter[256] = {}; + filter[0x7FF / 8U] |= (1U << (0x7FF % 8U)); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x7FF, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x7FFU); +} + +TEST_F(FdCanDeviceTest, rxQueueFullThenPartialDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + // Read some frames + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x11FU); + + // Clear all and verify empty + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, extendedIdFullRange) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + // Test with maximum extended ID + uint32_t maxExtId = 0x1FFFFFFFU; + placeRxFrame(0, maxExtId, true, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x80000000U | maxExtId); +} + +TEST_F(FdCanDeviceTest, transmitExtendedIdFullRange) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + uint32_t extId = 0x80000000U | 0x1FFFFFFFU; + ::can::CANFrame frame(extId, data, 0U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + EXPECT_EQ(txBuf[0], 0x1FFFFFFFU | (1U << 30U)); +} + +TEST_F(FdCanDeviceTest, disableEnableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); // double disable should be safe + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); // double enable should be safe + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, isBusOffWithOtherBitsSet) +{ + auto dev = makeStartedDevice(); + // Set BO along with other PSR bits - should still detect bus-off + fakeFdcan.PSR = 0xFFFFU; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, errorCountersWithBothFieldsNonZero) +{ + auto dev = makeStartedDevice(); + // TEC=200, REC=100 + fakeFdcan.ECR = 200U | (100U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 200U); + EXPECT_EQ(dev->getRxErrorCounter(), 100U); +} + +TEST_F(FdCanDeviceTest, errorCountersZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRMultipleFramesDifferentIds) +{ + auto dev = makeStartedDevice(); + + // Simulate 3 frames in FIFO (all at index 0 since our fake doesn't auto-advance) + uint8_t data1[8] = {0x01}; + uint8_t data2[8] = {0x02}; + uint8_t data3[8] = {0x03}; + + // First batch + placeRxFrame(0, 0x100, false, 1, data1); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + placeRxFrame(0, 0x200, false, 1, data2); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + placeRxFrame(0, 0x300, false, 1, data3); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0x01U); + EXPECT_EQ(dev->getRxFrame(1).getPayload()[0], 0x02U); + EXPECT_EQ(dev->getRxFrame(2).getPayload()[0], 0x03U); +} + +TEST_F(FdCanDeviceTest, configureFilterListEmptyList) +{ + auto dev = makeInitedDevice(); + + // Zero-length filter list + dev->configureFilterList(nullptr, 0U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); + + // ANFS and ANFE should both be 2 (reject) + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 2U); + EXPECT_EQ(anfe, 2U); +} + +TEST_F(FdCanDeviceTest, configureFilterListSingleId) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x7FF}; + dev->configureFilterList(idList, 1U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 1U); + + uint32_t f0 = getStdFilter(0); + EXPECT_EQ(f0, (2U << 30U) | (1U << 27U) | (0x7FFU << 16U) | 0x7FFU); +} + +TEST_F(FdCanDeviceTest, getRxFrameIndexWraps) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill half the queue + for (uint32_t i = 0; i < 16; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + // Clear (advances head to 16) + dev->clearRxQueue(); + + // Fill past the end of the array (head=16, fill 20 more -> wraps around) + for (uint32_t i = 0; i < 20; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 20U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(19).getId(), 0x213U); +} + +TEST_F(FdCanDeviceTest, initWithPrescaler1) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; // BRP = 0 + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 0U); +} + +TEST_F(FdCanDeviceTest, initWithPrescaler512) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; // BRP = 511 (max 9-bit field) + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 511U); +} + +TEST_F(FdCanDeviceTest, initWithMaxTseg1) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 255U; // max 8-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 255U); +} + +TEST_F(FdCanDeviceTest, initWithMaxTseg2) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 127U; // max 7-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 127U); +} + +TEST_F(FdCanDeviceTest, initWithMaxSjw) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 127U; // max 7-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 127U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin0) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 0U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + uint32_t txModer = (fakeTxGpio.MODER >> (0U * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin7) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 7U; + cfg.txAf = 11U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 11U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin8) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 8U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin15) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 15U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin0) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 0U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (0U * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin7) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 7U; + cfg.rxAf = 12U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 12U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin8) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 8U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin15) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 15U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(FdCanDeviceTest, initCCESetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // CCE must be set for configuration change + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_CCE, 0U); +} + +TEST_F(FdCanDeviceTest, doubleInitDoesNotBreakBitTiming) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 6U; + cfg.nts1 = 20U; + cfg.nts2 = 4U; + cfg.nsjw = 2U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtpFirst = fakeFdcan.NBTP; + dev.init(); + EXPECT_EQ(fakeFdcan.NBTP, nbtpFirst); +} + +TEST_F(FdCanDeviceTest, doubleInitPreservesGpioConfig) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txModerFirst = fakeTxGpio.MODER; + uint32_t rxModerFirst = fakeRxGpio.MODER; + dev.init(); + EXPECT_EQ(fakeTxGpio.MODER, txModerFirst); + EXPECT_EQ(fakeRxGpio.MODER, rxModerFirst); +} + +TEST_F(FdCanDeviceTest, initClockEnableBitPreserved) +{ + // Pre-set some other APB1ENR1 bits - init should not clear them + fakeRcc.APB1ENR1 = 0x00000001U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // FDCANEN should be set AND the pre-existing bit preserved + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); + EXPECT_NE(fakeRcc.APB1ENR1 & 0x00000001U, 0U); +} + +TEST_F(FdCanDeviceTest, initWithAllBitTimingZero) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.nts1 = 0U; + cfg.nts2 = 0U; + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + // NBTP should be 0 (prescaler-1=0, all others 0) + EXPECT_EQ(fakeFdcan.NBTP, 0U); +} + +TEST_F(FdCanDeviceTest, initTxGpioSpeedIsVeryHigh) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 3U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (3U * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); // Very high speed +} + +TEST_F(FdCanDeviceTest, initRxGpioPullUpEnabled) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 6U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (6U * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); // Pull-up +} + +TEST_F(FdCanDeviceTest, startWithoutInitLeavesINITUntouched) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // INIT starts at 0 (not initialized) + fakeFdcan.CCCR = 0U; + dev.start(); + // start() returned early - CCCR unchanged + EXPECT_EQ(fakeFdcan.CCCR, 0U); +} + +TEST_F(FdCanDeviceTest, startWithoutInitLeavesTXBTIEZero) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.start(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0U); +} + +TEST_F(FdCanDeviceTest, doubleStartDoesNotCrash) +{ + auto dev = makeInitedDevice(); + dev->start(); + dev->start(); // second start + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, doubleStartIEPreserved) +{ + auto dev = makeInitedDevice(); + dev->start(); + uint32_t ieFirst = fakeFdcan.IE; + dev->start(); + EXPECT_EQ(fakeFdcan.IE, ieFirst); +} + +TEST_F(FdCanDeviceTest, stopWithoutStartSafe) +{ + auto dev = makeInitedDevice(); + // init was called but not start - stop should not crash + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, stopClearsRF0NE) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, stopClearsTCE) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, stopThenStartIERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, stopThenStartTXBTIERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + dev->start(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, stopThenStartILERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + dev->start(); + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); +} + +TEST_F(FdCanDeviceTest, multipleStopStartCycles) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 5; i++) + { + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + } +} + +TEST_F(FdCanDeviceTest, startLeavesINITClearedEvenIfCCCRHasOtherBits) +{ + auto dev = makeInitedDevice(); + // Pre-set some bits in CCCR besides INIT + fakeFdcan.CCCR |= (1U << 4U); // some random bit + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsILSToZero) +{ + auto dev = makeInitedDevice(); + // Pre-set ILS to non-zero + fakeFdcan.ILS = 0xFFFFFFFFU; + dev->start(); + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsOnlyEINT0) +{ + auto dev = makeInitedDevice(); + dev->start(); + // Only EINT0 should be enabled, not EINT1 + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); + // ILE should be exactly EINT0 + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, stopDoesNotClearILE) +{ + auto dev = makeStartedDevice(); + dev->stop(); + // stop() only clears specific IE bits and enters init mode + // ILE is not explicitly cleared by stop() + // (verify the actual behavior) + // After stop, INIT should be set + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, stopDoesNotClearTXBTIE) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); + dev->stop(); + // stop() does not explicitly clear TXBTIE + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, startAfterStopClearsINIT) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, initDoesNotLeaveInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // After init, INIT bit should still be set + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startThenStopThenStartAgainIECorrect) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); + dev->stop(); + // stop() clears RF0NE and TCE + dev->start(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptAfterStartPreservesOtherIEBits) +{ + auto dev = makeStartedDevice(); + // Enable TCE manually so there is another IE bit to preserve + fakeFdcan.IE |= FDCAN_IE_TCE; + + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + // TCE should still be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); + + dev->enableRxInterrupt(); + // Both RF0NE and TCE should be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptPreservesTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE |= FDCAN_IE_TCE; + dev->disableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptMultipleTimes) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptMultipleTimes) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskRF0NE) +{ + auto dev = makeStartedDevice(); + // Verify RF0NE bit is at position 0 + EXPECT_EQ(FDCAN_IE_RF0NE, (1U << 0U)); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskTCE) +{ + // Verify TCE bit is at position 7 + EXPECT_EQ(FDCAN_IE_TCE, (1U << 7U)); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskTEFNE) +{ + // Verify TEFNE bit is at position 12 + EXPECT_EQ(FDCAN_IE_TEFNE, (1U << 12U)); +} + +TEST_F(FdCanDeviceTest, ILSZeroAfterStart) +{ + auto dev = makeStartedDevice(); + // All interrupts routed to line 0 + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, ILSPreSetIsOverwritten) +{ + auto dev = makeInitedDevice(); + fakeFdcan.ILS = 0xFFFFFFFFU; + dev->start(); + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, ILEOnlyEINT0Enabled) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, ILEPreSetIsOverwritten) +{ + auto dev = makeInitedDevice(); + fakeFdcan.ILE = FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1; + dev->start(); + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, TXBTIEAllThreeBuffers) +{ + auto dev = makeStartedDevice(); + // Bits 0, 1, 2 should all be set + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 0U), 0U); + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 1U), 0U); + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 2U), 0U); +} + +TEST_F(FdCanDeviceTest, TXBTIENoExtraBitsSet) +{ + auto dev = makeStartedDevice(); + // Only bits 0-2 should be set + EXPECT_EQ(fakeFdcan.TXBTIE & ~0x7U, 0U); +} + +TEST_F(FdCanDeviceTest, disableEnableRxInterruptCyclePreservesIE) +{ + auto dev = makeStartedDevice(); + uint32_t originalIE = fakeFdcan.IE; + + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + + EXPECT_EQ(fakeFdcan.IE, originalIE); +} + +TEST_F(FdCanDeviceTest, IEAfterStopHasRF0NECleared) +{ + auto dev = makeStartedDevice(); + dev->stop(); + // stop() clears RF0NE (and TCE) + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, ILSBitPositions) +{ + // SMSG is at position 2 + EXPECT_EQ(FDCAN_ILS_SMSG, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, ILEBitPositions) +{ + EXPECT_EQ(FDCAN_ILE_EINT0, (1U << 0U)); + EXPECT_EQ(FDCAN_ILE_EINT1, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptDoesNotSetTCE) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + // enableRxInterrupt only sets RF0NE, not TCE + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptDoesNotAffectILE) +{ + auto dev = makeStartedDevice(); + uint32_t ileBefore = fakeFdcan.ILE; + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.ILE, ileBefore); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptDoesNotAffectILS) +{ + auto dev = makeStartedDevice(); + uint32_t ilsBefore = fakeFdcan.ILS; + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.ILS, ilsBefore); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterSetsLSSZero) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterSetsLSEZero) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + uint32_t lse = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSE_Pos) & 0xFU; + EXPECT_EQ(lse, 0U); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterClearsRejectBits) +{ + auto dev = makeInitedDevice(); + fakeFdcan.RXGFC = 0xFFFFFFFFU; + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith1IdWritesOneElement) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x555}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + EXPECT_NE(f0, 0U); + // Element 1 should be 0 (only 1 configured) + uint32_t f1 = getStdFilter(1); + EXPECT_EQ(f1, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith28IdsAllWritten) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = i + 1U; + } + dev->configureFilterList(idList, 28U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 28U); + + // Verify all 28 elements are non-zero + for (uint8_t i = 0; i < 28; i++) + { + EXPECT_NE(getStdFilter(i), 0U); + } +} + +TEST_F(FdCanDeviceTest, filterListWith0IdsLSSIsZero) +{ + auto dev = makeInitedDevice(); + dev->configureFilterList(nullptr, 0U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); +} + +TEST_F(FdCanDeviceTest, filterListANFSRejectBit) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); +} + +TEST_F(FdCanDeviceTest, filterListANFERejectBit) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfe, 2U); +} + +TEST_F(FdCanDeviceTest, filterElementSFTIsClassicFilter) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sft = (f0 >> 30U) & 3U; + EXPECT_EQ(sft, 2U); // Classic filter (ID + mask) +} + +TEST_F(FdCanDeviceTest, filterElementSFECBitIsStoreInFIFO0) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfec = (f0 >> 27U) & 7U; + EXPECT_EQ(sfec, 1U); // Store in RX FIFO 0 +} + +TEST_F(FdCanDeviceTest, filterElementSFID1MatchesId) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x3AB}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x3ABU); +} + +TEST_F(FdCanDeviceTest, filterElementSFID2IsExactMatchMask) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x3AB}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid2 = f0 & 0x7FFU; + EXPECT_EQ(sfid2, 0x7FFU); // SFID2 is the mask (all 11 bits must match) +} + +TEST_F(FdCanDeviceTest, filterListIdMaskedTo11Bits) +{ + auto dev = makeInitedDevice(); + + // Pass an ID with bits above 10 set - should be masked to 11 bits + uint32_t idList[] = {0xFFF}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x7FFU); // Masked to 11 bits +} + +TEST_F(FdCanDeviceTest, filterListMultipleElementsCorrectOrder) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x001, 0x010, 0x100, 0x200, 0x7FF}; + dev->configureFilterList(idList, 5U); + + for (uint8_t i = 0; i < 5; i++) + { + uint32_t f = getStdFilter(i); + uint32_t sfid = (f >> 16U) & 0x7FFU; + EXPECT_EQ(sfid, idList[i]); + } +} + +TEST_F(FdCanDeviceTest, filterListOverwritesPreviousConfig) +{ + auto dev = makeInitedDevice(); + + uint32_t idList1[] = {0x100, 0x200, 0x300}; + dev->configureFilterList(idList1, 3U); + + uint32_t idList2[] = {0x400}; + dev->configureFilterList(idList2, 1U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x400U); +} + +TEST_F(FdCanDeviceTest, acceptAllThenFilterList) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); +} + +TEST_F(FdCanDeviceTest, filterListThenAcceptAll) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); + + dev->configureAcceptAllFilter(); + anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith5IdsHasCorrectLSS) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x001, 0x002, 0x003, 0x004, 0x005}; + dev->configureFilterList(idList, 5U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 5U); +} + +TEST_F(FdCanDeviceTest, filterListWith10IdsHasCorrectLSS) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[10]; + for (uint32_t i = 0; i < 10; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 10U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 10U); +} + +TEST_F(FdCanDeviceTest, filterListWith28IdsLSSIs28) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 28U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 28U); +} + +TEST_F(FdCanDeviceTest, filterListIdZero) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x000}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + uint32_t sfid2 = f0 & 0x7FFU; + EXPECT_EQ(sfid1, 0U); + EXPECT_EQ(sfid2, 0x7FFU); // SFID2 is the mask, not the ID +} + +TEST_F(FdCanDeviceTest, filterListIdMaxStandard) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x7FF}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x7FFU); +} + +TEST_F(FdCanDeviceTest, filterListElement27IsLast) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 28U); + + uint32_t f27 = getStdFilter(27); + uint32_t sfid1 = (f27 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x100U + 27U); +} + +TEST_F(FdCanDeviceTest, filterListRXGFCFullWord) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200}; + dev->configureFilterList(idList, 2U); + + uint32_t expected + = (2U << FDCAN_RXGFC_ANFS_Pos) | (2U << FDCAN_RXGFC_ANFE_Pos) | (2U << FDCAN_RXGFC_LSS_Pos); + EXPECT_EQ(fakeFdcan.RXGFC, expected); +} + +TEST_F(FdCanDeviceTest, busOffWithBOBitOnly) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = FDCAN_PSR_BO; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, notBusOffWithZeroPSR) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, notBusOffWithOtherBitsButNotBO) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0x0000007FU; // bits 0-6 set, but not bit 7 (BO) + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, busOffWithAllPSRBitsSet) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0xFFFFFFFFU; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, txErrorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, txErrorCounter128) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 128U; + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(FdCanDeviceTest, txErrorCounter255) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 255U; + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounter64) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = (64U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounter127) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = (127U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(FdCanDeviceTest, txErrorCounterNotAffectedByREC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U | (127U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounterNotAffectedByTEC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 255U | (0U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, bothErrorCountersIndependent) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 42U | (99U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 42U); + EXPECT_EQ(dev->getRxErrorCounter(), 99U); +} + +TEST_F(FdCanDeviceTest, PSRBOPositionCorrect) { EXPECT_EQ(FDCAN_PSR_BO, (1U << 7U)); } + +TEST_F(FdCanDeviceTest, ECRFieldPositionsCorrect) +{ + EXPECT_EQ(FDCAN_ECR_TEC_Pos, 0U); + EXPECT_EQ(FDCAN_ECR_REC_Pos, 8U); +} + +TEST_F(FdCanDeviceTest, getRxCountEmptyAfterConstruction) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueOnEmpty) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueDoubleClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueAfterPartialRead) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Add 5 frames + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + + // Read a frame (does not consume it) + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x102U); + + // Clear all + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxCountAfterMultipleReceives) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < 10; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 10U); +} + +TEST_F(FdCanDeviceTest, getRxFrameWrappingAtBoundary) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to exactly RX_QUEUE_SIZE + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Clear + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Receive 5 more - these wrap around + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(4).getId(), 0x204U); +} + +TEST_F(FdCanDeviceTest, queueHeadAdvancesOnClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Add 10 frames + for (uint32_t i = 0; i < 10; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Add 5 more - should start from new head position + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, multipleClearCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int cycle = 0; cycle < 10; cycle++) + { + placeRxFrame(0, static_cast(0x100 + cycle), false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), static_cast(0x100 + cycle)); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, getRxFrameIndex0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xAA}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); +} + +TEST_F(FdCanDeviceTest, getRxFrameLastIndex) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + EXPECT_EQ( + dev->getRxFrame(bios::FdCanDevice::RX_QUEUE_SIZE - 1).getId(), + 0x100U + bios::FdCanDevice::RX_QUEUE_SIZE - 1); +} + +TEST_F(FdCanDeviceTest, rxQueueDropsWhenFullAndContinuesAcknowledging) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + // Try one more - should return 0 (dropped) but still ack + placeRxFrame(0, 0x999, false, 1, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, clearThenFillToCapacityAgain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + dev->clearRxQueue(); + + // Fill again + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, getRxCountIncrementsByOne) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), i + 1U); + } +} + +TEST_F(FdCanDeviceTest, clearRxQueueAfterSingleFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxFrameAfterClearAndRefill) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // First fill + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + + // Clear + dev->clearRxQueue(); + + // Refill + placeRxFrame(0, 0x200, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, queueWrapsCorrectlyAt31To0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames, clear + for (uint32_t i = 0; i < 31; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Now head is at 31. Add 3 more - should wrap from 31 to 0, 1 + for (uint32_t i = 0; i < 3; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x201U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x202U); +} + +TEST_F(FdCanDeviceTest, rxQueueSizeIs32) { EXPECT_EQ(bios::FdCanDevice::RX_QUEUE_SIZE, 32U); } + +TEST_F(FdCanDeviceTest, getRxCountAfterExactlyFullQueue) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, receiveAfterClearRxQueueWorks) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + dev->clearRxQueue(); + + placeRxFrame(0, 0x200, false, 8, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueRepeatedlySafe) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 100; i++) + { + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, fillClearFillClearPattern) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int round = 0; round < 5; round++) + { + for (uint32_t i = 0; i < 8; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 8U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, getRxFramePayloadCorrectAfterWrap) +{ + auto dev = makeStartedDevice(); + + // Fill 30 frames and clear + uint8_t data[8] = {}; + for (uint32_t i = 0; i < 30; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Now add a frame with specific payload - wraps into beginning + uint8_t payload[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + placeRxFrame(0, 0x500, false, 8, payload); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x500U); + EXPECT_EQ(frame.getPayloadLength(), 8U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[7], 0xBEU); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel0ReturnsFalse) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel1ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(1U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel2ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(2U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel3ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex0) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 0U)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex1) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 1U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex2) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 2U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, txFifoFullThenFreeReturnsTrue) +{ + auto dev = makeStartedDevice(); + + // First: FIFO full + setTxFifoStatus(0U, 0U); + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); + + // Now: FIFO has space + setTxFifoStatus(1U, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFQSFieldPositions) +{ + EXPECT_EQ(FDCAN_TXFQS_TFFL_Pos, 0U); + EXPECT_EQ(FDCAN_TXFQS_TFQPI_Pos, 16U); +} + +TEST_F(FdCanDeviceTest, txFifoChecksOnlyTFFL) +{ + auto dev = makeStartedDevice(); + // TFFL = 0 but other fields non-zero + fakeFdcan.TXFQS = (2U << FDCAN_TXFQS_TFQPI_Pos); + // TFFL is 0, so transmit should fail + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitDoesNotModifyTXFQS) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + uint32_t txfqsBefore = fakeFdcan.TXFQS; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXFQS, txfqsBefore); +} + +TEST_F(FdCanDeviceTest, transmitSetsTXBAROnly) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + fakeFdcan.TXBAR = 0U; + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, 1U); +} + +TEST_F(FdCanDeviceTest, transmitTXBCIsZeroForFifoMode) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.TXBC, 0U); +} + +TEST_F(FdCanDeviceTest, txFifoSequentialPutIndices) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + + for (uint8_t i = 0; i < 3; i++) + { + setTxFifoStatus(3U - i, i); + dev->transmit(frame); + EXPECT_NE(fakeFdcan.TXBAR & (1U << i), 0U); + } +} + +TEST_F(FdCanDeviceTest, transmitWithFullFifoDoesNotWriteMessageRam) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); // Full + + // Zero out TXBAR + fakeFdcan.TXBAR = 0U; + + uint8_t data[8] = {0xFF}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_FALSE(dev->transmit(frame)); + + // TXBAR should not be written + EXPECT_EQ(fakeFdcan.TXBAR, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISREmptyFifoDoesNotWriteTXEFA) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + fakeFdcan.TXEFA = 0x12345678U; // Sentinel + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.TXEFA, 0x12345678U); +} + +TEST_F(FdCanDeviceTest, transmitISRMultipleCallsEmptyFifo) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 5; i++) + { + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); + } +} + +TEST_F(FdCanDeviceTest, transmitISRIRWriteIsTC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectIEWhenTCEClear) +{ + // transmitISR() clears TCE in IE; with TCE already clear, IE is unchanged + auto dev = makeStartedDevice(); + uint32_t ieBefore = fakeFdcan.IE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IE, ieBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectILE) +{ + auto dev = makeStartedDevice(); + uint32_t ileBefore = fakeFdcan.ILE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.ILE, ileBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectILS) +{ + auto dev = makeStartedDevice(); + uint32_t ilsBefore = fakeFdcan.ILS; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.ILS, ilsBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectCCCR) +{ + auto dev = makeStartedDevice(); + uint32_t cccrBefore = fakeFdcan.CCCR; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.CCCR, cccrBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectTXBTIE) +{ + auto dev = makeStartedDevice(); + uint32_t txbtieBefore = fakeFdcan.TXBTIE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.TXBTIE, txbtieBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRPreExistingIRBitsOverwritten) +{ + auto dev = makeStartedDevice(); + // Pre-set IR with some bits + fakeFdcan.IR = 0xFFFFFFFFU; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + // The last write to IR is the clear pattern + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRCalledBeforeAnyTransmit) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + // Should be safe even if no transmit was ever done + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRTEFNBitPosition) { EXPECT_EQ(FDCAN_IR_TEFN, (1U << 12U)); } + +TEST_F(FdCanDeviceTest, transmitISRTCBitPosition) { EXPECT_EQ(FDCAN_IR_TC, (1U << 7U)); } + +TEST_F(FdCanDeviceTest, transmitISRTXEFSFieldPositions) +{ + EXPECT_EQ(FDCAN_TXEFS_EFFL_Pos, 0U); + EXPECT_EQ(FDCAN_TXEFS_EFGI_Pos, 8U); +} + +TEST_F(FdCanDeviceTest, transmitISRSequentialCallsAllClearFlags) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + } + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, nbtpPrescaler1FieldValue0) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpPrescaler256FieldValue255) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 256U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 255U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg1Is0) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg1Is128) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 128U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 128U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg2Is0) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg2Is64) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 64U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 64U); +} + +TEST_F(FdCanDeviceTest, nbtpSjwIs0) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpSjwIs64) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 64U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 64U); +} + +TEST_F(FdCanDeviceTest, nbtpFieldPositions) +{ + EXPECT_EQ(FDCAN_NBTP_NTSEG2_Pos, 0U); + EXPECT_EQ(FDCAN_NBTP_NTSEG1_Pos, 8U); + EXPECT_EQ(FDCAN_NBTP_NBRP_Pos, 16U); + EXPECT_EQ(FDCAN_NBTP_NSJW_Pos, 25U); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation500kbps) +{ + // Typical 500kbps: prescaler=4, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 4U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (3U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation250kbps) +{ + // Typical 250kbps: prescaler=8, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (7U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation1Mbps) +{ + // Typical 1Mbps: prescaler=2, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (1U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpNoOverlapBetweenFields) +{ + // Set all fields to max and verify no bit overlap + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; // BRP = 511 = 0x1FF (9 bits at pos 16) + cfg.nts1 = 255U; // 8 bits at pos 8 + cfg.nts2 = 127U; // 7 bits at pos 0 + cfg.nsjw = 127U; // 7 bits at pos 25 + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + EXPECT_EQ((nbtp >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU, 127U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU, 255U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU, 511U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NSJW_Pos) & 0x7FU, 127U); +} + +TEST_F(FdCanDeviceTest, nbtpAllFieldsMinimal) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.nts1 = 0U; + cfg.nts2 = 0U; + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + EXPECT_EQ(fakeFdcan.NBTP, 0U); +} + +TEST_F(FdCanDeviceTest, clockEnableBitPosition) +{ + EXPECT_EQ(RCC_APB1ENR1_FDCANEN_Pos, 25U); + EXPECT_EQ(RCC_APB1ENR1_FDCANEN, (1U << 25U)); +} + +TEST_F(FdCanDeviceTest, initEnablesFDCANClockInAPB1ENR1) +{ + fakeRcc.APB1ENR1 = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initPreservesOtherAPB1ENR1Bits) +{ + fakeRcc.APB1ENR1 = 0x0000FFFFU; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // Original bits preserved + EXPECT_NE(fakeRcc.APB1ENR1 & 0x0000FFFFU, 0U); + // FDCANEN also set + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initSetsClockSelectPCLK1) +{ + fakeRcc.CCIPR = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); // PCLK1 +} + +TEST_F(FdCanDeviceTest, initClockSelectClearsOldValue) +{ + // Pre-set clock select to HSE (00) with other bits + fakeRcc.CCIPR = (3U << 24U); // was 11b + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); // Should be 10b = PCLK1 +} + +TEST_F(FdCanDeviceTest, initClockSelectPreservesOtherCCIPRBits) +{ + fakeRcc.CCIPR = 0x00FFFFFFU; // bits 0-23 set + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Lower 24 bits should still be set + EXPECT_EQ(fakeRcc.CCIPR & 0x00FFFFFFU, 0x00FFFFFFu); + // Clock select should be PCLK1 + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); +} + +TEST_F(FdCanDeviceTest, doubleInitDoesNotDoubleSetClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + uint32_t apb1After1 = fakeRcc.APB1ENR1; + dev.init(); + // OR-ing the same bit twice is idempotent + EXPECT_EQ(fakeRcc.APB1ENR1, apb1After1); +} + +TEST_F(FdCanDeviceTest, initClockSelectBits24And25) +{ + fakeRcc.CCIPR = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Bit 25 should be set, bit 24 should be clear (10b) + EXPECT_NE(fakeRcc.CCIPR & (1U << 25U), 0U); + EXPECT_EQ(fakeRcc.CCIPR & (1U << 24U), 0U); +} + +TEST_F(FdCanDeviceTest, initEnablesClockBeforeGPIO) +{ + // Verify clock is enabled (if clock wasn't enabled, GPIO writes would have no effect + // on real hardware - but we can verify the clock bit is set) + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initCCIPRUpperBitsUnaffected) +{ + fakeRcc.CCIPR = 0xFC000000U; // bits 26-31 set + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Bits 26-31 should still be set + EXPECT_EQ(fakeRcc.CCIPR & 0xFC000000U, 0xFC000000U); +} + +TEST_F(FdCanDeviceTest, transmitISRInvokesCallbackDelegate) +{ + bool callbackFired = false; + auto callback = ::etl::delegate::create([&callbackFired]() { callbackFired = true; }); + + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg, callback); + fakeFdcan.CCCR |= FDCAN_CCCR_INIT; + dev.init(); + dev.start(); + fakeFdcan.TXEFS = 0U; + + dev.transmitISR(); + EXPECT_TRUE(callbackFired); +} + +TEST_F(FdCanDeviceTest, transmitISRWithoutDelegateDoesNotCrash) +{ + // Construct without callback - existing API, should still work + auto dev = makeStartedDevice(); + fakeFdcan.TXEFS = 0U; + + dev->transmitISR(); // Must not crash + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitWithInterruptEnablesTCE) +{ + auto dev = makeStartedDevice(); + // Clear IE to known state (start() may have set it) + fakeFdcan.IE = FDCAN_IE_RF0NE; // Only RX enabled, no TCE + // TX FIFO has space + fakeFdcan.TXFQS = 0x3U; // TFFL=3 + + ::can::CANFrame frame(0x100U, nullptr, 0U); + bool ok = dev->transmit(frame, true); + EXPECT_TRUE(ok); + // TCE should now be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitWithoutInterruptDoesNotEnableTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE = FDCAN_IE_RF0NE; // Only RX, no TCE + fakeFdcan.TXFQS = 0x3U; + + ::can::CANFrame frame(0x100U, nullptr, 0U); + bool ok = dev->transmit(frame, false); + EXPECT_TRUE(ok); + // TCE should NOT be set + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRDisablesTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE = FDCAN_IE_RF0NE | FDCAN_IE_TCE; // Both enabled + fakeFdcan.TXEFS = 0U; + + dev->transmitISR(); + // TCE should be cleared after ISR (match S32K disableTransmitInterrupt) + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); + // RF0NE should still be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRKeepsRF0NEEnabled) +{ + auto dev = makeStartedDevice(); + // Enable RF0NE + fakeFdcan.IE = FDCAN_IE_RF0NE | FDCAN_IE_TCE; + // Put one frame in RX FIFO + fakeFdcan.RXF0S = (1U << FDCAN_RXF0S_F0FL_Pos); // F0FL=1 + fakeFdcan.IR = FDCAN_IR_RF0N; + + // Place a valid frame in message RAM at RX FIFO0 index 0 + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + placeRxFrame(0, 0x100, false, 8, data); + + dev->receiveISR(nullptr); + + // receiveISR does NOT touch IE - RF0NE disable (if needed) is the + // responsibility of the CanSystem ISR trampoline, because disabling + // here could permanently block RX if the dispatch is dedup-dropped. + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + // TCE should be unaffected + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} diff --git a/platforms/stm32/bsp/bspClock/CMakeLists.txt b/platforms/stm32/bsp/bspClock/CMakeLists.txt new file mode 100644 index 00000000000..ee439b596a5 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/CMakeLists.txt @@ -0,0 +1,10 @@ +# Clock configuration - chip-specific source selected by STM32_FAMILY +if (STM32_FAMILY STREQUAL "F4") + add_library(bspClock src/clockConfig_f4.cpp) +elseif (STM32_FAMILY STREQUAL "G4") + add_library(bspClock src/clockConfig_g4.cpp) +endif () + +target_include_directories(bspClock PUBLIC include) + +target_link_libraries(bspClock PRIVATE bspMcu) diff --git a/platforms/stm32/bsp/bspClock/doc/index.rst b/platforms/stm32/bsp/bspClock/doc/index.rst new file mode 100644 index 00000000000..6a423da77db --- /dev/null +++ b/platforms/stm32/bsp/bspClock/doc/index.rst @@ -0,0 +1,43 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspClock +======== + +Overview +-------- + +The ``bspClock`` module configures the system clock PLL for STM32F4 and STM32G4 +targets. Each family has its own translation unit (``clockConfig_f4.cpp``, +``clockConfig_g4.cpp``); the public API is a single ``extern "C"`` function +``configurePll()`` declared in ``clock/clockConfig.h``. It is called from the +startup code before ``main()`` and must not use heap, RTOS primitives, or BSW +services. On a PLL or oscillator timeout the system stays on HSI 16 MHz and +``SystemCoreClock`` is not updated. + +STM32F4 (STM32F413ZH) -- 96 MHz +------------------------------- + +- Clock source: HSE 8 MHz bypass (ST-LINK MCO on NUCLEO-F413ZH) +- PLL: M = 8, N = 384, P = 4 -> SYSCLK = 96 MHz +- AHB = 96 MHz, APB1 = 48 MHz (max 50 MHz per datasheet), APB2 = 96 MHz +- Flash latency: 3 wait states at 3.3 V, prefetch and caches enabled + +STM32G4 (STM32G474RE) -- 170 MHz +-------------------------------- + +- Clock source: HSI 16 MHz. HSE is not used because solder bridge SB15 may + not route the ST-LINK V3 MCO to PH0/OSC_IN on all NUCLEO-G474RE revisions. +- PLL: M = 4, N = 85, R = 2 -> SYSCLK = 170 MHz (voltage Range 1 boost mode) +- AHB = APB1 = APB2 = 170 MHz +- Flash latency: 4 wait states; ``FLASH_ACR`` is written read-modify-write to + preserve ``DBG_SWEN``. The switch above 150 MHz follows the AHB prescaler + transition sequence from RM0440 section 6.1.5. diff --git a/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h b/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h new file mode 100644 index 00000000000..2b7e0694111 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Implemented per target in clockConfig_*.cpp. Called from startup code +// before main(); must not use heap, RTOS primitives, or BSW services. +void configurePll(void); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/stm32/bsp/bspClock/module.spec b/platforms/stm32/bsp/bspClock/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspClock/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp b/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp new file mode 100644 index 00000000000..5c131eb55ac --- /dev/null +++ b/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +uint32_t SystemCoreClock = 16000000U; // Default HSI, updated by configurePll() + +// Clock stabilization timeout (~100 ms at 16 MHz HSI = 1.6M cycles). +static constexpr uint32_t CLK_TIMEOUT = 1600000U; + +extern "C" void configurePll() +{ + // Enable HSE bypass (8 MHz from ST-LINK) + RCC->CR |= RCC_CR_HSEBYP | RCC_CR_HSEON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_HSERDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Configure PLL: HSE / M=8 * N=384 / P=4 = 96 MHz + RCC->PLLCFGR = (8U << RCC_PLLCFGR_PLLM_Pos) // M = 8 -> VCO input = 1 MHz + | (384U << RCC_PLLCFGR_PLLN_Pos) // N = 384 -> VCO = 384 MHz + | (1U << RCC_PLLCFGR_PLLP_Pos) // P = 4 (register value 1) -> 96 MHz + | RCC_PLLCFGR_PLLSRC_HSE; + + RCC->CR |= RCC_CR_PLLON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_PLLRDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Flash latency 3 WS for 96 MHz @ 3.3V + FLASH->ACR = FLASH_ACR_LATENCY_3WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; + + // AHB = SYSCLK, APB1 = SYSCLK/2, APB2 = SYSCLK + RCC->CFGR = RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_SW_PLL; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + { + if (--t == 0U) + { + return; + } + } + } + + SystemCoreClock = 96000000U; +} diff --git a/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp b/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp new file mode 100644 index 00000000000..e3cd334e376 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp @@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +uint32_t SystemCoreClock = 16000000U; // Default HSI, updated by configurePll() + +// Clock stabilization timeout (~100 ms at 16 MHz HSI = 1.6M cycles). +// On timeout the system stays on HSI 16 MHz and SystemCoreClock is NOT +// updated, indicating to downstream code that PLL init failed. +static constexpr uint32_t CLK_TIMEOUT = 1600000U; + +extern "C" void configurePll() +{ + RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; + uint32_t volatile dummy = RCC->APB1ENR1; // Read-back for clock enable delay + (void)dummy; + + // Enable Range 1 boost mode (required for >150 MHz) + PWR->CR5 &= ~PWR_CR5_R1MODE; + // Wait for voltage scaling to stabilize + { + uint32_t t = CLK_TIMEOUT; + while ((PWR->SR2 & PWR_SR2_VOSF) != 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Configure PLL: HSI / M=4 * N=85 / R=2 = 170 MHz. + // HSI instead of HSE: NUCLEO-G474RE solder bridge SB15 may not route + // ST-LINK V3 MCO to PH0/OSC_IN on all board revisions. + RCC->PLLCFGR = (3U << RCC_PLLCFGR_PLLM_Pos) // M = 4 (register value M-1 = 3) + | (85U << RCC_PLLCFGR_PLLN_Pos) // N = 85 + | RCC_PLLCFGR_PLLSRC_HSI + | RCC_PLLCFGR_PLLREN; // Enable PLL R output (SYSCLK source) + + RCC->CR |= RCC_CR_PLLON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_PLLRDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Flash latency 4 WS for 170 MHz @ 3.3V boost mode + // Preserve DBG_SWEN (bit 18) - clearing it kills SWD debug flash access + { + uint32_t acr = FLASH->ACR; + acr &= ~FLASH_ACR_LATENCY_Msk; // Clear only latency bits, keep DBG_SWEN etc + acr |= FLASH_ACR_LATENCY_4WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; + FLASH->ACR = acr; + } + // Verify flash latency readback before switching clock (RM0440 section 3.3.3) + { + uint32_t t = CLK_TIMEOUT; + while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_4WS) + { + if (--t == 0U) + { + return; + } + } + } + + // Boost-mode >150 MHz transition: set AHB prescaler /2 before clock switch + RCC->CFGR = RCC_CFGR_HPRE_DIV2 | RCC_CFGR_SW_PLL; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + { + if (--t == 0U) + { + return; + } + } + } + + // Wait >= 1 us for voltage regulator to settle at new frequency + // At 170/2 = 85 MHz, 100 cycles > 1 us + for (uint32_t volatile i = 0U; i < 100U; i++) {} + + // Restore AHB prescaler to /1 + RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV1; + + SystemCoreClock = 170000000U; +} diff --git a/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt b/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt new file mode 100644 index 00000000000..4b70b48601c --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspEepromDriver src/eeprom/FlashEepromDriver.cpp) +target_include_directories(bspEepromDriver PUBLIC include) +target_link_libraries( + bspEepromDriver + PUBLIC bspMcu bsp + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h b/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h new file mode 100644 index 00000000000..65add360aeb --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace eeprom +{ + +// EEPROM emulation in internal flash using two pages in a ping-pong scheme: +// the active page holds current data behind a magic marker, the inactive +// page is erased and reprogrammed on each write, then the roles swap. +class FlashEepromDriver : public IEepromDriver +{ +public: + struct Config + { + uintptr_t page0Address; + uintptr_t page1Address; + uint32_t pageSize; + }; + + explicit FlashEepromDriver(Config const& config); + + bsp::BspReturnCode init() override; + bsp::BspReturnCode write(uint32_t address, uint8_t const* buffer, uint32_t length) override; + bsp::BspReturnCode read(uint32_t address, uint8_t* buffer, uint32_t length) override; + + bsp::BspReturnCode erase(); + +private: + static uint32_t const MAGIC = 0xEE50AA55U; // Page validity marker + + Config const fConfig; + uint8_t fActivePage; + + uintptr_t activePageAddress() const; + uintptr_t inactivePageAddress() const; + + bool isPageValid(uintptr_t pageAddr) const; + bsp::BspReturnCode erasePage(uintptr_t pageAddr); + bsp::BspReturnCode programPage(uintptr_t destAddr, uint8_t const* data, uint32_t length); + + bsp::BspReturnCode unlockFlash(); + void lockFlash(); + bsp::BspReturnCode waitForFlash(); +}; + +} // namespace eeprom diff --git a/platforms/stm32/bsp/bspEepromDriver/module.spec b/platforms/stm32/bsp/bspEepromDriver/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp b/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp new file mode 100644 index 00000000000..db96d3c9e45 --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp @@ -0,0 +1,303 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include +#include + +namespace eeprom +{ + +FlashEepromDriver::FlashEepromDriver(Config const& config) : fConfig(config), fActivePage(0U) {} + +bsp::BspReturnCode FlashEepromDriver::init() +{ + if (isPageValid(fConfig.page0Address)) + { + fActivePage = 0U; + } + else if (isPageValid(fConfig.page1Address)) + { + fActivePage = 1U; + } + else + { + // Neither page valid - format page 0 + if (erasePage(fConfig.page0Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + uint32_t const magic = MAGIC; + if (programPage(fConfig.page0Address, reinterpret_cast(&magic), 4U) + != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + lockFlash(); + fActivePage = 0U; + } + + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::read(uint32_t address, uint8_t* buffer, uint32_t length) +{ + uint32_t dataOffset = 4U; // Skip magic word + if ((address + length) > (fConfig.pageSize - dataOffset)) + { + return bsp::BSP_ERROR; + } + + uintptr_t srcAddr = activePageAddress() + dataOffset + address; + std::memcpy(buffer, reinterpret_cast(srcAddr), length); + return bsp::BSP_OK; +} + +bsp::BspReturnCode +FlashEepromDriver::write(uint32_t address, uint8_t const* buffer, uint32_t length) +{ + uint32_t dataOffset = 4U; + uint32_t dataSize = fConfig.pageSize - dataOffset; + + if ((address + length) > dataSize) + { + return bsp::BSP_ERROR; + } + + // Static buffer for page copy - avoids stack allocation in the safety task. + // Sized for G474RE (2KB pages). F413ZH uses 128KB sectors which are too + // large for this ping-pong scheme; use sector-level storage instead. + static uint8_t pageBuf[2048]; + if (dataSize > sizeof(pageBuf)) + { + return bsp::BSP_ERROR; + } + + std::memcpy(pageBuf, reinterpret_cast(activePageAddress() + dataOffset), dataSize); + std::memcpy(&pageBuf[address], buffer, length); + + uintptr_t inactiveAddr = inactivePageAddress(); + if (erasePage(inactiveAddr) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + uint32_t const magic = MAGIC; + if (programPage(inactiveAddr, reinterpret_cast(&magic), 4U) != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + + if (programPage(inactiveAddr + dataOffset, pageBuf, dataSize) != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + + lockFlash(); + + uintptr_t oldActiveAddr = activePageAddress(); + (void)erasePage(oldActiveAddr); + + fActivePage = (fActivePage == 0U) ? 1U : 0U; + + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::erase() +{ + if (erasePage(fConfig.page0Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + if (erasePage(fConfig.page1Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + return init(); +} + +uintptr_t FlashEepromDriver::activePageAddress() const +{ + return (fActivePage == 0U) ? fConfig.page0Address : fConfig.page1Address; +} + +uintptr_t FlashEepromDriver::inactivePageAddress() const +{ + return (fActivePage == 0U) ? fConfig.page1Address : fConfig.page0Address; +} + +bool FlashEepromDriver::isPageValid(uintptr_t pageAddr) const +{ + uint32_t const magic = *reinterpret_cast(pageAddr); + return magic == MAGIC; +} + +bsp::BspReturnCode FlashEepromDriver::unlockFlash() +{ + if ((FLASH->CR & FLASH_CR_LOCK) != 0U) + { + FLASH->KEYR = 0x45670123U; + FLASH->KEYR = 0xCDEF89ABU; + } + if ((FLASH->CR & FLASH_CR_LOCK) != 0U) + { + return bsp::BSP_ERROR; + } + return bsp::BSP_OK; +} + +void FlashEepromDriver::lockFlash() { FLASH->CR |= FLASH_CR_LOCK; } + +bsp::BspReturnCode FlashEepromDriver::waitForFlash() +{ + uint32_t timeout = 0xFFFFFFU; + while ((FLASH->SR & FLASH_SR_BSY) != 0U) + { + if (--timeout == 0U) + { + return bsp::BSP_ERROR; + } + } + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::erasePage(uintptr_t pageAddr) +{ + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (waitForFlash() != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + +#if defined(STM32G474xx) + // STM32G4: page erase + uint32_t pageNum = (pageAddr - 0x08000000U) / fConfig.pageSize; + FLASH->CR = (FLASH->CR & ~(FLASH_CR_PNB_Msk)) | (pageNum << FLASH_CR_PNB_Pos) | FLASH_CR_PER; + FLASH->CR |= FLASH_CR_STRT; +#elif defined(STM32F413xx) + // STM32F4: sector erase - determine sector from address + // Simplified: assume pageAddr maps directly to a sector number + // For sectors 12-15 (dual bank), SNB field encodes sector + bank + uint32_t sector = 0U; + if (pageAddr >= 0x08100000U) + { + sector = ((pageAddr - 0x08100000U) / 0x4000U) + 16U; // Bank 2 + } + else + { + sector = (pageAddr - 0x08000000U) / 0x20000U; // 128KB sectors (simplified) + } + FLASH->CR = (FLASH->CR & ~(FLASH_CR_SNB_Msk)) | (sector << FLASH_CR_SNB_Pos) | FLASH_CR_SER; + FLASH->CR |= FLASH_CR_STRT; +#endif + + bsp::BspReturnCode result = waitForFlash(); + +#if defined(STM32G474xx) + FLASH->CR &= ~(FLASH_CR_PER | FLASH_CR_STRT); +#elif defined(STM32F413xx) + FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_STRT); +#endif + + lockFlash(); + return result; +} + +bsp::BspReturnCode +FlashEepromDriver::programPage(uintptr_t destAddr, uint8_t const* data, uint32_t length) +{ + if (waitForFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + +#if defined(STM32G474xx) + // STM32G4 flash requires double-word (64-bit) programming + FLASH->CR |= FLASH_CR_PG; + + uint32_t i = 0U; + while (i < length) + { + uint32_t word0 = 0xFFFFFFFFU; + uint32_t word1 = 0xFFFFFFFFU; + + uint32_t remaining = length - i; + if (remaining > 0U) + { + std::memcpy(&word0, &data[i], (remaining >= 4U) ? 4U : remaining); + } + if (remaining > 4U) + { + uint32_t r2 = remaining - 4U; + std::memcpy(&word1, &data[i + 4U], (r2 >= 4U) ? 4U : r2); + } + + *reinterpret_cast(destAddr + i) = word0; + *reinterpret_cast(destAddr + i + 4U) = word1; + + if (waitForFlash() != bsp::BSP_OK) + { + FLASH->CR &= ~FLASH_CR_PG; + return bsp::BSP_ERROR; + } + + i += 8U; + } + + FLASH->CR &= ~FLASH_CR_PG; +#elif defined(STM32F413xx) + // STM32F4: word (32-bit) programming, PSIZE = x32 + FLASH->CR |= FLASH_CR_PG; + FLASH->CR = (FLASH->CR & ~FLASH_CR_PSIZE) | FLASH_CR_PSIZE_1; + + uint32_t i = 0U; + while (i < length) + { + uint32_t word = 0xFFFFFFFFU; + uint32_t remaining = length - i; + std::memcpy(&word, &data[i], (remaining >= 4U) ? 4U : remaining); + + *reinterpret_cast(destAddr + i) = word; + + if (waitForFlash() != bsp::BSP_OK) + { + FLASH->CR &= ~FLASH_CR_PG; + return bsp::BSP_ERROR; + } + + i += 4U; + } + + FLASH->CR &= ~FLASH_CR_PG; +#endif + + return bsp::BSP_OK; +} + +} // namespace eeprom diff --git a/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt b/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt new file mode 100644 index 00000000000..a2f0521d556 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt @@ -0,0 +1,18 @@ +if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") + set(bspInterruptsImplName "bspInterruptsImpl") +else () + set(bspInterruptsImplName "bspInterruptsImplUt") +endif () + +add_library(${bspInterruptsImplName} src/interrupt_manager.c) + +target_include_directories(${bspInterruptsImplName} PUBLIC include) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_include_directories(${bspInterruptsImplName} PUBLIC freertos/include) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_include_directories(${bspInterruptsImplName} PUBLIC threadx/include) + target_link_libraries(${bspInterruptsImplName} PUBLIC threadX) +endif () + +target_link_libraries(${bspInterruptsImplName} PRIVATE bspMcu platform) diff --git a/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst b/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst new file mode 100644 index 00000000000..023a6c52388 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst @@ -0,0 +1,111 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspInterruptsImpl +================= + +Overview +-------- + +The ``bspInterruptsImpl`` module provides interrupt management for STM32 +Cortex-M4 targets. It implements global interrupt disable/enable primitives and +integrates with FreeRTOS for context-safe critical sections. + +Global Interrupt Disable / Enable +--------------------------------- + +The header ``interrupts/disableEnableAllInterrupts.h`` provides two inline +assembly functions: + +``disableAllInterrupts()`` + Executes ``CPSID I`` to set the ``PRIMASK`` register, masking all + interrupts except NMI and HardFault. Followed by ``ISB``, ``DSB``, and + ``DMB`` barrier instructions to ensure the mask takes effect before any + subsequent memory access or instruction fetch. + +``enableAllInterrupts()`` + Executes ``ISB``, ``DSB``, and ``DMB`` barriers first, then ``CPSIE I`` to + clear ``PRIMASK`` and re-enable interrupts. The barrier-before-enable + ordering ensures all pending memory operations complete before interrupts + can fire. + +Both functions are declared ``static inline`` with +``__attribute__((always_inline))`` to guarantee zero-overhead inlining at every +call site. + +PRIMASK vs BASEPRI +------------------ + +The Cortex-M4 offers two mechanisms for interrupt masking: + +**PRIMASK (bare-metal variant)** + Setting PRIMASK to 1 disables all configurable interrupts (priority levels + 0--15). Only NMI (priority -2) and HardFault (priority -1) remain active. + This is the approach used by ``disableAllInterrupts()`` / + ``enableAllInterrupts()`` and is suitable for bare-metal critical sections + where no RTOS is running. + +**BASEPRI (FreeRTOS variant)** + FreeRTOS uses ``BASEPRI`` instead of ``PRIMASK`` for its critical sections. + Setting ``BASEPRI`` to ``configMAX_SYSCALL_INTERRUPT_PRIORITY`` masks only + interrupts at or below that priority threshold, leaving higher-priority + (lower numeric value) interrupts unmasked. This allows time-critical ISRs + (e.g., motor control, safety watchdog) to preempt FreeRTOS critical + sections. + + FreeRTOS-aware critical section macros (``taskENTER_CRITICAL`` / + ``taskEXIT_CRITICAL``) use this BASEPRI approach internally. The + ``bspInterruptsImpl`` module provides the ``SuspendResumeAllInterruptsScopedLock`` + RAII wrapper for C++ code that needs interrupt-safe sections compatible with + both variants. + +NVIC Priority Configuration +--------------------------- + +The STM32 Cortex-M4 implements 4-bit priority grouping (``__NVIC_PRIO_BITS = 4`` +from the CMSIS device header), yielding 16 priority levels (0 = highest, +15 = lowest). + +Priority assignment guidelines: + +- Priorities 0--3: Reserved for time-critical hardware ISRs that must not be + blocked by FreeRTOS critical sections (above + ``configMAX_SYSCALL_INTERRUPT_PRIORITY``). +- Priorities 4--15: Available for ISRs that may call FreeRTOS API functions + (``xSemaphoreGiveFromISR``, ``xQueueSendFromISR``, etc.). + +The FreeRTOS Cortex-M4 port layer uses the CMSIS ``__enable_irq()`` / +``__disable_irq()`` intrinsics directly for global interrupt control. + +Scoped Lock +----------- + +The ``SuspendResumeAllInterruptsScopedLock`` class (from the ``interrupts`` +namespace) provides RAII-style interrupt locking: + +.. code-block:: cpp + + { + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + // Critical section -- interrupts disabled + } + // Interrupts restored to previous state + +This pattern is used throughout the BSP (e.g., ``bspTimer`` tick accumulation) +to ensure interrupt state is always restored, even if an early return or +exception occurs. + +Dependencies +------------ + +- ARM Cortex-M4 ``PRIMASK`` and ``BASEPRI`` special registers +- CMSIS ``__enable_irq()`` / ``__disable_irq()`` intrinsics +- FreeRTOS ``configMAX_SYSCALL_INTERRUPT_PRIORITY`` (when FreeRTOS is active) diff --git a/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h new file mode 100644 index 00000000000..f07607f40c3 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "platform/estdint.h" + +typedef int32_t OldIntEnabledStatusValueType; +#define getOldIntEnabledStatusValueAndSuspendAllInterrupts \ + getMachineStateRegisterValueAndSuspendAllInterrupts + +// clang-format off +static inline __attribute__((always_inline)) +uint32_t getMachineStateRegisterValueAndSuspendAllInterrupts(void) +{ + uint32_t _PRIMASK; + __asm volatile (" mrs %0, PRIMASK\n" + " cpsid i\n" + : "=r" (_PRIMASK)); + return(_PRIMASK); +} +static inline __attribute__((always_inline)) +void resumeAllInterrupts(uint32_t oldMachineStateRegisterValue) +{ + __asm volatile (" msr PRIMASK,%[Input]\n" + ::[Input] "r" (oldMachineStateRegisterValue) + ); +} + +// clang-format on diff --git a/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h new file mode 100644 index 00000000000..3ebce505af9 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h @@ -0,0 +1,35 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +// clang-format off +static inline __attribute__((always_inline)) +void disableAllInterrupts(void) +{ +asm( +"cpsid i;" +"ISB;" +"DSB;" +"DMB;" +); +} +static inline __attribute__((always_inline)) +void enableAllInterrupts(void) +{ +asm ( +"ISB;" +"DSB;" +"DMB;" +"cpsie i;" +); +} + +// clang-format on diff --git a/platforms/stm32/bsp/bspInterruptsImpl/module.spec b/platforms/stm32/bsp/bspInterruptsImpl/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c b/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c new file mode 100644 index 00000000000..279d556a09d --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +// Placeholder - interrupt manager for STM32. +// NVIC priorities are configured by the application during board bring-up. diff --git a/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h new file mode 100644 index 00000000000..435f9d524c9 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +#include + +static inline __attribute__((always_inline)) void setThreadXInitialized(){}; + +typedef uint32_t OldIntEnabledStatusValueType; + +#define getMachineStateRegisterValueAndSuspendAllInterrupts \ + getOldIntEnabledStatusValueAndSuspendAllInterrupts + +static inline __attribute__((always_inline)) OldIntEnabledStatusValueType +getOldIntEnabledStatusValueAndSuspendAllInterrupts(void) +{ + return tx_interrupt_control(TX_INT_DISABLE); +} + +static inline __attribute__((always_inline)) void +resumeAllInterrupts(OldIntEnabledStatusValueType const oldIntEnabledStatusValue) +{ + tx_interrupt_control(oldIntEnabledStatusValue); +} diff --git a/platforms/stm32/bsp/bspIo/CMakeLists.txt b/platforms/stm32/bsp/bspIo/CMakeLists.txt new file mode 100644 index 00000000000..882c15e6af6 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspIo src/io/Gpio.cpp) +target_include_directories(bspIo PUBLIC include) +target_link_libraries( + bspIo + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspIo/include/io/Gpio.h b/platforms/stm32/bsp/bspIo/include/io/Gpio.h new file mode 100644 index 00000000000..245a2870500 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/include/io/Gpio.h @@ -0,0 +1,80 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace bios +{ + +// MODER register values +enum class GpioMode : uint8_t +{ + INPUT = 0U, + OUTPUT = 1U, + ALTERNATE = 2U, + ANALOG = 3U +}; + +// OTYPER register values +enum class GpioOutputType : uint8_t +{ + PUSH_PULL = 0U, + OPEN_DRAIN = 1U +}; + +// OSPEEDR register values +enum class GpioSpeed : uint8_t +{ + LOW = 0U, + MEDIUM = 1U, + HIGH = 2U, + VERY_HIGH = 3U +}; + +// PUPDR register values +enum class GpioPull : uint8_t +{ + NONE = 0U, + UP = 1U, + DOWN = 2U +}; + +struct GpioConfig +{ + GPIO_TypeDef* port; + uint8_t pin; // 0-15 + GpioMode mode; + GpioOutputType otype; + GpioSpeed speed; + GpioPull pull; + uint8_t af; // Alternate function number (0-15) +}; + +class Gpio +{ +public: + Gpio() = delete; + + static void enablePortClock(GPIO_TypeDef* port); + static void configure(GpioConfig const& cfg); + static void setMode(GPIO_TypeDef* port, uint8_t pin, GpioMode mode); + static void setOutputType(GPIO_TypeDef* port, uint8_t pin, GpioOutputType otype); + static void setSpeed(GPIO_TypeDef* port, uint8_t pin, GpioSpeed speed); + static void setPull(GPIO_TypeDef* port, uint8_t pin, GpioPull pull); + static void setAlternateFunction(GPIO_TypeDef* port, uint8_t pin, uint8_t af); + static bool readPin(GPIO_TypeDef* port, uint8_t pin); + static void writePin(GPIO_TypeDef* port, uint8_t pin, bool high); + static void togglePin(GPIO_TypeDef* port, uint8_t pin); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h b/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h new file mode 100644 index 00000000000..c540ba7e2e2 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h @@ -0,0 +1,77 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace bios +{ +namespace pins +{ + +#if defined(STM32F413xx) +// NUCLEO-F413ZH board pins + +// LD1 (green LED) +static constexpr GpioConfig LED1 + = {GPIOB, 0U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// LD2 (blue LED) +static constexpr GpioConfig LED2 + = {GPIOB, 7U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// LD3 (red LED) +static constexpr GpioConfig LED3 + = {GPIOB, 14U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// User button B1 (external pull-down on board) +static constexpr GpioConfig USER_BUTTON + = {GPIOC, 13U, GpioMode::INPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +static constexpr GpioConfig CAN1_RX = { + GPIOD, 0U, GpioMode::ALTERNATE, GpioOutputType::PUSH_PULL, GpioSpeed::HIGH, GpioPull::UP, 9U}; + +static constexpr GpioConfig CAN1_TX + = {GPIOD, + 1U, + GpioMode::ALTERNATE, + GpioOutputType::PUSH_PULL, + GpioSpeed::VERY_HIGH, + GpioPull::NONE, + 9U}; + +#elif defined(STM32G474xx) +// NUCLEO-G474RE board pins + +// LD2 (green LED) +static constexpr GpioConfig LED2 + = {GPIOA, 5U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// User button B1 (external pull-up on board) +static constexpr GpioConfig USER_BUTTON + = {GPIOC, 13U, GpioMode::INPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +static constexpr GpioConfig FDCAN1_RX = { + GPIOA, 11U, GpioMode::ALTERNATE, GpioOutputType::PUSH_PULL, GpioSpeed::HIGH, GpioPull::UP, 9U}; + +static constexpr GpioConfig FDCAN1_TX + = {GPIOA, + 12U, + GpioMode::ALTERNATE, + GpioOutputType::PUSH_PULL, + GpioSpeed::VERY_HIGH, + GpioPull::NONE, + 9U}; + +#endif + +} // namespace pins +} // namespace bios diff --git a/platforms/stm32/bsp/bspIo/module.spec b/platforms/stm32/bsp/bspIo/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspIo/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp b/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp new file mode 100644 index 00000000000..0c05aef10f2 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp @@ -0,0 +1,175 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +void Gpio::enablePortClock(GPIO_TypeDef* port) +{ +#if defined(STM32G474xx) + if (port == GPIOA) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + } + else if (port == GPIOB) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN; + } + else if (port == GPIOC) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN; + } +#if defined(GPIOD) + else if (port == GPIOD) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIODEN; + } +#endif +#if defined(GPIOE) + else if (port == GPIOE) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOEEN; + } +#endif +#if defined(GPIOF) + else if (port == GPIOF) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOFEN; + } +#endif +#if defined(GPIOG) + else if (port == GPIOG) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOGEN; + } +#endif +#elif defined(STM32F413xx) + if (port == GPIOA) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; + } + else if (port == GPIOB) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; + } + else if (port == GPIOC) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; + } + else if (port == GPIOD) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; + } +#if defined(GPIOE) + else if (port == GPIOE) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; + } +#endif +#if defined(GPIOF) + else if (port == GPIOF) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; + } +#endif +#if defined(GPIOG) + else if (port == GPIOG) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; + } +#endif +#if defined(GPIOH) + else if (port == GPIOH) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; + } +#endif +#endif + // Read-back for clock stabilization + uint32_t volatile dummy; + (void)dummy; +#if defined(STM32G474xx) + dummy = RCC->AHB2ENR; +#elif defined(STM32F413xx) + dummy = RCC->AHB1ENR; +#endif + (void)dummy; +} + +void Gpio::configure(GpioConfig const& cfg) +{ + enablePortClock(cfg.port); + setMode(cfg.port, cfg.pin, cfg.mode); + setOutputType(cfg.port, cfg.pin, cfg.otype); + setSpeed(cfg.port, cfg.pin, cfg.speed); + setPull(cfg.port, cfg.pin, cfg.pull); + if ((cfg.mode == GpioMode::ALTERNATE) && (cfg.af <= 15U)) + { + setAlternateFunction(cfg.port, cfg.pin, cfg.af); + } +} + +void Gpio::setMode(GPIO_TypeDef* port, uint8_t pin, GpioMode mode) +{ + uint32_t pos = pin * 2U; + port->MODER = (port->MODER & ~(3U << pos)) | (static_cast(mode) << pos); +} + +void Gpio::setOutputType(GPIO_TypeDef* port, uint8_t pin, GpioOutputType otype) +{ + if (otype == GpioOutputType::OPEN_DRAIN) + { + port->OTYPER |= (1U << pin); + } + else + { + port->OTYPER &= ~(1U << pin); + } +} + +void Gpio::setSpeed(GPIO_TypeDef* port, uint8_t pin, GpioSpeed speed) +{ + uint32_t pos = pin * 2U; + port->OSPEEDR = (port->OSPEEDR & ~(3U << pos)) | (static_cast(speed) << pos); +} + +void Gpio::setPull(GPIO_TypeDef* port, uint8_t pin, GpioPull pull) +{ + uint32_t pos = pin * 2U; + port->PUPDR = (port->PUPDR & ~(3U << pos)) | (static_cast(pull) << pos); +} + +void Gpio::setAlternateFunction(GPIO_TypeDef* port, uint8_t pin, uint8_t af) +{ + uint8_t afrIdx = (pin < 8U) ? 0U : 1U; + uint8_t afrPin = (pin < 8U) ? pin : (pin - 8U); + uint32_t pos = afrPin * 4U; + port->AFR[afrIdx] = (port->AFR[afrIdx] & ~(0xFU << pos)) | (static_cast(af) << pos); +} + +bool Gpio::readPin(GPIO_TypeDef* port, uint8_t pin) { return (port->IDR & (1U << pin)) != 0U; } + +void Gpio::writePin(GPIO_TypeDef* port, uint8_t pin, bool high) +{ + if (high) + { + port->BSRR = (1U << pin); + } + else + { + port->BSRR = (1U << (pin + 16U)); + } +} + +void Gpio::togglePin(GPIO_TypeDef* port, uint8_t pin) { port->ODR ^= (1U << pin); } + +} // namespace bios diff --git a/platforms/stm32/bsp/bspMcu/CMakeLists.txt b/platforms/stm32/bsp/bspMcu/CMakeLists.txt new file mode 100644 index 00000000000..f931f420073 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/CMakeLists.txt @@ -0,0 +1,18 @@ +enable_language(ASM) +add_library(bspMcu src/reset/softwareSystemReset.cpp ${STM32_STARTUP_ASM}) + +target_include_directories( + bspMcu PUBLIC include ${CMAKE_SOURCE_DIR}/libs/3rdparty/cmsis + ${CMAKE_SOURCE_DIR}/libs/3rdparty/cmsis/m-profile) + +if (STM32_FAMILY STREQUAL "F4") + target_include_directories(bspMcu PUBLIC include/3rdparty/st/stm32f4) +elseif (STM32_FAMILY STREQUAL "G4") + target_include_directories(bspMcu PUBLIC include/3rdparty/st/stm32g4) +endif () + +target_compile_definitions( + bspMcu PUBLIC ${STM32_DEVICE_UPPER} STM32_FAMILY_${STM32_FAMILY} + CAN_TYPE_${CAN_TYPE}) + +target_link_libraries(bspMcu PUBLIC platform) diff --git a/platforms/stm32/bsp/bspMcu/doc/index.rst b/platforms/stm32/bsp/bspMcu/doc/index.rst new file mode 100644 index 00000000000..e0ee61f3bf1 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/doc/index.rst @@ -0,0 +1,22 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspMcu +====== + +Overview +-------- + +The module ``bspMcu`` provides the MCU API for STM32F413xx and STM32G474xx. +``mcu/mcu.h`` selects the matching ST CMSIS device header from +``include/3rdparty/st`` and reuses the shared CMSIS core headers from +``libs/3rdparty/cmsis``. The module also contains the chip startup files and +a function for software system reset. diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE new file mode 100644 index 00000000000..7a4a3ea2424 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo new file mode 100644 index 00000000000..25fdf37d82b --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo @@ -0,0 +1,15 @@ +efaecd478788406b1d98ba8e37809c6ff9957ebe + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/STMicroelectronics/cmsis_device_f4.git +revision_sha1 : b364e5ad14523994ebb30f9e854a7c28537b0c28 +target_revision: v2.6.11 +ignores : stm32f401xc.h,stm32f401xe.h,stm32f405xx.h,stm32f407xx.h,stm32f410cx.h,stm32f410rx.h,stm32f410tx.h,stm32f411xe.h,stm32f412cx.h,stm32f412rx.h,stm32f412vx.h,stm32f412zx.h,stm32f415xx.h,stm32f417xx.h,stm32f423xx.h,stm32f427xx.h,stm32f429xx.h,stm32f437xx.h,stm32f439xx.h,stm32f446xx.h,stm32f469xx.h,stm32f479xx.h +checksum : d95d4934b47fee01f36cd076c1bb8af7ce8a228e +subdir : Include + +committer_date : 2025-05-20 15:35:16 +0100 + diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h new file mode 100644 index 00000000000..7a5ed4e7cef --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h @@ -0,0 +1,15466 @@ +/** + ****************************************************************************** + * @file stm32f413xx.h + * @author MCD Application Team + * @brief CMSIS STM32F413xx Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - peripherals registers declarations and bits definition + * - Macros to access peripheral's registers hardware + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f413xx + * @{ + */ + +#ifndef __STM32F413xx_H +#define __STM32F413xx_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F4XX provides an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F4XX uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< FPU present */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F4XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ + /****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ + /****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + DFSDM1_FLT0_IRQn = 61, /*!< DFSDM1 Filter 0 global Interrupt */ + DFSDM1_FLT1_IRQn = 62, /*!< DFSDM1 Filter 1 global Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + CAN3_TX_IRQn = 74, /*!< CAN3 TX Interrupt */ + CAN3_RX0_IRQn = 75, /*!< CAN3 RX0 Interrupt */ + CAN3_RX1_IRQn = 76, /*!< CAN3 RX1 Interrupt */ + CAN3_SCE_IRQn = 77, /*!< CAN3 SCE Interrupt */ + RNG_IRQn = 80, /*!< RNG global Interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + UART9_IRQn = 88, /*!< UART9 global Interrupt */ + UART10_IRQn = 89, /*!< UART10 global Interrupt */ + QUADSPI_IRQn = 92, /*!< QuadSPI global Interrupt */ + FMPI2C1_EV_IRQn = 95, /*!< FMPI2C1 Event Interrupt */ + FMPI2C1_ER_IRQn = 96, /*!< FMPI2C1 Error Interrupt */ + LPTIM1_IRQn = 97, /*!< LP TIM1 interrupt */ + DFSDM2_FLT0_IRQn = 98, /*!< DFSDM2 Filter 0 global Interrupt */ + DFSDM2_FLT1_IRQn = 99, /*!< DFSDM2 Filter 1 global Interrupt */ + DFSDM2_FLT2_IRQn = 100, /*!< DFSDM2 Filter 2 global Interrupt */ + DFSDM2_FLT3_IRQn = 101 /*!< DFSDM2 Filter 3 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f4xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ +} CRC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +} DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1, Address offset: 0x18 */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FSMC_Bank1E_TypeDef; +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED; /*!< Reserved, 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG Configuration register2, Address offset: 0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x24-0x28 */ + __IO uint32_t CFGR; /*!< SYSCFG Configuration register, Address offset: 0x2C */ + __IO uint32_t MCHDLYCR; /*!< SYSCFG multi-channel delay register, Address offset: 0x30 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */ + __IO uint32_t DR; /*!< I2C Data register, Address offset: 0x10 */ + __IO uint32_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */ + __IO uint32_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */ + __IO uint32_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */ + __IO uint32_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */ + __IO uint32_t FLTR; /*!< I2C FLTR register, Address offset: 0x24 */ +} I2C_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< FMPI2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< FMPI2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< FMPI2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< FMPI2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< FMPI2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< FMPI2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< FMPI2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< FMPI2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< FMPI2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< FMPI2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< FMPI2C Transmit data register, Address offset: 0x28 */ +} FMPI2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ +} IWDG_TypeDef; + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PWR power control register, Address offset: 0x00 */ + __IO uint32_t CSR; /*!< PWR power control/status register, Address offset: 0x04 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + uint32_t RESERVED7; /*!< Reserved, 0x84 */ + __IO uint32_t DCKCFGR; /*!< RCC Dedicated Clocks configuration register, Address offset: 0x8C */ + __IO uint32_t CKGATENR; /*!< RCC Clocks Gated ENable Register, Address offset: 0x90 */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x94 */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CALIBR; /*!< RTC calibration register, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAFCR; /*!< RTC tamper and alternate function configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR;/*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR;/*!< RTC alarm B sub second register, Address offset: 0x48 */ + uint32_t RESERVED7; /*!< Reserved, 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 1, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDIO power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDI clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDIO argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDIO command register, Address offset: 0x0C */ + __IO const uint32_t RESPCMD; /*!< SDIO command response register, Address offset: 0x10 */ + __IO const uint32_t RESP1; /*!< SDIO response 1 register, Address offset: 0x14 */ + __IO const uint32_t RESP2; /*!< SDIO response 2 register, Address offset: 0x18 */ + __IO const uint32_t RESP3; /*!< SDIO response 3 register, Address offset: 0x1C */ + __IO const uint32_t RESP4; /*!< SDIO response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDIO data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDIO data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDIO data control register, Address offset: 0x2C */ + __IO const uint32_t DCOUNT; /*!< SDIO data counter register, Address offset: 0x30 */ + __IO const uint32_t STA; /*!< SDIO status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDIO interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDIO mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __IO const uint32_t FIFOCNT; /*!< SDIO FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDIO data FIFO register, Address offset: 0x80 */ +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; + +/** + * @brief LPTIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define FLASH_BASE 0x08000000UL /*!< FLASH (up to 1.5 MB) base address in the alias region */ +#define SRAM1_BASE 0x20000000UL /*!< SRAM1(256 KB) base address in the alias region */ +#define SRAM2_BASE 0x20040000UL /*!< SRAM2(64 KB) base address in the alias region */ +#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */ +#define FSMC_R_BASE 0xA0000000UL /*!< FSMC registers base address */ +#define QSPI_R_BASE 0xA0001000UL /*!< QuadSPI registers base address */ +#define SRAM1_BB_BASE 0x22000000UL /*!< SRAM1(256 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE 0x22800000UL /*!< SRAM2(64 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE 0x42000000UL /*!< Peripheral base address in the bit-band region */ +#define FLASH_END 0x0817FFFFUL /*!< FLASH end address */ +#define FLASH_OTP_BASE 0x1FFF7800UL /*!< Base address of : (up to 528 Bytes) embedded FLASH OTP Area */ +#define FLASH_OTP_END 0x1FFF7A0FUL /*!< End address of : (up to 528 Bytes) embedded FLASH OTP Area */ + +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define I2S2ext_BASE (APB1PERIPH_BASE + 0x3400UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define I2S3ext_BASE (APB1PERIPH_BASE + 0x4000UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00UL) +#define FMPI2C1_BASE (APB1PERIPH_BASE + 0x6000UL) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800UL) +#define CAN3_BASE (APB1PERIPH_BASE + 0x6C00UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00UL) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400UL) +#define UART9_BASE (APB2PERIPH_BASE + 0x1800UL) +#define UART10_BASE (APB2PERIPH_BASE + 0x1C00UL) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000UL) +#define ADC1_COMMON_BASE (APB2PERIPH_BASE + 0x2300UL) +/* Legacy define */ +#define ADC_BASE ADC1_COMMON_BASE +#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400UL) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800UL) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000UL) +#define DFSDM1_BASE (APB2PERIPH_BASE + 0x6000UL) +#define DFSDM2_BASE (APB2PERIPH_BASE + 0x6400UL) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00UL) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20UL) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40UL) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60UL) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100UL) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180UL) +#define DFSDM2_Channel0_BASE (DFSDM2_BASE + 0x00UL) +#define DFSDM2_Channel1_BASE (DFSDM2_BASE + 0x20UL) +#define DFSDM2_Channel2_BASE (DFSDM2_BASE + 0x40UL) +#define DFSDM2_Channel3_BASE (DFSDM2_BASE + 0x60UL) +#define DFSDM2_Channel4_BASE (DFSDM2_BASE + 0x80UL) +#define DFSDM2_Channel5_BASE (DFSDM2_BASE + 0xA0UL) +#define DFSDM2_Channel6_BASE (DFSDM2_BASE + 0xC0UL) +#define DFSDM2_Channel7_BASE (DFSDM2_BASE + 0xE0UL) +#define DFSDM2_Filter0_BASE (DFSDM2_BASE + 0x100UL) +#define DFSDM2_Filter1_BASE (DFSDM2_BASE + 0x180UL) +#define DFSDM2_Filter2_BASE (DFSDM2_BASE + 0x200UL) +#define DFSDM2_Filter3_BASE (DFSDM2_BASE + 0x280UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) + +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00UL) +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000UL) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400UL) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) + +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800UL) + + +/*!< FSMC Bankx registers base address */ +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000UL) +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104UL) + +/*!< Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000UL +/*!< USB registers base address */ +#define USB_OTG_FS_PERIPH_BASE 0x50000000UL + +#define USB_OTG_GLOBAL_BASE 0x000UL +#define USB_OTG_DEVICE_BASE 0x800UL +#define USB_OTG_IN_ENDPOINT_BASE 0x900UL +#define USB_OTG_OUT_ENDPOINT_BASE 0xB00UL +#define USB_OTG_EP_REG_SIZE 0x20UL +#define USB_OTG_HOST_BASE 0x400UL +#define USB_OTG_HOST_PORT_BASE 0x440UL +#define USB_OTG_HOST_CHANNEL_BASE 0x500UL +#define USB_OTG_HOST_CHANNEL_SIZE 0x20UL +#define USB_OTG_PCGCCTL_BASE 0xE00UL +#define USB_OTG_FIFO_BASE 0x1000UL +#define USB_OTG_FIFO_SIZE 0x1000UL + +#define UID_BASE 0x1FFF7A10UL /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FFF7A22UL /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FFF7BF0UL /*!< Package size register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define I2S2ext ((SPI_TypeDef *) I2S2ext_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define I2S3ext ((SPI_TypeDef *) I2S3ext_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define FMPI2C1 ((FMPI2C_TypeDef *) FMPI2C1_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define CAN3 ((CAN_TypeDef *) CAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define UART10 ((USART_TypeDef *) UART10_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC1_COMMON ((ADC_Common_TypeDef *) ADC1_COMMON_BASE) +/* Legacy define */ +#define ADC ADC1_COMMON +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM2_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel0_BASE) +#define DFSDM2_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel1_BASE) +#define DFSDM2_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel2_BASE) +#define DFSDM2_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel3_BASE) +#define DFSDM2_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel4_BASE) +#define DFSDM2_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel5_BASE) +#define DFSDM2_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel6_BASE) +#define DFSDM2_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel7_BASE) +#define DFSDM2_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter0_BASE) +#define DFSDM2_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter1_BASE) +#define DFSDM2_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter2_BASE) +#define DFSDM2_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter3_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + +/** @addtogroup Hardware_Constant_Definition + * @{ + */ +#define LSI_STARTUP_TIME 40U /*!< LSI Maximum startup time in us */ +/** + * @} + */ + +/** @addtogroup Peripheral_Registers_Bits_Definition +* @{ +*/ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ + +/******************** Bit definition for ADC_SR register ********************/ +#define ADC_SR_AWD_Pos (0U) +#define ADC_SR_AWD_Msk (0x1UL << ADC_SR_AWD_Pos) /*!< 0x00000001 */ +#define ADC_SR_AWD ADC_SR_AWD_Msk /*! + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC interrupt and status register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC interrupt enable register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC configuration register 1, Address offset: 0x0C */ + __IO uint32_t CFGR2; /*!< ADC configuration register 2, Address offset: 0x10 */ + __IO uint32_t SMPR1; /*!< ADC sampling time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sampling time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x1C */ + __IO uint32_t TR1; /*!< ADC analog watchdog 1 threshold register, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC analog watchdog 2 threshold register, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC analog watchdog 3 threshold register, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x2C */ + __IO uint32_t SQR1; /*!< ADC group regular sequencer register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC group regular sequencer register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC group regular sequencer register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC group regular sequencer register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC group regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x44 */ + uint32_t RESERVED4; /*!< Reserved, 0x48 */ + __IO uint32_t JSQR; /*!< ADC group injected sequencer register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x50 - 0x5C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x70 - 0x7C */ + __IO uint32_t JDR1; /*!< ADC group injected rank 1 data register, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC group injected rank 2 data register, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC group injected rank 3 data register, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC group injected rank 4 data register, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC analog watchdog 2 configuration register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC analog watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC differential mode selection register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC calibration factors, Address offset: 0xB4 */ + uint32_t RESERVED10[2];/*!< Reserved, 0x0B8 - 0x0BC */ + __IO uint32_t GCOMP; /*!< ADC calibration factors, Address offset: 0xC0 */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC common status register, Address offset: 0x300 + 0x00 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x300 + 0x04 */ + __IO uint32_t CCR; /*!< ADC common configuration register, Address offset: 0x300 + 0x08 */ + __IO uint32_t CDR; /*!< ADC common group regular data register Address offset: 0x300 + 0x0C */ +} ADC_Common_TypeDef; + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< FDCAN Core Release register, Address offset: 0x000 */ + __IO uint32_t ENDN; /*!< FDCAN Endian register, Address offset: 0x004 */ + uint32_t RESERVED1; /*!< Reserved, 0x008 */ + __IO uint32_t DBTP; /*!< FDCAN Data Bit Timing & Prescaler register, Address offset: 0x00C */ + __IO uint32_t TEST; /*!< FDCAN Test register, Address offset: 0x010 */ + __IO uint32_t RWD; /*!< FDCAN RAM Watchdog register, Address offset: 0x014 */ + __IO uint32_t CCCR; /*!< FDCAN CC Control register, Address offset: 0x018 */ + __IO uint32_t NBTP; /*!< FDCAN Nominal Bit Timing & Prescaler register, Address offset: 0x01C */ + __IO uint32_t TSCC; /*!< FDCAN Timestamp Counter Configuration register, Address offset: 0x020 */ + __IO uint32_t TSCV; /*!< FDCAN Timestamp Counter Value register, Address offset: 0x024 */ + __IO uint32_t TOCC; /*!< FDCAN Timeout Counter Configuration register, Address offset: 0x028 */ + __IO uint32_t TOCV; /*!< FDCAN Timeout Counter Value register, Address offset: 0x02C */ + uint32_t RESERVED2[4]; /*!< Reserved, 0x030 - 0x03C */ + __IO uint32_t ECR; /*!< FDCAN Error Counter register, Address offset: 0x040 */ + __IO uint32_t PSR; /*!< FDCAN Protocol Status register, Address offset: 0x044 */ + __IO uint32_t TDCR; /*!< FDCAN Transmitter Delay Compensation register, Address offset: 0x048 */ + uint32_t RESERVED3; /*!< Reserved, 0x04C */ + __IO uint32_t IR; /*!< FDCAN Interrupt register, Address offset: 0x050 */ + __IO uint32_t IE; /*!< FDCAN Interrupt Enable register, Address offset: 0x054 */ + __IO uint32_t ILS; /*!< FDCAN Interrupt Line Select register, Address offset: 0x058 */ + __IO uint32_t ILE; /*!< FDCAN Interrupt Line Enable register, Address offset: 0x05C */ + uint32_t RESERVED4[8]; /*!< Reserved, 0x060 - 0x07C */ + __IO uint32_t RXGFC; /*!< FDCAN Global Filter Configuration register, Address offset: 0x080 */ + __IO uint32_t XIDAM; /*!< FDCAN Extended ID AND Mask register, Address offset: 0x084 */ + __IO uint32_t HPMS; /*!< FDCAN High Priority Message Status register, Address offset: 0x088 */ + uint32_t RESERVED5; /*!< Reserved, 0x08C */ + __IO uint32_t RXF0S; /*!< FDCAN Rx FIFO 0 Status register, Address offset: 0x090 */ + __IO uint32_t RXF0A; /*!< FDCAN Rx FIFO 0 Acknowledge register, Address offset: 0x094 */ + __IO uint32_t RXF1S; /*!< FDCAN Rx FIFO 1 Status register, Address offset: 0x098 */ + __IO uint32_t RXF1A; /*!< FDCAN Rx FIFO 1 Acknowledge register, Address offset: 0x09C */ + uint32_t RESERVED6[8]; /*!< Reserved, 0x0A0 - 0x0BC */ + __IO uint32_t TXBC; /*!< FDCAN Tx Buffer Configuration register, Address offset: 0x0C0 */ + __IO uint32_t TXFQS; /*!< FDCAN Tx FIFO/Queue Status register, Address offset: 0x0C4 */ + __IO uint32_t TXBRP; /*!< FDCAN Tx Buffer Request Pending register, Address offset: 0x0C8 */ + __IO uint32_t TXBAR; /*!< FDCAN Tx Buffer Add Request register, Address offset: 0x0CC */ + __IO uint32_t TXBCR; /*!< FDCAN Tx Buffer Cancellation Request register, Address offset: 0x0D0 */ + __IO uint32_t TXBTO; /*!< FDCAN Tx Buffer Transmission Occurred register, Address offset: 0x0D4 */ + __IO uint32_t TXBCF; /*!< FDCAN Tx Buffer Cancellation Finished register, Address offset: 0x0D8 */ + __IO uint32_t TXBTIE; /*!< FDCAN Tx Buffer Transmission Interrupt Enable register, Address offset: 0x0DC */ + __IO uint32_t TXBCIE; /*!< FDCAN Tx Buffer Cancellation Finished Interrupt Enable register, Address offset: 0x0E0 */ + __IO uint32_t TXEFS; /*!< FDCAN Tx Event FIFO Status register, Address offset: 0x0E4 */ + __IO uint32_t TXEFA; /*!< FDCAN Tx Event FIFO Acknowledge register, Address offset: 0x0E8 */ +} FDCAN_GlobalTypeDef; + +/** + * @brief FD Controller Area Network Configuration + */ + +typedef struct +{ + __IO uint32_t CKDIV; /*!< FDCAN clock divider register, Address offset: 0x100 + 0x000 */ +} FDCAN_Config_TypeDef; + +/** + * @brief Comparator + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint32_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED0; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Clock Recovery System + */ +typedef struct +{ + __IO uint32_t CR; /*!< CRS ccontrol register, Address offset: 0x00 */ + __IO uint32_t CFGR; /*!< CRS configuration register, Address offset: 0x04 */ + __IO uint32_t ISR; /*!< CRS interrupt and status register, Address offset: 0x08 */ + __IO uint32_t ICR; /*!< CRS interrupt flag clear register, Address offset: 0x0C */ +} CRS_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ + __IO uint32_t CCR; /*!< DAC calibration control register, Address offset: 0x38 */ + __IO uint32_t MCR; /*!< DAC mode control register, Address offset: 0x3C */ + __IO uint32_t SHSR1; /*!< DAC Sample and Hold sample time register 1, Address offset: 0x40 */ + __IO uint32_t SHSR2; /*!< DAC Sample and Hold sample time register 2, Address offset: 0x44 */ + __IO uint32_t SHHR; /*!< DAC Sample and Hold hold time register, Address offset: 0x48 */ + __IO uint32_t SHRR; /*!< DAC Sample and Hold refresh time register, Address offset: 0x4C */ + __IO uint32_t RESERVED[2]; + __IO uint32_t STR1; /*!< DAC Sawtooth register, Address offset: 0x58 */ + __IO uint32_t STR2; /*!< DAC Sawtooth register, Address offset: 0x5C */ + __IO uint32_t STMODR; /*!< DAC Sawtooth Mode register, Address offset: 0x60 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZR1; /*!< Debug MCU APB1 freeze register 1, Address offset: 0x08 */ + __IO uint32_t APB1FZR2; /*!< Debug MCU APB1 freeze register 2, Address offset: 0x0C */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x10 */ +} DBGMCU_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** + * @brief DMA Multiplexer + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA Multiplexer Channel x Control Register Address offset: 0x0004 * (channel x) */ +}DMAMUX_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< DMA Channel Status Register Address offset: 0x0080 */ + __IO uint32_t CFR; /*!< DMA Channel Clear Flag Register Address offset: 0x0084 */ +}DMAMUX_ChannelStatus_TypeDef; + +typedef struct +{ + __IO uint32_t RGCR; /*!< DMA Request Generator x Control Register Address offset: 0x0100 + 0x0004 * (Req Gen x) */ +}DMAMUX_RequestGen_TypeDef; + +typedef struct +{ + __IO uint32_t RGSR; /*!< DMA Request Generator Status Register Address offset: 0x0140 */ + __IO uint32_t RGCFR; /*!< DMA Request Generator Clear Flag Register Address offset: 0x0144 */ +}DMAMUX_RequestGenStatus_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR1; /*!< EXTI Interrupt mask register 1, Address offset: 0x00 */ + __IO uint32_t EMR1; /*!< EXTI Event mask register 1, Address offset: 0x04 */ + __IO uint32_t RTSR1; /*!< EXTI Rising trigger selection register 1, Address offset: 0x08 */ + __IO uint32_t FTSR1; /*!< EXTI Falling trigger selection register 1, Address offset: 0x0C */ + __IO uint32_t SWIER1; /*!< EXTI Software interrupt event register 1, Address offset: 0x10 */ + __IO uint32_t PR1; /*!< EXTI Pending register 1, Address offset: 0x14 */ + uint32_t RESERVED1; /*!< Reserved, 0x18 */ + uint32_t RESERVED2; /*!< Reserved, 0x1C */ + __IO uint32_t IMR2; /*!< EXTI Interrupt mask register 2, Address offset: 0x20 */ + __IO uint32_t EMR2; /*!< EXTI Event mask register 2, Address offset: 0x24 */ + __IO uint32_t RTSR2; /*!< EXTI Rising trigger selection register 2, Address offset: 0x28 */ + __IO uint32_t FTSR2; /*!< EXTI Falling trigger selection register 2, Address offset: 0x2C */ + __IO uint32_t SWIER2; /*!< EXTI Software interrupt event register 2, Address offset: 0x30 */ + __IO uint32_t PR2; /*!< EXTI Pending register 2, Address offset: 0x34 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t PDKEYR; /*!< FLASH power down key register, Address offset: 0x04 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x08 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x10 */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x14 */ + __IO uint32_t ECCR; /*!< FLASH ECC register, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved1, Address offset: 0x1C */ + __IO uint32_t OPTR; /*!< FLASH option register, Address offset: 0x20 */ + __IO uint32_t PCROP1SR; /*!< FLASH bank1 PCROP start address register, Address offset: 0x24 */ + __IO uint32_t PCROP1ER; /*!< FLASH bank1 PCROP end address register, Address offset: 0x28 */ + __IO uint32_t WRP1AR; /*!< FLASH bank1 WRP area A address register, Address offset: 0x2C */ + __IO uint32_t WRP1BR; /*!< FLASH bank1 WRP area B address register, Address offset: 0x30 */ + uint32_t RESERVED2[4]; /*!< Reserved2, Address offset: 0x34 */ + __IO uint32_t PCROP2SR; /*!< FLASH bank2 PCROP start address register, Address offset: 0x44 */ + __IO uint32_t PCROP2ER; /*!< FLASH bank2 PCROP end address register, Address offset: 0x48 */ + __IO uint32_t WRP2AR; /*!< FLASH bank2 WRP area A address register, Address offset: 0x4C */ + __IO uint32_t WRP2BR; /*!< FLASH bank2 WRP area B address register, Address offset: 0x50 */ + uint32_t RESERVED3[7]; /*!< Reserved3, Address offset: 0x54 */ + __IO uint32_t SEC1R; /*!< FLASH Securable memory register bank1, Address offset: 0x70 */ + __IO uint32_t SEC2R; /*!< FLASH Securable memory register bank2, Address offset: 0x74 */ +} FLASH_TypeDef; + +/** + * @brief FMAC + */ +typedef struct +{ + __IO uint32_t X1BUFCFG; /*!< FMAC X1 Buffer Configuration register, Address offset: 0x00 */ + __IO uint32_t X2BUFCFG; /*!< FMAC X2 Buffer Configuration register, Address offset: 0x04 */ + __IO uint32_t YBUFCFG; /*!< FMAC Y Buffer Configuration register, Address offset: 0x08 */ + __IO uint32_t PARAM; /*!< FMAC Parameter register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FMAC Control register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< FMAC Status register, Address offset: 0x14 */ + __IO uint32_t WDATA; /*!< FMAC Write Data register, Address offset: 0x18 */ + __IO uint32_t RDATA; /*!< FMAC Read Data register, Address offset: 0x1C */ +} FMAC_TypeDef; + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ + __IO uint32_t PCSCNTR; /*!< PSRAM chip-select counter register, Address offset: 0x20 */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register, Address offset: 0x8C */ + uint32_t RESERVED0; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ + __IO uint32_t BRR; /*!< GPIO Bit Reset register, Address offset: 0x28 */ +} GPIO_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + +/** + * @brief LPTIMER + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @brief Operational Amplifier (OPAMP) + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< OPAMP control/status register, Address offset: 0x00 */ + __IO uint32_t RESERVED[5]; /*!< OPAMP offset trimming register for normal mode, Address offset: 0x04 */ + __IO uint32_t TCMR; /*!< OPAMP timer controlled mux mode register, Address offset: 0x18 */ +} OPAMP_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< PWR power control register 3, Address offset: 0x08 */ + __IO uint32_t CR4; /*!< PWR power control register 4, Address offset: 0x0C */ + __IO uint32_t SR1; /*!< PWR power status register 1, Address offset: 0x10 */ + __IO uint32_t SR2; /*!< PWR power status register 2, Address offset: 0x14 */ + __IO uint32_t SCR; /*!< PWR power status reset register, Address offset: 0x18 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t PUCRA; /*!< Pull_up control register of portA, Address offset: 0x20 */ + __IO uint32_t PDCRA; /*!< Pull_Down control register of portA, Address offset: 0x24 */ + __IO uint32_t PUCRB; /*!< Pull_up control register of portB, Address offset: 0x28 */ + __IO uint32_t PDCRB; /*!< Pull_Down control register of portB, Address offset: 0x2C */ + __IO uint32_t PUCRC; /*!< Pull_up control register of portC, Address offset: 0x30 */ + __IO uint32_t PDCRC; /*!< Pull_Down control register of portC, Address offset: 0x34 */ + __IO uint32_t PUCRD; /*!< Pull_up control register of portD, Address offset: 0x38 */ + __IO uint32_t PDCRD; /*!< Pull_Down control register of portD, Address offset: 0x3C */ + __IO uint32_t PUCRE; /*!< Pull_up control register of portE, Address offset: 0x40 */ + __IO uint32_t PDCRE; /*!< Pull_Down control register of portE, Address offset: 0x44 */ + __IO uint32_t PUCRF; /*!< Pull_up control register of portF, Address offset: 0x48 */ + __IO uint32_t PDCRF; /*!< Pull_Down control register of portF, Address offset: 0x4C */ + __IO uint32_t PUCRG; /*!< Pull_up control register of portG, Address offset: 0x50 */ + __IO uint32_t PDCRG; /*!< Pull_Down control register of portG, Address offset: 0x54 */ + uint32_t RESERVED1[10]; /*!< Reserved Address offset: 0x58 - 0x7C */ + __IO uint32_t CR5; /*!< PWR power control register 5, Address offset: 0x80 */ +} PWR_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t ICSCR; /*!< RCC internal clock sources calibration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t PLLCFGR; /*!< RCC system PLL configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x10 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t CIER; /*!< RCC clock interrupt enable register, Address offset: 0x18 */ + __IO uint32_t CIFR; /*!< RCC clock interrupt flag register, Address offset: 0x1C */ + __IO uint32_t CICR; /*!< RCC clock interrupt clear register, Address offset: 0x20 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x24 */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x28 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x2C */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x30 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x34 */ + __IO uint32_t APB1RSTR1; /*!< RCC APB1 peripheral reset register 1, Address offset: 0x38 */ + __IO uint32_t APB1RSTR2; /*!< RCC APB1 peripheral reset register 2, Address offset: 0x3C */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x40 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x44 */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clocks enable register, Address offset: 0x48 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clocks enable register, Address offset: 0x4C */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clocks enable register, Address offset: 0x50 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x54 */ + __IO uint32_t APB1ENR1; /*!< RCC APB1 peripheral clocks enable register 1, Address offset: 0x58 */ + __IO uint32_t APB1ENR2; /*!< RCC APB1 peripheral clocks enable register 2, Address offset: 0x5C */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clocks enable register, Address offset: 0x60 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x64 */ + __IO uint32_t AHB1SMENR; /*!< RCC AHB1 peripheral clocks enable in sleep and stop modes register, Address offset: 0x68 */ + __IO uint32_t AHB2SMENR; /*!< RCC AHB2 peripheral clocks enable in sleep and stop modes register, Address offset: 0x6C */ + __IO uint32_t AHB3SMENR; /*!< RCC AHB3 peripheral clocks enable in sleep and stop modes register, Address offset: 0x70 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x74 */ + __IO uint32_t APB1SMENR1; /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 1, Address offset: 0x78 */ + __IO uint32_t APB1SMENR2; /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 2, Address offset: 0x7C */ + __IO uint32_t APB2SMENR; /*!< RCC APB2 peripheral clocks enable in sleep mode and stop modes register, Address offset: 0x80 */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0x84 */ + __IO uint32_t CCIPR; /*!< RCC peripherals independent clock configuration register, Address offset: 0x88 */ + uint32_t RESERVED9; /*!< Reserved, Address offset: 0x8C */ + __IO uint32_t BDCR; /*!< RCC backup domain control register, Address offset: 0x90 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x94 */ + __IO uint32_t CRRCR; /*!< RCC clock recovery RC register, Address offset: 0x98 */ + __IO uint32_t CCIPR2; /*!< RCC peripherals independent clock configuration register 2, Address offset: 0x9C */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ +/* +* @brief Specific device feature definitions +*/ +#define RTC_TAMP_INT_6_SUPPORT +#define RTC_TAMP_INT_NB 4u + +#define RTC_TAMP_NB 3u +#define RTC_BACKUP_NB 32u + + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x08 */ + __IO uint32_t ICSR; /*!< RTC initialization control and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved Address offset: 0x1C */ + uint32_t RESERVED1; /*!< Reserved Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved Address offset: 0x3C */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x48 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x4C */ + __IO uint32_t SR; /*!< RTC Status register, Address offset: 0x50 */ + __IO uint32_t MISR; /*!< RTC Masked Interrupt Status register, Address offset: 0x54 */ + uint32_t RESERVED3; /*!< Reserved Address offset: 0x58 */ + __IO uint32_t SCR; /*!< RTC Status Clear register, Address offset: 0x5C */ +} RTC_TypeDef; + +/** + * @brief Tamper and backup registers + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TAMP configuration register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TAMP configuration register 2, Address offset: 0x04 */ + uint32_t RESERVED0; /*!< no configuration register 3, Address offset: 0x08 */ + __IO uint32_t FLTCR; /*!< TAMP filter control register, Address offset: 0x0C */ + uint32_t RESERVED1[6]; /*!< Reserved Address offset: 0x10 - 0x24 */ + uint32_t RESERVED2; /*!< Reserved Address offset: 0x28 */ + __IO uint32_t IER; /*!< TAMP Interrupt enable register, Address offset: 0x2C */ + __IO uint32_t SR; /*!< TAMP Status register, Address offset: 0x30 */ + __IO uint32_t MISR; /*!< TAMP Masked Interrupt Status register Address offset: 0x34 */ + uint32_t RESERVED3; /*!< Reserved Address offset: 0x38 */ + __IO uint32_t SCR; /*!< TAMP Status clear register, Address offset: 0x3C */ + uint32_t RESERVED4[48]; /*!< Reserved Address offset: 0x040 - 0xFC */ + __IO uint32_t BKP0R; /*!< TAMP backup register 0, Address offset: 0x100 */ + __IO uint32_t BKP1R; /*!< TAMP backup register 1, Address offset: 0x104 */ + __IO uint32_t BKP2R; /*!< TAMP backup register 2, Address offset: 0x108 */ + __IO uint32_t BKP3R; /*!< TAMP backup register 3, Address offset: 0x10C */ + __IO uint32_t BKP4R; /*!< TAMP backup register 4, Address offset: 0x110 */ + __IO uint32_t BKP5R; /*!< TAMP backup register 5, Address offset: 0x114 */ + __IO uint32_t BKP6R; /*!< TAMP backup register 6, Address offset: 0x118 */ + __IO uint32_t BKP7R; /*!< TAMP backup register 7, Address offset: 0x11C */ + __IO uint32_t BKP8R; /*!< TAMP backup register 8, Address offset: 0x120 */ + __IO uint32_t BKP9R; /*!< TAMP backup register 9, Address offset: 0x124 */ + __IO uint32_t BKP10R; /*!< TAMP backup register 10, Address offset: 0x128 */ + __IO uint32_t BKP11R; /*!< TAMP backup register 11, Address offset: 0x12C */ + __IO uint32_t BKP12R; /*!< TAMP backup register 12, Address offset: 0x130 */ + __IO uint32_t BKP13R; /*!< TAMP backup register 13, Address offset: 0x134 */ + __IO uint32_t BKP14R; /*!< TAMP backup register 14, Address offset: 0x138 */ + __IO uint32_t BKP15R; /*!< TAMP backup register 15, Address offset: 0x13C */ + __IO uint32_t BKP16R; /*!< TAMP backup register 16, Address offset: 0x140 */ + __IO uint32_t BKP17R; /*!< TAMP backup register 17, Address offset: 0x144 */ + __IO uint32_t BKP18R; /*!< TAMP backup register 18, Address offset: 0x148 */ + __IO uint32_t BKP19R; /*!< TAMP backup register 19, Address offset: 0x14C */ + __IO uint32_t BKP20R; /*!< TAMP backup register 20, Address offset: 0x150 */ + __IO uint32_t BKP21R; /*!< TAMP backup register 21, Address offset: 0x154 */ + __IO uint32_t BKP22R; /*!< TAMP backup register 22, Address offset: 0x158 */ + __IO uint32_t BKP23R; /*!< TAMP backup register 23, Address offset: 0x15C */ + __IO uint32_t BKP24R; /*!< TAMP backup register 24, Address offset: 0x160 */ + __IO uint32_t BKP25R; /*!< TAMP backup register 25, Address offset: 0x164 */ + __IO uint32_t BKP26R; /*!< TAMP backup register 26, Address offset: 0x168 */ + __IO uint32_t BKP27R; /*!< TAMP backup register 27, Address offset: 0x16C */ + __IO uint32_t BKP28R; /*!< TAMP backup register 28, Address offset: 0x170 */ + __IO uint32_t BKP29R; /*!< TAMP backup register 29, Address offset: 0x174 */ + __IO uint32_t BKP30R; /*!< TAMP backup register 30, Address offset: 0x178 */ + __IO uint32_t BKP31R; /*!< TAMP backup register 31, Address offset: 0x17C */ +} TAMP_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + uint32_t RESERVED[17]; /*!< Reserved, Address offset: 0x00 to 0x40 */ + __IO uint32_t PDMCR; /*!< SAI PDM control register, Address offset: 0x44 */ + __IO uint32_t PDMDLY; /*!< SAI PDM delay register, Address offset: 0x48 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI Status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register, Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI Rx CRC register, Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI Tx CRC register, Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t CFGR1; /*!< SYSCFG configuration register 1, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + __IO uint32_t SCSR; /*!< SYSCFG CCMSRAM control and status register, Address offset: 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG configuration register 2, Address offset: 0x1C */ + __IO uint32_t SWPR; /*!< SYSCFG CCMSRAM write protection register, Address offset: 0x20 */ + __IO uint32_t SKR; /*!< SYSCFG CCMSRAM Key Register, Address offset: 0x24 */ +} SYSCFG_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t CCR5; /*!< TIM capture/compare register 5, Address offset: 0x48 */ + __IO uint32_t CCR6; /*!< TIM capture/compare register 6, Address offset: 0x4C */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x50 */ + __IO uint32_t DTR2; /*!< TIM deadtime register 2, Address offset: 0x54 */ + __IO uint32_t ECR; /*!< TIM encoder control register, Address offset: 0x58 */ + __IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x5C */ + __IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */ + __IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */ + __IO uint32_t OR ; /*!< TIM option register, Address offset: 0x68 */ + uint32_t RESERVED0[220];/*!< Reserved, Address offset: 0x6C */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x3DC */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x3E0 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Timeout register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ + __IO uint32_t PRESC; /*!< USART Prescaler register, Address offset: 0x2C */ +} USART_TypeDef; + +/** + * @brief Universal Serial Bus Full Speed Device + */ + +typedef struct +{ + __IO uint16_t EP0R; /*!< USB Endpoint 0 register, Address offset: 0x00 */ + __IO uint16_t RESERVED0; /*!< Reserved */ + __IO uint16_t EP1R; /*!< USB Endpoint 1 register, Address offset: 0x04 */ + __IO uint16_t RESERVED1; /*!< Reserved */ + __IO uint16_t EP2R; /*!< USB Endpoint 2 register, Address offset: 0x08 */ + __IO uint16_t RESERVED2; /*!< Reserved */ + __IO uint16_t EP3R; /*!< USB Endpoint 3 register, Address offset: 0x0C */ + __IO uint16_t RESERVED3; /*!< Reserved */ + __IO uint16_t EP4R; /*!< USB Endpoint 4 register, Address offset: 0x10 */ + __IO uint16_t RESERVED4; /*!< Reserved */ + __IO uint16_t EP5R; /*!< USB Endpoint 5 register, Address offset: 0x14 */ + __IO uint16_t RESERVED5; /*!< Reserved */ + __IO uint16_t EP6R; /*!< USB Endpoint 6 register, Address offset: 0x18 */ + __IO uint16_t RESERVED6; /*!< Reserved */ + __IO uint16_t EP7R; /*!< USB Endpoint 7 register, Address offset: 0x1C */ + __IO uint16_t RESERVED7[17]; /*!< Reserved */ + __IO uint16_t CNTR; /*!< Control register, Address offset: 0x40 */ + __IO uint16_t RESERVED8; /*!< Reserved */ + __IO uint16_t ISTR; /*!< Interrupt status register, Address offset: 0x44 */ + __IO uint16_t RESERVED9; /*!< Reserved */ + __IO uint16_t FNR; /*!< Frame number register, Address offset: 0x48 */ + __IO uint16_t RESERVEDA; /*!< Reserved */ + __IO uint16_t DADDR; /*!< Device address register, Address offset: 0x4C */ + __IO uint16_t RESERVEDB; /*!< Reserved */ + __IO uint16_t BTABLE; /*!< Buffer Table address register, Address offset: 0x50 */ + __IO uint16_t RESERVEDC; /*!< Reserved */ + __IO uint16_t LPMCSR; /*!< LPM Control and Status register, Address offset: 0x54 */ + __IO uint16_t RESERVEDD; /*!< Reserved */ + __IO uint16_t BCDR; /*!< Battery Charging detector register, Address offset: 0x58 */ + __IO uint16_t RESERVEDE; /*!< Reserved */ +} USB_TypeDef; + +/** + * @brief VREFBUF + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< VREFBUF control and status register, Address offset: 0x00 */ + __IO uint32_t CCR; /*!< VREFBUF calibration and control register, Address offset: 0x04 */ +} VREFBUF_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RNG + */ +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief CORDIC + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< CORDIC control and status register, Address offset: 0x00 */ + __IO uint32_t WDATA; /*!< CORDIC argument register, Address offset: 0x04 */ + __IO uint32_t RDATA; /*!< CORDIC result register, Address offset: 0x08 */ +} CORDIC_TypeDef; + +/** + * @brief UCPD + */ + +typedef struct +{ + __IO uint32_t CFG1; /*!< UCPD configuration register 1, Address offset: 0x00 */ + __IO uint32_t CFG2; /*!< UCPD configuration register 2, Address offset: 0x04 */ + __IO uint32_t RESERVED0; /*!< UCPD reserved register, Address offset: 0x08 */ + __IO uint32_t CR; /*!< UCPD control register, Address offset: 0x0C */ + __IO uint32_t IMR; /*!< UCPD interrupt mask register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< UCPD status register, Address offset: 0x14 */ + __IO uint32_t ICR; /*!< UCPD interrupt flag clear register Address offset: 0x18 */ + __IO uint32_t TX_ORDSET; /*!< UCPD Tx ordered set type register, Address offset: 0x1C */ + __IO uint32_t TX_PAYSZ; /*!< UCPD Tx payload size register, Address offset: 0x20 */ + __IO uint32_t TXDR; /*!< UCPD Tx data register, Address offset: 0x24 */ + __IO uint32_t RX_ORDSET; /*!< UCPD Rx ordered set type register, Address offset: 0x28 */ + __IO uint32_t RX_PAYSZ; /*!< UCPD Rx payload size register, Address offset: 0x2C */ + __IO uint32_t RXDR; /*!< UCPD Rx data register, Address offset: 0x30 */ + __IO uint32_t RX_ORDEXT1; /*!< UCPD Rx ordered set extension 1 register, Address offset: 0x34 */ + __IO uint32_t RX_ORDEXT2; /*!< UCPD Rx ordered set extension 2 register, Address offset: 0x38 */ +} UCPD_TypeDef; + +/** + * @brief High resolution Timer (HRTIM) + */ + +#define c7amba_hrtim1_v2_0 + +/* HRTIM master registers definition */ +typedef struct +{ + __IO uint32_t MCR; /*!< HRTIM Master Timer control register, Address offset: 0x00 */ + __IO uint32_t MISR; /*!< HRTIM Master Timer interrupt status register, Address offset: 0x04 */ + __IO uint32_t MICR; /*!< HRTIM Master Timer interrupt clear register, Address offset: 0x08 */ + __IO uint32_t MDIER; /*!< HRTIM Master Timer DMA/interrupt enable register Address offset: 0x0C */ + __IO uint32_t MCNTR; /*!< HRTIM Master Timer counter register, Address offset: 0x10 */ + __IO uint32_t MPER; /*!< HRTIM Master Timer period register, Address offset: 0x14 */ + __IO uint32_t MREP; /*!< HRTIM Master Timer repetition register, Address offset: 0x18 */ + __IO uint32_t MCMP1R; /*!< HRTIM Master Timer compare 1 register, Address offset: 0x1C */ + uint32_t RESERVED0; /*!< Reserved, 0x20 */ + __IO uint32_t MCMP2R; /*!< HRTIM Master Timer compare 2 register, Address offset: 0x24 */ + __IO uint32_t MCMP3R; /*!< HRTIM Master Timer compare 3 register, Address offset: 0x28 */ + __IO uint32_t MCMP4R; /*!< HRTIM Master Timer compare 4 register, Address offset: 0x2C */ + uint32_t RESERVED1[20]; /*!< Reserved, 0x30..0x7C */ +}HRTIM_Master_TypeDef; + +/* HRTIM Timer A to F registers definition */ +typedef struct +{ + __IO uint32_t TIMxCR; /*!< HRTIM Timerx control register, Address offset: 0x00 */ + __IO uint32_t TIMxISR; /*!< HRTIM Timerx interrupt status register, Address offset: 0x04 */ + __IO uint32_t TIMxICR; /*!< HRTIM Timerx interrupt clear register, Address offset: 0x08 */ + __IO uint32_t TIMxDIER; /*!< HRTIM Timerx DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t CNTxR; /*!< HRTIM Timerx counter register, Address offset: 0x10 */ + __IO uint32_t PERxR; /*!< HRTIM Timerx period register, Address offset: 0x14 */ + __IO uint32_t REPxR; /*!< HRTIM Timerx repetition register, Address offset: 0x18 */ + __IO uint32_t CMP1xR; /*!< HRTIM Timerx compare 1 register, Address offset: 0x1C */ + __IO uint32_t CMP1CxR; /*!< HRTIM Timerx compare 1 compound register, Address offset: 0x20 */ + __IO uint32_t CMP2xR; /*!< HRTIM Timerx compare 2 register, Address offset: 0x24 */ + __IO uint32_t CMP3xR; /*!< HRTIM Timerx compare 3 register, Address offset: 0x28 */ + __IO uint32_t CMP4xR; /*!< HRTIM Timerx compare 4 register, Address offset: 0x2C */ + __IO uint32_t CPT1xR; /*!< HRTIM Timerx capture 1 register, Address offset: 0x30 */ + __IO uint32_t CPT2xR; /*!< HRTIM Timerx capture 2 register, Address offset: 0x34 */ + __IO uint32_t DTxR; /*!< HRTIM Timerx dead time register, Address offset: 0x38 */ + __IO uint32_t SETx1R; /*!< HRTIM Timerx output 1 set register, Address offset: 0x3C */ + __IO uint32_t RSTx1R; /*!< HRTIM Timerx output 1 reset register, Address offset: 0x40 */ + __IO uint32_t SETx2R; /*!< HRTIM Timerx output 2 set register, Address offset: 0x44 */ + __IO uint32_t RSTx2R; /*!< HRTIM Timerx output 2 reset register, Address offset: 0x48 */ + __IO uint32_t EEFxR1; /*!< HRTIM Timerx external event filtering 1 register, Address offset: 0x4C */ + __IO uint32_t EEFxR2; /*!< HRTIM Timerx external event filtering 2 register, Address offset: 0x50 */ + __IO uint32_t RSTxR; /*!< HRTIM Timerx Reset register, Address offset: 0x54 */ + __IO uint32_t CHPxR; /*!< HRTIM Timerx Chopper register, Address offset: 0x58 */ + __IO uint32_t CPT1xCR; /*!< HRTIM Timerx Capture 1 register, Address offset: 0x5C */ + __IO uint32_t CPT2xCR; /*!< HRTIM Timerx Capture 2 register, Address offset: 0x60 */ + __IO uint32_t OUTxR; /*!< HRTIM Timerx Output register, Address offset: 0x64 */ + __IO uint32_t FLTxR; /*!< HRTIM Timerx Fault register, Address offset: 0x68 */ + __IO uint32_t TIMxCR2; /*!< HRTIM Timerx Control register 2, Address offset: 0x6C */ + __IO uint32_t EEFxR3; /*!< HRTIM Timerx external event filtering 3 register, Address offset: 0x70 */ + uint32_t RESERVED0[3]; /*!< Reserved, 0x74..0x7C */ +}HRTIM_Timerx_TypeDef; + +/* HRTIM common register definition */ +typedef struct +{ + __IO uint32_t CR1; /*!< HRTIM control register1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< HRTIM control register2, Address offset: 0x04 */ + __IO uint32_t ISR; /*!< HRTIM interrupt status register, Address offset: 0x08 */ + __IO uint32_t ICR; /*!< HRTIM interrupt clear register, Address offset: 0x0C */ + __IO uint32_t IER; /*!< HRTIM interrupt enable register, Address offset: 0x10 */ + __IO uint32_t OENR; /*!< HRTIM Output enable register, Address offset: 0x14 */ + __IO uint32_t ODISR; /*!< HRTIM Output disable register, Address offset: 0x18 */ + __IO uint32_t ODSR; /*!< HRTIM Output disable status register, Address offset: 0x1C */ + __IO uint32_t BMCR; /*!< HRTIM Burst mode control register, Address offset: 0x20 */ + __IO uint32_t BMTRGR; /*!< HRTIM Busrt mode trigger register, Address offset: 0x24 */ + __IO uint32_t BMCMPR; /*!< HRTIM Burst mode compare register, Address offset: 0x28 */ + __IO uint32_t BMPER; /*!< HRTIM Burst mode period register, Address offset: 0x2C */ + __IO uint32_t EECR1; /*!< HRTIM Timer external event control register1, Address offset: 0x30 */ + __IO uint32_t EECR2; /*!< HRTIM Timer external event control register2, Address offset: 0x34 */ + __IO uint32_t EECR3; /*!< HRTIM Timer external event control register3, Address offset: 0x38 */ + __IO uint32_t ADC1R; /*!< HRTIM ADC Trigger 1 register, Address offset: 0x3C */ + __IO uint32_t ADC2R; /*!< HRTIM ADC Trigger 2 register, Address offset: 0x40 */ + __IO uint32_t ADC3R; /*!< HRTIM ADC Trigger 3 register, Address offset: 0x44 */ + __IO uint32_t ADC4R; /*!< HRTIM ADC Trigger 4 register, Address offset: 0x48 */ + __IO uint32_t DLLCR; /*!< HRTIM DLL control register, Address offset: 0x4C */ + __IO uint32_t FLTINR1; /*!< HRTIM Fault input register1, Address offset: 0x50 */ + __IO uint32_t FLTINR2; /*!< HRTIM Fault input register2, Address offset: 0x54 */ + __IO uint32_t BDMUPR; /*!< HRTIM Burst DMA Master Timer update register, Address offset: 0x58 */ + __IO uint32_t BDTAUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x5C */ + __IO uint32_t BDTBUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x60 */ + __IO uint32_t BDTCUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x64 */ + __IO uint32_t BDTDUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x68 */ + __IO uint32_t BDTEUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x6C */ + __IO uint32_t BDMADR; /*!< HRTIM Burst DMA Master Data register, Address offset: 0x70 */ + __IO uint32_t BDTFUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x74 */ + __IO uint32_t ADCER; /*!< HRTIM ADC Extended Trigger register, Address offset: 0x78 */ + __IO uint32_t ADCUR; /*!< HRTIM ADC Trigger Update register, Address offset: 0x7C */ + __IO uint32_t ADCPS1; /*!< HRTIM ADC Post Scaler Register 1, Address offset: 0x80 */ + __IO uint32_t ADCPS2; /*!< HRTIM ADC Post Scaler Register 2, Address offset: 0x84 */ + __IO uint32_t FLTINR3; /*!< HRTIM Fault input register3, Address offset: 0x88 */ + __IO uint32_t FLTINR4; /*!< HRTIM Fault input register4, Address offset: 0x8C */ +}HRTIM_Common_TypeDef; + +/* HRTIM register definition */ +typedef struct { + HRTIM_Master_TypeDef sMasterRegs; + HRTIM_Timerx_TypeDef sTimerxRegs[6]; + HRTIM_Common_TypeDef sCommonRegs; +}HRTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ + +#define FLASH_BASE (0x08000000UL) /*!< FLASH (up to 512 kB) base address */ +#define SRAM1_BASE (0x20000000UL) /*!< SRAM1(up to 80 KB) base address */ +#define SRAM2_BASE (0x20014000UL) /*!< SRAM2(16 KB) base address */ +#define CCMSRAM_BASE (0x10000000UL) /*!< CCMSRAM(32 KB) base address */ +#define PERIPH_BASE (0x40000000UL) /*!< Peripheral base address */ +#define FMC_BASE (0x60000000UL) /*!< FMC base address */ +#define QSPI_BASE (0x90000000UL) /*!< QUADSPI memories accessible over AHB base address */ + +#define FMC_R_BASE (0xA0000000UL) /*!< FMC control registers base address */ +#define QSPI_R_BASE (0xA0001000UL) /*!< QUADSPI control registers base address */ +#define SRAM1_BB_BASE (0x22000000UL) /*!< SRAM1(80 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE (0x22280000UL) /*!< SRAM2(16 KB) base address in the bit-band region */ +#define CCMSRAM_BB_BASE (0x22300000UL) /*!< CCMSRAM(32 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE (0x42000000UL) /*!< Peripheral base address in the bit-band region */ +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + +#define SRAM1_SIZE_MAX (0x00014000UL) /*!< maximum SRAM1 size (up to 80 KBytes) */ +#define SRAM2_SIZE (0x00004000UL) /*!< SRAM2 size (16 KBytes) */ +#define CCMSRAM_SIZE (0x00008000UL) /*!< CCMSRAM size (32 KBytes) */ + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL) + +#define FMC_BANK1 FMC_BASE +#define FMC_BANK1_1 FMC_BANK1 +#define FMC_BANK1_2 (FMC_BANK1 + 0x04000000UL) +#define FMC_BANK1_3 (FMC_BANK1 + 0x08000000UL) +#define FMC_BANK1_4 (FMC_BANK1 + 0x0C000000UL) +#define FMC_BANK3 (FMC_BASE + 0x20000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define CRS_BASE (APB1PERIPH_BASE + 0x2000UL) +#define TAMP_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define USB_BASE (APB1PERIPH_BASE + 0x5C00UL) /*!< USB_IP Peripheral Registers base address */ +#define USB_PMAADDR (APB1PERIPH_BASE + 0x6000UL) /*!< USB_IP Packet Memory Area base address */ +#define FDCAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define FDCAN_CONFIG_BASE (APB1PERIPH_BASE + 0x6500UL) /*!< FDCAN configuration registers base address */ +#define FDCAN2_BASE (APB1PERIPH_BASE + 0x6800UL) +#define FDCAN3_BASE (APB1PERIPH_BASE + 0x6C00UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x7800UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x7C00UL) +#define LPUART1_BASE (APB1PERIPH_BASE + 0x8000UL) +#define I2C4_BASE (APB1PERIPH_BASE + 0x8400UL) +#define UCPD1_BASE (APB1PERIPH_BASE + 0xA000UL) +#define SRAMCAN_BASE (APB1PERIPH_BASE + 0xA400UL) + +/*!< APB2 peripherals */ +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x0000UL) +#define VREFBUF_BASE (APB2PERIPH_BASE + 0x0030UL) +#define COMP1_BASE (APB2PERIPH_BASE + 0x0200UL) +#define COMP2_BASE (APB2PERIPH_BASE + 0x0204UL) +#define COMP3_BASE (APB2PERIPH_BASE + 0x0208UL) +#define COMP4_BASE (APB2PERIPH_BASE + 0x020CUL) +#define COMP5_BASE (APB2PERIPH_BASE + 0x0210UL) +#define COMP6_BASE (APB2PERIPH_BASE + 0x0214UL) +#define COMP7_BASE (APB2PERIPH_BASE + 0x0218UL) +#define OPAMP_BASE (APB2PERIPH_BASE + 0x0300UL) +#define OPAMP1_BASE (APB2PERIPH_BASE + 0x0300UL) +#define OPAMP2_BASE (APB2PERIPH_BASE + 0x0304UL) +#define OPAMP3_BASE (APB2PERIPH_BASE + 0x0308UL) +#define OPAMP4_BASE (APB2PERIPH_BASE + 0x030CUL) +#define OPAMP5_BASE (APB2PERIPH_BASE + 0x0310UL) +#define OPAMP6_BASE (APB2PERIPH_BASE + 0x0314UL) + +#define EXTI_BASE (APB2PERIPH_BASE + 0x0400UL) +#define TIM1_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x3400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x3800UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM15_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM16_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM17_BASE (APB2PERIPH_BASE + 0x4800UL) +#define TIM20_BASE (APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5400UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x0004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x0024UL) +#define HRTIM1_BASE (APB2PERIPH_BASE + 0x6800UL) +#define HRTIM1_TIMA_BASE (HRTIM1_BASE + 0x0080UL) +#define HRTIM1_TIMB_BASE (HRTIM1_BASE + 0x0100UL) +#define HRTIM1_TIMC_BASE (HRTIM1_BASE + 0x0180UL) +#define HRTIM1_TIMD_BASE (HRTIM1_BASE + 0x0200UL) +#define HRTIM1_TIME_BASE (HRTIM1_BASE + 0x0280UL) +#define HRTIM1_TIMF_BASE (HRTIM1_BASE + 0x0300UL) +#define HRTIM1_COMMON_BASE (HRTIM1_BASE + 0x0380UL) + +/*!< AHB1 peripherals */ +#define DMA1_BASE (AHB1PERIPH_BASE) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define DMAMUX1_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define CORDIC_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define FMAC_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x2000UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) + +#define DMA1_Channel1_BASE (DMA1_BASE + 0x0008UL) +#define DMA1_Channel2_BASE (DMA1_BASE + 0x001CUL) +#define DMA1_Channel3_BASE (DMA1_BASE + 0x0030UL) +#define DMA1_Channel4_BASE (DMA1_BASE + 0x0044UL) +#define DMA1_Channel5_BASE (DMA1_BASE + 0x0058UL) +#define DMA1_Channel6_BASE (DMA1_BASE + 0x006CUL) +#define DMA1_Channel7_BASE (DMA1_BASE + 0x0080UL) +#define DMA1_Channel8_BASE (DMA1_BASE + 0x0094UL) + +#define DMA2_Channel1_BASE (DMA2_BASE + 0x0008UL) +#define DMA2_Channel2_BASE (DMA2_BASE + 0x001CUL) +#define DMA2_Channel3_BASE (DMA2_BASE + 0x0030UL) +#define DMA2_Channel4_BASE (DMA2_BASE + 0x0044UL) +#define DMA2_Channel5_BASE (DMA2_BASE + 0x0058UL) +#define DMA2_Channel6_BASE (DMA2_BASE + 0x006CUL) +#define DMA2_Channel7_BASE (DMA2_BASE + 0x0080UL) +#define DMA2_Channel8_BASE (DMA2_BASE + 0x0094UL) + +#define DMAMUX1_Channel0_BASE (DMAMUX1_BASE) +#define DMAMUX1_Channel1_BASE (DMAMUX1_BASE + 0x0004UL) +#define DMAMUX1_Channel2_BASE (DMAMUX1_BASE + 0x0008UL) +#define DMAMUX1_Channel3_BASE (DMAMUX1_BASE + 0x000CUL) +#define DMAMUX1_Channel4_BASE (DMAMUX1_BASE + 0x0010UL) +#define DMAMUX1_Channel5_BASE (DMAMUX1_BASE + 0x0014UL) +#define DMAMUX1_Channel6_BASE (DMAMUX1_BASE + 0x0018UL) +#define DMAMUX1_Channel7_BASE (DMAMUX1_BASE + 0x001CUL) +#define DMAMUX1_Channel8_BASE (DMAMUX1_BASE + 0x0020UL) +#define DMAMUX1_Channel9_BASE (DMAMUX1_BASE + 0x0024UL) +#define DMAMUX1_Channel10_BASE (DMAMUX1_BASE + 0x0028UL) +#define DMAMUX1_Channel11_BASE (DMAMUX1_BASE + 0x002CUL) +#define DMAMUX1_Channel12_BASE (DMAMUX1_BASE + 0x0030UL) +#define DMAMUX1_Channel13_BASE (DMAMUX1_BASE + 0x0034UL) +#define DMAMUX1_Channel14_BASE (DMAMUX1_BASE + 0x0038UL) +#define DMAMUX1_Channel15_BASE (DMAMUX1_BASE + 0x003CUL) +#define DMAMUX1_RequestGenerator0_BASE (DMAMUX1_BASE + 0x0100UL) +#define DMAMUX1_RequestGenerator1_BASE (DMAMUX1_BASE + 0x0104UL) +#define DMAMUX1_RequestGenerator2_BASE (DMAMUX1_BASE + 0x0108UL) +#define DMAMUX1_RequestGenerator3_BASE (DMAMUX1_BASE + 0x010CUL) + +#define DMAMUX1_ChannelStatus_BASE (DMAMUX1_BASE + 0x0080UL) +#define DMAMUX1_RequestGenStatus_BASE (DMAMUX1_BASE + 0x0140UL) + +/*!< AHB2 peripherals */ +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB2PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB2PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB2PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB2PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB2PERIPH_BASE + 0x1800UL) + +#define ADC1_BASE (AHB2PERIPH_BASE + 0x08000000UL) +#define ADC2_BASE (AHB2PERIPH_BASE + 0x08000100UL) +#define ADC12_COMMON_BASE (AHB2PERIPH_BASE + 0x08000300UL) +#define ADC3_BASE (AHB2PERIPH_BASE + 0x08000400UL) +#define ADC4_BASE (AHB2PERIPH_BASE + 0x08000500UL) +#define ADC5_BASE (AHB2PERIPH_BASE + 0x08000600UL) +#define ADC345_COMMON_BASE (AHB2PERIPH_BASE + 0x08000700UL) + +#define DAC_BASE (AHB2PERIPH_BASE + 0x08000800UL) +#define DAC1_BASE (AHB2PERIPH_BASE + 0x08000800UL) +#define DAC2_BASE (AHB2PERIPH_BASE + 0x08000C00UL) +#define DAC3_BASE (AHB2PERIPH_BASE + 0x08001000UL) +#define DAC4_BASE (AHB2PERIPH_BASE + 0x08001400UL) + +/*!< FMC Banks registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define RNG_BASE (AHB2PERIPH_BASE + 0x08060800UL) +/* Debug MCU registers base address */ +#define DBGMCU_BASE (0xE0042000UL) + +#define PACKAGE_BASE (0x1FFF7500UL) /*!< Package data register base address */ +#define UID_BASE (0x1FFF7590UL) /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE (0x1FFF75E0UL) /*!< Flash size data register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define CRS ((CRS_TypeDef *) CRS_BASE) +#define TAMP ((TAMP_TypeDef *) TAMP_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define USB ((USB_TypeDef *) USB_BASE) +#define FDCAN1 ((FDCAN_GlobalTypeDef *) FDCAN1_BASE) +#define FDCAN_CONFIG ((FDCAN_Config_TypeDef *) FDCAN_CONFIG_BASE) +#define FDCAN2 ((FDCAN_GlobalTypeDef *) FDCAN2_BASE) +#define FDCAN3 ((FDCAN_GlobalTypeDef *) FDCAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define LPUART1 ((USART_TypeDef *) LPUART1_BASE) +#define I2C4 ((I2C_TypeDef *) I2C4_BASE) +#define UCPD1 ((UCPD_TypeDef *) UCPD1_BASE) + +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define VREFBUF ((VREFBUF_TypeDef *) VREFBUF_BASE) +#define COMP1 ((COMP_TypeDef *) COMP1_BASE) +#define COMP2 ((COMP_TypeDef *) COMP2_BASE) +#define COMP3 ((COMP_TypeDef *) COMP3_BASE) +#define COMP4 ((COMP_TypeDef *) COMP4_BASE) +#define COMP5 ((COMP_TypeDef *) COMP5_BASE) +#define COMP6 ((COMP_TypeDef *) COMP6_BASE) +#define COMP7 ((COMP_TypeDef *) COMP7_BASE) + +#define OPAMP ((OPAMP_TypeDef *) OPAMP_BASE) +#define OPAMP1 ((OPAMP_TypeDef *) OPAMP1_BASE) +#define OPAMP2 ((OPAMP_TypeDef *) OPAMP2_BASE) +#define OPAMP3 ((OPAMP_TypeDef *) OPAMP3_BASE) +#define OPAMP4 ((OPAMP_TypeDef *) OPAMP4_BASE) +#define OPAMP5 ((OPAMP_TypeDef *) OPAMP5_BASE) +#define OPAMP6 ((OPAMP_TypeDef *) OPAMP6_BASE) + +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define TIM15 ((TIM_TypeDef *) TIM15_BASE) +#define TIM16 ((TIM_TypeDef *) TIM16_BASE) +#define TIM17 ((TIM_TypeDef *) TIM17_BASE) +#define TIM20 ((TIM_TypeDef *) TIM20_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define HRTIM1 ((HRTIM_TypeDef *) HRTIM1_BASE) +#define HRTIM1_TIMA ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMA_BASE) +#define HRTIM1_TIMB ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMB_BASE) +#define HRTIM1_TIMC ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMC_BASE) +#define HRTIM1_TIMD ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMD_BASE) +#define HRTIM1_TIME ((HRTIM_Timerx_TypeDef *) HRTIM1_TIME_BASE) +#define HRTIM1_TIMF ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMF_BASE) +#define HRTIM1_COMMON ((HRTIM_Common_TypeDef *) HRTIM1_COMMON_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMAMUX1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_BASE) +#define CORDIC ((CORDIC_TypeDef *) CORDIC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FMAC ((FMAC_TypeDef *) FMAC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) + +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC12_COMMON ((ADC_Common_TypeDef *) ADC12_COMMON_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC4 ((ADC_TypeDef *) ADC4_BASE) +#define ADC5 ((ADC_TypeDef *) ADC5_BASE) +#define ADC345_COMMON ((ADC_Common_TypeDef *) ADC345_COMMON_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) +#define DAC1 ((DAC_TypeDef *) DAC1_BASE) +#define DAC2 ((DAC_TypeDef *) DAC2_BASE) +#define DAC3 ((DAC_TypeDef *) DAC3_BASE) +#define DAC4 ((DAC_TypeDef *) DAC4_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) + +#define DMA1_Channel1 ((DMA_Channel_TypeDef *) DMA1_Channel1_BASE) +#define DMA1_Channel2 ((DMA_Channel_TypeDef *) DMA1_Channel2_BASE) +#define DMA1_Channel3 ((DMA_Channel_TypeDef *) DMA1_Channel3_BASE) +#define DMA1_Channel4 ((DMA_Channel_TypeDef *) DMA1_Channel4_BASE) +#define DMA1_Channel5 ((DMA_Channel_TypeDef *) DMA1_Channel5_BASE) +#define DMA1_Channel6 ((DMA_Channel_TypeDef *) DMA1_Channel6_BASE) +#define DMA1_Channel7 ((DMA_Channel_TypeDef *) DMA1_Channel7_BASE) +#define DMA1_Channel8 ((DMA_Channel_TypeDef *) DMA1_Channel8_BASE) + +#define DMA2_Channel1 ((DMA_Channel_TypeDef *) DMA2_Channel1_BASE) +#define DMA2_Channel2 ((DMA_Channel_TypeDef *) DMA2_Channel2_BASE) +#define DMA2_Channel3 ((DMA_Channel_TypeDef *) DMA2_Channel3_BASE) +#define DMA2_Channel4 ((DMA_Channel_TypeDef *) DMA2_Channel4_BASE) +#define DMA2_Channel5 ((DMA_Channel_TypeDef *) DMA2_Channel5_BASE) +#define DMA2_Channel6 ((DMA_Channel_TypeDef *) DMA2_Channel6_BASE) +#define DMA2_Channel7 ((DMA_Channel_TypeDef *) DMA2_Channel7_BASE) +#define DMA2_Channel8 ((DMA_Channel_TypeDef *) DMA2_Channel8_BASE) + +#define DMAMUX1_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel0_BASE) +#define DMAMUX1_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel1_BASE) +#define DMAMUX1_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel2_BASE) +#define DMAMUX1_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel3_BASE) +#define DMAMUX1_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel4_BASE) +#define DMAMUX1_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel5_BASE) +#define DMAMUX1_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel6_BASE) +#define DMAMUX1_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel7_BASE) +#define DMAMUX1_Channel8 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel8_BASE) +#define DMAMUX1_Channel9 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel9_BASE) +#define DMAMUX1_Channel10 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel10_BASE) +#define DMAMUX1_Channel11 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel11_BASE) +#define DMAMUX1_Channel12 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel12_BASE) +#define DMAMUX1_Channel13 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel13_BASE) +#define DMAMUX1_Channel14 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel14_BASE) +#define DMAMUX1_Channel15 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel15_BASE) + +#define DMAMUX1_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator0_BASE) +#define DMAMUX1_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator1_BASE) +#define DMAMUX1_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator2_BASE) +#define DMAMUX1_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator3_BASE) + +#define DMAMUX1_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX1_ChannelStatus_BASE) +#define DMAMUX1_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX1_RequestGenStatus_BASE) + +#define FMC_Bank1_R ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E_R ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank3_R ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) + +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) + +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Hardware_Constant_Definition + * @{ + */ +#define LSI_STARTUP_TIME 130U /*!< LSI Maximum startup time in us */ + + /** + * @} + */ + +/** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ + +/* + * @brief Specific device feature definitions (not present on all devices in the STM32G4 series) + */ +#define ADC_MULTIMODE_SUPPORT /*!< ADC feature available only on specific devices: multimode available on devices with several ADC instances */ + +/******************** Bit definition for ADC_ISR register *******************/ +#define ADC_ISR_ADRDY_Pos (0U) +#define ADC_ISR_ADRDY_Msk (0x1UL << ADC_ISR_ADRDY_Pos) /*!< 0x00000001 */ +#define ADC_ISR_ADRDY ADC_ISR_ADRDY_Msk /*!< ADC ready flag */ +#define ADC_ISR_EOSMP_Pos (1U) +#define ADC_ISR_EOSMP_Msk (0x1UL << ADC_ISR_EOSMP_Pos) /*!< 0x00000002 */ +#define ADC_ISR_EOSMP ADC_ISR_EOSMP_Msk /*!< ADC group regular end of sampling flag */ +#define ADC_ISR_EOC_Pos (2U) +#define ADC_ISR_EOC_Msk (0x1UL << ADC_ISR_EOC_Pos) /*!< 0x00000004 */ +#define ADC_ISR_EOC ADC_ISR_EOC_Msk /*!< ADC group regular end of unitary conversion flag */ +#define ADC_ISR_EOS_Pos (3U) +#define ADC_ISR_EOS_Msk (0x1UL << ADC_ISR_EOS_Pos) /*!< 0x00000008 */ +#define ADC_ISR_EOS ADC_ISR_EOS_Msk /*!< ADC group regular end of sequence conversions flag */ +#define ADC_ISR_OVR_Pos (4U) +#define ADC_ISR_OVR_Msk (0x1UL << ADC_ISR_OVR_Pos) /*!< 0x00000010 */ +#define ADC_ISR_OVR ADC_ISR_OVR_Msk /*!< ADC group regular overrun flag */ +#define ADC_ISR_JEOC_Pos (5U) +#define ADC_ISR_JEOC_Msk (0x1UL << ADC_ISR_JEOC_Pos) /*!< 0x00000020 */ +#define ADC_ISR_JEOC ADC_ISR_JEOC_Msk /*!< ADC group injected end of unitary conversion flag */ +#define ADC_ISR_JEOS_Pos (6U) +#define ADC_ISR_JEOS_Msk (0x1UL << ADC_ISR_JEOS_Pos) /*!< 0x00000040 */ +#define ADC_ISR_JEOS ADC_ISR_JEOS_Msk /*!< ADC group injected end of sequence conversions flag */ +#define ADC_ISR_AWD1_Pos (7U) +#define ADC_ISR_AWD1_Msk (0x1UL << ADC_ISR_AWD1_Pos) /*!< 0x00000080 */ +#define ADC_ISR_AWD1 ADC_ISR_AWD1_Msk /*!< ADC analog watchdog 1 flag */ +#define ADC_ISR_AWD2_Pos (8U) +#define ADC_ISR_AWD2_Msk (0x1UL << ADC_ISR_AWD2_Pos) /*!< 0x00000100 */ +#define ADC_ISR_AWD2 ADC_ISR_AWD2_Msk /*!< ADC analog watchdog 2 flag */ +#define ADC_ISR_AWD3_Pos (9U) +#define ADC_ISR_AWD3_Msk (0x1UL << ADC_ISR_AWD3_Pos) /*!< 0x00000200 */ +#define ADC_ISR_AWD3 ADC_ISR_AWD3_Msk /*!< ADC analog watchdog 3 flag */ +#define ADC_ISR_JQOVF_Pos (10U) +#define ADC_ISR_JQOVF_Msk (0x1UL << ADC_ISR_JQOVF_Pos) /*!< 0x00000400 */ +#define ADC_ISR_JQOVF ADC_ISR_JQOVF_Msk /*!< ADC group injected contexts queue overflow flag */ + +/******************** Bit definition for ADC_IER register *******************/ +#define ADC_IER_ADRDYIE_Pos (0U) +#define ADC_IER_ADRDYIE_Msk (0x1UL << ADC_IER_ADRDYIE_Pos) /*!< 0x00000001 */ +#define ADC_IER_ADRDYIE ADC_IER_ADRDYIE_Msk /*!< ADC ready interrupt */ +#define ADC_IER_EOSMPIE_Pos (1U) +#define ADC_IER_EOSMPIE_Msk (0x1UL << ADC_IER_EOSMPIE_Pos) /*!< 0x00000002 */ +#define ADC_IER_EOSMPIE ADC_IER_EOSMPIE_Msk /*!< ADC group regular end of sampling interrupt */ +#define ADC_IER_EOCIE_Pos (2U) +#define ADC_IER_EOCIE_Msk (0x1UL << ADC_IER_EOCIE_Pos) /*!< 0x00000004 */ +#define ADC_IER_EOCIE ADC_IER_EOCIE_Msk /*!< ADC group regular end of unitary conversion interrupt */ +#define ADC_IER_EOSIE_Pos (3U) +#define ADC_IER_EOSIE_Msk (0x1UL << ADC_IER_EOSIE_Pos) /*!< 0x00000008 */ +#define ADC_IER_EOSIE ADC_IER_EOSIE_Msk /*!< ADC group regular end of sequence conversions interrupt */ +#define ADC_IER_OVRIE_Pos (4U) +#define ADC_IER_OVRIE_Msk (0x1UL << ADC_IER_OVRIE_Pos) /*!< 0x00000010 */ +#define ADC_IER_OVRIE ADC_IER_OVRIE_Msk /*!< ADC group regular overrun interrupt */ +#define ADC_IER_JEOCIE_Pos (5U) +#define ADC_IER_JEOCIE_Msk (0x1UL << ADC_IER_JEOCIE_Pos) /*!< 0x00000020 */ +#define ADC_IER_JEOCIE ADC_IER_JEOCIE_Msk /*!< ADC group injected end of unitary conversion interrupt */ +#define ADC_IER_JEOSIE_Pos (6U) +#define ADC_IER_JEOSIE_Msk (0x1UL << ADC_IER_JEOSIE_Pos) /*!< 0x00000040 */ +#define ADC_IER_JEOSIE ADC_IER_JEOSIE_Msk /*!< ADC group injected end of sequence conversions interrupt */ +#define ADC_IER_AWD1IE_Pos (7U) +#define ADC_IER_AWD1IE_Msk (0x1UL << ADC_IER_AWD1IE_Pos) /*!< 0x00000080 */ +#define ADC_IER_AWD1IE ADC_IER_AWD1IE_Msk /*!< ADC analog watchdog 1 interrupt */ +#define ADC_IER_AWD2IE_Pos (8U) +#define ADC_IER_AWD2IE_Msk (0x1UL << ADC_IER_AWD2IE_Pos) /*!< 0x00000100 */ +#define ADC_IER_AWD2IE ADC_IER_AWD2IE_Msk /*!< ADC analog watchdog 2 interrupt */ +#define ADC_IER_AWD3IE_Pos (9U) +#define ADC_IER_AWD3IE_Msk (0x1UL << ADC_IER_AWD3IE_Pos) /*!< 0x00000200 */ +#define ADC_IER_AWD3IE ADC_IER_AWD3IE_Msk /*!< ADC analog watchdog 3 interrupt */ +#define ADC_IER_JQOVFIE_Pos (10U) +#define ADC_IER_JQOVFIE_Msk (0x1UL << ADC_IER_JQOVFIE_Pos) /*!< 0x00000400 */ +#define ADC_IER_JQOVFIE ADC_IER_JQOVFIE_Msk /*!< ADC group injected contexts queue overflow interrupt */ + +/******************** Bit definition for ADC_CR register ********************/ +#define ADC_CR_ADEN_Pos (0U) +#define ADC_CR_ADEN_Msk (0x1UL << ADC_CR_ADEN_Pos) /*!< 0x00000001 */ +#define ADC_CR_ADEN ADC_CR_ADEN_Msk /*!< ADC enable */ +#define ADC_CR_ADDIS_Pos (1U) +#define ADC_CR_ADDIS_Msk (0x1UL << ADC_CR_ADDIS_Pos) /*!< 0x00000002 */ +#define ADC_CR_ADDIS ADC_CR_ADDIS_Msk /*!< ADC disable */ +#define ADC_CR_ADSTART_Pos (2U) +#define ADC_CR_ADSTART_Msk (0x1UL << ADC_CR_ADSTART_Pos) /*!< 0x00000004 */ +#define ADC_CR_ADSTART ADC_CR_ADSTART_Msk /*!< ADC group regular conversion start */ +#define ADC_CR_JADSTART_Pos (3U) +#define ADC_CR_JADSTART_Msk (0x1UL << ADC_CR_JADSTART_Pos) /*!< 0x00000008 */ +#define ADC_CR_JADSTART ADC_CR_JADSTART_Msk /*!< ADC group injected conversion start */ +#define ADC_CR_ADSTP_Pos (4U) +#define ADC_CR_ADSTP_Msk (0x1UL << ADC_CR_ADSTP_Pos) /*!< 0x00000010 */ +#define ADC_CR_ADSTP ADC_CR_ADSTP_Msk /*!< ADC group regular conversion stop */ +#define ADC_CR_JADSTP_Pos (5U) +#define ADC_CR_JADSTP_Msk (0x1UL << ADC_CR_JADSTP_Pos) /*!< 0x00000020 */ +#define ADC_CR_JADSTP ADC_CR_JADSTP_Msk /*!< ADC group injected conversion stop */ +#define ADC_CR_ADVREGEN_Pos (28U) +#define ADC_CR_ADVREGEN_Msk (0x1UL << ADC_CR_ADVREGEN_Pos) /*!< 0x10000000 */ +#define ADC_CR_ADVREGEN ADC_CR_ADVREGEN_Msk /*!< ADC voltage regulator enable */ +#define ADC_CR_DEEPPWD_Pos (29U) +#define ADC_CR_DEEPPWD_Msk (0x1UL << ADC_CR_DEEPPWD_Pos) /*!< 0x20000000 */ +#define ADC_CR_DEEPPWD ADC_CR_DEEPPWD_Msk /*!< ADC deep power down enable */ +#define ADC_CR_ADCALDIF_Pos (30U) +#define ADC_CR_ADCALDIF_Msk (0x1UL << ADC_CR_ADCALDIF_Pos) /*!< 0x40000000 */ +#define ADC_CR_ADCALDIF ADC_CR_ADCALDIF_Msk /*!< ADC differential mode for calibration */ +#define ADC_CR_ADCAL_Pos (31U) +#define ADC_CR_ADCAL_Msk (0x1UL << ADC_CR_ADCAL_Pos) /*!< 0x80000000 */ +#define ADC_CR_ADCAL ADC_CR_ADCAL_Msk /*!< ADC calibration */ + +/******************** Bit definition for ADC_CFGR register ******************/ +#define ADC_CFGR_DMAEN_Pos (0U) +#define ADC_CFGR_DMAEN_Msk (0x1UL << ADC_CFGR_DMAEN_Pos) /*!< 0x00000001 */ +#define ADC_CFGR_DMAEN ADC_CFGR_DMAEN_Msk /*!< ADC DMA transfer enable */ +#define ADC_CFGR_DMACFG_Pos (1U) +#define ADC_CFGR_DMACFG_Msk (0x1UL << ADC_CFGR_DMACFG_Pos) /*!< 0x00000002 */ +#define ADC_CFGR_DMACFG ADC_CFGR_DMACFG_Msk /*!< ADC DMA transfer configuration */ + +#define ADC_CFGR_RES_Pos (3U) +#define ADC_CFGR_RES_Msk (0x3UL << ADC_CFGR_RES_Pos) /*!< 0x00000018 */ +#define ADC_CFGR_RES ADC_CFGR_RES_Msk /*!< ADC data resolution */ +#define ADC_CFGR_RES_0 (0x1UL << ADC_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC_CFGR_RES_1 (0x2UL << ADC_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR_EXTSEL_Pos (5U) +#define ADC_CFGR_EXTSEL_Msk (0x1FUL << ADC_CFGR_EXTSEL_Pos) /*!< 0x000003E0 */ +#define ADC_CFGR_EXTSEL ADC_CFGR_EXTSEL_Msk /*!< ADC group regular external trigger source */ +#define ADC_CFGR_EXTSEL_0 (0x1UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_CFGR_EXTSEL_1 (0x2UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000040 */ +#define ADC_CFGR_EXTSEL_2 (0x4UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000080 */ +#define ADC_CFGR_EXTSEL_3 (0x8UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000100 */ +#define ADC_CFGR_EXTSEL_4 (0x10UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000200 */ + +#define ADC_CFGR_EXTEN_Pos (10U) +#define ADC_CFGR_EXTEN_Msk (0x3UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000C00 */ +#define ADC_CFGR_EXTEN ADC_CFGR_EXTEN_Msk /*!< ADC group regular external trigger polarity */ +#define ADC_CFGR_EXTEN_0 (0x1UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000400 */ +#define ADC_CFGR_EXTEN_1 (0x2UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000800 */ + +#define ADC_CFGR_OVRMOD_Pos (12U) +#define ADC_CFGR_OVRMOD_Msk (0x1UL << ADC_CFGR_OVRMOD_Pos) /*!< 0x00001000 */ +#define ADC_CFGR_OVRMOD ADC_CFGR_OVRMOD_Msk /*!< ADC group regular overrun configuration */ +#define ADC_CFGR_CONT_Pos (13U) +#define ADC_CFGR_CONT_Msk (0x1UL << ADC_CFGR_CONT_Pos) /*!< 0x00002000 */ +#define ADC_CFGR_CONT ADC_CFGR_CONT_Msk /*!< ADC group regular continuous conversion mode */ +#define ADC_CFGR_AUTDLY_Pos (14U) +#define ADC_CFGR_AUTDLY_Msk (0x1UL << ADC_CFGR_AUTDLY_Pos) /*!< 0x00004000 */ +#define ADC_CFGR_AUTDLY ADC_CFGR_AUTDLY_Msk /*!< ADC low power auto wait */ +#define ADC_CFGR_ALIGN_Pos (15U) +#define ADC_CFGR_ALIGN_Msk (0x1UL << ADC_CFGR_ALIGN_Pos) /*!< 0x00008000 */ +#define ADC_CFGR_ALIGN ADC_CFGR_ALIGN_Msk /*!< ADC data alignment */ +#define ADC_CFGR_DISCEN_Pos (16U) +#define ADC_CFGR_DISCEN_Msk (0x1UL << ADC_CFGR_DISCEN_Pos) /*!< 0x00010000 */ +#define ADC_CFGR_DISCEN ADC_CFGR_DISCEN_Msk /*!< ADC group regular sequencer discontinuous mode */ + +#define ADC_CFGR_DISCNUM_Pos (17U) +#define ADC_CFGR_DISCNUM_Msk (0x7UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x000E0000 */ +#define ADC_CFGR_DISCNUM ADC_CFGR_DISCNUM_Msk /*!< ADC group regular sequencer discontinuous number of ranks */ +#define ADC_CFGR_DISCNUM_0 (0x1UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00020000 */ +#define ADC_CFGR_DISCNUM_1 (0x2UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00040000 */ +#define ADC_CFGR_DISCNUM_2 (0x4UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00080000 */ + +#define ADC_CFGR_JDISCEN_Pos (20U) +#define ADC_CFGR_JDISCEN_Msk (0x1UL << ADC_CFGR_JDISCEN_Pos) /*!< 0x00100000 */ +#define ADC_CFGR_JDISCEN ADC_CFGR_JDISCEN_Msk /*!< ADC group injected sequencer discontinuous mode */ +#define ADC_CFGR_JQM_Pos (21U) +#define ADC_CFGR_JQM_Msk (0x1UL << ADC_CFGR_JQM_Pos) /*!< 0x00200000 */ +#define ADC_CFGR_JQM ADC_CFGR_JQM_Msk /*!< ADC group injected contexts queue mode */ +#define ADC_CFGR_AWD1SGL_Pos (22U) +#define ADC_CFGR_AWD1SGL_Msk (0x1UL << ADC_CFGR_AWD1SGL_Pos) /*!< 0x00400000 */ +#define ADC_CFGR_AWD1SGL ADC_CFGR_AWD1SGL_Msk /*!< ADC analog watchdog 1 monitoring a single channel or all channels */ +#define ADC_CFGR_AWD1EN_Pos (23U) +#define ADC_CFGR_AWD1EN_Msk (0x1UL << ADC_CFGR_AWD1EN_Pos) /*!< 0x00800000 */ +#define ADC_CFGR_AWD1EN ADC_CFGR_AWD1EN_Msk /*!< ADC analog watchdog 1 enable on scope ADC group regular */ +#define ADC_CFGR_JAWD1EN_Pos (24U) +#define ADC_CFGR_JAWD1EN_Msk (0x1UL << ADC_CFGR_JAWD1EN_Pos) /*!< 0x01000000 */ +#define ADC_CFGR_JAWD1EN ADC_CFGR_JAWD1EN_Msk /*!< ADC analog watchdog 1 enable on scope ADC group injected */ +#define ADC_CFGR_JAUTO_Pos (25U) +#define ADC_CFGR_JAUTO_Msk (0x1UL << ADC_CFGR_JAUTO_Pos) /*!< 0x02000000 */ +#define ADC_CFGR_JAUTO ADC_CFGR_JAUTO_Msk /*!< ADC group injected automatic trigger mode */ + +#define ADC_CFGR_AWD1CH_Pos (26U) +#define ADC_CFGR_AWD1CH_Msk (0x1FUL << ADC_CFGR_AWD1CH_Pos) /*!< 0x7C000000 */ +#define ADC_CFGR_AWD1CH ADC_CFGR_AWD1CH_Msk /*!< ADC analog watchdog 1 monitored channel selection */ +#define ADC_CFGR_AWD1CH_0 (0x01UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x04000000 */ +#define ADC_CFGR_AWD1CH_1 (0x02UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x08000000 */ +#define ADC_CFGR_AWD1CH_2 (0x04UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x10000000 */ +#define ADC_CFGR_AWD1CH_3 (0x08UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x20000000 */ +#define ADC_CFGR_AWD1CH_4 (0x10UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x40000000 */ + +#define ADC_CFGR_JQDIS_Pos (31U) +#define ADC_CFGR_JQDIS_Msk (0x1UL << ADC_CFGR_JQDIS_Pos) /*!< 0x80000000 */ +#define ADC_CFGR_JQDIS ADC_CFGR_JQDIS_Msk /*!< ADC group injected contexts queue disable */ + +/******************** Bit definition for ADC_CFGR2 register *****************/ +#define ADC_CFGR2_ROVSE_Pos (0U) +#define ADC_CFGR2_ROVSE_Msk (0x1UL << ADC_CFGR2_ROVSE_Pos) /*!< 0x00000001 */ +#define ADC_CFGR2_ROVSE ADC_CFGR2_ROVSE_Msk /*!< ADC oversampler enable on scope ADC group regular */ +#define ADC_CFGR2_JOVSE_Pos (1U) +#define ADC_CFGR2_JOVSE_Msk (0x1UL << ADC_CFGR2_JOVSE_Pos) /*!< 0x00000002 */ +#define ADC_CFGR2_JOVSE ADC_CFGR2_JOVSE_Msk /*!< ADC oversampler enable on scope ADC group injected */ + +#define ADC_CFGR2_OVSR_Pos (2U) +#define ADC_CFGR2_OVSR_Msk (0x7UL << ADC_CFGR2_OVSR_Pos) /*!< 0x0000001C */ +#define ADC_CFGR2_OVSR ADC_CFGR2_OVSR_Msk /*!< ADC oversampling ratio */ +#define ADC_CFGR2_OVSR_0 (0x1UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000004 */ +#define ADC_CFGR2_OVSR_1 (0x2UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000008 */ +#define ADC_CFGR2_OVSR_2 (0x4UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR2_OVSS_Pos (5U) +#define ADC_CFGR2_OVSS_Msk (0xFUL << ADC_CFGR2_OVSS_Pos) /*!< 0x000001E0 */ +#define ADC_CFGR2_OVSS ADC_CFGR2_OVSS_Msk /*!< ADC oversampling shift */ +#define ADC_CFGR2_OVSS_0 (0x1UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000020 */ +#define ADC_CFGR2_OVSS_1 (0x2UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000040 */ +#define ADC_CFGR2_OVSS_2 (0x4UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000080 */ +#define ADC_CFGR2_OVSS_3 (0x8UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000100 */ + +#define ADC_CFGR2_TROVS_Pos (9U) +#define ADC_CFGR2_TROVS_Msk (0x1UL << ADC_CFGR2_TROVS_Pos) /*!< 0x00000200 */ +#define ADC_CFGR2_TROVS ADC_CFGR2_TROVS_Msk /*!< ADC oversampling discontinuous mode (triggered mode) for ADC group regular */ +#define ADC_CFGR2_ROVSM_Pos (10U) +#define ADC_CFGR2_ROVSM_Msk (0x1UL << ADC_CFGR2_ROVSM_Pos) /*!< 0x00000400 */ +#define ADC_CFGR2_ROVSM ADC_CFGR2_ROVSM_Msk /*!< ADC oversampling mode managing interlaced conversions of ADC group regular and group injected */ + +#define ADC_CFGR2_GCOMP_Pos (16U) +#define ADC_CFGR2_GCOMP_Msk (0x1UL << ADC_CFGR2_GCOMP_Pos) /*!< 0x00010000 */ +#define ADC_CFGR2_GCOMP ADC_CFGR2_GCOMP_Msk /*!< ADC Gain Compensation mode */ + +#define ADC_CFGR2_SWTRIG_Pos (25U) +#define ADC_CFGR2_SWTRIG_Msk (0x1UL << ADC_CFGR2_SWTRIG_Pos) /*!< 0x02000000 */ +#define ADC_CFGR2_SWTRIG ADC_CFGR2_SWTRIG_Msk /*!< ADC Software Trigger Bit for Sample time control trigger mode */ +#define ADC_CFGR2_BULB_Pos (26U) +#define ADC_CFGR2_BULB_Msk (0x1UL << ADC_CFGR2_BULB_Pos) /*!< 0x04000000 */ +#define ADC_CFGR2_BULB ADC_CFGR2_BULB_Msk /*!< ADC Bulb sampling mode */ +#define ADC_CFGR2_SMPTRIG_Pos (27U) +#define ADC_CFGR2_SMPTRIG_Msk (0x1UL << ADC_CFGR2_SMPTRIG_Pos) /*!< 0x08000000 */ +#define ADC_CFGR2_SMPTRIG ADC_CFGR2_SMPTRIG_Msk /*!< ADC Sample Time Control Trigger mode */ + +/******************** Bit definition for ADC_SMPR1 register *****************/ +#define ADC_SMPR1_SMP0_Pos (0U) +#define ADC_SMPR1_SMP0_Msk (0x7UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000007 */ +#define ADC_SMPR1_SMP0 ADC_SMPR1_SMP0_Msk /*!< ADC channel 0 sampling time selection */ +#define ADC_SMPR1_SMP0_0 (0x1UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000001 */ +#define ADC_SMPR1_SMP0_1 (0x2UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000002 */ +#define ADC_SMPR1_SMP0_2 (0x4UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR1_SMP1_Pos (3U) +#define ADC_SMPR1_SMP1_Msk (0x7UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000038 */ +#define ADC_SMPR1_SMP1 ADC_SMPR1_SMP1_Msk /*!< ADC channel 1 sampling time selection */ +#define ADC_SMPR1_SMP1_0 (0x1UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000008 */ +#define ADC_SMPR1_SMP1_1 (0x2UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000010 */ +#define ADC_SMPR1_SMP1_2 (0x4UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR1_SMP2_Pos (6U) +#define ADC_SMPR1_SMP2_Msk (0x7UL << ADC_SMPR1_SMP2_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR1_SMP2 ADC_SMPR1_SMP2_Msk /*!< ADC channel 2 sampling time selection */ +#define ADC_SMPR1_SMP2_0 (0x1UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000040 */ +#define ADC_SMPR1_SMP2_1 (0x2UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000080 */ +#define ADC_SMPR1_SMP2_2 (0x4UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR1_SMP3_Pos (9U) +#define ADC_SMPR1_SMP3_Msk (0x7UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR1_SMP3 ADC_SMPR1_SMP3_Msk /*!< ADC channel 3 sampling time selection */ +#define ADC_SMPR1_SMP3_0 (0x1UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000200 */ +#define ADC_SMPR1_SMP3_1 (0x2UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000400 */ +#define ADC_SMPR1_SMP3_2 (0x4UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR1_SMP4_Pos (12U) +#define ADC_SMPR1_SMP4_Msk (0x7UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00007000 */ +#define ADC_SMPR1_SMP4 ADC_SMPR1_SMP4_Msk /*!< ADC channel 4 sampling time selection */ +#define ADC_SMPR1_SMP4_0 (0x1UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00001000 */ +#define ADC_SMPR1_SMP4_1 (0x2UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00002000 */ +#define ADC_SMPR1_SMP4_2 (0x4UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR1_SMP5_Pos (15U) +#define ADC_SMPR1_SMP5_Msk (0x7UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00038000 */ +#define ADC_SMPR1_SMP5 ADC_SMPR1_SMP5_Msk /*!< ADC channel 5 sampling time selection */ +#define ADC_SMPR1_SMP5_0 (0x1UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00008000 */ +#define ADC_SMPR1_SMP5_1 (0x2UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00010000 */ +#define ADC_SMPR1_SMP5_2 (0x4UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR1_SMP6_Pos (18U) +#define ADC_SMPR1_SMP6_Msk (0x7UL << ADC_SMPR1_SMP6_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR1_SMP6 ADC_SMPR1_SMP6_Msk /*!< ADC channel 6 sampling time selection */ +#define ADC_SMPR1_SMP6_0 (0x1UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00040000 */ +#define ADC_SMPR1_SMP6_1 (0x2UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00080000 */ +#define ADC_SMPR1_SMP6_2 (0x4UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR1_SMP7_Pos (21U) +#define ADC_SMPR1_SMP7_Msk (0x7UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR1_SMP7 ADC_SMPR1_SMP7_Msk /*!< ADC channel 7 sampling time selection */ +#define ADC_SMPR1_SMP7_0 (0x1UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00200000 */ +#define ADC_SMPR1_SMP7_1 (0x2UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00400000 */ +#define ADC_SMPR1_SMP7_2 (0x4UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR1_SMP8_Pos (24U) +#define ADC_SMPR1_SMP8_Msk (0x7UL << ADC_SMPR1_SMP8_Pos) /*!< 0x07000000 */ +#define ADC_SMPR1_SMP8 ADC_SMPR1_SMP8_Msk /*!< ADC channel 8 sampling time selection */ +#define ADC_SMPR1_SMP8_0 (0x1UL << ADC_SMPR1_SMP8_Pos) /*!< 0x01000000 */ +#define ADC_SMPR1_SMP8_1 (0x2UL << ADC_SMPR1_SMP8_Pos) /*!< 0x02000000 */ +#define ADC_SMPR1_SMP8_2 (0x4UL << ADC_SMPR1_SMP8_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR1_SMP9_Pos (27U) +#define ADC_SMPR1_SMP9_Msk (0x7UL << ADC_SMPR1_SMP9_Pos) /*!< 0x38000000 */ +#define ADC_SMPR1_SMP9 ADC_SMPR1_SMP9_Msk /*!< ADC channel 9 sampling time selection */ +#define ADC_SMPR1_SMP9_0 (0x1UL << ADC_SMPR1_SMP9_Pos) /*!< 0x08000000 */ +#define ADC_SMPR1_SMP9_1 (0x2UL << ADC_SMPR1_SMP9_Pos) /*!< 0x10000000 */ +#define ADC_SMPR1_SMP9_2 (0x4UL << ADC_SMPR1_SMP9_Pos) /*!< 0x20000000 */ + +#define ADC_SMPR1_SMPPLUS_Pos (31U) +#define ADC_SMPR1_SMPPLUS_Msk (0x1UL << ADC_SMPR1_SMPPLUS_Pos) /*!< 0x80000000 */ +#define ADC_SMPR1_SMPPLUS ADC_SMPR1_SMPPLUS_Msk /*!< ADC channels sampling time additional setting */ + +/******************** Bit definition for ADC_SMPR2 register *****************/ +#define ADC_SMPR2_SMP10_Pos (0U) +#define ADC_SMPR2_SMP10_Msk (0x7UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000007 */ +#define ADC_SMPR2_SMP10 ADC_SMPR2_SMP10_Msk /*!< ADC channel 10 sampling time selection */ +#define ADC_SMPR2_SMP10_0 (0x1UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000001 */ +#define ADC_SMPR2_SMP10_1 (0x2UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000002 */ +#define ADC_SMPR2_SMP10_2 (0x4UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR2_SMP11_Pos (3U) +#define ADC_SMPR2_SMP11_Msk (0x7UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000038 */ +#define ADC_SMPR2_SMP11 ADC_SMPR2_SMP11_Msk /*!< ADC channel 11 sampling time selection */ +#define ADC_SMPR2_SMP11_0 (0x1UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000008 */ +#define ADC_SMPR2_SMP11_1 (0x2UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000010 */ +#define ADC_SMPR2_SMP11_2 (0x4UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR2_SMP12_Pos (6U) +#define ADC_SMPR2_SMP12_Msk (0x7UL << ADC_SMPR2_SMP12_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR2_SMP12 ADC_SMPR2_SMP12_Msk /*!< ADC channel 12 sampling time selection */ +#define ADC_SMPR2_SMP12_0 (0x1UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000040 */ +#define ADC_SMPR2_SMP12_1 (0x2UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000080 */ +#define ADC_SMPR2_SMP12_2 (0x4UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR2_SMP13_Pos (9U) +#define ADC_SMPR2_SMP13_Msk (0x7UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR2_SMP13 ADC_SMPR2_SMP13_Msk /*!< ADC channel 13 sampling time selection */ +#define ADC_SMPR2_SMP13_0 (0x1UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000200 */ +#define ADC_SMPR2_SMP13_1 (0x2UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000400 */ +#define ADC_SMPR2_SMP13_2 (0x4UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR2_SMP14_Pos (12U) +#define ADC_SMPR2_SMP14_Msk (0x7UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00007000 */ +#define ADC_SMPR2_SMP14 ADC_SMPR2_SMP14_Msk /*!< ADC channel 14 sampling time selection */ +#define ADC_SMPR2_SMP14_0 (0x1UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00001000 */ +#define ADC_SMPR2_SMP14_1 (0x2UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00002000 */ +#define ADC_SMPR2_SMP14_2 (0x4UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR2_SMP15_Pos (15U) +#define ADC_SMPR2_SMP15_Msk (0x7UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00038000 */ +#define ADC_SMPR2_SMP15 ADC_SMPR2_SMP15_Msk /*!< ADC channel 15 sampling time selection */ +#define ADC_SMPR2_SMP15_0 (0x1UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00008000 */ +#define ADC_SMPR2_SMP15_1 (0x2UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00010000 */ +#define ADC_SMPR2_SMP15_2 (0x4UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR2_SMP16_Pos (18U) +#define ADC_SMPR2_SMP16_Msk (0x7UL << ADC_SMPR2_SMP16_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR2_SMP16 ADC_SMPR2_SMP16_Msk /*!< ADC channel 16 sampling time selection */ +#define ADC_SMPR2_SMP16_0 (0x1UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00040000 */ +#define ADC_SMPR2_SMP16_1 (0x2UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00080000 */ +#define ADC_SMPR2_SMP16_2 (0x4UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR2_SMP17_Pos (21U) +#define ADC_SMPR2_SMP17_Msk (0x7UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR2_SMP17 ADC_SMPR2_SMP17_Msk /*!< ADC channel 17 sampling time selection */ +#define ADC_SMPR2_SMP17_0 (0x1UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00200000 */ +#define ADC_SMPR2_SMP17_1 (0x2UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00400000 */ +#define ADC_SMPR2_SMP17_2 (0x4UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR2_SMP18_Pos (24U) +#define ADC_SMPR2_SMP18_Msk (0x7UL << ADC_SMPR2_SMP18_Pos) /*!< 0x07000000 */ +#define ADC_SMPR2_SMP18 ADC_SMPR2_SMP18_Msk /*!< ADC channel 18 sampling time selection */ +#define ADC_SMPR2_SMP18_0 (0x1UL << ADC_SMPR2_SMP18_Pos) /*!< 0x01000000 */ +#define ADC_SMPR2_SMP18_1 (0x2UL << ADC_SMPR2_SMP18_Pos) /*!< 0x02000000 */ +#define ADC_SMPR2_SMP18_2 (0x4UL << ADC_SMPR2_SMP18_Pos) /*!< 0x04000000 */ + +/******************** Bit definition for ADC_TR1 register *******************/ +#define ADC_TR1_LT1_Pos (0U) +#define ADC_TR1_LT1_Msk (0xFFFUL << ADC_TR1_LT1_Pos) /*!< 0x00000FFF */ +#define ADC_TR1_LT1 ADC_TR1_LT1_Msk /*!< ADC analog watchdog 1 threshold low */ + +#define ADC_TR1_AWDFILT_Pos (12U) +#define ADC_TR1_AWDFILT_Msk (0x7UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00007000 */ +#define ADC_TR1_AWDFILT ADC_TR1_AWDFILT_Msk /*!< ADC analog watchdog filtering parameter */ +#define ADC_TR1_AWDFILT_0 (0x1UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00001000 */ +#define ADC_TR1_AWDFILT_1 (0x2UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00002000 */ +#define ADC_TR1_AWDFILT_2 (0x4UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00004000 */ + +#define ADC_TR1_HT1_Pos (16U) +#define ADC_TR1_HT1_Msk (0xFFFUL << ADC_TR1_HT1_Pos) /*!< 0x0FFF0000 */ +#define ADC_TR1_HT1 ADC_TR1_HT1_Msk /*!< ADC analog watchdog 1 threshold high */ + +/******************** Bit definition for ADC_TR2 register *******************/ +#define ADC_TR2_LT2_Pos (0U) +#define ADC_TR2_LT2_Msk (0xFFUL << ADC_TR2_LT2_Pos) /*!< 0x000000FF */ +#define ADC_TR2_LT2 ADC_TR2_LT2_Msk /*!< ADC analog watchdog 2 threshold low */ + +#define ADC_TR2_HT2_Pos (16U) +#define ADC_TR2_HT2_Msk (0xFFUL << ADC_TR2_HT2_Pos) /*!< 0x00FF0000 */ +#define ADC_TR2_HT2 ADC_TR2_HT2_Msk /*!< ADC analog watchdog 2 threshold high */ + +/******************** Bit definition for ADC_TR3 register *******************/ +#define ADC_TR3_LT3_Pos (0U) +#define ADC_TR3_LT3_Msk (0xFFUL << ADC_TR3_LT3_Pos) /*!< 0x000000FF */ +#define ADC_TR3_LT3 ADC_TR3_LT3_Msk /*!< ADC analog watchdog 3 threshold low */ + +#define ADC_TR3_HT3_Pos (16U) +#define ADC_TR3_HT3_Msk (0xFFUL << ADC_TR3_HT3_Pos) /*!< 0x00FF0000 */ +#define ADC_TR3_HT3 ADC_TR3_HT3_Msk /*!< ADC analog watchdog 3 threshold high */ + +/******************** Bit definition for ADC_SQR1 register ******************/ +#define ADC_SQR1_L_Pos (0U) +#define ADC_SQR1_L_Msk (0xFUL << ADC_SQR1_L_Pos) /*!< 0x0000000F */ +#define ADC_SQR1_L ADC_SQR1_L_Msk /*!< ADC group regular sequencer scan length */ +#define ADC_SQR1_L_0 (0x1UL << ADC_SQR1_L_Pos) /*!< 0x00000001 */ +#define ADC_SQR1_L_1 (0x2UL << ADC_SQR1_L_Pos) /*!< 0x00000002 */ +#define ADC_SQR1_L_2 (0x4UL << ADC_SQR1_L_Pos) /*!< 0x00000004 */ +#define ADC_SQR1_L_3 (0x8UL << ADC_SQR1_L_Pos) /*!< 0x00000008 */ + +#define ADC_SQR1_SQ1_Pos (6U) +#define ADC_SQR1_SQ1_Msk (0x1FUL << ADC_SQR1_SQ1_Pos) /*!< 0x000007C0 */ +#define ADC_SQR1_SQ1 ADC_SQR1_SQ1_Msk /*!< ADC group regular sequencer rank 1 */ +#define ADC_SQR1_SQ1_0 (0x01UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000040 */ +#define ADC_SQR1_SQ1_1 (0x02UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000080 */ +#define ADC_SQR1_SQ1_2 (0x04UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000100 */ +#define ADC_SQR1_SQ1_3 (0x08UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000200 */ +#define ADC_SQR1_SQ1_4 (0x10UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000400 */ + +#define ADC_SQR1_SQ2_Pos (12U) +#define ADC_SQR1_SQ2_Msk (0x1FUL << ADC_SQR1_SQ2_Pos) /*!< 0x0001F000 */ +#define ADC_SQR1_SQ2 ADC_SQR1_SQ2_Msk /*!< ADC group regular sequencer rank 2 */ +#define ADC_SQR1_SQ2_0 (0x01UL << ADC_SQR1_SQ2_Pos) /*!< 0x00001000 */ +#define ADC_SQR1_SQ2_1 (0x02UL << ADC_SQR1_SQ2_Pos) /*!< 0x00002000 */ +#define ADC_SQR1_SQ2_2 (0x04UL << ADC_SQR1_SQ2_Pos) /*!< 0x00004000 */ +#define ADC_SQR1_SQ2_3 (0x08UL << ADC_SQR1_SQ2_Pos) /*!< 0x00008000 */ +#define ADC_SQR1_SQ2_4 (0x10UL << ADC_SQR1_SQ2_Pos) /*!< 0x00010000 */ + +#define ADC_SQR1_SQ3_Pos (18U) +#define ADC_SQR1_SQ3_Msk (0x1FUL << ADC_SQR1_SQ3_Pos) /*!< 0x007C0000 */ +#define ADC_SQR1_SQ3 ADC_SQR1_SQ3_Msk /*!< ADC group regular sequencer rank 3 */ +#define ADC_SQR1_SQ3_0 (0x01UL << ADC_SQR1_SQ3_Pos) /*!< 0x00040000 */ +#define ADC_SQR1_SQ3_1 (0x02UL << ADC_SQR1_SQ3_Pos) /*!< 0x00080000 */ +#define ADC_SQR1_SQ3_2 (0x04UL << ADC_SQR1_SQ3_Pos) /*!< 0x00100000 */ +#define ADC_SQR1_SQ3_3 (0x08UL << ADC_SQR1_SQ3_Pos) /*!< 0x00200000 */ +#define ADC_SQR1_SQ3_4 (0x10UL<< ADC_SQR1_SQ3_Pos) /*!< 0x00400000 */ + +#define ADC_SQR1_SQ4_Pos (24U) +#define ADC_SQR1_SQ4_Msk (0x1FUL << ADC_SQR1_SQ4_Pos) /*!< 0x1F000000 */ +#define ADC_SQR1_SQ4 ADC_SQR1_SQ4_Msk /*!< ADC group regular sequencer rank 4 */ +#define ADC_SQR1_SQ4_0 (0x01UL << ADC_SQR1_SQ4_Pos) /*!< 0x01000000 */ +#define ADC_SQR1_SQ4_1 (0x02UL << ADC_SQR1_SQ4_Pos) /*!< 0x02000000 */ +#define ADC_SQR1_SQ4_2 (0x04UL << ADC_SQR1_SQ4_Pos) /*!< 0x04000000 */ +#define ADC_SQR1_SQ4_3 (0x08UL << ADC_SQR1_SQ4_Pos) /*!< 0x08000000 */ +#define ADC_SQR1_SQ4_4 (0x10UL << ADC_SQR1_SQ4_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR2 register ******************/ +#define ADC_SQR2_SQ5_Pos (0U) +#define ADC_SQR2_SQ5_Msk (0x1FUL << ADC_SQR2_SQ5_Pos) /*!< 0x0000001F */ +#define ADC_SQR2_SQ5 ADC_SQR2_SQ5_Msk /*!< ADC group regular sequencer rank 5 */ +#define ADC_SQR2_SQ5_0 (0x01UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000001 */ +#define ADC_SQR2_SQ5_1 (0x02UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000002 */ +#define ADC_SQR2_SQ5_2 (0x04UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000004 */ +#define ADC_SQR2_SQ5_3 (0x08UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000008 */ +#define ADC_SQR2_SQ5_4 (0x10UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000010 */ + +#define ADC_SQR2_SQ6_Pos (6U) +#define ADC_SQR2_SQ6_Msk (0x1FUL << ADC_SQR2_SQ6_Pos) /*!< 0x000007C0 */ +#define ADC_SQR2_SQ6 ADC_SQR2_SQ6_Msk /*!< ADC group regular sequencer rank 6 */ +#define ADC_SQR2_SQ6_0 (0x01UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000040 */ +#define ADC_SQR2_SQ6_1 (0x02UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000080 */ +#define ADC_SQR2_SQ6_2 (0x04UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000100 */ +#define ADC_SQR2_SQ6_3 (0x08UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000200 */ +#define ADC_SQR2_SQ6_4 (0x10UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000400 */ + +#define ADC_SQR2_SQ7_Pos (12U) +#define ADC_SQR2_SQ7_Msk (0x1FUL << ADC_SQR2_SQ7_Pos) /*!< 0x0001F000 */ +#define ADC_SQR2_SQ7 ADC_SQR2_SQ7_Msk /*!< ADC group regular sequencer rank 7 */ +#define ADC_SQR2_SQ7_0 (0x01UL << ADC_SQR2_SQ7_Pos) /*!< 0x00001000 */ +#define ADC_SQR2_SQ7_1 (0x02UL << ADC_SQR2_SQ7_Pos) /*!< 0x00002000 */ +#define ADC_SQR2_SQ7_2 (0x04UL << ADC_SQR2_SQ7_Pos) /*!< 0x00004000 */ +#define ADC_SQR2_SQ7_3 (0x08UL << ADC_SQR2_SQ7_Pos) /*!< 0x00008000 */ +#define ADC_SQR2_SQ7_4 (0x10UL << ADC_SQR2_SQ7_Pos) /*!< 0x00010000 */ + +#define ADC_SQR2_SQ8_Pos (18U) +#define ADC_SQR2_SQ8_Msk (0x1FUL << ADC_SQR2_SQ8_Pos) /*!< 0x007C0000 */ +#define ADC_SQR2_SQ8 ADC_SQR2_SQ8_Msk /*!< ADC group regular sequencer rank 8 */ +#define ADC_SQR2_SQ8_0 (0x01UL << ADC_SQR2_SQ8_Pos) /*!< 0x00040000 */ +#define ADC_SQR2_SQ8_1 (0x02UL << ADC_SQR2_SQ8_Pos) /*!< 0x00080000 */ +#define ADC_SQR2_SQ8_2 (0x04UL << ADC_SQR2_SQ8_Pos) /*!< 0x00100000 */ +#define ADC_SQR2_SQ8_3 (0x08UL << ADC_SQR2_SQ8_Pos) /*!< 0x00200000 */ +#define ADC_SQR2_SQ8_4 (0x10UL << ADC_SQR2_SQ8_Pos) /*!< 0x00400000 */ + +#define ADC_SQR2_SQ9_Pos (24U) +#define ADC_SQR2_SQ9_Msk (0x1FUL << ADC_SQR2_SQ9_Pos) /*!< 0x1F000000 */ +#define ADC_SQR2_SQ9 ADC_SQR2_SQ9_Msk /*!< ADC group regular sequencer rank 9 */ +#define ADC_SQR2_SQ9_0 (0x01UL << ADC_SQR2_SQ9_Pos) /*!< 0x01000000 */ +#define ADC_SQR2_SQ9_1 (0x02UL << ADC_SQR2_SQ9_Pos) /*!< 0x02000000 */ +#define ADC_SQR2_SQ9_2 (0x04UL << ADC_SQR2_SQ9_Pos) /*!< 0x04000000 */ +#define ADC_SQR2_SQ9_3 (0x08UL << ADC_SQR2_SQ9_Pos) /*!< 0x08000000 */ +#define ADC_SQR2_SQ9_4 (0x10UL << ADC_SQR2_SQ9_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR3 register ******************/ +#define ADC_SQR3_SQ10_Pos (0U) +#define ADC_SQR3_SQ10_Msk (0x1FUL << ADC_SQR3_SQ10_Pos) /*!< 0x0000001F */ +#define ADC_SQR3_SQ10 ADC_SQR3_SQ10_Msk /*!< ADC group regular sequencer rank 10 */ +#define ADC_SQR3_SQ10_0 (0x01UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000001 */ +#define ADC_SQR3_SQ10_1 (0x02UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000002 */ +#define ADC_SQR3_SQ10_2 (0x04UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000004 */ +#define ADC_SQR3_SQ10_3 (0x08UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000008 */ +#define ADC_SQR3_SQ10_4 (0x10UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000010 */ + +#define ADC_SQR3_SQ11_Pos (6U) +#define ADC_SQR3_SQ11_Msk (0x1FUL << ADC_SQR3_SQ11_Pos) /*!< 0x000007C0 */ +#define ADC_SQR3_SQ11 ADC_SQR3_SQ11_Msk /*!< ADC group regular sequencer rank 11 */ +#define ADC_SQR3_SQ11_0 (0x01UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000040 */ +#define ADC_SQR3_SQ11_1 (0x02UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000080 */ +#define ADC_SQR3_SQ11_2 (0x04UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000100 */ +#define ADC_SQR3_SQ11_3 (0x08UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000200 */ +#define ADC_SQR3_SQ11_4 (0x10UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000400 */ + +#define ADC_SQR3_SQ12_Pos (12U) +#define ADC_SQR3_SQ12_Msk (0x1FUL << ADC_SQR3_SQ12_Pos) /*!< 0x0001F000 */ +#define ADC_SQR3_SQ12 ADC_SQR3_SQ12_Msk /*!< ADC group regular sequencer rank 12 */ +#define ADC_SQR3_SQ12_0 (0x01UL << ADC_SQR3_SQ12_Pos) /*!< 0x00001000 */ +#define ADC_SQR3_SQ12_1 (0x02UL << ADC_SQR3_SQ12_Pos) /*!< 0x00002000 */ +#define ADC_SQR3_SQ12_2 (0x04UL << ADC_SQR3_SQ12_Pos) /*!< 0x00004000 */ +#define ADC_SQR3_SQ12_3 (0x08UL << ADC_SQR3_SQ12_Pos) /*!< 0x00008000 */ +#define ADC_SQR3_SQ12_4 (0x10UL << ADC_SQR3_SQ12_Pos) /*!< 0x00010000 */ + +#define ADC_SQR3_SQ13_Pos (18U) +#define ADC_SQR3_SQ13_Msk (0x1FUL << ADC_SQR3_SQ13_Pos) /*!< 0x007C0000 */ +#define ADC_SQR3_SQ13 ADC_SQR3_SQ13_Msk /*!< ADC group regular sequencer rank 13 */ +#define ADC_SQR3_SQ13_0 (0x01UL << ADC_SQR3_SQ13_Pos) /*!< 0x00040000 */ +#define ADC_SQR3_SQ13_1 (0x02UL << ADC_SQR3_SQ13_Pos) /*!< 0x00080000 */ +#define ADC_SQR3_SQ13_2 (0x04UL << ADC_SQR3_SQ13_Pos) /*!< 0x00100000 */ +#define ADC_SQR3_SQ13_3 (0x08UL << ADC_SQR3_SQ13_Pos) /*!< 0x00200000 */ +#define ADC_SQR3_SQ13_4 (0x10UL << ADC_SQR3_SQ13_Pos) /*!< 0x00400000 */ + +#define ADC_SQR3_SQ14_Pos (24U) +#define ADC_SQR3_SQ14_Msk (0x1FUL << ADC_SQR3_SQ14_Pos) /*!< 0x1F000000 */ +#define ADC_SQR3_SQ14 ADC_SQR3_SQ14_Msk /*!< ADC group regular sequencer rank 14 */ +#define ADC_SQR3_SQ14_0 (0x01UL << ADC_SQR3_SQ14_Pos) /*!< 0x01000000 */ +#define ADC_SQR3_SQ14_1 (0x02UL << ADC_SQR3_SQ14_Pos) /*!< 0x02000000 */ +#define ADC_SQR3_SQ14_2 (0x04UL << ADC_SQR3_SQ14_Pos) /*!< 0x04000000 */ +#define ADC_SQR3_SQ14_3 (0x08UL << ADC_SQR3_SQ14_Pos) /*!< 0x08000000 */ +#define ADC_SQR3_SQ14_4 (0x10UL << ADC_SQR3_SQ14_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR4 register ******************/ +#define ADC_SQR4_SQ15_Pos (0U) +#define ADC_SQR4_SQ15_Msk (0x1FUL << ADC_SQR4_SQ15_Pos) /*!< 0x0000001F */ +#define ADC_SQR4_SQ15 ADC_SQR4_SQ15_Msk /*!< ADC group regular sequencer rank 15 */ +#define ADC_SQR4_SQ15_0 (0x01UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000001 */ +#define ADC_SQR4_SQ15_1 (0x02UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000002 */ +#define ADC_SQR4_SQ15_2 (0x04UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000004 */ +#define ADC_SQR4_SQ15_3 (0x08UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000008 */ +#define ADC_SQR4_SQ15_4 (0x10UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000010 */ + +#define ADC_SQR4_SQ16_Pos (6U) +#define ADC_SQR4_SQ16_Msk (0x1FUL << ADC_SQR4_SQ16_Pos) /*!< 0x000007C0 */ +#define ADC_SQR4_SQ16 ADC_SQR4_SQ16_Msk /*!< ADC group regular sequencer rank 16 */ +#define ADC_SQR4_SQ16_0 (0x01UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000040 */ +#define ADC_SQR4_SQ16_1 (0x02UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000080 */ +#define ADC_SQR4_SQ16_2 (0x04UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000100 */ +#define ADC_SQR4_SQ16_3 (0x08UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000200 */ +#define ADC_SQR4_SQ16_4 (0x10UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000400 */ + +/******************** Bit definition for ADC_DR register ********************/ +#define ADC_DR_RDATA_Pos (0U) +#define ADC_DR_RDATA_Msk (0xFFFFUL << ADC_DR_RDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_DR_RDATA ADC_DR_RDATA_Msk /*!< ADC group regular conversion data */ + +/******************** Bit definition for ADC_JSQR register ******************/ +#define ADC_JSQR_JL_Pos (0U) +#define ADC_JSQR_JL_Msk (0x3UL << ADC_JSQR_JL_Pos) /*!< 0x00000003 */ +#define ADC_JSQR_JL ADC_JSQR_JL_Msk /*!< ADC group injected sequencer scan length */ +#define ADC_JSQR_JL_0 (0x1UL << ADC_JSQR_JL_Pos) /*!< 0x00000001 */ +#define ADC_JSQR_JL_1 (0x2UL << ADC_JSQR_JL_Pos) /*!< 0x00000002 */ + +#define ADC_JSQR_JEXTSEL_Pos (2U) +#define ADC_JSQR_JEXTSEL_Msk (0x1FUL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x0000007C */ +#define ADC_JSQR_JEXTSEL ADC_JSQR_JEXTSEL_Msk /*!< ADC group injected external trigger source */ +#define ADC_JSQR_JEXTSEL_0 (0x1UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000004 */ +#define ADC_JSQR_JEXTSEL_1 (0x2UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000008 */ +#define ADC_JSQR_JEXTSEL_2 (0x4UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000010 */ +#define ADC_JSQR_JEXTSEL_3 (0x8UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_JSQR_JEXTSEL_4 (0x10UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000040 */ + +#define ADC_JSQR_JEXTEN_Pos (7U) +#define ADC_JSQR_JEXTEN_Msk (0x3UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000180 */ +#define ADC_JSQR_JEXTEN ADC_JSQR_JEXTEN_Msk /*!< ADC group injected external trigger polarity */ +#define ADC_JSQR_JEXTEN_0 (0x1UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000080 */ +#define ADC_JSQR_JEXTEN_1 (0x2UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000100 */ + +#define ADC_JSQR_JSQ1_Pos (9U) +#define ADC_JSQR_JSQ1_Msk (0x1FUL << ADC_JSQR_JSQ1_Pos) /*!< 0x00003E00 */ +#define ADC_JSQR_JSQ1 ADC_JSQR_JSQ1_Msk /*!< ADC group injected sequencer rank 1 */ +#define ADC_JSQR_JSQ1_0 (0x01UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000200 */ +#define ADC_JSQR_JSQ1_1 (0x02UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000400 */ +#define ADC_JSQR_JSQ1_2 (0x04UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000800 */ +#define ADC_JSQR_JSQ1_3 (0x08UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00001000 */ +#define ADC_JSQR_JSQ1_4 (0x10UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00002000 */ + +#define ADC_JSQR_JSQ2_Pos (15U) +#define ADC_JSQR_JSQ2_Msk (0x1FUL << ADC_JSQR_JSQ2_Pos) /*!< 0x0007C000 */ +#define ADC_JSQR_JSQ2 ADC_JSQR_JSQ2_Msk /*!< ADC group injected sequencer rank 2 */ +#define ADC_JSQR_JSQ2_0 (0x01UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00004000 */ +#define ADC_JSQR_JSQ2_1 (0x02UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00008000 */ +#define ADC_JSQR_JSQ2_2 (0x04UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00010000 */ +#define ADC_JSQR_JSQ2_3 (0x08UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00020000 */ +#define ADC_JSQR_JSQ2_4 (0x10UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00040000 */ + +#define ADC_JSQR_JSQ3_Pos (21U) +#define ADC_JSQR_JSQ3_Msk (0x1FUL << ADC_JSQR_JSQ3_Pos) /*!< 0x03E00000 */ +#define ADC_JSQR_JSQ3 ADC_JSQR_JSQ3_Msk /*!< ADC group injected sequencer rank 3 */ +#define ADC_JSQR_JSQ3_0 (0x01UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00200000 */ +#define ADC_JSQR_JSQ3_1 (0x02UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00400000 */ +#define ADC_JSQR_JSQ3_2 (0x04UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00800000 */ +#define ADC_JSQR_JSQ3_3 (0x08UL << ADC_JSQR_JSQ3_Pos) /*!< 0x01000000 */ +#define ADC_JSQR_JSQ3_4 (0x10UL << ADC_JSQR_JSQ3_Pos) /*!< 0x02000000 */ + +#define ADC_JSQR_JSQ4_Pos (27U) +#define ADC_JSQR_JSQ4_Msk (0x1FUL << ADC_JSQR_JSQ4_Pos) /*!< 0xF8000000 */ +#define ADC_JSQR_JSQ4 ADC_JSQR_JSQ4_Msk /*!< ADC group injected sequencer rank 4 */ +#define ADC_JSQR_JSQ4_0 (0x01UL << ADC_JSQR_JSQ4_Pos) /*!< 0x08000000 */ +#define ADC_JSQR_JSQ4_1 (0x02UL << ADC_JSQR_JSQ4_Pos) /*!< 0x10000000 */ +#define ADC_JSQR_JSQ4_2 (0x04UL << ADC_JSQR_JSQ4_Pos) /*!< 0x20000000 */ +#define ADC_JSQR_JSQ4_3 (0x08UL << ADC_JSQR_JSQ4_Pos) /*!< 0x40000000 */ +#define ADC_JSQR_JSQ4_4 (0x10UL << ADC_JSQR_JSQ4_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_OFR1 register ******************/ +#define ADC_OFR1_OFFSET1_Pos (0U) +#define ADC_OFR1_OFFSET1_Msk (0xFFFUL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000FFF */ +#define ADC_OFR1_OFFSET1 ADC_OFR1_OFFSET1_Msk /*!< ADC offset number 1 offset level */ + +#define ADC_OFR1_OFFSETPOS_Pos (24U) +#define ADC_OFR1_OFFSETPOS_Msk (0x1UL << ADC_OFR1_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR1_OFFSETPOS ADC_OFR1_OFFSETPOS_Msk /*!< ADC offset number 1 positive */ +#define ADC_OFR1_SATEN_Pos (25U) +#define ADC_OFR1_SATEN_Msk (0x1UL << ADC_OFR1_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR1_SATEN ADC_OFR1_SATEN_Msk /*!< ADC offset number 1 saturation enable */ + +#define ADC_OFR1_OFFSET1_CH_Pos (26U) +#define ADC_OFR1_OFFSET1_CH_Msk (0x1FUL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR1_OFFSET1_CH ADC_OFR1_OFFSET1_CH_Msk /*!< ADC offset number 1 channel selection */ +#define ADC_OFR1_OFFSET1_CH_0 (0x01UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR1_OFFSET1_CH_1 (0x02UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR1_OFFSET1_CH_2 (0x04UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR1_OFFSET1_CH_3 (0x08UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR1_OFFSET1_CH_4 (0x10UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR1_OFFSET1_EN_Pos (31U) +#define ADC_OFR1_OFFSET1_EN_Msk (0x1UL << ADC_OFR1_OFFSET1_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR1_OFFSET1_EN ADC_OFR1_OFFSET1_EN_Msk /*!< ADC offset number 1 enable */ + +/******************** Bit definition for ADC_OFR2 register ******************/ +#define ADC_OFR2_OFFSET2_Pos (0U) +#define ADC_OFR2_OFFSET2_Msk (0xFFFUL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000FFF */ +#define ADC_OFR2_OFFSET2 ADC_OFR2_OFFSET2_Msk /*!< ADC offset number 2 offset level */ + +#define ADC_OFR2_OFFSETPOS_Pos (24U) +#define ADC_OFR2_OFFSETPOS_Msk (0x1UL << ADC_OFR2_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR2_OFFSETPOS ADC_OFR2_OFFSETPOS_Msk /*!< ADC offset number 2 positive */ +#define ADC_OFR2_SATEN_Pos (25U) +#define ADC_OFR2_SATEN_Msk (0x1UL << ADC_OFR2_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR2_SATEN ADC_OFR2_SATEN_Msk /*!< ADC offset number 2 saturation enable */ + +#define ADC_OFR2_OFFSET2_CH_Pos (26U) +#define ADC_OFR2_OFFSET2_CH_Msk (0x1FUL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR2_OFFSET2_CH ADC_OFR2_OFFSET2_CH_Msk /*!< ADC offset number 2 channel selection */ +#define ADC_OFR2_OFFSET2_CH_0 (0x01UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR2_OFFSET2_CH_1 (0x02UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR2_OFFSET2_CH_2 (0x04UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR2_OFFSET2_CH_3 (0x08UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR2_OFFSET2_CH_4 (0x10UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR2_OFFSET2_EN_Pos (31U) +#define ADC_OFR2_OFFSET2_EN_Msk (0x1UL << ADC_OFR2_OFFSET2_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR2_OFFSET2_EN ADC_OFR2_OFFSET2_EN_Msk /*!< ADC offset number 2 enable */ + +/******************** Bit definition for ADC_OFR3 register ******************/ +#define ADC_OFR3_OFFSET3_Pos (0U) +#define ADC_OFR3_OFFSET3_Msk (0xFFFUL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000FFF */ +#define ADC_OFR3_OFFSET3 ADC_OFR3_OFFSET3_Msk /*!< ADC offset number 3 offset level */ + +#define ADC_OFR3_OFFSETPOS_Pos (24U) +#define ADC_OFR3_OFFSETPOS_Msk (0x1UL << ADC_OFR3_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR3_OFFSETPOS ADC_OFR3_OFFSETPOS_Msk /*!< ADC offset number 3 positive */ +#define ADC_OFR3_SATEN_Pos (25U) +#define ADC_OFR3_SATEN_Msk (0x1UL << ADC_OFR3_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR3_SATEN ADC_OFR3_SATEN_Msk /*!< ADC offset number 3 saturation enable */ + +#define ADC_OFR3_OFFSET3_CH_Pos (26U) +#define ADC_OFR3_OFFSET3_CH_Msk (0x1FUL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR3_OFFSET3_CH ADC_OFR3_OFFSET3_CH_Msk /*!< ADC offset number 3 channel selection */ +#define ADC_OFR3_OFFSET3_CH_0 (0x01UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR3_OFFSET3_CH_1 (0x02UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR3_OFFSET3_CH_2 (0x04UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR3_OFFSET3_CH_3 (0x08UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR3_OFFSET3_CH_4 (0x10UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR3_OFFSET3_EN_Pos (31U) +#define ADC_OFR3_OFFSET3_EN_Msk (0x1UL << ADC_OFR3_OFFSET3_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR3_OFFSET3_EN ADC_OFR3_OFFSET3_EN_Msk /*!< ADC offset number 3 enable */ + +/******************** Bit definition for ADC_OFR4 register ******************/ +#define ADC_OFR4_OFFSET4_Pos (0U) +#define ADC_OFR4_OFFSET4_Msk (0xFFFUL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000FFF */ +#define ADC_OFR4_OFFSET4 ADC_OFR4_OFFSET4_Msk /*!< ADC offset number 4 offset level */ + +#define ADC_OFR4_OFFSETPOS_Pos (24U) +#define ADC_OFR4_OFFSETPOS_Msk (0x1UL << ADC_OFR4_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR4_OFFSETPOS ADC_OFR4_OFFSETPOS_Msk /*!< ADC offset number 4 positive */ +#define ADC_OFR4_SATEN_Pos (25U) +#define ADC_OFR4_SATEN_Msk (0x1UL << ADC_OFR4_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR4_SATEN ADC_OFR4_SATEN_Msk /*!< ADC offset number 4 saturation enable */ + +#define ADC_OFR4_OFFSET4_CH_Pos (26U) +#define ADC_OFR4_OFFSET4_CH_Msk (0x1FUL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR4_OFFSET4_CH ADC_OFR4_OFFSET4_CH_Msk /*!< ADC offset number 4 channel selection */ +#define ADC_OFR4_OFFSET4_CH_0 (0x01UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR4_OFFSET4_CH_1 (0x02UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR4_OFFSET4_CH_2 (0x04UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR4_OFFSET4_CH_3 (0x08UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR4_OFFSET4_CH_4 (0x10UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR4_OFFSET4_EN_Pos (31U) +#define ADC_OFR4_OFFSET4_EN_Msk (0x1UL << ADC_OFR4_OFFSET4_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR4_OFFSET4_EN ADC_OFR4_OFFSET4_EN_Msk /*!< ADC offset number 4 enable */ + +/******************** Bit definition for ADC_JDR1 register ******************/ +#define ADC_JDR1_JDATA_Pos (0U) +#define ADC_JDR1_JDATA_Msk (0xFFFFUL << ADC_JDR1_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR1_JDATA ADC_JDR1_JDATA_Msk /*!< ADC group injected sequencer rank 1 conversion data */ + +/******************** Bit definition for ADC_JDR2 register ******************/ +#define ADC_JDR2_JDATA_Pos (0U) +#define ADC_JDR2_JDATA_Msk (0xFFFFUL << ADC_JDR2_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR2_JDATA ADC_JDR2_JDATA_Msk /*!< ADC group injected sequencer rank 2 conversion data */ + +/******************** Bit definition for ADC_JDR3 register ******************/ +#define ADC_JDR3_JDATA_Pos (0U) +#define ADC_JDR3_JDATA_Msk (0xFFFFUL << ADC_JDR3_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR3_JDATA ADC_JDR3_JDATA_Msk /*!< ADC group injected sequencer rank 3 conversion data */ + +/******************** Bit definition for ADC_JDR4 register ******************/ +#define ADC_JDR4_JDATA_Pos (0U) +#define ADC_JDR4_JDATA_Msk (0xFFFFUL << ADC_JDR4_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR4_JDATA ADC_JDR4_JDATA_Msk /*!< ADC group injected sequencer rank 4 conversion data */ + +/******************** Bit definition for ADC_AWD2CR register ****************/ +#define ADC_AWD2CR_AWD2CH_Pos (0U) +#define ADC_AWD2CR_AWD2CH_Msk (0x7FFFFUL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x0007FFFF */ +#define ADC_AWD2CR_AWD2CH ADC_AWD2CR_AWD2CH_Msk /*!< ADC analog watchdog 2 monitored channel selection */ +#define ADC_AWD2CR_AWD2CH_0 (0x00001UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD2CR_AWD2CH_1 (0x00002UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD2CR_AWD2CH_2 (0x00004UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD2CR_AWD2CH_3 (0x00008UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD2CR_AWD2CH_4 (0x00010UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD2CR_AWD2CH_5 (0x00020UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD2CR_AWD2CH_6 (0x00040UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD2CR_AWD2CH_7 (0x00080UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD2CR_AWD2CH_8 (0x00100UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD2CR_AWD2CH_9 (0x00200UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD2CR_AWD2CH_10 (0x00400UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD2CR_AWD2CH_11 (0x00800UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD2CR_AWD2CH_12 (0x01000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD2CR_AWD2CH_13 (0x02000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD2CR_AWD2CH_14 (0x04000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD2CR_AWD2CH_15 (0x08000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD2CR_AWD2CH_16 (0x10000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD2CR_AWD2CH_17 (0x20000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD2CR_AWD2CH_18 (0x40000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_AWD3CR register ****************/ +#define ADC_AWD3CR_AWD3CH_Pos (0U) +#define ADC_AWD3CR_AWD3CH_Msk (0x7FFFFUL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x0007FFFF */ +#define ADC_AWD3CR_AWD3CH ADC_AWD3CR_AWD3CH_Msk /*!< ADC analog watchdog 3 monitored channel selection */ +#define ADC_AWD3CR_AWD3CH_0 (0x00001UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD3CR_AWD3CH_1 (0x00002UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD3CR_AWD3CH_2 (0x00004UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD3CR_AWD3CH_3 (0x00008UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD3CR_AWD3CH_4 (0x00010UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD3CR_AWD3CH_5 (0x00020UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD3CR_AWD3CH_6 (0x00040UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD3CR_AWD3CH_7 (0x00080UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD3CR_AWD3CH_8 (0x00100UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD3CR_AWD3CH_9 (0x00200UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD3CR_AWD3CH_10 (0x00400UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD3CR_AWD3CH_11 (0x00800UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD3CR_AWD3CH_12 (0x01000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD3CR_AWD3CH_13 (0x02000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD3CR_AWD3CH_14 (0x04000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD3CR_AWD3CH_15 (0x08000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD3CR_AWD3CH_16 (0x10000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD3CR_AWD3CH_17 (0x20000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD3CR_AWD3CH_18 (0x40000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_DIFSEL register ****************/ +#define ADC_DIFSEL_DIFSEL_Pos (0U) +#define ADC_DIFSEL_DIFSEL_Msk (0x7FFFFUL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x0007FFFF */ +#define ADC_DIFSEL_DIFSEL ADC_DIFSEL_DIFSEL_Msk /*!< ADC channel differential or single-ended mode */ +#define ADC_DIFSEL_DIFSEL_0 (0x00001UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000001 */ +#define ADC_DIFSEL_DIFSEL_1 (0x00002UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000002 */ +#define ADC_DIFSEL_DIFSEL_2 (0x00004UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000004 */ +#define ADC_DIFSEL_DIFSEL_3 (0x00008UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000008 */ +#define ADC_DIFSEL_DIFSEL_4 (0x00010UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000010 */ +#define ADC_DIFSEL_DIFSEL_5 (0x00020UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000020 */ +#define ADC_DIFSEL_DIFSEL_6 (0x00040UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000040 */ +#define ADC_DIFSEL_DIFSEL_7 (0x00080UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000080 */ +#define ADC_DIFSEL_DIFSEL_8 (0x00100UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000100 */ +#define ADC_DIFSEL_DIFSEL_9 (0x00200UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000200 */ +#define ADC_DIFSEL_DIFSEL_10 (0x00400UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000400 */ +#define ADC_DIFSEL_DIFSEL_11 (0x00800UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000800 */ +#define ADC_DIFSEL_DIFSEL_12 (0x01000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00001000 */ +#define ADC_DIFSEL_DIFSEL_13 (0x02000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00002000 */ +#define ADC_DIFSEL_DIFSEL_14 (0x04000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00004000 */ +#define ADC_DIFSEL_DIFSEL_15 (0x08000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00008000 */ +#define ADC_DIFSEL_DIFSEL_16 (0x10000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00010000 */ +#define ADC_DIFSEL_DIFSEL_17 (0x20000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00020000 */ +#define ADC_DIFSEL_DIFSEL_18 (0x40000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_CALFACT register ***************/ +#define ADC_CALFACT_CALFACT_S_Pos (0U) +#define ADC_CALFACT_CALFACT_S_Msk (0x7FUL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x0000007F */ +#define ADC_CALFACT_CALFACT_S ADC_CALFACT_CALFACT_S_Msk /*!< ADC calibration factor in single-ended mode */ +#define ADC_CALFACT_CALFACT_S_0 (0x01UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT_CALFACT_S_1 (0x02UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT_CALFACT_S_2 (0x04UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT_CALFACT_S_3 (0x08UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT_CALFACT_S_4 (0x10UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT_CALFACT_S_5 (0x20UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT_CALFACT_S_6 (0x40UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000030 */ + +#define ADC_CALFACT_CALFACT_D_Pos (16U) +#define ADC_CALFACT_CALFACT_D_Msk (0x7FUL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x007F0000 */ +#define ADC_CALFACT_CALFACT_D ADC_CALFACT_CALFACT_D_Msk /*!< ADC calibration factor in differential mode */ +#define ADC_CALFACT_CALFACT_D_0 (0x01UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT_CALFACT_D_1 (0x02UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT_CALFACT_D_2 (0x04UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT_CALFACT_D_3 (0x08UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT_CALFACT_D_4 (0x10UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT_CALFACT_D_5 (0x20UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT_CALFACT_D_6 (0x40UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00300000 */ + +/******************** Bit definition for ADC_GCOMP register *****************/ +#define ADC_GCOMP_GCOMPCOEFF_Pos (0U) +#define ADC_GCOMP_GCOMPCOEFF_Msk (0x3FFFUL << ADC_GCOMP_GCOMPCOEFF_Pos) /*!< 0x00003FFF */ +#define ADC_GCOMP_GCOMPCOEFF ADC_GCOMP_GCOMPCOEFF_Msk /*!< ADC Gain Compensation Coefficient */ + +/************************* ADC Common registers *****************************/ +/******************** Bit definition for ADC_CSR register *******************/ +#define ADC_CSR_ADRDY_MST_Pos (0U) +#define ADC_CSR_ADRDY_MST_Msk (0x1UL << ADC_CSR_ADRDY_MST_Pos) /*!< 0x00000001 */ +#define ADC_CSR_ADRDY_MST ADC_CSR_ADRDY_MST_Msk /*!< ADC multimode master ready flag */ +#define ADC_CSR_EOSMP_MST_Pos (1U) +#define ADC_CSR_EOSMP_MST_Msk (0x1UL << ADC_CSR_EOSMP_MST_Pos) /*!< 0x00000002 */ +#define ADC_CSR_EOSMP_MST ADC_CSR_EOSMP_MST_Msk /*!< ADC multimode master group regular end of sampling flag */ +#define ADC_CSR_EOC_MST_Pos (2U) +#define ADC_CSR_EOC_MST_Msk (0x1UL << ADC_CSR_EOC_MST_Pos) /*!< 0x00000004 */ +#define ADC_CSR_EOC_MST ADC_CSR_EOC_MST_Msk /*!< ADC multimode master group regular end of unitary conversion flag */ +#define ADC_CSR_EOS_MST_Pos (3U) +#define ADC_CSR_EOS_MST_Msk (0x1UL << ADC_CSR_EOS_MST_Pos) /*!< 0x00000008 */ +#define ADC_CSR_EOS_MST ADC_CSR_EOS_MST_Msk /*!< ADC multimode master group regular end of sequence conversions flag */ +#define ADC_CSR_OVR_MST_Pos (4U) +#define ADC_CSR_OVR_MST_Msk (0x1UL << ADC_CSR_OVR_MST_Pos) /*!< 0x00000010 */ +#define ADC_CSR_OVR_MST ADC_CSR_OVR_MST_Msk /*!< ADC multimode master group regular overrun flag */ +#define ADC_CSR_JEOC_MST_Pos (5U) +#define ADC_CSR_JEOC_MST_Msk (0x1UL << ADC_CSR_JEOC_MST_Pos) /*!< 0x00000020 */ +#define ADC_CSR_JEOC_MST ADC_CSR_JEOC_MST_Msk /*!< ADC multimode master group injected end of unitary conversion flag */ +#define ADC_CSR_JEOS_MST_Pos (6U) +#define ADC_CSR_JEOS_MST_Msk (0x1UL << ADC_CSR_JEOS_MST_Pos) /*!< 0x00000040 */ +#define ADC_CSR_JEOS_MST ADC_CSR_JEOS_MST_Msk /*!< ADC multimode master group injected end of sequence conversions flag */ +#define ADC_CSR_AWD1_MST_Pos (7U) +#define ADC_CSR_AWD1_MST_Msk (0x1UL << ADC_CSR_AWD1_MST_Pos) /*!< 0x00000080 */ +#define ADC_CSR_AWD1_MST ADC_CSR_AWD1_MST_Msk /*!< ADC multimode master analog watchdog 1 flag */ +#define ADC_CSR_AWD2_MST_Pos (8U) +#define ADC_CSR_AWD2_MST_Msk (0x1UL << ADC_CSR_AWD2_MST_Pos) /*!< 0x00000100 */ +#define ADC_CSR_AWD2_MST ADC_CSR_AWD2_MST_Msk /*!< ADC multimode master analog watchdog 2 flag */ +#define ADC_CSR_AWD3_MST_Pos (9U) +#define ADC_CSR_AWD3_MST_Msk (0x1UL << ADC_CSR_AWD3_MST_Pos) /*!< 0x00000200 */ +#define ADC_CSR_AWD3_MST ADC_CSR_AWD3_MST_Msk /*!< ADC multimode master analog watchdog 3 flag */ +#define ADC_CSR_JQOVF_MST_Pos (10U) +#define ADC_CSR_JQOVF_MST_Msk (0x1UL << ADC_CSR_JQOVF_MST_Pos) /*!< 0x00000400 */ +#define ADC_CSR_JQOVF_MST ADC_CSR_JQOVF_MST_Msk /*!< ADC multimode master group injected contexts queue overflow flag */ + +#define ADC_CSR_ADRDY_SLV_Pos (16U) +#define ADC_CSR_ADRDY_SLV_Msk (0x1UL << ADC_CSR_ADRDY_SLV_Pos) /*!< 0x00010000 */ +#define ADC_CSR_ADRDY_SLV ADC_CSR_ADRDY_SLV_Msk /*!< ADC multimode slave ready flag */ +#define ADC_CSR_EOSMP_SLV_Pos (17U) +#define ADC_CSR_EOSMP_SLV_Msk (0x1UL << ADC_CSR_EOSMP_SLV_Pos) /*!< 0x00020000 */ +#define ADC_CSR_EOSMP_SLV ADC_CSR_EOSMP_SLV_Msk /*!< ADC multimode slave group regular end of sampling flag */ +#define ADC_CSR_EOC_SLV_Pos (18U) +#define ADC_CSR_EOC_SLV_Msk (0x1UL << ADC_CSR_EOC_SLV_Pos) /*!< 0x00040000 */ +#define ADC_CSR_EOC_SLV ADC_CSR_EOC_SLV_Msk /*!< ADC multimode slave group regular end of unitary conversion flag */ +#define ADC_CSR_EOS_SLV_Pos (19U) +#define ADC_CSR_EOS_SLV_Msk (0x1UL << ADC_CSR_EOS_SLV_Pos) /*!< 0x00080000 */ +#define ADC_CSR_EOS_SLV ADC_CSR_EOS_SLV_Msk /*!< ADC multimode slave group regular end of sequence conversions flag */ +#define ADC_CSR_OVR_SLV_Pos (20U) +#define ADC_CSR_OVR_SLV_Msk (0x1UL << ADC_CSR_OVR_SLV_Pos) /*!< 0x00100000 */ +#define ADC_CSR_OVR_SLV ADC_CSR_OVR_SLV_Msk /*!< ADC multimode slave group regular overrun flag */ +#define ADC_CSR_JEOC_SLV_Pos (21U) +#define ADC_CSR_JEOC_SLV_Msk (0x1UL << ADC_CSR_JEOC_SLV_Pos) /*!< 0x00200000 */ +#define ADC_CSR_JEOC_SLV ADC_CSR_JEOC_SLV_Msk /*!< ADC multimode slave group injected end of unitary conversion flag */ +#define ADC_CSR_JEOS_SLV_Pos (22U) +#define ADC_CSR_JEOS_SLV_Msk (0x1UL << ADC_CSR_JEOS_SLV_Pos) /*!< 0x00400000 */ +#define ADC_CSR_JEOS_SLV ADC_CSR_JEOS_SLV_Msk /*!< ADC multimode slave group injected end of sequence conversions flag */ +#define ADC_CSR_AWD1_SLV_Pos (23U) +#define ADC_CSR_AWD1_SLV_Msk (0x1UL << ADC_CSR_AWD1_SLV_Pos) /*!< 0x00800000 */ +#define ADC_CSR_AWD1_SLV ADC_CSR_AWD1_SLV_Msk /*!< ADC multimode slave analog watchdog 1 flag */ +#define ADC_CSR_AWD2_SLV_Pos (24U) +#define ADC_CSR_AWD2_SLV_Msk (0x1UL << ADC_CSR_AWD2_SLV_Pos) /*!< 0x01000000 */ +#define ADC_CSR_AWD2_SLV ADC_CSR_AWD2_SLV_Msk /*!< ADC multimode slave analog watchdog 2 flag */ +#define ADC_CSR_AWD3_SLV_Pos (25U) +#define ADC_CSR_AWD3_SLV_Msk (0x1UL << ADC_CSR_AWD3_SLV_Pos) /*!< 0x02000000 */ +#define ADC_CSR_AWD3_SLV ADC_CSR_AWD3_SLV_Msk /*!< ADC multimode slave analog watchdog 3 flag */ +#define ADC_CSR_JQOVF_SLV_Pos (26U) +#define ADC_CSR_JQOVF_SLV_Msk (0x1UL << ADC_CSR_JQOVF_SLV_Pos) /*!< 0x04000000 */ +#define ADC_CSR_JQOVF_SLV ADC_CSR_JQOVF_SLV_Msk /*!< ADC multimode slave group injected contexts queue overflow flag */ + +/******************** Bit definition for ADC_CCR register *******************/ +#define ADC_CCR_DUAL_Pos (0U) +#define ADC_CCR_DUAL_Msk (0x1FUL << ADC_CCR_DUAL_Pos) /*!< 0x0000001F */ +#define ADC_CCR_DUAL ADC_CCR_DUAL_Msk /*!< ADC multimode mode selection */ +#define ADC_CCR_DUAL_0 (0x01UL << ADC_CCR_DUAL_Pos) /*!< 0x00000001 */ +#define ADC_CCR_DUAL_1 (0x02UL << ADC_CCR_DUAL_Pos) /*!< 0x00000002 */ +#define ADC_CCR_DUAL_2 (0x04UL << ADC_CCR_DUAL_Pos) /*!< 0x00000004 */ +#define ADC_CCR_DUAL_3 (0x08UL << ADC_CCR_DUAL_Pos) /*!< 0x00000008 */ +#define ADC_CCR_DUAL_4 (0x10UL << ADC_CCR_DUAL_Pos) /*!< 0x00000010 */ + +#define ADC_CCR_DELAY_Pos (8U) +#define ADC_CCR_DELAY_Msk (0xFUL << ADC_CCR_DELAY_Pos) /*!< 0x00000F00 */ +#define ADC_CCR_DELAY ADC_CCR_DELAY_Msk /*!< ADC multimode delay between 2 sampling phases */ +#define ADC_CCR_DELAY_0 (0x1UL << ADC_CCR_DELAY_Pos) /*!< 0x00000100 */ +#define ADC_CCR_DELAY_1 (0x2UL << ADC_CCR_DELAY_Pos) /*!< 0x00000200 */ +#define ADC_CCR_DELAY_2 (0x4UL << ADC_CCR_DELAY_Pos) /*!< 0x00000400 */ +#define ADC_CCR_DELAY_3 (0x8UL << ADC_CCR_DELAY_Pos) /*!< 0x00000800 */ + +#define ADC_CCR_DMACFG_Pos (13U) +#define ADC_CCR_DMACFG_Msk (0x1UL << ADC_CCR_DMACFG_Pos) /*!< 0x00002000 */ +#define ADC_CCR_DMACFG ADC_CCR_DMACFG_Msk /*!< ADC multimode DMA transfer configuration */ + +#define ADC_CCR_MDMA_Pos (14U) +#define ADC_CCR_MDMA_Msk (0x3UL << ADC_CCR_MDMA_Pos) /*!< 0x0000C000 */ +#define ADC_CCR_MDMA ADC_CCR_MDMA_Msk /*!< ADC multimode DMA transfer enable */ +#define ADC_CCR_MDMA_0 (0x1UL << ADC_CCR_MDMA_Pos) /*!< 0x00004000 */ +#define ADC_CCR_MDMA_1 (0x2UL << ADC_CCR_MDMA_Pos) /*!< 0x00008000 */ + +#define ADC_CCR_CKMODE_Pos (16U) +#define ADC_CCR_CKMODE_Msk (0x3UL << ADC_CCR_CKMODE_Pos) /*!< 0x00030000 */ +#define ADC_CCR_CKMODE ADC_CCR_CKMODE_Msk /*!< ADC common clock source and prescaler (prescaler only for clock source synchronous) */ +#define ADC_CCR_CKMODE_0 (0x1UL << ADC_CCR_CKMODE_Pos) /*!< 0x00010000 */ +#define ADC_CCR_CKMODE_1 (0x2UL << ADC_CCR_CKMODE_Pos) /*!< 0x00020000 */ + +#define ADC_CCR_PRESC_Pos (18U) +#define ADC_CCR_PRESC_Msk (0xFUL << ADC_CCR_PRESC_Pos) /*!< 0x003C0000 */ +#define ADC_CCR_PRESC ADC_CCR_PRESC_Msk /*!< ADC common clock prescaler, only for clock source asynchronous */ +#define ADC_CCR_PRESC_0 (0x1UL << ADC_CCR_PRESC_Pos) /*!< 0x00040000 */ +#define ADC_CCR_PRESC_1 (0x2UL << ADC_CCR_PRESC_Pos) /*!< 0x00080000 */ +#define ADC_CCR_PRESC_2 (0x4UL << ADC_CCR_PRESC_Pos) /*!< 0x00100000 */ +#define ADC_CCR_PRESC_3 (0x8UL << ADC_CCR_PRESC_Pos) /*!< 0x00200000 */ + +#define ADC_CCR_VREFEN_Pos (22U) +#define ADC_CCR_VREFEN_Msk (0x1UL << ADC_CCR_VREFEN_Pos) /*!< 0x00400000 */ +#define ADC_CCR_VREFEN ADC_CCR_VREFEN_Msk /*!< ADC internal path to VrefInt enable */ +#define ADC_CCR_VSENSESEL_Pos (23U) +#define ADC_CCR_VSENSESEL_Msk (0x1UL << ADC_CCR_VSENSESEL_Pos) /*!< 0x00800000 */ +#define ADC_CCR_VSENSESEL ADC_CCR_VSENSESEL_Msk /*!< ADC internal path to temperature sensor enable */ +#define ADC_CCR_VBATSEL_Pos (24U) +#define ADC_CCR_VBATSEL_Msk (0x1UL << ADC_CCR_VBATSEL_Pos) /*!< 0x01000000 */ +#define ADC_CCR_VBATSEL ADC_CCR_VBATSEL_Msk /*!< ADC internal path to battery voltage enable */ + +/******************** Bit definition for ADC_CDR register *******************/ +#define ADC_CDR_RDATA_MST_Pos (0U) +#define ADC_CDR_RDATA_MST_Msk (0xFFFFUL << ADC_CDR_RDATA_MST_Pos) /*!< 0x0000FFFF */ +#define ADC_CDR_RDATA_MST ADC_CDR_RDATA_MST_Msk /*!< ADC multimode master group regular conversion data */ + +#define ADC_CDR_RDATA_SLV_Pos (16U) +#define ADC_CDR_RDATA_SLV_Msk (0xFFFFUL << ADC_CDR_RDATA_SLV_Pos) /*!< 0xFFFF0000 */ +#define ADC_CDR_RDATA_SLV ADC_CDR_RDATA_SLV_Msk /*!< ADC multimode slave group regular conversion data */ + + +/******************************************************************************/ +/* */ +/* Analog Comparators (COMP) */ +/* */ +/******************************************************************************/ +/********************** Bit definition for COMP_CSR register ****************/ +#define COMP_CSR_EN_Pos (0U) +#define COMP_CSR_EN_Msk (0x1UL << COMP_CSR_EN_Pos) /*!< 0x00000001 */ +#define COMP_CSR_EN COMP_CSR_EN_Msk /*!< Comparator enable */ + +#define COMP_CSR_INMSEL_Pos (4U) +#define COMP_CSR_INMSEL_Msk (0xFUL << COMP_CSR_INMSEL_Pos) /*!< 0x00000070 */ +#define COMP_CSR_INMSEL COMP_CSR_INMSEL_Msk /*!< Comparator input minus selection */ +#define COMP_CSR_INMSEL_0 (0x1UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000010 */ +#define COMP_CSR_INMSEL_1 (0x2UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000020 */ +#define COMP_CSR_INMSEL_2 (0x4UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000040 */ +#define COMP_CSR_INMSEL_3 (0x8UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000080 */ + +#define COMP_CSR_INPSEL_Pos (8U) +#define COMP_CSR_INPSEL_Msk (0x1UL << COMP_CSR_INPSEL_Pos) /*!< 0x00000100 */ +#define COMP_CSR_INPSEL COMP_CSR_INPSEL_Msk /*!< Comparator input plus selection */ + +#define COMP_CSR_POLARITY_Pos (15U) +#define COMP_CSR_POLARITY_Msk (0x1UL << COMP_CSR_POLARITY_Pos) /*!< 0x00008000 */ +#define COMP_CSR_POLARITY COMP_CSR_POLARITY_Msk /*!< Comparator output polarity */ + +#define COMP_CSR_HYST_Pos (16U) +#define COMP_CSR_HYST_Msk (0x7UL << COMP_CSR_HYST_Pos) /*!< 0x00070000 */ +#define COMP_CSR_HYST COMP_CSR_HYST_Msk /*!< Comparator hysteresis */ +#define COMP_CSR_HYST_0 (0x1UL << COMP_CSR_HYST_Pos) /*!< 0x00010000 */ +#define COMP_CSR_HYST_1 (0x2UL << COMP_CSR_HYST_Pos) /*!< 0x00020000 */ +#define COMP_CSR_HYST_2 (0x4UL << COMP_CSR_HYST_Pos) /*!< 0x00040000 */ + +#define COMP_CSR_BLANKING_Pos (19U) +#define COMP_CSR_BLANKING_Msk (0x7UL << COMP_CSR_BLANKING_Pos) /*!< 0x00380000 */ +#define COMP_CSR_BLANKING COMP_CSR_BLANKING_Msk /*!< Comparator blanking source */ +#define COMP_CSR_BLANKING_0 (0x1UL << COMP_CSR_BLANKING_Pos) /*!< 0x00080000 */ +#define COMP_CSR_BLANKING_1 (0x2UL << COMP_CSR_BLANKING_Pos) /*!< 0x00100000 */ +#define COMP_CSR_BLANKING_2 (0x4UL << COMP_CSR_BLANKING_Pos) /*!< 0x00200000 */ + +#define COMP_CSR_BRGEN_Pos (22U) +#define COMP_CSR_BRGEN_Msk (0x1UL << COMP_CSR_BRGEN_Pos) /*!< 0x00400000 */ +#define COMP_CSR_BRGEN COMP_CSR_BRGEN_Msk /*!< Comparator scaler bridge enable */ + +#define COMP_CSR_SCALEN_Pos (23U) +#define COMP_CSR_SCALEN_Msk (0x1UL << COMP_CSR_SCALEN_Pos) /*!< 0x00800000 */ +#define COMP_CSR_SCALEN COMP_CSR_SCALEN_Msk /*!< Comparator voltage scaler enable */ + +#define COMP_CSR_VALUE_Pos (30U) +#define COMP_CSR_VALUE_Msk (0x1UL << COMP_CSR_VALUE_Pos) /*!< 0x40000000 */ +#define COMP_CSR_VALUE COMP_CSR_VALUE_Msk /*!< Comparator output level */ + +#define COMP_CSR_LOCK_Pos (31U) +#define COMP_CSR_LOCK_Msk (0x1UL << COMP_CSR_LOCK_Pos) /*!< 0x80000000 */ +#define COMP_CSR_LOCK COMP_CSR_LOCK_Msk /*!< Comparator lock */ + +/******************************************************************************/ +/* */ +/* CORDIC calculation unit */ +/* */ +/******************************************************************************/ +/******************* Bit definition for CORDIC_CSR register *****************/ +#define CORDIC_CSR_FUNC_Pos (0U) +#define CORDIC_CSR_FUNC_Msk (0xFUL << CORDIC_CSR_FUNC_Pos) /*!< 0x0000000F */ +#define CORDIC_CSR_FUNC CORDIC_CSR_FUNC_Msk /*!< Function */ +#define CORDIC_CSR_FUNC_0 (0x1UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000001 */ +#define CORDIC_CSR_FUNC_1 (0x2UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000002 */ +#define CORDIC_CSR_FUNC_2 (0x4UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000004 */ +#define CORDIC_CSR_FUNC_3 (0x8UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000008 */ +#define CORDIC_CSR_PRECISION_Pos (4U) +#define CORDIC_CSR_PRECISION_Msk (0xFUL << CORDIC_CSR_PRECISION_Pos) /*!< 0x000000F0 */ +#define CORDIC_CSR_PRECISION CORDIC_CSR_PRECISION_Msk /*!< Precision */ +#define CORDIC_CSR_PRECISION_0 (0x1UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000010 */ +#define CORDIC_CSR_PRECISION_1 (0x2UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000020 */ +#define CORDIC_CSR_PRECISION_2 (0x4UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000040 */ +#define CORDIC_CSR_PRECISION_3 (0x8UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000080 */ +#define CORDIC_CSR_SCALE_Pos (8U) +#define CORDIC_CSR_SCALE_Msk (0x7UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000700 */ +#define CORDIC_CSR_SCALE CORDIC_CSR_SCALE_Msk /*!< Scaling factor */ +#define CORDIC_CSR_SCALE_0 (0x1UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000100 */ +#define CORDIC_CSR_SCALE_1 (0x2UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000200 */ +#define CORDIC_CSR_SCALE_2 (0x4UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000400 */ +#define CORDIC_CSR_IEN_Pos (16U) +#define CORDIC_CSR_IEN_Msk (0x1UL << CORDIC_CSR_IEN_Pos) /*!< 0x00010000 */ +#define CORDIC_CSR_IEN CORDIC_CSR_IEN_Msk /*!< Interrupt Enable */ +#define CORDIC_CSR_DMAREN_Pos (17U) +#define CORDIC_CSR_DMAREN_Msk (0x1UL << CORDIC_CSR_DMAREN_Pos) /*!< 0x00020000 */ +#define CORDIC_CSR_DMAREN CORDIC_CSR_DMAREN_Msk /*!< DMA Read channel Enable */ +#define CORDIC_CSR_DMAWEN_Pos (18U) +#define CORDIC_CSR_DMAWEN_Msk (0x1UL << CORDIC_CSR_DMAWEN_Pos) /*!< 0x00040000 */ +#define CORDIC_CSR_DMAWEN CORDIC_CSR_DMAWEN_Msk /*!< DMA Write channel Enable */ +#define CORDIC_CSR_NRES_Pos (19U) +#define CORDIC_CSR_NRES_Msk (0x1UL << CORDIC_CSR_NRES_Pos) /*!< 0x00080000 */ +#define CORDIC_CSR_NRES CORDIC_CSR_NRES_Msk /*!< Number of results in WDATA register */ +#define CORDIC_CSR_NARGS_Pos (20U) +#define CORDIC_CSR_NARGS_Msk (0x1UL << CORDIC_CSR_NARGS_Pos) /*!< 0x00100000 */ +#define CORDIC_CSR_NARGS CORDIC_CSR_NARGS_Msk /*!< Number of arguments in RDATA register */ +#define CORDIC_CSR_RESSIZE_Pos (21U) +#define CORDIC_CSR_RESSIZE_Msk (0x1UL << CORDIC_CSR_RESSIZE_Pos) /*!< 0x00200000 */ +#define CORDIC_CSR_RESSIZE CORDIC_CSR_RESSIZE_Msk /*!< Width of output data */ +#define CORDIC_CSR_ARGSIZE_Pos (22U) +#define CORDIC_CSR_ARGSIZE_Msk (0x1UL << CORDIC_CSR_ARGSIZE_Pos) /*!< 0x00400000 */ +#define CORDIC_CSR_ARGSIZE CORDIC_CSR_ARGSIZE_Msk /*!< Width of input data */ +#define CORDIC_CSR_RRDY_Pos (31U) +#define CORDIC_CSR_RRDY_Msk (0x1UL << CORDIC_CSR_RRDY_Pos) /*!< 0x80000000 */ +#define CORDIC_CSR_RRDY CORDIC_CSR_RRDY_Msk /*!< Result Ready Flag */ + +/******************* Bit definition for CORDIC_WDATA register ***************/ +#define CORDIC_WDATA_ARG_Pos (0U) +#define CORDIC_WDATA_ARG_Msk (0xFFFFFFFFUL << CORDIC_WDATA_ARG_Pos) /*!< 0xFFFFFFFF */ +#define CORDIC_WDATA_ARG CORDIC_WDATA_ARG_Msk /*!< Input Argument */ + +/******************* Bit definition for CORDIC_RDATA register ***************/ +#define CORDIC_RDATA_RES_Pos (0U) +#define CORDIC_RDATA_RES_Msk (0xFFFFFFFFUL << CORDIC_RDATA_RES_Pos) /*!< 0xFFFFFFFF */ +#define CORDIC_RDATA_RES CORDIC_RDATA_RES_Msk /*!< Output Result */ + +/******************************************************************************/ +/* */ +/* CRC calculation unit */ +/* */ +/******************************************************************************/ +/******************* Bit definition for CRC_DR register *********************/ +#define CRC_DR_DR_Pos (0U) +#define CRC_DR_DR_Msk (0xFFFFFFFFUL << CRC_DR_DR_Pos) /*!< 0xFFFFFFFF */ +#define CRC_DR_DR CRC_DR_DR_Msk /*!< Data register bits */ + +/******************* Bit definition for CRC_IDR register ********************/ +#define CRC_IDR_IDR_Pos (0U) +#define CRC_IDR_IDR_Msk (0xFFFFFFFFUL << CRC_IDR_IDR_Pos) /*!< 0xFFFFFFFF */ +#define CRC_IDR_IDR CRC_IDR_IDR_Msk /*!< General-purpose 32-bit data register bits */ + +/******************** Bit definition for CRC_CR register ********************/ +#define CRC_CR_RESET_Pos (0U) +#define CRC_CR_RESET_Msk (0x1UL << CRC_CR_RESET_Pos) /*!< 0x00000001 */ +#define CRC_CR_RESET CRC_CR_RESET_Msk /*!< RESET the CRC computation unit bit */ +#define CRC_CR_POLYSIZE_Pos (3U) +#define CRC_CR_POLYSIZE_Msk (0x3UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000018 */ +#define CRC_CR_POLYSIZE CRC_CR_POLYSIZE_Msk /*!< Polynomial size bits */ +#define CRC_CR_POLYSIZE_0 (0x1UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000008 */ +#define CRC_CR_POLYSIZE_1 (0x2UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000010 */ +#define CRC_CR_REV_IN_Pos (5U) +#define CRC_CR_REV_IN_Msk (0x3UL << CRC_CR_REV_IN_Pos) /*!< 0x00000060 */ +#define CRC_CR_REV_IN CRC_CR_REV_IN_Msk /*!< REV_IN Reverse Input Data bits */ +#define CRC_CR_REV_IN_0 (0x1UL << CRC_CR_REV_IN_Pos) /*!< 0x00000020 */ +#define CRC_CR_REV_IN_1 (0x2UL << CRC_CR_REV_IN_Pos) /*!< 0x00000040 */ +#define CRC_CR_REV_OUT_Pos (7U) +#define CRC_CR_REV_OUT_Msk (0x1UL << CRC_CR_REV_OUT_Pos) /*!< 0x00000080 */ +#define CRC_CR_REV_OUT CRC_CR_REV_OUT_Msk /*!< REV_OUT Reverse Output Data bits */ + +/******************* Bit definition for CRC_INIT register *******************/ +#define CRC_INIT_INIT_Pos (0U) +#define CRC_INIT_INIT_Msk (0xFFFFFFFFUL << CRC_INIT_INIT_Pos) /*!< 0xFFFFFFFF */ +#define CRC_INIT_INIT CRC_INIT_INIT_Msk /*!< Initial CRC value bits */ + +/******************* Bit definition for CRC_POL register ********************/ +#define CRC_POL_POL_Pos (0U) +#define CRC_POL_POL_Msk (0xFFFFFFFFUL << CRC_POL_POL_Pos) /*!< 0xFFFFFFFF */ +#define CRC_POL_POL CRC_POL_POL_Msk /*!< Coefficients of the polynomial */ + +/******************************************************************************/ +/* */ +/* CRS Clock Recovery System */ +/******************************************************************************/ + +/******************* Bit definition for CRS_CR register *********************/ +#define CRS_CR_SYNCOKIE_Pos (0U) +#define CRS_CR_SYNCOKIE_Msk (0x1UL << CRS_CR_SYNCOKIE_Pos) /*!< 0x00000001 */ +#define CRS_CR_SYNCOKIE CRS_CR_SYNCOKIE_Msk /*!< SYNC event OK interrupt enable */ +#define CRS_CR_SYNCWARNIE_Pos (1U) +#define CRS_CR_SYNCWARNIE_Msk (0x1UL << CRS_CR_SYNCWARNIE_Pos) /*!< 0x00000002 */ +#define CRS_CR_SYNCWARNIE CRS_CR_SYNCWARNIE_Msk /*!< SYNC warning interrupt enable */ +#define CRS_CR_ERRIE_Pos (2U) +#define CRS_CR_ERRIE_Msk (0x1UL << CRS_CR_ERRIE_Pos) /*!< 0x00000004 */ +#define CRS_CR_ERRIE CRS_CR_ERRIE_Msk /*!< SYNC error or trimming error interrupt enable */ +#define CRS_CR_ESYNCIE_Pos (3U) +#define CRS_CR_ESYNCIE_Msk (0x1UL << CRS_CR_ESYNCIE_Pos) /*!< 0x00000008 */ +#define CRS_CR_ESYNCIE CRS_CR_ESYNCIE_Msk /*!< Expected SYNC interrupt enable */ +#define CRS_CR_CEN_Pos (5U) +#define CRS_CR_CEN_Msk (0x1UL << CRS_CR_CEN_Pos) /*!< 0x00000020 */ +#define CRS_CR_CEN CRS_CR_CEN_Msk /*!< Frequency error counter enable */ +#define CRS_CR_AUTOTRIMEN_Pos (6U) +#define CRS_CR_AUTOTRIMEN_Msk (0x1UL << CRS_CR_AUTOTRIMEN_Pos) /*!< 0x00000040 */ +#define CRS_CR_AUTOTRIMEN CRS_CR_AUTOTRIMEN_Msk /*!< Automatic trimming enable */ +#define CRS_CR_SWSYNC_Pos (7U) +#define CRS_CR_SWSYNC_Msk (0x1UL << CRS_CR_SWSYNC_Pos) /*!< 0x00000080 */ +#define CRS_CR_SWSYNC CRS_CR_SWSYNC_Msk /*!< Generate software SYNC event */ +#define CRS_CR_TRIM_Pos (8U) +#define CRS_CR_TRIM_Msk (0x7FUL << CRS_CR_TRIM_Pos) /*!< 0x00007F00 */ +#define CRS_CR_TRIM CRS_CR_TRIM_Msk /*!< HSI48 oscillator smooth trimming */ + +/******************* Bit definition for CRS_CFGR register *********************/ +#define CRS_CFGR_RELOAD_Pos (0U) +#define CRS_CFGR_RELOAD_Msk (0xFFFFUL << CRS_CFGR_RELOAD_Pos) /*!< 0x0000FFFF */ +#define CRS_CFGR_RELOAD CRS_CFGR_RELOAD_Msk /*!< Counter reload value */ +#define CRS_CFGR_FELIM_Pos (16U) +#define CRS_CFGR_FELIM_Msk (0xFFUL << CRS_CFGR_FELIM_Pos) /*!< 0x00FF0000 */ +#define CRS_CFGR_FELIM CRS_CFGR_FELIM_Msk /*!< Frequency error limit */ + +#define CRS_CFGR_SYNCDIV_Pos (24U) +#define CRS_CFGR_SYNCDIV_Msk (0x7UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x07000000 */ +#define CRS_CFGR_SYNCDIV CRS_CFGR_SYNCDIV_Msk /*!< SYNC divider */ +#define CRS_CFGR_SYNCDIV_0 (0x1UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x01000000 */ +#define CRS_CFGR_SYNCDIV_1 (0x2UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x02000000 */ +#define CRS_CFGR_SYNCDIV_2 (0x4UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x04000000 */ + +#define CRS_CFGR_SYNCSRC_Pos (28U) +#define CRS_CFGR_SYNCSRC_Msk (0x3UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x30000000 */ +#define CRS_CFGR_SYNCSRC CRS_CFGR_SYNCSRC_Msk /*!< SYNC signal source selection */ +#define CRS_CFGR_SYNCSRC_0 (0x1UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x10000000 */ +#define CRS_CFGR_SYNCSRC_1 (0x2UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x20000000 */ + +#define CRS_CFGR_SYNCPOL_Pos (31U) +#define CRS_CFGR_SYNCPOL_Msk (0x1UL << CRS_CFGR_SYNCPOL_Pos) /*!< 0x80000000 */ +#define CRS_CFGR_SYNCPOL CRS_CFGR_SYNCPOL_Msk /*!< SYNC polarity selection */ + +/******************* Bit definition for CRS_ISR register *********************/ +#define CRS_ISR_SYNCOKF_Pos (0U) +#define CRS_ISR_SYNCOKF_Msk (0x1UL << CRS_ISR_SYNCOKF_Pos) /*!< 0x00000001 */ +#define CRS_ISR_SYNCOKF CRS_ISR_SYNCOKF_Msk /*!< SYNC event OK flag */ +#define CRS_ISR_SYNCWARNF_Pos (1U) +#define CRS_ISR_SYNCWARNF_Msk (0x1UL << CRS_ISR_SYNCWARNF_Pos) /*!< 0x00000002 */ +#define CRS_ISR_SYNCWARNF CRS_ISR_SYNCWARNF_Msk /*!< SYNC warning flag */ +#define CRS_ISR_ERRF_Pos (2U) +#define CRS_ISR_ERRF_Msk (0x1UL << CRS_ISR_ERRF_Pos) /*!< 0x00000004 */ +#define CRS_ISR_ERRF CRS_ISR_ERRF_Msk /*!< Error flag */ +#define CRS_ISR_ESYNCF_Pos (3U) +#define CRS_ISR_ESYNCF_Msk (0x1UL << CRS_ISR_ESYNCF_Pos) /*!< 0x00000008 */ +#define CRS_ISR_ESYNCF CRS_ISR_ESYNCF_Msk /*!< Expected SYNC flag */ +#define CRS_ISR_SYNCERR_Pos (8U) +#define CRS_ISR_SYNCERR_Msk (0x1UL << CRS_ISR_SYNCERR_Pos) /*!< 0x00000100 */ +#define CRS_ISR_SYNCERR CRS_ISR_SYNCERR_Msk /*!< SYNC error */ +#define CRS_ISR_SYNCMISS_Pos (9U) +#define CRS_ISR_SYNCMISS_Msk (0x1UL << CRS_ISR_SYNCMISS_Pos) /*!< 0x00000200 */ +#define CRS_ISR_SYNCMISS CRS_ISR_SYNCMISS_Msk /*!< SYNC missed */ +#define CRS_ISR_TRIMOVF_Pos (10U) +#define CRS_ISR_TRIMOVF_Msk (0x1UL << CRS_ISR_TRIMOVF_Pos) /*!< 0x00000400 */ +#define CRS_ISR_TRIMOVF CRS_ISR_TRIMOVF_Msk /*!< Trimming overflow or underflow */ +#define CRS_ISR_FEDIR_Pos (15U) +#define CRS_ISR_FEDIR_Msk (0x1UL << CRS_ISR_FEDIR_Pos) /*!< 0x00008000 */ +#define CRS_ISR_FEDIR CRS_ISR_FEDIR_Msk /*!< Frequency error direction */ +#define CRS_ISR_FECAP_Pos (16U) +#define CRS_ISR_FECAP_Msk (0xFFFFUL << CRS_ISR_FECAP_Pos) /*!< 0xFFFF0000 */ +#define CRS_ISR_FECAP CRS_ISR_FECAP_Msk /*!< Frequency error capture */ + +/******************* Bit definition for CRS_ICR register *********************/ +#define CRS_ICR_SYNCOKC_Pos (0U) +#define CRS_ICR_SYNCOKC_Msk (0x1UL << CRS_ICR_SYNCOKC_Pos) /*!< 0x00000001 */ +#define CRS_ICR_SYNCOKC CRS_ICR_SYNCOKC_Msk /*!< SYNC event OK clear flag */ +#define CRS_ICR_SYNCWARNC_Pos (1U) +#define CRS_ICR_SYNCWARNC_Msk (0x1UL << CRS_ICR_SYNCWARNC_Pos) /*!< 0x00000002 */ +#define CRS_ICR_SYNCWARNC CRS_ICR_SYNCWARNC_Msk /*!< SYNC warning clear flag */ +#define CRS_ICR_ERRC_Pos (2U) +#define CRS_ICR_ERRC_Msk (0x1UL << CRS_ICR_ERRC_Pos) /*!< 0x00000004 */ +#define CRS_ICR_ERRC CRS_ICR_ERRC_Msk /*!< Error clear flag */ +#define CRS_ICR_ESYNCC_Pos (3U) +#define CRS_ICR_ESYNCC_Msk (0x1UL << CRS_ICR_ESYNCC_Pos) /*!< 0x00000008 */ +#define CRS_ICR_ESYNCC CRS_ICR_ESYNCC_Msk /*!< Expected SYNC clear flag */ + +/******************************************************************************/ +/* */ +/* Digital to Analog Converter */ +/* */ +/******************************************************************************/ +/* + * @brief Specific device feature definitions (not present on all devices in the STM32G4 series) + */ +#define DAC_CHANNEL2_SUPPORT /*!< DAC feature available only on specific devices: DAC channel 2 available */ + +/******************** Bit definition for DAC_CR register ********************/ +#define DAC_CR_EN1_Pos (0U) +#define DAC_CR_EN1_Msk (0x1UL << DAC_CR_EN1_Pos) /*!< 0x00000001 */ +#define DAC_CR_EN1 DAC_CR_EN1_Msk /*!*/ +#define DAC_CR_CEN1_Pos (14U) +#define DAC_CR_CEN1_Msk (0x1UL << DAC_CR_CEN1_Pos) /*!< 0x00004000 */ +#define DAC_CR_CEN1 DAC_CR_CEN1_Msk /*!*/ + +#define DAC_CR_HFSEL_Pos (15U) +#define DAC_CR_HFSEL_Msk (0x1UL << DAC_CR_HFSEL_Pos) /*!< 0x00008000 */ +#define DAC_CR_HFSEL DAC_CR_HFSEL_Msk /*!*/ + +#define DAC_CR_EN2_Pos (16U) +#define DAC_CR_EN2_Msk (0x1UL << DAC_CR_EN2_Pos) /*!< 0x00010000 */ +#define DAC_CR_EN2 DAC_CR_EN2_Msk /*!*/ +#define DAC_CR_CEN2_Pos (30U) +#define DAC_CR_CEN2_Msk (0x1UL << DAC_CR_CEN2_Pos) /*!< 0x40000000 */ +#define DAC_CR_CEN2 DAC_CR_CEN2_Msk /*!*/ + +/***************** Bit definition for DAC_SWTRIGR register ******************/ +#define DAC_SWTRIGR_SWTRIG1_Pos (0U) +#define DAC_SWTRIGR_SWTRIG1_Msk (0x1UL << DAC_SWTRIGR_SWTRIG1_Pos) /*!< 0x00000001 */ +#define DAC_SWTRIGR_SWTRIG1 DAC_SWTRIGR_SWTRIG1_Msk /*! */ + +/******************* Bit definition for HRTIM_CPT2R register ****************/ +#define HRTIM_CPT2R_CPT2R_Pos (0U) +#define HRTIM_CPT2R_CPT2R_Msk (0x0000FFFFUL << HRTIM_CPT2R_CPT2R_Pos) /*!< 0x0000FFFF */ +#define HRTIM_CPT2R_CPT2R HRTIM_CPT2R_CPT2R_Msk /*!< Capture 2 Value */ +#define HRTIM_CPT2R_DIR_Pos (16U) +#define HRTIM_CPT2R_DIR_Msk (0x1UL << HRTIM_CPT2R_DIR_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT2R_DIR HRTIM_CPT2R_DIR_Msk /*!< Capture 2 direction */ + +/******************** Bit definition for Slave Deadtime register **************/ +#define HRTIM_DTR_DTR_Pos (0U) +#define HRTIM_DTR_DTR_Msk (0x1FFUL << HRTIM_DTR_DTR_Pos) /*!< 0x000001FF */ +#define HRTIM_DTR_DTR HRTIM_DTR_DTR_Msk /*!< Dead time rising value */ +#define HRTIM_DTR_DTR_0 (0x001UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000001 */ +#define HRTIM_DTR_DTR_1 (0x002UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000002 */ +#define HRTIM_DTR_DTR_2 (0x004UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000004 */ +#define HRTIM_DTR_DTR_3 (0x008UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000008 */ +#define HRTIM_DTR_DTR_4 (0x010UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000010 */ +#define HRTIM_DTR_DTR_5 (0x020UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000020 */ +#define HRTIM_DTR_DTR_6 (0x040UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000040 */ +#define HRTIM_DTR_DTR_7 (0x080UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000080 */ +#define HRTIM_DTR_DTR_8 (0x100UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000100 */ +#define HRTIM_DTR_SDTR_Pos (9U) +#define HRTIM_DTR_SDTR_Msk (0x1UL << HRTIM_DTR_SDTR_Pos) /*!< 0x00000200 */ +#define HRTIM_DTR_SDTR HRTIM_DTR_SDTR_Msk /*!< Sign dead time rising value */ +#define HRTIM_DTR_DTPRSC_Pos (10U) +#define HRTIM_DTR_DTPRSC_Msk (0x7UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00001C00 */ +#define HRTIM_DTR_DTPRSC HRTIM_DTR_DTPRSC_Msk /*!< Dead time prescaler */ +#define HRTIM_DTR_DTPRSC_0 (0x1UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00000400 */ +#define HRTIM_DTR_DTPRSC_1 (0x2UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00000800 */ +#define HRTIM_DTR_DTPRSC_2 (0x4UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00001000 */ +#define HRTIM_DTR_DTRSLK_Pos (14U) +#define HRTIM_DTR_DTRSLK_Msk (0x1UL << HRTIM_DTR_DTRSLK_Pos) /*!< 0x00004000 */ +#define HRTIM_DTR_DTRSLK HRTIM_DTR_DTRSLK_Msk /*!< Dead time rising sign lock */ +#define HRTIM_DTR_DTRLK_Pos (15U) +#define HRTIM_DTR_DTRLK_Msk (0x1UL << HRTIM_DTR_DTRLK_Pos) /*!< 0x00008000 */ +#define HRTIM_DTR_DTRLK HRTIM_DTR_DTRLK_Msk /*!< Dead time rising lock */ +#define HRTIM_DTR_DTF_Pos (16U) +#define HRTIM_DTR_DTF_Msk (0x1FFUL << HRTIM_DTR_DTF_Pos) /*!< 0x01FF0000 */ +#define HRTIM_DTR_DTF HRTIM_DTR_DTF_Msk /*!< Dead time falling value */ +#define HRTIM_DTR_DTF_0 (0x001UL << HRTIM_DTR_DTF_Pos) /*!< 0x00010000 */ +#define HRTIM_DTR_DTF_1 (0x002UL << HRTIM_DTR_DTF_Pos) /*!< 0x00020000 */ +#define HRTIM_DTR_DTF_2 (0x004UL << HRTIM_DTR_DTF_Pos) /*!< 0x00040000 */ +#define HRTIM_DTR_DTF_3 (0x008UL << HRTIM_DTR_DTF_Pos) /*!< 0x00080000 */ +#define HRTIM_DTR_DTF_4 (0x010UL << HRTIM_DTR_DTF_Pos) /*!< 0x00100000 */ +#define HRTIM_DTR_DTF_5 (0x020UL << HRTIM_DTR_DTF_Pos) /*!< 0x00200000 */ +#define HRTIM_DTR_DTF_6 (0x040UL << HRTIM_DTR_DTF_Pos) /*!< 0x00400000 */ +#define HRTIM_DTR_DTF_7 (0x080UL << HRTIM_DTR_DTF_Pos) /*!< 0x00800000 */ +#define HRTIM_DTR_DTF_8 (0x100UL << HRTIM_DTR_DTF_Pos) /*!< 0x01000000 */ +#define HRTIM_DTR_SDTF_Pos (25U) +#define HRTIM_DTR_SDTF_Msk (0x1UL << HRTIM_DTR_SDTF_Pos) /*!< 0x02000000 */ +#define HRTIM_DTR_SDTF HRTIM_DTR_SDTF_Msk /*!< Sign dead time falling value */ +#define HRTIM_DTR_DTFSLK_Pos (30U) +#define HRTIM_DTR_DTFSLK_Msk (0x1UL << HRTIM_DTR_DTFSLK_Pos) /*!< 0x40000000 */ +#define HRTIM_DTR_DTFSLK HRTIM_DTR_DTFSLK_Msk /*!< Dead time falling sign lock */ +#define HRTIM_DTR_DTFLK_Pos (31U) +#define HRTIM_DTR_DTFLK_Msk (0x1UL << HRTIM_DTR_DTFLK_Pos) /*!< 0x80000000 */ +#define HRTIM_DTR_DTFLK HRTIM_DTR_DTFLK_Msk /*!< Dead time falling lock */ + +/**** Bit definition for Slave Output 1 set register **************************/ +#define HRTIM_SET1R_SST_Pos (0U) +#define HRTIM_SET1R_SST_Msk (0x1UL << HRTIM_SET1R_SST_Pos) /*!< 0x00000001 */ +#define HRTIM_SET1R_SST HRTIM_SET1R_SST_Msk /*!< software set trigger */ +#define HRTIM_SET1R_RESYNC_Pos (1U) +#define HRTIM_SET1R_RESYNC_Msk (0x1UL << HRTIM_SET1R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_SET1R_RESYNC HRTIM_SET1R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_SET1R_PER_Pos (2U) +#define HRTIM_SET1R_PER_Msk (0x1UL << HRTIM_SET1R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_SET1R_PER HRTIM_SET1R_PER_Msk /*!< Timer A period */ +#define HRTIM_SET1R_CMP1_Pos (3U) +#define HRTIM_SET1R_CMP1_Msk (0x1UL << HRTIM_SET1R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_SET1R_CMP1 HRTIM_SET1R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_SET1R_CMP2_Pos (4U) +#define HRTIM_SET1R_CMP2_Msk (0x1UL << HRTIM_SET1R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_SET1R_CMP2 HRTIM_SET1R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_SET1R_CMP3_Pos (5U) +#define HRTIM_SET1R_CMP3_Msk (0x1UL << HRTIM_SET1R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_SET1R_CMP3 HRTIM_SET1R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_SET1R_CMP4_Pos (6U) +#define HRTIM_SET1R_CMP4_Msk (0x1UL << HRTIM_SET1R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_SET1R_CMP4 HRTIM_SET1R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_SET1R_MSTPER_Pos (7U) +#define HRTIM_SET1R_MSTPER_Msk (0x1UL << HRTIM_SET1R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_SET1R_MSTPER HRTIM_SET1R_MSTPER_Msk /*!< Master period */ +#define HRTIM_SET1R_MSTCMP1_Pos (8U) +#define HRTIM_SET1R_MSTCMP1_Msk (0x1UL << HRTIM_SET1R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_SET1R_MSTCMP1 HRTIM_SET1R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_SET1R_MSTCMP2_Pos (9U) +#define HRTIM_SET1R_MSTCMP2_Msk (0x1UL << HRTIM_SET1R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_SET1R_MSTCMP2 HRTIM_SET1R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_SET1R_MSTCMP3_Pos (10U) +#define HRTIM_SET1R_MSTCMP3_Msk (0x1UL << HRTIM_SET1R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_SET1R_MSTCMP3 HRTIM_SET1R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_SET1R_MSTCMP4_Pos (11U) +#define HRTIM_SET1R_MSTCMP4_Msk (0x1UL << HRTIM_SET1R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_SET1R_MSTCMP4 HRTIM_SET1R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_SET1R_TIMEVNT1_Pos (12U) +#define HRTIM_SET1R_TIMEVNT1_Msk (0x1UL << HRTIM_SET1R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_SET1R_TIMEVNT1 HRTIM_SET1R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_SET1R_TIMEVNT2_Pos (13U) +#define HRTIM_SET1R_TIMEVNT2_Msk (0x1UL << HRTIM_SET1R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_SET1R_TIMEVNT2 HRTIM_SET1R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_SET1R_TIMEVNT3_Pos (14U) +#define HRTIM_SET1R_TIMEVNT3_Msk (0x1UL << HRTIM_SET1R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_SET1R_TIMEVNT3 HRTIM_SET1R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_SET1R_TIMEVNT4_Pos (15U) +#define HRTIM_SET1R_TIMEVNT4_Msk (0x1UL << HRTIM_SET1R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_SET1R_TIMEVNT4 HRTIM_SET1R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_SET1R_TIMEVNT5_Pos (16U) +#define HRTIM_SET1R_TIMEVNT5_Msk (0x1UL << HRTIM_SET1R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_SET1R_TIMEVNT5 HRTIM_SET1R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_SET1R_TIMEVNT6_Pos (17U) +#define HRTIM_SET1R_TIMEVNT6_Msk (0x1UL << HRTIM_SET1R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_SET1R_TIMEVNT6 HRTIM_SET1R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_SET1R_TIMEVNT7_Pos (18U) +#define HRTIM_SET1R_TIMEVNT7_Msk (0x1UL << HRTIM_SET1R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_SET1R_TIMEVNT7 HRTIM_SET1R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_SET1R_TIMEVNT8_Pos (19U) +#define HRTIM_SET1R_TIMEVNT8_Msk (0x1UL << HRTIM_SET1R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_SET1R_TIMEVNT8 HRTIM_SET1R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_SET1R_TIMEVNT9_Pos (20U) +#define HRTIM_SET1R_TIMEVNT9_Msk (0x1UL << HRTIM_SET1R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_SET1R_TIMEVNT9 HRTIM_SET1R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_SET1R_EXTVNT1_Pos (21U) +#define HRTIM_SET1R_EXTVNT1_Msk (0x1UL << HRTIM_SET1R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_SET1R_EXTVNT1 HRTIM_SET1R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_SET1R_EXTVNT2_Pos (22U) +#define HRTIM_SET1R_EXTVNT2_Msk (0x1UL << HRTIM_SET1R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_SET1R_EXTVNT2 HRTIM_SET1R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_SET1R_EXTVNT3_Pos (23U) +#define HRTIM_SET1R_EXTVNT3_Msk (0x1UL << HRTIM_SET1R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_SET1R_EXTVNT3 HRTIM_SET1R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_SET1R_EXTVNT4_Pos (24U) +#define HRTIM_SET1R_EXTVNT4_Msk (0x1UL << HRTIM_SET1R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_SET1R_EXTVNT4 HRTIM_SET1R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_SET1R_EXTVNT5_Pos (25U) +#define HRTIM_SET1R_EXTVNT5_Msk (0x1UL << HRTIM_SET1R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_SET1R_EXTVNT5 HRTIM_SET1R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_SET1R_EXTVNT6_Pos (26U) +#define HRTIM_SET1R_EXTVNT6_Msk (0x1UL << HRTIM_SET1R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_SET1R_EXTVNT6 HRTIM_SET1R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_SET1R_EXTVNT7_Pos (27U) +#define HRTIM_SET1R_EXTVNT7_Msk (0x1UL << HRTIM_SET1R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_SET1R_EXTVNT7 HRTIM_SET1R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_SET1R_EXTVNT8_Pos (28U) +#define HRTIM_SET1R_EXTVNT8_Msk (0x1UL << HRTIM_SET1R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_SET1R_EXTVNT8 HRTIM_SET1R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_SET1R_EXTVNT9_Pos (29U) +#define HRTIM_SET1R_EXTVNT9_Msk (0x1UL << HRTIM_SET1R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_SET1R_EXTVNT9 HRTIM_SET1R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_SET1R_EXTVNT10_Pos (30U) +#define HRTIM_SET1R_EXTVNT10_Msk (0x1UL << HRTIM_SET1R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_SET1R_EXTVNT10 HRTIM_SET1R_EXTVNT10_Msk /*!< External event 10 */ + +#define HRTIM_SET1R_UPDATE_Pos (31U) +#define HRTIM_SET1R_UPDATE_Msk (0x1UL << HRTIM_SET1R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_SET1R_UPDATE HRTIM_SET1R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 1 reset register ************************/ +#define HRTIM_RST1R_SRT_Pos (0U) +#define HRTIM_RST1R_SRT_Msk (0x1UL << HRTIM_RST1R_SRT_Pos) /*!< 0x00000001 */ +#define HRTIM_RST1R_SRT HRTIM_RST1R_SRT_Msk /*!< software reset trigger */ +#define HRTIM_RST1R_RESYNC_Pos (1U) +#define HRTIM_RST1R_RESYNC_Msk (0x1UL << HRTIM_RST1R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_RST1R_RESYNC HRTIM_RST1R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_RST1R_PER_Pos (2U) +#define HRTIM_RST1R_PER_Msk (0x1UL << HRTIM_RST1R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_RST1R_PER HRTIM_RST1R_PER_Msk /*!< Timer A period */ +#define HRTIM_RST1R_CMP1_Pos (3U) +#define HRTIM_RST1R_CMP1_Msk (0x1UL << HRTIM_RST1R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_RST1R_CMP1 HRTIM_RST1R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RST1R_CMP2_Pos (4U) +#define HRTIM_RST1R_CMP2_Msk (0x1UL << HRTIM_RST1R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_RST1R_CMP2 HRTIM_RST1R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RST1R_CMP3_Pos (5U) +#define HRTIM_RST1R_CMP3_Msk (0x1UL << HRTIM_RST1R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_RST1R_CMP3 HRTIM_RST1R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_RST1R_CMP4_Pos (6U) +#define HRTIM_RST1R_CMP4_Msk (0x1UL << HRTIM_RST1R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_RST1R_CMP4 HRTIM_RST1R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RST1R_MSTPER_Pos (7U) +#define HRTIM_RST1R_MSTPER_Msk (0x1UL << HRTIM_RST1R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_RST1R_MSTPER HRTIM_RST1R_MSTPER_Msk /*!< Master period */ +#define HRTIM_RST1R_MSTCMP1_Pos (8U) +#define HRTIM_RST1R_MSTCMP1_Msk (0x1UL << HRTIM_RST1R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_RST1R_MSTCMP1 HRTIM_RST1R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_RST1R_MSTCMP2_Pos (9U) +#define HRTIM_RST1R_MSTCMP2_Msk (0x1UL << HRTIM_RST1R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_RST1R_MSTCMP2 HRTIM_RST1R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_RST1R_MSTCMP3_Pos (10U) +#define HRTIM_RST1R_MSTCMP3_Msk (0x1UL << HRTIM_RST1R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_RST1R_MSTCMP3 HRTIM_RST1R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_RST1R_MSTCMP4_Pos (11U) +#define HRTIM_RST1R_MSTCMP4_Msk (0x1UL << HRTIM_RST1R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_RST1R_MSTCMP4 HRTIM_RST1R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_RST1R_TIMEVNT1_Pos (12U) +#define HRTIM_RST1R_TIMEVNT1_Msk (0x1UL << HRTIM_RST1R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_RST1R_TIMEVNT1 HRTIM_RST1R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_RST1R_TIMEVNT2_Pos (13U) +#define HRTIM_RST1R_TIMEVNT2_Msk (0x1UL << HRTIM_RST1R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_RST1R_TIMEVNT2 HRTIM_RST1R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_RST1R_TIMEVNT3_Pos (14U) +#define HRTIM_RST1R_TIMEVNT3_Msk (0x1UL << HRTIM_RST1R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_RST1R_TIMEVNT3 HRTIM_RST1R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_RST1R_TIMEVNT4_Pos (15U) +#define HRTIM_RST1R_TIMEVNT4_Msk (0x1UL << HRTIM_RST1R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_RST1R_TIMEVNT4 HRTIM_RST1R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_RST1R_TIMEVNT5_Pos (16U) +#define HRTIM_RST1R_TIMEVNT5_Msk (0x1UL << HRTIM_RST1R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_RST1R_TIMEVNT5 HRTIM_RST1R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_RST1R_TIMEVNT6_Pos (17U) +#define HRTIM_RST1R_TIMEVNT6_Msk (0x1UL << HRTIM_RST1R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_RST1R_TIMEVNT6 HRTIM_RST1R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_RST1R_TIMEVNT7_Pos (18U) +#define HRTIM_RST1R_TIMEVNT7_Msk (0x1UL << HRTIM_RST1R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_RST1R_TIMEVNT7 HRTIM_RST1R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_RST1R_TIMEVNT8_Pos (19U) +#define HRTIM_RST1R_TIMEVNT8_Msk (0x1UL << HRTIM_RST1R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_RST1R_TIMEVNT8 HRTIM_RST1R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_RST1R_TIMEVNT9_Pos (20U) +#define HRTIM_RST1R_TIMEVNT9_Msk (0x1UL << HRTIM_RST1R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_RST1R_TIMEVNT9 HRTIM_RST1R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_RST1R_EXTVNT1_Pos (21U) +#define HRTIM_RST1R_EXTVNT1_Msk (0x1UL << HRTIM_RST1R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_RST1R_EXTVNT1 HRTIM_RST1R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_RST1R_EXTVNT2_Pos (22U) +#define HRTIM_RST1R_EXTVNT2_Msk (0x1UL << HRTIM_RST1R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_RST1R_EXTVNT2 HRTIM_RST1R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_RST1R_EXTVNT3_Pos (23U) +#define HRTIM_RST1R_EXTVNT3_Msk (0x1UL << HRTIM_RST1R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_RST1R_EXTVNT3 HRTIM_RST1R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_RST1R_EXTVNT4_Pos (24U) +#define HRTIM_RST1R_EXTVNT4_Msk (0x1UL << HRTIM_RST1R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_RST1R_EXTVNT4 HRTIM_RST1R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_RST1R_EXTVNT5_Pos (25U) +#define HRTIM_RST1R_EXTVNT5_Msk (0x1UL << HRTIM_RST1R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_RST1R_EXTVNT5 HRTIM_RST1R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_RST1R_EXTVNT6_Pos (26U) +#define HRTIM_RST1R_EXTVNT6_Msk (0x1UL << HRTIM_RST1R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_RST1R_EXTVNT6 HRTIM_RST1R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_RST1R_EXTVNT7_Pos (27U) +#define HRTIM_RST1R_EXTVNT7_Msk (0x1UL << HRTIM_RST1R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_RST1R_EXTVNT7 HRTIM_RST1R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_RST1R_EXTVNT8_Pos (28U) +#define HRTIM_RST1R_EXTVNT8_Msk (0x1UL << HRTIM_RST1R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_RST1R_EXTVNT8 HRTIM_RST1R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_RST1R_EXTVNT9_Pos (29U) +#define HRTIM_RST1R_EXTVNT9_Msk (0x1UL << HRTIM_RST1R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_RST1R_EXTVNT9 HRTIM_RST1R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_RST1R_EXTVNT10_Pos (30U) +#define HRTIM_RST1R_EXTVNT10_Msk (0x1UL << HRTIM_RST1R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_RST1R_EXTVNT10 HRTIM_RST1R_EXTVNT10_Msk /*!< External event 10 */ +#define HRTIM_RST1R_UPDATE_Pos (31U) +#define HRTIM_RST1R_UPDATE_Msk (0x1UL << HRTIM_RST1R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_RST1R_UPDATE HRTIM_RST1R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 2 set register **************************/ +#define HRTIM_SET2R_SST_Pos (0U) +#define HRTIM_SET2R_SST_Msk (0x1UL << HRTIM_SET2R_SST_Pos) /*!< 0x00000001 */ +#define HRTIM_SET2R_SST HRTIM_SET2R_SST_Msk /*!< software set trigger */ +#define HRTIM_SET2R_RESYNC_Pos (1U) +#define HRTIM_SET2R_RESYNC_Msk (0x1UL << HRTIM_SET2R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_SET2R_RESYNC HRTIM_SET2R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_SET2R_PER_Pos (2U) +#define HRTIM_SET2R_PER_Msk (0x1UL << HRTIM_SET2R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_SET2R_PER HRTIM_SET2R_PER_Msk /*!< Timer A period */ +#define HRTIM_SET2R_CMP1_Pos (3U) +#define HRTIM_SET2R_CMP1_Msk (0x1UL << HRTIM_SET2R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_SET2R_CMP1 HRTIM_SET2R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_SET2R_CMP2_Pos (4U) +#define HRTIM_SET2R_CMP2_Msk (0x1UL << HRTIM_SET2R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_SET2R_CMP2 HRTIM_SET2R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_SET2R_CMP3_Pos (5U) +#define HRTIM_SET2R_CMP3_Msk (0x1UL << HRTIM_SET2R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_SET2R_CMP3 HRTIM_SET2R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_SET2R_CMP4_Pos (6U) +#define HRTIM_SET2R_CMP4_Msk (0x1UL << HRTIM_SET2R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_SET2R_CMP4 HRTIM_SET2R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_SET2R_MSTPER_Pos (7U) +#define HRTIM_SET2R_MSTPER_Msk (0x1UL << HRTIM_SET2R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_SET2R_MSTPER HRTIM_SET2R_MSTPER_Msk /*!< Master period */ +#define HRTIM_SET2R_MSTCMP1_Pos (8U) +#define HRTIM_SET2R_MSTCMP1_Msk (0x1UL << HRTIM_SET2R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_SET2R_MSTCMP1 HRTIM_SET2R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_SET2R_MSTCMP2_Pos (9U) +#define HRTIM_SET2R_MSTCMP2_Msk (0x1UL << HRTIM_SET2R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_SET2R_MSTCMP2 HRTIM_SET2R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_SET2R_MSTCMP3_Pos (10U) +#define HRTIM_SET2R_MSTCMP3_Msk (0x1UL << HRTIM_SET2R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_SET2R_MSTCMP3 HRTIM_SET2R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_SET2R_MSTCMP4_Pos (11U) +#define HRTIM_SET2R_MSTCMP4_Msk (0x1UL << HRTIM_SET2R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_SET2R_MSTCMP4 HRTIM_SET2R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_SET2R_TIMEVNT1_Pos (12U) +#define HRTIM_SET2R_TIMEVNT1_Msk (0x1UL << HRTIM_SET2R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_SET2R_TIMEVNT1 HRTIM_SET2R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_SET2R_TIMEVNT2_Pos (13U) +#define HRTIM_SET2R_TIMEVNT2_Msk (0x1UL << HRTIM_SET2R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_SET2R_TIMEVNT2 HRTIM_SET2R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_SET2R_TIMEVNT3_Pos (14U) +#define HRTIM_SET2R_TIMEVNT3_Msk (0x1UL << HRTIM_SET2R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_SET2R_TIMEVNT3 HRTIM_SET2R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_SET2R_TIMEVNT4_Pos (15U) +#define HRTIM_SET2R_TIMEVNT4_Msk (0x1UL << HRTIM_SET2R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_SET2R_TIMEVNT4 HRTIM_SET2R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_SET2R_TIMEVNT5_Pos (16U) +#define HRTIM_SET2R_TIMEVNT5_Msk (0x1UL << HRTIM_SET2R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_SET2R_TIMEVNT5 HRTIM_SET2R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_SET2R_TIMEVNT6_Pos (17U) +#define HRTIM_SET2R_TIMEVNT6_Msk (0x1UL << HRTIM_SET2R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_SET2R_TIMEVNT6 HRTIM_SET2R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_SET2R_TIMEVNT7_Pos (18U) +#define HRTIM_SET2R_TIMEVNT7_Msk (0x1UL << HRTIM_SET2R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_SET2R_TIMEVNT7 HRTIM_SET2R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_SET2R_TIMEVNT8_Pos (19U) +#define HRTIM_SET2R_TIMEVNT8_Msk (0x1UL << HRTIM_SET2R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_SET2R_TIMEVNT8 HRTIM_SET2R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_SET2R_TIMEVNT9_Pos (20U) +#define HRTIM_SET2R_TIMEVNT9_Msk (0x1UL << HRTIM_SET2R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_SET2R_TIMEVNT9 HRTIM_SET2R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_SET2R_EXTVNT1_Pos (21U) +#define HRTIM_SET2R_EXTVNT1_Msk (0x1UL << HRTIM_SET2R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_SET2R_EXTVNT1 HRTIM_SET2R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_SET2R_EXTVNT2_Pos (22U) +#define HRTIM_SET2R_EXTVNT2_Msk (0x1UL << HRTIM_SET2R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_SET2R_EXTVNT2 HRTIM_SET2R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_SET2R_EXTVNT3_Pos (23U) +#define HRTIM_SET2R_EXTVNT3_Msk (0x1UL << HRTIM_SET2R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_SET2R_EXTVNT3 HRTIM_SET2R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_SET2R_EXTVNT4_Pos (24U) +#define HRTIM_SET2R_EXTVNT4_Msk (0x1UL << HRTIM_SET2R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_SET2R_EXTVNT4 HRTIM_SET2R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_SET2R_EXTVNT5_Pos (25U) +#define HRTIM_SET2R_EXTVNT5_Msk (0x1UL << HRTIM_SET2R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_SET2R_EXTVNT5 HRTIM_SET2R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_SET2R_EXTVNT6_Pos (26U) +#define HRTIM_SET2R_EXTVNT6_Msk (0x1UL << HRTIM_SET2R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_SET2R_EXTVNT6 HRTIM_SET2R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_SET2R_EXTVNT7_Pos (27U) +#define HRTIM_SET2R_EXTVNT7_Msk (0x1UL << HRTIM_SET2R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_SET2R_EXTVNT7 HRTIM_SET2R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_SET2R_EXTVNT8_Pos (28U) +#define HRTIM_SET2R_EXTVNT8_Msk (0x1UL << HRTIM_SET2R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_SET2R_EXTVNT8 HRTIM_SET2R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_SET2R_EXTVNT9_Pos (29U) +#define HRTIM_SET2R_EXTVNT9_Msk (0x1UL << HRTIM_SET2R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_SET2R_EXTVNT9 HRTIM_SET2R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_SET2R_EXTVNT10_Pos (30U) +#define HRTIM_SET2R_EXTVNT10_Msk (0x1UL << HRTIM_SET2R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_SET2R_EXTVNT10 HRTIM_SET2R_EXTVNT10_Msk /*!< External event 10 */ + +#define HRTIM_SET2R_UPDATE_Pos (31U) +#define HRTIM_SET2R_UPDATE_Msk (0x1UL << HRTIM_SET2R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_SET2R_UPDATE HRTIM_SET2R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 2 reset register ************************/ +#define HRTIM_RST2R_SRT_Pos (0U) +#define HRTIM_RST2R_SRT_Msk (0x1UL << HRTIM_RST2R_SRT_Pos) /*!< 0x00000001 */ +#define HRTIM_RST2R_SRT HRTIM_RST2R_SRT_Msk /*!< software reset trigger */ +#define HRTIM_RST2R_RESYNC_Pos (1U) +#define HRTIM_RST2R_RESYNC_Msk (0x1UL << HRTIM_RST2R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_RST2R_RESYNC HRTIM_RST2R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_RST2R_PER_Pos (2U) +#define HRTIM_RST2R_PER_Msk (0x1UL << HRTIM_RST2R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_RST2R_PER HRTIM_RST2R_PER_Msk /*!< Timer A period */ +#define HRTIM_RST2R_CMP1_Pos (3U) +#define HRTIM_RST2R_CMP1_Msk (0x1UL << HRTIM_RST2R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_RST2R_CMP1 HRTIM_RST2R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RST2R_CMP2_Pos (4U) +#define HRTIM_RST2R_CMP2_Msk (0x1UL << HRTIM_RST2R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_RST2R_CMP2 HRTIM_RST2R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RST2R_CMP3_Pos (5U) +#define HRTIM_RST2R_CMP3_Msk (0x1UL << HRTIM_RST2R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_RST2R_CMP3 HRTIM_RST2R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_RST2R_CMP4_Pos (6U) +#define HRTIM_RST2R_CMP4_Msk (0x1UL << HRTIM_RST2R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_RST2R_CMP4 HRTIM_RST2R_CMP4_Msk /*!< Timer A compare 4 */ +#define HRTIM_RST2R_MSTPER_Pos (7U) +#define HRTIM_RST2R_MSTPER_Msk (0x1UL << HRTIM_RST2R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_RST2R_MSTPER HRTIM_RST2R_MSTPER_Msk /*!< Master period */ +#define HRTIM_RST2R_MSTCMP1_Pos (8U) +#define HRTIM_RST2R_MSTCMP1_Msk (0x1UL << HRTIM_RST2R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_RST2R_MSTCMP1 HRTIM_RST2R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_RST2R_MSTCMP2_Pos (9U) +#define HRTIM_RST2R_MSTCMP2_Msk (0x1UL << HRTIM_RST2R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_RST2R_MSTCMP2 HRTIM_RST2R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_RST2R_MSTCMP3_Pos (10U) +#define HRTIM_RST2R_MSTCMP3_Msk (0x1UL << HRTIM_RST2R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_RST2R_MSTCMP3 HRTIM_RST2R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_RST2R_MSTCMP4_Pos (11U) +#define HRTIM_RST2R_MSTCMP4_Msk (0x1UL << HRTIM_RST2R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_RST2R_MSTCMP4 HRTIM_RST2R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_RST2R_TIMEVNT1_Pos (12U) +#define HRTIM_RST2R_TIMEVNT1_Msk (0x1UL << HRTIM_RST2R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_RST2R_TIMEVNT1 HRTIM_RST2R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_RST2R_TIMEVNT2_Pos (13U) +#define HRTIM_RST2R_TIMEVNT2_Msk (0x1UL << HRTIM_RST2R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_RST2R_TIMEVNT2 HRTIM_RST2R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_RST2R_TIMEVNT3_Pos (14U) +#define HRTIM_RST2R_TIMEVNT3_Msk (0x1UL << HRTIM_RST2R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_RST2R_TIMEVNT3 HRTIM_RST2R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_RST2R_TIMEVNT4_Pos (15U) +#define HRTIM_RST2R_TIMEVNT4_Msk (0x1UL << HRTIM_RST2R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_RST2R_TIMEVNT4 HRTIM_RST2R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_RST2R_TIMEVNT5_Pos (16U) +#define HRTIM_RST2R_TIMEVNT5_Msk (0x1UL << HRTIM_RST2R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_RST2R_TIMEVNT5 HRTIM_RST2R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_RST2R_TIMEVNT6_Pos (17U) +#define HRTIM_RST2R_TIMEVNT6_Msk (0x1UL << HRTIM_RST2R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_RST2R_TIMEVNT6 HRTIM_RST2R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_RST2R_TIMEVNT7_Pos (18U) +#define HRTIM_RST2R_TIMEVNT7_Msk (0x1UL << HRTIM_RST2R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_RST2R_TIMEVNT7 HRTIM_RST2R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_RST2R_TIMEVNT8_Pos (19U) +#define HRTIM_RST2R_TIMEVNT8_Msk (0x1UL << HRTIM_RST2R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_RST2R_TIMEVNT8 HRTIM_RST2R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_RST2R_TIMEVNT9_Pos (20U) +#define HRTIM_RST2R_TIMEVNT9_Msk (0x1UL << HRTIM_RST2R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_RST2R_TIMEVNT9 HRTIM_RST2R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_RST2R_EXTVNT1_Pos (21U) +#define HRTIM_RST2R_EXTVNT1_Msk (0x1UL << HRTIM_RST2R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_RST2R_EXTVNT1 HRTIM_RST2R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_RST2R_EXTVNT2_Pos (22U) +#define HRTIM_RST2R_EXTVNT2_Msk (0x1UL << HRTIM_RST2R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_RST2R_EXTVNT2 HRTIM_RST2R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_RST2R_EXTVNT3_Pos (23U) +#define HRTIM_RST2R_EXTVNT3_Msk (0x1UL << HRTIM_RST2R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_RST2R_EXTVNT3 HRTIM_RST2R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_RST2R_EXTVNT4_Pos (24U) +#define HRTIM_RST2R_EXTVNT4_Msk (0x1UL << HRTIM_RST2R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_RST2R_EXTVNT4 HRTIM_RST2R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_RST2R_EXTVNT5_Pos (25U) +#define HRTIM_RST2R_EXTVNT5_Msk (0x1UL << HRTIM_RST2R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_RST2R_EXTVNT5 HRTIM_RST2R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_RST2R_EXTVNT6_Pos (26U) +#define HRTIM_RST2R_EXTVNT6_Msk (0x1UL << HRTIM_RST2R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_RST2R_EXTVNT6 HRTIM_RST2R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_RST2R_EXTVNT7_Pos (27U) +#define HRTIM_RST2R_EXTVNT7_Msk (0x1UL << HRTIM_RST2R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_RST2R_EXTVNT7 HRTIM_RST2R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_RST2R_EXTVNT8_Pos (28U) +#define HRTIM_RST2R_EXTVNT8_Msk (0x1UL << HRTIM_RST2R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_RST2R_EXTVNT8 HRTIM_RST2R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_RST2R_EXTVNT9_Pos (29U) +#define HRTIM_RST2R_EXTVNT9_Msk (0x1UL << HRTIM_RST2R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_RST2R_EXTVNT9 HRTIM_RST2R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_RST2R_EXTVNT10_Pos (30U) +#define HRTIM_RST2R_EXTVNT10_Msk (0x1UL << HRTIM_RST2R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_RST2R_EXTVNT10 HRTIM_RST2R_EXTVNT10_Msk /*!< External event 10 */ +#define HRTIM_RST2R_UPDATE_Pos (31U) +#define HRTIM_RST2R_UPDATE_Msk (0x1UL << HRTIM_RST2R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_RST2R_UPDATE HRTIM_RST2R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave external event filtering register 1 ***********/ +#define HRTIM_EEFR1_EE1LTCH_Pos (0U) +#define HRTIM_EEFR1_EE1LTCH_Msk (0x1UL << HRTIM_EEFR1_EE1LTCH_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR1_EE1LTCH HRTIM_EEFR1_EE1LTCH_Msk /*!< External Event 1 latch */ +#define HRTIM_EEFR1_EE1FLTR_Pos (1U) +#define HRTIM_EEFR1_EE1FLTR_Msk (0xFUL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x0000001E */ +#define HRTIM_EEFR1_EE1FLTR HRTIM_EEFR1_EE1FLTR_Msk /*!< External Event 1 filter mask */ +#define HRTIM_EEFR1_EE1FLTR_0 (0x1UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR1_EE1FLTR_1 (0x2UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR1_EE1FLTR_2 (0x4UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000008 */ +#define HRTIM_EEFR1_EE1FLTR_3 (0x8UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000010 */ + +#define HRTIM_EEFR1_EE2LTCH_Pos (6U) +#define HRTIM_EEFR1_EE2LTCH_Msk (0x1UL << HRTIM_EEFR1_EE2LTCH_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR1_EE2LTCH HRTIM_EEFR1_EE2LTCH_Msk /*!< External Event 2 latch */ +#define HRTIM_EEFR1_EE2FLTR_Pos (7U) +#define HRTIM_EEFR1_EE2FLTR_Msk (0xFUL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000780 */ +#define HRTIM_EEFR1_EE2FLTR HRTIM_EEFR1_EE2FLTR_Msk /*!< External Event 2 filter mask */ +#define HRTIM_EEFR1_EE2FLTR_0 (0x1UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR1_EE2FLTR_1 (0x2UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR1_EE2FLTR_2 (0x4UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR1_EE2FLTR_3 (0x8UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000400 */ + +#define HRTIM_EEFR1_EE3LTCH_Pos (12U) +#define HRTIM_EEFR1_EE3LTCH_Msk (0x1UL << HRTIM_EEFR1_EE3LTCH_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR1_EE3LTCH HRTIM_EEFR1_EE3LTCH_Msk /*!< External Event 3 latch */ +#define HRTIM_EEFR1_EE3FLTR_Pos (13U) +#define HRTIM_EEFR1_EE3FLTR_Msk (0xFUL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x0001E000 */ +#define HRTIM_EEFR1_EE3FLTR HRTIM_EEFR1_EE3FLTR_Msk /*!< External Event 3 filter mask */ +#define HRTIM_EEFR1_EE3FLTR_0 (0x1UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR1_EE3FLTR_1 (0x2UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00004000 */ +#define HRTIM_EEFR1_EE3FLTR_2 (0x4UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00008000 */ +#define HRTIM_EEFR1_EE3FLTR_3 (0x8UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00010000 */ + +#define HRTIM_EEFR1_EE4LTCH_Pos (18U) +#define HRTIM_EEFR1_EE4LTCH_Msk (0x1UL << HRTIM_EEFR1_EE4LTCH_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR1_EE4LTCH HRTIM_EEFR1_EE4LTCH_Msk /*!< External Event 4 latch */ +#define HRTIM_EEFR1_EE4FLTR_Pos (19U) +#define HRTIM_EEFR1_EE4FLTR_Msk (0xFUL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00780000 */ +#define HRTIM_EEFR1_EE4FLTR HRTIM_EEFR1_EE4FLTR_Msk /*!< External Event 4 filter mask */ +#define HRTIM_EEFR1_EE4FLTR_0 (0x1UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00080000 */ +#define HRTIM_EEFR1_EE4FLTR_1 (0x2UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR1_EE4FLTR_2 (0x4UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR1_EE4FLTR_3 (0x8UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00400000 */ + +#define HRTIM_EEFR1_EE5LTCH_Pos (24U) +#define HRTIM_EEFR1_EE5LTCH_Msk (0x1UL << HRTIM_EEFR1_EE5LTCH_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR1_EE5LTCH HRTIM_EEFR1_EE5LTCH_Msk /*!< External Event 5 latch */ +#define HRTIM_EEFR1_EE5FLTR_Pos (25U) +#define HRTIM_EEFR1_EE5FLTR_Msk (0xFUL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x1E000000 */ +#define HRTIM_EEFR1_EE5FLTR HRTIM_EEFR1_EE5FLTR_Msk /*!< External Event 5 filter mask */ +#define HRTIM_EEFR1_EE5FLTR_0 (0x1UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR1_EE5FLTR_1 (0x2UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR1_EE5FLTR_2 (0x4UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR1_EE5FLTR_3 (0x8UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x10000000 */ + +/**** Bit definition for Slave external event filtering register 2 ***********/ +#define HRTIM_EEFR2_EE6LTCH_Pos (0U) +#define HRTIM_EEFR2_EE6LTCH_Msk (0x1UL << HRTIM_EEFR2_EE6LTCH_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR2_EE6LTCH HRTIM_EEFR2_EE6LTCH_Msk /*!< External Event 6 latch */ +#define HRTIM_EEFR2_EE6FLTR_Pos (1U) +#define HRTIM_EEFR2_EE6FLTR_Msk (0xFUL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x0000001E */ +#define HRTIM_EEFR2_EE6FLTR HRTIM_EEFR2_EE6FLTR_Msk /*!< External Event 6 filter mask */ +#define HRTIM_EEFR2_EE6FLTR_0 (0x1UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR2_EE6FLTR_1 (0x2UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR2_EE6FLTR_2 (0x4UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000008 */ +#define HRTIM_EEFR2_EE6FLTR_3 (0x8UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000010 */ + +#define HRTIM_EEFR2_EE7LTCH_Pos (6U) +#define HRTIM_EEFR2_EE7LTCH_Msk (0x1UL << HRTIM_EEFR2_EE7LTCH_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR2_EE7LTCH HRTIM_EEFR2_EE7LTCH_Msk /*!< External Event 7 latch */ +#define HRTIM_EEFR2_EE7FLTR_Pos (7U) +#define HRTIM_EEFR2_EE7FLTR_Msk (0xFUL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000780 */ +#define HRTIM_EEFR2_EE7FLTR HRTIM_EEFR2_EE7FLTR_Msk /*!< External Event 7 filter mask */ +#define HRTIM_EEFR2_EE7FLTR_0 (0x1UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR2_EE7FLTR_1 (0x2UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR2_EE7FLTR_2 (0x4UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR2_EE7FLTR_3 (0x8UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000400 */ + +#define HRTIM_EEFR2_EE8LTCH_Pos (12U) +#define HRTIM_EEFR2_EE8LTCH_Msk (0x1UL << HRTIM_EEFR2_EE8LTCH_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR2_EE8LTCH HRTIM_EEFR2_EE8LTCH_Msk /*!< External Event 8 latch */ +#define HRTIM_EEFR2_EE8FLTR_Pos (13U) +#define HRTIM_EEFR2_EE8FLTR_Msk (0xFUL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x0001E000 */ +#define HRTIM_EEFR2_EE8FLTR HRTIM_EEFR2_EE8FLTR_Msk /*!< External Event 8 filter mask */ +#define HRTIM_EEFR2_EE8FLTR_0 (0x1UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR2_EE8FLTR_1 (0x2UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00004000 */ +#define HRTIM_EEFR2_EE8FLTR_2 (0x4UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00008000 */ +#define HRTIM_EEFR2_EE8FLTR_3 (0x8UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00010000 */ + +#define HRTIM_EEFR2_EE9LTCH_Pos (18U) +#define HRTIM_EEFR2_EE9LTCH_Msk (0x1UL << HRTIM_EEFR2_EE9LTCH_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR2_EE9LTCH HRTIM_EEFR2_EE9LTCH_Msk /*!< External Event 9 latch */ +#define HRTIM_EEFR2_EE9FLTR_Pos (19U) +#define HRTIM_EEFR2_EE9FLTR_Msk (0xFUL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00780000 */ +#define HRTIM_EEFR2_EE9FLTR HRTIM_EEFR2_EE9FLTR_Msk /*!< External Event 9 filter mask */ +#define HRTIM_EEFR2_EE9FLTR_0 (0x1UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00080000 */ +#define HRTIM_EEFR2_EE9FLTR_1 (0x2UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR2_EE9FLTR_2 (0x4UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR2_EE9FLTR_3 (0x8UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00400000 */ + +#define HRTIM_EEFR2_EE10LTCH_Pos (24U) +#define HRTIM_EEFR2_EE10LTCH_Msk (0x1UL << HRTIM_EEFR2_EE10LTCH_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR2_EE10LTCH HRTIM_EEFR2_EE10LTCH_Msk /*!< External Event 10 latch */ +#define HRTIM_EEFR2_EE10FLTR_Pos (25U) +#define HRTIM_EEFR2_EE10FLTR_Msk (0xFUL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x1E000000 */ +#define HRTIM_EEFR2_EE10FLTR HRTIM_EEFR2_EE10FLTR_Msk /*!< External Event 10 filter mask */ +#define HRTIM_EEFR2_EE10FLTR_0 (0x1UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR2_EE10FLTR_1 (0x2UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR2_EE10FLTR_2 (0x4UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR2_EE10FLTR_3 (0x8UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x10000000 */ + +/**** Bit definition for Slave Timer reset register ***************************/ + +#define HRTIM_RSTR_TIMFCMP1_Pos (0U) +#define HRTIM_RSTR_TIMFCMP1_Msk (0x1UL << HRTIM_RSTR_TIMFCMP1_Pos) /*!< 0x00000001 */ +#define HRTIM_RSTR_TIMFCMP1 HRTIM_RSTR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_RSTR_UPDATE_Pos (1U) +#define HRTIM_RSTR_UPDATE_Msk (0x1UL << HRTIM_RSTR_UPDATE_Pos) /*!< 0x00000002 */ +#define HRTIM_RSTR_UPDATE HRTIM_RSTR_UPDATE_Msk /*!< Timer update */ +#define HRTIM_RSTR_CMP2_Pos (2U) +#define HRTIM_RSTR_CMP2_Msk (0x1UL << HRTIM_RSTR_CMP2_Pos) /*!< 0x00000004 */ +#define HRTIM_RSTR_CMP2 HRTIM_RSTR_CMP2_Msk /*!< Timer compare2 */ +#define HRTIM_RSTR_CMP4_Pos (3U) +#define HRTIM_RSTR_CMP4_Msk (0x1UL << HRTIM_RSTR_CMP4_Pos) /*!< 0x00000008 */ +#define HRTIM_RSTR_CMP4 HRTIM_RSTR_CMP4_Msk /*!< Timer compare4 */ +#define HRTIM_RSTR_MSTPER_Pos (4U) +#define HRTIM_RSTR_MSTPER_Msk (0x1UL << HRTIM_RSTR_MSTPER_Pos) /*!< 0x00000010 */ +#define HRTIM_RSTR_MSTPER HRTIM_RSTR_MSTPER_Msk /*!< Master period */ +#define HRTIM_RSTR_MSTCMP1_Pos (5U) +#define HRTIM_RSTR_MSTCMP1_Msk (0x1UL << HRTIM_RSTR_MSTCMP1_Pos) /*!< 0x00000020 */ +#define HRTIM_RSTR_MSTCMP1 HRTIM_RSTR_MSTCMP1_Msk /*!< Master compare1 */ +#define HRTIM_RSTR_MSTCMP2_Pos (6U) +#define HRTIM_RSTR_MSTCMP2_Msk (0x1UL << HRTIM_RSTR_MSTCMP2_Pos) /*!< 0x00000040 */ +#define HRTIM_RSTR_MSTCMP2 HRTIM_RSTR_MSTCMP2_Msk /*!< Master compare2 */ +#define HRTIM_RSTR_MSTCMP3_Pos (7U) +#define HRTIM_RSTR_MSTCMP3_Msk (0x1UL << HRTIM_RSTR_MSTCMP3_Pos) /*!< 0x00000080 */ +#define HRTIM_RSTR_MSTCMP3 HRTIM_RSTR_MSTCMP3_Msk /*!< Master compare3 */ +#define HRTIM_RSTR_MSTCMP4_Pos (8U) +#define HRTIM_RSTR_MSTCMP4_Msk (0x1UL << HRTIM_RSTR_MSTCMP4_Pos) /*!< 0x00000100 */ +#define HRTIM_RSTR_MSTCMP4 HRTIM_RSTR_MSTCMP4_Msk /*!< Master compare4 */ +#define HRTIM_RSTR_EXTEVNT1_Pos (9U) +#define HRTIM_RSTR_EXTEVNT1_Msk (0x1UL << HRTIM_RSTR_EXTEVNT1_Pos) /*!< 0x00000200 */ +#define HRTIM_RSTR_EXTEVNT1 HRTIM_RSTR_EXTEVNT1_Msk /*!< External event 1 */ +#define HRTIM_RSTR_EXTEVNT2_Pos (10U) +#define HRTIM_RSTR_EXTEVNT2_Msk (0x1UL << HRTIM_RSTR_EXTEVNT2_Pos) /*!< 0x00000400 */ +#define HRTIM_RSTR_EXTEVNT2 HRTIM_RSTR_EXTEVNT2_Msk /*!< External event 2 */ +#define HRTIM_RSTR_EXTEVNT3_Pos (11U) +#define HRTIM_RSTR_EXTEVNT3_Msk (0x1UL << HRTIM_RSTR_EXTEVNT3_Pos) /*!< 0x00000800 */ +#define HRTIM_RSTR_EXTEVNT3 HRTIM_RSTR_EXTEVNT3_Msk /*!< External event 3 */ +#define HRTIM_RSTR_EXTEVNT4_Pos (12U) +#define HRTIM_RSTR_EXTEVNT4_Msk (0x1UL << HRTIM_RSTR_EXTEVNT4_Pos) /*!< 0x00001000 */ +#define HRTIM_RSTR_EXTEVNT4 HRTIM_RSTR_EXTEVNT4_Msk /*!< External event 4 */ +#define HRTIM_RSTR_EXTEVNT5_Pos (13U) +#define HRTIM_RSTR_EXTEVNT5_Msk (0x1UL << HRTIM_RSTR_EXTEVNT5_Pos) /*!< 0x00002000 */ +#define HRTIM_RSTR_EXTEVNT5 HRTIM_RSTR_EXTEVNT5_Msk /*!< External event 5 */ +#define HRTIM_RSTR_EXTEVNT6_Pos (14U) +#define HRTIM_RSTR_EXTEVNT6_Msk (0x1UL << HRTIM_RSTR_EXTEVNT6_Pos) /*!< 0x00004000 */ +#define HRTIM_RSTR_EXTEVNT6 HRTIM_RSTR_EXTEVNT6_Msk /*!< External event 6 */ +#define HRTIM_RSTR_EXTEVNT7_Pos (15U) +#define HRTIM_RSTR_EXTEVNT7_Msk (0x1UL << HRTIM_RSTR_EXTEVNT7_Pos) /*!< 0x00008000 */ +#define HRTIM_RSTR_EXTEVNT7 HRTIM_RSTR_EXTEVNT7_Msk /*!< External event 7 */ +#define HRTIM_RSTR_EXTEVNT8_Pos (16U) +#define HRTIM_RSTR_EXTEVNT8_Msk (0x1UL << HRTIM_RSTR_EXTEVNT8_Pos) /*!< 0x00010000 */ +#define HRTIM_RSTR_EXTEVNT8 HRTIM_RSTR_EXTEVNT8_Msk /*!< External event 8 */ +#define HRTIM_RSTR_EXTEVNT9_Pos (17U) +#define HRTIM_RSTR_EXTEVNT9_Msk (0x1UL << HRTIM_RSTR_EXTEVNT9_Pos) /*!< 0x00020000 */ +#define HRTIM_RSTR_EXTEVNT9 HRTIM_RSTR_EXTEVNT9_Msk /*!< External event 9 */ +#define HRTIM_RSTR_EXTEVNT10_Pos (18U) +#define HRTIM_RSTR_EXTEVNT10_Msk (0x1UL << HRTIM_RSTR_EXTEVNT10_Pos) /*!< 0x00040000 */ +#define HRTIM_RSTR_EXTEVNT10 HRTIM_RSTR_EXTEVNT10_Msk /*!< External event 10 */ + +/* Slave Timer A reset enable bits upon other slave timers events */ +#define HRTIM_RSTR_TIMBCMP1_Pos (19U) +#define HRTIM_RSTR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTR_TIMBCMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTR_TIMBCMP1 HRTIM_RSTR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTR_TIMBCMP2_Pos (20U) +#define HRTIM_RSTR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTR_TIMBCMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTR_TIMBCMP2 HRTIM_RSTR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTR_TIMBCMP4_Pos (21U) +#define HRTIM_RSTR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTR_TIMBCMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTR_TIMBCMP4 HRTIM_RSTR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTR_TIMCCMP1_Pos (22U) +#define HRTIM_RSTR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTR_TIMCCMP1 HRTIM_RSTR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTR_TIMCCMP2_Pos (23U) +#define HRTIM_RSTR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTR_TIMCCMP2 HRTIM_RSTR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTR_TIMCCMP4_Pos (24U) +#define HRTIM_RSTR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTR_TIMCCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTR_TIMCCMP4 HRTIM_RSTR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTR_TIMDCMP1 HRTIM_RSTR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTR_TIMDCMP2 HRTIM_RSTR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTR_TIMDCMP4 HRTIM_RSTR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTR_TIMECMP1_Pos (28U) +#define HRTIM_RSTR_TIMECMP1_Msk (0x1UL << HRTIM_RSTR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTR_TIMECMP1 HRTIM_RSTR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTR_TIMECMP2_Pos (29U) +#define HRTIM_RSTR_TIMECMP2_Msk (0x1UL << HRTIM_RSTR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTR_TIMECMP2 HRTIM_RSTR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTR_TIMECMP4_Pos (30U) +#define HRTIM_RSTR_TIMECMP4_Msk (0x1UL << HRTIM_RSTR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTR_TIMECMP4 HRTIM_RSTR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTR_TIMFCMP2 HRTIM_RSTR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer B reset enable bits upon other slave timers events */ +#define HRTIM_RSTBR_TIMACMP1_Pos (19U) +#define HRTIM_RSTBR_TIMACMP1_Msk (0x1UL << HRTIM_RSTBR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTBR_TIMACMP1 HRTIM_RSTBR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTBR_TIMACMP2_Pos (20U) +#define HRTIM_RSTBR_TIMACMP2_Msk (0x1UL << HRTIM_RSTBR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTBR_TIMACMP2 HRTIM_RSTBR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTBR_TIMACMP4_Pos (21U) +#define HRTIM_RSTBR_TIMACMP4_Msk (0x1UL << HRTIM_RSTBR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTBR_TIMACMP4 HRTIM_RSTBR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTBR_TIMCCMP1_Pos (22U) +#define HRTIM_RSTBR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTBR_TIMCCMP1 HRTIM_RSTBR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTBR_TIMCCMP2_Pos (23U) +#define HRTIM_RSTBR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTBR_TIMCCMP2 HRTIM_RSTBR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTBR_TIMCCMP4_Pos (24U) +#define HRTIM_RSTBR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTBR_TIMCCMP4 HRTIM_RSTBR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTBR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTBR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTBR_TIMDCMP1 HRTIM_RSTBR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTBR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTBR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTBR_TIMDCMP2 HRTIM_RSTBR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTBR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTBR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTBR_TIMDCMP4 HRTIM_RSTBR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTBR_TIMECMP1_Pos (28U) +#define HRTIM_RSTBR_TIMECMP1_Msk (0x1UL << HRTIM_RSTBR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTBR_TIMECMP1 HRTIM_RSTBR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTBR_TIMECMP2_Pos (29U) +#define HRTIM_RSTBR_TIMECMP2_Msk (0x1UL << HRTIM_RSTBR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTBR_TIMECMP2 HRTIM_RSTBR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTBR_TIMECMP4_Pos (30U) +#define HRTIM_RSTBR_TIMECMP4_Msk (0x1UL << HRTIM_RSTBR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTBR_TIMECMP4 HRTIM_RSTBR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTBR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTBR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTBR_TIMFCMP2 HRTIM_RSTBR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer C reset enable bits upon other slave timers events */ +#define HRTIM_RSTCR_TIMACMP1_Pos (19U) +#define HRTIM_RSTCR_TIMACMP1_Msk (0x1UL << HRTIM_RSTCR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTCR_TIMACMP1 HRTIM_RSTCR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTCR_TIMACMP2_Pos (20U) +#define HRTIM_RSTCR_TIMACMP2_Msk (0x1UL << HRTIM_RSTCR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTCR_TIMACMP2 HRTIM_RSTCR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTCR_TIMACMP4_Pos (21U) +#define HRTIM_RSTCR_TIMACMP4_Msk (0x1UL << HRTIM_RSTCR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTCR_TIMACMP4 HRTIM_RSTCR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTCR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTCR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTCR_TIMBCMP1 HRTIM_RSTCR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTCR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTCR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTCR_TIMBCMP2 HRTIM_RSTCR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTCR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTCR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTCR_TIMBCMP4 HRTIM_RSTCR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTCR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTCR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTCR_TIMDCMP1 HRTIM_RSTCR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTCR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTCR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTCR_TIMDCMP2 HRTIM_RSTCR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTCR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTCR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTCR_TIMDCMP4 HRTIM_RSTCR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTCR_TIMECMP1_Pos (28U) +#define HRTIM_RSTCR_TIMECMP1_Msk (0x1UL << HRTIM_RSTCR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTCR_TIMECMP1 HRTIM_RSTCR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTCR_TIMECMP2_Pos (29U) +#define HRTIM_RSTCR_TIMECMP2_Msk (0x1UL << HRTIM_RSTCR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTCR_TIMECMP2 HRTIM_RSTCR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTCR_TIMECMP4_Pos (30U) +#define HRTIM_RSTCR_TIMECMP4_Msk (0x1UL << HRTIM_RSTCR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTCR_TIMECMP4 HRTIM_RSTCR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTCR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTCR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTCR_TIMFCMP2 HRTIM_RSTCR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer D reset enable bits upon other slave timers events */ +#define HRTIM_RSTDR_TIMACMP1_Pos (19U) +#define HRTIM_RSTDR_TIMACMP1_Msk (0x1UL << HRTIM_RSTDR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTDR_TIMACMP1 HRTIM_RSTDR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTDR_TIMACMP2_Pos (20U) +#define HRTIM_RSTDR_TIMACMP2_Msk (0x1UL << HRTIM_RSTDR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTDR_TIMACMP2 HRTIM_RSTDR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTDR_TIMACMP4_Pos (21U) +#define HRTIM_RSTDR_TIMACMP4_Msk (0x1UL << HRTIM_RSTDR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTDR_TIMACMP4 HRTIM_RSTDR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTDR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTDR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTDR_TIMBCMP1 HRTIM_RSTDR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTDR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTDR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTDR_TIMBCMP2 HRTIM_RSTDR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTDR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTDR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTDR_TIMBCMP4 HRTIM_RSTDR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTDR_TIMCCMP1_Pos (25U) +#define HRTIM_RSTDR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTDR_TIMCCMP1 HRTIM_RSTDR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTDR_TIMCCMP2_Pos (26U) +#define HRTIM_RSTDR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTDR_TIMCCMP2 HRTIM_RSTDR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTDR_TIMCCMP4_Pos (27U) +#define HRTIM_RSTDR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTDR_TIMCCMP4 HRTIM_RSTDR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTDR_TIMECMP1_Pos (28U) +#define HRTIM_RSTDR_TIMECMP1_Msk (0x1UL << HRTIM_RSTDR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTDR_TIMECMP1 HRTIM_RSTDR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTDR_TIMECMP2_Pos (29U) +#define HRTIM_RSTDR_TIMECMP2_Msk (0x1UL << HRTIM_RSTDR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTDR_TIMECMP2 HRTIM_RSTDR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTDR_TIMECMP4_Pos (30U) +#define HRTIM_RSTDR_TIMECMP4_Msk (0x1UL << HRTIM_RSTDR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTDR_TIMECMP4 HRTIM_RSTDR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTDR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTDR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTDR_TIMFCMP2 HRTIM_RSTDR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer E reset enable bits upon other slave timers events */ +#define HRTIM_RSTER_TIMACMP1_Pos (19U) +#define HRTIM_RSTER_TIMACMP1_Msk (0x1UL << HRTIM_RSTER_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTER_TIMACMP1 HRTIM_RSTER_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTER_TIMACMP2_Pos (20U) +#define HRTIM_RSTER_TIMACMP2_Msk (0x1UL << HRTIM_RSTER_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTER_TIMACMP2 HRTIM_RSTER_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTER_TIMACMP4_Pos (21U) +#define HRTIM_RSTER_TIMACMP4_Msk (0x1UL << HRTIM_RSTER_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTER_TIMACMP4 HRTIM_RSTER_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTER_TIMBCMP1_Pos (22U) +#define HRTIM_RSTER_TIMBCMP1_Msk (0x1UL << HRTIM_RSTER_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTER_TIMBCMP1 HRTIM_RSTER_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTER_TIMBCMP2_Pos (23U) +#define HRTIM_RSTER_TIMBCMP2_Msk (0x1UL << HRTIM_RSTER_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTER_TIMBCMP2 HRTIM_RSTER_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTER_TIMBCMP4_Pos (24U) +#define HRTIM_RSTER_TIMBCMP4_Msk (0x1UL << HRTIM_RSTER_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTER_TIMBCMP4 HRTIM_RSTER_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTER_TIMCCMP1_Pos (25U) +#define HRTIM_RSTER_TIMCCMP1_Msk (0x1UL << HRTIM_RSTER_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTER_TIMCCMP1 HRTIM_RSTER_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTER_TIMCCMP2_Pos (26U) +#define HRTIM_RSTER_TIMCCMP2_Msk (0x1UL << HRTIM_RSTER_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTER_TIMCCMP2 HRTIM_RSTER_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTER_TIMCCMP4_Pos (27U) +#define HRTIM_RSTER_TIMCCMP4_Msk (0x1UL << HRTIM_RSTER_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTER_TIMCCMP4 HRTIM_RSTER_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTER_TIMDCMP1_Pos (28U) +#define HRTIM_RSTER_TIMDCMP1_Msk (0x1UL << HRTIM_RSTER_TIMDCMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTER_TIMDCMP1 HRTIM_RSTER_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTER_TIMDCMP2_Pos (29U) +#define HRTIM_RSTER_TIMDCMP2_Msk (0x1UL << HRTIM_RSTER_TIMDCMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTER_TIMDCMP2 HRTIM_RSTER_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTER_TIMDCMP4_Pos (30U) +#define HRTIM_RSTER_TIMDCMP4_Msk (0x1UL << HRTIM_RSTER_TIMDCMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTER_TIMDCMP4 HRTIM_RSTER_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTER_TIMFCMP2_Pos (31U) +#define HRTIM_RSTER_TIMFCMP2_Msk (0x1UL << HRTIM_RSTER_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTER_TIMFCMP2 HRTIM_RSTER_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer F reset enable bits upon other slave timers events */ +#define HRTIM_RSTFR_TIMACMP1_Pos (19U) +#define HRTIM_RSTFR_TIMACMP1_Msk (0x1UL << HRTIM_RSTFR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTFR_TIMACMP1 HRTIM_RSTFR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTFR_TIMACMP2_Pos (20U) +#define HRTIM_RSTFR_TIMACMP2_Msk (0x1UL << HRTIM_RSTFR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTFR_TIMACMP2 HRTIM_RSTFR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTFR_TIMACMP4_Pos (21U) +#define HRTIM_RSTFR_TIMACMP4_Msk (0x1UL << HRTIM_RSTFR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTFR_TIMACMP4 HRTIM_RSTFR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTFR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTFR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTFR_TIMBCMP1 HRTIM_RSTFR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTFR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTFR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTFR_TIMBCMP2 HRTIM_RSTFR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTFR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTFR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTFR_TIMBCMP4 HRTIM_RSTFR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTFR_TIMCCMP1_Pos (25U) +#define HRTIM_RSTFR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTFR_TIMCCMP1 HRTIM_RSTFR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTFR_TIMCCMP2_Pos (26U) +#define HRTIM_RSTFR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTFR_TIMCCMP2 HRTIM_RSTFR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTFR_TIMCCMP4_Pos (27U) +#define HRTIM_RSTFR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTFR_TIMCCMP4 HRTIM_RSTFR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTFR_TIMDCMP1_Pos (28U) +#define HRTIM_RSTFR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTFR_TIMDCMP1 HRTIM_RSTFR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTFR_TIMDCMP2_Pos (29U) +#define HRTIM_RSTFR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTFR_TIMDCMP2 HRTIM_RSTFR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTFR_TIMDCMP4_Pos (30U) +#define HRTIM_RSTFR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTFR_TIMDCMP4 HRTIM_RSTFR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTFR_TIMECMP2_Pos (31U) +#define HRTIM_RSTFR_TIMECMP2_Msk (0x1UL << HRTIM_RSTFR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTFR_TIMECMP2 HRTIM_RSTFR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Chopper register *************************/ +#define HRTIM_CHPR_CARFRQ_Pos (0U) +#define HRTIM_CHPR_CARFRQ_Msk (0xFUL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x0000000F */ +#define HRTIM_CHPR_CARFRQ HRTIM_CHPR_CARFRQ_Msk /*!< Timer carrier frequency value */ +#define HRTIM_CHPR_CARFRQ_0 (0x1UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000001 */ +#define HRTIM_CHPR_CARFRQ_1 (0x2UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000002 */ +#define HRTIM_CHPR_CARFRQ_2 (0x4UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000004 */ +#define HRTIM_CHPR_CARFRQ_3 (0x8UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000008 */ + +#define HRTIM_CHPR_CARDTY_Pos (4U) +#define HRTIM_CHPR_CARDTY_Msk (0x7UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000070 */ +#define HRTIM_CHPR_CARDTY HRTIM_CHPR_CARDTY_Msk /*!< Timer chopper duty cycle value */ +#define HRTIM_CHPR_CARDTY_0 (0x1UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000010 */ +#define HRTIM_CHPR_CARDTY_1 (0x2UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000020 */ +#define HRTIM_CHPR_CARDTY_2 (0x4UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000040 */ + +#define HRTIM_CHPR_STRPW_Pos (7U) +#define HRTIM_CHPR_STRPW_Msk (0xFUL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000780 */ +#define HRTIM_CHPR_STRPW HRTIM_CHPR_STRPW_Msk /*!< Timer start pulse width value */ +#define HRTIM_CHPR_STRPW_0 (0x1UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000080 */ +#define HRTIM_CHPR_STRPW_1 (0x2UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000100 */ +#define HRTIM_CHPR_STRPW_2 (0x4UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000200 */ +#define HRTIM_CHPR_STRPW_3 (0x8UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000400 */ + +/**** Bit definition for Slave Timer Capture 1 control register ***************/ +#define HRTIM_CPT1CR_SWCPT_Pos (0U) +#define HRTIM_CPT1CR_SWCPT_Msk (0x1UL << HRTIM_CPT1CR_SWCPT_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT1CR_SWCPT HRTIM_CPT1CR_SWCPT_Msk /*!< Software capture */ +#define HRTIM_CPT1CR_UPDCPT_Pos (1U) +#define HRTIM_CPT1CR_UPDCPT_Msk (0x1UL << HRTIM_CPT1CR_UPDCPT_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT1CR_UPDCPT HRTIM_CPT1CR_UPDCPT_Msk /*!< Update capture */ +#define HRTIM_CPT1CR_EXEV1CPT_Pos (2U) +#define HRTIM_CPT1CR_EXEV1CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV1CPT_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT1CR_EXEV1CPT HRTIM_CPT1CR_EXEV1CPT_Msk /*!< External event 1 capture */ +#define HRTIM_CPT1CR_EXEV2CPT_Pos (3U) +#define HRTIM_CPT1CR_EXEV2CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV2CPT_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT1CR_EXEV2CPT HRTIM_CPT1CR_EXEV2CPT_Msk /*!< External event 2 capture */ +#define HRTIM_CPT1CR_EXEV3CPT_Pos (4U) +#define HRTIM_CPT1CR_EXEV3CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV3CPT_Pos) /*!< 0x00000010 */ +#define HRTIM_CPT1CR_EXEV3CPT HRTIM_CPT1CR_EXEV3CPT_Msk /*!< External event 3 capture */ +#define HRTIM_CPT1CR_EXEV4CPT_Pos (5U) +#define HRTIM_CPT1CR_EXEV4CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV4CPT_Pos) /*!< 0x00000020 */ +#define HRTIM_CPT1CR_EXEV4CPT HRTIM_CPT1CR_EXEV4CPT_Msk /*!< External event 4 capture */ +#define HRTIM_CPT1CR_EXEV5CPT_Pos (6U) +#define HRTIM_CPT1CR_EXEV5CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV5CPT_Pos) /*!< 0x00000040 */ +#define HRTIM_CPT1CR_EXEV5CPT HRTIM_CPT1CR_EXEV5CPT_Msk /*!< External event 5 capture */ +#define HRTIM_CPT1CR_EXEV6CPT_Pos (7U) +#define HRTIM_CPT1CR_EXEV6CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV6CPT_Pos) /*!< 0x00000080 */ +#define HRTIM_CPT1CR_EXEV6CPT HRTIM_CPT1CR_EXEV6CPT_Msk /*!< External event 6 capture */ +#define HRTIM_CPT1CR_EXEV7CPT_Pos (8U) +#define HRTIM_CPT1CR_EXEV7CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV7CPT_Pos) /*!< 0x00000100 */ +#define HRTIM_CPT1CR_EXEV7CPT HRTIM_CPT1CR_EXEV7CPT_Msk /*!< External event 7 capture */ +#define HRTIM_CPT1CR_EXEV8CPT_Pos (9U) +#define HRTIM_CPT1CR_EXEV8CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV8CPT_Pos) /*!< 0x00000200 */ +#define HRTIM_CPT1CR_EXEV8CPT HRTIM_CPT1CR_EXEV8CPT_Msk /*!< External event 8 capture */ +#define HRTIM_CPT1CR_EXEV9CPT_Pos (10U) +#define HRTIM_CPT1CR_EXEV9CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV9CPT_Pos) /*!< 0x00000400 */ +#define HRTIM_CPT1CR_EXEV9CPT HRTIM_CPT1CR_EXEV9CPT_Msk /*!< External event 9 capture */ +#define HRTIM_CPT1CR_EXEV10CPT_Pos (11U) +#define HRTIM_CPT1CR_EXEV10CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV10CPT_Pos) /*!< 0x00000800 */ +#define HRTIM_CPT1CR_EXEV10CPT HRTIM_CPT1CR_EXEV10CPT_Msk /*!< External event 10 capture */ + +#define HRTIM_CPT1CR_TF1SET_Pos (0U) +#define HRTIM_CPT1CR_TF1SET_Msk (0x1UL << HRTIM_CPT1CR_TF1SET_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT1CR_TF1SET HRTIM_CPT1CR_TF1SET_Msk /*!< Timer F output 1 set */ +#define HRTIM_CPT1CR_TF1RST_Pos (1U) +#define HRTIM_CPT1CR_TF1RST_Msk (0x1UL << HRTIM_CPT1CR_TF1RST_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT1CR_TF1RST HRTIM_CPT1CR_TF1RST_Msk /*!< Timer F output 1 reset */ +#define HRTIM_CPT1CR_TIMFCMP1_Pos (2U) +#define HRTIM_CPT1CR_TIMFCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMFCMP1_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT1CR_TIMFCMP1 HRTIM_CPT1CR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_CPT1CR_TIMFCMP2_Pos (3U) +#define HRTIM_CPT1CR_TIMFCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMFCMP2_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT1CR_TIMFCMP2 HRTIM_CPT1CR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +#define HRTIM_CPT1CR_TA1SET_Pos (12U) +#define HRTIM_CPT1CR_TA1SET_Msk (0x1UL << HRTIM_CPT1CR_TA1SET_Pos) /*!< 0x00001000 */ +#define HRTIM_CPT1CR_TA1SET HRTIM_CPT1CR_TA1SET_Msk /*!< Timer A output 1 set */ +#define HRTIM_CPT1CR_TA1RST_Pos (13U) +#define HRTIM_CPT1CR_TA1RST_Msk (0x1UL << HRTIM_CPT1CR_TA1RST_Pos) /*!< 0x00002000 */ +#define HRTIM_CPT1CR_TA1RST HRTIM_CPT1CR_TA1RST_Msk /*!< Timer A output 1 reset */ +#define HRTIM_CPT1CR_TIMACMP1_Pos (14U) +#define HRTIM_CPT1CR_TIMACMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMACMP1_Pos) /*!< 0x00004000 */ +#define HRTIM_CPT1CR_TIMACMP1 HRTIM_CPT1CR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_CPT1CR_TIMACMP2_Pos (15U) +#define HRTIM_CPT1CR_TIMACMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMACMP2_Pos) /*!< 0x00008000 */ +#define HRTIM_CPT1CR_TIMACMP2 HRTIM_CPT1CR_TIMACMP2_Msk /*!< Timer A compare 2 */ + +#define HRTIM_CPT1CR_TB1SET_Pos (16U) +#define HRTIM_CPT1CR_TB1SET_Msk (0x1UL << HRTIM_CPT1CR_TB1SET_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT1CR_TB1SET HRTIM_CPT1CR_TB1SET_Msk /*!< Timer B output 1 set */ +#define HRTIM_CPT1CR_TB1RST_Pos (17U) +#define HRTIM_CPT1CR_TB1RST_Msk (0x1UL << HRTIM_CPT1CR_TB1RST_Pos) /*!< 0x00020000 */ +#define HRTIM_CPT1CR_TB1RST HRTIM_CPT1CR_TB1RST_Msk /*!< Timer B output 1 reset */ +#define HRTIM_CPT1CR_TIMBCMP1_Pos (18U) +#define HRTIM_CPT1CR_TIMBCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMBCMP1_Pos) /*!< 0x00040000 */ +#define HRTIM_CPT1CR_TIMBCMP1 HRTIM_CPT1CR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_CPT1CR_TIMBCMP2_Pos (19U) +#define HRTIM_CPT1CR_TIMBCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMBCMP2_Pos) /*!< 0x00080000 */ +#define HRTIM_CPT1CR_TIMBCMP2 HRTIM_CPT1CR_TIMBCMP2_Msk /*!< Timer B compare 2 */ + +#define HRTIM_CPT1CR_TC1SET_Pos (20U) +#define HRTIM_CPT1CR_TC1SET_Msk (0x1UL << HRTIM_CPT1CR_TC1SET_Pos) /*!< 0x00100000 */ +#define HRTIM_CPT1CR_TC1SET HRTIM_CPT1CR_TC1SET_Msk /*!< Timer C output 1 set */ +#define HRTIM_CPT1CR_TC1RST_Pos (21U) +#define HRTIM_CPT1CR_TC1RST_Msk (0x1UL << HRTIM_CPT1CR_TC1RST_Pos) /*!< 0x00200000 */ +#define HRTIM_CPT1CR_TC1RST HRTIM_CPT1CR_TC1RST_Msk /*!< Timer C output 1 reset */ +#define HRTIM_CPT1CR_TIMCCMP1_Pos (22U) +#define HRTIM_CPT1CR_TIMCCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_CPT1CR_TIMCCMP1 HRTIM_CPT1CR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_CPT1CR_TIMCCMP2_Pos (23U) +#define HRTIM_CPT1CR_TIMCCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_CPT1CR_TIMCCMP2 HRTIM_CPT1CR_TIMCCMP2_Msk /*!< Timer C compare 2 */ + +#define HRTIM_CPT1CR_TD1SET_Pos (24U) +#define HRTIM_CPT1CR_TD1SET_Msk (0x1UL << HRTIM_CPT1CR_TD1SET_Pos) /*!< 0x01000000 */ +#define HRTIM_CPT1CR_TD1SET HRTIM_CPT1CR_TD1SET_Msk /*!< Timer D output 1 set */ +#define HRTIM_CPT1CR_TD1RST_Pos (25U) +#define HRTIM_CPT1CR_TD1RST_Msk (0x1UL << HRTIM_CPT1CR_TD1RST_Pos) /*!< 0x02000000 */ +#define HRTIM_CPT1CR_TD1RST HRTIM_CPT1CR_TD1RST_Msk /*!< Timer D output 1 reset */ +#define HRTIM_CPT1CR_TIMDCMP1_Pos (26U) +#define HRTIM_CPT1CR_TIMDCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMDCMP1_Pos) /*!< 0x04000000 */ +#define HRTIM_CPT1CR_TIMDCMP1 HRTIM_CPT1CR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_CPT1CR_TIMDCMP2_Pos (27U) +#define HRTIM_CPT1CR_TIMDCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMDCMP2_Pos) /*!< 0x08000000 */ +#define HRTIM_CPT1CR_TIMDCMP2 HRTIM_CPT1CR_TIMDCMP2_Msk /*!< Timer D compare 2 */ + +#define HRTIM_CPT1CR_TE1SET_Pos (28U) +#define HRTIM_CPT1CR_TE1SET_Msk (0x1UL << HRTIM_CPT1CR_TE1SET_Pos) /*!< 0x10000000 */ +#define HRTIM_CPT1CR_TE1SET HRTIM_CPT1CR_TE1SET_Msk /*!< Timer E output 1 set */ +#define HRTIM_CPT1CR_TE1RST_Pos (29U) +#define HRTIM_CPT1CR_TE1RST_Msk (0x1UL << HRTIM_CPT1CR_TE1RST_Pos) /*!< 0x20000000 */ +#define HRTIM_CPT1CR_TE1RST HRTIM_CPT1CR_TE1RST_Msk /*!< Timer E output 1 reset */ +#define HRTIM_CPT1CR_TIMECMP1_Pos (30U) +#define HRTIM_CPT1CR_TIMECMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMECMP1_Pos) /*!< 0x40000000 */ +#define HRTIM_CPT1CR_TIMECMP1 HRTIM_CPT1CR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_CPT1CR_TIMECMP2_Pos (31U) +#define HRTIM_CPT1CR_TIMECMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_CPT1CR_TIMECMP2 HRTIM_CPT1CR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Capture 2 control register ***************/ +#define HRTIM_CPT2CR_SWCPT_Pos (0U) +#define HRTIM_CPT2CR_SWCPT_Msk (0x1UL << HRTIM_CPT2CR_SWCPT_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT2CR_SWCPT HRTIM_CPT2CR_SWCPT_Msk /*!< Software capture */ +#define HRTIM_CPT2CR_UPDCPT_Pos (1U) +#define HRTIM_CPT2CR_UPDCPT_Msk (0x1UL << HRTIM_CPT2CR_UPDCPT_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT2CR_UPDCPT HRTIM_CPT2CR_UPDCPT_Msk /*!< Update capture */ +#define HRTIM_CPT2CR_EXEV1CPT_Pos (2U) +#define HRTIM_CPT2CR_EXEV1CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV1CPT_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT2CR_EXEV1CPT HRTIM_CPT2CR_EXEV1CPT_Msk /*!< External event 1 capture */ +#define HRTIM_CPT2CR_EXEV2CPT_Pos (3U) +#define HRTIM_CPT2CR_EXEV2CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV2CPT_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT2CR_EXEV2CPT HRTIM_CPT2CR_EXEV2CPT_Msk /*!< External event 2 capture */ +#define HRTIM_CPT2CR_EXEV3CPT_Pos (4U) +#define HRTIM_CPT2CR_EXEV3CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV3CPT_Pos) /*!< 0x00000010 */ +#define HRTIM_CPT2CR_EXEV3CPT HRTIM_CPT2CR_EXEV3CPT_Msk /*!< External event 3 capture */ +#define HRTIM_CPT2CR_EXEV4CPT_Pos (5U) +#define HRTIM_CPT2CR_EXEV4CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV4CPT_Pos) /*!< 0x00000020 */ +#define HRTIM_CPT2CR_EXEV4CPT HRTIM_CPT2CR_EXEV4CPT_Msk /*!< External event 4 capture */ +#define HRTIM_CPT2CR_EXEV5CPT_Pos (6U) +#define HRTIM_CPT2CR_EXEV5CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV5CPT_Pos) /*!< 0x00000040 */ +#define HRTIM_CPT2CR_EXEV5CPT HRTIM_CPT2CR_EXEV5CPT_Msk /*!< External event 5 capture */ +#define HRTIM_CPT2CR_EXEV6CPT_Pos (7U) +#define HRTIM_CPT2CR_EXEV6CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV6CPT_Pos) /*!< 0x00000080 */ +#define HRTIM_CPT2CR_EXEV6CPT HRTIM_CPT2CR_EXEV6CPT_Msk /*!< External event 6 capture */ +#define HRTIM_CPT2CR_EXEV7CPT_Pos (8U) +#define HRTIM_CPT2CR_EXEV7CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV7CPT_Pos) /*!< 0x00000100 */ +#define HRTIM_CPT2CR_EXEV7CPT HRTIM_CPT2CR_EXEV7CPT_Msk /*!< External event 7 capture */ +#define HRTIM_CPT2CR_EXEV8CPT_Pos (9U) +#define HRTIM_CPT2CR_EXEV8CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV8CPT_Pos) /*!< 0x00000200 */ +#define HRTIM_CPT2CR_EXEV8CPT HRTIM_CPT2CR_EXEV8CPT_Msk /*!< External event 8 capture */ +#define HRTIM_CPT2CR_EXEV9CPT_Pos (10U) +#define HRTIM_CPT2CR_EXEV9CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV9CPT_Pos) /*!< 0x00000400 */ +#define HRTIM_CPT2CR_EXEV9CPT HRTIM_CPT2CR_EXEV9CPT_Msk /*!< External event 9 capture */ +#define HRTIM_CPT2CR_EXEV10CPT_Pos (11U) +#define HRTIM_CPT2CR_EXEV10CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV10CPT_Pos) /*!< 0x00000800 */ +#define HRTIM_CPT2CR_EXEV10CPT HRTIM_CPT2CR_EXEV10CPT_Msk /*!< External event 10 capture */ + +#define HRTIM_CPT2CR_TF1SET_Pos (0U) +#define HRTIM_CPT2CR_TF1SET_Msk (0x1UL << HRTIM_CPT2CR_TF1SET_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT2CR_TF1SET HRTIM_CPT2CR_TF1SET_Msk /*!< Timer F output 1 set */ +#define HRTIM_CPT2CR_TF1RST_Pos (1U) +#define HRTIM_CPT2CR_TF1RST_Msk (0x1UL << HRTIM_CPT2CR_TF1RST_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT2CR_TF1RST HRTIM_CPT2CR_TF1RST_Msk /*!< Timer F output 1 reset */ +#define HRTIM_CPT2CR_TIMFCMP1_Pos (2U) +#define HRTIM_CPT2CR_TIMFCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMFCMP1_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT2CR_TIMFCMP1 HRTIM_CPT2CR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_CPT2CR_TIMFCMP2_Pos (3U) +#define HRTIM_CPT2CR_TIMFCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMFCMP2_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT2CR_TIMFCMP2 HRTIM_CPT2CR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +#define HRTIM_CPT2CR_TA1SET_Pos (12U) +#define HRTIM_CPT2CR_TA1SET_Msk (0x1UL << HRTIM_CPT2CR_TA1SET_Pos) /*!< 0x00001000 */ +#define HRTIM_CPT2CR_TA1SET HRTIM_CPT2CR_TA1SET_Msk /*!< Timer A output 1 set */ +#define HRTIM_CPT2CR_TA1RST_Pos (13U) +#define HRTIM_CPT2CR_TA1RST_Msk (0x1UL << HRTIM_CPT2CR_TA1RST_Pos) /*!< 0x00002000 */ +#define HRTIM_CPT2CR_TA1RST HRTIM_CPT2CR_TA1RST_Msk /*!< Timer A output 1 reset */ +#define HRTIM_CPT2CR_TIMACMP1_Pos (14U) +#define HRTIM_CPT2CR_TIMACMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMACMP1_Pos) /*!< 0x00004000 */ +#define HRTIM_CPT2CR_TIMACMP1 HRTIM_CPT2CR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_CPT2CR_TIMACMP2_Pos (15U) +#define HRTIM_CPT2CR_TIMACMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMACMP2_Pos) /*!< 0x00008000 */ +#define HRTIM_CPT2CR_TIMACMP2 HRTIM_CPT2CR_TIMACMP2_Msk /*!< Timer A compare 2 */ + +#define HRTIM_CPT2CR_TB1SET_Pos (16U) +#define HRTIM_CPT2CR_TB1SET_Msk (0x1UL << HRTIM_CPT2CR_TB1SET_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT2CR_TB1SET HRTIM_CPT2CR_TB1SET_Msk /*!< Timer B output 1 set */ +#define HRTIM_CPT2CR_TB1RST_Pos (17U) +#define HRTIM_CPT2CR_TB1RST_Msk (0x1UL << HRTIM_CPT2CR_TB1RST_Pos) /*!< 0x00020000 */ +#define HRTIM_CPT2CR_TB1RST HRTIM_CPT2CR_TB1RST_Msk /*!< Timer B output 1 reset */ +#define HRTIM_CPT2CR_TIMBCMP1_Pos (18U) +#define HRTIM_CPT2CR_TIMBCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMBCMP1_Pos) /*!< 0x00040000 */ +#define HRTIM_CPT2CR_TIMBCMP1 HRTIM_CPT2CR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_CPT2CR_TIMBCMP2_Pos (19U) +#define HRTIM_CPT2CR_TIMBCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMBCMP2_Pos) /*!< 0x00080000 */ +#define HRTIM_CPT2CR_TIMBCMP2 HRTIM_CPT2CR_TIMBCMP2_Msk /*!< Timer B compare 2 */ + +#define HRTIM_CPT2CR_TC1SET_Pos (20U) +#define HRTIM_CPT2CR_TC1SET_Msk (0x1UL << HRTIM_CPT2CR_TC1SET_Pos) /*!< 0x00100000 */ +#define HRTIM_CPT2CR_TC1SET HRTIM_CPT2CR_TC1SET_Msk /*!< Timer C output 1 set */ +#define HRTIM_CPT2CR_TC1RST_Pos (21U) +#define HRTIM_CPT2CR_TC1RST_Msk (0x1UL << HRTIM_CPT2CR_TC1RST_Pos) /*!< 0x00200000 */ +#define HRTIM_CPT2CR_TC1RST HRTIM_CPT2CR_TC1RST_Msk /*!< Timer C output 1 reset */ +#define HRTIM_CPT2CR_TIMCCMP1_Pos (22U) +#define HRTIM_CPT2CR_TIMCCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_CPT2CR_TIMCCMP1 HRTIM_CPT2CR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_CPT2CR_TIMCCMP2_Pos (23U) +#define HRTIM_CPT2CR_TIMCCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_CPT2CR_TIMCCMP2 HRTIM_CPT2CR_TIMCCMP2_Msk /*!< Timer C compare 2 */ + +#define HRTIM_CPT2CR_TD1SET_Pos (24U) +#define HRTIM_CPT2CR_TD1SET_Msk (0x1UL << HRTIM_CPT2CR_TD1SET_Pos) /*!< 0x01000000 */ +#define HRTIM_CPT2CR_TD1SET HRTIM_CPT2CR_TD1SET_Msk /*!< Timer D output 1 set */ +#define HRTIM_CPT2CR_TD1RST_Pos (25U) +#define HRTIM_CPT2CR_TD1RST_Msk (0x1UL << HRTIM_CPT2CR_TD1RST_Pos) /*!< 0x02000000 */ +#define HRTIM_CPT2CR_TD1RST HRTIM_CPT2CR_TD1RST_Msk /*!< Timer D output 1 reset */ +#define HRTIM_CPT2CR_TIMDCMP1_Pos (26U) +#define HRTIM_CPT2CR_TIMDCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMDCMP1_Pos) /*!< 0x04000000 */ +#define HRTIM_CPT2CR_TIMDCMP1 HRTIM_CPT2CR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_CPT2CR_TIMDCMP2_Pos (27U) +#define HRTIM_CPT2CR_TIMDCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMDCMP2_Pos) /*!< 0x08000000 */ +#define HRTIM_CPT2CR_TIMDCMP2 HRTIM_CPT2CR_TIMDCMP2_Msk /*!< Timer D compare 2 */ + +#define HRTIM_CPT2CR_TE1SET_Pos (28U) +#define HRTIM_CPT2CR_TE1SET_Msk (0x1UL << HRTIM_CPT2CR_TE1SET_Pos) /*!< 0x10000000 */ +#define HRTIM_CPT2CR_TE1SET HRTIM_CPT2CR_TE1SET_Msk /*!< Timer E output 1 set */ +#define HRTIM_CPT2CR_TE1RST_Pos (29U) +#define HRTIM_CPT2CR_TE1RST_Msk (0x1UL << HRTIM_CPT2CR_TE1RST_Pos) /*!< 0x20000000 */ +#define HRTIM_CPT2CR_TE1RST HRTIM_CPT2CR_TE1RST_Msk /*!< Timer E output 1 reset */ +#define HRTIM_CPT2CR_TIMECMP1_Pos (30U) +#define HRTIM_CPT2CR_TIMECMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMECMP1_Pos) /*!< 0x40000000 */ +#define HRTIM_CPT2CR_TIMECMP1 HRTIM_CPT2CR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_CPT2CR_TIMECMP2_Pos (31U) +#define HRTIM_CPT2CR_TIMECMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_CPT2CR_TIMECMP2 HRTIM_CPT2CR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Output register **************************/ +#define HRTIM_OUTR_POL1_Pos (1U) +#define HRTIM_OUTR_POL1_Msk (0x1UL << HRTIM_OUTR_POL1_Pos) /*!< 0x00000002 */ +#define HRTIM_OUTR_POL1 HRTIM_OUTR_POL1_Msk /*!< Slave output 1 polarity */ +#define HRTIM_OUTR_IDLM1_Pos (2U) +#define HRTIM_OUTR_IDLM1_Msk (0x1UL << HRTIM_OUTR_IDLM1_Pos) /*!< 0x00000004 */ +#define HRTIM_OUTR_IDLM1 HRTIM_OUTR_IDLM1_Msk /*!< Slave output 1 idle mode */ +#define HRTIM_OUTR_IDLES1_Pos (3U) +#define HRTIM_OUTR_IDLES1_Msk (0x1UL << HRTIM_OUTR_IDLES1_Pos) /*!< 0x00000008 */ +#define HRTIM_OUTR_IDLES1 HRTIM_OUTR_IDLES1_Msk /*!< Slave output 1 idle state */ +#define HRTIM_OUTR_FAULT1_Pos (4U) +#define HRTIM_OUTR_FAULT1_Msk (0x3UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000030 */ +#define HRTIM_OUTR_FAULT1 HRTIM_OUTR_FAULT1_Msk /*!< Slave output 1 fault state */ +#define HRTIM_OUTR_FAULT1_0 (0x1UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000010 */ +#define HRTIM_OUTR_FAULT1_1 (0x2UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000020 */ +#define HRTIM_OUTR_CHP1_Pos (6U) +#define HRTIM_OUTR_CHP1_Msk (0x1UL << HRTIM_OUTR_CHP1_Pos) /*!< 0x00000040 */ +#define HRTIM_OUTR_CHP1 HRTIM_OUTR_CHP1_Msk /*!< Slave output 1 chopper enable */ +#define HRTIM_OUTR_DIDL1_Pos (7U) +#define HRTIM_OUTR_DIDL1_Msk (0x1UL << HRTIM_OUTR_DIDL1_Pos) /*!< 0x00000080 */ +#define HRTIM_OUTR_DIDL1 HRTIM_OUTR_DIDL1_Msk /*!< Slave output 1 dead time idle */ + +#define HRTIM_OUTR_DTEN_Pos (8U) +#define HRTIM_OUTR_DTEN_Msk (0x1UL << HRTIM_OUTR_DTEN_Pos) /*!< 0x00000100 */ +#define HRTIM_OUTR_DTEN HRTIM_OUTR_DTEN_Msk /*!< Slave output deadtime enable */ +#define HRTIM_OUTR_DLYPRTEN_Pos (9U) +#define HRTIM_OUTR_DLYPRTEN_Msk (0x1UL << HRTIM_OUTR_DLYPRTEN_Pos) /*!< 0x00000200 */ +#define HRTIM_OUTR_DLYPRTEN HRTIM_OUTR_DLYPRTEN_Msk /*!< Slave output delay protection enable */ +#define HRTIM_OUTR_DLYPRT_Pos (10U) +#define HRTIM_OUTR_DLYPRT_Msk (0x7UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00001C00 */ +#define HRTIM_OUTR_DLYPRT HRTIM_OUTR_DLYPRT_Msk /*!< Slave output delay protection */ +#define HRTIM_OUTR_DLYPRT_0 (0x1UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00000400 */ +#define HRTIM_OUTR_DLYPRT_1 (0x2UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00000800 */ +#define HRTIM_OUTR_DLYPRT_2 (0x4UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00001000 */ +#define HRTIM_OUTR_BIAR_Pos (14U) +#define HRTIM_OUTR_BIAR_Msk (0x1UL << HRTIM_OUTR_BIAR_Pos) /*!< 0x00004000 */ +#define HRTIM_OUTR_BIAR HRTIM_OUTR_BIAR_Msk /*!< Slave output Balanced Idle Automatic resume */ +#define HRTIM_OUTR_POL2_Pos (17U) +#define HRTIM_OUTR_POL2_Msk (0x1UL << HRTIM_OUTR_POL2_Pos) /*!< 0x00020000 */ +#define HRTIM_OUTR_POL2 HRTIM_OUTR_POL2_Msk /*!< Slave output 2 polarity */ +#define HRTIM_OUTR_IDLM2_Pos (18U) +#define HRTIM_OUTR_IDLM2_Msk (0x1UL << HRTIM_OUTR_IDLM2_Pos) /*!< 0x00040000 */ +#define HRTIM_OUTR_IDLM2 HRTIM_OUTR_IDLM2_Msk /*!< Slave output 2 idle mode */ +#define HRTIM_OUTR_IDLES2_Pos (19U) +#define HRTIM_OUTR_IDLES2_Msk (0x1UL << HRTIM_OUTR_IDLES2_Pos) /*!< 0x00080000 */ +#define HRTIM_OUTR_IDLES2 HRTIM_OUTR_IDLES2_Msk /*!< Slave output 2 idle state */ +#define HRTIM_OUTR_FAULT2_Pos (20U) +#define HRTIM_OUTR_FAULT2_Msk (0x3UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00300000 */ +#define HRTIM_OUTR_FAULT2 HRTIM_OUTR_FAULT2_Msk /*!< Slave output 2 fault state */ +#define HRTIM_OUTR_FAULT2_0 (0x1UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00100000 */ +#define HRTIM_OUTR_FAULT2_1 (0x2UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00200000 */ +#define HRTIM_OUTR_CHP2_Pos (22U) +#define HRTIM_OUTR_CHP2_Msk (0x1UL << HRTIM_OUTR_CHP2_Pos) /*!< 0x00400000 */ +#define HRTIM_OUTR_CHP2 HRTIM_OUTR_CHP2_Msk /*!< Slave output 2 chopper enable */ +#define HRTIM_OUTR_DIDL2_Pos (23U) +#define HRTIM_OUTR_DIDL2_Msk (0x1UL << HRTIM_OUTR_DIDL2_Pos) /*!< 0x00800000 */ +#define HRTIM_OUTR_DIDL2 HRTIM_OUTR_DIDL2_Msk /*!< Slave output 2 dead time idle */ + +/**** Bit definition for Timerx Fault register ***************************/ +#define HRTIM_FLTR_FLT1EN_Pos (0U) +#define HRTIM_FLTR_FLT1EN_Msk (0x1UL << HRTIM_FLTR_FLT1EN_Pos) /*!< 0x00000001 */ +#define HRTIM_FLTR_FLT1EN HRTIM_FLTR_FLT1EN_Msk /*!< Fault 1 enable */ +#define HRTIM_FLTR_FLT2EN_Pos (1U) +#define HRTIM_FLTR_FLT2EN_Msk (0x1UL << HRTIM_FLTR_FLT2EN_Pos) /*!< 0x00000002 */ +#define HRTIM_FLTR_FLT2EN HRTIM_FLTR_FLT2EN_Msk /*!< Fault 2 enable */ +#define HRTIM_FLTR_FLT3EN_Pos (2U) +#define HRTIM_FLTR_FLT3EN_Msk (0x1UL << HRTIM_FLTR_FLT3EN_Pos) /*!< 0x00000004 */ +#define HRTIM_FLTR_FLT3EN HRTIM_FLTR_FLT3EN_Msk /*!< Fault 3 enable */ +#define HRTIM_FLTR_FLT4EN_Pos (3U) +#define HRTIM_FLTR_FLT4EN_Msk (0x1UL << HRTIM_FLTR_FLT4EN_Pos) /*!< 0x00000008 */ +#define HRTIM_FLTR_FLT4EN HRTIM_FLTR_FLT4EN_Msk /*!< Fault 4 enable */ +#define HRTIM_FLTR_FLT5EN_Pos (4U) +#define HRTIM_FLTR_FLT5EN_Msk (0x1UL << HRTIM_FLTR_FLT5EN_Pos) /*!< 0x00000010 */ +#define HRTIM_FLTR_FLT5EN HRTIM_FLTR_FLT5EN_Msk /*!< Fault 5 enable */ +#define HRTIM_FLTR_FLT6EN_Pos (5U) +#define HRTIM_FLTR_FLT6EN_Msk (0x1UL << HRTIM_FLTR_FLT6EN_Pos) /*!< 0x00000020 */ +#define HRTIM_FLTR_FLT6EN HRTIM_FLTR_FLT6EN_Msk /*!< Fault 6 enable */ +#define HRTIM_FLTR_FLTLCK_Pos (31U) +#define HRTIM_FLTR_FLTLCK_Msk (0x1UL << HRTIM_FLTR_FLTLCK_Pos) /*!< 0x80000000 */ +#define HRTIM_FLTR_FLTLCK HRTIM_FLTR_FLTLCK_Msk /*!< Fault sources lock */ + +/**** Bit definition for HRTIM Timerx control register 2 ****************/ +#define HRTIM_TIMCR2_DCDE_Pos (0U) +#define HRTIM_TIMCR2_DCDE_Msk (0x1UL << HRTIM_TIMCR2_DCDE_Pos) /*!< 0x00000001 */ +#define HRTIM_TIMCR2_DCDE HRTIM_TIMCR2_DCDE_Msk /*!< Dual Channel DAC trigger enable */ +#define HRTIM_TIMCR2_DCDS_Pos (1U) +#define HRTIM_TIMCR2_DCDS_Msk (0x1UL << HRTIM_TIMCR2_DCDS_Pos) /*!< 0x00000002 */ +#define HRTIM_TIMCR2_DCDS HRTIM_TIMCR2_DCDS_Msk /*!< Dual Channel DAC step trigger */ +#define HRTIM_TIMCR2_DCDR_Pos (2U) +#define HRTIM_TIMCR2_DCDR_Msk (0x1UL << HRTIM_TIMCR2_DCDR_Pos) /*!< 0x00000004 */ +#define HRTIM_TIMCR2_DCDR HRTIM_TIMCR2_DCDR_Msk /*!< Dual Channel DAC reset trigger */ +#define HRTIM_TIMCR2_UDM_Pos (4U) +#define HRTIM_TIMCR2_UDM_Msk (0x1UL << HRTIM_TIMCR2_UDM_Pos) /*!< 0x00000010 */ +#define HRTIM_TIMCR2_UDM HRTIM_TIMCR2_UDM_Msk /*!< Up-Down Mode*/ +#define HRTIM_TIMCR2_ROM_Pos (6U) +#define HRTIM_TIMCR2_ROM_Msk (0x3UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x000000C0 */ +#define HRTIM_TIMCR2_ROM HRTIM_TIMCR2_ROM_Msk /*!< Roll-over Mode */ +#define HRTIM_TIMCR2_ROM_0 (0x1UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x00000040 */ +#define HRTIM_TIMCR2_ROM_1 (0x2UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x00000080 */ +#define HRTIM_TIMCR2_OUTROM_Pos (8U) +#define HRTIM_TIMCR2_OUTROM_Msk (0x3UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000300 */ +#define HRTIM_TIMCR2_OUTROM HRTIM_TIMCR2_OUTROM_Msk /*!< Output Roll-Over Mode */ +#define HRTIM_TIMCR2_OUTROM_0 (0x1UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000100 */ +#define HRTIM_TIMCR2_OUTROM_1 (0x2UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000200 */ +#define HRTIM_TIMCR2_ADROM_Pos (10U) +#define HRTIM_TIMCR2_ADROM_Msk (0x3UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000C00 */ +#define HRTIM_TIMCR2_ADROM HRTIM_TIMCR2_ADROM_Msk /*!< ADC Roll-Over Mode */ +#define HRTIM_TIMCR2_ADROM_0 (0x1UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000400 */ +#define HRTIM_TIMCR2_ADROM_1 (0x2UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000800 */ +#define HRTIM_TIMCR2_BMROM_Pos (12U) +#define HRTIM_TIMCR2_BMROM_Msk (0x3UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00003000 */ +#define HRTIM_TIMCR2_BMROM HRTIM_TIMCR2_BMROM_Msk /*!< Burst Mode Rollover Mode */ +#define HRTIM_TIMCR2_BMROM_0 (0x1UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00001000 */ +#define HRTIM_TIMCR2_BMROM_1 (0x2UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00002000 */ +#define HRTIM_TIMCR2_FEROM_Pos (14U) +#define HRTIM_TIMCR2_FEROM_Msk (0x3UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x0000C000 */ +#define HRTIM_TIMCR2_FEROM HRTIM_TIMCR2_FEROM_Msk /*!< Fault and Event Rollover Mode */ +#define HRTIM_TIMCR2_FEROM_0 (0x1UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x00004000 */ +#define HRTIM_TIMCR2_FEROM_1 (0x2UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x00008000 */ +#define HRTIM_TIMCR2_GTCMP1_Pos (16U) +#define HRTIM_TIMCR2_GTCMP1_Msk (0x1UL << HRTIM_TIMCR2_GTCMP1_Pos) /*!< 0x00010000 */ +#define HRTIM_TIMCR2_GTCMP1 HRTIM_TIMCR2_GTCMP1_Msk /*!< Greater than Compare 1 PWM mode */ +#define HRTIM_TIMCR2_GTCMP3_Pos (17U) +#define HRTIM_TIMCR2_GTCMP3_Msk (0x1UL << HRTIM_TIMCR2_GTCMP3_Pos) /*!< 0x00020000 */ +#define HRTIM_TIMCR2_GTCMP3 HRTIM_TIMCR2_GTCMP3_Msk /*!< Greater than Compare 3 PWM mode */ +#define HRTIM_TIMCR2_TRGHLF_Pos (20U) +#define HRTIM_TIMCR2_TRGHLF_Msk (0x1UL << HRTIM_TIMCR2_TRGHLF_Pos) /*!< 0x00100000 */ +#define HRTIM_TIMCR2_TRGHLF HRTIM_TIMCR2_TRGHLF_Msk /*!< Triggered-Half mode */ + +/**** Bit definition for Slave external event filtering register 3 ***********/ +#define HRTIM_EEFR3_EEVACE_Pos (0U) +#define HRTIM_EEFR3_EEVACE_Msk (0x1UL << HRTIM_EEFR3_EEVACE_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR3_EEVACE HRTIM_EEFR3_EEVACE_Msk /*!< External Event A Counter Enable */ +#define HRTIM_EEFR3_EEVACRES_Pos (1U) +#define HRTIM_EEFR3_EEVACRES_Msk (0x1UL << HRTIM_EEFR3_EEVACRES_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR3_EEVACRES HRTIM_EEFR3_EEVACRES_Msk /*!< External Event A Counter Reset */ +#define HRTIM_EEFR3_EEVARSTM_Pos (2U) +#define HRTIM_EEFR3_EEVARSTM_Msk (0x1UL << HRTIM_EEFR3_EEVARSTM_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR3_EEVARSTM HRTIM_EEFR3_EEVARSTM_Msk /*!< External Event A Counter Reset Mode */ +#define HRTIM_EEFR3_EEVASEL_Pos (4U) +#define HRTIM_EEFR3_EEVASEL_Msk (0xFUL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x000000F0 */ +#define HRTIM_EEFR3_EEVASEL HRTIM_EEFR3_EEVASEL_Msk /*!< External Event A Selection */ +#define HRTIM_EEFR3_EEVASEL_0 (0x1UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000010 */ +#define HRTIM_EEFR3_EEVASEL_1 (0x2UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000020 */ +#define HRTIM_EEFR3_EEVASEL_2 (0x4UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR3_EEVASEL_3 (0x8UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR3_EEVACNT_Pos (8U) +#define HRTIM_EEFR3_EEVACNT_Msk (0x3FUL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00003F00 */ +#define HRTIM_EEFR3_EEVACNT HRTIM_EEFR3_EEVACNT_Msk /*!< External Event A Selection */ +#define HRTIM_EEFR3_EEVACNT_0 (0x1UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR3_EEVACNT_1 (0x2UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR3_EEVACNT_2 (0x4UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000400 */ +#define HRTIM_EEFR3_EEVACNT_3 (0x8UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000800 */ +#define HRTIM_EEFR3_EEVACNT_4 (0x10UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR3_EEVACNT_5 (0x20UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR3_EEVBCE_Pos (16U) +#define HRTIM_EEFR3_EEVBCE_Msk (0x1UL << HRTIM_EEFR3_EEVBCE_Pos) /*!< 0x00010000 */ +#define HRTIM_EEFR3_EEVBCE HRTIM_EEFR3_EEVBCE_Msk /*!< External Event B Counter Enable */ +#define HRTIM_EEFR3_EEVBCRES_Pos (17U) +#define HRTIM_EEFR3_EEVBCRES_Msk (0x1UL << HRTIM_EEFR3_EEVBCRES_Pos) /*!< 0x00020000 */ +#define HRTIM_EEFR3_EEVBCRES HRTIM_EEFR3_EEVBCRES_Msk /*!< External Event B Counter Reset */ +#define HRTIM_EEFR3_EEVBRSTM_Pos (18U) +#define HRTIM_EEFR3_EEVBRSTM_Msk (0x1UL << HRTIM_EEFR3_EEVBRSTM_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR3_EEVBRSTM HRTIM_EEFR3_EEVBRSTM_Msk /*!< External Event B Counter Reset Mode */ +#define HRTIM_EEFR3_EEVBSEL_Pos (20U) +#define HRTIM_EEFR3_EEVBSEL_Msk (0xFUL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00F00000 */ +#define HRTIM_EEFR3_EEVBSEL HRTIM_EEFR3_EEVBSEL_Msk /*!< External Event B Selection */ +#define HRTIM_EEFR3_EEVBSEL_0 (0x1UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR3_EEVBSEL_1 (0x2UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR3_EEVBSEL_2 (0x4UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00400000 */ +#define HRTIM_EEFR3_EEVBSEL_3 (0x8UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00800000 */ +#define HRTIM_EEFR3_EEVBCNT_Pos (24U) +#define HRTIM_EEFR3_EEVBCNT_Msk (0x3FUL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x3F000000 */ +#define HRTIM_EEFR3_EEVBCNT HRTIM_EEFR3_EEVBCNT_Msk /*!< External Event B Counter */ +#define HRTIM_EEFR3_EEVBCNT_0 (0x1UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR3_EEVBCNT_1 (0x2UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR3_EEVBCNT_2 (0x4UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR3_EEVBCNT_3 (0x8UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR3_EEVBCNT_4 (0x10UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x10000000 */ +#define HRTIM_EEFR3_EEVBCNT_5 (0x20UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x20000000 */ + +/**** Bit definition for Common HRTIM Timer control register 1 ****************/ +#define HRTIM_CR1_MUDIS_Pos (0U) +#define HRTIM_CR1_MUDIS_Msk (0x1UL << HRTIM_CR1_MUDIS_Pos) /*!< 0x00000001 */ +#define HRTIM_CR1_MUDIS HRTIM_CR1_MUDIS_Msk /*!< Master update disable*/ +#define HRTIM_CR1_TAUDIS_Pos (1U) +#define HRTIM_CR1_TAUDIS_Msk (0x1UL << HRTIM_CR1_TAUDIS_Pos) /*!< 0x00000002 */ +#define HRTIM_CR1_TAUDIS HRTIM_CR1_TAUDIS_Msk /*!< Timer A update disable*/ +#define HRTIM_CR1_TBUDIS_Pos (2U) +#define HRTIM_CR1_TBUDIS_Msk (0x1UL << HRTIM_CR1_TBUDIS_Pos) /*!< 0x00000004 */ +#define HRTIM_CR1_TBUDIS HRTIM_CR1_TBUDIS_Msk /*!< Timer B update disable*/ +#define HRTIM_CR1_TCUDIS_Pos (3U) +#define HRTIM_CR1_TCUDIS_Msk (0x1UL << HRTIM_CR1_TCUDIS_Pos) /*!< 0x00000008 */ +#define HRTIM_CR1_TCUDIS HRTIM_CR1_TCUDIS_Msk /*!< Timer C update disable*/ +#define HRTIM_CR1_TDUDIS_Pos (4U) +#define HRTIM_CR1_TDUDIS_Msk (0x1UL << HRTIM_CR1_TDUDIS_Pos) /*!< 0x00000010 */ +#define HRTIM_CR1_TDUDIS HRTIM_CR1_TDUDIS_Msk /*!< Timer D update disable*/ +#define HRTIM_CR1_TEUDIS_Pos (5U) +#define HRTIM_CR1_TEUDIS_Msk (0x1UL << HRTIM_CR1_TEUDIS_Pos) /*!< 0x00000020 */ +#define HRTIM_CR1_TEUDIS HRTIM_CR1_TEUDIS_Msk /*!< Timer E update disable*/ +#define HRTIM_CR1_TFUDIS_Pos (6U) +#define HRTIM_CR1_TFUDIS_Msk (0x1UL << HRTIM_CR1_TFUDIS_Pos) /*!< 0x00000040 */ +#define HRTIM_CR1_TFUDIS HRTIM_CR1_TFUDIS_Msk /*!< Timer F update disable*/ +#define HRTIM_CR1_ADC1USRC_Pos (16U) +#define HRTIM_CR1_ADC1USRC_Msk (0x7UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00070000 */ +#define HRTIM_CR1_ADC1USRC HRTIM_CR1_ADC1USRC_Msk /*!< ADC Trigger 1 update source */ +#define HRTIM_CR1_ADC1USRC_0 (0x1UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00010000 */ +#define HRTIM_CR1_ADC1USRC_1 (0x2UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00020000 */ +#define HRTIM_CR1_ADC1USRC_2 (0x4UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00040000 */ +#define HRTIM_CR1_ADC2USRC_Pos (19U) +#define HRTIM_CR1_ADC2USRC_Msk (0x7UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00380000 */ +#define HRTIM_CR1_ADC2USRC HRTIM_CR1_ADC2USRC_Msk /*!< ADC Trigger 2 update source */ +#define HRTIM_CR1_ADC2USRC_0 (0x1UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00080000 */ +#define HRTIM_CR1_ADC2USRC_1 (0x2UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00100000 */ +#define HRTIM_CR1_ADC2USRC_2 (0x4UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00200000 */ +#define HRTIM_CR1_ADC3USRC_Pos (22U) +#define HRTIM_CR1_ADC3USRC_Msk (0x7UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x01C00000 */ +#define HRTIM_CR1_ADC3USRC HRTIM_CR1_ADC3USRC_Msk /*!< ADC Trigger 3 update source */ +#define HRTIM_CR1_ADC3USRC_0 (0x1UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x00400000 */ +#define HRTIM_CR1_ADC3USRC_1 (0x2UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x00800000 */ +#define HRTIM_CR1_ADC3USRC_2 (0x4UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x01000000 */ +#define HRTIM_CR1_ADC4USRC_Pos (25U) +#define HRTIM_CR1_ADC4USRC_Msk (0x7UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x0E000000 */ +#define HRTIM_CR1_ADC4USRC HRTIM_CR1_ADC4USRC_Msk /*!< ADC Trigger 4 update source */ +#define HRTIM_CR1_ADC4USRC_0 (0x1UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x02000000 */ +#define HRTIM_CR1_ADC4USRC_1 (0x2UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x04000000 */ +#define HRTIM_CR1_ADC4USRC_2 (0x0UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x0800000 */ + +/**** Bit definition for Common HRTIM Timer control register 2 ****************/ +#define HRTIM_CR2_MSWU_Pos (0U) +#define HRTIM_CR2_MSWU_Msk (0x1UL << HRTIM_CR2_MSWU_Pos) /*!< 0x00000001 */ +#define HRTIM_CR2_MSWU HRTIM_CR2_MSWU_Msk /*!< Master software update */ +#define HRTIM_CR2_TASWU_Pos (1U) +#define HRTIM_CR2_TASWU_Msk (0x1UL << HRTIM_CR2_TASWU_Pos) /*!< 0x00000002 */ +#define HRTIM_CR2_TASWU HRTIM_CR2_TASWU_Msk /*!< Timer A software update */ +#define HRTIM_CR2_TBSWU_Pos (2U) +#define HRTIM_CR2_TBSWU_Msk (0x1UL << HRTIM_CR2_TBSWU_Pos) /*!< 0x00000004 */ +#define HRTIM_CR2_TBSWU HRTIM_CR2_TBSWU_Msk /*!< Timer B software update */ +#define HRTIM_CR2_TCSWU_Pos (3U) +#define HRTIM_CR2_TCSWU_Msk (0x1UL << HRTIM_CR2_TCSWU_Pos) /*!< 0x00000008 */ +#define HRTIM_CR2_TCSWU HRTIM_CR2_TCSWU_Msk /*!< Timer C software update */ +#define HRTIM_CR2_TDSWU_Pos (4U) +#define HRTIM_CR2_TDSWU_Msk (0x1UL << HRTIM_CR2_TDSWU_Pos) /*!< 0x00000010 */ +#define HRTIM_CR2_TDSWU HRTIM_CR2_TDSWU_Msk /*!< Timer D software update */ +#define HRTIM_CR2_TESWU_Pos (5U) +#define HRTIM_CR2_TESWU_Msk (0x1UL << HRTIM_CR2_TESWU_Pos) /*!< 0x00000020 */ +#define HRTIM_CR2_TESWU HRTIM_CR2_TESWU_Msk /*!< Timer E software update */ +#define HRTIM_CR2_TFSWU_Pos (6U) +#define HRTIM_CR2_TFSWU_Msk (0x1UL << HRTIM_CR2_TFSWU_Pos) /*!< 0x00000040 */ +#define HRTIM_CR2_TFSWU HRTIM_CR2_TFSWU_Msk /*!< Timer F software update */ +#define HRTIM_CR2_MRST_Pos (8U) +#define HRTIM_CR2_MRST_Msk (0x1UL << HRTIM_CR2_MRST_Pos) /*!< 0x00000100 */ +#define HRTIM_CR2_MRST HRTIM_CR2_MRST_Msk /*!< Master count software reset */ +#define HRTIM_CR2_TARST_Pos (9U) +#define HRTIM_CR2_TARST_Msk (0x1UL << HRTIM_CR2_TARST_Pos) /*!< 0x00000200 */ +#define HRTIM_CR2_TARST HRTIM_CR2_TARST_Msk /*!< Timer A count software reset */ +#define HRTIM_CR2_TBRST_Pos (10U) +#define HRTIM_CR2_TBRST_Msk (0x1UL << HRTIM_CR2_TBRST_Pos) /*!< 0x00000400 */ +#define HRTIM_CR2_TBRST HRTIM_CR2_TBRST_Msk /*!< Timer B count software reset */ +#define HRTIM_CR2_TCRST_Pos (11U) +#define HRTIM_CR2_TCRST_Msk (0x1UL << HRTIM_CR2_TCRST_Pos) /*!< 0x00000800 */ +#define HRTIM_CR2_TCRST HRTIM_CR2_TCRST_Msk /*!< Timer C count software reset */ +#define HRTIM_CR2_TDRST_Pos (12U) +#define HRTIM_CR2_TDRST_Msk (0x1UL << HRTIM_CR2_TDRST_Pos) /*!< 0x00001000 */ +#define HRTIM_CR2_TDRST HRTIM_CR2_TDRST_Msk /*!< Timer D count software reset */ +#define HRTIM_CR2_TERST_Pos (13U) +#define HRTIM_CR2_TERST_Msk (0x1UL << HRTIM_CR2_TERST_Pos) /*!< 0x00002000 */ +#define HRTIM_CR2_TERST HRTIM_CR2_TERST_Msk /*!< Timer E count software reset */ +#define HRTIM_CR2_TFRST_Pos (14U) +#define HRTIM_CR2_TFRST_Msk (0x1UL << HRTIM_CR2_TFRST_Pos) /*!< 0x00004000 */ +#define HRTIM_CR2_TFRST HRTIM_CR2_TFRST_Msk /*!< Timer F count software reset */ +#define HRTIM_CR2_SWPA_Pos (16U) +#define HRTIM_CR2_SWPA_Msk (0x1UL << HRTIM_CR2_SWPA_Pos) /*!< 0x00010000 */ +#define HRTIM_CR2_SWPA HRTIM_CR2_SWPA_Msk /*!< Timer A swap outputs */ +#define HRTIM_CR2_SWPB_Pos (17U) +#define HRTIM_CR2_SWPB_Msk (0x1UL << HRTIM_CR2_SWPB_Pos) /*!< 0x00020000 */ +#define HRTIM_CR2_SWPB HRTIM_CR2_SWPB_Msk /*!< Timer B swap outputs */ +#define HRTIM_CR2_SWPC_Pos (18U) +#define HRTIM_CR2_SWPC_Msk (0x1UL << HRTIM_CR2_SWPC_Pos) /*!< 0x00040000 */ +#define HRTIM_CR2_SWPC HRTIM_CR2_SWPC_Msk /*!< Timer C swap outputs */ +#define HRTIM_CR2_SWPD_Pos (19U) +#define HRTIM_CR2_SWPD_Msk (0x1UL << HRTIM_CR2_SWPD_Pos) /*!< 0x00080000 */ +#define HRTIM_CR2_SWPD HRTIM_CR2_SWPD_Msk /*!< Timer D swap outputs */ +#define HRTIM_CR2_SWPE_Pos (20U) +#define HRTIM_CR2_SWPE_Msk (0x1UL << HRTIM_CR2_SWPE_Pos) /*!< 0x00100000 */ +#define HRTIM_CR2_SWPE HRTIM_CR2_SWPE_Msk /*!< Timer E swap outputs */ +#define HRTIM_CR2_SWPF_Pos (21U) +#define HRTIM_CR2_SWPF_Msk (0x1UL << HRTIM_CR2_SWPF_Pos) /*!< 0x00200000 */ +#define HRTIM_CR2_SWPF HRTIM_CR2_SWPF_Msk /*!< Timer F swap outputs */ + +/**** Bit definition for Common HRTIM Timer interrupt status register *********/ +#define HRTIM_ISR_FLT1_Pos (0U) +#define HRTIM_ISR_FLT1_Msk (0x1UL << HRTIM_ISR_FLT1_Pos) /*!< 0x00000001 */ +#define HRTIM_ISR_FLT1 HRTIM_ISR_FLT1_Msk /*!< Fault 1 interrupt flag */ +#define HRTIM_ISR_FLT2_Pos (1U) +#define HRTIM_ISR_FLT2_Msk (0x1UL << HRTIM_ISR_FLT2_Pos) /*!< 0x00000002 */ +#define HRTIM_ISR_FLT2 HRTIM_ISR_FLT2_Msk /*!< Fault 2 interrupt flag */ +#define HRTIM_ISR_FLT3_Pos (2U) +#define HRTIM_ISR_FLT3_Msk (0x1UL << HRTIM_ISR_FLT3_Pos) /*!< 0x00000004 */ +#define HRTIM_ISR_FLT3 HRTIM_ISR_FLT3_Msk /*!< Fault 3 interrupt flag */ +#define HRTIM_ISR_FLT4_Pos (3U) +#define HRTIM_ISR_FLT4_Msk (0x1UL << HRTIM_ISR_FLT4_Pos) /*!< 0x00000008 */ +#define HRTIM_ISR_FLT4 HRTIM_ISR_FLT4_Msk /*!< Fault 4 interrupt flag */ +#define HRTIM_ISR_FLT5_Pos (4U) +#define HRTIM_ISR_FLT5_Msk (0x1UL << HRTIM_ISR_FLT5_Pos) /*!< 0x00000010 */ +#define HRTIM_ISR_FLT5 HRTIM_ISR_FLT5_Msk /*!< Fault 5 interrupt flag */ +#define HRTIM_ISR_SYSFLT_Pos (5U) +#define HRTIM_ISR_SYSFLT_Msk (0x1UL << HRTIM_ISR_SYSFLT_Pos) /*!< 0x00000020 */ +#define HRTIM_ISR_SYSFLT HRTIM_ISR_SYSFLT_Msk /*!< System Fault interrupt flag */ +#define HRTIM_ISR_FLT6_Pos (6U) +#define HRTIM_ISR_FLT6_Msk (0x1UL << HRTIM_ISR_FLT6_Pos) /*!< 0x00000040 */ +#define HRTIM_ISR_FLT6 HRTIM_ISR_FLT6_Msk /*!< Fault 6 interrupt flag */ +#define HRTIM_ISR_DLLRDY_Pos (16U) +#define HRTIM_ISR_DLLRDY_Msk (0x1UL << HRTIM_ISR_DLLRDY_Pos) /*!< 0x00010000 */ +#define HRTIM_ISR_DLLRDY HRTIM_ISR_DLLRDY_Msk /*!< DLL ready interrupt flag */ +#define HRTIM_ISR_BMPER_Pos (17U) +#define HRTIM_ISR_BMPER_Msk (0x1UL << HRTIM_ISR_BMPER_Pos) /*!< 0x00020000 */ +#define HRTIM_ISR_BMPER HRTIM_ISR_BMPER_Msk /*!< Burst mode period interrupt flag */ + +/**** Bit definition for Common HRTIM Timer interrupt clear register **********/ +#define HRTIM_ICR_FLT1C_Pos (0U) +#define HRTIM_ICR_FLT1C_Msk (0x1UL << HRTIM_ICR_FLT1C_Pos) /*!< 0x00000001 */ +#define HRTIM_ICR_FLT1C HRTIM_ICR_FLT1C_Msk /*!< Fault 1 interrupt flag clear */ +#define HRTIM_ICR_FLT2C_Pos (1U) +#define HRTIM_ICR_FLT2C_Msk (0x1UL << HRTIM_ICR_FLT2C_Pos) /*!< 0x00000002 */ +#define HRTIM_ICR_FLT2C HRTIM_ICR_FLT2C_Msk /*!< Fault 2 interrupt flag clear */ +#define HRTIM_ICR_FLT3C_Pos (2U) +#define HRTIM_ICR_FLT3C_Msk (0x1UL << HRTIM_ICR_FLT3C_Pos) /*!< 0x00000004 */ +#define HRTIM_ICR_FLT3C HRTIM_ICR_FLT3C_Msk /*!< Fault 3 interrupt flag clear */ +#define HRTIM_ICR_FLT4C_Pos (3U) +#define HRTIM_ICR_FLT4C_Msk (0x1UL << HRTIM_ICR_FLT4C_Pos) /*!< 0x00000008 */ +#define HRTIM_ICR_FLT4C HRTIM_ICR_FLT4C_Msk /*!< Fault 4 interrupt flag clear */ +#define HRTIM_ICR_FLT5C_Pos (4U) +#define HRTIM_ICR_FLT5C_Msk (0x1UL << HRTIM_ICR_FLT5C_Pos) /*!< 0x00000010 */ +#define HRTIM_ICR_FLT5C HRTIM_ICR_FLT5C_Msk /*!< Fault 5 interrupt flag clear */ +#define HRTIM_ICR_SYSFLTC_Pos (5U) +#define HRTIM_ICR_SYSFLTC_Msk (0x1UL << HRTIM_ICR_SYSFLTC_Pos) /*!< 0x00000020 */ +#define HRTIM_ICR_SYSFLTC HRTIM_ICR_SYSFLTC_Msk /*!< System Fault interrupt flag clear */ + +#define HRTIM_ICR_FLT6C_Pos (6U) +#define HRTIM_ICR_FLT6C_Msk (0x1UL << HRTIM_ICR_FLT6C_Pos) /*!< 0x00000040 */ +#define HRTIM_ICR_FLT6C HRTIM_ICR_FLT6C_Msk /*!< Fault 6 interrupt flag clear */ + +#define HRTIM_ICR_DLLRDYC_Pos (16U) +#define HRTIM_ICR_DLLRDYC_Msk (0x1UL << HRTIM_ICR_DLLRDYC_Pos) /*!< 0x00010000 */ +#define HRTIM_ICR_DLLRDYC HRTIM_ICR_DLLRDYC_Msk /*!< DLL ready interrupt flag clear */ +#define HRTIM_ICR_BMPERC_Pos (17U) +#define HRTIM_ICR_BMPERC_Msk (0x1UL << HRTIM_ICR_BMPERC_Pos) /*!< 0x00020000 */ +#define HRTIM_ICR_BMPERC HRTIM_ICR_BMPERC_Msk /*!< Burst mode period interrupt flag clear */ + +/**** Bit definition for Common HRTIM Timer interrupt enable register *********/ +#define HRTIM_IER_FLT1_Pos (0U) +#define HRTIM_IER_FLT1_Msk (0x1UL << HRTIM_IER_FLT1_Pos) /*!< 0x00000001 */ +#define HRTIM_IER_FLT1 HRTIM_IER_FLT1_Msk /*!< Fault 1 interrupt enable */ +#define HRTIM_IER_FLT2_Pos (1U) +#define HRTIM_IER_FLT2_Msk (0x1UL << HRTIM_IER_FLT2_Pos) /*!< 0x00000002 */ +#define HRTIM_IER_FLT2 HRTIM_IER_FLT2_Msk /*!< Fault 2 interrupt enable */ +#define HRTIM_IER_FLT3_Pos (2U) +#define HRTIM_IER_FLT3_Msk (0x1UL << HRTIM_IER_FLT3_Pos) /*!< 0x00000004 */ +#define HRTIM_IER_FLT3 HRTIM_IER_FLT3_Msk /*!< Fault 3 interrupt enable */ +#define HRTIM_IER_FLT4_Pos (3U) +#define HRTIM_IER_FLT4_Msk (0x1UL << HRTIM_IER_FLT4_Pos) /*!< 0x00000008 */ +#define HRTIM_IER_FLT4 HRTIM_IER_FLT4_Msk /*!< Fault 4 interrupt enable */ +#define HRTIM_IER_FLT5_Pos (4U) +#define HRTIM_IER_FLT5_Msk (0x1UL << HRTIM_IER_FLT5_Pos) /*!< 0x00000010 */ +#define HRTIM_IER_FLT5 HRTIM_IER_FLT5_Msk /*!< Fault 5 interrupt enable */ +#define HRTIM_IER_SYSFLT_Pos (5U) +#define HRTIM_IER_SYSFLT_Msk (0x1UL << HRTIM_IER_SYSFLT_Pos) /*!< 0x00000020 */ +#define HRTIM_IER_SYSFLT HRTIM_IER_SYSFLT_Msk /*!< System Fault interrupt enable */ +#define HRTIM_IER_FLT6_Pos (6U) +#define HRTIM_IER_FLT6_Msk (0x1UL << HRTIM_IER_FLT6_Pos) /*!< 0x00000040 */ +#define HRTIM_IER_FLT6 HRTIM_IER_FLT6_Msk /*!< Fault 6 interrupt enable */ + +#define HRTIM_IER_DLLRDY_Pos (16U) +#define HRTIM_IER_DLLRDY_Msk (0x1UL << HRTIM_IER_DLLRDY_Pos) /*!< 0x00010000 */ +#define HRTIM_IER_DLLRDY HRTIM_IER_DLLRDY_Msk /*!< DLL ready interrupt enable */ +#define HRTIM_IER_BMPER_Pos (17U) +#define HRTIM_IER_BMPER_Msk (0x1UL << HRTIM_IER_BMPER_Pos) /*!< 0x00020000 */ +#define HRTIM_IER_BMPER HRTIM_IER_BMPER_Msk /*!< Burst mode period interrupt enable */ + +/**** Bit definition for Common HRTIM Timer output enable register ************/ +#define HRTIM_OENR_TA1OEN_Pos (0U) +#define HRTIM_OENR_TA1OEN_Msk (0x1UL << HRTIM_OENR_TA1OEN_Pos) /*!< 0x00000001 */ +#define HRTIM_OENR_TA1OEN HRTIM_OENR_TA1OEN_Msk /*!< Timer A Output 1 enable */ +#define HRTIM_OENR_TA2OEN_Pos (1U) +#define HRTIM_OENR_TA2OEN_Msk (0x1UL << HRTIM_OENR_TA2OEN_Pos) /*!< 0x00000002 */ +#define HRTIM_OENR_TA2OEN HRTIM_OENR_TA2OEN_Msk /*!< Timer A Output 2 enable */ +#define HRTIM_OENR_TB1OEN_Pos (2U) +#define HRTIM_OENR_TB1OEN_Msk (0x1UL << HRTIM_OENR_TB1OEN_Pos) /*!< 0x00000004 */ +#define HRTIM_OENR_TB1OEN HRTIM_OENR_TB1OEN_Msk /*!< Timer B Output 1 enable */ +#define HRTIM_OENR_TB2OEN_Pos (3U) +#define HRTIM_OENR_TB2OEN_Msk (0x1UL << HRTIM_OENR_TB2OEN_Pos) /*!< 0x00000008 */ +#define HRTIM_OENR_TB2OEN HRTIM_OENR_TB2OEN_Msk /*!< Timer B Output 2 enable */ +#define HRTIM_OENR_TC1OEN_Pos (4U) +#define HRTIM_OENR_TC1OEN_Msk (0x1UL << HRTIM_OENR_TC1OEN_Pos) /*!< 0x00000010 */ +#define HRTIM_OENR_TC1OEN HRTIM_OENR_TC1OEN_Msk /*!< Timer C Output 1 enable */ +#define HRTIM_OENR_TC2OEN_Pos (5U) +#define HRTIM_OENR_TC2OEN_Msk (0x1UL << HRTIM_OENR_TC2OEN_Pos) /*!< 0x00000020 */ +#define HRTIM_OENR_TC2OEN HRTIM_OENR_TC2OEN_Msk /*!< Timer C Output 2 enable */ +#define HRTIM_OENR_TD1OEN_Pos (6U) +#define HRTIM_OENR_TD1OEN_Msk (0x1UL << HRTIM_OENR_TD1OEN_Pos) /*!< 0x00000040 */ +#define HRTIM_OENR_TD1OEN HRTIM_OENR_TD1OEN_Msk /*!< Timer D Output 1 enable */ +#define HRTIM_OENR_TD2OEN_Pos (7U) +#define HRTIM_OENR_TD2OEN_Msk (0x1UL << HRTIM_OENR_TD2OEN_Pos) /*!< 0x00000080 */ +#define HRTIM_OENR_TD2OEN HRTIM_OENR_TD2OEN_Msk /*!< Timer D Output 2 enable */ +#define HRTIM_OENR_TE1OEN_Pos (8U) +#define HRTIM_OENR_TE1OEN_Msk (0x1UL << HRTIM_OENR_TE1OEN_Pos) /*!< 0x00000100 */ +#define HRTIM_OENR_TE1OEN HRTIM_OENR_TE1OEN_Msk /*!< Timer E Output 1 enable */ +#define HRTIM_OENR_TE2OEN_Pos (9U) +#define HRTIM_OENR_TE2OEN_Msk (0x1UL << HRTIM_OENR_TE2OEN_Pos) /*!< 0x00000200 */ +#define HRTIM_OENR_TE2OEN HRTIM_OENR_TE2OEN_Msk /*!< Timer E Output 2 enable */ +#define HRTIM_OENR_TF1OEN_Pos (10U) +#define HRTIM_OENR_TF1OEN_Msk (0x1UL << HRTIM_OENR_TF1OEN_Pos) /*!< 0x00000400 */ +#define HRTIM_OENR_TF1OEN HRTIM_OENR_TF1OEN_Msk /*!< Timer F Output 1 enable */ +#define HRTIM_OENR_TF2OEN_Pos (11U) +#define HRTIM_OENR_TF2OEN_Msk (0x1UL << HRTIM_OENR_TF2OEN_Pos) /*!< 0x00000800 */ +#define HRTIM_OENR_TF2OEN HRTIM_OENR_TF2OEN_Msk /*!< Timer F Output 2 enable */ + +/**** Bit definition for Common HRTIM Timer output disable register ***********/ +#define HRTIM_ODISR_TA1ODIS_Pos (0U) +#define HRTIM_ODISR_TA1ODIS_Msk (0x1UL << HRTIM_ODISR_TA1ODIS_Pos) /*!< 0x00000001 */ +#define HRTIM_ODISR_TA1ODIS HRTIM_ODISR_TA1ODIS_Msk /*!< Timer A Output 1 disable */ +#define HRTIM_ODISR_TA2ODIS_Pos (1U) +#define HRTIM_ODISR_TA2ODIS_Msk (0x1UL << HRTIM_ODISR_TA2ODIS_Pos) /*!< 0x00000002 */ +#define HRTIM_ODISR_TA2ODIS HRTIM_ODISR_TA2ODIS_Msk /*!< Timer A Output 2 disable */ +#define HRTIM_ODISR_TB1ODIS_Pos (2U) +#define HRTIM_ODISR_TB1ODIS_Msk (0x1UL << HRTIM_ODISR_TB1ODIS_Pos) /*!< 0x00000004 */ +#define HRTIM_ODISR_TB1ODIS HRTIM_ODISR_TB1ODIS_Msk /*!< Timer B Output 1 disable */ +#define HRTIM_ODISR_TB2ODIS_Pos (3U) +#define HRTIM_ODISR_TB2ODIS_Msk (0x1UL << HRTIM_ODISR_TB2ODIS_Pos) /*!< 0x00000008 */ +#define HRTIM_ODISR_TB2ODIS HRTIM_ODISR_TB2ODIS_Msk /*!< Timer B Output 2 disable */ +#define HRTIM_ODISR_TC1ODIS_Pos (4U) +#define HRTIM_ODISR_TC1ODIS_Msk (0x1UL << HRTIM_ODISR_TC1ODIS_Pos) /*!< 0x00000010 */ +#define HRTIM_ODISR_TC1ODIS HRTIM_ODISR_TC1ODIS_Msk /*!< Timer C Output 1 disable */ +#define HRTIM_ODISR_TC2ODIS_Pos (5U) +#define HRTIM_ODISR_TC2ODIS_Msk (0x1UL << HRTIM_ODISR_TC2ODIS_Pos) /*!< 0x00000020 */ +#define HRTIM_ODISR_TC2ODIS HRTIM_ODISR_TC2ODIS_Msk /*!< Timer C Output 2 disable */ +#define HRTIM_ODISR_TD1ODIS_Pos (6U) +#define HRTIM_ODISR_TD1ODIS_Msk (0x1UL << HRTIM_ODISR_TD1ODIS_Pos) /*!< 0x00000040 */ +#define HRTIM_ODISR_TD1ODIS HRTIM_ODISR_TD1ODIS_Msk /*!< Timer D Output 1 disable */ +#define HRTIM_ODISR_TD2ODIS_Pos (7U) +#define HRTIM_ODISR_TD2ODIS_Msk (0x1UL << HRTIM_ODISR_TD2ODIS_Pos) /*!< 0x00000080 */ +#define HRTIM_ODISR_TD2ODIS HRTIM_ODISR_TD2ODIS_Msk /*!< Timer D Output 2 disable */ +#define HRTIM_ODISR_TE1ODIS_Pos (8U) +#define HRTIM_ODISR_TE1ODIS_Msk (0x1UL << HRTIM_ODISR_TE1ODIS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODISR_TE1ODIS HRTIM_ODISR_TE1ODIS_Msk /*!< Timer E Output 1 disable */ +#define HRTIM_ODISR_TE2ODIS_Pos (9U) +#define HRTIM_ODISR_TE2ODIS_Msk (0x1UL << HRTIM_ODISR_TE2ODIS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODISR_TE2ODIS HRTIM_ODISR_TE2ODIS_Msk /*!< Timer E Output 2 disable */ +#define HRTIM_ODISR_TF1ODIS_Pos (10U) +#define HRTIM_ODISR_TF1ODIS_Msk (0x1UL << HRTIM_ODISR_TF1ODIS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODISR_TF1ODIS HRTIM_ODISR_TF1ODIS_Msk /*!< Timer F Output 1 disable */ +#define HRTIM_ODISR_TF2ODIS_Pos (11U) +#define HRTIM_ODISR_TF2ODIS_Msk (0x1UL << HRTIM_ODISR_TF2ODIS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODISR_TF2ODIS HRTIM_ODISR_TF2ODIS_Msk /*!< Timer F Output 2 disable */ + +/**** Bit definition for Common HRTIM Timer output disable status register *****/ +#define HRTIM_ODSR_TA1ODS_Pos (0U) +#define HRTIM_ODSR_TA1ODS_Msk (0x1UL << HRTIM_ODSR_TA1ODS_Pos) /*!< 0x00000001 */ +#define HRTIM_ODSR_TA1ODS HRTIM_ODSR_TA1ODS_Msk /*!< Timer A Output 1 disable status */ +#define HRTIM_ODSR_TA2ODS_Pos (1U) +#define HRTIM_ODSR_TA2ODS_Msk (0x1UL << HRTIM_ODSR_TA2ODS_Pos) /*!< 0x00000002 */ +#define HRTIM_ODSR_TA2ODS HRTIM_ODSR_TA2ODS_Msk /*!< Timer A Output 2 disable status */ +#define HRTIM_ODSR_TB1ODS_Pos (2U) +#define HRTIM_ODSR_TB1ODS_Msk (0x1UL << HRTIM_ODSR_TB1ODS_Pos) /*!< 0x00000004 */ +#define HRTIM_ODSR_TB1ODS HRTIM_ODSR_TB1ODS_Msk /*!< Timer B Output 1 disable status */ +#define HRTIM_ODSR_TB2ODS_Pos (3U) +#define HRTIM_ODSR_TB2ODS_Msk (0x1UL << HRTIM_ODSR_TB2ODS_Pos) /*!< 0x00000008 */ +#define HRTIM_ODSR_TB2ODS HRTIM_ODSR_TB2ODS_Msk /*!< Timer B Output 2 disable status */ +#define HRTIM_ODSR_TC1ODS_Pos (4U) +#define HRTIM_ODSR_TC1ODS_Msk (0x1UL << HRTIM_ODSR_TC1ODS_Pos) /*!< 0x00000010 */ +#define HRTIM_ODSR_TC1ODS HRTIM_ODSR_TC1ODS_Msk /*!< Timer C Output 1 disable status */ +#define HRTIM_ODSR_TC2ODS_Pos (5U) +#define HRTIM_ODSR_TC2ODS_Msk (0x1UL << HRTIM_ODSR_TC2ODS_Pos) /*!< 0x00000020 */ +#define HRTIM_ODSR_TC2ODS HRTIM_ODSR_TC2ODS_Msk /*!< Timer C Output 2 disable status */ +#define HRTIM_ODSR_TD1ODS_Pos (6U) +#define HRTIM_ODSR_TD1ODS_Msk (0x1UL << HRTIM_ODSR_TD1ODS_Pos) /*!< 0x00000040 */ +#define HRTIM_ODSR_TD1ODS HRTIM_ODSR_TD1ODS_Msk /*!< Timer D Output 1 disable status */ +#define HRTIM_ODSR_TD2ODS_Pos (7U) +#define HRTIM_ODSR_TD2ODS_Msk (0x1UL << HRTIM_ODSR_TD2ODS_Pos) /*!< 0x00000080 */ +#define HRTIM_ODSR_TD2ODS HRTIM_ODSR_TD2ODS_Msk /*!< Timer D Output 2 disable status */ +#define HRTIM_ODSR_TE1ODS_Pos (8U) +#define HRTIM_ODSR_TE1ODS_Msk (0x1UL << HRTIM_ODSR_TE1ODS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODSR_TE1ODS HRTIM_ODSR_TE1ODS_Msk /*!< Timer E Output 1 disable status */ +#define HRTIM_ODSR_TE2ODS_Pos (9U) +#define HRTIM_ODSR_TE2ODS_Msk (0x1UL << HRTIM_ODSR_TE2ODS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODSR_TE2ODS HRTIM_ODSR_TE2ODS_Msk /*!< Timer E Output 2 disable status */ +#define HRTIM_ODSR_TF1ODS_Pos (10U) +#define HRTIM_ODSR_TF1ODS_Msk (0x1UL << HRTIM_ODSR_TF1ODS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODSR_TF1ODS HRTIM_ODSR_TF1ODS_Msk /*!< Timer F Output 1 disable status */ +#define HRTIM_ODSR_TF2ODS_Pos (11U) +#define HRTIM_ODSR_TF2ODS_Msk (0x1UL << HRTIM_ODSR_TF2ODS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODSR_TF2ODS HRTIM_ODSR_TF2ODS_Msk /*!< Timer F Output 2 disable status */ + +/**** Bit definition for Common HRTIM Timer Burst mode control register ********/ +#define HRTIM_BMCR_BME_Pos (0U) +#define HRTIM_BMCR_BME_Msk (0x1UL << HRTIM_BMCR_BME_Pos) /*!< 0x00000001 */ +#define HRTIM_BMCR_BME HRTIM_BMCR_BME_Msk /*!< Burst mode enable */ +#define HRTIM_BMCR_BMOM_Pos (1U) +#define HRTIM_BMCR_BMOM_Msk (0x1UL << HRTIM_BMCR_BMOM_Pos) /*!< 0x00000002 */ +#define HRTIM_BMCR_BMOM HRTIM_BMCR_BMOM_Msk /*!< Burst mode operating mode */ +#define HRTIM_BMCR_BMCLK_Pos (2U) +#define HRTIM_BMCR_BMCLK_Msk (0xFUL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x0000003C */ +#define HRTIM_BMCR_BMCLK HRTIM_BMCR_BMCLK_Msk /*!< Burst mode clock source */ +#define HRTIM_BMCR_BMCLK_0 (0x1UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000004 */ +#define HRTIM_BMCR_BMCLK_1 (0x2UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000008 */ +#define HRTIM_BMCR_BMCLK_2 (0x4UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000010 */ +#define HRTIM_BMCR_BMCLK_3 (0x8UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000020 */ +#define HRTIM_BMCR_BMPRSC_Pos (6U) +#define HRTIM_BMCR_BMPRSC_Msk (0xFUL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x000003C0 */ +#define HRTIM_BMCR_BMPRSC HRTIM_BMCR_BMPRSC_Msk /*!< Burst mode prescaler */ +#define HRTIM_BMCR_BMPRSC_0 (0x1UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000040 */ +#define HRTIM_BMCR_BMPRSC_1 (0x2UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000080 */ +#define HRTIM_BMCR_BMPRSC_2 (0x4UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000100 */ +#define HRTIM_BMCR_BMPRSC_3 (0x8UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000200 */ +#define HRTIM_BMCR_BMPREN_Pos (10U) +#define HRTIM_BMCR_BMPREN_Msk (0x1UL << HRTIM_BMCR_BMPREN_Pos) /*!< 0x00000400 */ +#define HRTIM_BMCR_BMPREN HRTIM_BMCR_BMPREN_Msk /*!< Burst mode Preload bit */ +#define HRTIM_BMCR_MTBM_Pos (16U) +#define HRTIM_BMCR_MTBM_Msk (0x1UL << HRTIM_BMCR_MTBM_Pos) /*!< 0x00010000 */ +#define HRTIM_BMCR_MTBM HRTIM_BMCR_MTBM_Msk /*!< Master Timer Burst mode */ +#define HRTIM_BMCR_TABM_Pos (17U) +#define HRTIM_BMCR_TABM_Msk (0x1UL << HRTIM_BMCR_TABM_Pos) /*!< 0x00020000 */ +#define HRTIM_BMCR_TABM HRTIM_BMCR_TABM_Msk /*!< Timer A Burst mode */ +#define HRTIM_BMCR_TBBM_Pos (18U) +#define HRTIM_BMCR_TBBM_Msk (0x1UL << HRTIM_BMCR_TBBM_Pos) /*!< 0x00040000 */ +#define HRTIM_BMCR_TBBM HRTIM_BMCR_TBBM_Msk /*!< Timer B Burst mode */ +#define HRTIM_BMCR_TCBM_Pos (19U) +#define HRTIM_BMCR_TCBM_Msk (0x1UL << HRTIM_BMCR_TCBM_Pos) /*!< 0x00080000 */ +#define HRTIM_BMCR_TCBM HRTIM_BMCR_TCBM_Msk /*!< Timer C Burst mode */ +#define HRTIM_BMCR_TDBM_Pos (20U) +#define HRTIM_BMCR_TDBM_Msk (0x1UL << HRTIM_BMCR_TDBM_Pos) /*!< 0x00100000 */ +#define HRTIM_BMCR_TDBM HRTIM_BMCR_TDBM_Msk /*!< Timer D Burst mode */ +#define HRTIM_BMCR_TEBM_Pos (21U) +#define HRTIM_BMCR_TEBM_Msk (0x1UL << HRTIM_BMCR_TEBM_Pos) /*!< 0x00200000 */ +#define HRTIM_BMCR_TEBM HRTIM_BMCR_TEBM_Msk /*!< Timer E Burst mode */ + +#define HRTIM_BMCR_TFBM_Pos (22U) +#define HRTIM_BMCR_TFBM_Msk (0x1UL << HRTIM_BMCR_TFBM_Pos) /*!< 0x00400000 */ +#define HRTIM_BMCR_TFBM HRTIM_BMCR_TFBM_Msk /*!< Timer F Burst mode */ + +#define HRTIM_BMCR_BMSTAT_Pos (31U) +#define HRTIM_BMCR_BMSTAT_Msk (0x1UL << HRTIM_BMCR_BMSTAT_Pos) /*!< 0x80000000 */ +#define HRTIM_BMCR_BMSTAT HRTIM_BMCR_BMSTAT_Msk /*!< Burst mode status */ + +/**** Bit definition for Common HRTIM Timer Burst mode Trigger register *******/ +#define HRTIM_BMTRGR_SW_Pos (0U) +#define HRTIM_BMTRGR_SW_Msk (0x1UL << HRTIM_BMTRGR_SW_Pos) /*!< 0x00000001 */ +#define HRTIM_BMTRGR_SW HRTIM_BMTRGR_SW_Msk /*!< Software start */ +#define HRTIM_BMTRGR_MSTRST_Pos (1U) +#define HRTIM_BMTRGR_MSTRST_Msk (0x1UL << HRTIM_BMTRGR_MSTRST_Pos) /*!< 0x00000002 */ +#define HRTIM_BMTRGR_MSTRST HRTIM_BMTRGR_MSTRST_Msk /*!< Master reset */ +#define HRTIM_BMTRGR_MSTREP_Pos (2U) +#define HRTIM_BMTRGR_MSTREP_Msk (0x1UL << HRTIM_BMTRGR_MSTREP_Pos) /*!< 0x00000004 */ +#define HRTIM_BMTRGR_MSTREP HRTIM_BMTRGR_MSTREP_Msk /*!< Master repetition */ +#define HRTIM_BMTRGR_MSTCMP1_Pos (3U) +#define HRTIM_BMTRGR_MSTCMP1_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_BMTRGR_MSTCMP1 HRTIM_BMTRGR_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_BMTRGR_MSTCMP2_Pos (4U) +#define HRTIM_BMTRGR_MSTCMP2_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_BMTRGR_MSTCMP2 HRTIM_BMTRGR_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_BMTRGR_MSTCMP3_Pos (5U) +#define HRTIM_BMTRGR_MSTCMP3_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_BMTRGR_MSTCMP3 HRTIM_BMTRGR_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_BMTRGR_MSTCMP4_Pos (6U) +#define HRTIM_BMTRGR_MSTCMP4_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_BMTRGR_MSTCMP4 HRTIM_BMTRGR_MSTCMP4_Msk /*!< Master compare 4 */ +#define HRTIM_BMTRGR_TARST_Pos (7U) +#define HRTIM_BMTRGR_TARST_Msk (0x1UL << HRTIM_BMTRGR_TARST_Pos) /*!< 0x00000080 */ +#define HRTIM_BMTRGR_TARST HRTIM_BMTRGR_TARST_Msk /*!< Timer A reset */ +#define HRTIM_BMTRGR_TAREP_Pos (8U) +#define HRTIM_BMTRGR_TAREP_Msk (0x1UL << HRTIM_BMTRGR_TAREP_Pos) /*!< 0x00000100 */ +#define HRTIM_BMTRGR_TAREP HRTIM_BMTRGR_TAREP_Msk /*!< Timer A repetition */ +#define HRTIM_BMTRGR_TACMP1_Pos (9U) +#define HRTIM_BMTRGR_TACMP1_Msk (0x1UL << HRTIM_BMTRGR_TACMP1_Pos) /*!< 0x00000200 */ +#define HRTIM_BMTRGR_TACMP1 HRTIM_BMTRGR_TACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_BMTRGR_TACMP2_Pos (10U) +#define HRTIM_BMTRGR_TACMP2_Msk (0x1UL << HRTIM_BMTRGR_TACMP2_Pos) /*!< 0x00000400 */ +#define HRTIM_BMTRGR_TACMP2 HRTIM_BMTRGR_TACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_BMTRGR_TBRST_Pos (11U) +#define HRTIM_BMTRGR_TBRST_Msk (0x1UL << HRTIM_BMTRGR_TBRST_Pos) /*!< 0x00000800 */ +#define HRTIM_BMTRGR_TBRST HRTIM_BMTRGR_TBRST_Msk /*!< Timer B reset */ +#define HRTIM_BMTRGR_TBREP_Pos (12U) +#define HRTIM_BMTRGR_TBREP_Msk (0x1UL << HRTIM_BMTRGR_TBREP_Pos) /*!< 0x00001000 */ +#define HRTIM_BMTRGR_TBREP HRTIM_BMTRGR_TBREP_Msk /*!< Timer B repetition */ +#define HRTIM_BMTRGR_TBCMP1_Pos (13U) +#define HRTIM_BMTRGR_TBCMP1_Msk (0x1UL << HRTIM_BMTRGR_TBCMP1_Pos) /*!< 0x00002000 */ +#define HRTIM_BMTRGR_TBCMP1 HRTIM_BMTRGR_TBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_BMTRGR_TBCMP2_Pos (14U) +#define HRTIM_BMTRGR_TBCMP2_Msk (0x1UL << HRTIM_BMTRGR_TBCMP2_Pos) /*!< 0x00004000 */ +#define HRTIM_BMTRGR_TBCMP2 HRTIM_BMTRGR_TBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_BMTRGR_TCRST_Pos (15U) +#define HRTIM_BMTRGR_TCRST_Msk (0x1UL << HRTIM_BMTRGR_TCRST_Pos) /*!< 0x00008000 */ +#define HRTIM_BMTRGR_TCRST HRTIM_BMTRGR_TCRST_Msk /*!< Timer C reset */ +#define HRTIM_BMTRGR_TCREP_Pos (16U) +#define HRTIM_BMTRGR_TCREP_Msk (0x1UL << HRTIM_BMTRGR_TCREP_Pos) /*!< 0x00010000 */ +#define HRTIM_BMTRGR_TCREP HRTIM_BMTRGR_TCREP_Msk /*!< Timer C repetition */ +#define HRTIM_BMTRGR_TCCMP1_Pos (17U) +#define HRTIM_BMTRGR_TCCMP1_Msk (0x1UL << HRTIM_BMTRGR_TCCMP1_Pos) /*!< 0x00020000 */ +#define HRTIM_BMTRGR_TCCMP1 HRTIM_BMTRGR_TCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_BMTRGR_TFRST_Pos (18U) +#define HRTIM_BMTRGR_TFRST_Msk (0x1UL << HRTIM_BMTRGR_TFRST_Pos) /*!< 0x00040000 */ +#define HRTIM_BMTRGR_TFRST HRTIM_BMTRGR_TFRST_Msk /*!< Timer F reset */ +#define HRTIM_BMTRGR_TDRST_Pos (19U) +#define HRTIM_BMTRGR_TDRST_Msk (0x1UL << HRTIM_BMTRGR_TDRST_Pos) /*!< 0x00080000 */ +#define HRTIM_BMTRGR_TDRST HRTIM_BMTRGR_TDRST_Msk /*!< Timer D reset */ +#define HRTIM_BMTRGR_TDREP_Pos (20U) +#define HRTIM_BMTRGR_TDREP_Msk (0x1UL << HRTIM_BMTRGR_TDREP_Pos) /*!< 0x00100000 */ +#define HRTIM_BMTRGR_TDREP HRTIM_BMTRGR_TDREP_Msk /*!< Timer D repetition */ +#define HRTIM_BMTRGR_TFREP_Pos (21U) +#define HRTIM_BMTRGR_TFREP_Msk (0x1UL << HRTIM_BMTRGR_TFREP_Pos) /*!< 0x00200000 */ +#define HRTIM_BMTRGR_TFREP HRTIM_BMTRGR_TFREP_Msk /*!< Timer F repetition*/ +#define HRTIM_BMTRGR_TDCMP2_Pos (22U) +#define HRTIM_BMTRGR_TDCMP2_Msk (0x1UL << HRTIM_BMTRGR_TDCMP2_Pos) /*!< 0x00400000 */ +#define HRTIM_BMTRGR_TDCMP2 HRTIM_BMTRGR_TDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_BMTRGR_TFCMP1_Pos (23U) +#define HRTIM_BMTRGR_TFCMP1_Msk (0x1UL << HRTIM_BMTRGR_TFCMP1_Pos) /*!< 0x00800000 */ +#define HRTIM_BMTRGR_TFCMP1 HRTIM_BMTRGR_TFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_BMTRGR_TEREP_Pos (24U) +#define HRTIM_BMTRGR_TEREP_Msk (0x1UL << HRTIM_BMTRGR_TEREP_Pos) /*!< 0x01000000 */ +#define HRTIM_BMTRGR_TEREP HRTIM_BMTRGR_TEREP_Msk /*!< Timer E repetition */ +#define HRTIM_BMTRGR_TECMP1_Pos (25U) +#define HRTIM_BMTRGR_TECMP1_Msk (0x1UL << HRTIM_BMTRGR_TECMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_BMTRGR_TECMP1 HRTIM_BMTRGR_TECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_BMTRGR_TECMP2_Pos (26U) +#define HRTIM_BMTRGR_TECMP2_Msk (0x1UL << HRTIM_BMTRGR_TECMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_BMTRGR_TECMP2 HRTIM_BMTRGR_TECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_BMTRGR_TAEEV7_Pos (27U) +#define HRTIM_BMTRGR_TAEEV7_Msk (0x1UL << HRTIM_BMTRGR_TAEEV7_Pos) /*!< 0x08000000 */ +#define HRTIM_BMTRGR_TAEEV7 HRTIM_BMTRGR_TAEEV7_Msk /*!< Timer A period following External Event7 */ +#define HRTIM_BMTRGR_TDEEV8_Pos (28U) +#define HRTIM_BMTRGR_TDEEV8_Msk (0x1UL << HRTIM_BMTRGR_TDEEV8_Pos) /*!< 0x10000000 */ +#define HRTIM_BMTRGR_TDEEV8 HRTIM_BMTRGR_TDEEV8_Msk /*!< Timer D period following External Event8 */ +#define HRTIM_BMTRGR_EEV7_Pos (29U) +#define HRTIM_BMTRGR_EEV7_Msk (0x1UL << HRTIM_BMTRGR_EEV7_Pos) /*!< 0x20000000 */ +#define HRTIM_BMTRGR_EEV7 HRTIM_BMTRGR_EEV7_Msk /*!< External Event 7 */ +#define HRTIM_BMTRGR_EEV8_Pos (30U) +#define HRTIM_BMTRGR_EEV8_Msk (0x1UL << HRTIM_BMTRGR_EEV8_Pos) /*!< 0x40000000 */ +#define HRTIM_BMTRGR_EEV8 HRTIM_BMTRGR_EEV8_Msk /*!< External Event 8 */ +#define HRTIM_BMTRGR_OCHPEV_Pos (31U) +#define HRTIM_BMTRGR_OCHPEV_Msk (0x1UL << HRTIM_BMTRGR_OCHPEV_Pos) /*!< 0x80000000 */ +#define HRTIM_BMTRGR_OCHPEV HRTIM_BMTRGR_OCHPEV_Msk /*!< on-chip Event */ + +/******************* Bit definition for HRTIM_BMCMPR register ***************/ +#define HRTIM_BMCMPR_BMCMPR_Pos (0U) +#define HRTIM_BMCMPR_BMCMPR_Msk (0xFFFFUL << HRTIM_BMCMPR_BMCMPR_Pos) /*!< 0x0000FFFF */ +#define HRTIM_BMCMPR_BMCMPR HRTIM_BMCMPR_BMCMPR_Msk /*!(0xE0001000U); +uint32_t volatile& DWT_CYCCNT = *reinterpret_cast(0xE0001004U); +uint32_t volatile& DEMCR = *reinterpret_cast(0xE000EDFCU); + +uint64_t updateTicks() +{ + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + uint32_t const curDwt = DWT_CYCCNT; + state.ticks += static_cast((curDwt - state.lastDwt) / DWT_FREQ_MHZ); + state.lastDwt = curDwt; + return state.ticks; +} + +} // namespace + +extern "C" +{ +void initSystemTimer() +{ + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + + DEMCR = DEMCR | 0x01000000U; // TRCENA + DWT_CYCCNT = 0; + DWT_CTRL = DWT_CTRL | 0x00000001U; // CYCCNTENA + + state.ticks = 0; + state.lastDwt = 0; +} + +uint64_t getSystemTicks(void) { return updateTicks(); } + +uint32_t getSystemTicks32Bit(void) { return static_cast(updateTicks()); } + +uint64_t getSystemTimeNs(void) { return updateTicks() * 1000U / TICK_FREQ_MHZ; } + +uint64_t getSystemTimeUs(void) { return updateTicks() / TICK_FREQ_MHZ; } + +uint32_t getSystemTimeUs32Bit(void) { return static_cast(updateTicks() / TICK_FREQ_MHZ); } + +uint64_t getSystemTimeMs(void) { return updateTicks() / TICK_FREQ_MHZ / 1000U; } + +uint32_t getSystemTimeMs32Bit(void) +{ + return static_cast(updateTicks() / TICK_FREQ_MHZ / 1000U); +} + +uint64_t systemTicksToTimeUs(uint64_t const ticks) { return ticks / TICK_FREQ_MHZ; } + +uint64_t systemTicksToTimeNs(uint64_t const ticks) { return ticks * 1000U / TICK_FREQ_MHZ; } + +uint32_t getFastTicks(void) { return DWT_CYCCNT; } + +uint32_t getFastTicksPerSecond(void) { return DWT_FREQ_MHZ * 1000000U; } + +void sysDelayUs(uint32_t const delay) +{ + uint64_t const start = getSystemTimeUs(); + while (getSystemTimeUs() < start + delay) {} +} + +} // extern "C" diff --git a/platforms/stm32/bsp/bspUart/CMakeLists.txt b/platforms/stm32/bsp/bspUart/CMakeLists.txt new file mode 100644 index 00000000000..104912c30a4 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/CMakeLists.txt @@ -0,0 +1,13 @@ +if (BUILD_EXECUTABLE STREQUAL "unitTest") + # Header-only for unit tests (no hardware-dependent Uart.cpp) + add_library(bspUart INTERFACE) + target_include_directories(bspUart INTERFACE include) + target_link_libraries(bspUart INTERFACE bsp) +else () + add_library(bspUart src/Uart.cpp) + target_include_directories(bspUart PUBLIC include) + target_link_libraries( + bspUart + PUBLIC bsp + PRIVATE bspConfiguration bspMcu) +endif () diff --git a/platforms/stm32/bsp/bspUart/doc/index.rst b/platforms/stm32/bsp/bspUart/doc/index.rst new file mode 100644 index 00000000000..fdaf963be51 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/doc/index.rst @@ -0,0 +1,41 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspUart Driver +============== + +Overview +-------- + +The ``bspUart`` module provides a polling-mode UART driver for debug console +output on STM32 targets. It is used by the logger subsystem and the command +shell for human-readable output over the ST-LINK Virtual COM Port (VCP). The +driver is the ``bsp::Uart`` class and satisfies the ``UartConcept`` +compile-time interface check (``BSP_UART_CONCEPT_CHECKER``). + +All I/O is synchronous polling; no DMA or interrupts are used, which avoids +NVIC configuration dependencies at the cost of blocking the caller during +transmission. + +Configuration +------------- + +Each UART instance is described by a ``UartConfig`` struct in a compile-time +table (``_uartConfigs[]``): USART peripheral, GPIO port and TX/RX pins, +alternate function number, and baud rate register value (typically 115200 +baud for the debug console). + +Board configuration: + +- **NUCLEO-F413ZH** -- USART3 on PD8 (TX) / PD9 (RX), AF7. VCP via ST-LINK + V2-1. +- **NUCLEO-G474RE** -- USART2 on PA2 (TX) / PA3 (RX), AF7. VCP via ST-LINK + V3. diff --git a/platforms/stm32/bsp/bspUart/include/bsp/Uart.h b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h new file mode 100644 index 00000000000..c780bde3374 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace bsp +{ + +class Uart +{ +public: + enum class Id : size_t; + + size_t write(::etl::span const data); + size_t read(::etl::span data); + void init(); + bool isInitialized() const; + bool waitForTxReady(); + + static Uart& getInstance(Id id); + + struct UartConfig; + Uart(Uart::Id id); + +private: + bool writeByte(uint8_t data); + + UartConfig const& _uartConfig; + static UartConfig const _uartConfigs[]; +}; + +BSP_UART_CONCEPT_CHECKER(Uart) + +} // namespace bsp + +#include diff --git a/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h b/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h new file mode 100644 index 00000000000..a21f14706ce --- /dev/null +++ b/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +struct Uart::UartConfig +{ + USART_TypeDef* usart; + GPIO_TypeDef* gpioPort; + uint8_t txPin; + uint8_t rxPin; + uint8_t af; + uint32_t brr; + uint32_t rccGpioEnBit; + uint32_t rccUsartEnBit; + uint32_t volatile* rccGpioEnReg; + uint32_t volatile* rccUsartEnReg; +}; + +} // namespace bsp diff --git a/platforms/stm32/bsp/bspUart/module.spec b/platforms/stm32/bsp/bspUart/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspUart/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspUart/src/Uart.cpp b/platforms/stm32/bsp/bspUart/src/Uart.cpp new file mode 100644 index 00000000000..e10084bc992 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/src/Uart.cpp @@ -0,0 +1,127 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/UartParams.h" + +using bsp::Uart; + +static uint32_t const WRITE_TIMEOUT = 100000U; + +// STM32F4 uses SR/DR registers, STM32G4 uses ISR/TDR/RDR registers +#if defined(STM32F413xx) +#define UART_TX_EMPTY_FLAG USART_SR_TXE +#define UART_RX_READY_FLAG USART_SR_RXNE +#define UART_GET_STATUS(u) ((u)->SR) +#define UART_WRITE_DATA(u, d) ((u)->DR = (d)) +#define UART_READ_DATA(u) ((u)->DR & 0xFFU) +#elif defined(STM32G474xx) +#define UART_TX_EMPTY_FLAG USART_ISR_TXE +#define UART_RX_READY_FLAG USART_ISR_RXNE_RXFNE +#define UART_GET_STATUS(u) ((u)->ISR) +#define UART_WRITE_DATA(u, d) ((u)->TDR = (d)) +#define UART_READ_DATA(u) ((u)->RDR & 0xFFU) +#endif + +static void configureGpio(Uart::UartConfig const& cfg) +{ + *cfg.rccGpioEnReg |= cfg.rccGpioEnBit; + + cfg.gpioPort->MODER &= ~(3U << (cfg.txPin * 2U)); + cfg.gpioPort->MODER |= (2U << (cfg.txPin * 2U)); + uint32_t const txAfrIdx = cfg.txPin / 8U; + uint32_t const txAfrPos = (cfg.txPin % 8U) * 4U; + cfg.gpioPort->AFR[txAfrIdx] &= ~(0xFU << txAfrPos); + cfg.gpioPort->AFR[txAfrIdx] |= (static_cast(cfg.af) << txAfrPos); + + cfg.gpioPort->MODER &= ~(3U << (cfg.rxPin * 2U)); + cfg.gpioPort->MODER |= (2U << (cfg.rxPin * 2U)); + uint32_t const rxAfrIdx = cfg.rxPin / 8U; + uint32_t const rxAfrPos = (cfg.rxPin % 8U) * 4U; + cfg.gpioPort->AFR[rxAfrIdx] &= ~(0xFU << rxAfrPos); + cfg.gpioPort->AFR[rxAfrIdx] |= (static_cast(cfg.af) << rxAfrPos); +} + +Uart::Uart(Uart::Id id) : _uartConfig(_uartConfigs[static_cast(id)]) {} + +void Uart::init() +{ + configureGpio(_uartConfig); + + *_uartConfig.rccUsartEnReg |= _uartConfig.rccUsartEnBit; + + // BRR must be written while UE is cleared + _uartConfig.usart->CR1 = 0; + _uartConfig.usart->BRR = _uartConfig.brr; + _uartConfig.usart->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; +} + +bool Uart::isInitialized() const +{ + return (_uartConfig.usart->CR1 & (USART_CR1_TE | USART_CR1_RE)) != 0; +} + +bool Uart::waitForTxReady() +{ + uint32_t count = 0U; + + if (!isInitialized()) + { + init(); + } + + while ((UART_GET_STATUS(_uartConfig.usart) & UART_TX_EMPTY_FLAG) == 0) + { + if (++count > WRITE_TIMEOUT) + { + return false; + } + } + return true; +} + +bool Uart::writeByte(uint8_t data) +{ + if ((UART_GET_STATUS(_uartConfig.usart) & UART_TX_EMPTY_FLAG) != 0) + { + UART_WRITE_DATA(_uartConfig.usart, static_cast(data) & 0xFFU); + return waitForTxReady(); + } + return false; +} + +size_t Uart::write(::etl::span const data) +{ + size_t counter = 0; + + while (counter < data.size()) + { + if (!writeByte(data[counter])) + { + break; + } + counter++; + } + + return counter; +} + +size_t Uart::read(::etl::span data) +{ + size_t bytesRead = 0; + + while ((UART_GET_STATUS(_uartConfig.usart) & UART_RX_READY_FLAG) != 0 + && bytesRead < data.size()) + { + data[bytesRead] = static_cast(UART_READ_DATA(_uartConfig.usart)); + bytesRead++; + } + + return bytesRead; +} diff --git a/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt b/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt new file mode 100644 index 00000000000..bf2d9028e6d --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(bxCanTransceiver src/can/transceiver/bxcan/BxCanTransceiver.cpp) + +if (BUILD_EXECUTABLE STREQUAL "unitTest") + target_include_directories(bxCanTransceiver PUBLIC include + test/mock/include) + + target_link_libraries( + bxCanTransceiver + PUBLIC asyncMockImpl + bsp + cpp2can + etl + bspIo + lifecycle + gmock) +else () + target_include_directories(bxCanTransceiver PUBLIC include) + + target_link_libraries(bxCanTransceiver PUBLIC async bspCan lifecycle + cpp2can) +endif () diff --git a/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst b/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst new file mode 100644 index 00000000000..11d899d4605 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst @@ -0,0 +1,45 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bxCanTransceiver +================ + +Overview +-------- + +The ``bxCanTransceiver`` module implements the OpenBSW +``AbstractCANTransceiver`` interface for the STM32F4 bxCAN peripheral +(classic CAN, 500 kbps). It wraps ``BxCanDevice`` (from ``bspCan``) and adds +lifecycle management, async integration, ISR dispatch and bus-off recovery. + +``init()`` initialises the hardware and moves the transceiver from CLOSED to +INITIALIZED. ``open()`` starts the device (re-initialising it first when +called from CLOSED), schedules the cyclic bus-off poll and moves to OPEN. +``close()`` stops the device, clears the TX queue and returns to CLOSED from +any state; ``shutdown()`` delegates to ``close()``. ``mute()`` and +``unmute()`` toggle between OPEN and MUTED. + +TX: ``write(frame)`` transmits fire-and-forget and notifies registered sent +listeners synchronously. ``write(frame, listener)`` queues the job in a +3-entry TX queue; the listener callback runs in task context (via +``async::execute``) after the TX interrupt, which also sends the next queued +frame. When the hardware mailboxes or the TX queue are full, +``CAN_ERR_TX_HW_QUEUE_FULL`` is returned and an overrun counter increments. + +RX: ``receiveInterrupt()`` is called from the CAN RX ISR and drains the +hardware FIFO into the device's software queue (accept-all; per-listener +filtering happens later). ``receiveTask()`` runs in task context, notifies +the registered frame listeners, clears the software queue and re-enables the +RX interrupt. + +Bus-off recovery: ``cyclicTask()`` polls the bus-off flag every 10 ms, moving +OPEN to MUTED on bus-off and back to OPEN once the bus recovers, unless the +user had explicitly called ``mute()``. diff --git a/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h b/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h new file mode 100644 index 00000000000..6f699030458 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h @@ -0,0 +1,105 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace bios +{ + +/** + * CAN transceiver for STM32 bxCAN peripheral. + * + * Wraps BxCanDevice to implement the AbstractCANTransceiver interface. + * Operates in classic CAN mode at 500 kbps on STM32F4 family devices. + * + * \see AbstractCANTransceiver + * \see BxCanDevice + */ +class BxCanTransceiver : public ::can::AbstractCANTransceiver +{ +public: + BxCanTransceiver( + ::async::ContextType context, uint8_t busId, BxCanDevice::Config const& devConfig); + + ErrorCode init() override; + ErrorCode open() override; + ErrorCode open(::can::CANFrame const& frame) override; + ErrorCode close() override; + void shutdown() override; + ErrorCode mute() override; + ErrorCode unmute() override; + + /// Fire-and-forget transmit. Calls notifySentListeners() synchronously. + ErrorCode write(::can::CANFrame const& frame) override; + + /// Transmit with deferred callback. Queues {listener, frame} into fTxQueue. + /// canFrameSent() fires from task context after TX ISR, NOT from write(). + ErrorCode write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) override; + + uint32_t getBaudrate() const override; + uint16_t getHwQueueTimeout() const override; + + /// Number of TX frames dropped due to HW queue full or TX listener queue full. + uint32_t getOverrunCount() const { return fOverrunCount; } + + static uint8_t receiveInterrupt(uint8_t transceiverIndex); + static void transmitInterrupt(uint8_t transceiverIndex); + static void disableRxInterrupt(uint8_t transceiverIndex); + static void enableRxInterrupt(uint8_t transceiverIndex); + + void cyclicTask(); + void receiveTask(); + + /// Underlying bxCAN hardware device. Public for test access. + BxCanDevice fDevice; + +private: + /// TX job waiting for its deferred listener callback. + struct TxJobWithCallback + { + TxJobWithCallback(::can::ICANFrameSentListener& listener, ::can::CANFrame const& frame) + : _listener(listener), _frame(frame) + {} + + ::can::ICANFrameSentListener& _listener; + ::can::CANFrame _frame; + }; + + static uint32_t const TX_QUEUE_CAPACITY = 3U; + using TxQueue = ::etl::deque; + + /// Called from TX ISR - defers to task context via async::execute. + void canFrameSentCallback(); + + /// Runs in task context - pops TX queue, calls listener, sends next frame. + void canFrameSentAsyncCallback(); + + void notifyRegisteredSentListener(::can::CANFrame const& frame) { notifySentListeners(frame); } + + static BxCanTransceiver* fpTransceivers[3]; + + ::async::ContextType fContext; + ::async::TimeoutType fCyclicTimeout; + ::async::Function _cyclicTaskRunner; + ::async::Function _canFrameSent; + TxQueue fTxQueue; + uint32_t fOverrunCount; + bool fMuted; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/module.spec b/platforms/stm32/bsp/bxCanTransceiver/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp b/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp new file mode 100644 index 00000000000..28f390591e8 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp @@ -0,0 +1,310 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace bios +{ + +BxCanTransceiver* BxCanTransceiver::fpTransceivers[3] = {nullptr, nullptr, nullptr}; + +BxCanTransceiver::BxCanTransceiver( + ::async::ContextType context, uint8_t busId, BxCanDevice::Config const& devConfig) +: AbstractCANTransceiver(busId) +, fDevice(devConfig) +, fContext(context) +, fCyclicTimeout() +, _cyclicTaskRunner( + ::async::Function::CallType::create(*this)) +, _canFrameSent(::async::Function::CallType:: + create(*this)) +, fTxQueue() +, fOverrunCount(0U) +, fMuted(false) +{ + if (busId < 3U) + { + fpTransceivers[busId] = this; + } +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::init() +{ + if (getState() != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + setState(State::INITIALIZED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::open() +{ + State const state = getState(); + if (state != State::INITIALIZED && state != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (state == State::CLOSED) + { + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + } + + if (!fDevice.start()) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + setState(State::OPEN); + fMuted = false; + + // Schedule periodic bus-off polling. + ::async::scheduleAtFixedRate( + fContext, _cyclicTaskRunner, fCyclicTimeout, 10U, ::async::TimeUnit::MILLISECONDS); + + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::open(::can::CANFrame const& /* frame */) +{ + // Wake-up frame not supported on bxCAN + return open(); +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::close() +{ + if (getState() == State::CLOSED) + { + return ErrorCode::CAN_ERR_OK; + } + + fCyclicTimeout.cancel(); + fDevice.stop(); + fTxQueue.clear(); + setState(State::CLOSED); + return ErrorCode::CAN_ERR_OK; +} + +void BxCanTransceiver::shutdown() { close(); } + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::mute() +{ + if (getState() != State::OPEN) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = true; + fTxQueue.clear(); + setState(State::MUTED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::unmute() +{ + if (getState() != State::MUTED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = false; + setState(State::OPEN); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::write(::can::CANFrame const& frame) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (!fDevice.transmit(frame)) + { + fOverrunCount++; + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + notifySentListeners(frame); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode +BxCanTransceiver::write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (fTxQueue.full()) + { + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + bool const wasEmpty = fTxQueue.empty(); + fTxQueue.emplace_back(listener, frame); + + if (!wasEmpty) + { + // Not the first in queue - will be sent from the TX ISR chain + return ErrorCode::CAN_ERR_OK; + } + + // First in queue - transmit now + if (!fDevice.transmit(frame)) + { + fTxQueue.pop_front(); + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + // Wait for TX ISR -> canFrameSentCallback() -> canFrameSentAsyncCallback() + return ErrorCode::CAN_ERR_OK; +} + +uint32_t BxCanTransceiver::getBaudrate() const { return 500000U; } + +uint16_t BxCanTransceiver::getHwQueueTimeout() const { return 10U; } + +uint8_t BxCanTransceiver::receiveInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Accept all frames into the software queue - per-listener filtering + // is done by notifyListeners() in receiveTask(). + return fpTransceivers[transceiverIndex]->fDevice.receiveISR(nullptr); + } + return 0U; +} + +void BxCanTransceiver::transmitInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + BxCanTransceiver* self = fpTransceivers[transceiverIndex]; + self->fDevice.transmitISR(); + + if (!self->fTxQueue.empty()) + { + self->canFrameSentCallback(); + } + } +} + +void BxCanTransceiver::cyclicTask() +{ + // Check bus-off state + if (fDevice.isBusOff()) + { + if (getState() == State::OPEN) + { + setState(State::MUTED); + } + } + else if (getState() == State::MUTED && !fMuted) + { + setState(State::OPEN); + } +} + +void BxCanTransceiver::disableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.disableRxInterrupt(); + } +} + +void BxCanTransceiver::enableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.enableRxInterrupt(); + } +} + +void BxCanTransceiver::receiveTask() +{ + uint8_t count = fDevice.getRxCount(); + for (uint8_t i = 0U; i < count; i++) + { + notifyListeners(fDevice.getRxFrame(i)); + } + fDevice.clearRxQueue(); + + // Re-enable RX FIFO interrupt after draining queue + fDevice.enableRxInterrupt(); +} + +void BxCanTransceiver::canFrameSentCallback() { ::async::execute(fContext, _canFrameSent); } + +void BxCanTransceiver::canFrameSentAsyncCallback() +{ + if (!fTxQueue.empty()) + { + // If transceiver is no longer OPEN (bus-off, muted, closed), drop + // all queued TX jobs without calling listeners. + if (getState() != State::OPEN) + { + fTxQueue.clear(); + return; + } + + TxJobWithCallback& job = fTxQueue.front(); + ::can::CANFrame const& frame = job._frame; + ::can::ICANFrameSentListener& listener = job._listener; + fTxQueue.pop_front(); + + bool sendAgain = false; + if (!fTxQueue.empty()) + { + if (getState() == State::OPEN) + { + sendAgain = true; + } + else + { + fTxQueue.clear(); + } + } + + listener.canFrameSent(frame); + notifyRegisteredSentListener(frame); + + if (sendAgain) + { + ::can::CANFrame const& nextFrame = fTxQueue.front()._frame; + if (fDevice.transmit(nextFrame)) + { + // Wait for next TX ISR + return; + } + // HW queue full - no ISR will retrigger, clear remaining + fTxQueue.clear(); + notifyRegisteredSentListener(nextFrame); + } + } +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt b/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt new file mode 100644 index 00000000000..e73c320b832 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(BxCanTransceiverTest src/can/BxCanTransceiverTest.cpp) + +target_link_libraries( + BxCanTransceiverTest + PRIVATE asyncMockImpl + bsp + bxCanTransceiver + cpp2can + bspIo + lifecycle + bspMock + gmock) + +gtest_discover_tests(BxCanTransceiverTest PROPERTIES LABELS + "BxCanTransceiverTest") diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h b/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h new file mode 100644 index 00000000000..7718745836e --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace bios +{ + +// GMock replacement for the register-level BxCanDevice. +class BxCanDevice +{ +public: + struct Config + { + uint32_t baseAddress; + uint32_t prescaler; + uint32_t bs1; + uint32_t bs2; + uint32_t sjw; + uint32_t rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + uint32_t txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + explicit BxCanDevice(Config const& /* config */) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + MOCK_METHOD(bool, init, ()); + MOCK_METHOD(bool, start, ()); + MOCK_METHOD(void, stop, ()); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame)); + MOCK_METHOD(uint8_t, receiveISR, (uint8_t const* filterBitField)); + MOCK_METHOD(void, transmitISR, ()); + MOCK_METHOD(bool, isBusOff, (), (const)); + MOCK_METHOD(uint8_t, getTxErrorCounter, (), (const)); + MOCK_METHOD(uint8_t, getRxErrorCounter, (), (const)); + MOCK_METHOD(void, configureAcceptAllFilter, ()); + MOCK_METHOD(void, configureFilterList, (uint32_t const* idList, uint8_t count)); + MOCK_METHOD(::can::CANFrame const&, getRxFrame, (uint8_t index), (const)); + MOCK_METHOD(uint8_t, getRxCount, (), (const)); + MOCK_METHOD(void, clearRxQueue, ()); + MOCK_METHOD(void, disableRxInterrupt, ()); + MOCK_METHOD(void, enableRxInterrupt, ()); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp b/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp new file mode 100644 index 00000000000..d1447938d39 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp @@ -0,0 +1,943 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "can/transceiver/bxcan/BxCanTransceiver.h" + +#include "async/AsyncMock.h" +#include "bsp/timer/SystemTimerMock.h" +#include "can/canframes/ICANFrameSentListener.h" + +#include +#include + +namespace +{ +using namespace ::can; +using namespace ::testing; +using namespace ::bios; + +class MockCANFrameSentListener : public ::can::ICANFrameSentListener +{ +public: + MOCK_METHOD(void, canFrameSent, (::can::CANFrame const&), (override)); +}; + +class BxCanTransceiverTest : public Test +{ +public: + ::async::AsyncMock fAsyncMock; + ::async::ContextType fAsyncContext = 0; + uint8_t fBusId = 0; + BxCanDevice::Config fDevConfig{}; +}; + +TEST_F(BxCanTransceiverTest, initFromClosed) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); +} + +TEST_F(BxCanTransceiverTest, initTwiceFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.init()); +} + +TEST_F(BxCanTransceiverTest, receiveInterruptInvalidIndex) +{ + EXPECT_EQ(0U, BxCanTransceiver::receiveInterrupt(3)); +} + +TEST_F(BxCanTransceiverTest, writeFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, bxt.write(frame)); +} + +TEST_F(BxCanTransceiverTest, writeWithListenerFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + MockCANFrameSentListener listener; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, bxt.write(frame, listener)); +} + +TEST_F(BxCanTransceiverTest, closeFromClosedReturnsOk) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.close()); +} + +TEST_F(BxCanTransceiverTest, muteFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.mute()); +} + +TEST_F(BxCanTransceiverTest, unmuteFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.unmute()); +} + +TEST_F(BxCanTransceiverTest, openFromClosedWithoutInitFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + // CLOSED -> open() should either re-init or fail depending on impl. + // The contract says CLOSED->open() re-inits (see impl). So this tests + // that open() from CLOSED is allowed (re-init path). + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); +} + +class InitedBxCanTransceiverTest : public BxCanTransceiverTest +{ +public: + InitedBxCanTransceiverTest() : fBxt(fAsyncContext, fBusId, fDevConfig) + { + EXPECT_CALL(fBxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, stop()).Times(AnyNumber()); + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([](auto, auto& runnable) { runnable.execute(); }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.init()); + } + + BxCanTransceiver fBxt; +}; + +TEST_F(InitedBxCanTransceiverTest, open) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, openTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromInitializedReturnsOk) +{ + EXPECT_CALL(fBxt.fDevice, stop()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromMuted) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenOpen) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenMutedFails) +{ + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenFifoFull) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, getBaudrate) { EXPECT_EQ(500000U, fBxt.getBaudrate()); } + +TEST_F(InitedBxCanTransceiverTest, getHwQueueTimeout) { EXPECT_EQ(10U, fBxt.getHwQueueTimeout()); } + +TEST_F(InitedBxCanTransceiverTest, receiveTask) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskBusOff) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)); + fBxt.cyclicTask(); + + // After bus-off, write should fail because state is MUTED + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskBusRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Go to bus-off + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + + fBxt.cyclicTask(); // bus-off -> MUTED + fBxt.cyclicTask(); // bus-on -> OPEN (auto-recovery, not user mute) + + // Write should work again + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, shutdown) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + fBxt.shutdown(); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, receiveISR(_)).WillOnce(Return(0)); + BxCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, transmitInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerReturnsOkWhenOpen) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerWhenMutedFails) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerWhenFifoFullReturnsQueueFull) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerCallbackFromTransmitInterrupt) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // The listener should be called when transmitInterrupt fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, txQueueFillsToCapacity) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Queue capacity is 3 - fill it up without draining + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // 4th write should fail because queue is full + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, txQueueDrainAndRefill) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(listener, canFrameSent(_)).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Fill queue + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Drain one via TX ISR callback + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Now one slot is free + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackSingleFrame) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackTwoFrames) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame1, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame2, listener2)); + + // First TX ISR: listener1 fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener1, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Second TX ISR: listener2 fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener2, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackThreeFramesFifoOrder) +{ + CANFrame frame; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + MockCANFrameSentListener listener3; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener2)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener3)); + + InSequence seq; + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener1, canFrameSent(_)); + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener2, canFrameSent(_)); + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener3, canFrameSent(_)); + + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackEmptyQueue) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // TX ISR with nothing queued - should not crash + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackAbortedWhenMuted) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Mute before TX ISR fires + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + + // TX ISR should still call transmitISR but listener is NOT called (muted) + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackWithFireAndForgetWrite) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); + + // TX ISR fires - no listener was registered, no crash + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackSameListenerTwice) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + EXPECT_CALL(fBxt.fDevice, transmitISR()).Times(2); + EXPECT_CALL(listener, canFrameSent(_)).Times(2); + + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackMixedWriteStyles) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Fire-and-forget write + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); + // Write with listener + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // TX ISR fires for the listener-based write + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterruptPassesNullFilter) +{ + EXPECT_CALL(fBxt.fDevice, receiveISR(nullptr)).WillOnce(Return(1)); + uint8_t count = BxCanTransceiver::receiveInterrupt(fBusId); + EXPECT_EQ(1U, count); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterruptUnregisteredIndexReturnsZero) +{ + // Index 2 was never registered (only index 0 was) + EXPECT_EQ(0U, BxCanTransceiver::receiveInterrupt(2)); +} + +TEST_F(InitedBxCanTransceiverTest, receiveTaskWithFramesNotifiesListeners) +{ + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(2)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(0)).WillOnce(ReturnRef(frame1)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(1)).WillOnce(ReturnRef(frame2)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, receiveTaskReenablesInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, writeFireAndForgetNotifiesRegisteredSentListeners) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // notifySentListeners is called synchronously by write(frame) + // No registered sent listeners by default, so it just doesn't crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeFailureDoesNotNotifySentListeners) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // HW queue full - no notification expected + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, docanWriteThenTxIsr) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Simulates: DoCAN sets _sendPending after write() returns, + // then TX ISR fires, canFrameSent() clears _sendPending + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, docanConsecutiveMultiFrameSend) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(fBxt.fDevice, transmitISR()).Times(3); + EXPECT_CALL(listener, canFrameSent(_)).Times(3); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Simulate DoCAN sending 3 consecutive frames + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, openWithFrameDelegatesToOpen) +{ + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open(frame)); + // Second open should fail, proving state transitioned + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, openFromClosedReinitsDevice) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + + // Re-open from CLOSED should call init + start + EXPECT_CALL(fBxt.fDevice, init()).Times(1); + EXPECT_CALL(fBxt.fDevice, start()).Times(1); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromMutedReturnsOk) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, closeIsIdempotent) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, muteTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, getBusIdReturnsConfiguredValue) +{ + EXPECT_EQ(fBusId, fBxt.getBusId()); +} + +TEST_F(InitedBxCanTransceiverTest, getStateReflectsLifecycle) +{ + EXPECT_EQ(ICanTransceiver::State::INITIALIZED, fBxt.getState()); + + fBxt.open(); + EXPECT_EQ(ICanTransceiver::State::OPEN, fBxt.getState()); + + fBxt.mute(); + EXPECT_EQ(ICanTransceiver::State::MUTED, fBxt.getState()); + + fBxt.unmute(); + EXPECT_EQ(ICanTransceiver::State::OPEN, fBxt.getState()); + + fBxt.close(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, fBxt.getState()); +} + +TEST_F(InitedBxCanTransceiverTest, disableRxInterruptDelegatesToDevice) +{ + EXPECT_CALL(fBxt.fDevice, disableRxInterrupt()).Times(1); + BxCanTransceiver::disableRxInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, writeFromInitializedReturnsTxOffline) +{ + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerFromInitializedReturnsTxOffline) +{ + CANFrame frame; + MockCANFrameSentListener listener; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, openFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, initFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.init()); +} + +TEST_F(InitedBxCanTransceiverTest, initFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.init()); +} + +TEST_F(InitedBxCanTransceiverTest, writeAfterCloseReturnsTxOffline) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteRestoresWriteCapability) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.unmute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, shutdownFromInitialized) +{ + fBxt.shutdown(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, fBxt.getState()); +} + +TEST_F(BxCanTransceiverTest, fullLifecycle) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, stop()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::State::INITIALIZED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + EXPECT_EQ(ICanTransceiver::State::OPEN, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.mute()); + EXPECT_EQ(ICanTransceiver::State::MUTED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.close()); + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); +} + +TEST_F(BxCanTransceiverTest, reopenAfterShutdown) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, stop()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + bxt.shutdown(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); + + // Re-open from CLOSED + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + EXPECT_EQ(ICanTransceiver::State::OPEN, bxt.getState()); +} + +TEST_F(InitedBxCanTransceiverTest, busOffDuringPendingTxDropsCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Bus goes off before TX ISR + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)); + fBxt.cyclicTask(); // transitions to MUTED + + // TX ISR fires after bus-off - listener should NOT be called + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, busOffRecoveryResumesTx) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Bus-off -> MUTED + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + fBxt.cyclicTask(); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); + + // Bus-on -> OPEN + fBxt.cyclicTask(); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, concurrentRxTxSameCycle) +{ + CANFrame rxFrame; + CANFrame txFrame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // TX + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(txFrame)); + + // RX in same cycle + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(1)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(0)).WillOnce(ReturnRef(rxFrame)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, txIsrAndReceiveTaskInterleave) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // RX task runs + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + fBxt.receiveTask(); + + // TX ISR fires after receive task completed + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +class ManualAsyncBxCanTransceiverTest : public BxCanTransceiverTest +{ +public: + ManualAsyncBxCanTransceiverTest() : fBxt(fAsyncContext, fBusId, fDevConfig) + { + EXPECT_CALL(fBxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, stop()).Times(AnyNumber()); + + // Capture the runnable instead of auto-executing + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([this](auto, auto& runnable) { fCapturedRunnable = &runnable; }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.init()); + } + + void executeDeferred() + { + if (fCapturedRunnable != nullptr) + { + fCapturedRunnable->execute(); + fCapturedRunnable = nullptr; + } + } + + BxCanTransceiver fBxt; + ::async::RunnableType* fCapturedRunnable = nullptr; +}; + +TEST_F(ManualAsyncBxCanTransceiverTest, deferredCallbackNotCalledUntilAsyncFires) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // TX ISR fires - defers to async context + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Listener is called when async executes + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeDeferred(); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskUserMuteNoAutoRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // User mutes manually + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + + // Bus is not off, but fMuted is true - cyclicTask should NOT unmute + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(false)); + fBxt.cyclicTask(); + + // Should still be MUTED because user muted (fMuted = true) + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, enableRxInterruptDelegatesToDevice) +{ + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + BxCanTransceiver::enableRxInterrupt(fBusId); +} + +TEST_F(BxCanTransceiverTest, disableRxInterruptInvalidIndexSafe) +{ + BxCanTransceiver::disableRxInterrupt(3); +} + +TEST_F(BxCanTransceiverTest, enableRxInterruptInvalidIndexSafe) +{ + BxCanTransceiver::enableRxInterrupt(3); +} + +TEST_F(BxCanTransceiverTest, transmitInterruptInvalidIndexSafe) +{ + BxCanTransceiver::transmitInterrupt(3); +} + +} // namespace diff --git a/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt b/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt new file mode 100644 index 00000000000..0f4deeaedab --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(fdCanTransceiver src/can/transceiver/fdcan/FdCanTransceiver.cpp) + +if (BUILD_EXECUTABLE STREQUAL "unitTest") + target_include_directories(fdCanTransceiver PUBLIC include + test/mock/include) + + target_link_libraries( + fdCanTransceiver + PUBLIC asyncMockImpl + bsp + cpp2can + etl + bspIo + lifecycle + gmock) +else () + target_include_directories(fdCanTransceiver PUBLIC include) + + target_link_libraries(fdCanTransceiver PUBLIC async bspCan lifecycle + cpp2can) +endif () diff --git a/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst b/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst new file mode 100644 index 00000000000..a47de0ae6c4 --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +fdCanTransceiver +================ + +Overview +-------- +Implements the OpenBSW ``AbstractCANTransceiver`` interface for the STM32 +FDCAN peripheral. Wraps the register-level ``FdCanDevice`` (``bspCan`` module) +and adds lifecycle management, async task integration, ISR dispatch and +bus-off recovery. The peripheral runs in classic CAN mode at 500 kbps. + +Lifecycle +--------- +The transceiver follows the ``AbstractCANTransceiver`` state model +(``CLOSED`` -> ``INITIALIZED`` -> ``OPEN`` <-> ``MUTED``). ``init()`` and +``open()`` delegate to ``FdCanDevice::init()`` and ``FdCanDevice::start()``; +``close()`` stops the device from any state. ``mute()`` and ``unmute()`` +suppress and resume transmission while reception stays active. + +TX Path +------- +``write(frame)`` transmits fire-and-forget and notifies sent-listeners +synchronously. ``write(frame, listener)`` queues the job, transmits with the +TX-complete interrupt enabled, and invokes ``listener.canFrameSent()`` from +task context (deferred via ``async::execute``) after the TX ISR; further +queued frames are sent one after another from that callback chain. + +RX Path +------- +``receiveInterrupt()`` runs in the RX ISR and delegates to +``FdCanDevice::receiveISR(nullptr)``, accepting all frames into the software +queue; per-listener filtering happens later in ``notifyListeners()``. +``receiveTask()`` runs in task context, notifies listeners for each queued +frame, clears the queue and re-enables the RX interrupt. + +Bus-Off Recovery +---------------- +``cyclicTask()`` polls ``FdCanDevice::isBusOff()`` periodically. On bus-off +the transceiver transitions from ``OPEN`` to ``MUTED``; once the peripheral +recovers it returns to ``OPEN``, unless the user explicitly called ``mute()``. diff --git a/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h new file mode 100644 index 00000000000..1edbe5e39c1 --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h @@ -0,0 +1,106 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace bios +{ + +/** + * CAN transceiver for STM32 FDCAN peripheral. + * + * Wraps FdCanDevice to implement the AbstractCANTransceiver interface. + * Operates in classic CAN mode at 500 kbps on STM32G4 family devices. + * + * \see AbstractCANTransceiver + * \see FdCanDevice + */ +class FdCanTransceiver : public ::can::AbstractCANTransceiver +{ +public: + FdCanTransceiver( + ::async::ContextType context, uint8_t busId, FdCanDevice::Config const& devConfig); + + ErrorCode init() override; + ErrorCode open() override; + ErrorCode open(::can::CANFrame const& frame) override; + ErrorCode close() override; + void shutdown() override; + ErrorCode mute() override; + ErrorCode unmute() override; + + /// Fire-and-forget transmit. Calls notifySentListeners() synchronously. + ErrorCode write(::can::CANFrame const& frame) override; + + /// Transmit with deferred callback. Queues {listener, frame} into fTxQueue. + /// canFrameSent() fires from task context after TX ISR, NOT from write(). + ErrorCode write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) override; + + uint32_t getBaudrate() const override; + uint16_t getHwQueueTimeout() const override; + + /// Number of TX frames dropped due to HW queue full or TX listener queue full. + uint32_t getOverrunCount() const { return fOverrunCount; } + + static uint8_t receiveInterrupt(uint8_t transceiverIndex); + static void transmitInterrupt(uint8_t transceiverIndex); + static void disableRxInterrupt(uint8_t transceiverIndex); + static void enableRxInterrupt(uint8_t transceiverIndex); + static void pollTxCallback(uint8_t transceiverIndex); + + void cyclicTask(); + void receiveTask(); + + /// Underlying FDCAN hardware device. Public for test access. + FdCanDevice fDevice; + +private: + /// TX job queued for async callback. + struct TxJobWithCallback + { + TxJobWithCallback(::can::ICANFrameSentListener& listener, ::can::CANFrame const& frame) + : _listener(listener), _frame(frame) + {} + + ::can::ICANFrameSentListener& _listener; + ::can::CANFrame _frame; + }; + + static uint32_t const TX_QUEUE_CAPACITY = 3U; + using TxQueue = ::etl::deque; + + /// Called from TX ISR - defers to task context via async::execute. + void canFrameSentCallback(); + + /// Runs in task context - pops TX queue, calls listener, sends next frame. + void canFrameSentAsyncCallback(); + + void notifyRegisteredSentListener(::can::CANFrame const& frame) { notifySentListeners(frame); } + + static FdCanTransceiver* fpTransceivers[3]; + + ::async::ContextType fContext; + ::async::TimeoutType fCyclicTimeout; + ::async::Function _cyclicTaskRunner; + ::async::Function _canFrameSent; + TxQueue fTxQueue; + uint32_t fOverrunCount; + bool fMuted; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/module.spec b/platforms/stm32/bsp/fdCanTransceiver/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp new file mode 100644 index 00000000000..4834077949c --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp @@ -0,0 +1,311 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace bios +{ + +FdCanTransceiver* FdCanTransceiver::fpTransceivers[3] = {nullptr, nullptr, nullptr}; + +FdCanTransceiver::FdCanTransceiver( + ::async::ContextType context, uint8_t busId, FdCanDevice::Config const& devConfig) +: AbstractCANTransceiver(busId) +, fDevice( + devConfig, + ::etl::delegate::create( + *this)) +, fContext(context) +, fCyclicTimeout() +, _cyclicTaskRunner( + ::async::Function::CallType::create(*this)) +, _canFrameSent(::async::Function::CallType:: + create(*this)) +, fTxQueue() +, fOverrunCount(0U) +, fMuted(false) +{ + if (busId < 3U) + { + fpTransceivers[busId] = this; + } +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::init() +{ + if (getState() != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + setState(State::INITIALIZED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::open() +{ + State const state = getState(); + if (state != State::INITIALIZED && state != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (state == State::CLOSED) + { + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + } + + if (!fDevice.start()) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + setState(State::OPEN); + fMuted = false; + + // Auto-schedule bus-off polling. + ::async::scheduleAtFixedRate( + fContext, _cyclicTaskRunner, fCyclicTimeout, 10U, ::async::TimeUnit::MILLISECONDS); + + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::open(::can::CANFrame const& /* frame */) +{ + return open(); +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::close() +{ + if (getState() == State::CLOSED) + { + return ErrorCode::CAN_ERR_OK; + } + + fCyclicTimeout.cancel(); + fDevice.stop(); + setState(State::CLOSED); + return ErrorCode::CAN_ERR_OK; +} + +void FdCanTransceiver::shutdown() { close(); } + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::mute() +{ + if (getState() != State::OPEN) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = true; + setState(State::MUTED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::unmute() +{ + if (getState() != State::MUTED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = false; + setState(State::OPEN); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::write(::can::CANFrame const& frame) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + // Transmit without TX-complete interrupt; no deferred callback is needed. + if (!fDevice.transmit(frame, false)) + { + fOverrunCount++; + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + notifySentListeners(frame); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode +FdCanTransceiver::write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (fTxQueue.full()) + { + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + bool const wasEmpty = fTxQueue.empty(); + fTxQueue.emplace_back(listener, frame); + + if (!wasEmpty) + { + // Next frame will be sent from TX ISR callback chain. + return ErrorCode::CAN_ERR_OK; + } + + // First sender - transmit with TX-complete interrupt enabled. + // Device enables TCE, ISR will fire on completion and invoke callback delegate. + if (!fDevice.transmit(frame, true)) + { + fTxQueue.pop_front(); + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + // Wait until TX ISR -> fFrameSentCallback -> canFrameSentCallback(). + return ErrorCode::CAN_ERR_OK; +} + +uint32_t FdCanTransceiver::getBaudrate() const { return 500000U; } + +uint16_t FdCanTransceiver::getHwQueueTimeout() const { return 10U; } + +uint8_t FdCanTransceiver::receiveInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Accept all frames at ISR level. Per-listener filtering happens in + // notifyListeners() during receiveTask(). + return fpTransceivers[transceiverIndex]->fDevice.receiveISR(nullptr); + } + return 0U; +} + +void FdCanTransceiver::transmitInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Device's transmitISR() disables TCE, clears TC, and invokes the + // callback delegate (bound to canFrameSentCallback). + fpTransceivers[transceiverIndex]->fDevice.transmitISR(); + } +} + +void FdCanTransceiver::canFrameSentCallback() { ::async::execute(fContext, _canFrameSent); } + +void FdCanTransceiver::canFrameSentAsyncCallback() +{ + if (!fTxQueue.empty()) + { + bool sendAgain = false; + { + TxJobWithCallback& job = fTxQueue.front(); + ::can::CANFrame const& frame = job._frame; + ::can::ICANFrameSentListener& listener = job._listener; + fTxQueue.pop_front(); + + if (!fTxQueue.empty()) + { + // Send again only if same precondition as for write() is satisfied. + State const state = getState(); + if ((State::OPEN == state) || (State::INITIALIZED == state)) + { + sendAgain = true; + } + else + { + fTxQueue.clear(); + } + } + + listener.canFrameSent(frame); + notifyRegisteredSentListener(frame); + } + + if (sendAgain) + { + ::can::CANFrame const& frame = fTxQueue.front()._frame; + // Transmit next queued frame with TX-complete interrupt enabled. + if (!fDevice.transmit(frame, true)) + { + fTxQueue.clear(); + } + } + } +} + +void FdCanTransceiver::disableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.disableRxInterrupt(); + } +} + +void FdCanTransceiver::enableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.enableRxInterrupt(); + } +} + +void FdCanTransceiver::cyclicTask() +{ + if (fDevice.isBusOff()) + { + if (getState() == State::OPEN) + { + setState(State::MUTED); + } + } + else if (getState() == State::MUTED && !fMuted) + { + setState(State::OPEN); + } +} + +void FdCanTransceiver::pollTxCallback(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + FdCanTransceiver* self = fpTransceivers[transceiverIndex]; + if (!self->fTxQueue.empty()) + { + self->canFrameSentAsyncCallback(); + } + } +} + +void FdCanTransceiver::receiveTask() +{ + uint8_t count = fDevice.getRxCount(); + for (uint8_t i = 0U; i < count; i++) + { + notifyListeners(fDevice.getRxFrame(i)); + } + fDevice.clearRxQueue(); + // Re-enable RX FIFO interrupt after draining software queue + fDevice.enableRxInterrupt(); +} + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt b/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt new file mode 100644 index 00000000000..dd336a04d98 --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(FdCanTransceiverTest src/can/FdCanTransceiverTest.cpp) + +target_link_libraries( + FdCanTransceiverTest + PRIVATE asyncMockImpl + bsp + fdCanTransceiver + cpp2can + bspIo + lifecycle + bspMock + gmock) + +gtest_discover_tests(FdCanTransceiverTest PROPERTIES LABELS + "FdCanTransceiverTest") diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h b/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h new file mode 100644 index 00000000000..fa7c7ff816a --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h @@ -0,0 +1,94 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace bios +{ + +// GMock replacement for the register-level FdCanDevice. +class FdCanDevice +{ +public: + struct Config + { + uint32_t baseAddress; + uint32_t prescaler; + uint32_t nts1; + uint32_t nts2; + uint32_t nsjw; + uint32_t rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + uint32_t txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + explicit FdCanDevice(Config const& /* config */) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + FdCanDevice(Config const& /* config */, ::etl::delegate callback) + : fFrameSentCallback(callback) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + MOCK_METHOD(bool, init, ()); + MOCK_METHOD(bool, start, ()); + MOCK_METHOD(void, stop, ()); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame)); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame, bool txInterruptNeeded)); + MOCK_METHOD(uint8_t, receiveISR, (uint8_t const* filterBitField)); + + // Mock transmitISR - tests can set expectations on it. + // Default action invokes the stored callback delegate (matching real FdCanDevice). + MOCK_METHOD(void, transmitISR, ()); + + void setupDefaultTransmitISR() + { + ON_CALL(*this, transmitISR()) + .WillByDefault( + [this]() + { + if (fFrameSentCallback.is_valid()) + { + fFrameSentCallback(); + } + }); + } + + MOCK_METHOD(bool, isBusOff, (), (const)); + MOCK_METHOD(uint8_t, getTxErrorCounter, (), (const)); + MOCK_METHOD(uint8_t, getRxErrorCounter, (), (const)); + MOCK_METHOD(void, configureAcceptAllFilter, ()); + MOCK_METHOD(void, configureFilterList, (uint32_t const* idList, uint8_t count)); + MOCK_METHOD(::can::CANFrame const&, getRxFrame, (uint8_t index), (const)); + MOCK_METHOD(uint8_t, getRxCount, (), (const)); + MOCK_METHOD(void, clearRxQueue, ()); + MOCK_METHOD(void, disableRxInterrupt, ()); + MOCK_METHOD(void, enableRxInterrupt, ()); + + ::etl::delegate fFrameSentCallback; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp b/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp new file mode 100644 index 00000000000..8da3c37111d --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp @@ -0,0 +1,905 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "can/transceiver/fdcan/FdCanTransceiver.h" + +#include "async/AsyncMock.h" +#include "can/canframes/ICANFrameSentListener.h" + +#include + +namespace +{ +using namespace ::can; +using namespace ::testing; +using namespace ::bios; + +// Mock for ICANFrameSentListener (write-with-listener / DoCAN callback) +class MockCANFrameSentListener : public ::can::ICANFrameSentListener +{ +public: + MOCK_METHOD(void, canFrameSent, (::can::CANFrame const&), (override)); +}; + +// Fixture: bare transceiver (CLOSED state) +class FdCanTransceiverTest : public Test +{ +public: + ::async::AsyncMock fAsyncMock; + ::async::ContextType fAsyncContext = 0; + uint8_t fBusId = 0; + FdCanDevice::Config fDevConfig{}; +}; + +// Fixture: transceiver in INITIALIZED state (auto-executes async) +class InitedFdCanTransceiverTest : public FdCanTransceiverTest +{ +public: + InitedFdCanTransceiverTest() : fFct(fAsyncContext, fBusId, fDevConfig) + { + fFct.fDevice.setupDefaultTransmitISR(); + EXPECT_CALL(fFct.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, stop()).Times(AnyNumber()); + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([](auto, auto& runnable) { runnable.execute(); }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.init()); + } + + FdCanTransceiver fFct; +}; + +// Fixture: transceiver in INITIALIZED state with manual async control +// (async::execute is captured but NOT auto-executed) +class ManualAsyncFdCanTransceiverTest : public FdCanTransceiverTest +{ +public: + ManualAsyncFdCanTransceiverTest() : fFct(fAsyncContext, fBusId, fDevConfig) + { + fFct.fDevice.setupDefaultTransmitISR(); + EXPECT_CALL(fFct.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, stop()).Times(AnyNumber()); + // Capture async::execute calls but do NOT auto-run them + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([this](auto, auto& runnable) { fCapturedRunnable = &runnable; }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.init()); + } + + /// Manually execute the last captured async runnable (simulates task context) + void executeAsyncCallback() + { + ASSERT_NE(nullptr, fCapturedRunnable); + fCapturedRunnable->execute(); + fCapturedRunnable = nullptr; + } + + FdCanTransceiver fFct; + ::async::RunnableType* fCapturedRunnable = nullptr; +}; + +TEST_F(FdCanTransceiverTest, initFromClosed) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fct.init()); +} + +TEST_F(FdCanTransceiverTest, initTwiceFails) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fct.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.init()); +} + +TEST_F(InitedFdCanTransceiverTest, open) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openFromClosed) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, closeFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, closeFromClosedReturnsOk) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, muteFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, muteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteFromMuted) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenOpen) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenMutedFails) +{ + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenFifoFull) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, getBaudrate) { EXPECT_EQ(500000U, fFct.getBaudrate()); } + +TEST_F(InitedFdCanTransceiverTest, getHwQueueTimeout) { EXPECT_EQ(10U, fFct.getHwQueueTimeout()); } + +TEST_F(InitedFdCanTransceiverTest, receiveTask) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +TEST_F(InitedFdCanTransceiverTest, cyclicTaskBusOff) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)); + fFct.cyclicTask(); + + // After bus-off, write should fail + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, cyclicTaskBusRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + + fFct.cyclicTask(); // bus-off -> MUTED + fFct.cyclicTask(); // bus-on -> OPEN + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, shutdown) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + fFct.shutdown(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterrupt) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(0)); + FdCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterrupt) +{ + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, receiveInterruptInvalidIndex) +{ + EXPECT_EQ(0U, FdCanTransceiver::receiveInterrupt(3)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rOk) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rFail) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rWhenMuted) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rDoesNotCallListenerSynchronously) +{ + CANFrame frame; + StrictMock listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + // StrictMock: any call to canFrameSent during write() will FAIL + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Verify: listener was NOT called during write() + Mock::VerifyAndClearExpectations(&listener); + + // Now allow the callback to happen (TX ISR triggers it) + EXPECT_CALL(listener, canFrameSent(_)).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, writeOverFillQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + // Queue capacity is 3; items are popped only in canFrameSentAsyncCallback, + // which we never trigger here to simulate queue-full. + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame, listener)); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, writeSecondFrameNotTransmittedImmediately) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + // Only ONE transmit call expected from the first write() + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // At this point, only frame1 was transmitted to HW; frame2 waits in queue. + Mock::VerifyAndClearExpectations(&fFct.fDevice); + + // Now simulate TX ISR for frame1 - this should transmit frame2 + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + + FdCanTransceiver::transmitInterrupt(fBusId); + // Execute the deferred async callback + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithOneFrame) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires -> canFrameSentCallback -> async -> canFrameSentAsyncCallback + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +/// Note: S32K allows write in INITIALIZED, our port requires OPEN. Test uses OPEN. +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesChained) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // TX ISR for frame1 - pops frame1, notifies listener, transmits frame2 + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesInStateOpen) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesIsAbortedWhenMuted) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + fFct.mute(); + + // TX ISR fires - first frame was already submitted. Callback should pop + // frame1 and notify listener, but NOT submit frame2 (muted). + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesFailure) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + // First write fails at HW level + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame1, listener)); + // Second write succeeds + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesIsAbortedWhenNextSendFails) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // TX ISR for frame1 - callback pops frame1, tries to send frame2 but + // transmit returns false => aborted, queue cleared + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, canFrameSentCallbackUsesAsyncExecute) +{ + CANFrame frame; + StrictMock listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires - should call async::execute, NOT call listener directly + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Verify async::execute was called (runnable was captured) + EXPECT_NE(nullptr, fCapturedRunnable); + + // Listener not called yet (still in ISR context) + Mock::VerifyAndClearExpectations(&listener); + + // Now execute in task context + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, canFrameSentCallbackRunsInTaskContext) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // At this point, listener should NOT have been called + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + Mock::VerifyAndClearExpectations(&listener); + + // Execute the deferred callback (task context) + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterruptPassesFilterToDevice) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(1)); + FdCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterruptAcceptsAllFrames) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(1)).WillOnce(Return(3)); + + EXPECT_EQ(1U, FdCanTransceiver::receiveInterrupt(fBusId)); + EXPECT_EQ(3U, FdCanTransceiver::receiveInterrupt(fBusId)); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskNotifiesListenersForEachFrame) +{ + CANFrame frame0; + CANFrame frame1; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(2)); + EXPECT_CALL(fFct.fDevice, getRxFrame(0)).WillOnce(ReturnRef(frame0)); + EXPECT_CALL(fFct.fDevice, getRxFrame(1)).WillOnce(ReturnRef(frame1)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskReEnablesInterrupt) +{ + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +/// Note: upstream AbstractCANTransceiver only calls SystemTimer when +/// IFilteredCANFrameSentListener is registered. This test verifies the +/// write succeeds and the transceiver doesn't crash without registered listeners. +TEST_F(InitedFdCanTransceiverTest, notifyRegisteredSentListenerMatch) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, notifyRegisteredSentListenerFromCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR -> async callback -> pops queue -> calls listener.canFrameSent + // Also calls notifyRegisteredSentListener (notifySentListeners) + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F( + ManualAsyncFdCanTransceiverTest, writeWithListenerThenTransmitInterruptTriggersDeferredCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Step 1: TX ISR fires - defers to task context + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + ASSERT_NE(nullptr, fCapturedRunnable); + + // Step 2: Task context executes - listener gets called + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, multipleWriteWithListenerProcessedSerially) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)) + .WillOnce(Return(true)) // frame1 + .WillOnce(Return(true)); // frame2 (from callback chain) + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener2)); + + // TX ISR for frame1 + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Execute deferred callback for frame1 - should notify listener1, submit frame2 + EXPECT_CALL(listener1, canFrameSent(_)).Times(1); + EXPECT_CALL(listener2, canFrameSent(_)).Times(0); + executeAsyncCallback(); + + Mock::VerifyAndClearExpectations(&listener1); + Mock::VerifyAndClearExpectations(&listener2); + + // TX ISR for frame2 + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Execute deferred callback for frame2 - should notify listener2 + EXPECT_CALL(listener2, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterruptWithNoPendingListener) +{ + EXPECT_CALL(fFct.fDevice, transmitISR()); + + // No write(frame, listener) was called - TX ISR should just call transmitISR + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, transmitInterruptInvalidIndex) +{ + // Should not crash - just silently ignored + FdCanTransceiver::transmitInterrupt(3); + FdCanTransceiver::transmitInterrupt(255); +} + +TEST_F(InitedFdCanTransceiverTest, writeWithListenerWhenNotOpen) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + // State is INITIALIZED (not OPEN) - write should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, closeWithPendingTxQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Close with a pending TX job - should not crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + + // TX ISR after close - should not crash or call listener + EXPECT_CALL(listener, canFrameSent(_)).Times(0); +} + +TEST_F(InitedFdCanTransceiverTest, muteWithPendingTxQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Mute with a pending TX job - should not crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, disableRxInterruptDelegates) +{ + EXPECT_CALL(fFct.fDevice, disableRxInterrupt()).Times(1); + FdCanTransceiver::disableRxInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, enableRxInterruptDelegates) +{ + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + FdCanTransceiver::enableRxInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, disableRxInterruptInvalidIndex) +{ + // Should not crash + FdCanTransceiver::disableRxInterrupt(3); + FdCanTransceiver::disableRxInterrupt(255); +} + +TEST_F(FdCanTransceiverTest, enableRxInterruptInvalidIndex) +{ + // Should not crash + FdCanTransceiver::enableRxInterrupt(3); + FdCanTransceiver::enableRxInterrupt(255); +} + +TEST_F(FdCanTransceiverTest, writeInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeInInitializedState) +{ + CANFrame frame; + // State is INITIALIZED - write should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(FdCanTransceiverTest, write2rInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rInInitializedState) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(FdCanTransceiverTest, muteInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + EXPECT_CALL(fct.fDevice, init()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, muteInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.mute()); +} + +TEST_F(FdCanTransceiverTest, unmuteInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + EXPECT_CALL(fct.fDevice, init()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteInInitializedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteInOpenState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, openInOpenState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, closeInInitializedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, closeInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, busOffDuringPendingTxClearsQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Bus-off detected by cyclicTask + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)); + fFct.cyclicTask(); + + // After bus-off, write with listener should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, busRecoveryAfterQueueClear) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Bus-off + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + fFct.cyclicTask(); // OPEN -> MUTED + + // Bus recovery + fFct.cyclicTask(); // MUTED -> OPEN + + // New write should succeed + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskWhileTxPending) +{ + CANFrame txFrame; + CANFrame rxFrame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(txFrame, listener)); + + // RX task runs while TX is pending - should work independently + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(1)); + EXPECT_CALL(fFct.fDevice, getRxFrame(0)).WillOnce(ReturnRef(rxFrame)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + fFct.receiveTask(); + + // TX ISR still works after RX task + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterruptDuringReceiveTask) +{ + CANFrame txFrame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(txFrame, listener)); + + // TX ISR fires + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); + + // RX task runs after TX ISR - should still work normally + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + fFct.receiveTask(); +} + +} // namespace diff --git a/platforms/stm32/cmake/stm32f413zh.cmake b/platforms/stm32/cmake/stm32f413zh.cmake new file mode 100644 index 00000000000..4c52f9a54a4 --- /dev/null +++ b/platforms/stm32/cmake/stm32f413zh.cmake @@ -0,0 +1,15 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(STM32_FAMILY "F4") +set(STM32_DEVICE_UPPER "STM32F413xx") +set(CAN_TYPE "BXCAN") +set(STM32_STARTUP_ASM + "${CMAKE_CURRENT_LIST_DIR}/../bsp/bspMcu/startup/startup_stm32f413xx.s") diff --git a/platforms/stm32/cmake/stm32g474re.cmake b/platforms/stm32/cmake/stm32g474re.cmake new file mode 100644 index 00000000000..64a70fabdc2 --- /dev/null +++ b/platforms/stm32/cmake/stm32g474re.cmake @@ -0,0 +1,15 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(STM32_FAMILY "G4") +set(STM32_DEVICE_UPPER "STM32G474xx") +set(CAN_TYPE "FDCAN") +set(STM32_STARTUP_ASM + "${CMAKE_CURRENT_LIST_DIR}/../bsp/bspMcu/startup/startup_stm32g474xx.s") diff --git a/platforms/stm32/etlImpl/CMakeLists.txt b/platforms/stm32/etlImpl/CMakeLists.txt new file mode 100644 index 00000000000..c3f4d59b292 --- /dev/null +++ b/platforms/stm32/etlImpl/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(etlImpl src/print.cpp src/clocks.cpp) + +target_link_libraries(etlImpl PRIVATE etl bsp util) diff --git a/platforms/stm32/etlImpl/src/clocks.cpp b/platforms/stm32/etlImpl/src/clocks.cpp new file mode 100644 index 00000000000..fa1ab9ae743 --- /dev/null +++ b/platforms/stm32/etlImpl/src/clocks.cpp @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include + +extern "C" +{ +etl::chrono::high_resolution_clock::rep etl_get_high_resolution_clock() +{ + return etl::chrono::high_resolution_clock::rep{static_cast(getSystemTimeNs())}; +} + +etl::chrono::system_clock::rep etl_get_system_clock() +{ + return etl::chrono::system_clock::rep(static_cast(getSystemTimeUs())); +} + +etl::chrono::steady_clock::rep etl_get_steady_clock() +{ + return etl::chrono::steady_clock::rep(static_cast(getSystemTimeMs() / 1000)); +} +} diff --git a/platforms/stm32/etlImpl/src/print.cpp b/platforms/stm32/etlImpl/src/print.cpp new file mode 100644 index 00000000000..9d14f512b7b --- /dev/null +++ b/platforms/stm32/etlImpl/src/print.cpp @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include + +extern "C" void etl_putchar(int c) { putByteToStdout(static_cast(c)); } diff --git a/platforms/stm32/hardFaultHandler/CMakeLists.txt b/platforms/stm32/hardFaultHandler/CMakeLists.txt new file mode 100644 index 00000000000..2c7881695f5 --- /dev/null +++ b/platforms/stm32/hardFaultHandler/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(hardFaultHandler src/hardFaultHandler.s) + +# The .s file uses preprocessor conditionals for the chip family. +set_source_files_properties(src/hardFaultHandler.s + PROPERTIES COMPILE_OPTIONS "-x;assembler-with-cpp") + +set_target_properties(hardFaultHandler PROPERTIES COMPILE_WARNING_AS_ERROR OFF) + +target_compile_options( + hardFaultHandler + PRIVATE "$<$:-Wno-unused-command-line-argument>") + +target_link_libraries(hardFaultHandler PRIVATE bspMcu) diff --git a/platforms/stm32/hardFaultHandler/doc/index.rst b/platforms/stm32/hardFaultHandler/doc/index.rst new file mode 100644 index 00000000000..8a72ce4d537 --- /dev/null +++ b/platforms/stm32/hardFaultHandler/doc/index.rst @@ -0,0 +1,37 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +hardFaultHandler +================ + +Overview +-------- + +The ``hardFaultHandler`` module provides a Cortex-M4 hard fault handler that saves a register +dump to a no-init RAM region. The dump survives a software reset, allowing post-mortem +analysis of the fault cause after reboot. + +The following data is saved to the dump: + +- CPU registers of the faulting context (R0-R12, SP, LR, PC, xPSR) +- LR and PSR at the entry of the exception handler +- stack contents (as much as possible) +- checksum of the dump + +The handler is written in assembly to guarantee correct stack frame extraction from either +MSP or PSP, depending on which stack was active at the time of the fault. + +Integration +----------- + +The board integration must branch the HardFault vector to ``customHardFaultHandler`` and +provide ``HardFault_Handler_Final``. The dump region must be reserved by the board linker +script. diff --git a/platforms/stm32/hardFaultHandler/module.spec b/platforms/stm32/hardFaultHandler/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/hardFaultHandler/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s b/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s new file mode 100644 index 00000000000..f8bdbcc340c --- /dev/null +++ b/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s @@ -0,0 +1,144 @@ +// Copyright (c) 2024 Accenture +// Copyright (c) 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +.syntax unified +.arch armv7-m + +//------------------------------------------------------------------------------ +// Hard fault register dump for STM32 (Cortex-M4) +// +// Dumps all registers + stack snapshot to a fixed NO_INIT RAM location. +// RAM addresses are chip-specific - configured via defines. +//------------------------------------------------------------------------------ + +// Dump location - use top of SRAM for both F4 (320KB) and G4 (128KB). +// The dump region must be reserved by the board linker script (see the board +// integration). +// Default: use conservative address that works for G4 (smallest SRAM). +// G4: SRAM ends at 0x20020000, F4: SRAM ends at 0x20050000 +#if defined(STM32_FAMILY_F4) +.equ NO_INIT_RAM_START, 0x2004FC00 +#else +.equ NO_INIT_RAM_START, 0x2001FC00 +#endif +.equ NO_INIT_RAM_SIZE, 0x400 +.equ DUMP_SIZE, 0x140 +.equ DUMP_START, NO_INIT_RAM_START + NO_INIT_RAM_SIZE - DUMP_SIZE + +// Dump fields offsets +.equ DUMP_HANDLER_PSR, 0x000 +.equ DUMP_HANDLER_LR, 0x004 +.equ DUMP_ORIGIN_R0, 0x008 +.equ DUMP_ORIGIN_R1, 0x00C +.equ DUMP_ORIGIN_R2, 0x010 +.equ DUMP_ORIGIN_R3, 0x014 +.equ DUMP_ORIGIN_R4, 0x018 +.equ DUMP_ORIGIN_R5, 0x01C +.equ DUMP_ORIGIN_R6, 0x020 +.equ DUMP_ORIGIN_R7, 0x024 +.equ DUMP_ORIGIN_R8, 0x028 +.equ DUMP_ORIGIN_R9, 0x02C +.equ DUMP_ORIGIN_R10, 0x030 +.equ DUMP_ORIGIN_R11, 0x034 +.equ DUMP_ORIGIN_R12, 0x038 +.equ DUMP_ORIGIN_SP, 0x03C +.equ DUMP_ORIGIN_LR, 0x040 +.equ DUMP_ORIGIN_PC, 0x044 +.equ DUMP_ORIGIN_PSR, 0x048 +.equ DUMP_STACK_START, 0x04C +.equ DUMP_STACK_END, 0x13C +.equ DUMP_CHECKSUM, 0x13C + +// Stack frame offsets +.equ FRAME_R0, 0x00 +.equ FRAME_R1, 0x04 +.equ FRAME_R2, 0x08 +.equ FRAME_R3, 0x0C +.equ FRAME_R12, 0x10 +.equ FRAME_LR, 0x14 +.equ FRAME_PC, 0x18 +.equ FRAME_PSR, 0x1C +.equ FRAME_SIZE, 0x20 + +//------------------------------------------------------------------------------ +// WARNING: +// R4-R11 must not be used before they are written to the dump, since they are +// not saved in stack. Stack must not be used. +//------------------------------------------------------------------------------ + +.text +.globl customHardFaultHandler +.type customHardFaultHandler, %function + +customHardFaultHandler: + + // Save flags for dump in advance + mrs R12, XPSR + + // Save PSR and LR + ldr R2, = DUMP_START + str R12, [R2, #DUMP_HANDLER_PSR] + str LR, [R2, #DUMP_HANDLER_LR] + + // Save values of registers which are not framed + str R4, [R2, #DUMP_ORIGIN_R4] + str R5, [R2, #DUMP_ORIGIN_R5] + str R6, [R2, #DUMP_ORIGIN_R6] + str R7, [R2, #DUMP_ORIGIN_R7] + str R8, [R2, #DUMP_ORIGIN_R8] + str R9, [R2, #DUMP_ORIGIN_R9] + str R10, [R2, #DUMP_ORIGIN_R10] + str R11, [R2, #DUMP_ORIGIN_R11] + + // Analyze exception return code, calculate the value of SP which was + // active at the moment of exception, and save it + tst LR, 4 + ite eq + mrseq R1, MSP + mrsne R1, PSP + add R0, R1, FRAME_SIZE + str R0, [R2, #DUMP_ORIGIN_SP] + + // Save value of registers which are framed + ldr R0, [R1, #FRAME_R0] + str R0, [R2, #DUMP_ORIGIN_R0] + ldr R0, [R1, #FRAME_R1] + str R0, [R2, #DUMP_ORIGIN_R1] + ldr R0, [R1, #FRAME_R2] + str R0, [R2, #DUMP_ORIGIN_R2] + ldr R0, [R1, #FRAME_R3] + str R0, [R2, #DUMP_ORIGIN_R3] + ldr R0, [R1, #FRAME_R12] + str R0, [R2, #DUMP_ORIGIN_R12] + ldr R0, [R1, #FRAME_LR] + str R0, [R2, #DUMP_ORIGIN_LR] + ldr R0, [R1, #FRAME_PC] + str R0, [R2, #DUMP_ORIGIN_PC] + ldr R0, [R1, #FRAME_PSR] + str R0, [R2, #DUMP_ORIGIN_PSR] + + // Add some stack contents to dump + add R1, FRAME_SIZE + add R2, DUMP_STACK_START + add R3, R2, DUMP_STACK_END - DUMP_STACK_START + 1: + ldr R0, [R1], 4 + str R0, [R2], 4 + cmp R2, R3 + bne 1b + + // Calculate and save checksum + mov R1, 0 + ldr R2, = DUMP_START + ldr R3, = DUMP_START + DUMP_CHECKSUM + 1: + ldr R0, [R2], 4 + add R1, R0 + cmp R2, R3 + bne 1b + str R1, [R3] + + // Complete HardFault handling + b HardFault_Handler_Final diff --git a/platforms/stm32/safety/CMakeLists.txt b/platforms/stm32/safety/CMakeLists.txt new file mode 100644 index 00000000000..b13c0a3f24a --- /dev/null +++ b/platforms/stm32/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeBspMcuWatchdog) diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt b/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt new file mode 100644 index 00000000000..810161f1914 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeBspMcuWatchdog src/watchdog/Watchdog.cpp) +target_include_directories(safeBspMcuWatchdog PUBLIC include) +target_link_libraries( + safeBspMcuWatchdog + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h b/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h new file mode 100644 index 00000000000..4f5b3d63c7a --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h @@ -0,0 +1,90 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ +namespace bsp +{ + +// IWDG driver for STM32; the G474RE board's SafetyManager drives it directly. +// +// STM32 IWDG specifics: +// - LSI clock ~32 kHz (not trimmed - 17..47 kHz depending on device) +// - 12-bit reload register (0..4095) +// - Once started, cannot be disabled (hardware limitation) +// - Kick sequence: write 0xAAAA to KR +// - Unlock sequence: write 0x5555 to KR, then configure PR + RLR +// - Start sequence: write 0xCCCC to KR +class Watchdog +{ +public: + Watchdog() = default; + + explicit Watchdog(uint32_t const timeout, uint32_t const clockSpeed = DEFAULT_CLOCK_SPEED) + { + enableWatchdog(timeout, false, clockSpeed); + } + + /** + * \brief Enable the IWDG with the given timeout. + * \param timeout Timeout in milliseconds (max ~32 s at 32 kHz LSI). + * \param interruptActive Ignored on STM32 (IWDG has no interrupt mode). + * \param clockSpeed LSI clock speed in Hz (default 32000). + */ + static void enableWatchdog( + uint32_t timeout, bool interruptActive = false, uint32_t clockSpeed = DEFAULT_CLOCK_SPEED); + + /** + * \brief Cannot disable the STM32 IWDG once started. This is a no-op. + */ + static void disableWatchdog(); + + /** + * \brief Kick (service) the IWDG by writing 0xAAAA to KR. + */ + static void serviceWatchdog(); + + /** + * \brief Check that the IWDG prescaler and reload match the expected timeout. + * \return true if the configuration is consistent. + */ + static bool + checkWatchdogConfiguration(uint32_t timeout, uint32_t clockSpeed = DEFAULT_CLOCK_SPEED); + + /** + * \brief Check if the last reset was caused by the IWDG. + * \return true if RCC_CSR_IWDGRSTF is set. + */ + static bool isResetFromWatchdog(); + + /** + * \brief Clear the IWDG reset flag in RCC_CSR. + */ + static void clearResetFlag(); + + static uint32_t getWatchdogServiceCounter(); + + static constexpr uint32_t DEFAULT_TIMEOUT = 250U; ///< 250 ms + static constexpr uint32_t DEFAULT_CLOCK_SPEED = 32000U; ///< LSI ~32 kHz + +private: + static uint32_t watchdogServiceCounter; + + /// Compute prescaler divider and reload value for a given timeout. + static void computePrescalerAndReload( + uint32_t timeoutMs, uint32_t clockHz, uint8_t& prescalerCode, uint16_t& reload); +}; + +} // namespace bsp +} // namespace safety diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/module.spec b/platforms/stm32/safety/safeBspMcuWatchdog/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp b/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp new file mode 100644 index 00000000000..fd9b150e1cc --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp @@ -0,0 +1,109 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace safety +{ +namespace bsp +{ + +uint32_t Watchdog::watchdogServiceCounter = 0U; + +void Watchdog::computePrescalerAndReload( + uint32_t timeoutMs, uint32_t clockHz, uint8_t& prescalerCode, uint16_t& reload) +{ + // IWDG prescaler dividers: /4, /8, /16, /32, /64, /128, /256 + // prescalerCode 0..6 -> divider = 4 << code + // + // timeout_ms = (reload + 1) * divider * 1000 / clockHz + // reload = (timeout_ms * clockHz) / (divider * 1000) - 1 + + static uint16_t const dividers[] = {4, 8, 16, 32, 64, 128, 256}; + + for (uint8_t code = 0U; code < 7U; code++) + { + uint32_t ticks = (timeoutMs * (clockHz / 1000U)) / dividers[code]; + if (ticks == 0U) + { + ticks = 1U; + } + if (ticks <= 4096U) + { + prescalerCode = code; + reload = static_cast(ticks - 1U); + return; + } + } + + // Largest possible: prescaler /256, reload 4095 + prescalerCode = 6U; + reload = 4095U; +} + +void Watchdog::enableWatchdog(uint32_t timeout, bool /*interruptActive*/, uint32_t clockSpeed) +{ + uint8_t prescalerCode; + uint16_t reload; + computePrescalerAndReload(timeout, clockSpeed, prescalerCode, reload); + + // Unlock IWDG registers + IWDG->KR = 0x5555U; + + IWDG->PR = prescalerCode; + IWDG->RLR = reload; + + // Wait for registers to be updated + while ((IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) != 0U) {} + + // Start the watchdog (irreversible) + IWDG->KR = 0xCCCCU; + + serviceWatchdog(); +} + +void Watchdog::disableWatchdog() +{ + // STM32 IWDG cannot be disabled once started. + // This is intentionally a no-op. +} + +void Watchdog::serviceWatchdog() +{ + IWDG->KR = 0xAAAAU; + watchdogServiceCounter++; +} + +bool Watchdog::checkWatchdogConfiguration(uint32_t timeout, uint32_t clockSpeed) +{ + uint8_t expectedPrescaler; + uint16_t expectedReload; + computePrescalerAndReload(timeout, clockSpeed, expectedPrescaler, expectedReload); + + uint32_t actualPrescaler = IWDG->PR & 0x7U; + uint32_t actualReload = IWDG->RLR & 0xFFFU; + + return (actualPrescaler == expectedPrescaler) && (actualReload == expectedReload); +} + +bool Watchdog::isResetFromWatchdog() { return (RCC->CSR & RCC_CSR_IWDGRSTF) != 0U; } + +void Watchdog::clearResetFlag() +{ + // Writing RMVF bit clears all reset flags + RCC->CSR |= RCC_CSR_RMVF; +} + +uint32_t Watchdog::getWatchdogServiceCounter() { return watchdogServiceCounter; } + +} // namespace bsp +} // namespace safety diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt b/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt new file mode 100644 index 00000000000..12cf8635773 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(WatchdogTest src/WatchdogTest.cpp) + +target_include_directories(WatchdogTest PRIVATE include ../include ../src) + +target_link_libraries(WatchdogTest PRIVATE platform gmock gtest_main) + +gtest_discover_tests(WatchdogTest PROPERTIES LABELS "WatchdogTest") diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h b/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h new file mode 100644 index 00000000000..30abb173e91 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h @@ -0,0 +1,11 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp b/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp new file mode 100644 index 00000000000..380b4be59cf --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp @@ -0,0 +1,944 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Uses fake IWDG_TypeDef and RCC_TypeDef structs so register writes go +// to testable memory instead of real hardware. + +#include +#include + +#ifndef __IO +#define __IO volatile +#endif + +// Select G4 code path +#define STM32G474xx + +// --- Fake IWDG_TypeDef --- +typedef struct +{ + __IO uint32_t KR; + __IO uint32_t PR; + __IO uint32_t RLR; + __IO uint32_t SR; + __IO uint32_t WINR; +} IWDG_TypeDef; + +// --- Fake RCC_TypeDef (STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t ICSCR; + __IO uint32_t CFGR; + __IO uint32_t PLLCFGR; + uint32_t RESERVED0; + uint32_t RESERVED1; + __IO uint32_t CIER; + __IO uint32_t CIFR; + __IO uint32_t CICR; + uint32_t RESERVED2; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED3; + __IO uint32_t APB1RSTR1; + __IO uint32_t APB1RSTR2; + __IO uint32_t APB2RSTR; + uint32_t RESERVED4; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED5; + __IO uint32_t APB1ENR1; + __IO uint32_t APB1ENR2; + __IO uint32_t APB2ENR; + uint32_t RESERVED6; + __IO uint32_t AHB1SMENR; + __IO uint32_t AHB2SMENR; + __IO uint32_t AHB3SMENR; + uint32_t RESERVED7; + __IO uint32_t APB1SMENR1; + __IO uint32_t APB1SMENR2; + __IO uint32_t APB2SMENR; + uint32_t RESERVED8; + __IO uint32_t CCIPR; + uint32_t RESERVED9; + __IO uint32_t BDCR; + __IO uint32_t CSR; + __IO uint32_t CRRCR; + __IO uint32_t CCIPR2; +} RCC_TypeDef; + +// --- Static fake peripherals --- +static IWDG_TypeDef fakeIwdg; +static RCC_TypeDef fakeRcc; + +// --- Override hardware macros --- +#define IWDG (&fakeIwdg) +#define RCC (&fakeRcc) + +// IWDG SR bit definitions +#define IWDG_SR_PVU (1U << 0) +#define IWDG_SR_RVU (1U << 1) + +// RCC CSR bit definitions +#define RCC_CSR_IWDGRSTF (1U << 29) +#define RCC_CSR_RMVF (1U << 23) + +// Provide platform/estdint.h types +#ifndef PLATFORM_ESTDINT_H +#define PLATFORM_ESTDINT_H +#include +#endif + +// Include production code (header + implementation directly) +#include +#include + +#include + +using namespace safety::bsp; + +class WatchdogTest : public ::testing::Test +{ +protected: + void SetUp() override + { + std::memset(&fakeIwdg, 0, sizeof(fakeIwdg)); + std::memset(&fakeRcc, 0, sizeof(fakeRcc)); + // Reset the static service counter by calling enable + reading + // We will use a helper to reset state between tests + } +}; + +// computePrescalerAndReload tests (verified through enableWatchdog PR/RLR) + +TEST_F(WatchdogTest, enableWatchdog_1ms_32000Hz) +{ + // timeout=1ms, clock=32000 => ticks = (1 * 32) / 4 = 8, prescaler=0, reload=7 + Watchdog::enableWatchdog(1U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 7U); +} + +TEST_F(WatchdogTest, enableWatchdog_100ms_32000Hz) +{ + // timeout=100ms, clock=32000 => ticks = (100 * 32) / 4 = 800, prescaler=0, reload=799 + Watchdog::enableWatchdog(100U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 799U); +} + +TEST_F(WatchdogTest, enableWatchdog_250ms_32000Hz) +{ + // timeout=250ms, clock=32000 => ticks = (250 * 32) / 4 = 2000, prescaler=0, reload=1999 + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_500ms_32000Hz) +{ + // timeout=500ms, clock=32000 => ticks = (500 * 32) / 4 = 4000, prescaler=0, reload=3999 + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_32000Hz) +{ + // timeout=1000ms, clock=32000 => ticks = (1000 * 32) / 4 = 8000 > 4096 + // Try prescaler 1 (/8): ticks = (1000 * 32) / 8 = 4000, prescaler=1, reload=3999 + Watchdog::enableWatchdog(1000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_2000ms_32000Hz) +{ + // ticks@/4 = 16000; /8 = 8000; /16 = 4000 => prescaler=2, reload=3999 + Watchdog::enableWatchdog(2000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_4000ms_32000Hz) +{ + // ticks@/4=32000; /8=16000; /16=8000; /32=4000 => prescaler=3, reload=3999 + Watchdog::enableWatchdog(4000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_8000ms_32000Hz) +{ + // /64 = 4000 => prescaler=4, reload=3999 + Watchdog::enableWatchdog(8000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_16000ms_32000Hz) +{ + // /128 = 4000 => prescaler=5, reload=3999 + Watchdog::enableWatchdog(16000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_32000ms_32000Hz) +{ + // /256 = 4000 => prescaler=6, reload=3999 + Watchdog::enableWatchdog(32000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_MaxTimeout_Clamps) +{ + // Extremely large timeout, should clamp to prescaler=6, reload=4095 + Watchdog::enableWatchdog(100000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +// --- Different clock speeds --- + +TEST_F(WatchdogTest, enableWatchdog_250ms_40000Hz) +{ + // ticks = (250 * 40) / 4 = 2500, prescaler=0, reload=2499 + Watchdog::enableWatchdog(250U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_40000Hz) +{ + // ticks@/4 = 10000 > 4096; /8 = 5000 > 4096; /16 = 2500 => prescaler=2, reload=2499 + Watchdog::enableWatchdog(1000U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, enableWatchdog_100ms_128000Hz) +{ + // ticks = (100 * 128) / 4 = 3200, prescaler=0, reload=3199 + Watchdog::enableWatchdog(100U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3199U); +} + +TEST_F(WatchdogTest, enableWatchdog_250ms_128000Hz) +{ + // ticks@/4 = 8000; /8 = 4000 => prescaler=1, reload=3999 + Watchdog::enableWatchdog(250U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_128000Hz) +{ + // ticks@/4=32000; /8=16000; /16=8000; /32=4000 => prescaler=3, reload=3999 + Watchdog::enableWatchdog(1000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_500ms_128000Hz) +{ + // ticks@/4=16000; /8=8000; /16=4000 => prescaler=2, reload=3999 + Watchdog::enableWatchdog(500U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +// --- Prescaler code boundary: each prescaler code (0-6) --- + +TEST_F(WatchdogTest, prescalerCode0_SmallTimeout) +{ + // 10ms at 32kHz: ticks = (10*32)/4 = 80 => code 0, reload=79 + Watchdog::enableWatchdog(10U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 79U); +} + +TEST_F(WatchdogTest, prescalerCode0_AtLimit) +{ + // 512ms at 32kHz: ticks = (512*32)/4 = 4096 => code 0, reload=4095 + Watchdog::enableWatchdog(512U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +TEST_F(WatchdogTest, prescalerCode1_JustOverCode0) +{ + // 513ms at 32kHz: ticks@/4 = 4104 > 4096; /8 = 2052 => code 1, reload=2051 + Watchdog::enableWatchdog(513U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 2051U); +} + +TEST_F(WatchdogTest, prescalerCode2_Boundary) +{ + // 1025ms at 32kHz: ticks@/4=8200; /8=4100>4096; /16=2050 => code 2, reload=2049 + Watchdog::enableWatchdog(1025U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 2049U); +} + +TEST_F(WatchdogTest, prescalerCode3_Boundary) +{ + // 2049ms at 32kHz: ticks = (2049*32)/32 = 2049, reload = 2049-1 = 2048 + Watchdog::enableWatchdog(2049U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2048U); +} + +TEST_F(WatchdogTest, prescalerCode4_Boundary) +{ + // 4097ms at 32kHz: ticks=(4097*32)/64=2048, reload=2048-1=2047 + Watchdog::enableWatchdog(4097U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +TEST_F(WatchdogTest, prescalerCode5_Boundary) +{ + // 8193ms at 32kHz: (8193*32)/64=4096 fits code 4; need >4096 to force code 5 + // Use 8194ms: (8194*32)/64=4097>4096 -> code 5, ticks=(8194*32)/128=2048, rlr=2047 + Watchdog::enableWatchdog(8194U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +TEST_F(WatchdogTest, prescalerCode6_Boundary) +{ + // 16385ms: (16385*32)/128=4096 fits code 5; need >4096 to force code 6 + // 16386*32/128=4096 still fits code 5. Use 16389: (16389*32)/128=4100>4096 -> code 6 + // ticks=(16389*32)/256=2049, rlr=2048 + Watchdog::enableWatchdog(16389U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +// enableWatchdog: KR unlock/start sequence + +TEST_F(WatchdogTest, enableWatchdog_WritesUnlockKey) +{ + // We check that KR received the start key last (0xCCCC written, then 0xAAAA from + // serviceWatchdog) + Watchdog::enableWatchdog(250U, false, 32000U); + // After enableWatchdog, the last KR write was serviceWatchdog => 0xAAAA + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, enableWatchdog_SetsPrescaler) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); +} + +TEST_F(WatchdogTest, enableWatchdog_SetsReload) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_DefaultClockSpeed) +{ + Watchdog::enableWatchdog(250U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_IgnoresInterruptFlag) +{ + Watchdog::enableWatchdog(250U, true, 32000U); + // Same result regardless of interruptActive + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +// serviceWatchdog tests + +TEST_F(WatchdogTest, serviceWatchdog_WritesKickKey) +{ + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_IncrementsCounter) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 1U); +} + +TEST_F(WatchdogTest, serviceWatchdog_MultipleKicks) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + Watchdog::serviceWatchdog(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 3U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_KRAlwaysKickValue) +{ + fakeIwdg.KR = 0x1234U; + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_TenKicks) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + for (int i = 0; i < 10; i++) + { + Watchdog::serviceWatchdog(); + } + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 10U); +} + +// checkWatchdogConfiguration tests + +TEST_F(WatchdogTest, checkConfig_MatchingConfig_ReturnsTrue) +{ + // Set up fake IWDG to match 250ms at 32kHz: PR=0, RLR=1999 + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_MismatchedPR_ReturnsFalse) +{ + fakeIwdg.PR = 1U; // Wrong prescaler + fakeIwdg.RLR = 1999U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_MismatchedRLR_ReturnsFalse) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 2000U; // Wrong reload + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_BothMismatched_ReturnsFalse) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 500U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_1000ms_32kHz_Match) +{ + fakeIwdg.PR = 1U; + fakeIwdg.RLR = 3999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(1000U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_100ms_128kHz_Match) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 3199U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(100U, 128000U)); +} + +TEST_F(WatchdogTest, checkConfig_DefaultClockSpeed) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U)); +} + +TEST_F(WatchdogTest, checkConfig_ExtraBitsInPR_Masked) +{ + // PR has bits above bit 2 set - should be masked to 3 bits + fakeIwdg.PR = 0U | (0xF0U); + fakeIwdg.RLR = 1999U; + // checkWatchdogConfiguration masks PR with 0x7 + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_ExtraBitsInRLR_Masked) +{ + // RLR has bits above bit 11 set - should be masked to 12 bits + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U | (0xF000U); + // checkWatchdogConfiguration masks RLR with 0xFFF + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_500ms_40kHz_Match) +{ + // 500ms@40kHz: /4 => ticks=5000>4096; /8=>2500 => PR=1, RLR=2499 + fakeIwdg.PR = 1U; + fakeIwdg.RLR = 2499U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_AfterEnable_Consistent) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_AfterEnable_DifferentTimeout_False) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +// isResetFromWatchdog tests + +TEST_F(WatchdogTest, isResetFromWatchdog_FlagSet) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_FlagCleared) +{ + fakeRcc.CSR = 0U; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_OtherBitsSet) +{ + fakeRcc.CSR = 0xFFFFFFFFU & ~RCC_CSR_IWDGRSTF; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_AllBitsSet) +{ + fakeRcc.CSR = 0xFFFFFFFFU; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_OnlyIwdgBit) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +// clearResetFlag tests + +TEST_F(WatchdogTest, clearResetFlag_SetsRMVF) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +TEST_F(WatchdogTest, clearResetFlag_PreservesOtherBits) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_IWDGRSTF, 0U); +} + +TEST_F(WatchdogTest, clearResetFlag_MultipleCalls) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +// getWatchdogServiceCounter tests + +TEST_F(WatchdogTest, getWatchdogServiceCounter_IncrementsOnService) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); +} + +TEST_F(WatchdogTest, getWatchdogServiceCounter_EnableAlsoKicks) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::enableWatchdog(250U, false, 32000U); + // enableWatchdog calls serviceWatchdog once at the end + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); +} + +TEST_F(WatchdogTest, getWatchdogServiceCounter_ConsistentAfterMultipleCalls) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + for (uint32_t i = 0; i < 20; i++) + { + Watchdog::serviceWatchdog(); + } + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 20U); +} + +// disableWatchdog tests (no-op verification) + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeKR) +{ + fakeIwdg.KR = 0x1234U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0x1234U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangePR) +{ + fakeIwdg.PR = 3U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.PR, 3U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeRLR) +{ + fakeIwdg.RLR = 2000U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.RLR, 2000U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeSR) +{ + fakeIwdg.SR = 0x3U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.SR, 0x3U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeServiceCounter) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::disableWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev); +} + +// Edge case: timeout 0 + +TEST_F(WatchdogTest, enableWatchdog_Timeout0) +{ + // timeout=0ms, clock=32000 => ticks = 0, forced to 1, prescaler=0, reload=0 + Watchdog::enableWatchdog(0U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 0U); +} + +TEST_F(WatchdogTest, enableWatchdog_Timeout0_128kHz) +{ + Watchdog::enableWatchdog(0U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 0U); +} + +// Edge case: very large timeout overflows to max + +TEST_F(WatchdogTest, enableWatchdog_Overflow_50000ms) +{ + Watchdog::enableWatchdog(50000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +TEST_F(WatchdogTest, enableWatchdog_Overflow_128kHz_50000ms) +{ + Watchdog::enableWatchdog(50000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +// Constructor test + +TEST_F(WatchdogTest, constructor_CallsEnableWatchdog) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog wdt(250U, 32000U); + // Constructor calls enableWatchdog which calls serviceWatchdog + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, constructor_DefaultClock) +{ + Watchdog wdt(100U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 799U); +} + +// Full sequence tests + +TEST_F(WatchdogTest, fullSequence_EnableCheckService) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, fullSequence_EnableServiceDisableService) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + Watchdog::serviceWatchdog(); + Watchdog::disableWatchdog(); // no-op + // Registers should still be from enableWatchdog + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 32000U)); + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, fullSequence_CheckResetFlagFlow) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +// Additional prescaler/reload combination tests + +TEST_F(WatchdogTest, prescaler_5ms_32kHz) +{ + // ticks = (5*32)/4 = 40 => code 0, reload=39 + Watchdog::enableWatchdog(5U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 39U); +} + +TEST_F(WatchdogTest, prescaler_50ms_32kHz) +{ + // ticks = (50*32)/4 = 400 => code 0, reload=399 + Watchdog::enableWatchdog(50U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 399U); +} + +TEST_F(WatchdogTest, prescaler_200ms_32kHz) +{ + // ticks = (200*32)/4 = 1600 => code 0, reload=1599 + Watchdog::enableWatchdog(200U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1599U); +} + +TEST_F(WatchdogTest, prescaler_3000ms_32kHz) +{ + // ticks@/4=24000; /8=12000; /16=6000>4096; /32=3000 => code 3, reload=2999 + Watchdog::enableWatchdog(3000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2999U); +} + +TEST_F(WatchdogTest, prescaler_10000ms_32kHz) +{ + // /64=5000>4096; /128=2500 => code 5, reload=2499 + Watchdog::enableWatchdog(10000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, prescaler_20000ms_32kHz) +{ + // /128=5000>4096; /256=2500 => code 6, reload=2499 + Watchdog::enableWatchdog(20000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, prescaler_2ms_40kHz) +{ + // ticks = (2*40)/4 = 20 => code 0, reload=19 + Watchdog::enableWatchdog(2U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 19U); +} + +TEST_F(WatchdogTest, prescaler_10ms_128kHz) +{ + // ticks = (10*128)/4 = 320 => code 0, reload=319 + Watchdog::enableWatchdog(10U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 319U); +} + +TEST_F(WatchdogTest, prescaler_2000ms_128kHz) +{ + // /4=64000; /8=32000; /16=16000; /32=8000; /64=4000 => code 4, reload=3999 + Watchdog::enableWatchdog(2000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, prescaler_4000ms_128kHz) +{ + // /64=8000; /128=4000 => code 5, reload=3999 + Watchdog::enableWatchdog(4000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, prescaler_8000ms_128kHz) +{ + // /128=8000; /256=4000 => code 6, reload=3999 + Watchdog::enableWatchdog(8000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +// Re-enable with different timeouts + +TEST_F(WatchdogTest, reEnable_DifferentTimeout) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); + + // Re-enable with larger timeout + Watchdog::enableWatchdog(1000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, reEnable_SmallToLarge) +{ + Watchdog::enableWatchdog(1U, false, 32000U); + EXPECT_EQ(fakeIwdg.RLR, 7U); + + Watchdog::enableWatchdog(32000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_300ms_32kHz) +{ + // ticks = (300*32)/4 = 2400 => code 0, reload=2399 + Watchdog::enableWatchdog(300U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 2399U); +} + +TEST_F(WatchdogTest, enableWatchdog_400ms_32kHz) +{ + // ticks = (400*32)/4 = 3200 => code 0, reload=3199 + Watchdog::enableWatchdog(400U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3199U); +} + +TEST_F(WatchdogTest, enableWatchdog_150ms_40kHz) +{ + // ticks = (150*40)/4 = 1500 => code 0, reload=1499 + Watchdog::enableWatchdog(150U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1499U); +} + +TEST_F(WatchdogTest, enableWatchdog_50ms_40kHz) +{ + // ticks = (50*40)/4 = 500 => code 0, reload=499 + Watchdog::enableWatchdog(50U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 499U); +} + +TEST_F(WatchdogTest, enableWatchdog_2000ms_40kHz) +{ + // /4=20000; /8=10000; /16=5000>4096; /32=2500 => code 3, reload=2499 + Watchdog::enableWatchdog(2000U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_Match) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 2499U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_WrongPR) +{ + fakeIwdg.PR = 2U; + fakeIwdg.RLR = 2499U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_WrongRLR) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 2498U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_32000ms_32kHz_Match) +{ + fakeIwdg.PR = 6U; + fakeIwdg.RLR = 3999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(32000U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_1ms_32kHz_Match) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 7U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(1U, 32000U)); +} + +TEST_F(WatchdogTest, serviceWatchdog_AfterDisable_StillWorks) +{ + Watchdog::disableWatchdog(); + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeWINR) +{ + fakeIwdg.WINR = 0xABCDU; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.WINR, 0xABCDU); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_ZeroCSR) +{ + fakeRcc.CSR = 0U; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, clearResetFlag_InitiallyZero) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + EXPECT_EQ(fakeRcc.CSR, RCC_CSR_RMVF); +} + +TEST_F(WatchdogTest, enableWatchdog_25ms_32kHz) +{ + // ticks = (25*32)/4 = 200 => code 0, reload=199 + Watchdog::enableWatchdog(25U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 199U); +} + +TEST_F(WatchdogTest, enableWatchdog_75ms_32kHz) +{ + // ticks = (75*32)/4 = 600 => code 0, reload=599 + Watchdog::enableWatchdog(75U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 599U); +} + +TEST_F(WatchdogTest, constructor_500ms_40kHz) +{ + Watchdog wdt(500U, 40000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} diff --git a/platforms/stm32/unitTest/CMakeLists.txt b/platforms/stm32/unitTest/CMakeLists.txt new file mode 100644 index 00000000000..d0b1953a923 --- /dev/null +++ b/platforms/stm32/unitTest/CMakeLists.txt @@ -0,0 +1 @@ +# add STM32 specific configuration modules and settings here