~aleteoryx/muditaos

11aa4c7ffb706cbdfcee63d7ea1c2637396adc10 — Maciej-Mudita 5 years ago 2b64d8a
[EGD-5382] Add LowPower CpuSentinels

In order to synchronize the Low Power mode, the services were
immediately informed about the frequency change,
so that they can update their resources (e.g. PWM filling)
and services may request the maximum CPU frequency in order
to perform a task (e.g. screen redraw, telephone conversation)
M enabled_unittests => enabled_unittests +4 -0
@@ 212,6 212,10 @@ TESTS_LIST["catch2-service-evtmgr"]="
    ScreenLightControlFunctions;
"
#---------
TESTS_LIST["catch2-PowerManager"]="
    Power Manager CPU sentinels governor test;
"
#---------
TESTS_LIST["catch2-StatefulController-tests"]="
    Given StatefulController when turn on then turned on;
    Given StatefulController when error during device registration then turned off;

M module-bsp/board/linux/lpm/LinuxLPM.cpp => module-bsp/board/linux/lpm/LinuxLPM.cpp +7 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

//


@@ 20,11 20,16 @@ namespace bsp
        return 0;
    }

    void LinuxLPM::SetCpuFrequency(bsp::LowPowerMode::CpuFrequency freq)
    void LinuxLPM::SetCpuFrequency(bsp::CpuFrequencyHz freq)
    {
        currentFrequency = freq;
    }

    uint32_t LinuxLPM::GetCpuFrequency() const noexcept
    {
        return 0;
    }

    void LinuxLPM::SwitchOscillatorSource(bsp::LowPowerMode::OscillatorSource source)
    {
        currentOscSource = source;

M module-bsp/board/linux/lpm/LinuxLPM.h => module-bsp/board/linux/lpm/LinuxLPM.h +2 -1
@@ 14,7 14,8 @@ namespace bsp
      public:
        int32_t PowerOff() override final;
        int32_t Reboot() override final;
        void SetCpuFrequency(CpuFrequency freq) final;
        void SetCpuFrequency(CpuFrequencyHz freq) final;
        [[nodiscard]] uint32_t GetCpuFrequency() const noexcept final;
        void SwitchOscillatorSource(OscillatorSource source) final;
    };


M module-bsp/board/rt1051/bsp/lpm/RT1051LPM.cpp => module-bsp/board/rt1051/bsp/lpm/RT1051LPM.cpp +13 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RT1051LPM.hpp"


