| /* |
| * Copyright (c) 2017, 2019-2020 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "sim/power_domain.hh" |
| |
| #include <unordered_map> |
| |
| #include "debug/PowerDomain.hh" |
| |
| PowerDomain::PowerDomain(const PowerDomainParams* p) : |
| PowerState(p), |
| leaders(p->leaders), |
| pwrStateUpdateEvent(*this), |
| stats(*this) |
| { |
| // Check if there is at least one leader |
| fatal_if(leaders.empty(), "No leaders registered in %s!)", name()); |
| |
| // Go over the leaders and register this power domain with them |
| for (auto leader : leaders) { |
| leader->setControlledDomain(this); |
| } |
| |
| // We will assume a power domain to start in the most performant p-state |
| // This will be corrected during startup() |
| leaderTargetState = Enums::PwrState::ON; |
| _currState = Enums::PwrState::ON; |
| } |
| |
| void |
| PowerDomain::addFollower(PowerState* pwr_obj) |
| { |
| DPRINTF(PowerDomain, "%s is a follower in %s\n", pwr_obj->name(), name()); |
| followers.push_back(pwr_obj); |
| } |
| |
| void |
| PowerDomain::startup() |
| { |
| DPRINTF(PowerDomain, "Checks at startup\n"); |
| // Check if the leaders and followers have the correct power states. |
| DPRINTF(PowerDomain, "Checking power state of leaders & followers\n"); |
| for (const auto &objs : { leaders, followers }) { |
| for (const auto &obj : objs) { |
| const auto & states = obj->getPossibleStates(); |
| auto it = states.find(Enums::PwrState::ON); |
| fatal_if(it == states.end(), |
| "%s in %s does not have the required power states to be " |
| "part of a PowerDomain i.e. the ON state!", obj->name(), |
| name()); |
| } |
| } |
| |
| // Now all objects have been checked for the minimally required power |
| // states, calculate the possible power states for the domain. This is the |
| // intersection between the possible power states of the followers and |
| // leaders. |
| calculatePossiblePwrStates(); |
| |
| // Check that there is no objects which is both a leader and a |
| // follower. |
| DPRINTF(PowerDomain, "Checking for double entries\n"); |
| for (auto follower : followers) { |
| for (auto leader : leaders) { |
| fatal_if(leader == follower, "%s is both a leader and follower" |
| " in %s\n!", leader->name(), name()); |
| } |
| } |
| // Record the power states of the leaders and followers |
| DPRINTF(PowerDomain, "Recording the current power states in domain\n"); |
| for (auto leader : leaders) { |
| Enums::PwrState pws = leader->get(); |
| fatal_if(pws == Enums::PwrState::UNDEFINED, |
| "%s is in the UNDEFINED power state, not acceptable as " |
| "leader!", leader->name()); |
| } |
| |
| // Calculate the power state of the domain, only looking at leader |
| leaderTargetState = calculatePowerDomainState(); |
| // Set the power states of the followers, based upon leaderTargetState. |
| setFollowerPowerStates(); |
| } |
| |
| bool |
| PowerDomain::isPossiblePwrState(Enums::PwrState p_state) |
| { |
| for (const auto &objs : { leaders, followers }) { |
| for (const auto &obj : objs) { |
| const auto &obj_states = obj->getPossibleStates(); |
| if (obj_states.find(p_state) == obj_states.end()) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void |
| PowerDomain::calculatePossiblePwrStates() |
| { |
| assert(possibleStates.empty()); |
| for (auto p_state: leaders[0]->getPossibleStates()) { |
| if (isPossiblePwrState(p_state)) { |
| possibleStates.emplace(p_state); |
| DPRINTF(PowerDomain, "%u/%s is a p-state\n", p_state, |
| Enums::PwrStateStrings[p_state]); |
| } |
| } |
| } |
| |
| Enums::PwrState |
| PowerDomain::calculatePowerDomainState( |
| const std::vector<Enums::PwrState> &f_states) |
| { |
| DPRINTF(PowerDomain, "Calculating the power state\n"); |
| Enums::PwrState most_perf_state = Enums::PwrState::Num_PwrState; |
| std::string most_perf_leader; |
| for (auto leader : leaders) { |
| Enums::PwrState pw = leader->get(); |
| if (pw < most_perf_state) { |
| most_perf_state = pw; |
| most_perf_leader = leader->name(); |
| } |
| } |
| assert(most_perf_state != Enums::PwrState::Num_PwrState); |
| DPRINTF(PowerDomain, "Most performant leader is %s, at %u\n", |
| most_perf_leader, most_perf_state); |
| |
| // If asked to check the power states of the followers (f_states contains |
| // the power states of the followers) |
| if (!f_states.empty()) { |
| for (Enums::PwrState f_pw : f_states ) { |
| // Ignore UNDEFINED state of follower, at startup the followers |
| // might be in the UNDEFINED state, PowerDomain will pull them up |
| if ((f_pw != Enums::PwrState::UNDEFINED) && |
| (f_pw < most_perf_state)) { |
| most_perf_state = f_pw; |
| } |
| } |
| DPRINTF(PowerDomain, "Most performant state, including followers " |
| "is %u\n", most_perf_state); |
| } |
| return most_perf_state; |
| } |
| |
| void |
| PowerDomain::setFollowerPowerStates() |
| { |
| // Loop over all followers and tell them to change their power state so |
| // they match that of the power domain (or a more performant power state) |
| std::vector<Enums::PwrState> matched_states; |
| for (auto follower : followers) { |
| Enums::PwrState actual_pws = |
| follower->matchPwrState(leaderTargetState); |
| matched_states.push_back(actual_pws); |
| assert(actual_pws <= leaderTargetState); |
| DPRINTF(PowerDomain, "%u matched domain power state (%u) with %u\n", |
| follower->name(), leaderTargetState, |
| actual_pws); |
| } |
| // Now the power states of the follower have been changed recalculate the |
| // power state of the domain as a whole, including followers |
| Enums::PwrState new_power_state = |
| calculatePowerDomainState(matched_states); |
| if (new_power_state != _currState) { |
| // Change in power state of the domain, so update. Updates in power |
| // state need to happen via set() so it can propagate to |
| // overarching power domains (if there are any). |
| DPRINTF(PowerDomain, "Updated power domain state to %u\n", |
| new_power_state); |
| set(new_power_state); |
| } |
| } |
| |
| void |
| PowerDomain::pwrStateChangeCallback(Enums::PwrState new_pwr_state, |
| PowerState* leader) |
| { |
| DPRINTF(PowerDomain, "PwrState update to %u by %s\n", new_pwr_state, |
| leader->name()); |
| |
| Enums::PwrState old_target_state = leaderTargetState; |
| // Calculate the power state of the domain, based on the leaders |
| if (new_pwr_state < _currState) { |
| // The power state of the power domain always needs to match that of |
| // the most performant leader so no need to go over the other leaders |
| // The power state need to be changed via a the PwrStateCall() so any |
| // overarching power domains get informed |
| leaderTargetState = new_pwr_state; |
| } else { |
| // Need to calculate the newly required power state, based on the |
| // leaders only and change to that state. |
| leaderTargetState = calculatePowerDomainState(); |
| } |
| if (old_target_state!= leaderTargetState) { |
| // The followers will try to match that power state requested by the |
| // leaders in in the update event, based upon the actual power state, |
| // we will 'officially' change the power state of the domain by calling |
| // set() |
| schedule(pwrStateUpdateEvent, curTick() + updateLatency); |
| DPRINTF(PowerDomain, "TargetState change from %u to %u, followers will" |
| "be updated in %u ticks\n", old_target_state, |
| leaderTargetState, updateLatency); |
| stats.numLeaderCallsChangingState++; |
| } |
| stats.numLeaderCalls++; |
| } |
| |
| PowerDomain::PowerDomainStats::PowerDomainStats(PowerDomain &pd) |
| : Stats::Group(&pd), |
| ADD_STAT(numLeaderCalls, |
| "Number of calls by leaders to change power domain state"), |
| ADD_STAT(numLeaderCallsChangingState, |
| "Number of calls by leader to change power domain state " |
| "actually resulting in a power state change") |
| { |
| } |
| |
| void |
| PowerDomain::PowerDomainStats::regStats() |
| { |
| Stats::Group::regStats(); |
| |
| numLeaderCalls |
| .flags(Stats::nozero) |
| ; |
| numLeaderCallsChangingState |
| .flags(Stats::nozero) |
| ; |
| } |
| |
| PowerDomain* |
| PowerDomainParams::create() |
| { |
| return new PowerDomain(this); |
| } |