From e6648b565b2d591351902a6a56263dc5461c492b Mon Sep 17 00:00:00 2001 From: Marcin Wierzbicki Date: Wed, 23 Oct 2024 16:04:43 +0200 Subject: [PATCH] Add BenchmarkSystem that implements benchmarking Added these benchmark tests: - interrupt latency test, - task switch latency test, - task switch after timeout latency test, - load benchmark test. Change-Id: Ibf4548dec1ac2ef18d13daed29c9dddebdd6c9fe --- Filelists.cmake | 6 + doc/learning/benchmark/index.rst | 26 ++ doc/learning/overview.rst | 1 + .../referenceApp/application/CMakeLists.txt | 4 + .../application/include/app/BenchmarkLogger.h | 10 + .../include/systems/BenchmarkSystem.h | 156 +++++++++ .../referenceApp/application/src/app/app.cpp | 20 ++ .../application/src/logger/logger.cpp | 18 + .../src/systems/BenchmarkSystem.cpp | 308 ++++++++++++++++++ .../include/os/FreeRtosPlatformConfig.h | 6 +- .../s32k148evb/main/src/bsp/startUp.S | 2 +- .../platforms/s32k148evb/main/src/main.cpp | 6 + .../platforms/s32k148evb/main/src/osHooks.cpp | 4 + .../runtime/include/runtime/RuntimeMonitor.h | 22 ++ 14 files changed, 587 insertions(+), 2 deletions(-) create mode 100644 doc/learning/benchmark/index.rst create mode 100644 executables/referenceApp/application/include/app/BenchmarkLogger.h create mode 100644 executables/referenceApp/application/include/systems/BenchmarkSystem.h create mode 100644 executables/referenceApp/application/src/systems/BenchmarkSystem.cpp diff --git a/Filelists.cmake b/Filelists.cmake index bbe6b02ac0..90cd145d3e 100644 --- a/Filelists.cmake +++ b/Filelists.cmake @@ -26,6 +26,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/admin/cmake") option(BUILD_UNIT_TESTS "Build unit tests" OFF) +option(BUILD_BENCHMARK "Build benchmark mode" OFF) + add_compile_options( "$<$:-O2;-g3;-Werror;-Wall;-Wvla;-Woverloaded-virtual>" ) @@ -33,6 +35,10 @@ add_compile_options( add_compile_options( "$<$:-O2;-g3;-Werror;-Wall>") +if (BUILD_BENCHMARK) + add_compile_definitions(BENCHMARK=1) +endif () + if (BUILD_UNIT_TESTS) add_compile_definitions(UNIT_TEST=1) include(GoogleTest) diff --git a/doc/learning/benchmark/index.rst b/doc/learning/benchmark/index.rst new file mode 100644 index 0000000000..3c56cb978b --- /dev/null +++ b/doc/learning/benchmark/index.rst @@ -0,0 +1,26 @@ +.. _learning_benchmark: + +Benchmark mode +============== + +Previous: :ref:`learning_hwio` + +The reference app can be built to run benchmark tests. There is a BenchmarkSystem class that +implements the following tests: + +* interrupt latency test - to measure the time between raising the interrupt line and entering the ISR, +* task switch latency test - to measure the time between an async runnable setting an event + and another (woken up) runnable starting execution, +* task switch after timeout latency test - to measure the time between scheduling an async runnable + and its start of execution (woken up by a timer), +* load test - record CPU idle time during cyclic artificial load across various tasks. + +Use the following command to build the reference app in benchmark mode for the S32K148EVB: + +.. code-block:: bash + + cmake -B cmake-build-s32k148-benchmark -S executables/referenceApp -DBUILD_TARGET_PLATFORM="S32K148EVB" \ + -DBUILD_BENCHMARK=ON --toolchain ../../admin/cmake/ArmNoneEabi.cmake + cmake --build cmake-build-s32k148-benchmark --target app.referenceApp -j + +When started, the application runs benchmark tests and prints out measurement results on the console. diff --git a/doc/learning/overview.rst b/doc/learning/overview.rst index 2a8910cbf8..e013d264d8 100644 --- a/doc/learning/overview.rst +++ b/doc/learning/overview.rst @@ -18,3 +18,4 @@ Some simple lessons to get new users up and running. can/index uds/index hwio/index + benchmark/index diff --git a/executables/referenceApp/application/CMakeLists.txt b/executables/referenceApp/application/CMakeLists.txt index bf8dd9be06..91f48ab35a 100644 --- a/executables/referenceApp/application/CMakeLists.txt +++ b/executables/referenceApp/application/CMakeLists.txt @@ -25,6 +25,10 @@ add_executable( ${app.referenceAppExtraSources} src/main.cpp) +if (BUILD_BENCHMARK) + target_sources(app.referenceApp PRIVATE src/systems/BenchmarkSystem.cpp) +endif () + set_target_properties(app.referenceApp PROPERTIES SUFFIX ".elf") if (TARGET startUp) diff --git a/executables/referenceApp/application/include/app/BenchmarkLogger.h b/executables/referenceApp/application/include/app/BenchmarkLogger.h new file mode 100644 index 0000000000..468b45f977 --- /dev/null +++ b/executables/referenceApp/application/include/app/BenchmarkLogger.h @@ -0,0 +1,10 @@ +// Copyright 2024 Accenture. + +#ifndef GUARD_A2388DF9_4B35_42D5_8B75_DFC18C21C372 +#define GUARD_A2388DF9_4B35_42D5_8B75_DFC18C21C372 + +#include "util/logger/Logger.h" + +DECLARE_LOGGER_COMPONENT(BENCH) + +#endif /* GUARD_A2388DF9_4B35_42D5_8B75_DFC18C21C372 */ diff --git a/executables/referenceApp/application/include/systems/BenchmarkSystem.h b/executables/referenceApp/application/include/systems/BenchmarkSystem.h new file mode 100644 index 0000000000..0bd9cfb3e8 --- /dev/null +++ b/executables/referenceApp/application/include/systems/BenchmarkSystem.h @@ -0,0 +1,156 @@ +// Copyright 2024 Accenture. + +#ifndef GUARD_F5601CAB_D60E_48CB_B6A0_1E86BE0C6F24 +#define GUARD_F5601CAB_D60E_48CB_B6A0_1E86BE0C6F24 + +#include "app/BenchmarkLogger.h" + +#include +#include +#include +#include +#include +#include +#include + +#define GET_HW_COUNTER() (DWT->CYCCNT) + +using ::util::logger::BENCH; +using ::util::logger::Logger; + +namespace systems +{ +class BenchmarkSystem +: public ::lifecycle::AsyncLifecycleComponent +, private ::async::IRunnable +{ +public: + explicit BenchmarkSystem( + ::async::ContextType context, + ::lifecycle::ILifecycleManager& lifecycleManager, + ::async::AsyncBinding::RuntimeMonitorType& runtimeMonitor); + + BenchmarkSystem(BenchmarkSystem const&) = delete; + BenchmarkSystem& operator=(BenchmarkSystem const&) = delete; + + void init() override; + void run() override; + void shutdown() override; + + void cyclic(); + +private: + void execute() override; + + uint32_t getPercentage(uint64_t value, uint64_t total); + void startTaskSwitchLatencyTest(); + void handleTaskSwitchLatencyTest(); + void startTaskSwitchAfterTimeoutLatencyTest(); + void handleTaskSwitchAfterTimeoutLatencyTest(); + void startInterruptLatencyTest(); + void handleInterruptLatencyTest(); + void startLoadTest(); + void handleLoadTest(); + uint32_t calculateTimeDifference(uint32_t start, uint32_t end); + +private: + using TaskStatistics = ::runtime::declare::StatisticsContainer< + ::runtime::RuntimeStatistics, + ::async::AsyncBindingType::AdapterType::FREERTOS_TASK_COUNT>; + + class EventWaiterRunnable : public ::async::IRunnable + { + public: + explicit EventWaiterRunnable(::async::FutureSupport& future, uint32_t& latencyEnd) + : _future(future), _timestamp(latencyEnd) + {} + + void execute() override + { + _future.wait(); + _timestamp = GET_HW_COUNTER(); + } + + private: + ::async::FutureSupport& _future; + uint32_t& _timestamp; + }; + + class EventSetterRunnable : public ::async::IRunnable + { + public: + explicit EventSetterRunnable(::async::FutureSupport& future, uint32_t& latencyStart) + : _future(future), _timestamp(latencyStart) + {} + + void execute() override + { + _timestamp = GET_HW_COUNTER(); + _future.notify(); + } + + private: + ::async::FutureSupport& _future; + uint32_t& _timestamp; + }; + + class TimoutWaiterRunnable : public ::async::IRunnable + { + public: + explicit TimoutWaiterRunnable(uint32_t& latencyEnd) : _timestamp(latencyEnd) {} + + void execute() override { _timestamp = GET_HW_COUNTER(); } + + private: + uint32_t& _timestamp; + }; + + class LoadRunnable : public ::async::IRunnable + { + public: + explicit LoadRunnable(uint32_t limit) : _limit(limit) {} + + void execute() override + { + uint32_t volatile counter = 0; + while (counter++ < _limit) {} + } + + private: + uint32_t _limit; + }; + friend class EventWaiterRunnable; + friend class EventSetterRunnable; + friend class TimoutWaiterRunnable; + ::async::ContextType const _context; + uint32_t _taskSwitchLatencyStart; + uint32_t _taskSwitchLatencyEnd; + uint32_t _taskSwitchAfterTimeoutLatencyStart; + uint32_t _taskSwitchAfterTimeoutLatencyEnd; + ::async::TimeoutType _timeout; + ::async::TimeoutType _taskSwitchTimeout; + ::async::TimeoutType _sysadminTimeout; + ::async::TimeoutType _canTimeout; + ::async::TimeoutType _demoTimeout; + ::async::TimeoutType _udsTimeout; + ::async::TimeoutType _bgTimeout; + ::async::FutureSupport _future; + EventWaiterRunnable _eventWaiterRunnable; + EventSetterRunnable _eventSetterRunnable; + TimoutWaiterRunnable _timeoutWaiterRunnable; + LoadRunnable _sysadminLoadRunnable; + LoadRunnable _canLoadRunnable; + LoadRunnable _demoLoadRunnable; + LoadRunnable _udsLoadRunnable; + LoadRunnable _bgLoadRunnable; + bool _taskSwitchLatencyTestRunning; + bool _taskSwitchAfterTimeoutLatencyTestRunning; + bool _interruptLatencyTestRunning; + bool _loadTestRunning; + bool _runtimeMonitorRunning; + ::async::AsyncBinding::RuntimeMonitorType& _runtimeMonitor; +}; + +} // namespace systems + +#endif /* GUARD_F5601CAB_D60E_48CB_B6A0_1E86BE0C6F24 */ diff --git a/executables/referenceApp/application/src/app/app.cpp b/executables/referenceApp/application/src/app/app.cpp index 1f6f706203..1a679fc936 100644 --- a/executables/referenceApp/application/src/app/app.cpp +++ b/executables/referenceApp/application/src/app/app.cpp @@ -7,6 +7,9 @@ #include "logger/logger.h" #include "reset/softwareSystemReset.h" #include "systems/DemoSystem.h" +#ifdef BENCHMARK +#include "systems/BenchmarkSystem.h" +#endif #include "systems/RuntimeSystem.h" #include "systems/SysAdminSystem.h" @@ -69,9 +72,15 @@ LifecycleManager lifecycleManager{ TASK_SYSADMIN, ::lifecycle::LifecycleManager::GetTimestampType::create<&getSystemTimeUs32Bit>()}; +#ifndef BENCHMARK ::estd::typed_mem<::systems::RuntimeSystem> runtimeSystem; +#endif ::estd::typed_mem<::systems::SysAdminSystem> sysAdminSystem; +#ifndef BENCHMARK ::estd::typed_mem<::systems::DemoSystem> demoSystem; +#else +::estd::typed_mem<::systems::BenchmarkSystem> benchmarkSystem; +#endif #ifdef PLATFORM_SUPPORT_UDS ::estd::typed_mem<::transport::TransportSystem> transportSystem; @@ -133,8 +142,10 @@ void run() /* runlevel 1 */ ::platform::platformLifecycleAdd(lifecycleManager, 1U); +#ifndef BENCHMARK lifecycleManager.addComponent( "runtime", runtimeSystem.emplace(TASK_BACKGROUND, runtimeMonitor), 1U); +#endif /* runlevel 2 */ ::platform::platformLifecycleAdd(lifecycleManager, 2U); /* runlevel 3 */ @@ -164,6 +175,7 @@ void run() /* runlevel 8 */ ::platform::platformLifecycleAdd(lifecycleManager, 8U); +#ifndef BENCHMARK lifecycleManager.addComponent( "demo", demoSystem.emplace( @@ -175,10 +187,16 @@ void run() #endif ), 8U); +#else + lifecycleManager.addComponent( + "benchmark", benchmarkSystem.emplace(TASK_DEMO, lifecycleManager, runtimeMonitor), 8U); +#endif lifecycleManager.transitionToLevel(MaxNumLevels); +#ifndef BENCHMARK runtimeMonitor.start(); +#endif AsyncAdapter::run(); while (true) @@ -190,8 +208,10 @@ void run() void idle(AsyncAdapter::TaskContextType& taskContext) { taskContext.dispatchWhileWork(); +#ifndef BENCHMARK ::logger::run(); ::console::run(); +#endif if (lifecycleMonitor.isReadyForReset()) { staticShutdown(); diff --git a/executables/referenceApp/application/src/logger/logger.cpp b/executables/referenceApp/application/src/logger/logger.cpp index 5e4f6d3837..f3c8a5d589 100644 --- a/executables/referenceApp/application/src/logger/logger.cpp +++ b/executables/referenceApp/application/src/logger/logger.cpp @@ -19,6 +19,7 @@ DEFINE_LOGGER_COMPONENT(BSP); DEFINE_LOGGER_COMPONENT(COMMON); DEFINE_LOGGER_COMPONENT(DEMO); +DEFINE_LOGGER_COMPONENT(BENCH); DEFINE_LOGGER_COMPONENT(GLOBAL); DEFINE_LOGGER_COMPONENT(UDS); @@ -31,6 +32,7 @@ DEFINE_LOGGER_COMPONENT(UDS); START_LOGGER_COMPONENT_MAPPING_INFO_TABLE(loggerComponentInfoTable) /* start: adding logger components */ +#ifndef BENCHMARK LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, BSP, ::util::format::Color::DEFAULT_COLOR) LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, COMMON, ::util::format::Color::DEFAULT_COLOR) LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, DEMO, ::util::format::Color::DEFAULT_COLOR) @@ -45,6 +47,22 @@ LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, DOCAN, ::util::format::Color::LIGHT_GRAY) LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, UDS, ::util::format::Color::LIGHT_YELLOW) LOGGER_COMPONENT_MAPPING_INFO(_DEBUG, TPROUTER, ::util::format::Color::LIGHT_YELLOW) #endif // PLATFORM_SUPPORT_UDS +#else +LOGGER_COMPONENT_MAPPING_INFO(_WARN, BSP, ::util::format::Color::DEFAULT_COLOR) +LOGGER_COMPONENT_MAPPING_INFO(_WARN, COMMON, ::util::format::Color::DEFAULT_COLOR) +LOGGER_COMPONENT_MAPPING_INFO(_INFO, BENCH, ::util::format::Color::DEFAULT_COLOR) +LOGGER_COMPONENT_MAPPING_INFO(_INFO, GLOBAL, ::util::format::Color::DEFAULT_COLOR) +LOGGER_COMPONENT_MAPPING_INFO(_WARN, LIFECYCLE, ::util::format::Color::DARK_GRAY) +LOGGER_COMPONENT_MAPPING_INFO(_WARN, CONSOLE, ::util::format::Color::DEFAULT_COLOR) +#ifdef PLATFORM_SUPPORT_CAN +LOGGER_COMPONENT_MAPPING_INFO(_WARN, CAN, ::util::format::Color::LIGHT_BLUE) +LOGGER_COMPONENT_MAPPING_INFO(_WARN, DOCAN, ::util::format::Color::LIGHT_GRAY) +#endif // PLATFORM_SUPPORT_CAN +#ifdef PLATFORM_SUPPORT_UDS +LOGGER_COMPONENT_MAPPING_INFO(_WARN, UDS, ::util::format::Color::LIGHT_YELLOW) +LOGGER_COMPONENT_MAPPING_INFO(_WARN, TPROUTER, ::util::format::Color::LIGHT_YELLOW) +#endif // PLATFORM_SUPPORT_UDS +#endif /* end: adding logger components */ END_LOGGER_COMPONENT_MAPPING_INFO_TABLE(); diff --git a/executables/referenceApp/application/src/systems/BenchmarkSystem.cpp b/executables/referenceApp/application/src/systems/BenchmarkSystem.cpp new file mode 100644 index 0000000000..89781c9904 --- /dev/null +++ b/executables/referenceApp/application/src/systems/BenchmarkSystem.cpp @@ -0,0 +1,308 @@ +// Copyright 2024 Accenture. + +#include "systems/BenchmarkSystem.h" + +#include "console/console.h" +#include "logger/logger.h" + +#include +#include +#include +#include + +#define FTM4 IP_FTM4 + +static uint32_t interruptLatencyStart = 0; +static uint32_t interruptLatencyEnd = 0; + +using bios::Output; +using bios::OutputPwm; + +extern "C" +{ +void FTM4_Ch0_Ch1_IRQHandler() +{ + interruptLatencyEnd = GET_HW_COUNTER(); + SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; + FTM4->CONTROLS[1].CnSC &= ~FTM_CnSC_CHF_MASK; + FTM4->CONTROLS[1].CnSC &= ~FTM_CnSC_CHIE_MASK; + OutputPwm::setDuty(OutputPwm::EVAL_LED_RED_PWM, 0); +} +} + +namespace +{ +constexpr uint32_t SYSTEM_CYCLE_TIME = 2000; /* milliseconds */ +constexpr uint32_t LOAD_COUNTER_LIMIT = 0x1800; +} // namespace + +namespace systems +{ + +BenchmarkSystem::BenchmarkSystem( + ::async::ContextType const context, + ::lifecycle::ILifecycleManager& lifecycleManager, + ::async::AsyncBinding::RuntimeMonitorType& runtimeMonitor) +: _context(context) +, _taskSwitchLatencyStart(0) +, _taskSwitchLatencyEnd(0) +, _taskSwitchAfterTimeoutLatencyStart(0) +, _taskSwitchAfterTimeoutLatencyEnd(0) +, _future(0) +, _eventWaiterRunnable(_future, _taskSwitchLatencyEnd) +, _eventSetterRunnable(_future, _taskSwitchLatencyStart) +, _timeoutWaiterRunnable(_taskSwitchAfterTimeoutLatencyEnd) +, _sysadminLoadRunnable(LOAD_COUNTER_LIMIT) +, _canLoadRunnable(LOAD_COUNTER_LIMIT * 2) +, _demoLoadRunnable(LOAD_COUNTER_LIMIT * 4) +, _udsLoadRunnable(LOAD_COUNTER_LIMIT * 10) +, _bgLoadRunnable(LOAD_COUNTER_LIMIT * 20) +, _taskSwitchLatencyTestRunning(false) +, _taskSwitchAfterTimeoutLatencyTestRunning(false) +, _interruptLatencyTestRunning(false) +, _loadTestRunning(false) +, _runtimeMonitorRunning(false) +, _runtimeMonitor(runtimeMonitor) +{ + setTransitionContext(context); +} + +void BenchmarkSystem::init() { transitionDone(); } + +void BenchmarkSystem::run() +{ + if (DWT_CTRL_CYCCNTENA_Msk != (DWT_CTRL_CYCCNTENA_Msk & DWT->CTRL)) + { + /* enable DWT cyclic counter */ + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + } + Logger::info(BENCH, "--------------------------------------------------"); + Logger::info(BENCH, "Benchmark tests are starting now."); + Logger::info(BENCH, "Please ensure the system is in the expected state."); + Logger::info(BENCH, "--------------------------------------------------\n"); + _runtimeMonitor.start(); + _runtimeMonitorRunning = true; + ::async::scheduleAtFixedRate( + _context, *this, _timeout, SYSTEM_CYCLE_TIME, ::async::TimeUnit::MILLISECONDS); + transitionDone(); +} + +void BenchmarkSystem::shutdown() { transitionDone(); } + +void BenchmarkSystem::execute() +{ + if (_runtimeMonitorRunning) + { + startLoadTest(); + } + else if (_loadTestRunning) + { + handleLoadTest(); + ::logger::run(); + ::console::run(); + if (!_loadTestRunning) + { + _runtimeMonitor.stop(); + startInterruptLatencyTest(); + } + } + else if (_interruptLatencyTestRunning) + { + handleInterruptLatencyTest(); + ::logger::run(); + ::console::run(); + if (!_interruptLatencyTestRunning) + { + startTaskSwitchLatencyTest(); + } + } + else if (_taskSwitchLatencyTestRunning) + { + handleTaskSwitchLatencyTest(); + ::logger::run(); + ::console::run(); + if (!_taskSwitchLatencyTestRunning) + { + startTaskSwitchAfterTimeoutLatencyTest(); + } + } + else if (_taskSwitchAfterTimeoutLatencyTestRunning) + { + handleTaskSwitchAfterTimeoutLatencyTest(); + if (!_taskSwitchAfterTimeoutLatencyTestRunning) + { + Logger::info(BENCH, "---------------------------------------"); + Logger::info(BENCH, "Benchmark tests completed successfully."); + Logger::info(BENCH, "Review the results for analysis."); + Logger::info(BENCH, "---------------------------------------"); + ::logger::run(); + ::console::run(); + } + } + else + { + ::logger::run(); + ::console::run(); + } +} + +void BenchmarkSystem::startInterruptLatencyTest() +{ + _interruptLatencyTestRunning = true; + interruptLatencyStart = 0; + interruptLatencyEnd = 0; + OutputPwm::setDuty(OutputPwm::EVAL_LED_RED_PWM, 1000); + + /** + * The FTM timer is started, initiating the generation of a PWM signal. + * This code waits until the interrupt flag is set, the start timestamp is recorded, + * and the corresponding interrupt is enabled in the FTM. + * The end timestamp is recorded when the ISR is entered. + * Note: systick interrupt is disabled until the FTM interrupt ISR is called + */ + do + { + if (FTM4->CONTROLS[1].CnSC & FTM_CnSC_CHF_MASK) + { + SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; + interruptLatencyStart = GET_HW_COUNTER(); + FTM4->CONTROLS[1].CnSC |= FTM_CnSC_CHIE_MASK; + break; + } + } while (true); +} + +void BenchmarkSystem::handleInterruptLatencyTest() +{ + if (interruptLatencyStart != 0 && interruptLatencyEnd != 0) + { + uint32_t cycles = calculateTimeDifference(interruptLatencyStart, interruptLatencyEnd); + double latency = (static_cast(cycles) * 1000000000) / configCPU_CLOCK_HZ; + Logger::info( + BENCH, + "Interrupt latency: %llu ns (%u cycles)\n", + static_cast(latency), + cycles); + _interruptLatencyTestRunning = false; + } +} + +void BenchmarkSystem::startTaskSwitchLatencyTest() +{ + _taskSwitchLatencyTestRunning = true; + _taskSwitchLatencyStart = 0; + _taskSwitchLatencyEnd = 0; + ::async::execute(TASK_SYSADMIN, _eventWaiterRunnable); + ::async::execute(TASK_CAN, _eventSetterRunnable); +} + +void BenchmarkSystem::handleTaskSwitchLatencyTest() +{ + if (_taskSwitchLatencyStart != 0 && _taskSwitchLatencyEnd != 0) + { + uint32_t cycles = calculateTimeDifference(_taskSwitchLatencyStart, _taskSwitchLatencyEnd); + double latency = (static_cast(cycles) * 1000000000) / configCPU_CLOCK_HZ; + Logger::info( + BENCH, + "Task switch latency: %llu ns (%u cycles)\n", + static_cast(latency), + cycles); + _taskSwitchLatencyTestRunning = false; + } +} + +void BenchmarkSystem::startTaskSwitchAfterTimeoutLatencyTest() +{ + _taskSwitchAfterTimeoutLatencyTestRunning = true; + _taskSwitchAfterTimeoutLatencyEnd = 0; + _taskSwitchAfterTimeoutLatencyStart = GET_HW_COUNTER(); + ::async::schedule( + TASK_SYSADMIN, + _timeoutWaiterRunnable, + _taskSwitchTimeout, + 100, + ::async::TimeUnit::MILLISECONDS); +} + +void BenchmarkSystem::handleTaskSwitchAfterTimeoutLatencyTest() +{ + if (_taskSwitchAfterTimeoutLatencyStart != 0 && _taskSwitchAfterTimeoutLatencyEnd != 0) + { + uint32_t cycles = calculateTimeDifference( + _taskSwitchAfterTimeoutLatencyStart, _taskSwitchAfterTimeoutLatencyEnd); + double latency = (static_cast(cycles) * 1000000000) / configCPU_CLOCK_HZ; + Logger::info( + BENCH, + "Task switch after timeout latency: %llu ns (%u cycles)\n", + static_cast(latency), + cycles); + _taskSwitchAfterTimeoutLatencyTestRunning = false; + } +} + +void BenchmarkSystem::startLoadTest() +{ + TaskStatistics taskStatistics; + + _loadTestRunning = true; + + taskStatistics.copyFrom(_runtimeMonitor.getTaskStatistics()); + uint32_t totalRuntime = _runtimeMonitor.reset(); + _runtimeMonitorRunning = false; + uint32_t _idlePercentage + = getPercentage(taskStatistics.getStatistics(0).getTotalRuntime(), totalRuntime); + Logger::info( + BENCH, + "Load test: CPU idle: %d.%02d %% (NO load)", + _idlePercentage / 100U, + _idlePercentage % 100U); + + ::async::scheduleAtFixedRate( + TASK_SYSADMIN, _sysadminLoadRunnable, _sysadminTimeout, 5, ::async::TimeUnit::MILLISECONDS); + ::async::scheduleAtFixedRate( + TASK_CAN, _canLoadRunnable, _canTimeout, 10, ::async::TimeUnit::MILLISECONDS); + ::async::scheduleAtFixedRate( + TASK_DEMO, _demoLoadRunnable, _demoTimeout, 20, ::async::TimeUnit::MILLISECONDS); + ::async::scheduleAtFixedRate( + TASK_UDS, _udsLoadRunnable, _udsTimeout, 50, ::async::TimeUnit::MILLISECONDS); + ::async::scheduleAtFixedRate( + TASK_BACKGROUND, _bgLoadRunnable, _bgTimeout, 100, ::async::TimeUnit::MILLISECONDS); +} + +void BenchmarkSystem::handleLoadTest() +{ + TaskStatistics taskStatistics; + + taskStatistics.copyFrom(_runtimeMonitor.getTaskStatistics()); + uint32_t totalRuntime = _runtimeMonitor.reset(); + uint32_t _idlePercentage + = getPercentage(taskStatistics.getStatistics(0).getTotalRuntime(), totalRuntime); + _sysadminTimeout.cancel(); + _canTimeout.cancel(); + _demoTimeout.cancel(); + _udsTimeout.cancel(); + _bgTimeout.cancel(); + + Logger::info( + BENCH, + "Load test: CPU idle: %d.%02d %% (load) (LOAD_COUNTER_LIMIT: 0x%x)\n", + _idlePercentage / 100U, + _idlePercentage % 100U, + LOAD_COUNTER_LIMIT); + + _loadTestRunning = false; +} + +uint32_t BenchmarkSystem::getPercentage(uint64_t value, uint64_t total) +{ + return (total != 0U) ? static_cast( + static_cast(value) * 10000U / static_cast(total)) + : 0U; +} + +uint32_t BenchmarkSystem::calculateTimeDifference(uint32_t start, uint32_t end) +{ + return (end >= start) ? (end - start) : (end + (0xFFFFFFFF - start + 1)); +} + +} // namespace systems diff --git a/executables/referenceApp/platforms/s32k148evb/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h b/executables/referenceApp/platforms/s32k148evb/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h index c236def3aa..575d296661 100644 --- a/executables/referenceApp/platforms/s32k148evb/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h +++ b/executables/referenceApp/platforms/s32k148evb/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h @@ -116,10 +116,14 @@ See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ #if defined(__GNUC__) && (!defined(__ASSEMBLER__)) #include "mcu/mcu.h" #endif +#ifndef BENCHMARK #define configASSERT(x) DEV_ASSERT(x) +#else +#define configASSERT(x) ((void)0) +#endif /* Tickless Idle Mode */ -#define configUSE_TICKLESS_IDLE 0 +#define configUSE_TICKLESS_IDLE 1 #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 #define configUSE_TICKLESS_IDLE_DECISION_HOOK 0 diff --git a/executables/referenceApp/platforms/s32k148evb/main/src/bsp/startUp.S b/executables/referenceApp/platforms/s32k148evb/main/src/bsp/startUp.S index d20047463a..e3acb66b00 100644 --- a/executables/referenceApp/platforms/s32k148evb/main/src/bsp/startUp.S +++ b/executables/referenceApp/platforms/s32k148evb/main/src/bsp/startUp.S @@ -150,7 +150,7 @@ __isr_vector: .long DefaultISR /* FTM3_Ch6_Ch7_IRQHandler /* FTM3 Channel 6 and 7 interrupt*/ .long DefaultISR /* FTM3_Fault_IRQHandler /* FTM3 Fault interrupt*/ .long DefaultISR /* FTM3_Ovf_Reload_IRQHandler /* FTM3 Counter overflow and Reload interrupt*/ - .long DefaultISR /* FTM4_Ch0_Ch1_IRQHandler /* FTM4 Channel 0 and 1 interrupt*/ + .long FTM4_Ch0_Ch1_IRQHandler /* FTM4_Ch0_Ch1_IRQHandler /* FTM4 Channel 0 and 1 interrupt*/ .long DefaultISR /* FTM4_Ch2_Ch3_IRQHandler /* FTM4 Channel 2 and 3 interrupt*/ .long DefaultISR /* FTM4_Ch4_Ch5_IRQHandler /* FTM4 Channel 4 and 5 interrupt*/ .long DefaultISR /* FTM4_Ch6_Ch7_IRQHandler /* FTM4 Channel 6 and 7 interrupt*/ diff --git a/executables/referenceApp/platforms/s32k148evb/main/src/main.cpp b/executables/referenceApp/platforms/s32k148evb/main/src/main.cpp index dc09a9596b..e3717156cb 100644 --- a/executables/referenceApp/platforms/s32k148evb/main/src/main.cpp +++ b/executables/referenceApp/platforms/s32k148evb/main/src/main.cpp @@ -39,9 +39,15 @@ void setupApplicationsIsr(void) // interrupts SYS_SetPriority(CAN0_ORed_0_15_MB_IRQn, 8); // can0 buffer 0 - 15 SYS_SetPriority(CAN0_ORed_16_31_MB_IRQn, 8); // can0 buffer 16 - 32 +#ifdef BENCHMARK + SYS_SetPriority(FTM4_Ch0_Ch1_IRQn, 8); +#endif SYS_EnableIRQ(CAN0_ORed_0_15_MB_IRQn); SYS_EnableIRQ(CAN0_ORed_16_31_MB_IRQn); +#ifdef BENCHMARK + SYS_EnableIRQ(FTM4_Ch0_Ch1_IRQn); +#endif ENABLE_INTERRUPTS(); } diff --git a/executables/referenceApp/platforms/s32k148evb/main/src/osHooks.cpp b/executables/referenceApp/platforms/s32k148evb/main/src/osHooks.cpp index 5a47ef1b3b..11340f8c40 100644 --- a/executables/referenceApp/platforms/s32k148evb/main/src/osHooks.cpp +++ b/executables/referenceApp/platforms/s32k148evb/main/src/osHooks.cpp @@ -17,4 +17,8 @@ void vIllegalISR() for (;;) ; } + +#ifndef BENCHMARK +void FTM4_Ch0_Ch1_IRQHandler() { vIllegalISR(); } +#endif } diff --git a/libs/bsw/runtime/include/runtime/RuntimeMonitor.h b/libs/bsw/runtime/include/runtime/RuntimeMonitor.h index 16d1ffb970..df9bcb92ea 100644 --- a/libs/bsw/runtime/include/runtime/RuntimeMonitor.h +++ b/libs/bsw/runtime/include/runtime/RuntimeMonitor.h @@ -87,6 +87,13 @@ class RuntimeMonitor void enterTask(size_t const taskIdx) { +#ifdef BENCHMARK + if (taskIdx != 0) + { + _lastEnterTaskTimestamp = getSystemTicks32Bit(); + return; + } +#endif ::async::LockType const lock; uint32_t const timestamp = getSystemTicks32Bit(); _lastEnterTaskTimestamp = timestamp; @@ -95,6 +102,13 @@ class RuntimeMonitor void leaveTask(size_t const taskIdx) { +#ifdef BENCHMARK + if (taskIdx != 0) + { + _lastEnterTaskTimestamp = getSystemTicks32Bit(); + return; + } +#endif ::async::LockType const lock; uint32_t const timestamp = getSystemTicks32Bit(); _contextStack.popEntry(_taskStatistics.getEntry(taskIdx), timestamp); @@ -102,36 +116,44 @@ class RuntimeMonitor void enterIsrGroup(size_t const isrGroupIdx) { +#ifndef BENCHMARK ::async::LockType const lock; uint32_t const timestamp = getSystemTicks32Bit(); _contextStack.pushEntry(_isrGroupStatistics.getEntry(isrGroupIdx), timestamp); +#endif } void leaveIsrGroup(size_t const isrGroupIdx) { +#ifndef BENCHMARK ::async::LockType const lock; uint32_t const timestamp = getSystemTicks32Bit(); _contextStack.popEntry(_isrGroupStatistics.getEntry(isrGroupIdx), timestamp); +#endif } void enterFunction(FunctionEntryType& functionEntry) const { +#ifndef BENCHMARK ::async::LockType const lock; ContextEntryType* const topEntry = _contextStack.getTopEntry(); if (topEntry != nullptr) { topEntry->pushEntry(functionEntry, getSystemTicks32Bit()); } +#endif } void leaveFunction(FunctionEntryType& functionEntry) const { +#ifndef BENCHMARK ::async::LockType const lock; ContextEntryType* const topEntry = _contextStack.getTopEntry(); if (topEntry != nullptr) { topEntry->popEntry(functionEntry, getSystemTicks32Bit()); } +#endif } private: