1/* 2 * Copyright (c) 2013-2014 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Vasileios Spiliopoulos 38 * Akash Bagdia 39 * Stephan Diestelhorst 40 */ 41 42#include "sim/dvfs_handler.hh" 43 44#include <set> 45#include <utility> 46 47#include "base/logging.hh" 48#include "base/trace.hh" 49#include "debug/DVFS.hh" 50#include "params/DVFSHandler.hh" 51#include "sim/clock_domain.hh" 52#include "sim/eventq_impl.hh" 53#include "sim/stat_control.hh" 54#include "sim/voltage_domain.hh" 55 56// 57// 58// DVFSHandler methods implementation 59// 60 61DVFSHandler::DVFSHandler(const Params *p) 62 : SimObject(p), 63 sysClkDomain(p->sys_clk_domain), 64 enableHandler(p->enable), 65 _transLatency(p->transition_latency) 66{ 67 // Check supplied list of domains for sanity and add them to the 68 // domain ID -> domain* hash 69 for (auto dit = p->domains.begin(); dit != p->domains.end(); ++dit) { 70 SrcClockDomain *d = *dit; 71 DomainID domain_id = d->domainID(); 72 73 fatal_if(sysClkDomain == d, "DVFS: Domain config list has a "\ 74 "system clk domain entry"); 75 fatal_if(domain_id == SrcClockDomain::emptyDomainID, 76 "DVFS: Controlled domain %s needs to have a properly "\ 77 " assigned ID.\n", d->name()); 78 79 auto entry = std::make_pair(domain_id, d); 80 bool new_elem = domains.insert(entry).second; 81 fatal_if(!new_elem, "DVFS: Domain %s with ID %d does not have a "\ 82 "unique ID.\n", d->name(), domain_id); 83 84 // Create a dedicated event slot per known domain ID 85 UpdateEvent *event = &updatePerfLevelEvents[domain_id]; 86 event->domainIDToSet = d->domainID(); 87 88 // Add domain ID to the list of domains 89 domainIDList.push_back(d->domainID()); 90 } 91 UpdateEvent::dvfsHandler = this; 92} 93 94DVFSHandler *DVFSHandler::UpdateEvent::dvfsHandler; 95 96DVFSHandler::DomainID 97DVFSHandler::domainID(uint32_t index) const 98{ 99 fatal_if(index >= numDomains(), "DVFS: Requested index out of "\ 100 "bound, max value %d\n", (domainIDList.size() - 1)); 101 102 assert(domains.find(domainIDList[index]) != domains.end()); 103 104 return domainIDList[index]; 105} 106 107bool 108DVFSHandler::validDomainID(DomainID domain_id) const 109{ 110 assert(isEnabled()); 111 // This is ensure that the domain id as requested by the software is 112 // availabe in the handler. 113 if (domains.find(domain_id) != domains.end()) 114 return true; 115 warn("DVFS: invalid domain ID %d, the DVFS handler does not handle this "\ 116 "domain\n", domain_id); 117 return false; 118} 119 120bool 121DVFSHandler::perfLevel(DomainID domain_id, PerfLevel perf_level) 122{ 123 assert(isEnabled()); 124 125 DPRINTF(DVFS, "DVFS: setPerfLevel domain %d -> %d\n", domain_id, perf_level); 126 127 auto d = findDomain(domain_id); 128 if (!d->validPerfLevel(perf_level)) { 129 warn("DVFS: invalid performance level %d for domain ID %d, request "\ 130 "ignored\n", perf_level, domain_id); 131 return false; 132 } 133 134 UpdateEvent *update_event = &updatePerfLevelEvents[domain_id]; 135 // Drop an old DVFS change request once we have established that this is a 136 // reasonable request 137 if (update_event->scheduled()) { 138 DPRINTF(DVFS, "DVFS: Overwriting the previous DVFS event.\n"); 139 deschedule(update_event); 140 } 141 142 update_event->perfLevelToSet = perf_level; 143 144 // State changes that restore to the current state (and / or overwrite a not 145 // yet completed in-flight request) will be squashed 146 if (d->perfLevel() == perf_level) { 147 DPRINTF(DVFS, "DVFS: Ignoring ineffective performance level change "\ 148 "%d -> %d\n", d->perfLevel(), perf_level); 149 return false; 150 } 151 152 // At this point, a new transition will certainly take place -> schedule 153 Tick when = curTick() + _transLatency; 154 DPRINTF(DVFS, "DVFS: Update for perf event scheduled for %ld\n", when); 155 156 schedule(update_event, when); 157 return true; 158} 159 160void 161DVFSHandler::UpdateEvent::updatePerfLevel() 162{ 163 // Perform explicit stats dump for power estimation before performance 164 // level migration 165 Stats::dump(); 166 Stats::reset(); 167 168 // Update the performance level in the clock domain 169 auto d = dvfsHandler->findDomain(domainIDToSet); 170 assert(d->perfLevel() != perfLevelToSet); 171 172 d->perfLevel(perfLevelToSet); 173} 174 175double 176DVFSHandler::voltageAtPerfLevel(DomainID domain_id, PerfLevel perf_level) const 177{ 178 VoltageDomain *d = findDomain(domain_id)->voltageDomain(); 179 assert(d); 180 PerfLevel n = d->numVoltages(); 181 if (perf_level < n) 182 return d->voltage(perf_level); 183 184 // Request outside of the range of the voltage domain 185 if (n == 1) { 186 DPRINTF(DVFS, "DVFS: Request for perf-level %i for single-point "\ 187 "voltage domain %s. Returning voltage at level 0: %.2f "\ 188 "V\n", perf_level, d->name(), d->voltage(0)); 189 // Special case for single point voltage domain -> same voltage for 190 // all points 191 return d->voltage(0); 192 } 193 194 warn("DVFSHandler %s reads illegal voltage level %u from "\ 195 "VoltageDomain %s. Returning 0 V\n", name(), perf_level, d->name()); 196 return 0.; 197} 198 199void 200DVFSHandler::serialize(CheckpointOut &cp) const 201{ 202 //This is to ensure that the handler status is maintained during the 203 //entire simulation run and not changed from command line during checkpoint 204 //and restore 205 SERIALIZE_SCALAR(enableHandler); 206 207 // Pull out the hashed data structure into easy-to-serialise arrays; 208 // ensuring that the data associated with any pending update event is saved 209 std::vector<DomainID> domain_ids; 210 std::vector<PerfLevel> perf_levels; 211 std::vector<Tick> whens; 212 for (const auto &ev_pair : updatePerfLevelEvents) { 213 DomainID id = ev_pair.first; 214 const UpdateEvent *event = &ev_pair.second; 215 216 assert(id == event->domainIDToSet); 217 domain_ids.push_back(id); 218 perf_levels.push_back(event->perfLevelToSet); 219 whens.push_back(event->scheduled() ? event->when() : 0); 220 } 221 SERIALIZE_CONTAINER(domain_ids); 222 SERIALIZE_CONTAINER(perf_levels); 223 SERIALIZE_CONTAINER(whens); 224} 225 226void 227DVFSHandler::unserialize(CheckpointIn &cp) 228{ 229 bool temp = enableHandler; 230 231 UNSERIALIZE_SCALAR(enableHandler); 232 233 if (temp != enableHandler) { 234 warn("DVFS: Forcing enable handler status to unserialized value of %d", 235 enableHandler); 236 } 237 238 // Reconstruct the map of domain IDs and their scheduled events 239 std::vector<DomainID> domain_ids; 240 std::vector<PerfLevel> perf_levels; 241 std::vector<Tick> whens; 242 UNSERIALIZE_CONTAINER(domain_ids); 243 UNSERIALIZE_CONTAINER(perf_levels); 244 UNSERIALIZE_CONTAINER(whens); 245 246 for (size_t i = 0; i < domain_ids.size(); ++i) {; 247 UpdateEvent *event = &updatePerfLevelEvents[domain_ids[i]]; 248 249 event->domainIDToSet = domain_ids[i]; 250 event->perfLevelToSet = perf_levels[i]; 251 252 // Schedule all previously scheduled events 253 if (whens[i]) 254 schedule(event, whens[i]); 255 } 256 UpdateEvent::dvfsHandler = this; 257} 258 259DVFSHandler* 260DVFSHandlerParams::create() 261{ 262 return new DVFSHandler(this); 263} 264