
Attention: currently, MetalSVM didn't support the floating point unit => no using of RCCE_wtime => no using of the data type RCCE_double => RCCE_init expect an integer value as frequency in MHZ
619 lines
22 KiB
C
619 lines
22 KiB
C
//***************************************************************************************
|
|
// Power management (voltage + frequency) routines.
|
|
//***************************************************************************************
|
|
//
|
|
// Author: Rob F. Van der Wijngaart
|
|
// Intel Corporation
|
|
// Date: 12/22/2010
|
|
//
|
|
//***************************************************************************************
|
|
//#define POWER_DEBUG 1
|
|
//
|
|
// Copyright 2010 Intel Corporation
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
#include <asm/RCCE_lib.h>
|
|
#include <asm/SCC_API.h>
|
|
#include <asm/RCCE_lib_pwr.h>
|
|
|
|
#ifdef CONFIG_ROCKCREEK
|
|
|
|
//......................................................................................
|
|
// GLOBAL VARIABLES USED BY THE LIBRARY
|
|
//......................................................................................
|
|
t_vintp frequency_change_virtual_address[RCCE_MAXNP/2]; // one frequency change register per
|
|
// tile, RCCE_MAXNP/2 tiles on chip
|
|
t_vintp RPC_virtual_address; // only one RPC on chip
|
|
|
|
// keep track of frequency domain masters inside voltage domain
|
|
int RCCE_ue_F_masters[4];
|
|
RCCE_COMM RCCE_V_COMM;
|
|
RCCE_COMM RCCE_F_COMM;
|
|
|
|
RCCE_COMM RCCE_P_COMM;
|
|
|
|
#ifndef SCC
|
|
// the following structure is maintained in the MPB of one core, although space for it
|
|
// needs to be claimed on all cores. Interpretation of queue array elements:
|
|
// 0: no core owns this slot
|
|
// (m+1): core m owns this slot
|
|
// -(m+1): core m owns this slot, and is the head of the queue
|
|
// The queue is maintained as a circular buffer with a contiguous (in mod space) set
|
|
// of non-zeroes, representing the RPC priority queue
|
|
RCCE_RPC_REGULATOR *RCCE_RPC_regulator;
|
|
#endif
|
|
|
|
// the following array contains triples of voltage/VID value/max_frequency
|
|
triple RC_V_MHz_cap[] = {
|
|
/* 0 */ {0.7, 0x70, 460},
|
|
/* 1 */ {0.8, 0x80, 598},
|
|
/* 2 */ {0.9, 0x90, 644},
|
|
/* 3 */ {1.0, 0xA0, 748},
|
|
/* 4 */ {1.1, 0xB0, 875},
|
|
/* 5 */ {1.2, 0xC0, 1024},
|
|
/* 6 */ {1.3, 0xD0, 1198}
|
|
};
|
|
|
|
int RCCE_set_power_active = 0;
|
|
int RC_current_voltage_level = RC_DEFAULT_VOLTAGE_LEVEL;
|
|
int RC_current_frequency_divider = RC_DEFAULT_FREQUENCY_DIVIDER;
|
|
|
|
#ifndef SCC
|
|
long long RC_time_at_birth;
|
|
#endif
|
|
|
|
// tile clock change words, assuming constant router ratio of 2
|
|
unsigned int RC_frequency_change_words[][2] = {
|
|
// rtr clock ratio, bits 25:8
|
|
/* NOP */ { 2, 0x00000000},
|
|
/* 1 */ { 2, 0x000038e0},
|
|
/* 2 */ { 2, 0x000070e1},
|
|
/* 3 */ { 2, 0x0000a8e2},
|
|
/* 4 */ { 2, 0x0000e0e3},
|
|
/* 5 */ { 2, 0x000118e4},
|
|
/* 6 */ { 2, 0x000150e5},
|
|
/* 7 */ { 2, 0x000188e6},
|
|
/* 8 */ { 2, 0x0001c0e7},
|
|
/* 9 */ { 2, 0x0001f8e8},
|
|
/* 10 */ { 2, 0x000230e9},
|
|
/* 11 */ { 2, 0x000268ea},
|
|
/* 12 */ { 2, 0x0002a0eb},
|
|
/* 13 */ { 2, 0x0002d8ec},
|
|
/* 14 */ { 2, 0x000310ed},
|
|
/* 15 */ { 2, 0x000348ee},
|
|
/* 16 */ { 2, 0x000380ef}
|
|
};
|
|
|
|
// RCCE_FDOM_masters[VDOM][i] = physical core ID of frequency domain master
|
|
// within RCCE power domain VDOM (logical power domain)
|
|
int RCCE_FDOM_masters[6][4] = {
|
|
{ 0, 2, 12, 14}, { 4, 6, 16, 18}, { 8, 10, 20, 22},
|
|
{24, 26, 36, 38}, {28, 30, 40, 42}, {32, 34, 44, 46}
|
|
};
|
|
|
|
//......................................................................................
|
|
// END GLOBAL VARIABLES USED BY THE LIBRARY
|
|
//......................................................................................
|
|
|
|
#ifndef SCC
|
|
// fake MallocConfigReg; just return the physical address that would be accessed on RCK
|
|
int *MallocConfigReg(unsigned int address) { return (int *)address;}
|
|
#endif
|
|
|
|
static int RCCE_VDOM(int coreID) {
|
|
// back out true tile coordinates, then divide by 2 in both directions (vdoms are 2x2
|
|
// tiles each) and linearize to determine the logical voltage domain. That is not the
|
|
// same as the physical voltage domain
|
|
int x, y;
|
|
x = X_PID(coreID);
|
|
y = Y_PID(coreID);
|
|
|
|
return ((x/2)+(y/2)*3);
|
|
}
|
|
|
|
static int RCCE_FDOM(int coreID) {
|
|
// back out true tile coordinates, then linearize to detemine the logical frequency
|
|
// domain, which equals the logical tile number. That is not the same as the
|
|
// physical tile number
|
|
int x, y;
|
|
x = X_PID(coreID);
|
|
y = Y_PID(coreID);
|
|
return(6*y+x);
|
|
}
|
|
|
|
static int RCCE_voltage_domain(int ue) {
|
|
return(RCCE_VDOM(RC_COREID[ue]));
|
|
}
|
|
|
|
int RCCE_power_domain() {
|
|
return(RCCE_voltage_domain(RCCE_IAM));
|
|
}
|
|
|
|
static int RCCE_frequency_domain(int ue) {
|
|
return(RCCE_FDOM(RC_COREID[ue]));
|
|
}
|
|
|
|
static int RCCE_voltage_domain_master() {
|
|
return(RCCE_V_COMM.member[0]);
|
|
}
|
|
|
|
int RCCE_power_domain_master() {
|
|
return(RCCE_voltage_domain_master());
|
|
}
|
|
int RCCE_power_domain_size() {
|
|
return(RCCE_V_COMM.size);
|
|
}
|
|
|
|
#ifndef SCC
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// SUPPORT FUNCTIONS TO MAINTAIN RPC QUEUE
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int RCCE_RPC_add_self_to_queue() {
|
|
int ue, ueq, error, found_slot;
|
|
t_vcharp RPC_buffer[REGULATOR_LENGTH];
|
|
RCCE_RPC_REGULATOR *privp;
|
|
volatile int *queue;
|
|
|
|
// privp needs to point to space to hold the RPC regulator that is cache lined padded
|
|
privp = (RCCE_RPC_REGULATOR *) RPC_buffer;
|
|
queue = privp->queue;
|
|
error = RCCE_SUCCESS;
|
|
|
|
if (error=RCCE_get((t_vcharp)privp, (t_vcharp)RCCE_RPC_regulator, REGULATOR_LENGTH, RPC_ROOT))
|
|
{
|
|
#ifdef POWER_DEBUG
|
|
printf("UE %d could not copy regulator from MPB\n", RCCE_IAM); fflush(NULL);
|
|
#endif POWER_DEBUG
|
|
return(error);
|
|
}
|
|
else {
|
|
#ifdef POWER_DEBUG
|
|
printf("UE %d read regulator %d, %d, %d, %d, %d, %d, clock %lld\n", RCCE_IAM,
|
|
queue[0], queue[1], queue[2], queue[3], queue[4], queue[5],
|
|
privp->start_time); fflush(NULL);
|
|
#endif
|
|
}
|
|
|
|
// find the head of the queue
|
|
int head = -1;
|
|
for (ue = 0; ue<RC_NUM_VOLTAGE_DOMAINS; ue++) {
|
|
if (head==-1 && queue[ue]<0) {
|
|
head = ue;
|
|
break;
|
|
}
|
|
}
|
|
// if there was no head, the queue was empty; insert calling ue in first slot
|
|
if (head==-1) {
|
|
queue[0] = -(RCCE_IAM+1);
|
|
}
|
|
else {
|
|
// there was a head, so we add calling ue to end of queue
|
|
for (found_slot=0,ue = 1; ue<RC_NUM_VOLTAGE_DOMAINS; ue++) {
|
|
ueq = (head+ue)%(RC_NUM_VOLTAGE_DOMAINS);
|
|
if (queue[ueq]==0) {
|
|
queue[ueq] = RCCE_IAM+1;
|
|
found_slot = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found_slot) {
|
|
error = RCCE_ERROR_RPC_INTERNAL;
|
|
}
|
|
}
|
|
error=RCCE_put((t_vcharp)RCCE_RPC_regulator, (t_vcharp)privp, REGULATOR_LENGTH, RPC_ROOT);
|
|
return(error);
|
|
}
|
|
|
|
static long long RCCE_RPC_remove_self_from_queue_and_reset_start(int *error) {
|
|
int ue;
|
|
t_vcharp RPC_buffer[REGULATOR_LENGTH];
|
|
RCCE_RPC_REGULATOR *privp;
|
|
volatile int *queue;
|
|
|
|
// privp needs to point to space to hold the RPC regulator that is cache lined padded
|
|
privp = (RCCE_RPC_REGULATOR *) RPC_buffer;
|
|
queue = privp->queue;
|
|
*error = RCCE_SUCCESS;
|
|
|
|
RCCE_get((t_vcharp)privp, (t_vcharp)RCCE_RPC_regulator, REGULATOR_LENGTH, RPC_ROOT);
|
|
|
|
// find the head of the queue
|
|
int head = -1;
|
|
for (ue = 0; ue<RC_NUM_VOLTAGE_DOMAINS; ue++) if (head==-1 && queue[ue]<0) {
|
|
head = ue;
|
|
break;
|
|
}
|
|
|
|
// if there was no head, or the head does not match the calling ue, emit error
|
|
if (head==-1 || queue[head] != -(RCCE_IAM+1)) {
|
|
*error = RCCE_ERROR_RPC_INTERNAL;
|
|
return(-1);
|
|
}
|
|
else {
|
|
// found the head of the head of the queue; remove, mark the next queue item as
|
|
// the head, if present, and reset timer
|
|
queue[head] = 0;
|
|
if (queue[(head+1)%(RC_NUM_VOLTAGE_DOMAINS)]>0) {
|
|
queue[(head+1)%(RC_NUM_VOLTAGE_DOMAINS)] *= -1;
|
|
}
|
|
privp->start_time = RC_global_clock();
|
|
}
|
|
RCCE_put((t_vcharp)RCCE_RPC_regulator, (t_vcharp)privp, REGULATOR_LENGTH, RPC_ROOT);
|
|
|
|
// return start time of request just issued, to be used in the RCCE_wait_power() call
|
|
return(privp->start_time);
|
|
}
|
|
|
|
static int RCCE_RPC_my_turn() {
|
|
int ue;
|
|
t_vcharp RPC_buffer[REGULATOR_LENGTH];
|
|
RCCE_RPC_REGULATOR *privp;
|
|
volatile int *queue;
|
|
|
|
// privp needs to point to space to hold the RPC regulator that is cache lined padded
|
|
privp = (RCCE_RPC_REGULATOR *) RPC_buffer;
|
|
queue = privp->queue;
|
|
|
|
RCCE_get((t_vcharp)privp, (t_vcharp)RCCE_RPC_regulator, REGULATOR_LENGTH, RPC_ROOT);
|
|
|
|
// find the head of the queue
|
|
int head = -1;
|
|
for (ue = 0; ue<(RC_NUM_VOLTAGE_DOMAINS); ue++) {
|
|
if (head==-1 && queue[ue]<0) {
|
|
head = ue;
|
|
break;
|
|
}
|
|
}
|
|
// if there was no head, or the head does not match the calling ue, or time has not
|
|
// yet come, return failure
|
|
if (head==-1 || queue[head] != -(RCCE_IAM+1) ||
|
|
RC_global_clock() < privp->start_time + RC_WAIT_CYCLES) return(0);
|
|
else return(1);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// END SUPPORT FUNCTIONS TO MAINTAIN RPC QUEUE
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
#endif
|
|
|
|
|
|
// when setting new voltage, model competition for RPC by maintaining short queue of RPC
|
|
// service requests, and by blocking the calling core if the queue is not empty
|
|
static long long RC_set_voltage(int domain, int Vlevel, int *error){
|
|
unsigned int vidwd;
|
|
long long start_time = 0;
|
|
int ready_confirmation;
|
|
|
|
//we only maintain the RPC queue on the simulator, and use the SCC auto-block feature
|
|
//for multiple outstanding RPC requests to make sure the target voltage is reached
|
|
#ifndef SCC
|
|
|
|
// because there can only be one power change request in flight at any time for each
|
|
// core, we know we can blindly add the calling core to the RPC queue; the queue
|
|
// update must be atomic, so we protect it with a lock */
|
|
|
|
RCCE_acquire_lock(RPC_ROOT);
|
|
*error = RCCE_RPC_add_self_to_queue();
|
|
RCCE_release_lock(RPC_ROOT);
|
|
|
|
// block until we have come to the front of the queue AND can start having our RPC
|
|
// request serviced; we keep checking whether we can remove ourself from the queue
|
|
// without acquiring the RPC lock, for efficiency. When it looks like we're ready,
|
|
// we acquire the lock and check again. If we mistakenly thought that our turn had
|
|
// come because we checked the queue in the middle of an update by another core, we
|
|
// can now confirm whether we are truly ready to have our RPC request processed.
|
|
ready_confirmation = 0;
|
|
while (!ready_confirmation) {
|
|
while (!RCCE_RPC_my_turn());
|
|
RCCE_acquire_lock(RPC_ROOT);
|
|
if (ready_confirmation=RCCE_RPC_my_turn()) {
|
|
start_time = RCCE_RPC_remove_self_from_queue_and_reset_start(error);
|
|
}
|
|
RCCE_release_lock(RPC_ROOT);
|
|
}
|
|
|
|
#else
|
|
// FILE *power_file;
|
|
#ifdef POWER_DEBUG
|
|
printf("UE %d trying to write VID word %x (level %d, V %lf) to address %x\n",
|
|
RCCE_IAM, VID_word(Vlevel, domain), Vlevel, RC_V_MHz_cap[Vlevel].volt,
|
|
RPC_virtual_address); fflush(NULL);
|
|
#endif
|
|
*RPC_virtual_address = VID_word(Vlevel, domain);
|
|
*error = RCCE_SUCCESS;
|
|
// char name[50] = "/shared/DEMOS/ECO_Q/power";
|
|
// char postfix[50];
|
|
// sprintf(postfix, "%d", RCCE_power_domain());
|
|
// strcat(name, postfix);
|
|
// printf("Core %d opens file %s and writes %d\n", RCCE_IAM, name, step);
|
|
// power_file = fopen(name,"w");
|
|
// fprintf(power_file, "%d", step);
|
|
// fclose(power_file);
|
|
#endif
|
|
return(start_time);
|
|
}
|
|
|
|
|
|
// return for a given voltage domain the integral value that needs to be written
|
|
// to the RPC to establish a new voltage. Bit 16 needs to be one. Shiftid is the
|
|
// corrected value to be used to identify the physuical voltage domain. Domains
|
|
// 2 and 6 are skipped, these are used for non-tile domains; the domain occupies
|
|
// bits 8:10 in the control value
|
|
int VID_control_value(int voltage_domain) {
|
|
int VID_shift[] = {4,5,7,0,1,3};
|
|
int shiftid = VID_shift[voltage_domain];
|
|
return((1<<16) + (shiftid<<8));
|
|
}
|
|
|
|
// generate the complete word to be written to the RPC
|
|
unsigned int VID_word(int Vlevel, int power_domain) {
|
|
unsigned int cv, vid;
|
|
cv = VID_control_value(power_domain);
|
|
vid = RC_V_MHz_cap[Vlevel].VID;
|
|
return(cv+vid);
|
|
}
|
|
|
|
// voltage level corresponding to the chosen frequency
|
|
int RC_voltage_level(int Fdiv) {
|
|
int Vlevel, found, MHz;
|
|
|
|
MHz = RC_GLOBAL_CLOCK_MHZ/Fdiv;
|
|
found = 0;
|
|
for (Vlevel=0; Vlevel<=RC_NUM_VOLTAGE_LEVELS; Vlevel++)
|
|
if (RC_V_MHz_cap[Vlevel].MHz_cap >= MHz) {found=1; break;}
|
|
if (found) return(Vlevel);
|
|
else return(-1);
|
|
}
|
|
|
|
// generate the complete word to be written to the CRB for tile clock freq
|
|
unsigned int FID_word(int Fdiv, int tile_ID) {
|
|
#ifdef SCC
|
|
unsigned int lower_bits = (*(frequency_change_virtual_address[tile_ID]))&((1<<8)-1);
|
|
#else
|
|
unsigned int lower_bits = 0x0;
|
|
#endif
|
|
return(((RC_frequency_change_words[Fdiv][1])<<8)+lower_bits);
|
|
}
|
|
|
|
// get the complete word from the CRB for tile clock frequency
|
|
int get_divider(int tile_ID) {
|
|
#ifdef SCC
|
|
int step;
|
|
unsigned int word;
|
|
// shift to the right to get the correct bits
|
|
word = (*(frequency_change_virtual_address[tile_ID]))>>8;
|
|
for (step=0; step<=16; step++) {
|
|
if (word==RC_frequency_change_words[step][1]) break;
|
|
}
|
|
if (word==RC_frequency_change_words[step][1]) return(step);
|
|
else return(-1);
|
|
#else
|
|
return(0);
|
|
#endif
|
|
}
|
|
|
|
// print all tile clock dividers
|
|
void print_dividers(void) {
|
|
int tile_ID;
|
|
for (tile_ID=0; tile_ID<24; tile_ID++)
|
|
printf("Clock divider for tile %d is %d\n", tile_ID, get_divider(tile_ID));
|
|
}
|
|
|
|
#ifndef SCC
|
|
long long RC_global_clock(void){
|
|
return(_rdtsc()-RC_time_at_birth);
|
|
}
|
|
#endif
|
|
|
|
// use these color functions for the power domain communicators
|
|
int RCCE_V_color(int rank, void *nothing) {return(RCCE_VDOM(RC_COREID[rank]));}
|
|
int RCCE_F_color(int rank, void *nothing) {return(RCCE_FDOM(RC_COREID[rank]));}
|
|
|
|
int RCCE_init_RPC(int *RC_COREID, int RCCE_IAM, int RCCE_NP) {
|
|
|
|
int ue, tile, i, x, y;
|
|
void *nothing = NULL;
|
|
|
|
// set the frequency domain masters of power domain containing the calling core
|
|
for (tile=0; tile<4; tile++)
|
|
RCCE_ue_F_masters[tile] = RCCE_FDOM_masters[RCCE_VDOM(RC_COREID[RCCE_IAM])][tile];
|
|
|
|
// create communicators that span the local voltage (8 cores) and local frequency
|
|
// domains, respectively
|
|
RCCE_comm_split(RCCE_V_color, nothing, &RCCE_V_COMM);
|
|
RCCE_comm_split(RCCE_F_color, nothing, &RCCE_F_COMM);
|
|
|
|
// copy the voltage domain communicator to the externally visible power communicator
|
|
RCCE_P_COMM = RCCE_V_COMM;
|
|
|
|
// compute virtual addresses of RPC register and tile clock dividers
|
|
RPC_virtual_address = (t_vintp) MallocConfigReg(RPC_PHYSICAL_ADDRESS);
|
|
for (tile=0; tile<RCCE_MAXNP/2; tile++) {
|
|
x = tile%6; y = tile/6;
|
|
frequency_change_virtual_address[tile] =
|
|
(t_vintp) MallocConfigReg(CRB_ADDR(x,y)+TILEDIVIDER);
|
|
}
|
|
|
|
#ifndef SCC
|
|
// space for RPC regulator has been preallocated; while all UEs use the regulator
|
|
// in RPC_ROOT's MPB, they need only compute the offset in their own MPB
|
|
RCCE_RPC_regulator = (RCCE_RPC_REGULATOR *)(RCCE_comm_buffer[RCCE_IAM]-REGULATOR_LENGTH);
|
|
// we do not need to initialize the regulator, zeroes are fine
|
|
|
|
// create semblance of global time (i.e. across different cores): place barrier that
|
|
// roughly synchronizes all cores and store value of local clock as offset
|
|
RCCE_barrier(&RCCE_COMM_WORLD);
|
|
RC_time_at_birth = _rdtsc();
|
|
#endif
|
|
|
|
printf("UE %d, Core ID %d; size of V dom %d is %d, size of F dom %d is %d\n",
|
|
RCCE_IAM, RC_MY_COREID, RCCE_VDOM(RC_COREID[RCCE_IAM]),
|
|
RCCE_V_COMM.size, RCCE_FDOM(RC_COREID[RCCE_IAM]), RCCE_F_COMM.size);
|
|
fflush(NULL);
|
|
return(RCCE_SUCCESS);
|
|
}
|
|
|
|
// this function takes as an input a requested new frequency divider for the
|
|
// globally distributed clock, as applied to all the tiles in the voltage
|
|
// domain containing the calling core. It automatically selects the right
|
|
// minimum voltage so support the new target frequency. The return value is
|
|
// the actual frequency divider applied.
|
|
int RCCE_iset_power(int Fdiv, RCCE_REQUEST *req, int *new_Fdiv, int *new_Vlevel) {
|
|
|
|
// Fdiv: requested clock divider
|
|
int error, ue, Vlevel;
|
|
|
|
// only the domain master executes the actual request
|
|
if (!(RCCE_IAM == RCCE_voltage_domain_master())) return(RCCE_SUCCESS);
|
|
|
|
// Cannot have more than one power stepping in flight
|
|
if (RCCE_set_power_active)
|
|
return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_MULTIPLE_RPC_REQUESTS));
|
|
|
|
if (Fdiv > RC_MAX_FREQUENCY_DIVIDER) Fdiv = RC_MAX_FREQUENCY_DIVIDER;
|
|
else
|
|
if (Fdiv < RC_MIN_FREQUENCY_DIVIDER) Fdiv = RC_MIN_FREQUENCY_DIVIDER;
|
|
*new_Fdiv = Fdiv;
|
|
|
|
// determine the voltage level for the requested frequency divider
|
|
Vlevel = RC_voltage_level(Fdiv);
|
|
*new_Vlevel = Vlevel;
|
|
|
|
// if the voltage could not be adjusted to accommodate the requested frequency
|
|
// divider, exit the function
|
|
if (Vlevel <0) return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_FDIVIDER));
|
|
|
|
req->release = 0;
|
|
req->old_voltage_level = RC_current_voltage_level;
|
|
req->new_voltage_level = Vlevel;
|
|
req->old_frequency_divider = RC_current_frequency_divider;
|
|
req->new_frequency_divider = Fdiv;
|
|
|
|
// if new frequency divider greater than current, adjust frequency immediately;
|
|
// this can always be done safely if the current power state is feasible
|
|
if (req->new_frequency_divider >= req->old_frequency_divider){
|
|
// need to set frequency divider on all tiles of the voltage domain
|
|
RCCE_set_frequency_divider(Fdiv, new_Fdiv);
|
|
RC_current_frequency_divider = req->new_frequency_divider;
|
|
}
|
|
|
|
RCCE_set_power_active = 1;
|
|
req->start_cycle = RC_set_voltage(RCCE_voltage_domain(RCCE_IAM),
|
|
Vlevel, &error);
|
|
|
|
return(RCCE_error_return(RCCE_debug_RPC, error));
|
|
}
|
|
|
|
// set frequency divider on a single tile
|
|
static int RC_set_frequency_divider(int tile_ID, int Fdiv) {
|
|
|
|
// we don't have to do anything for frequency in the emulator
|
|
// printf("UE %d writes FID_word %x to address %x on tile %d\n", RCCE_IAM,
|
|
// (int *)(FID_word(Fdiv, tile_ID)), frequency_change_virtual_address[tile_ID],
|
|
// tile_ID);
|
|
#ifdef SCC
|
|
*(frequency_change_virtual_address[tile_ID]) = FID_word(Fdiv, tile_ID);
|
|
#endif
|
|
|
|
return(RCCE_SUCCESS);
|
|
}
|
|
|
|
int RCCE_wait_power(RCCE_REQUEST *req) {
|
|
|
|
int new_Fdiv, ue, Fdiv;
|
|
|
|
Fdiv = req->new_frequency_divider;
|
|
// only the domain master executes the actual request
|
|
if (!(RCCE_IAM == RCCE_voltage_domain_master())) return(RCCE_SUCCESS);
|
|
|
|
// if no power change request in flight, don't wait
|
|
if (!RCCE_set_power_active) {
|
|
return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_NO_ACTIVE_RPC_REQUEST));
|
|
}
|
|
// unset flag indicating power change is in flight
|
|
RCCE_set_power_active = 0;
|
|
|
|
if (req->release) {
|
|
return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_STALE_RPC_REQUEST));
|
|
}
|
|
|
|
// wait until target voltage has been reached
|
|
RC_wait_voltage(req);
|
|
RC_current_voltage_level = req->new_voltage_level;
|
|
// if we asked for a decrease in the clock divider, apply it now, after the
|
|
// required target voltage has been reached.
|
|
if (req->new_frequency_divider < req->old_frequency_divider) {
|
|
// need to set frequency divider on all tiles of the voltage domain
|
|
RCCE_set_frequency_divider(Fdiv, &new_Fdiv);
|
|
RC_current_frequency_divider = req->new_frequency_divider;
|
|
}
|
|
#ifdef POWER_DEBUG
|
|
printf("UE %d at voltage level %d, frequency divider %d\n",
|
|
RCCE_IAM, RC_current_voltage_level, RC_current_frequency_divider);
|
|
#endif
|
|
req->release = 1;
|
|
|
|
return(RCCE_SUCCESS);
|
|
}
|
|
|
|
int RC_wait_voltage(RCCE_REQUEST *req) {
|
|
|
|
//COULD USE SOME ERROR HANDLING CODE HERE
|
|
#ifndef SCC
|
|
// on the emulator we need to wait for a certain amount of time to elapse
|
|
long long target_cycles = RC_WAIT_CYCLES + req->start_cycle;
|
|
while (RC_global_clock() < target_cycles);
|
|
#else
|
|
// on SCC we simply reissue the RPC command. It will block until the previous
|
|
// command has been processed
|
|
#ifdef POWER_DEBUG
|
|
printf("UE %d writes VID word again\n", RCCE_IAM);
|
|
#endif
|
|
// do it twice, see findings by Nikolias Ioannou
|
|
*RPC_virtual_address = VID_word(req->new_voltage_level, RCCE_voltage_domain(RCCE_IAM));
|
|
*RPC_virtual_address = VID_word(req->new_voltage_level, RCCE_voltage_domain(RCCE_IAM));
|
|
#endif
|
|
|
|
return(RCCE_SUCCESS);
|
|
}
|
|
|
|
int RCCE_set_frequency_divider(int Fdiv, int *new_Fdiv) {
|
|
int ue, Vlevel;
|
|
|
|
// only the domain master executes the actual request
|
|
if (!(RCCE_IAM == RCCE_voltage_domain_master())) return(RCCE_SUCCESS);
|
|
|
|
if (RCCE_set_power_active) {
|
|
return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_MULTIPLE_RPC_REQUESTS));
|
|
}
|
|
|
|
if (Fdiv > RC_MAX_FREQUENCY_DIVIDER) Fdiv = RC_MAX_FREQUENCY_DIVIDER;
|
|
else
|
|
if (Fdiv < RC_MIN_FREQUENCY_DIVIDER) Fdiv = RC_MIN_FREQUENCY_DIVIDER;
|
|
|
|
// check to see if the new frequency divider is valid
|
|
Vlevel = RC_voltage_level(Fdiv);
|
|
if (RC_current_voltage_level < Vlevel || Vlevel < 0)
|
|
return(RCCE_error_return(RCCE_debug_RPC, RCCE_ERROR_FDIVIDER));
|
|
*new_Fdiv = Fdiv;
|
|
|
|
// need to set frequency divider on all tiles of the voltage domain
|
|
for (ue=0; ue<4; ue++) {
|
|
RC_set_frequency_divider(RCCE_FDOM(RCCE_ue_F_masters[ue]), Fdiv);
|
|
}
|
|
return(RCCE_SUCCESS);
|
|
}
|
|
|
|
#endif
|