/* * 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 #include "base/trace.hh" #include "debug/PowerDomain.hh" namespace gem5 { 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 &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 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) : statistics::Group(&pd), ADD_STAT(numLeaderCalls, statistics::units::Count::get(), "Number of calls by leaders to change power domain state"), ADD_STAT(numLeaderCallsChangingState, statistics::units::Count::get(), "Number of calls by leader to change power domain state actually " "resulting in a power state change") { } void PowerDomain::PowerDomainStats::regStats() { statistics::Group::regStats(); numLeaderCalls .flags(statistics::nozero) ; numLeaderCallsChangingState .flags(statistics::nozero) ; } } // namespace gem5