@@ 45,32 45,37 @@ namespace bsp
        return 0;
    }

    void RT1051LPM::SetCpuFrequency(bsp::LowPowerMode::CpuFrequency freq)
    void RT1051LPM::SetCpuFrequency(bsp::CpuFrequencyHz freq)
    {
        currentFrequency = freq;
        switch (freq) {
        case bsp::LowPowerMode::CpuFrequency::Level_1:
        case bsp::CpuFrequencyHz::Level_1:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Osc_12_Mhz);
            break;
        case bsp::LowPowerMode::CpuFrequency::Level_2:
        case bsp::CpuFrequencyHz::Level_2:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Osc_24_Mhz);
            break;
        case bsp::LowPowerMode::CpuFrequency::Level_3:
        case bsp::CpuFrequencyHz::Level_3:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Pll2_66_Mhz);
            break;
        case bsp::LowPowerMode::CpuFrequency::Level_4:
        case bsp::CpuFrequencyHz::Level_4:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Pll2_132_Mhz);
            break;
        case bsp::LowPowerMode::CpuFrequency::Level_5:
        case bsp::CpuFrequencyHz::Level_5:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Pll2_264_Mhz);
            break;
        case bsp::LowPowerMode::CpuFrequency::Level_6:
        case bsp::CpuFrequencyHz::Level_6:
            CpuFreq->SetCpuFrequency(CpuFreqLPM::CpuClock::CpuClock_Pll2_528_Mhz);
            break;
        }
        LOG_INFO("CPU frequency changed to %lu", CLOCK_GetFreq(kCLOCK_CpuClk));
    }

    uint32_t RT1051LPM::GetCpuFrequency() const noexcept
    {
        return CLOCK_GetCpuClkFreq();
    }

    void RT1051LPM::SwitchOscillatorSource(bsp::LowPowerMode::OscillatorSource source)
    {
        if (source == bsp::LowPowerMode::OscillatorSource::Internal) {

M module-bsp/board/rt1051/bsp/lpm/RT1051LPM.hpp => module-bsp/board/rt1051/bsp/lpm/RT1051LPM.hpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef PUREPHONE_RT1051LPM_HPP


@@ 18,7 18,8 @@ namespace bsp
        RT1051LPM();
        int32_t PowerOff() override final;
        int32_t Reboot() override final;
        void SetCpuFrequency(CpuFrequency freq) final;
        void SetCpuFrequency(CpuFrequencyHz freq) final;
        [[nodiscard]] uint32_t GetCpuFrequency() const noexcept final;
        void SwitchOscillatorSource(OscillatorSource source) final;

      private:

M module-bsp/bsp/common.hpp => module-bsp/bsp/common.hpp +14 -0
@@ 1,3 1,6 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

//TODO maybe move KeyEvents to keyboard.hpp


@@ 15,6 18,17 @@ namespace bsp

    };

    /// CPU frequency is dependent on the clock settings.
    /// Only a few thresholds are available in the current configuration
    enum class CpuFrequencyHz
    {
        Level_1 = 12000000,
        Level_2 = 24000000,
        Level_3 = 66000000,
        Level_4 = 132000000,
        Level_5 = 264000000,
        Level_6 = 528000000
    };

    enum class Board{
    	T3,

M module-bsp/bsp/lpm/bsp_lpm.cpp => module-bsp/bsp/lpm/bsp_lpm.cpp +4 -1
@@ 1,3 1,6 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "bsp_lpm.hpp"




@@ 26,7 29,7 @@ namespace bsp{
        return inst;
    }

    LowPowerMode::CpuFrequency LowPowerMode::GetCurrentFrequency() const noexcept
    CpuFrequencyHz LowPowerMode::GetCurrentFrequencyLevel() const noexcept
    {
    	return currentFrequency;
    }

M module-bsp/bsp/lpm/bsp_lpm.hpp => module-bsp/bsp/lpm/bsp_lpm.hpp +9 -15
@@ 1,23 1,17 @@
#ifndef PUREPHONE_BSP_LPM_HPP
#define PUREPHONE_BSP_LPM_HPP
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <optional>
#include <memory>
#include <bsp/common.hpp>

namespace bsp {

    class LowPowerMode
    {
      public:
        enum class CpuFrequency
        {
            Level_1, // 12 MHz
            Level_2, // 24 MHz
            Level_3, // 66 MHz
            Level_4, // 132 MHz
            Level_5, // 264 MHz
            Level_6  // 528 MHz
        };
        enum class OscillatorSource
        {
            External,


@@ 32,16 26,16 @@ namespace bsp {
        virtual int32_t PowerOff() = 0;
        virtual int32_t Reboot() = 0;

        virtual void SetCpuFrequency(CpuFrequency freq) = 0;
        [[nodiscard]] CpuFrequency GetCurrentFrequency() const noexcept;
        virtual void SetCpuFrequency(CpuFrequencyHz freq) = 0;
        [[nodiscard]] CpuFrequencyHz GetCurrentFrequencyLevel() const noexcept;
        [[nodiscard]] virtual uint32_t GetCpuFrequency() const noexcept = 0;

        virtual void SwitchOscillatorSource(OscillatorSource source) = 0;
        [[nodiscard]] OscillatorSource GetCurrentOscillatorSource() const noexcept;

    protected:
        CpuFrequency currentFrequency = CpuFrequency::Level_6;
        CpuFrequencyHz currentFrequency = CpuFrequencyHz::Level_6;
        OscillatorSource currentOscSource = OscillatorSource::External;
    };
} // namespace bsp

#endif //PUREPHONE_BSP_LPM_HPP

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +10 -0
@@ 14,6 14,7 @@
#include "NetworkSettings.hpp"
#include "service-cellular/RequestFactory.hpp"
#include "service-cellular/CellularRequestHandler.hpp"
#include "SystemManager/messages/SentinelRegistrationMessage.hpp"

#include <Audio/AudioCommon.hpp>
#include <BaseInterface.hpp>


@@ 246,6 247,15 @@ sys::ReturnCodes ServiceCellular::InitHandler()
        [this](const std::string &value) { apnListChanged(value); },
        ::settings::SettingsScope::Global);

    cpuSentinel = std::make_shared<sys::CpuSentinel>(serviceName, this);

    auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(cpuSentinel);
    bus.sendUnicast(sentinelRegistrationMsg, service::name::system_manager);

    // temporarily limit the minimum CPU frequency
    // due to problems with the UART of the GSM modem
    cpuSentinel->HoldMinimumFrequency(bsp::CpuFrequencyHz::Level_4);

    return sys::ReturnCodes::Success;
}


M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +2 -0
@@ 19,6 19,7 @@
#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/CpuSentinel.hpp>
#include <bsp/common.hpp>
#include <utf8/UTF8.hpp>
#include <optional> // for optional


@@ 159,6 160,7 @@ class ServiceCellular : public sys::Service

  private:
    std::unique_ptr<TS0710> cmux = std::make_unique<TS0710>(PortSpeed_e::PS460800, this);
    std::shared_ptr<sys::CpuSentinel> cpuSentinel;

    // used for polling for call state
    std::unique_ptr<sys::Timer> callStateTimer;

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +12 -0
@@ 12,6 12,7 @@
#include <messages/EinkMessage.hpp>
#include <messages/ImageMessage.hpp>
#include <SystemManager/messages/DeviceRegistrationMessage.hpp>
#include <SystemManager/messages/SentinelRegistrationMessage.hpp>
#include <SystemManager/Constants.hpp>

#include <cstring>


@@ 59,6 60,13 @@ namespace service::eink
        auto deviceRegistrationMsg = std::make_shared<sys::DeviceRegistrationMessage>(display.getDevice());
        bus.sendUnicast(deviceRegistrationMsg, service::name::system_manager);

        cpuSentinel = std::make_shared<sys::CpuSentinel>(name::eink, this, [this](bsp::CpuFrequencyHz newFrequency) {
            updateResourcesAfterCpuFrequencyChange(newFrequency);
        });

        auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(cpuSentinel);
        bus.sendUnicast(sentinelRegistrationMsg, service::name::system_manager);

        display.powerOn();

        auto msg = std::make_shared<service::gui::EinkInitialized>(display.getSize());


@@ 200,4 208,8 @@ namespace service::eink
    {
        return currentState == state;
    }

    void ServiceEink::updateResourcesAfterCpuFrequencyChange(bsp::CpuFrequencyHz newFrequency)
    {}

} // namespace service::eink

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +7 -0
@@ 7,6 7,7 @@
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/Timer.hpp>
#include <Service/CpuSentinel.hpp>

#include "EinkDisplay.hpp"



@@ 54,6 55,11 @@ namespace service::eink
        EinkStatus_e refreshDisplay(::gui::RefreshModes refreshMode);
        EinkStatus_e updateDisplay(uint8_t *frameBuffer, ::gui::RefreshModes refreshMode);

        // function called from the PowerManager context
        // to update resources immediately
        // critical section or mutex support necessary
        void updateResourcesAfterCpuFrequencyChange(bsp::CpuFrequencyHz newFrequency);

        sys::MessagePointer handleEinkModeChangedMessage(sys::Message *message);
        sys::MessagePointer handleImageMessage(sys::Message *message);
        sys::MessagePointer handlePrepareEarlyRequest(sys::Message *message);


@@ 61,6 67,7 @@ namespace service::eink
        EinkDisplay display;
        State currentState;
        std::unique_ptr<sys::Timer> displayPowerOffTimer;
        std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    };
} // namespace service::eink


