system_type = 34
system_rev = 1 << 10
- boot_cpu_frequency = Param.Frequency(Self.cpu[0].clk_domain.clock.frequency,
+ boot_cpu_frequency = Param.Frequency(Self.cpu[0].clk_domain.clock[0]
+ .frequency,
"boot processor frequency")
class FreebsdAlphaSystem(AlphaSystem):
system_type = 34
system_rev = 1 << 10
- boot_cpu_frequency = Param.Frequency(Self.cpu[0].clk_domain.clock.frequency,
+ boot_cpu_frequency = Param.Frequency(Self.cpu[0].clk_domain.clock[0]
+ .frequency,
"boot processor frequency")
class BareIronMipsSystem(MipsSystem):
-# 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
#
# Authors: Vasileios Spiliopoulos
# Akash Bagdia
+# Stephan Diestelhorst
from m5.params import *
from m5.SimObject import 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):
--- /dev/null
+# 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")
SimObject('ClockDomain.py')
SimObject('VoltageDomain.py')
SimObject('System.py')
+SimObject('DVFSHandler.py')
Source('arguments.cc')
Source('async.cc')
Source('clock_domain.cc')
Source('voltage_domain.cc')
Source('system.cc')
+Source('dvfs_handler.cc')
if env['TARGET_ISA'] != 'null':
SimObject('InstTracer.py')
DebugFlag('WorkItems')
DebugFlag('ClockDomain')
DebugFlag('VoltageDomain')
+DebugFlag('DVFS')
from m5.params import *
from m5.proxy import *
+from DVFSHandler import *
from SimpleMemory import *
class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing',
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()
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)")
/*
- * Copyright (c) 2013 ARM Limited
+ * Copyright (c) 2013-2014 ARM Limited
* Copyright (c) 2013 Cornell University
* All rights reserved
*
* Akash Bagdia
* Andreas Hansson
* Christopher Torng
+ * Stephan Diestelhorst
*/
+#include <algorithm>
+#include <functional>
+
#include "debug/ClockDomain.hh"
#include "params/ClockDomain.hh"
#include "params/DerivedClockDomain.hh"
}
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
}
}
+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()
{
/*
- * Copyright (c) 2013 ARM Limited
+ * Copyright (c) 2013-2014 ARM Limited
* Copyright (c) 2013 Cornell University
* All rights reserved
*
* Authors: Vasileios Spiliopoulos
* Akash Bagdia
* Christopher Torng
+ * Stephan Diestelhorst
*/
/**
*
* @return Clock period in ticks
*/
- inline Tick clockPeriod() const { return _clockPeriod; }
+ Tick clockPeriod() const { return _clockPeriod; }
/**
* Register a ClockedObject to this ClockDomain.
*
* @return Voltage applied to the clock domain
*/
- inline double voltage() const;
+ double voltage() const;
/**
* Add a derived domain.
/**
* 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
{
// 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;
};
/**
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__
/// 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.
/*
- * 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
* 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")
;
{
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);
+}
#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"
/**
*/
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