diff options
Diffstat (limited to 'src/sim')
-rw-r--r-- | src/sim/ClockDomain.py | 23 | ||||
-rw-r--r-- | src/sim/DVFSHandler.py | 68 | ||||
-rw-r--r-- | src/sim/SConscript | 3 | ||||
-rw-r--r-- | src/sim/System.py | 5 | ||||
-rw-r--r-- | src/sim/VoltageDomain.py | 11 | ||||
-rw-r--r-- | src/sim/clock_domain.cc | 72 | ||||
-rw-r--r-- | src/sim/clock_domain.hh | 91 | ||||
-rw-r--r-- | src/sim/dvfs_handler.cc | 216 | ||||
-rw-r--r-- | src/sim/dvfs_handler.hh | 230 | ||||
-rw-r--r-- | src/sim/eventq.hh | 4 | ||||
-rw-r--r-- | src/sim/voltage_domain.cc | 92 | ||||
-rw-r--r-- | src/sim/voltage_domain.hh | 82 |
12 files changed, 857 insertions, 40 deletions
diff --git a/src/sim/ClockDomain.py b/src/sim/ClockDomain.py index 2a3b6addf..4d2b24914 100644 --- a/src/sim/ClockDomain.py +++ b/src/sim/ClockDomain.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013 ARM Limited +# Copyright (c) 2013-2014 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -35,6 +35,7 @@ # # Authors: Vasileios Spiliopoulos # Akash Bagdia +# Stephan Diestelhorst from m5.params import * from m5.SimObject import SimObject @@ -46,15 +47,31 @@ class ClockDomain(SimObject): cxx_header = "sim/clock_domain.hh" abstract = True -# Source clock domain with an actual clock +# Source clock domain with an actual clock, and a list of voltage and frequency +# op points class SrcClockDomain(ClockDomain): type = 'SrcClockDomain' cxx_header = "sim/clock_domain.hh" - clock = Param.Clock("Clock period") + + # Single clock frequency value, or list of frequencies for DVFS + # Frequencies must be ordered in descending order + # Note: Matching voltages should be defined in the voltage domain + clock = VectorParam.Clock("Clock period") # A source clock must be associated with a voltage domain voltage_domain = Param.VoltageDomain("Voltage domain") + # Domain ID is an identifier for the DVFS domain as understood by the + # necessary control logic (either software or hardware). For example, in + # case of software control via cpufreq framework the IDs should correspond + # to the neccessary identifier in the device tree blob which is interpretted + # by the device driver to communicate to the domain controller in hardware. + domain_id = Param.Int32(-1, "domain id") + + # Initial performance level from the list of available operation points + # Defaults to maximum performance + init_perf_level = Param.UInt32(0, "Initial performance level") + # Derived clock domain with a parent clock domain and a frequency # divider class DerivedClockDomain(ClockDomain): diff --git a/src/sim/DVFSHandler.py b/src/sim/DVFSHandler.py new file mode 100644 index 000000000..e6d558852 --- /dev/null +++ b/src/sim/DVFSHandler.py @@ -0,0 +1,68 @@ +# 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 + +from m5.params import * +from m5.SimObject import SimObject +from m5.proxy import * + +# The handler in its current form is design to be centeralized, one per system +# and manages all the source clock domains (SrcClockDomain) it is configured to +# handle. The specific voltage and frequency points are configured per clock +# and voltage domain. +class DVFSHandler(SimObject): + type = 'DVFSHandler' + cxx_header = "sim/dvfs_handler.hh" + + # List of controllable clock domains which in turn reference the appropriate + # voltage domains + domains = VectorParam.SrcClockDomain([], "list of domains") + + # System domain (its clock and voltage) is not controllable + sys_clk_domain = Param.SrcClockDomain(Parent.clk_domain, + "Clk domain in which the handler is instantiated") + + enable = Param.Bool(False, "Enable/Disable the handler") + + # The transition latency depends on how much time the PLLs and voltage + # regualators takes to migrate from current levels to the new level, is + # usally variable and hardware implementation dependent. In order to + # accomodate this effect with ease, we provide a fixed transition latency + # associated with all migrations. Configure this to maximum latency that + # the hardware will take to migratate between any two perforamnce levels. + transition_latency = Param.Latency('100us', + "fixed latency for perf level migration") diff --git a/src/sim/SConscript b/src/sim/SConscript index 769c8240d..5a5c1ab8a 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -36,6 +36,7 @@ SimObject('Root.py') SimObject('ClockDomain.py') SimObject('VoltageDomain.py') SimObject('System.py') +SimObject('DVFSHandler.py') Source('arguments.cc') Source('async.cc') @@ -55,6 +56,7 @@ Source('stat_control.cc') Source('clock_domain.cc') Source('voltage_domain.cc') Source('system.cc') +Source('dvfs_handler.cc') if env['TARGET_ISA'] != 'null': SimObject('InstTracer.py') @@ -86,3 +88,4 @@ DebugFlag('VtoPhys') DebugFlag('WorkItems') DebugFlag('ClockDomain') DebugFlag('VoltageDomain') +DebugFlag('DVFS') diff --git a/src/sim/System.py b/src/sim/System.py index 95162be89..b8f15c209 100644 --- a/src/sim/System.py +++ b/src/sim/System.py @@ -33,6 +33,7 @@ from m5.defines import buildEnv from m5.params import * from m5.proxy import * +from DVFSHandler import * from SimpleMemory import * class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing', @@ -88,3 +89,7 @@ class System(MemObject): load_addr_mask = Param.UInt64(0xffffffffff, "Address to mask loading binaries with") load_offset = Param.UInt64(0, "Address to offset loading binaries with") + + # Dynamic voltage and frequency handler for the system, disabled by default + # Provide list of domains that need to be controlled by the handler + dvfs_handler = DVFSHandler() diff --git a/src/sim/VoltageDomain.py b/src/sim/VoltageDomain.py index ad84d756b..d6ccf9979 100644 --- a/src/sim/VoltageDomain.py +++ b/src/sim/VoltageDomain.py @@ -42,6 +42,11 @@ from m5.params import * class VoltageDomain(SimObject): type = 'VoltageDomain' cxx_header = "sim/voltage_domain.hh" - # We use a default voltage of 1V to avoid forcing users to set it - # even if they are not interested in using the functionality - voltage = Param.Voltage('1V', "Operational voltage") + + # Single or list of voltages for the voltage domain. If only a single + # voltage is specified, it is used for all different frequencies. + # Otherwise, the number of specified voltges and frequencies in the clock + # domain (src/sim/ClockDomain.py) must match. Voltages must be specified in + # descending order. We use a default voltage of 1V to avoid forcing users to + # set it even if they are not interested in using the functionality + voltage = VectorParam.Voltage('1V', "Operational voltage(s)") diff --git a/src/sim/clock_domain.cc b/src/sim/clock_domain.cc index 0d1836e83..746ef12fd 100644 --- a/src/sim/clock_domain.cc +++ b/src/sim/clock_domain.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 ARM Limited + * Copyright (c) 2013-2014 ARM Limited * Copyright (c) 2013 Cornell University * All rights reserved * @@ -39,8 +39,12 @@ * Akash Bagdia * Andreas Hansson * Christopher Torng + * Stephan Diestelhorst */ +#include <algorithm> +#include <functional> + #include "debug/ClockDomain.hh" #include "params/ClockDomain.hh" #include "params/DerivedClockDomain.hh" @@ -70,9 +74,37 @@ ClockDomain::voltage() const } SrcClockDomain::SrcClockDomain(const Params *p) : - ClockDomain(p, p->voltage_domain) + ClockDomain(p, p->voltage_domain), + freqOpPoints(p->clock), + _domainID(p->domain_id), + _perfLevel(p->init_perf_level) { - clockPeriod(p->clock); + VoltageDomain *vdom = p->voltage_domain; + + fatal_if(freqOpPoints.empty(), "DVFS: Empty set of frequencies for "\ + "domain %d %s\n", _domainID, name()); + + fatal_if(!vdom, "DVFS: Empty voltage domain specified for "\ + "domain %d %s\n", _domainID, name()); + + fatal_if((vdom->numVoltages() > 1) && + (vdom->numVoltages() != freqOpPoints.size()), + "DVFS: Number of frequency and voltage scaling points do "\ + "not match: %d:%d ID: %d %s.\n", vdom->numVoltages(), + freqOpPoints.size(), _domainID, name()); + + // Frequency (& voltage) points should be declared in descending order, + // NOTE: Frequency is inverted to ticks, so checking for ascending ticks + fatal_if(!std::is_sorted(freqOpPoints.begin(), freqOpPoints.end()), + "DVFS: Frequency operation points not in descending order for "\ + "domain with ID %d\n", _domainID); + + fatal_if(_perfLevel >= freqOpPoints.size(), "DVFS: Initial DVFS point %d "\ + "is outside of list for Domain ID: %d\n", _perfLevel, _domainID); + + clockPeriod(freqOpPoints[_perfLevel]); + + vdom->registerSrcClockDom(this); } void @@ -99,6 +131,40 @@ SrcClockDomain::clockPeriod(Tick clock_period) } } +void +SrcClockDomain::perfLevel(PerfLevel perf_level) +{ + assert(validPerfLevel(perf_level)); + + DPRINTF(ClockDomain, "DVFS: Switching performance level of domain %s "\ + "(id: %d) from %d to %d\n", name(), domainID(), _perfLevel, + perf_level); + + _perfLevel = perf_level; + + // Signal the voltage domain that we have changed our perf level so that the + // voltage domain can recompute its performance level + voltageDomain()->sanitiseVoltages(); + + // Integrated switching of the actual clock value, too + clockPeriod(clkPeriodAtPerfLevel()); +} + +void +SrcClockDomain::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(_perfLevel); + ClockDomain::serialize(os); +} + +void +SrcClockDomain::unserialize(Checkpoint *cp, const std::string §ion) +{ + ClockDomain::unserialize(cp, section); + UNSERIALIZE_SCALAR(_perfLevel); + perfLevel(_perfLevel); +} + SrcClockDomain * SrcClockDomainParams::create() { diff --git a/src/sim/clock_domain.hh b/src/sim/clock_domain.hh index b597b6611..946e5bd27 100644 --- a/src/sim/clock_domain.hh +++ b/src/sim/clock_domain.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 ARM Limited + * Copyright (c) 2013-2014 ARM Limited * Copyright (c) 2013 Cornell University * All rights reserved * @@ -38,6 +38,7 @@ * Authors: Vasileios Spiliopoulos * Akash Bagdia * Christopher Torng + * Stephan Diestelhorst */ /** @@ -119,7 +120,7 @@ class ClockDomain : public SimObject * * @return Clock period in ticks */ - inline Tick clockPeriod() const { return _clockPeriod; } + Tick clockPeriod() const { return _clockPeriod; } /** * Register a ClockedObject to this ClockDomain. @@ -146,7 +147,7 @@ class ClockDomain : public SimObject * * @return Voltage applied to the clock domain */ - inline double voltage() const; + double voltage() const; /** * Add a derived domain. @@ -161,7 +162,11 @@ class ClockDomain : public SimObject /** * The source clock domains provides the notion of a clock domain that is * connected to a tunable clock source. It maintains the clock period and - * provides methods for setting/getting the clock. + * provides methods for setting/getting the clock and configuration parameters + * for clock domain that handler is going to manage. This includes frequency + * values at various performance levels, domain id, and current performance + * level. Note that a performance level as requested by the software corresponds + * to one of the frequency operational points the domain can operate at. */ class SrcClockDomain : public ClockDomain { @@ -179,6 +184,84 @@ class SrcClockDomain : public ClockDomain // Explicitly import the otherwise hidden clockPeriod using ClockDomain::clockPeriod; + + typedef int32_t DomainID; + static const DomainID emptyDomainID = -1; + + /** + * @return the domainID of the domain + */ + uint32_t domainID() const { return _domainID; } + + typedef uint32_t PerfLevel; + /** + * Checks whether the performance level requested exists in the current + * domain configuration + * + * @param the target performance level of the domain + * + * @return validity status of the given performance level + */ + bool validPerfLevel(PerfLevel perf_level) const { + return perf_level < numPerfLevels(); + } + + /** + * Sets the current performance level of the domain + * + * @param perf_level the target performance level + */ + void perfLevel(PerfLevel perf_level); + + /** + * @return the current performance level of the domain + */ + PerfLevel perfLevel() const { return _perfLevel; } + + /** + * Get the number of available performance levels for this clock domain. + * + * @return Number of perf levels configured for this domain. + */ + PerfLevel numPerfLevels() const {return freqOpPoints.size();} + + /** + * @returns the clock period (expressed in ticks) for the current + * performance level + */ + Tick clkPeriodAtPerfLevel() const { return freqOpPoints[perfLevel()]; } + + Tick clkPeriodAtPerfLevel(PerfLevel perf_level) const + { + assert(validPerfLevel(perf_level)); + return freqOpPoints[perf_level]; + } + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + private: + /** + * List of possible frequency operational points, should be in + * descending order + * An empty list corresponds to default frequency specified for its + * clock domain, overall implying NO DVFS + */ + const std::vector<Tick> freqOpPoints; + + /** + * Software recognizable id number for the domain, should be unique for + * each domain + */ + const uint32_t _domainID; + + /** + * Current performance level the domain is set to. + * The performance level corresponds to one selected frequency (and related + * voltage) from the supplied list of frequencies, with perfLevel = 0 being + * the fastest performance state. + */ + PerfLevel _perfLevel; }; /** diff --git a/src/sim/dvfs_handler.cc b/src/sim/dvfs_handler.cc new file mode 100644 index 000000000..bb60b1850 --- /dev/null +++ b/src/sim/dvfs_handler.cc @@ -0,0 +1,216 @@ +/* + * 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 <set> +#include <utility> + +#include "base/misc.hh" +#include "debug/DVFS.hh" +#include "params/DVFSHandler.hh" +#include "sim/clock_domain.hh" +#include "sim/dvfs_handler.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(); + } + UpdateEvent::dvfsHandler = this; +} + +DVFSHandler *DVFSHandler::UpdateEvent::dvfsHandler; + +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); +} + +void +DVFSHandler::serialize(std::ostream &os) +{ + //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 (auto it = updatePerfLevelEvents.begin(); + it != updatePerfLevelEvents.end(); ++it) { + DomainID id = it->first; + UpdateEvent *event = &it->second; + + assert(id == event->domainIDToSet); + domain_ids.push_back(id); + perf_levels.push_back(event->perfLevelToSet); + whens.push_back(event->scheduled() ? event->when() : 0); + } + arrayParamOut(os, "domain_ids", domain_ids); + arrayParamOut(os, "perf_levels", perf_levels); + arrayParamOut(os, "whens", whens); +} + +void +DVFSHandler::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(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; + arrayParamIn(cp, section, "domain_ids", domain_ids); + arrayParamIn(cp, section, "perf_levels", perf_levels); + arrayParamIn(cp, section, "whens", 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); +} diff --git a/src/sim/dvfs_handler.hh b/src/sim/dvfs_handler.hh new file mode 100644 index 000000000..ba8ed3e8e --- /dev/null +++ b/src/sim/dvfs_handler.hh @@ -0,0 +1,230 @@ +/* + * 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 + */ + +/** + * @file + * DVFSHandler and DomainConfig class declaration used for managing voltage + * and frequency scaling of the various DVFS domains in the system (with each + * domain having their independent domain configuration information) + */ + + +#ifndef __SIM_DVFS_HANDLER_HH__ +#define __SIM_DVFS_HANDLER_HH__ + +#include <vector> + +#include "params/ClockDomain.hh" +#include "params/DVFSHandler.hh" +#include "params/VoltageDomain.hh" +#include "sim/clock_domain.hh" +#include "sim/eventq.hh" +#include "sim/sim_object.hh" + + +/** + * DVFS Handler class, maintains a list of all the domains it can handle. + * Each entry of that list is an object of the DomainConfig class, and the + * handler uses the methods provided by that class to get access to the + * configuration of each domain. The handler is responsible for setting/getting + * clock periods and voltages from clock/voltage domains. + * The handler acts the bridge between software configurable information + * for each domain as provided to the controller and the hardware + * implementation details for those domains. + */ +class DVFSHandler : public SimObject +{ + public: + typedef DVFSHandlerParams Params; + DVFSHandler(const Params *p); + + typedef SrcClockDomain::DomainID DomainID; + typedef SrcClockDomain::PerfLevel PerfLevel; + + /** + * Check whether a domain ID is known to the handler or not. + * @param domain_id Domain ID to check + * @return Domain ID known to handler? + */ + bool validDomainID(DomainID domain_id) const; + + /** + * Get transition latency to switch between performance levels. + * @return Transition latency + */ + Tick transLatency() const { return _transLatency; } + + /** + * Set a new performance level for the specified domain. The actual update + * will be delayed by transLatency(). + * + * @param domain_id Software visible ID of the domain to be configured + * @param perf_level Requested performance level (0 - fast, >0 slower) + * @return status whether the setting was successful + */ + bool perfLevel(DomainID domain_id, PerfLevel perf_level); + + /** + * Get the current performance level of a domain. While a change request is + * in-flight, will return the current (i.e. old, unmodified) value. + * + * @param domain_id Domain ID to query + * @return Current performance level of the specified domain + */ + PerfLevel perfLevel(DomainID domain_id) const { + assert(isEnabled()); + return findDomain(domain_id)->perfLevel(); + } + + /** + * Read the clock period of the specified domain at the specified + * performance level. + * @param domain_id Domain ID to query + * @param perf_level Performance level of interest + * @return Clock period in ticks for the requested performance level of + * the respective domain + */ + Tick clkPeriodAtPerfLevel(DomainID domain_id, PerfLevel perf_level) const + { + return findDomain(domain_id)->clkPeriodAtPerfLevel(perf_level); + } + + /** + * Get the total number of available performance levels. + * + * @param domain_id Domain ID to query + * @return Number of performance levels that where configured for the + * respective domain + */ + PerfLevel numPerfLevels(PerfLevel domain_id) const + { + return findDomain(domain_id)->numPerfLevels(); + } + + /** + * Check enable status of the DVFS handler, when the handler is disabled, no + * request should be sent to the handler. + * @return True, if the handler is enabled + */ + bool isEnabled() const { return enableHandler; } + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + private: + typedef std::map<DomainID, SrcClockDomain*> Domains; + Domains domains; + + /** + * Clock domain of the system the handler is instantiated. + */ + SrcClockDomain* sysClkDomain; + + /** + * Search for a domain based on the domain ID. + * + * @param domain_id Domain ID to search for + * @return Pointer to the source clock domain with matching ID. + */ + SrcClockDomain *findDomain(DomainID domain_id) const { + auto it = domains.find(domain_id); + panic_if(it == domains.end(), + "DVFS: Could not find a domain for ID %d.\n",domain_id ); + return domains.find(domain_id)->second; + } + + /** + * Disabling the DVFS handler ensures that all the DVFS migration requests + * are ignored. Domains remain at their default frequency and voltage. + */ + bool enableHandler; + + + /** + * This corresponds to the maximum transition latency associated with the + * hardware transitioning from a particular performance level to the other + */ + const Tick _transLatency; + + + + /** + * Update performance level event, encapsulates all the required information + * for a future call to change a domain's performance level. + */ + struct UpdateEvent : public Event { + UpdateEvent() : Event(DVFS_Update_Pri) {} + + /** + * Static pointer to the single DVFS hander for all the update events + */ + static DVFSHandler *dvfsHandler; + + /** + * ID of the domain that will be changed by the in-flight event + */ + DomainID domainIDToSet; + + /** + * Target performance level of the in-flight event + */ + PerfLevel perfLevelToSet; + + /** + * Updates the performance level by modifying the clock and the voltage + * of the associated clocked objects. Gets information from + * domainIDToSet and perfLevelToSet for easier calling through an + * event. + */ + void updatePerfLevel(); + + void process() { updatePerfLevel(); } + }; + + typedef std::map<DomainID, UpdateEvent> UpdatePerfLevelEvents; + /** + * Map from domain IDs -> perf level update events, records in-flight change + * requests per domain ID. + */ + UpdatePerfLevelEvents updatePerfLevelEvents; +}; + +#endif // __SIM_DVFS_HANDLER_HH__ diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index 9b3cd9917..eaefdb2e2 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -144,6 +144,10 @@ class EventBase /// Default is zero for historical reasons. static const Priority Default_Pri = 0; + /// DVFS update event leads to stats dump therefore given a lower priority + /// to ensure all relevant states have been updated + static const Priority DVFS_Update_Pri = 31; + /// Serailization needs to occur before tick events also, so /// that a serialize/unserialize is identical to an on-line /// CPU switch. diff --git a/src/sim/voltage_domain.cc b/src/sim/voltage_domain.cc index 461e672ea..b5673feda 100644 --- a/src/sim/voltage_domain.cc +++ b/src/sim/voltage_domain.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 ARM Limited + * Copyright (c) 2012-2014 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -38,36 +38,93 @@ * Akash Bagdia */ +#include <algorithm> + +#include "base/statistics.hh" #include "debug/VoltageDomain.hh" #include "params/VoltageDomain.hh" #include "sim/sim_object.hh" #include "sim/voltage_domain.hh" VoltageDomain::VoltageDomain(const Params *p) - : SimObject(p), _voltage(0) + : SimObject(p), voltageOpPoints(p->voltage), _perfLevel(0) { - voltage(p->voltage); + fatal_if(voltageOpPoints.empty(), "DVFS: Empty set of voltages for "\ + "voltage domain %s\n", name()); + + // Voltages must be sorted in descending order. + fatal_if(!std::is_sorted(voltageOpPoints.begin(), voltageOpPoints.end(), + std::greater_equal<Voltages::value_type>()), "DVFS: Voltage "\ + "operation points not in descending order for voltage domain "\ + "%s\n", name()); } void -VoltageDomain::voltage(double voltage) +VoltageDomain::perfLevel(PerfLevel perf_level) +{ + chatty_assert(perf_level < voltageOpPoints.size(), + "DVFS: Requested voltage ID %d is outside the known "\ + "range for domain %s.\n", perf_level, name()); + + _perfLevel = perf_level; + + DPRINTF(VoltageDomain, "Setting voltage to %.3fV idx: %d for domain %s\n", + voltage(), perf_level, name()); +} + +bool +VoltageDomain::sanitiseVoltages() { - if (voltage <= 0) { - fatal("Voltage should be greater than zero.\n"); + if (numVoltages() == 1) + return false; + + // Find the highest requested performance level and update the voltage + // domain with it + PerfLevel perf_max = (PerfLevel)-1; + for (auto dit = srcClockChildren.begin(); dit != srcClockChildren.end(); ++dit) { + SrcClockDomain* d = *dit; + chatty_assert(d->voltageDomain() == this, "DVFS: Clock domain %s "\ + "(id: %d) should not be registered with voltage domain "\ + "%s\n", d->name(), d->domainID(), name()); + + PerfLevel perf = d->perfLevel(); + + DPRINTF(VoltageDomain, "DVFS: Clock domain %s (id: %d) requests perf "\ + "level %d\n", d->name(), d->domainID(), perf); + + // NOTE: Descending sort of performance levels: 0 - fast, 5 - slow + if (perf < perf_max) { + DPRINTF(VoltageDomain, "DVFS: Updating max perf level %d -> %d\n", + perf_max, perf); + perf_max = perf; + } } + DPRINTF(VoltageDomain, "DVFS: Setting perf level of voltage domain %s "\ + "from %d to %d.\n", name(), perfLevel(), perf_max); - _voltage = voltage; - DPRINTF(VoltageDomain, - "Setting voltage to %f for domain %s\n", _voltage, name()); + // Set the performance level + if (perf_max != perfLevel()) { + perfLevel(perf_max); + return true; + } else { + return false; + } +} + +void +VoltageDomain::startup() { + bool changed = sanitiseVoltages(); + if (changed) { + warn("DVFS: Perf level for voltage domain %s adapted to "\ + "requested perf levels from source clock domains.\n", name()); + } } void VoltageDomain::regStats() { - using namespace Stats; - currentVoltage - .scalar(_voltage) + .method(this, &VoltageDomain::voltage) .name(params()->name + ".voltage") .desc("Voltage in Volts") ; @@ -78,3 +135,14 @@ VoltageDomainParams::create() { return new VoltageDomain(this); } + +void +VoltageDomain::serialize(std::ostream &os) { + SERIALIZE_SCALAR(_perfLevel); +} + +void +VoltageDomain::unserialize(Checkpoint *cp, const std::string §ion) { + UNSERIALIZE_SCALAR(_perfLevel); + perfLevel(_perfLevel); +} diff --git a/src/sim/voltage_domain.hh b/src/sim/voltage_domain.hh index b2f6715cf..3904c80c9 100644 --- a/src/sim/voltage_domain.hh +++ b/src/sim/voltage_domain.hh @@ -41,8 +41,11 @@ #ifndef __SIM_VOLTAGE_DOMAIN_HH__ #define __SIM_VOLTAGE_DOMAIN_HH__ +#include <vector> + #include "base/statistics.hh" #include "params/VoltageDomain.hh" +#include "sim/clock_domain.hh" #include "sim/sim_object.hh" /** @@ -52,40 +55,89 @@ */ class VoltageDomain : public SimObject { + public: - private: + typedef VoltageDomainParams Params; + VoltageDomain(const Params *p); /** - * The voltage of the domain expressed in Volts + * Get the current voltage. + * + * @return Voltage of the domain */ - double _voltage; + double voltage() const { return voltageOpPoints[_perfLevel]; } + + uint32_t numVoltages() const { return (uint32_t)voltageOpPoints.size(); } + + typedef SrcClockDomain::PerfLevel PerfLevel; /** - * Stat for reporting voltage of the domain + * Set the voltage point of the domain. + * @param Voltage value to be set */ - Stats::Value currentVoltage; + void perfLevel(PerfLevel perf_level); - public: + /** + * Get the voltage point of the domain. + * @param Voltage value to be set + */ + PerfLevel perfLevel() const { return _perfLevel; } - typedef VoltageDomainParams Params; - VoltageDomain(const Params *p); + /** + * Register a SrcClockDomain with this voltage domain. + * @param src_clock_domain The SrcClockDomain to register. + */ + void registerSrcClockDom(SrcClockDomain *src_clock_dom) { + assert(src_clock_dom->voltageDomain() == this); + srcClockChildren.push_back(src_clock_dom); + } /** - * Get the current volate. - * - * @return Voltage of the domain + * Startup has all SrcClockDomains registered with this voltage domain, so + * try to make sure that all perf level requests from them are met. */ - inline double voltage() const { return _voltage; } + void startup(); /** - * Set the voltage of the domain. + * Recomputes the highest (fastest, i.e., numerically lowest) requested + * performance level of all associated clock domains, and updates the + * performance level of this voltage domain to match. This means that for + * two connected clock domains, one fast and one slow, the voltage domain + * will provide the voltage associated with the fast DVFS operation point. + * Must be called whenever a clock domain decides to swtich its performance + * level. * - * @param Voltage value to be set + * @return True, if the voltage was actually changed. */ - void voltage(double voltage); + bool sanitiseVoltages(); void regStats(); + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + private: + typedef std::vector<double> Voltages; + /** + * List of possible minimum voltage at each of the frequency operational + * points, should be in descending order and same size as freqOpPoints. + * An empty list corresponds to default voltage specified for the voltage + * domain its clock domain belongs. The same voltage is applied for each + * freqOpPoints, overall implying NO DVS + */ + const Voltages voltageOpPoints; + PerfLevel _perfLevel; + + /** + * Stat for reporting voltage of the domain + */ + Stats::Value currentVoltage; + + /** + * List of associated SrcClockDomains that are connected to this voltage + * domain. + */ + typedef std::vector<SrcClockDomain *> SrcClockChildren; + SrcClockChildren srcClockChildren; }; #endif |