M module-sys/CMakeLists.txt => module-sys/CMakeLists.txt +2 -0
@@ 13,11 13,13 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Message.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Service.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Timer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/CpuSentinel.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/SystemManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DependencyGraph.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/PowerManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/CpuStatistics.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DeviceManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/CpuGovernor.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/graph/TopologicalSort.cpp

        )

A module-sys/Service/CpuSentinel.cpp => module-sys/Service/CpuSentinel.cpp +34 -0
@@ 0,0 1,34 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CpuSentinel.hpp"
#include "SystemManager/messages/RequestCpuFrequencyMessage.hpp"
#include "SystemManager/Constants.hpp"
#include <memory>

namespace sys
{

    CpuSentinel::CpuSentinel(std::string name, sys::Service *service, std::function<void(bsp::CpuFrequencyHz)> callback)
        : name(name), owner(service), callback(callback)
    {}

    [[nodiscard]] auto CpuSentinel::GetName() const noexcept -> std::string
    {
        return name;
    }

    void CpuSentinel::HoldMinimumFrequency(bsp::CpuFrequencyHz frequencyToHold)
    {
        auto msg = std::make_shared<sys::RequestCpuFrequencyMessage>(GetName(), frequencyToHold);
        owner->bus.sendUnicast(msg, service::name::system_manager);
    }

