File: [local] / prex-old / dev / power / dvs.c (download)
Revision 1.1.1.1 (vendor branch), Tue Jun 3 09:38:42 2008 UTC (16 years, 3 months ago) by nbrk
Branch: MAIN, KOHSUKE
CVS Tags: PREX_0_8_BASE, PREX_0_7_BASE, HEAD Changes since 1.1: +0 -0 lines
Yeah, this is an initial import of Prex, portable real-time microkernel
operating system. I wanna hack it for non-profit but fun, so let it in.
|
/*-
* Copyright (c) 2007, Kohsuke Ohtani
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*/
/*
* dvs.c - dynamic voltage scaling feature
*/
/*
* Dynamic voltage scaling (DVS) is widely used with mobile systems
* to save the processor power consumption, with minimum impact on
* performance. The basic idea is come from the fact the power
* consumption is proportional to V^2 x f, where V is voltage and
* f is frequency. Since processor does not always require the full
* performance, we can reduce power consumption by lowering voltage
* and frequeceny.
*/
#include <driver.h>
#include <pm.h>
#include "dvs.h"
/* #define DEBUG_DVS 1 */
#ifdef DEBUG_DVS
#define dvs_printf(fmt, args...) printk("dvs: " fmt, ## args)
#else
#define dvs_printf(fmt...) do {} while (0)
#endif
#define INTERVAL_MSEC 50
#define INTERVAL_TICK msec_to_tick(INTERVAL_MSEC)
#define WEIGHT 20
static struct dpc dvs_dpc; /* DVC object */
static int dvs_capable; /* True if the system has dvs capability */
static int dvs_enabled; /* True if dvs is enabled */
static int cur_speed; /* Current CPU speed (%) */
static int max_speed; /* Maximum CPU speed (%) */
static int min_speed; /* Minimum CPU speed (%) */
static int run_cycles; /* The non-idle CPU cycles in the last interval */
static int idle_cycles; /* The idle CPU cycles in the last interval */
static int excess_cycles; /* The cycles left over from the last interval */
static int avg_workload; /* Average workload */
static int avg_deadline; /* Average deadline */
static u_long elapsed_ticks;
/*
* Predict CPU speed
*
* DVS Algorithm: Weiser Style
*
* If the utilization prediction x is high (over 70%), increase the speed
* by 20% of the maximum speed. If the utilization prediction is low
* (under 50%), decrease the speed by (60 - x)% of the maximum speed.
*
* excess_cycles is defined as the number of uncompleted run cycles from
* the last interval. For example, if we find 70% activity when runnig
* at full speed, and their processor speed was set to 50% during that
* interval, excess_cycles is set to 20%. This value (20%) is used to
* calculate the processor speed in the next interval.
*
* Refernce:
* M.Weiser, B.Welch, A.Demers, and S.Shenker,
* "Scheduling for Reduced CPU Energy", In Proceedings of the
* 1st Symposium on Operating Systems Design and Implementation,
* pages 13-23, November 1994.
*/
static int
predict_cpu_speed(int speed)
{
int next_excess;
int run_percent;
int newspeed = speed;
run_cycles += excess_cycles;
run_percent = (run_cycles * 100) / (idle_cycles + run_cycles);
next_excess = run_cycles - speed * (run_cycles + idle_cycles) / 100;
if (next_excess < 0)
next_excess = 0;
if (excess_cycles > idle_cycles)
newspeed = 100;
else if (run_percent > 70)
newspeed = speed + 20;
else if (run_percent < 50)
newspeed = speed - (60 - run_percent);
if (newspeed > max_speed)
newspeed = max_speed;
if (newspeed < min_speed)
newspeed = min_speed;
dvs_printf("run_percent=%d next_excess=%d newspeed=%d\n\n",
run_percent, next_excess, newspeed);
excess_cycles = next_excess;
return newspeed;
}
/*
* Predict max CPU speed
*
* DVS Algorithm: AVG<3>
*
* Computes an exponentially moving average of the previous intervals.
* <wight> is the relative wighting of past intervals relative to
* the current interval.
*
* predict = (weight x current + past) / (weight + 1)
*
* Refernce:
* K.Govil, E.Chan, H.Wasserman,
* "Comparing Algorithm for Dynamic Speed-Setting of a Low-Power CPU".
* Proc. 1st Int'l Conference on Mobile Computing and Networking, Nov 1995.
*/
static int
predict_max_speed(int speed)
{
int new_workload;
int new_deadline;
int newspeed;
new_workload = run_cycles * speed;
new_deadline = (run_cycles + idle_cycles) * speed;
avg_workload = (avg_workload * WEIGHT + new_workload) / (WEIGHT + 1);
avg_deadline = (avg_deadline * WEIGHT + new_deadline) / (WEIGHT + 1);
newspeed = avg_workload * 100 / avg_deadline;
dvs_printf("new_workload=%u new_deadline=%u\n",
new_workload, new_deadline);
dvs_printf("avg_workload=%u avg_deadline=%u\n",
avg_workload, avg_deadline);
return newspeed;
}
/*
* DPC routine to set CPU speed.
*/
static void
dpc_adjust_speed(void *arg)
{
int newspeed = (int)arg;
dvs_printf("dvs_adjust_speed: speed=%d\n", newspeed);
cpu_setperf(newspeed);
cur_speed = cpu_getperf();
}
/*
* Timer hook routine called by tick handler.
*/
static void
dvs_tick(int idle)
{
int newspeed;
elapsed_ticks++;
if (idle)
idle_cycles++;
else
run_cycles++;
if (elapsed_ticks < INTERVAL_TICK)
return;
/* Predict max CPU speed */
max_speed = predict_max_speed(cur_speed);
dvs_printf("DVS: run_cycles=%d idle_cycles=%d cur_speed=%d max_speed=%d\n",
run_cycles, idle_cycles, cur_speed, max_speed);
/*
* Predict next CPU speed
*/
newspeed = predict_cpu_speed(cur_speed);
if (newspeed != cur_speed) {
sched_dpc(&dvs_dpc, dpc_adjust_speed,
(void *)newspeed);
}
run_cycles = 0;
idle_cycles = 0;
elapsed_ticks = 0;
}
/*
* Enable DVS operation
*/
void
dvs_enable(void)
{
if (!dvs_capable)
return;
run_cycles = 0;
idle_cycles = 0;
elapsed_ticks = 0;
max_speed = 100; /* max 100% */
min_speed = 5; /* min 5% */
cur_speed = cpu_getperf();
timer_hook(dvs_tick);
dvs_enabled = 1;
}
/*
* Disable DVS operation
*/
void
dvs_disable(void)
{
if (!dvs_capable)
return;
timer_hook(NULL);
/* Set CPU speed to 100% */
cpu_setperf(100);
dvs_enabled = 0;
}
/*
* Initialize dvs
*/
void
dvs_init(void)
{
if (cpu_initperf())
return;
dvs_capable = 1;
}