// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include "SystemManager/cpu/algorithm/FrequencyHold.hpp" #include "SystemManager/cpu/algorithm/ImmediateUpscale.hpp" #include "SystemManager/cpu/algorithm/FrequencyStepping.hpp" #include "cpu/AlgorithmFactory.hpp" #include "magic_enum.hpp" #include #include #include #include #include #include #include #include namespace sys { namespace { constexpr auto lowestLevelName{"lowestCpuFrequency"}; constexpr auto middleLevelName{"middleCpuFrequency"}; constexpr auto highestLevelName{"highestCpuFrequency"}; constexpr auto WfiName{"WFI"}; constexpr bsp::CpuFrequencyMHz logDumpFrequencyToHold{bsp::CpuFrequencyMHz::Level_4}; } // namespace CpuFrequencyMonitor::CpuFrequencyMonitor(const std::string &name) : levelName(name) {} [[nodiscard]] auto CpuFrequencyMonitor::GetName() const noexcept -> std::string { return levelName; } [[nodiscard]] auto CpuFrequencyMonitor::GetTotalRuntimePercentage( const TickType_t totalTicksIncrease) const noexcept -> std::uint32_t { return totalTicksIncrease == 0 ? 0 : ((static_cast(totalTicksCount) * 100) / totalTicksIncrease); } [[nodiscard]] auto CpuFrequencyMonitor::GetPeriodRuntimePercentage( const TickType_t periodTicksIncrease) const noexcept -> std::uint32_t { return periodTicksIncrease == 0 ? 0 : ((static_cast(utils::computeIncrease(totalTicksCount, lastTotalTicksCount)) * 100) / periodTicksIncrease); } void CpuFrequencyMonitor::IncreaseTicks(TickType_t ticks) { totalTicksCount += ticks; } void CpuFrequencyMonitor::SavePeriodTicks() { lastTotalTicksCount = totalTicksCount; } PowerManager::PowerManager(CpuStatistics &cpuStats, TaskStatistics &taskStats) : powerProfile{bsp::getPowerProfile()}, cpuStatistics(cpuStats), taskStatistics(taskStats) { driverSEMC = drivers::DriverSEMC::Create(drivers::name::ExternalRAM); lowPowerControl = bsp::LowPowerMode::Create().value_or(nullptr); cpuGovernor = std::make_unique(); logSentinel = std::make_unique(logDumpFrequencyToHold); Log::Logger::get().preDumpToFile = [this]() { logSentinel->HoldMinimumFrequency(); }; Log::Logger::get().postDumpToFile = [this]() { logSentinel->ReleaseMinimumFrequency(); }; cpuAlgorithms = std::make_unique(); cpuAlgorithms->emplace(sys::cpu::AlgoID::ImmediateUpscale, std::make_unique()); cpuAlgorithms->emplace(sys::cpu::AlgoID::FrequencyStepping, std::make_unique(powerProfile)); cpuFrequencyMonitors.push_back(CpuFrequencyMonitor(lowestLevelName)); cpuFrequencyMonitors.push_back(CpuFrequencyMonitor(middleLevelName)); cpuFrequencyMonitors.push_back(CpuFrequencyMonitor(highestLevelName)); cpuFrequencyMonitors.push_back(CpuFrequencyMonitor(WfiName)); } PowerManager::~PowerManager() {} std::int32_t PowerManager::PowerOff() { return lowPowerControl->PowerOff(); } std::int32_t PowerManager::Reboot() { return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::NormalRestart); } std::int32_t PowerManager::RebootMSC() { return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToMSC); } std::int32_t PowerManager::RebootToRecovery(RecoveryReason reason) { switch (reason) { case RecoveryReason::FactoryReset: return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToRecoveryFactoryReset); case RecoveryReason::Recovery: return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToRecoveryRecovery); case RecoveryReason::Update: return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToRecoveryUpdate); case RecoveryReason::Backup: return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToRecoveryBackup); case RecoveryReason::Restore: return lowPowerControl->Reboot(bsp::LowPowerMode::RebootType::GoToRecoveryRestore); default: return -1; } } [[nodiscard]] cpu::UpdateResult PowerManager::UpdateCpuFrequency() { const std::uint32_t cpuLoad = cpuStatistics.GetPercentageCpuLoad(); cpu::UpdateResult retval; const cpu::AlgorithmData data{ cpuLoad, lowPowerControl->GetCurrentFrequencyLevel(), GetMinimumCpuFrequencyRequested()}; auto _ = gsl::finally([&retval, this, data] { retval.frequencySet = lowPowerControl->GetCurrentFrequencyLevel(); retval.data = data.sentinel; }); auto algorithms = { sys::cpu::AlgoID::FrequencyHold, sys::cpu::AlgoID::ImmediateUpscale, sys::cpu::AlgoID::FrequencyStepping}; auto result = cpuAlgorithms->calculate(algorithms, data, &retval.id); retval.changed = result.change; if (result.change == cpu::algorithm::Change::NoChange or result.change == cpu::algorithm::Change::Hold) { return retval; } SetCpuFrequency(result.value); cpuAlgorithms->reset(algorithms); return retval; } void PowerManager::RegisterNewSentinel(std::shared_ptr newSentinel) const { if (cpuGovernor->RegisterNewSentinel(newSentinel)) { newSentinel->ReadRegistrationData(lowPowerControl->GetCurrentFrequencyLevel()); } } void PowerManager::RemoveSentinel(std::string sentinelName) const { cpuGovernor->RemoveSentinel(std::move(sentinelName)); } void PowerManager::SetCpuFrequencyRequest(const std::string &sentinelName, bsp::CpuFrequencyMHz request) { cpuGovernor->SetCpuFrequencyRequest(sentinelName, request); auto ret = UpdateCpuFrequency(); cpuStatistics.TrackChange(ret); } void PowerManager::ResetCpuFrequencyRequest(const std::string &sentinelName) { cpuGovernor->ResetCpuFrequencyRequest(sentinelName); auto ret = UpdateCpuFrequency(); cpuStatistics.TrackChange(ret); } void PowerManager::BlockWfiMode(const std::string &sentinelName, bool block) { cpuGovernor->BlockWfiMode(sentinelName, block); } bool PowerManager::IsCpuPermanentFrequency() { return cpuAlgorithms->get(sys::cpu::AlgoID::FrequencyHold) != nullptr; } void PowerManager::SetPermanentFrequency(bsp::CpuFrequencyMHz freq) { cpuAlgorithms->emplace(sys::cpu::AlgoID::FrequencyHold, std::make_unique(freq, powerProfile)); } void PowerManager::ResetPermanentFrequency() { cpuAlgorithms->remove(sys::cpu::AlgoID::FrequencyHold); } void PowerManager::SetCpuFrequency(bsp::CpuFrequencyMHz freq) { UpdateCpuFrequencyMonitor(lowPowerControl->GetCurrentFrequencyLevel()); while (lowPowerControl->GetCurrentFrequencyLevel() != freq) { lowPowerControl->SetCpuFrequency(freq); logSentinel->UpdateCurrentFrequency(freq); cpuGovernor->InformSentinelsAboutCpuFrequencyChange(freq); } } [[nodiscard]] auto PowerManager::GetMinimumCpuFrequencyRequested() const noexcept -> sentinel::View { const auto governorSentinelsView = cpuGovernor->GetMinimumFrequencyRequested(); const auto logSentinelView = logSentinel->GetRequestedFrequency(); return (logSentinelView.minFrequency > governorSentinelsView.minFrequency) ? logSentinelView : governorSentinelsView; } [[nodiscard]] auto PowerManager::getExternalRamDevice() const noexcept -> std::shared_ptr { return driverSEMC; } void PowerManager::UpdateCpuFrequencyMonitor(bsp::CpuFrequencyMHz currentFreq) { auto ticks = xTaskGetTickCount(); auto levelName = currentFreq == powerProfile.minimalFrequency ? lowestLevelName : (currentFreq == bsp::CpuFrequencyMHz::Level_6 ? highestLevelName : middleLevelName); UpdateCpuFrequencyMonitor(levelName, ticks - lastCpuFrequencyChangeTimestamp); lastCpuFrequencyChangeTimestamp = ticks; } void PowerManager::UpdateCpuFrequencyMonitor(const std::string &name, std::uint32_t tickIncrease) { for (auto &level : cpuFrequencyMonitors) { if (level.GetName() == name) { level.IncreaseTicks(tickIncrease); } } } void PowerManager::UpdateCpuFrequencyMonitorTimestamp() { lastCpuFrequencyChangeTimestamp = xTaskGetTickCount(); } void PowerManager::EnterWfiIfReady() { if (cpuGovernor->IsWfiBlocked()) { return; } const auto timeSpentInWFI = lowPowerControl->EnterWfiModeIfAllowed(); if (timeSpentInWFI > 0) { /* We increase the frequency immediately after exiting WFI so that the xTaskCatchUpTicks procedure has * time to execute and does not block the button press detection mechanism. */ SetCpuFrequency(bsp::CpuFrequencyMHz::Level_4); portENTER_CRITICAL(); lowPowerControl->DisableSysTick(); xTaskCatchUpTicks(cpp_freertos::Ticks::MsToTicks(timeSpentInWFI)); lowPowerControl->EnableSysTick(); portEXIT_CRITICAL(); UpdateCpuFrequencyMonitor(WfiName, timeSpentInWFI); UpdateCpuFrequencyMonitorTimestamp(); } } void PowerManager::LogPowerManagerStatistics() { const TickType_t tickCount = xTaskGetTickCount(); const TickType_t periodTickIncrease = tickCount - lastLogStatisticsTimestamp; UpdateCpuFrequencyMonitor(lowPowerControl->GetCurrentFrequencyLevel()); std::string log{"Last period (total): "}; for (auto &level : cpuFrequencyMonitors) { log.append(level.GetName() + ": " + std::to_string(level.GetPeriodRuntimePercentage(periodTickIncrease)) + "% (" + std::to_string(level.GetTotalRuntimePercentage(tickCount)) + "%) "); level.SavePeriodTicks(); } lastLogStatisticsTimestamp = tickCount; LOG_INFO("%s", log.c_str()); taskStatistics.Update(); taskStatistics.LogCpuUsage(); cpuGovernor->PrintActiveSentinels(); } } // namespace sys