    void CpuSentinel::CpuFrequencyHasChanged(bsp::CpuFrequencyHz newFrequency)
    {
        if (callback) {
            callback(newFrequency);
        }
    }

} // namespace sys

A module-sys/Service/CpuSentinel.hpp => module-sys/Service/CpuSentinel.hpp +40 -0
@@ 0,0 1,40 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <string>
#include <functional>
#include <bsp/common.hpp>
#include "Service.hpp"

namespace sys
{
    /// Each sentinel manages the requests, i.e. when it is needed it sends messages to CpuGovernor with the required
    /// minimum CPU frequency to perform the task (e.g. screen redraw). Furthermore, every sentinel is informed
    /// immediately after changing the frequency. This allows it to invoke a callback to the service to update their
    /// resources (e.g. PWM filling). Every sentinel must register itself on startup to CpuGovernor by sending
    /// "SentinelRegistrationMessage".
    class CpuSentinel
    {
      public:
        explicit CpuSentinel(std::string name,
                             sys::Service *service,
                             std::function<void(bsp::CpuFrequencyHz)> callback = nullptr);
        ~CpuSentinel() = default;

        [[nodiscard]] auto GetName() const noexcept -> std::string;
        void HoldMinimumFrequency(bsp::CpuFrequencyHz frequencyToHold);
        void CpuFrequencyHasChanged(bsp::CpuFrequencyHz newFrequency);

      protected:
        const std::string name;
        sys::Service *owner{nullptr};

        /// function called from the PowerManager context
        /// to update resources immediately
        /// critical section or mutex support necessary
        std::function<void(bsp::CpuFrequencyHz)> callback;
    };

} // namespace sys

A module-sys/SystemManager/CpuGovernor.cpp => module-sys/SystemManager/CpuGovernor.cpp +118 -0
@@ 0,0 1,118 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CpuGovernor.hpp"
#include <algorithm>
#include <log/log.hpp>

namespace sys
{

    GovernorSentinel::GovernorSentinel(std::shared_ptr<CpuSentinel> newSentinel)
        : sentinelPtr(newSentinel), requestedFrequency(bsp::CpuFrequencyHz::Level_1)
    {}

    [[nodiscard]] auto GovernorSentinel::GetSentinel() const noexcept -> SentinelPointer
    {
        return sentinelPtr;
    }

    [[nodiscard]] auto GovernorSentinel::GetRequestedFrequency() const noexcept -> bsp::CpuFrequencyHz
    {
        return requestedFrequency;
    }

    void GovernorSentinel::SetRequestedFrequency(bsp::CpuFrequencyHz newFrequency)
    {
        requestedFrequency = newFrequency;
    }

    void CpuGovernor::RegisterNewSentinel(std::shared_ptr<CpuSentinel> newSentinel)
    {
        if (newSentinel) {
            auto isNewSentinelAlreadyRegistered = false;

            for (auto &sentinel : sentinels) {
                auto sentinelWeakPointer = sentinel->GetSentinel();
                if (!sentinelWeakPointer.expired()) {
                    std::shared_ptr<CpuSentinel> sharedResource = sentinelWeakPointer.lock();
                    if (sharedResource->GetName() == newSentinel->GetName()) {
                        isNewSentinelAlreadyRegistered = true;
                        break;
                    }
                }
            }

            if (!isNewSentinelAlreadyRegistered) {
                sentinels.push_back(std::make_unique<GovernorSentinel>(newSentinel));
            }
            else {
                LOG_WARN("New sentinel %s is already registered", newSentinel->GetName().c_str());
            }
        }
    }

    [[nodiscard]] auto CpuGovernor::GetNumberOfRegisteredSentinels() const noexcept -> uint32_t
    {
        return sentinels.size();
    }

    void CpuGovernor::PrintAllSentinels() const noexcept
    {
        std::for_each(std::begin(sentinels), std::end(sentinels), PrintName);
    }

    void CpuGovernor::SetCpuFrequencyRequest(std::string sentinelName, bsp::CpuFrequencyHz request)
    {
        for (auto &sentinel : sentinels) {
            auto sentinelWeakPointer = sentinel->GetSentinel();
            if (!sentinelWeakPointer.expired()) {
                std::shared_ptr<CpuSentinel> sharedResource = sentinelWeakPointer.lock();
                if (sharedResource->GetName() == sentinelName) {
                    sentinel->SetRequestedFrequency(request);
                }
            }
        }
    }

