Files
MuditaOS/module-sys/SystemManager/PowerManager.cpp
Maciej Gibowicz 919288eef2 [BH-1950] Fix incorrect Power Management statistics in the logs
When the CPU is in WFI mode, the CPU usage statistics should only
reflect the WFI range and not the other frequency ranges.
2024-04-26 08:44:09 +02:00

281 lines
11 KiB
C++

// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/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 <SystemManager/CpuStatistics.hpp>
#include <SystemManager/PowerManager.hpp>
#include <gsl/util>
#include <log/log.hpp>
#include <Logger.hpp>
#include <Utils.hpp>
#include <FreeRTOS.h>
#include <ticks.hpp>
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<std::uint64_t>(totalTicksCount) * 100) / totalTicksIncrease);
}
[[nodiscard]] auto CpuFrequencyMonitor::GetPeriodRuntimePercentage(
const TickType_t periodTicksIncrease) const noexcept -> std::uint32_t
{
return periodTicksIncrease == 0
? 0
: ((static_cast<std::uint64_t>(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<CpuGovernor>();
logSentinel = std::make_unique<LogSentinel>(logDumpFrequencyToHold);
Log::Logger::get().preDumpToFile = [this]() { logSentinel->HoldMinimumFrequency(); };
Log::Logger::get().postDumpToFile = [this]() { logSentinel->ReleaseMinimumFrequency(); };
cpuAlgorithms = std::make_unique<cpu::AlgorithmFactory>();
cpuAlgorithms->emplace(sys::cpu::AlgoID::ImmediateUpscale, std::make_unique<sys::cpu::ImmediateUpscale>());
cpuAlgorithms->emplace(sys::cpu::AlgoID::FrequencyStepping,
std::make_unique<sys::cpu::FrequencyStepping>(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<CpuSentinel> 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<sys::cpu::FrequencyHold>(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<devices::Device>
{
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