| /* |
| * Copyright (c) 2013-2014 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/dvfs_handler.hh" |
| |
| #include <set> |
| #include <utility> |
| |
| #include "base/trace.hh" |
| #include "debug/DVFS.hh" |
| #include "params/DVFSHandler.hh" |
| #include "sim/serialize.hh" |
| #include "sim/stat_control.hh" |
| #include "sim/voltage_domain.hh" |
| |
| // |
| // |
| // DVFSHandler methods implementation |
| // |
| |
| DVFSHandler::DVFSHandler(const Params &p) |
| : SimObject(p), |
| sysClkDomain(p.sys_clk_domain), |
| enableHandler(p.enable), |
| _transLatency(p.transition_latency) |
| { |
| // Check supplied list of domains for sanity and add them to the |
| // domain ID -> domain* hash |
| for (auto dit = p.domains.begin(); dit != p.domains.end(); ++dit) { |
| SrcClockDomain *d = *dit; |
| DomainID domain_id = d->domainID(); |
| |
| fatal_if(sysClkDomain == d, "DVFS: Domain config list has a "\ |
| "system clk domain entry"); |
| fatal_if(domain_id == SrcClockDomain::emptyDomainID, |
| "DVFS: Controlled domain %s needs to have a properly "\ |
| " assigned ID.\n", d->name()); |
| |
| auto entry = std::make_pair(domain_id, d); |
| bool new_elem = domains.insert(entry).second; |
| fatal_if(!new_elem, "DVFS: Domain %s with ID %d does not have a "\ |
| "unique ID.\n", d->name(), domain_id); |
| |
| // Create a dedicated event slot per known domain ID |
| UpdateEvent *event = &updatePerfLevelEvents[domain_id]; |
| event->domainIDToSet = d->domainID(); |
| |
| // Add domain ID to the list of domains |
| domainIDList.push_back(d->domainID()); |
| } |
| UpdateEvent::dvfsHandler = this; |
| } |
| |
| DVFSHandler *DVFSHandler::UpdateEvent::dvfsHandler; |
| |
| DVFSHandler::DomainID |
| DVFSHandler::domainID(uint32_t index) const |
| { |
| fatal_if(index >= numDomains(), "DVFS: Requested index out of "\ |
| "bound, max value %d\n", (domainIDList.size() - 1)); |
| |
| assert(domains.find(domainIDList[index]) != domains.end()); |
| |
| return domainIDList[index]; |
| } |
| |
| bool |
| DVFSHandler::validDomainID(DomainID domain_id) const |
| { |
| assert(isEnabled()); |
| // This is ensure that the domain id as requested by the software is |
| // availabe in the handler. |
| if (domains.find(domain_id) != domains.end()) |
| return true; |
| warn("DVFS: invalid domain ID %d, the DVFS handler does not handle this "\ |
| "domain\n", domain_id); |
| return false; |
| } |
| |
| bool |
| DVFSHandler::perfLevel(DomainID domain_id, PerfLevel perf_level) |
| { |
| assert(isEnabled()); |
| |
| DPRINTF(DVFS, "DVFS: setPerfLevel domain %d -> %d\n", domain_id, perf_level); |
| |
| auto d = findDomain(domain_id); |
| if (!d->validPerfLevel(perf_level)) { |
| warn("DVFS: invalid performance level %d for domain ID %d, request "\ |
| "ignored\n", perf_level, domain_id); |
| return false; |
| } |
| |
| UpdateEvent *update_event = &updatePerfLevelEvents[domain_id]; |
| // Drop an old DVFS change request once we have established that this is a |
| // reasonable request |
| if (update_event->scheduled()) { |
| DPRINTF(DVFS, "DVFS: Overwriting the previous DVFS event.\n"); |
| deschedule(update_event); |
| } |
| |
| update_event->perfLevelToSet = perf_level; |
| |
| // State changes that restore to the current state (and / or overwrite a not |
| // yet completed in-flight request) will be squashed |
| if (d->perfLevel() == perf_level) { |
| DPRINTF(DVFS, "DVFS: Ignoring ineffective performance level change "\ |
| "%d -> %d\n", d->perfLevel(), perf_level); |
| return false; |
| } |
| |
| // At this point, a new transition will certainly take place -> schedule |
| Tick when = curTick() + _transLatency; |
| DPRINTF(DVFS, "DVFS: Update for perf event scheduled for %ld\n", when); |
| |
| schedule(update_event, when); |
| return true; |
| } |
| |
| void |
| DVFSHandler::UpdateEvent::updatePerfLevel() |
| { |
| // Perform explicit stats dump for power estimation before performance |
| // level migration |
| Stats::dump(); |
| Stats::reset(); |
| |
| // Update the performance level in the clock domain |
| auto d = dvfsHandler->findDomain(domainIDToSet); |
| assert(d->perfLevel() != perfLevelToSet); |
| |
| d->perfLevel(perfLevelToSet); |
| } |
| |
| double |
| DVFSHandler::voltageAtPerfLevel(DomainID domain_id, PerfLevel perf_level) const |
| { |
| VoltageDomain *d = findDomain(domain_id)->voltageDomain(); |
| assert(d); |
| PerfLevel n = d->numVoltages(); |
| if (perf_level < n) |
| return d->voltage(perf_level); |
| |
| // Request outside of the range of the voltage domain |
| if (n == 1) { |
| DPRINTF(DVFS, "DVFS: Request for perf-level %i for single-point "\ |
| "voltage domain %s. Returning voltage at level 0: %.2f "\ |
| "V\n", perf_level, d->name(), d->voltage(0)); |
| // Special case for single point voltage domain -> same voltage for |
| // all points |
| return d->voltage(0); |
| } |
| |
| warn("DVFSHandler %s reads illegal voltage level %u from "\ |
| "VoltageDomain %s. Returning 0 V\n", name(), perf_level, d->name()); |
| return 0.; |
| } |
| |
| void |
| DVFSHandler::serialize(CheckpointOut &cp) const |
| { |
| //This is to ensure that the handler status is maintained during the |
| //entire simulation run and not changed from command line during checkpoint |
| //and restore |
| SERIALIZE_SCALAR(enableHandler); |
| |
| // Pull out the hashed data structure into easy-to-serialise arrays; |
| // ensuring that the data associated with any pending update event is saved |
| std::vector<DomainID> domain_ids; |
| std::vector<PerfLevel> perf_levels; |
| std::vector<Tick> whens; |
| for (const auto &ev_pair : updatePerfLevelEvents) { |
| DomainID id = ev_pair.first; |
| const UpdateEvent *event = &ev_pair.second; |
| |
| assert(id == event->domainIDToSet); |
| domain_ids.push_back(id); |
| perf_levels.push_back(event->perfLevelToSet); |
| whens.push_back(event->scheduled() ? event->when() : 0); |
| } |
| SERIALIZE_CONTAINER(domain_ids); |
| SERIALIZE_CONTAINER(perf_levels); |
| SERIALIZE_CONTAINER(whens); |
| } |
| |
| void |
| DVFSHandler::unserialize(CheckpointIn &cp) |
| { |
| bool temp = enableHandler; |
| |
| UNSERIALIZE_SCALAR(enableHandler); |
| |
| if (temp != enableHandler) { |
| warn("DVFS: Forcing enable handler status to unserialized value of %d", |
| enableHandler); |
| } |
| |
| // Reconstruct the map of domain IDs and their scheduled events |
| std::vector<DomainID> domain_ids; |
| std::vector<PerfLevel> perf_levels; |
| std::vector<Tick> whens; |
| UNSERIALIZE_CONTAINER(domain_ids); |
| UNSERIALIZE_CONTAINER(perf_levels); |
| UNSERIALIZE_CONTAINER(whens); |
| |
| for (size_t i = 0; i < domain_ids.size(); ++i) {; |
| UpdateEvent *event = &updatePerfLevelEvents[domain_ids[i]]; |
| |
| event->domainIDToSet = domain_ids[i]; |
| event->perfLevelToSet = perf_levels[i]; |
| |
| // Schedule all previously scheduled events |
| if (whens[i]) |
| schedule(event, whens[i]); |
| } |
| UpdateEvent::dvfsHandler = this; |
| } |