    void CpuGovernor::ResetCpuFrequencyRequest(std::string sentinelName)
    {
        SetCpuFrequencyRequest(sentinelName, bsp::CpuFrequencyHz::Level_1);
    }

    [[nodiscard]] auto CpuGovernor::GetMinimumFrequencyRequested() const noexcept -> bsp::CpuFrequencyHz
    {
        bsp::CpuFrequencyHz minFrequency = bsp::CpuFrequencyHz::Level_1;

        for (auto &sentinel : sentinels) {
            const auto sentinelFrequency = sentinel->GetRequestedFrequency();

            if (sentinelFrequency > minFrequency) {
                minFrequency = sentinelFrequency;
            }
        }

        return minFrequency;
    }

    void CpuGovernor::InformSentinelsAboutCpuFrequencyChange(bsp::CpuFrequencyHz newFrequency) const noexcept
    {
        for (auto &sentinel : sentinels) {
            auto sentinelWeakPointer = sentinel->GetSentinel();
            if (!sentinelWeakPointer.expired()) {
                std::shared_ptr<CpuSentinel> sharedResource = sentinelWeakPointer.lock();
                sharedResource->CpuFrequencyHasChanged(newFrequency);
            }
        }
    }

    void CpuGovernor::PrintName(const GovernorSentinelPointer &element)
    {
        auto sentinelWeakPointer = element->GetSentinel();
        if (!sentinelWeakPointer.expired()) {
            std::shared_ptr<CpuSentinel> sharedResource = sentinelWeakPointer.lock();
            LOG_INFO("Sentinel %s", sharedResource->GetName().c_str());
        }
    }

} // namespace sys

A module-sys/SystemManager/CpuGovernor.hpp => module-sys/SystemManager/CpuGovernor.hpp +52 -0
@@ 0,0 1,52 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <memory>
#include <vector>
#include "Service/CpuSentinel.hpp"

namespace sys
{
    using SentinelPointer = std::weak_ptr<CpuSentinel>;

    class GovernorSentinel
    {
      public:
        explicit GovernorSentinel(std::shared_ptr<CpuSentinel> newSentinel);
        [[nodiscard]] auto GetSentinel() const noexcept -> SentinelPointer;
        [[nodiscard]] auto GetRequestedFrequency() const noexcept -> bsp::CpuFrequencyHz;
        void SetRequestedFrequency(bsp::CpuFrequencyHz newFrequency);

      private:
        SentinelPointer sentinelPtr;
        bsp::CpuFrequencyHz requestedFrequency;
    };

    using GovernorSentinelPointer = std::unique_ptr<GovernorSentinel>;
    using GovernorSentinelsVector = std::vector<GovernorSentinelPointer>;

    /// CpuGovernor manages all sentinels in the system and has CPU frequency requests from them (e.g. eInkSentinel).
    /// It is also responsible for informing all sentinels that the CPU frequency has changed.
    class CpuGovernor
    {

      public:
        void RegisterNewSentinel(std::shared_ptr<CpuSentinel> newSentinel);
        [[nodiscard]] auto GetNumberOfRegisteredSentinels() const noexcept -> uint32_t;
        void PrintAllSentinels() const noexcept;

        void SetCpuFrequencyRequest(std::string sentinelName, bsp::CpuFrequencyHz request);
        void ResetCpuFrequencyRequest(std::string sentinelName);

        [[nodiscard]] auto GetMinimumFrequencyRequested() const noexcept -> bsp::CpuFrequencyHz;
        void InformSentinelsAboutCpuFrequencyChange(bsp::CpuFrequencyHz newFrequency) const noexcept;

      private:
        static void PrintName(const GovernorSentinelPointer &element);

        GovernorSentinelsVector sentinels;
    };

} // namespace sys

M module-sys/SystemManager/PowerManager.cpp => module-sys/SystemManager/PowerManager.cpp +51 -22
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>


@@ 11,6 11,7 @@ namespace sys
    {
        lowPowerControl = bsp::LowPowerMode::Create().value_or(nullptr);
        driverSEMC      = drivers::DriverSEMC::Create("ExternalRAM");
        cpuGovernor     = std::make_unique<CpuGovernor>();
    }

    PowerManager::~PowerManager()


