summaryrefslogtreecommitdiffstats
path: root/src/occ_405/wof/wof.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/occ_405/wof/wof.c')
-rw-r--r--src/occ_405/wof/wof.c635
1 files changed, 580 insertions, 55 deletions
diff --git a/src/occ_405/wof/wof.c b/src/occ_405/wof/wof.c
index b0f7457..08a204b 100644
--- a/src/occ_405/wof/wof.c
+++ b/src/occ_405/wof/wof.c
@@ -31,34 +31,71 @@
#include <p9_pstates_occ.h>
#include <occ_service_codes.h>
#include <wof_service_codes.h>
+#include <amec_sys.h>
+#include <occ_sys_config.h>
#include "wof.h"
+//******************************************************************************
+// External Globals
+//******************************************************************************
+extern amec_sys_t g_amec_sys;
+extern OCCPstateParmBlock G_oppb;
+extern GPE_BUFFER(ipcmsg_wof_vfrt_t G_wof_vfrt_parms);
+extern GpeRequest G_wof_vfrt_req;
+extern uint32_t G_pgpe_shared_sram_address;
+extern uint32_t G_pgpe_pstate_table_address;
+extern uint32_t G_pgpe_pstate_table_sz;
//******************************************************************************
// Globals
//******************************************************************************
-uint32_t G_wof_active_quads_sram_addr;
-uint32_t G_wof_tables_main_mem_addr;
-uint32_t G_wof_tables_len;
-uint8_t G_requested_active_quad_update;
-uint8_t G_previous_active_quads;
-bool G_run_wof_main;
uint8_t G_sram_vfrt_ping_buffer[MIN_BCE_REQ_SIZE] __attribute__ ((section(".vfrt_ping_buffer")));
uint8_t G_sram_vfrt_pong_buffer[MIN_BCE_REQ_SIZE] __attribute__ ((section(".vfrt_pong_buffer")));
-uint8_t * G_current_ping_pong_buf;
+
wof_header_data_t G_wof_header __attribute__ ((section (".global_data")));
-uint32_t G_current_vfrt_addr = 0;
+
+// Quad state structs to temporarily hold the data from the doublewords to
+// then populate in amec structure
quad_state0_t G_quad_state_0 = {0};
-quad_state0_t G_quad_state_1 = {0};
+quad_state1_t G_quad_state_1 = {0};
-//******************************************************************************
-// External Globals
-//******************************************************************************
-extern OCCPstateParmBlock G_oppb;
-extern GPE_BUFFER(ipcmsg_wof_vfrt_t G_wof_vfrt_parms);
-extern GpeRequest G_wof_vfrt_req;
-extern uint32_t G_pgpe_shared_sram_address;
+// Create a pointer to amec WOF structure
+amec_wof_t * g_wof = &(g_amec_sys.wof);
+
+// Core IDDQ voltages array (voltages in 100uV)
+uint16_t G_iddq_voltages[CORE_IDDQ_MEASUREMENTS] =
+{
+ 6000,
+ 7000,
+ 8000,
+ 9000,
+ 10000,
+ 11000
+};
+
+// Approximate y = 1.3^((T-tvpd_leak)/10)
+// Interpolate (T-tvpd_leak) in the table below to find m.
+// y ~= (T*m) >> 10 (shift out 10 bits)
+// Error in estimation is no more than 0.9%
+// The first column represents the result of T-tvpd_leak where T is the
+// associated temperature sensor. The second column represents the associated
+// m(slope) when the delta temp the first value.
+int16_t G_wof_iddq_mult_table[][2] = {
+ //Delta Temperature in C, m
+ {-50, 276},
+ {-40, 359},
+ {-30, 466},
+ {-20, 606},
+ {-10, 788},
+ {0, 1024},
+ {10, 1331},
+ {20, 1731},
+ {30, 2250},
+ {40, 2925},
+ {50, 3802}
+};
+#define WOF_IDDQ_MULT_TABLE_N 11
/**
@@ -76,6 +113,10 @@ void wof_main(void)
// TODO Read out necessary Sensor data for WOF calculation
// uint16_t l_current_vdd = getSensorByGsid(CURVDD)->sample;
// uint16_t l_current_vdn = getSensorByGsid(CURVDN)->sample
+
+ // Read VOLTVDDSENSE once here to be used in the algorithm and save it to amec
+ g_wof->volt_vdd_sense = getSensorByGsid(VOLTVDDSENSE)->sample;
+
// Functions to calculate Ceff_vdd and Ceff_vdn here.
uint16_t ceff_vdd = 0; // TODO: replace with future function call
uint16_t ceff_vdn = 0; // TODO: replace with future function call
@@ -98,12 +139,11 @@ void wof_main(void)
// NOTE to Reviewers: This trace is here just to put references to the above
// variables such that compilation is successful. Will be removed in final
// version
- TRAC_INFO("Step from start VDN = %d, VDN = %d",
+ TRAC_INFO("Step from start VDN = %d, VDD = %d",
vdn_step_from_start,
vdd_step_from_start );
-
}
@@ -198,7 +238,7 @@ uint32_t calc_vfrt_mainstore_addr( uint16_t i_vdd_step_from_start,
i_vdd_step_from_start) ) + i_quad_step_from_start);
// Skip the wof header at the beginning of wof tables
- uint32_t wof_tables_base = G_wof_tables_main_mem_addr + WOF_HEADER_SIZE;
+ uint32_t wof_tables_base = g_wof->vfrt_tbls_main_mem_addr + WOF_HEADER_SIZE;
return wof_tables_base + offset;
}
@@ -227,20 +267,23 @@ void copy_vfrt_to_sram( copy_vfrt_to_sram_parms_t * i_parms)
// Static variable to trac which buffer is open for use
// 0 = PING; 1 = PONG;
int l_gperc; // gpe schedule return code
- static uint8_t L_pingpong = 0;
uint8_t * l_buffer_address;
- if( L_pingpong == 0 )
+
+ if(g_wof->curr_ping_pong_buf == (uint32_t)G_sram_vfrt_ping_buffer)
{
- // Use ping buffer
- l_buffer_address = G_sram_vfrt_ping_buffer;
+ // Switch to pong buffer
+ l_buffer_address = G_sram_vfrt_pong_buffer;
}
else
{
- // Use pong buffer
- l_buffer_address = G_sram_vfrt_pong_buffer;
+ // Switch to ping buffer
+ l_buffer_address = G_sram_vfrt_ping_buffer;
}
+ // Update global "next" ping pong buffer for callback function
+ g_wof->next_ping_pong_buf = (uint32_t)l_buffer_address;
+
// Copy the vfrt data into the buffer
memcpy( l_buffer_address,
&(i_parms->vfrt_table->data[i_parms->pad]),
@@ -248,7 +291,7 @@ void copy_vfrt_to_sram( copy_vfrt_to_sram_parms_t * i_parms)
// Set the parameters for the GpeRequest
G_wof_vfrt_parms.vfrt_ptr = l_buffer_address;
- G_wof_vfrt_parms.active_quads = G_requested_active_quad_update;
+ G_wof_vfrt_parms.active_quads = g_wof->req_active_quad_update;
// Send IPC command to PGPE with new vfrt address and active quads
// Should not need to check if request is idle as wof_main does before
@@ -291,21 +334,62 @@ void copy_vfrt_to_sram( copy_vfrt_to_sram_parms_t * i_parms)
// Commit error log
commitErrl(&l_errl);
}
- else
- {
- // Sent the IPC command successfully, update which buffer we should look
- // at next time.
- L_pingpong = ~L_pingpong;
- // Update the previous active quads
- G_previous_active_quads = G_requested_active_quad_update;
+}
- // Update Current ping pong buffer
- G_current_ping_pong_buf = l_buffer_address;
+/**
+ * switch_ping_pong_buffer
+ *
+ * Description: Callback function for G_wof_vfrt_req GPE request to
+ * confirm the new VFRT is being used by the PGPE and
+ * record the switch on the 405
+ */
+//TODO RTC 166301 - will be renamed wof_vfrt_req_callback
+void switch_ping_pong_buffer( void )
+{
+ // Confirm the WOF VFRT PGPE request has completed with no errors
+ if( G_wof_vfrt_parms.msg_cb.rc == PGPE_WOF_RC_VFRT_QUAD_MISMATCH )
+ {
+ // TODO RTC 166301 - Implement this logic with read_req_active_quads
+ // function.
+ // Quad Mismatch.
+ // Reread the requested active quads from Shared SRAM and set
+ // parameters
+ // Trace the mismatch and let the code exit. Will retry
+ // next invocation of wof_main.
+ // Keep retrying if this return code is seen
}
+ else if( G_wof_vfrt_parms.msg_cb.rc == PGPE_RC_SUCCESS )
+ {
+ // GpeRequest went through successfully. update global ping pong buffer
+ g_wof->curr_ping_pong_buf = g_wof->next_ping_pong_buf;
-}
+ // Update previous active quads
+ g_wof->prev_req_active_quads = g_wof->req_active_quad_update;
+ }
+ else
+ {
+ // Some other failure case. Reset PM complex.
+ /* @
+ * @errortype
+ * @moduleid WOF_VFRT_CALLBACK
+ * @reasoncode WOF_VFRT_REQ_FAILURE
+ * @userdata1 The GpeRequest RC
+ * @userdata4 OCC_NO_EXTENDED_RC
+ */
+ errlHndl_t l_errl = createErrl( WOF_VFRT_CALLBACK,
+ WOF_VFRT_REQ_FAILURE,
+ OCC_NO_EXTENDED_RC,
+ ERRL_SEV_UNRECOVERABLE,
+ NULL,
+ DEFAULT_TRACE_SIZE,
+ G_wof_vfrt_parms.msg_cb.rc,
+ 0 );
+
+ REQUEST_RESET( l_errl );
+ }
+}
/**
* send_vfrt_to_pgpe
@@ -315,9 +399,9 @@ void copy_vfrt_to_sram( copy_vfrt_to_sram_parms_t * i_parms)
* to the PGPE
* Note: If desired VFRT is the same as previous, skip.
*
- * Param[in]: i_vfrt_address - Address of the desired vfrt table.
+ * Param[in]: i_vfrt_main_mem_addr - Address of the desired vfrt table.
*/
-void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
+void send_vfrt_to_pgpe( uint32_t i_vfrt_main_mem_addr )
{
int l_ssxrc = SSX_OK;
int l_gperc = 0;
@@ -326,19 +410,21 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
do
{
- if( (i_vfrt_address == G_current_vfrt_addr ) &&
- (G_requested_active_quad_update == G_previous_active_quads) )
+ if( (i_vfrt_main_mem_addr == g_wof->curr_vfrt_main_mem_addr ) &&
+ (g_wof->req_active_quad_update ==
+ g_wof->prev_req_active_quads) )
{
// VFRT and requested active quads are unchanged. Skip
break;
}
- else if( (i_vfrt_address == G_current_vfrt_addr) &&
- (G_requested_active_quad_update != G_previous_active_quads) )
+ else if( (i_vfrt_main_mem_addr == g_wof->curr_vfrt_main_mem_addr)&&
+ (g_wof->req_active_quad_update !=
+ g_wof->prev_req_active_quads) )
{
// Only requested active quads changed. No need to do a BCE request
// for new VFRT. Just send IPC command with updated active quads
- G_wof_vfrt_parms.vfrt_ptr = G_current_ping_pong_buf;
- G_wof_vfrt_parms.active_quads = G_requested_active_quad_update;
+ G_wof_vfrt_parms.vfrt_ptr = (VFRT_Hcode_t*)g_wof->curr_ping_pong_buf;
+ G_wof_vfrt_parms.active_quads = g_wof->req_active_quad_update;
//Send IPC command to PGPE with new active quad update
l_gperc = gpe_request_schedule( &G_wof_vfrt_req );
@@ -347,7 +433,7 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
if(l_gperc != 0)
{
//Error in scheduling pgpe clip update task
- TRAC_ERR("copy_vfrt_to_sram: Failed to schedule WOF VFRT task rc=%x",
+ TRAC_ERR("send_vfrt_to_sram: Failed to schedule WOF VFRT task rc=%x",
l_gperc);
/* @
@@ -379,11 +465,6 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
// Commit error log
commitErrl(&l_errl);
}
- else
- {
- // Successful Schedule. Update previous active quads
- G_previous_active_quads = G_requested_active_quad_update;
- }
}
else
{
@@ -394,8 +475,8 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
temp_bce_request_buffer_t l_temp_bce_buff = {{0}};
- uint8_t l_pad = i_vfrt_address%128;
- uint32_t l_vfrt_addr_128_aligned = i_vfrt_address - l_pad;
+ uint8_t l_pad = i_vfrt_main_mem_addr%128;
+ uint32_t l_vfrt_addr_128_aligned = i_vfrt_main_mem_addr - l_pad;
// Create structure to hold parameters for callback function
copy_vfrt_to_sram_parms_t l_callback_parms;
@@ -417,7 +498,7 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
if(l_ssxrc != SSX_OK)
{
- CMDH_TRAC_ERR("read_wof_header: BCDE request create failure rc=[%08X]", -l_ssxrc);
+ CMDH_TRAC_ERR("send_vfrt_to_pgpe: BCDE request create failure rc=[%08X]", -l_ssxrc);
/*
* @errortype
* @moduleid SEND_VFRT_TO_PGPE
@@ -437,7 +518,7 @@ void send_vfrt_to_pgpe( uint32_t i_vfrt_address )
if(l_ssxrc != SSX_OK)
{
- CMDH_TRAC_ERR("read_wof_header: BCE request schedule failure rc=[%08X]", -l_ssxrc);
+ CMDH_TRAC_ERR("send_vfrt_to_pgpe: BCE request schedule failure rc=[%08X]", -l_ssxrc);
/*
* @errortype
* @moduleid SEND_VFRT_TO_PGPE
@@ -500,6 +581,450 @@ void read_shared_sram( void )
// Get the requested active quad update
uint64_t l_doubleword = in64(current_pgpe_sram_addr);
- memcpy(&G_requested_active_quad_update, &l_doubleword, sizeof(uint8_t));
+ memcpy(&g_wof->req_active_quad_update, &l_doubleword, sizeof(uint8_t));
+
+ // merge the 16-bit power-on field from quad state 0 and the 16-bit power-on
+ // field from quad state 1 and save it to amec.
+ g_wof->core_pwr_on =
+ (((uint32_t)G_quad_state_0.fields.core_poweron_state) << 16)
+ | ((uint32_t)G_quad_state_1.fields.core_poweron_state);
+
+ // Clear out current quad pstates
+ memset(g_wof->quad_x_pstates, 0 , MAX_NUM_QUADS);
+
+ // Add the quad states to the global quad state array for easy looping.
+ g_wof->quad_x_pstates[0] = (uint8_t)G_quad_state_0.fields.quad0_pstate;
+ g_wof->quad_x_pstates[1] = (uint8_t)G_quad_state_0.fields.quad1_pstate;
+ g_wof->quad_x_pstates[2] = (uint8_t)G_quad_state_0.fields.quad2_pstate;
+ g_wof->quad_x_pstates[3] = (uint8_t)G_quad_state_0.fields.quad3_pstate;
+ g_wof->quad_x_pstates[4] = (uint8_t)G_quad_state_1.fields.quad4_pstate;
+ g_wof->quad_x_pstates[5] = (uint8_t)G_quad_state_1.fields.quad5_pstate;
+
+
+ // Save IVRM bit vector states to amec
+ g_wof->quad_ivrm_states =
+ (((uint8_t)G_quad_state_0.fields.ivrm_state) << 4)
+ | ((uint8_t)G_quad_state_1.fields.ivrm_state);
}
+
+/**
+ * calculate_core_voltage
+ *
+ * Description: Calculate the core voltage based on Pstate and IVRM state.
+ * Same for all cores in the quad so only need to calculate
+ * once per quad.
+ */
+void calculate_core_voltage( void )
+{
+ uint32_t l_voltage;
+ uint8_t l_quad_mask;
+ int l_quad_idx = 0;
+ for(; l_quad_idx < MAX_NUM_QUADS; l_quad_idx++)
+ {
+ // Adjust current mask. (IVRM_STATE_QUAD_MASK = 0x80)
+ l_quad_mask = IVRM_STATE_QUAD_MASK >> l_quad_idx;
+
+ // Check IVRM state of quad 0.
+ // 0 = BYPASS, 1 = REGULATION
+ if( (g_wof->quad_ivrm_states & l_quad_mask ) == 0 )
+ {
+ l_voltage = g_wof->volt_vdd_sense;
+ }
+ else
+ {
+ // Calculate the address of the pstate for the current quad.
+ uint32_t pstate_addr = G_pgpe_pstate_table_address +
+ (g_wof->quad_x_pstates[l_quad_idx] * sizeof(OCCPstateTable_entry_t));
+
+ // Get the Pstate
+ OCCPstateTable_entry_t * pstate_entry_ptr;
+ uint32_t current_pstate = in32(pstate_addr);
+ pstate_entry_ptr = (OCCPstateTable_entry_t *)(&current_pstate);
+
+ // Get the internal vid (ivid) from the pstate table
+ uint8_t current_vid = pstate_entry_ptr->internal_vdd_vid;
+
+ // Convert the vid to voltage and then convert units to 100uV
+ // Vid-to-voltage = 512mV + ivid*4mV
+ // mV to 100uV = mV*10
+ l_voltage = (512 + (current_vid*4))*10;
+ }
+
+ // Save the voltage to amec_wof_t global struct
+ g_wof->v_core_100uV[l_quad_idx] = l_voltage;
+ }
+}
+
+
+/**
+ * calculate_core_leakage
+ *
+ * Description: Calculate core-level leakage
+ *
+ * Return: the calculated core leakage
+ */
+void calculate_core_leakage( void )
+{
+
+ int l_chip_v_idx = 0;
+ uint16_t l_quad_x_cache;
+ uint16_t idc_vdd = 0;
+ uint8_t num_quads_off = 0;
+ uint16_t temperature = 0;
+
+ // Get the VOLTVDDSENSE sensor and choose the appropriate
+ // chip voltage index
+ uint32_t l_v_chip = g_wof->volt_vdd_sense;
+
+ if( l_v_chip <= G_iddq_voltages[0] )
+ {
+ // Voltage is <= to first entry. Use first two entries.
+ l_chip_v_idx = 0;
+ }
+ else if( l_v_chip >= G_iddq_voltages[CORE_IDDQ_MEASUREMENTS-1] )
+ {
+ // Voltage is >= to last entry. Use last two entries.
+ l_chip_v_idx = CORE_IDDQ_MEASUREMENTS - 2;
+ }
+ else
+ {
+ // Search for entries on either side of our voltage
+ for(;l_chip_v_idx < CORE_IDDQ_MEASUREMENTS - 1; l_chip_v_idx++)
+ {
+ if( (l_v_chip >= G_iddq_voltages[l_chip_v_idx]) &&
+ (l_v_chip <= G_iddq_voltages[l_chip_v_idx]) )
+ {
+ break;
+ }
+ }
+
+ }
+ // Save index used for interpolating voltages to amec
+ g_wof->voltage_idx = l_chip_v_idx;
+
+
+ // Calculate all variables that will be used in the core
+ // loop that only need to be calculated once.
+
+ // Read the Nest Temperature sensor for calculations & save to amec
+ g_wof->tempnest_sense = getSensorByGsid(TEMPNEST)->sample;
+
+ // Look up Tvpd_leak for calculations when either the core or quad is off
+ // avttemp values in 0.5C. Divide by 2 to convert to 1C
+ g_wof->tvpd_leak_off =
+ G_oppb.iddq.avgtemp_all_cores_off_caches_off[l_chip_v_idx] >> 1;
+
+ // Look up Tvpd_leak for calculations involving the cache.
+ g_wof->tvpd_leak_cache =
+ G_oppb.iddq.avgtemp_all_good_cores_off[l_chip_v_idx] >> 1;
+
+ // Take the difference between the temperature and tvpd_leak_off
+ // used for multiplier calculation
+ g_wof->nest_delta_temp = g_wof->tempnest_sense -
+ g_wof->tvpd_leak_off;
+
+ // Calculate IDDQ_TEMP_FACTOR^((TEMPNEST - tvpd_leak)/10)
+ g_wof->nest_mult = calculate_multiplier(g_wof->nest_delta_temp);
+
+
+ // Look up leakage current.
+ // Divide by 6 to get just one quad
+ g_wof->idc_quad =
+ G_oppb.iddq.ivdd_all_cores_off_caches_off[l_chip_v_idx] /
+ MAX_NUM_QUADS;
+
+
+
+
+ // Calculate ALL_CORES_OFF_ISO
+ // Perform linear interpolation using the neighboring entries:
+ // Y = m*(X-x1) + y1, where m = (y2-y1) / (x2-x1)
+ g_wof->all_cores_off_iso =
+ interpolate_linear((int32_t)l_v_chip,
+ (int32_t)G_iddq_voltages[l_chip_v_idx],
+ (int32_t)G_iddq_voltages[l_chip_v_idx+1],
+ (int32_t)G_oppb.iddq.ivdd_all_cores_off_caches_off[l_chip_v_idx],
+ (int32_t)G_oppb.iddq.ivdd_all_cores_off_caches_off[l_chip_v_idx+1]);
+
+ // Multiply by nest leakage percentage
+ // TODO: This percentage(60%) will eventually be added to the OCC Pstate Parameter
+ // block once it is added as a system attribute to the MRW.
+ // G_oppb.iddq.nestLeakagePercentage
+ g_wof->all_cores_off_iso = g_wof->all_cores_off_iso * 60 / 100;
+
+
+ // Calculate ALL_CACHES_ON_ISO
+ g_wof->all_caches_on_iso =
+ G_oppb.iddq.ivdd_all_good_cores_off_good_caches_on[l_chip_v_idx] -
+ g_wof->all_cores_off_iso;
+
+ l_quad_x_cache = g_wof->all_caches_on_iso / MAX_NUM_QUADS;
+
+ // Loop through all Quads and their respective Cores to calculate
+ // leakage.
+ int quad_idx = 0; // Quad Index (0-5)
+ uint8_t core_idx = 0; // Actual core index (0-23)
+ int core_loop_idx = 0; // On a per quad basis (0-3)
+
+
+
+ for(quad_idx = 0; quad_idx < MAX_NUM_QUADS; quad_idx++)
+ {
+ if(g_wof->quad_x_pstates[quad_idx] == QUAD_POWERED_OFF)
+ {
+ // Increment the number of quads found to be off
+ num_quads_off++;
+
+ }
+ else // Quad i is on
+ {
+ // Calculate the index of the first core in the quad.
+ core_idx = quad_idx * NUM_CORES_PER_QUAD;
+
+ // Get the voltage for the current core.
+ // (Same for all cores within a single quad)
+ uint16_t cur_core_voltage = g_wof->v_core_100uV[quad_idx];
+
+ // Calculate the number of cores on within the current quad.
+ g_wof->cores_on_per_quad[quad_idx] =
+ num_cores_on_in_quad(quad_idx);
+
+ // Look up tvpd_leak_on for calculations when the core/quad is on
+ // avttemp in IDDQ table is in 0.5C. Divide by 2 to convert to 1C.
+ g_wof->tvpd_leak_on = G_oppb.iddq.avgtemp_quad_good_cores_on
+ [quad_idx][cur_core_voltage] >> 1;
+
+
+ // Calculate Quadx_good_cores_only
+ g_wof->quad_good_cores_only[quad_idx] =
+ G_oppb.iddq.ivdd_quad_good_cores_on_good_caches_on
+ [quad_idx][cur_core_voltage] -
+ G_oppb.iddq.ivdd_all_good_cores_off_good_caches_on
+ [l_chip_v_idx] +
+ g_wof->all_cores_off_iso*
+ G_oppb.iddq.good_normal_cores[quad_idx]/24;
+
+
+ // Calculate quadx_ON_cores
+ g_wof->quad_on_cores[quad_idx] =
+ (g_wof->quad_good_cores_only[quad_idx]*
+ g_wof->cores_on_per_quad[quad_idx]) /
+ G_oppb.iddq.good_normal_cores[quad_idx];
+
+
+ // Calculate quadx_BAD_OFF_cores
+ g_wof->quad_bad_off_cores[quad_idx] =
+ g_wof->all_cores_off_iso*G_oppb.iddq.good_normal_cores[quad_idx]/24;
+
+ // Reset num_cores_off_in_quad before processing current quads cores
+ uint8_t num_cores_off_in_quad = 0;
+ // Loop all cores within current quad
+ for(core_loop_idx = 0; core_loop_idx < NUM_CORES_PER_QUAD; core_loop_idx++)
+ {
+
+ if(core_powered_on(core_idx))
+ {
+
+ // Get the core temperature from TEMPPROCTHRMC sensor
+ temperature = AMECSENSOR_ARRAY_PTR(TEMPPROCTHRMC0,
+ core_idx)->sample;
+
+ // If the TEMPPROCTHRMCy is 0, use TEMPQx
+ if(temperature == 0)
+ {
+ temperature = AMECSENSOR_ARRAY_PTR(TEMPQ0,
+ quad_idx)->sample;
+ // If TEMPQx is also 0, use TEMPNEST
+ if(temperature == 0)
+ {
+ temperature = g_wof->tempnest_sense;
+ }
+ }
+
+ // Save the selected temperature
+ g_wof->tempprocthrmc[core_idx] = temperature;
+
+
+
+ // Get the difference between the temperature and tvpd_leak
+ g_wof->core_delta_temp[core_idx] =
+ g_wof->tempprocthrmc[core_idx] -
+ g_wof->tvpd_leak_on;
+
+ // Calculate the multiplier for the core
+ g_wof->core_mult[core_idx] =
+ calculate_multiplier(g_wof->core_delta_temp[core_idx]);
+
+ // For each core, incorporate core on calculation into
+ // leakage
+ idc_vdd += (g_wof->quad_on_cores[quad_idx]*
+ g_wof->core_mult[core_idx]) >> 10;
+
+ }
+ else // Core is powered off
+ {
+ // Increment the number of cores found to be off
+ num_cores_off_in_quad++;
+ }
+ // Increment the Core Index for the next iteration
+ core_idx++;
+ } // core loop
+
+ // After all cores within the current quad have been processed,
+ // incorporate calculation for cores that were off into leakage
+ idc_vdd +=
+ ((g_wof->quad_bad_off_cores[quad_idx]*g_wof->nest_mult)
+ >> 10)* num_cores_off_in_quad;
+
+ temperature = AMECSENSOR_ARRAY_PTR(TEMPQ0,
+ quad_idx)->sample;
+
+ // If TEMPQ0 is 0, use TEMPNEST
+ if( temperature == 0 )
+ {
+ temperature = g_wof->tempnest_sense;
+ }
+
+ // Save selected temperature off to amec
+ g_wof->tempq[quad_idx] = temperature;
+
+
+ // Get the quad delta temperature for cache calc
+ g_wof->quad_delta_temp[quad_idx] =
+ g_wof->tempq[quad_idx] -
+ g_wof->tvpd_leak_cache;
+
+ //Calculate the multiplier for the quad
+ g_wof->quad_mult[quad_idx] =
+ calculate_multiplier(g_wof->quad_delta_temp[quad_idx]);
+
+ // Incorporate the cache into the leakage calculation
+ idc_vdd += (l_quad_x_cache*g_wof->quad_mult[quad_idx]) >> 10;
+
+
+ }
+ } // quad loop
+ // After all Quads have been processed, incorporate calculation for quads
+ // that off into leakage
+ idc_vdd += ((g_wof->idc_quad*g_wof->nest_mult) >> 10)* num_quads_off;
+
+
+ // Finally, save the calculated leakage to amec
+ g_wof->idc_vdd = idc_vdd;
+
+}
+
+
+/**
+ * core_powered_on
+ *
+ * Description: Helper function to determine whether the given core
+ * is on based off the most recently read data from
+ * OCC-PGPE Shared SRAM
+ *
+ * Param: The desired core number
+ *
+ * Return: Returns TRUE if the core is powered on, FALSE otherwise
+ */
+inline bool core_powered_on(uint8_t i_core_num)
+{
+ return ( g_wof->core_pwr_on & (0x80000000 >> i_core_num));
+}
+
+/**
+ * num_cores_on_in_quad
+ *
+ * Description: Helper function that returns the number of cores
+ * currently powered on in the given quad based off
+ * the most recently read data from OCC-PGPE Shared SRAM
+ *
+ * Param: The Quad number
+ *
+ * Return: Returns the number of cores powered on within the given quad.
+ */
+uint8_t num_cores_on_in_quad( uint8_t i_quad_num )
+{
+ int start_index = i_quad_num * NUM_CORES_PER_QUAD;
+ int i;
+ uint8_t num_powered_on_cores = 0;
+ for(i = start_index; i < (start_index + NUM_CORES_PER_QUAD); i++)
+ {
+ if( core_powered_on(i) )
+ {
+ num_powered_on_cores++;
+ }
+ }
+
+ return num_powered_on_cores;
+}
+
+/**
+ * interpolate_linear
+ *
+ * Description: Helper function that takes in the necessary input for
+ * a linear interpolation and returns the result of the
+ * calculation
+ *
+ * Y = m*(X-x1) + y1, where m = (y2-y1) / (x2-x1)
+ *
+ * Return: The result Y of the formula above
+ */
+inline int32_t interpolate_linear( int32_t i_X,
+ int32_t i_x1,
+ int32_t i_x2,
+ int32_t i_y1,
+ int32_t i_y2 )
+{
+ return (i_X - i_x1)*((i_y2 - i_y1) / (i_x2 - i_x1)) + i_y1;
+}
+
+/**
+ * calculate_multiplier
+ *
+ * Description: This function calculates the 'm' in the formula
+ * y ~= (T*m) >> 10 by choosing the appropriate row in
+ * G_wof_iddq_mult_table based on the passed in temp, and interpolates
+ * the values of the 'm' column in order to find the appropriate multiplier
+ *
+ * Param: the delta temp between tvpd_leak and a temperature sensor. Used
+ * to find the appropriate row index into G_wof_iddq_mult_table.
+ *
+ * Return: The multiplier representing the temperature factor
+ */
+int32_t calculate_multiplier( int32_t i_temp )
+{
+ int mult_idx;
+
+ if( i_temp < G_wof_iddq_mult_table[0][0] )
+ {
+ mult_idx = 0;
+ }
+ else if( i_temp >= G_wof_iddq_mult_table[WOF_IDDQ_MULT_TABLE_N-1][0] )
+ {
+ mult_idx = WOF_IDDQ_MULT_TABLE_N - 2;
+ }
+ else
+ {
+ for(mult_idx = 0 ; mult_idx < WOF_IDDQ_MULT_TABLE_N-1; mult_idx++)
+ {
+ if( (G_wof_iddq_mult_table[mult_idx][0] <= i_temp) &&
+ (G_wof_iddq_mult_table[mult_idx+1][0] >= i_temp) )
+ {
+ break;
+ }
+ }
+ }
+
+
+ // mult index now has the row index into G_wof_iddq_mult_table.
+ // use it to calculate the final multiplier
+ return interpolate_linear( i_temp,
+ (int32_t)G_wof_iddq_mult_table[mult_idx][0],
+ (int32_t)G_wof_iddq_mult_table[mult_idx+1][0],
+ (int32_t)G_wof_iddq_mult_table[mult_idx][1],
+ (int32_t)G_wof_iddq_mult_table[mult_idx+1][1]);
+}
+
+
+
OpenPOWER on IntegriCloud