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
This commit is contained in:
Stephan Diestelhorst 2014-06-30 13:56:06 -04:00
parent 641e602830
commit 65cea4708e
14 changed files with 865 additions and 46 deletions

View file

@ -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):

View file

@ -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):

View file

@ -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):

68
src/sim/DVFSHandler.py Normal file
View file

@ -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")

View file

@ -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')

View file

@ -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()

View file

@ -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)")

View file

@ -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 &section)
{
ClockDomain::unserialize(cp, section);
UNSERIALIZE_SCALAR(_perfLevel);
perfLevel(_perfLevel);
}
SrcClockDomain *
SrcClockDomainParams::create()
{

View file

@ -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 &section);
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;
};
/**

216
src/sim/dvfs_handler.cc Normal file
View file

@ -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 &section)
{
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);
}

230
src/sim/dvfs_handler.hh Normal file
View file

@ -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 &section);
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__

View file

@ -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.

View file

@ -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)
{
if (voltage <= 0) {
fatal("Voltage should be greater than zero.\n");
}
chatty_assert(perf_level < voltageOpPoints.size(),
"DVFS: Requested voltage ID %d is outside the known "\
"range for domain %s.\n", perf_level, name());
_voltage = voltage;
DPRINTF(VoltageDomain,
"Setting voltage to %f for domain %s\n", _voltage, name());
_perfLevel = perf_level;
DPRINTF(VoltageDomain, "Setting voltage to %.3fV idx: %d for domain %s\n",
voltage(), perf_level, name());
}
bool
VoltageDomain::sanitiseVoltages()
{
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);
// 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 &section) {
UNSERIALIZE_SCALAR(_perfLevel);
perfLevel(_perfLevel);
}

View file

@ -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
{
private:
/**
* The voltage of the domain expressed in Volts
*/
double _voltage;
/**
* Stat for reporting voltage of the domain
*/
Stats::Value currentVoltage;
public:
typedef VoltageDomainParams Params;
VoltageDomain(const Params *p);
/**
* Get the current volate.
* Get the current voltage.
*
* @return Voltage of the domain
*/
inline double voltage() const { return _voltage; }
double voltage() const { return voltageOpPoints[_perfLevel]; }
uint32_t numVoltages() const { return (uint32_t)voltageOpPoints.size(); }
typedef SrcClockDomain::PerfLevel PerfLevel;
/**
* Set the voltage of the domain.
*
* Set the voltage point of the domain.
* @param Voltage value to be set
*/
void voltage(double voltage);
void perfLevel(PerfLevel perf_level);
/**
* Get the voltage point of the domain.
* @param Voltage value to be set
*/
PerfLevel perfLevel() const { return _perfLevel; }
/**
* 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);
}
/**
* Startup has all SrcClockDomains registered with this voltage domain, so
* try to make sure that all perf level requests from them are met.
*/
void startup();
/**
* 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.
*
* @return True, if the voltage was actually changed.
*/
bool sanitiseVoltages();
void regStats();
void serialize(std::ostream &os);
void unserialize(Checkpoint *cp, const std::string &section);
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