/* * 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. * * Authors: Vasileios Spiliopoulos * Akash Bagdia * Stephan Diestelhorst */ #include "sim/dvfs_handler.hh" #include #include #include "base/logging.hh" #include "base/trace.hh" #include "debug/DVFS.hh" #include "params/DVFSHandler.hh" #include "sim/clock_domain.hh" #include "sim/eventq_impl.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 domain_ids; std::vector perf_levels; std::vector 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 domain_ids; std::vector perf_levels; std::vector 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; } DVFSHandler* DVFSHandlerParams::create() { return new DVFSHandler(this); }