From 65cea4708e2f2f2cb361e12b6385d4bc29618223 Mon Sep 17 00:00:00 2001 From: Stephan Diestelhorst Date: Mon, 30 Jun 2014 13:56:06 -0400 Subject: [PATCH] power: Add basic DVFS support for gem5 Adds DVFS capabilities to gem5, by allowing users to specify lists for frequencies and voltages in SrcClockDomains and VoltageDomains respectively. A separate component, DVFSHandler, provides a small interface to change operating points of the associated domains. Clock domains will be linked to voltage domains and thus allow separate clock, but shared voltage lines. Currently all the valid performance-level updates are performed with a fixed transition latency as specified for the domain. Config file example: ... vd = VoltageDomain(voltage = ['1V','0.95V','0.90V','0.85V']) tsys.cluster1.clk_domain.clock = ['1GHz','700MHz','400MHz','230MHz'] tsys.cluster2.clk_domain.clock = ['1GHz','700MHz','400MHz','230MHz'] tsys.cluster1.clk_domain.domain_id = 0 tsys.cluster2.clk_domain.domain_id = 1 tsys.cluster1.clk_domain.voltage_domain = vd tsys.cluster2.clk_domain.voltage_domain = vd tsys.dvfs_handler.domains = [tsys.cluster1.clk_domain, tsys.cluster2.clk_domain] tsys.dvfs_handler.enable = True --- src/arch/alpha/AlphaSystem.py | 3 +- src/arch/mips/MipsSystem.py | 3 +- src/sim/ClockDomain.py | 23 +++- src/sim/DVFSHandler.py | 68 ++++++++++ src/sim/SConscript | 3 + src/sim/System.py | 5 + src/sim/VoltageDomain.py | 11 +- src/sim/clock_domain.cc | 72 ++++++++++- src/sim/clock_domain.hh | 91 +++++++++++++- src/sim/dvfs_handler.cc | 216 +++++++++++++++++++++++++++++++ src/sim/dvfs_handler.hh | 230 ++++++++++++++++++++++++++++++++++ src/sim/eventq.hh | 4 + src/sim/voltage_domain.cc | 92 ++++++++++++-- src/sim/voltage_domain.hh | 82 +++++++++--- 14 files changed, 861 insertions(+), 42 deletions(-) create mode 100644 src/sim/DVFSHandler.py create mode 100644 src/sim/dvfs_handler.cc create mode 100644 src/sim/dvfs_handler.hh diff --git a/src/arch/alpha/AlphaSystem.py b/src/arch/alpha/AlphaSystem.py index cc8e453b1..5e4822f00 100644 --- a/src/arch/alpha/AlphaSystem.py +++ b/src/arch/alpha/AlphaSystem.py @@ -45,7 +45,8 @@ class LinuxAlphaSystem(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 FreebsdAlphaSystem(AlphaSystem): diff --git a/src/arch/mips/MipsSystem.py b/src/arch/mips/MipsSystem.py index 4605b21a7..58e30f28d 100644 --- a/src/arch/mips/MipsSystem.py +++ b/src/arch/mips/MipsSystem.py @@ -50,7 +50,8 @@ class LinuxMipsSystem(MipsSystem): 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): 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 +#include + #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 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 +#include + +#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 domain_ids; + std::vector perf_levels; + std::vector 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 domain_ids; + std::vector perf_levels; + std::vector 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 + +#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 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 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 + +#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()), "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 + #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 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 SrcClockChildren; + SrcClockChildren srcClockChildren; }; #endif -- 2.30.2