@@ 28,13 29,14 @@ namespace sys

    void PowerManager::UpdateCpuFrequency(uint32_t cpuLoad)
    {
        const auto freq = lowPowerControl->GetCurrentFrequency();
        const auto currentCpuFreq        = lowPowerControl->GetCurrentFrequencyLevel();
        const auto minFrequencyRequested = cpuGovernor->GetMinimumFrequencyRequested();

        if (cpuLoad > frequencyShiftUpperThreshold && freq < bsp::LowPowerMode::CpuFrequency::Level_6) {
        if (cpuLoad > frequencyShiftUpperThreshold && currentCpuFreq < bsp::CpuFrequencyHz::Level_6) {
            aboveThresholdCounter++;
            belowThresholdCounter = 0;
        }
        else if (cpuLoad < frequencyShiftLowerThreshold && freq > bsp::LowPowerMode::CpuFrequency::Level_1) {
        else if (cpuLoad < frequencyShiftLowerThreshold && currentCpuFreq > bsp::CpuFrequencyHz::Level_1) {
            belowThresholdCounter++;
            aboveThresholdCounter = 0;
        }


@@ 42,22 44,24 @@ namespace sys
            ResetFrequencyShiftCounter();
        }

        if (aboveThresholdCounter >= maxAboveThresholdCount) {
        if (aboveThresholdCounter >= maxAboveThresholdCount || minFrequencyRequested > currentCpuFreq) {
            ResetFrequencyShiftCounter();
            IncreaseCpuFrequency();
        }
        if (belowThresholdCounter >= maxBelowThresholdCount) {
            ResetFrequencyShiftCounter();
            DecreaseCpuFrequency();
        else {
            if (belowThresholdCounter >= maxBelowThresholdCount && currentCpuFreq > minFrequencyRequested) {
                ResetFrequencyShiftCounter();
                DecreaseCpuFrequency();
            }
        }
    }

    void PowerManager::IncreaseCpuFrequency() const
    {
        const auto freq      = lowPowerControl->GetCurrentFrequency();
        const auto freq      = lowPowerControl->GetCurrentFrequencyLevel();
        const auto oscSource = lowPowerControl->GetCurrentOscillatorSource();

        if (freq == bsp::LowPowerMode::CpuFrequency::Level_1) {
        if (freq == bsp::CpuFrequencyHz::Level_1) {
            // switch osc source first
            if (oscSource == bsp::LowPowerMode::OscillatorSource::Internal) {
                lowPowerControl->SwitchOscillatorSource(bsp::LowPowerMode::OscillatorSource::External);


@@ 70,33 74,42 @@ namespace sys
        }

        // and increase frequency
        if (freq < bsp::LowPowerMode::CpuFrequency::Level_6) {
            lowPowerControl->SetCpuFrequency(bsp::LowPowerMode::CpuFrequency::Level_6);
        if (freq < bsp::CpuFrequencyHz::Level_6) {
            SetCpuFrequency(bsp::CpuFrequencyHz::Level_6);
        }
    }

    void PowerManager::DecreaseCpuFrequency() const
    {
        const auto freq = lowPowerControl->GetCurrentFrequency();
        auto level      = bsp::LowPowerMode::CpuFrequency::Level_1;
        const auto freq = lowPowerControl->GetCurrentFrequencyLevel();
        auto level      = bsp::CpuFrequencyHz::Level_1;

        // We temporarily limit the minimum CPU frequency
        // due to problems with the UART of the GSM modem
        switch (freq) {
        case bsp::LowPowerMode::CpuFrequency::Level_6:
            level = bsp::LowPowerMode::CpuFrequency::Level_5;
        case bsp::CpuFrequencyHz::Level_6:
            level = bsp::CpuFrequencyHz::Level_5;
            break;
        case bsp::CpuFrequencyHz::Level_5:
            level = bsp::CpuFrequencyHz::Level_4;
            break;
        default:
            level = bsp::LowPowerMode::CpuFrequency::Level_4;
        case bsp::CpuFrequencyHz::Level_4:
            level = bsp::CpuFrequencyHz::Level_3;
            break;
        case bsp::CpuFrequencyHz::Level_3:
            level = bsp::CpuFrequencyHz::Level_2;
            break;
        case bsp::CpuFrequencyHz::Level_2:
            level = bsp::CpuFrequencyHz::Level_1;
            break;
        case bsp::CpuFrequencyHz::Level_1:
            break;
        }

        // decrease frequency first
        if (level != freq) {
            lowPowerControl->SetCpuFrequency(level);
            SetCpuFrequency(level);
        }

        if (level == bsp::LowPowerMode::CpuFrequency::Level_1) {
        if (level == bsp::CpuFrequencyHz::Level_1) {
            const auto oscSource = lowPowerControl->GetCurrentOscillatorSource();

            // then switch osc source


@@ 111,6 124,22 @@ namespace sys
        }
    }

    void PowerManager::RegisterNewSentinel(std::shared_ptr<CpuSentinel> newSentinel) const
    {
        cpuGovernor->RegisterNewSentinel(newSentinel);
    }

    void PowerManager::SetCpuFrequencyRequest(std::string sentinelName, bsp::CpuFrequencyHz request) const
    {
        cpuGovernor->SetCpuFrequencyRequest(sentinelName, request);
    }

    void PowerManager::SetCpuFrequency(bsp::CpuFrequencyHz freq) const
    {
        lowPowerControl->SetCpuFrequency(freq);
        cpuGovernor->InformSentinelsAboutCpuFrequencyChange(freq);
    }

    void PowerManager::ResetFrequencyShiftCounter()
    {
        aboveThresholdCounter = 0;

M module-sys/SystemManager/PowerManager.hpp => module-sys/SystemManager/PowerManager.hpp +7 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef PUREPHONE_POWERMANAGER_HPP


@@ 8,6 8,7 @@

#include "bsp/lpm/bsp_lpm.hpp"
#include "drivers/semc/DriverSEMC.hpp"
#include "CpuGovernor.hpp"

namespace sys
{


@@ 45,14 46,19 @@ namespace sys

        [[nodiscard]] auto getExternalRamDevice() const noexcept -> std::shared_ptr<devices::Device>;

        void RegisterNewSentinel(std::shared_ptr<CpuSentinel> newSentinel) const;
        void SetCpuFrequencyRequest(std::string sentinelName, bsp::CpuFrequencyHz request) const;

      private:
        void ResetFrequencyShiftCounter();
        void SetCpuFrequency(bsp::CpuFrequencyHz freq) const;

        uint32_t belowThresholdCounter{0};
        uint32_t aboveThresholdCounter{0};

        std::unique_ptr<bsp::LowPowerMode> lowPowerControl;
        std::shared_ptr<drivers::DriverSEMC> driverSEMC;
        std::unique_ptr<CpuGovernor> cpuGovernor;
    };

} // namespace sys

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +16 -0
@@ 23,6 23,8 @@
#include <service-appmgr/service-appmgr/Controller.hpp>
#include "messages/CpuFrequencyMessage.hpp"
#include "messages/DeviceRegistrationMessage.hpp"
#include "messages/SentinelRegistrationMessage.hpp"
#include "messages/RequestCpuFrequencyMessage.hpp"
#include <time/ScopedTime.hpp>

const inline size_t systemManagerStack = 4096 * 2;


@@ 354,6 356,20 @@ namespace sys
            return sys::MessageNone{};
        });

        connect(typeid(sys::SentinelRegistrationMessage), [this](sys::Message *message) -> sys::MessagePointer {
            auto msg = static_cast<sys::SentinelRegistrationMessage *>(message);
            powerManager->RegisterNewSentinel(msg->getSentinel());

            return sys::MessageNone{};
        });

        connect(typeid(sys::RequestCpuFrequencyMessage), [this](sys::Message *message) -> sys::MessagePointer {
            auto msg = static_cast<sys::RequestCpuFrequencyMessage *>(message);
            powerManager->SetCpuFrequencyRequest(msg->getName(), msg->getRequest());

            return sys::MessageNone{};
        });

        deviceManager->RegisterNewDevice(powerManager->getExternalRamDevice());

        return ReturnCodes::Success;

A module-sys/SystemManager/messages/RequestCpuFrequencyMessage.hpp => module-sys/SystemManager/messages/RequestCpuFrequencyMessage.hpp +35 -0
@@ 0,0 1,35 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>
#include <bsp/common.hpp>

namespace sys
{

    class RequestCpuFrequencyMessage : public sys::DataMessage
    {
      public:
        RequestCpuFrequencyMessage(std::string sentinelName, bsp::CpuFrequencyHz request)
            : sys::DataMessage(MessageType::SystemManagerCpuFrequency), sentinelName(sentinelName),
              frequencyRequested(request)
        {}

        [[nodiscard]] auto getRequest() const noexcept
        {
            return frequencyRequested;
        };

        [[nodiscard]] auto getName() const noexcept
        {
            return sentinelName;
        };

      private:
        std::string sentinelName;
        bsp::CpuFrequencyHz frequencyRequested = bsp::CpuFrequencyHz::Level_1;
    };

} // namespace sys

A module-sys/SystemManager/messages/SentinelRegistrationMessage.hpp => module-sys/SystemManager/messages/SentinelRegistrationMessage.hpp +26 -0
@@ 0,0 1,26 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Message.hpp>
#include "Service/CpuSentinel.hpp"

namespace sys
{
    class SentinelRegistrationMessage : public sys::DataMessage
    {
      public:
        SentinelRegistrationMessage(std::shared_ptr<CpuSentinel> sentinelPtr)
            : sys::DataMessage(MessageType::SystemManagerRegistration), sentinel(sentinelPtr)
        {}

        [[nodiscard]] auto getSentinel() const noexcept -> std::shared_ptr<CpuSentinel>
        {
            return sentinel;
        };

      private:
        std::shared_ptr<CpuSentinel> sentinel;
    };
} // namespace sys

M module-sys/SystemManager/tests/CMakeLists.txt => module-sys/SystemManager/tests/CMakeLists.txt +9 -0
@@ 7,3 7,12 @@ add_catch2_executable(
    LIBS
        module-sys
)

add_catch2_executable(
    NAME
        PowerManager
    SRCS
        unittest_CpuSentinelsGovernor.cpp
    LIBS
        module-sys
)
\ No newline at end of file

A module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp => module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp +62 -0
@@ 0,0 1,62 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>
#include <SystemManager/CpuGovernor.hpp>
#include <Service/CpuSentinel.hpp>

TEST_CASE("Power Manager CPU sentinels governor test")
{
    using namespace sys;
    std::shared_ptr<CpuSentinel> testSentinel_0 = nullptr;
    auto testSentinel_1                         = std::make_shared<CpuSentinel>("testSentinel_1", nullptr);
    auto testSentinel_2                         = std::make_shared<CpuSentinel>("testSentinel_2", nullptr);
    auto testSentinel_3                         = std::make_shared<CpuSentinel>("testSentinel_1", nullptr);

    SECTION("Sentinel registration")
    {
        auto governor = std::make_unique<CpuGovernor>();
        REQUIRE(governor->GetNumberOfRegisteredSentinels() == 0);

        governor->RegisterNewSentinel(testSentinel_0);
        REQUIRE(governor->GetNumberOfRegisteredSentinels() == 0);

        governor->RegisterNewSentinel(testSentinel_1);
        REQUIRE(governor->GetNumberOfRegisteredSentinels() == 1);

        governor->RegisterNewSentinel(testSentinel_2);
        REQUIRE(governor->GetNumberOfRegisteredSentinels() == 2);

        governor->RegisterNewSentinel(testSentinel_3);
        REQUIRE(governor->GetNumberOfRegisteredSentinels() == 2);
    }

    SECTION("Sentinel demands")
    {
        auto governor = std::make_unique<CpuGovernor>();
        governor->RegisterNewSentinel(testSentinel_1);
        governor->RegisterNewSentinel(testSentinel_2);

        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_1);

        governor->SetCpuFrequencyRequest("testSentinel_1", bsp::CpuFrequencyHz::Level_4);
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_4);

        governor->SetCpuFrequencyRequest("testSentinel_2", bsp::CpuFrequencyHz::Level_6);
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_6);

        governor->SetCpuFrequencyRequest("testSentinel_1", bsp::CpuFrequencyHz::Level_2);
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_6);

        governor->ResetCpuFrequencyRequest("testSentinel_2");
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_2);

        governor->SetCpuFrequencyRequest("bedNameSentinel", bsp::CpuFrequencyHz::Level_6);
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_2);

        governor->ResetCpuFrequencyRequest("testSentinel_1");
        REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyHz::Level_1);
    }
}

M source/MessageType.hpp => source/MessageType.hpp +5 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef SOURCE_MESSAGETYPE_HPP_


@@ 169,6 169,10 @@ enum class MessageType
    // System manager
    DeviceRegistration,

    // System manager
    SystemManagerCpuFrequency,
    SystemManagerRegistration,

    // battery charger messages
    EVMBatteryLevel,
    EVMChargerPlugged,