diff --git a/examples/platform/silabs/FreeRTOSRuntimeStats.c b/examples/platform/silabs/FreeRTOSRuntimeStats.c index a2b7d10ff7..7962b15a63 100644 --- a/examples/platform/silabs/FreeRTOSRuntimeStats.c +++ b/examples/platform/silabs/FreeRTOSRuntimeStats.c @@ -308,7 +308,6 @@ uint32_t ulGetAllTaskInfo(TaskInfo * taskInfoArray, uint32_t taskInfoArraySize, if (systemStats != NULL) { systemStats->terminatedTaskCount = deletedCount; - systemStats->totalTaskCount = systemStats->activeTaskCount + deletedCount; systemStats->activeTaskCount = actualTasks; } diff --git a/examples/platform/silabs/FreeRTOSRuntimeStats.h b/examples/platform/silabs/FreeRTOSRuntimeStats.h index e521f6b688..942d0ca7b9 100644 --- a/examples/platform/silabs/FreeRTOSRuntimeStats.h +++ b/examples/platform/silabs/FreeRTOSRuntimeStats.h @@ -64,7 +64,6 @@ typedef struct uint32_t systemPreemptionRatio; uint32_t activeTaskCount; uint32_t terminatedTaskCount; - uint32_t totalTaskCount; } SystemTaskStats; /** diff --git a/examples/platform/silabs/MatterConfig.cpp b/examples/platform/silabs/MatterConfig.cpp index 78792cc864..4f18a72a6f 100644 --- a/examples/platform/silabs/MatterConfig.cpp +++ b/examples/platform/silabs/MatterConfig.cpp @@ -363,6 +363,7 @@ CHIP_ERROR SilabsMatterConfig::InitMatter(const char * appName) #if MATTER_TRACING_ENABLED static Tracing::Silabs::BackendImpl backend; Tracing::Register(backend); + backend.RegisterPowerManagerTracing(); #endif // MATTER_TRACING_ENABLED chip::DeviceLayer::PlatformMgr().UnlockChipStack(); diff --git a/examples/platform/silabs/shell/tracing/TracingShellCommands.cpp b/examples/platform/silabs/shell/tracing/TracingShellCommands.cpp index c76cd247a3..c2b9af5e31 100644 --- a/examples/platform/silabs/shell/tracing/TracingShellCommands.cpp +++ b/examples/platform/silabs/shell/tracing/TracingShellCommands.cpp @@ -99,6 +99,11 @@ CHIP_ERROR TasksCommandHandler(int argc, char ** argv) return SilabsTracer::Instance().OutputTaskStatistics(); } +CHIP_ERROR EnergyCommandHandler(int argc, char ** argv) +{ + return SilabsTracer::Instance().OutputPowerManagerStatistics(); +} + } // namespace namespace TracingCommands { @@ -111,6 +116,7 @@ void RegisterCommands() { &MetricsCommandHandler, "metrics", "Display runtime metrics. Usage: metrics " }, { &FlushCommandHandler, "flush", "Display buffered traces. Usage: flush " }, { &TasksCommandHandler, "tasks", "Display FreeRTOS task statistics." }, + { &EnergyCommandHandler, "energy", "Display energy mode statistics." }, }; static const Shell::Command cmds_silabs_tracing = { &TracingCommandHandler, "tracing", "Dispatch Silicon Labs Tracing command" }; diff --git a/src/platform/silabs/tracing/BUILD.gn b/src/platform/silabs/tracing/BUILD.gn index dc596d5357..3dd3b99a39 100644 --- a/src/platform/silabs/tracing/BUILD.gn +++ b/src/platform/silabs/tracing/BUILD.gn @@ -26,6 +26,7 @@ declare_args() { # - Runtime statistics timer configuration # - Task switching hooks for statistics collection silabs_tracing_runtime_stats = false + silabs_tracing_energy_stats = false } buildconfig_header("silabs_tracing_config") { @@ -34,6 +35,10 @@ buildconfig_header("silabs_tracing_config") { defines = [] + if (silabs_tracing_energy_stats == true) { + defines += [ "SILABS_TRACING_ENERGY_STATS=1" ] + } + if (silabs_tracing_runtime_stats == true) { defines += [ "TRACING_RUNTIME_STATS=1", diff --git a/src/platform/silabs/tracing/BackendImpl.cpp b/src/platform/silabs/tracing/BackendImpl.cpp index 24cf65daf3..7a2719b4ed 100644 --- a/src/platform/silabs/tracing/BackendImpl.cpp +++ b/src/platform/silabs/tracing/BackendImpl.cpp @@ -96,6 +96,11 @@ void BackendImpl::LogMetricEvent(const MetricEvent & event) } } +void BackendImpl::RegisterPowerManagerTracing() +{ + SilabsTracer::Instance().RegisterPowerManagerTracing(); +} + } // namespace Silabs } // namespace Tracing } // namespace chip diff --git a/src/platform/silabs/tracing/BackendImpl.h b/src/platform/silabs/tracing/BackendImpl.h index 0594e78bc4..bdb909843c 100644 --- a/src/platform/silabs/tracing/BackendImpl.h +++ b/src/platform/silabs/tracing/BackendImpl.h @@ -52,6 +52,8 @@ class BackendImpl : public ::chip::Tracing::Backend * SilabsTracer.Init() needs to be called before this is called. */ void LogMetricEvent(const MetricEvent &) override; + + void RegisterPowerManagerTracing(); }; } // namespace Silabs diff --git a/src/platform/silabs/tracing/SilabsTracing.cpp b/src/platform/silabs/tracing/SilabsTracing.cpp index fcf1aa7489..8701a25b66 100644 --- a/src/platform/silabs/tracing/SilabsTracing.cpp +++ b/src/platform/silabs/tracing/SilabsTracing.cpp @@ -21,6 +21,8 @@ #include #include // Include the necessary header for std::string +#include "sl_power_manager_debug.h" + // Include FreeRTOS configuration first #if defined(TRACING_RUNTIME_STATS) && TRACING_RUNTIME_STATS extern "C" { @@ -821,8 +823,8 @@ CHIP_ERROR SilabsTracer::OutputTaskStatistics() VerifyOrReturnError(taskCount > 0, CHIP_ERROR_INTERNAL, ChipLogError(DeviceLayer, "Failed to get task information")); ChipLogProgress(DeviceLayer, "=== Task Statistics ==="); - ChipLogProgress(DeviceLayer, "Active tasks: %lu | Terminated tasks: %lu | Total tasks: %lu", systemStats.activeTaskCount, - systemStats.terminatedTaskCount, systemStats.totalTaskCount); + ChipLogProgress(DeviceLayer, "Active tasks: %lu | Terminated tasks: %lu", systemStats.activeTaskCount, + systemStats.terminatedTaskCount); ChipLogProgress(DeviceLayer, "Total Runtime: %lu ms", systemStats.totalRunTime); ChipLogProgress(DeviceLayer, "System Preemption Ratio: %lu.%02lu%% (%lu/%lu switches)", (systemStats.systemPreemptionRatio / 100), (systemStats.systemPreemptionRatio % 100), @@ -883,6 +885,91 @@ CHIP_ERROR SilabsTracer::OutputTaskStatistics() #endif // configGENERATE_RUN_TIME_STATS == 1 +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 + +void SilabsTracer::PowerManagerTransitionCallback(sl_power_manager_em_t from, sl_power_manager_em_t to) +{ + if (from == mCurrentEnergyMode) + { + auto currentTime = SILABS_GET_TIME(); + auto timeDiff = currentTime - mLastEnergyStateTransitionTime; + mTimeInEnergyState[from] += timeDiff; + mTransitionCountToEnergyState[to]++; + } + else + { + ChipLogError(DeviceLayer, "Unexpected power manager transition from EM%d to EM%d (expected EM%d to EM%d)", from, to, + mCurrentEnergyMode, to); + } + + // Update time spent in previous energy mode + mCurrentEnergyMode = to; + mLastEnergyStateTransitionTime = SILABS_GET_TIME(); + + if (mCurrentEnergyMode == SL_POWER_MANAGER_EM1) + { + OutputPowerManagerStatistics(); + } +} + +void SilabsTracer::StaticPowerManagerTransitionCallback(sl_power_manager_em_t from, sl_power_manager_em_t to) +{ + Instance().PowerManagerTransitionCallback(from, to); +} + +#endif // SILABS_TRACING_ENERGY_STATS + +void SilabsTracer::RegisterPowerManagerTracing() +{ +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 + memset(mTimeInEnergyState, 0, sizeof(mTimeInEnergyState)); + memset(mTransitionCountToEnergyState, 0, sizeof(mTransitionCountToEnergyState)); + mCurrentEnergyMode = static_cast(0); + mLastEnergyStateTransitionTime = SILABS_GET_TIME(); + + sl_power_manager_init(); + sl_power_manager_subscribe_em_transition_event(&mPowerManagerEmTransitionEventHandle, &mPowerManagerEmTransitionEventInfo); +#endif // SILABS_TRACING_ENERGY_STATS +} + +CHIP_ERROR SilabsTracer::OutputPowerManagerStatistics() +{ +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 + VerifyOrReturnError(isLogInitialized(), CHIP_ERROR_UNINITIALIZED); + + auto currentTime = SILABS_GET_TIME(); + + ChipLogProgress(DeviceLayer, "=== Power Manager Energy Mode Statistics ==="); + ChipLogProgress(DeviceLayer, "Total Runtime: %lu ms", currentTime.count()); + + // Table header + ChipLogProgress(DeviceLayer, "| %-12s| %-12s | %-10s | %-10s |", "Energy Mode", "Time (ms)", "Percentage", "Transitions"); + ChipLogProgress(DeviceLayer, "|%-13s|%-14s|%-12s|%-12s|", "-------------", "--------------", "------------", "------------"); + + for (size_t em = 0; em <= SL_POWER_MANAGER_EM2; em++) + { + System::Clock::Milliseconds32 totalTimeInMode = mTimeInEnergyState[em]; + if (mCurrentEnergyMode == static_cast(em)) + { + totalTimeInMode += currentTime - mLastEnergyStateTransitionTime; + } + // Use 64-bit arithmetic to avoid overflow, then calculate percentage with 4 decimal places + uint64_t percentage = + currentTime.count() > 0 ? (static_cast(totalTimeInMode.count()) * 1000000) / currentTime.count() : 0; + ChipLogProgress(DeviceLayer, "| EM%-10d| %-12lu | %3lu.%04lu%% | %-10u |", static_cast(em), totalTimeInMode.count(), + static_cast(percentage / 10000), static_cast(percentage % 10000), + mTransitionCountToEnergyState[em]); + } + + sl_power_manager_debug_print_em_requirements(); + + return CHIP_NO_ERROR; +#else // SILABS_TRACING_ENERGY_STATS != 1 + ChipLogError(DeviceLayer, "Power Manager statistics not available - SILABS_TRACING_ENERGY_STATS not enabled"); + return CHIP_ERROR_UNINITIALIZED; +#endif // SILABS_TRACING_ENERGY_STATS +} + } // namespace Silabs } // namespace Tracing } // namespace chip diff --git a/src/platform/silabs/tracing/SilabsTracing.h b/src/platform/silabs/tracing/SilabsTracing.h index 4b73911629..59284f311b 100644 --- a/src/platform/silabs/tracing/SilabsTracing.h +++ b/src/platform/silabs/tracing/SilabsTracing.h @@ -24,6 +24,12 @@ #include #include #include +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 +#include +#define SLEEP_EM_EVENT_MASK \ + (SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM0 | SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM1 | \ + SL_POWER_MANAGER_EVENT_TRANSITION_ENTERING_EM2) +#endif // SILABS_TRACING_ENERGY_STATS #include #include @@ -283,6 +289,18 @@ class SilabsTracer */ CHIP_ERROR OutputTaskStatistics(); + /** + * @brief Registers the power manager tracing functionality with the power manager to receive notifications when the device + * changes its power state. + */ + void RegisterPowerManagerTracing(); + + /** @brief Output power manager energy mode statistics + * This function outputs the total time spent in each energy mode and the percentage of total time. + * @return CHIP_ERROR, returns CHIP_ERROR_UNINITIALIZED if the feature or logs are not initialized + */ + CHIP_ERROR OutputPowerManagerStatistics(); + private: struct TimeTrackerList { @@ -358,6 +376,17 @@ class SilabsTracer PersistentStorageDelegate * mStorage = nullptr; size_t mBufferedTrackerCount = 0; +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 + + System::Clock::Milliseconds32 mTimeInEnergyState[SL_POWER_MANAGER_EM2 + 1]; + uint16_t mTransitionCountToEnergyState[SL_POWER_MANAGER_EM2 + 1]; + System::Clock::Milliseconds32 mLastEnergyStateTransitionTime; + sl_power_manager_em_t mCurrentEnergyMode; + sl_power_manager_em_transition_event_handle_t mPowerManagerEmTransitionEventHandle; + sl_power_manager_em_transition_event_info_t mPowerManagerEmTransitionEventInfo = { .event_mask = SLEEP_EM_EVENT_MASK, + .on_event = + StaticPowerManagerTransitionCallback }; +#endif // SILABS_TRACING_ENERGY_STATS /** @brief Clear the trace buffer */ void TraceBufferClear(); @@ -397,6 +426,24 @@ class SilabsTracer * @return CHIP_ERROR, returns CHIP_ERROR_INVALID_ARGUMENT if the format is not respected. */ CHIP_ERROR SplitNamedTraceString(CharSpan appOperationKey, CharSpan & groupSpan, CharSpan & labelSpan) const; + +#if defined(SILABS_TRACING_ENERGY_STATS) && SILABS_TRACING_ENERGY_STATS == 1 + + /** @brief Callback for power manager energy mode transitions + * This function is called by the power manager when the device transitions between energy modes. + * It updates the time spent in each energy mode. + * @param from The energy mode the device is transitioning from + * @param to The energy mode the device is transitioning to + */ + void PowerManagerTransitionCallback(sl_power_manager_em_t from, sl_power_manager_em_t to); + + /** @brief Static callback for power manager energy mode transitions + * This function is a static wrapper that calls the instance method PowerManagerTransitionCallback. + * @param from The energy mode the device is transitioning from + * @param to The energy mode the device is transitioning to + */ + static void StaticPowerManagerTransitionCallback(sl_power_manager_em_t from, sl_power_manager_em_t to); +#endif // SILABS_TRACING_ENERGY_STATS }; } // namespace Silabs