diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4c91888 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.15) + +project (TestApp C CXX) + +set(PROJECT_DESCRIPTION "TestApp Software") + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(TESTAPP_STANDALONE_BUILD TRUE) +else() + set(TESTAPP_STANDALONE_BUILD "") +endif() + +set(RUNNING_UNITTESTS_WITH_EMULATOR OFF CACHE BOOL "is crosscompiled") + +if("${CMAKE_PROJECT_NAME}" STREQUAL "TestApp") + if("${DEVICE_UNDER_TEST_ARCH}" STREQUAL "") + set(PROJECT_PLATFORM "") + set(DEVICE_UNDER_TEST_ARCH "") + endif() +endif() + +# Setup Project configuration and pull dependencies +include(${PROJECT_SOURCE_DIR}/cmake/testAppSetupProject.cmake) +include(testAppPullDependencies) + +# Enable download dependencies only in order to save resources by dowloading all toolchain depencies once +if(TESTAPP_DOWNLOAD_ONLY) + return() +endif() +if(TESTAPP_ENABLE_TESTS) + include(testAppAddTest) +endif() + + +if("${CMAKE_PROJECT_NAME}" STREQUAL "TestApp") + # Standalone build: for example tests that can be run on Target. + # Add integration test example to create 'IntegrationTests' Object library for tests to be referenced. + # when TestApp is built as submodule, 'IntegrationTests' library should be defined in the top project and extended within its context. + add_subdirectory(${PROJECT_SOURCE_DIR}/IntegrationTests) +endif() +add_subdirectory(${PROJECT_SOURCE_DIR}/Platform) +add_subdirectory(${PROJECT_SOURCE_DIR}/TestApp) +add_subdirectory(${PROJECT_SOURCE_DIR}/OSAdapter/src/Observer) +add_subdirectory(${PROJECT_SOURCE_DIR}/OSAdapter/src/OsAbstraction) +add_subdirectory(${PROJECT_SOURCE_DIR}/OSAdapter/src/OsExtension) + +if(DEVICE_UNDER_TEST_ARCH STREQUAL "ARM") + message(STATUS "DEVICE_UNDER_TEST_ARCH ${DEVICE_UNDER_TEST_ARCH}") +else() + message(STATUS "DEVICE_UNDER_TEST_ARCH '${DEVICE_UNDER_TEST_ARCH}' is not defined") +endif() + +message(STATUS "RUNNING_UNITTESTS_WITH_EMULATOR ${RUNNING_UNITTESTS_WITH_EMULATOR}") +message(STATUS "APP_OSADAPTER ${APP_OSADAPTER}") +message(STATUS "TESTAPP_ENABLE_TESTS ${TESTAPP_ENABLE_TESTS}") +message(STATUS "TESTAPP_ENABLE_MOCKS ${TESTAPP_ENABLE_MOCKS}") +message(STATUS "TESTAPP_STANDALONE_BUILD ${TESTAPP_STANDALONE_BUILD}") +message(STATUS "MCU ${MCU}") + +# Add TestApp unit tests +if(TESTAPP_ENABLE_TESTS) + add_subdirectory(TestApp/test) +endif() + +# Add TestApp mocks +if(TESTAPP_ENABLE_MOCKS) + add_subdirectory(TestApp/mock) +endif() diff --git a/IntegrationTests/CMakeLists.txt b/IntegrationTests/CMakeLists.txt new file mode 100644 index 0000000..6e22da8 --- /dev/null +++ b/IntegrationTests/CMakeLists.txt @@ -0,0 +1,29 @@ +# In order to define new integration test object library within top project: +# You need to creates an Object Library which compiles source files but does not archive or link their object files into a library. +# Instead other targets created by add_library() such as TestApp in our case will reference the objects. + +add_subdirectory(DemoApplication) + +# 1. Add integration tests to object library to be referenced by TestApp +add_library(IntegrationTests OBJECT + ${CMAKE_CURRENT_SOURCE_DIR}/test_suite/testSuite1.cpp +) + +# 2. Add includes for test dependencies... +target_include_directories(IntegrationTests + PUBLIC +) + +# 3. Specify c++ compile features used in the integartion tests +target_compile_features(IntegrationTests + PUBLIC + cxx_std_14 + ) + +# 4. Add test dependencies... +target_link_libraries(IntegrationTests + PUBLIC + Demoapplication + TestApp-Interface + TestEnvironment +) diff --git a/IntegrationTests/DemoApplication/CMakeLists.txt b/IntegrationTests/DemoApplication/CMakeLists.txt new file mode 100644 index 0000000..ff63d75 --- /dev/null +++ b/IntegrationTests/DemoApplication/CMakeLists.txt @@ -0,0 +1,112 @@ +# Interface used to write the test +add_library(DemoappTypes + INTERFACE + ) + +target_include_directories(DemoappTypes + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include/DemoappTypes + ) + +target_link_libraries(DemoappTypes + INTERFACE + etl + ) + +add_library(Tier1competetion + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier1competetion/Tier1competetion.cpp + ) +target_include_directories(Tier1competetion + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/Tier1competetion + ) +# Specify c++ compile features used in the Tier1competetion +target_compile_features(Tier1competetion + PUBLIC + cxx_std_14 + ) +# Tier1competetion dependencies +target_link_libraries(Tier1competetion + PUBLIC + DemoappTypes + OsExtension + etl + ) + +add_library(Dashboard + ${CMAKE_CURRENT_SOURCE_DIR}/src/Dashboard/Dashboard.cpp + ) +target_include_directories(Dashboard + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/Dashboard + ) +# Specify c++ compile features used in the Dashboard +target_compile_features(Dashboard + PUBLIC + cxx_std_14 + ) +# Dashboard dependencies +target_link_libraries(Dashboard + PUBLIC + DemoappTypes + OsExtension + etl + ) + +add_library(Tier2competetion + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier2competetion/Tier2competetion.cpp + ) +target_include_directories(Tier2competetion + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/Tier2competetion + ) +# Specify c++ compile features used in the Dashboard +target_compile_features(Tier2competetion + PUBLIC + cxx_std_14 + ) +# Tier2competetion dependencies +target_link_libraries(Tier2competetion + PUBLIC + DemoappTypes + OsExtension + etl + ) + +if(DEVICE_UNDER_TEST_ARCH MATCHES "PPC|ARM" AND NOT RUNNING_UNITTESTS_WITH_EMULATOR) + # TestApp Library (to link within the tests and target platform) + add_library(Demoapplication + ${CMAKE_CURRENT_SOURCE_DIR}/src/Dashboard/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Dashboard/Dashboard.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier1competetion/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier1competetion/Tier1competetion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier2competetion/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Tier2competetion/Tier2competetion.cpp + ) + + target_include_directories(Demoapplication + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/DemoappTypes + ${CMAKE_CURRENT_SOURCE_DIR}/include/Dashboard + ${CMAKE_CURRENT_SOURCE_DIR}/include/Tier1competetion + ${CMAKE_CURRENT_SOURCE_DIR}/include/Tier2competetion + ${PROJECT_SOURCE_DIR}/Platform/RequiredInterface/TestAppRtosInterface + ${PROJECT_SOURCE_DIR}/Platform/RequiredInterface/CommunicationChannelInterface + ) + + # Specify c++ compile features used in the TestApp + target_compile_features(Demoapplication + PUBLIC + cxx_std_14 + ) + + # TestApp dependencies + target_link_libraries(Demoapplication + PUBLIC + TestAppRtos + etl + ) + +endif() + + diff --git a/IntegrationTests/DemoApplication/README.md b/IntegrationTests/DemoApplication/README.md new file mode 100644 index 0000000..1a789ea --- /dev/null +++ b/IntegrationTests/DemoApplication/README.md @@ -0,0 +1,8 @@ +# IntegrationTests + +The tests defined here maps to either +## test_suite_fdx_lauterbach : https://github.com/eclipse/kiso-testing/blob/master/examples/fdx_lauterbach.yaml or + +## test_suite_rtt : https://github.com/eclipse/kiso-testing/blob/master/examples/j_link_rtt_segger.yaml + +regarding on which tests (channel in use) that were invoked from host side. diff --git a/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Dashboard.hpp b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Dashboard.hpp new file mode 100644 index 0000000..eb719f8 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Dashboard.hpp @@ -0,0 +1,98 @@ +/** + * @file include/Dashboard/Dashboard/Dashboard.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef DASHBOARD_DASHBOARD_HPP +#define DASHBOARD_DASHBOARD_HPP + +#include + +namespace dashboard { + +class Dashboard { +public: + /// Constructor. + Dashboard() = default; + + /// Default destructor + ~Dashboard() = default; + + /// Copy constructor. + Dashboard(Dashboard const&) = delete; + + /// Move constructor. + Dashboard(Dashboard&&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + Dashboard& operator=(Dashboard const&) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + Dashboard& operator=(Dashboard&&) = delete; + + + /** + * @brief one cycle Method to be run by TestRuner task + */ + void oneCycle(QueueSetMemberHandle_t& queueSetMemberHandle); + + /** + * @brief set _dashboardToTier2competetionMessageBuffer Reference + */ + void setDashboardToTier2competetionMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _tier2competetionToDashboardMessageBuffer Reference + */ + void setTier2competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _dashboardToTier1competetionMessageBuffer Reference + */ + void setDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _tier1competetionToDashboardMessageBuffer Reference + */ + void setTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set dashboard queue set handle + */ + void setDashboardQueueSetHandle(QueueSetHandle_t queueSetHandle); + + /** + * @brief get dashboard queue set handle + */ + QueueSetHandle_t getDashboardQueueSetHandle() const; + +private: + /// Message Buffer reference for incoming messages from Tier1competetion + osextension::MessageBufferReference* _tier1competetionToDashboardMessageBufferReference = nullptr; + + /// Message Buffer reference for incoming messages from Tier2competetion + osextension::MessageBufferReference* _tier2competetionToDashboardMessageBufferReference = nullptr; + + /// Outgoing Message Buffer reference to the Tier1competetion + osextension::MessageBufferReference* _dashboardToTier1competetionMessageBufferReference = nullptr; + + /// Outgoing Message Buffer reference to the Tier2competetion + osextension::MessageBufferReference* _dashboardToTier2competetionMessageBufferReference = nullptr; + + /// FreeRTOS queue set handle + QueueSetHandle_t _queueSetHandle = nullptr; +}; + +} // namespace dashboard + +#endif // DASHBOARD_DASHBOARD_HPP diff --git a/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Task.hpp b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Task.hpp new file mode 100644 index 0000000..2d0dc8f --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/Task.hpp @@ -0,0 +1,125 @@ +/** + * @file include/Dashboard/Dashboard/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef DASHBOARD_TASK_HPP +#define DASHBOARD_TASK_HPP + +#include +#include +// #include +#include + +#include +#include + +#include + +/// @brief dashboard task namespace +namespace dashboard { + +/** + * @brief dashboard class + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor + * @param[in] taskConfiguration Task configuration + */ + explicit Task(const testapprtos::TaskConfiguration &taskConfiguration); + + /** + * @brief Run function + */ + void run() override; + + /// @copydoc Task::initializeTask() + void initialize() override { initializeTask(); } + + /// @copydoc + /// TaskInterface::getDashboardToTier2competetionMessageBufferReference + osextension::MessageBufferReference + getDashboardToTier2competetionMessageBufferReference() const override; + + /// @copydoc + /// TaskInterface::getTier2competetionToDashboardMessageBufferReference + osextension::MessageBufferReference + getTier2competetionToDashboardMessageBufferReference() const override; + +private: + Task() = delete; /**< Remove empty (default) constructor */ + Task(const Task &) = delete; /**< Remove copy Constructor */ + Task(const Task &&) = delete; /**< Remove assign Constructor */ + const Task & + operator=(const Task &) = delete; /**< Remove assign Operation @return NULL */ + const Task & + operator=(Task &&) = delete; /**< Remove move Operation @return NULL */ + + /// Size of message buffer in bytes for incoming message bus messages + static constexpr size_t IncomingMessageBufferSize = 265u; // 265 bytes + + /// Maximum number of messages in message buffer for incoming message bus + /// messages + static constexpr size_t IncomingMessageBufferMaximumNumberOfMessages = 10u; + + /// Size of interrupt queue (= number of buttons) + static constexpr size_t sizeOfInterruptQueue = 1u; + + /** + * @brief Length of QueueSet inside TaskWakeUp object (number of events which + * has the QueueSet to handle at all) + * @note Must be set to the sum of the lengths of the queues added to the set, + * where binary semaphores and mutexes have a length of 1, and counting + * semaphores have a length set by their maximum count value + */ + static constexpr auto numberOfEventsInQueueSet = + IncomingMessageBufferMaximumNumberOfMessages + sizeOfInterruptQueue + + testapprtos::maximumNumberOfEvents; + + /// Message buffer for incoming messages from TestEnv + osextension::MessageBuffer + _tier1competetionToDashboardMessageBuffer{}; + + /// Message buffer for incoming messages from Tier2competetion + osextension::MessageBuffer + _tier2competetionToDashboardMessageBuffer{}; + + /// Outgoing message buffer to the TestEnv + osextension::MessageBuffer + _dashboardToTier1competetionMessageBuffer{}; + + /// Outgoing message buffer to the Tier2competetion + osextension::MessageBuffer + _dashboardToTier2competetionMessageBuffer{}; + + /// Handle type of FreeRTOS + using HandleType = void *; + + /// FreeRTOS queue set struct + StaticQueue_t _staticQueue{}; + + /// Queue set storage area + std::array + _queueSetStorage = {0u}; + + /// Dashboard Instance + Dashboard _dashboard{}; +}; + +} // namespace dashboard + +#endif // DASHBOARD_TASK_HPP diff --git a/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/TaskInterface.hpp b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/TaskInterface.hpp new file mode 100644 index 0000000..fb90b18 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Dashboard/Dashboard/TaskInterface.hpp @@ -0,0 +1,69 @@ +/** + * @file include/Dashboard/Dashboard/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef DASHBOARD_TASKINTERFACE_HPP +#define DASHBOARD_TASKINTERFACE_HPP + +#include +#include + +/// @brief dashboard task namespace +namespace dashboard { + +/** + * @brief: Task Interface + */ +class TaskInterface { +public: + /// Deleted copy constructor + TaskInterface(const TaskInterface&) = delete; + + /// Deleted move constructor + TaskInterface(const TaskInterface&&) = delete; + + /// Deleted copy assignment operator + const TaskInterface& operator=(const TaskInterface&) = delete; + + /// Deleted move assignment operator + const TaskInterface& operator=(TaskInterface&&) = delete; + + /** + * @brief Get reference to _tier2competetionToDashboardMessageBuffer + * @return MessageBufferReference to _tier2competetionToDashboardMessageBuffer + */ + virtual osextension::MessageBufferReference getTier2competetionToDashboardMessageBufferReference() const = 0; + + /** + * @brief Get reference to _dashboardToTier2competetionMessageBuffer + * @return MessageBufferReference to _dashboardToTier2competetionMessageBuffer + */ + virtual osextension::MessageBufferReference getDashboardToTier2competetionMessageBufferReference() const = 0; + + /// Initialize + virtual void initialize() = 0; + +protected: + /// Default constructor + TaskInterface() = default; + + /// Destructor + virtual ~TaskInterface() = default; +}; + +/// Global interface to task +extern TaskInterface& taskInterface; + +} // namespace dashboard + +#endif // DASHBOARD_TASKINTERFACE_HPP diff --git a/IntegrationTests/DemoApplication/include/DemoappTypes/DemoappTypes/DemoappTypes.hpp b/IntegrationTests/DemoApplication/include/DemoappTypes/DemoappTypes/DemoappTypes.hpp new file mode 100644 index 0000000..0f594a0 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/DemoappTypes/DemoappTypes/DemoappTypes.hpp @@ -0,0 +1,91 @@ +#ifndef DEMOAPPTYPES_DEMOAPPTYPES_HPP +#define DEMOAPPTYPES_DEMOAPPTYPES_HPP + +#include +#include +#include +#include + +namespace demoapptypes +{ + constexpr uint8_t LANES = 8u; + + enum class Tier : uint8_t + { + TierA = 0, + TierB + }; + + enum class Style : uint8_t + { + FREESTYLE = 0, + BACKSTROKE, + BUTTERFLY, + BREASTSTROKE + }; + + enum class Laps : uint8_t + { + _50M = 0, + _100M, + _200M, + _400M + }; + + struct Round + { + Laps laps; + Style style; + }; + + struct Athlete + { + // public: + // Athlete(std::string const &l_name, float l_time) : + // name(l_name), time(l_time) {} + // virtual ~Athlete() = default; + + // float lapsTime() { return time; } + // uint16_t AddPoints(uint16_t p) { return p + points; } + + // private: + const char* name; + float time; + uint16_t points; + }; + + class Race + { + + public: + Race(std::vector vec) : athletes(std::move(vec)){}; + + void operator+=(Athlete athlete) + { + athletes.push_back(athlete); + } + + const std::vector& getAthletes() const{ return athletes; } + + Round round; + private: + std::vector athletes; + + + Race(const Race &) = delete; + Race &operator=(Race) = delete; + }; + + // Race::Race(std::vector vec) : athletes(std::move(vec)){} + + +struct CompetetionMessageT +{ + Tier competerionTier; + Round round; + std::array todashboard; +}; + +} // demoapptypes + +#endif // DEMOAPPTYPES_DEMOAPPTYPES_HPP diff --git a/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Task.hpp b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Task.hpp new file mode 100644 index 0000000..64fd7b3 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Task.hpp @@ -0,0 +1,89 @@ +/** + * @file include/Tier1competetion/Tier1competetion/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TIER1COMPETETION_TASK_HPP +#define TIER1COMPETETION_TASK_HPP + +#include +#include +// #include +// #include +#include +#include +#include +#include + +/** + * @brief: tier1competetion namespace. + */ +namespace tier1competetion { +/** + * @brief Tier2competetion class + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor + * @param[in] taskConfiguration Task configuration + */ + explicit Task(const testapprtos::TaskConfiguration &taskConfiguration); + + /** + * @brief Register MessageBufferReference from Dashboard to Tier1competetion. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerDashboardToTier1competetionMessageBufferReference( + osextension::MessageBufferReference &messageBufferReference) override; + + /** + * @brief Register MessageBufferReference from Tier1competetion to Dashboard. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTier1competetionToDashboardMessageBufferReference( + osextension::MessageBufferReference &messageBufferReference) override; + + /** + * @brief Run function. + */ + void run() override; + + /** + * @brief Initialize. + */ + /// @copydoc Task::initializeTask() + void initialize() override { initializeTask(); } + +private: + Task() = delete; /**< Remove empty (default) constructor. */ + Task(const Task &) = delete; /**< Remove copy Constructor. */ + Task(const Task &&) = delete; /**< Remove assign Constructor. */ + const Task &operator=(const Task &) = + delete; /**< Remove assign Operation @return NULL. */ + const Task & + operator=(Task &&) = delete; /**< Remove move Operation @return NULL. */ + Tier1competetion tier1competetion; /**< Tier1competetion instance. */ + /** + * @brief Semaphore to signal, that the message buffer from TestRunnter to + * Tier1competetion was registered. + */ + osextension::Semaphore _dashboardToTier1competetionMessageBufferAdded{1, 0}; + /** + * @brief Semaphore to signal, that the message buffer from Tier1competetion + * to TestRunnter was registered. + */ + osextension::Semaphore _tier1competetionToDashboardMessageBufferAdded{1, 0}; +}; + +} // namespace tier1competetion + +#endif // TIER1COMPETETION_TASK_HPP diff --git a/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/TaskInterface.hpp b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/TaskInterface.hpp new file mode 100644 index 0000000..c83b9ab --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/TaskInterface.hpp @@ -0,0 +1,75 @@ +/** + * @file include/TestEnvironment/TestEnvironment/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TIER1COMPETETION_TASKINTERFACE_HPP +#define TIER1COMPETETION_TASKINTERFACE_HPP + +#include +#include + +#include + +/** + * @brief: tier1competetion namespace. + */ +namespace tier1competetion { + +/** + * @brief: Task Interface + */ +class TaskInterface { +public: + /// Deleted copy constructor + TaskInterface(const TaskInterface&) = delete; + + /// Deleted move constructor + TaskInterface(const TaskInterface&&) = delete; + + /// Deleted copy assignment operator + const TaskInterface& operator=(const TaskInterface&) = delete; + + /// Deleted move assignment operator + const TaskInterface& operator=(TaskInterface&&) = delete; + + /** + * @brief Register MessageBufferReference from Dashboar to TestEnvironment. + * @param[in] messageBufferReference Reference to the message buffer. + */ + virtual void registerDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) = 0; + + /** + * @brief Register MessageBufferReference from TestEnvironment to Dashboar. + * @param[in] messageBufferReference Reference to the message buffer. + */ + virtual void registerTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) = 0; + + /// Initialize + virtual void initialize() = 0; + + Tier1competetion tier1competetion; /**< Tier1competetion instance. */ + +protected: + /// Default constructor + TaskInterface() = default; + + /// Destructor + virtual ~TaskInterface() = default; +}; + +/// Global interface to task +extern TaskInterface& taskInterface; + +} // namespace tier1competetion. + +#endif // TIER1COMPETETION_TASKINTERFACE_HPP diff --git a/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Tier1competetion.hpp b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Tier1competetion.hpp new file mode 100644 index 0000000..8e5e8ac --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier1competetion/Tier1competetion/Tier1competetion.hpp @@ -0,0 +1,74 @@ +/** + * @file include/Tier1competetion/Tier1competetion/Tier1competetion.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TIER1COMPETETION_TIER1COMPETETION_HPP +#define TIER1COMPETETION_TIER1COMPETETION_HPP + +#include +#include +/** + * @brief: tier1competetion namespace. + */ +namespace tier1competetion { + +/** + * @brief: Tier1competetion class. + */ +class Tier1competetion { +public: + /** + * @brief Run one cycle. + */ + void runOneCycle(); + + /** + * @brief Register MessageBufferReference from Dashboard to Tier1competetion. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference& messageBufferReference); + + /** + * @brief Register MessageBufferReference from Tier1competetion To Dashboard. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& messageBufferReference); + + /** + * @brief onecycle method is invoked in run method of task mechanism + * + */ + void oneCycle(); + + void StartRace(const demoapptypes::Race& l_race); +private: + /** + * @brief MessageBufferReference from Dashboard to Tier1competetion. + */ + osextension::MessageBufferReference* _dashboardToTier1competetionMessageBufferReference = nullptr; + + /** + * @brief MessageBufferReference from Tier1competetion to Dashboard. + */ + osextension::MessageBufferReference* _tier1competetionToDashboardMessageBufferReference = nullptr; + + std::array currentRace; + demoapptypes::Round currentRound{}; + uint8_t results = 0; + // demoapptypes::Race currentRace; + const uint8_t tier = 1; +}; + +} // namespace tier1competetion + +#endif // TIER1COMPETETION_TIER1COMPETETION_HPP diff --git a/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Task.hpp b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Task.hpp new file mode 100644 index 0000000..33b3a55 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Task.hpp @@ -0,0 +1,76 @@ +/** + * @file include/TIER2COMPETETION/TIER2COMPETETION/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TIER2COMPETETION_TASK_HPP +#define TIER2COMPETETION_TASK_HPP +#include "Dashboard/TaskInterface.hpp" +#include "Tier2competetion/TaskInterface.hpp" +#include "Tier2competetion/Tier2competetion.hpp" +// #include +#include + +namespace tier2competetion { +/** + * @brief Tier2competetion class + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor + * @param[in] taskConfiguration Task configuration + */ + explicit Task(const testapprtos::TaskConfiguration &taskConfiguration); + + /** + * @brief run function + */ + void run() override; + + /// @copydoc Task::initializeTask() + void initialize() override { initializeTask(); } + +private: + Task() = delete; /**< Remove empty (default) constructor */ + Task(const Task &) = delete; /**< Remove copy Constructor */ + Task(const Task &&) = delete; /**< Remove assign Constructor */ + const Task & + operator=(const Task &) = delete; /**< Remove assign Operation @return NULL */ + const Task & + operator=(Task &&) = delete; /**< Remove move Operation @return NULL */ + + /// Size of message buffer in bytes for incoming message bus messages + static constexpr size_t IncomingMessageBufferSize = 265u; + + /// Maximum number of messages in message buffer for incoming message bus + /// messages + static constexpr size_t IncomingMessageBufferMaximumNumberOfMessages = 1u; + + /// Message Buffer reference for incoming messages from Dashboard + osextension::MessageBufferReference _dashboardToTier2competetionMessageBuffer; + + /// Message Buffer reference for Outgoing messages to Dashboard + osextension::MessageBufferReference _tier2competetionToDashboardMessageBuffer; + + /// Semaphore + osextension::Semaphore _TCCInterfacerReferencePairAdded{1, 0}; + + /// Tier2competetion instance + Tier2competetion _tier2competetion{}; + + /// Task delay time + TickType_t _blockingTime{0}; +}; + +} // namespace tier2competetion + +#endif diff --git a/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/TaskInterface.hpp b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/TaskInterface.hpp new file mode 100644 index 0000000..5c75304 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/TaskInterface.hpp @@ -0,0 +1,55 @@ +/** + * @file include/TIER2COMPETETION/TIER2COMPETETION/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TIER2COMPETETION_TIER2COMPETETIONINTERFACE_HPP +#define TIER2COMPETETION_TIER2COMPETETIONINTERFACE_HPP + +#include +#include + +/// @brief testrunner task namespace +namespace tier2competetion { +/** + * @brief: Task Interface + */ +class TaskInterface { +public: + /// Deleted copy constructor + TaskInterface(const TaskInterface&) = delete; + + /// Deleted move constructor + TaskInterface(const TaskInterface&&) = delete; + + /// Deleted copy assignment operator + const TaskInterface& operator=(const TaskInterface&) = delete; + + /// Deleted move assignment operator + const TaskInterface& operator=(TaskInterface&&) = delete; + + /// Initialize + virtual void initialize() = 0; +protected: + /// Default constructor + TaskInterface() = default; + + /// Destructor + virtual ~TaskInterface() = default; +}; + +/// Global interface to task +extern TaskInterface& taskInterface; + +} // namespace tier2competetion + +#endif // TIER2COMPETETION_TIER2COMPETETIONINTERFACE_HPP diff --git a/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Tier2competetion.hpp b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Tier2competetion.hpp new file mode 100644 index 0000000..d9d9ff7 --- /dev/null +++ b/IntegrationTests/DemoApplication/include/Tier2competetion/Tier2competetion/Tier2competetion.hpp @@ -0,0 +1,69 @@ +/** + * @file include/Tier2competetion/Tier2competetion/Tier2competetion.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TIER2COMPETETION_HPP +#define Tier2competetion_HPP +#include +#include +#include + +namespace tier2competetion +{ + class Tier2competetion + { + public: + /// Constructor. + Tier2competetion() = default; + + ///default constructor + ~Tier2competetion() = default; + + /// Copy constructor. + Tier2competetion(Tier2competetion const &) = delete; + + /// Move constructor. + Tier2competetion(Tier2competetion &&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + Tier2competetion &operator=(Tier2competetion const &) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + Tier2competetion &operator=(Tier2competetion &&) = delete; + + /** + * @brief set _DashboardToTier2competetionMessageBuffer Reference + */ + void setTier2competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _DashboardToTier2competetionMessageBuffer Reference + */ + void setDashboardTier2competetionMessageBufferReference(osextension::MessageBufferReference& mbf); + /** + * @brief onecycle method is invoked in run method of task mechanism + * + */ + void oneCycle(); + + private: + /// Message Buffer reference for incoming messages from Dashboard + osextension::MessageBufferReference* _dashboardToTier2competetionMessageBuffer = nullptr; + + /// Message Buffer reference for Outgoing messages from Dashboard + osextension::MessageBufferReference* _tier2competetionToDashboardMessageBuffer = nullptr; + }; + +} // namespace tier2competetion +#endif diff --git a/IntegrationTests/DemoApplication/src/Dashboard/Dashboard.cpp b/IntegrationTests/DemoApplication/src/Dashboard/Dashboard.cpp new file mode 100644 index 0000000..586fa27 --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Dashboard/Dashboard.cpp @@ -0,0 +1,40 @@ +/** + * @file src/Dashboard/Dashboard/Dashboard.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#include + +namespace dashboard +{ + /// set dashboard message Buffer references + void Dashboard::setDashboardToTier2competetionMessageBufferReference(osextension::MessageBufferReference& mbf) { + _dashboardToTier2competetionMessageBufferReference = &mbf; + } + void Dashboard::setTier2competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf) { + _tier2competetionToDashboardMessageBufferReference = &mbf; + } + void Dashboard::setDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference& mbf) { + _dashboardToTier1competetionMessageBufferReference = &mbf; + } + void Dashboard::setTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf) { + _tier1competetionToDashboardMessageBufferReference = &mbf; + } + + /// dashboard queue set setter and getter + void Dashboard::setDashboardQueueSetHandle(QueueSetHandle_t queueSetHandle) { + _queueSetHandle = queueSetHandle; + } + QueueSetHandle_t Dashboard::getDashboardQueueSetHandle() const { + return _queueSetHandle; + } +} // namespace testrunner + diff --git a/IntegrationTests/DemoApplication/src/Dashboard/Task.cpp b/IntegrationTests/DemoApplication/src/Dashboard/Task.cpp new file mode 100644 index 0000000..1c1d4fd --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Dashboard/Task.cpp @@ -0,0 +1,85 @@ +/** + * @file src/Dashboard/Task.cpp + * @brief Dashboard Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +#include +#include + +namespace dashboard +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[ taskStackSize ]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "dashboard", + 4u + }; + + /// Creation of global task object + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) + { + // set dashboard MessageBuffer references + _dashboard.setDashboardToTier2competetionMessageBufferReference(_dashboardToTier2competetionMessageBuffer); + _dashboard.setTier2competetionToDashboardMessageBufferReference(_tier2competetionToDashboardMessageBuffer); + _dashboard.setDashboardToTier1competetionMessageBufferReference(_dashboardToTier1competetionMessageBuffer); + _dashboard.setTier1competetionToDashboardMessageBufferReference(_tier1competetionToDashboardMessageBuffer); + + // Create static QueueSet + _dashboard.setDashboardQueueSetHandle(osabstraction::queueCreateSetStatic(numberOfEventsInQueueSet, _queueSetStorage.data(), &_staticQueue)); + + // Add all semaphores to the QueueSet + if (xQueueAddToSet(static_cast(_tier1competetionToDashboardMessageBuffer.getHandleForQueueSet()), _dashboard.getDashboardQueueSetHandle()) != pdTRUE) + { + assert(false); + } + if (xQueueAddToSet(static_cast(_tier2competetionToDashboardMessageBuffer.getHandleForQueueSet()), _dashboard.getDashboardQueueSetHandle()) != pdTRUE) + { + assert(false); + } + } + + // Get Reference of the incoming message buffer from Tier2competetionoordinator + osextension::MessageBufferReference Task::getTier2competetionToDashboardMessageBufferReference() const + { + return _tier2competetionToDashboardMessageBuffer.getReference(); + } + + osextension::MessageBufferReference Task::getDashboardToTier2competetionMessageBufferReference() const + { + return _dashboardToTier2competetionMessageBuffer.getReference(); + } + + /** + * @brief run function + */ + void Task::run() + { + // Register the message buffer reference to _dashboardToTier1competetionMessageBuffer + tier1competetion::taskInterface.registerDashboardToTier1competetionMessageBufferReference(_dashboardToTier1competetionMessageBuffer); + + // Register the message buffer reference to _tier1competetionToDashboardMessageBuffer + tier1competetion::taskInterface.registerTier1competetionToDashboardMessageBufferReference(_tier1competetionToDashboardMessageBuffer); + + QueueSetMemberHandle_t queueSetMemberHandle = nullptr; + + while (true) + { + _dashboard.oneCycle(queueSetMemberHandle); + } + } // Task::Run +} // namespace testrunner diff --git a/IntegrationTests/DemoApplication/src/Tier1competetion/Task.cpp b/IntegrationTests/DemoApplication/src/Tier1competetion/Task.cpp new file mode 100644 index 0000000..e5d9816 --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Tier1competetion/Task.cpp @@ -0,0 +1,64 @@ +/** + * @file src/Tier1competetion/Task.cpp + * @brief Tier1competetion Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +/** + * @brief: Tier1competetion namespace. + */ +namespace tier1competetion +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[ taskStackSize ]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "tier1competetion", + 4u + }; + + /// Creation of global task object + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) {} + + void Task::registerDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference &messageBufferReference) + { + tier1competetion.registerDashboardToTier1competetionMessageBufferReference(messageBufferReference); + _dashboardToTier1competetionMessageBufferAdded.signal(); + } + + void Task::registerTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference &messageBufferReference) + { + tier1competetion.registerTier1competetionToDashboardMessageBufferReference(messageBufferReference); + _tier1competetionToDashboardMessageBufferAdded.signal(); + } + + void Task::run() + { + // Wait until the input and output message buffer were registered. + _dashboardToTier1competetionMessageBufferAdded.wait(portMAX_DELAY); + _tier1competetionToDashboardMessageBufferAdded.wait(portMAX_DELAY); + while (true) + { + tier1competetion.runOneCycle(); + } + } + +} // namespace tier1competetion diff --git a/IntegrationTests/DemoApplication/src/Tier1competetion/Tier1competetion.cpp b/IntegrationTests/DemoApplication/src/Tier1competetion/Tier1competetion.cpp new file mode 100644 index 0000000..7aa0a52 --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Tier1competetion/Tier1competetion.cpp @@ -0,0 +1,52 @@ +/** + * @file src/Tier1competetion/Tier1competetion.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include "Tier1competetion/TaskInterface.hpp" +#include + + +/** + * @brief: tier1competetion namespace. + */ +namespace tier1competetion { + +void Tier1competetion::runOneCycle() { +} + +void Tier1competetion::registerDashboardToTier1competetionMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) { + _dashboardToTier1competetionMessageBufferReference = &messageBufferReference; +} + +void Tier1competetion::registerTier1competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) { + _tier1competetionToDashboardMessageBufferReference = &messageBufferReference; +} + +void Tier1competetion::oneCycle() { + if(results =! 0) + { + demoapptypes::CompetetionMessageT messageToDashboard{demoapptypes::Tier::TierA, currentRound, currentRace}; + _tier1competetionToDashboardMessageBufferReference->send(static_cast(&messageToDashboard), sizeof(messageToDashboard), portMAX_DELAY); + results = 0; + } + +} + +void Tier1competetion::StartRace(const demoapptypes::Race& l_race){ + std::vector vec = l_race.getAthletes(); + std::copy_n(l_race.getAthletes().begin(), demoapptypes::LANES, currentRace.begin()); + currentRound.laps = l_race.round.laps; + currentRound.style = l_race.round.style; + results = 1; +} +} // namespace tier1competetion diff --git a/IntegrationTests/DemoApplication/src/Tier2competetion/Task.cpp b/IntegrationTests/DemoApplication/src/Tier2competetion/Task.cpp new file mode 100644 index 0000000..203035e --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Tier2competetion/Task.cpp @@ -0,0 +1,56 @@ +/** + * @file src/Tier2competetion/Task.cpp + * @brief Tier2competetion Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +namespace tier2competetion +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[ taskStackSize ]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "tier2competetion", + 4u + }; + + /// Creation of global task object + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) + { + // register callbacks + _tier2competetion.setTier2competetionToDashboardMessageBufferReference(_tier2competetionToDashboardMessageBuffer); + _tier2competetion.setDashboardTier2competetionMessageBufferReference(_dashboardToTier2competetionMessageBuffer); + } + + /** + * @brief run function + */ + void Task::run() + { + // Insert the message buffer reference to TestCoordinator + _dashboardToTier2competetionMessageBuffer = dashboard::taskInterface.getDashboardToTier2competetionMessageBufferReference(); + + // Register the message buffer reference of the Tier2competetion to Dashboard task + _tier2competetionToDashboardMessageBuffer = dashboard::taskInterface.getTier2competetionToDashboardMessageBufferReference(); + + _tier2competetion.oneCycle(); + } +} // tier2competetion diff --git a/IntegrationTests/DemoApplication/src/Tier2competetion/Tier2competetion.cpp b/IntegrationTests/DemoApplication/src/Tier2competetion/Tier2competetion.cpp new file mode 100644 index 0000000..5c0164b --- /dev/null +++ b/IntegrationTests/DemoApplication/src/Tier2competetion/Tier2competetion.cpp @@ -0,0 +1,37 @@ +/** + * @file src/Tier2competetion/Tier2competetion/Tier2competetion.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#include "Tier2competetion/Tier2competetion.hpp" +#include "Tier2competetion/TaskInterface.hpp" +#include +#include +#include + +namespace tier2competetion +{ + static bool receiveNextMessage = true; + + /// set tPC message Buffer references outgoing + void Tier2competetion::setTier2competetionToDashboardMessageBufferReference(osextension::MessageBufferReference& mbf) { + _tier2competetionToDashboardMessageBuffer = &mbf; + } + + /// set tPC message Buffer references incoming + void Tier2competetion::setDashboardTier2competetionMessageBufferReference(osextension::MessageBufferReference& mbf) { + _dashboardToTier2competetionMessageBuffer = &mbf; + } + + void Tier2competetion::oneCycle() { + } + +} // namespace tpcprotocol diff --git a/IntegrationTests/test_suite/README.md b/IntegrationTests/test_suite/README.md new file mode 100644 index 0000000..4589cc8 --- /dev/null +++ b/IntegrationTests/test_suite/README.md @@ -0,0 +1,8 @@ +# IntegrationTests + +The tests defined here maps to either +## test_suite_fdx_lauterbach : https://github.com/eclipse/kiso-testing/blob/master/examples/fdx_lauterbach.yaml or + +## test_suite_rtt : https://github.com/eclipse/kiso-testing/blob/master/examples/j_link_rtt_segger.yaml + +regarding on which tests (channel in use) that were invoked from host (pykiso) side. diff --git a/IntegrationTests/test_suite/testSuite1.cpp b/IntegrationTests/test_suite/testSuite1.cpp new file mode 100644 index 0000000..cb31e66 --- /dev/null +++ b/IntegrationTests/test_suite/testSuite1.cpp @@ -0,0 +1,105 @@ +/** + * @file examples/test_suite/test_suite_1.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include +#include + +// Test Suite DummyTestSuite1 +DECLARE_TESTSUITE(DummyTestSuite1, 1) +testapp::ErrCode doSuiteSetup() override { + + logging.logInfo("TestSuite 1 - 1st doSuiteSetup Hello TestApp!"); + logging.logDebug("TestSuite 1 - 2nd Log message"); + logging.logWarning("TestSuite 1 - 3rd Log message"); + assertion.TA_ASSERT_TRUE(true); + // uncomment this to make the test fail + // logging.logError("TestSuite 1 - error Log-message"); + // logging.logCritical("TestSuite 1 - critical error Log-message"); + // logging.logInfo("TestSuite 1 doSuiteSetup should fail"); + + return returnReportAndReset(); +} + +testapp::ErrCode doSuiteTearDown() override { + + logging.logInfo("TestSuite 1 doSuiteTearDown"); + assertion.TA_ASSERT_TRUE(true); + assertion.TA_ASSERT_FALSE(false); + assertion.TA_ASSERT_EQ(5,5); + assertion.TA_ASSERT_NE(5,4); + + logging.logInfo("TestSuite 1 doSuiteTearDown should pass successfully"); + + return returnReportAndReset(); +} +END_TESTSUITE(DummyTestSuite1) + +// Tast Suite : DummyTestSuite1 +// Test Case : TestCase1 +DECLARE_TESTCASE(DummyTestSuite1, TestCase1, 1) + +testapp::ErrCode doSetup() override { + + logging.logInfo("TestCase 1 doSetup"); + assertion.TA_ASSERT_LT(5,6); + assertion.TA_ASSERT_LE(6,6); + assertion.TA_ASSERT_STREQ("message1", "message1"); + logging.logInfo("TestCase 1 doSetup should pass successfully"); + + return returnReportAndReset(); +} + +testapp::ErrCode runTest() override { + + logging.logInfo("TestCase 1 runTest"); + assertion.TA_ASSERT_GT(7,6); + assertion.TA_ASSERT_GE(7,7); + logging.logInfo("TestCase 1 runTest should pass successfully"); + + return returnReportAndReset(); +} + +testapp::ErrCode doTearDown() override { + logging.logInfo("TestCase 1 doTearDown"); + + assertion.TA_ASSERT_STREQ("message1", "message2"); + logging.logInfo("TestCase 1 doTearDown should fail"); + + return returnReportAndReset(); +} + +END_TESTCASE(TestCase1) + +// Test Suite : DummyTestSuite1 +// Test Case : TestCase2 +DECLARE_TESTCASE(DummyTestSuite1, TestCase2, 2) + +testapp::ErrCode doSetup() override { + logging.logInfo("TestCase 2 Starting Mars"); + return returnReportAndReset(); +} + +testapp::ErrCode runTest() override { + logging.logInfo("TestCase 2 Hello Mars"); + return returnReportAndReset(); +} + +testapp::ErrCode doTearDown() override { + logging.logInfo("TestCase 2 Ending Mars"); + return returnReportAndReset(); +} + +END_TESTCASE(TestCase2) diff --git a/OSAdapter/include/Observer/Observer/Observer.hpp b/OSAdapter/include/Observer/Observer/Observer.hpp new file mode 100644 index 0000000..04d3289 --- /dev/null +++ b/OSAdapter/include/Observer/Observer/Observer.hpp @@ -0,0 +1,132 @@ +/** + * @file include/Observer/Observer/Observer.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef SYSTEMCOMMON_OBSERVABLE_HPP +#define SYSTEMCOMMON_OBSERVABLE_HPP + +namespace observer { + +/** + * Observable implementing the generic observer design pattern for + * single observers. + * + * The observer pattern allows to notify changes of this observable + * object's state to external observers by means of event notifications. + * Doing so event processing can be delegated to an observer which needs + * not to be known to this observable object and so effectively decouples + * event detection and emission from event processing. + * + * This low-complexity implementation supports only the registration of + * a single observer to each observable object as opposed to most common + * implementation as most use cases almost always require only a single + * observer. In cases where multiple observers are needed, a dispatching + * observer can easily be implemented. + * + * @tparam ObserverType Type of the observer defining the observable + * event types + */ +template +class Observable { +public: + /// @return true if there is an observer attached + bool hasObserver() { + return _observer != nullptr; + } + + /** + * Set the observer to notify in case of an event + * + * @param observer Observing object + */ + void setObserver(ObserverType& observer) { + _observer = &observer; + } + + /// Removes the current observer if any + void removeObserver() { + _observer = nullptr; + } + +protected: + /// Notification handler without arguments + void notifyObserver() { + if (hasObserver()) { + _observer->onNotify(); + } + } + + /** + * Generic notification of arbitrarily typed events + * + * @tparam Generic event type of the event to notify + * @param event Event to notify + */ + template + void notifyObserver(Event event) { + if (hasObserver()) { + _observer->onNotify(event); + } + } + +private: + /// observer receiving notifications (onNotify(...)) + ObserverType* _observer{nullptr}; +}; + +/** + * Generic observer supporting multiple event notification types + * + * @tparam Event1 + * @tparam Events + */ +template +/// @cond IGNORE +class Observer : Observer, Observer { + /// @endcond +public: + /// declare onNotify from base classes for single argument + using Observer::onNotify; + + /// declare onNotify from base classes for multiple arguments + using Observer::onNotify; +}; + +/** + * Generic observer for a single event notification type + * + * @tparam Event Event type to notify + */ +template +class Observer { +public: + /** + * Notification handler for the given event type + * + * Defaults to an empty implementation effectively ignoring + * this given type of events. + */ + virtual void onNotify(Event){}; +}; + +/** + * Specialized observer for no-argument notifications. + */ +template <> +class Observer { +public: + /// no-argument event notification + virtual void onNotify(){}; +}; + +} // namespace observer + +#endif //SYSTEMCOMMON_OBSERVABLE_HPP diff --git a/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstraction.hpp b/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstraction.hpp new file mode 100644 index 0000000..5278e0d --- /dev/null +++ b/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstraction.hpp @@ -0,0 +1,575 @@ +/** + * @file include/OsAbstraction/OsAbstraction/OsAbstraction.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSABSTRACTION_OSABSTRACTION_HPP +#define OSABSTRACTION_OSABSTRACTION_HPP + +// the define below will be passed to the compiler by cmake +// dependend on selected OsAdapter +// (DISABLE_FREERTOS is always set when FreeRtosAdapter is NOT selected) +#ifdef DISABLE_FREERTOS +#include +#else +#include +#include +#include +#include +#include +#endif + +#include + +#ifdef __cplusplus +/// @brief OS Abstraction +namespace osabstraction { +extern "C" { +#endif // __cplusplus + +/* task.h abstraction */ + +/** + * @brief See FreeRTOS task.h + * @param xTicksToDelay Ticks the calling task should block + */ +void taskDelay(TickType_t xTicksToDelay); + +/** + * @brief See FreeRTOS task.h + * @param xTaskToQuery Task to query + * @return Task name + */ +char* taskGetName(TaskHandle_t xTaskToQuery); + +/** + * @brief See FreeRTOS task.h + * @return current Task handle + */ +TaskHandle_t taskGetCurrentTaskHandle(void); + +/** + * @brief See FreeRTOS task.h + * @return Tick count + */ +TickType_t taskGetTickCount(void); + +/** + * @brief See FreeRTOS task.h + * @return Tick count + */ +TickType_t taskGetTickCountFromISR(void); + +/** + * @brief See FreeRTOS task.h + * @return Scheduler state + */ +BaseType_t taskGetSchedulerState(void); + +/** + * @brief See FreeRTOS task.h + * @param xTaskToSuspend Handle of the task that shall be suspended. + */ +void taskSuspend(TaskHandle_t xTaskToSuspend); + +/* queue.h abstraction QueueSets */ + +/** + * @brief Creates a FreeRTOS QueueSet with static memory + * + * For further details see FreeRTOS queue.h xQueueCreateSet() + * Note: Works just like a regular QueueSet with dynamic memory allocation + * + * @param uxEventQueueLength max amount of QueueSetMemberHandle_t in that QueueSet + * @param pucQueueSetStorage static storage area of that QueueSet (should have sizeof(void*) * uxEventQueueLength in bytes) + * @param pxStaticQueueSet StaticQueue_t struct defined by FreeRTOS + * @return QueueSetHandle_t to be used by client + */ +QueueSetHandle_t queueCreateSetStatic(UBaseType_t uxEventQueueLength, uint8_t* pucQueueSetStorage, StaticQueue_t* pxStaticQueueSet); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueueSet + * @param xBlockTimeTicks + * @return + */ +QueueSetMemberHandle_t queueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueueOrSemaphore + * @param xQueueSet + * @return + */ +BaseType_t queueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueueOrSemaphore + * @param xQueueSet + * @return + */ +BaseType_t queueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet); + +/* queue.h abstraction */ + +/** + * @brief See FreeRTOS queue.h + * + * @param uxQueueLength + * @param uxItemSize + * @param pucQueueStorage + * @param pxStaticQueue + * @param ucQueueType + * @return + */ +QueueHandle_t queueGenericCreateStatic(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t* pucQueueStorage, //NOLINT(readability-avoid-const-params-in-decls) + StaticQueue_t* pxStaticQueue, const uint8_t ucQueueType); //NOLINT(readability-avoid-const-params-in-decls) +/** + * @brief See FreeRTOS queue.h + * + * @param uxQueueLength + * @param uxItemSize + * @param pucQueueStorage + * @param pxQueueBuffer + * @return + */ +QueueHandle_t queueCreateStatic(UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t* pucQueueStorage, StaticQueue_t* pxQueueBuffer); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param name + */ +void queueRegister(QueueHandle_t xQueue, const char* name); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue queue to unregister + */ +void queueUnregister(QueueHandle_t xQueue); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvItemToQueue + * @param xTicksToWait + * @param xCopyPosition + * @return + */ +BaseType_t queueGenericSend(QueueHandle_t xQueue, const void* const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition); //NOLINT(readability-avoid-const-params-in-decls) + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvItemToQueue + * @param xTicksToWait + * @return + */ +BaseType_t queueSend(QueueHandle_t xQueue, const void* pvItemToQueue, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvItemToQueue + * @param pxHigherPriorityTaskWoken + * @param xCopyPosition + * @return + */ +BaseType_t queueGenericSendFromISR(QueueHandle_t xQueue, const void* const pvItemToQueue, BaseType_t* const pxHigherPriorityTaskWoken, //NOLINT(readability-avoid-const-params-in-decls) + const BaseType_t xCopyPosition); //NOLINT(readability-avoid-const-params-in-decls) + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvItemToQueue + * @param pxHigherPriorityTaskWoken + * @return + */ +BaseType_t queueSendFromISR(QueueHandle_t xQueue, const void* pvItemToQueue, BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvBuffer + * @param xTicksToWait + * @return + */ +BaseType_t queueReceive(QueueHandle_t xQueue, void* pvBuffer, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param pvBuffer + * @param pxHigherPriorityTaskWoken + * @return + */ +BaseType_t queueReceiveFromISR(QueueHandle_t xQueue, void* pvBuffer, BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @param xNewQueue + * @return + */ +BaseType_t queueGenericReset(QueueHandle_t xQueue, BaseType_t xNewQueue); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @return + */ +BaseType_t queueReset(QueueHandle_t xQueue); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + * @return + */ +UBaseType_t queueSpacesAvailable(QueueHandle_t xQueue); + +/** + * @brief See FreeRTOS queue.h + * + * @param xQueue + */ +void queueDelete(QueueHandle_t xQueue); + +/* stream_buffer.h abstraction */ + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xBufferSizeBytes + * @param xTriggerLevelBytes + * @param xIsMessageBuffer + * @param pucStreamBufferStorageArea + * @param pxStaticStreamBuffer + * @return + */ +StreamBufferHandle_t streamBufferGenericCreateStatic(size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, + uint8_t* const pucStreamBufferStorageArea, //NOLINT(readability-avoid-const-params-in-decls) + StaticStreamBuffer_t* const pxStaticStreamBuffer); //NOLINT(readability-avoid-const-params-in-decls) + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @param pvTxData + * @param xDataLengthBytes + * @param xTicksToWait + * @return + */ +size_t streamBufferSend(StreamBufferHandle_t xStreamBuffer, const void* pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @param pvTxData + * @param xDataLengthBytes + * @param pxHigherPriorityTaskWoken + * @return + */ +size_t streamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken); //NOLINT(readability-avoid-const-params-in-decls) + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @param pvRxData + * @param xBufferLengthBytes + * @param xTicksToWait + * @return + */ +size_t streamBufferReceive(StreamBufferHandle_t xStreamBuffer, void* pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @param pvRxData + * @param xBufferLengthBytes + * @param pxHigherPriorityTaskWoken + * @return + */ +size_t streamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken); //NOLINT(readability-avoid-const-params-in-decls) + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + */ +void streamBufferDelete(StreamBufferHandle_t xStreamBuffer); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @return + */ +size_t streamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @return + */ +size_t streamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @return + */ +BaseType_t streamBufferReset(StreamBufferHandle_t xStreamBuffer); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @return + */ +BaseType_t streamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer); + +/** + * @brief See FreeRTOS stream_buffer.h + * + * @param xStreamBuffer + * @return + */ +BaseType_t streamBufferIsFull(StreamBufferHandle_t xStreamBuffer); + +/* message_buffer.h abstraction */ + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xBufferSizeBytes + * @param pucMessageBufferStorageArea + * @param pxStaticMessageBuffer + * @return + */ +MessageBufferHandle_t messageBufferCreateStatic(size_t xBufferSizeBytes, uint8_t* pucMessageBufferStorageArea, + StaticMessageBuffer_t* pxStaticMessageBuffer); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @param pvTxData + * @param xDataLengthBytes + * @param xTicksToWait + * @return + */ +size_t messageBufferSend(MessageBufferHandle_t xMessageBuffer, const void* pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @param pvTxData + * @param xDataLengthBytes + * @param pxHigherPriorityTaskWoken + * @return + */ +size_t messageBufferSendFromISR(MessageBufferHandle_t xMessageBuffer, const void* pvTxData, size_t xDataLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @param pvRxData + * @param xBufferLengthBytes + * @param xTicksToWait + * @return + */ +size_t messageBufferReceive(MessageBufferHandle_t xMessageBuffer, void* pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @param pvRxData + * @param xBufferLengthBytes + * @param pxHigherPriorityTaskWoken + * @return + */ +size_t messageBufferReceiveFromISR(MessageBufferHandle_t xMessageBuffer, void* pvRxData, size_t xBufferLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + */ +void messageBufferDelete(MessageBufferHandle_t xMessageBuffer); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @return + */ +size_t messageBufferSpacesAvailable(MessageBufferHandle_t xMessageBuffer); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @return + */ +BaseType_t messageBufferReset(MessageBufferHandle_t xMessageBuffer); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @return + */ +BaseType_t messageBufferIsEmpty(MessageBufferHandle_t xMessageBuffer); + +/** + * @brief See FreeRTOS message_buffer.h + * + * @param xMessageBuffer + * @return + */ +BaseType_t messageBufferIsFull(MessageBufferHandle_t xMessageBuffer); + +/* semphr.h abstraction */ + +/** + * @brief See FreeRTOS semphr.h + * + * @param uxMaxCount + * @param uxInitialCount + * @param pxSemaphoreBuffer + * @return + */ +SemaphoreHandle_t semaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t* pxSemaphoreBuffer); + +/** + * @brief See FreeRTOS semphr.h + * + * @param pxSemaphoreBuffer + * @return + */ +SemaphoreHandle_t semaphoreCreateBinaryStatic(StaticSemaphore_t* pxSemaphoreBuffer); + +/** + * @brief See FreeRTOS semphr.h + * + * @param pxMutexBuffer + * @return + */ +SemaphoreHandle_t mutexCreateStatic(StaticSemaphore_t* pxMutexBuffer); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + */ +void semaphoreDelete(SemaphoreHandle_t xSemaphore); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + * @param xBlockTime + * @return + */ +BaseType_t semaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + * @param pxHigherPriorityTaskWoken + * @return + */ +BaseType_t semaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + * @return + */ +BaseType_t semaphoreGive(SemaphoreHandle_t xSemaphore); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + * @param pxHigherPriorityTaskWoken + * @return + */ +BaseType_t semaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken); + +/** + * @brief See FreeRTOS semphr.h + * + * @param xSemaphore + * @return + */ +UBaseType_t semaphoreGetCount(SemaphoreHandle_t xSemaphore); + +/** + * @brief Implements getCount for semaphores to be used inside ISR + * + * This uses the original FreeRTOS API, however this interface does not exist + * in the original FreeRTOS semphr.h API + * + * @param xSemaphore + * @return + */ +UBaseType_t semaphoreGetCountFromISR(SemaphoreHandle_t xSemaphore); + +/* portmacro.h abstraction */ + +/** + * @brief Request a context switch in ISR + * + * For further details see FreeRTOS portmacro.h portYIELD_FROM_ISR + * + * @param xHigherPriorityTaskWoken + */ +void yieldFromISR(BaseType_t xHigherPriorityTaskWoken); + +#ifdef __cplusplus +} // extern "C" +} // namespace osabstraction +#endif // __cplusplus + +#endif /* OSABSTRACTION_OSABSTRACTION_HPP */ diff --git a/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstractionTypes.hpp b/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstractionTypes.hpp new file mode 100644 index 0000000..10e97cb --- /dev/null +++ b/OSAdapter/include/OsAbstraction/OsAbstraction/OsAbstractionTypes.hpp @@ -0,0 +1,292 @@ +/** + * @file include/OsAbstraction/OsAbstraction/OsAbstractionTypes.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSABSTRACTION_OSABSTRACTIONTYPES_HPP +#define OSABSTRACTION_OSABSTRACTIONTYPES_HPP + +#include +#include + +// FreeRTOSConfig.h +/// see FreeRTOS API documentation +#define configMAX_TASK_NAME_LEN (10) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define configTICK_RATE_HZ ((portTickType)1000) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +// projdefs.h +/// see FreeRTOS API documentation +typedef void (*TaskFunction_t)(void*); //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define pdMS_TO_TICKS(xTimeInMs) ((TickType_t)(((TickType_t)(xTimeInMs) * (TickType_t)configTICK_RATE_HZ) / (TickType_t)1000)) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define pdFALSE ((BaseType_t)0) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define pdTRUE ((BaseType_t)1) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +// portmacro.h +/// see FreeRTOS API documentation +typedef uint32_t TickType_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef long BaseType_t; //NOLINT(google-runtime-int, modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef unsigned long UBaseType_t; //NOLINT(google-runtime-int, modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define portSTACK_TYPE size_t //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef portSTACK_TYPE StackType_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define portMAX_DELAY (TickType_t)0xffffffffUL //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define portTICK_PERIOD_MS ((TickType_t)1000 / configTICK_RATE_HZ) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +// FreeRTOS.h +/// see FreeRTOS API documentation +struct xSTATIC_MINI_LIST_ITEM { //NOLINT(readability-identifier-naming,readability-identifier-naming) defined by FreeRTOS +#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1) + TickType_t xDummy1; +#endif + /// see FreeRTOS API documentation + TickType_t xDummy2; + /// see FreeRTOS API documentation + void* pvDummy3[2]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS +}; +typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xSTATIC_LIST { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1) + TickType_t xDummy1; +#endif + /// see FreeRTOS API documentation + UBaseType_t uxDummy2; + /// see FreeRTOS API documentation + void* pvDummy3; + /// see FreeRTOS API documentation + StaticMiniListItem_t xDummy4; +#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1) + TickType_t xDummy5; +#endif +} StaticList_t; //NOLINT(readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xSTATIC_QUEUE { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + /// see FreeRTOS API documentation + void* pvDummy1[3]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + + /// see FreeRTOS API documentation + union { + /// see FreeRTOS API documentation + void* pvDummy2; + /// see FreeRTOS API documentation + UBaseType_t uxDummy2; + } u; + + /// see FreeRTOS API documentation + StaticList_t xDummy3[2]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + /// see FreeRTOS API documentation + UBaseType_t uxDummy4[3]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + /// see FreeRTOS API documentation + uint8_t ucDummy5[2]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + +#if ((configSUPPORT_STATIC_ALLOCATION == 1) && (configSUPPORT_DYNAMIC_ALLOCATION == 1)) + uint8_t ucDummy6; +#endif + +#if (configUSE_QUEUE_SETS == 1) + void* pvDummy7; +#endif + +#if (configUSE_TRACE_FACILITY == 1) + UBaseType_t uxDummy8; + uint8_t ucDummy9; +#endif + +} StaticQueue_t; //NOLINT(readability-identifier-naming,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef StaticQueue_t StaticSemaphore_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xSTATIC_STREAM_BUFFER { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + /// see FreeRTOS API documentation + size_t uxDummy1[4]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + /// see FreeRTOS API documentation + void* pvDummy2[3]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + /// see FreeRTOS API documentation + uint8_t ucDummy3; +#if (configUSE_TRACE_FACILITY == 1) + UBaseType_t uxDummy4; +#endif +} StaticStreamBuffer_t; //NOLINT(readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef StaticStreamBuffer_t StaticMessageBuffer_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +#define configSTACK_DEPTH_TYPE uint16_t //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +struct xSTATIC_LIST_ITEM { //NOLINT(readability-identifier-naming) defined by FreeRTOS +#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1) + TickType_t xDummy1; +#endif + /// see FreeRTOS API documentation + TickType_t xDummy2; + /// see FreeRTOS API documentation + void* pvDummy3[4]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS +#if (configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1) + TickType_t xDummy4; +#endif +}; +typedef struct xSTATIC_LIST_ITEM StaticListItem_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xSTATIC_TCB { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + /// see FreeRTOS API documentation + void* pxDummy1; +#if (portUSING_MPU_WRAPPERS == 1) + xMPU_SETTINGS xDummy2; +#endif + /// see FreeRTOS API documentation + StaticListItem_t xDummy3[2]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS + /// see FreeRTOS API documentation + UBaseType_t uxDummy5; + /// see FreeRTOS API documentation + void* pxDummy6; + /// see FreeRTOS API documentation + uint8_t ucDummy7[configMAX_TASK_NAME_LEN]; //NOLINT(modernize-avoid-c-arrays, cppcoreguidelines-avoid-c-arrays, hicpp-avoid-c-arrays) defined by FreeRTOS +#if ((portSTACK_GROWTH > 0) || (configRECORD_STACK_HIGH_ADDRESS == 1)) + void* pxDummy8; +#endif +#if (portCRITICAL_NESTING_IN_TCB == 1) + UBaseType_t uxDummy9; +#endif +#if (configUSE_TRACE_FACILITY == 1) + UBaseType_t uxDummy10[2]; +#endif +#if (configUSE_MUTEXES == 1) + UBaseType_t uxDummy12[2]; +#endif +#if (configUSE_APPLICATION_TASK_TAG == 1) + void* pxDummy14; +#endif +#if (configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0) + void* pvDummy15[configNUM_THREAD_LOCAL_STORAGE_POINTERS]; +#endif +#if (configGENERATE_RUN_TIME_STATS == 1) + uint32_t ulDummy16; +#endif +#if (configUSE_NEWLIB_REENTRANT == 1) + struct _reent xDummy17; +#endif +#if (configUSE_TASK_NOTIFICATIONS == 1) + uint32_t ulDummy18; + uint8_t ucDummy19; +#endif +#if (tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0) //NOLINT(cppcoreguidelines-macro-usage) defined by FreeRTOS + uint8_t uxDummy20; +#endif + +#if (INCLUDE_xTaskAbortDelay == 1) + uint8_t ucDummy21; +#endif +#if (configUSE_POSIX_ERRNO == 1) + int iDummy22; +#endif +} StaticTask_t; //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +#define portTickType TickType_t //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +// task.h +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +/// see FreeRTOS API documentation +typedef struct tskTaskControlBlock* TaskHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define taskSCHEDULER_NOT_STARTED ((BaseType_t)1) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define taskSCHEDULER_RUNNING ((BaseType_t)2) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xTIME_OUT { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + /// see FreeRTOS API documentation + BaseType_t xOverflowCount; + /// see FreeRTOS API documentation + TickType_t xTimeOnEntering; +} TimeOut_t; //NOLINT(readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef enum { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eRunning = 0, /* A task is querying the state of itself, so must be running. */ + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eReady, /* The task being queried is in a read or pending ready list. */ + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eBlocked, /* The task being queried is in the Blocked state. */ + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ + //NOLINTNEXTLINE(readability-identifier-naming) defined by FreeRTOS + eInvalid /* Used as an 'invalid state' value. */ +} eTaskState; //NOLINT(readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef struct xTASK_STATUS { //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + /// see FreeRTOS API documentation + TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ + /// see FreeRTOS API documentation + const char* pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ + /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + /// see FreeRTOS API documentation + UBaseType_t xTaskNumber; /* A number unique to the task. */ + /// see FreeRTOS API documentation + eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ + /// see FreeRTOS API documentation + UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ + /// see FreeRTOS API documentation + UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + /// see FreeRTOS API documentation + uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + /// see FreeRTOS API documentation + StackType_t* pxStackBase; /* Points to the lowest address of the task's stack area. */ + /// see FreeRTOS API documentation + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ +} TaskStatus_t; //NOLINT(readability-identifier-naming,readability-identifier-naming) defined by FreeRTOS + +/// see FreeRTOS API documentation +typedef BaseType_t (*TaskHookFunction_t)(void*); //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +// queue.h +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +/// see FreeRTOS API documentation +typedef struct QueueDefinition* QueueHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef struct QueueDefinition* QueueSetHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +typedef struct QueueDefinition* QueueSetMemberHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS +/// see FreeRTOS API documentation +#define queueQUEUE_TYPE_SET ((uint8_t)0U) //NOLINT(cppcoreguidelines-macro-usage,readability-identifier-naming) defined by FreeRTOS + +// stream_buffer.h +struct StreamBufferDef_t; +/// see FreeRTOS API documentation +typedef struct StreamBufferDef_t* StreamBufferHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +// message_buffer.h +/// see FreeRTOS API documentation +typedef void* MessageBufferHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +// semphr.h +/// see FreeRTOS API documentation +typedef QueueHandle_t SemaphoreHandle_t; //NOLINT(modernize-use-using,readability-identifier-naming) defined by FreeRTOS + +#endif /* OSABSTRACTION_OSABSTRACTIONTYPES_HPP */ diff --git a/OSAdapter/include/OsExtension/OsExtension/MessageBuffer.hpp b/OSAdapter/include/OsExtension/OsExtension/MessageBuffer.hpp new file mode 100644 index 0000000..b6da2fa --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/MessageBuffer.hpp @@ -0,0 +1,316 @@ +/** + * @file include/OsExtension/OsExtension/MessageBuffer.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_MESSAGEBUFFER_HPP +#define OSEXTENSION_MESSAGEBUFFER_HPP + +#include +#include +#include + +#include + +#include + +namespace osextension { + +/** + * Event indicating that data has been added to the given buffer + */ +struct DataAddedEvent { + + /// buffer to which data has been added + MessageBufferInterface& buffer; + + /// pointer to the data added + const void* data; + + /// length of the added data in number of bytes + const size_t length; +}; + +/** + * Event indicating that data has been removed from given buffer + */ +struct DataRemovedEvent { + + /// buffer to which data has been added + MessageBufferInterface& buffer; + + /// pointer to the data removed + const void* data; + + /// length of the data removed in number of bytes + const size_t length; +}; + +/** + * Event indicating that data sent to the buffer is dropped due to a full buffer + */ +struct DataDroppedEvent { + /// buffer which dropped the data + MessageBufferInterface& buffer; + + /// pointer to the dropped data + const void* data; + + /// length of the data dropped in number of bytes + const size_t length; +}; + +/// Type alias for MessageBuffer observers +using MessageBufferObserver = observer::Observer; + +/// Type alias for observable MessageBuffer implementations +using MessageBufferObservable = observer::Observable; + +/** + * @brief Reference to a FreeRTOS Message Buffer + * + * This class is lightweight reference to a FreeRTOS queue which can be copied + * to have a thread & MPU safe access to the queue. + * It can be observed by attaching an MessageBufferObserver to listen to addition + * or deletions of messages (@see MessageBufferMonitor.hpp) + */ +class MessageBufferReference : public MessageBufferInterface, + public MessageBufferObservable { +public: + /// Constructor + MessageBufferReference() = default; + + /// Copy constructor + MessageBufferReference(MessageBufferReference const&) = default; + + /// Move constructor + MessageBufferReference(MessageBufferReference&&) noexcept = default; + + /// Copy assignment operator @return + MessageBufferReference& operator=(MessageBufferReference const&) = default; + + /// Move assignment operator @return + MessageBufferReference& operator=(MessageBufferReference&&) noexcept = default; + + /// Destructor + ~MessageBufferReference() override = default; + + /// @copydoc MessageBufferInterface::send(const void*, size_t) + size_t send(const void* data, size_t length) override; + + /// @copydoc MessageBufferInterface::send(const void*, size_t, TickType_t) + size_t send(const void* data, size_t length, TickType_t ticksToWait) override; + + /// @copydoc MessageBufferInterface::send(etl::array_view) + size_t send(etl::array_view data) override; + + /// @copydoc MessageBufferInterface::send(etl::array_view, TickType_t) + size_t send(etl::array_view data, TickType_t ticksToWait) override; + + /// @copydoc MessageBufferInterface::sendFromISR(const void*, size_t, BaseType_t*) + size_t sendFromISR(const void* data, size_t length, BaseType_t* higherPriorityTaskWoken) override; + + /// @copydoc MessageBufferInterface::sendFromISR(etl::array_view, BaseType_t*) + size_t sendFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) override; + + /// @copydoc MessageBufferInterface::receive(void*, size_t, TickType_t) + size_t receive(void* data, size_t length, TickType_t xTicksToWait) override; + + /// @copydoc MessageBufferInterface::receive(etl::array_view, TickType_t) + size_t receive(etl::array_view data, TickType_t xTicksToWait) override; + + /// @copydoc MessageBufferInterface::receiveFromISR(void*, size_t, BaseType_t*) + size_t receiveFromISR(void* data, size_t length, BaseType_t* higherPriorityTaskWoken) override; + + /// @copydoc MessageBufferInterface::receiveFromISR(etl::array_view, BaseType_t*) + size_t receiveFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) override; + + /// @copydoc MessageBufferInterface::reset() + bool reset() override; + + /// @copydoc MessageBufferInterface::isEmpty() + bool isEmpty() override; + + /// @copydoc MessageBufferInterface::isFull() + bool isFull() override; + + /// @copydoc MessageBufferInterface::numMessagesAvailable() + size_t numMessagesAvailable() override; + + /// @copydoc MessageBufferInterface::numBytesAvailable() + size_t numBytesAvailable() override; + + /** + * @copydoc OsExtensionInterface::getHandleForQueueSet + */ + OsExtensionHandleType getHandleForQueueSet() const override; + +protected: + /** + * @brief Get the internal FreeRTOS message buffer handle + * + * @return FreeRTOS queue handle + */ + MessageBufferHandle_t getHandle() const { + return _handle; + } + + /** + * @brief Set the internal FreeRTOS message buffer handle + * + * @param[in] handle handle + */ + void setHandle(MessageBufferHandle_t handle) { + _handle = handle; + } + + /** + * @brief Set the internal counting semaphore reference + * + * @param[in] countingSemaphoreReference counting semaphore reference + */ + void setCountingSemaphoreReference(SemaphoreReference countingSemaphoreReference) { + _countingSemaphoreReference = std::move(countingSemaphoreReference); + } + + /** + * @brief Set the internal binary semaphore reference + * + * @param[in] binarySemaphoreReference binary semaphore reference + */ + void setBinarySemaphoreReference(SemaphoreReference binarySemaphoreReference) { + _binarySemaphoreReference = std::move(binarySemaphoreReference); + } + + /** + * @brief Set the length + * + * @param[in] length length + */ + void setLength(uint32_t length) { + _length = length; + } + +private: + /// represents FreeRTOS binary semaphore handle + SemaphoreReference _binarySemaphoreReference; + + /// represents FreeRTOS counting semaphore handle + SemaphoreReference _countingSemaphoreReference; + + /// Internal FreeRTOS handle + MessageBufferHandle_t _handle{nullptr}; + + /// Length + uint32_t _length = 0u; +}; + +/** + * @brief FreeRTOS Message Buffer with external storage buffer + * + * This class is encapsulates a FreeRTOS message buffer where the storage buffer + * for the FreeRTOS queue is outside of the object. + * + * @tparam size Size (number of bytes) of message buffer + * @tparam length Length (number of items) of message buffer + */ +template +class MessageBufferWithExternalMemory : public MessageBufferReference { +public: + /** + * @brief Constructs message buffer with external memory + * + * @param[in] storageBuffer Reference to storage buffer of the message buffer + */ + explicit MessageBufferWithExternalMemory(std::array& storageBuffer) : _staticBinarySemaphore{1u, 1u}, _staticCountingSemaphore{length, 0u} { + auto* const handle = osabstraction::messageBufferCreateStatic(size, reinterpret_cast(&storageBuffer), &_buffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast required due to external API + this->setHandle(handle); + + this->setCountingSemaphoreReference(_staticCountingSemaphore.getReference()); + this->setBinarySemaphoreReference(_staticBinarySemaphore.getReference()); + this->setLength(length); + } + + /// Deleted copy constructor + MessageBufferWithExternalMemory(MessageBufferWithExternalMemory const&) = delete; + + /// Deleted move constructor + MessageBufferWithExternalMemory(MessageBufferWithExternalMemory&&) = delete; + + /// Delete copy assignment operator @return + MessageBufferWithExternalMemory& operator=(MessageBufferWithExternalMemory const&) = delete; + + /// Deleted move assignment operator @return + MessageBufferWithExternalMemory& operator=(MessageBufferWithExternalMemory&&) = delete; + + /// Destructor + ~MessageBufferWithExternalMemory() override { + osabstraction::messageBufferDelete(this->getHandle()); + } + + /** + * @brief Gets a reference to this message buffer + * @return Reference to this message buffer + */ + MessageBufferReference getReference() const { + return MessageBufferReference(*this); //NOLINT(cppcoreguidelines-slicing) this is exactly what we want here + } + +private: + /// Static message buffer + StaticMessageBuffer_t _buffer{}; + + /// represents FreeRTOS binary semaphore object + Semaphore _staticBinarySemaphore; + + /// represents FreeRTOS counting semaphore object + Semaphore _staticCountingSemaphore; +}; + +/** + * @brief FreeRTOS Message Buffer with internal storage buffer + * + * This class is encapsulates a FreeRTOS message buffer where the storage buffer + * for the FreeRTOS queue is outside of the object. + * + * @tparam size Size (number of bytes) of message buffer + * @tparam length Length (number of items) of message buffer + */ +template +class MessageBuffer : public MessageBufferWithExternalMemory { +public: + /** + * @brief: Constructs message buffer + */ + MessageBuffer() : MessageBufferWithExternalMemory{_storageBuffer} {} + + /// Deleted copy constructor + MessageBuffer(MessageBuffer const&) = delete; + + /// Deleted move constructor + MessageBuffer(MessageBuffer&&) = delete; + + /// Delete copy assignment operator @return + MessageBuffer& operator=(MessageBuffer const&) = delete; + + /// Deleted move assignment operator @return + MessageBuffer& operator=(MessageBuffer&&) = delete; + + /// Destructor + ~MessageBuffer() override = default; + +private: + /// Storage buffer + std::array _storageBuffer{}; +}; + +} // namespace osextension + +#endif /* OSEXTENSION_MESSAGEBUFFER_HPP */ diff --git a/OSAdapter/include/OsExtension/OsExtension/MessageBufferInterface.hpp b/OSAdapter/include/OsExtension/OsExtension/MessageBufferInterface.hpp new file mode 100644 index 0000000..49ad5f0 --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/MessageBufferInterface.hpp @@ -0,0 +1,190 @@ +/** + * @file include/OsExtension/OsExtension/MessageBufferInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef OSEXTENSION_MESSAGEBUFFERINTERFACE_HPP +#define OSEXTENSION_MESSAGEBUFFERINTERFACE_HPP + +#include +#include + +#include + +#include + +namespace osextension { + +/** + * @brief Interface to a Message Buffer. + */ +class MessageBufferInterface : public OsExtensionInterface { +public: + /// Constructor + MessageBufferInterface() = default; + + /// Copy constructor + MessageBufferInterface(MessageBufferInterface const&) = default; + + /// Move constructor + MessageBufferInterface(MessageBufferInterface&&) noexcept = default; + + /// Copy assignment operator @return + MessageBufferInterface& operator=(MessageBufferInterface const&) = default; + + /// Move assignment operator @return + MessageBufferInterface& operator=(MessageBufferInterface&&) noexcept = default; + + /// Destructor + ~MessageBufferInterface() override = default; + + /** + * @brief Sends/puts a new element into the message buffer + * + * @param data pointer to buffer containing the data to be sent + * @param length amount of bytes to be sent + * @return number of bytes written into the message buffer + */ + virtual size_t send(const void* data, size_t length) = 0; + + /** + * @brief Sends/puts a new element into the message buffer, blocking on full buffer till timeout. + * @param data pointer to buffer containing the data to be sent + * @param length length amount of bytes to be sent + * @param ticksToWait timeout in os systicks until the send/put is aborted + * @return number of bytes written into the message buffer + */ + virtual size_t send(const void* data, size_t length, TickType_t ticksToWait) = 0; + + /** + * @brief Sends/puts a new element into the message buffer + * + * @param data pointer to buffer containing the data to be sent + * @return number of bytes written into the message buffer + */ + virtual size_t send(etl::array_view data) = 0; + + /** + * @brief Sends/puts a new element into the message buffer, blocking on full buffer till timeout. + * @param data pointer to buffer containing the data to be sent + * @param ticksToWait timeout in os systicks until the send/put is aborted + * @return number of bytes written into the message buffer + */ + virtual size_t send(etl::array_view data, TickType_t ticksToWait) = 0; + + /** + * @brief Sends/puts a new element into the message buffer from ISR context + * + * @param data pointer to buffer containing the data to be sent + * @param length amount of bytes to be sent + * @param higherPriorityTaskWoken is set to true if the send has lead to a context switch + * @return number of bytes written into the message buffer + */ + virtual size_t sendFromISR(const void* data, size_t length, BaseType_t* higherPriorityTaskWoken) = 0; + + /** + * @brief Sends/puts a new element into the message buffer from ISR context + * + * @param data pointer to buffer containing the data to be sent + * @param higherPriorityTaskWoken is set to true if the send has lead to a context switch + * @return number of bytes written into the message buffer + */ + virtual size_t sendFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) = 0; + + /** + * @brief Receives/gets message from message buffer + * + * Note: This function does not support multiple receiving tasks + * + * @param data pointer to a buffer that will be filled with received bytes + * @param length how many bytes to receive + * @param xTicksToWait timeout in os systicks until the receive is aborted + * @return number of bytes read from the message buffer + */ + virtual size_t receive(void* data, size_t length, TickType_t xTicksToWait) = 0; + + /** + * @brief Receives/gets message from message buffer + * + * Note: This function does not support multiple receiving tasks + * + * @param data pointer to a buffer that will be filled with received bytes + * @param xTicksToWait timeout in os systicks until the receive is aborted + * @return number of bytes read from the message buffer + */ + virtual size_t receive(etl::array_view data, TickType_t xTicksToWait) = 0; + + /** + * @brief Receives/gets message from message buffer from ISR context + * + * Note: This function does not support multiple receiving tasks + * + * @param data pointer to a buffer that will be filled with received bytes + * @param length how many bytes to receive + * @param higherPriorityTaskWoken is set to true if the receiving forced a context switch + * @return number of bytes read from the message buffer + */ + virtual size_t receiveFromISR(void* data, size_t length, BaseType_t* higherPriorityTaskWoken) = 0; + + /** + * @brief Receives/gets message from message buffer from ISR context + * + * Note: This function does not support multiple receiving tasks + * + * @param data pointer to a buffer that will be filled with received bytes + * @param higherPriorityTaskWoken is set to true if the receiving forced a context switch + * @return number of bytes read from the message buffer + */ + virtual size_t receiveFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) = 0; + + /** + * @brief Resets message buffer and binary semaphore + * + * @retval true on sucess + * @retval false on failure + */ + virtual bool reset(void) = 0; + + /** + * @brief Checks if message buffer is empty + * + * @retval true if queue is empty + * @retval false if queue is not empty + */ + virtual bool isEmpty(void) = 0; + + /** + * @brief Checks if message buffer is full + * + * @retval true when queue is full + * @retval false when queue is not full + */ + virtual bool isFull(void) = 0; + + /** + * @brief Number of available messages stored in the buffer + * + * @return number of messages + */ + virtual size_t numMessagesAvailable() = 0; + + /** + * @brief Number of available bytes stored in the buffer + * + * @return number of bytes + */ + virtual size_t numBytesAvailable() = 0; +}; + +} // namespace osextension + +#endif /* OSEXTENSION_MESSAGEBUFFERINTERFACE_HPP */ diff --git a/OSAdapter/include/OsExtension/OsExtension/OsExtensionInterface.hpp b/OSAdapter/include/OsExtension/OsExtension/OsExtensionInterface.hpp new file mode 100644 index 0000000..1e0bb0d --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/OsExtensionInterface.hpp @@ -0,0 +1,56 @@ +/** + * @file include/OsExtension/OsExtension/OsExtensionInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_OSEXTENSIONINTERFACE_HPP +#define OSEXTENSION_OSEXTENSIONINTERFACE_HPP + +#include + +namespace osextension { + +/** + * @brief Interface for all OsExtension modules to implement + */ +class OsExtensionInterface { +public: + /** Reassign native OS type for readability and portability */ + using OsExtensionHandleType = QueueSetMemberHandle_t; + + /// Constructor + OsExtensionInterface() = default; + + /// Copy constructor + OsExtensionInterface(OsExtensionInterface const&) = default; + + /// Move constructor + OsExtensionInterface(OsExtensionInterface&&) noexcept = default; + + /// Copy assignment operator @return + OsExtensionInterface& operator=(OsExtensionInterface const&) = default; + + /// Move assignment operator @return + OsExtensionInterface& operator=(OsExtensionInterface&&) noexcept = default; + + /// Destructor + virtual ~OsExtensionInterface() = default; + + /** + * @brief Gets a handle for use with FreeRTOS QueueSets + * + * @return handle + */ + virtual OsExtensionHandleType getHandleForQueueSet(void) const = 0; +}; + +} // namespace osextension + +#endif /* OSEXTENSION_OSEXTENSIONINTERFACE_HPP */ diff --git a/OSAdapter/include/OsExtension/OsExtension/Queue.hpp b/OSAdapter/include/OsExtension/OsExtension/Queue.hpp new file mode 100644 index 0000000..aaa04ea --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/Queue.hpp @@ -0,0 +1,241 @@ +/** + * @file include/OsExtension/OsExtension/Queue.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_QUEUE_HPP +#define OSEXTENSION_QUEUE_HPP + +#include +#include +#include +#include + +namespace osextension { + +/** + * @brief Reference to a FreeRTOS Queue + * + * This class is lightweight reference to a FreeRTOS queue which can be copied + * to have a thread & MPU safe access to the queue. + * + * @tparam ItemType Type for queue items + */ +template +class QueueReference : public QueueInterface { +public: + /// Constructor + QueueReference() = default; + + /// Copy constructor + QueueReference(QueueReference const&) = default; + + /// Move constructor + QueueReference(QueueReference&&) noexcept = default; + + /// Copy assignment operator @return + QueueReference& operator=(QueueReference const&) = default; + + /// Move assignment operator @return + QueueReference& operator=(QueueReference&&) noexcept = default; + + /// Destructor + ~QueueReference() override = default; + + /// @copydoc QueueInterface::send(const ItemType&, uint32_t) + bool send(const ItemType& data, uint32_t timeout) override { + return (osabstraction::queueSend(_handle, &data, timeout) == pdTRUE); + } + + /// @copydoc QueueInterface::send(const ItemType&) + bool send(const ItemType& data) override { + return (osabstraction::queueSend(_handle, &data, portMAX_DELAY) == pdTRUE); + } + + /// @copydoc QueueInterface::sendFromIsr(const ItemType&) + bool sendFromIsr(const ItemType& data) override { + return (osabstraction::queueSendFromISR(_handle, &data, nullptr) == pdTRUE); + } + + /// @copydoc QueueInterface::sendFromIsr(const ItemType&, QueueTaskWokenTokenType*) + bool sendFromIsr(const ItemType& data, QueueTaskWokenTokenType* taskWoken) override { + return (osabstraction::queueSendFromISR(_handle, &data, taskWoken) == pdTRUE); + } + + /// @copydoc QueueInterface::receive(ItemType&, uint32_t) + bool receive(ItemType& data, uint32_t timeout) override { + return (osabstraction::queueReceive(_handle, &data, timeout) == pdTRUE); + } + + /// @copydoc QueueInterface::receive(ItemType&) + bool receive(ItemType& data) override { + return (osabstraction::queueReceive(_handle, &data, portMAX_DELAY) == pdTRUE); + } + + /// @copydoc QueueInterface::receiveFromIsr(ItemType&) + bool receiveFromIsr(ItemType& data) override { + return (osabstraction::queueReceiveFromISR(_handle, &data, nullptr) == pdTRUE); + } + + /// @copydoc QueueInterface::spacesAvailable() + size_t spacesAvailable() override { + return (osabstraction::queueSpacesAvailable(_handle)); + } + + /// @copydoc OsExtensionInterface::getHandleForQueueSet() + OsExtensionInterface::OsExtensionHandleType getHandleForQueueSet() const override { + return _handle; + } + + /** + * @brief Get the internal FreeRTOS queue handle + * + * @return FreeRTOS queue handle + */ + [[deprecated("Copy object to share references or use getHandleForQueueSet() if you need add to FreeRTOS queue set.")]] QueueHandle_t getHandle() const { + return _handle; + } + +protected: + /** + * @brief Get the internal FreeRTOS queue handle + * + * @return FreeRTOS queue handle + */ + QueueHandle_t getInternalHandle() const { + return _handle; + } + + /** + * @brief Set the internal FreeRTOS queue handle + * + * @param[in] handle handle + */ + void setHandle(QueueHandle_t handle) { + _handle = handle; + } + +private: + /// Internal FreeRTOS handle + QueueHandle_t _handle{nullptr}; +}; + +/** + * @brief FreeRTOS Queue with external storage buffer + * + * This class is encapsulates a FreeRTOS queue where the storage buffer for + * the FreeRTOS queue is outside of the object. + * + * @tparam ItemType Type for queue items + * @tparam length Length (number of items) of queue + */ +template +class QueueWithExternalMemory : public QueueReference { +public: + /** + * @brief Constructs queue with external memory + * + * @param[in] storageBuffer Reference to storage buffer of the queue + * @param[in] name Queue name or nullptr if no name is desired. Pointer must + * be valid for the life-time of the this object. + */ + explicit QueueWithExternalMemory(std::array& storageBuffer, const char* name = nullptr) { + auto* const handle = osabstraction::queueCreateStatic(length, sizeof(ItemType), reinterpret_cast(&storageBuffer), &_buffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast required due to external API + + this->setHandle(handle); + if ((handle != nullptr) and (name != nullptr)) { + /* We want this queue to be viewable in a RTOS kernel aware debugger, + so register it. */ + osabstraction::queueRegister(handle, name); + } + } + + /// Deleted copy constructor + QueueWithExternalMemory(const QueueWithExternalMemory&) = delete; + + /// Deleted move constructor + QueueWithExternalMemory(QueueWithExternalMemory&&) = delete; + + /// Delete copy assignment operator @return + QueueWithExternalMemory operator=(const QueueWithExternalMemory&) = delete; + + /// Deleted move assignment operator @return + QueueWithExternalMemory& operator=(QueueWithExternalMemory&&) = delete; + + /// Destructor + ~QueueWithExternalMemory() override { + osabstraction::queueUnregister(this->getInternalHandle()); + osabstraction::queueDelete(this->getInternalHandle()); + } + + /** + * @brief Gets a reference to this queue + * @return Reference to this queue + */ + QueueReference getReference() const { + return QueueReference(*this); //NOLINT(cppcoreguidelines-slicing) this is exactly what we want here + } + + /** + * @brief Gets a reference to this queue + * @return Reference to this queue + */ + [[deprecated("Use getReference().")]] QueueReference getQueueReference() const { + return getReference(); + } + +private: + /// Static queue buffer + StaticQueue_t _buffer{}; +}; + +/** + * @brief FreeRTOS Queue with internal storage buffer + * + * This class is encapsulates a FreeRTOS queue where the storage buffer for + * the FreeRTOS queue is inside of the object. + * + * @tparam ItemType Type for queue items + * @tparam length Length (number of items) of queue + */ +template +class Queue : public QueueWithExternalMemory { +public: + /** + * @brief: Constructs queue + * + * @param[in] name Queue name or nullptr if no name is desired. Pointer must + * be valid for the life-time of the this object. + */ + explicit Queue(const char* name = nullptr) : QueueWithExternalMemory{_storageBuffer, name} {} + + /// Deleted copy constructor + Queue(const Queue&) = delete; + + /// Deleted move constructor + Queue(Queue&&) = delete; + + /// Delete copy assignment operator @return + Queue operator=(const Queue&) = delete; + + /// Deleted move assignment operator @return + Queue& operator=(Queue&&) = delete; + + /// Destructor + ~Queue() override = default; + +private: + /// Storage buffer + std::array _storageBuffer{}; +}; + +} // namespace osextension + +#endif // OSEXTENSION_QUEUE_HPP diff --git a/OSAdapter/include/OsExtension/OsExtension/QueueInterface.hpp b/OSAdapter/include/OsExtension/OsExtension/QueueInterface.hpp new file mode 100644 index 0000000..2908577 --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/QueueInterface.hpp @@ -0,0 +1,144 @@ +/** + * @file include/OsExtension/OsExtension/QueueInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_QUEUEINTERFACE_HPP +#define OSEXTENSION_QUEUEINTERFACE_HPP + +#include +#include +#include + +namespace osextension { + +/** Reassign native OS Type for readability and portability */ +/* Note, this cannot be done inside a template since template types cannot be inherited */ +using QueueTaskWokenTokenType = BaseType_t; + +/** + * @brief Interface to a Queue. + * + * @tparam ItemType Type for queue items + */ +template +class QueueInterface : public OsExtensionInterface { +public: + /// Constructor + QueueInterface() = default; + + /// Copy constructor + QueueInterface(QueueInterface const&) = default; + + /// Move constructor + QueueInterface(QueueInterface&&) noexcept = default; + + /// Copy assignment operator @return + QueueInterface& operator=(QueueInterface const&) = default; + + /// Move assignment operator @return + QueueInterface& operator=(QueueInterface&&) noexcept = default; + + /// Destructor + ~QueueInterface() override = default; + + /** + * @brief Sends a message to the back of the queue. + * If timeout > 0, the task waits for space to become available on the + * queue, should it be already full. + * Timeout is only available in Task Context. + * + * @param[in] data Pointer to the data to be sent + * @param[in] timeout Time the task waits for free space in the queue + * + * @retval true Message sent + * @retval false Message sending failed + */ + virtual bool send(const ItemType& data, uint32_t timeout) = 0; + + /** + * @brief Sends a message to the back of the queue. + * + * @param[in] data data to be sent + * + * @retval true Message sent + * @retval false Message sending failed + */ + virtual bool send(const ItemType& data) = 0; + + /** + * @brief Sends a message to the back of the queue. + * This Function is to be used in an ISR Context. + * + * @param[in] data data to be sent + * + * @retval true Message sent + * @retval false Message sending failed + */ + virtual bool sendFromIsr(const ItemType& data) = 0; + + /** + * @brief Sends a message to the back of the queue. + * This Function is to be used in an ISR Context. + * + * @param[in] data data to be sent + * @param[out] taskWoken Token set to 1 if the send wakes a higher prior Task. + * + * @retval true Message sent + * @retval false Message sending failed + */ + virtual bool sendFromIsr(const ItemType& data, QueueTaskWokenTokenType* taskWoken) = 0; + + /** + * @brief Receives a message from the queue. + * If timeout > 0, the task waits for a new item until timeout occurs. + * + * @param[out] data Pointer to data to be received + * @param[in] timeout Time the task waits for data + * + * @retval true Message received + * @retval false Message receiving failed + */ + virtual bool receive(ItemType& data, uint32_t timeout) = 0; + + /** + * @brief Receives a message from the queue. + * This method will block for an empty queue until a new message is available! + * @see receive(ItemType& data, uint32_t timeout) to specify a timeout. + * + * @param[out] data data to be received + * + * @retval true Message received + * @retval false Message receiving failed + */ + virtual bool receive(ItemType& data) = 0; + + /** + * @brief Receives a message from the queue in an ISR. + * If no message is waiting to be read the call returns immediately. + * + * @param[out] data data to be received + * + * @retval true Message received + * @retval false No message in Queue + */ + virtual bool receiveFromIsr(ItemType& data) = 0; + + /** + * @brief Evaluates number of items that the queue can still accommodate. + * + * @return Number of items that the queue can still accommodate. + */ + virtual size_t spacesAvailable() = 0; +}; + +} // namespace osextension + +#endif // OSEXTENSION_QUEUEINTERFACE_HPP diff --git a/OSAdapter/include/OsExtension/OsExtension/Semaphore.hpp b/OSAdapter/include/OsExtension/OsExtension/Semaphore.hpp new file mode 100644 index 0000000..c307065 --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/Semaphore.hpp @@ -0,0 +1,149 @@ +/** + * @file include/OsExtension/OsExtension/Semaphore.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_SEMAPHORE_HPP +#define OSEXTENSION_SEMAPHORE_HPP + +#include + +#include + +namespace osextension { + +/** + * @brief Reference Class for Controlling a OS Semaphore + * Used to copy and share for a thread safe Inter- + * task-signaling of OS Semaphore Objects + */ +class SemaphoreReference : public SemaphoreInterface { +public: + /** Reassign native OS Handle Type for readability and portability */ + using SemaphoreHandleType = SemaphoreHandle_t; + + /// Constructor + SemaphoreReference(void) = default; + + /// Copy constructor + SemaphoreReference(const SemaphoreReference&) = default; + + /// Move constructor + SemaphoreReference(SemaphoreReference&&) noexcept = default; + + /// Copy assignment operator @return + SemaphoreReference& operator=(const SemaphoreReference&) = default; + + /// Move assignment operator @return + SemaphoreReference& operator=(SemaphoreReference&&) noexcept = default; + + /// Destructor + ~SemaphoreReference(void) override = default; + + /// @copydoc SemaphoreInterface::signal() + bool signal(void) override; + + /// @copydoc SemaphoreInterface::signalFromIsr(SemaphoreTaskWokenTokenType*) + bool signalFromIsr(SemaphoreTaskWokenTokenType* taskWoken) override; + + /// @copydoc SemaphoreInterface::wait() + bool wait(uint32_t timeout) override; + + /// @copydoc SemaphoreInterface::waitFromIsr(SemaphoreTaskWokenTokenType*) + bool waitFromIsr(SemaphoreTaskWokenTokenType* taskWoken) override; + + /// @copydoc SemaphoreInterface::getCount() + size_t getCount(void) override; + + /// @copydoc SemaphoreInterface::getCount() + size_t getCountFromIsr(void) override; + + /// @copydoc SemaphoreInterface::reset() + bool reset(void) override; + + /** + * @brief Returns handle to the Rtos Semaphore. + * @return Semaphore Handle. + */ + [[deprecated("Copy object to share references or use getHandleForQueueSet() if you need add to FreeRTOS queue set")]] SemaphoreHandleType getHandle(void) const { + return _semaphore; + } + + /** + * @copydoc OsExtensionInterface::getHandleForQueueSet + */ + OsExtensionHandleType getHandleForQueueSet(void) const override { + return _semaphore; + } + +protected: + /// Semaphore Handle + SemaphoreHandleType _semaphore = {}; //NOLINT(cppcoreguidelines-non-private-member-variables-in-classes,misc-non-private-member-variables-in-classes) attribute should be accessible by derived classes +}; + +/** + * The Memory Allocation Template Class for a Rtos Semaphore + */ +class Semaphore : public SemaphoreReference { +public: + /** Reassign native OS Type for readability and portability */ + using StaticSemaphoreType = StaticSemaphore_t; + + /** + * @brief Constructor + * + * @param[in] maxCount Maximum Semaphore counter value + * @param[in] initialCount Initial Semaphore counter value + */ + Semaphore(uint32_t maxCount, uint32_t initialCount) { + /* Differentiate between a counting and a binary Semaphore */ + if (maxCount == 1u) { + _semaphore = osabstraction::semaphoreCreateBinaryStatic(&_semaphoreDef); + if (initialCount >= 1u) { + /* Check if Binary Semaphore is initially open */ + osabstraction::semaphoreGive(_semaphore); + } + } else { + _semaphore = osabstraction::semaphoreCreateCountingStatic(maxCount, initialCount, &_semaphoreDef); + } + } + + /// Deleted copy constructor + Semaphore(const Semaphore&) = delete; + + /// Deleted move constructor + Semaphore(Semaphore&&) = delete; + + /// Delete copy assignment operator @return + Semaphore& operator=(const Semaphore&) = delete; + + /// Deleted move assignment operator @return + Semaphore& operator=(Semaphore&&) = delete; + + /// Destructor + ~Semaphore(void) override { + osabstraction::semaphoreDelete(this->_semaphore); + } + + /** + * @brief Constructs and returns a Semaphore Reference to this Object + * @return Semaphore Reference Object + */ + SemaphoreReference getReference(void) const { + return SemaphoreReference(*this); //NOLINT(cppcoreguidelines-slicing) That is the wanted point here. + } + +private: + StaticSemaphoreType _semaphoreDef = {}; /**< Static Semaphore Definition */ +}; + +} // namespace osextension + +#endif // OSEXTENSION_SEMAPHORE_HPP diff --git a/OSAdapter/include/OsExtension/OsExtension/SemaphoreInterface.hpp b/OSAdapter/include/OsExtension/OsExtension/SemaphoreInterface.hpp new file mode 100644 index 0000000..8daa9ad --- /dev/null +++ b/OSAdapter/include/OsExtension/OsExtension/SemaphoreInterface.hpp @@ -0,0 +1,123 @@ +/** + * @file include/OsExtension/OsExtension/SemaphoreInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ + +#ifndef OSEXTENSION_SEMAPHOREINTERFACE_HPP +#define OSEXTENSION_SEMAPHOREINTERFACE_HPP + +#include +#include +#include + +namespace osextension { + +/** + * @brief Interface to a Semaphore. + */ +class SemaphoreInterface : public OsExtensionInterface { +public: + /** Reassign native OS Type for readability and portability */ + using SemaphoreTaskWokenTokenType = BaseType_t; + + /// Constructor + SemaphoreInterface(void) = default; + + /// Copy constructor + SemaphoreInterface(const SemaphoreInterface&) = default; + + /// Move constructor + SemaphoreInterface(SemaphoreInterface&&) noexcept = default; + + /// Copy assignment operator @return + SemaphoreInterface& operator=(const SemaphoreInterface&) = default; + + /// Move assignment operator @return + SemaphoreInterface& operator=(SemaphoreInterface&&) noexcept = default; + + /// Destructor + ~SemaphoreInterface(void) override = default; + + /** + * @brief Signals a Semaphore to give resources. + * + * @retval true Semaphore was signaled and resource given. + * @retval false Semaphore is not taken by anyone or maximum count is reached. + */ + virtual bool signal(void) = 0; + + /** + * @brief Signals a Semaphore to give resources from an ISR. + * + * @param[out] taskWoken Pointer to variable containing a bool expression + * whether or not a task was woken by this call. + * + * @retval true Semaphore was signaled and resource given. + * @retval false Semaphore is not taken by anyone or maximum count is reached. + */ + virtual bool signalFromIsr(SemaphoreTaskWokenTokenType* taskWoken) = 0; + + /** + * @brief Waits for this Semaphore Object resource. + * + * @param[in] timeout Time the task waits for Semaphore + * + * @retval true Resource is allocated and open for Task + * @retval false Timeout occurred + */ + virtual bool wait(uint32_t timeout) = 0; + + /** + * @brief Waits for this Semaphore Object resource within ISR Context. + * Note: There is no blocking in ISR, so if the resource is not available this + * call will return immediately. + * + * @param[out] taskWoken Pointer to variable containing a bool expression + * whether or not a task was woken by this call. + * + * @retval true Resource is allocated and open for Task + * @retval false Resource not availiable. + */ + virtual bool waitFromIsr(SemaphoreTaskWokenTokenType* taskWoken) = 0; + + /** + * @brief Returns count as integer of Semaphore Object. + * + * If semaphore is a counting semaphore then the semaphores + * current count is returned. If the semaphore is a binary semaphore + * then 1 is returned if the semaphore is available, and 0 is returned if the semaphore is blocked. + * + * @return count between 0 and N, 0 if blocked + */ + virtual size_t getCount(void) = 0; + + /** + * @brief Returns count as integer of Semaphore Object from ISR. + * + * If semaphore is a counting semaphore then the semaphores + * current count is returned. If the semaphore is a binary semaphore + * then 1 is returned if the semaphore is available, and 0 is returned if the semaphore is blocked. + * + * @return count between 0 and N, 0 if blocked + */ + virtual size_t getCountFromIsr(void) = 0; + + /** + * @brief Resets the given Semaphore to its initial state. + * + * @retval true Semaphore is reset. + * @retval false Resource is blocked or not available anymore + */ + virtual bool reset(void) = 0; +}; + +} // namespace osextension + +#endif // OSEXTENSION_SEMAPHORE_HPP diff --git a/OSAdapter/src/Observer/CMakeLists.txt b/OSAdapter/src/Observer/CMakeLists.txt new file mode 100644 index 0000000..bcc45d6 --- /dev/null +++ b/OSAdapter/src/Observer/CMakeLists.txt @@ -0,0 +1,29 @@ +set(MODULE_NAME "Observer") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_include_directories(${MODULE_NAME}-Interface + INTERFACE + ${PROJECT_SOURCE_DIR}/OSAdapter/include/${MODULE_NAME} + ) + +add_library(${MODULE_NAME} + INTERFACE + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + INTERFACE + ${MODULE_NAME}-Interface + ) diff --git a/OSAdapter/src/OsAbstraction/CMakeLists.txt b/OSAdapter/src/OsAbstraction/CMakeLists.txt new file mode 100644 index 0000000..3b6528e --- /dev/null +++ b/OSAdapter/src/OsAbstraction/CMakeLists.txt @@ -0,0 +1,37 @@ +set(MODULE_NAME "OsAbstraction") + +add_subdirectory(OsAdapter) + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_include_directories(${MODULE_NAME}-Interface + INTERFACE + ${PROJECT_SOURCE_DIR}/OSAdapter/include/${MODULE_NAME} + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + OsAdapter-Interface + ) + +add_library(${MODULE_NAME} + INTERFACE + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + INTERFACE + ${MODULE_NAME}-Interface + OsAdapter + ) diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/CMakeLists.txt b/OSAdapter/src/OsAbstraction/OsAdapter/CMakeLists.txt new file mode 100644 index 0000000..ca521ab --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/CMakeLists.txt @@ -0,0 +1,3 @@ +if(APP_OSADAPTER) + add_subdirectory(${APP_OSADAPTER}) +endif() \ No newline at end of file diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/CMakeLists.txt b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/CMakeLists.txt new file mode 100644 index 0000000..3913163 --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/CMakeLists.txt @@ -0,0 +1,46 @@ +set(MODULE_NAME "OsAdapter") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + freertos-Interface + OsAbstraction-Interface + ) + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/FreeRtosAdapter.cpp + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + ${MODULE_NAME}-Interface + PRIVATE + OsAbstraction + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + freertos + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + ) diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/FreeRtosAdapter.cpp b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/FreeRtosAdapter.cpp new file mode 100644 index 0000000..22802e8 --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosAdapter/FreeRtosAdapter.cpp @@ -0,0 +1,350 @@ +/** + * @file src/OsAbstraction/OsAdapter/FreeRtosAdapter/FreeRtosAdapter.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include + +namespace osabstraction { + +extern "C" { + +/// @copydoc queueCreateSetStatic() +QueueSetHandle_t queueCreateSetStatic(const UBaseType_t uxEventQueueLength, uint8_t* pucQueueSetStorage, StaticQueue_t* pxStaticQueueSet) { + QueueSetHandle_t pxQueue = queueGenericCreateStatic(uxEventQueueLength, static_cast(sizeof(void*)), pucQueueSetStorage, + pxStaticQueueSet, queueQUEUE_TYPE_SET); + + return pxQueue; +} + +/* task.h abstraction */ + +/// @copydoc taskDelay() +void taskDelay(const TickType_t xTicksToDelay) { + return vTaskDelay(xTicksToDelay); +} + +/// @copydoc taskGetName() +char* taskGetName(TaskHandle_t xTaskToQuery) { + return pcTaskGetName(xTaskToQuery); +} + +/// @copydoc taskGetTickCount() +TickType_t taskGetTickCount(void) { + return xTaskGetTickCount(); +} + +/// @copydoc taskGetCurrentTaskHandle() +TaskHandle_t taskGetCurrentTaskHandle(void) { + return xTaskGetCurrentTaskHandle(); +} + +/// @copydoc taskGetTickCountFromISR() +TickType_t taskGetTickCountFromISR(void) { + return xTaskGetTickCountFromISR(); +} + +/// @copydoc taskGetSchedulerState() +BaseType_t taskGetSchedulerState(void) { + return xTaskGetSchedulerState(); +} + +/// @copydoc taskSuspend() +void taskSuspend(TaskHandle_t xTaskToSuspend) { + vTaskSuspend(xTaskToSuspend); +} + +/* queue.h abstraction QueueSets */ + +/// @copydoc queueSelectFromSet() +QueueSetMemberHandle_t queueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks) { + return xQueueSelectFromSet(xQueueSet, xBlockTimeTicks); +} + +/// @copydoc queueAddToSet() +BaseType_t queueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { + return xQueueAddToSet(xQueueOrSemaphore, xQueueSet); +} + +/// @copydoc queueRemoveFromSet() +BaseType_t queueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { + return xQueueRemoveFromSet(xQueueOrSemaphore, xQueueSet); +} + +/* queue.h abstraction */ + +/// @copydoc queueGenericCreateStatic() +QueueHandle_t queueGenericCreateStatic(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t* pucQueueStorage, + StaticQueue_t* pxStaticQueue, const uint8_t ucQueueType) { + return xQueueGenericCreateStatic(uxQueueLength, uxItemSize, pucQueueStorage, pxStaticQueue, ucQueueType); +} + +/// @copydoc queueCreateStatic() +QueueHandle_t queueCreateStatic(UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t* pucQueueStorage, StaticQueue_t* pxQueueBuffer) { + return xQueueGenericCreateStatic((uxQueueLength), (uxItemSize), (pucQueueStorage), (pxQueueBuffer), (queueQUEUE_TYPE_BASE)); +} + +/// @copydoc queueRegister() +void queueRegister(QueueHandle_t xQueue, const char* name) { +#if (configQUEUE_REGISTRY_SIZE > 0) + vQueueAddToRegistry(xQueue, name); +#endif +} + +/// @copydoc queueUnregister() +void queueUnregister(QueueHandle_t xQueue) { +#if (configQUEUE_REGISTRY_SIZE > 0) + vQueueUnregisterQueue(xQueue); +#endif +} + +/// @copydoc queueGenericSend() +BaseType_t queueGenericSend(QueueHandle_t xQueue, const void* const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition) { + return xQueueGenericSend(xQueue, pvItemToQueue, xTicksToWait, xCopyPosition); +} + +/// @copydoc queueSend() +BaseType_t queueSend(QueueHandle_t xQueue, const void* pvItemToQueue, TickType_t xTicksToWait) { + return xQueueGenericSend((xQueue), (pvItemToQueue), (xTicksToWait), queueSEND_TO_BACK); +} + +/// @copydoc queueGenericSendFromISR() +BaseType_t queueGenericSendFromISR(QueueHandle_t xQueue, const void* const pvItemToQueue, BaseType_t* const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition) { + return xQueueGenericSendFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken, xCopyPosition); +} + +/// @copydoc queueSendFromISR() +BaseType_t queueSendFromISR(QueueHandle_t xQueue, const void* pvItemToQueue, BaseType_t* pxHigherPriorityTaskWoken) { + return xQueueGenericSendFromISR((xQueue), (pvItemToQueue), (pxHigherPriorityTaskWoken), queueSEND_TO_BACK); +} + +/// @copydoc queueReceive() +BaseType_t queueReceive(QueueHandle_t xQueue, void* pvBuffer, TickType_t xTicksToWait) { + return xQueueReceive(xQueue, pvBuffer, xTicksToWait); +} + +/// @copydoc queueReceiveFromISR() +BaseType_t queueReceiveFromISR(QueueHandle_t xQueue, void* pvBuffer, BaseType_t* pxHigherPriorityTaskWoken) { + return xQueueReceiveFromISR(xQueue, pvBuffer, pxHigherPriorityTaskWoken); +} + +/// @copydoc queueGenericReset() +BaseType_t queueGenericReset(QueueHandle_t xQueue, BaseType_t xNewQueue) { + return xQueueGenericReset(xQueue, xNewQueue); +} + +/// @copydoc queueReset() +BaseType_t queueReset(QueueHandle_t xQueue) { + return xQueueGenericReset(xQueue, pdFALSE); +} + +/// @copydoc queueSpacesAvailable() +UBaseType_t queueSpacesAvailable(QueueHandle_t xQueue) { + return uxQueueSpacesAvailable(xQueue); +} + +/// @copydoc queueDelete() +void queueDelete(QueueHandle_t xQueue) { + vQueueDelete(xQueue); +} + +/* stream_buffer.h abstraction */ + +/// @copydoc streamBufferGenericCreateStatic() +StreamBufferHandle_t streamBufferGenericCreateStatic(size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, + uint8_t* const pucStreamBufferStorageArea, + StaticStreamBuffer_t* const pxStaticStreamBuffer) { + return xStreamBufferGenericCreateStatic(xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, pucStreamBufferStorageArea, pxStaticStreamBuffer); +} + +/// @copydoc streamBufferSend() +size_t streamBufferSend(StreamBufferHandle_t xStreamBuffer, const void* pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait) { + return xStreamBufferSend(xStreamBuffer, pvTxData, xDataLengthBytes, xTicksToWait); +} + +/// @copydoc streamBufferSendFromISR() +size_t streamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + return xStreamBufferSendFromISR(xStreamBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc streamBufferReceive() +size_t streamBufferReceive(StreamBufferHandle_t xStreamBuffer, void* pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait) { + return xStreamBufferReceive(xStreamBuffer, pvRxData, xBufferLengthBytes, xTicksToWait); +} + +/// @copydoc streamBufferReceiveFromISR() +size_t streamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + return xStreamBufferReceiveFromISR(xStreamBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc streamBufferDelete() +void streamBufferDelete(StreamBufferHandle_t xStreamBuffer) { + vStreamBufferDelete(xStreamBuffer); +} + +/// @copydoc streamBufferBytesAvailable() +size_t streamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer) { + return xStreamBufferBytesAvailable(xStreamBuffer); +} + +/// @copydoc streamBufferSpacesAvailable() +size_t streamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer) { + return xStreamBufferSpacesAvailable(xStreamBuffer); +} + +/// @copydoc streamBufferReset() +BaseType_t streamBufferReset(StreamBufferHandle_t xStreamBuffer) { + return xStreamBufferReset(xStreamBuffer); +} + +/// @copydoc streamBufferIsEmpty() +BaseType_t streamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer) { + return xStreamBufferIsEmpty(xStreamBuffer); +} + +/// @copydoc streamBufferIsFull() +BaseType_t streamBufferIsFull(StreamBufferHandle_t xStreamBuffer) { + return xStreamBufferIsFull(xStreamBuffer); +} + +/* message_buffer.h abstraction */ + +/// @copydoc messageBufferCreateStatic() +MessageBufferHandle_t messageBufferCreateStatic(size_t xBufferSizeBytes, uint8_t* pucMessageBufferStorageArea, + StaticMessageBuffer_t* pxStaticMessageBuffer) { + return static_cast(streamBufferGenericCreateStatic(xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer)); +} + +/// @copydoc messageBufferSend() +size_t messageBufferSend(MessageBufferHandle_t xMessageBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait) { + return streamBufferSend(static_cast(xMessageBuffer), pvTxData, xDataLengthBytes, xTicksToWait); +} + +/// @copydoc messageBufferSendFromISR() +size_t messageBufferSendFromISR(MessageBufferHandle_t xMessageBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken) { + return streamBufferSendFromISR(static_cast(xMessageBuffer), pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc messageBufferReceive() +size_t messageBufferReceive(MessageBufferHandle_t xMessageBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait) { + return streamBufferReceive(static_cast(xMessageBuffer), pvRxData, xBufferLengthBytes, xTicksToWait); +} + +/// @copydoc messageBufferReceiveFromISR() +size_t messageBufferReceiveFromISR(MessageBufferHandle_t xMessageBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken) { + return streamBufferReceiveFromISR(static_cast(xMessageBuffer), pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc messageBufferDelete() +void messageBufferDelete(MessageBufferHandle_t xMessageBuffer) { + streamBufferDelete(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferSpacesAvailable() +size_t messageBufferSpacesAvailable(MessageBufferHandle_t xMessageBuffer) { + return streamBufferSpacesAvailable(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferReset() +BaseType_t messageBufferReset(MessageBufferHandle_t xMessageBuffer) { + return streamBufferReset(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferIsEmpty() +BaseType_t messageBufferIsEmpty(MessageBufferHandle_t xMessageBuffer) { + return streamBufferIsEmpty(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferIsFull() +BaseType_t messageBufferIsFull(MessageBufferHandle_t xMessageBuffer) { + return streamBufferIsFull(static_cast(xMessageBuffer)); +} + +/* semphr.h abstraction */ + +/// @copydoc semaphoreCreateCountingStatic() +SemaphoreHandle_t semaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t* pxSemaphoreBuffer) { + return xQueueCreateCountingSemaphoreStatic((uxMaxCount), (uxInitialCount), (pxSemaphoreBuffer)); +} + +/// @copydoc semaphoreCreateBinaryStatic() +SemaphoreHandle_t semaphoreCreateBinaryStatic(StaticSemaphore_t* pxSemaphoreBuffer) { + return xSemaphoreCreateBinaryStatic(pxSemaphoreBuffer); +} + +/// @copydoc mutexCreateStatic() +SemaphoreHandle_t mutexCreateStatic(StaticSemaphore_t* pxMutexBuffer) { + return xSemaphoreCreateMutexStatic(pxMutexBuffer); +} + +/// @copydoc semaphoreDelete() +void semaphoreDelete(SemaphoreHandle_t xSemaphore) { + queueDelete(static_cast(xSemaphore)); +} + +/// @copydoc semaphoreTake() +BaseType_t semaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime) { + return xQueueSemaphoreTake((xSemaphore), (xBlockTime)); +} + +/// @copydoc semaphoreTakeFromISR() +BaseType_t semaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken) { + return queueReceiveFromISR(static_cast(xSemaphore), nullptr, (pxHigherPriorityTaskWoken)); +} + +/// @copydoc semaphoreGive() +BaseType_t semaphoreGive(SemaphoreHandle_t xSemaphore) { + return queueGenericSend(static_cast(xSemaphore), nullptr, semGIVE_BLOCK_TIME, queueSEND_TO_BACK); +} + +/// @copydoc semaphoreGiveFromISR() +BaseType_t semaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken) { + return xQueueGiveFromISR(static_cast(xSemaphore), (pxHigherPriorityTaskWoken)); +} + +/// @copydoc semaphoreGetCount() +UBaseType_t semaphoreGetCount(SemaphoreHandle_t xSemaphore) { + return uxSemaphoreGetCount(xSemaphore); +} + +/// @copydoc semaphoreGetCountFromISR() +UBaseType_t semaphoreGetCountFromISR(SemaphoreHandle_t xSemaphore) { + return uxQueueMessagesWaitingFromISR(static_cast(xSemaphore)); +} + +/* portmacro.h abstraction */ + +/// @copydoc yieldFromISR() +void yieldFromISR(const BaseType_t xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +} // extern "C" + +} // namespace osabstraction diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/CMakeLists.txt b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/CMakeLists.txt new file mode 100644 index 0000000..fa402ca --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/CMakeLists.txt @@ -0,0 +1,42 @@ +set(MODULE_NAME "OsAdapter") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + freertos-Interface + OsAbstraction-Interface + ) + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/FreeRtosTestAdapter.cpp + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + ${MODULE_NAME}-Interface + freertos-Mock + PRIVATE + OsAbstraction + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + ) diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/FreeRtosTestAdapter.cpp b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/FreeRtosTestAdapter.cpp new file mode 100644 index 0000000..2e82b83 --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/FreeRtosTestAdapter.cpp @@ -0,0 +1,350 @@ +/** + * @file src/OsAbstraction/OsAdapter/FreeRtosTestAdapter/FreeRtosTestAdapter.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +#include + +#include + +namespace osabstraction { + +extern "C" { + +/// @copydoc queueCreateSetStatic() +QueueSetHandle_t queueCreateSetStatic(const UBaseType_t uxEventQueueLength, uint8_t* pucQueueSetStorage, StaticQueue_t* pxStaticQueueSet) { + QueueSetHandle_t pxQueue = queueGenericCreateStatic(uxEventQueueLength, static_cast(sizeof(void*)), pucQueueSetStorage, + pxStaticQueueSet, queueQUEUE_TYPE_SET); + + return pxQueue; +} + +/* task.h abstraction */ + +/// @copydoc taskDelay() +void taskDelay(const TickType_t xTicksToDelay) { + return freertos::mock->vTaskDelay(xTicksToDelay); +} + +/// @copydoc taskGetName() +char* taskGetName(TaskHandle_t xTaskToQuery) { + return freertos::mock->pcTaskGetName(xTaskToQuery); +} + +/// @copydoc taskGetTickCount() +TickType_t taskGetTickCount(void) { + return freertos::mock->xTaskGetTickCount(); +} + +/// @copydoc taskGetCurrentTaskHandle() +TaskHandle_t taskGetCurrentTaskHandle(void) { + return freertos::mock->xTaskGetCurrentTaskHandle(); +} + +/// @copydoc taskGetTickCountFromISR() +TickType_t taskGetTickCountFromISR(void) { + return freertos::mock->xTaskGetTickCountFromISR(); +} + +/// @copydoc taskGetSchedulerState() +BaseType_t taskGetSchedulerState(void) { + return freertos::mock->xTaskGetSchedulerState(); +} + +/// @copydoc taskSuspend() +void taskSuspend(TaskHandle_t xTaskToSuspend) { + freertos::mock->vTaskSuspend(xTaskToSuspend); +} + +/* queue.h abstraction QueueSets */ + +/// @copydoc queueSelectFromSet() +QueueSetMemberHandle_t queueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks) { + return freertos::mock->xQueueSelectFromSet(xQueueSet, xBlockTimeTicks); +} + +/// @copydoc queueAddToSet() +BaseType_t queueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { + return freertos::mock->xQueueAddToSet(xQueueOrSemaphore, xQueueSet); +} + +/// @copydoc queueRemoveFromSet() +BaseType_t queueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet) { + return freertos::mock->xQueueRemoveFromSet(xQueueOrSemaphore, xQueueSet); +} + +/* queue.h abstraction */ + +/// @copydoc queueGenericCreateStatic() +QueueHandle_t queueGenericCreateStatic(const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t* pucQueueStorage, + StaticQueue_t* pxStaticQueue, const uint8_t ucQueueType) { + return freertos::mock->xQueueGenericCreateStatic(uxQueueLength, uxItemSize, pucQueueStorage, pxStaticQueue, ucQueueType); +} + +/// @copydoc queueCreateStatic() +QueueHandle_t queueCreateStatic(UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t* pucQueueStorage, StaticQueue_t* pxQueueBuffer) { + return freertos::mock->xQueueGenericCreateStatic((uxQueueLength), (uxItemSize), (pucQueueStorage), (pxQueueBuffer), (queueQUEUE_TYPE_BASE)); +} + +/// @copydoc queueRegister() +void queueRegister(QueueHandle_t xQueue, const char* name) { +#if (configQUEUE_REGISTRY_SIZE > 0) + freertos::mock->vQueueAddToRegistry(xQueue, name); +#endif +} + +/// @copydoc queueUnregister() +void queueUnregister(QueueHandle_t xQueue) { +#if (configQUEUE_REGISTRY_SIZE > 0) + freertos::mock->vQueueUnregisterQueue(xQueue); +#endif +} + +/// @copydoc queueGenericSend() +BaseType_t queueGenericSend(QueueHandle_t xQueue, const void* const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition) { + return freertos::mock->xQueueGenericSend(xQueue, pvItemToQueue, xTicksToWait, xCopyPosition); +} + +/// @copydoc queueSend() +BaseType_t queueSend(QueueHandle_t xQueue, const void* pvItemToQueue, TickType_t xTicksToWait) { + return freertos::mock->xQueueGenericSend((xQueue), (pvItemToQueue), (xTicksToWait), queueSEND_TO_BACK); +} + +/// @copydoc queueGenericSendFromISR() +BaseType_t queueGenericSendFromISR(QueueHandle_t xQueue, const void* const pvItemToQueue, BaseType_t* const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition) { + return freertos::mock->xQueueGenericSendFromISR(xQueue, pvItemToQueue, pxHigherPriorityTaskWoken, xCopyPosition); +} + +/// @copydoc queueSendFromISR() +BaseType_t queueSendFromISR(QueueHandle_t xQueue, const void* pvItemToQueue, BaseType_t* pxHigherPriorityTaskWoken) { + return freertos::mock->xQueueGenericSendFromISR((xQueue), (pvItemToQueue), (pxHigherPriorityTaskWoken), queueSEND_TO_BACK); +} + +/// @copydoc queueReceive() +BaseType_t queueReceive(QueueHandle_t xQueue, void* pvBuffer, TickType_t xTicksToWait) { + return freertos::mock->xQueueReceive(xQueue, pvBuffer, xTicksToWait); +} + +/// @copydoc queueReceiveFromISR() +BaseType_t queueReceiveFromISR(QueueHandle_t xQueue, void* pvBuffer, BaseType_t* pxHigherPriorityTaskWoken) { + return freertos::mock->xQueueReceiveFromISR(xQueue, pvBuffer, pxHigherPriorityTaskWoken); +} + +/// @copydoc queueGenericReset() +BaseType_t queueGenericReset(QueueHandle_t xQueue, BaseType_t xNewQueue) { + return freertos::mock->xQueueGenericReset(xQueue, xNewQueue); +} + +/// @copydoc queueReset() +BaseType_t queueReset(QueueHandle_t xQueue) { + return freertos::mock->xQueueGenericReset(xQueue, pdFALSE); +} + +/// @copydoc queueSpacesAvailable() +UBaseType_t queueSpacesAvailable(QueueHandle_t xQueue) { + return freertos::mock->uxQueueSpacesAvailable(xQueue); +} + +/// @copydoc queueDelete() +void queueDelete(QueueHandle_t xQueue) { + freertos::mock->vQueueDelete(xQueue); +} + +/* stream_buffer.h abstraction */ + +/// @copydoc streamBufferGenericCreateStatic() +StreamBufferHandle_t streamBufferGenericCreateStatic(size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, + uint8_t* const pucStreamBufferStorageArea, + StaticStreamBuffer_t* const pxStaticStreamBuffer) { + return freertos::mock->xStreamBufferGenericCreateStatic(xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, pucStreamBufferStorageArea, pxStaticStreamBuffer); +} + +/// @copydoc streamBufferSend() +size_t streamBufferSend(StreamBufferHandle_t xStreamBuffer, const void* pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait) { + return freertos::mock->xStreamBufferSend(xStreamBuffer, pvTxData, xDataLengthBytes, xTicksToWait); +} + +/// @copydoc streamBufferSendFromISR() +size_t streamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + return freertos::mock->xStreamBufferSendFromISR(xStreamBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc streamBufferReceive() +size_t streamBufferReceive(StreamBufferHandle_t xStreamBuffer, void* pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait) { + return freertos::mock->xStreamBufferReceive(xStreamBuffer, pvRxData, xBufferLengthBytes, xTicksToWait); +} + +/// @copydoc streamBufferReceiveFromISR() +size_t streamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + return freertos::mock->xStreamBufferReceiveFromISR(xStreamBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc streamBufferDelete() +void streamBufferDelete(StreamBufferHandle_t xStreamBuffer) { + freertos::mock->vStreamBufferDelete(xStreamBuffer); +} + +/// @copydoc streamBufferBytesAvailable() +size_t streamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer) { + return freertos::mock->xStreamBufferBytesAvailable(xStreamBuffer); +} + +/// @copydoc streamBufferSpacesAvailable() +size_t streamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer) { + return freertos::mock->xStreamBufferSpacesAvailable(xStreamBuffer); +} + +/// @copydoc streamBufferReset() +BaseType_t streamBufferReset(StreamBufferHandle_t xStreamBuffer) { + return freertos::mock->xStreamBufferReset(xStreamBuffer); +} + +/// @copydoc streamBufferIsEmpty() +BaseType_t streamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer) { + return freertos::mock->xStreamBufferIsEmpty(xStreamBuffer); +} + +/// @copydoc streamBufferIsFull() +BaseType_t streamBufferIsFull(StreamBufferHandle_t xStreamBuffer) { + return freertos::mock->xStreamBufferIsFull(xStreamBuffer); +} + +/* message_buffer.h abstraction */ + +/// @copydoc messageBufferCreateStatic() +MessageBufferHandle_t messageBufferCreateStatic(size_t xBufferSizeBytes, uint8_t* pucMessageBufferStorageArea, + StaticMessageBuffer_t* pxStaticMessageBuffer) { + return static_cast(streamBufferGenericCreateStatic(xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer)); +} + +/// @copydoc messageBufferSend() +size_t messageBufferSend(MessageBufferHandle_t xMessageBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait) { + return streamBufferSend(static_cast(xMessageBuffer), pvTxData, xDataLengthBytes, xTicksToWait); +} + +/// @copydoc messageBufferSendFromISR() +size_t messageBufferSendFromISR(MessageBufferHandle_t xMessageBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken) { + return streamBufferSendFromISR(static_cast(xMessageBuffer), pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc messageBufferReceive() +size_t messageBufferReceive(MessageBufferHandle_t xMessageBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait) { + return streamBufferReceive(static_cast(xMessageBuffer), pvRxData, xBufferLengthBytes, xTicksToWait); +} + +/// @copydoc messageBufferReceiveFromISR() +size_t messageBufferReceiveFromISR(MessageBufferHandle_t xMessageBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* pxHigherPriorityTaskWoken) { + return streamBufferReceiveFromISR(static_cast(xMessageBuffer), pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken); +} + +/// @copydoc messageBufferDelete() +void messageBufferDelete(MessageBufferHandle_t xMessageBuffer) { + streamBufferDelete(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferSpacesAvailable() +size_t messageBufferSpacesAvailable(MessageBufferHandle_t xMessageBuffer) { + return streamBufferSpacesAvailable(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferReset() +BaseType_t messageBufferReset(MessageBufferHandle_t xMessageBuffer) { + return streamBufferReset(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferIsEmpty() +BaseType_t messageBufferIsEmpty(MessageBufferHandle_t xMessageBuffer) { + return streamBufferIsEmpty(static_cast(xMessageBuffer)); +} + +/// @copydoc messageBufferIsFull() +BaseType_t messageBufferIsFull(MessageBufferHandle_t xMessageBuffer) { + return streamBufferIsFull(static_cast(xMessageBuffer)); +} + +/* semphr.h abstraction */ + +/// @copydoc semaphoreCreateCountingStatic() +SemaphoreHandle_t semaphoreCreateCountingStatic(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t* pxSemaphoreBuffer) { + return freertos::mock->xQueueCreateCountingSemaphoreStatic((uxMaxCount), (uxInitialCount), (pxSemaphoreBuffer)); +} + +/// @copydoc semaphoreCreateBinaryStatic() +SemaphoreHandle_t semaphoreCreateBinaryStatic(StaticSemaphore_t* pxSemaphoreBuffer) { + return freertos::mock->xSemaphoreCreateBinaryStatic(pxSemaphoreBuffer); +} + +/// @copydoc mutexCreateStatic() +SemaphoreHandle_t mutexCreateStatic(StaticSemaphore_t* pxMutexBuffer) { + return freertos::mock->xSemaphoreCreateMutexStatic(pxMutexBuffer); +} + +/// @copydoc semaphoreDelete() +void semaphoreDelete(SemaphoreHandle_t xSemaphore) { + queueDelete(static_cast(xSemaphore)); +} + +/// @copydoc semaphoreTake() +BaseType_t semaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xBlockTime) { + return freertos::mock->xQueueSemaphoreTake((xSemaphore), (xBlockTime)); +} + +/// @copydoc semaphoreTakeFromISR() +BaseType_t semaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken) { + return queueReceiveFromISR(static_cast(xSemaphore), nullptr, (pxHigherPriorityTaskWoken)); +} + +/// @copydoc semaphoreGive() +BaseType_t semaphoreGive(SemaphoreHandle_t xSemaphore) { + return queueGenericSend(static_cast(xSemaphore), nullptr, semGIVE_BLOCK_TIME, queueSEND_TO_BACK); +} + +/// @copydoc semaphoreGiveFromISR() +BaseType_t semaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, BaseType_t* pxHigherPriorityTaskWoken) { + return freertos::mock->xQueueGiveFromISR(static_cast(xSemaphore), (pxHigherPriorityTaskWoken)); +} + +/// @copydoc semaphoreGetCount() +UBaseType_t semaphoreGetCount(SemaphoreHandle_t xSemaphore) { + return freertos::mock->uxQueueMessagesWaiting(xSemaphore); +} + +/// @copydoc semaphoreGetCountFromISR() +UBaseType_t semaphoreGetCountFromISR(SemaphoreHandle_t xSemaphore) { + return freertos::mock->uxQueueMessagesWaitingFromISR(static_cast(xSemaphore)); +} + +/* portmacro.h abstraction */ + +/// @copydoc yieldFromISR() +void yieldFromISR(const BaseType_t xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +} // extern "C" + +} // namespace osabstraction diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/CMakeLists.txt b/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/CMakeLists.txt new file mode 100644 index 0000000..1b01c03 --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/CMakeLists.txt @@ -0,0 +1,45 @@ +set(MODULE_NAME "OsAdapter") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + OsAbstraction-Interface + ) + +target_compile_definitions(${MODULE_NAME}-Interface + INTERFACE + DISABLE_FREERTOS + ) + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/NullOsAdapter.cpp + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + ${MODULE_NAME}-Interface + PRIVATE + OsAbstraction + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + ) diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/NullOsAdapter.cpp b/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/NullOsAdapter.cpp new file mode 100644 index 0000000..7558cdb --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/NullOsAdapter/NullOsAdapter.cpp @@ -0,0 +1,311 @@ +/** + * @file src/OsAbstraction/OsAdapter/NullOsAdapter/NullOsAdapter.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +#include + +namespace osabstraction { + +QueueSetHandle_t queueCreateSetStatic(const UBaseType_t uxEventQueueLength, uint8_t* pucQueueSetStorage, StaticQueue_t* pxStaticQueueSet) { + QueueSetHandle_t pxQueue = queueGenericCreateStatic(uxEventQueueLength, static_cast(sizeof(void*)), pucQueueSetStorage, + pxStaticQueueSet, queueQUEUE_TYPE_SET); + + return pxQueue; +} + +/* task.h abstraction */ + +void taskDelay(const TickType_t xTicksToDelay __attribute__((unused))) { +} + +char* taskGetName(TaskHandle_t xTaskToQuery __attribute__((unused))) { + return nullptr; +} + +TaskHandle_t taskGetCurrentTaskHandle(void) { + return nullptr; +} + +TickType_t taskGetTickCount(void) { + return 0u; +} + +TickType_t taskGetTickCountFromISR(void) { + return 0u; +} + +BaseType_t taskGetSchedulerState(void) { + // scheduler not started is appropriate value: No OS --> no scheduler + // value for 'scheduler not stated' adopted from FreeRTOS task.h + return taskSCHEDULER_NOT_STARTED; +} + +void taskSuspend(TaskHandle_t xTaskToSuspend __attribute__((unused))) { +} + +/* queue.h abstraction QueueSets */ + +QueueSetMemberHandle_t queueSelectFromSet(QueueSetHandle_t xQueueSet __attribute__((unused)), + TickType_t xBlockTimeTicks __attribute__((unused))) { + return nullptr; +} + +BaseType_t queueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore __attribute__((unused)), + QueueSetHandle_t xQueueSet __attribute__((unused))) { + return 0u; +} + +BaseType_t queueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore __attribute__((unused)), + QueueSetHandle_t xQueueSet __attribute__((unused))) { + return 0u; +} + +/* queue.h abstraction */ + +QueueHandle_t queueGenericCreateStatic(const UBaseType_t uxQueueLength __attribute__((unused)), + const UBaseType_t uxItemSize __attribute__((unused)), + uint8_t* pucQueueStorage __attribute__((unused)), + StaticQueue_t* pxStaticQueue __attribute__((unused)), + const uint8_t ucQueueType __attribute__((unused))) { + return nullptr; +} + +QueueHandle_t queueCreateStatic(UBaseType_t uxQueueLength __attribute__((unused)), UBaseType_t uxItemSize __attribute__((unused)), uint8_t* pucQueueStorage __attribute__((unused)), StaticQueue_t* pxQueueBuffer __attribute__((unused))) { + return nullptr; +} + +void queueRegister(QueueHandle_t xQueue __attribute__((unused)), const char* name __attribute__((unused))) { +} + +void queueUnregister(QueueHandle_t xQueue __attribute__((unused))) { +} + +BaseType_t queueGenericSend(QueueHandle_t xQueue __attribute__((unused)), + const void* const pvItemToQueue __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused)), + const BaseType_t xCopyPosition __attribute__((unused))) { + return 0u; +} + +BaseType_t queueSend(QueueHandle_t xQueue __attribute__((unused)), + const void* pvItemToQueue __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +BaseType_t queueGenericSendFromISR(QueueHandle_t xQueue __attribute__((unused)), + const void* const pvItemToQueue __attribute__((unused)), + BaseType_t* const pxHigherPriorityTaskWoken __attribute__((unused)), + const BaseType_t xCopyPosition __attribute__((unused))) { + return 0u; +} + +BaseType_t queueSendFromISR(QueueHandle_t xQueue __attribute__((unused)), + const void* pvItemToQueue __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +BaseType_t queueReceive(QueueHandle_t xQueue __attribute__((unused)), + void* pvBuffer __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +BaseType_t queueReceiveFromISR(QueueHandle_t xQueue __attribute__((unused)), + void* pvBuffer __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +BaseType_t queueGenericReset(QueueHandle_t xQueue __attribute__((unused)), + BaseType_t xNewQueue __attribute__((unused))) { + return 0u; +} + +BaseType_t queueReset(QueueHandle_t xQueue __attribute__((unused))) { + return 0u; +} + +UBaseType_t queueSpacesAvailable(QueueHandle_t xQueue __attribute__((unused))) { + return 0u; +} + +void queueDelete(QueueHandle_t xQueue __attribute__((unused))) { +} + +/* stream_buffer.h abstraction */ + +StreamBufferHandle_t streamBufferGenericCreateStatic(size_t xBufferSizeBytes __attribute__((unused)), + size_t xTriggerLevelBytes __attribute__((unused)), + BaseType_t xIsMessageBuffer __attribute__((unused)), + uint8_t* const pucStreamBufferStorageArea __attribute__((unused)), + StaticStreamBuffer_t* const pxStaticStreamBuffer __attribute__((unused))) { + return nullptr; +} + +size_t streamBufferSend(StreamBufferHandle_t xStreamBuffer __attribute__((unused)), + const void* pvTxData __attribute__((unused)), + size_t xDataLengthBytes __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +size_t streamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer __attribute__((unused)), + const void* pvTxData __attribute__((unused)), + size_t xDataLengthBytes __attribute__((unused)), + BaseType_t* const pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +size_t streamBufferReceive(StreamBufferHandle_t xStreamBuffer __attribute__((unused)), + void* pvRxData __attribute__((unused)), + size_t xBufferLengthBytes __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +size_t streamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer __attribute__((unused)), + void* pvRxData __attribute__((unused)), + size_t xBufferLengthBytes __attribute__((unused)), + BaseType_t* const pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +void streamBufferDelete(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { +} + +size_t streamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { + return 0u; +} + +size_t streamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { + return 0u; +} + +BaseType_t streamBufferReset(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { + return 0u; +} + +BaseType_t streamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { + return 0u; +} + +BaseType_t streamBufferIsFull(StreamBufferHandle_t xStreamBuffer __attribute__((unused))) { + return 0u; +} + +/* message_buffer.h abstraction */ + +MessageBufferHandle_t messageBufferCreateStatic(size_t xBufferSizeBytes __attribute__((unused)), + uint8_t* pucMessageBufferStorageArea __attribute__((unused)), + StaticMessageBuffer_t* pxStaticMessageBuffer __attribute__((unused))) { + return nullptr; +} + +size_t messageBufferSend(MessageBufferHandle_t xMessageBuffer __attribute__((unused)), + const void* pvTxData __attribute__((unused)), + size_t xDataLengthBytes __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +size_t messageBufferSendFromISR(MessageBufferHandle_t xMessageBuffer __attribute__((unused)), + const void* pvTxData __attribute__((unused)), + size_t xDataLengthBytes __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +size_t messageBufferReceive(MessageBufferHandle_t xMessageBuffer __attribute__((unused)), + void* pvRxData __attribute__((unused)), + size_t xBufferLengthBytes __attribute__((unused)), + TickType_t xTicksToWait __attribute__((unused))) { + return 0u; +} + +size_t messageBufferReceiveFromISR(MessageBufferHandle_t xMessageBuffer __attribute__((unused)), + void* pvRxData __attribute__((unused)), + size_t xBufferLengthBytes __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +void messageBufferDelete(MessageBufferHandle_t xMessageBuffer __attribute__((unused))) { +} + +size_t messageBufferSpacesAvailable(MessageBufferHandle_t xMessageBuffer) { + return streamBufferSpacesAvailable(static_cast(xMessageBuffer)); +} + +BaseType_t messageBufferReset(MessageBufferHandle_t xMessageBuffer) { + return streamBufferReset(static_cast(xMessageBuffer)); +} + +BaseType_t messageBufferIsEmpty(MessageBufferHandle_t xMessageBuffer __attribute__((unused))) { + return 0u; +} + +BaseType_t messageBufferIsFull(MessageBufferHandle_t xMessageBuffer __attribute__((unused))) { + return 0u; +} + +/* semphr.h abstraction */ + +SemaphoreHandle_t semaphoreCreateCountingStatic(UBaseType_t uxMaxCount __attribute__((unused)), + UBaseType_t uxInitialCount __attribute__((unused)), + StaticSemaphore_t* pxSemaphoreBuffer __attribute__((unused))) { + return nullptr; +} + +SemaphoreHandle_t semaphoreCreateBinaryStatic(StaticSemaphore_t* pxSemaphoreBuffer __attribute__((unused))) { + return nullptr; +} + +SemaphoreHandle_t mutexCreateStatic(StaticSemaphore_t* pxMutexBuffer __attribute__((unused))) { + return nullptr; +} + +void semaphoreDelete(SemaphoreHandle_t xSemaphore __attribute__((unused))) { +} + +BaseType_t semaphoreTake(SemaphoreHandle_t xSemaphore __attribute__((unused)), + TickType_t xBlockTime __attribute__((unused))) { + return 0u; +} + +BaseType_t semaphoreTakeFromISR(SemaphoreHandle_t xSemaphore __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +BaseType_t semaphoreGive(SemaphoreHandle_t xSemaphore __attribute__((unused))) { + return 0u; +} + +BaseType_t semaphoreGiveFromISR(SemaphoreHandle_t xSemaphore __attribute__((unused)), + BaseType_t* pxHigherPriorityTaskWoken __attribute__((unused))) { + return 0u; +} + +UBaseType_t semaphoreGetCount(SemaphoreHandle_t xSemaphore __attribute__((unused))) { + return 0u; +} + +UBaseType_t semaphoreGetCountFromISR(SemaphoreHandle_t xSemaphore __attribute__((unused))) { + return 0u; +} + +/* portmacro.h abstraction */ + +void yieldFromISR(const BaseType_t xHigherPriorityTaskWoken __attribute__((unused))) { +} + +} // namespace osabstraction diff --git a/OSAdapter/src/OsAbstraction/OsAdapter/TestOsAdapter/CMakeLists.txt b/OSAdapter/src/OsAbstraction/OsAdapter/TestOsAdapter/CMakeLists.txt new file mode 100644 index 0000000..5b22756 --- /dev/null +++ b/OSAdapter/src/OsAbstraction/OsAdapter/TestOsAdapter/CMakeLists.txt @@ -0,0 +1,39 @@ +set(MODULE_NAME "OsAdapter") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_include_directories(${MODULE_NAME}-Interface + INTERFACE + ${PROJECT_SOURCE_DIR}/include/OsAbstraction + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + OsAbstraction-Interface + ) + +target_compile_definitions(${MODULE_NAME}-Interface + INTERFACE + DISABLE_FREERTOS + ) + +add_library(${MODULE_NAME} + INTERFACE + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + INTERFACE + OsAbstraction + ) diff --git a/OSAdapter/src/OsExtension/CMakeLists.txt b/OSAdapter/src/OsExtension/CMakeLists.txt new file mode 100644 index 0000000..a2dd015 --- /dev/null +++ b/OSAdapter/src/OsExtension/CMakeLists.txt @@ -0,0 +1,48 @@ +set(MODULE_NAME "OsExtension") + +add_library(${MODULE_NAME}-Interface + INTERFACE + ) + +target_compile_features(${MODULE_NAME}-Interface + INTERFACE + cxx_std_14 + ) + +target_include_directories(${MODULE_NAME}-Interface + INTERFACE + ${PROJECT_SOURCE_DIR}/OSAdapter/include/${MODULE_NAME} + ) + +target_link_libraries(${MODULE_NAME}-Interface + INTERFACE + OsAbstraction-Interface + Observer-Interface + etl + ) + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/MessageBuffer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Semaphore.cpp + + ) + +target_compile_features(${MODULE_NAME} + INTERFACE + cxx_std_14 + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + ${MODULE_NAME}-Interface + OsAbstraction + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + ) diff --git a/OSAdapter/src/OsExtension/MessageBuffer.cpp b/OSAdapter/src/OsExtension/MessageBuffer.cpp new file mode 100644 index 0000000..e13728e --- /dev/null +++ b/OSAdapter/src/OsExtension/MessageBuffer.cpp @@ -0,0 +1,128 @@ +/** + * @file src/OsExtension/MessageBuffer.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace osextension { + +size_t MessageBufferReference::send(const void* data, size_t length) { + return send(data, length, 0u); +} + +size_t MessageBufferReference::send(const void* data, size_t length, TickType_t ticksToWait) { + size_t result = 0u; + if (this->_binarySemaphoreReference.wait(portMAX_DELAY)) { + // do only allow to push to message buffer when counting semaphore can be signalled + if (_countingSemaphoreReference.getCount() < _length) { + result = osabstraction::messageBufferSend(_handle, data, length, ticksToWait); + + if (result != 0u) { + notifyObserver(DataAddedEvent{*this, data, length}); + this->_countingSemaphoreReference.signal(); + } else { + notifyObserver(DataDroppedEvent{*this, data, length}); + } + } + this->_binarySemaphoreReference.signal(); + } + return result; +} + +size_t MessageBufferReference::send(etl::array_view data) { + return send(&data[0], data.size(), 0u); +} + +size_t MessageBufferReference::send(etl::array_view data, TickType_t ticksToWait) { + return send(&data[0], data.size(), ticksToWait); +} + +size_t MessageBufferReference::sendFromISR(const void* data, size_t length, BaseType_t* higherPriorityTaskWoken) { + size_t result = 0u; + if (this->_binarySemaphoreReference.waitFromIsr(higherPriorityTaskWoken)) { + // do only allow to push to message buffer when counting semaphore can be signalled + if (_countingSemaphoreReference.getCountFromIsr() < _length) { + result = osabstraction::messageBufferSendFromISR(_handle, data, length, higherPriorityTaskWoken); + + if (result != 0u) { + notifyObserver(DataAddedEvent{*this, data, length}); + this->_countingSemaphoreReference.signalFromIsr(higherPriorityTaskWoken); + } else { + notifyObserver(DataDroppedEvent{*this, data, length}); + } + } + this->_binarySemaphoreReference.signalFromIsr(higherPriorityTaskWoken); + } + return result; +} + +size_t MessageBufferReference::sendFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) { + return sendFromISR(&data[0], data.size(), higherPriorityTaskWoken); +} + +size_t MessageBufferReference::receive(void* data, size_t length, TickType_t xTicksToWait) { + size_t result = 0u; + if (this->_countingSemaphoreReference.wait(xTicksToWait)) { + result = osabstraction::messageBufferReceive(_handle, data, length, 0u); + if (result != 0u) { + notifyObserver(DataRemovedEvent{*this, data, length}); + } + } + return result; +} + +size_t MessageBufferReference::receive(etl::array_view data, TickType_t xTicksToWait) { + return receive(&data[0], data.size(), xTicksToWait); +} + +size_t MessageBufferReference::receiveFromISR(void* data, size_t length, BaseType_t* higherPriorityTaskWoken) { + size_t result = 0u; + if (this->_countingSemaphoreReference.waitFromIsr(higherPriorityTaskWoken)) { + result = osabstraction::messageBufferReceiveFromISR(_handle, data, length, higherPriorityTaskWoken); + if (result != 0u) { + notifyObserver(DataRemovedEvent{*this, data, length}); + } + } + return result; +} + +size_t MessageBufferReference::receiveFromISR(etl::array_view data, BaseType_t* higherPriorityTaskWoken) { + return receiveFromISR(&data[0], data.size(), higherPriorityTaskWoken); +} + +bool MessageBufferReference::reset() { + this->_countingSemaphoreReference.reset(); + return osabstraction::messageBufferReset(_handle) == pdTRUE; +} + +bool MessageBufferReference::isEmpty() { + return osabstraction::messageBufferIsEmpty(_handle) == pdTRUE; +} + +bool MessageBufferReference::isFull() { + return osabstraction::messageBufferIsFull(_handle) == pdTRUE; +} + +size_t MessageBufferReference::numMessagesAvailable() { + return _countingSemaphoreReference.getCount(); +} + +size_t MessageBufferReference::numBytesAvailable() { + return osabstraction::streamBufferBytesAvailable(static_cast(_handle)); +} + +OsExtensionInterface::OsExtensionHandleType MessageBufferReference::getHandleForQueueSet() const { + return this->_countingSemaphoreReference.getHandleForQueueSet(); +} + +} // namespace osextension diff --git a/OSAdapter/src/OsExtension/Semaphore.cpp b/OSAdapter/src/OsExtension/Semaphore.cpp new file mode 100644 index 0000000..8ff7783 --- /dev/null +++ b/OSAdapter/src/OsExtension/Semaphore.cpp @@ -0,0 +1,47 @@ +/** + * @file src/OsExtension/Semaphore.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace osextension { + +bool SemaphoreReference::signal(void) { + return (osabstraction::semaphoreGive(_semaphore) == pdTRUE); +} + +bool SemaphoreReference::signalFromIsr(SemaphoreTaskWokenTokenType* taskWoken) { + return (osabstraction::semaphoreGiveFromISR(_semaphore, taskWoken) == pdTRUE); +} + +bool SemaphoreReference::wait(uint32_t timeout) { + return (osabstraction::semaphoreTake(_semaphore, timeout) == pdTRUE); +} + +bool SemaphoreReference::waitFromIsr(SemaphoreTaskWokenTokenType* taskWoken) { + return (osabstraction::semaphoreTakeFromISR(_semaphore, taskWoken) == pdTRUE); +} + +bool SemaphoreReference::reset(void) { + return (osabstraction::queueReset(_semaphore) == pdTRUE); +} + +size_t SemaphoreReference::getCount(void) { + return osabstraction::semaphoreGetCount(_semaphore); +} + +size_t SemaphoreReference::getCountFromIsr(void) { + return osabstraction::semaphoreGetCountFromISR(_semaphore); +} + +} // namespace osextension diff --git a/Platform/CMakeLists.txt b/Platform/CMakeLists.txt new file mode 100644 index 0000000..ea6fb9a --- /dev/null +++ b/Platform/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(RequiredInterface) + +if (NOT(PROJECT_PLATFORM STREQUAL "")) + add_subdirectory(${PROJECT_PLATFORM}) +endif() diff --git a/Platform/RequiredInterface/CMakeLists.txt b/Platform/RequiredInterface/CMakeLists.txt new file mode 100644 index 0000000..c66c640 --- /dev/null +++ b/Platform/RequiredInterface/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library(CommunicationChannel-Interface + INTERFACE + ) + +target_include_directories(CommunicationChannel-Interface + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/CommunicationChannelInterface + ) + +add_library(TestAppRtos-Interface + INTERFACE + ) + +target_include_directories(TestAppRtos-Interface + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/TestAppRtosInterface + ) + +if(TESTAPP_STANDALONE_BUILD) + target_compile_definitions(TestAppRtos-Interface + INTERFACE + TESTAPP_STANDALONE_BUILD=1 + ) +endif() diff --git a/Platform/RequiredInterface/CommunicationChannelInterface/CommunicationChannelInterface/TccInterface.hpp b/Platform/RequiredInterface/CommunicationChannelInterface/CommunicationChannelInterface/TccInterface.hpp new file mode 100644 index 0000000..b987eb2 --- /dev/null +++ b/Platform/RequiredInterface/CommunicationChannelInterface/CommunicationChannelInterface/TccInterface.hpp @@ -0,0 +1,112 @@ +/** + * @file Platform/RequiredInterface/CommunicationChannelInterface/CommunicationChannelInterface/TccInterface.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef COMMUNICATIONCHANNELINTERFACE_TCCINTERFACEHPP +#define COMMUNICATIONCHANNELINTERFACE_TCCINTERFACEHPP + +#include +#include +#include +#include + +namespace tccinterface{ + +/// custom error handeling +enum class ErrCode : uint8_t { + SUCCESS = 0, + ERRCODE_TEST_CCHANNEL_UNINITIALIZED, + ERRCODE_TEST_CCHANNEL_INITIALISATION_FAILURE, + ERRCODE_TEST_CCHANNEL_ALREADY_INITIALIZED, + ERRCODE_TEST_CCHANNEL_BUSY_OR_RESPONSE_FAILURE, + ERRCODE_TEST_CCHANNEL_DATA_SIZE_OVERLIMIT, + ERRCODE_TEST_CCHANNEL_SEND_FAILURE, + ERRCODE_UNDEFINED +}; + +/// Communication channel operation mode +enum class OperationMode : uint8_t +{ + INTERRUPT_MODE = 0, /// Interrupt based communication between target and Host + POLLING_MODE = 1 /// Periodically call the required Api for communication to receive data +}; + +/** + * @brief: + * The class declaration is used to define the interface for the different Test coordination channel interface + * It defines the methods invoked by TestApp components to send and receive + * the data from Integration Testing framework (ITF) + * The platform specific implementation will override the base implementation + * + */ +class TccInterface{ +public: + /// Deleted copy constructor + TccInterface(const TccInterface&) = delete; + + /// Deleted move constructor + TccInterface(TccInterface&&) = delete; + + /// Deleted copy assignment operator + TccInterface& operator=(const TccInterface&) = delete; + + /// Delete move assignment operator + TccInterface& operator=(TccInterface&&) = delete; + + + /// Test Coordination Channel Interface Initialization function + virtual tccinterface::ErrCode init(void) = 0; + + /// Test Coordination Channel Data receive interface function + virtual tccinterface::ErrCode receive(size_t* length, void* buf, size_t maxsize) = 0; + + /// Test Coordination Channel Data receive interface function + virtual tccinterface::ErrCode receive(size_t* length, etl::array_view& bufView) = 0; + + /// Test Coordination Channel Data send interface function + virtual tccinterface::ErrCode send(const void* datastream, size_t datalen) = 0; + + /// Test Coordination Channel Data send interface function + virtual tccinterface::ErrCode send(const etl::array_view& datastream) = 0; + + /// Test Coordination Channel Interface Shutdown Interface + virtual tccinterface::ErrCode shutdown(void) = 0; + + /** + * @brief: + * Provide to the TPC, which operation mode needs to be setup. + * In case of INTERRUPT_MODE, registerSemaphoreReference() needs to be used to get the semaphore that will trigger a receive(). + * In case of POLLING_MODE, registerPollingPeriod() needs to be used to tell the TPC the period in ms receive() needs to be trigger. + */ + virtual OperationMode getOperationMode(void) const = 0; + + /// register reference to TPC semaphore (non-pure virtual function - Derived class does not have to override it) + /// In case of INTERRUPT_MODE this method has to be defined by the derived class + virtual void registerSemaphoreReference(__attribute__((unused)) osextension::SemaphoreReference &TPCSemaphore) {}; + + /// Needs to be implemented if OperationMode is set to POLLING_MODE (non-pure virtual function - Derived class does not have to override it) + virtual TickType_t registerPollingPeriod() const { return 0; }; + +protected: + /// Default constructor + TccInterface() = default; + + /// Destructor + virtual ~TccInterface() = default; + +}; + +extern TccInterface* tccInterface; + +} //namespace tccinterface + +#endif //COMMUNICATIONCHANNELINTERFACE_TCCINTERFACEHPP diff --git a/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/Task.hpp b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/Task.hpp new file mode 100644 index 0000000..08febab --- /dev/null +++ b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/Task.hpp @@ -0,0 +1,254 @@ +/** + * @file Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTAPPRTOSINTERFACE_TASK_HPP +#define TESTAPPRTOSINTERFACE_TASK_HPP + +#include +#include +#include +#include +#include +#include +#include + +/** + * Macro used in submacros to create strings + */ +#define HELPER_STRINGERIZER(reg) #reg +/** + * Helper Macro for Stack Definition and Access + * Usage like: + * STACK_DEF(name, size) ... Use this where you want to setup your stack variable - The space + * STACK(name) ... use the name you given your stack with STACK_DEF to get the access pointer + */ +#define STACK_DEF(name, size, region_name) \ + alignas(32) static StackType_t taskContextStack_##name[size / 4u] __attribute__((section(HELPER_STRINGERIZER(.task_section_##region_name##_stack)))) __attribute__((used)) /* NOLINT (cppcoreguidelines-avoid-c-arrays) it cannot be modified because it uses Third-Party-Macros */ +/** + * Access Stack defined by STACK_DEF + */ +#define STACK(name) taskContextStack_##name + + +namespace testapprtos { + +/** + * Helper Struct to communicate NVIC Operations to designated Systemcall + */ +struct IsrControlType { + uint32_t irqNumber; /**< Corresponding NVIC Control Line */ + bool enableCtrl; /**< Instruction */ +}; + +/** + * @brief: + * The class is used as base for the two types of Tasks that can be defined by an underlying + * OS. It contains all needed Data for static allocalisation of the Task and all its needed Resources. + * Furthermore the classes are setup to be linked into a list of objects to + * guarantee a consistent way of starting and initializing all Tasks. + */ +class Task { +public: + /// Deleted default constructor + Task() = delete; + + /// Deleted copy constructor + Task(const Task&) = delete; + + /// Deleted move constructor + Task(Task&&) = delete; + + /// Deleted copy assignment operator + Task& operator=(const Task&) = delete; + + /// Delete move assignment operator + Task& operator=(Task&&) = delete; + + /** + * @brief: Define a Constructor for the static initialization + * @param[in] taskConfiguration @see class rtosTaskConfiguration + */ + explicit Task(const TaskConfiguration& taskConfiguration) : _taskConfiguration(taskConfiguration), _taskEventQueue(taskConfiguration.name) { + // Setup a Linked List for Initialization Chain + Task* pointerToLinkedList = firstTask; + if (firstTask == nullptr) { + firstTask = this; + } else { + while (pointerToLinkedList->nextTask != nullptr) { + pointerToLinkedList = pointerToLinkedList->nextTask; + } + pointerToLinkedList->nextTask = this; + } + }; + + /** + * @brief: Destructor of this Object. Ends Task and removes it from execution list + */ + virtual ~Task() { + vTaskDelete(taskHandle); + }; + + /// One Function to start all OS related Tasks and Startup the OS + static void startAllTasks() __attribute__((used)); + + /** + * @brief: Get Task Object by ID + * @param[in] id Task Handle ID + * @return Reference to Object + */ + static Task* getTaskById(TaskHandle_t id) __attribute__((used)); + /** + * @brief: Get Task Object by Name + * @param[name] name Task Name + * @return Reference to Object + */ + static Task* getTaskByName(const char* name) __attribute__((used)); + + // Virtual Functions for Deriving + virtual void initializeTask(); /**< Creates Static Task (is called upon startup) */ + +#if configENABLE_MPU + /** + * @brief Drop Privileged Rights of Task + */ + static void dropPrivilege(); +#endif //#if configENABLE_MPU + + /** + * @brief Update Runtime Stats + * + * This Function is called during a Task Switch Process. + * It updates all relevant Timings to determine the CPU stress per Task. + * + * @param executionTime Time the task was run in CPU Cycles + */ + void updateRuntimeStats(uint32_t executionTime); + + /** + * @brief: Get Workload of Task + * Return Collected Workload Data of task Object and associated Task. + * @return Workload of this Task in Percent + */ + uint8_t getTaskWorkload() const; + +#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + /** + * @brief Invoke Custom SVC + * + * Generates a user Interrupt to run a given executer. + * These are unique for each task. + * + * @param pArg Pointer to the custom argument that is given to the Executer in SVC Context + * @param syscall Pointer to Syscall Function that should be executed on this invoke. Will be stored. + */ + void invokeTaskCustomSvc(void* pArg, const std::function& syscall); +#endif //#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + + /** + * @brief Returns the currently Registered Task Custom SVC Executer + * @return Custom SVC + */ + std::function getCustomSvcExecuter(); + + /** + * @brief Get Custom SVC Argument + * @return Stored SVC Argument + */ + void* getCustomSvcArgument(); + + /** + * @brief Get Task Object OS Handle + * @return freeRTOS Task Handle + */ + TaskHandle_t getTaskHandle(); + + /** + * @brief Yield current Task from a privileged Context. + * Careful! This Function call may ONLY be executed in a privileged + * Context to force a reschedule of Tasks. + * E.G.: ISR, Privileged Task or SystCall. + */ + static void yieldFromPrivilegedContext(); + +#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + /** + * @brief Control an Interrupt + * + * This Function is used to enable or disable a System Interrupt. + * @param irqNumber Designated IRQ Line Number + * @param enable true = turns line on, false = turns line off + */ + void controlInterrupt(uint32_t irqNumber, bool enable); +#endif //#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + + /** + * @brief Get reference to task event queue + * @return Reference to task event queue + */ + TaskEventQueueReference getTaskEventQueueReference(); + + /// Default run function that handles shutdown task event + virtual void run(); + + /// Init function to be executed once after task creation + virtual void init(); + + /// Deinit function to be executed before termination + virtual void deinit(); + + /// Process function to be executed cyclically + virtual void process(); + + /** + * @brief Evaluates whether the shutdown was triggered + * @return True when shutdown has been triggered, false otherwise. + */ + bool isShutdownNotified(); + + /** + * @brief Get ISR Control Field + * ISR Pending Control can be obtained via this call. + * Its a bitfield encoded Word to instruct SVC NVIC Operations. + * @return pendingIsrControl + */ + IsrControlType getPendingIsrControl(); + + /// Indicates whether this task should terminate itself + bool shouldTerminate = false; + + // First item in list of os objects + static Task* firstTask; + + /// Next item in list of os objects + Task* nextTask = nullptr; + +protected: + /// The task configuration + const TaskConfiguration& _taskConfiguration; + + StaticTask_t taskControlBlock; /**< Static Control Block for task */ + TaskHandle_t taskHandle; /**< Task Handle is Initialized upon the start of the OS */ + + /// Queue of task events to be processed + TaskEventQueue _taskEventQueue; + +private: + uint64_t _taskRuntime = 0u; /**< Total Time this Task has run */ + void* _customSvcArgument = nullptr; /**< Pointer to a SVC Element that is given in new Context */ + std::function _customSvcExecuter; /**< System Call Executed Function that is given customSvcArgument */ + IsrControlType _pendingIsrControl; /**< Field for Storing Interrupt controlling Operations */ +}; + +} //namespace testapprtos + +#endif //TESTAPPRTOSINTERFACE_TASK_HPP diff --git a/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskConfig.hpp b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskConfig.hpp new file mode 100644 index 0000000..aa383c8 --- /dev/null +++ b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskConfig.hpp @@ -0,0 +1,127 @@ +/** + * @file Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskConfig.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTAPPRTOSINTERFACE_TASKCONFIG_HPP +#define TESTAPPRTOSINTERFACE_TASKCONFIG_HPP + +#include +#include +#include + +namespace testapprtos { + +#if configENABLE_MPU +/** Expression to define how many custom regions can be defined via RtosTaskUnprivileged::defineCustomRegion */ +static constexpr int32_t maxCustomDefinableRegions = 10; + +/** + * This container holds the necessary members + * that all Task Regions have to define + * in order to setup the regions access of RAM and + * exRAM. + */ +struct FixedUsedRegions { + void* ramBase; /**< Internal Ram Address of allowed section */ + void* ramEnd; /**< Maximum internal Ram Address of Task Region */ + void* exRamBase; /**< External Ram Address of allowed section */ + void* exRamEnd; /**< Maximum external Ram Address of Task Region */ +}; + +/** + * Enumeration for assigning the task region sections + * in appropriate order for the rtos Mpu port. + */ +enum class TaskRegionAssignment : uint8_t { + RegionAssignedRAM, /**< Region ram section */ + RegionAssignedExRAM, /**< Region exram section */ + UserRegion01, /**< User definable Region 1 */ + UserRegion02, /**< User definable Region 2 */ + UserRegion03, /**< User definable Region 3 */ + UserRegion04, /**< User definable Region 4 */ + + NumberOfElements /**< Elements in this enumeration */ +}; + +/* + * Helper expressions for + * portable and easy access to according Sections + */ +/** First User Region */ +static constexpr TaskRegionAssignment firstUserRegion = TaskRegionAssignment::UserRegion01; +/** Last User Region */ +static constexpr TaskRegionAssignment lastUserRegion = TaskRegionAssignment::UserRegion04; +/** Determine User region Amount */ +static constexpr size_t userRegionAmount = static_cast(static_cast(lastUserRegion) - static_cast(firstUserRegion) + 1u); +/** Expression for Enum comparison */ +static constexpr size_t taskRegionAssignment_amount = static_cast(TaskRegionAssignment::NumberOfElements); + +/** + * List of all User Definable Regions to + * grant a task access to. + */ +enum class TaskAccessRegions : uint8_t { + NoRegionDefined, /**< No User Task Access */ + /* Memories */ + FullExram, /**< Full Access to all exram elements */ + FullRam, /**< Full Access to all internal ram elements */ + FullOtp, /**< Full Access to otp memory */ + FullTestRunner, /**< Full Access to TestRunner */ + FullTestEnvironment, /**< Full Access to TestEnvironment */ + Bootloader1Version, /**< Access to bootloader 1 version section */ + Bootloader2Version, /**< Access to bootloader 2 version section */ + /* Peripherals */ + FullPeripheralAccess, /**< Full Access to all peripheral elements */ + PeripheralI2c2, /**< Task Access to Peripheral: I2C2 */ + PeripheralI2c4, /**< Task Access to Peripheral: I2C4 */ + PeripheralUart, /**< Task Access to Peripheral: UART */ + PeripheralGpio, /**< Task Access to Peripheral: All GPIOs */ + PeripheralUsb, /**< Task Access to Peripheral: USB */ + PeripheralCan, /**< Task Access to Peripheral: CAN */ + PeripheralHash, /**< Task Access to Peripheral: Hash Generator */ + PeripheralRng, /**< Task Access to Peripheral: Random Number Generator */ + PeripheralTimer2, /**< Task Access to Peripheral: Timer 2 */ + PeripheralOpAmp, /**< Task Access to Peripheral: Operation Amplifier */ + PeripheralRtc, /**< Task Access to Peripheral: Real Time Clock */ + PeripheralDma, /**< Task Access to Peripheral: DMA */ + PeripheralSdmmc, /**< Task Access to Peripheral: SDMMC */ + PeripheralAes, /**< Task Access to Peripheral: AES */ + PeripheralFlash, /**< Task Access to Peripheral: Flash Controller */ + PeripheralICache, /**< Task Access to Peripheral: ICache */ + PeripheralTamp, /**< Task Access to Peripheral: TAMP */ + PeripheralRcc, /**< Task Access to Peripheral: Reset and Clock Control */ + + NumberOfElements /**< Elements in Enumeration */ +}; + +/** Expression for Enum comparisons */ +static constexpr size_t taskAccessRightTypes_amount = static_cast(TaskAccessRegions::NumberOfElements); +#endif //configENABLE_MPU + +/** + * Given Task Definition Parameter by User + */ +struct TaskConfiguration { + // Settings for Thread Resources + const uint32_t stackSize; /**< Stack Size in bytes */ + StackType_t * stackPtr; /**< Pointer to the Stack */ + const char* name; /**< Display Name */ + const uint8_t priority; /**< Priority (caution, free rtos priority a.k.a 0 = lowest) */ +#if configENABLE_MPU + FixedUsedRegions memRegions; /**< @see fixedUsedRegions */ + std::array regionPermit; /**< @see taskAccessRegions */ +#endif +}; + +} // namespace testapprtos + +#endif // TESTAPPRTOSINTERFACE_TASKCONFIG_HPP diff --git a/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskEventQueue.hpp b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskEventQueue.hpp new file mode 100644 index 0000000..da8145a --- /dev/null +++ b/Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskEventQueue.hpp @@ -0,0 +1,38 @@ +/** + * @file Platform/RequiredInterface/TestAppRtosInterface/TestAppRtosInterface/TaskEventQueue.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTAPPRTOSINTERFACE_TASKEVENTQUEUE_HPP +#define TESTAPPRTOSINTERFACE_TASKEVENTQUEUE_HPP + +#include +#include + +namespace testapprtos { + +/// Task event identifier +enum class TaskEventId : uint8_t { + Shutdown +}; + +/// Maximum number of events that can simultaneously occur +constexpr uint8_t maximumNumberOfEvents = 1; + +/// Queue for task events +using TaskEventQueue = osextension::Queue; + +/// Reference to queue for task events +using TaskEventQueueReference = osextension::QueueReference; + +} // namespace testapprtos + +#endif /* TESTAPPRTOSINTERFACE_TASKEVENTQUEUE_HPP */ diff --git a/Platform/hostMachine-gcc/config/FreeRTOS/FreeRTOSConfig.h b/Platform/hostMachine-gcc/config/FreeRTOS/FreeRTOSConfig.h new file mode 100644 index 0000000..f3860b1 --- /dev/null +++ b/Platform/hostMachine-gcc/config/FreeRTOS/FreeRTOSConfig.h @@ -0,0 +1,150 @@ +/* + FreeRTOS V7.1.1 - Copyright (C) 2012 Real Time Engineers Ltd. + + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong? * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + + http://www.FreeRTOS.org - Documentation, training, latest information, + license and contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool. + + Real Time Engineers ltd license FreeRTOS to High Integrity Systems, who sell + the code with commercial support, indemnification, and middleware, under + the OpenRTOS brand: http://www.OpenRTOS.com. High Integrity Systems also + provide a safety engineered and independently SIL3 certified version under + the SafeRTOS brand: http://www.SafeRTOS.com. +*/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ (168000000UL) +#define configTICK_RATE_HZ ((portTickType)1000) +#define configMAX_PRIORITIES (5) +#define configMINIMAL_STACK_SIZE ((unsigned short)70) +#define configTOTAL_HEAP_SIZE ((size_t)(80 * 1024)) +#define configMAX_TASK_NAME_LEN (10) +#define configUSE_TRACE_FACILITY 0 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 16 +#define configGENERATE_RUN_TIME_STATS 0 +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configUSE_RECURSIVE_MUTEXES 0 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 0 +#define configSUPPORT_STATIC_ALLOCATION 1 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES (2) + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (3) +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 + +/* Use the system definition, if there is one */ +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 4 /* 15 priority levels */ +#endif + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + +/* The lowest priority. */ +#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +/* Priority 5, or 95 as only the top four bits are implemented. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) + +#define configASSERT(x) \ + if ((x) == 0) { \ + taskDISABLE_INTERRUPTS(); \ + for (;;) \ + ; \ + } + +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler + +#endif /* FREERTOS_CONFIG_H */ diff --git a/Platform/hostMachine-gcc/config/FreeRTOS/NullPort/portmacro.h b/Platform/hostMachine-gcc/config/FreeRTOS/NullPort/portmacro.h new file mode 100644 index 0000000..7dbbaa5 --- /dev/null +++ b/Platform/hostMachine-gcc/config/FreeRTOS/NullPort/portmacro.h @@ -0,0 +1,136 @@ +/* + * This file has been forked from MSVC-MingW portmacro.h + * + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +/****************************************************************************** + Defines +******************************************************************************/ +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE size_t +#define portBASE_TYPE long +#define portPOINTER_SIZE_TYPE size_t + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if (configUSE_16_BIT_TICKS == 1) +typedef uint16_t TickType_t; +#define portMAX_DELAY (TickType_t)0xffff +#else +typedef uint32_t TickType_t; +#define portMAX_DELAY (TickType_t)0xffffffffUL + +/* 32/64-bit tick type on a 32/64-bit architecture, so reads of the tick + count do not need to be guarded with a critical section. */ +#define portTICK_TYPE_IS_ATOMIC 1 +#endif + +/* Hardware specifics. */ +#define portSTACK_GROWTH (-1) +#define portTICK_PERIOD_MS ((TickType_t)1000 / configTICK_RATE_HZ) +#define portINLINE __inline + +#define portBYTE_ALIGNMENT 4 + +#define portYIELD() vPortGenerateSimulatedInterrupt(portINTERRUPT_YIELD) + +/* Simulated interrupts return pdFALSE if no context switch should be performed, +or a non-zero number if a context switch should be performed. */ +#define portYIELD_FROM_ISR(x) (void)x +#define portEND_SWITCHING_ISR(x) portYIELD_FROM_ISR((x)) + +void vPortCloseRunningThread(void* pvTaskToDelete, volatile BaseType_t* pxPendYield); +void vPortDeleteThread(void* pvThreadToDelete); +#define portCLEAN_UP_TCB(pxTCB) vPortDeleteThread(pxTCB) +#define portPRE_TASK_DELETE_HOOK(pvTaskToDelete, pxPendYield) vPortCloseRunningThread((pvTaskToDelete), (pxPendYield)) +#define portDISABLE_INTERRUPTS() vPortEnterCritical() +#define portENABLE_INTERRUPTS() vPortExitCritical() + +/* Critical section handling. */ +void vPortEnterCritical(void); +void vPortExitCritical(void); + +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + +/* 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) (void)(uxTopPriority = 0u, uxReadyPriorities) + +#endif /* taskRECORD_READY_PRIORITY */ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO(vFunction, pvParameters) void vFunction(void* pvParameters) +#define portTASK_FUNCTION(vFunction, pvParameters) void vFunction(void* pvParameters) + +#define portINTERRUPT_YIELD (0UL) +#define portINTERRUPT_TICK (1UL) + +/* + * Raise a simulated interrupt represented by the bit mask in ulInterruptMask. + * Each bit can be used to represent an individual interrupt - with the first + * two bits being used for the Yield and Tick interrupts respectively. +*/ +void vPortGenerateSimulatedInterrupt(uint32_t ulInterruptNumber); + +/* + * Install an interrupt handler to be called by the simulated interrupt handler + * thread. The interrupt number must be above any used by the kernel itself + * (at the time of writing the kernel was using interrupt numbers 0, 1, and 2 + * as defined above). The number must also be lower than 32. + * + * Interrupt handler functions must return a non-zero value if executing the + * handler resulted in a task switch being required. + */ +void vPortSetInterruptHandler(uint32_t ulInterruptNumber, uint32_t (*pvHandler)(void)); + +#endif diff --git a/Platform/stm32f401re/CMakeLists.txt b/Platform/stm32f401re/CMakeLists.txt new file mode 100644 index 0000000..97288ae --- /dev/null +++ b/Platform/stm32f401re/CMakeLists.txt @@ -0,0 +1,17 @@ +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" CACHE FORCE) + +# Target platform source code +add_subdirectory(src) + +# Target platform config +if(APP_OSADAPTER STREQUAL "FreeRtosAdapter") + #set_property(TARGET stm32cubef4-hal stm32cubef4-usb-device APPEND PROPERTY COMPILE_OPTIONS "-w") + get_target_property(stm32cubef4-hal-type stm32cubef4-hal TYPE) + + if (NOT stm32cubef4-hal-type STREQUAL "INTERFACE_LIBRARY") + target_compile_definitions(stm32cubef4-hal PUBLIC register=) # Workaround to disable use of register keyword in public headers as this has been removed from C++17 + endif() + target_link_libraries(freertos PRIVATE stm32cubef4-cmsis) + # disable compiler warnings for freertos + set_property(TARGET freertos APPEND PROPERTY COMPILE_OPTIONS "-w") +endif() diff --git a/Platform/stm32f401re/cmake/platformLinkerConfiguration.cmake b/Platform/stm32f401re/cmake/platformLinkerConfiguration.cmake new file mode 100644 index 0000000..9beaee7 --- /dev/null +++ b/Platform/stm32f401re/cmake/platformLinkerConfiguration.cmake @@ -0,0 +1,16 @@ +# \brief Set linker flags for the passed target +# +# \param TARGET target the flags shall be linked to +# \param LIBRARY_PATH path containing the linker script +# \param LINKER_SCRIPT specifies the linker script name (not containing the path) which should be used +# \param MAP_FILE specifies the name of the generated map file +function (arm_target_set_linkerflags_of_target) + set(options "") + set(oneValueArgs TARGET LIBRARY_PATH LINKER_SCRIPT MAP_FILE) + set(multiValueArgs "") + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + # set custom linker flags + set_target_properties(${TARGET} PROPERTIES LINK_OPTIONS "SHELL:-specs=nano.specs LINKER:-L${ARG_LIBRARY_PATH},-T${ARG_LINKER_SCRIPT},-Map=${ARG_MAP_FILE},--gc-sections,--cref") + +endfunction() diff --git a/Platform/stm32f401re/config/FreeRTOS/FreeRTOSConfig.h b/Platform/stm32f401re/config/FreeRTOS/FreeRTOSConfig.h new file mode 100644 index 0000000..8b5ac6d --- /dev/null +++ b/Platform/stm32f401re/config/FreeRTOS/FreeRTOSConfig.h @@ -0,0 +1,213 @@ +/* + FreeRTOS V9.0.0 - Copyright (C) 2015 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +// #define configUSE_PREEMPTION 1 +// #define configUSE_IDLE_HOOK 1 +// #define configUSE_TICK_HOOK 1 +#define configENABLE_TRUSTZONE 0 ///< 0 = disable trustzone +#define configSUPPORT_DYNAMIC_ALLOCATION 0 ///< 0 = see configSUPPORT_STATIC_ALLOCATION +#define configQUEUE_REGISTRY_SIZE 16 ///< use same length as task name for debugging the queue +#define configUSE_QUEUE_SETS 1 ///< 1 = using Queue Sets + + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configUSE_TICKLESS_IDLE 0 //1 +#define configCPU_CLOCK_HZ ( 16000000 ) +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES ( 7 ) +#define configMINIMAL_STACK_SIZE ( ( uint16_t ) 128 ) +// #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 3 * 1024 ) ) +#define configMAX_TASK_NAME_LEN ( 16 ) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configGENERATE_RUN_TIME_STATS 0 +#define configSUPPORT_STATIC_ALLOCATION 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 3 ///< number of indexes in each task's thread local storage array + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) + +/* Software timer definitions. */ +#define configUSE_TIMERS 0 +#define configTIMER_TASK_PRIORITY ( 2 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +// #define INCLUDE_vTaskPrioritySet 1 +// #define INCLUDE_uxTaskPriorityGet 1 +// #define INCLUDE_vTaskDelete 1 +// #define INCLUDE_vTaskCleanUpResources 0 +// #define INCLUDE_vTaskSuspend 1 +// #define INCLUDE_vTaskDelayUntil 0 +// #define INCLUDE_vTaskDelay 1 +// #define INCLUDE_xTaskGetSchedulerState 1 + + +// Optional functions - most linkers will remove unused functions anyway. +#define INCLUDE_vTaskPrioritySet 1 ///< 1 = vTaskPrioritySet available +#define INCLUDE_uxTaskPriorityGet 1 ///< 1 = uxTaskPriorityGet available +#define INCLUDE_vTaskDelete 1 ///< 1 = vTaskDelete available +#define INCLUDE_vTaskSuspend 1 ///< 1 = vTaskSuspend available +#define INCLUDE_xResumeFromISR 1 ///< 1 =xResumeFromISR available +#define INCLUDE_vTaskDelayUntil 1 ///< 1 = vTaskDelayUntil available +#define INCLUDE_vTaskDelay 1 ///< 1 = vTaskDelay available +#define INCLUDE_xTaskGetSchedulerState 1 ///< 1 = xTaskGetSchedulerState available +#define INCLUDE_xTaskGetCurrentTaskHandle 1 ///< 1 = xTaskGetCurrentTaskHandle available +#define INCLUDE_uxTaskGetStackHighWaterMark 0 ///< 0 = uxTaskGetStackHighWaterMark not available +#define INCLUDE_xTaskGetIdleTaskHandle 1 ///< 1 = xTaskGetIdleTaskHandle is available +#define INCLUDE_eTaskGetState 0 ///< 0 = eTaskGetState not available +#define INCLUDE_xEventGroupSetBitFromISR 1 ///< 1 = xEventGroupSetBitFromISR available +#define INCLUDE_xTimerPendFunctionCall 0 ///< 0 = xTimerPendFunctionCall not available +#define INCLUDE_xTaskAbortDelay 0 ///< 0 = xTaskAbortDelay not available +#define INCLUDE_xTaskGetHandle 0 ///< 0 = xTaskGetHandle not available +#define INCLUDE_xTaskResumeFromISR 1 ///< 1 = xTaskResumeFromISR available + +/* Cortex-M specific definitions. */ +#ifdef __NVIC_PRIO_BITS + /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ + #define configPRIO_BITS __NVIC_PRIO_BITS +#else + #define configPRIO_BITS 4 /* 15 priority levels */ +#endif + +/* The lowest interrupt priority that can be used in a call to a "set priority" +function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf + +/* The highest interrupt priority that can be used by any interrupt service +routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL +INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER +PRIORITY THAN THIS! (higher priorities are lower numeric values. */ +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + +/* Interrupt priorities used by the kernel port layer itself. These are generic +to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! +See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) + +/* Normal assert() semantics without relying on the provision of an assert.h +header file. */ +#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } + +/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS + standard names. */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler + +#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) +void PreSleepProcessing(uint32_t ulExpectedIdleTime); +void PostSleepProcessing(uint32_t ulExpectedIdleTime); +#endif /* defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) */ + +/* The configPRE_SLEEP_PROCESSING() and configPOST_SLEEP_PROCESSING() macros +allow the application writer to add additional code before and after the MCU is +placed into the low power state respectively. The empty implementations +provided in this demo can be extended to save even more power. */ +#if configUSE_TICKLESS_IDLE == 1 + +#define configPRE_SLEEP_PROCESSING(__x__) \ + do{ \ + __x__ = 0; \ + PreSleepProcessing(__x__); \ + }while(0) +#define configPOST_SLEEP_PROCESSING PostSleepProcessing +#endif /* configUSE_TICKLESS_IDLE == 1 */ + +/* IMPORTANT: This define MUST be commented when used with STM32Cube firmware, + to prevent overwriting SysTick_Handler defined within STM32Cube HAL */ +#define xPortSysTickHandler SysTick_Handler + +#endif /* FREERTOS_CONFIG_H */ diff --git a/Platform/stm32f401re/config/Linker/stm32f4.ld b/Platform/stm32f401re/config/Linker/stm32f4.ld new file mode 100644 index 0000000..05322c4 --- /dev/null +++ b/Platform/stm32f401re/config/Linker/stm32f4.ld @@ -0,0 +1,199 @@ +/* +****************************************************************************** +** +** @file : LinkerScript.ld +** +** @author : Auto-generated by STM32CubeIDE +** +** Abstract : Linker script for NUCLEO-F401RE Board embedding STM32F401RETx Device from stm32f4 series +** 512Kbytes FLASH +** 96Kbytes RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used +** +** Target : STMicroelectronics STM32 +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +****************************************************************************** +** @attention +** +** Copyright (c) 2023 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. +** +****************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ + +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Memories definition */ +MEMORY +{ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K + SEGGER (rw) : ORIGIN = 0x20017800, LENGTH = 0x0800 + FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K +} + +/* Sections */ +SECTIONS +{ + /* The startup code into "FLASH" Rom type memory */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data into "FLASH" Rom type memory */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data into "FLASH" Rom type memory */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab : { + . = ALIGN(4); + *(.ARM.extab* .gnu.linkonce.armextab.*) + . = ALIGN(4); + } >FLASH + + .ARM : { + . = ALIGN(4); + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + . = ALIGN(4); + } >FLASH + + .preinit_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + . = ALIGN(4); + } >FLASH + + .init_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + . = ALIGN(4); + } >FLASH + + .fini_array : + { + . = ALIGN(4); + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + . = ALIGN(4); + } >FLASH + + + /* Used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + + + /* Initialized data sections into "RAM" Ram type memory */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + + } >RAM AT> FLASH + + /* Uninitialized data section into "RAM" Ram type memory */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + .rtt_section : + { + __rtt_section_start__ = .; + *(.rtt_section) + __rtt_section_end__ = .; + __rtt_buffer_section_start__ = .; + *(.rtt_buffer_section) + __rtt_buffer_section_end__ = .; + } >SEGGER + + /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM + + /* Remove information from the compiler libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/Platform/stm32f401re/config/RTT/SEGGER_RTT_Conf.h b/Platform/stm32f401re/config/RTT/SEGGER_RTT_Conf.h new file mode 100644 index 0000000..2c4a087 --- /dev/null +++ b/Platform/stm32f401re/config/RTT/SEGGER_RTT_Conf.h @@ -0,0 +1,415 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2019 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* condition is met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this condition and the following disclaimer. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT_Conf.h +Purpose : Implementation of SEGGER real-time transfer (RTT) which + allows real-time communication on targets which support + debugger memory accesses while the CPU is running. +Revision: $Rev: 17698 $ + +*/ + +#ifndef SEGGER_RTT_CONF_H +#define SEGGER_RTT_CONF_H + +#ifdef __IAR_SYSTEMS_ICC__ +#include +#endif + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ +/// Place the RTT section into its own section +#define SEGGER_RTT_SECTION ".rtt_section" +/// Place the RTT buffer section into its own section +#define SEGGER_RTT_BUFFER_SECTION ".rtt_buffer_section" + +#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS +#define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3) // Max. number of up-buffers (T->H) available on this target (Default: 3) +#endif + +#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS +#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3) // Max. number of down-buffers (H->T) available on this target (Default: 3) +#endif + +#ifndef BUFFER_SIZE_DOWN +#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16) +#endif + +#ifndef BUFFER_SIZE_UP +#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k) +#endif + +#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE +#define SEGGER_RTT_PRINTF_BUFFER_SIZE (128u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64) +#endif + +#ifndef SEGGER_RTT_MODE_DEFAULT +#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0) +#endif + +/********************************************************************* +* +* RTT memcpy configuration +* +* memcpy() is good for large amounts of data, +* but the overhead is big for small amounts, which are usually stored via RTT. +* With SEGGER_RTT_MEMCPY_USE_BYTELOOP a simple byte loop can be used instead. +* +* SEGGER_RTT_MEMCPY() can be used to replace standard memcpy() in RTT functions. +* This is may be required with memory access restrictions, +* such as on Cortex-A devices with MMU. +*/ +#ifndef SEGGER_RTT_MEMCPY_USE_BYTELOOP +#define SEGGER_RTT_MEMCPY_USE_BYTELOOP 0 // 0: Use memcpy/SEGGER_RTT_MEMCPY, 1: Use a simple byte-loop +#endif +// +// Example definition of SEGGER_RTT_MEMCPY to external memcpy with GCC toolchains and Cortex-A targets +// +//#if ((defined __SES_ARM) || (defined __CROSSWORKS_ARM) || (defined __GNUC__)) && (defined (__ARM_ARCH_7A__)) +// #define SEGGER_RTT_MEMCPY(pDest, pSrc, NumBytes) SEGGER_memcpy((pDest), (pSrc), (NumBytes)) +//#endif + +// +// Target is not allowed to perform other RTT operations while string still has not been stored completely. +// Otherwise we would probably end up with a mixed string in the buffer. +// If using RTT from within interrupts, multiple tasks or multi processors, define the SEGGER_RTT_LOCK() and SEGGER_RTT_UNLOCK() function here. +// +// SEGGER_RTT_MAX_INTERRUPT_PRIORITY can be used in the sample lock routines on Cortex-M3/4. +// Make sure to mask all interrupts which can send RTT data, i.e. generate SystemView events, or cause task switches. +// When high-priority interrupts must not be masked while sending RTT data, SEGGER_RTT_MAX_INTERRUPT_PRIORITY needs to be adjusted accordingly. +// (Higher priority = lower priority number) +// Default value for embOS: 128u +// Default configuration in FreeRTOS: configMAX_SYSCALL_INTERRUPT_PRIORITY: ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) +// In case of doubt mask all interrupts: 1 << (8 - BASEPRI_PRIO_BITS) i.e. 1 << 5 when 3 bits are implemented in NVIC +// or define SEGGER_RTT_LOCK() to completely disable interrupts. +// +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) // Interrupt priority to lock on SEGGER_RTT_LOCK on Cortex-M3/4 (Default: 0x20) +#endif + +/********************************************************************* +* +* RTT lock configuration for SEGGER Embedded Studio, +* Rowley CrossStudio and GCC +*/ +#if ((defined(__SES_ARM) || defined(__SES_RISCV) || defined(__CROSSWORKS_ARM) || defined(__GNUC__) || defined(__clang__)) && !defined(__CC_ARM)) +#if (defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_8M_BASE__)) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + __asm volatile( \ + "mrs %0, primask \n\t" \ + "movs r1, $1 \n\t" \ + "msr primask, r1 \n\t" \ + : "=r"(LockState) \ + : \ + : "r1"); + +#define SEGGER_RTT_UNLOCK() \ + __asm volatile("msr primask, %0 \n\t" \ + : \ + : "r"(LockState) \ + :); \ + } +#elif (defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)) +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) +#endif +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + __asm volatile( \ + "mrs %0, basepri \n\t" \ + "mov r1, %1 \n\t" \ + "msr basepri, r1 \n\t" \ + : "=r"(LockState) \ + : "i"(SEGGER_RTT_MAX_INTERRUPT_PRIORITY) \ + : "r1"); + +#define SEGGER_RTT_UNLOCK() \ + __asm volatile("msr basepri, %0 \n\t" \ + : \ + : "r"(LockState) \ + :); \ + } + +#elif defined(__ARM_ARCH_7A__) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + __asm volatile( \ + "mrs r1, CPSR \n\t" \ + "mov %0, r1 \n\t" \ + "orr r1, r1, #0xC0 \n\t" \ + "msr CPSR_c, r1 \n\t" \ + : "=r"(LockState) \ + : \ + : "r1"); + +#define SEGGER_RTT_UNLOCK() \ + __asm volatile( \ + "mov r0, %0 \n\t" \ + "mrs r1, CPSR \n\t" \ + "bic r1, r1, #0xC0 \n\t" \ + "and r0, r0, #0xC0 \n\t" \ + "orr r1, r1, r0 \n\t" \ + "msr CPSR_c, r1 \n\t" \ + : \ + : "r"(LockState) \ + : "r0", "r1"); \ + } +#elif defined(__riscv) || defined(__riscv_xlen) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + __asm volatile( \ + "csrr %0, mstatus \n\t" \ + "csrci mstatus, 8 \n\t" \ + "andi %0, %0, 8 \n\t" \ + : "=r"(LockState) \ + : \ + :); + +#define SEGGER_RTT_UNLOCK() \ + __asm volatile( \ + "csrr a1, mstatus \n\t" \ + "or %0, %0, a1 \n\t" \ + "csrs mstatus, %0 \n\t" \ + : \ + : "r"(LockState) \ + : "a1"); \ + } +#else +#define SEGGER_RTT_LOCK() +#define SEGGER_RTT_UNLOCK() +#endif +#endif + +/********************************************************************* +* +* RTT lock configuration for IAR EWARM +*/ +#ifdef __ICCARM__ +#if (defined(__ARM6M__) && (__CORE__ == __ARM6M__)) || \ + (defined(__ARM8M_BASELINE__) && (__CORE__ == __ARM8M_BASELINE__)) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + LockState = __get_PRIMASK(); \ + __set_PRIMASK(1); + +#define SEGGER_RTT_UNLOCK() \ + __set_PRIMASK(LockState); \ + } +#elif (defined(__ARM7EM__) && (__CORE__ == __ARM7EM__)) || \ + (defined(__ARM7M__) && (__CORE__ == __ARM7M__)) || \ + (defined(__ARM8M_MAINLINE__) && (__CORE__ == __ARM8M_MAINLINE__)) || \ + (defined(__ARM8M_MAINLINE__) && (__CORE__ == __ARM8M_MAINLINE__)) +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) +#endif +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + LockState = __get_BASEPRI(); \ + __set_BASEPRI(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); + +#define SEGGER_RTT_UNLOCK() \ + __set_BASEPRI(LockState); \ + } +#endif +#endif + +/********************************************************************* +* +* RTT lock configuration for IAR RX +*/ +#ifdef __ICCRX__ +#define SEGGER_RTT_LOCK() \ + { \ + unsigned long LockState; \ + LockState = __get_interrupt_state(); \ + __disable_interrupt(); + +#define SEGGER_RTT_UNLOCK() \ + __set_interrupt_state(LockState); \ + } +#endif + +/********************************************************************* +* +* RTT lock configuration for IAR RL78 +*/ +#ifdef __ICCRL78__ +#define SEGGER_RTT_LOCK() \ + { \ + __istate_t LockState; \ + LockState = __get_interrupt_state(); \ + __disable_interrupt(); + +#define SEGGER_RTT_UNLOCK() \ + __set_interrupt_state(LockState); \ + } +#endif + +/********************************************************************* +* +* RTT lock configuration for KEIL ARM +*/ +#ifdef __CC_ARM +#if (defined __TARGET_ARCH_6S_M) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + register unsigned char PRIMASK __asm("primask"); \ + LockState = PRIMASK; \ + PRIMASK = 1u; \ + __schedule_barrier(); + +#define SEGGER_RTT_UNLOCK() \ + PRIMASK = LockState; \ + __schedule_barrier(); \ + } +#elif (defined(__TARGET_ARCH_7_M) || defined(__TARGET_ARCH_7E_M)) +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) +#endif +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + register unsigned char BASEPRI __asm("basepri"); \ + LockState = BASEPRI; \ + BASEPRI = SEGGER_RTT_MAX_INTERRUPT_PRIORITY; \ + __schedule_barrier(); + +#define SEGGER_RTT_UNLOCK() \ + BASEPRI = LockState; \ + __schedule_barrier(); \ + } +#endif +#endif + +/********************************************************************* +* +* RTT lock configuration for TI ARM +*/ +#ifdef __TI_ARM__ +#if defined(__TI_ARM_V6M0__) +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + LockState = __get_PRIMASK(); \ + __set_PRIMASK(1); + +#define SEGGER_RTT_UNLOCK() \ + __set_PRIMASK(LockState); \ + } +#elif (defined(__TI_ARM_V7M3__) || defined(__TI_ARM_V7M4__)) +#ifndef SEGGER_RTT_MAX_INTERRUPT_PRIORITY +#define SEGGER_RTT_MAX_INTERRUPT_PRIORITY (0x20) +#endif +#define SEGGER_RTT_LOCK() \ + { \ + unsigned int LockState; \ + LockState = _set_interrupt_priority(SEGGER_RTT_MAX_INTERRUPT_PRIORITY); + +#define SEGGER_RTT_UNLOCK() \ + _set_interrupt_priority(LockState); \ + } +#endif +#endif + +/********************************************************************* +* +* RTT lock configuration for CCRX +*/ +#ifdef __RX +#define SEGGER_RTT_LOCK() \ + { \ + unsigned long LockState; \ + LockState = get_psw() & 0x010000; \ + clrpsw_i(); + +#define SEGGER_RTT_UNLOCK() \ + set_psw(get_psw() | LockState); \ + } +#endif + +/********************************************************************* +* +* RTT lock configuration for embOS Simulation on Windows +* (Can also be used for generic RTT locking with embOS) +*/ +#if defined(WIN32) || defined(SEGGER_RTT_LOCK_EMBOS) + +void OS_SIM_EnterCriticalSection(void); +void OS_SIM_LeaveCriticalSection(void); + +#define SEGGER_RTT_LOCK() \ + { \ + OS_SIM_EnterCriticalSection(); + +#define SEGGER_RTT_UNLOCK() \ + OS_SIM_LeaveCriticalSection(); \ + } +#endif + +/********************************************************************* +* +* RTT lock configuration fallback +*/ +#ifndef SEGGER_RTT_LOCK +#define SEGGER_RTT_LOCK() // Lock RTT (nestable) (i.e. disable interrupts) +#endif + +#ifndef SEGGER_RTT_UNLOCK +#define SEGGER_RTT_UNLOCK() // Unlock RTT (nestable) (i.e. enable previous interrupt lock state) +#endif + +#endif +/*************************** End of file ****************************/ diff --git a/Platform/stm32f401re/config/STM32CubeF4/cmake/CMakeLists.txt b/Platform/stm32f401re/config/STM32CubeF4/cmake/CMakeLists.txt new file mode 100644 index 0000000..c254544 --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/cmake/CMakeLists.txt @@ -0,0 +1,141 @@ +# Options to be defined by application: +# +# * STM32CUBEF4_DEVICE +# Allowed values: STM32F462QE +# +# * STM32CUBEF4_CONF +# Directory where to put configuration headers +# Example: ${PROJECT_SOURCE_DIR}/config/STM32CubeF4 +# +# * STM32CUBEF4_CMSIS_STARTUP_FILE +# Allowed values: startup_stm32f462xx.s +# +# * STM32CUBEF4_USBD_CLASS +# Allowed values: AUDIO, CDC, DFU, HID, MSC +# +# * STM32CUBEF4_DOXYGEN_OUTPUT_DIRECTORY +# Directory where to put doxygen output + +cmake_minimum_required(VERSION 3.15) + +# if(NOT DEFINED STM32CUBEF4_DEVICE) +# message(FATAL_ERROR "No processor defined") +# endif(NOT DEFINED STM32CUBEF4_DEVICE) + +# string(TOUPPER ${STM32CUBEF4_DEVICE} STM32CUBEF4_DEVICE_U) +# string(TOLOWER ${STM32CUBEF4_DEVICE} STM32CUBEF4_DEVICE_L) + +# # Determine STM32CUBEF4_DEVICE family +# string(REGEX MATCH "^(STM32[FL][0-9])" STM32CUBEF4_CPU_FAMILY_U "${STM32CUBEF4_DEVICE_U}") +# string(TOLOWER ${STM32CUBEF4_CPU_FAMILY_U} STM32CUBEF4_CPU_FAMILY_L) + +# # Generic families +# string(REGEX MATCH "^(STM32[FL][0-9][0-9][0-9])([A-Z])([A-Z0-9])" STM32CUBEF4_CPU_FAMILY_MATCH "${STM32CUBEF4_DEVICE_U}") +# set(STM32CUBEF4_CPU_FAMILY_A "${CMAKE_MATCH_1}x${CMAKE_MATCH_3}") + +# # Determine short device type +# string(REGEX MATCH "^(STM32[FL][0-9][0-9][0-9])" STM32CUBEF4_CPU_TYPE_U "${STM32CUBEF4_DEVICE_U}") +# string(TOLOWER ${STM32CUBEF4_CPU_TYPE_U} STM32CUBEF4_CPU_TYPE_L) + +# # Set CPU type for compiler +# if(${STM32CUBEF4_CPU_FAMILY_U} STREQUAL "STM32F4") +# set(STM32CUBEF4_CPU_TYPE "m4") +# else() +# message(FATAL_ERROR "Unrecognised device family: ${STM32CUBEF4_CPU_FAMILY_U}") +# endif() + +# if(NOT ${STM32CUBEF4_CPU_FAMILY_U} STREQUAL "STM32F4") +# message(FATAL_ERROR "CPU family ${STM32CUBEF4_CPU_FAMILY_U} not supported.") +# endif() + +# # Print configuration +# message("STM32CubeF4 configuration") +# message(" Device: ${STM32CUBEF4_DEVICE}") +# if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") +# message(" Library type: Full (compiling for arm)") +# else() +# message(" Library type: Interface only (not compiling for arm)") +# endif() + +#set(stm32cubeF4_DEVICE ${MCU}) +set(STM32CUBEF4_CONF ${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/stm32cubeF4) + +include(${stm32cubef4_SOURCE_DIR}/cmake/cmsis.cmake) +include(${stm32cubef4_SOURCE_DIR}/cmake/hal.cmake) +##here +#include(${CMAKE_CURRENT_LIST_DIR}/cmake/usb-device.cmake) + +# Doxygen documentation +# find_package(Doxygen) + +# if(DOXYGEN_FOUND) +# # Create new scope to avoid setting DOXYGEN_* variables in top level scope +# function(stm32cubef4_add_doxygen) +# # Include Doxygen default parameters +# include(${PROJECT_SOURCE_DIR}/external/Toolchain/cmake/config/doxygen.cmake) + +# # Set Doxygen project-specific parameters +# if(STM32CUBEF4_DOXYGEN_OUTPUT_DIRECTORY) +# set(DOXYGEN_OUTPUT_DIRECTORY ${STM32CUBEF4_DOXYGEN_OUTPUT_DIRECTORY}) +# else() +# set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/doxygen") +# endif() +# set(DOXYGEN_WARN_LOGFILE "${DOXYGEN_OUTPUT_DIRECTORY}/doxygen.log") +# set(DOXYGEN_STRIP_FROM_PATH "${PROJECT_SOURCE_DIR}") + +# set(DOXYGEN_PROJECT_BRIEF "") +# set(DOXYGEN_PROJECT_NAME "STM32CubeF4") +# set(DOXYGEN_PROJECT_NUMBER "") +# set(DOXYGEN_GENERATE_TAGFILE "${DOXYGEN_OUTPUT_DIRECTORY}/stm32cubef4.tag") + +# # Get files for CMSIS +# get_target_property(CMSIS_TYPE stm32cubef4-cmsis TYPE) +# if (CMSIS_TYPE STREQUAL "INTERFACE_LIBRARY") +# get_target_property(CMSIS_SOURCES stm32cubef4-cmsis INTERFACE_SOURCES) +# if (CMSIS_SOURCES STREQUAL "CMSIS_SOURCES-NOTFOUND") +# set(CMSIS_SOURCES "") +# endif() +# get_target_property(CMSIS_INCLUDE_DIRECTORIES stm32cubef4-cmsis INTERFACE_INCLUDE_DIRECTORIES) +# else() +# get_target_property(CMSIS_SOURCES stm32cubef4-cmsis SOURCES) +# get_target_property(CMSIS_INCLUDE_DIRECTORIES stm32cubef4-cmsis INCLUDE_DIRECTORIES) +# endif() + +# # Get files for HAL +# get_target_property(HAL_TYPE stm32cubef4-hal TYPE) +# if (HAL_TYPE STREQUAL "INTERFACE_LIBRARY") +# get_target_property(HAL_SOURCES stm32cubef4-hal INTERFACE_SOURCES) +# if (HAL_SOURCES STREQUAL "HAL_SOURCES-NOTFOUND") +# set(HAL_SOURCES "") +# endif() +# get_target_property(HAL_INCLUDE_DIRECTORIES stm32cubef4-hal INTERFACE_INCLUDE_DIRECTORIES) +# else() +# get_target_property(HAL_SOURCES stm32cubef4-hal SOURCES) +# get_target_property(HAL_INCLUDE_DIRECTORIES stm32cubef4-hal INCLUDE_DIRECTORIES) +# endif() + +# # Get files for USB device +# get_target_property(USB_DEVICE_TYPE stm32cubef4-usb-device TYPE) +# if (USB_DEVICE_TYPE STREQUAL "INTERFACE_LIBRARY") +# get_target_property(USB_DEVICE_SOURCES stm32cubef4-usb-device INTERFACE_SOURCES) +# if (USB_DEVICE_SOURCES STREQUAL "USB_DEVICE_SOURCES-NOTFOUND") +# set(USB_DEVICE_SOURCES "") +# endif() +# get_target_property(USB_DEVICE_INCLUDE_DIRECTORIES stm32cubef4-usb-device INTERFACE_INCLUDE_DIRECTORIES) +# else() +# get_target_property(USB_DEVICE_SOURCES stm32cubef4-usb-device SOURCES) +# get_target_property(USB_DEVICE_INCLUDE_DIRECTORIES stm32cubef4-usb-device INCLUDE_DIRECTORIES) +# endif() + +# doxygen_add_docs( +# doxygen-stm32cubef4 +# ${CMSIS_SOURCES} ${CMSIS_INCLUDE_DIRECTORIES} ${HAL_SOURCES} ${HAL_INCLUDE_DIRECTORIES} ${USB_DEVICE_SOURCES} ${USB_DEVICE_INCLUDE_DIRECTORIES} +# COMMENT "Generate doxygen documentation" +# ) + +# set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${DOXYGEN_OUTPUT_DIRECTORY}) +# endfunction() + +# stm32cubef4_add_doxygen() + +#endif() diff --git a/Platform/stm32f401re/config/STM32CubeF4/cmake/cmsis.cmake b/Platform/stm32f401re/config/STM32CubeF4/cmake/cmsis.cmake new file mode 100644 index 0000000..4d82bd5 --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/cmake/cmsis.cmake @@ -0,0 +1,106 @@ +get_filename_component(STM32CUBEF4_CMSIS_BASE "${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS" REALPATH) + +set(STM32CUBEF4_CMSIS_DEVICE_BASE "${STM32CUBEF4_CMSIS_BASE}/Device/ST/TSM32F4xx") +set(STM32CUBEF4_CMSIS_STARTUP_BASE ${STM32CUBEF4_CMSIS_DEVICE_BASE}/Source/Templates/gcc) +set(STM32CUBEF4_CMSIS_CURRENT_LEN 0) + + + + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + # message(STATUS "Startup file ${STM32CUBEF4_CMSIS_STARTUP_FILE}") + # # Find startup file + # if(EXISTS "${STM32CUBEF4_CMSIS_STARTUP_FILE}") + # elseif(EXISTS "${STM32CUBEF4_CMSIS_STARTUP_BASE}/${STM32CUBEF4_CMSIS_STARTUP_FILE}") + # set(STM32CUBEF4_CMSIS_STARTUP_FILE ${STM32CUBEF4_CMSIS_STARTUP_BASE}/${STM32CUBEF4_CMSIS_STARTUP_FILE}) + # elseif(EXISTS ${STM32CUBEF4_CMSIS_DEVICE_BASE}/Source/Templates/gcc/startup_${STM32CUBEF4_DEVICE_L}.s) + # # Simple solution (direct match) + # set(STM32CUBEF4_CMSIS_STARTUP_FILE ${STM32CUBEF4_CMSIS_DEVICE_BASE}/Source/Templates/gcc/startup_${STM32CUBEF4_DEVICE_L}.s) + # else() + # # Complicated solution, match family and chip revision + # file(GLOB STM32CUBEF4_CMSIS_STARTUP_FILES RELATIVE ${STM32CUBEF4_CMSIS_STARTUP_BASE} ${STM32CUBEF4_CMSIS_STARTUP_BASE}/startup_${STM32CUBEF4_CPU_TYPE_L}*.s) + # foreach(FILE ${STM32CUBEF4_CMSIS_STARTUP_FILES}) + # string(REGEX REPLACE "\\.[^.]*$" "" STM32CUBEF4_CMSIS_TEST_FILE ${FILE}) + # string(REGEX REPLACE "x" "[a-zA-Z]" STM32CUBEF4_CMSIS_TEST_FILE ${STM32CUBEF4_CMSIS_TEST_FILE}) + # set(STM32CUBEF4_CMSIS_TEST_MATCH false) + # string(REGEX MATCH "^(${STM32CUBEF4_CMSIS_TEST_FILE})" STM32CUBEF4_CMSIS_TEST_MATCH "startup_${STM32CUBEF4_DEVICE_L}.s") + # if(STM32CUBEF4_CMSIS_TEST_MATCH) + # string(LENGTH ${FILE} STM32CUBEF4_CMSIS_FILE_LEN) + # if(${STM32CUBEF4_CMSIS_FILE_LEN} GREATER ${STM32CUBEF4_CMSIS_CURRENT_LEN}}) + # set(STM32CUBEF4_CMSIS_STARTUP_FILE ${STM32CUBEF4_CMSIS_STARTUP_BASE}/${FILE}) + # string(LENGTH ${FILE} STM32CUBEF4_CMSIS_CURRENT_LEN) + # endif() + # endif() + # endforeach(FILE) + # endif() + + # set(STM32CUBEF4_CMSIS_STARTUP_FILE ${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f401xe.s) + # message(STATUS "Startup file ${STM32CUBEF4_CMSIS_STARTUP_FILE}") + + # if (EXISTS "${STM32CUBEF4_CMSIS_STARTUP_FILE}") + # string(REPLACE ${PROJECT_SOURCE_DIR} "" STM32CUBEF4_CMSIS_STARTUP_FILE_NO_PROJECT_DIR ${STM32CUBEF4_CMSIS_STARTUP_FILE}) + # message(" Startup file: ${STM32CUBEF4_CMSIS_STARTUP_FILE_NO_PROJECT_DIR}") + # else() + # message(FATAL_ERROR "Startup file ${STM32CUBEF4_CMSIS_STARTUP_FILE} not found") + # endif() + + # Set system file name + set(CMSIS_SYSTEM_FILE ${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c) + + + add_library(stm32cubef4-cmsis + ${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f401xe.s + ${CMSIS_SYSTEM_FILE} + ) + + target_include_directories(stm32cubef4-cmsis SYSTEM + PUBLIC + ${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS/Include + ${stm32cubef4_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Include + + #${STM32CUBEF4_CMSIS_BASE}/Include + #${STM32CUBEF4_CMSIS_DEVICE_BASE}/Include + ) + + target_compile_options(stm32cubef4-cmsis + PUBLIC + #-DSTM32F401RE -D${STM32CUBEF4_DEVICE} + -DSTM32F401xE + #-D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + ) + + set_target_properties(stm32cubef4-cmsis PROPERTIES LINKER_LANGUAGE CXX) + # target_compile_options(stm32cubef4-cmsis + # INTERFACE + # -D # -D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + # ) + +else() + + add_library(stm32cubef4-cmsis INTERFACE + ) + + target_include_directories(stm32cubef4-cmsis SYSTEM + INTERFACE + ${STM32CUBEF4_CMSIS_BASE}/Include + ${STM32CUBEF4_CMSIS_DEVICE_BASE}/Include + ) + + set_target_properties(stm32cubef4-cmsis PROPERTIES LINKER_LANGUAGE CXX) + # target_compile_options(stm32cubef4-cmsis + # INTERFACE + # -DSTM32F401xx # -D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + # ) + + target_compile_options(stm32cubef4-cmsis + INTERFACE + -D${STM32CUBEF4_DEVICE} -D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + ) + +endif() + + +# message(STATUS "STM32CUBEF4_DEVICE ${STM32CUBEF4_DEVICE}") +# message(STATUS "STM32CUBEF4_CPU_TYPE_U ${STM32CUBEF4_CPU_TYPE_U}") +# message(STATUS "STM32CUBEF4_CPU_FAMILY_U ${STM32CUBEF4_CPU_FAMILY_U}") +# message(STATUS "STM32CUBEF4_CPU_FAMILY_A ${STM32CUBEF4_CPU_FAMILY_A}") diff --git a/Platform/stm32f401re/config/STM32CubeF4/cmake/hal.cmake b/Platform/stm32f401re/config/STM32CubeF4/cmake/hal.cmake new file mode 100644 index 0000000..cbb8236 --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/cmake/hal.cmake @@ -0,0 +1,67 @@ +# get_filename_component(STM32CUBEF4_HAL_BASE "${stm32cubef4_SOURCE_DIR}/Drivers" REALPATH) + +# set(STM32CUBEF4_HAL_LOCATION ${STM32CUBEF4_HAL_BASE}/STM32F4xx_HAL_Driver) +# set(STM32CUBEF4_HAL_SOURCES +# ${STM32CUBEF4_HAL_LOCATION}/Src +# ) + +# message(STATUS "stm32cubef4_SOURCE_DIR ${stm32cubef4_SOURCE_DIR}") + +if(NOT DEFINED STM32CUBEF4_CONF) + message(FATAL_ERROR "STM32CUBEF4_CONF not defined.") +endif(NOT DEFINED STM32CUBEF4_CONF) + +# string(REPLACE ${stm32cubef4_SOURCE_DIR} "" STM32CUBEF4_CONF_NO_PROJECT_DIR ${STM32CUBEF4_CONF}) +# message("Config directory: ${STM32CUBEF4_CONF_NO_PROJECT_DIR}") + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + + add_library(stm32cubef4-hal + ${stm32cubef4_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src + ) + + # the include directories are explicitly marked as non system includes as cmake apparently removes system include paths from the dependency tree + target_include_directories(stm32cubef4-hal + PUBLIC + ${stm32cubef4_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Inc + ${STM32CUBEF4_CONF} + ) + + target_link_libraries(stm32cubef4-hal + PUBLIC + stm32cubef4-cmsis + ) + + target_compile_options(stm32cubef4-hal + PUBLIC + #-D${STM32CUBEF4_DEVICE} -DSTM32F401RE + -DSTM32F401xE + #-D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + ) + set_target_properties(stm32cubef4-hal PROPERTIES LINKER_LANGUAGE CXX) + +else() + + add_library(stm32cubef4-hal INTERFACE + ) + + # the include directories are explicitly marked as non system includes as cmake apparently removes system include paths from the dependency tree + target_include_directories(stm32cubef4-hal + INTERFACE + ${stm32cubef4_SOURCE_DIR}/Drivers/STM32F4xx_HAL_Driver/Inc + ${STM32CUBEF4_CONF} + ) + + target_link_libraries(stm32cubef4-hal + INTERFACE + stm32cubef4-cmsis + ) + + # target_compile_options(stm32cubef4-hal + # INTERFACE + # -D${STM32CUBEF4_DEVICE} -D${STM32CUBEF4_CPU_TYPE_U}xx -D${STM32CUBEF4_CPU_FAMILY_U} -D${STM32CUBEF4_CPU_FAMILY_A} + # ) + + set_target_properties(stm32cubef4-hal PROPERTIES LINKER_LANGUAGE CXX) + +endif() \ No newline at end of file diff --git a/Platform/stm32f401re/config/STM32CubeF4/cmake/usb-device.cmake b/Platform/stm32f401re/config/STM32CubeF4/cmake/usb-device.cmake new file mode 100644 index 0000000..1558dda --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/cmake/usb-device.cmake @@ -0,0 +1,71 @@ +get_filename_component(USB_BASE_PATH "${stm32cubef4_SOURCE_DIR}/Middlewares/ST/STM32_USB_Device_Library" REALPATH) + +if(STM32CUBEF4_USBD_CLASS) + if(${STM32CUBEF4_USBD_CLASS} STREQUAL "AUDIO") + set(USB_CLASS_FILES ${USB_BASE_PATH}/Class/AUDIO/Src/usbd_audio.c) + set(USB_CLASS_INC ${USB_BASE_PATH}/Class/AUDIO/Inc) + elseif(${STM32CUBEF4_USBD_CLASS} STREQUAL "CDC") + set(USB_CLASS_FILES ${USB_BASE_PATH}/Class/CDC/Src/usbd_cdc.c) + set(USB_CLASS_INC ${USB_BASE_PATH}/Class/CDC/Inc) + elseif(${STM32CUBEF4_USBD_CLASS} STREQUAL "DFU") + set(USB_CLASS_FILES ${USB_BASE_PATH}/Class/DFU/Src/usbd_dfu.c) + set(USB_CLASS_INC ${USB_BASE_PATH}/Class/DFU/Inc) + elseif(${STM32CUBEF4_USBD_CLASS} STREQUAL "HID") + set(USB_CLASS_FILES ${USB_BASE_PATH}/Class/HID/Src/usbd_hid.c) + set(USB_CLASS_INC ${USB_BASE_PATH}/Class/HID/Inc) + elseif(${STM32CUBEF4_USBD_CLASS} STREQUAL "MSC") + set(USB_CLASS_FILES + ${USB_BASE_PATH}/Class/MSC/Src/usbd_msc_bot.c + ${USB_BASE_PATH}/Class/MSC/Src/usbd_msc_data.c + ${USB_BASE_PATH}/Class/MSC/Src/usbd_msc_scsi.c + ${USB_BASE_PATH}/Class/MSC/Src/usbd_msc.c + ) + set(USB_CLASS_INC ${USB_BASE_PATH}/Class/MSC/Inc) + endif() +endif() + +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + + add_library(stm32cubef4-usb-device + ${USB_CLASS_FILES} + ${USB_BASE_PATH}/Core/Src/usbd_core.c + ${USB_BASE_PATH}/Core/Src/usbd_ctlreq.c + ${USB_BASE_PATH}/Core/Src/usbd_ioreq.c + ) + + target_include_directories(stm32cubef4-usb-device + PUBLIC + ${STM32CUBEF4_CONF} + ${USB_CLASS_INC} + ${USB_BASE_PATH}/Core/Inc + ) + + target_link_libraries(stm32cubef4-usb-device + PUBLIC + # The config file (usbd_conf.h) most likely needs to access HAL functions, + # thus even if the library's source file itself does not have this dependency, + # this link library needs to be added. + stm32cubef4-hal + ) + +else() + + add_library(stm32cubef4-usb-device INTERFACE + ) + + target_include_directories(stm32cubef4-usb-device + INTERFACE + ${STM32CUBEF4_CONF} + ${USB_CLASS_INC} + ${USB_BASE_PATH}/Core/Inc + ) + + target_link_libraries(stm32cubef4-usb-device + INTERFACE + # The config file (usbd_conf.h) most likely needs to access HAL functions, + # thus even if the library's source file itself does not have this dependency, + # this link library needs to be added. + stm32cubef4-hal + ) + +endif() diff --git a/Platform/stm32f401re/config/STM32CubeF4/stm32f4xx_hal_conf.h b/Platform/stm32f401re/config/STM32CubeF4/stm32f4xx_hal_conf.h new file mode 100644 index 0000000..92102f7 --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/stm32f4xx_hal_conf.h @@ -0,0 +1,406 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_conf.h + * @author MCD Application Team + * @version V1.2.1 + * @date 13-March-2015 + * @brief HAL configuration file + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_CONF_H +#define __STM32F4xx_HAL_CONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +/* ########################## Module Selection ############################## */ +/** + * @brief This is the list of modules to be used in the HAL driver + */ +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_CAN_MODULE_ENABLED +#define HAL_CRC_MODULE_ENABLED +#define HAL_CRYP_MODULE_ENABLED +#define HAL_DAC_MODULE_ENABLED +#define HAL_DCMI_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_DMA2D_MODULE_ENABLED +#define HAL_ETH_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_NAND_MODULE_ENABLED +#define HAL_NOR_MODULE_ENABLED +#define HAL_PCCARD_MODULE_ENABLED +#define HAL_SRAM_MODULE_ENABLED +#define HAL_SDRAM_MODULE_ENABLED +#define HAL_HASH_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_I2S_MODULE_ENABLED +#define HAL_IWDG_MODULE_ENABLED +#define HAL_LTDC_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +#define HAL_SAI_MODULE_ENABLED +#define HAL_SD_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED +#define HAL_IRDA_MODULE_ENABLED +#define HAL_SMARTCARD_MODULE_ENABLED +#define HAL_WWDG_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +#define HAL_HCD_MODULE_ENABLED + + +/* ########################## HSE/HSI Values adaptation ##################### */ +/** + * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSE is used as system clock source, directly or through the PLL). + */ +#if !defined (HSE_VALUE) + #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ +#endif /* HSE_VALUE */ + +#if !defined (HSE_STARTUP_TIMEOUT) + #define HSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for HSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSI is used as system clock source, directly or through the PLL). + */ +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* HSI_VALUE */ + +/** + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined (LSI_VALUE) + #define LSI_VALUE ((uint32_t)32000) +#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz + The real value may vary depending on the variations + in voltage and temperature. */ +/** + * @brief External Low Speed oscillator (LSE) value. + */ +#if !defined (LSE_VALUE) + #define LSE_VALUE ((uint32_t)32768) /*!< Value of the External Low Speed oscillator in Hz */ +#endif /* LSE_VALUE */ + +/** + * @brief External clock source for I2S peripheral + * This value is used by the I2S HAL module to compute the I2S clock source + * frequency, this source is inserted directly through I2S_CKIN pad. + */ +#if !defined (EXTERNAL_CLOCK_VALUE) + #define EXTERNAL_CLOCK_VALUE ((uint32_t)12288000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* EXTERNAL_CLOCK_VALUE */ + +/* Tip: To avoid modifying this file each time you need to use different HSE, + === you can define the HSE value in your toolchain compiler preprocessor. */ + +/* ########################### System Configuration ######################### */ +/** + * @brief This is the HAL system configuration section + */ +#define VDD_VALUE ((uint32_t)3300) /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY ((uint32_t)0x0F) /*!< tick interrupt priority */ +#define USE_RTOS 0 +#define PREFETCH_ENABLE 1 +#define INSTRUCTION_CACHE_ENABLE 1 +#define DATA_CACHE_ENABLE 1 + +/* ########################## Assert Selection ############################## */ +/** + * @brief Uncomment the line below to expanse the "assert_param" macro in the + * HAL drivers code + */ +/* #define USE_FULL_ASSERT 1 */ + +/* ################## Ethernet peripheral configuration ##################### */ + +/* Section 1 : Ethernet peripheral configuration */ + +/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */ +#define MAC_ADDR0 2 +#define MAC_ADDR1 0 +#define MAC_ADDR2 0 +#define MAC_ADDR3 0 +#define MAC_ADDR4 0 +#define MAC_ADDR5 0 + +/* Definition of the Ethernet driver buffers size and count */ +#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */ +#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */ +#define ETH_RXBUFNB ((uint32_t)4) /* 4 Rx buffers of size ETH_RX_BUF_SIZE */ +#define ETH_TXBUFNB ((uint32_t)4) /* 4 Tx buffers of size ETH_TX_BUF_SIZE */ + +/* Section 2: PHY configuration section */ + +/* DP83848 PHY Address*/ +#define DP83848_PHY_ADDRESS 0x01 +/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/ +#define PHY_RESET_DELAY ((uint32_t)0x000000FF) +/* PHY Configuration delay */ +#define PHY_CONFIG_DELAY ((uint32_t)0x00000FFF) + +#define PHY_READ_TO ((uint32_t)0x0000FFFF) +#define PHY_WRITE_TO ((uint32_t)0x0000FFFF) + +/* Section 3: Common PHY Registers */ + +#define PHY_BCR ((uint16_t)0x00) /*!< Transceiver Basic Control Register */ +#define PHY_BSR ((uint16_t)0x01) /*!< Transceiver Basic Status Register */ + +#define PHY_RESET ((uint16_t)0x8000) /*!< PHY Reset */ +#define PHY_LOOPBACK ((uint16_t)0x4000) /*!< Select loop-back mode */ +#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */ +#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */ +#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */ +#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */ +#define PHY_AUTONEGOTIATION ((uint16_t)0x1000) /*!< Enable auto-negotiation function */ +#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200) /*!< Restart auto-negotiation function */ +#define PHY_POWERDOWN ((uint16_t)0x0800) /*!< Select the power down mode */ +#define PHY_ISOLATE ((uint16_t)0x0400) /*!< Isolate PHY from MII */ + +#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */ +#define PHY_LINKED_STATUS ((uint16_t)0x0004) /*!< Valid link established */ +#define PHY_JABBER_DETECTION ((uint16_t)0x0002) /*!< Jabber condition detected */ + +/* Section 4: Extended PHY Registers */ + +#define PHY_SR ((uint16_t)0x10) /*!< PHY status register Offset */ +#define PHY_MICR ((uint16_t)0x11) /*!< MII Interrupt Control Register */ +#define PHY_MISR ((uint16_t)0x12) /*!< MII Interrupt Status and Misc. Control Register */ + +#define PHY_LINK_STATUS ((uint16_t)0x0001) /*!< PHY Link mask */ +#define PHY_SPEED_STATUS ((uint16_t)0x0002) /*!< PHY Speed mask */ +#define PHY_DUPLEX_STATUS ((uint16_t)0x0004) /*!< PHY Duplex mask */ + +#define PHY_MICR_INT_EN ((uint16_t)0x0002) /*!< PHY Enable interrupts */ +#define PHY_MICR_INT_OE ((uint16_t)0x0001) /*!< PHY Enable output interrupt events */ + +#define PHY_MISR_LINK_INT_EN ((uint16_t)0x0020) /*!< Enable Interrupt on change of link status */ +#define PHY_LINK_INTERRUPT ((uint16_t)0x2000) /*!< PHY link status interrupt mask */ + +/* Includes ------------------------------------------------------------------*/ +/** + * @brief Include module's header file + */ + +#ifdef HAL_RCC_MODULE_ENABLED + #include "stm32f4xx_hal_rcc.h" +#endif /* HAL_RCC_MODULE_ENABLED */ + +#ifdef HAL_GPIO_MODULE_ENABLED + #include "stm32f4xx_hal_gpio.h" +#endif /* HAL_GPIO_MODULE_ENABLED */ + +#ifdef HAL_DMA_MODULE_ENABLED + #include "stm32f4xx_hal_dma.h" +#endif /* HAL_DMA_MODULE_ENABLED */ + +#ifdef HAL_CORTEX_MODULE_ENABLED + #include "stm32f4xx_hal_cortex.h" +#endif /* HAL_CORTEX_MODULE_ENABLED */ + +#ifdef HAL_ADC_MODULE_ENABLED + #include "stm32f4xx_hal_adc.h" +#endif /* HAL_ADC_MODULE_ENABLED */ + +#ifdef HAL_CAN_MODULE_ENABLED + #include "stm32f4xx_hal_can.h" +#endif /* HAL_CAN_MODULE_ENABLED */ + +#ifdef HAL_CRC_MODULE_ENABLED + #include "stm32f4xx_hal_crc.h" +#endif /* HAL_CRC_MODULE_ENABLED */ + +#ifdef HAL_CRYP_MODULE_ENABLED + #include "stm32f4xx_hal_cryp.h" +#endif /* HAL_CRYP_MODULE_ENABLED */ + +#ifdef HAL_DMA2D_MODULE_ENABLED + #include "stm32f4xx_hal_dma2d.h" +#endif /* HAL_DMA2D_MODULE_ENABLED */ + +#ifdef HAL_DAC_MODULE_ENABLED + #include "stm32f4xx_hal_dac.h" +#endif /* HAL_DAC_MODULE_ENABLED */ + +#ifdef HAL_DCMI_MODULE_ENABLED + #include "stm32f4xx_hal_dcmi.h" +#endif /* HAL_DCMI_MODULE_ENABLED */ + +#ifdef HAL_ETH_MODULE_ENABLED + #include "stm32f4xx_hal_eth.h" +#endif /* HAL_ETH_MODULE_ENABLED */ + +#ifdef HAL_FLASH_MODULE_ENABLED + #include "stm32f4xx_hal_flash.h" +#endif /* HAL_FLASH_MODULE_ENABLED */ + +#ifdef HAL_SRAM_MODULE_ENABLED + #include "stm32f4xx_hal_sram.h" +#endif /* HAL_SRAM_MODULE_ENABLED */ + +#ifdef HAL_NOR_MODULE_ENABLED + #include "stm32f4xx_hal_nor.h" +#endif /* HAL_NOR_MODULE_ENABLED */ + +#ifdef HAL_NAND_MODULE_ENABLED + #include "stm32f4xx_hal_nand.h" +#endif /* HAL_NAND_MODULE_ENABLED */ + +#ifdef HAL_PCCARD_MODULE_ENABLED + #include "stm32f4xx_hal_pccard.h" +#endif /* HAL_PCCARD_MODULE_ENABLED */ + +#ifdef HAL_SDRAM_MODULE_ENABLED + #include "stm32f4xx_hal_sdram.h" +#endif /* HAL_SDRAM_MODULE_ENABLED */ + +#ifdef HAL_HASH_MODULE_ENABLED + #include "stm32f4xx_hal_hash.h" +#endif /* HAL_HASH_MODULE_ENABLED */ + +#ifdef HAL_I2C_MODULE_ENABLED + #include "stm32f4xx_hal_i2c.h" +#endif /* HAL_I2C_MODULE_ENABLED */ + +#ifdef HAL_I2S_MODULE_ENABLED + #include "stm32f4xx_hal_i2s.h" +#endif /* HAL_I2S_MODULE_ENABLED */ + +#ifdef HAL_IWDG_MODULE_ENABLED + #include "stm32f4xx_hal_iwdg.h" +#endif /* HAL_IWDG_MODULE_ENABLED */ + +#ifdef HAL_LTDC_MODULE_ENABLED + #include "stm32f4xx_hal_ltdc.h" +#endif /* HAL_LTDC_MODULE_ENABLED */ + +#ifdef HAL_PWR_MODULE_ENABLED + #include "stm32f4xx_hal_pwr.h" +#endif /* HAL_PWR_MODULE_ENABLED */ + +#ifdef HAL_RNG_MODULE_ENABLED + #include "stm32f4xx_hal_rng.h" +#endif /* HAL_RNG_MODULE_ENABLED */ + +#ifdef HAL_RTC_MODULE_ENABLED + #include "stm32f4xx_hal_rtc.h" +#endif /* HAL_RTC_MODULE_ENABLED */ + +#ifdef HAL_SAI_MODULE_ENABLED + #include "stm32f4xx_hal_sai.h" +#endif /* HAL_SAI_MODULE_ENABLED */ + +#ifdef HAL_SD_MODULE_ENABLED + #include "stm32f4xx_hal_sd.h" +#endif /* HAL_SD_MODULE_ENABLED */ + +#ifdef HAL_SPI_MODULE_ENABLED + #include "stm32f4xx_hal_spi.h" +#endif /* HAL_SPI_MODULE_ENABLED */ + +#ifdef HAL_TIM_MODULE_ENABLED + #include "stm32f4xx_hal_tim.h" +#endif /* HAL_TIM_MODULE_ENABLED */ + +#ifdef HAL_UART_MODULE_ENABLED + #include "stm32f4xx_hal_uart.h" +#endif /* HAL_UART_MODULE_ENABLED */ + +#ifdef HAL_USART_MODULE_ENABLED + #include "stm32f4xx_hal_usart.h" +#endif /* HAL_USART_MODULE_ENABLED */ + +#ifdef HAL_IRDA_MODULE_ENABLED + #include "stm32f4xx_hal_irda.h" +#endif /* HAL_IRDA_MODULE_ENABLED */ + +#ifdef HAL_SMARTCARD_MODULE_ENABLED + #include "stm32f4xx_hal_smartcard.h" +#endif /* HAL_SMARTCARD_MODULE_ENABLED */ + +#ifdef HAL_WWDG_MODULE_ENABLED + #include "stm32f4xx_hal_wwdg.h" +#endif /* HAL_WWDG_MODULE_ENABLED */ + +#ifdef HAL_PCD_MODULE_ENABLED + #include "stm32f4xx_hal_pcd.h" +#endif /* HAL_PCD_MODULE_ENABLED */ + +#ifdef HAL_HCD_MODULE_ENABLED + #include "stm32f4xx_hal_hcd.h" +#endif /* HAL_HCD_MODULE_ENABLED */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + + + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_HAL_CONF_H */ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file diff --git a/Platform/stm32f401re/config/STM32CubeF4/usbd_conf.h b/Platform/stm32f401re/config/STM32CubeF4/usbd_conf.h new file mode 100644 index 0000000..c3581b6 --- /dev/null +++ b/Platform/stm32f401re/config/STM32CubeF4/usbd_conf.h @@ -0,0 +1,72 @@ +/** + ****************************************************************************** + * @file USB_Device/HID_Standalone/Inc/usbd_conf.h + * @author MCD Application Team + * @brief General low level driver configuration + ****************************************************************************** + * @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. + * + ****************************************************************************** + */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_CONF_H +#define __USBD_CONF_H + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_hal.h" +#include +#include +#include + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Common Config */ +#define USBD_MAX_NUM_INTERFACES 1 +#define USBD_MAX_NUM_CONFIGURATION 1 +#define USBD_MAX_STR_DESC_SIZ 0x100 +#define USBD_SELF_POWERED 1 +#define USBD_DEBUG_LEVEL 0 + +/* Exported macro ------------------------------------------------------------*/ +/* Memory management macros */ +#define USBD_malloc malloc +#define USBD_free free +#define USBD_memset memset +#define USBD_memcpy memcpy +#define USBD_Delay HAL_Delay + +/* DEBUG macros */ +#if (USBD_DEBUG_LEVEL > 0) +#define USBD_UsrLog(...) printf(__VA_ARGS__);\ + printf("\n"); +#else +#define USBD_UsrLog(...) +#endif + +#if (USBD_DEBUG_LEVEL > 1) + +#define USBD_ErrLog(...) printf("ERROR: ") ;\ + printf(__VA_ARGS__);\ + printf("\n"); +#else +#define USBD_ErrLog(...) +#endif + +#if (USBD_DEBUG_LEVEL > 2) +#define USBD_DbgLog(...) printf("DEBUG : ") ;\ + printf(__VA_ARGS__);\ + printf("\n"); +#else +#define USBD_DbgLog(...) +#endif + +/* Exported functions ------------------------------------------------------- */ + +#endif /* __USBD_CONF_H */ diff --git a/Platform/stm32f401re/config/etl/etl_profile.h b/Platform/stm32f401re/config/etl/etl_profile.h new file mode 100644 index 0000000..77bd870 --- /dev/null +++ b/Platform/stm32f401re/config/etl/etl_profile.h @@ -0,0 +1,59 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com +Copyright(c) 2017 jwellbelove, scott-eddy +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. +******************************************************************************/ +#ifndef __ETL_PROFILE_H__ +#define __ETL_PROFILE_H__ +#if defined(PROFILE_MSVC) +#include "msvc_x86.h" +#elif defined(PROFILE_GCC_GENERIC) +#include "gcc_generic.h" +#elif defined(PROFILE_GCC_LINUX_X86) +#include "gcc_linux_x86.h" +#elif defined(PROFILE_GCC_WINDOWS_X86) +#include "gcc_windows_x86.h" +#elif defined(PROFILE_CLANG_GENERIC) +#include "clang_generic.h" +#elif defined(PROFILE_ARM_V5_GENERIC) +#include "armv5.h" +#elif defined(PROFILE_ARM_V6_GENERIC) +#include "armv6.h" +#elif defined(PROFILE_ARDUINO) +#include "arduino_arm.h" +#elif defined(PROFILE_TICC) +#include "ticc.h" +#elif defined(PROFILE_CPP03_GENERIC) +#include "cpp03.h" +#elif defined(PROFILE_CPP11_GENERIC) +#include "cpp11.h" +#elif defined(PROFILE_CPP14_GENERIC) +#include "cpp14.h" +#elif defined(PROFILE_CPP17_GENERIC) +#include "cpp17.h" +#elif defined(PROFILE_CUSTOM) +#include "custom_profile.h" +#else +#include "auto.h" +#endif + +#endif // __ETL_PROFILE_H_ diff --git a/Platform/stm32f401re/include/Rtos/TestAppRtos/Rtos.hpp b/Platform/stm32f401re/include/Rtos/TestAppRtos/Rtos.hpp new file mode 100644 index 0000000..a00fe1d --- /dev/null +++ b/Platform/stm32f401re/include/Rtos/TestAppRtos/Rtos.hpp @@ -0,0 +1,52 @@ +/** + * @file include/Rtos/Rtos/Rtos.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTAPPRTOS_HPP +#define TESTAPPRTOS_HPP + +#include +#include +#include +#include +#include +#include + +/// Namespace for Rtos Wrapper +namespace testapprtos { +/** + * @brief + * Inherited from @see class RtosTask it uses default properties of + * an os task to setup a privileged level executing task. + */ +class TaskSetup : public Task { +public: + /** + * @brief: Constructor calling into parent with fixed privileged level. + * @param[in] taskConfiguration @see rtosDef::rtosTaskConfiguration + */ + explicit TaskSetup(const TaskConfiguration& taskConfiguration) : Task(taskConfiguration){}; + +protected: +private: +}; + +extern "C" void vApplicationGetIdleTaskMemory( + StaticTask_t** ppxIdleTaskTCBBuffer, StackType_t** ppxIdleTaskStackBuffer, + uint32_t* pulIdleTaskStackSize); + +// The external RTos Hook called when idle Task is reached it might be used to redirect into C++ space +extern "C" void vApplicationIdleHook(); + +} // namespace testapprtos + +#endif // TESTAPPRTOS_HPP diff --git a/Platform/stm32f401re/src/CMakeLists.txt b/Platform/stm32f401re/src/CMakeLists.txt new file mode 100644 index 0000000..89b1805 --- /dev/null +++ b/Platform/stm32f401re/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(Rtos) +add_subdirectory(System) +add_subdirectory(Middleware) diff --git a/Platform/stm32f401re/src/Middleware/CMakeLists.txt b/Platform/stm32f401re/src/Middleware/CMakeLists.txt new file mode 100644 index 0000000..4ba0625 --- /dev/null +++ b/Platform/stm32f401re/src/Middleware/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(CommunicationChannel + ${CMAKE_CURRENT_SOURCE_DIR}/RttChannel.cpp +) + +target_compile_features(CommunicationChannel + PUBLIC + cxx_std_14 + ) + +target_include_directories(CommunicationChannel + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +target_link_libraries(CommunicationChannel + PUBLIC + CommunicationChannel-Interface + OsExtension + rtt + ) diff --git a/Platform/stm32f401re/src/Middleware/RttChannel.cpp b/Platform/stm32f401re/src/Middleware/RttChannel.cpp new file mode 100644 index 0000000..696df79 --- /dev/null +++ b/Platform/stm32f401re/src/Middleware/RttChannel.cpp @@ -0,0 +1,74 @@ +/** + * @file Platform/stm32f401re/src/Middleware/RttChannel.cpp + * @copyright Copyright 2021 Robert Bosch GmbH. All rights reserved. + * + * This source code is copyright protected and proprietary + * to Robert Bosch GmbH. Only those rights that have been + * explicitly granted to you by Robert Bosch GmbH in written + * form may be exercised. All other rights remain with + * Robert Bosch GmbH. + * + */ + +#include + +namespace tccinterface { +/// Creation of global rttChannel object +RttChannel rttChannel(OperationMode::POLLING_MODE, pdMS_TO_TICKS(200)); +TccInterface* tccInterface = &rttChannel; + +OperationMode RttChannel::getOperationMode() const { + return _operationMode; +} + +TickType_t RttChannel::registerPollingPeriod() const { + return _blockingTime; +} + +tccinterface::ErrCode RttChannel::init() { + const char* channelName = "TestApp"; + // use rtt channel 1 if the channel specified is channel 0 or out of rang + _channelId = (rttChannelId == 0u || rttChannelId > SEGGER_RTT_MAX_NUM_UP_BUFFERS - 1) ? 1u : rttChannelId; + + SEGGER_RTT_ConfigUpBuffer(_channelId, channelName, &_channelUpBuffer[0], channelUpBufferSize, SEGGER_RTT_MODE_DEFAULT); + SEGGER_RTT_ConfigDownBuffer(_channelId, channelName, &_channelDownBuffer[0], channelDownBufferSize, SEGGER_RTT_MODE_DEFAULT); + _initialized = true; + + return (tccinterface::ErrCode::SUCCESS); +} + +tccinterface::ErrCode RttChannel::send(const void* dataStream, size_t dataLength) { + tccinterface::ErrCode errCode = tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_SEND_FAILURE; + + if (_initialized) { + auto count = SEGGER_RTT_WriteNoLock(_channelId, dataStream, dataLength); + if (count == dataLength) { + errCode = tccinterface::ErrCode::SUCCESS; + } + } + return errCode; +} + +tccinterface::ErrCode RttChannel::send(const etl::array_view& datastream) { + return send(static_cast(datastream.data()), datastream.size()); +} + +tccinterface::ErrCode RttChannel::receive(size_t* length, void* buf, size_t maxsize) { + tccinterface::ErrCode errCode = tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_BUSY_OR_RESPONSE_FAILURE; + + if (_initialized && (nullptr != length)) { + *length = static_cast(SEGGER_RTT_Read(_channelId, buf, maxsize)); + errCode = tccinterface::ErrCode::SUCCESS; + } + return errCode; +} + +tccinterface::ErrCode RttChannel::receive(size_t* length, etl::array_view& bufView) { + return receive(length, static_cast(bufView.data()), bufView.size()); +} + +tccinterface::ErrCode RttChannel::shutdown() { + _initialized = false; + return (tccinterface::ErrCode::SUCCESS); +} +} // namespace tccinterface diff --git a/Platform/stm32f401re/src/Middleware/include/RttChannel/RttChannel.hpp b/Platform/stm32f401re/src/Middleware/include/RttChannel/RttChannel.hpp new file mode 100644 index 0000000..c1ee997 --- /dev/null +++ b/Platform/stm32f401re/src/Middleware/include/RttChannel/RttChannel.hpp @@ -0,0 +1,130 @@ +/** + * @file Platform/stm32f401re/src/Middleware/include/RttChannel/RttChannel.hpp + * @copyright Copyright 2021 Robert Bosch GmbH. All rights reserved. + * + * This source code is copyright protected and proprietary + * to Robert Bosch GmbH. Only those rights that have been + * explicitly granted to you by Robert Bosch GmbH in written + * form may be exercised. All other rights remain with + * Robert Bosch GmbH. + * + */ + +#ifndef RTTCHANNEL_RTTCHANNEL_HPP +#define RTTCHANNEL_RTTCHANNEL_HPP + +#include +#include +#include +#include +#include +#include + +namespace tccinterface { +/// The size in bytes for rtt channel 1 down buffer +constexpr uint16_t channelDownBufferSize = 256u; + +/// The size in bytes for rtt channel 1 up buffer +constexpr uint16_t channelUpBufferSize = 256u; + +/// rtt channel Id; should be in rang [1, SEGGER_RTT_MAX_NUM_UP_BUFFERS-1] otherwise channel 1 is used +constexpr uint8_t rttChannelId = 1u; + +class RttChannel : public TccInterface { +public: + /// Constructor + explicit RttChannel(OperationMode operationMode, TickType_t blockingTime) + : _operationMode{operationMode}, _blockingTime{blockingTime} {} + + /// Remove copy Constructor + RttChannel(const RttChannel&) = delete; + + /// Remove assign Constructor + RttChannel(const RttChannel&&) = delete; + + /// Remove assign Operation + const RttChannel& operator=(const RttChannel&) = delete; + + /// Remove move Operation + const RttChannel& operator=(RttChannel&&) = delete; + + /** + * @brief: Interface to initialize the coordination channel for data send and data reception + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode init() override; + + /** + * @brief: invokes rtt SEGGER_RTT_WriteNoLock to Store a specified number of characters in SEGGER RTT control block which is then read by the host. + * @param[in] datastream: Variable which holds the pointer to the data to be sent + * @param[in] dataLength: Variable to hold the dataLength to be sent out + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode send(const void* datastream, size_t dataLength) override; + + /** + * @brief: invokes rtt SEGGER_RTT_WriteNoLock to Store a specified number of characters in SEGGER RTT control block which is then read by the host. + * @param[in] datastream: data to be sent in a array_view + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode send(const etl::array_view& datastream) override; + + /** + * @brief:Data receive interface invoked to receive the data from remote. + * @details: The caller of this interface can invoke the interface until received data becomes '0'. + * @param[out] length: The pointer to the variable which holds received dataLength info + * @param[in] buf: The pointer to datastream buffer to which received data will be copied + * @param[in] maxsize: The variable which holds the maximum size of data is to be received. + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode receive(size_t* length, void* buf, size_t maxsize) override; + + /** + * @brief:Data receive interface invoked to receive the data from remote. + * @details: The caller of this interface can invoke the interface until received data becomes '0'. + * @param[out] length: The pointer to the variable which holds received dataLength info + * @param[out] bufView: array_view datastream to which received data will be copied + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode receive(size_t* length, etl::array_view& bufView) override; + + /** + * @brief: Shutdown all the channels initialized + * @return: tccinterface::ErrCode enum with possible communication channel failures + */ + tccinterface::ErrCode shutdown() override; + + /** + * @brief: getter to the operation mode interrupt/polling in use + * @return: Operation Mode for channel communication: INTERRUPT_MODE = 0, POLLING_MODE = 1; + */ + OperationMode getOperationMode() const override; + + /** + * @brief: Needs to be implemented if OperationMode is set to POLLING_MODE + * @return: Polling time in Ticks + */ + TickType_t registerPollingPeriod() const override; + +protected: + /// buffers for RTT channel 1 + std::array _channelDownBuffer; + std::array _channelUpBuffer; + +private: + /// Channnel initialization status + bool _initialized{false}; + + /// channel Id + uint8_t _channelId{1u}; + + /// INTERRUPT_MODE = 0, POLLING_MODE = 1; In case of INTERRUPT_MODE implementation a semaphore reference to TPC semaphore has to be used + OperationMode _operationMode; + + /// Polling time in Ticks + TickType_t _blockingTime; +}; + +} // namespace tccinterface + +#endif //RTTCHANNEL_RTTCHANNEL_HPP diff --git a/Platform/stm32f401re/src/Rtos/CMakeLists.txt b/Platform/stm32f401re/src/Rtos/CMakeLists.txt new file mode 100644 index 0000000..0e8fe03 --- /dev/null +++ b/Platform/stm32f401re/src/Rtos/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library(TestAppRtos + IdleTask.cpp + Rtos.cpp + ) + +target_include_directories(TestAppRtos + PUBLIC + ${PROJECT_SOURCE_DIR}/Platform/${PROJECT_PLATFORM}/include/Rtos + ) + +target_compile_features(TestAppRtos + PUBLIC + cxx_std_14 + ) + +target_link_libraries(TestAppRtos + PUBLIC + freertos + OsExtension + OsAbstraction + TestAppRtos-Interface + PRIVATE + #Hal + #Logger + #PlatformMemoryDefinitions + #Led + ) + + diff --git a/Platform/stm32f401re/src/Rtos/IdleTask.cpp b/Platform/stm32f401re/src/Rtos/IdleTask.cpp new file mode 100644 index 0000000..19432b8 --- /dev/null +++ b/Platform/stm32f401re/src/Rtos/IdleTask.cpp @@ -0,0 +1,36 @@ +/** + * @file Platform/stm32f401re/src/Rtos/IdleTask.cpp + * @brief Idle Task Related Callbacks and static memory allocation. + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +#if (configSUPPORT_STATIC_ALLOCATION == 1) && (configUSE_TIMERS == 1) + +/* configSUPPORT_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so + the application must provide an implementation of + vApplicationGetTimerTaskMemory() to provide the memory that is used by the Timer + service task. */ + +static constexpr size_t timer_task_stack_size = configTIMER_TASK_STACK_DEPTH; +static StackType_t timer_task_stack[timer_task_stack_size]; +static StaticTask_t timer_task_buffer; + +extern "C" void vApplicationGetTimerTaskMemory( + StaticTask_t** ppxTimerTaskTCBBuffer, StackType_t** ppxTimerTaskStackBuffer, + uint32_t* pulTimerTaskStackSize) { + *ppxTimerTaskTCBBuffer = &timer_task_buffer; + *ppxTimerTaskStackBuffer = timer_task_stack; + *pulTimerTaskStackSize = timer_task_stack_size; +} +#endif diff --git a/Platform/stm32f401re/src/Rtos/Rtos.cpp b/Platform/stm32f401re/src/Rtos/Rtos.cpp new file mode 100644 index 0000000..8f4b188 --- /dev/null +++ b/Platform/stm32f401re/src/Rtos/Rtos.cpp @@ -0,0 +1,312 @@ +/** + * @file src/Rtos/Rtos.cpp + * @brief This Module serves as Integration Layer for freeRTOS and is used to + * create static task objects within a mpu protected task region managed system. + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include + +#include +#include +/** + * Import of the SVC Logger call + */ +namespace logger { +/** + * \brief Function that sees to having the log message formatted and output. + * \param[in] arg Pointer to argument vector. + */ +void logSyscall(void* arg); // NOLINT(readability-identifier-naming) Defined by 3rd Party Library. +} //namespace logger + +namespace testapprtos { + +/// Index of the local Storage pointer feature of freertos containing the pointer to task object. +// static constexpr long localStoragePointerTaskObjectIndex = 0u; +BaseType_t localStoragePointerTaskObjectIndex = 0u; + +#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED +/// Memory Space keeping the Syscall_log Argument for invoking and executing out of SVC Context +static void* loggerArgument = nullptr; +#endif //configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + +/// Unique defined Pointer to first Item of Linked list of OS Objects. +Task* Task::firstTask = nullptr; +/// Time since OS Started up. Used for determine runtimes +static uint64_t totalOsRuntime = 0u; + +/** + * @brief: Function Prototype for a wrapper function of the privileged Task + * @param pArg Pointer Task Object + */ +static void taskSetupWrapper(void* pArg); + +void Task::startAllTasks() { +#ifdef SYSTEMVIEW_TRACE + SEGGER_SYSVIEW_Conf(); + SEGGER_SYSVIEW_Start(); +#endif + // Start OS + vTaskStartScheduler(); +} + +Task* Task::getTaskById(TaskHandle_t id) { + // Start by Idle Task and Search handles till found + Task* foundTaskPtr = firstTask; + while (foundTaskPtr->taskHandle != id) { + if (foundTaskPtr->nextTask != nullptr) { + foundTaskPtr = foundTaskPtr->nextTask; + } else { + break; + } + } + return foundTaskPtr; +} + +Task* Task::getTaskByName(const char* name) { + if (nullptr == name) + return nullptr; + // Start by Idle Task and Search handles till found + Task* foundTaskPtr = firstTask; + while (strcmp(name, foundTaskPtr->_taskConfiguration.name) != 0) { + if (foundTaskPtr->nextTask != nullptr) { + foundTaskPtr = foundTaskPtr->nextTask; + } else { + break; + } + } + return foundTaskPtr; +} + +std::function Task::getCustomSvcExecuter() { + return _customSvcExecuter; +} + +void* Task::getCustomSvcArgument() { + return _customSvcArgument; +} + +TaskHandle_t Task::getTaskHandle() { + return taskHandle; +} + +void Task::yieldFromPrivilegedContext() { + taskYIELD(); +} + +TaskEventQueueReference Task::getTaskEventQueueReference() { + return _taskEventQueue.getReference(); +} + +bool Task::isShutdownNotified() { + testapprtos::TaskEventId eventId; + return _taskEventQueue.receive(eventId, 0u) && (eventId == testapprtos::TaskEventId::Shutdown); +} + +IsrControlType Task::getPendingIsrControl() { + return _pendingIsrControl; +} + +void Task::initializeTask() { + // Create Static Task + taskHandle = xTaskCreateStatic(taskSetupWrapper, _taskConfiguration.name, _taskConfiguration.stackSize / 4u, static_cast(this), + _taskConfiguration.priority, _taskConfiguration.stackPtr, &taskControlBlock); + // Register Task Object in TCB + if (nullptr != taskHandle) { + vTaskSetThreadLocalStoragePointer(taskHandle, localStoragePointerTaskObjectIndex, this); + } else { + // LOG_ERROR("Could not assign Task to %s", _taskConfiguration.name); + } +} + +// Default run function +void Task::run() { + init(); + while (shouldTerminate == false) { + process(); + } + deinit(); + // LOG_DEBUG("Shutting down task: %s", _taskConfiguration.name); + vTaskDelete(taskHandle); +} + +// Empty functions for convenience of derived classes that don't need to +// override them +void Task::init() {} +void Task::deinit() {} +void Task::process() {} + +void Task::updateRuntimeStats(uint32_t executionTime) { + _taskRuntime += executionTime; +} + +uint8_t Task::getTaskWorkload() const { + constexpr uint8_t percentMultiplier = 100u; + uint64_t percentTask = (_taskRuntime * percentMultiplier); + return static_cast(percentTask / totalOsRuntime); +} + +#if configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED +void Task::invokeTaskCustomSvc(void* pArg, const std::function& syscall) { + const uint32_t ipsr = hal::getIsrNumber(); + + if (ipsr == 0u) { + _customSvcExecuter = syscall; /* Note: we dont have to check for nullptr here, since it is checked at execution */ + _customSvcArgument = pArg; + portBRC_PEND_CUSTOM_SVC(configTASK_INVOKED_SVC); + } else { + // This is already SVC/IRQ Context, simply execute + syscall(pArg); + } +} + +/** + * @brief Executes the given NVIC Operation + * @param isrControl Containing NVIC Instructions + */ +void controlInterruptSyscall(IsrControlType isrControl); + +void Task::controlInterrupt(uint32_t irqNumber, bool enable) { + uint32_t ipsr = hal::getIsrNumber(); + uint32_t control = hal::getControlRegister(); + control &= 0x01u; // Mask Control Bit + + /* Information for Pending NVIC Operation is stored in Task Object */ + _pendingIsrControl.enableCtrl = enable; + _pendingIsrControl.irqNumber = irqNumber; + + /* Check if in Interrupt or privileged */ + if ((ipsr > 0u) or (control == 0u)) { + /* In Interrupt the Processor is in privileged state and can execute NVIC operations */ + controlInterruptSyscall(_pendingIsrControl); + } else { + /* Not in Privileged State, pend a syscall dedicated to controlling the NVIC */ + portBRC_PEND_CUSTOM_SVC(configIRQCTRL_INVOKED_SVC); + } +} + +void loggerSyscallInvoker(void* argument) { + uint32_t ipsr = hal::getIsrNumber(); + if ((ipsr == 0u) and (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)) { + // Store parameter for SVC Execution + loggerArgument = argument; + portBRC_PEND_CUSTOM_SVC(configLOGGER_INVOKED_SVC); + } else { + // Already in SVC Context, simply execute logger syscall + logger::logSyscall(argument); + } +} + +extern "C" void customSvcExecuterWrapper(uint32_t svCallId) { + switch (svCallId) { + case configIRQCTRL_INVOKED_SVC: { + // Get Task Object + auto* callerTask = xTaskGetCurrentTaskHandle(); + auto* taskObject = static_cast(pvTaskGetThreadLocalStoragePointer(callerTask, localStoragePointerTaskObjectIndex)); + auto pendIsrControl = taskObject->getPendingIsrControl(); + // Execute NVIC Operation + controlInterruptSyscall(pendIsrControl); + break; + } + case configLOGGER_INVOKED_SVC: { + logger::logSyscall(loggerArgument); + break; + } + case configTASK_INVOKED_SVC: { + // Get Calling Task Handle and grab the Task Object + auto* callerTask = xTaskGetCurrentTaskHandle(); + auto* taskObject = static_cast(pvTaskGetThreadLocalStoragePointer(callerTask, localStoragePointerTaskObjectIndex)); + auto svCall = taskObject->getCustomSvcExecuter(); + // Check Null and execute SVC + if (svCall) { + svCall(taskObject->getCustomSvcArgument()); + } + break; + } + default: { + configASSERT(svCallId); + } + } +} + +void controlInterruptSyscall(IsrControlType isrControl) { + if (isrControl.enableCtrl) { + hal::enableInterrupt(isrControl.irqNumber); + } else { + hal::disableInterrupt(isrControl.irqNumber); + } +} +#endif //configENABLE_CUSTOM_SVC_EXECUTER_ON_UNHANDLED + +#if configENABLE_TASK_SWITCH_OUT_HOOK +extern "C" void taskSwitchedOutHook() { + // Note: This Function is running privileged, since it is called in freeRTOS context during Task Switch + auto* callerTask = xTaskGetCurrentTaskHandle(); + auto switchTime = hal::cpu::getCpuCycles(); + uint32_t runtime = (switchTime - taskSwitchInTime); + // Idle Task has no Task Object and is not tracked, so do not update Runtime stats + if (callerTask != xTaskGetIdleTaskHandle()) { + auto* taskObject = static_cast(pvTaskGetThreadLocalStoragePointer(callerTask, localStoragePointerTaskObjectIndex)); + taskObject->updateRuntimeStats(runtime); + } + taskSwitchInTime = switchTime; + totalOsRuntime += runtime; + +#ifdef SYSTEMVIEW_TRACE + SEGGER_SYSVIEW_OnTaskStopExec(); +#endif +} +#endif //configENABLE_TASK_SWITCH_OUT_HOOK + +static void taskSetupWrapper(void* pArg) { + /* The parameter value is expected to be 1 as 1 is passed in the + pArg value in the call to xTaskCreateStatic(). */ + // configASSERT( ( uint32_t ) pArg == 1UL ); + + auto* taskObject = static_cast(pArg); + + // Run the Task + taskObject->run(); + + //Log Error + // LOG_ERROR("Run Function returned."); +} + +#if (configSUPPORT_STATIC_ALLOCATION == 1) +/* configSUPPORT_STATIC_ALLOCATION is set to 1, so the application must provide + an implementation of vApplicationGetIdleTaskMemory() to provide the memory that + is used by the Idle task. */ + +/** Definition of Idle task stack size in bytes */ +constexpr uint32_t idleTaskStackSize = 1024u; +constexpr uint32_t taskStackSize = 2048u; +StackType_t idleStack[ idleTaskStackSize ]; + +/** Task Control Block of Idle Task*/ +static StaticTask_t idleTaskTcb; +/** Stack of Idle Task */ +// STACK_DEF(idleStack, idleTaskStackSize, common); + +extern "C" void vApplicationIdleHook() {} + +extern "C" void vApplicationGetIdleTaskMemory( + StaticTask_t** ppxIdleTaskTCBBuffer, StackType_t** ppxIdleTaskStackBuffer, + uint32_t* pulIdleTaskStackSize) { + *ppxIdleTaskTCBBuffer = &idleTaskTcb; + *ppxIdleTaskStackBuffer = idleStack; + *pulIdleTaskStackSize = (idleTaskStackSize / 4u); +} +#endif + +} // namespace testapprtos diff --git a/Platform/stm32f401re/src/System/CMakeLists.txt b/Platform/stm32f401re/src/System/CMakeLists.txt new file mode 100644 index 0000000..bd3a5c8 --- /dev/null +++ b/Platform/stm32f401re/src/System/CMakeLists.txt @@ -0,0 +1,47 @@ +include(platformLinkerConfiguration) + +set(TARGET ${PROJECT_NAME}.elf) +set(MAP_FILE ${PROJECT_BINARY_DIR}/Platform/${PROJECT_PLATFORM}/src/System/${PROJECT_NAME}.map) + +add_executable(${TARGET} + Main.cpp + Handler.cpp + syscalls.c + Isr.cpp + ) + +target_compile_features(${TARGET} + PRIVATE + cxx_std_14 + c_std_99 + ) + +target_link_libraries(${TARGET} + PRIVATE + stm32cubef4-hal + freertos + rtt + # # Reference test objects to main (this is to avoid Linkage order issue with GNU ld linker) + $ + TestApp + TestAppRtos + ) + +# # Set the linker flags +set(LIBRARY_PATH "${PROJECT_BINARY_DIR}/Platform/${PROJECT_PLATFORM}/config/Linker/") +set(LINKER_SCRIPT ${PROJECT_SOURCE_DIR}/Platform/${PROJECT_PLATFORM}/config/Linker/stm32f4.ld) + +arm_target_set_linkerflags_of_target( + TARGET ${TARGET} + LIBRARY_PATH ${LIBRARY_PATH} + LINKER_SCRIPT ${LINKER_SCRIPT} + MAP_FILE ${MAP_FILE} +) + +set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${MAP_FILE}) + +add_custom_command( + TARGET ${TARGET} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} ARGS -O ihex ${TARGET} ${PROJECT_NAME}.hex +) diff --git a/Platform/stm32f401re/src/System/Handler.cpp b/Platform/stm32f401re/src/System/Handler.cpp new file mode 100644 index 0000000..4e44425 --- /dev/null +++ b/Platform/stm32f401re/src/System/Handler.cpp @@ -0,0 +1,196 @@ +/** + * @file Platform/${PROJECT_PLATFORM}/src/System/Handler.cpp + * @brief Hardfault Handler + * + * The Hard Fault Handler will be called if an Hardfault, Usagefault, Memfault, Busfault or an Securefault occurs. + * If an error happen, the assembler code saves all relevant Register and Stack information onto the MSP Stack. + * + * MSP Stack before Fault Handler Call + * | | Register | Position | + * |-----------:|:----------:|:------------| + * | MSP -> | SP | Element(0) | + * | | MSP | Element(1) | + * | | PSP | Element(2) | + * | | R0 | Element(3) | + * | | R1 | Element(4) | + * | | R2 | Element(5) | + * | | R3 | Element(6) | + * | | R12 | Element(7) | + * | | LR | Element(8) | + * | | PC | Element(9) | + * | | xPSR | Element(10) | + * | | R4 | Element(11) | + * | | R5 | Element(12) | + * | | R6 | Element(13) | + * | | R7 | Element(14) | + * | | R8 | Element(15) | + * | | R9 | Element(16) | + * | | R10 | Element(17) | + * | | R11 | Element(18) | + * | old_MSP -> | old Value | | + * + * Then the FaultHandler function will be called to do a stack dump and fault analysis. + * The function ends with a BKPT instruction to force control back into the + * debugger or stops in a while loop if no debugger is connected. + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +// #include +#include +#include +#include +#include +// #include +#include +//#include + +/** + * Implemented Zero Vector that is directly called upon + * System Reset. + * + * It Initializes all RAM Data for all Process Regions + * and Calls Constructors + */ +extern "C" void resetHandlerC(); + +/** + * @brief Application Start Main + * External Linkage Symbol to main + */ +extern "C" int main(); + +/** + * @brief Provided System Start Function + * External Linkage Symbol Calling the System Clock Bootup + */ +extern "C" void SystemInit(); // NOLINT(readability-identifier-naming) Name defined by third-party library + +/** + * @brief Provided System Start Function for Clock Variable Update + * External Linkage Symbol Calling the System Clock Update after Init to make sure all Variables are set correct + */ +extern "C" void SystemCoreClockUpdate(); // NOLINT(readability-identifier-naming) Name defined by third-party library + +/** + * @brief Newlib init array + * External Linkage Symbols calling to newlib + * members. Used for Constructing + */ +extern "C" void __libc_init_array(); // NOLINT(readability-identifier-naming) Name defined by third-party library + +/** + * @brief Newlib finit array + * External Linkage Symbols calling to newlib + * members. Used for Constructing + */ +extern "C" void __libc_fini_array(); // NOLINT(readability-identifier-naming) Name defined by third-party library + +/** + * @brief Reset Handler Provided to ISR vector Table. + * + * It Sets up Main Stack Pointer and its Checking Register, + * then jumps to the C Routine initializing the System Memories. + */ +extern "C" __attribute__((naked)) void Reset_Handler() { // NOLINT(readability-identifier-naming) Name defined by third-party library + __asm volatile( + "ldr sp, =_estack \n" /* Import Main-Stack and set Stack Pointer */ + // "ldr r0, =_sstack \n" /* Get lowest address of stack region into R0 */ + "adds r0, #4 \n" /* Increase by 4 (word width) to point to next empty RAM Cell */ + "msr msplim, r0 \n" /* Set MSP Limit Register */ + "bl resetHandlerC \n" /* Branch to Reset Handler that is written in a more sane manner */ + ); +} + +/** + * @brief Handles faults from CPU. + */ +extern "C" __attribute__((naked)) void HardFault_Handler() { // NOLINT(readability-identifier-naming) Name defined by third-party library + __asm volatile( + " tst lr, #4 \n" // check if third bit in the link register is set. This check is to figure out if MSP or PSP stack before Fault was active. + " ite eq \n" + " mrseq r0, msp \n" // Move MSP Register value into R0 + " mrsne r0, psp \n" // Move PSP Register value into R0 + " mrs r1, msp \n" // Move MSP Register value into R1 + " mrs r2, psp \n" // Move PSP Register value into R2 + " ldr r3, =0xE000ED08 \n" // Read Vector Table offset Register Address into R3 + " ldr r3, [r3] \n" // Read Vector Table offset Value in R3 + " ldr r3, [r3] \n" // Read initial MSP value + " msr msp, r3 \n" // Move R3 Register value into MSP Register to reinitialize MSP + " push {r4-r11} \n" // Push R4-R11 Register onto the Stack to save value during exception + " ldm r0, {r4-r11} \n" // Load the contextswitch data into Register R4-R11 (R0-R3,R12,LR,PC,xPSR this values will be stacked by hardware if a fault happen) + " push {r4-r11} \n" // Push R4-R11 onto MSP Stack + " push {r0-r2} \n" // Push R0-R2 onto MSP + " mrs r0, msp \n" // Move MSP into R0 + " b hardFaultHandlerC \n"); // Call Fault Handler with Argument R0=MSP +} + +/** + * @brief FaultHandler C-Function to do a stack dump and fault analysis + * @param hardfaultArgs Pointer to Stack for stack dump + */ +extern "C" void hardFaultHandlerC(const uint32_t* hardfaultArgs) { + + __asm("BKPT #0\n"); // cause the debugger to stop + + while (true) { // stop if no debugger is present + } +} + +/** + * @brief C Routine for System Reset + * + * Loops All Task Regions and sets up all Data and Bss for the system. + */ +extern "C" void resetHandlerC() { + /* Import of Standard Regions */ + extern uint32_t _sidata[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + extern uint32_t _sdata[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + extern uint32_t _edata[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + extern uint32_t _sbss[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + extern uint32_t _ebss[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + /* Import of Privileged Regions */ + // extern uint32_t __privileged_sram_start__[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + // extern uint32_t __privileged_sram_loadaddr__[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + // extern uint32_t __privileged_sram_end__[]; // NOLINT(readability-identifier-naming) Name defined by third-party library + + // TODO This file replaces the default asm startup completely. Make sure it is not in the build process. + + /* Start System PLL and Clock for faster initialization */ + // // SystemInit(); + + /* Copy Standard Area Data (is common, but just in case) */ + memcpy(_sdata, _sidata, (uint32_t)((uint32_t)_edata - (uint32_t)_sdata)); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) This type of Cast is absolutely needed at this point, since there is no other way to interpret Linker variables to initialize bss and Data Regions + memset(_sbss, 0, (uint32_t)((uint32_t)_ebss - (uint32_t)_sbss)); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) This type of Cast is absolutely needed at this point, since there is no other way to interpret Linker variables to initialize bss and Data Regions + /* Set Privileged Data */ + // memcpy(__privileged_sram_start__, __privileged_sram_loadaddr__, (uint32_t)((uint32_t)__privileged_sram_end__ - (uint32_t)__privileged_sram_start__)); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) This type of Cast is absolutely needed at this point, since there is no other way to interpret Linker variables to initialize bss and Data Regions + + /* ------------------------------------------------- User Process Region Initialization -------------------------------------------------- */ + + // /* Copy Task Region Data */ + // INITIALIZE_REGION(TestRunnerRegion); + + // INITIALIZE_REGION(TestEnvironmentRegion); + + /* This call must be performed AFTER the Initialization of the memory regions, otherwise the variable gets overwritten with its init values */ + // // SystemCoreClockUpdate(); + + /* Initialize Constructors */ + __libc_init_array(); +#pragma GCC diagnostic ignored "-Wpedantic" + main(); // NOLINT(clang-diagnostic-main) This is the Reset Handler. The only place where the call to main is necessary. +#pragma GCC diagnostic warning "-Wpedantic" + /* Call to destructors */ + __libc_fini_array(); + + while (true) { + }; +} diff --git a/Platform/stm32f401re/src/System/Isr.cpp b/Platform/stm32f401re/src/System/Isr.cpp new file mode 100644 index 0000000..09b768c --- /dev/null +++ b/Platform/stm32f401re/src/System/Isr.cpp @@ -0,0 +1,546 @@ +/** + * @file src/Core/Isr.cpp + * @brief Interrupt Vector Table + * + * The Interrupt Vector Table is linked at a specific Address to map + * occuring ISRs onto the right Functions. + * The Naming Conventions are suppressed here, since the standard + * CMSIS Vector names are used. + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +/** + * Import the top of Stack provided by linker + */ +extern uint32_t _estack; // NOLINT(readability-identifier-naming) Linker defined standard variable. + +/** ISR Vector definition */ +using IsrVectorEntry = void (*)(); + +struct IsrVectorTable { + uint32_t* topOfStack; /**< Stack Pointer is Vector Table First entry by definition */ + IsrVectorEntry resetHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry nmiHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry hardFaultHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry memManageHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry busFaultHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry usageFaultHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry secureFaultHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved01; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved02; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved03; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry svcHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry debugMonHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved04; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry pendSvHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry sysTickHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry wwdgIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry pdvPvmIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry rtcIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry rtcSIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tampIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tampSIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry flashIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry flashSIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry gtzcIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry rccIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry rccSIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti0IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti4IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti5IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti6IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti7IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti8IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti9IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti10IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti11IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti12IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti13IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti14IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry exti15IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dmaMux1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dmaMux1SIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel4IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel5IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel6IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel7IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma1Channel8IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry adc12IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dacIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry fdcan1It0IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry fdcan1It1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim1BrkIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim1UpIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim1TrgComIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim1CcIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim4IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim5IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim6IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim7IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim8BrkIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim8UpIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim8TrgComIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim8CcIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c1EvIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c1ErIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c2EvIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c2ErIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry spi1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry spi2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry usart1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry usart2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry usart3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry uart4IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry uart5IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry lpuart1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry lptim1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry lptim2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim15IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim16IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tim17IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry compIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry usbFsIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry crsIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry fmcIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry octospi1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved05; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry sdmmc1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry reserved06; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel4IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel5IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel6IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel7IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dma2Channel8IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c3EvIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c3ErIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry sai1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry sai2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry tscIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry aesIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry rngIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry fpuIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry hashIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry pkaIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry lptim3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry spi3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c4ErIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry i2c4EvIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dfsdm1Flt0IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dfsdm1Flt1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dfsdm1Flt2IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry dfsdm1Flt3IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry ucpd1IrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry icacheIrqHandler; /**< Member Correlating to an ISR Vector */ + IsrVectorEntry otfdec1IrqHandler; /**< Member Correlating to an ISR Vector */ +}; +/** Reassign Type of vector Table */ +using IsrVectorTable_t = struct IsrVectorTable; // NOLINT(readability-identifier-naming) Readability is better this way. + +/** + * @brief 'Generic' C handler for faults + * + * This Handler is a Fallback if a unhandled and/or unimplemented + * ISR is called. + * This handler jumps straight to Hard Fault for Stack unwinding. + */ +extern "C" void HandlerC(); // NOLINT(readability-identifier-naming) Name defined by third-party library + +/** @brief Import of the System Reset Handler */ +extern "C" __attribute__((naked)) void Reset_Handler(); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of the System Hard Fault Handler */ +extern "C" __attribute__((naked)) void HardFault_Handler(); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void NMI_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void MemManage_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void BusFault_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void UsageFault_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SecureFault_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SVC_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DebugMon_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void PendSV_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SysTick_Handler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void WWDG_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void PVD_PVM_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void RTC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void RTC_S_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TAMP_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TAMP_S_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FLASH_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FLASH_S_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void GTZC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void RCC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void RCC_S_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI0_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI4_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI5_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI6_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI7_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI8_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI9_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI10_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI11_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI12_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI13_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI14_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void EXTI15_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMAMUX1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMAMUX1_S_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel4_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel5_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel6_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel7_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA1_Channel8_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void ADC1_2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DAC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FDCAN1_IT0_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FDCAN1_IT1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM1_BRK_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM1_UP_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM1_TRG_COM_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM1_CC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM4_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM5_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM6_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM7_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM8_BRK_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM8_UP_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM8_TRG_COM_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM8_CC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C1_EV_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C1_ER_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C2_EV_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C2_ER_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SPI1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SPI2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void USART1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void USART2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void USART3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void UART4_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void UART5_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void LPUART1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void LPTIM1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void LPTIM2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM15_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM16_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TIM17_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void COMP_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void USB_FS_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void CRS_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FMC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void OCTOSPI1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SDMMC1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel4_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel5_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel6_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel7_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DMA2_Channel8_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C3_EV_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C3_ER_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SAI1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SAI2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void TSC_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void AES_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void RNG_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void FPU_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void HASH_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void PKA_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void LPTIM3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void SPI3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C4_ER_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void I2C4_EV_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DFSDM1_FLT0_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DFSDM1_FLT1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DFSDM1_FLT2_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void DFSDM1_FLT3_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void UCPD1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void ICACHE_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library +/** @brief Import of Standard ISR Vector */ +extern "C" void OTFDEC1_IRQHandler() __attribute__((weak, alias("HandlerC"))); // NOLINT(readability-identifier-naming) Name defined by third-party library + +extern "C" void HandlerC() { // NOLINT(readability-identifier-naming) Name defined by third-party library + HardFault_Handler(); /* Jump to hard fault handler for unhandled Interrupts */ +} + +/** + * @brief ISR Vector Table. + * Contains all ISR Vectors for the system. + * Is used to dispatch any Interrupt Request. + * It was implemented to use the CMSIS Standard names + * for all Vectors. + */ +// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init) estack is a constant - Linker defined. +__attribute__((used)) __attribute__((section(".isr_vector"))) static const IsrVectorTable_t isrVectorTable = { + // TODO ISR Grouped in separate file + &_estack, /**< topOfStack */ + Reset_Handler, /**< resetHandler */ + NMI_Handler, /**< nmiHandler */ + HardFault_Handler, /**< hardFaultHandler */ + MemManage_Handler, /**< memManageHandler */ + BusFault_Handler, /**< busFaultHandler */ + UsageFault_Handler, /**< usageFaultHandler */ + SecureFault_Handler, /**< secureFaultHandler */ + nullptr, /**< reserved01 */ + nullptr, /**< reserved02 */ + nullptr, /**< reserved03 */ + SVC_Handler, /**< svcHandler */ + DebugMon_Handler, /**< debugMonHandler */ + nullptr, /**< reserved04 */ + PendSV_Handler, /**< pendSvHandler */ + SysTick_Handler, /**< sysTickHandler */ + WWDG_IRQHandler, /**< wwdgIrqHandler */ + PVD_PVM_IRQHandler, /**< pdvPvmIrqHandler */ + RTC_IRQHandler, /**< rtcIrqHandler */ + RTC_S_IRQHandler, /**< rtcSIrqHandler */ + TAMP_IRQHandler, /**< tampIrqHandler */ + TAMP_S_IRQHandler, /**< tampSIrqHandler */ + FLASH_IRQHandler, /**< flashIrqHandler */ + FLASH_S_IRQHandler, /**< flashSIrqHandler */ + GTZC_IRQHandler, /**< gtzcIrqHandler */ + RCC_IRQHandler, /**< rccIrqHandler */ + RCC_S_IRQHandler, /**< rccSIrqHandler */ + EXTI0_IRQHandler, /**< exti0IrqHandler */ + EXTI1_IRQHandler, /**< exti1IrqHandler */ + EXTI2_IRQHandler, /**< exti2IrqHandler */ + EXTI3_IRQHandler, /**< exti3IrqHandler */ + EXTI4_IRQHandler, /**< exti4IrqHandler */ + EXTI5_IRQHandler, /**< exti5IrqHandler */ + EXTI6_IRQHandler, /**< exti6IrqHandler */ + EXTI7_IRQHandler, /**< exti7IrqHandler */ + EXTI8_IRQHandler, /**< exti8IrqHandler */ + EXTI9_IRQHandler, /**< exti9IrqHandler */ + EXTI10_IRQHandler, /**< exti10IrqHandler */ + EXTI11_IRQHandler, /**< exti11IrqHandler */ + EXTI12_IRQHandler, /**< exti12IrqHandler */ + EXTI13_IRQHandler, /**< exti13IrqHandler */ + EXTI14_IRQHandler, /**< exti14IrqHandler */ + EXTI15_IRQHandler, /**< exti15IrqHandler */ + DMAMUX1_IRQHandler, /**< dmaMux1IrqHandler */ + DMAMUX1_S_IRQHandler, /**< dmaMux1SIrqHandler; */ + DMA1_Channel1_IRQHandler, /**< dma1Channel1IrqHandler */ + DMA1_Channel2_IRQHandler, /**< dma1Channel2IrqHandler */ + DMA1_Channel3_IRQHandler, /**< dma1Channel3IrqHandler */ + DMA1_Channel4_IRQHandler, /**< dma1Channel4IrqHandler */ + DMA1_Channel5_IRQHandler, /**< dma1Channel5IrqHandler */ + DMA1_Channel6_IRQHandler, /**< dma1Channel6IrqHandler */ + DMA1_Channel7_IRQHandler, /**< dma1Channel7IrqHandler */ + DMA1_Channel8_IRQHandler, /**< dma1Channel8IrqHandler */ + ADC1_2_IRQHandler, /**< adc12IrqHandler */ + DAC_IRQHandler, /**< dacIrqHandler */ + FDCAN1_IT0_IRQHandler, /**< fdcan1It0IrqHandler */ + FDCAN1_IT1_IRQHandler, /**< fdcan1It1IrqHandler */ + TIM1_BRK_IRQHandler, /**< tim1BrkIrqHandler */ + TIM1_UP_IRQHandler, /**< tim1UpIrqHandler */ + TIM1_TRG_COM_IRQHandler, /**< tim1TrgComIrqHandler */ + TIM1_CC_IRQHandler, /**< tim1CcIrqHandler */ + TIM2_IRQHandler, /**< tim2IrqHandler */ + TIM3_IRQHandler, /**< tim3IrqHandler */ + TIM4_IRQHandler, /**< tim4IrqHandler */ + TIM5_IRQHandler, /**< tim5IrqHandler */ + TIM6_IRQHandler, /**< tim6IrqHandler */ + TIM7_IRQHandler, /**< tim7IrqHandler */ + TIM8_BRK_IRQHandler, /**< tim8BrkIrqHandler */ + TIM8_UP_IRQHandler, /**< tim8UpIrqHandler */ + TIM8_TRG_COM_IRQHandler, /**< tim8TrgComIrqHandler */ + TIM8_CC_IRQHandler, /**< tim8CcIrqHandler */ + I2C1_EV_IRQHandler, /**< i2c1EvIrqHandler */ + I2C1_ER_IRQHandler, /**< i2c1ErIrqHandler */ + I2C2_EV_IRQHandler, /**< i2c2EvIrqHandler */ + I2C2_ER_IRQHandler, /**< i2c2ErIrqHandler */ + SPI1_IRQHandler, /**< spi1IrqHandler */ + SPI2_IRQHandler, /**< spi2IrqHandler */ + USART1_IRQHandler, /**< usart1IrqHandler */ + USART2_IRQHandler, /**< usart2IrqHandler */ + USART3_IRQHandler, /**< usart3IrqHandler */ + UART4_IRQHandler, /**< uart4IrqHandler */ + UART5_IRQHandler, /**< uart5IrqHandler */ + LPUART1_IRQHandler, /**< lpuart1IrqHandler */ + LPTIM1_IRQHandler, /**< lptim1IrqHandler */ + LPTIM2_IRQHandler, /**< lptim2IrqHandler */ + TIM15_IRQHandler, /**< tim15IrqHandler */ + TIM16_IRQHandler, /**< tim16IrqHandler */ + TIM17_IRQHandler, /**< tim17IrqHandler */ + COMP_IRQHandler, /**< compIrqHandler */ + USB_FS_IRQHandler, /**< usbFsIrqHandler */ + CRS_IRQHandler, /**< crsIrqHandler */ + FMC_IRQHandler, /**< fmcIrqHandler */ + OCTOSPI1_IRQHandler, /**< octospi1IrqHandler */ + nullptr, /**< reserved05 */ + SDMMC1_IRQHandler, /**< sdmmc1IrqHandler */ + nullptr, /**< reserved06 */ + DMA2_Channel1_IRQHandler, /**< dma2Channel1IrqHandler */ + DMA2_Channel2_IRQHandler, /**< dma2Channel2IrqHandler */ + DMA2_Channel3_IRQHandler, /**< dma2Channel3IrqHandler */ + DMA2_Channel4_IRQHandler, /**< dma2Channel4IrqHandler */ + DMA2_Channel5_IRQHandler, /**< dma2Channel5IrqHandler */ + DMA2_Channel6_IRQHandler, /**< dma2Channel6IrqHandler */ + DMA2_Channel7_IRQHandler, /**< dma2Channel7IrqHandler */ + DMA2_Channel8_IRQHandler, /**< dma2Channel8IrqHandler */ + I2C3_EV_IRQHandler, /**< i2c3EvIrqHandler */ + I2C3_ER_IRQHandler, /**< i2c3ErIrqHandler */ + SAI1_IRQHandler, /**< sai1IrqHandler */ + SAI2_IRQHandler, /**< sai2IrqHandler */ + TSC_IRQHandler, /**< tscIrqHandler */ + AES_IRQHandler, /**< aesIrqHandler */ + RNG_IRQHandler, /**< rngIrqHandler */ + FPU_IRQHandler, /**< fpuIrqHandler */ + HASH_IRQHandler, /**< hashIrqHandler */ + PKA_IRQHandler, /**< pkaIrqHandler */ + LPTIM3_IRQHandler, /**< lptim3IrqHandler */ + SPI3_IRQHandler, /**< spi3IrqHandler */ + I2C4_ER_IRQHandler, /**< i2c4ErIrqHandler */ + I2C4_EV_IRQHandler, /**< i2c4EvIrqHandler */ + DFSDM1_FLT0_IRQHandler, /**< dfsdm1Flt0IrqHandler */ + DFSDM1_FLT1_IRQHandler, /**< dfsdm1Flt1IrqHandler */ + DFSDM1_FLT2_IRQHandler, /**< dfsdm1Flt2IrqHandler */ + DFSDM1_FLT3_IRQHandler, /**< dfsdm1Flt3IrqHandler */ + UCPD1_IRQHandler, /**< ucpd1IrqHandler */ + ICACHE_IRQHandler, /**< icacheIrqHandler */ + OTFDEC1_IRQHandler, /**< otfdec1IrqHandler */ +}; diff --git a/Platform/stm32f401re/src/System/Main.cpp b/Platform/stm32f401re/src/System/Main.cpp new file mode 100644 index 0000000..2bce334 --- /dev/null +++ b/Platform/stm32f401re/src/System/Main.cpp @@ -0,0 +1,40 @@ +/** + * @file Platform/${PROJECT_PLATFORM}/src/Core/Main.cpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include +#include +#include +#include + + +/** + * @brief Entrance point to C application + * @return integer 0 upon exit success + */ +int main() { + SEGGER_RTT_Init(); + // Initialize Tasks + testrunner::taskInterface.initialize(); + testenvironment::taskInterface.initialize(); + tpcprotocol::taskInterface.initialize(); + + // FreeRTOS setup and start + testapprtos::Task::startAllTasks(); + + // Endless loop. This should never be reached. + while (true) { + assert(false); + } + + return 0; +} diff --git a/Platform/stm32f401re/src/System/syscalls.c b/Platform/stm32f401re/src/System/syscalls.c new file mode 100644 index 0000000..802d4bc --- /dev/null +++ b/Platform/stm32f401re/src/System/syscalls.c @@ -0,0 +1,296 @@ +/** + * @file Platform/${PROJECT_PLATFORM}/src/System/syscalls.c + * + * @brief Custom implementation for syscalls + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include +#include +#undef errno +/// Error number +extern int errno; + +/// Helper entry for environ +char* __env[1] = {0}; // NOLINT(readability-identifier-naming) Don't pollute global namespace + +/** + * @brief Stub for environ. + * + * This must point to a null terminated list of environment variable name-value pairs. + */ +char** environ = __env; + +/** + * @brief Stub for syscall _exit + * + * This will get called if a program should exit without any clean-up. + * This stub will just loop indefinitely. + * + * @param[in] rc return code + */ +void _exit(int rc __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + while (1) { + }; +} + +/** + * @brief Stub for syscall _close + * + * This will get called if a file should be closed. + * As no files are supported and stdout cannot be closed, this stub always fails. + * + * @param[in] fd file descriptor + * @return -1 + */ +int _close(int fd __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = EBADF; + return -1; +} + +/** + * @brief Stub for syscall _execve + * + * This will be called to transfer control to a new process + * + * As no processes are supported, this stub always returns -1. + * + * @param[in] name name + * @param[in] argv arguments + * @param[in] env environment + * @return -1 + */ +int _execve(char* name __attribute__((unused)), char** argv __attribute__((unused)), char** env __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = ENOMEM; + return -1; +} + +/** + * @brief Stub for syscall _fork + * + * This will be called to fork + * + * As no processes are supported, this stub always returns -1. + * + * @return -1 + */ +int _fork(void) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = EAGAIN; + return -1; +} + +/** + * @brief Stub for syscall _fstat + * + * This will be called to get a status of an open file. + * As no files are supported, this stub reports all files as character devices. + * + * @param[in] fd file descriptor + * @param[out] stat status + * @return 0 + */ +int _fstat(int fd __attribute__((unused)), struct stat* stat) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + stat->st_mode = S_IFCHR; + return 0; +} + +/** + * @brief Stub for syscall _getpid + * + * This will be called to get the process id of the currently running process. + * As no processes are supported, this stub always returns 0. + * + * @return 0 + */ +int _getpid(void) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + return 0; +} + +/** + * @brief Stub for syscall _isatty + * + * This will be called to check if a file is a terminal. + * As the only 'file' supported is stdout (the console), this stub always returns 1. + * + * @param[in] fd file descriptor + * @return 1 + */ +int _isatty(int fd __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + return 1; +} + +/** + * @brief Stub for syscall _kill + * + * This will be called to signal (or terminate) processes. + * As no processes are supported, this stub always returns -1. + * + * @param[in] pid process id + * @param[in] sig signal + * @return -1 + */ +int _kill(int pid __attribute__((unused)), int sig __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = EINVAL; + + return -1; +} + +/** + * @brief Stub for syscall _link + * + * This will be called to link files. + * + * As no files are supported, this stub always returns -1. + * + * @param[in] old old name + * @param[in] new new name + * @return -1 + */ +int _link(char* old __attribute__((unused)), char* new __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = EMLINK; + return -1; +} + +/** + * @brief Stub for syscall _lseek + * + * This will be called to set a position in a file. + * As no files are supported, this stub will always return 0. + * + * @param[in] fd file descriptor + * @param[in] offset offset + * @param[in] whence whence + * @return 0 + */ +int _lseek(int fd __attribute__((unused)), int offset __attribute__((unused)), int whence __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + return 0; +} + +/** + * @brief Stub for syscall _open + * + * This will be called to open a file. + * As no files are supported, this stub will always return -1. + * + * @param[in] name name + * @param[in] flags flags + * @param[in] mode mode + * @return -1 + */ +int _open(const char* name __attribute__((unused)), int flags __attribute__((unused)), int mode __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + errno = ENOSYS; + return -1; +} + +/** + * @brief Stub for syscall _read + * + * This will be called to read from a file. + * As no files are supported, this stub will always return 0. + * + * @param[in] fd file descriptor + * @param[in] buf buffer + * @param[in] count count + * @return 0 + */ +int _read(int fd __attribute__((unused)), void* buf __attribute__((unused)), int count __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + return 0; +} + +/** + * @brief Stub for syscall _sbrk + * + * This will be called to allocate memory from heap. + * As no heap is supported, this stub will always return -1. + * + * @param[in] nbytes number of bytes + * @return -1 + */ +int _sbrk(int nbytes __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by third-party library + assert(0); + errno = ENOMEM; + return -1; +} + +/** + * @brief Stub for syscall _stat + * + * This will be called to get a status of a file. + * As no files are supported, this stub reports all files as character devices. + * + * @param[in] name file name + * @param[out] st status + * @return 0 + */ +int _stat(const char* name __attribute__((unused)), struct stat* st) { // NOLINT(readability-identifier-naming) Name defined by third-party library + st->st_mode = S_IFCHR; + return 0; +} + +/** + * @brief Stub for syscall _times + * + * This will be called to get process timing information. + * As no processes are supported, this stub always returns -1. + * + * @param[out] tms timing information + * @return -1 + */ +int _times(struct tms* tms __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by third-party library + errno = EACCES; + return -1; +} + +/** + * @brief Stub for syscall _unlink + * + * This will be called to unlink a file. + * As no files are supported, this stub always returns -1. + * + * @param[in] name file name + * @return -1 + */ +int _unlink(const char* name __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by third-party library + errno = ENOENT; + return -1; +} + +/** + * @brief Stub for syscall _wait + * + * This will be called to wait for other processes. + * As no processes are supported, this stub always returns -1. + * + * @param[out] status status + * @return -1 + */ +int _wait(int* status __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by third-party library + errno = ECHILD; + return -1; +} + +/** + * @brief Stub for syscall _write + * + * This will be called to write to a file. + * As no files are supported, this stub always returns 0. + * + * @param[in] fd file descriptor + * @param[in] buf buffer + * @param[in] count count + * @return 0 + */ +int _write(int fd __attribute__((unused)), const void* buf __attribute__((unused)), int count __attribute__((unused))) { // NOLINT(readability-identifier-naming) Name defined by 3rd party + return 0; +} diff --git a/README.md b/README.md index 665af8f..692abc2 100644 --- a/README.md +++ b/README.md @@ -1 +1,42 @@ -# kiso-testing-testapp +# TestApp + +## Toolchain setup + +## 1. Clone the repository ## +``` + git clone https://github.com/eclipse/kiso-testing-testapp.git +``` + +## 2. Run one of the following CMake generators from within the CMake build folder for the corresponding toolchain ## + +### 2.1. GCC ### +``` + mkdir build_gcc && cd build_gcc + cmake -G"Ninja" ../integration-testapp -DCMAKE_TOOLCHAIN_FILE=../integration-testapp/cmake/toolchains/GCC.cmake -DAPP_OSADAPTER=FreeRtosTestAdapter +``` +### 2.3. ARM "Hardware toolchain: ARM32" ### +``` + mkdir build_arm && cd build_arm + cmake -G"Ninja" ../integration-testapp -DCMAKE_TOOLCHAIN_FILE=../integration-testapp/cmake/toolchains/stm32-arm-gcc-f401re.cmake -DAPP_OSADAPTER=FreeRtosAdapter +``` +#### Or, To wrap unit tests with ARM emulator in Linux `qemu` : #### +``` + mkdir build_arm && cd build_arm + cmake -G"Ninja" ../integration-testapp -DCMAKE_TOOLCHAIN_FILE=../integration-testapp/cmake/toolchains/unittest-qemu-arm.cmake -DAPP_OSADAPTER=FreeRtosTestAdapter +``` + +## 3. Run Ninja targets ## + +### 3.1. Build TestApp project ### +``` + ninja +``` +### 3.2. Run unit test targets ### +Build-only all unitt tests: +``` + ninja build-unit-tests-all +``` +Build and Run all unit tests: +``` + ninja run-unit-tests-all +``` diff --git a/TestApp/CMakeLists.txt b/TestApp/CMakeLists.txt new file mode 100644 index 0000000..985b814 --- /dev/null +++ b/TestApp/CMakeLists.txt @@ -0,0 +1,149 @@ +# Interface used to write the test +add_library(TestApp-Interface + INTERFACE + ) + +target_include_directories(TestApp-Interface + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestCase + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestEnvironment + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestSuite + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestRegistry + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestFactory + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestAppTypes + ) + +target_link_libraries(TestApp-Interface + INTERFACE + etl + ) + +# MessageHandler used to define common message Type and methods to handle messages +add_library(MessageHandler + ${CMAKE_CURRENT_SOURCE_DIR}/src/MessageHandler/MessageHandler.cpp + ) + +target_include_directories(MessageHandler + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/MessageHandler + ) + +target_link_libraries(MessageHandler + TestApp-Interface + ) + +if(DEVICE_UNDER_TEST_ARCH MATCHES "PPC|ARM" AND NOT RUNNING_UNITTESTS_WITH_EMULATOR) + # TestApp Library (to link within the tests and target platform) + add_library(TestApp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestRunner/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestRunner/TestRunner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/Assert.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/AssertReport.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/Logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/TestEnvironment.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TPC/Task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TPC/TPC.cpp + ) + + target_include_directories(TestApp + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestRunner + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestEnvironment + ${CMAKE_CURRENT_SOURCE_DIR}/include/TPC + ${PROJECT_SOURCE_DIR}/Platform/RequiredInterface/TestAppRtosInterface + ${PROJECT_SOURCE_DIR}/Platform/RequiredInterface/CommunicationChannelInterface + ) + + # Specify c++ compile features used in the TestApp + target_compile_features(TestApp + PUBLIC + cxx_std_14 + ) + + # TestApp dependencies + target_link_libraries(TestApp + PRIVATE + TestApp-Interface + PUBLIC + MessageHandler + # Reference test objects + $ + # We need communication channel to be configured/implemented + CommunicationChannel + # We need RTOS implementation for the specific target + TestAppRtos + # Third Party Dependencies for TestApp + etl + ) +endif() + +add_library(TestEnvironment + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/Assert.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/AssertReport.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/Logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestEnvironment/TestEnvironment.cpp + ) +target_include_directories(TestEnvironment + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestEnvironment + ) +# Specify c++ compile features used in the TestEnvironment +target_compile_features(TestEnvironment + PUBLIC + cxx_std_14 + ) +# TestEnvironment dependencies +target_link_libraries(TestEnvironment + PRIVATE + TestApp-Interface + PUBLIC + OsExtension + etl + MessageHandler + ) + +add_library(TestRunner + ${CMAKE_CURRENT_SOURCE_DIR}/src/TestRunner/TestRunner.cpp + ) +target_include_directories(TestRunner + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/TestRunner + ) +# Specify c++ compile features used in the TestRunner +target_compile_features(TestRunner + PUBLIC + cxx_std_14 + ) +# TestRunner dependencies +target_link_libraries(TestRunner + PRIVATE + TestApp-Interface + PUBLIC + OsExtension + etl + MessageHandler + ) + +add_library(TPC + ${CMAKE_CURRENT_SOURCE_DIR}/src/TPC/TPC.cpp + ) +target_include_directories(TPC + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include/TPC + ) +# Specify c++ compile features used in the TestRunner +target_compile_features(TPC + PUBLIC + cxx_std_14 + ) +# TPC dependencies +target_link_libraries(TPC + PRIVATE + TestApp-Interface + PUBLIC + OsExtension + etl + MessageHandler + CommunicationChannel-Interface + ) diff --git a/TestApp/include/MessageHandler/MessageHandler/MessageHandler.hpp b/TestApp/include/MessageHandler/MessageHandler/MessageHandler.hpp new file mode 100644 index 0000000..8606923 --- /dev/null +++ b/TestApp/include/MessageHandler/MessageHandler/MessageHandler.hpp @@ -0,0 +1,182 @@ +/** + * @file include/MessageHandler/MessageHandler/MessageHandler.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef MESSAGEHANDLER_HPP +#define MESSAGEHANDLER_HPP + +#include +#include +#include +#include +#include "TestAppTypes/TestAppTypes.hpp" + + +namespace message +{ + /// @brief Maximum size of an FDX frame payload in bytes. + constexpr uint8_t maxPayloadSize = 247u; + constexpr uint8_t tlvHeaderSize = 2u; + constexpr uint8_t messageSize = maxPayloadSize - tlvHeaderSize; + constexpr uint8_t messageHeaderSize = 8u; + constexpr uint8_t messageArraySize = 255u; + + constexpr uint8_t headerInfoIndex = 0U; + constexpr uint8_t tokenIndex = 1U; + constexpr uint8_t subTypeIndex = 2U; + constexpr uint8_t errorCodeIndex = 3U; + constexpr uint8_t testSectionIndex = 4U; + constexpr uint8_t testSuiteIndex = 5U; + constexpr uint8_t testCaseIndex = 6U; + constexpr uint8_t payloadLengthIndex = 7U; + constexpr uint8_t payloadIndex = 8U; + + constexpr uint8_t reportTlvType = 110u; + + /// @brief Identifies the message type of an FDX message + enum MessageType : uint8_t + { + Command = 0u, + Report, + Acknowledgement, + Log + }; + + ///Enum class Messagetype + enum class MessageSubType : uint8_t + { + //types of messages to be handled + //setup + TESTSECTION_SETUP = 1, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTSUITE_SETUP = 2, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTCASE_SETUP = 3, //NOLINT (readability-identifier-naming): we want keep googletest style + + //run + TESTSECTION_RUN = 11, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTSUITE_RUN = 12, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTCASE_RUN = 13, //NOLINT (readability-identifier-naming): we want keep googletest style + + //teardown + TESTSECTION_TEARDOWN = 21, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTSUITE_TEARDOWN = 22, //NOLINT (readability-identifier-naming): we want keep googletest style + TESTCASE_TEARDOWN = 23, //NOLINT (readability-identifier-naming): we want keep googletest style + + //type of messages received + + TEST_PASS = 0, //NOLINT (readability-identifier-naming): we want keep googletest style + TEST_FAIL = 1, //NOLINT (readability-identifier-naming): we want keep googletest style + TEST_NOTIMPLEMENTED = 2, //NOLINT (readability-identifier-naming): we want keep googletest style + + //possible ack messages + ACKNOWLEDGEMENT = 0, //NOLINT (readability-identifier-naming): we want keep googletest style + NACK = 1, //NOLINT (readability-identifier-naming): we want keep googletest style + + //REPORT + REPORT_SUCCESS = 110, //NOLINT (readability-identifier-naming): we want keep googletest style + REPORT_FAILURE = 112, //NOLINT (readability-identifier-naming): we want keep googletest style + + //PING->PONG + PING = 0, //NOLINT (readability-identifier-naming): we want keep googletest style + PONG = 1, //NOLINT (readability-identifier-naming): we want keep googletest style + + //LOG + RESERVED = 0 //NOLINT (readability-identifier-naming): we want keep googletest style + }; + + // Arrays of fixed size, each array contains valid commands, reports or acknowledgment messages + // Will be used to check the validity of the messages received from ITF + constexpr std::array msgCommands = {message::MessageSubType::PING, + message::MessageSubType::TESTSECTION_SETUP, message::MessageSubType::TESTSUITE_SETUP, message::MessageSubType::TESTCASE_SETUP, + message::MessageSubType::TESTSECTION_RUN, message::MessageSubType::TESTSUITE_RUN, message::MessageSubType::TESTCASE_RUN, + message::MessageSubType::TESTSECTION_TEARDOWN, message::MessageSubType::TESTSUITE_TEARDOWN, message::MessageSubType::TESTCASE_TEARDOWN}; + + constexpr std::array msgReports = {message::MessageSubType::TEST_PASS, message::MessageSubType::TEST_FAIL, message::MessageSubType::TEST_NOTIMPLEMENTED, + message::MessageSubType::REPORT_SUCCESS, message::MessageSubType::REPORT_FAILURE}; + + constexpr std::array msgAcknowledgements = {message::MessageSubType::ACKNOWLEDGEMENT, message::MessageSubType::NACK}; + + #if defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + // Bit field used that can be used to read the first element of a header + struct FirstS + { + uint8_t res : 4; + uint8_t messageType : 2; + uint8_t protocolVersion : 2; + }; + #elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + // Bit field used that can be used to read the first element of a header + struct FirstS + { + uint8_t protocolVersion : 2; + uint8_t messageType : 2; + uint8_t res : 4; + }; + #else + #error No suitbale endianess defined + #endif + + using FirstT = struct FirstS; + + // TestApp generic Message type + template + struct Message + { + /** + Template with messagestruct, containing + headerinfo -->version|message type|reserved + MessageHeader-->subtype|errorcode|testsection|testsuite|testcase|payloadlength + */ + FirstT headerInfo{}; + MessageType messageType{}; + uint8_t token{0u}; + MessageSubType subType{}; + testapp::ErrCode errorCode{}; + uint8_t testSection{0u}; + uint8_t testSuite{0u}; + uint8_t testCase{0u}; + uint8_t payloadLength{0u}; + std::array payload{}; + }; + + /// Data type sent from TestRunner to Test Environment to process command + struct TestExecutionMessage + { + MessageSubType subType{}; + void* testSuiteOrCase = nullptr; + }; + + struct TLV + { + uint8_t tag = 0; + uint8_t length = 0; + uint8_t *value = nullptr; + }; + + using MessageStructure = Message; + using MessageBytes = std::array; + + /*functions for serializing, deserializing, parsing and adding TLV'S*/ + /*Deserialize bytestream to a message struct*/ + testapp::RetCode deserialize(const etl::array_view& bytestream, MessageStructure &msg); + + /*Serialize messagestruct to bytearray*/ + testapp::RetCode serialize(const MessageStructure &msg, etl::array_view& bytearray); + + /// Parse tlv element + testapp::RetCode parseTLV(MessageStructure &msg, TLV &nextTlv); + + /*Add tlv element to the message structure*/ + testapp::RetCode addTLV(const TLV &nextTlv, MessageStructure &msg); + +} // namespace message +#endif diff --git a/TestApp/include/TPC/TPC/TPC.hpp b/TestApp/include/TPC/TPC/TPC.hpp new file mode 100644 index 0000000..08f16e8 --- /dev/null +++ b/TestApp/include/TPC/TPC/TPC.hpp @@ -0,0 +1,99 @@ +/** + * @file include/TPC/TPC/TPC.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TPC_HPP +#define TPC_HPP +#include +#include +#include +#include +#include +#include + +namespace tpcprotocol +{ + class TPC + { + public: + /// Constructor. + TPC() = default; + + ///default constructor + ~TPC() = default; + + /// Copy constructor. + TPC(TPC const &) = delete; + + /// Move constructor. + TPC(TPC &&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + TPC &operator=(TPC const &) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + TPC &operator=(TPC &&) = delete; + + /** + * @brief Method to validate bytestream sent from ITF, by calculatingCRC and verifying CRC + * + * @return testapp::ErrCode + */ + static bool validateITFByteStream(const etl::array_view& bytestream, uint8_t length); + + /** + * @brief Method to send Acknowledgement or Nacknowledgement back to ITF + * + * @return testapp::ErrCode + */ + static testapp::RetCode sendACKorNACK(message::MessageSubType subType, uint8_t tokenID); + + /** + * @brief Method to send TestReport to ITF using FDX_send API + * + * @return testapp::ErrCode + */ + static testapp::RetCode sendReportToITF(const message::MessageStructure &reportMsg, uint8_t reportLength); + + /** + * @brief set _TestRunnerToTPCMessageBuffer Reference + */ + void setTPCToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _TestRunnerToTPCMessageBuffer Reference + */ + void setTestRunnerTPCMessageBufferReference(osextension::MessageBufferReference& mbf); + /** + * @brief onecycle method is invoked in run method of task mechanism + * + */ + void oneCycle(); + + private: + /** + * @brief Method to calculate CRC + * @return uint16_t buffer + */ + static uint16_t calculateCRC(const etl::array_view& buffer, uint8_t length); + + /// Message Buffer reference for incoming messages from TestRunner + osextension::MessageBufferReference* _testRunnerToTPCMessageBuffer = nullptr; + + /// Message Buffer reference for Outgoing messages from TestRunner + osextension::MessageBufferReference* _tpcToTestRunnerMessageBuffer = nullptr; + }; + +} // namespace tpcprotocol +#endif diff --git a/TestApp/include/TPC/TPC/Task.hpp b/TestApp/include/TPC/TPC/Task.hpp new file mode 100644 index 0000000..3fd4c01 --- /dev/null +++ b/TestApp/include/TPC/TPC/Task.hpp @@ -0,0 +1,76 @@ +/** + * @file include/TPC/TPC/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TPC_TASK_HPP +#define TPC_TASK_HPP +#include "TestRunner/TaskInterface.hpp" +#include "TPC/TaskInterface.hpp" +#include "TPC/TPC.hpp" +#include +#include +#include + +namespace tpcprotocol{ +/** + * @brief TPCProtocol class + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor + * @param[in] taskConfiguration Task configuration + */ + explicit Task(const testapprtos::TaskConfiguration& taskConfiguration); + + /** + * @brief run function + */ + void run() override; + + /// @copydoc Task::initializeTask() + void initialize() override { + initializeTask(); + } + +private: + Task() = delete; /**< Remove empty (default) constructor */ + Task(const Task&) = delete; /**< Remove copy Constructor */ + Task(const Task&&) = delete; /**< Remove assign Constructor */ + const Task& operator=(const Task&) = delete; /**< Remove assign Operation @return NULL */ + const Task& operator=(Task&&) = delete; /**< Remove move Operation @return NULL */ + + /// Size of message buffer in bytes for incoming message bus messages + static constexpr size_t IncomingMessageBufferSize = 265u; + + /// Maximum number of messages in message buffer for incoming message bus messages + static constexpr size_t IncomingMessageBufferMaximumNumberOfMessages = 1u; + + /// Message Buffer reference for incoming messages from TestRunner + osextension::MessageBufferReference _TestRunnerToTPCMessageBuffer; + + /// Message Buffer reference for Outgoing messages to TestRunner + osextension::MessageBufferReference _TPCToTestRunnerMessageBuffer; + + /// Semaphore + osextension::Semaphore _TCCInterfacerReferencePairAdded{1,0}; + + ///TPC instance + TPC _tpc{}; + + /// Task delay time + TickType_t _blockingTime{0}; +}; + +} //namespace TPCProtocol + +#endif diff --git a/TestApp/include/TPC/TPC/TaskInterface.hpp b/TestApp/include/TPC/TPC/TaskInterface.hpp new file mode 100644 index 0000000..55fe1f9 --- /dev/null +++ b/TestApp/include/TPC/TPC/TaskInterface.hpp @@ -0,0 +1,56 @@ +/** + * @file include/TPC/TPC/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TPC_TPCINTERFACE_HPP +#define TPC_TPCINTERFACE_HPP + +#include +#include +#include + +/// @brief testrunner task namespace +namespace tpcprotocol { +/** + * @brief: Task Interface + */ +class TaskInterface { +public: + /// Deleted copy constructor + TaskInterface(const TaskInterface&) = delete; + + /// Deleted move constructor + TaskInterface(const TaskInterface&&) = delete; + + /// Deleted copy assignment operator + const TaskInterface& operator=(const TaskInterface&) = delete; + + /// Deleted move assignment operator + const TaskInterface& operator=(TaskInterface&&) = delete; + + /// Initialize + virtual void initialize() = 0; +protected: + /// Default constructor + TaskInterface() = default; + + /// Destructor + virtual ~TaskInterface() = default; +}; + +/// Global interface to task +extern TaskInterface& taskInterface; + +} // namespace tpcprotocol + +#endif // TPC_TPCINTERFACE_HPP diff --git a/TestApp/include/TestAppTypes/TestAppTypes/TestAppTypes.hpp b/TestApp/include/TestAppTypes/TestAppTypes/TestAppTypes.hpp new file mode 100644 index 0000000..7f00dc2 --- /dev/null +++ b/TestApp/include/TestAppTypes/TestAppTypes/TestAppTypes.hpp @@ -0,0 +1,86 @@ +/** + * @file include/TestAppMessage/TestAppMessage/TestAppMessage.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTAPPTYPES_HPP +#define TESTAPPTYPES_HPP + +#include +#include +#include +#include +namespace testapp { + +using TestIDType = size_t; + +constexpr uint8_t messagebufferSize = 245u; /**< maximum payload size - 2 bytes for tlv-element type and length */ +constexpr uint8_t maxAssertStringLength = 255u; + +enum class RetCode { + RETCODE_SUCCESS = 0, // NOLINT(readability-identifier-naming) invalid case style for enum constant + RETCODE_FAILURE, /**< An unspecified failure has occurred e.g. error in third party code */ //NOLINT + RETCODE_OUT_OF_RESOURCES, /**< System resources have been exhausted e.g. not enough RAM */ //NOLINT + RETCODE_INVALID_PARAM, /*NOLINT*/ /**< One or more passed input parameters was invalid + (e.g. passed a null pointing buffer) */ + RETCODE_NOT_SUPPORTED, /**< The requested operation is not supported by the current implementation */ //NOLINT + RETCODE_INCONSISTENT_STATE, /**< The module is in a state preventing it from executing the requested operation*/ //NOLINT + RETCODE_UNINITIALIZED, /**< The module has not been initialized */ //NOLINT + RETCODE_NULL_POINTER, /**< During function execution a null pointer has been encountered */ //NOLINT + RETCODE_UNEXPECTED_BEHAVIOR, /**< The function has produced an unintended side effect*/ //NOLINT + RETCODE_DOUBLE_INITIALIZATION, /**< Reinitializing the module may produce inconsistency */ //NOLINT + RETCODE_TIMEOUT, /**< A timeout has occurred while waiting for the operation to complete */ //NOLINT + RETCODE_TIMER_ERROR, //NOLINT + RETCODE_SEMAPHORE_ERROR, //NOLINT + RETCODE_QUEUE_ERROR, //NOLINT + RETCODE_INVALID_SIGNAL_IN_ISR, //NOLINT + RETCODE_NULLPOINTER, //NOLINT + RETCODE_LASTELEMENT //NOLINT +}; + +enum class MessageType : uint8_t{ + MESSAGE_INFO = 0, //NOLINT + MESSAGE_DEBUG, //NOLINT + MESSAGE_WARNING, //NOLINT + MESSAGE_ERROR, //NOLINT + MESSAGE_CRITICAL, //NOLINT + MESSAGE_LOG, //NOLINT +} ; +struct LogMessageT +{ + MessageType messageType{}; + std::array text{}; +}; + +/// custom error handeling +enum class ErrCode : uint8_t { + SUCCESS = 0, //NOLINT + FAILURE, //NOLINT + ERRCODE_TEST_SUITE_UNKNOWN, //NOLINT + ERRCODE_TEST_CASE_UNKNOWN, //NOLINT + ERRCODE_TEST_SUITE_ALREADY_REGISTERED, //NOLINT + ERRCODE_TEST_CASE_ALREADY_REGISTERED, //NOLINT + ERRCODE_CCHANNEL_NOT_SPECIFIED, //NOLINT + ERRCODE_TEST_CCHANNEL_INITIALISATION_FAILED, //NOLINT + ERRCODE_TEST_CCHANNEL_RESPONSE_FAILURE, //NOLINT + ERRCODE_MSG_INCOMPLETE, //NOLINT + ERRCODE_MSG_INCORRECT_RESPONSE, //NOLINT + ERRCODE_MSG_CRC_MISMATCH, //NOLINT + ERRCODE_VERSION_MISMATCH, //NOLINT + ERRCODE_TLVELEMENT_NOT_FOUND, //NOLINT + ERRCODE_REPORT_TIMEOUT, //NOLINT + ERRCODE_UNDEFINED //NOLINT +}; + +} // namespace testapp + +#endif diff --git a/TestApp/include/TestCase/TestCase/TestCaseBase.hpp b/TestApp/include/TestCase/TestCase/TestCaseBase.hpp new file mode 100644 index 0000000..4039e28 --- /dev/null +++ b/TestApp/include/TestCase/TestCase/TestCaseBase.hpp @@ -0,0 +1,66 @@ +/** + * @file TestCaseBase.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTCASEBASE_HPP +#define TESTCASEBASE_HPP + +#include +#include +#include +#include + +/// Capture a reference to logging singleton +static testenvironment::LoggingInterface& logging = testenvironment::Logging::getInstance(); + +/// Capture a reference to Assert singleton +static testenvironment::Assert& assertion = testenvironment::Assert::getInstance(); + +/** + * @brief: TestCaseBase Class, it is a helper class to automatically register any derived TestCase + * @tparam NewTestCaseClass the new TestCase class + * @tparam ItsTestSuiteClass the parent TestSuite which is a factory for this class + */ +template +class TestCaseBase : protected TestFactoryRegistrar { +public: + TestCaseBase() { + assertion.setLogger(logging); + } + + testapp::ErrCode returnReportAndReset() { + // A successful TestCase or TestSuite shall have no failure, otherwise a failure report is sent + // Evaluate assertions and logging messages such as errors and critical logs and reset. + testapp::ErrCode errCodeLogger = logging.getFailureFlag() ? testapp::ErrCode::FAILURE : testapp::ErrCode::SUCCESS; + logging.resetFailureFlag(); + + testapp::ErrCode errCodeAssert = (assertion.returnReportAndReset() == testenvironment::ReportResult::ReportSuccess) ? + testapp::ErrCode::SUCCESS : testapp::ErrCode::FAILURE; + + return (errCodeLogger != testapp::ErrCode::SUCCESS || errCodeAssert != testapp::ErrCode::SUCCESS) ? + testapp::ErrCode::FAILURE : testapp::ErrCode::SUCCESS; + } + +}; + +/// Provides a macro for the start of defining a TestCase +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] we want to keep googletest style +#define DECLARE_TESTCASE(ITS_TESTSUITE_CLASS, NEW_TESTCASE_CLASS, ID) \ +class NEW_TESTCASE_CLASS : public testcase::TestCaseInterface, public TestCaseBase { /*NOLINT [bugprone-macro-parentheses]*/ \ +public: \ + NEW_TESTCASE_CLASS() {(void)registered;} \ + +/// Provides a macro for the end of defining a TestCase +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] we want to keep googletest style +#define END_TESTCASE(NEW_TESTCASE_CLASS) }; + +#endif diff --git a/TestApp/include/TestCase/TestCase/TestCaseInterface.hpp b/TestApp/include/TestCase/TestCase/TestCaseInterface.hpp new file mode 100644 index 0000000..ee150f6 --- /dev/null +++ b/TestApp/include/TestCase/TestCase/TestCaseInterface.hpp @@ -0,0 +1,71 @@ +/** + * @file TestCaseAbstract.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTCASEINTERFACE_HPP +#define TESTCASEINTERFACE_HPP + +#include +#include +#include +namespace testcase { +/** + * @brief The interface for a test case + */ +class TestCaseInterface { +public: + /// Constructor. + TestCaseInterface(void) = default; + + /// Copy constructor. + TestCaseInterface(TestCaseInterface const&) = delete; + + /// Move constructor. + TestCaseInterface(TestCaseInterface&&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + TestCaseInterface& operator=(TestCaseInterface const&) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + TestCaseInterface& operator=(TestCaseInterface&&) = delete; + + /// Default destructor. + virtual ~TestCaseInterface(void) = default; + + /** + * @brief Prepares environment for testcase execution + * + * @return testapp::ErrCode + */ + virtual testapp::ErrCode doSetup() = 0; + + /** + * @brief Method to execute the different steps of the testcase specification. + * @param TestStatus reference to the object holding the Status during the execution + * + * Note The method can be called multiple times within the execution context and will execute different steps on the StatusC object state + * + * @return testapp::ErrCode + */ + virtual testapp::ErrCode runTest() = 0; + + /** + * @brief Method to implement the testcase teardown and reset the Environment for further testcase execution + */ + virtual testapp::ErrCode doTearDown() = 0; +}; + +} // namespace testcase + +#endif diff --git a/TestApp/include/TestEnvironment/TestEnvironment/Assert.hpp b/TestApp/include/TestEnvironment/TestEnvironment/Assert.hpp new file mode 100644 index 0000000..38b3647 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/Assert.hpp @@ -0,0 +1,356 @@ +/** + * @file include/TestEnvironment/TestEnvironment/Assert.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#ifndef TESTENVIRONMENT_ASSERT_HPP +#define TESTENVIRONMENT_ASSERT_HPP +#include +#include +#include +#include +namespace testenvironment { + +/** + * Define Test App assertions. + * Named to TA_ASSERT* because it would conflict with the GTest macros + */ +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_TRUE(obj) assertTrue(obj, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_FALSE(obj) assertFalse(obj, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_EQ(objA, objB) assertEq(objA, objB, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_NE(objA, objB) assertNe(objA, objB, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_LT(objA, objB) assertLt(objA, objB, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_LE(objA, objB) assertLe(objA, objB, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_STREQ(str1, str2) assertStrEq(str1, str2, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_GT(objA, objB) assertGt(objA, objB, __LINE__, __FILE__) +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] with source_location from c++20 this __LINE__ and __FILE__ can be replaced in c++ way +#define TA_ASSERT_GE(objA, objB) assertGe(objA, objB, __LINE__, __FILE__) + +/** + * @brief Assert class for the execution of assert operations + * + * - Provides assert operations + * - Loggs errors + * - Provides report of multiple assert operations together (Success/Failure) + * + */ +class Assert { +public: + /// Copy constructor. + Assert(Assert const&) = delete; // Explicitly deleted + + /// Move constructor. + Assert(Assert&&) = delete; // Explicitly deleted + + /// Copy assignment operator. + Assert& operator=(Assert const&) = delete; // Explicitly deleted + + /// Move assignment operator. + Assert& operator=(Assert&&) = delete; // Explicitly deleted + + /** + * @brief Get the Instance object + * + * @return Assert& Instance object + */ + static Assert& getInstance(void); + + /** + * @brief Checks if obj == true + * + * @tparam T obj type + * @param obj obj to be verified + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: obj == true + * @return testapp::RetCode::RETCODE_FAILURE: obj != true + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertTrue(const T obj, const int line, const char* file) { + testapp::RetCode retCode = (obj) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + AssertReport assertReport(AssertTypes::ASSERT_TRUE, static_cast(obj), 1.0F, retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if obj == false + * + * @tparam T obj type + * @param obj obj to be verified + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: obj == false + * @return testapp::RetCode::RETCODE_FAILURE: obj != false + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertFalse(const T obj, const int line, const char* file) { + testapp::RetCode retCode = (!obj) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + AssertReport assertReport(AssertTypes::ASSERT_FALSE, static_cast(obj), 0.0F, retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA == objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA == objB + * @return testapp::RetCode::RETCODE_FAILURE: objA != objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertEq(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA == objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_EQ, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA != objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA != objB + * @return testapp::RetCode::RETCODE_FAILURE: objA == objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertNe(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA != objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_NE, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA < objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA < objB + * @return testapp::RetCode::RETCODE_FAILURE: objA >= objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertLt(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA < objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_LT, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA <= objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA <= objB + * @return testapp::RetCode::RETCODE_FAILURE: objA > objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertLe(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA <= objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_LE, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA > objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA > objB + * @return testapp::RetCode::RETCODE_FAILURE: objA <= objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertGt(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA > objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_GT, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if objA >= objB + * + * @tparam T obj type + * @param objA First object to compare + * @param objB Second object to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: objA >= objB + * @return testapp::RetCode::RETCODE_FAILURE: objA < objB + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + template + testapp::RetCode assertGe(const T objA, const T objB, const int line, const char* file) { + testapp::RetCode retCode = (objA >= objB) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + + AssertReport assertReport(AssertTypes::ASSERT_GE, static_cast(objA), static_cast(objB), retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; + } + + /** + * @brief Checks if str1 == str2 + * + * @param str1 First string to compare + * @param str2 Second string to compare + * @param line line from where the operation has been called from + * @param file file from where the operation has been called from + * @return testapp::RetCode::RETCODE_SUCCESS: str1 == str2 + * @return testapp::RetCode::RETCODE_FAILURE: str1 != str2 + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during + * operation + */ + //NOLINTNEXTLINE(readability-avoid-const-params-in-decls) although const here has no effect but to keep declaration and definition similar, it should be ignored + testapp::RetCode assertStrEq(const char* str1, const char* str2, const int line, const char* file); + + /** + * @brief Use Logging class to log the assertion if it failed + * + * @param AssertReport Assert class with test information + * @return testapp::RetCode::RETCODE_SUCCESS + * @return testapp::RetCode::RETCODE_UNINITIALIZED: internal _logger variable has not + * been set + * @return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR: Internal error during report + * creation + */ + testapp::RetCode logAssert(const AssertReport& assertReport); + + /** + * @brief Set the Logger object + * + * @param logger Logger reference to be set + * @return testapp::RetCode::RETCODE_SUCCESS + */ + testapp::RetCode setLogger(LoggingInterface& logger); + + /** + * @brief Clears the assert instance and returns the report state from the + * testrun + * + * @return ReportResult State of the testrun (Success/Failed) + */ + ReportResult returnReportAndReset(); + +private: + /// Constructor. + Assert() = default; // Default implementation + /// Destructor. + ~Assert() = default; // Explicitly defaulted + + /** + * @brief Queries if the internal logger variable has been set + * + * @return true Logger variable has been set + * @return false Logger variable is still set to nullptr + */ + bool isLoggerInitialized(void) const; + + LoggingInterface* _logger = nullptr; + ReportResult _report = ReportResult::ReportSuccess; +}; + +} // namespace testenvironment +#endif // TESTENVIRONMENT_ASSERT_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/AssertReport.hpp b/TestApp/include/TestEnvironment/TestEnvironment/AssertReport.hpp new file mode 100644 index 0000000..517a430 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/AssertReport.hpp @@ -0,0 +1,112 @@ +/** + * @file include/TestEnvironment/TestEnvironment/AssertReport.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#ifndef TESTENVIRONMENT_ASSERTREPORT_HPP +#define TESTENVIRONMENT_ASSERTREPORT_HPP + +#include +#include + +#include + +namespace testenvironment { + + constexpr uint8_t operatorStringMaxSize = 13u; + + +/** + * @brief Holds the overall test status for the report + * + */ +enum class ReportResult { ReportSuccess, + ReportFailure }; + +/** + * @brief Class for creating a log string for the respective assert operation + * + */ +class AssertReport { +public: + /** + * @brief Returns the stored fileName + * + * @return const char* fileName + */ + const char* getFilename() const; + + /** + * @brief Returns the stored result of the assert operation + * + * @return testapp::RetCode_T Assert operation result + */ + testapp::RetCode getResult() const; + + /** + * @brief Construct a new Assert Report object for every assert operation + * beside string compare + * + * @param assertType Type of assert operation + * @param input1 First object of assert operation + * @param input2 Second object of assert operation + * @param retCode Outcome of the assert operation + * @param line Line number, where the assert operation is written in the file + * @param fileName File, where the assert operation is written in + */ + //NOLINTNEXTLINE(readability-avoid-const-params-in-decls) although const here has no effect but to keep declaration and definition similar, it should be ignored + AssertReport(const AssertTypes assertType, const float input1, const float input2, const testapp::RetCode retCode, const uint16_t line, const char* fileName); + + /** + * @brief Construct a new Assert Report object for string compare assert + * operation + * + * @param assertType Type of assert operation + * @param input1 First string of assert operation + * @param input2 Second string of assert operation + * @param retCode Outcome of the assert operation + * @param line Line number, where the assert operation is written in the file + * @param fileName File, where the assert operation is written in + */ + //NOLINTNEXTLINE(readability-avoid-const-params-in-decls) although const here has no effect but to keep declaration and definition similar, it should be ignored + AssertReport(const AssertTypes assertType, const char* input1, const char* input2, const testapp::RetCode retCode, const uint16_t line, const char* fileName); + + /** + * @brief Create and return a report string for the stored assert operation + * + * @param report Pointer to char [UINT8_MAX] array for report + * @return testapp::RetCode_T Returncode of operation + */ + testapp::RetCode createReport(char* report) const; + + /** + * @brief Extract fileName from path string + * + * @param fileName Pointer to path string + * @return testapp::RetCode_T + */ + testapp::RetCode setFilename(const char* fileName); //NOLINT(readability-avoid-const-params-in-decls) although const here has no effect but to keep declaration and definition similar, it should be ignored + static constexpr uint8_t maxFilenameSize = UINT8_MAX; + static constexpr uint8_t maxReportSize = UINT8_MAX; + +private: + const AssertTypes _assertType; + const float _input1; + const float _input2; + const char* _inputString1; + const char* _inputString2; + const testapp::RetCode _retCode; + const uint16_t _line; + std::array _fileName; +}; + +} // namespace testenvironment +#endif // TESTENVIRONMENT_ASSERTREPORT_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/Logging.hpp b/TestApp/include/TestEnvironment/TestEnvironment/Logging.hpp new file mode 100644 index 0000000..4afe61a --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/Logging.hpp @@ -0,0 +1,154 @@ +/** + * @file include/TestEnvironment/TestEnvironment/Logging.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTENVIRONMENT_LOGGING_HPP +#define TESTENVIRONMENT_LOGGING_HPP + +#include + +#include +#include + +#include +#include + +/** + * @brief: testenvironment namespace + */ +namespace testenvironment { + constexpr uint8_t errorTypes = 6u; + constexpr uint8_t messageTypeStringsMaxSize = 8u; + constexpr uint8_t delimiterStringSize = 2u; + constexpr std::array messageTypeDelimiter{':', ' '}; + struct MessageTypeString { + std::array value; + }; + constexpr struct {MessageTypeString info, debug, warning, error, critical, log;} messageTypeString = { + {"INFO"}, {"DEBUG"}, {"WARNING"}, {"ERROR"}, {"CRITICAL"}, {"LOG"} + }; + +/** + * @brief Logging + */ +class Logging : public LoggingInterface { +public: + /* Deleted copy constructor */ + Logging(const Logging&) = delete; + /* Deleted move constructor */ + Logging(const Logging&&) = delete; + /* Deleted copy assignment */ + Logging& operator=(const Logging&) = delete; + /* Deleted move assignment*/ + Logging& operator=(Logging&&) = delete; + /** + * @brief Get the Instance object + * + * @return Logging& + */ + static Logging& getInstance(); + + /** + * @brief Set the Message Buffer object + * + * @param mbuf + * @return testapp::RetCode_T::RETCODE_SUCCESS + */ + testapp::RetCode setMessageBuffer(osextension::MessageBufferReference& mbuf); + + /** + * @brief Set the Debug Log object + * + * @param isDebugLog + * @return testapp::RetCode_T::RETCODE_SUCCESS + */ + testapp::RetCode setDebugLog(bool isDebugLog); + testapp::RetCode logInfo(const char* msg) override; + testapp::RetCode logDebug(const char* msg) override; + testapp::RetCode logWarning(const char* msg) override; + testapp::RetCode logError(const char* msg) override; + testapp::RetCode logCritical(const char* msg) override; + testapp::RetCode logMessage(const char* msg) override; + + /** + * @brief catch failures while logging messages. + * @param None + * @return bool + */ + bool getFailureFlag() override; + + /** + * @brief reset logging flag _hasFailed; the flag could be set to true when an errorLog + * or criticalLog was invoked or when other issues such as msg too long occur. + * @param None + * @return None + */ + void resetFailureFlag() override; + + /** + * @brief Sends the message + * + * @param msg + * @param messageType + */ + void sendMessage(const char* msg, testapp::MessageType messageType); + +private: + /** + * @brief To prevent repetitve code, logGeneric calls checkMessage + * and sends the corresponding log message and type. + * + * @param msg + * @param messageType + * @return testapp::RetCode::RETCODE_SUCCESS + * @return testapp::RetCode::RETCODE_NULL_POINTER (msg is nullpointer) + * @return testapp::RetCode::RETCODE_UNINITIALIZED (_testEnvironmentToTestRunnerMessageBufferReference unitialized) + * @return testapp::RetCode::RETCODE_OUT_OF_RESOURCES (msg too long) + * @return testapp::RetCode::RETCODE_INVALID_PARAM (empty msg) + */ + testapp::RetCode logGeneric(const char* msg, testapp::MessageType messageType); + + /** + * @brief Checks whether it is a valid message + * + * @param msg + * @return testapp::RetCode::RETCODE_SUCCESS + * @return testapp::RetCode::RETCODE_NULL_POINTER (msg is nullpointer) + * @return testapp::RetCode::RETCODE_UNINITIALIZED (_testEnvironmentToTestRunnerMessageBufferReference unitialized) + * @return testapp::RetCode::RETCODE_OUT_OF_RESOURCES (msg too long) + * @return testapp::RetCode::RETCODE_INVALID_PARAM (empty msg) + */ + testapp::RetCode checkMessage(const char* msg); + + /// A map of MessageTpes to charachters + const etl::map _messageTypeStrings = { + {testapp::MessageType::MESSAGE_INFO, messageTypeString.info}, + {testapp::MessageType::MESSAGE_DEBUG, messageTypeString.debug}, + {testapp::MessageType::MESSAGE_WARNING, messageTypeString.warning}, + {testapp::MessageType::MESSAGE_ERROR, messageTypeString.error}, + {testapp::MessageType::MESSAGE_CRITICAL, messageTypeString.critical}, + {testapp::MessageType::MESSAGE_LOG, messageTypeString.log} + }; + + /// Message Buffer reference for Outgoing messages from testEnvironment to TestRunner + osextension::MessageBufferReference* _testEnvironmentToTestRunnerMessageBufferReference = nullptr; // Queues are Message buffers + bool _isDebugLog = false; + bool _hasFailed = false; + + Logging() = default; + ~Logging() = default; + +}; + +} // namespace testenvironment +#endif // TESTENVIRONMENT_LOGGING_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/LoggingInterface.hpp b/TestApp/include/TestEnvironment/TestEnvironment/LoggingInterface.hpp new file mode 100644 index 0000000..81e4f27 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/LoggingInterface.hpp @@ -0,0 +1,41 @@ +/** + * @file include/TestEnvironment/TestEnvironment/LoggingInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#ifndef TESTENVIRONMENT_LOGGINGINTERFACE_HPP +#define TESTENVIRONMENT_LOGGINGINTERFACE_HPP + +#include + +/** + * @brief: testenvironment namespace + */ +namespace testenvironment { +/** + * @brief LoggingInterface + */ +class LoggingInterface { +public: + /* Enable default constructor */ + LoggingInterface() = default; + virtual testapp::RetCode logInfo(const char* msg) = 0; + virtual testapp::RetCode logDebug(const char* msg) = 0; + virtual testapp::RetCode logWarning(const char* msg) = 0; + virtual testapp::RetCode logError(const char* msg) = 0; + virtual testapp::RetCode logCritical(const char* msg) = 0; + virtual testapp::RetCode logMessage(const char* msg) = 0; + virtual void resetFailureFlag() = 0; + virtual bool getFailureFlag() = 0; +}; + +} // namespace testenvironment +#endif // TESTENVIRONMENT_LOGGINGINTERFACE_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/Task.hpp b/TestApp/include/TestEnvironment/TestEnvironment/Task.hpp new file mode 100644 index 0000000..064d3c3 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/Task.hpp @@ -0,0 +1,81 @@ +/** + * @file include/TestEnvironment/TestEnvironment/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTENVIRONMENT_TASK_HPP +#define TESTENVIRONMENT_TASK_HPP + +#include +#include +#include +#include +#include +#include + +/** + * @brief: testenvironment namespace. + */ +namespace testenvironment { + +/** + * @brief Task class, which executes the TestEnvironment functionality. + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor. + * @param[in] taskConfiguration Task configuration. + */ + explicit Task(const testapprtos::TaskConfiguration& taskConfiguration); + + /** + * @brief Register MessageBufferReference from TestRunner to TestEnvironment. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTestRunnerToTestEnvMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) override; + + /** + * @brief Register MessageBufferReference from TestEnvironment to TestRunner. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTestEnvToTestRunnerMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) override; + + /** + * @brief Run function. + */ + void run() override; + + /** + * @brief Initialize. + */ + void initialize() override; + +private: + Task() = delete; /**< Remove empty (default) constructor. */ + Task(const Task&) = delete; /**< Remove copy Constructor. */ + Task(const Task&&) = delete; /**< Remove assign Constructor. */ + const Task& operator=(const Task&) = delete; /**< Remove assign Operation @return NULL. */ + const Task& operator=(Task&&) = delete; /**< Remove move Operation @return NULL. */ + TestEnvironment testEnvironment; /**< TestEnvironment instance. */ + /** + * @brief Semaphore to signal, that the message buffer from TestRunnter to TestEnvironment was registered. + */ + osextension::Semaphore _testRunnerToTestEnvMessageBufferAdded{1, 0}; + /** + * @brief Semaphore to signal, that the message buffer from TestEnvironment to TestRunnter was registered. + */ + osextension::Semaphore _testEnvToTestRunnerMessageBufferAdded{1, 0}; +}; + +} // namespace testenvironment + +#endif // TESTENVIRONMENT_TASK_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/TaskInterface.hpp b/TestApp/include/TestEnvironment/TestEnvironment/TaskInterface.hpp new file mode 100644 index 0000000..fb6b9bf --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/TaskInterface.hpp @@ -0,0 +1,66 @@ +/** + * @file include/TestEnvironment/TestEnvironment/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTENVIRONMENT_TASKINTERFACE_HPP +#define TESTENVIRONMENT_TASKINTERFACE_HPP + +#include +#include + +/** + * @brief: testenvironment namespace. + */ +namespace testenvironment { + +/** + * @brief: Interface for the Task class, which executes the TestEnvironment functionality. + */ +class TaskInterface { +public: + TaskInterface(const TaskInterface&) = delete; /**< Deleted copy constructor. */ + TaskInterface(const TaskInterface&&) = delete; /**< Deleted move constructor. */ + const TaskInterface& operator=(const TaskInterface&) = delete; /**< Deleted copy assignment operator. */ + const TaskInterface& operator=(TaskInterface&&) = delete; /**< Deleted move assignment operator. */ + + /** + * @brief Register MessageBufferReference from TestRunner to TestEnvironment. + * @param[in] messageBufferReference Reference to the message buffer. + */ + virtual void registerTestRunnerToTestEnvMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) = 0; + + /** + * @brief Register MessageBufferReference from TestEnvironment to TestRunner. + * @param[in] messageBufferReference Reference to the message buffer. + */ + virtual void registerTestEnvToTestRunnerMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) = 0; + + /** + * @brief Initialize. + */ + virtual void initialize() = 0; + +protected: + /// Default constructor. + TaskInterface() = default; + + /// Destructor. + virtual ~TaskInterface() = default; +}; + +/// Global interface to task. +extern TaskInterface& taskInterface; + +} // namespace testenvironment. + +#endif // TESTENVIRONMENT_TASKINTERFACE_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironment.hpp b/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironment.hpp new file mode 100644 index 0000000..1d50cc4 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironment.hpp @@ -0,0 +1,69 @@ +/** + * @file include/TestEnvironment/TestEnvironment/TestEnvironment.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTENVIRONMENT_TESTENVIRONMENT_HPP +#define TESTENVIRONMENT_TESTENVIRONMENT_HPP + +#include +#include +#include + +/** + * @brief: testenvironment namespace. + */ +namespace testenvironment { + +/** + * @brief: TestEnvironment class. + */ +class TestEnvironment { +public: + /** + * @brief Run one cycle. + */ + void runOneCycle(); + + /** + * @brief Register MessageBufferReference from TestRunner to TestEnvironment. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTestRunnerToTestEnvMessageBufferReference(osextension::MessageBufferReference& messageBufferReference); + + /** + * @brief Register MessageBufferReference from TestEnvironment To TestRunner. + * @param[in] messageBufferReference Reference to the message buffer. + */ + void registerTestEnvToTestRunnerMessageBufferReference(osextension::MessageBufferReference& messageBufferReference); + +private: + /** + * @brief Processes a message. + * @param[in] message Message to process. + */ + static testapp::ErrCode processMessage(const message::TestExecutionMessage& message); + + /** + * @brief MessageBufferReference from TestRunner to TestEnvironment. + */ + osextension::MessageBufferReference* _testRunnerToTestEnvMessageBufferReference = nullptr; + + /** + * @brief MessageBufferReference from TestEnvironment to TestRunner. + */ + osextension::MessageBufferReference* _testEnvironmentToTestRunnerMessageBufferReference = nullptr; +}; + +} // namespace testenvironment + +#endif // TESTENVIRONMENT_TESTENVIRONMENT_HPP diff --git a/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironmentTypes.hpp b/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironmentTypes.hpp new file mode 100644 index 0000000..0d9e627 --- /dev/null +++ b/TestApp/include/TestEnvironment/TestEnvironment/TestEnvironmentTypes.hpp @@ -0,0 +1,21 @@ +#ifndef TESTENVIRONMENT_HPP +#define TESTENVIRONMENT_HPP + +namespace testenvironment +{ +enum class AssertTypes +{ + ASSERT_TRUE = 0, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_FALSE, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_EQ, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_NE, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_LT, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_LE, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_GT, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_GE, //NOLINT (readability-identifier-naming): we want keep googletest style + ASSERT_STREQ //NOLINT (readability-identifier-naming): we want keep googletest style +}; + + +} // namespace testenvironment +#endif // TESTENVIRONMENT_HPP diff --git a/TestApp/include/TestFactory/TestFactory/TestFactoryBase.hpp b/TestApp/include/TestFactory/TestFactory/TestFactoryBase.hpp new file mode 100644 index 0000000..d2b9b06 --- /dev/null +++ b/TestApp/include/TestFactory/TestFactory/TestFactoryBase.hpp @@ -0,0 +1,71 @@ +/** + * @file include/TestFactory/TestFactory/TestFactoryBase.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TEST_FACTORY_TESTFACTORYBASE_HPP +#define TEST_FACTORY_TESTFACTORYBASE_HPP + +#include +#include + +constexpr uint8_t maxFactoryCapacity = 10u; + +/** + * @brief: A Generic nested Factory Class + * @tparam Parent dummy parent factory class, just will be used to categorize + * the factories based on the Parent and Interface + * @tparam CommonInterface refers to the common abstract class from which + * the containing registered class have been inherited from + */ +template +class TestFactoryBase { + + /// Makes the factory register method accessible by any FactoryRegistrar class + template + friend class TestFactoryRegistrar; + + /// Type definition for the createInstance method provided by any to be registered class + using CreateInstanceCallable = CommonInterface*(*)(void *memoryLocation, size_t memorySize); + /// Type definition for the map of registered classes in factory + using RegisteredDescendentsMap = etl::unordered_map; + +protected: + /// Creates a possible instance of a registered class in factory at desired memory address + static CommonInterface* createObject(testapp::TestIDType descendentID, void *memoryLocation, size_t memorySize) { + auto it = registeredDescendents().find(descendentID); + if (it != registeredDescendents().end()) { + return it->second(memoryLocation, memorySize); + } + return nullptr; + } + + /// Returns the reference of map of registered classes in factory + static RegisteredDescendentsMap& registeredDescendents(){ + static RegisteredDescendentsMap registeredDescendents{}; + return registeredDescendents; + } + + /// Registers a class in factory by desired id and its createInstance method pointer + static bool registerClass(testapp::TestIDType desiredID, CreateInstanceCallable createInstanceFunction) { + bool ret = false; + if(registeredDescendents().available()) { + auto it = registeredDescendents().find(desiredID); + if(it == registeredDescendents().end()) { + registeredDescendents()[desiredID] = createInstanceFunction; + ret = true; + } + } + return ret; + } +}; + +#endif diff --git a/TestApp/include/TestFactory/TestFactory/TestFactoryRegistrar.hpp b/TestApp/include/TestFactory/TestFactory/TestFactoryRegistrar.hpp new file mode 100644 index 0000000..8665fe3 --- /dev/null +++ b/TestApp/include/TestFactory/TestFactory/TestFactoryRegistrar.hpp @@ -0,0 +1,48 @@ +/** + * @file include/TestFactory/TestFactory/TestFactoryRegistrar.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTFACTORY_TESTFACTORYREGISTRAR_HPP +#define TESTFACTORY_TESTFACTORYREGISTRAR_HPP + +/** + * @brief: Factory Registrar Class + * @tparam Interface the pure abstract class which is the base class of all registered and derived classes in the factory + * @tparam Derived the class which is going to be registered + * @tparam Factory the factory class in which the derived class would be registered + */ +template +class TestFactoryRegistrar { +public: + /// A static member which its initilization will do the trick + /// of initializing of the derived class prior to the main function + static bool registered; + + /// A static method which is responsible for the creation of an + /// instance of the derived class using placement new in a specific + /// memory address. it will be called by the factory class in case of need + static Interface* createInstance(void *memoryLocation, size_t memorySize) { + if (sizeof(Derived) <= memorySize) { + //NOLINTNEXTLINE [cppcoreguidelines-owning-memory] returning a newly created resource of type 'Interface *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' + return new (memoryLocation) Derived(); + } + return nullptr; + } +}; + +/// registered member initialization which will eventually do the trick of +/// derived class initilization +template +bool TestFactoryRegistrar::registered = + Factory::registerClass(ID, TestFactoryRegistrar::createInstance); + +#endif diff --git a/TestApp/include/TestRegistry/TestRegistry/TestRegistry.hpp b/TestApp/include/TestRegistry/TestRegistry/TestRegistry.hpp new file mode 100644 index 0000000..44e21d7 --- /dev/null +++ b/TestApp/include/TestRegistry/TestRegistry/TestRegistry.hpp @@ -0,0 +1,90 @@ +/** + * @file include/TestRegistry/TestRegistry/TestRegistry.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTREGISTRY_TESTREGISTRY_HPP +#define TESTREGISTRY_TESTREGISTRY_HPP + +#include +#include +#include +#include + +/** +* @brief Dummy Class to be passed to the TestRegistry as it is a root factory class, +* and there is no parent factory class associated with it +*/ +class NoFactory{ +}; + +/** + * @brief TestRegistry Class which is a singleton that holds the registered TestSuites +*/ +class TestRegistry final : public TestFactoryBase { +public: + /// Deleted copy constructor + TestRegistry(const TestRegistry &) = delete; + /// Deleted move constructor + TestRegistry(const TestRegistry &&) = delete; + /// Deleted copy assignment + TestRegistry &operator=(const TestRegistry&) = delete; + /// Deleted move assignment + TestRegistry &operator=(TestRegistry &&) = delete; + + /// Returns the singleton instance + static TestRegistry& getInstance() { + static TestRegistry testRegistry; + return testRegistry; + } + + /// Loads a desired TestSuite, it is not completed yet as + /// we have to reflect the usage of the IDs and also perform error handling + testsuite::TestSuiteInterface* loadTestSuite(testapp::TestIDType testSuiteID) { + return TestRegistry::createObject(testSuiteID, static_cast(_testSuiteSpace.data()), maxTestSuiteSpace); + } + + /// Loads a desired TestCase, it is not completed yet as + /// we have to reflect the usage of the IDs and also perform error handling + testcase::TestCaseInterface* loadTestCase(testapp::TestIDType testSuiteID, testapp::TestIDType testCaseID) { + if (auto *tsuit = TestRegistry::createObject(testSuiteID, static_cast(_testSuiteSpace.data()), maxTestSuiteSpace)) { + if (auto *tcase = tsuit->createTestCase(testCaseID, static_cast(_testcaseSpace.data()), maxTestCaseSpace)) { + return tcase; + } + } + return nullptr; + } + + static constexpr size_t testSuiteMemoryCapacity() { + return(maxTestSuiteSpace - sizeof(testsuite::TestSuiteInterface)); + } + + static constexpr size_t testCaseMemoryCapacity() { + return(maxTestCaseSpace - sizeof(testcase::TestCaseInterface)); + } + +private: + /// A static storage place for loading the desired TestSuite and TestCase, using + /// the placement new operator, the implementation is not finished yet as it should + /// checks the required spaces by the relevant TestCase or TestSuite, so it is + /// hard-coded now + static constexpr size_t maxTestSuiteSpace = 200; + static constexpr size_t maxTestCaseSpace = 200; + std::array _testSuiteSpace; + std::array _testcaseSpace; + + /// Hiding the default constructor and destructor to generate compiler errors + /// in case of abuse of singleton + TestRegistry() = default; + ~TestRegistry() = default; +}; + +#endif diff --git a/TestApp/include/TestRunner/TestRunner/Task.hpp b/TestApp/include/TestRunner/TestRunner/Task.hpp new file mode 100644 index 0000000..2e73f6d --- /dev/null +++ b/TestApp/include/TestRunner/TestRunner/Task.hpp @@ -0,0 +1,108 @@ +/** + * @file include/TestRunner/TestRunner/Task.hpp + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTRUNNER_TASK_HPP +#define TESTRUNNER_TASK_HPP + +#include +#include +#include +#include +#include + +#include +#include + +#include + +/// @brief testrunner task namespace +namespace testrunner { + +/** + * @brief testrunner class + */ +class Task : public testapprtos::TaskSetup, public TaskInterface { +public: + /** + * @brief Constructor + * @param[in] taskConfiguration Task configuration + */ + explicit Task(const testapprtos::TaskConfiguration& taskConfiguration); + + /** + * @brief Run function + */ + void run() override; + + /// @copydoc Task::initializeTask() + void initialize() override { + initializeTask(); + } + + /// @copydoc TaskInterface::getTestRunnerToTPCMessageBufferReference + osextension::MessageBufferReference getTestRunnerToTPCMessageBufferReference() const override; + + /// @copydoc TaskInterface::getTPCToTestRunnerMessageBufferReference + osextension::MessageBufferReference getTPCToTestRunnerMessageBufferReference() const override; + +private: + Task() = delete; /**< Remove empty (default) constructor */ + Task(const Task&) = delete; /**< Remove copy Constructor */ + Task(const Task&&) = delete; /**< Remove assign Constructor */ + const Task& operator=(const Task&) = delete; /**< Remove assign Operation @return NULL */ + const Task& operator=(Task&&) = delete; /**< Remove move Operation @return NULL */ + + /// Size of message buffer in bytes for incoming message bus messages + static constexpr size_t IncomingMessageBufferSize = sizeof(message::MessageStructure) + 5u; //265 bytes + + /// Maximum number of messages in message buffer for incoming message bus messages + static constexpr size_t IncomingMessageBufferMaximumNumberOfMessages = 10u; + + /// Size of interrupt queue (= number of buttons) + static constexpr size_t sizeOfInterruptQueue = 1u; + + /** + * @brief Length of QueueSet inside TaskWakeUp object (number of events which has the QueueSet to handle at all) + * @note Must be set to the sum of the lengths of the queues added to the set, where binary semaphores + * and mutexes have a length of 1, and counting semaphores have a length set by their maximum count value + */ + static constexpr auto numberOfEventsInQueueSet = IncomingMessageBufferMaximumNumberOfMessages + sizeOfInterruptQueue + testapprtos::maximumNumberOfEvents; + + /// Message buffer for incoming messages from TestEnv + osextension::MessageBuffer _testEnvironmentToTestRunnerMessageBuffer{}; + + /// Message buffer for incoming messages from TPC + osextension::MessageBuffer _tpcToTestRunnerMessageBuffer{}; + + /// Outgoing message buffer to the TestEnv + osextension::MessageBuffer _testRunnerToTestEnvironmentMessageBuffer{}; + + /// Outgoing message buffer to the TPC + osextension::MessageBuffer _testRunnerToTPCMessageBuffer{}; + + /// Handle type of FreeRTOS + using HandleType = void*; + + /// FreeRTOS queue set struct + StaticQueue_t _staticQueue{}; + + /// Queue set storage area + std::array _queueSetStorage = {0u}; + + /// TestRunner Instance + TestRunner _testRunner{}; +}; + +} // namespace testrunner + +#endif // TESTRUNNER_TASK_HPP diff --git a/TestApp/include/TestRunner/TestRunner/TaskInterface.hpp b/TestApp/include/TestRunner/TestRunner/TaskInterface.hpp new file mode 100644 index 0000000..fe77f6b --- /dev/null +++ b/TestApp/include/TestRunner/TestRunner/TaskInterface.hpp @@ -0,0 +1,69 @@ +/** + * @file include/TestRunner/TestRunner/TaskInterface.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef TESTRUNNER_TASKINTERFACE_HPP +#define TESTRUNNER_TASKINTERFACE_HPP + +#include +#include + +/// @brief testrunner task namespace +namespace testrunner { + +/** + * @brief: Task Interface + */ +class TaskInterface { +public: + /// Deleted copy constructor + TaskInterface(const TaskInterface&) = delete; + + /// Deleted move constructor + TaskInterface(const TaskInterface&&) = delete; + + /// Deleted copy assignment operator + const TaskInterface& operator=(const TaskInterface&) = delete; + + /// Deleted move assignment operator + const TaskInterface& operator=(TaskInterface&&) = delete; + + /** + * @brief Get reference to _tpcToTestRunnerMessageBuffer + * @return MessageBufferReference to _tpcToTestRunnerMessageBuffer + */ + virtual osextension::MessageBufferReference getTPCToTestRunnerMessageBufferReference() const = 0; + + /** + * @brief Get reference to _testRunnerToTPCMessageBuffer + * @return MessageBufferReference to _testRunnerToTPCMessageBuffer + */ + virtual osextension::MessageBufferReference getTestRunnerToTPCMessageBufferReference() const = 0; + + /// Initialize + virtual void initialize() = 0; + +protected: + /// Default constructor + TaskInterface() = default; + + /// Destructor + virtual ~TaskInterface() = default; +}; + +/// Global interface to task +extern TaskInterface& taskInterface; + +} // namespace testrunner + +#endif // TESTRUNNER_TASKINTERFACE_HPP diff --git a/TestApp/include/TestRunner/TestRunner/TestRunner.hpp b/TestApp/include/TestRunner/TestRunner/TestRunner.hpp new file mode 100644 index 0000000..73c0ca2 --- /dev/null +++ b/TestApp/include/TestRunner/TestRunner/TestRunner.hpp @@ -0,0 +1,113 @@ +/** + * @file include/TestRunner/TestRunner/TestRunner.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTRUNNER_TESTRUNNER_HPP +#define TESTRUNNER_TESTRUNNER_HPP + +#include +#include +#include + +namespace testrunner { + +class TestRunner { +public: + /// Constructor. + TestRunner() = default; + + /// Default destructor + ~TestRunner() = default; + + /// Copy constructor. + TestRunner(TestRunner const&) = delete; + + /// Move constructor. + TestRunner(TestRunner&&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + TestRunner& operator=(TestRunner const&) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + TestRunner& operator=(TestRunner&&) = delete; + + /** + * @brief This is the callback method which handles the data received from ITF to process the command to execute TestSuite and Testcase + * + * @return testapp::ErrCode + */ + testapp::ErrCode processTestCmd(const message::MessageStructure &command, message::TestExecutionMessage& dataSentToTestEnv); + + /** + * @brief Method to send TestReport to ITF using TPC data sharing mechanism + * + * @return testapp::ErrCode + */ + void sendReport(message::MessageStructure &report, const message::MessageStructure &dataReceivedFromTPC); + + /** + * @brief one cycle Method to be run by TestRuner task + */ + void oneCycle(QueueSetMemberHandle_t& queueSetMemberHandle); + + /** + * @brief set _testRunnerToTPCMessageBuffer Reference + */ + void setTestRunnerToTPCMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _tpcToTestRunnerMessageBuffer Reference + */ + void setTPCToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _testRunnerToTestEnvironmentMessageBuffer Reference + */ + void setTestRunnerToTestEnvironmentMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set _testEnvironmentToTestRunnerMessageBuffer Reference + */ + void setTestEnvironmentToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf); + + /** + * @brief set testRunner queue set handle + */ + void setTestRunnerQueueSetHandle(QueueSetHandle_t queueSetHandle); + + /** + * @brief get testRunner queue set handle + */ + QueueSetHandle_t getTestRunnerQueueSetHandle() const; + +private: + /// Message Buffer reference for incoming messages from TestEnv + osextension::MessageBufferReference* _testEnvironmentToTestRunnerMessageBufferReference = nullptr; + + /// Message Buffer reference for incoming messages from TPC + osextension::MessageBufferReference* _tpcToTestRunnerMessageBufferReference = nullptr; + + /// Outgoing Message Buffer reference to the TestEnv + osextension::MessageBufferReference* _testRunnerToTestEnvironmentMessageBufferReference = nullptr; + + /// Outgoing Message Buffer reference to the TPC + osextension::MessageBufferReference* _testRunnerToTPCMessageBufferReference = nullptr; + + /// FreeRTOS queue set handle + QueueSetHandle_t _queueSetHandle = nullptr; +}; + +} // namespace testrunner + +#endif // TESTRUNNER_TESTRUNNER_HPP diff --git a/TestApp/include/TestSuite/TestSuite/TestSuiteBase.hpp b/TestApp/include/TestSuite/TestSuite/TestSuiteBase.hpp new file mode 100644 index 0000000..418efb6 --- /dev/null +++ b/TestApp/include/TestSuite/TestSuite/TestSuiteBase.hpp @@ -0,0 +1,63 @@ +/** + * @file TestSuiteBase.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTSUITEBASE_HPP +#define TESTSUITEBASE_HPP + +#include +#include +#include +#include +#include + +/** + * @brief: TestSuiteBase Class, it is a helper class to automatically register any derived TestSuite in + * the TestRegistry + * @tparam NewTestSuiteClass the new TestSuite class + */template +class TestSuiteBase : public TestFactoryBase, + protected TestFactoryRegistrar { +public: + TestSuiteBase() { + assertion.setLogger(logging); + } + testapp::ErrCode returnReportAndReset() { + // A successful TestCase or TestSuite shall have no failure, otherwise a failure report is sent + // Evaluate assertions and logging messages such as errors and critical logs and reset. + testapp::ErrCode errCodeLogger = logging.getFailureFlag() ? testapp::ErrCode::FAILURE : testapp::ErrCode::SUCCESS; + logging.resetFailureFlag(); + + testapp::ErrCode errCodeAssert = (assertion.returnReportAndReset() == testenvironment::ReportResult::ReportSuccess) ? + testapp::ErrCode::SUCCESS : testapp::ErrCode::FAILURE; + + return (errCodeLogger != testapp::ErrCode::SUCCESS || errCodeAssert != testapp::ErrCode::SUCCESS) ? + testapp::ErrCode::FAILURE : testapp::ErrCode::SUCCESS; + } + +}; + +/// Provides a macro for the start of defining a TestSuite +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] we want to keep googletest style +#define DECLARE_TESTSUITE(NEW_TESTSUITE_CLASS, ID) \ +class NEW_TESTSUITE_CLASS : public testsuite::TestSuiteInterface, public TestSuiteBase { /*NOLINT [bugprone-macro-parentheses]*/ \ +public: \ + NEW_TESTSUITE_CLASS() {(void)registered;} \ + testcase::TestCaseInterface* createTestCase(testapp::TestIDType testCaseID, void *memoryLocation, size_t memorySize) final { \ + return(TestSuiteBase::createObject(testCaseID, memoryLocation, memorySize));\ + } + +/// Provides a macro for the end of defining a TestSuite +//NOLINTNEXTLINE [cppcoreguidelines-macro-usage] we want to keep googletest style +#define END_TESTSUITE(NEW_TESTSUITE_CLASS) }; + +#endif diff --git a/TestApp/include/TestSuite/TestSuite/TestSuiteInterface.hpp b/TestApp/include/TestSuite/TestSuite/TestSuiteInterface.hpp new file mode 100644 index 0000000..4cf8361 --- /dev/null +++ b/TestApp/include/TestSuite/TestSuite/TestSuiteInterface.hpp @@ -0,0 +1,70 @@ +/** + * @file TestSuiteAbstract.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#ifndef TESTSUITEABSTRACT_HPP +#define TESTSUITEABSTRACT_HPP + +#include +#include +#include + +namespace testsuite { +/** + * @brief The interface for a test suite + */ +class TestSuiteInterface { +public: + /// Constructor. + TestSuiteInterface(void) = default; + + /// Copy constructor. + TestSuiteInterface(TestSuiteInterface const&) = delete; + + /// Move constructor. + TestSuiteInterface(TestSuiteInterface&&) = delete; + + /// @brief Copy assignment operator. + /// @return result of copy assignment + TestSuiteInterface& operator=(TestSuiteInterface const&) = delete; + + /// @brief Move assignment operator. + /// @return result of move assignment + TestSuiteInterface& operator=(TestSuiteInterface&&) = delete; + + /// Default destructor. + virtual ~TestSuiteInterface(void) = default; + + /** + * @brief Prepares environment for testcase execution + * + * @return testapp::ErrCode + */ + virtual testapp::ErrCode doSuiteSetup() = 0; + + /** + * @brief Method to implement the testsuite teardown and reset the environment to load the new TestSuite + * + * @return testapp::ErrCode + */ + virtual testapp::ErrCode doSuiteTearDown() = 0; + + /** + * @brief Method to instantiate a possible registered test case in this suit + * + * @return TestCaseInterface* + */ virtual testcase::TestCaseInterface* createTestCase(testapp::TestIDType testCaseID, void *memoryLocation, size_t memorySize) = 0; + +}; + +} // namespace testsuite +#endif diff --git a/TestApp/mock/CMakeLists.txt b/TestApp/mock/CMakeLists.txt new file mode 100644 index 0000000..570d311 --- /dev/null +++ b/TestApp/mock/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(MessageBufferMock) +add_subdirectory(SemaphoreMock) +add_subdirectory(CommunicationChannelMock) diff --git a/TestApp/mock/CommunicationChannelMock/CMakeLists.txt b/TestApp/mock/CommunicationChannelMock/CMakeLists.txt new file mode 100644 index 0000000..e724eaa --- /dev/null +++ b/TestApp/mock/CommunicationChannelMock/CMakeLists.txt @@ -0,0 +1,18 @@ +set(MODULE_NAME "CommunicationChannel-Mock") + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/CommunicationChannelMock.cpp + ) + +target_include_directories(${MODULE_NAME} + PUBLIC + ${PROJECT_SOURCE_DIR}/TestApp/mock/CommunicationChannelMock + # ${CMAKE_SOURCE_DIR}/Platform/RequiredInterface/CommunicationChannelInterface + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + gmock + CommunicationChannel-Interface + OsExtension + ) diff --git a/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock.cpp b/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock.cpp new file mode 100644 index 0000000..472584a --- /dev/null +++ b/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock.cpp @@ -0,0 +1,24 @@ +/** + * @file FDXModuleMock.cpp + * @author Abirami Sridharan + * + * @brief See *.hpp for more details + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace tccinterface +{ + TccInterfaceMock::TccInterfaceMock() {} + TccInterfaceMock::~TccInterfaceMock() {} +} // namespace tccinterface diff --git a/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock/CommunicationChannelMock.hpp b/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock/CommunicationChannelMock.hpp new file mode 100644 index 0000000..cd7932c --- /dev/null +++ b/TestApp/mock/CommunicationChannelMock/CommunicationChannelMock/CommunicationChannelMock.hpp @@ -0,0 +1,41 @@ +/** + * @file mock/FDXModuleMock/FDXModuleMock.hpp + * + * @copyright Copyright 2020 Robert Bosch GmbH. All rights reserved. + * + * This source code is copyright protected and proprietary + * to Robert Bosch GmbH. Only those rights that have been + * explicallRunnersIteratorly granted to you by Robert Bosch GmbH in wrallRunnersIteratorten + * form may be exercised. All other rights remain wallRunnersIteratorh + * Robert Bosch GmbH. + */ + +#ifndef FDXMODULEMOCK_HPP +#define FDXMODULEMOCK_HPP + +#include +#include + +#include +#include + +namespace tccinterface +{ + class TccInterfaceMock : public TccInterface + { + public: + TccInterfaceMock(); + virtual ~TccInterfaceMock(); + + MOCK_METHOD(ErrCode, init, (), (override)); + MOCK_METHOD(ErrCode, send, (const void * sendbuffer, size_t len), (override)); + MOCK_METHOD(ErrCode, send, (const etl::array_view& datastream), (override)); + MOCK_METHOD(ErrCode, receive, (size_t * length, void *buf, size_t maxsize), (override)); + MOCK_METHOD(ErrCode, receive, (size_t * length, etl::array_view& bufView), (override)); + MOCK_METHOD(OperationMode, getOperationMode, (), (override, const)); + MOCK_METHOD(void, registerSemaphoreReference, (osextension::SemaphoreReference& TPCSemaphore)); + MOCK_METHOD(TickType_t, registerPollingPeriod, (), (const)); + MOCK_METHOD(ErrCode, shutdown, (), (override)); + }; +} // namespace tccinterface +#endif diff --git a/TestApp/mock/MessageBufferMock/CMakeLists.txt b/TestApp/mock/MessageBufferMock/CMakeLists.txt new file mode 100644 index 0000000..465530e --- /dev/null +++ b/TestApp/mock/MessageBufferMock/CMakeLists.txt @@ -0,0 +1,16 @@ +set(MODULE_NAME "MessageBufferReference-Mock") + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/MessageBufferReferenceMock.cpp + ) + +target_include_directories(${MODULE_NAME} + PUBLIC + ${PROJECT_SOURCE_DIR}/TestApp/mock/MessageBufferMock + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + OsExtension-Interface + gmock + ) diff --git a/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock.cpp b/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock.cpp new file mode 100644 index 0000000..654721b --- /dev/null +++ b/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock.cpp @@ -0,0 +1,21 @@ +/** + * @file mock/MessageBufferMock/MessageBufferReferenceMock.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace osextension { + +MessageBufferReferenceMock::MessageBufferReferenceMock(void) {} +MessageBufferReferenceMock::~MessageBufferReferenceMock(void) {} +} // namespace osextension diff --git a/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock/MessageBufferReferenceMock.hpp b/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock/MessageBufferReferenceMock.hpp new file mode 100644 index 0000000..2fa6675 --- /dev/null +++ b/TestApp/mock/MessageBufferMock/MessageBufferReferenceMock/MessageBufferReferenceMock.hpp @@ -0,0 +1,48 @@ +/** + * @file mock/MessageBufferMock/MessageBufferReferenceMock/MessageBufferReferenceMock.hpp + * + * @copyright Copyright 2020 Robert Bosch GmbH. All rights reserved. + * + * This source code is copyright protected and proprietary + * to Robert Bosch GmbH. Only those rights that have been + * explicallRunnersIteratorly granted to you by Robert Bosch GmbH in wrallRunnersIteratorten + * form may be exercised. All other rights remain wallRunnersIteratorh + * Robert Bosch GmbH. + */ + +#ifndef OSEXTENSION_MESSAGEBUFFERREFERENCEMOCK_HPP +#define OSEXTENSION_MESSAGEBUFFERREFERENCEMOCK_HPP + +#include + +#include +#include + +namespace osextension { + +class MessageBufferReferenceMock : public MessageBufferReference { +public: + MessageBufferReferenceMock(void); + virtual ~MessageBufferReferenceMock(); + + MOCK_METHOD(size_t, send, (const void*, size_t), (override)); + MOCK_METHOD(size_t, send, (const void*, size_t, TickType_t), (override)); + MOCK_METHOD(size_t, send, (etl::array_view), (override)); + MOCK_METHOD(size_t, send, (etl::array_view, TickType_t), (override)); + MOCK_METHOD(size_t, sendFromISR, (const void*, size_t, BaseType_t*), (override)); + MOCK_METHOD(size_t, sendFromISR, (etl::array_view, BaseType_t*), (override)); + MOCK_METHOD(size_t, receive, (void*, size_t, TickType_t), (override)); + MOCK_METHOD(size_t, receive, (etl::array_view, TickType_t), (override)); + MOCK_METHOD(size_t, receiveFromISR, (void*, size_t, BaseType_t*), (override)); + MOCK_METHOD(size_t, receiveFromISR, (etl::array_view data, BaseType_t*), (override)); + MOCK_METHOD(bool, reset, (), (override)); + MOCK_METHOD(bool, isEmpty, (), (override)); + MOCK_METHOD(bool, isFull, (), (override)); + MOCK_METHOD(size_t, numMessagesAvailable, (), (override)); + MOCK_METHOD(size_t, numBytesAvailable, (), (override)); + MOCK_METHOD(OsExtensionHandleType, getHandleForQueueSet, (), (const override)); + }; + +} // namespace osextension + +#endif /* OSEXTENSION_MESSAGEBUFFERREFERENCEMOCK_HPP */ diff --git a/TestApp/mock/SemaphoreMock/CMakeLists.txt b/TestApp/mock/SemaphoreMock/CMakeLists.txt new file mode 100644 index 0000000..a5a25de --- /dev/null +++ b/TestApp/mock/SemaphoreMock/CMakeLists.txt @@ -0,0 +1,16 @@ +set(MODULE_NAME "SemaphoreMockMock") + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/SemaphoreMock.cpp + ) + +target_include_directories(${MODULE_NAME} + PUBLIC + ${PROJECT_SOURCE_DIR}/TestApp/mock/SemaphoreMock + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + OsExtension-Interface + gmock + ) diff --git a/TestApp/mock/SemaphoreMock/SemaphoreMock.cpp b/TestApp/mock/SemaphoreMock/SemaphoreMock.cpp new file mode 100644 index 0000000..7b0295a --- /dev/null +++ b/TestApp/mock/SemaphoreMock/SemaphoreMock.cpp @@ -0,0 +1,24 @@ +/** + * @file mock/SemaphoreMock/SemaphoreMock.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace osextension { + +// See +// https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#making-the-compilation-faster +SemaphoreMock::SemaphoreMock() {} +SemaphoreMock::~SemaphoreMock() {} + +} // namespace osextension \ No newline at end of file diff --git a/TestApp/mock/SemaphoreMock/SemaphoreMock/SemaphoreMock.hpp b/TestApp/mock/SemaphoreMock/SemaphoreMock/SemaphoreMock.hpp new file mode 100644 index 0000000..eab8983 --- /dev/null +++ b/TestApp/mock/SemaphoreMock/SemaphoreMock/SemaphoreMock.hpp @@ -0,0 +1,44 @@ +/** + * @file mock/SemaphoreMock/SemaphoreMock.hpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#ifndef OSEXTENSION_SEMAPHOREMOCK_HPP +#define OSEXTENSION_SEMAPHOREMOCK_HPP + +#include +#include + +#include + +#include + +namespace osextension { + +class SemaphoreMock : public SemaphoreInterface { +public: + SemaphoreMock(); + virtual ~SemaphoreMock(); + + MOCK_METHOD(bool, signal, (), (override)); + MOCK_METHOD(bool, signalFromIsr, (SemaphoreTaskWokenTokenType*), (override)); + MOCK_METHOD(bool, wait, (TickType_t), (override)); + MOCK_METHOD(bool, waitFromIsr, (SemaphoreTaskWokenTokenType*), (override)); + MOCK_METHOD(bool, reset, (), (override)); + MOCK_METHOD(size_t, getCount, (), (override)); + MOCK_METHOD(size_t, getCountFromIsr, (), (override)); + MOCK_METHOD(OsExtensionHandleType, getHandleForQueueSet, (), (const override)); +}; + +} // namespace osextension + +#endif /* OSEXTENSION_SEMAPHOREMOCK_HPP */ \ No newline at end of file diff --git a/TestApp/src/MessageHandler/MessageHandler.cpp b/TestApp/src/MessageHandler/MessageHandler.cpp new file mode 100644 index 0000000..125b927 --- /dev/null +++ b/TestApp/src/MessageHandler/MessageHandler.cpp @@ -0,0 +1,143 @@ +/** + * @file src/MessageHandler/MessageHandler.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include "MessageHandler/MessageHandler.hpp" +#include +#include + +namespace message +{ + /*Method to deserialize bytearray to message struct*/ + testapp::RetCode deserialize(const etl::array_view& bytestream, MessageStructure &msg) + { + // msg.headerInfo.protocolVersion = ((bytestream[0] & 0xC0) >> 6); + // msg.headerInfo.res = (bytestream[0] & 0x0F); + const uint8_t messageTypeMask = 0x30U; + const uint8_t messageTypeBitOffset = 4U; + + std::memcpy(&msg.headerInfo, &bytestream.at(headerInfoIndex), sizeof(msg.headerInfo)); + msg.messageType = static_cast((bytestream.at(headerInfoIndex) & messageTypeMask) >> messageTypeBitOffset); + msg.token = bytestream.at(tokenIndex); + + msg.subType = static_cast(bytestream.at(subTypeIndex)); + switch (msg.messageType) + { + case message::MessageType::Command: + if( std::find(msgCommands.begin(), msgCommands.end(), msg.subType) == msgCommands.end() ) + { + return testapp::RetCode::RETCODE_INVALID_PARAM; + } + break; + case message::MessageType::Report: + if( std::find(msgReports.begin(), msgReports.end(), msg.subType) == msgReports.end() ) + { + return testapp::RetCode::RETCODE_INVALID_PARAM; + } + break; + case message::MessageType::Acknowledgement: + if( std::find(msgAcknowledgements.begin(), msgAcknowledgements.end(), msg.subType) == msgAcknowledgements.end() ) + { + return testapp::RetCode::RETCODE_INVALID_PARAM; + } + break; + default: + return testapp::RetCode::RETCODE_INVALID_PARAM; + break; + } + + msg.errorCode = static_cast(bytestream.at(errorCodeIndex)); + msg.testSection = bytestream.at(testSectionIndex); + msg.testSuite = bytestream.at(testSuiteIndex); + msg.testCase = bytestream.at(testCaseIndex); + msg.payloadLength = bytestream.at(payloadLengthIndex); + std::memcpy(&msg.payload, &bytestream.at(payloadIndex), msg.payloadLength); + + return testapp::RetCode::RETCODE_SUCCESS; + } + + /*Method to serialize messagestruct to bytearray*/ + testapp::RetCode serialize(const MessageStructure &msg, etl::array_view& bytestream) + { + std::memcpy(&bytestream.at(headerInfoIndex), &msg.headerInfo, sizeof(msg.headerInfo)); + bytestream.at(tokenIndex) = msg.token; + bytestream.at(subTypeIndex) = static_cast(msg.subType); + bytestream.at(errorCodeIndex) = static_cast(msg.errorCode); + bytestream.at(testSectionIndex) = msg.testSection; + bytestream.at(testSuiteIndex) = msg.testSuite; + bytestream.at(testCaseIndex) = msg.testCase; + bytestream.at(payloadLengthIndex) = msg.payloadLength; + + std::memcpy(&bytestream.at(payloadIndex), &msg.payload, msg.payloadLength); + + return testapp::RetCode::RETCODE_SUCCESS; + } + + testapp::RetCode parseTLV(MessageStructure &msg, TLV &nextTlv) + { + static uint8_t nextTLVPlace = 0; + + // Find the next TLV element + nextTlv.tag = msg.payload[nextTLVPlace]; + nextTlv.length = msg.payload[nextTLVPlace + 1]; + nextTlv.value = &msg.payload[nextTLVPlace + 2]; + + if (nextTLVPlace + (tlvHeaderSize + nextTlv.length) > msg.payloadLength) + { + // Somebody tried to escape its scope + nextTlv.tag = nextTlv.length = 0; + nextTlv.value = nullptr; + } + // Allocate the right nextTLVPlace + nextTLVPlace += tlvHeaderSize + nextTlv.length; + + // Check validity of the nextTLVPlace + if (nextTLVPlace >= msg.payloadLength) + { + nextTLVPlace = 0; + } + + // Return the right error + if (nextTLVPlace == 0) + { + return testapp::RetCode::RETCODE_LASTELEMENT; + } + else + { + return testapp::RetCode::RETCODE_SUCCESS; + } + } + + /*Add tlv element to the message structure*/ + testapp::RetCode addTLV(const TLV &nextTlv, MessageStructure &msg) + { + if (nextTlv.length > messageSize) + { + return testapp::RetCode::RETCODE_FAILURE; + } + + if (nextTlv.length + tlvHeaderSize + msg.payloadLength > maxPayloadSize) + { + return testapp::RetCode::RETCODE_FAILURE; + } + + msg.payload[msg.payloadLength] = nextTlv.tag; + msg.payload[msg.payloadLength + 1] = nextTlv.length; + + memcpy(&msg.payload[msg.payloadLength + tlvHeaderSize], nextTlv.value, nextTlv.length); + + msg.payloadLength = msg.payloadLength + nextTlv.length + tlvHeaderSize; + + return testapp::RetCode::RETCODE_SUCCESS; + } +} // namespace message diff --git a/TestApp/src/TPC/TPC.cpp b/TestApp/src/TPC/TPC.cpp new file mode 100644 index 0000000..45c3726 --- /dev/null +++ b/TestApp/src/TPC/TPC.cpp @@ -0,0 +1,248 @@ +/** + * @file src/TPC/TPC/TPC.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#include "TPC/TPC.hpp" +#include "TPC/TaskInterface.hpp" +#include +#include +#include + +constexpr uint8_t crcDefault = UINT8_C(0); +constexpr uint16_t crcBitShift4 = UINT16_C(4); /** 4 bit shifting for CRC calculation */ +constexpr uint16_t crcBitShift5 = UINT16_C(5); /** 5 bit shifting for CRC calculation */ +constexpr uint16_t crcBitShift8 = UINT16_C(8); /** 8 bit shifting for CRC calculation */ +constexpr uint16_t crcBitShift12 = UINT16_C(12); /** 12 bit shifting for CRC calculation */ +constexpr uint16_t crcByteMask = UINT16_C(0xFF); +constexpr uint8_t ackNackMessageLength = 10U; +constexpr uint8_t crcSize = 2U; +constexpr uint16_t tpcBufferSize = 256U; + +std::array buf{}; +etl::array_view bufView{buf}; + +namespace tpcprotocol +{ + static bool receiveNextMessage = true; + + /// set tPC message Buffer references outgoing + void TPC::setTPCToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf) { + _tpcToTestRunnerMessageBuffer = &mbf; + } + + /// set tPC message Buffer references incoming + void TPC::setTestRunnerTPCMessageBufferReference(osextension::MessageBufferReference& mbf) { + _testRunnerToTPCMessageBuffer = &mbf; + } + + void TPC::oneCycle() { + // initialization of the global variable storing the received data + message::Message dataReceivedFromITF{}; + message::Message dataReceivedFromTestRunner{}; + size_t receivedBytesFromTestRunner = 0u; + size_t receivedBytesFromITF = 0; + + if (tccinterface::tccInterface != nullptr) { + tccinterface::tccInterface->receive(&receivedBytesFromITF, bufView); + } + + if (receivedBytesFromITF > 0) { + // Resize the buffer view (Does not copy the elements in the range). + bufView.assign(bufView.begin(), receivedBytesFromITF); + + // check if the message is valid + if (validateITFByteStream(bufView, receivedBytesFromITF) && bufView.size() == receivedBytesFromITF) { + + // Deserialize ITF bytestream to a MessageStructure for further processing + if (testapp::RetCode::RETCODE_SUCCESS == message::deserialize(bufView, dataReceivedFromITF)) { + // ITF has sent a command to be processed further by TestRunner and TestEnvironment + if ((dataReceivedFromITF.messageType != message::MessageType::Acknowledgement) && receiveNextMessage) { + + // Send an Acknowledgement back to ITF + sendACKorNACK(message::MessageSubType::ACKNOWLEDGEMENT, dataReceivedFromITF.token); + + // Process ITF command + if (dataReceivedFromITF.subType != message::MessageSubType::PING) { + // Send data to TestRunner to be processed + _tpcToTestRunnerMessageBuffer->send(&dataReceivedFromITF, sizeof(dataReceivedFromITF)); + + // Receive Log/report messages from TestRunner after proccess + receivedBytesFromTestRunner = _testRunnerToTPCMessageBuffer->receive(&dataReceivedFromTestRunner, sizeof(dataReceivedFromTestRunner), portMAX_DELAY); + + // send the Log/report message to ITF + if (receivedBytesFromTestRunner > 0){ + sendReportToITF(dataReceivedFromTestRunner, message::messageHeaderSize + dataReceivedFromTestRunner.payloadLength); + } + + // In case of empty TestCase/TestSuite + receiveNextMessage = dataReceivedFromTestRunner.messageType == message::MessageType::Report ? true : false; + } + } + // The last command received from ITF is still in progress, and ITF sends Acknowledgement for the previously received Log or report + else if (dataReceivedFromITF.messageType == message::MessageType::Acknowledgement && !receiveNextMessage) { + + // wait for Log messages or final report message. + receivedBytesFromTestRunner = _testRunnerToTPCMessageBuffer->receive(&dataReceivedFromTestRunner, sizeof(dataReceivedFromTestRunner), portMAX_DELAY); + + // send the Log/report message to ITF + if (receivedBytesFromTestRunner > 0) { + sendReportToITF(dataReceivedFromTestRunner, message::messageHeaderSize + dataReceivedFromTestRunner.payloadLength); + + // when the final report is received, TPC sends the report to ITF and shall process the next command (command message type) coming from ITF. + if (dataReceivedFromTestRunner.messageType == message::MessageType::Report){ + receiveNextMessage = true; + } + } + } + else { + // > when the report has been sent to ITF, further Acknowledgement messages from ITF will be ignored. + // > Receive and process next message. + } + } + } + else { + // invalid message from ITF; send back Nack. + sendACKorNACK(message::MessageSubType::NACK, bufView.at(message::tokenIndex)); + } + } + } + + /** + * @brief ValidateITFByteStream Method to validate CRC for the bytestream + * Compares the received and calculatedCRC values + * returns a boolen if it is valid|invalid + */ + bool TPC::validateITFByteStream(const etl::array_view& bytestream, uint8_t length) + { + bool ret = false; + const uint8_t metadataLength = 10U; // 8 header info + 2 crc + + if (length >= metadataLength) + { + //possibility to receive multiple messages on RTT segger channel on receive api call + //Fix to process only the first message received and discard other + if(length > (bytestream.at(message::payloadLengthIndex) + metadataLength)){ + length = bytestream.at(message::payloadLengthIndex) + metadataLength; + } + + uint16_t receivedCRC = 0; + receivedCRC = bytestream.at(length - 1); + receivedCRC <<= crcBitShift8; + receivedCRC += bytestream.at(length - 2); + + /*calculate the crc for the bytestream based on its length*/ + uint16_t calculatedCRC = 0; + //etl::array_view bytestreamView{reinterpret_cast(&message), sizeof(message)}; + calculatedCRC = calculateCRC(bytestream, length - crcSize); + + /*check if receivedcrc = calcCRC*/ + if (receivedCRC == calculatedCRC){ + ret = true; + } + else{ + ret = false; + } + } + return (ret); + } + + /** + * @brief:To send AC|NACK to ITF + * > construct an ack message + * > add the crc to the bytearray + * > send via fdx/RTT to ITF + */ + testapp::RetCode TPC::sendACKorNACK(message::MessageSubType subType, uint8_t tokenID) + { + std::array byteArray = {0u}; + etl::array_view byteArrayView{byteArray}; + //construct ACK|NACKmsg + + message::MessageStructure ackMessage; + ackMessage.headerInfo.protocolVersion = 1; + ackMessage.headerInfo.messageType = message::MessageType::Acknowledgement; + ackMessage.token = tokenID; + ackMessage.subType = subType; + + /*Serialize the struct to bytearray to send it to ITF + Calculate crc and add crc*/ + auto reportSerialize = message::serialize(ackMessage, byteArrayView); + if(reportSerialize != testapp::RetCode::RETCODE_SUCCESS){ + return testapp::RetCode::RETCODE_FAILURE; + } + + auto bareMessageLength = ackNackMessageLength - crcSize; + auto crcAck = calculateCRC(byteArrayView, bareMessageLength); + + //AddCRC to the bytearray + byteArrayView.at(bareMessageLength) = static_cast(crcAck & crcByteMask); + byteArrayView.at(bareMessageLength + 1) = static_cast((crcAck >> crcBitShift8) & crcByteMask); + + //callfdx sendapi() with bytearray pointer and length + if(tccinterface::tccInterface != nullptr){ + tccinterface::tccInterface->send(byteArrayView); + } + + return testapp::RetCode::RETCODE_SUCCESS; + } + + /** + * @brief : Serializes the messagestruct to bytearray + adds CRC to the bytearray + Calls the fdxsend method to send the report + * + */ + testapp::RetCode TPC::sendReportToITF(const message::MessageStructure &reportMsg, uint8_t reportLength) + { + std::array byteArrayReport = {0u}; + etl::array_view reportView{byteArrayReport.begin(), reportLength + crcSize}; + + auto reportSerialize = message::serialize(reportMsg, reportView); + if(reportSerialize != testapp::RetCode::RETCODE_SUCCESS){ + return testapp::RetCode::RETCODE_FAILURE; + } + + auto crcReport = calculateCRC(reportView, reportLength); + //send byteArray to ITF + //add crc to the reportmsg + reportView.at(reportLength) = static_cast(crcReport & crcByteMask); + reportView.at(reportLength + 1) = static_cast((crcReport >> crcBitShift8) & crcByteMask); + + //callfdx sendapi() with bytearray pointer and length + if(tccinterface::tccInterface != nullptr){ + tccinterface::tccInterface->send(reportView); + } + //send byteArray to kiso host + + return testapp::RetCode::RETCODE_SUCCESS; + } + + /** + * @brief : Method to calculateCRC + * + */ + uint16_t TPC::calculateCRC(const etl::array_view& buffer, uint8_t length) + { + uint16_t crc = crcDefault; + + for (uint8_t i = 0; i < length; i++) + { + crc = (crc >> (crcBitShift8)) | (crc << (crcBitShift8)); + crc ^= buffer.at(i); + crc ^= (crc & (crcByteMask)) >> (crcBitShift4); + crc ^= crc << (crcBitShift12); + crc ^= (crc & (crcByteMask)) << (crcBitShift5); + } + return crc; + } + +} // namespace tpcprotocol diff --git a/TestApp/src/TPC/Task.cpp b/TestApp/src/TPC/Task.cpp new file mode 100644 index 0000000..d14bd4e --- /dev/null +++ b/TestApp/src/TPC/Task.cpp @@ -0,0 +1,76 @@ +/** + * @file src/TPC/Task.cpp + * @brief TPC Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include +#include +#include +namespace tpcprotocol +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[taskStackSize]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "tpcprotocol", + 4u}; + + /// Creation of global task object + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) + { + // register callbacks + _tpc.setTPCToTestRunnerMessageBufferReference(_TPCToTestRunnerMessageBuffer); + _tpc.setTestRunnerTPCMessageBufferReference(_TestRunnerToTPCMessageBuffer); + } + + /** + * @brief run function + */ + void Task::run() + { + // Insert the message buffer reference to TestCoordinator + _TestRunnerToTPCMessageBuffer = testrunner::taskInterface.getTestRunnerToTPCMessageBufferReference(); + + // Register the message buffer reference of the TPCoordinator to TestRunner task + _TPCToTestRunnerMessageBuffer = testrunner::taskInterface.getTPCToTestRunnerMessageBufferReference(); + + if (tccinterface::OperationMode::INTERRUPT_MODE == tccinterface::tccInterface->getOperationMode()) + // INTERRUPT_MODE : pass reference to TPC semaphore to TCCInterface implementation + tccinterface::tccInterface->registerSemaphoreReference(_TCCInterfacerReferencePairAdded); + else + // POLLING_MODE : get the polling timeout from the corresponding communication channel + _blockingTime = tccinterface::tccInterface->registerPollingPeriod(); + + tccinterface::tccInterface->init(); + + while (1) + { + if (tccinterface::OperationMode::INTERRUPT_MODE == tccinterface::tccInterface->getOperationMode()) + _TCCInterfacerReferencePairAdded.wait(portMAX_DELAY); + else + vTaskDelay(_blockingTime); + + _tpc.oneCycle(); + } + } + +} // tpcprotocol diff --git a/TestApp/src/TestEnvironment/Assert.cpp b/TestApp/src/TestEnvironment/Assert.cpp new file mode 100644 index 0000000..79eb523 --- /dev/null +++ b/TestApp/src/TestEnvironment/Assert.cpp @@ -0,0 +1,92 @@ +/** + * @file src/TestEnvironment/Assert.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include + +namespace testenvironment { + +Assert& Assert::getInstance(void) { + static Assert instance; + return instance; +} + +testapp::RetCode Assert::assertStrEq(const char* str1, const char* str2, const int line, const char* file) { + // Check if Logger has been set + if (!isLoggerInitialized()) { + _report = ReportResult::ReportFailure; + return testapp::RetCode::RETCODE_UNINITIALIZED; + } + + // Check if input strings are valid + if (nullptr == str1 || nullptr == str2) { + _logger->logError("assert_streq: Input string invalid"); + _report = ReportResult::ReportFailure; + return testapp::RetCode::RETCODE_NULL_POINTER; + } + + testapp::RetCode retCode = (strcmp(str1, str2) == 0) ? testapp::RetCode::RETCODE_SUCCESS : testapp::RetCode::RETCODE_FAILURE; + AssertReport assertReport(AssertTypes::ASSERT_STREQ, str1, str2, retCode, line, file); + if (logAssert(assertReport) != testapp::RetCode::RETCODE_SUCCESS) { + retCode = testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + return retCode; +} + +testapp::RetCode Assert::logAssert(const AssertReport& assertReport) { + testapp::RetCode retCode = testapp::RetCode::RETCODE_SUCCESS; + std::array tmpMessage = {'\0'}; + + // Check if Logger has been set + if (!isLoggerInitialized()) { + _report = ReportResult::ReportFailure; + return testapp::RetCode::RETCODE_UNINITIALIZED; + } + + // Create report and store it + if (assertReport.createReport(tmpMessage.data()) != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + return testapp::RetCode::RETCODE_UNEXPECTED_BEHAVIOR; + } + + // Call log Module only if test detected a failure + if (assertReport.getResult() != testapp::RetCode::RETCODE_SUCCESS) { + retCode = _logger->logError(tmpMessage.data()); + } + + if (retCode != testapp::RetCode::RETCODE_SUCCESS) { + _report = ReportResult::ReportFailure; + } + + return retCode; +} + +testapp::RetCode Assert::setLogger(LoggingInterface& logger) { + _logger = &logger; + return testapp::RetCode::RETCODE_SUCCESS; +} + +ReportResult Assert::returnReportAndReset() { + ReportResult returnCode = _report; + _report = ReportResult::ReportSuccess; + return returnCode; +} + +bool Assert::isLoggerInitialized(void) const { + return (nullptr != _logger) ? true : false; +} + +} // namespace testenvironment diff --git a/TestApp/src/TestEnvironment/AssertReport.cpp b/TestApp/src/TestEnvironment/AssertReport.cpp new file mode 100644 index 0000000..5a78f5d --- /dev/null +++ b/TestApp/src/TestEnvironment/AssertReport.cpp @@ -0,0 +1,162 @@ +/** + * @file src/TestEnvironment/AssertReport.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include + +#include + +namespace testenvironment { + +const char* AssertReport::getFilename(void) const { + return _fileName.data(); +} + +testapp::RetCode AssertReport::getResult(void) const { + return _retCode; +} + +AssertReport::AssertReport(const AssertTypes assertType, const float input1, + const float input2, const testapp::RetCode retCode, + const uint16_t line, const char* fileName) + : _assertType(assertType), + _input1(input1), + _input2(input2), + _inputString1(nullptr), + _inputString2(nullptr), + _retCode(retCode), + _line(line), + _fileName{} { + setFilename(fileName); +} + +AssertReport::AssertReport(const AssertTypes assertType, const char* input1, + const char* input2, const testapp::RetCode retCode, + const uint16_t line, const char* fileName) + : _assertType(assertType), + _input1(0), + _input2(0), + _inputString1(input1), + _inputString2(input2), + _retCode(retCode), + _line(line), + _fileName{} { + setFilename(fileName); +} + +testapp::RetCode AssertReport::createReport(char* report) const { + std::array operatorString = {'\0'}; + std::array tmpReport = {'\0'}; + + switch (_assertType) { + case (AssertTypes::ASSERT_TRUE): + strncpy(operatorString.data(), "ASSERT_TRUE", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_FALSE): + strncpy(operatorString.data(), "ASSERT_FALSE", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_EQ): + strncpy(operatorString.data(), "ASSERT_EQ", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_NE): + strncpy(operatorString.data(), "ASSERT_NE", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_LT): + strncpy(operatorString.data(), "ASSERT_LT", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_LE): + strncpy(operatorString.data(), "ASSERT_LE", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_GT): + strncpy(operatorString.data(), "ASSERT_GT", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_GE): + strncpy(operatorString.data(), "ASSERT_GE", operatorString.max_size()); + break; + case (AssertTypes::ASSERT_STREQ): + strncpy(operatorString.data(), "ASSERT_STREQ", operatorString.max_size()); + break; + default: + return testapp::RetCode::RETCODE_NOT_SUPPORTED; + } + + int16_t snprintfErrorCode = 0; + if (AssertTypes::ASSERT_TRUE == _assertType || AssertTypes::ASSERT_FALSE == _assertType) { + snprintfErrorCode = snprintf( //NOLINT(hicpp-vararg, cppcoreguidelines-pro-type-vararg) The alternative bytestream uses dynamic memory allocation (heap allocation) + tmpReport.data(), maxReportSize, "%s:%d: %s %s input: %s", _fileName.data(), _line, + (testapp::RetCode::RETCODE_SUCCESS == _retCode) ? "Success" : "Failure", + operatorString.data(), (_input1 != 0.0f) ? "true" : "false"); + } else if (AssertTypes::ASSERT_STREQ == _assertType) { + snprintfErrorCode = + snprintf(tmpReport.data(), maxReportSize, //NOLINT(hicpp-vararg, cppcoreguidelines-pro-type-vararg) The alternative bytestream uses dynamic memory allocation (heap allocation) + "%s:%d: %s %s input 1: %s input 2: %s", _fileName.data(), _line, + (testapp::RetCode::RETCODE_SUCCESS == _retCode) ? "Success" : "Failure", + operatorString.data(), _inputString1, _inputString2); + } else { + auto floatToInt = [](const float x) -> std::pair { + int32_t intpart = static_cast(x); + int32_t decpart = static_cast((x - intpart) * 100); + return std::make_pair(intpart, decpart); + }; + auto i1 = floatToInt(static_cast(_input1)); + auto i2 = floatToInt(static_cast(_input2)); + snprintfErrorCode = + snprintf(tmpReport.data(), maxReportSize, //NOLINT(hicpp-vararg, cppcoreguidelines-pro-type-vararg) The alternative bytestream uses dynamic memory allocation (heap allocation) + "%s:%d: %s %s input 1: %d.%02d input 2: %d.%02d", _fileName.data(), _line, + (testapp::RetCode::RETCODE_SUCCESS == _retCode) ? "Success" : "Failure", + operatorString.data(), i1.first,i1.second, + i2.first,i2.second); + } + + if ((snprintfErrorCode < 0) || (snprintfErrorCode >= maxReportSize)) { + strncpy(report, "Report too long", maxReportSize); + return testapp::RetCode::RETCODE_FAILURE; + } + + strncpy(report, tmpReport.data(), maxReportSize); + return testapp::RetCode::RETCODE_SUCCESS; +} + +testapp::RetCode AssertReport::setFilename(const char* fileName) { + uint16_t slashPos = 0; + uint16_t lenFilename = 0; + + for (uint16_t i = 0U; i < maxFilenameSize; i++) { + if ('/' == fileName[i]) { //NOLINT (cppcoreguidelines-pro-bounds-pointer-arithmetic): pointer arithmetic can only be resolved with another container + // Store position of '/' + slashPos = i; + } else if ('\0' == fileName[i]) { //NOLINT (cppcoreguidelines-pro-bounds-pointer-arithmetic): pointer arithmetic can only be resolved with another container + // End Loop when '\0' occurs. lenFilename + 1 because i is 0 indexed + lenFilename = i + 1; + break; + } else { + // Do nothing + } + } + + if (lenFilename > maxFilenameSize || lenFilename == 0) { + strncpy(_fileName.data(), "Filename string not terminated or too long", maxFilenameSize); + return testapp::RetCode::RETCODE_INVALID_PARAM; + } + + // Crop fileName after slash + if (slashPos > 0) { + // If string has a '/'. Copy from '/' to end. + memcpy(_fileName.data(), &fileName[slashPos + 1], lenFilename - (slashPos)); //NOLINT (cppcoreguidelines-pro-bounds-pointer-arithmetic): pointer arithmetic can only be resolved with another container + } else { // Copy whole string if no '/' was found. + memcpy(_fileName.data(), fileName, lenFilename); + } + return testapp::RetCode::RETCODE_SUCCESS; +} + +} // namespace testenvironment diff --git a/TestApp/src/TestEnvironment/Logging.cpp b/TestApp/src/TestEnvironment/Logging.cpp new file mode 100644 index 0000000..3b9d1f8 --- /dev/null +++ b/TestApp/src/TestEnvironment/Logging.cpp @@ -0,0 +1,157 @@ +/** + * @file src/TestEnvironment/Logging.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include + +namespace testenvironment { + +Logging& Logging::getInstance() { + static Logging logging; + return logging; +} + +testapp::RetCode Logging::setMessageBuffer(osextension::MessageBufferReference& mbuf) { + _testEnvironmentToTestRunnerMessageBufferReference = &mbuf; + + return testapp::RetCode::RETCODE_SUCCESS; +} + +testapp::RetCode Logging::setDebugLog(bool isDebugLog) { + _isDebugLog = isDebugLog; + + return testapp::RetCode::RETCODE_SUCCESS; +} + +testapp::RetCode Logging::logInfo(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_INFO); + + return retCode; +} + +testapp::RetCode Logging::logDebug(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_DEBUG); + + if (testapp::RetCode::RETCODE_SUCCESS == retCode && true != _isDebugLog) { + // Error: isDebugLog not set + retCode = testapp::RetCode::RETCODE_FAILURE; + } + + return retCode; +} + +testapp::RetCode Logging::logWarning(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_WARNING); + + return retCode; +} + +testapp::RetCode Logging::logError(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_ERROR); + + return retCode; +} + +testapp::RetCode Logging::logCritical(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_CRITICAL); + + return retCode; +} + +testapp::RetCode Logging::logMessage(const char* msg) { + testapp::RetCode retCode = logGeneric(msg, testapp::MessageType::MESSAGE_LOG); + + return retCode; +} + +testapp::RetCode Logging::logGeneric(const char* msg, testapp::MessageType messageType){ + testapp::RetCode retCode = checkMessage(msg); + + if (testapp::RetCode::RETCODE_SUCCESS == retCode){ + sendMessage(msg, messageType); + } + + if (testapp::RetCode::RETCODE_OUT_OF_RESOURCES == retCode) { + const char* errMsg = "ERROR: MESSAGE TOO LONG"; + sendMessage(errMsg, messageType); + } + + // catch failures + if (retCode != testapp::RetCode::RETCODE_SUCCESS || + messageType == testapp::MessageType::MESSAGE_ERROR || + messageType == testapp::MessageType::MESSAGE_CRITICAL){ + _hasFailed = true; + } + + return retCode; +} + +testapp::RetCode Logging::checkMessage(const char* msg) { + testapp::RetCode retCode = testapp::RetCode::RETCODE_SUCCESS; + + // Error: nullpointer + if (nullptr == msg){ + retCode = testapp::RetCode::RETCODE_NULL_POINTER; + } + // Error: _testEnvironmentToTestRunnerMessageBufferReference not initialized + else if (nullptr == _testEnvironmentToTestRunnerMessageBufferReference) { + retCode = testapp::RetCode::RETCODE_UNINITIALIZED; + } + // Error: message too long + else if (strlen(msg) + messageTypeStringsMaxSize >= testapp::messagebufferSize) { + retCode = testapp::RetCode::RETCODE_OUT_OF_RESOURCES; + } + // Error: empty message + else if (strlen(msg) <= 0) { + retCode = testapp::RetCode::RETCODE_INVALID_PARAM; + } + + return retCode; +} + + +void Logging::sendMessage(const char* msg, testapp::MessageType messageType) { + testapp::LogMessageT log; + message::MessageStructure message{}; + message.messageType = message::MessageType::Log; + message.subType = message::MessageSubType::RESERVED; + + auto messageTypeLength = strlen(_messageTypeStrings.at(messageType).value.data()); + auto payloadLength = strlen(msg) + messageTypeLength + messageTypeDelimiter.size(); + + /* Construct tlv element message */ + // Message Format: : + // Add preliminary token; e.g. "DEBUG" + std::copy_n(_messageTypeStrings.at(messageType).value.data(), messageTypeLength, log.text.begin()); + // Add Delimiter ": " + std::copy_n(messageTypeDelimiter.begin(), messageTypeDelimiter.size(), log.text.begin() + messageTypeLength); + // Add log message + std::copy_n(msg, strlen(msg), log.text.begin() + messageTypeLength + messageTypeDelimiter.size()); + + /* Add tlv element */ + message::TLV payloadInTlvFormat{message::reportTlvType, static_cast(payloadLength), log.text.data()}; + message::addTLV(payloadInTlvFormat, message); + + /* send message to TestRunner */ + _testEnvironmentToTestRunnerMessageBufferReference->send(&message, sizeof(message), portMAX_DELAY); +} + +void Logging::resetFailureFlag() { + _hasFailed = false; +} + +bool Logging::getFailureFlag() { + return _hasFailed; +} + +} // namespace testenvironment diff --git a/TestApp/src/TestEnvironment/Task.cpp b/TestApp/src/TestEnvironment/Task.cpp new file mode 100644 index 0000000..96fa733 --- /dev/null +++ b/TestApp/src/TestEnvironment/Task.cpp @@ -0,0 +1,67 @@ +/** + * @file src/TestEnvironment/Task.cpp + * @brief TestEnvironment Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include + +/** + * @brief: testenvironment namespace. + */ +namespace testenvironment +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[taskStackSize]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "testenvironment", + 4u}; + + /// Creation of global task object. + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) {} + + void Task::registerTestRunnerToTestEnvMessageBufferReference(osextension::MessageBufferReference &messageBufferReference) + { + testEnvironment.registerTestRunnerToTestEnvMessageBufferReference(messageBufferReference); + _testRunnerToTestEnvMessageBufferAdded.signal(); + } + + void Task::registerTestEnvToTestRunnerMessageBufferReference(osextension::MessageBufferReference &messageBufferReference) + { + testEnvironment.registerTestEnvToTestRunnerMessageBufferReference(messageBufferReference); + testenvironment::Logging::getInstance().setMessageBuffer(messageBufferReference); + _testEnvToTestRunnerMessageBufferAdded.signal(); + } + + void Task::run() + { + // Wait until the input and output message buffer were registered. + _testRunnerToTestEnvMessageBufferAdded.wait(portMAX_DELAY); + _testEnvToTestRunnerMessageBufferAdded.wait(portMAX_DELAY); + while (true) + { + testEnvironment.runOneCycle(); + } + } + + void Task::initialize() { initializeTask(); } + +} // namespace testenvironment diff --git a/TestApp/src/TestEnvironment/TestEnvironment.cpp b/TestApp/src/TestEnvironment/TestEnvironment.cpp new file mode 100644 index 0000000..797ecfc --- /dev/null +++ b/TestApp/src/TestEnvironment/TestEnvironment.cpp @@ -0,0 +1,81 @@ +/** + * @file src/TestEnvironment/TestEnvironment.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include + +/** + * @brief: testenvironment namespace. + */ +namespace testenvironment { +static message::TestExecutionMessage message{}; +static testapp::ErrCode errCode = testapp::ErrCode::ERRCODE_UNDEFINED; +static size_t bytesReceived = 0; + +testapp::ErrCode TestEnvironment::processMessage(const message::TestExecutionMessage& message) { + testapp::ErrCode errCodeProcessMessage = testapp::ErrCode::ERRCODE_MSG_INCORRECT_RESPONSE; + switch (message.subType) { + case message::MessageSubType::TESTSUITE_SETUP: + errCodeProcessMessage = static_cast(message.testSuiteOrCase)->doSuiteSetup(); + break; + case message::MessageSubType::TESTCASE_SETUP: + errCodeProcessMessage = static_cast(message.testSuiteOrCase)->doSetup(); + break; + case message::MessageSubType::TESTCASE_RUN: + errCodeProcessMessage = static_cast(message.testSuiteOrCase)->runTest(); + break; + case message::MessageSubType::TESTSUITE_TEARDOWN: + errCodeProcessMessage = static_cast(message.testSuiteOrCase)->doSuiteTearDown(); + break; + case message::MessageSubType::TESTCASE_TEARDOWN: + errCodeProcessMessage = static_cast(message.testSuiteOrCase)->doTearDown(); + break; + default: + return testapp::ErrCode::ERRCODE_MSG_INCORRECT_RESPONSE; + break; + } + return errCodeProcessMessage; +} + +void TestEnvironment::runOneCycle() { + // Check that the MessageBufferReferences was set. + if (_testRunnerToTestEnvMessageBufferReference != nullptr && _testEnvironmentToTestRunnerMessageBufferReference != nullptr) { + // Wait until there is a newly received message from TestRunner. + bytesReceived = _testRunnerToTestEnvMessageBufferReference->receive(static_cast(&message), sizeof(message), portMAX_DELAY); + if (bytesReceived > 0){ + errCode = processMessage(message); + } + + // when the process is done, TestEnvironment shall send the final report to TestRunner + if (errCode != testapp::ErrCode::ERRCODE_UNDEFINED) { + message::MessageStructure reportMessage{}; + reportMessage.messageType = message::MessageType::Report; + reportMessage.errorCode = errCode; + reportMessage.subType = (errCode == testapp::ErrCode::SUCCESS) ? message::MessageSubType::TEST_PASS : message::MessageSubType::TEST_FAIL; + + _testEnvironmentToTestRunnerMessageBufferReference->send(static_cast(&reportMessage), sizeof(reportMessage), portMAX_DELAY); + } + } +} + +void TestEnvironment::registerTestRunnerToTestEnvMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) { + _testRunnerToTestEnvMessageBufferReference = &messageBufferReference; +} + +void TestEnvironment::registerTestEnvToTestRunnerMessageBufferReference(osextension::MessageBufferReference& messageBufferReference) { + _testEnvironmentToTestRunnerMessageBufferReference = &messageBufferReference; +} + +} // namespace testenvironment diff --git a/TestApp/src/TestRunner/Task.cpp b/TestApp/src/TestRunner/Task.cpp new file mode 100644 index 0000000..0b8fdd5 --- /dev/null +++ b/TestApp/src/TestRunner/Task.cpp @@ -0,0 +1,90 @@ +/** + * @file src/TestRunner/Task.cpp + * @brief TestRunner Task + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include +#include + +namespace testrunner +{ + /// Create the task without using any dynamic memory allocation + constexpr uint32_t taskStackSize = 2048u; + StackType_t xStack[taskStackSize]; + + /// Task descriptor + const testapprtos::TaskConfiguration taskConfiguration = { + taskStackSize, + xStack, + "testrunner", + 4u}; + + /// Creation of global task object + Task task{taskConfiguration}; + TaskInterface &taskInterface = task; + + /// Reference to TestRegistry singleton + static TestRegistry &testRegistry = TestRegistry::getInstance(); + + Task::Task(const testapprtos::TaskConfiguration &taskConfiguration) : TaskSetup(taskConfiguration) + { + // set testRunner MessageBuffer references + _testRunner.setTestRunnerToTPCMessageBufferReference(_testRunnerToTPCMessageBuffer); + _testRunner.setTPCToTestRunnerMessageBufferReference(_tpcToTestRunnerMessageBuffer); + _testRunner.setTestRunnerToTestEnvironmentMessageBufferReference(_testRunnerToTestEnvironmentMessageBuffer); + _testRunner.setTestEnvironmentToTestRunnerMessageBufferReference(_testEnvironmentToTestRunnerMessageBuffer); + + // Create static QueueSet + _testRunner.setTestRunnerQueueSetHandle(osabstraction::queueCreateSetStatic(numberOfEventsInQueueSet, _queueSetStorage.data(), &_staticQueue)); + + // Add all semaphores to the QueueSet + if (xQueueAddToSet(static_cast(_testEnvironmentToTestRunnerMessageBuffer.getHandleForQueueSet()), _testRunner.getTestRunnerQueueSetHandle()) != pdTRUE) + { + assert(false); + } + if (xQueueAddToSet(static_cast(_tpcToTestRunnerMessageBuffer.getHandleForQueueSet()), _testRunner.getTestRunnerQueueSetHandle()) != pdTRUE) + { + assert(false); + } + } + + // Get Reference of the incoming message buffer from TPCoordinator + osextension::MessageBufferReference Task::getTPCToTestRunnerMessageBufferReference() const + { + return _tpcToTestRunnerMessageBuffer.getReference(); + } + + osextension::MessageBufferReference Task::getTestRunnerToTPCMessageBufferReference() const + { + return _testRunnerToTPCMessageBuffer.getReference(); + } + + /** + * @brief run function + */ + void Task::run() + { + // Register the message buffer reference to _testRunnerToTestEnvironmentMessageBuffer + testenvironment::taskInterface.registerTestRunnerToTestEnvMessageBufferReference(_testRunnerToTestEnvironmentMessageBuffer); + + // Register the message buffer reference to _testEnvironmentToTestRunnerMessageBuffer + testenvironment::taskInterface.registerTestEnvToTestRunnerMessageBufferReference(_testEnvironmentToTestRunnerMessageBuffer); + + QueueSetMemberHandle_t queueSetMemberHandle = nullptr; + + while (true) + { + _testRunner.oneCycle(queueSetMemberHandle); + } + } // Task::Run +} // namespace testrunner diff --git a/TestApp/src/TestRunner/TestRunner.cpp b/TestApp/src/TestRunner/TestRunner.cpp new file mode 100644 index 0000000..fe89dcd --- /dev/null +++ b/TestApp/src/TestRunner/TestRunner.cpp @@ -0,0 +1,172 @@ +/** + * @file src/TestRunner/TestRunner/TestRunner.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + */ + +#include +#include + +namespace testrunner +{ + /// Reference to TestRegistry singleton + static TestRegistry &testRegistry = TestRegistry::getInstance(); + + /// set testRunner message Buffer references + void TestRunner::setTestRunnerToTPCMessageBufferReference(osextension::MessageBufferReference& mbf) { + _testRunnerToTPCMessageBufferReference = &mbf; + } + void TestRunner::setTPCToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf) { + _tpcToTestRunnerMessageBufferReference = &mbf; + } + void TestRunner::setTestRunnerToTestEnvironmentMessageBufferReference(osextension::MessageBufferReference& mbf) { + _testRunnerToTestEnvironmentMessageBufferReference = &mbf; + } + void TestRunner::setTestEnvironmentToTestRunnerMessageBufferReference(osextension::MessageBufferReference& mbf) { + _testEnvironmentToTestRunnerMessageBufferReference = &mbf; + } + + /// testRunner queue set setter and getter + void TestRunner::setTestRunnerQueueSetHandle(QueueSetHandle_t queueSetHandle) { + _queueSetHandle = queueSetHandle; + } + QueueSetHandle_t TestRunner::getTestRunnerQueueSetHandle() const { + return _queueSetHandle; + } + + void TestRunner::oneCycle(QueueSetMemberHandle_t &queueSetMemberHandle) { + // Wait until there is something to do + queueSetMemberHandle = xQueueSelectFromSet(_queueSetHandle, pdMS_TO_TICKS(portMAX_DELAY)); + message::MessageStructure dataReceivedFromTPC; + message::MessageStructure dataReceivedFromTestEnv; + message::TestExecutionMessage dataSentToTestEnv; + message::MessageStructure reportToTPC; + testapp::ErrCode retCode; + + if (queueSetMemberHandle != nullptr) { + if (queueSetMemberHandle == _tpcToTestRunnerMessageBufferReference->getHandleForQueueSet()) { + size_t receivedBytesFromTPC = _tpcToTestRunnerMessageBufferReference->receive(static_cast(&dataReceivedFromTPC), + sizeof(message::MessageStructure), portMAX_DELAY); + if (receivedBytesFromTPC > 0) { + switch (dataReceivedFromTPC.messageType) { + case message::MessageType::Command: + // ProcessCmd e.g. send the entire message + retCode = processTestCmd(dataReceivedFromTPC, dataSentToTestEnv); + // check for error and send a report message to TPC + if (testapp::ErrCode::SUCCESS != retCode) { + reportToTPC.errorCode = retCode; + reportToTPC.messageType = message::MessageType::Report; + reportToTPC.subType = message::MessageSubType::TEST_FAIL; + sendReport(reportToTPC, dataReceivedFromTPC); + } + break; + case message::MessageType::Acknowledgement: + case message::MessageType::Report: + default: + // send error message to TPC + reportToTPC.errorCode = testapp::ErrCode::ERRCODE_MSG_INCORRECT_RESPONSE; + reportToTPC.messageType = message::MessageType::Report; + reportToTPC.subType = message::MessageSubType::TEST_FAIL; + sendReport(reportToTPC, dataReceivedFromTPC); + break; + } + } + else { + // send error message to TPC + reportToTPC.errorCode = testapp::ErrCode::ERRCODE_MSG_INCOMPLETE; + reportToTPC.messageType = message::MessageType::Report; + reportToTPC.subType = message::MessageSubType::TEST_FAIL; + sendReport(reportToTPC, dataReceivedFromTPC); + } + } + else if (queueSetMemberHandle == _testEnvironmentToTestRunnerMessageBufferReference->getHandleForQueueSet()) { + // Receive Message from TestEnvironment + size_t receivedBytesFromTestEnv = _testEnvironmentToTestRunnerMessageBufferReference->receive(static_cast(&dataReceivedFromTestEnv), + sizeof(dataReceivedFromTestEnv), portMAX_DELAY); + if (receivedBytesFromTestEnv > 0u && + (dataReceivedFromTestEnv.messageType == message::MessageType::Report || + dataReceivedFromTestEnv.messageType == message::MessageType::Log)) { + memcpy(&reportToTPC, &dataReceivedFromTestEnv, sizeof(dataReceivedFromTestEnv)); + // send logging messages and final report message to TPC + sendReport(reportToTPC, dataReceivedFromTPC); + } + else { + // send error message to TPC + reportToTPC.errorCode = testapp::ErrCode::ERRCODE_MSG_INCOMPLETE; + reportToTPC.messageType = message::MessageType::Report; + reportToTPC.subType = message::MessageSubType::TEST_FAIL; + sendReport(reportToTPC, dataReceivedFromTPC); + } + } + else { + /*Nothing todo*/ + } + } + } + + testapp::ErrCode TestRunner::processTestCmd(const message::MessageStructure &command, message::TestExecutionMessage &dataSentToTestEnv) { + size_t bytesSentToTestEnv = 0u; + testapp::ErrCode errCode = testapp::ErrCode::ERRCODE_UNDEFINED; + testsuite::TestSuiteInterface *testSuitePtr = nullptr; + testcase::TestCaseInterface *testCasePtr = nullptr; + // check message subtype, load corresponding testCase or testSuite, and + // send corresponding information to TestEnv or set an errCode to be sent to TPC + switch (command.subType) { + case message::MessageSubType::TESTSUITE_SETUP: + case message::MessageSubType::TESTSUITE_RUN: + case message::MessageSubType::TESTSUITE_TEARDOWN: + // load and get testSuite to be forwarded to testEnv + testSuitePtr = testrunner::testRegistry.loadTestSuite(command.testSuite); + if (nullptr != testSuitePtr) { + dataSentToTestEnv.subType = command.subType; + dataSentToTestEnv.testSuiteOrCase = testSuitePtr; + // send message to TestEnv + bytesSentToTestEnv = _testRunnerToTestEnvironmentMessageBufferReference->send(&dataSentToTestEnv, sizeof(dataSentToTestEnv)); + errCode = (0u != bytesSentToTestEnv) ? testapp::ErrCode::SUCCESS : testapp::ErrCode::FAILURE; + } + else{ + errCode = testapp::ErrCode::ERRCODE_TEST_SUITE_UNKNOWN; + } + break; + case message::MessageSubType::TESTCASE_SETUP: + case message::MessageSubType::TESTCASE_RUN: + case message::MessageSubType::TESTCASE_TEARDOWN: + // load and get testCase to be forwarded to testEnv + testCasePtr = testrunner::testRegistry.loadTestCase(command.testSuite, command.testCase); + if (nullptr != testCasePtr) { + dataSentToTestEnv.subType = command.subType; + dataSentToTestEnv.testSuiteOrCase = testCasePtr; + // send message to TestEnv + bytesSentToTestEnv = _testRunnerToTestEnvironmentMessageBufferReference->send(&dataSentToTestEnv, sizeof(dataSentToTestEnv)); + errCode = (0u != bytesSentToTestEnv) ? testapp::ErrCode::SUCCESS : testapp::ErrCode::FAILURE; + } + else{ + errCode = testapp::ErrCode::ERRCODE_TEST_CASE_UNKNOWN; + } + break; + default: + errCode = testapp::ErrCode::FAILURE; + break; + } + return errCode; + } + + void TestRunner::sendReport(message::MessageStructure &reportMessage, const message::MessageStructure &dataReceivedFromTPC) { + reportMessage.headerInfo.messageType = (reportMessage.messageType == message::MessageType::Report) ? 1u : 3u; + reportMessage.headerInfo.protocolVersion = dataReceivedFromTPC.headerInfo.protocolVersion; + reportMessage.headerInfo.res = dataReceivedFromTPC.headerInfo.res; + reportMessage.token = dataReceivedFromTPC.token; + reportMessage.testSection = dataReceivedFromTPC.testSection; + reportMessage.testSuite = dataReceivedFromTPC.testSuite; + reportMessage.testCase = dataReceivedFromTPC.testCase; + + _testRunnerToTPCMessageBufferReference->send(&reportMessage, sizeof(reportMessage), portMAX_DELAY); + } +} // namespace testrunner diff --git a/TestApp/test/CMakeLists.txt b/TestApp/test/CMakeLists.txt new file mode 100644 index 0000000..f415005 --- /dev/null +++ b/TestApp/test/CMakeLists.txt @@ -0,0 +1,7 @@ +# Include unittests only when: +# 1. using the emulator toolchains (unittest-ppc-ghs-emu & unittest-stm32-arm-gcc) +# 2. using gcc toolchain for a Native build +if( NOT(RUNNING_UNITTESTS_WITH_EMULATOR STREQUAL "OFF" AND DEVICE_UNDER_TEST_ARCH MATCHES "ARM|PPC") ) + add_subdirectory(Unit) +endif() + diff --git a/TestApp/test/Unit/CMakeLists.txt b/TestApp/test/Unit/CMakeLists.txt new file mode 100644 index 0000000..c5b5838 --- /dev/null +++ b/TestApp/test/Unit/CMakeLists.txt @@ -0,0 +1,8 @@ +add_custom_target(doxygen-testapp) +add_subdirectory(TestRegistry) +add_subdirectory(TestEnvironment) +add_subdirectory(MessageHandler) +add_subdirectory(TestRunner) +#add_subdirectory(TPC) +add_subdirectory(CommunicationChannel) +add_subdirectory(Tier1competetion) \ No newline at end of file diff --git a/TestApp/test/Unit/CommunicationChannel/CMakeLists.txt b/TestApp/test/Unit/CommunicationChannel/CMakeLists.txt new file mode 100644 index 0000000..9ce6dc6 --- /dev/null +++ b/TestApp/test/Unit/CommunicationChannel/CMakeLists.txt @@ -0,0 +1,25 @@ +if(DEVICE_UNDER_TEST_ARCH STREQUAL "ARM|PPC") +set(MODULE_NAME "CommunicationChannel") + +add_executable(${MODULE_NAME}-Test + ${MODULE_NAME}Test.cpp + ) + +target_include_directories(${MODULE_NAME}-Test + PUBLIC + ${PROJECT_SOURCE_DIR}/Platform/RequiredInterface/CommunicationChannelInterface + ) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + CommunicationChannel + gtest + gmock_main + ) + +# Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + +endif() \ No newline at end of file diff --git a/TestApp/test/Unit/CommunicationChannel/CommunicationChannelTest.cpp b/TestApp/test/Unit/CommunicationChannel/CommunicationChannelTest.cpp new file mode 100644 index 0000000..c913ebb --- /dev/null +++ b/TestApp/test/Unit/CommunicationChannel/CommunicationChannelTest.cpp @@ -0,0 +1,90 @@ +/** + * @file test/CommunicationChannelInterface/CommunicationChannelInterfaceTest.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include +#include + +class CommunicationChanneTest : public testing::Test +{ +public: + tccinterface::ErrCode errCode = tccinterface::ErrCode::ERRCODE_UNDEFINED; + tccinterface::TccInterface *tccInterfaceInstance = nullptr; + + virtual void SetUp() + { + tccInterfaceInstance = tccinterface::tccInterface; + } + + virtual void TearDown() + { + tccInterfaceInstance = nullptr; + errCode = tccinterface::ErrCode::ERRCODE_UNDEFINED; + } +}; + +TEST_F(CommunicationChanneTest, init_and_shutdown) +{ + // initialize communication channel + errCode = tccInterfaceInstance->init(); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // Re-initialize communication channel -> should fail with errorCode + errCode = tccInterfaceInstance->init(); + EXPECT_EQ(tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_ALREADY_INITIALIZED, errCode); + + // shutdown + errCode = tccInterfaceInstance->shutdown(); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // shutdown again + errCode = tccInterfaceInstance->shutdown(); + EXPECT_EQ(tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_UNINITIALIZED, errCode); +} + +TEST_F(CommunicationChanneTest, send) +{ + char buf[512u*15u] = "message to be sent to ITF"; + // initialize communication channel + errCode = tccInterfaceInstance->init(); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // send data + errCode = tccInterfaceInstance->send(buf, sizeof(buf)); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // send data failure + char buf2[512u*16u] = "message to be sent to ITF too long"; + errCode = tccInterfaceInstance->send(buf2, sizeof(buf2)); + EXPECT_EQ(tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_SEND_FAILURE, errCode); + + // receive data + char receiveBuf[30]; + size_t length = 0; + errCode = tccInterfaceInstance->receive(&length, &receiveBuf, 256); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // shutdown + errCode = tccInterfaceInstance->shutdown(); + EXPECT_EQ(tccinterface::ErrCode::SUCCESS, errCode); + + // send data + errCode = tccInterfaceInstance->send(buf, sizeof(buf)); + EXPECT_EQ(tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_UNINITIALIZED, errCode); + + // receive data + errCode = tccInterfaceInstance->receive(&length, &receiveBuf, 256); + EXPECT_EQ(tccinterface::ErrCode::ERRCODE_TEST_CCHANNEL_UNINITIALIZED, errCode); +} diff --git a/TestApp/test/Unit/MessageHandler/CMakeLists.txt b/TestApp/test/Unit/MessageHandler/CMakeLists.txt new file mode 100644 index 0000000..afac43d --- /dev/null +++ b/TestApp/test/Unit/MessageHandler/CMakeLists.txt @@ -0,0 +1,39 @@ +set(MODULE_NAME "MessageHandler") + +add_executable(${MODULE_NAME}-Test + ${MODULE_NAME}Test.cpp + ) + +target_include_directories(${MODULE_NAME}-Test + PUBLIC + ${PROJECT_SOURCE_DIR}/TestApp/include) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + MessageHandler + gtest + gmock_main + etl + ) + + # Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + +# # Adds gcov target +# testapp_add_gcovr( +# NAME ${MODULE_NAME} +# SRC ${PROJECT_SOURCE_DIR}/TestApp/src/${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# DEPENDENCIES Run-${MODULE_NAME}-Test +# ) + +# Add clang-tidy scans +add_subdirectory(Clang-Tidy) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) diff --git a/TestApp/test/Unit/MessageHandler/Clang-Tidy/CMakeLists.txt b/TestApp/test/Unit/MessageHandler/Clang-Tidy/CMakeLists.txt new file mode 100644 index 0000000..5a99c9d --- /dev/null +++ b/TestApp/test/Unit/MessageHandler/Clang-Tidy/CMakeLists.txt @@ -0,0 +1,28 @@ +set(MODULE_NAME "MessageHandler") + +add_library(clang-tidy-${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME}ClangTidy.cpp + ) + +target_link_libraries(clang-tidy-${MODULE_NAME} + PRIVATE + ${MODULE_NAME} + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(clang-tidy-${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + #enable the option once all warnings are resolved + #CLANG_TIDY_WARNINGS_AS_ERRORS "*" + ) + +# # Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET clang-tidy-${MODULE_NAME} +# INCLUDES +# ) \ No newline at end of file diff --git a/TestApp/test/Unit/MessageHandler/Clang-Tidy/MessageHandlerClangTidy.cpp b/TestApp/test/Unit/MessageHandler/Clang-Tidy/MessageHandlerClangTidy.cpp new file mode 100644 index 0000000..b09b4d7 --- /dev/null +++ b/TestApp/test/Unit/MessageHandler/Clang-Tidy/MessageHandlerClangTidy.cpp @@ -0,0 +1,15 @@ +/** + * @file * @file test/Unit/MessageHandler/Clang-Tidy/MessageHandlerClangTidy.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @details This is needed to be able to scan MessageHandler.hpp with clang-tidy + */ + +#include diff --git a/TestApp/test/Unit/MessageHandler/MessageHandlerTest.cpp b/TestApp/test/Unit/MessageHandler/MessageHandlerTest.cpp new file mode 100644 index 0000000..fb4d526 --- /dev/null +++ b/TestApp/test/Unit/MessageHandler/MessageHandlerTest.cpp @@ -0,0 +1,271 @@ +/** + * @file test/MessageHandler/MessageHandlerTest.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include +#include +#include +#include + +void CheckTlv(message::TLV tlv1, message::TLV tlv2) +{ + EXPECT_EQ(tlv1.tag, tlv2.tag); + EXPECT_EQ(tlv1.length, tlv2.length); + + /*looping through tlv val*/ + for (int i = 0; i < tlv1.length; i++) + { + EXPECT_EQ(tlv1.value[i], tlv2.value[i]); + } +} +TEST(MessageTest, MessageHandlerTest) +{ + message::MessageStructure msg; + ASSERT_EQ(sizeof(msg.headerInfo), sizeof(uint8_t)); + + msg.headerInfo.protocolVersion = 1; + msg.headerInfo.res = 0; + msg.token = 0; + msg.messageType = message::MessageType::Command; + msg.subType = message::MessageSubType::TESTCASE_SETUP; + msg.errorCode = testapp::ErrCode::ERRCODE_TEST_CASE_ALREADY_REGISTERED; + msg.testSection = 1; + msg.testCase = 2; + msg.testSuite = 3; + msg.payloadLength = 0; + + /*adding tlv's*/ + uint8_t arr[11] = {1, 3, 5, 6, 7, 8, 2, 4, 5, 7, 9}; + + message::TLV tlvElement; + tlvElement.tag = 1; + tlvElement.length = 11; + tlvElement.value = arr; + /*call the AddTlv method*/ + auto ret = message::addTLV(tlvElement, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + /*searialize back to bytearray + Decode bytearray + check if all the expected results +*/ + std::array bytestream; + etl::array_view byteStreamView(bytestream); + + message::MessageStructure msg2; + ret = message::serialize(msg, byteStreamView); + + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + + ret = message::deserialize(byteStreamView, msg2); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + EXPECT_EQ(msg.headerInfo.protocolVersion, msg2.headerInfo.protocolVersion); + EXPECT_EQ(msg.headerInfo.messageType, msg2.headerInfo.messageType); + EXPECT_EQ(msg.headerInfo.res, msg2.headerInfo.res); + EXPECT_EQ(msg.token, msg2.token); + EXPECT_EQ(msg.testSection, msg2.testSection); + EXPECT_EQ(msg.testSuite, msg2.testSuite); + EXPECT_EQ(msg.testCase, msg2.testCase); + + message::TLV tlvOpt1; + message::parseTLV(msg2, tlvOpt1); + CheckTlv(tlvOpt1, tlvElement); +} + +TEST(MessageTest, MessageHandlerMultipleTLVTest) +{ + message::MessageStructure msg; + msg.headerInfo.protocolVersion = 1; + msg.headerInfo.res = 0; + msg.token = 0; + msg.messageType = message::MessageType::Command; + msg.subType = message::MessageSubType::TESTCASE_SETUP; + msg.errorCode = testapp::ErrCode::ERRCODE_TEST_CASE_ALREADY_REGISTERED; + msg.testSection = 1; + msg.testCase = 2; + msg.testSuite = 3; + msg.payloadLength = 0; + + /*adding multiple tlv's*/ + uint8_t arr[15] = {1, 3, 5, 6, 7, 8, 2, 4, 5, 7, 8, 9, 16, 15, 14}; + + message::TLV tlvElement; + tlvElement.tag = 1; + tlvElement.length = 15; + tlvElement.value = arr; + auto ret = message::addTLV(tlvElement, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + + uint8_t arr1[12] = {1, 3, 5, 6, 7, 8, 2, 4, 5, 7, 8, 9}; + + message::TLV tlvElement2; + tlvElement2.tag = 1; + tlvElement2.length = 12; + tlvElement2.value = arr1; + ret = message::addTLV(tlvElement2, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + + uint8_t arr2[10] = {1, 3, 5, 6, 7, 8, 2, 4, 5, 7}; + + message::TLV tlvElement3; + tlvElement3.tag = 1; + tlvElement3.length = 10; + tlvElement3.value = arr2; + ret = message::addTLV(tlvElement3, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + + std::array bytestream; + etl::array_view byteStreamView(bytestream); + + message::MessageStructure msg2; + ret = message::serialize(msg, byteStreamView); + + ret = message::deserialize(byteStreamView, msg2); + + message::TLV tlv; + ret = message::parseTLV(msg2, tlv); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + CheckTlv(tlv, tlvElement); + //check tlv with tlvElement + ret = message::parseTLV(msg2, tlv); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + CheckTlv(tlv, tlvElement2); + ret = message::parseTLV(msg2, tlv); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_LASTELEMENT); + CheckTlv(tlv, tlvElement3); +} + +TEST(MessageTest, MessageHandlerBufferOverloadTest) +{ + message::MessageStructure msg; + msg.headerInfo.protocolVersion = 1; + msg.headerInfo.res = 0; + msg.token = 0; + msg.messageType = message::MessageType::Command; + msg.subType = message::MessageSubType::TESTCASE_SETUP; + msg.errorCode = testapp::ErrCode::ERRCODE_TEST_CASE_ALREADY_REGISTERED; + msg.testSection = 1; + msg.testCase = 2; + msg.testSuite = 3; + msg.payloadLength = 0; + + uint8_t arr[200]; + message::TLV tlvElement; + tlvElement.tag = 1; + tlvElement.length = 200; + tlvElement.value = arr; + /*call the AddTlv method*/ + auto ret = message::addTLV(tlvElement, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_SUCCESS); + ret = message::addTLV(tlvElement, msg); + EXPECT_EQ(ret, testapp::RetCode::RETCODE_FAILURE); +} + +TEST(MessageTest, deserialize) +{ + constexpr uint8_t numberOfTestCases = 21u; + std::array, numberOfTestCases> byteStream = {{ + {0x0, 0u, 1u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x01}, + {0x0, 0u, 2u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x02}, + {0x0, 0u, 3u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x03}, + {0x0, 0u, 11u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x04}, + {0x0, 0u, 12u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x05}, + {0x0, 0u, 13u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x06}, + {0x0, 0u, 21u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x07}, + {0x0, 0u, 22u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x08}, + {0x0, 0u, 23u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x09}, + {0x10, 0u, 0u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0A}, + {0x10, 0u, 1u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0B}, + {0x10, 0u, 2u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0C}, + {0x20, 0u, 0u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0D}, + {0x20, 0u, 1u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0E}, + {0x10, 0u, 110u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x0F}, + {0x10, 0u, 112u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x10}, + {0x0, 0u, 0u, 0u, 0u, 1u, 0u, 8u, 'p','a','y','l','o','a','d',0x11}, + {0x0, 0u, 1u, 0u, 0u, 1u, 0u, 0u, }, + // Failure test cases: + {0x0, 0u, 110u, 0u, 0u, 1u, 0u, 0u, }, + {0x10, 0u, 13u, 0u, 0u, 1u, 0u, 0u, }, + {0x20, 0u, 12u, 0u, 0u, 1u, 0u, 0u, } + }}; + + // Expected Deserialized message + message::MessageStructure expectedMsg[numberOfTestCases] = { + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSECTION_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x01}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x02}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x03}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSECTION_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x04}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x05}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x06}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSECTION_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x07}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x08}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x09}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TEST_PASS, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0A}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TEST_FAIL, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0B}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TEST_NOTIMPLEMENTED, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0C}}, + {{0, 2, 0}, message::MessageType::Acknowledgement, 0u, message::MessageSubType::ACKNOWLEDGEMENT, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0D}}, + {{0, 2, 0}, message::MessageType::Acknowledgement, 0u, message::MessageSubType::NACK, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0E}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::REPORT_SUCCESS, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x0F}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::REPORT_FAILURE, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x010}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::PING, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 8u, {'p','a','y','l','o','a','d',0x011}}, + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::PONG, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + // Failure test cases: + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::REPORT_SUCCESS, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{0, 2, 0}, message::MessageType::Acknowledgement, 0u, message::MessageSubType::TESTSUITE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + }; + + // Expected Return Code + testapp::RetCode expectedRetCode[numberOfTestCases] = { + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + testapp::RetCode::RETCODE_SUCCESS, + // Failure test cases: + testapp::RetCode::RETCODE_INVALID_PARAM, + testapp::RetCode::RETCODE_INVALID_PARAM, + testapp::RetCode::RETCODE_INVALID_PARAM, + }; + + for (uint8_t iter = 0u; iter < numberOfTestCases; iter++) + { + message::MessageStructure msg; + etl::array_view byteStreamView(byteStream.at(iter)); + EXPECT_EQ(expectedRetCode[iter], message::deserialize(byteStreamView, msg)) << "Failure at test case # "<< (int16_t)iter+1; + if(testapp::RetCode::RETCODE_SUCCESS == expectedRetCode[iter]) + { + EXPECT_EQ(expectedMsg[iter].messageType, msg.messageType) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].token, msg.token) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].subType, msg.subType) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].errorCode, msg.errorCode) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].testSection, msg.testSection) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].testSuite, msg.testSuite) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].testCase, msg.testCase) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(expectedMsg[iter].payloadLength, msg.payloadLength) << "Failure at test case # " << (int16_t)iter + 1; + EXPECT_EQ(0, memcmp(&expectedMsg[iter].payload, &msg.payload, sizeof(msg.payload))) << "Failure at test case # " << (int16_t)iter + 1; + } + } +} diff --git a/TestApp/test/Unit/TPC/CMakeLists.txt b/TestApp/test/Unit/TPC/CMakeLists.txt new file mode 100644 index 0000000..e31d107 --- /dev/null +++ b/TestApp/test/Unit/TPC/CMakeLists.txt @@ -0,0 +1,40 @@ +set(MODULE_NAME "TPC") + +add_executable(${MODULE_NAME}-Test + ${MODULE_NAME}Test.cpp + ) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + MessageHandler + TestApp-Interface + TPC + CommunicationChannel-Mock + MessageBufferReference-Mock + OsExtension-Mock + # Third Party Dependencies for TestApp + etl + gtest + gmock_main + ) + + # Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + + # testapp_add_gcovr( + # NAME ${MODULE_NAME} + # SRC ${PROJECT_SOURCE_DIR}/TestApp/src/${MODULE_NAME} + # INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} + # DEPENDENCIES Run-${MODULE_NAME}-Test + # ) + +# Add pseudo cpp to enable clang-tidy scans +add_subdirectory(Clang-Tidy) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) diff --git a/TestApp/test/Unit/TPC/Clang-Tidy/CMakeLists.txt b/TestApp/test/Unit/TPC/Clang-Tidy/CMakeLists.txt new file mode 100644 index 0000000..1d4fd1d --- /dev/null +++ b/TestApp/test/Unit/TPC/Clang-Tidy/CMakeLists.txt @@ -0,0 +1,26 @@ +set(MODULE_NAME "TPC") + +add_library(clang-tidy-${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME}ClangTidy.cpp + ) + +target_link_libraries(clang-tidy-${MODULE_NAME} + PRIVATE + ${MODULE_NAME} + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(clang-tidy-${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + ) + +# # Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET clang-tidy-${MODULE_NAME} +# INCLUDES +# ) \ No newline at end of file diff --git a/TestApp/test/Unit/TPC/Clang-Tidy/TPCClangTidy.cpp b/TestApp/test/Unit/TPC/Clang-Tidy/TPCClangTidy.cpp new file mode 100644 index 0000000..0094aa0 --- /dev/null +++ b/TestApp/test/Unit/TPC/Clang-Tidy/TPCClangTidy.cpp @@ -0,0 +1,16 @@ +/** + * @file test/Unit/TPC/Clang-Tidy/TPCClangTidy.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @details This file is only here to enable clang-tidy checks. + */ + +#include +#include \ No newline at end of file diff --git a/TestApp/test/Unit/TPC/TPCTest.cpp b/TestApp/test/Unit/TPC/TPCTest.cpp new file mode 100644 index 0000000..1cd31d6 --- /dev/null +++ b/TestApp/test/Unit/TPC/TPCTest.cpp @@ -0,0 +1,142 @@ + +#include "MessageHandler/MessageHandler.hpp" +#include +#include +#include +#include "TPC/TPC.hpp" + +#include // std::memcmp +#include +#include + +#include +#include +#include +#include +#include + +using ::testing::A; +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; + + +std::vector> getValidITFMessages() +{ + + std::vector> ValidITFMessages = { + {0x40, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0xc5, 0xa}, + {0x40, 0x1, 0x0, 0x0, 0x0, 0xaa, 0xaa, 0x0, 0x59, 0xc6}, + {0x40, 0x2, 0x2, 0x0, 0x0, 0x1, 0x0, 0x0, 0x43, 0x2a}, + {0x60, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5b, 0xed}, + {0x40, 0x3, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x83, 0xd3}, + {0x60, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x55}, + {0x40, 0x4, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x79}, + {0x60, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7e, 0x4c}, + {0x50, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x6e, 0xc, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x2d, 0x95}, + {0x60, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xf4}, + {0x40, 0x6, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x85}, + {0x60, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9d, 0x2c}, + {0x40, 0x7, 0x16, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf1, 0x78}, + {0x40, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x94, 0xef} + + }; + + return ValidITFMessages; +} + +namespace tccinterface +{ + TccInterface *tccInterface = nullptr; +} // namespace tccinterface + + class TPCTest : public testing::Test +{ +public: + osextension::MessageBufferReferenceMock _TestRunnerToTPCMessageBuffer; + osextension::MessageBufferReferenceMock _TPCToTestRunnerMessageBuffer; + tpcprotocol::TPC tpc{}; + message::MessageStructure dataReceivedFromITF{}; + tccinterface::TccInterfaceMock tccInterfaceMock{}; + + virtual void SetUp() + { + tccinterface::tccInterface = &tccInterfaceMock; + tpc.setTPCToTestRunnerMessageBufferReference(_TPCToTestRunnerMessageBuffer); + tpc.setTestRunnerTPCMessageBufferReference(_TestRunnerToTPCMessageBuffer); + } + + virtual void TearDown() + { + tccinterface::tccInterface = nullptr; + } +}; + + TEST_F(TPCTest, ValidateInCorrectITFMessageBunch1) + { + auto ValidITFMessages = getValidITFMessages(); + for (uint8_t i = 0; i < ValidITFMessages.size(); i++) + { + ValidITFMessages[i][ValidITFMessages[i].size() - 1]++; + + etl::array_view ValidITFMessagesView{ValidITFMessages[i].begin(), ValidITFMessages[i].size()}; + auto ret = tpc.validateITFByteStream(ValidITFMessagesView, ValidITFMessagesView.size()); + EXPECT_FALSE(ret); + } + + } + +TEST_F(TPCTest, ValidateCorrectITFMessageTest) +{ + auto ValidITFMessages = getValidITFMessages(); + + for (uint8_t i = 0; i < ValidITFMessages.size(); i++) + { + etl::array_view ValidITFMessagesView{ValidITFMessages[i].begin(), ValidITFMessages[i].size()}; + bool ret = tpc.validateITFByteStream(ValidITFMessagesView, ValidITFMessagesView.size()); + EXPECT_TRUE(ret); + } +} + +TEST_F(TPCTest, SendReportMessageToITF) +{ + const uint8_t nbTestCases = 6u; + + //Input Data + testapp::LogMessageT dataReceivedFromTestRunner[nbTestCases] = { + {testapp::MessageType::MESSAGE_INFO, "INFO: process test TESTCASE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_DEBUG, "DEBUG: process test TESTSUITE_SETUP"}, + {testapp::MessageType::MESSAGE_WARNING, "WARNING: process test TESTSUITE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_ERROR, "ERROR: process test suite TESTCASE_SETUP"}, + {testapp::MessageType::MESSAGE_CRITICAL, "CRITICAL: process test TESTCASE_RUN"}, + {testapp::MessageType::MESSAGE_LOG, "LOG: process test TESTCASE_TEARDOWN"}}; + + message::MessageStructure _ReportMessage[nbTestCases] = { + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[0].text.data())))) + 2), {}}, + {{1, 1, 0}, message::MessageType::Report, 1u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[1].text.data())))) + 2), {}}, + {{2, 1, 0}, message::MessageType::Report, 2u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[2].text.data())))) + 2), {}}, + {{0, 1, 0}, message::MessageType::Report, 3u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[3].text.data())))) + 2), {}}, + {{1, 1, 0}, message::MessageType::Report, 4u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[4].text.data())))) + 2), {}}, + {{2, 1, 0}, message::MessageType::Report, 5u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(dataReceivedFromTestRunner[5].text.data())))) + 2), {}}}; + + for (uint8_t iter = 0; iter < nbTestCases; iter++) + { + message::MessageStructure reportToTPC = _ReportMessage[iter]; + EXPECT_CALL(tccInterfaceMock, send(_)).Times(1); + + tpc.sendReportToITF(reportToTPC, message::messageHeaderSize + reportToTPC.payloadLength); + } +} + +TEST_F(TPCTest, oneCycleTest_ITF_TPC_recieve) +{ + //Input data + dataReceivedFromITF = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 2u, 0u, 0u, {}}; + + EXPECT_CALL(tccInterfaceMock, receive(_, A&>())).Times(1); + EXPECT_CALL(tccInterfaceMock, send(_)).Times(0); + EXPECT_CALL(_TPCToTestRunnerMessageBuffer, send(A(), _)).Times(0); + //function call + tpc.oneCycle(); +} diff --git a/TestApp/test/Unit/TestEnvironment/AssertReportTest.cpp b/TestApp/test/Unit/TestEnvironment/AssertReportTest.cpp new file mode 100644 index 0000000..eef34af --- /dev/null +++ b/TestApp/test/Unit/TestEnvironment/AssertReportTest.cpp @@ -0,0 +1,101 @@ +/** + * @file test/Unit/TestEnvironment/AssertReportTest.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include + +#include + +using namespace testing; +using namespace testapp; +using namespace testenvironment; + +// Test class AssertReportResult_T +TEST(AssertReportResult_TestSuite, AssertReport_setFilename) { // Create Testinstance + char fileName[AssertReport::maxFilenameSize] = ""; + AssertReport assertReport(AssertTypes::ASSERT_TRUE, 0.0, 0.0, RetCode::RETCODE_SUCCESS, 5, + fileName); + + // create testrings with size of AssertReport::maxFilenameSize and + // AssertReport::maxFilenameSize +1 + char longStringMaxSize[AssertReport::maxFilenameSize] = ""; + char longStringExceedsMaxSize[AssertReport::maxFilenameSize + 1] = ""; + + // Fill teststrings with something + for (uint16_t i = 0; i < AssertReport::maxFilenameSize; i++) { + longStringMaxSize[i] = 'x'; + longStringExceedsMaxSize[i] = 'x'; + } + + // Set the termination character '\0' at the end of the string + longStringMaxSize[AssertReport::maxFilenameSize - 1] = '\0'; + longStringExceedsMaxSize[AssertReport::maxFilenameSize] = '\0'; + + // Test path with '/' + ASSERT_EQ(assertReport.setFilename("awesomePath/foo"), + RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(assertReport.getFilename(), "foo"); + + // Test path with mulitiple '/' + ASSERT_EQ(assertReport.setFilename("awesomePath/awesomeSubPath/foo"), + RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(assertReport.getFilename(), "foo"); + + // Test plain fileName + ASSERT_EQ(assertReport.setFilename("foo"), RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(assertReport.getFilename(), "foo"); + + // Test if string is empty + ASSERT_EQ(assertReport.setFilename(""), RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(assertReport.getFilename(), ""); + + // Test when path has AssertReport::maxFilenameSize length + ASSERT_EQ(assertReport.setFilename(longStringMaxSize), + RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(assertReport.getFilename(), longStringMaxSize); + + // Test if path exceeds AssertReport::maxFilenameSize length + ASSERT_EQ(assertReport.setFilename(longStringExceedsMaxSize), + RetCode::RETCODE_INVALID_PARAM); + ASSERT_STREQ(assertReport.getFilename(), + "Filename string not terminated or too long"); +} + +TEST(AssertReportResult_TestSuite, AssertReport_createReport) { + char report[AssertReport::maxReportSize] = ""; + { + AssertReport assertReport(AssertTypes::ASSERT_TRUE, 0.0, 0.0, RetCode::RETCODE_SUCCESS, + 5, ""); + + // Test with empty fileName + ASSERT_EQ(assertReport.createReport(report), RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(report, ":5: Success ASSERT_TRUE input: false"); + } + + AssertReport assertReport(AssertTypes::ASSERT_TRUE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, + ""); + // Test with reasonably sized fileName + assertReport.setFilename("testFileName"); + ASSERT_EQ(assertReport.createReport(report), RetCode::RETCODE_SUCCESS); + ASSERT_STREQ(report, "testFileName:5: Success ASSERT_TRUE input: true"); + + // Test with oversized fileName (leaves not enough room for full report) + char oversizedFilename[250]; + for (uint16_t i = 0; i < 250; i++) { + oversizedFilename[i] = 'x'; + } + oversizedFilename[249] = '\0'; + assertReport.setFilename(oversizedFilename); + ASSERT_EQ(assertReport.createReport(report), RetCode::RETCODE_FAILURE); + ASSERT_STREQ(report, "Report too long"); +} diff --git a/TestApp/test/Unit/TestEnvironment/AssertTest.cpp b/TestApp/test/Unit/TestEnvironment/AssertTest.cpp new file mode 100644 index 0000000..8548e36 --- /dev/null +++ b/TestApp/test/Unit/TestEnvironment/AssertTest.cpp @@ -0,0 +1,337 @@ +/** + * @file test/Unit/TestEnvironment/AssertTest.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ +#include +#include + +#include +#include + +using namespace testing; +using namespace testapp; +using namespace testenvironment; + +class MockLogger : public LoggingInterface { +public: + MockLogger() = default; + MOCK_METHOD(RetCode, logInfo, (const char*), (override)); + MOCK_METHOD(RetCode, logDebug, (const char*), (override)); + MOCK_METHOD(RetCode, logWarning, (const char*), (override)); + MOCK_METHOD(RetCode, logError, (const char*), (override)); + MOCK_METHOD(RetCode, logCritical, (const char*), (override)); + MOCK_METHOD(RetCode, logMessage, (const char*), (override)); + MOCK_METHOD(void, resetFailureFlag, (), (override)); + MOCK_METHOD(bool, getFailureFlag, (), (override)); +}; + +TEST(Assert_TestSuite, test_unititializedLogger) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + + // Test assert_streq when Assert is not initialized + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "abc"), RetCode::RETCODE_UNINITIALIZED); + + // Test logAssert() with uninitialized logger + { + AssertReport AssertReport(AssertTypes::ASSERT_GE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, "testfile"); + ASSERT_EQ(assert.logAssert(AssertReport), RetCode::RETCODE_UNINITIALIZED); + } + + // Test returnReportAndReset() with uninitialized logger + { + AssertReport AssertReport(AssertTypes::ASSERT_GE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, "testfile"); + ASSERT_EQ(assert.logAssert(AssertReport), RetCode::RETCODE_UNINITIALIZED); + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportFailure); + } +} + +TEST(Assert_TestSuite, test_setLogger) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + ASSERT_EQ(assert.setLogger(mocklogger), RetCode::RETCODE_SUCCESS); +} + +TEST(Assert_TestSuite, test_logAssert) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + // Test with oversized fileName (leaves not enough room for full report) + { + char oversizedFilename[250]; + for (uint16_t i = 0; i < 250; i++) { + oversizedFilename[i] = 'x'; + } + oversizedFilename[249] = '\0'; + AssertReport AssertReport(AssertTypes::ASSERT_GE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, oversizedFilename); + ASSERT_EQ(assert.logAssert(AssertReport), RetCode::RETCODE_UNEXPECTED_BEHAVIOR); + } + // Tets successful case + { + AssertReport AssertReport(AssertTypes::ASSERT_GE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, "testfile"); + ASSERT_EQ(assert.logAssert(AssertReport), RetCode::RETCODE_SUCCESS); + } +} + +TEST(Assert_TestSuite, Assert_assert_true) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_TRUE input: true"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_TRUE input: false"))).Times(2); + + // Test equal + ASSERT_EQ(assert.TA_ASSERT_TRUE(2), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_TRUE(2.0f), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_TRUE(0.0f), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_TRUE(true), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_TRUE(false), RetCode::RETCODE_FAILURE); +} + +TEST(Assert_TestSuite, Assert_assert_false) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_FALSE input: false"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_FALSE input: true"))).Times(3); + + ASSERT_EQ(assert.TA_ASSERT_FALSE(2), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_FALSE(2.0f), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_FALSE(0.0f), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_FALSE(true), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_FALSE(false), RetCode::RETCODE_SUCCESS); +} + +TEST(Assert_TestSuite, Assert_assert_eq) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_EQ"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_EQ"))).Times(2); + + // Test equal + ASSERT_EQ(assert.TA_ASSERT_EQ(2, 3), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_EQ(1, 1), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_EQ(42UL, 42UL), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_EQ(42UL, 56UL), RetCode::RETCODE_FAILURE); +} + +TEST(Assert_TestSuite, test_assert_ne) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_NE"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_NE"))).Times(2); + + // Test not equal + ASSERT_EQ(assert.TA_ASSERT_NE(5, 6), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_NE(1, 1), RetCode::RETCODE_FAILURE); + + ASSERT_EQ(assert.TA_ASSERT_NE(42UL, 56UL), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_NE(5UL, 5UL), RetCode::RETCODE_FAILURE); +} + +TEST(Assert_TestSuite, test_assert_lt) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_LT"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_LT"))).Times(4); + + // Test less than + ASSERT_EQ(assert.TA_ASSERT_LT(255, 2), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_LT(3, 5), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_LT(42UL, 42UL), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_LT(24UL, 32UL), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_LT(2.9f, 3.0f), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LT(0.0f, 0.0f), RetCode::RETCODE_FAILURE); + + ASSERT_EQ(assert.TA_ASSERT_LT(-128, -3), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LT(-127, -128), RetCode::RETCODE_FAILURE); +} + +TEST(Assert_TestSuite, test_assert_le) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_LE"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_LE"))).Times(4); + + // Test less than or equal + ASSERT_EQ(assert.TA_ASSERT_LE(0, 0), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LE(5, 7), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LE(255, 3), RetCode::RETCODE_FAILURE); + + ASSERT_EQ(assert.TA_ASSERT_LE(56UL, 56UL), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LE(56UL, 48UL), RetCode::RETCODE_FAILURE); + + ASSERT_EQ(assert.TA_ASSERT_LE(3.9f, 4.0f), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LE(9.0f, 3.0f), RetCode::RETCODE_FAILURE); + + ASSERT_EQ(assert.TA_ASSERT_LE(-128, -7), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_LE(-3, -5), RetCode::RETCODE_FAILURE); +} + +TEST(Assert_TestSuite, test_assert_gt) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_GT"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_GT"))).Times(12); + + // Test greater then + ASSERT_EQ(assert.TA_ASSERT_GT(2.000, 2.001), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(2.000, 2.000), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(2.000, 1.999), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GT(-2.000, -1.999), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(-2.000, -2.000), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(-2.000, -2.001), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GT(0.000, 0.001), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(0.000, 0.000), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(0.000, -0.001), RetCode::RETCODE_SUCCESS); + + int32_t longMinValue = std::numeric_limits::min(); + int32_t longMaxValue = std::numeric_limits::max(); + int32_t longZeroValue = 0; + + ASSERT_EQ(assert.TA_ASSERT_GT(longMaxValue - 1, longMaxValue), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longMaxValue, longMaxValue), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longMaxValue, longMaxValue - 1), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GT(longMinValue, longMinValue + 1), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longMinValue, longMinValue), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longMinValue + 1, longMinValue), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GT(longZeroValue, longZeroValue + 1), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longZeroValue, longZeroValue), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GT(longZeroValue, longZeroValue - 1), RetCode::RETCODE_SUCCESS); +} + +TEST(Assert_TestSuite, test_assert_ge) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success ASSERT_GE"))).Times(0); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_GE"))).Times(6); + + // Test greater equal + ASSERT_EQ(assert.TA_ASSERT_GE(2.000, 2.001), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(2.000, 2.000), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(2.000, 1.999), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GE(-2.000, -1.999), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(-2.000, -2.000), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(-2.000, -2.001), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GE(0.000, 0.001), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(0.000, 0.000), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(0.000, -0.001), RetCode::RETCODE_SUCCESS); + + int32_t longMinValue = std::numeric_limits::min(); + int32_t longMaxValue = std::numeric_limits::max(); + int32_t longZeroValue = 0; + + ASSERT_EQ(assert.TA_ASSERT_GE(longMaxValue - 1, longMaxValue), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(longMaxValue, longMaxValue), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(longMaxValue, longMaxValue - 1), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GE(longMinValue, longMinValue + 1), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(longMinValue, longMinValue), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(longMinValue + 1, longMinValue), RetCode::RETCODE_SUCCESS); + + ASSERT_EQ(assert.TA_ASSERT_GE(longZeroValue, longZeroValue + 1), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_GE(longZeroValue, longZeroValue), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_GE(longZeroValue, longZeroValue - 1), RetCode::RETCODE_SUCCESS); +} + +TEST(Assert_TestSuite, test_assert_streq) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Success"))).Times(0); + // Ensure, that different string variables are passed as arguments to the + // assert Trace function by explicitly checking one complete failed string + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_STREQ input 1: ab input 2: abc"))).Times(1); + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_STREQ input 1: abc"))).Times(3); + EXPECT_CALL(mocklogger, logError(HasSubstr("assert_streq: Input string invalid"))).Times(2); + + // Test string equal + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "abc"), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "abd"), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "abce"), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_STREQ("ab", "abc"), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "\0"), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", NULL), RetCode::RETCODE_NULL_POINTER); + ASSERT_EQ(assert.TA_ASSERT_STREQ(NULL, "abc"), RetCode::RETCODE_NULL_POINTER); +} + +TEST(Assert_TestSuite, test_assert_streq_logger_error) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError).WillOnce(Return(RetCode::RETCODE_FAILURE)); + + // Force call logError in assert_streq. Happens when assert fails + ASSERT_EQ(assert.TA_ASSERT_STREQ("abc", "abdc"), RetCode::RETCODE_UNEXPECTED_BEHAVIOR); +} + +TEST(Assert_TestSuite, returnReportAndReset) { + Assert& assert = Assert::getInstance(); + MockLogger mocklogger; + assert.setLogger(mocklogger); + + EXPECT_CALL(mocklogger, logError(HasSubstr("Failure ASSERT_TRUE input: false"))).Times(1); + EXPECT_CALL(mocklogger, logError(HasSubstr("assert_streq: Input string invalid"))).Times(1); + + // Test with oversized fileName (leaves not enough room for full report) + { + char oversizedFilename[250]; + for (uint16_t i = 0; i < 250; i++) { + oversizedFilename[i] = 'x'; + } + oversizedFilename[249] = '\0'; + AssertReport AssertReport(AssertTypes::ASSERT_GE, 1.0, 1.0, RetCode::RETCODE_SUCCESS, 5, oversizedFilename); + ASSERT_EQ(assert.logAssert(AssertReport), RetCode::RETCODE_UNEXPECTED_BEHAVIOR); + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportFailure); + } + + // Test with failed test + ASSERT_EQ(assert.TA_ASSERT_TRUE(false), RetCode::RETCODE_FAILURE); + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportFailure); + + // Test if report successful after reset + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportSuccess); + + // Test with nullpointer-failed streq + ASSERT_EQ(assert.TA_ASSERT_STREQ(NULL, "ab"), RetCode::RETCODE_NULL_POINTER); + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportFailure); + + // Test with successful test + ASSERT_EQ(assert.TA_ASSERT_STREQ("ab", "ab"), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(assert.returnReportAndReset(), ReportResult::ReportSuccess); +} diff --git a/TestApp/test/Unit/TestEnvironment/CMakeLists.txt b/TestApp/test/Unit/TestEnvironment/CMakeLists.txt new file mode 100644 index 0000000..37bf798 --- /dev/null +++ b/TestApp/test/Unit/TestEnvironment/CMakeLists.txt @@ -0,0 +1,45 @@ +set(MODULE_NAME "TestEnvironment") + +add_executable(${MODULE_NAME}-Test + AssertTest.cpp + AssertReportTest.cpp + LoggingTest.cpp + TestEnvironmentTest.cpp + ) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + MessageHandler + TestApp-Interface + TestEnvironment + etl + MessageBufferReference-Mock + gtest + gmock_main + ) + + # Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + +# Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) + +# # Adds gcov target +# testapp_add_gcovr( +# NAME ${MODULE_NAME} +# SRC ${PROJECT_SOURCE_DIR}/TestApp/src/${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# DEPENDENCIES Run-${MODULE_NAME}-Test +# ) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) diff --git a/TestApp/test/Unit/TestEnvironment/LoggingTest.cpp b/TestApp/test/Unit/TestEnvironment/LoggingTest.cpp new file mode 100644 index 0000000..3225d60 --- /dev/null +++ b/TestApp/test/Unit/TestEnvironment/LoggingTest.cpp @@ -0,0 +1,160 @@ +#include +#include + +#include +#include + +using ::testing::A; +using ::testing::_; + +using namespace testapp; + +TEST(Logging_TestSuite, Logging_ErrorConditions) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + const char* testEmptyMessage = ""; + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + + // Test 1: error conditions + ASSERT_EQ(Logger.logDebug(nullptr), RetCode::RETCODE_NULL_POINTER); + ASSERT_EQ(Logger.logDebug(testDebugMessage), RetCode::RETCODE_FAILURE); + ASSERT_EQ(Logger.setDebugLog(true), RetCode::RETCODE_SUCCESS); + ASSERT_EQ(Logger.logDebug(testEmptyMessage), RetCode::RETCODE_INVALID_PARAM); + ASSERT_EQ(Logger.logDebug(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} + +TEST(Logging_TestSuite, Logging_DebugMessage50) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + // Test 2: send debug message with 50 chars + EXPECT_CALL(mbuf, send(_, _, _)).Times(1); + ASSERT_EQ(Logger.logDebug(testDebugMessage), RetCode::RETCODE_SUCCESS); +} + +TEST(Logging_TestSuite, Logging_DebugMessage100) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testLongMessage = "This is a debug message that is too long to be sent in 1 packet. Will be split to several messages."; + // Test 3: send debug message with 100 chars + EXPECT_CALL(mbuf, send(_, _, _)).Times(1); + ASSERT_EQ(Logger.logDebug(testLongMessage), RetCode::RETCODE_SUCCESS); +} + +TEST(Logging_TestSuite, Logging_CriticalMessage) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testCriticalMessage = "Battery greater 500 degree"; + // send critical message with 27 chars + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + ASSERT_EQ(Logger.logCritical(testCriticalMessage), RetCode::RETCODE_SUCCESS); + + //test with tooLongMessage + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + ASSERT_EQ(Logger.logCritical(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} + +TEST(Logging_TestSuite, Logging_logInfo) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + ASSERT_EQ(Logger.logInfo(testDebugMessage), RetCode::RETCODE_SUCCESS); + + //test with tooLongMessage + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + ASSERT_EQ(Logger.logInfo(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} + +TEST(Logging_TestSuite, Logging_logWarning) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + ASSERT_EQ(Logger.logWarning(testDebugMessage), RetCode::RETCODE_SUCCESS); + + //test with tooLongMessage + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + ASSERT_EQ(Logger.logWarning(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} + +TEST(Logging_TestSuite, Logging_logError) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + ASSERT_EQ(Logger.logError(testDebugMessage), RetCode::RETCODE_SUCCESS); + + //test with tooLongMessage + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + ASSERT_EQ(Logger.logError(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} + +TEST(Logging_TestSuite, Logging_logMessage) { + osextension::MessageBufferReferenceMock mbuf; + + testenvironment::Logging& Logger = testenvironment::Logging::getInstance(); + + ASSERT_EQ(Logger.setMessageBuffer(mbuf), RetCode::RETCODE_SUCCESS); + + const char* testDebugMessage = "This is a test (debug) information. Please ignore"; + + EXPECT_CALL(mbuf, send(_, _, _)).Times(2); + ASSERT_EQ(Logger.logMessage(testDebugMessage), RetCode::RETCODE_SUCCESS); + + //test with tooLongMessage + char testTooLongMessage[256]; + int size = 256; + memset(testTooLongMessage, 'a', size); + testTooLongMessage[255] = '\0'; + ASSERT_EQ(Logger.logMessage(testTooLongMessage), RetCode::RETCODE_OUT_OF_RESOURCES); +} diff --git a/TestApp/test/Unit/TestEnvironment/TestEnvironmentTest.cpp b/TestApp/test/Unit/TestEnvironment/TestEnvironmentTest.cpp new file mode 100644 index 0000000..809e8a4 --- /dev/null +++ b/TestApp/test/Unit/TestEnvironment/TestEnvironmentTest.cpp @@ -0,0 +1,100 @@ +#include "TestEnvironment/TestEnvironment.hpp" +#include "TestCase/TestCaseInterface.hpp" +#include "TestSuite/TestSuiteInterface.hpp" +#include "MessageBufferReferenceMock/MessageBufferReferenceMock.hpp" +#include +#include +#include + +using namespace testapp; + +class MockTestCase : public testcase::TestCaseInterface { +public: + MockTestCase() = default; + MOCK_METHOD(ErrCode, doSetup, (), (override)); + MOCK_METHOD(ErrCode, doTearDown, (), (override)); + MOCK_METHOD(ErrCode, runTest, (), (override)); +}; + +class MockTestSuite : public testsuite::TestSuiteInterface { +public: + MockTestSuite() = default; + MOCK_METHOD(ErrCode, doSuiteSetup, (), (override)); + MOCK_METHOD(ErrCode, doSuiteTearDown, (), (override)); + MOCK_METHOD(testcase::TestCaseInterface*, createTestCase, (testapp::TestIDType testCaseID, void* memoryLocation, size_t memorySize), (override)); +}; + +// Custom action to cast the arg0 of the message buffer receive function to TestExecutionMessage. +ACTION_P(AssignMyType, param) { *static_cast(arg0) = param; } + +TEST(testEnvironment, test_runOneCycle_testSuite) { + testenvironment::TestEnvironment testEnvironment; + MockTestSuite testSuite; + message::TestExecutionMessage msg; + msg.testSuiteOrCase = &testSuite; + + // Set up message buffer. + osextension::MessageBufferReferenceMock TestRunnerToTestEnvMock, TestEnvToTestRunnerMock; + testEnvironment.registerTestRunnerToTestEnvMessageBufferReference(TestRunnerToTestEnvMock); + testEnvironment.registerTestEnvToTestRunnerMessageBufferReference(TestEnvToTestRunnerMock); + + // Check call doSuiteSetup. + msg.subType = message::MessageSubType::TESTSUITE_SETUP; + EXPECT_CALL(TestRunnerToTestEnvMock, receive(testing::_, sizeof(message::TestExecutionMessage), portMAX_DELAY)) + .WillOnce(testing::DoAll(AssignMyType(msg), testing::Return(sizeof(message::TestExecutionMessage)))); + EXPECT_CALL(TestEnvToTestRunnerMock, send(testing::A(), sizeof(message::MessageStructure), testing::_)).Times(1); + EXPECT_CALL(testSuite, doSuiteSetup()).Times(1); + testEnvironment.runOneCycle(); + + // Check calls doSuiteTearDown. + msg.subType = message::MessageSubType::TESTSUITE_TEARDOWN; + EXPECT_CALL(TestRunnerToTestEnvMock, receive(testing::_, sizeof(message::TestExecutionMessage), portMAX_DELAY)) + .WillOnce(testing::DoAll(AssignMyType(msg), testing::Return(sizeof(message::TestExecutionMessage)))); + EXPECT_CALL(TestEnvToTestRunnerMock, send(testing::A(), sizeof(message::MessageStructure), testing::_)).Times(1); + EXPECT_CALL(testSuite, doSuiteTearDown()).Times(1); + testEnvironment.runOneCycle(); +} + +TEST(testEnvironment, test_runOneCycle_testCase) { + testenvironment::TestEnvironment testEnvironment; + MockTestCase testCase; + message::TestExecutionMessage msg; + msg.testSuiteOrCase = &testCase; + + // Set up message buffer. + osextension::MessageBufferReferenceMock TestRunnerToTestEnvMock, TestEnvToTestRunnerMock; + testEnvironment.registerTestRunnerToTestEnvMessageBufferReference(TestRunnerToTestEnvMock); + testEnvironment.registerTestEnvToTestRunnerMessageBufferReference(TestEnvToTestRunnerMock); + + // Check call doSetup. + msg.subType = message::MessageSubType::TESTCASE_SETUP; + EXPECT_CALL(TestRunnerToTestEnvMock, receive(testing::_, sizeof(message::TestExecutionMessage), portMAX_DELAY)) + .WillOnce(testing::DoAll(AssignMyType(msg), testing::Return(sizeof(message::TestExecutionMessage)))); + EXPECT_CALL(TestEnvToTestRunnerMock, send(testing::A(), sizeof(message::MessageStructure), testing::_)).Times(1); + EXPECT_CALL(testCase, doSetup()).Times(1); + testEnvironment.runOneCycle(); + + // Check call runTest. + msg.subType = message::MessageSubType::TESTCASE_RUN; + EXPECT_CALL(TestRunnerToTestEnvMock, receive(testing::_, sizeof(message::TestExecutionMessage), portMAX_DELAY)) + .WillOnce(testing::DoAll(AssignMyType(msg), testing::Return(sizeof(message::TestExecutionMessage)))); + EXPECT_CALL(TestEnvToTestRunnerMock, send(testing::A(), sizeof(message::MessageStructure), testing::_)).Times(1); + EXPECT_CALL(testCase, runTest()).Times(1); + testEnvironment.runOneCycle(); + + // Check calls doTearDown. + msg.subType = message::MessageSubType::TESTCASE_TEARDOWN; + EXPECT_CALL(TestRunnerToTestEnvMock, receive(testing::_, sizeof(message::TestExecutionMessage), portMAX_DELAY)) + .WillOnce(testing::DoAll(AssignMyType(msg), testing::Return(sizeof(message::TestExecutionMessage)))); + EXPECT_CALL(TestEnvToTestRunnerMock, send(testing::A(), sizeof(message::MessageStructure), testing::_)).Times(1); + EXPECT_CALL(testCase, doTearDown()).Times(1); + testEnvironment.runOneCycle(); +} + +TEST(testEnvironment, test_runOneCycle_noBuffer) { + testenvironment::TestEnvironment testEnvironment; + MockTestSuite testSuite; + + EXPECT_CALL(testSuite, doSuiteSetup()).Times(0); + testEnvironment.runOneCycle(); +} diff --git a/TestApp/test/Unit/TestRegistry/CMakeLists.txt b/TestApp/test/Unit/TestRegistry/CMakeLists.txt new file mode 100644 index 0000000..33200bc --- /dev/null +++ b/TestApp/test/Unit/TestRegistry/CMakeLists.txt @@ -0,0 +1,54 @@ +set(MODULE_NAME "TestRegistry") + +add_executable(${MODULE_NAME}-Test + ${MODULE_NAME}Test.cpp + ) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + TestApp-Interface + etl + gtest + gmock_main + TestEnvironment + ) + +# Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + + +set(MODULE_NAME "TestApp") +add_library(clang-tidy-${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/TestRegistryClangTidy.cpp + ) + +target_link_libraries(clang-tidy-${MODULE_NAME} + PUBLIC + ${MODULE_NAME}-Interface + MessageHandler + TestEnvironment +) + +#Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +# set_target_properties(clang-tidy-${MODULE_NAME} +# PROPERTIES +# CLANG_TIDY_CHECKS_C_TARGET "" +# CLANG_TIDY_CHECKS_CXX_TARGET "" +# CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" +# CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" +# ) + +# # Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET clang-tidy-${MODULE_NAME} +# INCLUDES +# ) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME TestRegistry +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/TestRegistry ${PROJECT_SOURCE_DIR}/TestApp/include/TestFactory ${PROJECT_SOURCE_DIR}/TestApp/include/TestSuite ${PROJECT_SOURCE_DIR}/TestApp/include/TestCase +# ) diff --git a/TestApp/test/Unit/TestRegistry/TestCases.cpp b/TestApp/test/Unit/TestRegistry/TestCases.cpp new file mode 100644 index 0000000..e9d0b11 --- /dev/null +++ b/TestApp/test/Unit/TestRegistry/TestCases.cpp @@ -0,0 +1,70 @@ +#include "TestSuites.cpp" +#include + +DECLARE_TESTCASE(TestSuite1, Ts1Tc1, 1) + uint8_t _data[100]; + + testapp::ErrCode doSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode runTest() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doTearDown() override { + return testapp::ErrCode::SUCCESS; + } + +END_TESTCASE(TestCase1) + +DECLARE_TESTCASE(TestSuite1, Ts1Tc2, 2) + uint8_t _data[100]; + + testapp::ErrCode doSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode runTest() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doTearDown() override { + return testapp::ErrCode::SUCCESS; + } + +END_TESTCASE(TestCase1) + +DECLARE_TESTCASE(TestSuite2, Ts2Tc1, 1) + uint8_t _data[100]; + + testapp::ErrCode doSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode runTest() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doTearDown() override { + return testapp::ErrCode::SUCCESS; + } + +END_TESTCASE(TestCase1) + +DECLARE_TESTCASE(TestSuite2, Ts2Tc2, 2) + uint8_t _data[100]; + + testapp::ErrCode doSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode runTest() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doTearDown() override { + return testapp::ErrCode::SUCCESS; + } + +END_TESTCASE(TestCase1) diff --git a/TestApp/test/Unit/TestRegistry/TestRegistryClangTidy.cpp b/TestApp/test/Unit/TestRegistry/TestRegistryClangTidy.cpp new file mode 100644 index 0000000..4989be3 --- /dev/null +++ b/TestApp/test/Unit/TestRegistry/TestRegistryClangTidy.cpp @@ -0,0 +1,25 @@ +/** + * @file test/Unit/TestRegistryClangTidy.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @details This file is only here to enable clang-tidy checks. + */ +#include + +#include +#include + +#include +#include + +#include +#include + +#include \ No newline at end of file diff --git a/TestApp/test/Unit/TestRegistry/TestRegistryTest.cpp b/TestApp/test/Unit/TestRegistry/TestRegistryTest.cpp new file mode 100644 index 0000000..952845f --- /dev/null +++ b/TestApp/test/Unit/TestRegistry/TestRegistryTest.cpp @@ -0,0 +1,80 @@ +#include +#include "TestCases.cpp" + +#include // std::memcmp +#include // TO be removed + +#include +#include + +TEST(TestRegistryTest, TestExistance) { + TestRegistry &testRegistry = TestRegistry::getInstance(); + { + auto tsi = testRegistry.loadTestSuite(1); + ASSERT_TRUE(tsi); + auto tci = testRegistry.loadTestCase(1, 1); + ASSERT_TRUE(tci); + tci = testRegistry.loadTestCase(1, 2); + ASSERT_TRUE(tci); + } + { + auto tsi = testRegistry.loadTestSuite(2); + ASSERT_TRUE(tsi); + auto tci = testRegistry.loadTestCase(2, 1); + ASSERT_TRUE(tci); + tci = testRegistry.loadTestCase(2, 2); + ASSERT_TRUE(tci); + } +} + +TEST(TestRegistryTest, TestNonExistance) { + TestRegistry &testRegistry = TestRegistry::getInstance(); + { + for(int i = 4; i < 50; ++i) { + auto tsi = testRegistry.loadTestSuite(i); + ASSERT_FALSE(tsi); + } + } + { + auto tsi = testRegistry.loadTestSuite(1); + ASSERT_TRUE(tsi); + for(int i = 4; i < 50; ++i) { + auto tci = testRegistry.loadTestCase(1, i); + ASSERT_FALSE(tci); + } + } + { + auto tsi = testRegistry.loadTestSuite(2); + ASSERT_TRUE(tsi); + for(int i = 4; i < 50; ++i) { + auto tci = testRegistry.loadTestCase(2, i); + ASSERT_FALSE(tci); + } + } +} + +TEST(TestRegistryTest, TestSimpleDataMember) { + TestRegistry &testRegistry = TestRegistry::getInstance(); + { + auto tsi = testRegistry.loadTestSuite(1); + ASSERT_TRUE(tsi); + TestSuite1& ts = *static_cast(tsi); + for(size_t i = 0; i < sizeof(ts._data); ++i) + ts._data[i] = i; + } + { + auto tsi = testRegistry.loadTestSuite(2); + ASSERT_TRUE(tsi); + TestSuite2& ts = *static_cast(tsi); + for(size_t i = 0; i < sizeof(ts._data); ++i) + EXPECT_EQ(ts._data[i], i); + } +} + +TEST(TestRegistryTest, TestOverCapacity) { + TestRegistry &testRegistry = TestRegistry::getInstance(); + { + auto tsi = testRegistry.loadTestSuite(3); + ASSERT_FALSE(tsi); + } +} diff --git a/TestApp/test/Unit/TestRegistry/TestSuites.cpp b/TestApp/test/Unit/TestRegistry/TestSuites.cpp new file mode 100644 index 0000000..addf74b --- /dev/null +++ b/TestApp/test/Unit/TestRegistry/TestSuites.cpp @@ -0,0 +1,35 @@ +#include +#include + +DECLARE_TESTSUITE(TestSuite1, 1) + testapp::ErrCode doSuiteSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doSuiteTearDown() override { + return testapp::ErrCode::SUCCESS; + } + uint8_t _data[TestRegistry::testSuiteMemoryCapacity()]; +END_TESTSUITE(TestSuite1) + +DECLARE_TESTSUITE(TestSuite2, 2) + testapp::ErrCode doSuiteSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doSuiteTearDown() override { + return testapp::ErrCode::SUCCESS; + } + uint8_t _data[TestRegistry::testSuiteMemoryCapacity()]; +END_TESTSUITE(TestSuite2) + +DECLARE_TESTSUITE(TestSuite3, 3) + testapp::ErrCode doSuiteSetup() override { + return testapp::ErrCode::SUCCESS; + } + + testapp::ErrCode doSuiteTearDown() override { + return testapp::ErrCode::SUCCESS; + } + uint8_t _data[TestRegistry::testSuiteMemoryCapacity()+1]; +END_TESTSUITE(TestSuite2) diff --git a/TestApp/test/Unit/TestRunner/CMakeLists.txt b/TestApp/test/Unit/TestRunner/CMakeLists.txt new file mode 100644 index 0000000..5648690 --- /dev/null +++ b/TestApp/test/Unit/TestRunner/CMakeLists.txt @@ -0,0 +1,43 @@ +set(MODULE_NAME "TestRunner") + +add_executable(${MODULE_NAME}-Test + TestRunnerTest.cpp + ) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + MessageHandler + TestApp-Interface + TestRunner + TestEnvironment + OsAdapter # includes freertos-Mock + MessageBufferReference-Mock + gtest + gmock_main + ) + +# Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + +# # Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) + +# # Adds gcov target +# testapp_add_gcovr( +# NAME ${MODULE_NAME} +# SRC ${PROJECT_SOURCE_DIR}/TestApp/src/${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# DEPENDENCIES Run-${MODULE_NAME}-Test +# ) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) diff --git a/TestApp/test/Unit/TestRunner/TestRunnerTest.cpp b/TestApp/test/Unit/TestRunner/TestRunnerTest.cpp new file mode 100644 index 0000000..6ee7b8d --- /dev/null +++ b/TestApp/test/Unit/TestRunner/TestRunnerTest.cpp @@ -0,0 +1,395 @@ +#include +#include + +#include +#include + +#include +#include "testSuite1.cpp" + +#include // std::memcmp +#include +#include + +using ::testing::A; +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::DoAll; + +// Test fixture class +class TestRunnerTest : public testing::Test +{ +public: + osextension::MessageBufferReferenceMock TestEnvironmentToTestRunnerMessageBuffer; + osextension::MessageBufferReferenceMock TPCToTestRunnerMessageBuffer; + osextension::MessageBufferReferenceMock TestRunnerToTestEnvironmentMessageBuffer; + osextension::MessageBufferReferenceMock TestRunnerToTPCMessageBuffer; + + QueueSetMemberHandle_t queueSetMemberHandle; + testrunner::TestRunner testRunner{}; + message::TestExecutionMessage dataSentToTestEnv{}; + message::MessageStructure dataReceivedFromTestEnv{}; + message::MessageStructure dataReceivedFromTPC{}; + message::MessageStructure reportToTPC{}; + testapp::ErrCode retErrCode; + + virtual void SetUp() + { + freertos::mock = std::make_unique(); + testRunner.setTestEnvironmentToTestRunnerMessageBufferReference(TestEnvironmentToTestRunnerMessageBuffer); + testRunner.setTPCToTestRunnerMessageBufferReference(TPCToTestRunnerMessageBuffer); + testRunner.setTestRunnerToTestEnvironmentMessageBufferReference(TestRunnerToTestEnvironmentMessageBuffer); + testRunner.setTestRunnerToTPCMessageBufferReference(TestRunnerToTPCMessageBuffer); + + dataSentToTestEnv.subType = message::MessageSubType::TEST_NOTIMPLEMENTED; + dataSentToTestEnv.testSuiteOrCase = nullptr; + retErrCode = testapp::ErrCode::ERRCODE_UNDEFINED; + } + + virtual void TearDown() + { + freertos::mock.reset(nullptr); + } +}; + +TEST_F(TestRunnerTest, processTestCmdTestSuiteSetup) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTSUITE_SETUP; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdTestCaseSetup) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTCASE_SETUP; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdTestSuiteRun) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTSUITE_RUN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdTestCaseRun) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTCASE_RUN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdTestCaseRunTestSuiteTearDown) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTSUITE_TEARDOWN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdTestCaseRunTestCaseTearDown) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::SUCCESS; + // expected Message subType to be sent to TestEnvironment + message::MessageSubType expectedMessageSubType = message::MessageSubType::TESTCASE_TEARDOWN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + EXPECT_EQ(expectedMessageSubType, dataSentToTestEnv.subType); + + // check pointer to TestSuite Or TestCase + EXPECT_NE((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdWrongMessageSubType) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::ACKNOWLEDGEMENT, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::FAILURE; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(0); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); +} + +TEST_F(TestRunnerTest, processTestCmdUndefinedTestSuite) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 3u, 1u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::ERRCODE_TEST_SUITE_UNKNOWN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(0); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + + // check pointer to TestSuite Or TestCase + EXPECT_EQ((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, processTestCmdUndefinedTestCase) +{ + // Input data + message::MessageStructure command = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 3u, 0u, {}}; + // expected returned code + testapp::ErrCode expectedErrCode = testapp::ErrCode::ERRCODE_TEST_CASE_UNKNOWN; + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(0); + + retErrCode = testRunner.processTestCmd(command, dataSentToTestEnv); + EXPECT_EQ(expectedErrCode, retErrCode); + + // check pointer to TestSuite Or TestCase + EXPECT_EQ((void *)0, dataSentToTestEnv.testSuiteOrCase); +} + +TEST_F(TestRunnerTest, writeLogTest) +{ + const uint8_t nbTestCases = 6u; + + // Input Data + testapp::LogMessageT _logsFromTestEnv[nbTestCases] = { + {testapp::MessageType::MESSAGE_INFO, "INFO: process test TESTCASE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_DEBUG, "DEBUG: process test TESTSUITE_SETUP"}, + {testapp::MessageType::MESSAGE_WARNING, "WARNING: process test TESTSUITE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_ERROR, "ERROR: process test suite TESTCASE_SETUP"}, + {testapp::MessageType::MESSAGE_CRITICAL, "CRITICAL: process test TESTCASE_RUN"}, + {testapp::MessageType::MESSAGE_LOG, "LOG: process test TESTCASE_TEARDOWN"}}; + + message::MessageStructure _dataReceivedFromTestEnv{}; + + message::MessageStructure _dataReceivedFromTPC[nbTestCases] = { + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}, + {{1, 0, 0}, message::MessageType::Command, 1u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{2, 0, 0}, message::MessageType::Command, 2u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{0, 0, 0}, message::MessageType::Command, 3u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, 0u, {}}, + {{1, 0, 0}, message::MessageType::Command, 4u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, 0u, {}}, + {{2, 0, 0}, message::MessageType::Command, 5u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}}; + + // expected report + message::MessageStructure expectedLogMessage[nbTestCases] = { + {{0, 3, 0}, message::MessageType::Log, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[0].text.data()))))), {}}, + {{1, 3, 0}, message::MessageType::Log, 1u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[1].text.data()))))), {}}, + {{2, 3, 0}, message::MessageType::Log, 2u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[2].text.data()))))), {}}, + {{0, 3, 0}, message::MessageType::Log, 3u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[3].text.data()))))), {}}, + {{1, 3, 0}, message::MessageType::Log, 4u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[4].text.data()))))), {}}, + {{2, 3, 0}, message::MessageType::Log, 5u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[5].text.data()))))), {}}}; + + for(uint8_t iter = 0; iter < nbTestCases; iter++) + { + // set input data + _dataReceivedFromTestEnv.messageType = message::MessageType::Log; + _dataReceivedFromTestEnv.payloadLength = (uint8_t)strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[iter].text.data())))); + memcpy(_dataReceivedFromTestEnv.payload.data(), + _logsFromTestEnv[iter].text.data(), + _dataReceivedFromTestEnv.payload.size()); + + dataReceivedFromTestEnv = _dataReceivedFromTestEnv; + dataReceivedFromTPC = _dataReceivedFromTPC[iter]; + + EXPECT_CALL(TestRunnerToTPCMessageBuffer, send(A(), _, _)).Times(1).WillOnce(Return(sizeof(dataReceivedFromTestEnv))); + + // function call + testRunner.sendReport(dataReceivedFromTestEnv, dataReceivedFromTPC); + + EXPECT_TRUE(0 == memcmp(&expectedLogMessage[iter].headerInfo, &dataReceivedFromTestEnv.headerInfo, sizeof(dataReceivedFromTestEnv.headerInfo))); + EXPECT_EQ(expectedLogMessage[iter].errorCode, dataReceivedFromTestEnv.errorCode); + EXPECT_EQ(expectedLogMessage[iter].messageType, dataReceivedFromTestEnv.messageType); + EXPECT_EQ(expectedLogMessage[iter].testSuite, dataReceivedFromTestEnv.testSuite); + EXPECT_EQ(expectedLogMessage[iter].testCase, dataReceivedFromTestEnv.testCase); + EXPECT_EQ(expectedLogMessage[iter].token, dataReceivedFromTestEnv.token); + EXPECT_EQ(expectedLogMessage[iter].payloadLength, dataReceivedFromTestEnv.payloadLength); + EXPECT_TRUE(0 == memcmp(&dataReceivedFromTestEnv.payload[0], _logsFromTestEnv[iter].text.data(), sizeof(dataReceivedFromTestEnv.payload.size()))); + } +} + +TEST_F(TestRunnerTest, reportTest) +{ + const uint8_t nbTestCases = 6u; + + // Input Data + testapp::LogMessageT _logsFromTestEnv[nbTestCases] = { + {testapp::MessageType::MESSAGE_INFO, "INFO: process test TESTCASE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_DEBUG, "DEBUG: process test TESTSUITE_SETUP"}, + {testapp::MessageType::MESSAGE_WARNING, "WARNING: process test TESTSUITE_TEARDOWN"}, + {testapp::MessageType::MESSAGE_ERROR, "ERROR: process test suite TESTCASE_SETUP"}, + {testapp::MessageType::MESSAGE_CRITICAL, "CRITICAL: process test TESTCASE_RUN"}, + {testapp::MessageType::MESSAGE_LOG, "LOG: process test TESTCASE_TEARDOWN"}}; + + message::MessageStructure _MessageFromTPC[nbTestCases] = { + {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}, + {{1, 0, 0}, message::MessageType::Command, 1u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{2, 0, 0}, message::MessageType::Command, 2u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}, + {{0, 0, 0}, message::MessageType::Command, 3u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, 0u, {}}, + {{1, 0, 0}, message::MessageType::Command, 4u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, 0u, {}}, + {{2, 0, 0}, message::MessageType::Command, 5u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, 0u, {}}}; + + + message::MessageStructure _ReportMessage[nbTestCases] = { + {{0, 1, 0}, message::MessageType::Report, 0u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[0].text.data())))) + 2), {}}, + {{1, 1, 0}, message::MessageType::Report, 1u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[1].text.data())))) + 2), {}}, + {{2, 1, 0}, message::MessageType::Report, 2u, message::MessageSubType::TESTSUITE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[2].text.data())))) + 2), {}}, + {{0, 1, 0}, message::MessageType::Report, 3u, message::MessageSubType::TESTCASE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[3].text.data())))) + 2), {}}, + {{1, 1, 0}, message::MessageType::Report, 4u, message::MessageSubType::TESTCASE_RUN, testapp::ErrCode::SUCCESS, 0u, 1u, 2u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[4].text.data())))) + 2), {}}, + {{2, 1, 0}, message::MessageType::Report, 5u, message::MessageSubType::TESTCASE_TEARDOWN, testapp::ErrCode::SUCCESS, 0u, 1u, 1u, (uint8_t)(strlen(const_cast(static_cast(static_cast(_logsFromTestEnv[5].text.data())))) + 2), {}}}; + + for(uint8_t iter = 0; iter < nbTestCases; iter++) + { + // set input data + reportToTPC = _ReportMessage[iter]; + dataReceivedFromTPC = _MessageFromTPC[iter]; + + EXPECT_CALL(TestRunnerToTPCMessageBuffer, send(A(), _, _)).Times(1).WillOnce(Return(sizeof(reportToTPC))); + + // function call + testRunner.sendReport(reportToTPC, dataReceivedFromTPC); + } +} + +ACTION_P(MessageStructureType, param) { *static_cast(arg0) = param; } + +TEST_F(TestRunnerTest, oneCycleTest_TPC_Runner_Env) +{ + // Input data + dataReceivedFromTPC = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}; + + uint8_t fakeHandle = 1u; + QueueHandle_t fakeQueueHandleOne = (QueueHandle_t)&fakeHandle; + + EXPECT_CALL(TPCToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(Return(fakeQueueHandleOne)); + + EXPECT_CALL(*freertos::mock, xQueueSelectFromSet(_, _)).WillOnce(Return(fakeQueueHandleOne)); + EXPECT_CALL(TPCToTestRunnerMessageBuffer, receive(_, sizeof(dataReceivedFromTPC), portMAX_DELAY)) + .WillOnce(DoAll(MessageStructureType(dataReceivedFromTPC), Return(sizeof(dataReceivedFromTPC)))); + + EXPECT_CALL(TestRunnerToTestEnvironmentMessageBuffer, send(A(), _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + // function call + testRunner.oneCycle(queueSetMemberHandle); +} + +TEST_F(TestRunnerTest, oneCycleTest_TPC_Runner_TPC) +{ + // Input data + dataReceivedFromTPC = {{0, 0, 0}, message::MessageType::Command, 0u, message::MessageSubType::TESTSUITE_SETUP, testapp::ErrCode::SUCCESS, 0u, 2u, 0u, 0u, {}}; + + uint8_t fakeHandle = 1u; + QueueHandle_t fakeQueueHandleOne = (QueueHandle_t)&fakeHandle; + + EXPECT_CALL(TPCToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(Return(fakeQueueHandleOne)); + + EXPECT_CALL(*freertos::mock, xQueueSelectFromSet(_, _)).WillOnce(Return(fakeQueueHandleOne)); + EXPECT_CALL(TPCToTestRunnerMessageBuffer, receive(_, sizeof(dataReceivedFromTPC), portMAX_DELAY)) + .WillOnce(DoAll(MessageStructureType(dataReceivedFromTPC), Return(sizeof(dataReceivedFromTPC)))); + + EXPECT_CALL(TestRunnerToTPCMessageBuffer, send(A(), _, _)).Times(1).WillOnce(Return(sizeof(dataSentToTestEnv))); + + // function call + testRunner.oneCycle(queueSetMemberHandle); +} + +ACTION_P(MessageFromTestEnvironmentType, param) { *static_cast(arg0) = param; } + +TEST_F(TestRunnerTest, oneCycleTest_Env_Runner_TPC) +{ + // Input data (Log from TestEnvironment) + testapp::LogMessageT _logsFromTestEnv{testapp::MessageType::MESSAGE_INFO, "INFO: process test TESTCASE_TEARDOWN"}; + + dataReceivedFromTestEnv = {{0, 3, 0}, message::MessageType::Log, 0u, message::MessageSubType::RESERVED, testapp::ErrCode::SUCCESS, 0u, 1u, 0u, 0u, {}}; + dataReceivedFromTestEnv.payloadLength = sizeof(_logsFromTestEnv.text); + std::copy(&_logsFromTestEnv.text[0], &_logsFromTestEnv.text[0] + sizeof(_logsFromTestEnv.text),dataReceivedFromTestEnv.payload.data()); + + uint8_t fakeHandle = 1u; + QueueHandle_t fakeQueueHandleOne = (QueueHandle_t)&fakeHandle; + + EXPECT_CALL(TPCToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(TestEnvironmentToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(Return(fakeQueueHandleOne)); + + EXPECT_CALL(*freertos::mock, xQueueSelectFromSet(_, _)).WillOnce(Return(fakeQueueHandleOne)); + EXPECT_CALL(TestEnvironmentToTestRunnerMessageBuffer, receive(_, sizeof(dataReceivedFromTestEnv), portMAX_DELAY)) + .WillOnce(DoAll(MessageFromTestEnvironmentType(dataReceivedFromTestEnv), Return(sizeof(dataReceivedFromTestEnv)))); + + EXPECT_CALL(TestRunnerToTPCMessageBuffer, send(A(), _, _)).Times(1).WillOnce(Return(sizeof(reportToTPC))); + + // function call + testRunner.oneCycle(queueSetMemberHandle); +} + +TEST_F(TestRunnerTest, oneCycleTest_nullHandleForQueueSet) +{ + EXPECT_CALL(TPCToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(TestEnvironmentToTestRunnerMessageBuffer, getHandleForQueueSet()).WillRepeatedly(nullptr); + EXPECT_CALL(*freertos::mock, xQueueSelectFromSet(_, _)).WillOnce(Return(nullptr)); + + // function call + testRunner.oneCycle(queueSetMemberHandle); +} diff --git a/TestApp/test/Unit/TestRunner/testSuite1.cpp b/TestApp/test/Unit/TestRunner/testSuite1.cpp new file mode 100644 index 0000000..155c0f1 --- /dev/null +++ b/TestApp/test/Unit/TestRunner/testSuite1.cpp @@ -0,0 +1,105 @@ +/** + * @file examples/test_suite/test_suite_1.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include +#include +#include + +// Test Suite DummyTestSuite1 +DECLARE_TESTSUITE(DummyTestSuite1, 1) +testapp::ErrCode doSuiteSetup() override { + + logging.logInfo("TestSuite 1 - 1st doSuiteSetup Hello TestApp!"); + logging.logDebug("TestSuite 1 - 2nd Log message"); + logging.logWarning("TestSuite 1 - 3rd Log message"); + assertion.TA_ASSERT_TRUE(true); + // send error and critical log (test sould fail) + logging.logError("TestSuite 1 - error Log-message"); + logging.logCritical("TestSuite 1 - critical error Log-message"); + logging.logInfo("TestSuite 1 doSuiteSetup should fail"); + + return returnReportAndReset(); +} + +testapp::ErrCode doSuiteTearDown() override { + + logging.logInfo("TestSuite 1 doSuiteTearDown"); + assertion.TA_ASSERT_TRUE(true); + assertion.TA_ASSERT_FALSE(false); + assertion.TA_ASSERT_EQ(5,5); + assertion.TA_ASSERT_NE(5,4); + + logging.logInfo("TestSuite 1 doSuiteTearDown should pass successfully"); + + return returnReportAndReset(); +} +END_TESTSUITE(DummyTestSuite1) + +// Tast Suite : DummyTestSuite1 +// Test Case : TestCase1 +DECLARE_TESTCASE(DummyTestSuite1, TestCase1, 1) + +testapp::ErrCode doSetup() override { + + logging.logInfo("TestCase 1 doSetup"); + assertion.TA_ASSERT_LT(5,6); + assertion.TA_ASSERT_LE(6,6); + assertion.TA_ASSERT_STREQ("message1", "message1"); + logging.logInfo("TestCase 1 doSetup should pass successfully"); + + return returnReportAndReset(); +} + +testapp::ErrCode runTest() override { + + logging.logInfo("TestCase 1 runTest"); + assertion.TA_ASSERT_GT(7,6); + assertion.TA_ASSERT_GE(7,7); + logging.logInfo("TestCase 1 runTest should pass successfully"); + + return returnReportAndReset(); +} + +testapp::ErrCode doTearDown() override { + logging.logInfo("TestCase 1 doTearDown"); + + assertion.TA_ASSERT_STREQ("message1", "message2"); + logging.logInfo("TestCase 1 doTearDown should fail"); + + return returnReportAndReset(); +} + +END_TESTCASE(TestCase1) + +// Test Suite : DummyTestSuite1 +// Test Case : TestCase2 +DECLARE_TESTCASE(DummyTestSuite1, TestCase2, 2) + +testapp::ErrCode doSetup() override { + logging.logInfo("TestCase 2 Starting Mars"); + return returnReportAndReset(); +} + +testapp::ErrCode runTest() override { + logging.logInfo("TestCase 2 Hello Mars"); + return returnReportAndReset(); +} + +testapp::ErrCode doTearDown() override { + logging.logInfo("TestCase 2 Ending Mars"); + return returnReportAndReset(); +} + +END_TESTCASE(TestCase2) diff --git a/TestApp/test/Unit/Tier1competetion/CMakeLists.txt b/TestApp/test/Unit/Tier1competetion/CMakeLists.txt new file mode 100644 index 0000000..f891a37 --- /dev/null +++ b/TestApp/test/Unit/Tier1competetion/CMakeLists.txt @@ -0,0 +1,40 @@ +set(MODULE_NAME "Tier1competetion") + +add_executable(${MODULE_NAME}-Test + ${MODULE_NAME}Test.cpp + ) + +target_include_directories(${MODULE_NAME}-Test + PUBLIC + ${PROJECT_SOURCE_DIR}/TestApp/include) + +target_link_libraries(${MODULE_NAME}-Test + PUBLIC + Tier1competetion + gtest + gmock_main + MessageBufferReference-Mock + etl + ) + + # Adds test executable as well target to run the test and generate test results +testapp_add_test( + MODULE_NAME ${MODULE_NAME} + ) + +# # Adds gcov target +# testapp_add_gcovr( +# NAME ${MODULE_NAME} +# SRC ${PROJECT_SOURCE_DIR}/TestApp/src/${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# DEPENDENCIES Run-${MODULE_NAME}-Test +# ) + +# Add clang-tidy scans +add_subdirectory(Clang-Tidy) + +# # Adds doxygen target +# testapp_add_doxygen( +# NAME ${MODULE_NAME} +# INCLUDES ${PROJECT_SOURCE_DIR}/TestApp/include/${MODULE_NAME} +# ) diff --git a/TestApp/test/Unit/Tier1competetion/Clang-Tidy/CMakeLists.txt b/TestApp/test/Unit/Tier1competetion/Clang-Tidy/CMakeLists.txt new file mode 100644 index 0000000..e88fdcc --- /dev/null +++ b/TestApp/test/Unit/Tier1competetion/Clang-Tidy/CMakeLists.txt @@ -0,0 +1,28 @@ +set(MODULE_NAME "Tier1competetion") + +add_library(clang-tidy-${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_NAME}ClangTidy.cpp + ) + +target_link_libraries(clang-tidy-${MODULE_NAME} + PRIVATE + ${MODULE_NAME} + ) + +# Checks/Options must be provided as semicolon-separated list inside a string, e.g. "-cppcoreguidelines-non-private-member-variables-in-classes;-misc-non-private-member-variables-in-classes" +set_target_properties(clang-tidy-${MODULE_NAME} + PROPERTIES + CLANG_TIDY_CHECKS_C_TARGET "" + CLANG_TIDY_CHECKS_CXX_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_C_TARGET "" + CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET "" + #enable the option once all warnings are resolved + #CLANG_TIDY_WARNINGS_AS_ERRORS "*" + ) + +# # Adds clang-tidy target +# testapp_setup_clang_tidy_target( +# MODULE_NAME ${MODULE_NAME} +# PARENT_TARGET clang-tidy-${MODULE_NAME} +# INCLUDES +# ) \ No newline at end of file diff --git a/TestApp/test/Unit/Tier1competetion/Clang-Tidy/Tier1competetionClangTidy.cpp b/TestApp/test/Unit/Tier1competetion/Clang-Tidy/Tier1competetionClangTidy.cpp new file mode 100644 index 0000000..ae7947e --- /dev/null +++ b/TestApp/test/Unit/Tier1competetion/Clang-Tidy/Tier1competetionClangTidy.cpp @@ -0,0 +1,15 @@ +/** + * @file * @file test/Unit/Tier1competetion/Clang-Tidy/Tier1competetionClangTidy.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @details This is needed to be able to scan Tier1competetion.hpp with clang-tidy + */ + +#include diff --git a/TestApp/test/Unit/Tier1competetion/Tier1competetionTest.cpp b/TestApp/test/Unit/Tier1competetion/Tier1competetionTest.cpp new file mode 100644 index 0000000..e488956 --- /dev/null +++ b/TestApp/test/Unit/Tier1competetion/Tier1competetionTest.cpp @@ -0,0 +1,76 @@ +/** + * @file test/MessageHandler/MessageHandlerTest.cpp + * + * @copyright Copyright 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * @author Aymen Bouldjadj + */ + +#include +#include + +#include +#include +#include + +#include +#include + +using ::testing::_; +using ::testing::A; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Return; +// Test fixture class +class Tier1competetionTest : public testing::Test { +public: + osextension::MessageBufferReferenceMock + Tier1competetionToDashboardMessageBuffer; + + tier1competetion::Tier1competetion tier1competetion{}; + demoapptypes::CompetetionMessageT dataSentToDashboard{}; + + virtual void SetUp() { + freertos::mock = std::make_unique(); + tier1competetion.registerTier1competetionToDashboardMessageBufferReference( + Tier1competetionToDashboardMessageBuffer); + } + + virtual void TearDown() { freertos::mock.reset(nullptr); } +}; + +TEST_F(Tier1competetionTest, processTestCmdTestSuiteSetup) { + std::vector vec; + demoapptypes::Race race(vec); + race += demoapptypes::Athlete{"Athlete1", 21.56, 0}; + race += demoapptypes::Athlete{"Athlete2", 21.42, 0}; + race += demoapptypes::Athlete{"Athlete3", 21.52, 0}; + race += demoapptypes::Athlete{"Athlete4", 21.15, 0}; + race += demoapptypes::Athlete{"Athlete5", 21.52, 0}; + race += demoapptypes::Athlete{"Athlete6", 21.25, 0}; + race += demoapptypes::Athlete{"Athlete7", 21.22, 0}; + race += demoapptypes::Athlete{"Athlete8", 21.13, 0}; + race.round = {demoapptypes::Laps::_50M, demoapptypes::Style::FREESTYLE}; + + std::copy_n(race.getAthletes().begin(), demoapptypes::LANES, + dataSentToDashboard.todashboard.begin()); + dataSentToDashboard.competerionTier = demoapptypes::Tier::TierA; + dataSentToDashboard.round = {demoapptypes::Laps::_50M, + demoapptypes::Style::FREESTYLE}; + + EXPECT_CALL(Tier1competetionToDashboardMessageBuffer, + send(A(), _, _)) + .Times(1) + .WillOnce(Return(sizeof(dataSentToDashboard))); + + tier1competetion.StartRace(race); + tier1competetion.oneCycle(); +} + +TEST(Test1, Tier1competetionTest2) { EXPECT_TRUE(1); } diff --git a/cmake/TestAppMemoryUsage.cmake b/cmake/TestAppMemoryUsage.cmake new file mode 100644 index 0000000..b963480 --- /dev/null +++ b/cmake/TestAppMemoryUsage.cmake @@ -0,0 +1,22 @@ +find_package(Python3 3.7 COMPONENTS Interpreter REQUIRED) + +add_custom_command( + OUTPUT + ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.html + ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.csv + DEPENDS + ${PROJECT_NAME}.elf + COMMAND + ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/tools/MemoryUsage/memory_usage.py + --directory ${PROJECT_BINARY_DIR} + --map-file ${PROJECT_BINARY_DIR}/Platform/stm32l562qe/src/System/${PROJECT_NAME}.map + --html ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.html --csv ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.csv + COMMENT "Generating html and csv" +) + +add_custom_target(memory_usage + COMMENT "Analyzing Memory Usage..." + DEPENDS + ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.html + ${PROJECT_BINARY_DIR}/memory_usage/memory_usage.csv +) diff --git a/cmake/testAppAddDoxygen.cmake b/cmake/testAppAddDoxygen.cmake new file mode 100644 index 0000000..a5e16bd --- /dev/null +++ b/cmake/testAppAddDoxygen.cmake @@ -0,0 +1,66 @@ +include(config/doxygen) + +# Doxygen documentation +# \brief Creates doxygen top level and module targets +# \param[in] If not defined top level target Doxygen-testapp is created otherwise the module related target Doxygen-${name}. +function(testapp_add_doxygen) + set(options) + set(oneValueArgs NAME) + set(multiValueArgs INCLUDES) + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + # Find required packages + find_package(Doxygen REQUIRED) + + if(DOXYGEN_FOUND) + # set the path for doxygen output + set(TESTAPP_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/integration-testapp/doxygen") + + if(ARG_NAME) + if(EXISTS "${PROJECT_SOURCE_DIR}/TestApp/src/${ARG_NAME}") + set(DOXYGEN_SRC_DIR "${DOXYGEN_SRC_DIR}" "${PROJECT_SOURCE_DIR}/TestApp/src/${ARG_NAME}") + endif() + if(EXISTS "${PROJECT_SOURCE_DIR}/TestApp/include/${ARG_NAME}") + set(DOXYGEN_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/TestApp/include/${ARG_NAME}") + endif() + set(DOXYGEN_SRC_DIR "${DOXYGEN_SRC_DIR}" ${ARG_INCLUDES}) + set(DOXYGEN_INCLUDE_DIR "${DOXYGEN_INCLUDE_DIR}" ${ARG_INCLUDES}) + set(DOXYGEN_TARGET_NAME "doxygen-${ARG_NAME}") + set(DOXYGEN_OUTPUT_DIRECTORY "${TESTAPP_DOXYGEN_OUTPUT_DIR}/${ARG_NAME}") + set(DOXYGEN_WARN_LOGFILE "${TESTAPP_DOXYGEN_OUTPUT_DIR}/${ARG_NAME}/doxygen.log") + set(DOXYGEN_COMMENT "Generate doxygen documentation for module ${ARG_NAME}") + else() + # Set Doxygen project-specific parameters + set(DOXYGEN_SRC_DIR ${PROJECT_SOURCE_DIR}/TestApp/src ${PROJECT_SOURCE_DIR}/TestApp/include) + set(DOXYGEN_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/TestApp/include) + set(DOXYGEN_TARGET_NAME "doxygen-all") + set(DOXYGEN_OUTPUT_DIRECTORY "${TESTAPP_DOXYGEN_OUTPUT_DIR}/all") + set(DOXYGEN_WARN_LOGFILE "${TESTAPP_DOXYGEN_OUTPUT_DIR}/all/doxygen.log") + set(DOXYGEN_COMMENT "Generate doxygen documentation") + set(DOXYGEN_GENERATE_TAGFILE "${DOXYGEN_OUTPUT_DIRECTORY}/TestApp.tag") + endif() + set(DOXYGEN_FILE_PATTERNS ${DOXYGEN_FILE_PATTERNS} "*.md") + set(DOXYGEN_STRIP_FROM_PATH "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}") + set(DOXYGEN_HAVE_DOT "YES") + set(DOXYGEN_DOTFILE_DIRS "${PROJECT_BINARY_DIR}/integration-testapp/dot/cmake") + set(DOXYGEN_IMAGE_PATH "${PROJECT_BINARY_DIR}/integration-testapp/dot/cmake") + # Create doxygen target + doxygen_add_docs(${DOXYGEN_TARGET_NAME} + ${DOXYGEN_SRC_DIR} + ${DOXYGEN_INCLUDE_DIR} + COMMENT ${DOXYGEN_COMMENT} + ) + + if(TESTAPP_STANDALONE_BUILD) + add_custom_command(TARGET ${DOXYGEN_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -DINPUT_FILE=${DOXYGEN_WARN_LOGFILE} -P ${PROJECT_SOURCE_DIR}/cmake/testAppReadFileAndPrintContent.cmake + ) + endif() + + if(ARG_NAME) + add_dependencies(doxygen-testapp ${DOXYGEN_TARGET_NAME}) + endif() + endif() +endfunction() + diff --git a/cmake/testAppAddGcovr.cmake b/cmake/testAppAddGcovr.cmake new file mode 100644 index 0000000..2afde7b --- /dev/null +++ b/cmake/testAppAddGcovr.cmake @@ -0,0 +1,44 @@ +if(NOT BASH_PATH) + message(FATAL_ERROR "testAppAddGcovr.cmake: cannot find bash path") +endif() + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# \brief Creates gcovr top level and module targets +# \param[in] Top-level-Target: none +# \param[in] NAME module name defined in the corresponding CMakeLists.txt +# \param[in] DEPENDENCIS list of targets that must be executed to generate all required analysis and object files +if(NOT TARGET gcovr-all) + add_custom_target(gcovr-all + COMMAND ${Python3_EXECUTABLE} -m gcovr --version + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/integration-testapp/gcovr/all + COMMAND ${BASH_PATH} -c "find ${PROJECT_BINARY_DIR}/CMakeFiles -name '*.gcno' -exec rm {} +" + COMMAND ${Python3_EXECUTABLE} -m gcovr --branches -r ${PROJECT_SOURCE_DIR} --object-directory ${PROJECT_BINARY_DIR} -f ${PROJECT_SOURCE_DIR}/TestApp/src -f ${PROJECT_SOURCE_DIR}/TestApp/include --html-details -o ${PROJECT_BINARY_DIR}/integration-testapp/gcovr/all/index.html --html-title integration-testapp + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS run-unit-tests-all + ) +endif() + + +function(testapp_add_gcovr) + set(options) + set(oneValueArgs NAME) + set(multiValueArgs DEPENDENCIES SRC INCLUDES) + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + if (ARG_NAME) + set(GCOVR_TARGET_NAME gcovr-${ARG_NAME}) + list(TRANSFORM ARG_INCLUDES PREPEND -f\;) + list(TRANSFORM ARG_SRC PREPEND -f\;) + + add_custom_target(${GCOVR_TARGET_NAME} + COMMAND ${Python3_EXECUTABLE} -m gcovr --version + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/integration-testapp/gcovr/${ARG_NAME} + COMMAND ${BASH_PATH} -c "find ${PROJECT_BINARY_DIR}/CMakeFiles -name '*.gcno' -exec rm {} +" + COMMAND ${Python3_EXECUTABLE} -m gcovr --branches -r ${PROJECT_SOURCE_DIR} --object-directory ${PROJECT_BINARY_DIR} ${ARG_SRC} ${ARG_INCLUDES} --html-details -o ${PROJECT_BINARY_DIR}/integration-testapp/gcovr/${ARG_NAME}/index.html --html-title ${ARG_NAME} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${ARG_DEPENDENCIES} + ) + endif() +endfunction() \ No newline at end of file diff --git a/cmake/testAppAddTest.cmake b/cmake/testAppAddTest.cmake new file mode 100644 index 0000000..c5ca00a --- /dev/null +++ b/cmake/testAppAddTest.cmake @@ -0,0 +1,43 @@ +# Create a target to build-only all unit tests +add_custom_target(build-unit-tests-all) + +# Create a target to run all unit tests +if(RUNNING_UNITTESTS_WITH_EMULATOR) + # create a target without appending commands since the emulator invokes it's own test suit + add_custom_target(run-unit-tests-all) +else() + add_custom_target(run-unit-tests-all + COMMAND ${CMAKE_CTEST_COMMAND} + COMMAND gcc --version + ) +endif() + +# \brief Creates a test executable and a target to execute the test and to generate the gtest results as xml-format +# \param[in] MODULE_NAME module name defined in the corresponding CMakeLists.txt +# \param[in] XML_PATH Path to put the XML file, that contains the test results +function(testapp_add_test) + set(options) + set(oneValueArgs MODULE_NAME XML_PATH) + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + if (ARG_MODULE_NAME) + # Generate mapfile for test target when using GNU toolchain (gnu-gcc or arm-none-eabi-gcc) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_options(${ARG_MODULE_NAME}-Test + PRIVATE + "-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${ARG_MODULE_NAME}-Test.map" + ) + endif() + + add_test(NAME ${ARG_MODULE_NAME}-Test COMMAND ./${ARG_MODULE_NAME}-Test) + + add_custom_target( + Run-${ARG_MODULE_NAME}-Test + COMMAND ${ARG_MODULE_NAME}-Test --gtest_color=yes + DEPENDS ${ARG_MODULE_NAME}-Test + ) + + add_dependencies(run-unit-tests-all Run-${ARG_MODULE_NAME}-Test) + add_dependencies(build-unit-tests-all ${ARG_MODULE_NAME}-Test) + endif() +endfunction() diff --git a/cmake/testAppBuildFreeRtos.cmake b/cmake/testAppBuildFreeRtos.cmake new file mode 100644 index 0000000..e429f90 --- /dev/null +++ b/cmake/testAppBuildFreeRtos.cmake @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.11) + +# Optional FreeRTOS-Plus modules +set(FREERTOS_PLUS_CLI OFF CACHE BOOL "Support for FreeRTOS-PLUS-CLI") + +if(${ARCH} STREQL "GCC") + set(FREERTOS_BASE ${CMAKE_CURRENT_BINARY_DIR}/FreeRTOS/Source) +set(FREERTOS_PLUS_BASE ${CMAKE_CURRENT_LIST_DIR}/FreeRTOS-Plus/Source) + +set(FREERTOS_SOURCES + ${FREERTOS_BASE}/croutine.c + ${FREERTOS_BASE}/event_groups.c + ${FREERTOS_BASE}/list.c + ${FREERTOS_BASE}/queue.c + ${FREERTOS_BASE}/stream_buffer.c + ${FREERTOS_BASE}/tasks.c + ${FREERTOS_BASE}/timers.c + ) + +# Declare empty lists to make sure they can be appended by optional FreeRTOS-Plus modules +set(FREERTOS_PLUS_SOURCES "") +set(FREERTOS_PLUS_INCLUDES "") + +if(FREERTOS_ENABLE_MPU) + list(APPEND FREERTOS_SOURCES ${FREERTOS_BASE}/portable/Common/mpu_wrappers.c) +endif() + +if(NOT DEFINED FREERTOS_PORT) + message(FATAL_ERROR "No FreeRTOS port defined") +endif(NOT DEFINED FREERTOS_PORT) + +if(NOT DEFINED FREERTOS_PORT_BASE) + set(FREERTOS_PORT_BASE ${FREERTOS_BASE}/portable) +endif(NOT DEFINED FREERTOS_PORT_BASE) + +if (NOT EXISTS "${FREERTOS_PORT_BASE}") + message(FATAL_ERROR "FreeRTOS port directory ${FREERTOS_PORT_BASE} not found") +endif() + +# Append base dir to source files +foreach(f ${FREERTOS_PORT_SOURCES}) + list(APPEND FREERTOS_PORT_SOURCES_WITH_PORT_BASE "${FREERTOS_PORT_BASE}/${FREERTOS_PORT}/${f}") +endforeach(f) + +if (FREERTOS_PLUS_CLI) + list(APPEND FREERTOS_PLUS_SOURCES ${FREERTOS_PLUS_BASE}/FreeRTOS-Plus-CLI/FreeRTOS_CLI.c) + list(APPEND FREERTOS_PLUS_INCLUDES ${FREERTOS_PLUS_BASE}/FreeRTOS-Plus-CLI/) +endif() + +# Create hal library +add_library(freertos + ${FREERTOS_SOURCES} + ${FREERTOS_PLUS_SOURCES} + ${FREERTOS_PORT_SOURCES_WITH_PORT_BASE} + ) + +target_include_directories(freertos SYSTEM + PUBLIC + ${FREERTOS_BASE}/include + ${FREERTOS_PORT_BASE}/${FREERTOS_PORT} + ${FREERTOS_CONF} + ${FREERTOS_PLUS_INCLUDES} + ) + +target_link_libraries(freertos + PRIVATE + ${FREERTOS_LINK_LIBRARIES} + ) + +add_library(freertos-Interface INTERFACE) + +target_include_directories(freertos-Interface + INTERFACE + ${FREERTOS_BASE}/include + ${FREERTOS_PORT_BASE}/${FREERTOS_PORT} + ${FREERTOS_CONF} + ${FREERTOS_PLUS_INCLUDES} + ) + +add_subdirectory(mock EXCLUDE_FROM_ALL) + +unset(FREERTOS_SOURCES) diff --git a/cmake/testAppGtestOutputParser.cmake b/cmake/testAppGtestOutputParser.cmake new file mode 100644 index 0000000..b4f131f --- /dev/null +++ b/cmake/testAppGtestOutputParser.cmake @@ -0,0 +1,20 @@ +# Parses gtest xml output into a JUnit readable format +# \param MODULE_NAME Name of the module that should be displayed +# \param XML_PATH Path to the XML file +# \param PARENT_TARGET Target that executes the custom command +function(testapp_gtest_xml_output_parser) + set(options) + set(oneValueArgs MODULE_NAME XML_PATH PARENT_TARGET) + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + find_package(Python3 COMPONENTS Interpreter REQUIRED) + + # GTEST_XML_PARSER_PATH is set within testAppPullDependencies.cmake after download + add_custom_command( + TARGET ${ARG_PARENT_TARGET} + POST_BUILD + COMMAND ${Python3_EXECUTABLE} ${GTEST_XML_PARSER_PATH} -x ${ARG_XML_PATH} -m ${ARG_MODULE_NAME} + ) + +endfunction() diff --git a/cmake/testAppPullDependencies.cmake b/cmake/testAppPullDependencies.cmake new file mode 100644 index 0000000..f4c93c0 --- /dev/null +++ b/cmake/testAppPullDependencies.cmake @@ -0,0 +1,185 @@ +# Download depedencies (in case we're build Common stand-alone) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + # I am top-level project. + include(FetchContent) + + set(EXTERNAL_LOCATION ${PROJECT_BINARY_DIR}/external CACHE STRING "Download location for externals") + set(EXTERNAL_DOWNLOAD ON CACHE STRING "Enable download for externals") + set(EXTERNAL_DOWNLOAD_ALL OFF CACHE STRING "Enable download of all externals compiler ID independent") + + get_filename_component(EXTERNAL_LOCATION ${EXTERNAL_LOCATION} ABSOLUTE BASE_DIR ${PROJECT_BINARY_DIR}) + + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + set(DOWNLOAD_ARM_EXTERNALS ON CACHE STRING "Enable download for arm externals") + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")) + set(DOWNLOAD_GCC_EXTERNALS ON CACHE STRING "Enable download for gcc externals") + endif() + + if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) + set(DOWNLOAD_CLANG_EXTERNALS ON CACHE STRING "Enable download for clang externals") + endif() + + # TODO download all external repositories only once and checkout the required tags into different folders. + if(EXTERNAL_DOWNLOAD_ALL) + set(DOWNLOAD_GCC_EXTERNALS ON CACHE STRING "Enable download for gcc externals") + set(DOWNLOAD_GHS_EXTERNALS ON CACHE STRING "Enable download for ghs externals") + set(DOWNLOAD_CLANG_EXTERNALS ON CACHE STRING "Enable download for clang externals") + set(DOWNLOAD_ARM_EXTERNALS ON CACHE STRING "Enable download for arm externals") + endif() + + if(NOT EXTERNAL_DOWNLOAD) + set(FETCHCONTENT_FULLY_DISCONNECTED ON) + + get_filename_component(FETCHCONTENT_SOURCE_DIR_GOOGLETEST ${EXTERNAL_LOCATION}/googletest-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_ETL ${EXTERNAL_LOCATION}/etl-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_FREERTOS_ARM ${EXTERNAL_LOCATION}/arm/FreeRTOS-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_FREERTOS_GCC ${EXTERNAL_LOCATION}/FreeRTOS-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_SYSTEMCOMMON ${EXTERNAL_LOCATION}/SystemCommon-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_STM32F401RE ${EXTERNAL_LOCATION}/stm32cubef4-src ABSOLUTE) + get_filename_component(FETCHCONTENT_SOURCE_DIR_RTT ${EXTERNAL_LOCATION}/RTT-src ABSOLUTE) + + endif() + + # Googletest + set(GTEST_ARM_SOURCE_DIR ${EXTERNAL_LOCATION}/arm/googletest-src) + set(GTEST_ALL_BINARY_DIR ${PROJECT_BINARY_DIR}/external/googletest-build) + + + # Googletest for ARM + if(DOWNLOAD_ARM_EXTERNALS AND RUNNING_UNITTESTS_WITH_EMULATOR) + FetchContent_Declare( + googletest_arm_ghs + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 + SOURCE_DIR ${GTEST_ARM_SOURCE_DIR} + BINARY_DIR ${GTEST_ALL_BINARY_DIR} + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/googletest-subbuild + ) + FetchContent_GetProperties(googletest_arm_ghs) + FetchContent_Populate(googletest_arm_ghs) + add_subdirectory(${GTEST_ARM_SOURCE_DIR} ${GTEST_ALL_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + if((DOWNLOAD_GCC_EXTERNALS) OR (DOWNLOAD_CLANG_EXTERNALS)) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.1 + SOURCE_DIR ${EXTERNAL_LOCATION}/googletest-src + BINARY_DIR ${GTEST_ALL_BINARY_DIR} + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/googletest-subbuild + ) + FetchContent_GetProperties(googletest) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${GTEST_ALL_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + # FreeRTOS + set(FREERTOS_ARM_SOURCE_DIR ${EXTERNAL_LOCATION}/arm/FreeRTOS-src) + set(FREERTOS_ALL_BINARY_DIR ${PROJECT_BINARY_DIR}/external/FreeRTOS-build) + + if(DOWNLOAD_ARM_EXTERNALS AND NOT RUNNING_UNITTESTS_WITH_EMULATOR) + # FreeRTOS for ARM + FetchContent_Declare( + freertos_arm + GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS.git + GIT_TAG V10.3.0 + SOURCE_DIR ${FREERTOS_ARM_SOURCE_DIR} + BINARY_DIR ${FREERTOS_ALL_BINARY_DIR} + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/FreeRTOS-subbuild + ) + + FetchContent_GetProperties(freertos_arm) + FetchContent_Populate(freertos_arm) + + set(FREERTOS_CONF ${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/FreeRTOS) + set(FREERTOS_PORT GCC/ARM_CM4F) + #set(FREERTOS_LINK_LIBRARIES) + set(FREERTOS_PORT_SOURCES port.c) + endif() + + if(DOWNLOAD_GCC_EXTERNALS OR DOWNLOAD_CLANG_EXTERNALS OR RUNNING_UNITTESTS_WITH_EMULATOR) + # FreeRTOS from SystemCommon + FetchContent_Declare( + freertos_gcc + GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS.git + GIT_TAG V10.3.0 + SOURCE_DIR ${EXTERNAL_LOCATION}/FreeRTOS-src + BINARY_DIR ${FREERTOS_ALL_BINARY_DIR} + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/FreeRTOS-subbuild + ) + + FetchContent_GetProperties(freertos_gcc) + FetchContent_Populate(freertos_gcc) + + set(FREERTOS_CONF ${PROJECT_SOURCE_DIR}/Platform/hostMachine-gcc/config/FreeRTOS) + set(FREERTOS_PORT NullPort) + set(FREERTOS_LINK_LIBRARIES) + set(FREERTOS_PORT_SOURCES) + endif() + + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + + add_subdirectory(${PROJECT_SOURCE_DIR}/external/FreeRTOS ${FREERTOS_ALL_BINARY_DIR} EXCLUDE_FROM_ALL) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GHS") + # add_subdirectory(${freertos_ghs_SOURCE_DIR} ${FREERTOS_ALL_BINARY_DIR} EXCLUDE_FROM_ALL) #TODO cleanup PPC Platform (freeRtos should be pulled and configured as an external dependency) + else() + add_subdirectory(${PROJECT_SOURCE_DIR}/external/FreeRTOS ${FREERTOS_ALL_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + # stm32cubef4 + if(DOWNLOAD_ARM_EXTERNALS AND MCU STREQUAL "STM32F401RE") + # stm32cubef4 + FetchContent_Declare( + stm32cubef4 + GIT_REPOSITORY https://github.com/STMicroelectronics/STM32CubeF4.git + GIT_TAG v1.27.1 + SOURCE_DIR ${EXTERNAL_LOCATION}/stm32cubef4-src + BINARY_DIR ${PROJECT_BINARY_DIR}/external/stm32cubef4-build + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/stm32cubef4-subbuild + ) + FetchContent_GetProperties(stm32cubef4) + FetchContent_Populate(stm32cubef4) + endif() + + if(MCU STREQUAL "STM32F401RE") + configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/hal.cmake ${PROJECT_BINARY_DIR}/external/stm32cubef4-src/cmake/hal.cmake) + configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/cmsis.cmake ${PROJECT_BINARY_DIR}/external/stm32cubef4-src/cmake/cmsis.cmake) + configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/usb-device.cmake ${PROJECT_BINARY_DIR}/external/stm32cubef4-src/cmake/usb-device.cmake) + configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/CMakeLists.txt ${PROJECT_BINARY_DIR}/external/stm32cubef4-src/CMakeLists.txt) + + set(stm32cubef4_DEVICE ${MCU}) + set(stm32cubef4_CONF ${PROJECT_SOURCE_DIR}/Platform/stm32cubef4/config/stm32cubeF4) + # workaround to move doxygen.cmake file to the path referred to by tps "stm32cubef4". + # TODO: decide whether if an stm32cubef4 tag (with the corresponding new path) should be created, create an external folder and add the file, or use the following to move it there at build time. + # configure_file(${toolchain_SOURCE_DIR}/cmake/config/doxygen.cmake ${PROJECT_SOURCE_DIR}/external/Toolchain/cmake/config/doxygen.cmake) + + add_subdirectory(${stm32cubef4_SOURCE_DIR} ${stm32cubef4_BINARY_DIR} EXCLUDE_FROM_ALL) + + endif() + + # rtt is not pulled is still a dependency + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + set(RTT_CONF ${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/RTT) + add_subdirectory(${PROJECT_SOURCE_DIR}/external/Segger-RTT EXCLUDE_FROM_ALL) + endif() + + FetchContent_Declare( + etl + GIT_REPOSITORY https://github.com/ETLCPP/etl + GIT_TAG 18.1.3 + SOURCE_DIR ${EXTERNAL_LOCATION}/etl-src + BINARY_DIR ${PROJECT_BINARY_DIR}/external/etl-build + SUBBUILD_DIR ${PROJECT_BINARY_DIR}/external/etl-subbuild + ) + FetchContent_GetProperties(etl) + FetchContent_Populate(etl) + + add_subdirectory(${etl_SOURCE_DIR} ${etl_BINARY_DIR} EXCLUDE_FROM_ALL) + target_compile_definitions(etl INTERFACE -DETL_ARRAY_VIEW_IS_MUTABLE) + + # etl config to be provided by target-platform + target_include_directories(etl INTERFACE ${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/etl) +endif() diff --git a/cmake/testAppReadFileAndPrintContent.cmake b/cmake/testAppReadFileAndPrintContent.cmake new file mode 100644 index 0000000..b28d27e --- /dev/null +++ b/cmake/testAppReadFileAndPrintContent.cmake @@ -0,0 +1,5 @@ +# /brief Read file and print content to console +# /param[in] INPUT_FILE Path to file +file(READ "${INPUT_FILE}" FILE_OUTPUT) +message("Print content of file: ${INPUT_FILE}") +message("${FILE_OUTPUT}") diff --git a/cmake/testAppSetupClangTidy.cmake b/cmake/testAppSetupClangTidy.cmake new file mode 100644 index 0000000..33410b7 --- /dev/null +++ b/cmake/testAppSetupClangTidy.cmake @@ -0,0 +1,132 @@ +# Check if clang-tidy is available +find_program(CLANG_TIDY_PATH clang-tidy) + +if(CLANG_TIDY_PATH) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(CLANG_TIDY_EXTRA_ARGS "-extra-arg=--stdlib=libstdc++" "-extra-arg=--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() + # Include BES3 clang-tidy default options + include(config/clang-tidy) +else() + message(FATAL_ERROR "Cannot find clang-tidy path") +endif() + +if(NOT TARGET clang-tidy-all) + add_custom_target(clang-tidy-all COMMAND clang-tidy --version) +endif() + +# clang-based tools ignore response files like @CMakeFiles/include.rsp (https://reviews.llvm.org/D34440) +# Deactivate response files for msys so clang-tidy works correctly. +# If you encounter include problems when using clang-tidy and use another generator, +# add it to the condition below. +# Be aware: For long commands (more than 8191 characters), the command line limit can be a problem! +if(CMAKE_GENERATOR MATCHES ".*Makefile.*") + set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES OFF) + set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES OFF) + #set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS OFF) + #set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_LIBRARIES OFF) +endif() + +# \brief Create +# \param[in] TARGET name of variable to list of targets +# \param[in] MODULE_NAME name of the module that should be scanned +# \param[in] PARENT_TARGET name of the parent target from which the properties are derived +# \param[in] INCLUDES path of all includes needed by Clang-Tidy target +function(testapp_setup_clang_tidy_target) + set(options "") + set(oneValueArgs MODULE_NAME PARENT_TARGET INCLUDES) + set(multiValueArgs "") + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + set(CLANG_TIDY_TARGET_NAME clang-tidy-${ARG_MODULE_NAME}) + if(NOT TARGET ${CLANG_TIDY_TARGET_NAME}) + get_target_property(TARGET_SOURCES ${ARG_PARENT_TARGET} SOURCES) + if(TARGET_SOURCES) + add_library(${CLANG_TIDY_TARGET_NAME} + ${TARGET_SOURCES} + ) + endif() + + get_target_property(MODULE_COMPILE_FLAGS ${ARG_PARENT_TARGET} COMPILE_FLAGS) + if(MODULE_COMPILE_FLAGS) + set_target_properties(${CLANG_TIDY_TARGET_NAME} + PROPERTIES + COMPILE_FLAGS ${MODULE_COMPILE_FLAGS} + ) + endif() + + get_target_property(TARGET_LIBRARIES ${ARG_PARENT_TARGET} LINK_LIBRARIES) + if(TARGET_LIBRARIES) + target_link_libraries(${CLANG_TIDY_TARGET_NAME} + PRIVATE + ${TARGET_LIBRARIES} + ) + endif() + + get_target_property(TARGET_INCLUDES ${ARG_PARENT_TARGET} INCLUDE_DIRECTORIES) + if(TARGET_INCLUDES) + target_include_directories(${CLANG_TIDY_TARGET_NAME} + PUBLIC + ${TARGET_INCLUDES} ${ARG_INCLUDES} + ) + endif() + + get_target_property(TARGET_COMPILE_FEATURES ${ARG_PARENT_TARGET} COMPILE_FEATURES) + if(TARGET_COMPILE_FEATURES) + target_compile_features(${CLANG_TIDY_TARGET_NAME} + PRIVATE + ${TARGET_COMPILE_FEATURES} + ) + endif() + + get_target_property(TARGET_COMPILE_DEFINITIONS ${ARG_PARENT_TARGET} COMPILE_DEFINITIONS) + if(TARGET_COMPILE_DEFINITIONS) + add_compile_definitions(${TARGET_COMPILE_DEFINITIONS}) + endif() + endif() + if(CLANG_TIDY_PATH) + # Get additional checks specific for target + get_property(CLANG_TIDY_CHECKS_C_TARGET TARGET ${ARG_PARENT_TARGET} PROPERTY CLANG_TIDY_CHECKS_C_TARGET) + get_property(CLANG_TIDY_CHECKS_CXX_TARGET TARGET ${ARG_PARENT_TARGET} PROPERTY CLANG_TIDY_CHECKS_CXX_TARGET) + get_property(CLANG_TIDY_CHECK_OPTIONS_C_TARGET TARGET ${ARG_PARENT_TARGET} PROPERTY CLANG_TIDY_CHECK_OPTIONS_C_TARGET) + get_property(CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET TARGET ${ARG_PARENT_TARGET} PROPERTY CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET) + + set(CLANG_TIDY_WARNINGS_AS_ERRORS "-warnings-as-errors=") + get_property(CLANG_TIDY_WARNINGS_AS_ERRORS_TARGET TARGET ${ARG_PARENT_TARGET} PROPERTY CLANG_TIDY_WARNINGS_AS_ERRORS) + if(CLANG_TIDY_WARNINGS_AS_ERRORS_TARGET) + set(CLANG_TIDY_WARNINGS_AS_ERRORS "-warnings-as-errors=${CLANG_TIDY_WARNINGS_AS_ERRORS_TARGET}") + endif() + + # Combine global and target settings + set(CLANG_TIDY_CHECKS_C_COMBINED ${CLANG_TIDY_CHECKS_C} ${CLANG_TIDY_CHECKS_C_TARGET}) + set(CLANG_TIDY_CHECKS_CXX_COMBINED ${CLANG_TIDY_CHECKS_CXX} ${CLANG_TIDY_CHECKS_CXX_TARGET}) + set(CLANG_TIDY_CHECK_OPTIONS_C_COMBINED ${CLANG_TIDY_CHECK_OPTIONS_C} ${CLANG_TIDY_CHECK_OPTIONS_C_TARGET}) + set(CLANG_TIDY_CHECK_OPTIONS_CXX_COMBINED ${CLANG_TIDY_CHECK_OPTIONS_CXX} ${CLANG_TIDY_CHECK_OPTIONS_CXX_TARGET}) + + # Join settings + list(JOIN CLANG_TIDY_CHECKS_C_COMBINED "," CLANG_TIDY_CHECKS_C_COMBINED_JOINED) + list(JOIN CLANG_TIDY_CHECKS_CXX_COMBINED "," CLANG_TIDY_CHECKS_CXX_COMBINED_JOINED) + list(JOIN CLANG_TIDY_CHECK_OPTIONS_C_COMBINED "," CLANG_TIDY_CHECK_OPTIONS_C_COMBINED_JOINED) + list(JOIN CLANG_TIDY_CHECK_OPTIONS_CXX_COMBINED "," CLANG_TIDY_CHECK_OPTIONS_CXX_COMBINED_JOINED) + + set(CLANG_TIDY_HEADER_FILTER "(${PROJECT_SOURCE_DIR}\/TestApp\/include.*)|(${PROJECT_SOURCE_DIR}\/TestApp\/src.*)") + + set(CLANG_TIDY_CMD_C "${CLANG_TIDY_PATH}" "-header-filter=${CLANG_TIDY_HEADER_FILTER}" "${CLANG_TIDY_WARNINGS_AS_ERRORS}" "-checks=${CLANG_TIDY_CHECKS_C_COMBINED_JOINED}" "-config={CheckOptions: [ ${CLANG_TIDY_CHECK_OPTIONS_C_COMBINED_JOINED} ]}" ${CLANG_TIDY_EXTRA_ARGS}) + set(CLANG_TIDY_CMD_CXX "${CLANG_TIDY_PATH}" "-header-filter=${CLANG_TIDY_HEADER_FILTER}" "${CLANG_TIDY_WARNINGS_AS_ERRORS}" "-checks=${CLANG_TIDY_CHECKS_CXX_COMBINED_JOINED}" "-config={CheckOptions: [ ${CLANG_TIDY_CHECK_OPTIONS_CXX_COMBINED_JOINED} ]}" ${CLANG_TIDY_EXTRA_ARGS}) + + set_target_properties( + ${CLANG_TIDY_TARGET_NAME} PROPERTIES + CXX_CLANG_TIDY "${CLANG_TIDY_CMD_CXX}" + C_CLANG_TIDY "${CLANG_TIDY_CMD_C}" + EXCLUDE_FROM_ALL TRUE + ) + + add_custom_command( + TARGET ${CLANG_TIDY_TARGET_NAME} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/integration-testapp/clang-tidy/${ARG_PARENT_TARGET} + ) + add_dependencies(clang-tidy-all ${CLANG_TIDY_TARGET_NAME}) + endif() +endfunction() \ No newline at end of file diff --git a/cmake/testAppSetupProject.cmake b/cmake/testAppSetupProject.cmake new file mode 100644 index 0000000..646eaf9 --- /dev/null +++ b/cmake/testAppSetupProject.cmake @@ -0,0 +1,50 @@ +# Project options. +# Value = OFF: Disable feature +# Value = ON: Enable feature +# Value unset: Enable feature + +set(TESTAPP_DOWNLOAD_ONLY OFF CACHE STRING "Return cmake generation after download") + +# (TODO) +option(TESTAPP_ENABLE_COMMIT_CHECKS "Enable git pre-push hook to pre-check commit messages" OFF) + +# Enable doxygen-all target. +# Requires +# - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} <...>/Toolchain-src/cmake) +option(TESTAPP_ENABLE_DOXYGEN "Enable to generate integration-testapp doxygen report" ON) + +# Enable gcovr-all target +# Requires following flags: -fprofile-arcs -ftest-coverage +option(TESTAPP_ENABLE_GCOVR "Enable to generate integration-testapp coverage report" ON) + +# (TODO) Enable ninja targets to be executed from subfolders. Ninja only +option(TESTAPP_ENABLE_NINJA_ECLIPSE "Enable eclipse target execution from any subfolder" OFF) + +# Enable unit-test-all and clang-tidy-all targets and all module targets for clang-tidy, doxygen, gcovr and unit testing. +# Requires: +# - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} <...>/Toolchain-src/cmake) +# - gcovr only: -fprofile-arcs -ftest-coverage +option(TESTAPP_ENABLE_TESTS "Enable building of tests" ON) +if(TESTAPP_ENABLE_TESTS) + enable_testing() +endif() + +# Include SystemCommon mocks +option(TESTAPP_ENABLE_MOCKS "Includes mock libraries" OFF) + +if(NOT (CMAKE_CXX_STANDARD AND CMAKE_C_STANDARD)) + set(CMAKE_CXX_STANDARD 14) + set(CMAKE_C_STANDARD 99) +endif() + +# Generate compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# include all cmake files to the path +list(APPEND CMAKE_MODULE_PATH + "${PROJECT_SOURCE_DIR}/cmake" + "${PROJECT_SOURCE_DIR}/external/Toolchain/cmake" +) + +# integration-testapp Output path +file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/integration-testapp) diff --git a/cmake/testAppWsl.cmake b/cmake/testAppWsl.cmake new file mode 100644 index 0000000..7b6d028 --- /dev/null +++ b/cmake/testAppWsl.cmake @@ -0,0 +1,41 @@ +# Helper function for WSL (Windows subsystem for Linux) + +# @brief Convert to Windows path if running inside WSL, otherwise returns same path +# @param[in] INPUT_PATH Path to be converted +# @param[in] OUTPUT_PATH_VARIABLE Name of variable to which output path should be written +function(testapp_wsl_windows_path_if_running_wsl) + set(options "") + set(oneValueArgs INPUT_PATH OUTPUT_PATH_VARIABLE) + set(multiValueArgs "") + + cmake_parse_arguments(PARSE_ARGV 0 ARG "${options}" "${oneValueArgs}" "${multiValueArgs}") + + if(NOT ARG_INPUT_PATH) + set(${ARG_OUTPUT_PATH_VARIABLE} "" PARENT_SCOPE) + return() + endif() + + if(NOT ARG_OUTPUT_PATH_VARIABLE) + message(FATAL_ERROR "OUTPUT_PATH_VARIABLE not set.") + endif() + + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_HOST_SYSTEM_VERSION MATCHES "Microsoft") + # Running inside WSL + execute_process( + COMMAND wslpath -m ${ARG_INPUT_PATH} + OUTPUT_VARIABLE OUTPUT + ERROR_VARIABLE ERROR_OUTPUT + RESULT_VARIABLE RETURN_VALUE + ) + + if (NOT RETURN_VALUE EQUAL 0) + message(FATAL_ERROR "Could not convert ${ARG_INPUT_PATH}: ${ERROR_OUTPUT}") + else() + string(STRIP "${OUTPUT}" OUTPUT_STRIPPED) + set(${ARG_OUTPUT_PATH_VARIABLE} ${OUTPUT_STRIPPED} PARENT_SCOPE) + endif() + + else() + set(${ARG_OUTPUT_PATH_VARIABLE} ${ARG_INPUT_PATH} PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/toolchains/GCC.cmake b/cmake/toolchains/GCC.cmake new file mode 100644 index 0000000..17076ff --- /dev/null +++ b/cmake/toolchains/GCC.cmake @@ -0,0 +1,41 @@ +# \brief Common modules toolchain file. Uses the gcc compiler for building the unit tests +# \param[in] DCMAKE_MAKE_PROGRAM Path to +# \param[in] DCMAKE_PROGRAM_PATH Program search path + +# Set default build type to "Debug", if not otherwise configured via cli +if(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Debug CACHE STRING "Supported types: Debug, Release") +endif() + +if(CMAKE_HOST_WIN32) + set(CMAKE_SYSTEM_NAME windows) + set(CMAKE_SYSTEM_PROCESSOR x86_64) +endif() + +set(CMAKE_C_COMPILER clang CACHE FILEPATH "C Compiler") +set(CMAKE_CXX_COMPILER clang++ CACHE FILEPATH "CXX Compiler") + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_C_STANDARD 99) + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(CMAKE_C_COMPILER_TARGET x86_64-pc-windows-msvc) # x86_64-pc-windows-gnu) + set(CMAKE_CXX_COMPILER_TARGET x86_64-pc-windows-msvc) # x86_64-pc-windows-gnu) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(CMAKE_C_COMPILER_TARGET x86_64-pc-linux-gnu) + set(CMAKE_CXX_COMPILER_TARGET x86_64-pc-linux-gnu) +endif() + +# TODO Remove or at least reduce -03 flag +# The flag let us get around the error "string table overflow at offset 10000001. Cannot close *.cpp.obj: File too big" +# Note: GNU linker flags to generate mapfiles are set in the testAppAddTest.cmake file and only relevant for test targets +set(COMMON_FLAGS "-Wall -Wpedantic -Wextra -Winvalid-pch -O3 -fprofile-arcs -ftest-coverage -fno-exceptions -fno-inline -fdiagnostics-color=always -w") +set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}") +set(CMAKE_C_FLAGS "${COMMON_FLAGS}") + +set(COMMON_DEBUG_FLAGS "-g -gdwarf-2") +set(CMAKE_C_FLAGS_DEBUG "${COMMON_DEBUG_FLAGS}") +set(CMAKE_CXX_FLAGS_DEBUG "${COMMON_DEBUG_FLAGS}") + +# Include TestApp mock libraries +set(TESTAPP_ENABLE_MOCKS ON) diff --git a/cmake/toolchains/ppc-ghs.cmake b/cmake/toolchains/ppc-ghs.cmake new file mode 100644 index 0000000..4adb1da --- /dev/null +++ b/cmake/toolchains/ppc-ghs.cmake @@ -0,0 +1,125 @@ +# Try to force the compiler +SET(CMAKE_C_COMPILER_WORKS TRUE CACHE INTERNAL "") +SET(CMAKE_CXX_COMPILER_WORKS TRUE CACHE INTERNAL "") +SET(CMAKE_C_COMPILER_FORCED TRUE CACHE INTERNAL "") +SET(CMAKE_CXX_COMPILER_FORCED TRUE CACHE INTERNAL "") +SET(CMAKE_C_COMPILER_ID_RUN TRUE CACHE INTERNAL "") +SET(CMAKE_CXX_COMPILER_ID_RUN TRUE CACHE INTERNAL "") + +set(CMAKE_CXX_COMPILER_ID "GHS") +set(CMAKE_CXX_COMPILER_VERSION "") +set(CMAKE_CXX_COMPILER_WRAPPER "") +set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_14") + +set(CMAKE_C_COMPILER_ID_RUN "GHS") +set(CMAKE_C_COMPILER_VERSION "") +set(CMAKE_C_COMPILER_WRAPPER "") +set(CMAKE_C_COMPILE_FEATURES "c_std_99") + +set(CMAKE_SYSTEM_NAME "Generic") +set(CMAKE_SYSTEM_PROCESSOR "ppc") + +# set pathes for all used tools to ghs, since it is a MULTI Compiler +set(CMAKE_C_COMPILER ccppc CACHE FILEPATH "") +set(CMAKE_C_LINKER cxppc CACHE FILEPATH "") +set(CMAKE_CXX_COMPILER cxppc CACHE FILEPATH "") +set(CMAKE_AR cxppc CACHE FILEPATH "" ) + +# clean all host includes since it is a cross compiling run +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# needed to get correct dependency-files +set(CMAKE_C_DEPFILE_EXTENSION_REPLACE 1) +set(CMAKE_CXX_DEPFILE_EXTENSION_REPLACE 1) + +# disable +set(GHS_FLAGS_C " -noSPE " #disable SPE + " -fhard " #use hardware floating point unit + " --short_enum " #make enums as short as possible + " -floatsingle " #use same size for float and double + " -MD" # make dependency files + " -time64" + ) + +set(GHS_FLAGS_CPP " -language=cxx " #enable cpp + " --stdl" # use std lib without exceptions + " --no_exceptions" #disable exceptions + " --new_style_casts" #enable static_cast<> + " --c++14" # use cpp 14 + " --pending_instantiations=0" #allow template recursion + ) + +set(GHS_FLAGS_DEBUGING " -Omaxdebug " # max debug information + " -dwarf2" # enable debugging with DWARF + " -g" # enable source level debugging in debug mode only + ) + +set(GHS_FLAGS_RELEASING " -Ospeed " # optimize for size + " -dwarf2" # enable debugging with DWARF + ) + + +set(GHS_FLAGS_LINKING " -map" # generate default map file + " -Mx" # enables Map File Cross-Referencing + " -vle" # enable variable length encoding + " -hex=${PROJECT_NAME}.hex" # enable hex files + " -sda" + " -farcallpatch" + " -paddr_offset=0" + ) + +set(GHS_FLAGS_CONSOL_OUTPUT " --no_wrap_diagnostics" # no line break after 80 chars + " --brief_diagnostics" # only print short diagnostics + #" -full_debug_info" # print infos on defined and never used symbols + ) + +set(PPC_CPU "ppc5746mz420") +set(GHS_FLAGS_BSP " -bsp generic " # no specific bsl + " -cpu=${PPC_CPU} " # our cpu + ) + +# since we defined all flags as lists of stings to be able to comment them easily, we need to join them together +# again in one large string that we can pass to the compiler +string(REPLACE ";" " " GHS_FLAGS_C "${GHS_FLAGS_C}") +string(REPLACE ";" " " GHS_FLAGS_CPP "${GHS_FLAGS_CPP}") +string(REPLACE ";" " " GHS_FLAGS_DEBUGING "${GHS_FLAGS_DEBUGING}") +string(REPLACE ";" " " GHS_FLAGS_RELEASING "${GHS_FLAGS_RELEASING}") +string(REPLACE ";" " " GHS_FLAGS_LINKING "${GHS_FLAGS_LINKING}") +string(REPLACE ";" " " GHS_FLAGS_CONSOL_OUTPUT "${GHS_FLAGS_CONSOL_OUTPUT}") +string(REPLACE ";" " " GHS_FLAGS_BSP "${GHS_FLAGS_BSP}") + +set(GHS_FLAGS_DEBUG "${GHS_FLAGS_C} ${GHS_FLAGS_CPP} ${GHS_FLAGS_DEBUGING} ${GHS_FLAGS_LINKING} ${GHS_FLAGS_CONSOL_OUTPUT} ${GHS_FLAGS_BSP} ${} -gsize ") +set(GHS_FLAGS_RELEASE "${GHS_FLAGS_C} ${GHS_FLAGS_CPP} ${GHS_FLAGS_RELEASING} ${GHS_FLAGS_LINKING} ${GHS_FLAGS_CONSOL_OUTPUT} ${GHS_FLAGS_BSP} ${} -gsize ") + +unset(CMAKE_C_FLAGS CACHE) +unset(CMAKE_CXX_FLAGS CACHE) +unset(CMAKE_EXE_LINKER_FLAGS CACHE) +unset(CMAKE_C_COMPILE_OBJECT CACHE) +unset(CMAKE_C_CREATE_STATIC_LIBRARY CACHE) +unset(CMAKE_CXX_CREATE_STATIC_LIBRARY CACHE) +unset(CMAKE_CXX_LINK_EXECUTABLE CACHE) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${GHS_FLAGS_DEBUG}" CACHE STRING "" FORCE) + set(CMAKE_CXX_FLAGS "${GHS_FLAGS_DEBUG} --diag_suppress=111" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${GHS_FLAGS_DEBUG}" CACHE STRING "" FORCE) +else() #not a debug build + set(CMAKE_C_FLAGS "${GHS_FLAGS_RELEASE}" CACHE STRING "" FORCE) + set(CMAKE_CXX_FLAGS "${GHS_FLAGS_RELEASE} --diag_suppress=111" CACHE STRING "" FORCE) + set(CMAKE_EXE_LINKER_FLAGS "${GHS_FLAGS_RELEASE}" CACHE STRING "" FORCE) +endif() + +set(CMAKE_C_COMPILE_OBJECT " -o -c ") +set(CMAKE_C_CREATE_STATIC_LIBRARY " -archive -o ") +set(CMAKE_CXX_CREATE_STATIC_LIBRARY " -archive -o ") +set(CMAKE_CXX_LINK_EXECUTABLE " -o ") +#Added for systemcommon +set(ETL_PROFILE "PROFILE_CPP14_GENERIC" CACHE STRING "Defines what profile header to include. See https://www.etlcpp.com/setup.html" FORCE) +add_definitions(-DPB_CONVERT_DOUBLE_FLOAT) +set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-sys_include_directory ") + +set(DEVICE_UNDER_TEST_ARCH "PPC") +set(PROJECT_PLATFORM "ppc5746mz420") diff --git a/cmake/toolchains/stm32-arm-gcc-f401re.cmake b/cmake/toolchains/stm32-arm-gcc-f401re.cmake new file mode 100644 index 0000000..22c9527 --- /dev/null +++ b/cmake/toolchains/stm32-arm-gcc-f401re.cmake @@ -0,0 +1,49 @@ +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-none-eabi-gcc) +set(CMAKE_CXX_COMPILER arm-none-eabi-g++) +set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) +set(CMAKE_OBJCOPY arm-none-eabi-objcopy) +set(CMAKE_OBJDUMP arm-none-eabi-objdump) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_ASM_FLAGS "") + +set(SHARED_LIBS OFF) +set(STATIC_LIBS ON) + +# Target platform device +set(MCU "STM32F401RE") +set(DEVICE_UNDER_TEST_ARCH "ARM") +set(PROJECT_PLATFORM "stm32f401re") + +# Generate compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if(NOT DEFINED ENV{JENKINS_URL}) + # Not running on Jenkins -> enable color output + set(COMMON_FLAGS "${COMMON_FLAGS} -fdiagnostics-color=always") +endif() + +# Compiler Flags +# Note: GNU linker flags to generate mapfiles are set in the testAppAddTest.cmake file and only relevant for test targets +set(COMMON_FLAGS "${COMMON_FLAGS} -mfloat-abi=softfp -mcpu=cortex-m4 -mfpu=vfpv4 -mthumb -fno-builtin -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP") +#set(COMMON_FLAGS "${COMMON_FLAGS} -mcpu=cortex-m4 -mfloat-abi=softfp -MP -MM -fuse-ld=bfd") +#set(COMMON_FLAGS "${COMMON_FLAGS} -Os -g -Wall -mcpu=cortex-m4 -mfloat-abi=hard -M") -T ${PROJECT_SOURCE_DIR}/Platform/${PROJECT_PLATFORM}/config/Linker/stm32l562xx.ld +set(CMAKE_C_FLAGS "${COMMON_FLAGS} ${DEPFLAGS}") +set(CMAKE_CXX_FLAGS "${COMMON_FLAGS} ${DEPFLAGS} -fno-rtti -fno-exceptions") +set(CMAKE_ASM_FLAGS "${COMMON_FLAGS} -x assembler-with-cpp -specs=nosys.specs") + +set(CMAKE_C_COMPILER_TARGET arm-none-eabi) +set(CMAKE_CXX_COMPILER_TARGET arm-none-eabi) + + + +# configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/hal.cmake ${PROJECT_BINARY_DIR}/external/stm32cubeF4-src/cmake/cmsic.hal) +# configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/cmsic.cmake ${PROJECT_BINARY_DIR}/external/stm32cubeF4-src/cmake/cmsic.cmake) +# configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/cmake/usb-device.cmake ${PROJECT_BINARY_DIR}/external/stm32cubeF4-src/cmake/usb-device.cmake) +# configure_file(${PROJECT_SOURCE_DIR}/Platform/stm32f401re/config/STM32CubeF4/CMakeLists.txt ${PROJECT_BINARY_DIR}/external/stm32cubeF4-src/CMakeLists.txt) diff --git a/cmake/toolchains/stm32-arm-gcc.cmake b/cmake/toolchains/stm32-arm-gcc.cmake new file mode 100644 index 0000000..d4fd1f4 --- /dev/null +++ b/cmake/toolchains/stm32-arm-gcc.cmake @@ -0,0 +1,40 @@ +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-none-eabi-gcc) +set(CMAKE_CXX_COMPILER arm-none-eabi-g++) +set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) +set(CMAKE_OBJCOPY arm-none-eabi-objcopy) +set(CMAKE_OBJDUMP arm-none-eabi-objdump) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_ASM_FLAGS "") + +set(SHARED_LIBS OFF) +set(STATIC_LIBS ON) + +# Generate compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if(NOT DEFINED ENV{JENKINS_URL}) + # Not running on Jenkins -> enable color output + set(COMMON_FLAGS "${COMMON_FLAGS} -fdiagnostics-color=always") +endif() + +# Compiler Flags +# Note: GNU linker flags to generate mapfiles are set in the testAppAddTest.cmake file and only relevant for test targets +set(COMMON_FLAGS "${COMMON_FLAGS} -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -mcpu=cortex-m33 -mthumb -fno-builtin -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP") +set(CMAKE_C_FLAGS "${COMMON_FLAGS} ${DEPFLAGS}") +set(CMAKE_CXX_FLAGS "${COMMON_FLAGS} ${DEPFLAGS} -fno-rtti -fno-exceptions") +set(CMAKE_ASM_FLAGS "${COMMON_FLAGS} -x assembler-with-cpp") + +set(CMAKE_C_COMPILER_TARGET arm-none-eabi) +set(CMAKE_CXX_COMPILER_TARGET arm-none-eabi) + +# Target platform device +set(MCU "STM32L562QE") +set(DEVICE_UNDER_TEST_ARCH "ARM") +set(PROJECT_PLATFORM "stm32l562qe") #stm32f401re \ No newline at end of file diff --git a/cmake/toolchains/unittest-ppc-ghs-emu.cmake b/cmake/toolchains/unittest-ppc-ghs-emu.cmake new file mode 100644 index 0000000..47b8c8b --- /dev/null +++ b/cmake/toolchains/unittest-ppc-ghs-emu.cmake @@ -0,0 +1,10 @@ +# Based on the toolchain for the actual hardware +include(${CMAKE_CURRENT_LIST_DIR}/ppc-ghs.cmake) + +# Tell CMake to wrap unit tests with ghs-ppc emulator +set(CMAKE_CROSSCOMPILING_EMULATOR simppc -cpu="${PPC_CPU}") +# Set cross compiling to ON +set(RUNNING_UNITTESTS_WITH_EMULATOR ON CACHE BOOL "is crosscompiled" FORCE) + +# Include TestApp mock libraries +set(TESTAPP_ENABLE_MOCKS ON) diff --git a/cmake/toolchains/unittest-qemu-arm.cmake b/cmake/toolchains/unittest-qemu-arm.cmake new file mode 100644 index 0000000..c849fa0 --- /dev/null +++ b/cmake/toolchains/unittest-qemu-arm.cmake @@ -0,0 +1,21 @@ +# Based on the toolchain for the actual hardware +include(${CMAKE_CURRENT_LIST_DIR}/stm32-arm-gcc.cmake) + +# Overwrite Linkerflags: Use generic semihosting instead of the actual MCU +set(CMAKE_EXE_LINKER_FLAGS "--specs=rdimon.specs") + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + # Tell CMake to wrap tests with qemu + find_program(QEMU_ARM qemu-arm) + set(CMAKE_CROSSCOMPILING_EMULATOR ${QEMU_ARM}) +endif() + +# Set cross compiling to ON +set(RUNNING_UNITTESTS_WITH_EMULATOR ON CACHE BOOL "is crosscompiled" FORCE) + +# Configure GTest for embedded +add_compile_definitions(arm) +set(gtest_disable_pthreads ON CACHE BOOL "Disable uses of pthreads in gtest." FORCE) + +# Include TestApp mock libraries +set(TESTAPP_ENABLE_MOCKS ON) diff --git a/cmake/tools/MemoryUsage/Readme.md b/cmake/tools/MemoryUsage/Readme.md new file mode 100644 index 0000000..435c7cc --- /dev/null +++ b/cmake/tools/MemoryUsage/Readme.md @@ -0,0 +1,43 @@ +# Memory Usage Script + +## Description + +This script uses the mapfile and the folder structure of the build directory to determine the memory usage of all tasks, their Modules and even their functions and compiles a presentable HTML file. +This HTML file presents multiple sortable tables for each Task Region and its corresponding modules. +If you click on a module row, the function names and their memory usage should appear below as additional childrows. +The function names will automatically be demangled if you have `arm-none-eabi-c++filt` installed and in your path. This can be done by activating your `conan` environment for the **Application** project. If you do not have a demangler in your path, it will simply print out the mangled names. + +## Usage + +`[python] memory_usage.py [-h] [--directory [DIRECTORY [DIRECTORY ...]]] [--exclude-folders [EXCLUDE_FOLDERS [EXCLUDE_FOLDERS ...]]] [--exclude-files [EXCLUDE_FILES [EXCLUDE_FILES ...]]] [--modules [MODULES [MODULES ...]]] --map-file MAP_FILE` + +### Examples + +Use the given mapfile and the check for the modules in the current working directory: +`> python memory_usage.py -m /path/to/mapfile.map` +Generates a memory_usage.html file in the current directory. + +Use the given mapfile, use the current working directory but exclude the BLE folder and every file that corresponds with "reliance" and "usb": +`> python memory_usage.py -m /path/to/mapfile.map -e BLE --exclude-files *reliance* *usb*` +Generates a memory_usage.html file in the current directory, where the BLE Module and every Module, that only had libraries with reliance or usb in their names, are missing. + +Use the given mapfile, use the given directory and only look after the BLE Module: +`> python memory_usage.py -m /path/to/mapfile.map -d /path/to/root -mod BLE` +Generates a memory_usage.html file in the current directory, where ONLY the BLE modules is listed. + + +### Required Arguments + +Command | Type | Help | Default +:-------|:----:|:-----|:------- +-m, --map, --map-file| String | The path to the mapfile of the build. | None + +### Optional Arguments + +Command | Type | Help | Default +:-------|:----:|:-----|:------- +-h, --help | Flag | show this help message and exit | False +-d, --dir,
--directory | Path List | The directories this program looks into. | Current Directory +-e, --exclude-folders | String List | Exclude these folders from the search. | Empty List +--exclude-files | Regex List | Ignores files that match the given Regular Expression. For more Infos check: https://docs.python.org/3/library/re.html#regular-expression-syntax | Empty List +-mod,
--modules | String List | Specify the module names you are interested in. | Everything diff --git a/cmake/tools/MemoryUsage/memory_usage.py b/cmake/tools/MemoryUsage/memory_usage.py new file mode 100644 index 0000000..b34be41 --- /dev/null +++ b/cmake/tools/MemoryUsage/memory_usage.py @@ -0,0 +1,1012 @@ +#!/usr/bin/env python3 + +import glob +import subprocess +import re +import sys +import os +import argparse +from pathlib import Path + +class Function: + """Data Class for a Function inside a Module""" + + name = "" + flash_size = 0 + ram_size = 0 + exram_size = 0 + module = "" + + def __init__(self, name: str): + self.name = name + self.flash_size = 0 + self.ram_size = 0 + + def __repr__(self): + val = str( + "[" + + self.name + + " in " + + self.module + + " Flash: " + + self.flash_size + + " B RAM: " + + self.ram_size + + " B]" + ) + return val + + def __str__(self): + val = str( + "[" + + self.name + + " in " + + self.module + + " Flash: " + + self.flash_size + + " B RAM: " + + self.ram_size + + " B]" + ) + return val + + +class Module: + name = "" + libraries = [] + functions = [] + task_name = "common" + flash_size = 0 + ram_size = 0 + + def __init__(self, name: str): + self.name = name + self.libraries = [] + self.functions = [] + self.task_name = "common" + self.flash_size = 0 + self.ram_size = 0 + + def __repr__(self): + val = str( + self.name + + "@" + + self.task_name + + " libs: " + + str(self.libraries) + + "\nFlash Size: " + + str(self.flash_size) + + " Bytes\nRAM Size: " + + str(self.ram_size) + + " Bytes" + ) + return val + + def __str__(self): + val = ( + self.name + + "@" + + self.task_name + + " libs: " + + str(self.libraries) + + "\nFlash Size: " + + str(self.flash_size) + + " Bytes\nRAM Size: " + + str(self.ram_size) + + " Bytes" + ) + return val + + +class Task: + name = "" + modules = [] + exram = 0 + stack = 0 + + def __init__(self, name: str): + self.name = name + self.modules = [] + self.exram = 0 + self.stack = 0 + + def total_flash(self): + s = 0 + for m in self.modules: + s += m.flash_size + return s + + def total_ram(self): + s = 0 + for m in self.modules: + s += m.ram_size + s += self.stack + return s + + def __repr__(self): + return "Task: " + self.name + + +debug = False + + +def main(): + global debug + # read_object_files() + # with open('modules.json', 'r') as fp: + # modules = json.load(fp) + # print_summary(modules) + parser = argparse.ArgumentParser() + parser.add_argument("--directory", help="The directories this program looks into. (Default: Current Directory)", nargs="*", required=False, default=["."]) + parser.add_argument("--exclude-folders", help="Exclude these folders from the search. (Default: None)", nargs="*", required=False, default=[]) + parser.add_argument("--exclude-files", help="Ignores files that match the given Regular Expression. (Default: None)", nargs="*", required=False, default=[]) + parser.add_argument("--modules", help="Specify the module names you are interested in. (Default: Everything)", nargs="*", required=False, default=None) + parser.add_argument("--map-file", help="The map file that needs to be scanned. (Default: None)", required=True, default="") + parser.add_argument("--html", help="Specifies HTML output filename. (Default: None)") + parser.add_argument("--csv", help="Specifies CSV output filename. (Default: None)") + opts = vars(parser.parse_args(sys.argv[1:])) + if debug: + for option, values in opts.items(): + print(str(option) + ": " + str(values)) + modules = [] + for dir in opts["directory"]: + modules.extend(scan_for_module_directories(dir, opts)) + filt_found = False + try: + subprocess.run(["arm-none-eabi-c++filt", "--version"], capture_output=True) + filt_found = True + except FileNotFoundError: + pass + tasks = scan_map_file(opts["map_file"], modules, filt_found) + total_exram, total_flash, total_ram = get_total_ram_and_flash( + opts["map_file"]) + + if opts.get('html', None): + print_HTML(opts['html'], tasks, total_ram, total_exram, total_flash) + + if opts.get('csv', None): + print_csv(opts['csv'], tasks) + + +def print_csv(filename, tasks): + used_flash, used_ram, used_exram = get_used_memory(tasks) + + # Create output directory if non-existent + directory = os.path.dirname(filename) + if not os.path.isdir(directory): + os.makedirs(directory) + + with open(filename, "w") as f: + f.write('"Total Ram", "Total Exram", "Total Flash"\n') + f.write("{}, {}, {}".format(str(round(used_ram / 1024, ndigits=2)), str(round(used_exram / 1024, ndigits=2)), str(round(used_flash / 1024, ndigits=2)))) + + +def scan_for_module_directories(dir: str, opts: dict): + global debug + path = Path(dir) + if not path.is_dir() or path in opts["exclude_folders"]: + return + src = path.joinpath("src") + externals = path.joinpath("external") + modules = scan_dir_for_modules(path, opts) + if src.exists(): + modules.extend(scan_dir_for_modules(src, opts)) + if externals.exists(): + if debug: + print("Externals found.") + modules.extend(scan_for_module_directories(externals, opts)) + return modules + + +def scan_dir_for_modules(path: Path, opts: dict): + global debug + if debug: + print(path.name) + modules = [] + mods = [x for x in path.iterdir() if ( + x.is_dir() and x.name != "CMakeFiles")] + for m in mods: + if opts["modules"] is not None: + if m.name.lower() not in map(lambda x: x.lower(), opts["modules"]): + continue + new = Module(m.name) + libs = [ + x.name for x in Path(m).iterdir() if x.is_file() and x.name.endswith(".a") + ] + new.libraries = libs.copy() + for lib in libs: + for x in opts["exclude_files"]: + regex = re.compile(x) + matches = regex.search(lib, re.IGNORECASE) + if matches: + new.libraries.remove(lib) + if len(new.libraries) != 0: + modules.append(new) + if debug: + print(str(new)) + print("-------------------") + return modules + + +def get_total_ram_and_flash(mapfile: str): + total_ram = 0 + total_exram = 0 + total_flash = 0 + with open(mapfile, "r") as f: + for line in f: + matches = re.match( + r"^(?PAPPLICATION|APPLICATION_DATA_INIT|RAM|PSRAM)\s+0x[0-9a-f]+\s+0x(?P[0-9a-f]+)", + line, + ) + if matches: + val = int(matches.group("size"), base=16) + if matches.group("memory") in ["APPLICATION", "APPLICATION_DATA_INIT"]: + total_flash += val + elif matches.group("memory") in ["RAM"]: + total_ram += val + elif matches.group("memory") in ["PSRAM"]: + total_exram += val + return total_exram, total_flash, total_ram + + +def get_used_memory(tasks): + flash = 0 + ram = 0 + exram = 0 + for t in tasks: + flash += t.total_flash() + ram += t.total_ram() + exram += t.exram + return (flash, ram, exram) + + +def scan_map_file(mapfile: str, modules: list, filt_found: bool): + correctSection = False + currentTask: Task = None + currentMemory = "" + currentFunction = None + currentLibrary = "" + currentModule = None + earlier_match = False + common_match = False + prev_addr = None + privileged_match = False + tasks = [Task("common")] + tasks[0].modules.append(Module("Standard Library")) + with open(mapfile, "r") as f: + for line in f: + if correctSection: + if line.strip() == "/DISCARD/": + modules = sort_out_empty_modules(modules, tasks) + for m in modules: + if m.task_name == "common": + tasks[0].modules.append(m) + tasks = sort_out_empty_tasks(tasks) + return tasks + if common_match: + if "*fill*" in line: + continue + matches = re.search(r"COMMON\s+0x(?P
[0-9a-f]{16}).*(?Plib.*?\.a)", line) + if matches: + if prev_addr is not None: + prev_function.ram_size = int(matches.group('address'), base=16) - prev_addr + currentModule.ram_size += prev_function.ram_size + currentLibrary = matches.group('library') + prev_addr = None + prev_function = None + continue + matches = re.search(r"0x(?P
[0-9a-f]{16})\s+(?P[^0-9]\S+)", line) + if matches: + if prev_addr is not None: + prev_function.ram_size = int(matches.group('address'), base=16) - prev_addr + currentModule.ram_size += prev_function.ram_size + if "__task_section" in matches.group('name'): + common_match = False + continue + prev_addr = int(matches.group('address'), base=16) + prev_function = Function(matches.group('name')) + for m in modules: + if currentLibrary in m.libraries: + currentModule = m + m.functions.append(prev_function) + if currentTask.name != "common" and m not in currentTask.modules: + currentTask.modules.append(m) + m.task_name = currentTask.name + break + if currentModule is None: + currentModule = tasks[0].modules[0] + else: # address and name match + common_match = False + continue + matches = re.match( + r"^\.privileged_(?Pram|functions)$", line) + if matches: + currentTask = tasks[0] + if matches.group("memory") == "functions": + currentMemory = "flash" + else: + currentMemory = "ram" + privileged_match = True + continue + matches = re.match( + r"^ *\.task_section_(?P.+?)_(?Pram|exram|stack|flash)", + line, + ) + if matches: + currentTask = find_name_in_tasks( + tasks, matches.group("name")) + if currentTask is None: + currentTask = Task(matches.group("name")) + tasks.append(currentTask) + currentMemory = matches.group("memory") + if currentTask.name == "common" and currentMemory == "flash": + currentFunction = None + earlier_match = True + continue # set flag to do stuffs. + matches = re.match( + r"^ *\.(?Ptext|rodata|init_array)\.", line) + if matches: + currentTask = None + currentMemory = matches.group("memory") + earlier_match = True + if earlier_match: + privileged_match = False + matches = re.search(r"COMMON[ \t]+0x[0-9a-f]+\s+0x[0-9a-f]+[ \t]+.*(?Plib.+?\.a)", line) + if matches: + currentLibrary = matches.group('library') + common_match = True + earlier_match = False + prev_addr = None + prev_function = None + continue + matches = re.match( + r"^[ \t]*\.(text|bss|data|rodata)\.(?P\S*)(?P.+)?", line + ) + if matches: + mangle = matches.group("mangle").strip().split('.')[0] + name = demangle(mangle, filt_found) + currentFunction = Function(name) + if matches.group("else") is None: + continue + # regex to find libs + # 0xADRESSE 0xLÄNGE path/to/libName.a + matches = re.search( + r"0x[0-9a-f]+\s+0x(?P[0-9a-f]+)[ \t]+.*(?Plib.+?\.a)", + line, + ) + if currentMemory == "ram": + if matches: + right_module = tasks[0].modules[0] + for m in modules: + if matches.group("library") in m.libraries: + # pylint: disable=line-too-long + if ( + m not in currentTask.modules + and currentTask.name != "common" + ): # noqa + currentTask.modules.append(m) + m.task_name = currentTask.name + right_module = m + break + # und speichere die LÄNGE in das Module mit der Library + if currentFunction is not None: + if currentFunction not in right_module.functions: + right_module.functions.append( + currentFunction) + currentFunction.ram_size = int( + matches.group("length"), base=16 + ) + right_module.ram_size += int( + matches.group("length"), base=16) + currentFunction = None + continue + if currentMemory == "stack": + if matches: + for m in modules: + if matches.group("library") in m.libraries: + if ( + m not in currentTask.modules + and currentTask.name != "common" + ): # noqa + currentTask.modules.append(m) + m.task_name = currentTask.name + # und speichere die LÄNGE in das Module mit der Library + currentTask.stack += int( + matches.group("length"), base=16 + ) + break + currentMemory = "ram" + continue + if currentMemory == "exram": + if matches: + for m in modules: + if matches.group("library") in m.libraries: + if ( + m not in currentTask.modules + and currentTask.name != "common" + ): # noqa + currentTask.modules.append(m) + m.task_name = currentTask.name + # und speichere die LÄNGE in das Module mit der Library + currentTask.exram += int( + matches.group("length"), base=16 + ) + break + continue + if currentMemory in ["flash", "text", "rodata", "init_array"]: + if matches: + right_module = tasks[0].modules[0] + for m in modules: + if matches.group("library") in m.libraries: + right_module = m + break + right_module.flash_size += int( + matches.group("length"), base=16 + ) + if currentFunction is not None: + if currentFunction not in right_module.functions: + right_module.functions.append( + currentFunction) + currentFunction.flash_size = int( + matches.group("length"), base=16 + ) + currentFunction = None + continue + if privileged_match: + matches = re.search( + r"0x[0-9a-f]{16}\s+0x(?P[0-9a-f]+)[ \t]+.*" + + r"(?Plib.+?\.a)", + line, + ) + if matches: + if currentMemory in ["flash"]: + for m in modules: + if matches.group("library") in m.libraries: + m.flash_size += int( + matches.group("length"), base=16 + ) + break + elif currentMemory in ["ram"]: + for m in modules: + if matches.group("library") in m.libraries: + m.ram_size += int(matches.group("length"), base=16) + break + continue + else: + if line.strip() == "Linker script and memory map": + correctSection = True + return tasks + + +def find_name_in_tasks(tasks, name: str): + for t in tasks: + if t.name == name: + return t + return None + + +def sort_out_empty_modules(modules, tasks): + new_mods = modules.copy() + for m in modules: + if m.flash_size == 0 and m.ram_size == 0: + for t in tasks: + if m in t.modules: + t.modules.remove(m) + new_mods.remove(m) + return modules + + +def sort_out_empty_tasks(tasks): + new_tasks = tasks.copy() + for t in tasks: + if len(t.modules) == 0: + new_tasks.remove(t) + return new_tasks + + +def sort_task(val): + return val.name + + +def sort_modules(val): + return val.name + + +def print_HTML(filename, tasks, total_ram, total_exram, total_flash): + """Jenkins has a nice way to present HTML files, so I chose this over XML""" + # Pre-Sorting tasks and modules by Name + tasks.sort(key=sort_task) + for t in tasks: + t.modules.sort(key=sort_modules) + for m in t.modules: + m.functions.sort(key=sort_modules) + used_flash, used_ram, used_exram = get_used_memory(tasks) + + # Create output directory if non-existent + directory = os.path.dirname(filename) + if not os.path.isdir(directory): + os.makedirs(directory) + + with open(filename, "w") as f: + # Write HTML Header with CSS Styles. Goal is to have exactly one .HTML file, to make it presentable and compact. + print( + r""" + + + + + + Memory Usage + + + + + + +

TestApp Memory Usage RAM: """ + + str(round(used_ram / 1024, ndigits=2)) + + " KB (" + + "{0:05.2f}".format((used_ram / float(total_ram)) * 100) + + "%) Flash: " + + str(round(used_flash / 1024, ndigits=2)) + + " KB (" + + "{0:05.2f}".format((used_flash / float(total_flash)) * 100) + + "%) EXRAM: " + + str(round(used_exram / 1024, ndigits=2)) + + " KB (" + + "{0:05.2f}".format((used_exram / float(total_exram)) * 100) + + "%)

" + + """ +
+ + +
+ """, + file=f, + ) + # Write the html module toggler for every task + for t in tasks: + print( + """
+ +
+ + + + + + + + + + + + + + + """, + file=f, + ) + # Write the table for all modules as child item of our toggle buttons. + for m in t.modules: + print( + """ + + + + + """, + file=f, + ) + # Write all child-rows for each module + for func in m.functions: + print( + """ + + + + """, + file=f, + ) + print( + """ + +
ModuleRAMFlash
""" + + m.name + + """""" + + str(m.ram_size) + + " B (" + + "{0:05.2f}".format(m.ram_size / float(total_ram) * 100) + + "%)" + """""" + + str(m.flash_size) + + " B (" + + "{0:05.2f}".format(m.flash_size / float(total_flash) * 100) + + "%)" + """
""" + + func.name + + """""" + + str(func.ram_size) + + " B (" + + "{0:05.2f}".format(func.ram_size / float(total_ram) * 100) + + "%)" + + """""" + + str(func.flash_size) + + " B (" + + "{0:05.2f}".format(func.flash_size / float(total_flash) * 100) + + "%)" + + """
+
+
+""", + file=f, + ) + # Embed all Javascript into the HTML to make it more compact. + print( + r""" + +""", + file=f, + ) + + +def demangle(names, filt_found): + try: + import cpp_demangle + try: + return cpp_demangle.demangle(names) + except ValueError: + return names + except ImportError: + if filt_found: + args = ["arm-none-eabi-c++filt"] + args.append(names) + pipe = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + stdout, _ = pipe.communicate() + demangled = stdout.decode("utf8") + return demangled.rstrip(os.linesep) + else: + return names + + +if __name__ == "__main__": + main() + + +""" +Module sind Directory Namen innerhalb der -d flags. [X] +Ein Modul kann mehrere libraries haben. [x] +per regex RAM daten von task_sections suchen (s.u.) innerhalb der Region, alle Module in die man findet in Task hineinwerfen +^\\.task_section_(?P.+?)_(?Pram|exram|stack) +Zuerst werden Module angelegt, und deren flash und ram size ausgelesen +Dann werden Task_Sections durchgegangen, Tasks angelegt und die Module den Tasks aufgrund der libraries zugewiesen. +Dann wird nach \\.text\\..* oder \\.data\\..* gesucht und dort dann die libs herausgesucht und die Länge dem Flash(text)- oder Ram(data)-Speicher zugeordnet. +Wenn eine lib zu einem Modul ohne Task zugeordnet gefunden wurde, gehört dieses Modul zur "Common"-Task +gcc -fStackUsage +Wenn RAM task_section, dann überprüf nach +0xADRESSE 0xLÄNGE path/to/libName.a +und speichere die LÄNGE in das Module mit der Library + +Prozentualer Verbrauch der Task vom Gesamtspeicher anzeigen. +Gesamt Speicher findet sich unter "Memory Configuration" + +""" diff --git a/external/FreeRTOS/CMakeLists.txt b/external/FreeRTOS/CMakeLists.txt new file mode 100644 index 0000000..c0a8976 --- /dev/null +++ b/external/FreeRTOS/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 3.11) + +# Optional FreeRTOS-Plus modules +set(FREERTOS_PLUS_CLI OFF CACHE BOOL "Support for FreeRTOS-PLUS-CLI") + +message(${FREERTOS_PORT_BASE}/${FREERTOS_PORT}) +if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") + set(FREERTOS_BASE ${CMAKE_CURRENT_BINARY_DIR}/../arm/FreeRTOS-src/FreeRTOS/Source) + set(FREERTOS_PLUS_BASE ${CMAKE_CURRENT_BINARY_DIR}/../arm/FreeRTOS-src/FreeRTOS-Plus/Source) +else() + message(${CMAKE_CURRENT_SOURCE_DIR}) + set(FREERTOS_BASE ${CMAKE_CURRENT_BINARY_DIR}/../FreeRTOS-src/FreeRTOS/Source) + set(FREERTOS_PLUS_BASE ${CMAKE_CURRENT_BINARY_DIR}/../FreeRTOS-src/FreeRTOS-Plus/Source) + set(FREERTOS_PORT_BASE ${CMAKE_CURRENT_SOURCE_DIR}/Source/portable) +endif() + + +set(FREERTOS_SOURCES + ${FREERTOS_BASE}/croutine.c + ${FREERTOS_BASE}/event_groups.c + ${FREERTOS_BASE}/list.c + ${FREERTOS_BASE}/queue.c + ${FREERTOS_BASE}/stream_buffer.c + ${FREERTOS_BASE}/tasks.c + ${FREERTOS_BASE}/timers.c + ) + +# Declare empty lists to make sure they can be appended by optional FreeRTOS-Plus modules +set(FREERTOS_PLUS_SOURCES "") +set(FREERTOS_PLUS_INCLUDES "") + +if(FREERTOS_ENABLE_MPU) + list(APPEND FREERTOS_SOURCES ${FREERTOS_BASE}/portable/Common/mpu_wrappers.c) +endif() + +if(NOT DEFINED FREERTOS_PORT) + message(FATAL_ERROR "No FreeRTOS port defined") +endif(NOT DEFINED FREERTOS_PORT) + +if(NOT DEFINED FREERTOS_PORT_BASE) + set(FREERTOS_PORT_BASE ${FREERTOS_BASE}/portable) +endif(NOT DEFINED FREERTOS_PORT_BASE) + +if (NOT EXISTS "${FREERTOS_PORT_BASE}") + message(FATAL_ERROR "FreeRTOS port directory ${FREERTOS_PORT_BASE} not found") +endif() + +# Append base dir to source files +foreach(f ${FREERTOS_PORT_SOURCES}) + list(APPEND FREERTOS_PORT_SOURCES_WITH_PORT_BASE "${FREERTOS_PORT_BASE}/${FREERTOS_PORT}/${f}") +endforeach(f) + +if (FREERTOS_PLUS_CLI) + list(APPEND FREERTOS_PLUS_SOURCES ${FREERTOS_PLUS_BASE}/FreeRTOS-Plus-CLI/FreeRTOS_CLI.c) + list(APPEND FREERTOS_PLUS_INCLUDES ${FREERTOS_PLUS_BASE}/FreeRTOS-Plus-CLI/) +endif() + +# Create hal library +add_library(freertos + ${FREERTOS_SOURCES} + ${FREERTOS_PLUS_SOURCES} + ${FREERTOS_PORT_SOURCES_WITH_PORT_BASE} + ) + +target_include_directories(freertos SYSTEM + PUBLIC + ${FREERTOS_BASE}/include + ${FREERTOS_PORT_BASE}/${FREERTOS_PORT} + ${FREERTOS_CONF} + ${FREERTOS_PLUS_INCLUDES} + ) + +target_link_libraries(freertos + PRIVATE + ${FREERTOS_LINK_LIBRARIES} + ) + +add_library(freertos-Interface INTERFACE) + +target_include_directories(freertos-Interface + INTERFACE + ${FREERTOS_BASE}/include + ${FREERTOS_PORT_BASE}/${FREERTOS_PORT} + ${FREERTOS_CONF} + ${FREERTOS_PLUS_INCLUDES} + ) + +add_subdirectory(mock EXCLUDE_FROM_ALL) + +unset(FREERTOS_SOURCES) diff --git a/external/FreeRTOS/Source/portable/NullPort/port.c b/external/FreeRTOS/Source/portable/NullPort/port.c new file mode 100644 index 0000000..86b7f1d --- /dev/null +++ b/external/FreeRTOS/Source/portable/NullPort/port.c @@ -0,0 +1,72 @@ +/* + * FreeRTOS Kernel V10.3.0 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + */ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack __attribute__((unused)), TaskFunction_t pxCode __attribute__((unused)), void *pvParameters __attribute__((unused))) { + return ( StackType_t * ) NULL; +} + +BaseType_t xPortStartScheduler( void ) { + return 0u; +} + +void vPortDeleteThread( void *pvTaskToDelete __attribute__((unused))) {} + +void vPortCloseRunningThread( void *pvTaskToDelete __attribute__((unused)), volatile BaseType_t *pxPendYield __attribute__((unused))) {} + +void vPortEndScheduler( void ) {} + +BaseType_t xPortIsInsideInterrupt( void ) { return pdFALSE; } + +void vPortGenerateSimulatedInterrupt( uint32_t ulInterruptNumber __attribute__((unused)) ) {} + +void vPortSetInterruptHandler( uint32_t ulInterruptNumber __attribute__((unused)), uint32_t (*pvHandler)( void ) __attribute__((unused))) {} + +void vPortEnterCritical( void ) {} + +void vPortExitCritical( void ) {} + +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions __attribute__((unused))) {} + +void vPortGetHeapStats( HeapStats_t *pxHeapStats __attribute__((unused))) {} + +void *pvPortMalloc( size_t xSize __attribute__((unused))) { + return NULL; +} + +void vPortFree( void *pv __attribute__((unused))) {} + +void vPortInitialiseBlocks( void ) {} + +size_t xPortGetFreeHeapSize( void ) { + return 0u; +} + +size_t xPortGetMinimumEverFreeHeapSize( void ) { + return 0u; +} \ No newline at end of file diff --git a/external/FreeRTOS/Source/portable/NullPort/portmacro.h b/external/FreeRTOS/Source/portable/NullPort/portmacro.h new file mode 100644 index 0000000..eacfe09 --- /dev/null +++ b/external/FreeRTOS/Source/portable/NullPort/portmacro.h @@ -0,0 +1,139 @@ +/* + * This file has been forked from MSVC-MingW portmacro.h + * + * This port is meant to be a generic port of freeRTOS. It is targeted to use cases like + * having compilable freeRTOS mocks without having an actual port to a target platform. + * + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + */ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +/****************************************************************************** + Defines +******************************************************************************/ +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE size_t +#define portBASE_TYPE long +#define portPOINTER_SIZE_TYPE size_t + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if (configUSE_16_BIT_TICKS == 1) +typedef uint16_t TickType_t; +#define portMAX_DELAY (TickType_t)0xffff +#else +typedef uint32_t TickType_t; +#define portMAX_DELAY (TickType_t)0xffffffffUL + +/* 32/64-bit tick type on a 32/64-bit architecture, so reads of the tick + count do not need to be guarded with a critical section. */ +#define portTICK_TYPE_IS_ATOMIC 1 +#endif + +/* Hardware specifics. */ +#define portSTACK_GROWTH (-1) +#define portTICK_PERIOD_MS ((TickType_t)1000 / configTICK_RATE_HZ) +#define portINLINE __inline + +#define portBYTE_ALIGNMENT 4 + +extern BaseType_t xPortIsInsideInterrupt(void); + +#define portYIELD() vPortGenerateSimulatedInterrupt(portINTERRUPT_YIELD) + +/* Simulated interrupts return pdFALSE if no context switch should be performed, +or a non-zero number if a context switch should be performed. */ +#define portYIELD_FROM_ISR(x) (void)x +#define portEND_SWITCHING_ISR(x) portYIELD_FROM_ISR((x)) + +void vPortCloseRunningThread(void* pvTaskToDelete, volatile BaseType_t* pxPendYield); +void vPortDeleteThread(void* pvThreadToDelete); +#define portCLEAN_UP_TCB(pxTCB) vPortDeleteThread(pxTCB) +#define portPRE_TASK_DELETE_HOOK(pvTaskToDelete, pxPendYield) vPortCloseRunningThread((pvTaskToDelete), (pxPendYield)) +#define portDISABLE_INTERRUPTS() vPortEnterCritical() +#define portENABLE_INTERRUPTS() vPortExitCritical() + +/* Critical section handling. */ +void vPortEnterCritical(void); +void vPortExitCritical(void); + +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + +/* 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) (void)(uxTopPriority = 0u, uxReadyPriorities) + +#endif /* taskRECORD_READY_PRIORITY */ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO(vFunction, pvParameters) void vFunction(void* pvParameters) +#define portTASK_FUNCTION(vFunction, pvParameters) void vFunction(void* pvParameters) + +#define portINTERRUPT_YIELD (0UL) +#define portINTERRUPT_TICK (1UL) + +/* + * Raise a simulated interrupt represented by the bit mask in ulInterruptMask. + * Each bit can be used to represent an individual interrupt - with the first + * two bits being used for the Yield and Tick interrupts respectively. +*/ +void vPortGenerateSimulatedInterrupt(uint32_t ulInterruptNumber); + +/* + * Install an interrupt handler to be called by the simulated interrupt handler + * thread. The interrupt number must be above any used by the kernel itself + * (at the time of writing the kernel was using interrupt numbers 0, 1, and 2 + * as defined above). The number must also be lower than 32. + * + * Interrupt handler functions must return a non-zero value if executing the + * handler resulted in a task switch being required. + */ +void vPortSetInterruptHandler(uint32_t ulInterruptNumber, uint32_t (*pvHandler)(void)); + +#endif \ No newline at end of file diff --git a/external/FreeRTOS/mock/CMakeLists.txt b/external/FreeRTOS/mock/CMakeLists.txt new file mode 100644 index 0000000..b97a0f6 --- /dev/null +++ b/external/FreeRTOS/mock/CMakeLists.txt @@ -0,0 +1,16 @@ +set(MODULE_NAME "freertos-Mock") + +add_library(${MODULE_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/FreeRtosMock.cpp + ) + +target_include_directories(${MODULE_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +target_link_libraries(${MODULE_NAME} + PUBLIC + freertos-Interface + gmock + ) diff --git a/external/FreeRTOS/mock/FreeRtosMock.cpp b/external/FreeRTOS/mock/FreeRtosMock.cpp new file mode 100644 index 0000000..6ef3797 --- /dev/null +++ b/external/FreeRTOS/mock/FreeRtosMock.cpp @@ -0,0 +1,498 @@ +/** + * @file FreeRtosMock.cpp + * @author Kris Wolfgramm + * + * @brief See *.hpp for more details + * + * @copyright Copyright 2019 Robert Bosch GmbH. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +#include + +namespace freertos { + +std::unique_ptr mock{}; + +// See https://github.com/google/googletest/blob/master/googlemock/docs/cook_book.md#making-the-compilation-faster +Mock::Mock() {} +Mock::~Mock() {} + +/* task.h abstraction */ + +extern "C" TaskHandle_t xTaskGetCurrentTaskHandle() { + assert(mock); + return mock->xTaskGetCurrentTaskHandle(); +} + +extern "C" char* pcTaskGetName(TaskHandle_t xTaskToQuery) { + assert(mock); + return mock->pcTaskGetName(xTaskToQuery); +} + +extern "C" TickType_t xTaskGetTickCount() { + assert(mock); + return mock->xTaskGetTickCount(); +} + +extern "C" TickType_t xTaskGetTickCountFromISR() { + assert(mock); + return mock->xTaskGetTickCountFromISR(); +} + +extern "C" BaseType_t xTaskGetSchedulerState() { + assert(mock); + return mock->xTaskGetSchedulerState(); +} + +extern "C" void vTaskSuspend(TaskHandle_t xTaskToSuspend) { + assert(mock); + mock->vTaskSuspend(xTaskToSuspend); +} + +extern "C" void vTaskSuspendAll() { + assert(mock); + mock->vTaskSuspendAll(); +} + +extern "C" void vTaskResume(TaskHandle_t xTaskToResume) { + assert(mock); + mock->vTaskResume(xTaskToResume); +} + +extern "C" void vTaskDelay(const TickType_t xTicksToDelay) { + assert(mock); + mock->vTaskDelay(xTicksToDelay); +} + +extern "C" void vTaskDelayUntil(TickType_t* const pxPreviousWakeTime, + const TickType_t xTimeIncrement) { + assert(mock); + mock->vTaskDelayUntil(pxPreviousWakeTime, xTimeIncrement); +} + +extern "C" TaskHandle_t +xTaskCreateStatic(TaskFunction_t pxTaskCode, const char* const pcName, + const uint32_t ulStackDepth, void* const pvParameters, + UBaseType_t uxPriority, StackType_t* const puxStackBuffer, + StaticTask_t* const pxTaskBuffer) { + assert(mock); + return mock->xTaskCreateStatic(pxTaskCode, pcName, ulStackDepth, pvParameters, + uxPriority, puxStackBuffer, pxTaskBuffer); +} + +extern "C" void vTaskDelete(TaskHandle_t xTaskToDelete) { + assert(mock); + mock->vTaskDelete(xTaskToDelete); +} + +extern "C" BaseType_t +xTaskGenericNotify(TaskHandle_t xTaskToNotify, uint32_t ulValue, + eNotifyAction eAction, + uint32_t* pulPreviousNotificationValue) { + assert(mock); + return mock->xTaskGenericNotify(xTaskToNotify, ulValue, eAction, + pulPreviousNotificationValue); +} + +extern "C" BaseType_t +xTaskGenericNotifyFromISR(TaskHandle_t xTaskToNotify, uint32_t ulValue, + eNotifyAction eAction, + uint32_t* pulPreviousNotificationValue, + BaseType_t* pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xTaskGenericNotifyFromISR(xTaskToNotify, ulValue, eAction, + pulPreviousNotificationValue, pxHigherPriorityTaskWoken); +} + +extern "C" BaseType_t +xTaskNotifyWait(uint32_t ulBitsToClearOnEntry, + uint32_t ulBitsToClearOnExit, + uint32_t* pulNotificationValue, + TickType_t xTicksToWait) { + assert(mock); + return mock->xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, + pulNotificationValue, xTicksToWait); +} + +extern "C" uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit, + TickType_t xTicksToWait) { + assert(mock); + return mock->ulTaskNotifyTake(xClearCountOnExit, xTicksToWait); +} + +extern "C" void vTaskNotifyGiveFromISR(TaskHandle_t xTaskToNotify, + BaseType_t *pxHigherPriorityTaskWoken) { + assert(mock); + mock->vTaskNotifyGiveFromISR(xTaskToNotify, pxHigherPriorityTaskWoken); +} + +extern "C" UBaseType_t uxTaskPriorityGet(const TaskHandle_t pxTask) { + assert(mock); + return mock->uxTaskPriorityGet(pxTask); +} + +extern "C" void vTaskPrioritySet(TaskHandle_t pxTask, + UBaseType_t uxNewPriority) { + assert(mock); + mock->vTaskPrioritySet(pxTask, uxNewPriority); +} + +/* queue.h abstraction QueueSets */ + +extern "C" QueueSetMemberHandle_t +xQueueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks) { + assert(mock); + return mock->xQueueSelectFromSet(xQueueSet, xBlockTimeTicks); +} + +extern "C" BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet) { + assert(mock); + return mock->xQueueAddToSet(xQueueOrSemaphore, xQueueSet); +} + +extern "C" BaseType_t +xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, + QueueSetHandle_t xQueueSet) { + assert(mock); + return mock->xQueueRemoveFromSet(xQueueOrSemaphore, xQueueSet); +} + +extern "C" eTaskState eTaskGetState(TaskHandle_t pxTask) { + assert(mock); + return mock->eTaskGetState(pxTask); +} + +extern "C" UBaseType_t uxTaskGetNumberOfTasks() { + assert(mock); + return mock->uxTaskGetNumberOfTasks(); +} + +/* queue.h abstraction */ + +extern "C" QueueHandle_t xQueueGenericCreateStatic( + const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, + uint8_t* pucQueueStorage, StaticQueue_t* pxStaticQueue, + const uint8_t ucQueueType) { + assert(mock); + return mock->xQueueGenericCreateStatic( + uxQueueLength, uxItemSize, pucQueueStorage, pxStaticQueue, ucQueueType); +} + +extern "C" QueueHandle_t xQueueCreateMutexStatic(const uint8_t ucQueueType, + StaticQueue_t* pxStaticQueue) { + assert(mock); + return mock->xQueueCreateMutexStatic(ucQueueType, pxStaticQueue); +} + +extern "C" BaseType_t xQueueGenericSend(QueueHandle_t xQueue, + const void* const pvItemToQueue, + TickType_t xTicksToWait, + const BaseType_t xCopyPosition) { + assert(mock); + return mock->xQueueGenericSend(xQueue, pvItemToQueue, xTicksToWait, + xCopyPosition); +} + +extern "C" BaseType_t +xQueueGenericSendFromISR(QueueHandle_t xQueue, const void* const pvItemToQueue, + BaseType_t* const pxHigherPriorityTaskWoken, + const BaseType_t xCopyPosition) { + assert(mock); + return mock->xQueueGenericSendFromISR( + xQueue, pvItemToQueue, pxHigherPriorityTaskWoken, xCopyPosition); +} + +extern "C" BaseType_t xQueueReceive(QueueHandle_t xQueue, void* pvBuffer, + TickType_t xTicksToWait) { + assert(mock); + return mock->xQueueReceive(xQueue, pvBuffer, xTicksToWait); +} + +extern "C" BaseType_t +xQueueReceiveFromISR(QueueHandle_t xQueue, void* pvBuffer, + BaseType_t* pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xQueueReceiveFromISR(xQueue, pvBuffer, + pxHigherPriorityTaskWoken); +} + +extern "C" BaseType_t xQueueGenericReset(QueueHandle_t xQueue, + BaseType_t xNewQueue) { + assert(mock); + return mock->xQueueGenericReset(xQueue, xNewQueue); +} + +extern "C" void vQueueDelete(QueueHandle_t xQueue) { + assert(mock); + mock->vQueueDelete(xQueue); +} + +#if (configQUEUE_REGISTRY_SIZE > 0) +extern "C" void vQueueAddToRegistry(QueueHandle_t xQueue, const char* name) { + assert(mock); + mock->vQueueAddToRegistry(xQueue, name); +} + +extern "C" void vQueueUnregisterQueue(QueueHandle_t xQueue) { + assert(mock); + mock->vQueueUnregisterQueue(xQueue); +} + +extern "C" const char* pcQueueGetName(QueueHandle_t xQueue) { + assert(mock); + return mock->pcQueueGetName(xQueue); +} +#endif + +extern "C" TaskHandle_t xQueueGetMutexHolder(QueueHandle_t xSemaphore) { + assert(mock); + return mock->xQueueGetMutexHolder(xSemaphore); +} + +extern "C" BaseType_t xQueueTakeMutexRecursive(QueueHandle_t xMutex, TickType_t xTicksToWait) { + assert(mock); + return mock->xQueueTakeMutexRecursive(xMutex, xTicksToWait); +} + +extern "C" BaseType_t xQueueGiveMutexRecursive(QueueHandle_t xMutex) { + assert(mock); + return mock->xQueueGiveMutexRecursive(xMutex); +} + +extern "C" UBaseType_t uxQueueSpacesAvailable(QueueHandle_t xQueue) { + assert(mock); + return mock->uxQueueSpacesAvailable(xQueue); +} + +extern "C" UBaseType_t uxQueueMessagesWaiting(const QueueHandle_t xQueue) { + assert(mock); + return mock->uxQueueMessagesWaiting(xQueue); +} + +extern "C" UBaseType_t +uxQueueMessagesWaitingFromISR(const QueueHandle_t xQueue) { + assert(mock); + return mock->uxQueueMessagesWaitingFromISR(xQueue); +} + +/* stream_buffer.h abstraction */ + +extern "C" StreamBufferHandle_t xStreamBufferGenericCreateStatic( + size_t xBufferSizeBytes, size_t xTriggerLevelBytes, + BaseType_t xIsMessageBuffer, uint8_t* const pucStreamBufferStorageArea, + StaticStreamBuffer_t* const pxStaticStreamBuffer) { + assert(mock); + return mock->xStreamBufferGenericCreateStatic( + xBufferSizeBytes, xTriggerLevelBytes, xIsMessageBuffer, + pucStreamBufferStorageArea, pxStaticStreamBuffer); +} + +extern "C" size_t xStreamBufferSend(StreamBufferHandle_t xStreamBuffer, + const void* pvTxData, + size_t xDataLengthBytes, + TickType_t xTicksToWait) { + assert(mock); + return mock->xStreamBufferSend(xStreamBuffer, pvTxData, xDataLengthBytes, + xTicksToWait); +} + +extern "C" size_t +xStreamBufferSendFromISR(StreamBufferHandle_t xStreamBuffer, + const void* pvTxData, size_t xDataLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xStreamBufferSendFromISR( + xStreamBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken); +} + +extern "C" size_t xStreamBufferReceive(StreamBufferHandle_t xStreamBuffer, + void* pvRxData, + size_t xBufferLengthBytes, + TickType_t xTicksToWait) { + assert(mock); + return mock->xStreamBufferReceive(xStreamBuffer, pvRxData, xBufferLengthBytes, + xTicksToWait); +} + +extern "C" size_t +xStreamBufferReceiveFromISR(StreamBufferHandle_t xStreamBuffer, void* pvRxData, + size_t xBufferLengthBytes, + BaseType_t* const pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xStreamBufferReceiveFromISR( + xStreamBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken); +} + +extern "C" void vStreamBufferDelete(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + mock->vStreamBufferDelete(xStreamBuffer); +} + +extern "C" size_t +xStreamBufferBytesAvailable(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + return mock->xStreamBufferBytesAvailable(xStreamBuffer); +} + +extern "C" size_t +xStreamBufferSpacesAvailable(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + return mock->xStreamBufferSpacesAvailable(xStreamBuffer); +} + +extern "C" BaseType_t xStreamBufferReset(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + return mock->xStreamBufferReset(xStreamBuffer); +} + +extern "C" BaseType_t xStreamBufferIsEmpty(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + return mock->xStreamBufferIsEmpty(xStreamBuffer); +} + +extern "C" BaseType_t xStreamBufferIsFull(StreamBufferHandle_t xStreamBuffer) { + assert(mock); + return mock->xStreamBufferIsFull(xStreamBuffer); +} + +/* semphr.h abstraction */ + +extern "C" SemaphoreHandle_t +xQueueCreateCountingSemaphoreStatic(UBaseType_t uxMaxCount, + UBaseType_t uxInitialCount, + StaticSemaphore_t* pxSemaphoreBuffer) { + assert(mock); + return mock->xQueueCreateCountingSemaphoreStatic(uxMaxCount, uxInitialCount, + pxSemaphoreBuffer); +} + +extern "C" BaseType_t xQueueSemaphoreTake(SemaphoreHandle_t xSemaphore, + TickType_t xBlockTime) { + assert(mock); + return mock->xQueueSemaphoreTake(xSemaphore, xBlockTime); +} + +extern "C" BaseType_t xQueueGiveFromISR(SemaphoreHandle_t xSemaphore, + BaseType_t* pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xQueueGiveFromISR(xSemaphore, pxHigherPriorityTaskWoken); +} + +/* event_groups.h abstraction */ + +extern "C" EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToSet) { + assert(mock); + return mock->xEventGroupSetBits(xEventGroup, uxBitsToSet); +} + +extern "C" EventBits_t xEventGroupWaitBits(EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToWaitFor, + const BaseType_t xClearOnExit, + const BaseType_t xWaitForAllBits, + TickType_t xTicksToWait) { + assert(mock); + return mock->xEventGroupWaitBits(xEventGroup, uxBitsToWaitFor, xClearOnExit, + xWaitForAllBits, xTicksToWait); +} + +extern "C" EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup, + const EventBits_t uxBitsToClear) { + assert(mock); + return mock->xEventGroupClearBits(xEventGroup, uxBitsToClear); +} + +extern "C" EventGroupHandle_t +xEventGroupCreateStatic(StaticEventGroup_t* pxEventGroupBuffer) { + assert(mock); + return mock->xEventGroupCreateStatic(pxEventGroupBuffer); +} + +extern "C" void +vEventGroupSetBitsCallback(void* pvEventGroup, const uint32_t ulBitsToSet) { + assert(mock); + return mock->vEventGroupSetBitsCallback(pvEventGroup, ulBitsToSet); +} + +/* portmacro.h abstraction */ + +extern "C" void vPortEnterCritical() { + assert(mock); + return mock->vPortEnterCritical(); +} + +extern "C" void vPortExitCritical() { + assert(mock); + return mock->vPortExitCritical(); +} + +extern "C" void vClearInterruptMask(uint32_t ulMask) { + assert(mock); + mock->vClearInterruptMask(ulMask); +} + +extern "C" uint32_t ulSetInterruptMask() { + assert(mock); + return mock->ulSetInterruptMask(); +} + +extern "C" BaseType_t xPortIsInsideInterrupt() { + assert(mock); + return mock->xPortIsInsideInterrupt(); +} + +/* timers.h abstraction */ + +extern "C" TimerHandle_t xTimerCreateStatic(const char* const pcTimerName, + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void* const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t* pxTimerBuffer) { + assert(mock); + return mock->xTimerCreateStatic(pcTimerName, xTimerPeriodInTicks, uxAutoReload, + pvTimerID, pxCallbackFunction, pxTimerBuffer); +} + +extern "C" BaseType_t xTimerGenericCommand(TimerHandle_t xTimer, + const BaseType_t xCommandID, + const TickType_t xOptionalValue, + BaseType_t* const pxHigherPriorityTaskWoken, + const TickType_t xTicksToWait) { + assert(mock); + return mock->xTimerGenericCommand(xTimer, xCommandID, xOptionalValue, + pxHigherPriorityTaskWoken, xTicksToWait); +} + +extern "C" void* pvTimerGetTimerID(const TimerHandle_t xTimer) { + assert(mock); + return mock->pvTimerGetTimerID(xTimer); +} + +extern "C" TickType_t xTimerGetPeriod(TimerHandle_t xTimer) { + assert(mock); + return mock->xTimerGetPeriod(xTimer); +} + +extern "C" BaseType_t xTimerIsTimerActive(TimerHandle_t xTimer) { + assert(mock); + return mock->xTimerIsTimerActive(xTimer); +} + +extern "C" BaseType_t xTimerPendFunctionCallFromISR(PendedFunction_t xFunctionToPend, + void* pvParameter1, + uint32_t ulParameter2, + BaseType_t* pxHigherPriorityTaskWoken) { + assert(mock); + return mock->xTimerPendFunctionCallFromISR(xFunctionToPend, pvParameter1, + ulParameter2, + pxHigherPriorityTaskWoken); +} + +} // namespace freertos diff --git a/external/FreeRTOS/mock/FreeRtosMock.hpp b/external/FreeRTOS/mock/FreeRtosMock.hpp new file mode 100644 index 0000000..fda4677 --- /dev/null +++ b/external/FreeRTOS/mock/FreeRtosMock.hpp @@ -0,0 +1,167 @@ +/** + * @file FreeRtosMock.hpp + * @author Kris Wolfgramm + * + * @brief Mocks FreeRTOS functionality + * + * @copyright Copyright 2019 Robert Bosch GmbH. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + */ +#ifndef MOCK_FREERTOSMOCK_HPP +#define MOCK_FREERTOSMOCK_HPP + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace freertos { + +class Mock { +public: + Mock(); + virtual ~Mock(); + + /* task.h abstraction */ + + MOCK_METHOD(char*, pcTaskGetName, (TaskHandle_t)); + MOCK_METHOD(TickType_t, xTaskGetTickCount, ()); + MOCK_METHOD(TickType_t, xTaskGetTickCountFromISR, ()); + MOCK_METHOD(BaseType_t, xTaskGetSchedulerState, ()); + MOCK_METHOD(void, vTaskSuspend, (TaskHandle_t)); + MOCK_METHOD(void, vTaskSuspendAll, ()); + MOCK_METHOD(void, vTaskResume, (TaskHandle_t)); + MOCK_METHOD(void, vTaskDelay, (const TickType_t)); + MOCK_METHOD(void, vTaskDelayUntil, (TickType_t*, TickType_t)); + MOCK_METHOD(TaskHandle_t, xTaskCreateStatic, + (TaskFunction_t, const char*, uint32_t, void*, UBaseType_t, + StackType_t*, StaticTask_t*)); + MOCK_METHOD(void, vTaskDelete, (TaskHandle_t)); + MOCK_METHOD(BaseType_t, xTaskGenericNotify, + (TaskHandle_t, uint32_t, eNotifyAction, uint32_t*)); + MOCK_METHOD(BaseType_t, xTaskGenericNotifyFromISR, + (TaskHandle_t, uint32_t, eNotifyAction, uint32_t*, BaseType_t*)); + MOCK_METHOD(BaseType_t, xTaskNotifyWait, + (uint32_t, uint32_t, uint32_t*, TickType_t)); + MOCK_METHOD(uint32_t, ulTaskNotifyTake, (BaseType_t, TickType_t)); + MOCK_METHOD(void, vTaskNotifyGiveFromISR, (TaskHandle_t, BaseType_t*)); + MOCK_METHOD(TaskHandle_t, xTaskGetCurrentTaskHandle, ()); + MOCK_METHOD(UBaseType_t, uxTaskPriorityGet, (const TaskHandle_t)); + MOCK_METHOD(void, vTaskPrioritySet, (TaskHandle_t, UBaseType_t)); + MOCK_METHOD(eTaskState, eTaskGetState, (TaskHandle_t)); + MOCK_METHOD(UBaseType_t, uxTaskGetNumberOfTasks, ()); + + /* queue.h abstraction QueueSets */ + + MOCK_METHOD(QueueSetMemberHandle_t, xQueueSelectFromSet, + (QueueSetHandle_t, TickType_t)); + MOCK_METHOD(BaseType_t, xQueueAddToSet, + (QueueSetMemberHandle_t, QueueSetHandle_t)); + MOCK_METHOD(BaseType_t, xQueueRemoveFromSet, + (QueueSetMemberHandle_t, QueueSetHandle_t)); + + /* queue.h abstraction */ + + MOCK_METHOD(QueueHandle_t, xQueueGenericCreateStatic, + (UBaseType_t, UBaseType_t, uint8_t*, StaticQueue_t*, uint8_t)); + MOCK_METHOD(QueueHandle_t, xQueueCreateMutexStatic, + (uint8_t, StaticQueue_t*)); + MOCK_METHOD(BaseType_t, xQueueGenericSend, + (QueueHandle_t, const void*, TickType_t, BaseType_t)); + MOCK_METHOD(BaseType_t, xQueueGenericSendFromISR, + (QueueHandle_t, const void*, BaseType_t*, BaseType_t)); + MOCK_METHOD(BaseType_t, xQueueReceive, (QueueHandle_t, void*, TickType_t)); + MOCK_METHOD(BaseType_t, xQueueReceiveFromISR, + (QueueHandle_t, void*, BaseType_t*)); + MOCK_METHOD(BaseType_t, xQueueGenericReset, (QueueHandle_t, BaseType_t)); + MOCK_METHOD(void, vQueueDelete, (QueueHandle_t)); + MOCK_METHOD(TaskHandle_t, xQueueGetMutexHolder, (QueueHandle_t)); + MOCK_METHOD(BaseType_t, xQueueTakeMutexRecursive, (QueueHandle_t, TickType_t)); + MOCK_METHOD(BaseType_t, xQueueGiveMutexRecursive, (QueueHandle_t)); + MOCK_METHOD(UBaseType_t, uxQueueSpacesAvailable, (QueueHandle_t)); + MOCK_METHOD(UBaseType_t, uxQueueMessagesWaiting, (QueueHandle_t)); + MOCK_METHOD(UBaseType_t, uxQueueMessagesWaitingFromISR, (QueueHandle_t)); + +#if (configQUEUE_REGISTRY_SIZE > 0) + MOCK_METHOD(void, vQueueAddToRegistry, (QueueHandle_t, const char*)); + MOCK_METHOD(void, vQueueUnregisterQueue, (QueueHandle_t)); + MOCK_METHOD(const char*, pcQueueGetName, (QueueHandle_t)); +#endif + + /* stream_buffer.h abstraction */ + + MOCK_METHOD(StreamBufferHandle_t, xStreamBufferGenericCreateStatic, + (size_t, size_t, BaseType_t, uint8_t*, StaticStreamBuffer_t*)); + MOCK_METHOD(size_t, xStreamBufferSend, + (StreamBufferHandle_t, const void*, size_t, TickType_t)); + MOCK_METHOD(size_t, xStreamBufferSendFromISR, + (StreamBufferHandle_t, const void*, size_t, BaseType_t*)); + MOCK_METHOD(size_t, xStreamBufferReceive, + (StreamBufferHandle_t, void*, size_t, TickType_t)); + MOCK_METHOD(size_t, xStreamBufferReceiveFromISR, + (StreamBufferHandle_t, void*, size_t, BaseType_t*)); + MOCK_METHOD(void, vStreamBufferDelete, (StreamBufferHandle_t)); + MOCK_METHOD(size_t, xStreamBufferBytesAvailable, (StreamBufferHandle_t)); + MOCK_METHOD(size_t, xStreamBufferSpacesAvailable, (StreamBufferHandle_t)); + MOCK_METHOD(BaseType_t, xStreamBufferReset, (StreamBufferHandle_t)); + MOCK_METHOD(BaseType_t, xStreamBufferIsEmpty, (StreamBufferHandle_t)); + MOCK_METHOD(BaseType_t, xStreamBufferIsFull, (StreamBufferHandle_t)); + + /* semphr.h abstraction */ + + MOCK_METHOD(SemaphoreHandle_t, xQueueCreateCountingSemaphoreStatic, + (UBaseType_t, UBaseType_t, StaticSemaphore_t*)); + MOCK_METHOD(BaseType_t, xQueueSemaphoreTake, (SemaphoreHandle_t, TickType_t)); + MOCK_METHOD(BaseType_t, xQueueGiveFromISR, (SemaphoreHandle_t, BaseType_t*)); + + /* event_groups.h abstraction */ + + MOCK_METHOD(EventBits_t, xEventGroupSetBits, + (EventGroupHandle_t, const EventBits_t)); + MOCK_METHOD(EventBits_t, xEventGroupWaitBits, + (EventGroupHandle_t, const EventBits_t, const BaseType_t, + const BaseType_t, TickType_t)); + MOCK_METHOD(EventBits_t, xEventGroupClearBits, + (EventGroupHandle_t, EventBits_t)); + MOCK_METHOD(EventGroupHandle_t, xEventGroupCreateStatic, + (StaticEventGroup_t*)); + MOCK_METHOD(void, vEventGroupSetBitsCallback, + (void*, const uint32_t)); + + /* portmacro.h abstraction */ + MOCK_METHOD(void, vPortEnterCritical, ()); + MOCK_METHOD(void, vPortExitCritical, ()); + MOCK_METHOD(void, vClearInterruptMask, (uint32_t)); + MOCK_METHOD(uint32_t, ulSetInterruptMask, ()); + MOCK_METHOD(BaseType_t, xPortIsInsideInterrupt, ()); + + /* timers.h abstraction */ + MOCK_METHOD(TimerHandle_t, xTimerCreateStatic, + (const char* const, const TickType_t, const UBaseType_t, + void* const, TimerCallbackFunction_t, StaticTimer_t*)); + MOCK_METHOD(BaseType_t, xTimerGenericCommand, + (TimerHandle_t, const BaseType_t, const TickType_t, + BaseType_t* const, const TickType_t)); + MOCK_METHOD(void*, pvTimerGetTimerID, (const TimerHandle_t)); + MOCK_METHOD(TickType_t, xTimerGetPeriod, (TimerHandle_t)); + MOCK_METHOD(BaseType_t, xTimerIsTimerActive, (TimerHandle_t)); + MOCK_METHOD(BaseType_t, xTimerPendFunctionCallFromISR, + (PendedFunction_t, void*, uint32_t, BaseType_t*)); +}; + +extern std::unique_ptr mock; + +} // namespace freertos + +#endif /* MOCK_FREERTOSMOCK_HPP */ diff --git a/external/Segger-RTT/CMakeLists.txt b/external/Segger-RTT/CMakeLists.txt new file mode 100644 index 0000000..995d797 --- /dev/null +++ b/external/Segger-RTT/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.11) + +set(RTT_BASE ${CMAKE_CURRENT_LIST_DIR}/RTT) + +add_library(rtt + EXCLUDE_FROM_ALL + ${RTT_BASE}/SEGGER_RTT.c + ${RTT_BASE}/SEGGER_RTT_printf.c + ${RTT_BASE}/SEGGER_RTT_ASM_ARMv7M.S + ) + +target_include_directories(rtt SYSTEM + PUBLIC + ${RTT_BASE} + ${RTT_CONF} + ) + +target_compile_features(rtt + PUBLIC + c_std_99 + ) diff --git a/external/Segger-RTT/RTT/SEGGER_RTT.c b/external/Segger-RTT/RTT/SEGGER_RTT.c new file mode 100644 index 0000000..a7a5a62 --- /dev/null +++ b/external/Segger-RTT/RTT/SEGGER_RTT.c @@ -0,0 +1,2061 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2019 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* condition is met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this condition and the following disclaimer. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT.c +Purpose : Implementation of SEGGER real-time transfer (RTT) which + allows real-time communication on targets which support + debugger memory accesses while the CPU is running. +Revision: $Rev: 20869 $ + +Additional information: + Type "int" is assumed to be 32-bits in size + H->T Host to target communication + T->H Target to host communication + + RTT channel 0 is always present and reserved for Terminal usage. + Name is fixed to "Terminal" + + Effective buffer size: SizeOfBuffer - 1 + + WrOff == RdOff: Buffer is empty + WrOff == (RdOff - 1): Buffer is full + WrOff > RdOff: Free space includes wrap-around + WrOff < RdOff: Used space includes wrap-around + (WrOff == (SizeOfBuffer - 1)) && (RdOff == 0): + Buffer full and wrap-around after next byte + + +---------------------------------------------------------------------- +*/ + +#include "SEGGER_RTT.h" + +#include // for memcpy + +/********************************************************************* +* +* Configuration, default values +* +********************************************************************** +*/ + +#if SEGGER_RTT_CPU_CACHE_LINE_SIZE + #ifdef SEGGER_RTT_CB_ALIGN + #error "Custom SEGGER_RTT_CB_ALIGN() is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif + #ifdef SEGGER_RTT_BUFFER_ALIGN + #error "Custom SEGGER_RTT_BUFFER_ALIGN() is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif + #ifdef SEGGER_RTT_PUT_CB_SECTION + #error "Custom SEGGER_RTT_PUT_CB_SECTION() is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif + #ifdef SEGGER_RTT_PUT_BUFFER_SECTION + #error "Custom SEGGER_RTT_PUT_BUFFER_SECTION() is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif + #ifdef SEGGER_RTT_BUFFER_ALIGNMENT + #error "Custom SEGGER_RTT_BUFFER_ALIGNMENT is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif + #ifdef SEGGER_RTT_ALIGNMENT + #error "Custom SEGGER_RTT_ALIGNMENT is not supported for SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif +#endif + +#ifndef BUFFER_SIZE_UP + #define BUFFER_SIZE_UP 1024 // Size of the buffer for terminal output of target, up to host +#endif + +#ifndef BUFFER_SIZE_DOWN + #define BUFFER_SIZE_DOWN 16 // Size of the buffer for terminal input to target from host (Usually keyboard input) +#endif + +#ifndef SEGGER_RTT_MAX_NUM_UP_BUFFERS + #define SEGGER_RTT_MAX_NUM_UP_BUFFERS 2 // Number of up-buffers (T->H) available on this target +#endif + +#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS + #define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS 2 // Number of down-buffers (H->T) available on this target +#endif + +#ifndef SEGGER_RTT_BUFFER_SECTION + #if defined(SEGGER_RTT_SECTION) + #define SEGGER_RTT_BUFFER_SECTION SEGGER_RTT_SECTION + #endif +#endif + +#ifndef SEGGER_RTT_ALIGNMENT + #define SEGGER_RTT_ALIGNMENT SEGGER_RTT_CPU_CACHE_LINE_SIZE +#endif + +#ifndef SEGGER_RTT_BUFFER_ALIGNMENT + #define SEGGER_RTT_BUFFER_ALIGNMENT SEGGER_RTT_CPU_CACHE_LINE_SIZE +#endif + +#ifndef SEGGER_RTT_MODE_DEFAULT + #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP +#endif + +#ifndef SEGGER_RTT_LOCK + #define SEGGER_RTT_LOCK() +#endif + +#ifndef SEGGER_RTT_UNLOCK + #define SEGGER_RTT_UNLOCK() +#endif + +#ifndef STRLEN + #define STRLEN(a) strlen((a)) +#endif + +#ifndef STRCPY + #define STRCPY(pDest, pSrc) strcpy((pDest), (pSrc)) +#endif + +#ifndef SEGGER_RTT_MEMCPY_USE_BYTELOOP + #define SEGGER_RTT_MEMCPY_USE_BYTELOOP 0 +#endif + +#ifndef SEGGER_RTT_MEMCPY + #ifdef MEMCPY + #define SEGGER_RTT_MEMCPY(pDest, pSrc, NumBytes) MEMCPY((pDest), (pSrc), (NumBytes)) + #else + #define SEGGER_RTT_MEMCPY(pDest, pSrc, NumBytes) memcpy((pDest), (pSrc), (NumBytes)) + #endif +#endif + +#ifndef MIN + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX + #define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +// +// For some environments, NULL may not be defined until certain headers are included +// +#ifndef NULL + #define NULL 0 +#endif + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ +#if (defined __ICCARM__) || (defined __ICCRX__) + #define RTT_PRAGMA(P) _Pragma(#P) +#endif + +#if SEGGER_RTT_ALIGNMENT || SEGGER_RTT_BUFFER_ALIGNMENT + #if (defined __GNUC__) + #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) + #elif (defined __ICCARM__) || (defined __ICCRX__) + #define PRAGMA(A) _Pragma(#A) +#define SEGGER_RTT_ALIGN(Var, Alignment) RTT_PRAGMA(data_alignment=Alignment) \ + Var + #elif (defined __CC_ARM) + #define SEGGER_RTT_ALIGN(Var, Alignment) Var __attribute__ ((aligned (Alignment))) + #else + #error "Alignment not supported for this compiler." + #endif +#else + #define SEGGER_RTT_ALIGN(Var, Alignment) Var +#endif + +#if defined(SEGGER_RTT_SECTION) || defined (SEGGER_RTT_BUFFER_SECTION) + #if (defined __GNUC__) + #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section))) Var + #elif (defined __ICCARM__) || (defined __ICCRX__) +#define SEGGER_RTT_PUT_SECTION(Var, Section) RTT_PRAGMA(location=Section) \ + Var + #elif (defined __CC_ARM) + #define SEGGER_RTT_PUT_SECTION(Var, Section) __attribute__ ((section (Section), zero_init)) Var + #else + #error "Section placement not supported for this compiler." + #endif +#else + #define SEGGER_RTT_PUT_SECTION(Var, Section) Var +#endif + +#if SEGGER_RTT_ALIGNMENT + #define SEGGER_RTT_CB_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_ALIGNMENT) +#else + #define SEGGER_RTT_CB_ALIGN(Var) Var +#endif + +#if SEGGER_RTT_BUFFER_ALIGNMENT + #define SEGGER_RTT_BUFFER_ALIGN(Var) SEGGER_RTT_ALIGN(Var, SEGGER_RTT_BUFFER_ALIGNMENT) +#else + #define SEGGER_RTT_BUFFER_ALIGN(Var) Var +#endif + + +#if defined(SEGGER_RTT_SECTION) + #define SEGGER_RTT_PUT_CB_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_SECTION) +#else + #define SEGGER_RTT_PUT_CB_SECTION(Var) Var +#endif + +#if defined(SEGGER_RTT_BUFFER_SECTION) + #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) SEGGER_RTT_PUT_SECTION(Var, SEGGER_RTT_BUFFER_SECTION) +#else + #define SEGGER_RTT_PUT_BUFFER_SECTION(Var) Var +#endif + +/********************************************************************* +* +* Static const data +* +********************************************************************** +*/ + +static unsigned char _aTerminalId[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +/********************************************************************* +* +* Static data +* +********************************************************************** +*/ + +// +// RTT Control Block and allocate buffers for channel 0 +// +SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT)); +SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acUpBuffer [SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_UP)])); +SEGGER_RTT_PUT_BUFFER_SECTION(SEGGER_RTT_BUFFER_ALIGN(static char _acDownBuffer[SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(BUFFER_SIZE_DOWN)])); + +static unsigned char _ActiveTerminal; + +/********************************************************************* +* +* Static functions +* +********************************************************************** +*/ + +/********************************************************************* +* +* _DoInit() +* +* Function description +* Initializes the control block an buffers. +* May only be called via INIT() to avoid overriding settings. +* +*/ +#define INIT() { \ + volatile SEGGER_RTT_CB* pRTTCBInit; \ + pRTTCBInit = (volatile SEGGER_RTT_CB*)((char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); \ + do { \ + if (pRTTCBInit->acID[0] == '\0') { \ + _DoInit(); \ + } \ + } while (0); \ + } + +static void _DoInit(void) { + volatile SEGGER_RTT_CB* p; // Volatile to make sure that compiler cannot change the order of accesses to the control block + // + // Initialize control block + // + p = (volatile SEGGER_RTT_CB*)((char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access control block uncached so that nothing in the cache ever becomes dirty and all changes are visible in HW directly + p->MaxNumUpBuffers = SEGGER_RTT_MAX_NUM_UP_BUFFERS; + p->MaxNumDownBuffers = SEGGER_RTT_MAX_NUM_DOWN_BUFFERS; + // + // Initialize up buffer 0 + // + p->aUp[0].sName = "Terminal"; + p->aUp[0].pBuffer = _acUpBuffer; + p->aUp[0].SizeOfBuffer = BUFFER_SIZE_UP; + p->aUp[0].RdOff = 0u; + p->aUp[0].WrOff = 0u; + p->aUp[0].Flags = SEGGER_RTT_MODE_DEFAULT; + // + // Initialize down buffer 0 + // + p->aDown[0].sName = "Terminal"; + p->aDown[0].pBuffer = _acDownBuffer; + p->aDown[0].SizeOfBuffer = BUFFER_SIZE_DOWN; + p->aDown[0].RdOff = 0u; + p->aDown[0].WrOff = 0u; + p->aDown[0].Flags = SEGGER_RTT_MODE_DEFAULT; + // + // Finish initialization of the control block. + // Copy Id string in three steps to make sure "SEGGER RTT" is not found + // in initializer memory (usually flash) by J-Link + // + STRCPY((char*)&p->acID[7], "RTT"); + RTT__DMB(); // Force order of memory accessed inside core for cores that allow to change the order + STRCPY((char*)&p->acID[0], "SEGGER"); + RTT__DMB(); // Force order of memory accessed inside core for cores that allow to change the order + p->acID[6] = ' '; + RTT__DMB(); // Force order of memory accessed inside core for cores that allow to change the order +} + +/********************************************************************* +* +* _WriteBlocking() +* +* Function description +* Stores a specified number of characters in SEGGER RTT ring buffer +* and updates the associated write pointer which is periodically +* read by the host. +* The caller is responsible for managing the write chunk sizes as +* _WriteBlocking() will block until all data has been posted successfully. +* +* Parameters +* pRing Ring buffer to post to. +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* >= 0 - Number of bytes written into buffer. +*/ +static unsigned _WriteBlocking(SEGGER_RTT_BUFFER_UP* pRing, const char* pBuffer, unsigned NumBytes) { + unsigned NumBytesToWrite; + unsigned NumBytesWritten; + unsigned RdOff; + unsigned WrOff; + volatile char* pDst; + // + // Write data to buffer and handle wrap-around if necessary + // + NumBytesWritten = 0u; + WrOff = pRing->WrOff; + do { + RdOff = pRing->RdOff; // May be changed by host (debug probe) in the meantime + if (RdOff > WrOff) { + NumBytesToWrite = RdOff - WrOff - 1u; + } else { + NumBytesToWrite = pRing->SizeOfBuffer - (WrOff - RdOff + 1u); + } + NumBytesToWrite = MIN(NumBytesToWrite, (pRing->SizeOfBuffer - WrOff)); // Number of bytes that can be written until buffer wrap-around + NumBytesToWrite = MIN(NumBytesToWrite, NumBytes); + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytesWritten += NumBytesToWrite; + NumBytes -= NumBytesToWrite; + WrOff += NumBytesToWrite; + while (NumBytesToWrite--) { + *pDst++ = *pBuffer++; + }; +#else + SEGGER_RTT_MEMCPY((void*)pDst, pBuffer, NumBytesToWrite); + NumBytesWritten += NumBytesToWrite; + pBuffer += NumBytesToWrite; + NumBytes -= NumBytesToWrite; + WrOff += NumBytesToWrite; +#endif + if (WrOff == pRing->SizeOfBuffer) { + WrOff = 0u; + } + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff; + } while (NumBytes); + return NumBytesWritten; +} + +/********************************************************************* +* +* _WriteNoCheck() +* +* Function description +* Stores a specified number of characters in SEGGER RTT ring buffer +* and updates the associated write pointer which is periodically +* read by the host. +* It is callers responsibility to make sure data actually fits in buffer. +* +* Parameters +* pRing Ring buffer to post to. +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Notes +* (1) If there might not be enough space in the "Up"-buffer, call _WriteBlocking +*/ +static void _WriteNoCheck(SEGGER_RTT_BUFFER_UP* pRing, const char* pData, unsigned NumBytes) { + unsigned NumBytesAtOnce; + unsigned WrOff; + unsigned Rem; + volatile char* pDst; + + WrOff = pRing->WrOff; + Rem = pRing->SizeOfBuffer - WrOff; + if (Rem > NumBytes) { + // + // All data fits before wrap around + // + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + WrOff += NumBytes; + while (NumBytes--) { + *pDst++ = *pData++; + }; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff; +#else + SEGGER_RTT_MEMCPY((void*)pDst, pData, NumBytes); + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff + NumBytes; +#endif + } else { + // + // We reach the end of the buffer, so need to wrap around + // +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; + NumBytesAtOnce = Rem; + while (NumBytesAtOnce--) { + *pDst++ = *pData++; + }; + pDst = pRing->pBuffer + SEGGER_RTT_UNCACHED_OFF; + NumBytesAtOnce = NumBytes - Rem; + while (NumBytesAtOnce--) { + *pDst++ = *pData++; + }; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = NumBytes - Rem; +#else + NumBytesAtOnce = Rem; + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; + SEGGER_RTT_MEMCPY((void*)pDst, pData, NumBytesAtOnce); + NumBytesAtOnce = NumBytes - Rem; + pDst = pRing->pBuffer + SEGGER_RTT_UNCACHED_OFF; + SEGGER_RTT_MEMCPY((void*)pDst, pData + Rem, NumBytesAtOnce); + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = NumBytesAtOnce; +#endif + } +} + +/********************************************************************* +* +* _PostTerminalSwitch() +* +* Function description +* Switch terminal to the given terminal ID. It is the caller's +* responsibility to ensure the terminal ID is correct and there is +* enough space in the buffer for this to complete successfully. +* +* Parameters +* pRing Ring buffer to post to. +* TerminalId Terminal ID to switch to. +*/ +static void _PostTerminalSwitch(SEGGER_RTT_BUFFER_UP* pRing, unsigned char TerminalId) { + unsigned char ac[2]; + + ac[0] = 0xFFu; + ac[1] = _aTerminalId[TerminalId]; // Caller made already sure that TerminalId does not exceed our terminal limit + _WriteBlocking(pRing, (const char*)ac, 2u); +} + +/********************************************************************* +* +* _GetAvailWriteSpace() +* +* Function description +* Returns the number of bytes that can be written to the ring +* buffer without blocking. +* +* Parameters +* pRing Ring buffer to check. +* +* Return value +* Number of bytes that are free in the buffer. +*/ +static unsigned _GetAvailWriteSpace(SEGGER_RTT_BUFFER_UP* pRing) { + unsigned RdOff; + unsigned WrOff; + unsigned r; + // + // Avoid warnings regarding volatile access order. It's not a problem + // in this case, but dampen compiler enthusiasm. + // + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + if (RdOff <= WrOff) { + r = pRing->SizeOfBuffer - 1u - WrOff + RdOff; + } else { + r = RdOff - WrOff - 1u; + } + return r; +} + +/********************************************************************* +* +* Public code +* +********************************************************************** +*/ + +/********************************************************************* +* +* SEGGER_RTT_ReadUpBufferNoLock() +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the application. +* Do not lock against interrupts and multiple access. +* Used to do the same operation that J-Link does, to transfer +* RTT data via other channels, such as TCP/IP or UART. +* +* Parameters +* BufferIndex Index of Up-buffer to be used. +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-up-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +* +* Additional information +* This function must not be called when J-Link might also do RTT. +*/ +unsigned SEGGER_RTT_ReadUpBufferNoLock(unsigned BufferIndex, void* pData, unsigned BufferSize) { + unsigned NumBytesRem; + unsigned NumBytesRead; + unsigned RdOff; + unsigned WrOff; + unsigned char* pBuffer; + SEGGER_RTT_BUFFER_UP* pRing; + volatile char* pSrc; + + INIT(); + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + pBuffer = (unsigned char*)pData; + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + NumBytesRead = 0u; + // + // Read from current read position to wrap-around of buffer, first + // + if (RdOff > WrOff) { + NumBytesRem = pRing->SizeOfBuffer - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + pSrc = (pRing->pBuffer + RdOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytesRead += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + while (NumBytesRem--) { + *pBuffer++ = *pSrc++; + }; +#else + SEGGER_RTT_MEMCPY(pBuffer, (void*)pSrc, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; +#endif + // + // Handle wrap-around of buffer + // + if (RdOff == pRing->SizeOfBuffer) { + RdOff = 0u; + } + } + // + // Read remaining items of buffer + // + NumBytesRem = WrOff - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + if (NumBytesRem > 0u) { + pSrc = (pRing->pBuffer + RdOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytesRead += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + while (NumBytesRem--) { + *pBuffer++ = *pSrc++; + }; +#else + SEGGER_RTT_MEMCPY(pBuffer, (void*)pSrc, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; +#endif + } + // + // Update read offset of buffer + // + if (NumBytesRead) { + pRing->RdOff = RdOff; + } + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_ReadNoLock() +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the host. +* Do not lock against interrupts and multiple access. +* +* Parameters +* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +*/ +unsigned SEGGER_RTT_ReadNoLock(unsigned BufferIndex, void* pData, unsigned BufferSize) { + unsigned NumBytesRem; + unsigned NumBytesRead; + unsigned RdOff; + unsigned WrOff; + unsigned char* pBuffer; + SEGGER_RTT_BUFFER_DOWN* pRing; + volatile char* pSrc; + // + INIT(); + pRing = (SEGGER_RTT_BUFFER_DOWN*)((char*)&_SEGGER_RTT.aDown[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + pBuffer = (unsigned char*)pData; + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + NumBytesRead = 0u; + // + // Read from current read position to wrap-around of buffer, first + // + if (RdOff > WrOff) { + NumBytesRem = pRing->SizeOfBuffer - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + pSrc = (pRing->pBuffer + RdOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytesRead += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + while (NumBytesRem--) { + *pBuffer++ = *pSrc++; + }; +#else + SEGGER_RTT_MEMCPY(pBuffer, (void*)pSrc, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; +#endif + // + // Handle wrap-around of buffer + // + if (RdOff == pRing->SizeOfBuffer) { + RdOff = 0u; + } + } + // + // Read remaining items of buffer + // + NumBytesRem = WrOff - RdOff; + NumBytesRem = MIN(NumBytesRem, BufferSize); + if (NumBytesRem > 0u) { + pSrc = (pRing->pBuffer + RdOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytesRead += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; + while (NumBytesRem--) { + *pBuffer++ = *pSrc++; + }; +#else + SEGGER_RTT_MEMCPY(pBuffer, (void*)pSrc, NumBytesRem); + NumBytesRead += NumBytesRem; + pBuffer += NumBytesRem; + BufferSize -= NumBytesRem; + RdOff += NumBytesRem; +#endif + } + if (NumBytesRead) { + pRing->RdOff = RdOff; + } + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_ReadUpBuffer +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the application. +* Used to do the same operation that J-Link does, to transfer +* RTT data via other channels, such as TCP/IP or UART. +* +* Parameters +* BufferIndex Index of Up-buffer to be used. +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-up-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +* +* Additional information +* This function must not be called when J-Link might also do RTT. +* This function locks against all other RTT operations. I.e. during +* the read operation, writing is also locked. +* If only one consumer reads from the up buffer, +* call sEGGER_RTT_ReadUpBufferNoLock() instead. +*/ +unsigned SEGGER_RTT_ReadUpBuffer(unsigned BufferIndex, void* pBuffer, unsigned BufferSize) { + unsigned NumBytesRead; + + SEGGER_RTT_LOCK(); + // + // Call the non-locking read function + // + NumBytesRead = SEGGER_RTT_ReadUpBufferNoLock(BufferIndex, pBuffer, BufferSize); + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_Read +* +* Function description +* Reads characters from SEGGER real-time-terminal control block +* which have been previously stored by the host. +* +* Parameters +* BufferIndex Index of Down-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to buffer provided by target application, to copy characters from RTT-down-buffer to. +* BufferSize Size of the target application buffer. +* +* Return value +* Number of bytes that have been read. +*/ +unsigned SEGGER_RTT_Read(unsigned BufferIndex, void* pBuffer, unsigned BufferSize) { + unsigned NumBytesRead; + + SEGGER_RTT_LOCK(); + // + // Call the non-locking read function + // + NumBytesRead = SEGGER_RTT_ReadNoLock(BufferIndex, pBuffer, BufferSize); + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + // + return NumBytesRead; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteWithOverwriteNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block. +* SEGGER_RTT_WriteWithOverwriteNoLock does not lock the application +* and overwrites data if the data does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, data is overwritten. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +* (3) Do not use SEGGER_RTT_WriteWithOverwriteNoLock if a J-Link +* connection reads RTT data. +*/ +void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + volatile char* pDst; + // + // Get "to-host" ring buffer and copy some elements into local variables. + // + pData = (const char *)pBuffer; + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // Check if we will overwrite data and need to adjust the RdOff. + // + if (pRing->WrOff == pRing->RdOff) { + Avail = pRing->SizeOfBuffer - 1u; + } else if ( pRing->WrOff < pRing->RdOff) { + Avail = pRing->RdOff - pRing->WrOff - 1u; + } else { + Avail = pRing->RdOff - pRing->WrOff - 1u + pRing->SizeOfBuffer; + } + if (NumBytes > Avail) { + pRing->RdOff += (NumBytes - Avail); + while (pRing->RdOff >= pRing->SizeOfBuffer) { + pRing->RdOff -= pRing->SizeOfBuffer; + } + } + // + // Write all data, no need to check the RdOff, but possibly handle multiple wrap-arounds + // + Avail = pRing->SizeOfBuffer - pRing->WrOff; + do { + if (Avail > NumBytes) { + // + // Last round + // + pDst = (pRing->pBuffer + pRing->WrOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + Avail = NumBytes; + while (NumBytes--) { + *pDst++ = *pData++; + }; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff += Avail; +#else + SEGGER_RTT_MEMCPY((void*)pDst, pData, NumBytes); + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff += NumBytes; +#endif + break; + } else { + // + // Wrap-around necessary, write until wrap-around and reset WrOff + // + pDst = (pRing->pBuffer + pRing->WrOff) + SEGGER_RTT_UNCACHED_OFF; +#if SEGGER_RTT_MEMCPY_USE_BYTELOOP + NumBytes -= Avail; + while (Avail--) { + *pDst++ = *pData++; + }; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = 0; +#else + SEGGER_RTT_MEMCPY((void*)pDst, pData, Avail); + pData += Avail; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = 0; + NumBytes -= Avail; +#endif + Avail = (pRing->SizeOfBuffer - 1); + } + } while (NumBytes); +} + +/********************************************************************* +* +* SEGGER_RTT_WriteSkipNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* SEGGER_RTT_WriteSkipNoLock does not lock the application and +* skips all data, if the data does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* MUST be > 0!!! +* This is done for performance reasons, so no initial check has do be done. +* +* Return value +* 1: Data has been copied +* 0: No space, data has not been copied +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, all data is dropped. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ +#if (RTT_USE_ASM == 0) +unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + unsigned RdOff; + unsigned WrOff; + unsigned Rem; + volatile char* pDst; + // + // Cases: + // 1) RdOff <= WrOff => Space until wrap-around is sufficient + // 2) RdOff <= WrOff => Space after wrap-around needed (copy in 2 chunks) + // 3) RdOff < WrOff => No space in buf + // 4) RdOff > WrOff => Space is sufficient + // 5) RdOff > WrOff => No space in buf + // + // 1) is the most common case for large buffers and assuming that J-Link reads the data fast enough + // + pData = (const char *)pBuffer; + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + RdOff = pRing->RdOff; + WrOff = pRing->WrOff; + if (RdOff <= WrOff) { // Case 1), 2) or 3) + Avail = pRing->SizeOfBuffer - WrOff - 1u; // Space until wrap-around (assume 1 byte not usable for case that RdOff == 0) + if (Avail >= NumBytes) { // Case 1)? +CopyStraight: + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; + memcpy((void*)pDst, pData, NumBytes); + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff + NumBytes; + return 1; + } + Avail += RdOff; // Space incl. wrap-around + if (Avail >= NumBytes) { // Case 2? => If not, we have case 3) (does not fit) + Rem = pRing->SizeOfBuffer - WrOff; // Space until end of buffer + pDst = (pRing->pBuffer + WrOff) + SEGGER_RTT_UNCACHED_OFF; + memcpy((void*)pDst, pData, Rem); // Copy 1st chunk + NumBytes -= Rem; + // + // Special case: First check that assumed RdOff == 0 calculated that last element before wrap-around could not be used + // But 2nd check (considering space until wrap-around and until RdOff) revealed that RdOff is not 0, so we can use the last element + // In this case, we may use a copy straight until buffer end anyway without needing to copy 2 chunks + // Therefore, check if 2nd memcpy is necessary at all + // + if (NumBytes) { + pDst = pRing->pBuffer + SEGGER_RTT_UNCACHED_OFF; + memcpy((void*)pDst, pData + Rem, NumBytes); + } + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = NumBytes; + return 1; + } + } else { // Potential case 4) + Avail = RdOff - WrOff - 1u; + if (Avail >= NumBytes) { // Case 4)? => If not, we have case 5) (does not fit) + goto CopyStraight; + } + } + return 0; // No space in buffer +} +#endif + +/********************************************************************* +* +* SEGGER_RTT_WriteDownBufferNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block inside a buffer. +* SEGGER_RTT_WriteDownBufferNoLock does not lock the application. +* Used to do the same operation that J-Link does, to transfer +* RTT data from other channels, such as TCP/IP or UART. +* +* Parameters +* BufferIndex Index of "Down"-buffer to be used. +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Down"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +* +* Additional information +* This function must not be called when J-Link might also do RTT. +*/ +unsigned SEGGER_RTT_WriteDownBufferNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + unsigned Avail; + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + // + // Get "to-target" ring buffer. + // It is save to cast that to a "to-host" buffer. Up and Down buffer differ in volatility of offsets that might be modified by J-Link. + // + pData = (const char *)pBuffer; + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aDown[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // How we output depends upon the mode... + // + switch (pRing->Flags) { + case SEGGER_RTT_MODE_NO_BLOCK_SKIP: + // + // If we are in skip mode and there is no space for the whole + // of this output, don't bother. + // + Avail = _GetAvailWriteSpace(pRing); + if (Avail < NumBytes) { + Status = 0u; + } else { + Status = NumBytes; + _WriteNoCheck(pRing, pData, NumBytes); + } + break; + case SEGGER_RTT_MODE_NO_BLOCK_TRIM: + // + // If we are in trim mode, trim to what we can output without blocking. + // + Avail = _GetAvailWriteSpace(pRing); + Status = Avail < NumBytes ? Avail : NumBytes; + _WriteNoCheck(pRing, pData, Status); + break; + case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: + // + // If we are in blocking mode, output everything. + // + Status = _WriteBlocking(pRing, pData, NumBytes); + break; + default: + Status = 0u; + break; + } + // + // Finish up. + // + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* SEGGER_RTT_WriteNoLock does not lock the application. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ +unsigned SEGGER_RTT_WriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + unsigned Avail; + const char* pData; + SEGGER_RTT_BUFFER_UP* pRing; + // + // Get "to-host" ring buffer. + // + pData = (const char *)pBuffer; + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // How we output depends upon the mode... + // + switch (pRing->Flags) { + case SEGGER_RTT_MODE_NO_BLOCK_SKIP: + // + // If we are in skip mode and there is no space for the whole + // of this output, don't bother. + // + Avail = _GetAvailWriteSpace(pRing); + if (Avail < NumBytes) { + Status = 0u; + } else { + Status = NumBytes; + _WriteNoCheck(pRing, pData, NumBytes); + } + break; + case SEGGER_RTT_MODE_NO_BLOCK_TRIM: + // + // If we are in trim mode, trim to what we can output without blocking. + // + Avail = _GetAvailWriteSpace(pRing); + Status = Avail < NumBytes ? Avail : NumBytes; + _WriteNoCheck(pRing, pData, Status); + break; + case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: + // + // If we are in blocking mode, output everything. + // + Status = _WriteBlocking(pRing, pData, NumBytes); + break; + default: + Status = 0u; + break; + } + // + // Finish up. + // + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteDownBuffer +* +* Function description +* Stores a specified number of characters in SEGGER RTT control block in a buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Down"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +* +* Additional information +* This function must not be called when J-Link might also do RTT. +* This function locks against all other RTT operations. I.e. during +* the write operation, writing from the application is also locked. +* If only one consumer writes to the down buffer, +* call SEGGER_RTT_WriteDownBufferNoLock() instead. +*/ +unsigned SEGGER_RTT_WriteDownBuffer(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + + INIT(); + SEGGER_RTT_LOCK(); + Status = SEGGER_RTT_WriteDownBufferNoLock(BufferIndex, pBuffer, NumBytes); // Call the non-locking write function + SEGGER_RTT_UNLOCK(); + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_Write +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +*/ +unsigned SEGGER_RTT_Write(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes) { + unsigned Status; + + INIT(); + SEGGER_RTT_LOCK(); + Status = SEGGER_RTT_WriteNoLock(BufferIndex, pBuffer, NumBytes); // Call the non-locking write function + SEGGER_RTT_UNLOCK(); + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_WriteString +* +* Function description +* Stores string in SEGGER RTT control block. +* This data is read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* s Pointer to string. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +* (2) String passed to this function has to be \0 terminated +* (3) \0 termination character is *not* stored in RTT buffer +*/ +unsigned SEGGER_RTT_WriteString(unsigned BufferIndex, const char* s) { + unsigned Len; + + Len = STRLEN(s); + return SEGGER_RTT_Write(BufferIndex, s, Len); +} + +/********************************************************************* +* +* SEGGER_RTT_PutCharSkipNoLock +* +* Function description +* Stores a single character/byte in SEGGER RTT buffer. +* SEGGER_RTT_PutCharSkipNoLock does not lock the application and +* skips the byte, if it does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* c Byte to be stored. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, the character is dropped. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ + +unsigned SEGGER_RTT_PutCharSkipNoLock(unsigned BufferIndex, char c) { + SEGGER_RTT_BUFFER_UP* pRing; + unsigned WrOff; + unsigned Status; + volatile char* pDst; + // + // Get "to-host" ring buffer. + // + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // Get write position and handle wrap-around if necessary + // + WrOff = pRing->WrOff + 1; + if (WrOff == pRing->SizeOfBuffer) { + WrOff = 0; + } + // + // Output byte if free space is available + // + if (WrOff != pRing->RdOff) { + pDst = (pRing->pBuffer + pRing->WrOff) + SEGGER_RTT_UNCACHED_OFF; + *pDst = c; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff; + Status = 1; + } else { + Status = 0; + } + // + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_PutCharSkip +* +* Function description +* Stores a single character/byte in SEGGER RTT buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* c Byte to be stored. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, the character is dropped. +*/ + +unsigned SEGGER_RTT_PutCharSkip(unsigned BufferIndex, char c) { + SEGGER_RTT_BUFFER_UP* pRing; + unsigned WrOff; + unsigned Status; + volatile char* pDst; + // + // Prepare + // + INIT(); + SEGGER_RTT_LOCK(); + // + // Get "to-host" ring buffer. + // + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // Get write position and handle wrap-around if necessary + // + WrOff = pRing->WrOff + 1; + if (WrOff == pRing->SizeOfBuffer) { + WrOff = 0; + } + // + // Output byte if free space is available + // + if (WrOff != pRing->RdOff) { + pDst = (pRing->pBuffer + pRing->WrOff) + SEGGER_RTT_UNCACHED_OFF; + *pDst = c; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff; + Status = 1; + } else { + Status = 0; + } + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + // + return Status; +} + + /********************************************************************* +* +* SEGGER_RTT_PutChar +* +* Function description +* Stores a single character/byte in SEGGER RTT buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* c Byte to be stored. +* +* Return value +* Number of bytes which have been stored in the "Up"-buffer. +* +* Notes +* (1) Data is stored according to buffer flags. +*/ + +unsigned SEGGER_RTT_PutChar(unsigned BufferIndex, char c) { + SEGGER_RTT_BUFFER_UP* pRing; + unsigned WrOff; + unsigned Status; + volatile char* pDst; + // + // Prepare + // + INIT(); + SEGGER_RTT_LOCK(); + // + // Get "to-host" ring buffer. + // + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // Get write position and handle wrap-around if necessary + // + WrOff = pRing->WrOff + 1; + if (WrOff == pRing->SizeOfBuffer) { + WrOff = 0; + } + // + // Wait for free space if mode is set to blocking + // + if (pRing->Flags == SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL) { + while (WrOff == pRing->RdOff) { + ; + } + } + // + // Output byte if free space is available + // + if (WrOff != pRing->RdOff) { + pDst = (pRing->pBuffer + pRing->WrOff) + SEGGER_RTT_UNCACHED_OFF; + *pDst = c; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + pRing->WrOff = WrOff; + Status = 1; + } else { + Status = 0; + } + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_GetKey +* +* Function description +* Reads one character from the SEGGER RTT buffer. +* Host has previously stored data there. +* +* Return value +* < 0 - No character available (buffer empty). +* >= 0 - Character which has been read. (Possible values: 0 - 255) +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0. +*/ +int SEGGER_RTT_GetKey(void) { + char c; + int r; + + r = (int)SEGGER_RTT_Read(0u, &c, 1u); + if (r == 1) { + r = (int)(unsigned char)c; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_WaitKey +* +* Function description +* Waits until at least one character is avaible in the SEGGER RTT buffer. +* Once a character is available, it is read and this function returns. +* +* Return value +* >=0 - Character which has been read. +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0 +* (2) This function is blocking if no character is present in RTT buffer +*/ +int SEGGER_RTT_WaitKey(void) { + int r; + + do { + r = SEGGER_RTT_GetKey(); + } while (r < 0); + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_HasKey +* +* Function description +* Checks if at least one character for reading is available in the SEGGER RTT buffer. +* +* Return value +* == 0 - No characters are available to read. +* == 1 - At least one character is available. +* +* Notes +* (1) This function is only specified for accesses to RTT buffer 0 +*/ +int SEGGER_RTT_HasKey(void) { + SEGGER_RTT_BUFFER_DOWN* pRing; + unsigned RdOff; + int r; + + INIT(); + pRing = (SEGGER_RTT_BUFFER_DOWN*)((char*)&_SEGGER_RTT.aDown[0] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + RdOff = pRing->RdOff; + if (RdOff != pRing->WrOff) { + r = 1; + } else { + r = 0; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_HasData +* +* Function description +* Check if there is data from the host in the given buffer. +* +* Return value: +* ==0: No data +* !=0: Data in buffer +* +*/ +unsigned SEGGER_RTT_HasData(unsigned BufferIndex) { + SEGGER_RTT_BUFFER_DOWN* pRing; + unsigned v; + + pRing = (SEGGER_RTT_BUFFER_DOWN*)((char*)&_SEGGER_RTT.aDown[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + v = pRing->WrOff; + return v - pRing->RdOff; +} + +/********************************************************************* +* +* SEGGER_RTT_HasDataUp +* +* Function description +* Check if there is data remaining to be sent in the given buffer. +* +* Return value: +* ==0: No data +* !=0: Data in buffer +* +*/ +unsigned SEGGER_RTT_HasDataUp(unsigned BufferIndex) { + SEGGER_RTT_BUFFER_UP* pRing; + unsigned v; + + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + v = pRing->RdOff; + return pRing->WrOff - v; +} + +/********************************************************************* +* +* SEGGER_RTT_AllocDownBuffer +* +* Function description +* Run-time configuration of the next down-buffer (H->T). +* The next buffer, which is not used yet is configured. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. Buffer Index +* < 0 - Error +*/ +int SEGGER_RTT_AllocDownBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int BufferIndex; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + SEGGER_RTT_LOCK(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + BufferIndex = 0; + do { + if (pRTTCB->aDown[BufferIndex].pBuffer == NULL) { + break; + } + BufferIndex++; + } while (BufferIndex < pRTTCB->MaxNumDownBuffers); + if (BufferIndex < pRTTCB->MaxNumDownBuffers) { + pRTTCB->aDown[BufferIndex].sName = sName; + pRTTCB->aDown[BufferIndex].pBuffer = (char*)pBuffer; + pRTTCB->aDown[BufferIndex].SizeOfBuffer = BufferSize; + pRTTCB->aDown[BufferIndex].RdOff = 0u; + pRTTCB->aDown[BufferIndex].WrOff = 0u; + pRTTCB->aDown[BufferIndex].Flags = Flags; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + } else { + BufferIndex = -1; + } + SEGGER_RTT_UNLOCK(); + return BufferIndex; +} + +/********************************************************************* +* +* SEGGER_RTT_AllocUpBuffer +* +* Function description +* Run-time configuration of the next up-buffer (T->H). +* The next buffer, which is not used yet is configured. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. Buffer Index +* < 0 - Error +*/ +int SEGGER_RTT_AllocUpBuffer(const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int BufferIndex; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + SEGGER_RTT_LOCK(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + BufferIndex = 0; + do { + if (pRTTCB->aUp[BufferIndex].pBuffer == NULL) { + break; + } + BufferIndex++; + } while (BufferIndex < pRTTCB->MaxNumUpBuffers); + if (BufferIndex < pRTTCB->MaxNumUpBuffers) { + pRTTCB->aUp[BufferIndex].sName = sName; + pRTTCB->aUp[BufferIndex].pBuffer = (char*)pBuffer; + pRTTCB->aUp[BufferIndex].SizeOfBuffer = BufferSize; + pRTTCB->aUp[BufferIndex].RdOff = 0u; + pRTTCB->aUp[BufferIndex].WrOff = 0u; + pRTTCB->aUp[BufferIndex].Flags = Flags; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + } else { + BufferIndex = -1; + } + SEGGER_RTT_UNLOCK(); + return BufferIndex; +} + +/********************************************************************* +* +* SEGGER_RTT_ConfigUpBuffer +* +* Function description +* Run-time configuration of a specific up-buffer (T->H). +* Buffer to be configured is specified by index. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* BufferIndex Index of the buffer to configure. +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 - O.K. +* < 0 - Error +* +* Additional information +* Buffer 0 is configured on compile-time. +* May only be called once per buffer. +* Buffer name and flags can be reconfigured using the appropriate functions. +*/ +int SEGGER_RTT_ConfigUpBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumUpBuffers) { + SEGGER_RTT_LOCK(); + if (BufferIndex > 0u) { + pRTTCB->aUp[BufferIndex].sName = sName; + pRTTCB->aUp[BufferIndex].pBuffer = (char*)pBuffer; + pRTTCB->aUp[BufferIndex].SizeOfBuffer = BufferSize; + pRTTCB->aUp[BufferIndex].RdOff = 0u; + pRTTCB->aUp[BufferIndex].WrOff = 0u; + } + pRTTCB->aUp[BufferIndex].Flags = Flags; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_ConfigDownBuffer +* +* Function description +* Run-time configuration of a specific down-buffer (H->T). +* Buffer to be configured is specified by index. +* This includes: Buffer address, size, name, flags, ... +* +* Parameters +* BufferIndex Index of the buffer to configure. +* sName Pointer to a constant name string. +* pBuffer Pointer to a buffer to be used. +* BufferSize Size of the buffer. +* Flags Operating modes. Define behavior if buffer is full (not enough space for entire message). +* +* Return value +* >= 0 O.K. +* < 0 Error +* +* Additional information +* Buffer 0 is configured on compile-time. +* May only be called once per buffer. +* Buffer name and flags can be reconfigured using the appropriate functions. +*/ +int SEGGER_RTT_ConfigDownBuffer(unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumDownBuffers) { + SEGGER_RTT_LOCK(); + if (BufferIndex > 0u) { + pRTTCB->aDown[BufferIndex].sName = sName; + pRTTCB->aDown[BufferIndex].pBuffer = (char*)pBuffer; + pRTTCB->aDown[BufferIndex].SizeOfBuffer = BufferSize; + pRTTCB->aDown[BufferIndex].RdOff = 0u; + pRTTCB->aDown[BufferIndex].WrOff = 0u; + } + pRTTCB->aDown[BufferIndex].Flags = Flags; + RTT__DMB(); // Force data write to be complete before writing the , in case CPU is allowed to change the order of memory accesses + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetNameUpBuffer +* +* Function description +* Run-time configuration of a specific up-buffer name (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer to renamed. +* sName Pointer to a constant name string. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetNameUpBuffer(unsigned BufferIndex, const char* sName) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumUpBuffers) { + SEGGER_RTT_LOCK(); + pRTTCB->aUp[BufferIndex].sName = sName; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetNameDownBuffer +* +* Function description +* Run-time configuration of a specific Down-buffer name (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer to renamed. +* sName Pointer to a constant name string. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetNameDownBuffer(unsigned BufferIndex, const char* sName) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumDownBuffers) { + SEGGER_RTT_LOCK(); + pRTTCB->aDown[BufferIndex].sName = sName; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetFlagsUpBuffer +* +* Function description +* Run-time configuration of specific up-buffer flags (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer. +* Flags Flags to set for the buffer. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetFlagsUpBuffer(unsigned BufferIndex, unsigned Flags) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumUpBuffers) { + SEGGER_RTT_LOCK(); + pRTTCB->aUp[BufferIndex].Flags = Flags; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_SetFlagsDownBuffer +* +* Function description +* Run-time configuration of specific Down-buffer flags (T->H). +* Buffer to be configured is specified by index. +* +* Parameters +* BufferIndex Index of the buffer to renamed. +* Flags Flags to set for the buffer. +* +* Return value +* >= 0 O.K. +* < 0 Error +*/ +int SEGGER_RTT_SetFlagsDownBuffer(unsigned BufferIndex, unsigned Flags) { + int r; + volatile SEGGER_RTT_CB* pRTTCB; + + INIT(); + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + if (BufferIndex < (unsigned)pRTTCB->MaxNumDownBuffers) { + SEGGER_RTT_LOCK(); + pRTTCB->aDown[BufferIndex].Flags = Flags; + SEGGER_RTT_UNLOCK(); + r = 0; + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_Init +* +* Function description +* Initializes the RTT Control Block. +* Should be used in RAM targets, at start of the application. +* +*/ +void SEGGER_RTT_Init (void) { + _DoInit(); +} + +/********************************************************************* +* +* SEGGER_RTT_SetTerminal +* +* Function description +* Sets the terminal to be used for output on channel 0. +* +* Parameters +* TerminalId Index of the terminal. +* +* Return value +* >= 0 O.K. +* < 0 Error (e.g. if RTT is configured for non-blocking mode and there was no space in the buffer to set the new terminal Id) +* +* Notes +* (1) Buffer 0 is always reserved for terminal I/O, so we can use index 0 here, fixed +*/ +int SEGGER_RTT_SetTerminal (unsigned char TerminalId) { + unsigned char ac[2]; + SEGGER_RTT_BUFFER_UP* pRing; + unsigned Avail; + int r; + + INIT(); + r = 0; + ac[0] = 0xFFu; + if (TerminalId < sizeof(_aTerminalId)) { // We only support a certain number of channels + ac[1] = _aTerminalId[TerminalId]; + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[0] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + SEGGER_RTT_LOCK(); // Lock to make sure that no other task is writing into buffer, while we are and number of free bytes in buffer does not change downwards after checking and before writing + if ((pRing->Flags & SEGGER_RTT_MODE_MASK) == SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL) { + _ActiveTerminal = TerminalId; + _WriteBlocking(pRing, (const char*)ac, 2u); + } else { // Skipping mode or trim mode? => We cannot trim this command so handling is the same for both modes + Avail = _GetAvailWriteSpace(pRing); + if (Avail >= 2) { + _ActiveTerminal = TerminalId; // Only change active terminal in case of success + _WriteNoCheck(pRing, (const char*)ac, 2u); + } else { + r = -1; + } + } + SEGGER_RTT_UNLOCK(); + } else { + r = -1; + } + return r; +} + +/********************************************************************* +* +* SEGGER_RTT_TerminalOut +* +* Function description +* Writes a string to the given terminal +* without changing the terminal for channel 0. +* +* Parameters +* TerminalId Index of the terminal. +* s String to be printed on the terminal. +* +* Return value +* >= 0 - Number of bytes written. +* < 0 - Error. +* +*/ +int SEGGER_RTT_TerminalOut (unsigned char TerminalId, const char* s) { + int Status; + unsigned FragLen; + unsigned Avail; + SEGGER_RTT_BUFFER_UP* pRing; + // + INIT(); + // + // Validate terminal ID. + // + if (TerminalId < (char)sizeof(_aTerminalId)) { // We only support a certain number of channels + // + // Get "to-host" ring buffer. + // + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[0] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + // + // Need to be able to change terminal, write data, change back. + // Compute the fixed and variable sizes. + // + FragLen = STRLEN(s); + // + // How we output depends upon the mode... + // + SEGGER_RTT_LOCK(); + Avail = _GetAvailWriteSpace(pRing); + switch (pRing->Flags & SEGGER_RTT_MODE_MASK) { + case SEGGER_RTT_MODE_NO_BLOCK_SKIP: + // + // If we are in skip mode and there is no space for the whole + // of this output, don't bother switching terminals at all. + // + if (Avail < (FragLen + 4u)) { + Status = 0; + } else { + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, FragLen); + _PostTerminalSwitch(pRing, _ActiveTerminal); + } + break; + case SEGGER_RTT_MODE_NO_BLOCK_TRIM: + // + // If we are in trim mode and there is not enough space for everything, + // trim the output but always include the terminal switch. If no room + // for terminal switch, skip that totally. + // + if (Avail < 4u) { + Status = -1; + } else { + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, (FragLen < (Avail - 4u)) ? FragLen : (Avail - 4u)); + _PostTerminalSwitch(pRing, _ActiveTerminal); + } + break; + case SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL: + // + // If we are in blocking mode, output everything. + // + _PostTerminalSwitch(pRing, TerminalId); + Status = (int)_WriteBlocking(pRing, s, FragLen); + _PostTerminalSwitch(pRing, _ActiveTerminal); + break; + default: + Status = -1; + break; + } + // + // Finish up. + // + SEGGER_RTT_UNLOCK(); + } else { + Status = -1; + } + return Status; +} + +/********************************************************************* +* +* SEGGER_RTT_GetAvailWriteSpace +* +* Function description +* Returns the number of bytes available in the ring buffer. +* +* Parameters +* BufferIndex Index of the up buffer. +* +* Return value +* Number of bytes that are free in the selected up buffer. +*/ +unsigned SEGGER_RTT_GetAvailWriteSpace (unsigned BufferIndex) { + SEGGER_RTT_BUFFER_UP* pRing; + + pRing = (SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[BufferIndex] + SEGGER_RTT_UNCACHED_OFF); // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + return _GetAvailWriteSpace(pRing); +} + + +/********************************************************************* +* +* SEGGER_RTT_GetBytesInBuffer() +* +* Function description +* Returns the number of bytes currently used in the up buffer. +* +* Parameters +* BufferIndex Index of the up buffer. +* +* Return value +* Number of bytes that are used in the buffer. +*/ +unsigned SEGGER_RTT_GetBytesInBuffer(unsigned BufferIndex) { + unsigned RdOff; + unsigned WrOff; + unsigned r; + volatile SEGGER_RTT_CB* pRTTCB; + // + // Avoid warnings regarding volatile access order. It's not a problem + // in this case, but dampen compiler enthusiasm. + // + pRTTCB = (volatile SEGGER_RTT_CB*)((unsigned char*)&_SEGGER_RTT + SEGGER_RTT_UNCACHED_OFF); // Access RTTCB uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + RdOff = pRTTCB->aUp[BufferIndex].RdOff; + WrOff = pRTTCB->aUp[BufferIndex].WrOff; + if (RdOff <= WrOff) { + r = WrOff - RdOff; + } else { + r = pRTTCB->aUp[BufferIndex].SizeOfBuffer - (WrOff - RdOff); + } + return r; +} + +/*************************** End of file ****************************/ diff --git a/external/Segger-RTT/RTT/SEGGER_RTT.h b/external/Segger-RTT/RTT/SEGGER_RTT.h new file mode 100644 index 0000000..5292b44 --- /dev/null +++ b/external/Segger-RTT/RTT/SEGGER_RTT.h @@ -0,0 +1,414 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2019 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* condition is met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this condition and the following disclaimer. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT.h +Purpose : Implementation of SEGGER real-time transfer which allows + real-time communication on targets which support debugger + memory accesses while the CPU is running. +Revision: $Rev: 20869 $ +---------------------------------------------------------------------- +*/ + +#ifndef SEGGER_RTT_H +#define SEGGER_RTT_H + +#include "SEGGER_RTT_Conf.h" + +/********************************************************************* +* +* Defines, defaults +* +********************************************************************** +*/ +#ifndef RTT_USE_ASM + #if (defined __SES_ARM) // SEGGER Embedded Studio + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #elif (defined __CROSSWORKS_ARM) // Rowley Crossworks + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #elif (defined __ARMCC_VERSION) // ARM compiler + #if (__ARMCC_VERSION >= 6000000) // ARM compiler V6.0 and later is clang based + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #else + #define _CC_HAS_RTT_ASM_SUPPORT 0 + #endif + #elif (defined __GNUC__) // GCC + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #elif (defined __clang__) // Clang compiler + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #elif ((defined __IASMARM__) || (defined __ICCARM__)) // IAR assembler/compiler + #define _CC_HAS_RTT_ASM_SUPPORT 1 + #else + #define _CC_HAS_RTT_ASM_SUPPORT 0 + #endif + #if ((defined __IASMARM__) || (defined __ICCARM__)) // IAR assembler/compiler + // + // IAR assembler / compiler + // + #if (__VER__ < 6300000) + #define VOLATILE + #else + #define VOLATILE volatile + #endif + #if (defined __ARM7M__) // Needed for old versions that do not know the define yet + #if (__CORE__ == __ARM7M__) // Cortex-M3 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #endif + #endif + #if (defined __ARM7EM__) // Needed for old versions that do not know the define yet + #if (__CORE__ == __ARM7EM__) // Cortex-M4/M7 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() asm VOLATILE ("DMB"); + #endif + #endif + #if (defined __ARM8M_BASELINE__) // Needed for old versions that do not know the define yet + #if (__CORE__ == __ARM8M_BASELINE__) // Cortex-M23 + #define _CORE_HAS_RTT_ASM_SUPPORT 0 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() asm VOLATILE ("DMB"); + #endif + #endif + #if (defined __ARM8M_MAINLINE__) // Needed for old versions that do not know the define yet + #if (__CORE__ == __ARM8M_MAINLINE__) // Cortex-M33 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() asm VOLATILE ("DMB"); + #endif + #endif + #else + // + // GCC / Clang + // + #if (defined __ARM_ARCH_7M__) // Cortex-M3 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #elif (defined __ARM_ARCH_7EM__) // Cortex-M4/M7 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() __asm volatile ("dmb\n" : : :); + #elif (defined __ARM_ARCH_8M_BASE__) // Cortex-M23 + #define _CORE_HAS_RTT_ASM_SUPPORT 0 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() __asm volatile ("dmb\n" : : :); + #elif (defined __ARM_ARCH_8M_MAIN__) // Cortex-M33 + #define _CORE_HAS_RTT_ASM_SUPPORT 1 + #define _CORE_NEEDS_DMB 1 + #define RTT__DMB() __asm volatile ("dmb\n" : : :); + #else + #define _CORE_HAS_RTT_ASM_SUPPORT 0 + #endif + #endif + // + // If IDE and core support the ASM version, enable ASM version by default + // + #ifndef _CORE_HAS_RTT_ASM_SUPPORT + #define _CORE_HAS_RTT_ASM_SUPPORT 0 // Default for unknown cores + #endif + #if (_CC_HAS_RTT_ASM_SUPPORT && _CORE_HAS_RTT_ASM_SUPPORT) + #define RTT_USE_ASM (1) + #else + #define RTT_USE_ASM (0) + #endif +#endif + +// +// We need to know if a DMB is needed to make sure that on Cortex-M7 etc. +// the order of accesses to the ring buffers is guaranteed +// Needed for: Cortex-M7, Cortex-M23, Cortex-M33 +// +#ifndef _CORE_NEEDS_DMB + #define _CORE_NEEDS_DMB 0 +#endif + +#ifndef RTT__DMB + #if _CORE_NEEDS_DMB + #error "Don't know how to place inline assembly for DMB" + #else + #define RTT__DMB() + #endif +#endif + +#ifndef SEGGER_RTT_CPU_CACHE_LINE_SIZE + #define SEGGER_RTT_CPU_CACHE_LINE_SIZE (0) // On most target systems where RTT is used, we do not have a CPU cache, therefore 0 is a good default here +#endif + +#ifndef SEGGER_RTT_UNCACHED_OFF + #if SEGGER_RTT_CPU_CACHE_LINE_SIZE + #error "SEGGER_RTT_UNCACHED_OFF must be defined when setting SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #else + #define SEGGER_RTT_UNCACHED_OFF (0) + #endif +#endif +#if RTT_USE_ASM + #if SEGGER_RTT_CPU_CACHE_LINE_SIZE + #error "RTT_USE_ASM is not available if SEGGER_RTT_CPU_CACHE_LINE_SIZE != 0" + #endif +#endif + +#ifndef SEGGER_RTT_ASM // defined when SEGGER_RTT.h is included from assembly file +#include +#include + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ + +// +// Determine how much we must pad the control block to make it a multiple of a cache line in size +// Assuming: U8 = 1B +// U16 = 2B +// U32 = 4B +// U8/U16/U32* = 4B +// +#if SEGGER_RTT_CPU_CACHE_LINE_SIZE // Avoid division by zero in case we do not have any cache + #define SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(NumBytes) (((NumBytes + SEGGER_RTT_CPU_CACHE_LINE_SIZE - 1) / SEGGER_RTT_CPU_CACHE_LINE_SIZE) * SEGGER_RTT_CPU_CACHE_LINE_SIZE) +#else + #define SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(NumBytes) (NumBytes) +#endif +#define SEGGER_RTT__CB_SIZE (16 + 4 + 4 + (SEGGER_RTT_MAX_NUM_UP_BUFFERS * 24) + (SEGGER_RTT_MAX_NUM_DOWN_BUFFERS * 24)) +#define SEGGER_RTT__CB_PADDING (SEGGER_RTT__ROUND_UP_2_CACHE_LINE_SIZE(SEGGER_RTT__CB_SIZE) - SEGGER_RTT__CB_SIZE) + +/********************************************************************* +* +* Types +* +********************************************************************** +*/ + +// +// Description for a circular buffer (also called "ring buffer") +// which is used as up-buffer (T->H) +// +typedef struct { + const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" + char* pBuffer; // Pointer to start of buffer + unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. + unsigned WrOff; // Position of next item to be written by either target. + volatile unsigned RdOff; // Position of next item to be read by host. Must be volatile since it may be modified by host. + unsigned Flags; // Contains configuration flags +} SEGGER_RTT_BUFFER_UP; + +// +// Description for a circular buffer (also called "ring buffer") +// which is used as down-buffer (H->T) +// +typedef struct { + const char* sName; // Optional name. Standard names so far are: "Terminal", "SysView", "J-Scope_t4i4" + char* pBuffer; // Pointer to start of buffer + unsigned SizeOfBuffer; // Buffer size in bytes. Note that one byte is lost, as this implementation does not fill up the buffer in order to avoid the problem of being unable to distinguish between full and empty. + volatile unsigned WrOff; // Position of next item to be written by host. Must be volatile since it may be modified by host. + unsigned RdOff; // Position of next item to be read by target (down-buffer). + unsigned Flags; // Contains configuration flags +} SEGGER_RTT_BUFFER_DOWN; + +// +// RTT control block which describes the number of buffers available +// as well as the configuration for each buffer +// +// +typedef struct { + char acID[16]; // Initialized to "SEGGER RTT" + int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2) + int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2) + SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host + SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target +#if SEGGER_RTT__CB_PADDING + unsigned char aDummy[SEGGER_RTT__CB_PADDING]; +#endif +} SEGGER_RTT_CB; + +/********************************************************************* +* +* Global data +* +********************************************************************** +*/ +extern SEGGER_RTT_CB _SEGGER_RTT; + +/********************************************************************* +* +* RTT API functions +* +********************************************************************** +*/ +#ifdef __cplusplus + extern "C" { +#endif +int SEGGER_RTT_AllocDownBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_AllocUpBuffer (const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags); +int SEGGER_RTT_GetKey (void); +unsigned SEGGER_RTT_HasData (unsigned BufferIndex); +int SEGGER_RTT_HasKey (void); +unsigned SEGGER_RTT_HasDataUp (unsigned BufferIndex); +void SEGGER_RTT_Init (void); +unsigned SEGGER_RTT_Read (unsigned BufferIndex, void* pBuffer, unsigned BufferSize); +unsigned SEGGER_RTT_ReadNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize); +int SEGGER_RTT_SetNameDownBuffer (unsigned BufferIndex, const char* sName); +int SEGGER_RTT_SetNameUpBuffer (unsigned BufferIndex, const char* sName); +int SEGGER_RTT_SetFlagsDownBuffer (unsigned BufferIndex, unsigned Flags); +int SEGGER_RTT_SetFlagsUpBuffer (unsigned BufferIndex, unsigned Flags); +int SEGGER_RTT_WaitKey (void); +unsigned SEGGER_RTT_Write (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_ASM_WriteSkipNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteString (unsigned BufferIndex, const char* s); +void SEGGER_RTT_WriteWithOverwriteNoLock(unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_PutChar (unsigned BufferIndex, char c); +unsigned SEGGER_RTT_PutCharSkip (unsigned BufferIndex, char c); +unsigned SEGGER_RTT_PutCharSkipNoLock (unsigned BufferIndex, char c); +unsigned SEGGER_RTT_GetAvailWriteSpace (unsigned BufferIndex); +unsigned SEGGER_RTT_GetBytesInBuffer (unsigned BufferIndex); +// +// Function macro for performance optimization +// +#define SEGGER_RTT_HASDATA(n) (((SEGGER_RTT_BUFFER_DOWN*)((char*)&_SEGGER_RTT.aDown[n] + SEGGER_RTT_UNCACHED_OFF))->WrOff - ((SEGGER_RTT_BUFFER_DOWN*)((char*)&_SEGGER_RTT.aDown[n] + SEGGER_RTT_UNCACHED_OFF))->RdOff) + +#if RTT_USE_ASM + #define SEGGER_RTT_WriteSkipNoLock SEGGER_RTT_ASM_WriteSkipNoLock +#endif + +/********************************************************************* +* +* RTT transfer functions to send RTT data via other channels. +* +********************************************************************** +*/ +unsigned SEGGER_RTT_ReadUpBuffer (unsigned BufferIndex, void* pBuffer, unsigned BufferSize); +unsigned SEGGER_RTT_ReadUpBufferNoLock (unsigned BufferIndex, void* pData, unsigned BufferSize); +unsigned SEGGER_RTT_WriteDownBuffer (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); +unsigned SEGGER_RTT_WriteDownBufferNoLock (unsigned BufferIndex, const void* pBuffer, unsigned NumBytes); + +#define SEGGER_RTT_HASDATA_UP(n) (((SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[n] + SEGGER_RTT_UNCACHED_OFF))->WrOff - ((SEGGER_RTT_BUFFER_UP*)((char*)&_SEGGER_RTT.aUp[n] + SEGGER_RTT_UNCACHED_OFF))->RdOff) // Access uncached to make sure we see changes made by the J-Link side and all of our changes go into HW directly + +/********************************************************************* +* +* RTT "Terminal" API functions +* +********************************************************************** +*/ +int SEGGER_RTT_SetTerminal (unsigned char TerminalId); +int SEGGER_RTT_TerminalOut (unsigned char TerminalId, const char* s); + +/********************************************************************* +* +* RTT printf functions (require SEGGER_RTT_printf.c) +* +********************************************************************** +*/ +int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...); +int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList); + +#ifdef __cplusplus + } +#endif + +#endif // ifndef(SEGGER_RTT_ASM) + +/********************************************************************* +* +* Defines +* +********************************************************************** +*/ + +// +// Operating modes. Define behavior if buffer is full (not enough space for entire message) +// +#define SEGGER_RTT_MODE_NO_BLOCK_SKIP (0) // Skip. Do not block, output nothing. (Default) +#define SEGGER_RTT_MODE_NO_BLOCK_TRIM (1) // Trim: Do not block, output as much as fits. +#define SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL (2) // Block: Wait until there is space in the buffer. +#define SEGGER_RTT_MODE_MASK (3) + +// +// Control sequences, based on ANSI. +// Can be used to control color, and clear the screen +// +#define RTT_CTRL_RESET "\x1B[0m" // Reset to default colors +#define RTT_CTRL_CLEAR "\x1B[2J" // Clear screen, reposition cursor to top left + +#define RTT_CTRL_TEXT_BLACK "\x1B[2;30m" +#define RTT_CTRL_TEXT_RED "\x1B[2;31m" +#define RTT_CTRL_TEXT_GREEN "\x1B[2;32m" +#define RTT_CTRL_TEXT_YELLOW "\x1B[2;33m" +#define RTT_CTRL_TEXT_BLUE "\x1B[2;34m" +#define RTT_CTRL_TEXT_MAGENTA "\x1B[2;35m" +#define RTT_CTRL_TEXT_CYAN "\x1B[2;36m" +#define RTT_CTRL_TEXT_WHITE "\x1B[2;37m" + +#define RTT_CTRL_TEXT_BRIGHT_BLACK "\x1B[1;30m" +#define RTT_CTRL_TEXT_BRIGHT_RED "\x1B[1;31m" +#define RTT_CTRL_TEXT_BRIGHT_GREEN "\x1B[1;32m" +#define RTT_CTRL_TEXT_BRIGHT_YELLOW "\x1B[1;33m" +#define RTT_CTRL_TEXT_BRIGHT_BLUE "\x1B[1;34m" +#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "\x1B[1;35m" +#define RTT_CTRL_TEXT_BRIGHT_CYAN "\x1B[1;36m" +#define RTT_CTRL_TEXT_BRIGHT_WHITE "\x1B[1;37m" + +#define RTT_CTRL_BG_BLACK "\x1B[24;40m" +#define RTT_CTRL_BG_RED "\x1B[24;41m" +#define RTT_CTRL_BG_GREEN "\x1B[24;42m" +#define RTT_CTRL_BG_YELLOW "\x1B[24;43m" +#define RTT_CTRL_BG_BLUE "\x1B[24;44m" +#define RTT_CTRL_BG_MAGENTA "\x1B[24;45m" +#define RTT_CTRL_BG_CYAN "\x1B[24;46m" +#define RTT_CTRL_BG_WHITE "\x1B[24;47m" + +#define RTT_CTRL_BG_BRIGHT_BLACK "\x1B[4;40m" +#define RTT_CTRL_BG_BRIGHT_RED "\x1B[4;41m" +#define RTT_CTRL_BG_BRIGHT_GREEN "\x1B[4;42m" +#define RTT_CTRL_BG_BRIGHT_YELLOW "\x1B[4;43m" +#define RTT_CTRL_BG_BRIGHT_BLUE "\x1B[4;44m" +#define RTT_CTRL_BG_BRIGHT_MAGENTA "\x1B[4;45m" +#define RTT_CTRL_BG_BRIGHT_CYAN "\x1B[4;46m" +#define RTT_CTRL_BG_BRIGHT_WHITE "\x1B[4;47m" + + +#endif + +/*************************** End of file ****************************/ diff --git a/external/Segger-RTT/RTT/SEGGER_RTT_ASM_ARMv7M.S b/external/Segger-RTT/RTT/SEGGER_RTT_ASM_ARMv7M.S new file mode 100644 index 0000000..cbbc52f --- /dev/null +++ b/external/Segger-RTT/RTT/SEGGER_RTT_ASM_ARMv7M.S @@ -0,0 +1,242 @@ +/********************************************************************* +* (c) SEGGER Microcontroller GmbH * +* The Embedded Experts * +* www.segger.com * +********************************************************************** + +-------------------------- END-OF-HEADER ----------------------------- + +File : SEGGER_RTT_ASM_ARMv7M.S +Purpose : Assembler implementation of RTT functions for ARMv7M + +Additional information: + This module is written to be assembler-independent and works with + GCC and clang (Embedded Studio) and IAR. +*/ + +#define SEGGER_RTT_ASM // Used to control processed input from header file +#include "SEGGER_RTT.h" + +/********************************************************************* +* +* Defines, fixed +* +********************************************************************** +*/ + +#define _CCIAR 0 +#define _CCCLANG 1 + +#if (defined __SES_ARM) || (defined __GNUC__) || (defined __clang__) + #define _CC_TYPE _CCCLANG + #define _PUB_SYM .global + #define _EXT_SYM .extern + #define _END .end + #define _WEAK .weak + #define _THUMB_FUNC .thumb_func + #define _THUMB_CODE .code 16 + #define _WORD .word + #define _SECTION(Sect, Type, AlignExp) .section Sect ##, "ax" + #define _ALIGN(Exp) .align Exp + #define _PLACE_LITS .ltorg + #define _DATA_SECT_START + #define _C_STARTUP _start + #define _STACK_END __stack_end__ + #define _RAMFUNC + // + // .text => Link to flash + // .fast => Link to RAM + // OtherSect => Usually link to RAM + // Alignment is 2^x + // +#elif defined (__IASMARM__) + #define _CC_TYPE _CCIAR + #define _PUB_SYM PUBLIC + #define _EXT_SYM EXTERN + #define _END END + #define _WEAK _WEAK + #define _THUMB_FUNC + #define _THUMB_CODE THUMB + #define _WORD DCD + #define _SECTION(Sect, Type, AlignExp) SECTION Sect ## : ## Type ## :REORDER:NOROOT ## (AlignExp) + #define _ALIGN(Exp) alignrom Exp + #define _PLACE_LITS + #define _DATA_SECT_START DATA + #define _C_STARTUP __iar_program_start + #define _STACK_END sfe(CSTACK) + #define _RAMFUNC SECTION_TYPE SHT_PROGBITS, SHF_WRITE | SHF_EXECINSTR + // + // .text => Link to flash + // .textrw => Link to RAM + // OtherSect => Usually link to RAM + // NOROOT => Allows linker to throw away the function, if not referenced + // Alignment is 2^x + // +#endif + +#if (_CC_TYPE == _CCIAR) + NAME SEGGER_RTT_ASM_ARMv7M +#else + .syntax unified +#endif + +#if defined (RTT_USE_ASM) && (RTT_USE_ASM == 1) + #define SHT_PROGBITS 0x1 + +/********************************************************************* +* +* Public / external symbols +* +********************************************************************** +*/ + + _EXT_SYM __aeabi_memcpy + _EXT_SYM __aeabi_memcpy4 + _EXT_SYM _SEGGER_RTT + + _PUB_SYM SEGGER_RTT_ASM_WriteSkipNoLock + +/********************************************************************* +* +* SEGGER_RTT_WriteSkipNoLock +* +* Function description +* Stores a specified number of characters in SEGGER RTT +* control block which is then read by the host. +* SEGGER_RTT_WriteSkipNoLock does not lock the application and +* skips all data, if the data does not fit into the buffer. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used (e.g. 0 for "Terminal"). +* pBuffer Pointer to character array. Does not need to point to a \0 terminated string. +* NumBytes Number of bytes to be stored in the SEGGER RTT control block. +* MUST be > 0!!! +* This is done for performance reasons, so no initial check has do be done. +* +* Return value +* 1: Data has been copied +* 0: No space, data has not been copied +* +* Notes +* (1) If there is not enough space in the "Up"-buffer, all data is dropped. +* (2) For performance reasons this function does not call Init() +* and may only be called after RTT has been initialized. +* Either by calling SEGGER_RTT_Init() or calling another RTT API function first. +*/ + _SECTION(.text, CODE, 2) + _ALIGN(2) + _THUMB_FUNC +SEGGER_RTT_ASM_WriteSkipNoLock: // unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void* pData, unsigned NumBytes) { + // + // Cases: + // 1) RdOff <= WrOff => Space until wrap-around is sufficient + // 2) RdOff <= WrOff => Space after wrap-around needed (copy in 2 chunks) + // 3) RdOff < WrOff => No space in buf + // 4) RdOff > WrOff => Space is sufficient + // 5) RdOff > WrOff => No space in buf + // + // 1) is the most common case for large buffers and assuming that J-Link reads the data fast enough + // + // Register usage: + // R0 Temporary needed as RdOff, register later on + // R1 pData + // R2 + // R3 register. Hold free for subroutine calls + // R4 + // R5 pRing->pBuffer + // R6 pRing (Points to active struct SEGGER_RTT_BUFFER_DOWN) + // R7 WrOff + // + PUSH {R4-R7} + ADD R3,R0,R0, LSL #+1 + LDR.W R0,=_SEGGER_RTT // pRing = &_SEGGER_RTT.aUp[BufferIndex]; + ADD R0,R0,R3, LSL #+3 + ADD R6,R0,#+24 + LDR R0,[R6, #+16] // RdOff = pRing->RdOff; + LDR R7,[R6, #+12] // WrOff = pRing->WrOff; + LDR R5,[R6, #+4] // pRing->pBuffer + CMP R7,R0 + BCC.N _CheckCase4 // if (RdOff <= WrOff) { => Case 1), 2) or 3) + // + // Handling for case 1, later on identical to case 4 + // + LDR R3,[R6, #+8] // Avail = pRing->SizeOfBuffer - WrOff - 1u; => Space until wrap-around (assume 1 byte not usable for case that RdOff == 0) + SUBS R4,R3,R7 // (Used in case we jump into case 2 afterwards) + SUBS R3,R4,#+1 // + CMP R3,R2 + BCC.N _CheckCase2 // if (Avail >= NumBytes) { => Case 1)? +_Case4: + ADDS R5,R7,R5 // pBuffer += WrOff + ADDS R0,R2,R7 // v = WrOff + NumBytes + // + // 2x unrolling for the copy loop that is used most of the time + // This is a special optimization for small SystemView packets and makes them even faster + // + _ALIGN(2) +_LoopCopyStraight: // memcpy(pRing->pBuffer + WrOff, pData, NumBytes); + LDRB R3,[R1], #+1 + STRB R3,[R5], #+1 // *pDest++ = *pSrc++ + SUBS R2,R2,#+1 + BEQ _CSDone + LDRB R3,[R1], #+1 + STRB R3,[R5], #+1 // *pDest++ = *pSrc++ + SUBS R2,R2,#+1 + BNE _LoopCopyStraight +_CSDone: +#if _CORE_NEEDS_DMB // Do not slow down cores that do not need a DMB instruction here + DMB // Cortex-M7 may delay memory writes and also change the order in which the writes happen. Therefore, make sure that all buffer writes are finished, before updating the in the struct +#endif + STR R0,[R6, #+12] // pRing->WrOff = WrOff + NumBytes; + MOVS R0,#+1 + POP {R4-R7} + BX LR // Return 1 +_CheckCase2: + ADDS R0,R0,R3 // Avail += RdOff; => Space incl. wrap-around + CMP R0,R2 + BCC.N _Case3 // if (Avail >= NumBytes) { => Case 2? => If not, we have case 3) (does not fit) + // + // Handling for case 2 + // + ADDS R0,R7,R5 // v = pRing->pBuffer + WrOff => Do not change pRing->pBuffer here because 2nd chunk needs org. value + SUBS R2,R2,R4 // NumBytes -= Rem; (Rem = pRing->SizeOfBuffer - WrOff; => Space until end of buffer) +_LoopCopyBeforeWrapAround: // memcpy(pRing->pBuffer + WrOff, pData, Rem); => Copy 1st chunk + LDRB R3,[R1], #+1 + STRB R3,[R0], #+1 // *pDest++ = *pSrc++ + SUBS R4,R4,#+1 + BNE _LoopCopyBeforeWrapAround + // + // Special case: First check that assumed RdOff == 0 calculated that last element before wrap-around could not be used + // But 2nd check (considering space until wrap-around and until RdOff) revealed that RdOff is not 0, so we can use the last element + // In this case, we may use a copy straight until buffer end anyway without needing to copy 2 chunks + // Therefore, check if 2nd memcpy is necessary at all + // + ADDS R4,R2,#+0 // Save (needed as counter in loop but must be written to after the loop). Also use this inst to update the flags to skip 2nd loop if possible + BEQ.N _No2ChunkNeeded // if (NumBytes) { +_LoopCopyAfterWrapAround: // memcpy(pRing->pBuffer, pData + Rem, NumBytes); + LDRB R3,[R1], #+1 // pData already points to the next src byte due to copy loop increment before this loop + STRB R3,[R5], #+1 // *pDest++ = *pSrc++ + SUBS R2,R2,#+1 + BNE _LoopCopyAfterWrapAround +_No2ChunkNeeded: +#if _CORE_NEEDS_DMB // Do not slow down cores that do not need a DMB instruction here + DMB // Cortex-M7 may delay memory writes and also change the order in which the writes happen. Therefore, make sure that all buffer writes are finished, before updating the in the struct +#endif + STR R4,[R6, #+12] // pRing->WrOff = NumBytes; => Must be written after copying data because J-Link may read control block asynchronously while writing into buffer + MOVS R0,#+1 + POP {R4-R7} + BX LR // Return 1 +_CheckCase4: + SUBS R0,R0,R7 + SUBS R0,R0,#+1 // Avail = RdOff - WrOff - 1u; + CMP R0,R2 + BCS.N _Case4 // if (Avail >= NumBytes) { => Case 4) == 1) ? => If not, we have case 5) == 3) (does not fit) +_Case3: + MOVS R0,#+0 + POP {R4-R7} + BX LR // Return 0 + _PLACE_LITS + +#endif // defined (RTT_USE_ASM) && (RTT_USE_ASM == 1) + _END + +/*************************** End of file ****************************/ diff --git a/external/Segger-RTT/RTT/SEGGER_RTT_printf.c b/external/Segger-RTT/RTT/SEGGER_RTT_printf.c new file mode 100644 index 0000000..4c996b4 --- /dev/null +++ b/external/Segger-RTT/RTT/SEGGER_RTT_printf.c @@ -0,0 +1,500 @@ +/********************************************************************* +* SEGGER Microcontroller GmbH * +* The Embedded Experts * +********************************************************************** +* * +* (c) 1995 - 2019 SEGGER Microcontroller GmbH * +* * +* www.segger.com Support: support@segger.com * +* * +********************************************************************** +* * +* SEGGER RTT * Real Time Transfer for embedded targets * +* * +********************************************************************** +* * +* All rights reserved. * +* * +* SEGGER strongly recommends to not make any changes * +* to or modify the source code of this software in order to stay * +* compatible with the RTT protocol and J-Link. * +* * +* Redistribution and use in source and binary forms, with or * +* without modification, are permitted provided that the following * +* condition is met: * +* * +* o Redistributions of source code must retain the above copyright * +* notice, this condition and the following disclaimer. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * +* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * +* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * +* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * +* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * +* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * +* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * +* DAMAGE. * +* * +********************************************************************** +---------------------------END-OF-HEADER------------------------------ +File : SEGGER_RTT_printf.c +Purpose : Replacement for printf to write formatted data via RTT +Revision: $Rev: 17697 $ +---------------------------------------------------------------------- +*/ +#include "SEGGER_RTT.h" +#include "SEGGER_RTT_Conf.h" + +/********************************************************************* +* +* Defines, configurable +* +********************************************************************** +*/ + +#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE + #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64) +#endif + +#include +#include + + +#define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0) +#define FORMAT_FLAG_PAD_ZERO (1u << 1) +#define FORMAT_FLAG_PRINT_SIGN (1u << 2) +#define FORMAT_FLAG_ALTERNATE (1u << 3) + +/********************************************************************* +* +* Types +* +********************************************************************** +*/ + +typedef struct { + char* pBuffer; + unsigned BufferSize; + unsigned Cnt; + + int ReturnValue; + + unsigned RTTBufferIndex; +} SEGGER_RTT_PRINTF_DESC; + +/********************************************************************* +* +* Function prototypes +* +********************************************************************** +*/ + +/********************************************************************* +* +* Static code +* +********************************************************************** +*/ +/********************************************************************* +* +* _StoreChar +*/ +static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) { + unsigned Cnt; + + Cnt = p->Cnt; + if ((Cnt + 1u) <= p->BufferSize) { + *(p->pBuffer + Cnt) = c; + p->Cnt = Cnt + 1u; + p->ReturnValue++; + } + // + // Write part of string, when the buffer is full + // + if (p->Cnt == p->BufferSize) { + if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) { + p->ReturnValue = -1; + } else { + p->Cnt = 0u; + } + } +} + +/********************************************************************* +* +* _PrintUnsigned +*/ +static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { + static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + unsigned Div; + unsigned Digit; + unsigned Number; + unsigned Width; + char c; + + Number = v; + Digit = 1u; + // + // Get actual field width + // + Width = 1u; + while (Number >= Base) { + Number = (Number / Base); + Width++; + } + if (NumDigits > Width) { + Width = NumDigits; + } + // + // Print leading chars if necessary + // + if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) { + if (FieldWidth != 0u) { + if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) { + c = '0'; + } else { + c = ' '; + } + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, c); + if (pBufferDesc->ReturnValue < 0) { + break; + } + } + } + } + if (pBufferDesc->ReturnValue >= 0) { + // + // Compute Digit. + // Loop until Digit has the value of the highest digit required. + // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100. + // + while (1) { + if (NumDigits > 1u) { // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned) + NumDigits--; + } else { + Div = v / Digit; + if (Div < Base) { // Is our divider big enough to extract the highest digit from value? => Done + break; + } + } + Digit *= Base; + } + // + // Output digits + // + do { + Div = v / Digit; + v -= Div * Digit; + _StoreChar(pBufferDesc, _aV2C[Div]); + if (pBufferDesc->ReturnValue < 0) { + break; + } + Digit /= Base; + } while (Digit); + // + // Print trailing spaces if necessary + // + if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, ' '); + if (pBufferDesc->ReturnValue < 0) { + break; + } + } + } + } + } +} + +/********************************************************************* +* +* _PrintInt +*/ +static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) { + unsigned Width; + int Number; + + Number = (v < 0) ? -v : v; + + // + // Get actual field width + // + Width = 1u; + while (Number >= (int)Base) { + Number = (Number / (int)Base); + Width++; + } + if (NumDigits > Width) { + Width = NumDigits; + } + if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) { + FieldWidth--; + } + + // + // Print leading spaces if necessary + // + if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, ' '); + if (pBufferDesc->ReturnValue < 0) { + break; + } + } + } + } + // + // Print sign if necessary + // + if (pBufferDesc->ReturnValue >= 0) { + if (v < 0) { + v = -v; + _StoreChar(pBufferDesc, '-'); + } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) { + _StoreChar(pBufferDesc, '+'); + } else { + + } + if (pBufferDesc->ReturnValue >= 0) { + // + // Print leading zeros if necessary + // + if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) { + if (FieldWidth != 0u) { + while ((FieldWidth != 0u) && (Width < FieldWidth)) { + FieldWidth--; + _StoreChar(pBufferDesc, '0'); + if (pBufferDesc->ReturnValue < 0) { + break; + } + } + } + } + if (pBufferDesc->ReturnValue >= 0) { + // + // Print number without sign + // + _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags); + } + } + } +} + +/********************************************************************* +* +* Public code +* +********************************************************************** +*/ +/********************************************************************* +* +* SEGGER_RTT_vprintf +* +* Function description +* Stores a formatted string in SEGGER RTT control block. +* This data is read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal") +* sFormat Pointer to format string +* pParamList Pointer to the list of arguments for the format string +* +* Return values +* >= 0: Number of bytes which have been stored in the "Up"-buffer. +* < 0: Error +*/ +int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) { + char c; + SEGGER_RTT_PRINTF_DESC BufferDesc; + int v; + unsigned NumDigits; + unsigned FormatFlags; + unsigned FieldWidth; + char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE]; + + BufferDesc.pBuffer = acBuffer; + BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE; + BufferDesc.Cnt = 0u; + BufferDesc.RTTBufferIndex = BufferIndex; + BufferDesc.ReturnValue = 0; + + do { + c = *sFormat; + sFormat++; + if (c == 0u) { + break; + } + if (c == '%') { + // + // Filter out flags + // + FormatFlags = 0u; + v = 1; + do { + c = *sFormat; + switch (c) { + case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break; + case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break; + case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break; + case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break; + default: v = 0; break; + } + } while (v); + // + // filter out field with + // + FieldWidth = 0u; + do { + c = *sFormat; + if ((c < '0') || (c > '9')) { + break; + } + sFormat++; + FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0'); + } while (1); + + // + // Filter out precision (number of digits to display) + // + NumDigits = 0u; + c = *sFormat; + if (c == '.') { + sFormat++; + do { + c = *sFormat; + if ((c < '0') || (c > '9')) { + break; + } + sFormat++; + NumDigits = NumDigits * 10u + ((unsigned)c - '0'); + } while (1); + } + // + // Filter out length modifier + // + c = *sFormat; + do { + if ((c == 'l') || (c == 'h')) { + sFormat++; + c = *sFormat; + } else { + break; + } + } while (1); + // + // Handle specifiers + // + switch (c) { + case 'c': { + char c0; + v = va_arg(*pParamList, int); + c0 = (char)v; + _StoreChar(&BufferDesc, c0); + break; + } + case 'd': + v = va_arg(*pParamList, int); + _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags); + break; + case 'u': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags); + break; + case 'x': + case 'X': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags); + break; + case 's': + { + const char * s = va_arg(*pParamList, const char *); + do { + c = *s; + s++; + if (c == '\0') { + break; + } + _StoreChar(&BufferDesc, c); + } while (BufferDesc.ReturnValue >= 0); + } + break; + case 'p': + v = va_arg(*pParamList, int); + _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u); + break; + case '%': + _StoreChar(&BufferDesc, '%'); + break; + default: + break; + } + sFormat++; + } else { + _StoreChar(&BufferDesc, c); + } + } while (BufferDesc.ReturnValue >= 0); + + if (BufferDesc.ReturnValue > 0) { + // + // Write remaining data, if any + // + if (BufferDesc.Cnt != 0u) { + SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt); + } + BufferDesc.ReturnValue += (int)BufferDesc.Cnt; + } + return BufferDesc.ReturnValue; +} + +/********************************************************************* +* +* SEGGER_RTT_printf +* +* Function description +* Stores a formatted string in SEGGER RTT control block. +* This data is read by the host. +* +* Parameters +* BufferIndex Index of "Up"-buffer to be used. (e.g. 0 for "Terminal") +* sFormat Pointer to format string, followed by the arguments for conversion +* +* Return values +* >= 0: Number of bytes which have been stored in the "Up"-buffer. +* < 0: Error +* +* Notes +* (1) Conversion specifications have following syntax: +* %[flags][FieldWidth][.Precision]ConversionSpecifier +* (2) Supported flags: +* -: Left justify within the field width +* +: Always print sign extension for signed conversions +* 0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision +* Supported conversion specifiers: +* c: Print the argument as one char +* d: Print the argument as a signed integer +* u: Print the argument as an unsigned integer +* x: Print the argument as an hexadecimal integer +* s: Print the string pointed to by the argument +* p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.) +*/ +int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) { + int r; + va_list ParamList; + + va_start(ParamList, sFormat); + r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList); + va_end(ParamList); + return r; +} +/*************************** End of file ****************************/ diff --git a/external/Toolchain/cmake/config/doxygen.cmake b/external/Toolchain/cmake/config/doxygen.cmake new file mode 100644 index 0000000..46b313a --- /dev/null +++ b/external/Toolchain/cmake/config/doxygen.cmake @@ -0,0 +1,24 @@ +# This file defines the default values for Doxygen generation + +set(DOXYGEN_GENERATE_HTML "YES") +set(DOXYGEN_GENERATE_MAN "NO") +set(DOXYGEN_FILE_PATTERNS "*.c" "*.h" "*.cpp" "*.hpp") +set(DOXYGEN_EXTRACT_PRIVATE "YES") +set(DOXYGEN_EXTRACT_PACKAGE "YES") +set(DOXYGEN_EXTRACT_STATIC "YES") +set(DOXYGEN_EXTRACT_LOCAL_CLASSES "YES") +set(DOXYGEN_SOURCE_BROWSER "YES") +set(DOXYGEN_INLINE_SOURCES "NO") +set(DOXYGEN_ENUM_VALUES_PER_LINE "1") +set(DOXYGEN_GENERATE_TREEVIEW "YES") +set(DOXYGEN_HTML_TIMESTAMP "YES") +set(DOXYGEN_TAB_SIZE "2") +set(DOXYGEN_WARN_NO_PARAMDOC "YES") +set(DOXYGEN_BUILTIN_STL_SUPPORT "YES") +set(DOXYGEN_INLINE_INHERITED_MEMB "YES") +set(DOXYGEN_TYPEDEF_HIDES_STRUCT "YES") +set(DOXYGEN_ENABLE_PREPROCESSING "YES") +set(DOXYGEN_EXPAND_ONLY_PREDEF "YES") +set(DOXYGEN_MACRO_EXPANSION "YES") +set(DOXYGEN_PREDEFINED "__attribute__(x)=") +set(DOXYGEN_QUIET "YES")