mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-01 02:19:00 -05:00
When the CPU is in WFI mode, the CPU usage statistics should only reflect the WFI range and not the other frequency ranges.
281 lines
11 KiB
C++
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
|