diff options
author | Chris Cain <cjcain@us.ibm.com> | 2016-03-18 13:02:40 -0500 |
---|---|---|
committer | Christopher J. Cain <cjcain@us.ibm.com> | 2016-04-19 12:06:25 -0400 |
commit | 2107913c20087fee59aa7df8b9c4d2daad3cfb6b (patch) | |
tree | 8701736a7ddd6df66005ed732f128f4d4693d9c4 /src/occ_405 | |
parent | cf092445d8ec7ae40238b2e38cc5f15414a4891a (diff) | |
download | talos-occ-2107913c20087fee59aa7df8b9c4d2daad3cfb6b.tar.gz talos-occ-2107913c20087fee59aa7df8b9c4d2daad3cfb6b.zip |
Poll with sensor support and power accumulator resize
Change-Id: I64eb18a567e8d899de085903412a9b4fd13e4be7
RTC: 148327
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/22365
Tested-by: FSP CI Jenkins
Reviewed-by: Martha Broyles <mbroyles@us.ibm.com>
Reviewed-by: William A. Bryan <wilbryan@us.ibm.com>
Reviewed-by: Christopher J. Cain <cjcain@us.ibm.com>
Diffstat (limited to 'src/occ_405')
-rwxr-xr-x | src/occ_405/amec/amec_amester.c | 7 | ||||
-rw-r--r-- | src/occ_405/amec/amec_amester.h | 45 | ||||
-rwxr-xr-x | src/occ_405/amec/amec_analytics.c | 146 | ||||
-rwxr-xr-x | src/occ_405/amec/amec_sensors_core.c | 6 | ||||
-rwxr-xr-x | src/occ_405/amec/amec_sensors_power.c | 65 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_fsp.c | 2 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_fsp_cmds.c | 14 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_fsp_cmds.h | 11 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_fsp_cmds_datacnfg.c | 267 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_mnfg_intf.c | 3 | ||||
-rwxr-xr-x | src/occ_405/cmdh/cmdh_snapshot.c | 3 | ||||
-rwxr-xr-x | src/occ_405/sensor/sensor.h | 2 |
12 files changed, 295 insertions, 276 deletions
diff --git a/src/occ_405/amec/amec_amester.c b/src/occ_405/amec/amec_amester.c index 4ddf734..e70b7e1 100755 --- a/src/occ_405/amec/amec_amester.c +++ b/src/occ_405/amec/amec_amester.c @@ -383,7 +383,6 @@ uint8_t amester_api( const IPMIMsg_t * i_msg, SensorInfo.value_min=l_sensor_ptr->sample_min; SensorInfo.value_max=l_sensor_ptr->sample_max; memcpy(&SensorInfo.status,&l_sensor_ptr->status,sizeof(uint16_t)); - SensorInfo.test=0; //TODO: This field is not supported for now. Maybe need to change in the future. // Copy to output buffer. t=(char *)&SensorInfo; @@ -1102,6 +1101,10 @@ void amec_tb_record(const AMEC_TB_GUID i_guid) } case 3: { //accumulator + *l_w++ = (UINT8)(l_s->accumulator >> 56); + *l_w++ = (UINT8)(l_s->accumulator >> 48); + *l_w++ = (UINT8)(l_s->accumulator >> 40); + *l_w++ = (UINT8)(l_s->accumulator >> 32); *l_w++ = (UINT8)(l_s->accumulator >> 24); *l_w++ = (UINT8)(l_s->accumulator >> 16); *l_w++ = (UINT8)(l_s->accumulator >> 8); @@ -1345,7 +1348,7 @@ void amec_tb_cmd_set_config(const IPMIMsg_t *i_psMsg, l_trace->entry_size += 2; break; case 3: //acc - l_trace->entry_size += 4; + l_trace->entry_size += 8; break; case 4: //updates l_trace->entry_size += 4; diff --git a/src/occ_405/amec/amec_amester.h b/src/occ_405/amec/amec_amester.h index 8a3510f..c279712 100644 --- a/src/occ_405/amec/amec_amester.h +++ b/src/occ_405/amec/amec_amester.h @@ -75,16 +75,16 @@ // Autonomic Management of Energy (AME) Parameters #define AME_API_MAJ 2 // API version major -#define AME_API_MIN 26 // API version minor +#define AME_API_MIN 27 // API version minor #define AME_VERSION_MAJ 7 // Major Version (e.g. Ver. 1.4 has MAJ=1) -#define AME_VERSION_MIN 27 // Minor Version (e.g. Ver. 1.4 has MIN=4) +#define AME_VERSION_MIN 28 // Minor Version (e.g. Ver. 1.4 has MIN=4) #define AME_YEAR 2015 // Year of Release (e.g. 2006) -#define AME_MONTH 4 // Month of Release (e.g. September=9) -#define AME_DAY 2 // Day of Release +#define AME_MONTH 7 // Month of Release (e.g. September=9) +#define AME_DAY 31 // Day of Release -#define AME_SDRS 22 // AME Sensor Data Record Size: 18 bytes +#define AME_SDRS 24 // AME Sensor Data Record Size: 24 bytes // AME data types for AME_GetInfo_*() functions #define AME_INFO_NAME 0 @@ -136,7 +136,7 @@ typedef struct sensorrec { uint32_t timestamp; uint32_t updates; - uint32_t accumulated_value; + uint64_t accumulated_value; uint16_t value; uint16_t value_min; uint16_t value_max; @@ -207,39 +207,6 @@ typedef struct sensorrec // if set to 0, this is a normal AME scalar // sensor. - uint16_t test; - // if bit 15 is set to 1 in the status register, the - // test register can only be used to support - // the vector sensor. In this mode, the high 8 - // bits are used to hold two 4 bit - // indices of minimum values. The low 8 bits are - // used to hold two 4 bit indices of - // maximum values. Vector sensors can only hold - // up to 12 elements, so each 4 bit index - // can point to the vector index of interest. - // Here is a summary of what each 4 bit field - // shows: - // bits 15-12: index of minimum vector sensor - // value seen for all time (since - // last reset) - // bits 11-8: index of minimum vector sensor - // value seen for the latest time - // instant - // bits 7-4: index of maximum vector sensor - // value seen for all time (since - // last reset) - // bits 3-0: index of maximum vector sensor - // value seen for the latest time - // instant - // Vector sensors should not be longer than 12 - // elements because one can only guard out - // up to 12 elements with the vector sensor - // enable mask and to avoid extreme cycle spikes. - // A RESET of this sensor will cause the test - // register (high and low bytes) to be set to - // 0xFF. - // if bit 15 is set to 0 in the status register, this - // register can be used to store anything. } sensorrec_t; typedef UINT8 AMEC_TB_GUID; diff --git a/src/occ_405/amec/amec_analytics.c b/src/occ_405/amec/amec_analytics.c index 376ef46..29e8fe9 100755 --- a/src/occ_405/amec/amec_analytics.c +++ b/src/occ_405/amec/amec_analytics.c @@ -145,6 +145,25 @@ void amec_analytics_sb_recording(void) } } +/* + Implementation note: + + amec_analytics_main is called every 2 ms from an AMEC task. + + This routine uses the sensor accumulator to compute average values + over 2ms. Normally, the sensor accumulator is 64-bit, but it is + enough to save and use just 32-bit for the purposes of computing the + 2 ms average, since there is no danger of wrapping the accumulator + for 16-bit sensor values. The key is that unsigned subtraction + between any two accumulator values, gives you a valid change in the + accumulator, even when the accumulator overflows and wraps back to + 0, as long as the total change is small enough to fit in the + accumulators range (32-bit). 8 updates (in 2 ms) * 2^16-1 < 2^32-1, + so in 2ms, the accumulator's range of values cannot be exceeded. + + The code below uses cast from 64-bit to 32-bit to make clear the intention. + + */ void amec_analytics_main(void) { /*------------------------------------------------------------------------*/ @@ -195,46 +214,79 @@ void amec_analytics_main(void) g_amec->g44_avg[(i*MSA)+2] = (UINT32)g_amec->sys.todclock1.sample; // ptr to middle 16 bits of 48 bit TOD clock g_amec->g44_avg[(i*MSA)+4] = (UINT32)g_amec->sys.todclock2.sample; // ptr to low 16 bits of 48 bit TOD clock - tempaccum=g_amec->sys.pwr250us.src_accum_snapshot; // load pwr250us accum from last 2msec - g_amec->sys.pwr250us.src_accum_snapshot = g_amec->sys.pwr250us.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->sys.pwr250us.accumulator - tempaccum; // total accumulation over 2msec - tempaccum=tempaccum>>3; // divide by 8 + // load pwr250us accum from last 2msec + tempaccum = g_amec->sys.pwr250us.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->sys.pwr250us.src_accum_snapshot = + (uint32_t)g_amec->sys.pwr250us.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->sys.pwr250us.accumulator + - tempaccum; + tempaccum = tempaccum>>3; // divide by 8 g_amec->g44_avg[(i*MSA)+6] = g_amec->g44_avg[(i*MSA)+6] + tempaccum; - tempaccum=g_amec->sys.pwr250usgpu.src_accum_snapshot; // load pwr250usgpu accum from last 2msec - g_amec->sys.pwr250usgpu.src_accum_snapshot = g_amec->sys.pwr250usgpu.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->sys.pwr250usgpu.accumulator - tempaccum; // total accumulation over 2msec - tempaccum=tempaccum>>3; // divide by 8 + // load pwr250usgpu accum from last 2msec + tempaccum = g_amec->sys.pwr250usgpu.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->sys.pwr250usgpu.src_accum_snapshot = + (uint32_t)g_amec->sys.pwr250usgpu.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->sys.pwr250usgpu.accumulator + - tempaccum; + tempaccum = tempaccum>>3; // divide by 8 g_amec->g44_avg[(i*MSA)+8] = g_amec->g44_avg[(i*MSA)+8] + tempaccum; - tempaccum=g_amec->proc[i].pwr250us.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[i].pwr250us.src_accum_snapshot = g_amec->proc[i].pwr250us.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->proc[i].pwr250us.accumulator - tempaccum; // total accumulation over 2msec - tempaccum=tempaccum>>3; // divide by 8 + // load accumulator from last 2msec + tempaccum = g_amec->proc[i].pwr250us.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->proc[i].pwr250us.src_accum_snapshot = + (uint32_t)g_amec->proc[i].pwr250us.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->proc[i].pwr250us.accumulator + - tempaccum; + tempaccum = tempaccum>>3; // divide by 8 g_amec->g44_avg[(i*MSA)+10] = g_amec->g44_avg[(i*MSA)+10] + tempaccum; - tempaccum=g_amec->proc[i].pwr250usvdd.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[i].pwr250usvdd.src_accum_snapshot = g_amec->proc[i].pwr250usvdd.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->proc[i].pwr250usvdd.accumulator - tempaccum; // total accumulation over 2msec - tempaccum=tempaccum>>3; + // load accumulator from last 2msec + tempaccum = g_amec->proc[i].pwr250usvdd.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->proc[i].pwr250usvdd.src_accum_snapshot = + (uint32_t)g_amec->proc[i].pwr250usvdd.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->proc[i].pwr250usvdd.accumulator + - tempaccum; + tempaccum = tempaccum>>3; g_amec->g44_avg[(i*MSA)+11] = g_amec->g44_avg[(i*MSA)+11] + tempaccum; - tempaccum=g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot = g_amec->proc[i].vrm[0].volt250us.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->proc[i].vrm[0].volt250us.accumulator - tempaccum; // total accumulation over 2msec + // load accumulator from last 2msec + tempaccum = g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot = + (uint32_t)g_amec->proc[i].vrm[0].volt250us.accumulator; + // total accumulation over 2msec + tempaccum = + (uint32_t)g_amec->proc[i].vrm[0].volt250us.accumulator + - tempaccum; temp32 = tempaccum<<3; // Pi, Vdd tempreg = 4000; // Convert voltage from 100uV resolution to 6.25mV resolution tempreg = (UINT16)(UTIL_DIV32(temp32, tempreg)); - g_amec->g44_avg[(i*MSA)+12] = g_amec->g44_avg[(i*MSA)+12] + (UINT32)tempreg; - - tempaccum=g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot = g_amec->proc[i].vrm[1].volt250us.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->proc[i].vrm[1].volt250us.accumulator - tempaccum; // total accumulation over 2msec + g_amec->g44_avg[(i*MSA)+12] = g_amec->g44_avg[(i*MSA)+12] + + (UINT32)tempreg; + + // load accumulator from last 2msec + tempaccum = g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot = + (uint32_t)g_amec->proc[i].vrm[1].volt250us.accumulator; + // total accumulation over 2msec + tempaccum = + (uint32_t)g_amec->proc[i].vrm[1].volt250us.accumulator + - tempaccum; temp32 = tempaccum<<3; // Pi, Vcs tempreg = 4000; // Convert voltage from 100uV resolution to 6.25mV resolution @@ -242,14 +294,19 @@ void amec_analytics_main(void) g_amec->g44_avg[(i*MSA)+13] = g_amec->g44_avg[(i*MSA)+13] + (UINT32)tempreg; - tempaccum=g_amec->proc[i].cur250usvdd.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[i].cur250usvdd.src_accum_snapshot = g_amec->proc[i].cur250usvdd.accumulator; // save current accum state for next 2msec - tempaccum=g_amec->proc[i].cur250usvdd.accumulator - tempaccum; // total accumulation over 2msec - tempaccum=tempaccum>>3; + // load accumulator from last 2msec + tempaccum = g_amec->proc[i].cur250usvdd.src_accum_snapshot; + // save current accum state for next 2msec + g_amec->proc[i].cur250usvdd.src_accum_snapshot = + (uint32_t)g_amec->proc[i].cur250usvdd.accumulator; + tempaccum = (uint32_t)g_amec->proc[i].cur250usvdd.accumulator + - tempaccum; // total accumulation over 2msec + tempaccum = tempaccum>>3; g_amec->g44_avg[(i*MSA)+14] = g_amec->g44_avg[(i*MSA)+14] + tempaccum/100; + // hottest processor core temperature (average??) g_amec->g44_avg[(i*MSA)+15] = g_amec->g44_avg[(i*MSA)+15] + - (UINT32)g_amec->proc[i].temp4ms.sample; // hottest processor core temperature (average??) + (UINT32)g_amec->proc[i].temp4ms.sample; // major changes below to accommodate Group 45 @@ -433,31 +490,46 @@ void amec_analytics_main(void) (g_amec->analytics_thermal_offset + 1); // modulo 8 tempaccum = g_amec->fan.pwr250usfan.src_accum_snapshot; // load accumulator from last 2msec - g_amec->fan.pwr250usfan.src_accum_snapshot = g_amec->fan.pwr250usfan.accumulator; // save current accum state for next 2msec - tempaccum = g_amec->fan.pwr250usfan.accumulator - tempaccum; // total accumulation over 2msec + // save current accum state for next 2msec + g_amec->fan.pwr250usfan.src_accum_snapshot = + (uint32_t)g_amec->fan.pwr250usfan.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->fan.pwr250usfan.accumulator + - tempaccum; tempaccum = tempaccum >> g_amec->stream_vector_rate; tempreg = tempreg | (0xff & ((UINT16)tempaccum)); g_amec->analytics_array[5] = tempreg; tempaccum = g_amec->io.pwr250usio.src_accum_snapshot; // load accumulator from last 2msec - g_amec->io.pwr250usio.src_accum_snapshot = g_amec->io.pwr250usio.accumulator; // save current accum state for next 2msec - tempaccum = g_amec->io.pwr250usio.accumulator - tempaccum; // total accumulation over 2msec + // save current accum state for next 2msec + g_amec->io.pwr250usio.src_accum_snapshot = + (uint32_t)g_amec->io.pwr250usio.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->io.pwr250usio.accumulator + - tempaccum; tempaccum = tempaccum >> g_amec->stream_vector_rate; tempreg = ((UINT16)tempaccum) << 8; // upper byte tempaccum = g_amec->storage.pwr250usstore.src_accum_snapshot; // load accumulator from last 2msec - g_amec->storage.pwr250usstore.src_accum_snapshot = g_amec->storage.pwr250usstore.accumulator; // save current accum state for next 2msec - tempaccum = g_amec->storage.pwr250usstore.accumulator - tempaccum; // total accumulation over 2msec + // save current accum state for next 2msec + g_amec->storage.pwr250usstore.src_accum_snapshot = + (uint32_t)g_amec->storage.pwr250usstore.accumulator; + tempaccum = (uint32_t)g_amec->storage.pwr250usstore.accumulator + - tempaccum; // total accumulation over 2msec tempaccum = tempaccum >> g_amec->stream_vector_rate; tempreg = tempreg | (0xff & ((UINT16)tempaccum)); g_amec->analytics_array[6] = tempreg; tempaccum = g_amec->proc[0].pwr250usmem.src_accum_snapshot; // load accumulator from last 2msec - g_amec->proc[0].pwr250usmem.src_accum_snapshot = g_amec->proc[0].pwr250usmem.accumulator; // save current accum state for next 2msec - tempaccum = g_amec->proc[0].pwr250usmem.accumulator - tempaccum; // total accumulation over 2msec + // save current accum state for next 2msec + g_amec->proc[0].pwr250usmem.src_accum_snapshot = + (uint32_t)g_amec->proc[0].pwr250usmem.accumulator; + // total accumulation over 2msec + tempaccum = (uint32_t)g_amec->proc[0].pwr250usmem.accumulator + - tempaccum; tempaccum = tempaccum >> g_amec->stream_vector_rate; tempreg = ((UINT16)tempaccum) << 8; // upper byte diff --git a/src/occ_405/amec/amec_sensors_core.c b/src/occ_405/amec/amec_sensors_core.c index 5e58dde..d330898 100755 --- a/src/occ_405/amec/amec_sensors_core.c +++ b/src/occ_405/amec/amec_sensors_core.c @@ -597,13 +597,13 @@ void amec_calc_freq_and_util_sensors(CoreData * i_core_data_ptr, uint8_t i_core) if(g_amec->proc[0].core[i_core].sample_count == l_time_interval) { // Increase resolution of the UTIL accumulator by two decimal places - temp32 = AMECSENSOR_ARRAY_PTR(UTIL2MSP0C0,i_core)->accumulator * 100; + temp32 = (uint32_t)AMECSENSOR_ARRAY_PTR(UTIL2MSP0C0,i_core)->accumulator * 100; // Calculate average utilization of this core temp32 = temp32 / g_amec->proc[0].core[i_core].sample_count; g_amec->proc[0].core[i_core].avg_util = temp32; // Increase resolution of the FREQA accumulator by two decimal places - temp32 = AMECSENSOR_ARRAY_PTR(FREQA2MSP0C0,i_core)->accumulator * 100; + temp32 = (uint32_t)AMECSENSOR_ARRAY_PTR(FREQA2MSP0C0,i_core)->accumulator * 100; // Calculate average frequency of this core temp32 = temp32 / g_amec->proc[0].core[i_core].sample_count; g_amec->proc[0].core[i_core].avg_freq = temp32; @@ -837,7 +837,7 @@ void amec_calc_spurr(uint8_t i_core) sensor_update( AMECSENSOR_ARRAY_PTR(SPURR2MSP0C0,i_core), (uint16_t) temp32); } } -#endif +#endif /*----------------------------------------------------------------------------*/ /* End */ diff --git a/src/occ_405/amec/amec_sensors_power.c b/src/occ_405/amec/amec_sensors_power.c index 2815912..ef70864 100755 --- a/src/occ_405/amec/amec_sensors_power.c +++ b/src/occ_405/amec/amec_sensors_power.c @@ -170,29 +170,6 @@ uint32_t amec_value_from_apss_adc(uint8_t i_chan) #define ADCMULT_ROUND ADCMULT_TO_UNITS/2 // Function Specification // -// Name: amec_update_channel_sensor -// -// Description: Used to calculate power based on raw data obtained from APSS -// -// End Function Specification -void amec_update_channel_sensor(const uint8_t i_channel) -{ - if (i_channel < MAX_APSS_ADC_CHANNELS) - { - if(AMECSENSOR_PTR(PWRAPSSCH0 + i_channel)->ipmi_sid != 0) - { - uint32_t l_bulk_voltage = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); - - uint32_t l_temp32 = ADC_CONVERTED_VALUE(i_channel); - l_temp32 = ((l_temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; - sensor_update(AMECSENSOR_PTR(PWRAPSSCH0 + i_channel), (uint16_t) l_temp32); - } - } -} - - -// Function Specification -// // Name: amec_update_apss_sensors // // Description: Calculates sensor from raw ADC values obtained from APSS @@ -234,37 +211,27 @@ void amec_update_apss_sensors(void) l_bulk_current_sum += G_lastValidAdcValue[l_idx]; } - //Only for FSP-LESS systems do we update channel sensors. - if (FSP_SUPPORTED_OCC != G_occ_interrupt_type) - { - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.memory[l_proc][0]); - for (l_idx = 1; l_idx < MAX_PROC_CENT_CH; l_idx++) - { - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.memory[l_proc][l_idx]); - } - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.vdd[l_proc]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.vcs_vio_vpcie[l_proc]); + // -------------------------------------------------------------- + // Convert 12Vsense into interim value - this has to happen first + // -------------------------------------------------------------- + // Calculations involving bulk_voltage must be 64bit so final result + // does not get truncated (before dividing by ADCMULT_TO_UNITS) + uint64_t l_bulk_voltage = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); - if (OCC_MASTER == G_occ_role) + if (OCC_MASTER == G_occ_role) + { + // Update channel sensors for all channels (except 12v and gnd) + for (l_idx = 0; l_idx < MAX_APSS_ADC_CHANNELS; l_idx++) { - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[0]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[1]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[2]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.fans[0]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.fans[1]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.storage_media[0]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.storage_media[1]); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.total_current_12v); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.mem_cache); - amec_update_channel_sensor(G_sysConfigData.apss_adc_map.gpu); + if ((l_idx != G_sysConfigData.apss_adc_map.sense_12v) && + (l_idx != G_sysConfigData.apss_adc_map.remote_gnd)) + { + temp32 = ((ADC_CONVERTED_VALUE(l_idx) * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update(AMECSENSOR_PTR(PWRAPSSCH0 + l_idx), (uint16_t) temp32); + } } } - // -------------------------------------------------------------- - // Convert 12Vsense into interim value - this has to happen first - // -------------------------------------------------------------- - uint32_t l_bulk_voltage = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); - // ---------------------------------------------------------- // Convert Raw Vdd/Vcs/Vio/Vpcie Power from APSS into sensors // ---------------------------------------------------------- diff --git a/src/occ_405/cmdh/cmdh_fsp.c b/src/occ_405/cmdh/cmdh_fsp.c index d9bfdb9..6b88798 100755 --- a/src/occ_405/cmdh/cmdh_fsp.c +++ b/src/occ_405/cmdh/cmdh_fsp.c @@ -1275,11 +1275,11 @@ errlHndl_t cmdh_processTmgtRequest (const cmdh_fsp_cmd_t * i_cmd_ptr, case CMDH_QUERYFWLEVEL: cmdh_tmgt_query_fw (i_cmd_ptr,i_rsp_ptr); break; +*/ case CMDH_DEBUGPT: cmdh_dbug_cmd (i_cmd_ptr,i_rsp_ptr); break; -*/ case CMDH_SETMODESTATE: l_err = cmdh_tmgt_setmodestate(i_cmd_ptr,i_rsp_ptr); diff --git a/src/occ_405/cmdh/cmdh_fsp_cmds.c b/src/occ_405/cmdh/cmdh_fsp_cmds.c index edd49f3..fb3d606 100755 --- a/src/occ_405/cmdh/cmdh_fsp_cmds.c +++ b/src/occ_405/cmdh/cmdh_fsp_cmds.c @@ -380,9 +380,13 @@ ERRL_RC cmdh_poll_v20(cmdh_fsp_rsp_t * o_rsp_ptr) cmdh_poll_power_sensor_t l_pwrSensorList[MAX_APSS_ADC_CHANNELS]; for (k = 0; k < MAX_APSS_ADC_CHANNELS; k++) { - if (G_amec_sensor_list[PWRAPSSCH0 + k]->ipmi_sid != 0) + if ((G_apss_ch_to_function[k] != ADC_12V_SENSE) && + (G_apss_ch_to_function[k] != ADC_GND_REMOTE_SENSE)) { l_pwrSensorList[l_sensorHeader.count].id = G_amec_sensor_list[PWRAPSSCH0 + k]->ipmi_sid; + l_pwrSensorList[l_sensorHeader.count].function_id = G_apss_ch_to_function[k]; + l_pwrSensorList[l_sensorHeader.count].apss_channel = k; + l_pwrSensorList[l_sensorHeader.count].reserved = 0; l_pwrSensorList[l_sensorHeader.count].current = G_amec_sensor_list[PWRAPSSCH0 + k]->sample; l_pwrSensorList[l_sensorHeader.count].accumul = G_amec_sensor_list[PWRAPSSCH0 + k]->accumulator; l_pwrSensorList[l_sensorHeader.count].update_tag = G_amec_sensor_list[PWRAPSSCH0 + k]->update_tag; @@ -398,7 +402,7 @@ ERRL_RC cmdh_poll_v20(cmdh_fsp_rsp_t * o_rsp_ptr) // Write data to resp buffer if any. if (l_sensorHeader.count) { - uint8_t l_sensordataSz = l_sensorHeader.count * l_sensorHeader.length; + uint16_t l_sensordataSz = l_sensorHeader.count * l_sensorHeader.length; // Copy sensor data into response buffer. memcpy ((void *) &(o_rsp_ptr->data[l_rsp_index]), (void *)l_pwrSensorList, l_sensordataSz); // Increment index into response buffer. @@ -962,8 +966,6 @@ errlHndl_t cmdh_get_elog (const cmdh_fsp_cmd_t * i_cmd_ptr, void cmdh_dbug_get_apss_data (const cmdh_fsp_cmd_t * i_cmd_ptr, cmdh_fsp_rsp_t * o_rsp_ptr) { -/* TEMP -- NOT ENABLED YET (NEED DCOM / AMEC) */ -#if 0 uint8_t l_rc = ERRL_RC_SUCCESS; uint16_t i = 0; uint16_t l_resp_data_length = 0; @@ -1006,7 +1008,6 @@ void cmdh_dbug_get_apss_data (const cmdh_fsp_cmd_t * i_cmd_ptr, o_rsp_ptr->rc = l_rc; o_rsp_ptr->data_length[0] = ((uint8_t *)&l_resp_data_length)[0]; o_rsp_ptr->data_length[1] = ((uint8_t *)&l_resp_data_length)[1]; -#endif // #if 0 } // Function Specification @@ -1121,11 +1122,12 @@ void cmdh_dbug_cmd (const cmdh_fsp_cmd_t * i_cmd_ptr, commitErrl( &l_errl ); } break; +#endif // #if 0 case DBUG_DUMP_APSS_DATA: cmdh_dbug_get_apss_data(i_cmd_ptr, o_rsp_ptr); break; -#endif // #if 0 + default: l_rc = ERRL_RC_INVALID_DATA; //should NEVER get here... break; diff --git a/src/occ_405/cmdh/cmdh_fsp_cmds.h b/src/occ_405/cmdh/cmdh_fsp_cmds.h index 69643e0..bcb8bcc 100755 --- a/src/occ_405/cmdh/cmdh_fsp_cmds.h +++ b/src/occ_405/cmdh/cmdh_fsp_cmds.h @@ -194,10 +194,13 @@ typedef struct __attribute__ ((packed)) cmdh_poll_freq_sensor // Only available from master occ. typedef struct __attribute__ ((packed)) cmdh_poll_powr_sensor { - uint32_t id; // Sensor id - to represent the power. - uint32_t update_tag; // Count of number of 250us samples represented by accumulator. - uint32_t accumul; // Accumulation of 250us power readings - uint16_t current; // Most recent 250us reading in watts. + uint32_t id; // Sensor id - to represent the power. + uint8_t function_id; // Identify what the reading is for (ADC_CHANNEL_ID in xml file) + uint8_t apss_channel; // APSS channel that the power was read from + uint16_t reserved; + uint32_t update_tag; // Count of number of 250us samples represented by accumulator. + uint64_t accumul; // Accumulation of 250us power readings + uint16_t current; // Most recent 250us reading in watts. } cmdh_poll_power_sensor_t; // Only available from master occ. diff --git a/src/occ_405/cmdh/cmdh_fsp_cmds_datacnfg.c b/src/occ_405/cmdh/cmdh_fsp_cmds_datacnfg.c index d9abb17..94d58ee 100755 --- a/src/occ_405/cmdh/cmdh_fsp_cmds_datacnfg.c +++ b/src/occ_405/cmdh/cmdh_fsp_cmds_datacnfg.c @@ -1489,6 +1489,18 @@ errlHndl_t data_store_mem_cfg(const cmdh_fsp_cmd_t * i_cmd_ptr, { l_data_length = CMDH_DATALEN_FIELD_UINT16((&l_cmd_ptr->header)); + // Clear all sensor IDs (hw and temperature) + memset(G_sysConfigData.dimm_huids, 0, sizeof(G_sysConfigData.dimm_huids)); + int memctl, dimm; + for(memctl=0; memctl < MAX_NUM_MEM_CONTROLLERS; ++memctl) + { + g_amec->proc[0].memctl[memctl].centaur.temp_sid = 0; + for(dimm=0; dimm < NUM_DIMMS_PER_CENTAUR; ++dimm) + { + g_amec->proc[0].memctl[memctl].centaur.dimm_temps[dimm].temp_sid = 0; + } + } + // Process data based on version if(l_cmd_ptr->header.version == DATA_MEM_CFG_VERSION_20) { @@ -1508,157 +1520,148 @@ errlHndl_t data_store_mem_cfg(const cmdh_fsp_cmd_t * i_cmd_ptr, break; } - // Store the memory type. Memory must all be the same type, save from first and verify remaining - G_sysConfigData.mem_type = l_cmd_ptr->data_set[0].memory_type; - if(G_sysConfigData.mem_type == MEM_TYPE_NIMBUS) - { - // Nimbus type -- dimm_info1 is I2C engine which must be the same - // save from first entry and verify remaining - G_sysConfigData.dimm_i2c_engine = l_cmd_ptr->data_set[0].dimm_info1; - } - else - { - // TODO - CUMULUS not supported yet - //G_sysConfigData.mem_type = MEM_TYPE_CUMULUS; - - CMDH_TRAC_ERR("data_store_mem_cfg: Invalid mem type 0x%02X in config data packet version[0x%02X] num_data_sets[%u]", - l_cmd_ptr->data_set[0].memory_type, - l_cmd_ptr->header.version, - l_cmd_ptr->header.num_data_sets); - - cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); - break; - } - - // Clear all sensor IDs (hw and temperature) - memset(G_sysConfigData.dimm_huids, 0, sizeof(G_sysConfigData.dimm_huids)); - int memctl, dimm; - for(memctl=0; memctl < MAX_NUM_MEM_CONTROLLERS; ++memctl) + if (l_cmd_ptr->header.num_data_sets > 0) { - g_amec->proc[0].memctl[memctl].centaur.temp_sid = 0; - for(dimm=0; dimm < NUM_DIMMS_PER_CENTAUR; ++dimm) + // Store the memory type. Memory must all be the same type, save from first and verify remaining + G_sysConfigData.mem_type = l_cmd_ptr->data_set[0].memory_type; + if(G_sysConfigData.mem_type == MEM_TYPE_NIMBUS) { - g_amec->proc[0].memctl[memctl].centaur.dimm_temps[dimm].temp_sid = 0; + // Nimbus type -- dimm_info1 is I2C engine which must be the same + // save from first entry and verify remaining + G_sysConfigData.dimm_i2c_engine = l_cmd_ptr->data_set[0].dimm_info1; } - } - - // Store the hardware sensor ID and the temperature sensor ID - for(i=0; i<l_cmd_ptr->header.num_data_sets; i++) - { - cmdh_mem_cfg_v20_t* l_cmd2_ptr = (cmdh_mem_cfg_v20_t*)i_cmd_ptr; - cmdh_mem_cfg_data_set_v20_t* l_data_set = &l_cmd2_ptr->data_set[i]; - - // Verify matching memory type and process based on memory type - if( (l_data_set->memory_type == G_sysConfigData.mem_type) && - (l_data_set->memory_type == MEM_TYPE_NIMBUS) ) + else { - // Nimbus type: dimm info is I2C Engine, I2C Port, I2C Address - l_i2c_engine = l_data_set->dimm_info1; - l_i2c_port = l_data_set->dimm_info2; - l_i2c_addr = l_data_set->dimm_info3; - - // Validate the i2c info for this data set. Any failure will result in error and - // memory monitoring disabled. - - // Engine must be the same - if (l_i2c_engine != G_sysConfigData.dimm_i2c_engine) - { - CMDH_TRAC_ERR("data_store_mem_cfg: I2c engine mismatch. entry=%d, engine=%d, expected=%d", - i, - l_i2c_engine, - G_sysConfigData.dimm_i2c_engine); - cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); - break; - } - - // Port must be 0 or 1. - if((l_i2c_port != 0) && (l_i2c_port != 1)) - { - CMDH_TRAC_ERR("data_store_mem_cfg: Invalid I2C port. entry=%d, port=%d", - i, - l_i2c_port); - cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); - break; - } - - // I2C addr must be 0x3z where z is even - if( ( (l_i2c_addr & 0xF0) != 0x30) || - ( (l_i2c_addr & 0x01) != 0 ) ) - { - CMDH_TRAC_ERR("data_store_mem_cfg: Invalid I2C address. entry=%d, addr=%d", - i, - l_i2c_addr); - cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); - break; - } - - // DIMM info is good for this DIMM, save the sensor IDs for the DIMM - // The location of the HW sensor ID in the 2D dimm_huids array is used to know i2c port - // and i2c address for reading the DIMM. "centaur num" index is port "dimm num" is - // translated from i2c address, we already verified the i2c addr is 0x3z above - l_dimm_num = (l_i2c_addr & 0x0F) >> 1; - - // Store the hardware sensor ID - G_sysConfigData.dimm_huids[l_i2c_port][l_dimm_num] = l_data_set->hw_sensor_id; - - // Store the temperature sensor ID - g_amec->proc[0].memctl[l_i2c_port].centaur.dimm_temps[l_dimm_num].temp_sid = - l_data_set->temp_sensor_id; + // TODO - CUMULUS not supported yet + //G_sysConfigData.mem_type = MEM_TYPE_CUMULUS; - l_num_dimms++; - - } - else // must be cumulus and the "memory type" byte is the centaur# - { - CMDH_TRAC_ERR("data_store_mem_cfg: Invalid mem type 0x%02X in config data packet entry %d (entry 0 type: 0x%02X)", - l_data_set->memory_type, i, G_sysConfigData.mem_type); + CMDH_TRAC_ERR("data_store_mem_cfg: Invalid mem type 0x%02X in config data packet version[0x%02X] num_data_sets[%u]", + l_cmd_ptr->data_set[0].memory_type, + l_cmd_ptr->header.version, + l_cmd_ptr->header.num_data_sets); cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); break; + } -#if 0 - // TODO - CUMULUS not supported yet - // per spec for cumulus memory type is the centaur# and dimm info1 is DIMM# - uint8_t l_centaur_num = l_data_set->memory_type; - l_dimm_num = l_data_set->dimm_info1; + // Store the hardware sensor ID and the temperature sensor ID + for(i=0; i<l_cmd_ptr->header.num_data_sets; i++) + { + cmdh_mem_cfg_v20_t* l_cmd2_ptr = (cmdh_mem_cfg_v20_t*)i_cmd_ptr; + cmdh_mem_cfg_data_set_v20_t* l_data_set = &l_cmd2_ptr->data_set[i]; - // Validate the centaur and dimm #'s for this data set - if( (l_centaur_num >= MAX_NUM_CENTAURS) || - (l_dimm_num != 0xFF && l_dimm_num >= NUM_DIMMS_PER_CENTAUR) ) + // Verify matching memory type and process based on memory type + if( (l_data_set->memory_type == G_sysConfigData.mem_type) && + (l_data_set->memory_type == MEM_TYPE_NIMBUS) ) { - CMDH_TRAC_ERR("data_store_mem_cfg: Invalid dimm or centaur number. entry=%d, cent=%d, dimm=%d", - i, - l_centaur_num, - l_dimm_num); - cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); - break; - } + // Nimbus type: dimm info is I2C Engine, I2C Port, I2C Address + l_i2c_engine = l_data_set->dimm_info1; + l_i2c_port = l_data_set->dimm_info2; + l_i2c_addr = l_data_set->dimm_info3; + + // Validate the i2c info for this data set. Any failure will result in error and + // memory monitoring disabled. + + // Engine must be the same + if (l_i2c_engine != G_sysConfigData.dimm_i2c_engine) + { + CMDH_TRAC_ERR("data_store_mem_cfg: I2c engine mismatch. entry=%d, engine=%d, expected=%d", + i, + l_i2c_engine, + G_sysConfigData.dimm_i2c_engine); + cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); + break; + } + + // Port must be 0 or 1. + if((l_i2c_port != 0) && (l_i2c_port != 1)) + { + CMDH_TRAC_ERR("data_store_mem_cfg: Invalid I2C port. entry=%d, port=%d", + i, + l_i2c_port); + cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); + break; + } + + // I2C addr must be 0x3z where z is even + if( ( (l_i2c_addr & 0xF0) != 0x30) || + ( (l_i2c_addr & 0x01) != 0 ) ) + { + CMDH_TRAC_ERR("data_store_mem_cfg: Invalid I2C address. entry=%d, addr=%d", + i, + l_i2c_addr); + cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); + break; + } + + // DIMM info is good for this DIMM, save the sensor IDs for the DIMM + // The location of the HW sensor ID in the 2D dimm_huids array is used to know i2c port + // and i2c address for reading the DIMM. "centaur num" index is port "dimm num" is + // translated from i2c address, we already verified the i2c addr is 0x3z above + l_dimm_num = (l_i2c_addr & 0x0F) >> 1; - // Per the spec, if dimm_num = 0xFF then this is a centaur ID - if(l_dimm_num == 0xFF) - { // Store the hardware sensor ID - G_sysConfigData.centaur_huids[l_centaur_num] = l_data_set->hw_sensor_id; + G_sysConfigData.dimm_huids[l_i2c_port][l_dimm_num] = l_data_set->hw_sensor_id; // Store the temperature sensor ID - g_amec->proc[0].memctl[l_centaur_num].centaur.temp_sid = l_data_set->temp_sensor_id; + g_amec->proc[0].memctl[l_i2c_port].centaur.dimm_temps[l_dimm_num].temp_sid = + l_data_set->temp_sensor_id; + + l_num_dimms++; - l_num_centaurs++; } - else + else // must be cumulus and the "memory type" byte is the centaur# { - // Store the hardware sensor ID - G_sysConfigData.dimm_huids[l_centaur_num][l_dimm_num] = l_data_set->hw_sensor_id; + CMDH_TRAC_ERR("data_store_mem_cfg: Invalid mem type 0x%02X in config data packet entry %d (entry 0 type: 0x%02X)", + l_data_set->memory_type, i, G_sysConfigData.mem_type); - // Store the temperature sensor ID - g_amec->proc[0].memctl[l_centaur_num].centaur.dimm_temps[l_dimm_num].temp_sid = - l_data_set->temp_sensor_id; + cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); + break; - l_num_dimms++; - } +#if 0 + // TODO - CUMULUS not supported yet + // per spec for cumulus memory type is the centaur# and dimm info1 is DIMM# + uint8_t l_centaur_num = l_data_set->memory_type; + l_dimm_num = l_data_set->dimm_info1; + + // Validate the centaur and dimm #'s for this data set + if( (l_centaur_num >= MAX_NUM_CENTAURS) || + (l_dimm_num != 0xFF && l_dimm_num >= NUM_DIMMS_PER_CENTAUR) ) + { + CMDH_TRAC_ERR("data_store_mem_cfg: Invalid dimm or centaur number. entry=%d, cent=%d, dimm=%d", + i, + l_centaur_num, + l_dimm_num); + cmdh_build_errl_rsp(i_cmd_ptr, o_rsp_ptr, ERRL_RC_INVALID_DATA, &l_err); + break; + } + + // Per the spec, if dimm_num = 0xFF then this is a centaur ID + if(l_dimm_num == 0xFF) + { + // Store the hardware sensor ID + G_sysConfigData.centaur_huids[l_centaur_num] = l_data_set->hw_sensor_id; + + // Store the temperature sensor ID + g_amec->proc[0].memctl[l_centaur_num].centaur.temp_sid = l_data_set->temp_sensor_id; + + l_num_centaurs++; + } + else + { + // Store the hardware sensor ID + G_sysConfigData.dimm_huids[l_centaur_num][l_dimm_num] = l_data_set->hw_sensor_id; + + // Store the temperature sensor ID + g_amec->proc[0].memctl[l_centaur_num].centaur.dimm_temps[l_dimm_num].temp_sid = + l_data_set->temp_sensor_id; + + l_num_dimms++; + } #endif - } // Cumulus - } // for each data set + } // Cumulus + } // for each data set + } // else no data sets given } // version 0x20 else // version not supported { @@ -1679,11 +1682,11 @@ errlHndl_t data_store_mem_cfg(const cmdh_fsp_cmd_t * i_cmd_ptr, // No errors so we can enable memory monitoring if the data indicates it should be enabled if(l_cmd_ptr->header.num_data_sets == 0) // num data sets of 0 indicates memory monitoring disabled { - CMDH_TRAC_IMP("Memory monitoring is not allowed (mem config data sets = 0)"); + CMDH_TRAC_IMP("Memory monitoring is not allowed (mem config data sets = 0)"); } else { - // This notifies other code that we need to request the mem throt packet + // This notifies other code that we need to request the mem throttle packet // and we need to enable memory monitoring when we enter observation state G_mem_monitoring_allowed = TRUE; diff --git a/src/occ_405/cmdh/cmdh_mnfg_intf.c b/src/occ_405/cmdh/cmdh_mnfg_intf.c index 8fd8feb..7edb43d 100755 --- a/src/occ_405/cmdh/cmdh_mnfg_intf.c +++ b/src/occ_405/cmdh/cmdh_mnfg_intf.c @@ -633,7 +633,8 @@ uint8_t cmdh_mnfg_get_sensor(const cmdh_fsp_cmd_t * i_cmd_ptr, l_resp_ptr->sample = l_sensor_ptr->sample; l_resp_ptr->min = l_sensor_ptr->sample_min; l_resp_ptr->max = l_sensor_ptr->sample_max; - l_resp_ptr->accumulator = l_sensor_ptr->accumulator; + // Truncate accumulator to 4 bytes (should not be used) + l_resp_ptr->accumulator = (uint32_t)l_sensor_ptr->accumulator; l_resp_ptr->status = *(uint8_t*)(&l_sensor_ptr->status); } diff --git a/src/occ_405/cmdh/cmdh_snapshot.c b/src/occ_405/cmdh/cmdh_snapshot.c index 5d73174..d8629b7 100755 --- a/src/occ_405/cmdh/cmdh_snapshot.c +++ b/src/occ_405/cmdh/cmdh_snapshot.c @@ -350,7 +350,8 @@ void cmdh_snapshot_callback(void * arg) static uint32_t L_prev_pwr_accum = 0; // Holds the previous power sensor accumulator static uint32_t L_prev_pwr_update_tag = 0; // Holds the previous power sensor update tag. - uint32_t l_pwr_accum = AMECSENSOR_PTR(PWR250US)->accumulator; + uint32_t l_pwr_accum = + (uint32_t)AMECSENSOR_PTR(PWR250US)->accumulator; uint32_t l_pwr_update_tag = AMECSENSOR_PTR(PWR250US)->update_tag; uint32_t l_avg_pwr = 0; uint32_t l_min_pwr = g_pwr250us_over30sec.min; diff --git a/src/occ_405/sensor/sensor.h b/src/occ_405/sensor/sensor.h index 8fa4e8e..24c518d 100755 --- a/src/occ_405/sensor/sensor.h +++ b/src/occ_405/sensor/sensor.h @@ -146,7 +146,7 @@ struct sensor uint16_t sample; // Latest sample of this sensor uint16_t sample_min; // Minimum value since last reset uint16_t sample_max; // Maximum Value since last reset - uint32_t accumulator; // Accumulator register for this sensor + uint64_t accumulator; // Accumulator register for this sensor uint32_t src_accum_snapshot; // Copy of the source sensor's accumulator // used for time-derived sensors uint32_t update_tag; // Count of the number of 'ticks' that have passed |