From 4ce53f71e9fcf0c22fdb83af31b450db975df233 Mon Sep 17 00:00:00 2001 From: Alvin Wang Date: Tue, 16 Apr 2019 10:57:56 -0500 Subject: Move power_thermal lib to generic Change-Id: I2851b7fa990d7e8c5a2d726b650b4e2fc11f3fe7 Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72525 Tested-by: FSP CI Jenkins Tested-by: Jenkins Server Dev-Ready: STEPHEN GLANCY Tested-by: Hostboot CI Tested-by: HWSV CI Reviewed-by: Louis Stermole Reviewed-by: STEPHEN GLANCY Reviewed-by: Jennifer A. Stofer Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72845 Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Reviewed-by: Christian R. Geddes --- .../procedures/hwp/memory/exp_bulk_pwr_throttles.C | 59 + .../procedures/hwp/memory/exp_bulk_pwr_throttles.H | 39 + .../hwp/memory/exp_bulk_pwr_throttles.mk | 6 + .../procedures/hwp/memory/exp_config_thermal.C | 152 +++ .../procedures/hwp/memory/exp_config_thermal.H | 31 + .../procedures/hwp/memory/exp_config_thermal.mk | 7 + .../hwp/memory/lib/power_thermal/exp_decoder.C | 261 +++++ .../hwp/memory/lib/power_thermal/exp_decoder.H | 58 + .../hwp/memory/lib/power_thermal/exp_throttle.C | 134 +++ .../hwp/memory/lib/power_thermal/exp_throttle.H | 57 + .../memory/lib/power_thermal/exp_throttle_traits.H | 144 +++ .../xml/attribute_info/exp_power_thermal.xml | 164 +++ .../chips/p9/procedures/hwp/memory/lib/dimm/kind.H | 4 +- .../hwp/memory/lib/mss_attribute_accessors.H | 135 +++ .../memory/lib/power_thermal/accessor_wrapper.H | 257 +++++ .../hwp/memory/lib/power_thermal/decoder.C | 177 +-- .../hwp/memory/lib/power_thermal/decoder.H | 275 +---- .../hwp/memory/lib/power_thermal/throttle.C | 821 +------------ .../hwp/memory/lib/power_thermal/throttle.H | 319 +----- .../hwp/memory/lib/power_thermal/throttle_traits.H | 122 ++ .../procedures/hwp/memory/lib/shared/mss_const.H | 5 - .../hwp/memory/p9_mss_bulk_pwr_throttles.C | 6 +- .../hwp/memory/p9_mss_eff_config_thermal.C | 19 +- .../hwp/memory/p9_mss_utils_to_throttle.C | 17 +- .../p9_memory_mss_eff_config_thermal.xml | 189 +-- .../memory/lib/mss_generic_attribute_getters.H | 47 + src/import/generic/memory/lib/utils/dimm/kind.H | 246 ++++ src/import/generic/memory/lib/utils/mss_math.H | 18 + .../memory/lib/utils/power_thermal/gen_decoder.H | 584 ++++++++++ .../memory/lib/utils/power_thermal/gen_throttle.H | 1204 ++++++++++++++++++++ .../lib/utils/power_thermal/gen_throttle_traits.H | 33 + .../memory/lib/utils/shared/mss_generic_consts.H | 8 + .../generic_memory_eff_attributes.xml | 16 + .../generic_memory_mrw_attributes.xml | 132 +++ .../procedures/xml/error_info/generic_error.xml | 229 ++++ 35 files changed, 4243 insertions(+), 1732 deletions(-) (limited to 'src') diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.C b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.C index 28aad7416..880004e23 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.C +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.C @@ -22,3 +22,62 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_bulk_pwr_throttles.C +/// @brief The explorer thermal/power config +/// +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: Memory + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ + /// + /// @brief Set ATTR_EXP_PORT_MAXPOWER, ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_SLOT, ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_PORT + /// @param[in] i_targets vector of OCMB chips + /// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK + /// @note determines the throttle levels based off of the port's power curve, + /// sets the slot throttles to the same + /// @note Enums are POWER for power egulator throttles and THERMAL for thermal throttles + /// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value + /// + fapi2::ReturnCode exp_bulk_pwr_throttles( const std::vector< fapi2::Target >& i_targets, + const mss::throttle_type i_throttle_type) + { + FAPI_INF("Start exp_bulk_pwr_throttles for %s type throttling", + (( i_throttle_type == mss::throttle_type::THERMAL) ? "THERMAL" : "POWER")); + + for ( const auto& l_ocmb : i_targets) + { + FAPI_TRY(mss::power_thermal::pwr_throttles(l_ocmb, i_throttle_type)); + } + + // Equalizes the throttles to the lowest of runtime and the lowest slot-throttle value + FAPI_TRY(mss::power_thermal::equalize_throttles(i_targets, i_throttle_type)); + + FAPI_INF("End exp_bulk_pwr_throttles"); + return fapi2::current_err; + + fapi_try_exit: + FAPI_ERR("Error calculating exp_bulk_pwr_throttles using %s throttling", + ((i_throttle_type == mss::throttle_type::POWER) ? "power" : "thermal")); + return fapi2::current_err; + } + +} //extern C diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.H b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.H index 1112aefb8..79472932b 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.H +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.H @@ -22,3 +22,42 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_bulk_pwr_throttles.H +/// @brief The explorer thermal/power config +/// +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: Memory +#ifndef EXP_BULK_PWR_THROTTLES_H_ +#define EXP_BULK_PWR_THROTTLES_H_ + +#include +#include + +typedef fapi2::ReturnCode (*exp_bulk_pwr_throttles_FP_t) ( const + std::vector< fapi2::Target >&, + const mss::throttle_type i_throttle_type); + +extern "C" +{ + + /// + /// @brief Set ATTR_MSS_PORT_MAXPOWER, ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT + /// @param[in] i_targets vector of OCMB chip + /// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note determines the throttle levels based off of the port's power curve, + /// sets the slot throttles to the same + /// @note Enums are POWER for power egulator throttles and THERMAL for thermal throttles + /// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value + /// + fapi2::ReturnCode exp_bulk_pwr_throttles( const std::vector< fapi2::Target >& i_targets, + const mss::throttle_type i_throttle_type); +} + +#endif diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.mk b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.mk index 6dfbc4fe6..c0ef17e69 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.mk +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_bulk_pwr_throttles.mk @@ -22,3 +22,9 @@ # permissions and limitations under the License. # # IBM_PROLOG_END_TAG + +-include 00exp_common.mk + +PROCEDURE=exp_bulk_pwr_throttles +$(eval $(call ADD_EXP_MEMORY_INCDIRS,$(PROCEDURE))) +$(call BUILD_PROCEDURE) diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.C b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.C index c552c0454..215cc7829 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.C +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.C @@ -22,3 +22,155 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_config_thermal.C +/// @brief The explorer thermal/power config +/// +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: Memory + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ + /// + /// @brief Perform thermal calculations + /// @param[in] i_targets vector of OCMB chip + /// @return FAPI2_RC_SUCCESS iff ok + fapi2::ReturnCode exp_mss_eff_config_thermal( const std::vector< fapi2::Target >& + i_targets ) + { + using TT = mss::power_thermal::throttle_traits<>; + fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; + FAPI_INF("Start exp_mss_eff_config_thermal"); + + // Return error if safemode throttle utilization is less than MIN_UTIL + // This section needs to be in braces otherwise the compile will fail + const uint64_t l_min_util = TT::MIN_UTIL; + uint32_t l_safemode_util = 0; + FAPI_TRY( mss::attr::get_mrw_safemode_dram_databus_util(l_safemode_util), "Error in exp_mss_eff_config_thermal" ); + FAPI_ASSERT( l_safemode_util >= TT::MIN_UTIL, + fapi2::MSS_MRW_SAFEMODE_UTIL_THROTTLE_NOT_SUPPORTED() + .set_MRW_SAFEMODE_UTIL(l_safemode_util) + .set_MIN_UTIL_VALUE(l_min_util), + "MRW safemode util (%d centi percent) has less util than the min util allowed (%d centi percent)", + l_safemode_util, l_min_util ); + + for ( const auto& l_ocmb : i_targets) + { + //Restore runtime_throttles from safemode setting + //Sets throttles to max_databus_util value + FAPI_INF("Restoring throttles for %s", mss::c_str(l_ocmb)); + FAPI_TRY( mss::power_thermal::restore_runtime_throttles<>(l_ocmb), "Error in exp_mss_eff_config_thermal"); + + //Not doing any work if there are no dimms installed + if (mss::count_dimm(l_ocmb) == 0) + { + FAPI_INF("Skipping eff_config thermal because no dimms for %s", mss::c_str(l_ocmb)); + continue; + } + + // Thermal power (OCMB+DRAM) + uint64_t l_thermal_power_limit[TT::SIZE_OF_THERMAL_LIMIT_ATTR] = {0}; + uint64_t l_thermal_power_slope[TT::SIZE_OF_THERMAL_SLOPE_ATTR] = {0}; + uint64_t l_thermal_power_intecept[TT::SIZE_OF_THERMAL_INTERCEPT_ATTR] = {0}; + // Power (PMIC) + uint64_t l_current_curve_with_limit[TT::SIZE_OF_CURRENT_CURVE_WITH_LIMIT_ATTR] = {0}; + + // Get the data from MRW + FAPI_TRY( mss::attr::get_mrw_ocmb_thermal_memory_power_limit (l_thermal_power_limit), + "Error in exp_mss_eff_config_thermal"); + FAPI_TRY( mss::attr::get_mrw_ocmb_pwr_slope (l_thermal_power_slope), "Error in exp_mss_eff_config_thermal"); + FAPI_TRY( mss::attr::get_mrw_ocmb_pwr_intercept (l_thermal_power_intecept), "Error in exp_mss_eff_config_thermal"); + FAPI_TRY( mss::attr::get_mrw_ocmb_current_curve_with_limit (l_current_curve_with_limit), + "Error in exp_mss_eff_config_thermal"); + + // Convert array to vector + using namespace std; + vector l_thermal_power_limit_v ( begin(l_thermal_power_limit), end(l_thermal_power_limit) ); + vector l_thermal_power_slope_v ( begin(l_thermal_power_slope), end(l_thermal_power_slope) ); + vector l_thermal_power_intecept_v ( begin(l_thermal_power_intecept), end(l_thermal_power_intecept) ); + vector l_current_curve_with_limit_v( begin(l_current_curve_with_limit), end(l_current_curve_with_limit) ); + + + uint16_t l_slope [TT::DIMMS_PER_PORT] = {0}; + uint16_t l_intercept[TT::DIMMS_PER_PORT] = {0}; + uint32_t l_limit [TT::DIMMS_PER_PORT] = {0}; + + const std::vector throttle_types{ mss::throttle_type::POWER, mss::throttle_type::THERMAL}; + + for (const auto& l_throttle_type : throttle_types ) + { + for (const auto& l_port : mss::find_targets(l_ocmb)) + { + //Don't run if there are no dimms on the port + if (mss::count_dimm(l_port) == 0) + { + continue; + } + + // Set the thermal power throttle + //Set the PMIC current slope, intercept and limit + mss::power_thermal::get_power_attrs (l_throttle_type, + l_port, + l_thermal_power_slope_v, + l_thermal_power_intecept_v, + l_thermal_power_limit_v, + l_current_curve_with_limit_v, + l_slope, + l_intercept, + l_limit); + + FAPI_TRY(mss::attr::set_total_pwr_slope(l_port, l_slope)); + FAPI_TRY(mss::attr::set_total_pwr_intercept(l_port, l_intercept)); + FAPI_TRY(mss::attr::set_dimm_thermal_limit(l_port, l_limit)); + + FAPI_INF( "PMIC current curve slope is %d, intercept is %d, limit is %d for %s", l_slope[0], l_intercept[0], + l_limit[0], mss::c_str(l_port)); + + FAPI_INF("Starting pwr_throttles(%s)", mss::throttle_type::POWER == l_throttle_type ? "POWER" : "THERMAL"); + //get the power limits, done per dimm and set to worst case for the slot and port throttles + FAPI_TRY(mss::power_thermal::pwr_throttles(l_ocmb, mss::throttle_type::POWER)); + FAPI_TRY(l_rc, "Failed running exp_mss_eff_config_thermal with %s throttling", + mss::throttle_type::POWER == l_throttle_type ? "POWER" : "THERMAL"); + + //Set runtime throttles to worst case between ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_SLOT + //and ATTR_EXP_MEM_RUNTIME_THROTTLED_N_COMMANDS_PER_SLOT and the _PORT equivalents also + FAPI_INF("Starting update"); + FAPI_TRY( mss::power_thermal::update_runtime_throttle(l_ocmb), "Error in exp_mss_eff_config_thermal for %d", + mss::c_str(l_port)); + FAPI_INF("finished update"); + } + } + } + + // Equalizes the throttles to the lowest of runtime and the lowest slot-throttle value + FAPI_TRY(mss::power_thermal::equalize_throttles(i_targets, mss::throttle_type::POWER)); + FAPI_TRY(mss::power_thermal::equalize_throttles(i_targets, mss::throttle_type::THERMAL)); + + //Done + FAPI_INF( "End exp_mss_eff_config_thermal"); + + fapi_try_exit: + return fapi2::current_err; + } + +} //extern C diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.H b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.H index 76f5cd416..dd03ba8c0 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.H +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.H @@ -22,3 +22,34 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_config_thermal.H +/// @brief The explorer thermal/power config +/// +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: Memory +#ifndef EXP_CONFIG_THERMAL_H_ +#define EXP_CONFIG_THERMAL_H_ + +#include + +typedef fapi2::ReturnCode (*exp_mss_eff_config_thermal_FP_t) (const + std::vector< fapi2::Target >&); + +extern "C" +{ + + /// + /// @brief Config the thermal/power throttle setting + /// @param[in] i_target vector of OCMB chip + /// @return FAPI2_RC_SUCCESS iff ok + /// + fapi2::ReturnCode exp_mss_eff_config_thermal( const std::vector< fapi2::Target >& + i_targets); +} + +#endif diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.mk b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.mk index 22abeb618..dfba66804 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.mk +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/exp_config_thermal.mk @@ -22,3 +22,10 @@ # permissions and limitations under the License. # # IBM_PROLOG_END_TAG + +-include 00exp_common.mk + +PROCEDURE=exp_config_thermal +$(eval $(call ADD_EXP_MEMORY_INCDIRS,$(PROCEDURE))) +lib$(PROCEDURE)_DEPLIBS+=exp_bulk_pwr_throttles +$(call BUILD_PROCEDURE) diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.C b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.C index eedfafe84..d08f0a4bb 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.C +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.C @@ -22,3 +22,264 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_decoder.C +/// @brief Decode MSS_MRW_PWR_CURVE_SLOPE, PWR_CURVE_INTERCEPT, and THERMAL_POWER_LIMIT +/// +// *HWP HWP Owner: Louis Stermole +// *HWP HWP Backup: Andre Marin +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#include +// fapi2 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mss +{ +namespace power_thermal +{ + +const std::vector< std::pair > throttle_traits::DIMM_TYPE_MAP = +{ + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_RDIMM, 0b00}, + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_UDIMM, 0b01}, + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_LRDIMM, 0b10}, + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_DDIMM, 0b11}, + {ANY_TYPE, 0b111} +}; + +/// +/// @brief Finds a value for the power curve slope attributes by matching the generated hashes +/// @param[in] i_slope vector of generated key-values from POWER_CURVE_SLOPE +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @note populates iv_vddr_slope, iv_total_slop +/// +template<> +fapi2::ReturnCode decoder::find_slope ( + const std::vector< const std::vector* >& i_slope) +{ + using TT = throttle_traits; + + // For explorer, two attribute are used to get slope (i_slope[0], i_slope[1]) + // ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_PWR_SLOPE is for thermal power slope + // ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT is for power slope + FAPI_ASSERT(i_slope.size() == 2, + fapi2::MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT() + .set_FUNCTION(SLOPE) + .set_INPUT_SIZE(i_slope.size()) + .set_EXPECTED_SIZE(2), + "The attributes vectors size is incorrect for find_slope input:%d, expected:%d", + i_slope.size(), + 2); + + // To get thermal power slope + get_power_thermal_value( + *i_slope[0], + "ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_PWR_SLOPE", + iv_total_slope); + + // To get power slope + get_power_thermal_value( + *i_slope[1], + "ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT", + iv_vddr_slope); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Finds a value for power curve intercept attributes by matching the generated hashes +/// @param[in] i_intercept vector of generated key-values for ATTR_MSS_MRW_POWER_CURVE_INTERCEPT +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @note populates iv_vddr_intercept, iv_total_intercept +/// +template<> +fapi2::ReturnCode decoder::find_intercept ( + const std::vector< const std::vector* >& i_intercept) +{ + using TT = throttle_traits; + + // For explorer, two attribute are used to get slope (i_slope[0], i_slope[1]) + // ATTR_MSS_MRW_OCMB_POWER_INTERCEPT is for thermal power intercept + // ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT is for power intercept + FAPI_ASSERT(i_intercept.size() == 2, + fapi2::MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT() + .set_FUNCTION(INTERCEPT) + .set_INPUT_SIZE(i_intercept.size()) + .set_EXPECTED_SIZE(2), + "The attributes vectors size is incorrect for find_intercept input:%d, expected:%d", + i_intercept.size(), + 2); + + // To get thermal power intercept + get_power_thermal_value( + *i_intercept[0], + "ATTR_MSS_MRW_OCMB_POWER_INTERCEPT", + iv_total_intercept); + + // To get power intercept + get_power_thermal_value( + *i_intercept[1], + "ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT", + iv_vddr_intercept); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Finds a value from ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT and stores in iv variable +/// @param[in] i_thermal_limits is a vector of the generated values from ATTR_MSS_MRW_THERMAL_POWER_LIMIT +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @note populates thermal_power_limit. +/// +template<> +fapi2::ReturnCode decoder::find_thermal_power_limit ( + const std::vector< const std::vector* >& i_thermal_limits) +{ + using TT = throttle_traits; + + // For explorer, two attribute are used to get slope (i_slope[0], i_slope[1]) + // ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT is for thermal power limit + // ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT is for power limit + FAPI_ASSERT(i_thermal_limits.size() == 2, + fapi2::MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT() + .set_FUNCTION(INTERCEPT) + .set_INPUT_SIZE(i_thermal_limits.size()) + .set_EXPECTED_SIZE(2), + "The attributes vectors size is incorrect for find_thermal_power_limit input:%d, expected:%d", + i_thermal_limits.size(), + 2); + + // To get thermal power limit + get_power_thermal_value( + *i_thermal_limits[0], + "ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT", + iv_thermal_power_limit); + + // To get power intercept + get_power_thermal_value( + *i_thermal_limits[1], + "ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT", + iv_power_limit); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief find the power curve attributes for each dimm on an MCS target +/// @param[in] i_targets vector of MCS targets on which dimm attrs will be set +/// @param[in] i_slope vector of generated hashes for encoding and values for MSS_MRW_POWER_SLOPE +/// @param[in] i_intercept vector of generated hashes for encoding and values for MSS_MRW_POWER_INTERCEPT +/// @param[in] i_thermal_power_limit vector of generated hashes for encoding and values for MSS_MRW_THERMAL_MEMORY_POWER_LIMIT +/// @param[out] o_vddr_slope the VDDR power curve slope for each dimm +/// @param[out] o_vddr_int the VDDR power curve intercept for each dimm +/// @param[out] o_total_slope the VDDR+VPP power curve slope for each dimm +/// @param[out] o_total_int the VDDR+VPP power curve intercept for each dimm +/// @param[out] o_thermal_power the thermal power limit for the dimm +/// @return FAPI2_RC_SUCCESS iff ok +/// @note used to set power curve attributes in calling function +/// @note decodes the attribute "encoding" to get the vddr and vddr/vpp power curves for a dimm +/// +fapi2::ReturnCode get_power_attrs (const mss::throttle_type i_throttle_type, + const fapi2::Target& i_port, + const std::vector< uint64_t >& i_slope, + const std::vector< uint64_t >& i_intercept, + const std::vector< uint64_t >& i_thermal_power_limit, + const std::vector< uint64_t >& i_current_curve_with_limit, + uint16_t o_slope [throttle_traits<>::DIMMS_PER_PORT], + uint16_t o_intercept [throttle_traits<>::DIMMS_PER_PORT], + uint32_t o_limit [throttle_traits<>::DIMMS_PER_PORT]) +{ + using TT = throttle_traits; + + for (const auto& l_dimm : find_targets (i_port)) + { + const auto l_dimm_pos = mss::index (l_dimm); + mss::dimm::kind l_kind (l_dimm); + mss::power_thermal::decoder<> l_decoder(l_kind); + + FAPI_TRY( l_decoder.generate_encoding(), "%s Error in get_power_attrs", mss::c_str(l_dimm) ); + + // The first entry into these arrays must be valid + // If we don't find any values, the attributes aren't found so go with some defaults + if (i_slope.empty() || i_slope[0] == 0) + { + FAPI_INF("%s ATTR_MSS_MRW_OCMB_PWR_SLOPE not found!!", mss::c_str(l_dimm)); + + o_slope[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? TT::POWER_SLOPE : TT::TOTAL_SLOPE; + } + else + { + const std::vector< const std::vector* > l_slope {&i_slope, &i_current_curve_with_limit}; + + FAPI_TRY( l_decoder.find_slope(l_slope), "%s Error in get_power_attrs", mss::c_str(l_dimm) ); + + o_slope[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? l_decoder.iv_vddr_slope : l_decoder.iv_total_slope; + } + + if (i_intercept.empty() || i_intercept[0] == 0) + { + FAPI_INF("%s ATTR_MSS_MRW_OCMB_PWR_INTERCEPT not found!!", mss::c_str(l_dimm)); + + o_intercept[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? TT::POWER_INT : TT::TOTAL_INT; + } + else + { + const std::vector< const std::vector* > l_intercept {&i_intercept, &i_current_curve_with_limit}; + + FAPI_TRY( l_decoder.find_intercept(l_intercept), "%s Error in get_power_attrs", mss::c_str(l_dimm) ); + + o_intercept[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? l_decoder.iv_vddr_intercept : l_decoder.iv_total_intercept; + } + + if (i_thermal_power_limit.empty() || i_thermal_power_limit[0] == 0) + { + FAPI_INF("%s ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT not found!!", mss::c_str(l_dimm)); + + // The unit of limit and intercept is cA but limit is dA in mss::throttle_type::POWER + // So we need to transfer them to the same unit + o_limit[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? TT::POWER_LIMIT * DECI_TO_CENTI : TT::THERMAL_LIMIT; + } + else + { + std::vector< const std::vector* > l_thermal_power_limit {&i_thermal_power_limit, &i_current_curve_with_limit}; + + FAPI_TRY( l_decoder.find_thermal_power_limit(l_thermal_power_limit), + "%s Error in get_power_attrs", mss::c_str(l_dimm) ); + // The unit of limit and intercept is cA but limit is dA in mss::throttle_type::POWER + // So we need to transfer them to the same unit + o_limit[l_dimm_pos] = + (i_throttle_type == mss::throttle_type::POWER) ? l_decoder.iv_power_limit * DECI_TO_CENTI : + l_decoder.iv_thermal_power_limit; + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +} //ns power_thermal +} // ns mss diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.H b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.H index 3ce2f3ed3..9f49ee70f 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.H +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_decoder.H @@ -22,3 +22,61 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_decoder.H +/// @brief Decoder for ATTR_MSS_MRW_PWR_CURVE_SLOPE and _INTERCEPT and THERMAL_POWER_LIMIT +/// +// *HWP HWP Owner: Louis Stermole +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_POWER_EXP_DECODER__ +#define _MSS_POWER_EXP_DECODER__ + +#include + +#include +#include +#include +#include + + +namespace mss +{ + +namespace power_thermal +{ + + +/// +/// @brief find the power curve attributes for each dimm on an MCS target +/// @param[in] i_targets vector of MCS targets on which dimm attrs will be set +/// @param[in] i_slope vector of generated hashes for encoding and values for MSS_MRW_POWER_SLOPE +/// @param[in] i_intercept vector of generated hashes for encoding and values for MSS_MRW_POWER_INTERCEPT +/// @param[in] i_thermal_power_limit vector of generated hashes for encoding and values for MSS_MRW_THERMAL_MEMORY_POWER_LIMIT +/// @param[out] o_vddr_slope the VDDR power curve slope for each dimm +/// @param[out] o_vddr_int the VDDR power curve intercept for each dimm +/// @param[out] o_total_slope the VDDR+VPP power curve slope for each dimm +/// @param[out] o_total_int the VDDR+VPP power curve intercept for each dimm +/// @param[out] o_thermal_power the thermal power limit for the dimm +/// @return FAPI2_RC_SUCCESS iff ok +/// @note used to set power curve attributes in calling function +/// @note decodes the attribute "encoding" to get the vddr and vddr/vpp power curves for a dimm +/// +fapi2::ReturnCode get_power_attrs (const mss::throttle_type i_throttle_type, + const fapi2::Target& i_port, + const std::vector< uint64_t >& i_slope, + const std::vector< uint64_t >& i_intercept, + const std::vector< uint64_t >& i_thermal_power_limit, + const std::vector< uint64_t >& i_current_curve_with_limit, + uint16_t o_slope [throttle_traits<>::DIMMS_PER_PORT], + uint16_t o_intercept [throttle_traits<>::DIMMS_PER_PORT], + uint32_t o_limit [throttle_traits<>::DIMMS_PER_PORT]); + + +} // power_thermal +} // mss +#endif diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.C b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.C index b3114480e..0e95aebb7 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.C +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.C @@ -22,3 +22,137 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_throttle.C +/// @brief Determine throttle settings for memory +/// +// *HWP HWP Owner: Andre A. Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#include +// fapi2 +#include + +#include +#include +#include +#include +#include + +namespace mss +{ +namespace power_thermal +{ +/// +/// @brief Calcuate the throttle values based on throttle type +/// @param[in] i_target +/// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note determines the throttle levels based off of the port's power curve, +/// sets the slot throttles to the same +/// @note Enums are POWER for power egulator throttles and THERMAL for thermal throttles +/// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value +/// +fapi2::ReturnCode pwr_throttles( const fapi2::Target& i_target, + const mss::throttle_type i_throttle_type) +{ + FAPI_INF("Start exp_bulk_pwr_throttle for %s type throttling for %s", + (( i_throttle_type == mss::throttle_type::THERMAL) ? "THERMAL" : "POWER"), mss::c_str(i_target)); + + if (mss::count_dimm (i_target) == 0) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + uint16_t l_slot = 0; + uint16_t l_port = 0; + uint32_t l_power = 0; + + for (const auto& l_port_target : mss::find_targets(i_target)) + { + fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; + + //Don't run if there are no dimms on the port + if (mss::count_dimm(l_port_target) == 0) + { + continue; + } + + mss::power_thermal::throttle<> l_pwr_struct(l_port_target, l_rc); + FAPI_TRY(l_rc, "Error constructing mss:power_thermal::throttle object for target %s", + mss::c_str(l_port_target)); + + //Let's do the actual work now + if ( i_throttle_type == mss::throttle_type::THERMAL) + { + FAPI_TRY (l_pwr_struct.thermal_throttles()); + } + else + { + FAPI_TRY (l_pwr_struct.power_regulator_throttles()); + } + + l_slot = l_pwr_struct.iv_n_slot; + l_port = l_pwr_struct.iv_n_port; + l_power = l_pwr_struct.iv_calc_port_maxpower; + + FAPI_INF("For target %s Calculated power is %d, throttle per slot is %d, throttle per port is %d", + mss::c_str(l_port_target), l_power, l_slot, l_port); + + FAPI_TRY(mss::attr::set_port_maxpower( l_port_target, l_power)); + FAPI_TRY(mss::attr::set_mem_throttled_n_commands_per_slot( l_port_target, l_slot)); + FAPI_TRY(mss::attr::set_mem_throttled_n_commands_per_port( l_port_target, l_port)); + } + + FAPI_INF("End bulk_pwr_throttles for %s", mss::c_str(i_target)); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("Error calculating bulk_pwr_throttles using %s throttling", + ((i_throttle_type == mss::throttle_type::POWER) ? "power" : "thermal")); + return fapi2::current_err; +} + +/// +/// @brief Equalize the throttles among OCMB chips +/// @param[in] i_targets vector of OCMB chips +/// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value +/// +fapi2::ReturnCode equalize_throttles( const std::vector< fapi2::Target >& i_targets, + const mss::throttle_type i_throttle_type) +{ + FAPI_INF("Start equalize_throttles for %s type throttling", + (( i_throttle_type == mss::throttle_type::THERMAL) ? "THERMAL" : "POWER")); + + std::vector< fapi2::Target > l_exceeded_power; + + // Set all of the throttles to the lowest value per port for performance reasons + FAPI_TRY(mss::power_thermal::equalize_throttles(i_targets, i_throttle_type, l_exceeded_power)); + + // Report any port that exceeded the max power limit, and return a failing RC if we have any + for (const auto& l_port : l_exceeded_power) + { + FAPI_ERR(" MEM_PORT %s estimated power exceeded the maximum allowed", mss::c_str(l_port) ); + fapi2::current_err = fapi2::FAPI2_RC_FALSE; + } + + FAPI_INF("End equalize_throttles"); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("Error calculating equalize_throttles using %s throttling", + ((i_throttle_type == mss::throttle_type::POWER) ? "power" : "thermal")); + return fapi2::current_err; +} + + + +}//namespace power_thermal +}//namespace mss diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.H b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.H index 83559db83..683e484e0 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.H +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle.H @@ -22,3 +22,60 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_throttle.H +/// @brief throttle API +/// + +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_EXP_THROTTLE_ +#define _MSS_EXP_THROTTLE_ + +#include +#include +#include +#include +#include +#include +#include + +namespace mss +{ +namespace power_thermal +{ + +/// +/// @brief Set ATTR_EXP_PORT_MAXPOWER, ATTR_OCMB_EXP_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, +/// ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_PORT +/// @param[in] i_target +/// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note determines the throttle levels based off of the port's power curve, +/// sets the slot throttles to the same +/// @note Enums are POWER for power egulator throttles and THERMAL for thermal throttles +/// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value +/// +fapi2::ReturnCode pwr_throttles( const fapi2::Target& i_target, + const mss::throttle_type i_throttle_type); + +/// +/// @brief Equalize the throttles among OCMB chips +/// @param[in] i_targets vector of OCMB chips +/// @param[in] i_throttle_type thermal boolean to determine whether to calculate throttles based on the power regulator or thermal limits +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note equalizes the throttles to the lowest of runtime and the lowest slot-throttle value +/// +fapi2::ReturnCode equalize_throttles( const std::vector< fapi2::Target >& i_targets, + const mss::throttle_type i_throttle_type); + +}//power_thermal +}// mss + +#endif diff --git a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle_traits.H b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle_traits.H index 1c8945f89..d782ce050 100644 --- a/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle_traits.H +++ b/src/import/chips/ocmb/explorer/procedures/hwp/memory/lib/power_thermal/exp_throttle_traits.H @@ -22,3 +22,147 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file exp_throttle_traits.H +/// @brief throttle API +/// + +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_EXP_THROTTLE_TRAITS_ +#define _MSS_EXP_THROTTLE_TRAITS_ + +#include +#include + +namespace mss +{ +namespace power_thermal +{ + +/// +/// @class Traits and policy class for throttle code - specialization for the Explorer mc type +/// +template<> +class throttle_traits +{ + public: + ////////////////////////////////////////////////////////////// + // Target types + ////////////////////////////////////////////////////////////// + static constexpr fapi2::TargetType MC_TARGET_TYPE = fapi2::TARGET_TYPE_OCMB_CHIP; + static constexpr fapi2::TargetType PORT_TARGET_TYPE = fapi2::TARGET_TYPE_MEM_PORT; + + ////////////////////////////////////////////////////////////// + // Traits values + ////////////////////////////////////////////////////////////// + // MIN_UTIL is in c% + static const uint64_t MIN_UTIL = 2500; + // IDLE_UTIL is in c% + static const uint64_t IDLE_UTIL = 0; + // Minimum throttle allowed for the port and or slot. If we set to 0, we brick the port + static const uint64_t MIN_THROTTLE = 1; + + enum size_of_attrs : size_t + { + // Thermal power (OCMB+DRAM) + SIZE_OF_THERMAL_LIMIT_ATTR = 25, + SIZE_OF_THERMAL_SLOPE_ATTR = 50, + SIZE_OF_THERMAL_INTERCEPT_ATTR = 50, + + // Power (PMIC) + SIZE_OF_CURRENT_CURVE_WITH_LIMIT_ATTR = 25, + }; + + enum default_power + { + //Values are the worst case defaults for power curves + //They are the default/ catch-all values from the power curve attributes + //Shouldn't be used if system is set up correctly and attributes are available + //This will throttle the DIMMs to ~76% dram data bus utilization + //(using the mrw regulator power limit of 1700 cW and thermal power limit here of 1940 cW). + // TODO: Need to fill the right value. The current value is from Nimbus + // Thermal power (OCMB+DRAM) + TOTAL_SLOPE = 0x44C, + TOTAL_INT = 0x44C, + THERMAL_LIMIT = 0x794, + + // Power (PMIC) + POWER_SLOPE = 0x41A, + POWER_INT = 0x384, + POWER_LIMIT = 0x794, + + }; + + enum + { + PORTS_PER_MC = 1, + DIMMS_PER_PORT = 2, + }; + + //Bit positions for different section of the attribute + //first 32 bits are the encoding, second are for values + enum DECODE_BUFFER_POS + { + ENCODING_START = 0, + ENCODING_LENGTH = 32, + + // Thermal total power (OCMB+DRAM) + THERMAL_START = 32, + THERMAL_LENGTH = 16, + + // Current power (PMIC) + POWER_LIMIT_START = 32, + POWER_LIMIT_LENGTH = 8, + POWER_SLOPE_START = 40, + POWER_SLOPE_LENGTH = 12, + POWER_INTERCEPT_START = 52, + POWER_INTERCEPT_LENGTH = 12, + }; + + //Positions and lengths of the encodings + enum ATTR_DECODE_INFO + { + DIMM_SIZE_START = 0, + DIMM_SIZE_LEN = 4, + + DRAM_GEN_START = 4, + DRAM_GEN_LEN = 2, + + DIMM_TYPE_START = 6, + DIMM_TYPE_LEN = 3, + + DRAM_WIDTH_START = 9, + DRAM_WIDTH_LEN = 3, + + DRAM_DENSITY_START = 12, + DRAM_DENSITY_LEN = 3, + + DRAM_STACK_TYPE_START = 15, + DRAM_STACK_TYPE_LEN = 2, + + DRAM_MFGID_START = 17, + DRAM_MFGID_LEN = 3, + + DIMM_MODULE_HEIGHT_START = 20, + DIMM_MODULE_HEIGHT_LEN = 2, + + // Invalid for Explorer but compile will fail without them + DIMMS_PER_PORT_START = 0, + DIMMS_PER_PORT_LEN = 1, + }; + + // Definition is in chip folder + static const std::vector< std::pair > DIMM_TYPE_MAP; + +}; +}//power_thermal +}// mss + + +#endif diff --git a/src/import/chips/ocmb/explorer/procedures/xml/attribute_info/exp_power_thermal.xml b/src/import/chips/ocmb/explorer/procedures/xml/attribute_info/exp_power_thermal.xml index eaa83be48..8b50d2594 100644 --- a/src/import/chips/ocmb/explorer/procedures/xml/attribute_info/exp_power_thermal.xml +++ b/src/import/chips/ocmb/explorer/procedures/xml/attribute_info/exp_power_thermal.xml @@ -23,4 +23,168 @@ + + ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_PORT + TARGET_TYPE_MEM_PORT + + This is the throttled N commands per window + of M DRAM clocks setting for cfg_nm_n_per_port. + + + uint16 + + mem_throttled_n_commands_per_port + + + + ATTR_EXP_MEM_THROTTLED_N_COMMANDS_PER_SLOT + TARGET_TYPE_MEM_PORT + This is the throttle numerator setting for cfg_nm_n_per_slot + + uint16 + + mem_throttled_n_commands_per_slot + + + + ATTR_EXP_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT + TARGET_TYPE_MEM_PORT + + Runtime throttled N commands per + M DRAM clocks setting for cfg_nm_n_per_port. + + + uint16 + + runtime_mem_throttled_n_commands_per_port + + + + ATTR_EXP_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT + TARGET_TYPE_MEM_PORT + Runtime throttle numerator setting for cfg_nm_n_per_slot + + uint16 + + runtime_mem_throttled_n_commands_per_slot + + + + ATTR_EXP_SAFEMODE_MEM_THROTTLED_N_COMMANDS_PER_PORT + TARGET_TYPE_MEM_PORT + + Safe mode throttle value for numerator cfg_nm_n_per_port + Set to below optimum value/ rate. + On a per port basis + Also used for emergency mode throttle MBA_FARB4Q_EMERGENCY_N + Used to thermally protect the system in all supported environmental conditions when OCC is not functional + + uint16 + 32 + + + safemode_mem_throttled_n_commands_per_port + + + + ATTR_EXP_RUNTIME_MEM_M_DRAM_CLOCKS + TARGET_TYPE_MEM_PORT + Runtime for M DRAM clocks setting for cfg_nm_m + + uint32 + + + + + ATTR_EXP_MEM_PORT_POS_OF_FAIL_THROTTLE + TARGET_TYPE_SYSTEM + + This is the fapi position of the port that failed to calculate + memory throttles given the passed in watt target and or utilization + + + uint64 + + + + + ATTR_EXP_MEM_WATT_TARGET + TARGET_TYPE_MEM_PORT + + Total memory power used to throttle for each dimm + Used to compute the throttles on the channel and/or dimms for OCC + OCC sets after IPL + + + cW + uint32 + + 2 + mem_watt_target + + + + ATTR_EXP_TOTAL_PWR_SLOPE + TARGET_TYPE_MEM_PORT + + VDDR+VPP Power slope value for dimm + creator: mss_eff_config + consumer: mss_bulk_pwr_throttles + + + uint16 + + 2 + total_pwr_slope + + + + ATTR_EXP_TOTAL_PWR_INTERCEPT + TARGET_TYPE_MEM_PORT + + VDDR+VPP Power intercept value for dimm + + + uint16 + + 2 + total_pwr_intercept + + + + ATTR_EXP_DATABUS_UTIL + TARGET_TYPE_MEM_PORT + + Databus utilization per port limit used to calculate memory throttles and power limit + + + uint32 + + databus_util + + + + ATTR_EXP_PORT_MAXPOWER + TARGET_TYPE_MEM_PORT + Channel Pair Max Power output from thermal procedures + + uint32 + + port_maxpower + + + + ATTR_EXP_DIMM_THERMAL_LIMIT + TARGET_TYPE_MEM_PORT + + DIMM Max Power based on a thermal limit + Decoded from ATTR_MSS_MRW_THERMAL_POWER_LIMIT + + + uint32 + + cW + 2 + dimm_thermal_limit + diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/kind.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/kind.H index ee707c821..eb7873253 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/kind.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/kind.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2018 */ +/* Contributors Listed Below - COPYRIGHT 2016,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -237,6 +237,8 @@ class kind uint8_t iv_hybrid; uint8_t iv_hybrid_memory_type; uint16_t iv_rcd_mfgid; + // This is just a workaround. There is another task that will port kind library to generic folder + uint8_t iv_module_height; /// /// @brief equal_config diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mss_attribute_accessors.H b/src/import/chips/p9/procedures/hwp/memory/lib/mss_attribute_accessors.H index c482989dd..8d6458ed3 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mss_attribute_accessors.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mss_attribute_accessors.H @@ -21501,6 +21501,141 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT getter +/// @param[out] uint64_t* memory to store the value +/// @note Generated by gen_accessors.pl generateParameters (SYSTEM A) +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Machine Readable Workbook Thermal Memory Power Limit Used to calculate throttles +/// to be at or under the power limit Per DIMM basis KEY (0-21): In order DIMM_SIZE +/// = bits 0-3, DIMM_GEN = 4-5, DIMM_TYPE = 6-8, DIMM_WIDTH = 9-11, DIMM_DENSITY = +/// 12-14, DIMM_STACK_TYPE = 15-16, DRAM_MFGID = 17-19, DIMM_HEIGHT = 20-21, Bits +/// 22-32: Not used VALUE (bits 32-47) in cW: OCMB+DRAM thermal power limit per DIMM +/// = +/// 32-47 +/// +inline fapi2::ReturnCode mrw_ocmb_thermal_memory_power_limit(uint64_t* o_array) +{ + uint64_t l_value[25]; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT, fapi2::Target(), + l_value) ); + memcpy(o_array, &l_value, 200); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT: 0x%lx (system target)", + uint64_t(fapi2::current_err)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MRW_OCMB_PWR_SLOPE getter +/// @param[out] uint64_t* memory to store the value +/// @note Generated by gen_accessors.pl generateParameters (SYSTEM A) +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Machine Readable Workbook Power Curve Slope for DIMM Used to get the OCMB+DRAM +/// power curve for each DIMM Per DIMM basis KEY (0-21): In order DIMM_SIZE = bits +/// 0-3, DIMM_GEN = 4-5, DIMM_TYPE = 6-8, DIMM_WIDTH = 9-11, DIMM_DENSITY = 12-14, +/// DIMM_STACK_TYPE = 15-16, DRAM_MFGID = 17-19, DIMM_HEIGHT = 20-21, Bits 22-32: +/// Not used VALUE (bits 32-47) in cW/utilization: OCMB+DRAM thermal power limit per +/// DIMM = +/// 32-47 +/// +inline fapi2::ReturnCode mrw_ocmb_pwr_slope(uint64_t* o_array) +{ + uint64_t l_value[50]; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_OCMB_PWR_SLOPE, fapi2::Target(), l_value) ); + memcpy(o_array, &l_value, 400); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_MRW_OCMB_PWR_SLOPE: 0x%lx (system target)", + uint64_t(fapi2::current_err)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MRW_OCMB_PWR_INTERCEPT getter +/// @param[out] uint64_t* memory to store the value +/// @note Generated by gen_accessors.pl generateParameters (SYSTEM A) +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Machine Readable Workbook Power Curve Intercept for DIMM Used to get the +/// OCMB+DRAM power curve for each DIMM Per DIMM basis KEY (0-21): In order +/// DIMM_SIZE = bits 0-3, DIMM_GEN = 4-5, DIMM_TYPE = 6-8, DIMM_WIDTH = 9-11, +/// DIMM_DENSITY = 12-14, DIMM_STACK_TYPE = 15-16, DRAM_MFGID = 17-19, DIMM_HEIGHT = +/// 20-21, Bits 22-32: Not used VALUE (bits 32-47) in cW/utilization: OCMB+DRAM +/// thermal power limit per DIMM = +/// 32-47 +/// +inline fapi2::ReturnCode mrw_ocmb_pwr_intercept(uint64_t* o_array) +{ + uint64_t l_value[50]; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_OCMB_PWR_INTERCEPT, fapi2::Target(), l_value) ); + memcpy(o_array, &l_value, 400); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_MRW_OCMB_PWR_INTERCEPT: 0x%lx (system target)", + uint64_t(fapi2::current_err)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT getter +/// @param[out] uint64_t* memory to store the value +/// @note Generated by gen_accessors.pl generateParameters (SYSTEM A) +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Machine Readable Workbook Power Curve Intercept and limit for DIMM Used to get +/// the PMIC power curve and limit for each DIMM Per DIMM basis KEY (0-21): In order +/// DIMM_SIZE = bits 0-3, DIMM_GEN = 4-5, DIMM_TYPE = 6-8, DIMM_WIDTH = 9-11, +/// DIMM_DENSITY = 12-14, DIMM_STACK_TYPE = 15-16, DRAM_MFGID = 17-19, DIMM_HEIGHT = +/// 20-21, Bits 22-32: Not used VALUE (bits 32-39): Current limit (dA) VALUE (bits +/// 40-51): Current slope (cA/utilization) VALUE (bits 52-63): Current intercept +/// (cA) +/// +inline fapi2::ReturnCode mrw_ocmb_current_curve_with_limit(uint64_t* o_array) +{ + uint64_t l_value[25]; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT, fapi2::Target(), + l_value) ); + memcpy(o_array, &l_value, 200); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT: 0x%lx (system target)", + uint64_t(fapi2::current_err)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL getter +/// @param[out] uint32_t& reference to store the value +/// @note Generated by gen_accessors.pl generateParameters (SYSTEM) +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Machine Readable Workbook value for safe mode dram data bus utilization in centi +/// percent (c%). Set to below optimum value/ rate. On a per port basis Also used +/// for emergency mode throttle Used to thermally protect the system in all +/// supported environmental conditions when OCC is not functional Consumer: +/// thermal_init, initfile Default to 2500 +/// c%% +/// +inline fapi2::ReturnCode mrw_safemode_dram_databus_util(uint32_t& o_value) +{ + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL, fapi2::Target(), + o_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed accessing ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL: 0x%lx (system target)", + uint64_t(fapi2::current_err)); + return fapi2::current_err; +} + } diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/accessor_wrapper.H b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/accessor_wrapper.H index a9cebab3a..fd0991a7a 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/accessor_wrapper.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/accessor_wrapper.H @@ -22,3 +22,260 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ +/// +/// @file accessor_wrapper.H +/// @brief The wrapper of new accessor API(get_/set_) to old accessor API +/// + +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_ACCESSOR_WRAPPER_ +#define _MSS_ACCESSOR_WRAPPER_ + +#include +#include +#include + + +namespace mss +{ +namespace attr +{ +/// +/// @brief ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t& reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Runtime throttle numerator setting for cfg_nm_n_per_slot +/// +inline fapi2::ReturnCode get_runtime_mem_throttled_n_commands_per_slot(const fapi2::Target& + i_target, uint16_t& o_value) +{ + return mss::runtime_mem_throttled_n_commands_per_slot(i_target, o_value); +} + +/// +/// @brief ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT setter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[in] uint16_t the value to set +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff set is OK +/// @note Runtime throttle numerator setting for cfg_nm_n_per_slot +/// +inline fapi2::ReturnCode set_runtime_mem_throttled_n_commands_per_slot(const fapi2::Target& + i_target, uint16_t i_value) +{ + uint16_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT, + i_target.getParent(), l_value) ); + l_value[mss::index(i_target)] = i_value; + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT, + i_target.getParent(), l_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed setting ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + + +/// +/// @brief ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t& reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Runtime throttled N commands per M DRAM clocks setting for cfg_nm_n_per_port. +/// +inline fapi2::ReturnCode get_runtime_mem_throttled_n_commands_per_port(const fapi2::Target& + i_target, uint16_t& o_value) +{ + return mss::runtime_mem_throttled_n_commands_per_port(i_target, o_value); +} + +/// +/// @brief ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT setter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[in] uint16_t the value to set +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff set is OK +/// @note Runtime throttled N commands per M DRAM clocks setting for cfg_nm_n_per_port. +/// +inline fapi2::ReturnCode set_runtime_mem_throttled_n_commands_per_port(const fapi2::Target& + i_target, uint16_t i_value) +{ + uint16_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT, + i_target.getParent(), l_value) ); + l_value[mss::index(i_target)] = i_value; + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT, + i_target.getParent(), l_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed setting ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t& reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note This is the throttle numerator setting for cfg_nm_n_per_slot +/// +inline fapi2::ReturnCode get_mem_throttled_n_commands_per_slot(const fapi2::Target& i_target, + uint16_t& o_value) +{ + return mss::mem_throttled_n_commands_per_slot(i_target, o_value); +} + +/// +/// @brief ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT setter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[in] uint16_t the value to set +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff set is OK +/// @note This is the throttled N commands per window of M DRAM clocks setting for cfg_nm_n_per_port. +/// +inline fapi2::ReturnCode set_mem_throttled_n_commands_per_port(const fapi2::Target& i_target, + uint16_t i_value) +{ + uint16_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT, i_target.getParent(), + l_value) ); + l_value[mss::index(i_target)] = i_value; + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT, i_target.getParent(), + l_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed setting ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t& reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note This is the throttled N commands per window of M DRAM clocks setting for cfg_nm_n_per_port. +/// +inline fapi2::ReturnCode get_mem_throttled_n_commands_per_port(const fapi2::Target& i_target, + uint16_t& o_value) +{ + return mss::mem_throttled_n_commands_per_port(i_target, o_value); +} + +/// +/// @brief ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT setter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[in] uint16_t the value to set +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff set is OK +/// @note This is the throttle numerator setting for cfg_nm_n_per_slot +/// +inline fapi2::ReturnCode set_mem_throttled_n_commands_per_slot(const fapi2::Target& i_target, + uint16_t i_value) +{ + uint16_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, i_target.getParent(), + l_value) ); + l_value[mss::index(i_target)] = i_value; + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, i_target.getParent(), + l_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed setting ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MSS_PORT_MAXPOWER setter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[in] uint32_t the value to set +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff set is OK +/// @note Channel Pair Max Power output from thermal procedures +/// +inline fapi2::ReturnCode set_port_maxpower(const fapi2::Target& i_target, uint32_t i_value) +{ + uint32_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MSS_PORT_MAXPOWER, i_target.getParent(), l_value) ); + l_value[mss::index(i_target)] = i_value; + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_MSS_PORT_MAXPOWER, i_target.getParent(), l_value) ); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed setting ATTR_MSS_PORT_MAXPOWER: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + + +/// +/// @brief ATTR_MSS_DIMM_THERMAL_LIMIT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint32_t&[] array reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note DIMM Max Power based on a thermal limit Decoded from ATTR_MSS_MRW_THERMAL_POWER_LIMIT +/// +inline fapi2::ReturnCode get_dimm_thermal_limit(const fapi2::Target& i_target, + uint32_t (&o_array)[2]) +{ + return mss::dimm_thermal_limit(i_target, o_array); +} + +/// +/// @brief ATTR_MSS_TOTAL_PWR_INTERCEPT getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t&[] array reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note VDDR+VPP Power intercept value for dimm creator: mss_eff_config consumer: mss_bulk_pwr_throttles +/// +inline fapi2::ReturnCode get_total_pwr_intercept(const fapi2::Target& i_target, + uint16_t (&o_array)[2]) +{ + return total_pwr_intercept(i_target, o_array); +} + +/// +/// @brief ATTR_MSS_TOTAL_PWR_SLOPE getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint16_t&[] array reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note VDDR+VPP Power slope value for dimm creator: mss_eff_config consumer: mss_bulk_pwr_throttles +/// +inline fapi2::ReturnCode get_total_pwr_slope(const fapi2::Target& i_target, + uint16_t (&o_array)[2]) +{ + return mss::total_pwr_slope(i_target, o_array); +} + +/// +/// @brief ATTR_MSS_MEM_WATT_TARGET getter +/// @param[in] const ref to the TARGET_TYPE_MCA +/// @param[out] uint32_t&[] array reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Total memory power used to throttle for each dimm Used to compute the throttles +/// on the channel and/or dimms for OCC OCC sets after IPL creator: mss_eff_config consumer: +/// mss_bulk_pwr_throttle, mss_utils_to_throttle firmware notes: none. +/// +inline fapi2::ReturnCode get_mem_watt_target(const fapi2::Target& i_target, + uint32_t& o_value) +{ + return mss::mem_watt_target(i_target, o_value); +} + +}// namespace attr +}// namespace mss + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.C b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.C index b381042a2..13e2cafe3 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2018 */ +/* Contributors Listed Below - COPYRIGHT 2016,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -32,6 +32,7 @@ // *HWP Level: 3 // *HWP Consumed by: FSP:HB +#include // fapi2 #include #include @@ -44,100 +45,21 @@ #include #include #include +#include #include #include -using fapi2::TARGET_TYPE_MCA; -using fapi2::TARGET_TYPE_MCS; -using fapi2::TARGET_TYPE_DIMM; -using fapi2::TARGET_TYPE_MCBIST; - namespace mss { namespace power_thermal { -/// -/// @brief generates the 32 bit encoding for the power curve attributes -/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful -/// @note populates iv_gen_keys -/// -fapi2::ReturnCode decoder::generate_encoding() -{ - //DIMM_SIZE - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_size, DIMM_SIZE_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DIMM_SIZE", iv_kind.iv_size, c_str(iv_kind.iv_target) ); - - //DRAM_GEN - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_dram_generation, DRAM_GEN_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DRAM_GEN", iv_kind.iv_dram_generation, c_str(iv_kind.iv_target) ); - - //DIMM_TYPE - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_dimm_type, DIMM_TYPE_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DIMM_TYPE", iv_kind.iv_dimm_type, c_str(iv_kind.iv_target) ); - - //DRAM WIDTH - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_dram_width, DRAM_WIDTH_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DRAM_WIDTH", iv_kind.iv_dram_width, c_str(iv_kind.iv_target) ); - - //DRAM DENSITY - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_dram_density, DRAM_DENSITY_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DRAM_DENSITY", iv_kind.iv_dram_density, c_str(iv_kind.iv_target) ); - - //DRAM STACK TYPE - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_stack_type, DRAM_STACK_TYPE_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DRAM_STACK_TYPE", iv_kind.iv_stack_type, c_str(iv_kind.iv_target) ); - - //DRAM MFG ID - FAPI_TRY(( encode - (iv_kind.iv_target, iv_kind.iv_mfgid, DRAM_MFGID_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DRAM_MFG_ID", iv_kind.iv_mfgid, c_str(iv_kind.iv_target) ); - - //NUM DROPS PER PORT - FAPI_TRY(( encode - (iv_kind.iv_target, iv_dimms_per_port, DIMMS_PORT_MAP, iv_gen_key)), - "Failed to generate power thermal encoding for %s val %d on target: %s", - "DIMMS_PER_PORT", iv_dimms_per_port, c_str(iv_kind.iv_target) ); - -fapi_try_exit: - return fapi2::current_err; -} -/// -///@brief a compare functor for the decoder::find_attr functions below -///@note AND's the input hash with the generated hash from the DIMM to see if they match -///@note Using an AND instead of == because not all DIMM configs have power slopes, so defaults are needed -/// -struct is_match +const std::vector< std::pair > throttle_traits::DIMM_TYPE_MAP = { - /// - ///@brief functor constructor - ///@param[in] i_gen_key the class object's constructed hash for the installed dimm, to be compared with the attr array - /// - is_match(const uint32_t i_gen_key) : iv_gen_key(i_gen_key) {} - const fapi2::buffer iv_gen_key; - - /// - ///@brief Boolean compare used for find_if function - /// - bool operator()(const uint64_t i_hash) - { - // l_this_hash is the first half of the i_hash's bits - uint32_t l_this_hash = i_hash >> 32; - return ((l_this_hash & iv_gen_key) == iv_gen_key); - } + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_RDIMM, 0b00}, + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_UDIMM, 0b01}, + {fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_LRDIMM, 0b10}, + {ANY_TYPE, 0b11} }; /// @@ -146,16 +68,21 @@ struct is_match /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful /// @note populates iv_vddr_slope, iv_total_slop /// -fapi2::ReturnCode decoder::find_slope (const std::vector& i_slope) +template<> +fapi2::ReturnCode decoder::find_slope ( + const std::vector< const std::vector* >& i_slope) { + using TT = throttle_traits; + + // For nimbus only one attribute is used to get slope (i_slope[0]) // Find iterator to matching key (if it exists) - const auto l_value_iterator = std::find_if(i_slope.begin(), - i_slope.end(), - is_match(iv_gen_key)); + const auto l_value_iterator = std::find_if((*i_slope[0]).begin(), + (*i_slope[0]).end(), + is_match<>(iv_gen_key)); //Should have matched with the default ATTR value at least //The last value should always be the default value - FAPI_ASSERT(l_value_iterator != i_slope.end(), + FAPI_ASSERT(l_value_iterator != (*i_slope[0]).end(), fapi2::MSS_NO_POWER_THERMAL_ATTR_FOUND() .set_GENERATED_KEY(iv_gen_key) .set_FUNCTION(SLOPE) @@ -184,8 +111,8 @@ fapi2::ReturnCode decoder::find_slope (const std::vector& i_slope) { const fapi2::buffer l_temp(*l_value_iterator); - l_temp.extractToRight( iv_vddr_slope); - l_temp.extractToRight(iv_total_slope); + l_temp.extractToRight( iv_vddr_slope); + l_temp.extractToRight(iv_total_slope); } fapi_try_exit: return fapi2::current_err; @@ -197,15 +124,19 @@ fapi_try_exit: /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful /// @note populates iv_vddr_intercept, iv_total_intercept /// -fapi2::ReturnCode decoder::find_intercept (const std::vector& i_intercept) +template<> +fapi2::ReturnCode decoder::find_intercept ( + const std::vector< const std::vector* >& i_intercept) { + using TT = throttle_traits; + // Find iterator to matching key (if it exists) - const auto l_value_iterator = std::find_if(i_intercept.begin(), - i_intercept.end(), - is_match(iv_gen_key)); + const auto l_value_iterator = std::find_if((*i_intercept[0]).begin(), + (*i_intercept[0]).end(), + is_match<>(iv_gen_key)); //Should have matched with the all default ATTR at least //The last value should always be the default value - FAPI_ASSERT(l_value_iterator != i_intercept.end(), + FAPI_ASSERT(l_value_iterator != (*i_intercept[0]).end(), fapi2::MSS_NO_POWER_THERMAL_ATTR_FOUND() .set_GENERATED_KEY(iv_gen_key) .set_FUNCTION(INTERCEPT) @@ -234,31 +165,36 @@ fapi2::ReturnCode decoder::find_intercept (const std::vector& i_interc { const fapi2::buffer l_temp(*l_value_iterator); - l_temp.extractToRight( iv_vddr_intercept); - l_temp.extractToRight(iv_total_intercept); + l_temp.extractToRight( iv_vddr_intercept); + l_temp.extractToRight(iv_total_intercept); } fapi_try_exit: return fapi2::current_err; } + /// /// @brief Finds a value from ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT and stores in iv variable /// @param[in] i_thermal_limits is a vector of the generated values from ATTR_MSS_MRW_THERMAL_POWER_LIMIT /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful -/// @note populates thermal_power_limit +/// @note populates thermal_power_limit. /// -fapi2::ReturnCode decoder::find_thermal_power_limit (const std::vector& i_thermal_limits) +template<> +fapi2::ReturnCode decoder::find_thermal_power_limit ( + const std::vector< const std::vector* >& i_thermal_limits) { + using TT = throttle_traits; + // Find iterator to matching key (if it exists) - const auto l_value_iterator = std::find_if(i_thermal_limits.begin(), - i_thermal_limits.end(), - is_match(iv_gen_key)); + const auto l_value_iterator = std::find_if((*i_thermal_limits[0]).begin(), + (*i_thermal_limits[0]).end(), + is_match<>(iv_gen_key)); fapi2::buffer l_temp; //Should have matched with the all default ATTR at least //The last value should always be the default value - FAPI_ASSERT(l_value_iterator != i_thermal_limits.end(), + FAPI_ASSERT(l_value_iterator != (*i_thermal_limits[0]).end(), fapi2::MSS_NO_POWER_THERMAL_ATTR_FOUND() .set_GENERATED_KEY(iv_gen_key) .set_FUNCTION(POWER_LIMIT) @@ -287,11 +223,13 @@ fapi2::ReturnCode decoder::find_thermal_power_limit (const std::vector { const fapi2::buffer l_temp(*l_value_iterator); - l_temp.extractToRight( iv_thermal_power_limit); + l_temp.extractToRight( iv_thermal_power_limit); } fapi_try_exit: return fapi2::current_err; } + + /// /// @brief find the power curve attributes for each dimm on an MCS target /// @param[in] i_targets vector of MCS targets on which dimm attrs will be set @@ -317,13 +255,15 @@ fapi2::ReturnCode get_power_attrs (const fapi2::Target& uint16_t o_total_int [PORTS_PER_MCS][MAX_DIMM_PER_PORT], uint32_t o_thermal_power [PORTS_PER_MCS][MAX_DIMM_PER_PORT]) { + using TT = throttle_traits; + for (const auto& l_dimm : find_targets (i_mcs)) { - const auto l_mca_pos = mss::index (find_target(l_dimm)); + const auto l_mca_pos = mss::index (find_target(l_dimm)); const auto l_dimm_pos = mss::index (l_dimm); mss::dimm::kind l_kind (l_dimm); - mss::power_thermal::decoder l_decoder(l_kind); + mss::power_thermal::decoder<> l_decoder(l_kind); FAPI_TRY( l_decoder.generate_encoding(), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); // The first entry into these arrays must be valid @@ -331,12 +271,13 @@ fapi2::ReturnCode get_power_attrs (const fapi2::Target& if (i_slope.empty() || i_slope[0] == 0) { FAPI_INF("%s ATTR_MSS_MRW_PWR_SLOPE not found!!", mss::c_str(i_mcs)); - o_vddr_slope [l_mca_pos][l_dimm_pos] = default_power::VDDR_SLOPE; - o_total_slope [l_mca_pos][l_dimm_pos] = default_power::TOTAL_SLOPE; + o_vddr_slope [l_mca_pos][l_dimm_pos] = TT::VDDR_SLOPE; + o_total_slope [l_mca_pos][l_dimm_pos] = TT::TOTAL_SLOPE; } else { - FAPI_TRY( l_decoder.find_slope(i_slope), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); + const std::vector< const std::vector* > l_slope {&i_slope}; + FAPI_TRY( l_decoder.find_slope(l_slope), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); o_vddr_slope [l_mca_pos][l_dimm_pos] = l_decoder.iv_vddr_slope; o_total_slope [l_mca_pos][l_dimm_pos] = l_decoder.iv_total_slope; } @@ -344,12 +285,13 @@ fapi2::ReturnCode get_power_attrs (const fapi2::Target& if (i_intercept.empty() || i_intercept[0] == 0) { FAPI_INF("%s ATTR_MSS_MRW_PWR_INTERCEPT not found!!", mss::c_str(i_mcs)); - o_total_int [l_mca_pos][l_dimm_pos] = default_power::TOTAL_INT; - o_vddr_int [l_mca_pos][l_dimm_pos] = default_power::VDDR_INT; + o_total_int [l_mca_pos][l_dimm_pos] = TT::TOTAL_INT; + o_vddr_int [l_mca_pos][l_dimm_pos] = TT::VDDR_INT; } else { - FAPI_TRY( l_decoder.find_intercept(i_intercept), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); + std::vector< const std::vector* > l_intercept {&i_intercept}; + FAPI_TRY( l_decoder.find_intercept(l_intercept), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); o_vddr_int [l_mca_pos][l_dimm_pos] = l_decoder.iv_vddr_intercept; o_total_int [l_mca_pos][l_dimm_pos] = l_decoder.iv_total_intercept; } @@ -357,11 +299,12 @@ fapi2::ReturnCode get_power_attrs (const fapi2::Target& if (i_thermal_power_limit.empty() || i_thermal_power_limit[0] == 0) { FAPI_INF("%s ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT not found!!", mss::c_str(i_mcs)); - o_thermal_power [l_mca_pos][l_dimm_pos] = default_power::THERMAL_LIMIT; + o_thermal_power [l_mca_pos][l_dimm_pos] = TT::THERMAL_LIMIT; } else { - FAPI_TRY( l_decoder.find_thermal_power_limit(i_thermal_power_limit), + std::vector< const std::vector* > l_thermal_power_limit {&i_thermal_power_limit}; + FAPI_TRY( l_decoder.find_thermal_power_limit(l_thermal_power_limit), "%s Error in get_power_attrs", mss::c_str(i_mcs) ); o_thermal_power [l_mca_pos][l_dimm_pos] = l_decoder.iv_thermal_power_limit; } diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.H b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.H index ac1efcdbe..f26421ea4 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/decoder.H @@ -36,222 +36,17 @@ #define _MSS_POWER_DECODER__ #include +#include #include #include +#include + namespace mss { namespace power_thermal { -enum size_of_attrs : size_t -{ - SIZE_OF_POWER_CURVES_ATTRS = 100, - SIZE_OF_THERMAL_ATTR = 10, -}; - -enum default_power -{ - //Values are the worst case defaults for power curves - //They are the default/ catch-all values from the power curve attributes - //Shouldn't be used if system is set up correctly and attributes are available - //This will throttle the DIMMs to ~76% dram data bus utilization - //(using the mrw regulator power limit of 1700 cW and thermal power limit here of 1940 cW). - VDDR_SLOPE = 0x41A, - VDDR_INT = 0x384, - TOTAL_SLOPE = 0x44C, - TOTAL_INT = 0x44C, - THERMAL_LIMIT = 0x794, -}; - -//Currently needs to be in sorted order for lookup to work -static const std::vector< std::pair > DIMM_SIZE_MAP = -{ - {4, 0b0000}, - {8, 0b0001}, - {16, 0b0010}, - {32, 0b0011}, - {64, 0b0100}, - {128, 0b0101}, - {256, 0b0110}, - {512, 0b0111}, -}; - -static const std::vector< std::pair > DIMM_TYPE_MAP = -{ - {fapi2::ENUM_ATTR_EFF_DIMM_TYPE_RDIMM, 0b00}, - {fapi2::ENUM_ATTR_EFF_DIMM_TYPE_UDIMM, 0b01}, - {fapi2::ENUM_ATTR_EFF_DIMM_TYPE_LRDIMM, 0b10}, -}; - -static const std::vector< std::pair > DRAM_GEN_MAP = -{ - {fapi2::ENUM_ATTR_EFF_DRAM_GEN_EMPTY, 0b00}, - {fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR3, 0b01}, - {fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR4, 0b10} -}; - -static const std::vector > DRAM_WIDTH_MAP = -{ - {fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4, 0b000}, - {fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8, 0b001}, - {fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16, 0b010}, - {fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X32, 0b011} -}; - -static const std::vector< std::pair > DRAM_DENSITY_MAP = -{ - {4, 0b000}, - {8, 0b001}, - {16, 0b010}, - {32, 0b011}, - {64, 0b100}, -}; - -static const std::vector > DRAM_STACK_TYPE_MAP = -{ - {fapi2::ENUM_ATTR_EFF_PRIM_STACK_TYPE_SDP, 0b00}, - {fapi2::ENUM_ATTR_EFF_PRIM_STACK_TYPE_DDP_QDP, 0b01}, - {fapi2::ENUM_ATTR_EFF_PRIM_STACK_TYPE_3DS, 0b10} -}; - -//Note, the first entries of the pairs need to be in sorted order!! -static const std::vector > DRAM_MFGID_MAP = -{ - //Kingston - {0x0198, 0b011}, - //A-data - {0x04CB, 0b101}, - //Micron - {0x802C, 0b000}, - //HYNIX - {0x80AD, 0b001}, - //SAMSUNG - {0x80CE, 0b010}, - //Innodisk - {0x86F1, 0b100}, -}; - -static const std::vector < std::pair< uint8_t, uint8_t> > DIMMS_PORT_MAP = -{ - //Num dimms per MCA, only 1 or 2 possible options. 0 is no-op - {1, 0b00}, - {2, 0b01} -}; - -//Bit positions for different section of the attribute -//first 32 bits are the encoding, second are for values -enum DECODE_BUFFER_POS -{ - ENCODING_START = 0, - ENCODING_LENGTH = 32, - VDDR_START = 32, - VDDR_LENGTH = 16, - TOTAL_START = 48, - TOTAL_LENGTH = 16, - THERMAL_POWER_START = 32, - THERMAL_POWER_LENGTH = 32, -}; - -//Positions and lengths of the encodings -enum ATTR_DECODE_INFO -{ - DIMM_SIZE_START = 0, - DIMM_SIZE_LEN = 4, - - DRAM_GEN_START = 4, - DRAM_GEN_LEN = 2, - - DIMM_TYPE_START = 6, - DIMM_TYPE_LEN = 2, - - DRAM_WIDTH_START = 8, - DRAM_WIDTH_LEN = 3, - - DRAM_DENSITY_START = 11, - DRAM_DENSITY_LEN = 3, - - DRAM_STACK_TYPE_START = 14, - DRAM_STACK_TYPE_LEN = 2, - - DRAM_MFGID_START = 16, - DRAM_MFGID_LEN = 3, - - DIMMS_PER_PORT_START = 19, - DIMMS_PER_PORT_LEN = 2, -}; - -/// -/// @class decoder -/// @brief Decodes the power curve and thermal power limit attributes for eff_config_thermal -/// -class decoder -{ - public: - - //IVs for all of the attributes per MCS - const mss::dimm::kind iv_kind; - - //Left in here rather than calculating during encode for testing - uint8_t iv_dimms_per_port; - - //Power thermal attributes, both total and vddr versions will be used in eff_config_thermal - uint16_t iv_vddr_slope; - uint16_t iv_vddr_intercept; - uint16_t iv_total_slope; - uint16_t iv_total_intercept; - - uint32_t iv_thermal_power_limit; - //Generated key, used to decode all three power curve attributes - fapi2::buffer iv_gen_key; - - /// - /// @brief Constructor - /// @param[in] dimm::kind to call power thermal stuff on - /// - decoder( mss::dimm::kind& i_kind): - iv_kind(i_kind) - { - iv_dimms_per_port = mss::count_dimm (find_target(iv_kind.iv_target)); - }; - - // - // @brief Default destructor - // - ~decoder() = default; - - /// - /// @brief generates the 32 bit encoding for the power curve attributes - /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful - /// @note populates iv_gen_key - /// - fapi2::ReturnCode generate_encoding (); - - /// - /// @brief Finds a value for the power curve slope attributes by matching the generated hashes - /// @param[in] i_array is a vector of the attribute values - /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful - /// @note populates iv_vddr_slope, iv_total_slope - /// - fapi2::ReturnCode find_slope (const std::vector& i_slope); - - /// - /// @brief Finds a value for power curve intercept attributes by matching the generated hashes - /// @param[in] i_array is a vector of the attribute values - /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful - /// @note populates iv_vddr_intercept, iv_total_intercept - /// - fapi2::ReturnCode find_intercept (const std::vector& i_intercept); - - /// - /// @brief Finds a value from ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT and stores in iv variable - /// @param[in] i_array is a vector of the attribute values - /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful - /// @note populates iv_thermal_power_limit - /// - fapi2::ReturnCode find_thermal_power_limit (const std::vector& i_thermal_limits); - -}; /// /// @brief find the power curve attributes for each dimm on an MCS target @@ -277,70 +72,6 @@ fapi2::ReturnCode get_power_attrs (const fapi2::Target& uint16_t o_total_slope [PORTS_PER_MCS][MAX_DIMM_PER_PORT], uint16_t o_total_int [PORTS_PER_MCS][MAX_DIMM_PER_PORT], uint32_t o_thermal_power [PORTS_PER_MCS][MAX_DIMM_PER_PORT]); -/// -/// @brief Encode the attribute into a bit encoding -/// @tparam[in] S *ATTR*_SIZE enum used for fapi2::buffer position -/// @tparam[in] L *ATTR*_LEN enum used for fapi2::buffer position -/// @tparam[in] OT fapi2::buffer of some integral type -/// @tparam[in] T integral type of key -/// @param[in] i_target the DIMM the encoding is for -/// @param[in] i_attr the attribute key being used for the encoding -/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value, sorted -/// @param[out] o_buf the fapi2::buffer where the encoding is going into -/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup -/// -template -inline fapi2::ReturnCode encode ( const fapi2::Target& i_target, - const T& i_attr, - const std::vector >& i_map, - fapi2::buffer& o_buf) -{ - //used to hold result from vector pair lookup - OT l_encoding = 0; - - //Failing out if we don't find an encoding. All suported types should be encoded above - FAPI_ASSERT( mss::find_value_from_key (i_map, i_attr, l_encoding), - fapi2::MSS_POWER_THERMAL_ENCODE_ERROR() - .set_ATTR(i_attr) - .set_DIMM_TARGET(i_target), - "Couldn't find encoding for power thermal encode for value: %x target: %s", i_attr, mss::c_str(i_target)); - o_buf.insertFromRight(l_encoding); - -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Decode the attribute into a bit encoding -/// @tparam[in] S DRAM_GEN_SIZE enum used for fapi2::buffer position -/// @tparam[in] L DRAM_GEN_LEN enum used for fapi2::buffer position -/// @tparam[in] OT fapi2::buffer of some integral type -/// @tparam[in] T integral type of key -/// @param[in] i_target the DIMM the encoding is for -/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value -/// @param[in] i_buf the fapi2::buffer that has the encoding to parse -/// @param[out] o_attr the attribute value from the encoding is going -/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup -/// -template -inline fapi2::ReturnCode decode ( const fapi2::Target& i_target, - const std::vector >& i_map, - fapi2::buffer& i_buf, - T& o_attr ) -{ - //used to hold result from vector pair lookup - OT l_encoding = 0; - i_buf.extractToRight(l_encoding); - - //Failing out if we don't find an decoding. All suported types should be encoded above - FAPI_ASSERT( mss::find_key_from_value (i_map, l_encoding, o_attr), - fapi2::MSS_POWER_THERMAL_DECODE_ERROR() - .set_ATTR(l_encoding) - .set_DIMM_TARGET(i_target), - "Couldn't find encoding for power thermal decode for target: %s", mss::c_str(i_target)); -fapi_try_exit: - return fapi2::current_err; -} } // power_thermal } // mss diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.C b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.C index 82a5e2c6e..0ef22bddd 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.C @@ -31,6 +31,8 @@ // *HWP Team: Memory // *HWP Level: 3 // *HWP Consumed by: FSP:HB + +#include //std lib #include // fapi2 @@ -41,686 +43,11 @@ #include #include -using fapi2::TARGET_TYPE_MCA; -using fapi2::TARGET_TYPE_MCS; -using fapi2::TARGET_TYPE_DIMM; -using fapi2::TARGET_TYPE_MCBIST; - namespace mss { namespace power_thermal { -/// -/// @brief Constructor -/// @param[in] i_target MCS target to call power thermal stuff on -/// @param[out] o_rc, a return code which determines the success of the constructor -/// -throttle::throttle( const fapi2::Target& i_mca, fapi2::ReturnCode& o_rc) : - iv_target(i_mca), - iv_databus_port_max(0), - iv_runtime_n_slot(0), - iv_runtime_n_port(0), - iv_n_slot(0), - iv_n_port(0), - iv_port_power_limit(0), - iv_calc_port_maxpower(0) -{ - //holder for watt_target to add up for port - uint32_t l_dimm_power_limits [MAX_DIMM_PER_PORT] = {}; - - FAPI_TRY( mrw_max_dram_databus_util(iv_databus_port_max), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( mrw_dimm_power_curve_percent_uplift(iv_power_uplift), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( mrw_dimm_power_curve_percent_uplift_idle(iv_power_uplift_idle), "%s Error in throttle ctor", - mss::c_str(i_mca) ); - FAPI_TRY( dimm_thermal_limit( iv_target, iv_dimm_thermal_limit), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( total_pwr_intercept( iv_target, iv_pwr_int), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( total_pwr_slope( iv_target, iv_pwr_slope), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( runtime_mem_throttled_n_commands_per_slot(iv_target, iv_runtime_n_slot ), "%s Error in throttle ctor", - mss::c_str(i_mca) ); - FAPI_TRY( runtime_mem_throttled_n_commands_per_port(iv_target, iv_runtime_n_port ), "%s Error in throttle ctor", - mss::c_str(i_mca) ); - FAPI_TRY( mem_watt_target( iv_target, l_dimm_power_limits), "%s Error in throttle ctor", mss::c_str(i_mca) ); - FAPI_TRY( mrw_mem_m_dram_clocks(iv_m_clocks), "%s Error in throttle ctor", mss::c_str(i_mca) ); - - //Port power limit = sum of dimm power limits - for ( const auto& l_dimm : mss::find_targets(iv_target) ) - { - iv_port_power_limit += l_dimm_power_limits[mss::index(l_dimm)]; - } - - FAPI_INF("Setting up throttle for target %s, Values are: max databus is %d, uplifts are %d %d, runtime throttles are %d %d", - mss::c_str(iv_target), - iv_databus_port_max, - iv_power_uplift, - iv_power_uplift_idle, - iv_runtime_n_slot, - iv_runtime_n_port); - - FAPI_INF("The dimm power limit for dimm0 is %d, dimm1 is %d, port power limit is %d, dram clocks are %d, dimm power curve slopes are %d %d,", - l_dimm_power_limits[0], - l_dimm_power_limits[1], - iv_port_power_limit, - iv_m_clocks, - iv_pwr_slope[0], - iv_pwr_slope[1]); - - FAPI_INF("DIMM power curve intercepts are %d %d, DIMM power thermal limits are %d %d", - iv_pwr_int[0], - iv_pwr_int[1], - iv_dimm_thermal_limit[0], - iv_dimm_thermal_limit[1]); - - FAPI_ASSERT( (iv_databus_port_max != 0), - fapi2::MSS_NO_DATABUS_UTILIZATION() - .set_PORT_DATABUS_UTIL(iv_databus_port_max) - .set_DIMM_COUNT(mss::count_dimm(iv_target)), - "Failed to get max databus utilization for target %s", - mss::c_str(iv_target)); - - FAPI_ASSERT( (iv_port_power_limit != 0), - fapi2::MSS_NO_PORT_POWER_LIMIT() - .set_COUNT_DIMMS( mss::count_dimm(iv_target)) - .set_PORT_POWER_LIMIT( iv_port_power_limit), - "Error calculating port_power_limit on target %s with %d DIMMs installed", - mss::c_str(iv_target), - iv_port_power_limit); - - //Checking to make sure all of the attributes are valid - for ( const auto& l_dimm : mss::find_targets(iv_target) ) - { - const auto l_pos = mss::index(l_dimm); - FAPI_ASSERT( (iv_pwr_int[l_pos] != 0), - fapi2::MSS_POWER_INTERCEPT_NOT_SET(), - "The attribute ATTR_MSS_TOTAL_PWR_INTERCEPT equals 0 for %s", - mss::c_str(l_dimm)); - - FAPI_ASSERT( (iv_pwr_slope[l_pos] != 0), - fapi2::MSS_POWER_SLOPE_NOT_SET(), - "The attribute ATTR_MSS_TOTAL_PWR_SLOPE equals 0 for %s", - mss::c_str(l_dimm)); - } - -fapi_try_exit: - o_rc = fapi2::current_err; - return; -} - -/// -/// @brief Set ATTR_MSS_CHANNEL_PAIR_MAXPOWER, ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and _PER_PORT -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK -/// @note Called in p9_mss_bulk_pwr_throttles -/// @note determines the throttle levels based off of the port's power curve, -/// @note the _per_slot throttles are set to the _per_port values -/// @note throttles are all equalized and set to the worst case value -/// -fapi2::ReturnCode throttle::power_regulator_throttles () -{ - double l_port_power_calc_idle = 0; - double l_port_power_calc_max = 0; - uint32_t l_port_power_slope = 0; - uint32_t l_port_power_int = 0; - double l_calc_util_port = 0; - double l_databus_dimm_max[MAX_DIMM_PER_PORT] = {}; - double l_calc_databus_port_idle[MAX_DIMM_PER_PORT] = {IDLE_UTIL, IDLE_UTIL}; - - FAPI_INF("Starting power regulator throttles"); - - //Decide utilization for each dimm based off of dimm count and power slopes - FAPI_TRY( calc_databus(iv_databus_port_max, l_databus_dimm_max), - "Failed to calculate each DIMMs' percentage of dram databus utilization for target %s, max port databus is %d", - mss::c_str(iv_target), - iv_databus_port_max); - - //Use the dimm utilizations and dimm power slopes to calculate port min and max power - FAPI_TRY( calc_port_power(l_calc_databus_port_idle, - l_databus_dimm_max, - l_port_power_calc_idle, - l_port_power_calc_max), - "Failed to calculate the max and idle power for port %s", - mss::c_str(iv_target)); - - FAPI_INF("POWER throttles: %s max port power is %f", mss::c_str(iv_target), l_port_power_calc_max); - - //Calculate the power curve slope and intercept using the port's min and max power values - FAPI_TRY(calc_power_curve(l_port_power_calc_idle, - l_port_power_calc_max, - l_port_power_slope, - l_port_power_int), - "Failed to calculate the power curve for port %s, calculated port power max is %d, idle is %d", - mss::c_str(iv_target), - l_port_power_calc_max, - l_port_power_calc_idle); - - FAPI_INF("%s POWER Port power limit is %d", mss::c_str(iv_target), iv_port_power_limit); - //Calculate the port's utilization to get under watt target using the port's calculated slopes - calc_util_usage(l_port_power_slope, - l_port_power_int, - iv_port_power_limit, - l_calc_util_port); - - FAPI_INF("%s POWER calc util port is %f", mss::c_str(iv_target), l_calc_util_port); - - //Calculate the new slot values and the max power value for the port - FAPI_TRY( calc_slots_and_power( l_calc_util_port), - "%s Error calculating the final throttles and power values for target with passed in port utilization %d", - mss::c_str(iv_target), - l_calc_util_port); - -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower -/// @param[in] i_util_port pass in the calculated port databus utilization -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK -/// -fapi2::ReturnCode throttle::calc_slots_and_power (const double i_util_port) -{ - //Calculate the Port N throttles - iv_n_port = power_thermal::throttled_cmds(i_util_port, iv_m_clocks); - - //Set iv_n_slot to the lower value between the slot runtime and iv_n_port - iv_n_slot = (iv_runtime_n_slot != 0) ? std::min (iv_n_port, iv_runtime_n_slot) : iv_n_port; - - //Choose the lowest value of the runtime and the calculated - iv_n_port = (iv_runtime_n_port != 0) ? std::min (iv_n_port, iv_runtime_n_port) : iv_n_port; - - //Use the throttle value to calculate the power that gets to exactly that value - FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower)); - -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and PER_PORT -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK -/// @note Called in p9_mss_bulk_pwr_throttles -/// @note Sets the throttle levels based off of the dimm's thermal limits -/// @note both DIMM's on a port are set to the same throttle level -/// -fapi2::ReturnCode throttle::thermal_throttles () -{ - double l_dimm_power_idle [MAX_DIMM_PER_PORT] = {}; - double l_dimm_power_max [MAX_DIMM_PER_PORT] = {}; - uint32_t l_dimm_power_slope [MAX_DIMM_PER_PORT] = {}; - uint32_t l_dimm_power_int [MAX_DIMM_PER_PORT] = {}; - double l_calc_util [MAX_DIMM_PER_PORT] = {}; - const auto l_count = count_dimm (iv_target); - - //Calculate the dimm power range for each dimm at max utilization for each - calc_dimm_power(power_thermal::IDLE_UTIL, - iv_databus_port_max, - l_dimm_power_idle, - l_dimm_power_max); - - //Let's calculate the N throttle for each DIMM - for ( const auto& l_dimm : mss::find_targets(iv_target) ) - { - uint16_t l_temp_n_slot = 0; - uint8_t l_pos = mss::index(l_dimm); - //Calculate the power curve taking the thermal limit into account - FAPI_TRY( calc_power_curve(l_dimm_power_idle[l_pos], - l_dimm_power_max[l_pos], - l_dimm_power_slope[l_pos], - l_dimm_power_int[l_pos]), - "Failed to calculate the power curve for dimm %s, calculated dimm power curve slope is %d, intercept %d", - mss::c_str(l_dimm), - l_dimm_power_slope[l_pos], - l_dimm_power_int[l_pos]); - - //Calculate the databus utilization at the calculated power curve - calc_util_usage(l_dimm_power_slope[l_pos], - l_dimm_power_int[l_pos], - iv_dimm_thermal_limit[l_pos], - l_calc_util[l_pos]); - - FAPI_INF("THERMAL throttles: %s dram databus utilization is %f", mss::c_str(l_dimm), l_calc_util[l_pos]); - - l_temp_n_slot = power_thermal::throttled_cmds (l_calc_util[l_pos], iv_m_clocks); - - //Set to the min between the two value - //If iv_n_slot == 0 (so uninitialized), set it to the calculated slot value - //The l_n_slot value can't be equal to 0 because there's a dimm installed - if ((l_temp_n_slot < iv_n_slot) || (iv_n_slot == 0)) - { - iv_n_slot = l_temp_n_slot; - } - } - - //Set to lowest value between calculated and runtime - FAPI_INF("THERMAL throttles: runtime slot is %d, calc n slot is %d", iv_runtime_n_slot, iv_n_slot); - //Taking the min of the SLOT * (# of dimms on the port) and the iv_runtime_port throttle value - //Thermal throttling happens after the POWER calculations. the iv_runtime_n_port value shouldn't be set to 0 - iv_n_port = std::min(iv_runtime_n_port, static_cast(iv_n_slot * l_count)); - iv_n_port = (iv_n_port == 0) ? MIN_THROTTLE : iv_n_port; - - iv_n_slot = std::min(iv_n_slot, iv_runtime_n_slot); - iv_n_slot = (iv_n_slot == 0) ? MIN_THROTTLE : iv_n_slot; - - //Now time to get and set iv_calc_port_max from the calculated N throttle - FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower), - "Failed to calculate the final max port maxpower. Slot throttle value is %d, port value is %d", - iv_n_slot, - iv_n_port); - - return fapi2::FAPI2_RC_SUCCESS; -fapi_try_exit: - FAPI_ERR("Error calculating mss::power_thermal::thermal_throttles()"); - return fapi2::current_err; -} - -/// -/// @brief Calculates the min and max power usage for a port based off of power curves and utilizations -/// @param[in] i_idle_util the utilization of the databus in idle mode (0% most likely) -/// @param[in] i_max_util the utilization of the dimm at maximum possible percentage (mrw or calculated) -/// @param[out] o_port_power_idle max value of port power in cW -/// @param[out] o_port_power_max max value of port power in cW -/// @return fapi2::FAPI2_RC_SUCCESS iff the method was a success -/// @note Called twice in p9_mss_bulk_pwr_throttles -/// @note uses dimm power curves from class variables -/// -fapi2::ReturnCode throttle::calc_port_power(const double i_idle_util [MAX_DIMM_PER_PORT], - const double i_max_util [MAX_DIMM_PER_PORT], - double& o_port_power_idle, - double& o_port_power_max) const -{ - //Playing it safe - o_port_power_idle = 0; - o_port_power_max = 0; - - //Calculate the port power curve info by summing the dimms on the port - for ( const auto& l_dimm : mss::find_targets(iv_target) ) - { - const auto l_pos = mss::index(l_dimm); - //Printing as decimals because HB messes up floats - FAPI_INF("%s max dram databus for DIMM in pos %d is %d, databus for idle is %d", - mss::c_str(iv_target), - l_pos, - static_cast( i_max_util[l_pos]), - static_cast( i_idle_util[l_pos]) ); - //Sum up the dim's power to calculate the port power curve - o_port_power_idle += calc_power(i_idle_util[l_pos], l_pos); - o_port_power_max += calc_power(i_max_util[l_pos], l_pos); - } - - //Raise the powers by the uplift percent - calc_power_uplift(iv_power_uplift_idle, o_port_power_idle); - calc_power_uplift(iv_power_uplift, o_port_power_max); - - FAPI_ASSERT( (o_port_power_max > 0), - fapi2::MSS_NO_PORT_POWER() - .set_COUNT_DIMMS(mss::count_dimm(iv_target)) - .set_MAX_UTILIZATION_DIMM_0(i_max_util[0]) - .set_MAX_UTILIZATION_DIMM_1(i_max_util[1]), - "No Port Power limit was calculated for %s, %d DIMMs installed, utilizations: DIMM 0 %d, DIMM 1 %d", - mss::c_str(iv_target), - mss::count_dimm(iv_target), - i_max_util[0], - i_max_util[1]); - - //FAPI_ASSERTs don't set the current err to good - return fapi2::FAPI2_RC_SUCCESS; -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Calculates max and min power usages based off of DIMM power curves -/// @param[in] i_databus_idle idle databus utilization (either calculated or mrw) -/// @param[in] i_databus_max max databus utilization (either calculated or mrw) -/// @param[out] o_dimm_power_idle array of dimm power in cW -/// @param[out] o_dimm_power_max array of dimm power in cW -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK -/// @note Called in p9_mss_bulk_pwr_throttles -/// @note used for the thermal throttles -/// -void throttle::calc_dimm_power(const double i_databus_idle, - const double i_databus_max, - double o_dimm_power_idle [MAX_DIMM_PER_PORT], - double o_dimm_power_max [MAX_DIMM_PER_PORT]) const -{ - for ( const auto& l_dimm : mss::find_targets(iv_target) ) - { - const uint8_t l_pos = mss::index(l_dimm); - o_dimm_power_idle[l_pos] = calc_power(i_databus_idle, l_pos); - o_dimm_power_max[l_pos] = calc_power(i_databus_max, l_pos); - - //Raise the powers by the uplift percent - - calc_power_uplift(iv_power_uplift_idle, o_dimm_power_idle[l_pos]); - calc_power_uplift(iv_power_uplift, o_dimm_power_max[l_pos]); - - FAPI_INF("Calc_dimm_power: dimm (%d) power max is %f, %f for dimm slope of %d, intercept of %d", - l_pos, - o_dimm_power_max[l_pos], - o_dimm_power_max[l_pos], - iv_pwr_slope[l_pos], - iv_pwr_int[l_pos]); - } -} - -/// -/// @brief Calculate the port power curve in order to calculate the port utilization -/// @param[in] i_power_idle double of the port's power consumption at idle -/// @param[in] i_power_max double of the port's power consumption at max utilization -/// @param[out] o_slope -/// @param[out] o_int -/// @note Called in p9_mss_bulk_pwr_throttles -/// @note Port power curve needed to calculate the port utilization -/// -fapi2::ReturnCode throttle::calc_power_curve(const double i_power_idle, - const double i_power_max, - uint32_t& o_slope, - uint32_t& o_int) const -{ - const double l_divisor = ((static_cast(iv_databus_port_max) / UTIL_CONVERSION) - IDLE_UTIL); - FAPI_ASSERT ((l_divisor > 0), - fapi2::MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO() - .set_PORT_DATABUS_UTIL(iv_databus_port_max) - .set_UTIL_CONVERSION(UTIL_CONVERSION) - .set_IDLE_UTIL(IDLE_UTIL) - .set_RESULT(l_divisor), - "Calculated zero for the divisor in calc_power_curve on target %s", - mss::c_str(iv_target) ); - - o_slope = (i_power_max - i_power_idle) / l_divisor; - o_int = i_power_idle - (o_slope * IDLE_UTIL); - FAPI_INF("Calc_power_curve: power idle is %f, max is %f, slope is %d, int is %d", - i_power_idle, - i_power_max, - o_slope, - o_int); - return fapi2::FAPI2_RC_SUCCESS; - -fapi_try_exit: - FAPI_INF("Error calculating mss::power_thermal::calc_power_curve"); - return fapi2::current_err; - -} - -/// -/// @brief Calculate the databus utilization given the power curve -/// @param[in] i_slope -/// @param[in] i_int -/// @param[in] i_power_limit either the port_power_limit or the dimm thermal power limit -/// @param[out] o_port_util the port's databus utilization -/// @note Called in p9_mss_bulk_pwr_throttles -/// @note Chooses worst case between the maximum allowed databus utilization and the calculated value -/// -void throttle::calc_util_usage(const uint32_t i_slope, - const uint32_t i_int, - const uint32_t i_power_limit, - double& o_util) const -{ - o_util = ((static_cast(i_power_limit) - i_int) / i_slope ) * UTIL_CONVERSION; - - //Cast to uint32 for edge case where it has decimals - o_util = (static_cast(o_util) < iv_databus_port_max) ? static_cast(o_util) : iv_databus_port_max; - - // Check for the minimum threshnold and update if need be - if(o_util < MIN_UTIL) - { - FAPI_INF("Calculated utilization (%lu) is less than the minimum utilization: %lu. Setting to minimum value", o_util, - MIN_UTIL); - o_util = MIN_UTIL; - } -} - -/// -/// @brief calculated the output power estimate from the calculated N throttle -/// @param[in] i_n_slot the throttle per slot in terms of N commands -/// @param[in] i_n_port the throttle per port in terms of N commands -/// @param[out] o_power the calculated power -/// @return fapi2::ReturnCode iff it was a success -/// -fapi2::ReturnCode throttle::calc_power_from_n (const uint16_t i_n_slot, - const uint16_t i_n_port, - uint32_t& o_power) const -{ - double l_calc_util_port = 0; - double l_calc_util_slot = 0; - double l_calc_databus_port_max[MAX_DIMM_PER_PORT] = {}; - double l_calc_databus_port_idle[MAX_DIMM_PER_PORT] = {}; - double l_port_power_max = 0; - double l_port_power_idle = 0; - - FAPI_TRY( calc_util_from_throttles(i_n_slot, iv_m_clocks, l_calc_util_slot), - "%s Error calculating utilization from slot throttle %d and mem clocks %d", - mss::c_str(iv_target), - i_n_slot, - iv_m_clocks); - FAPI_TRY( calc_util_from_throttles(i_n_port, iv_m_clocks, l_calc_util_port), - "%s Error calculating utilization from port throttle %d and mem clocks %d", - mss::c_str(iv_target), - i_n_port, - iv_m_clocks); - - //Determine the utilization for each DIMM that will maximize the port power - FAPI_TRY( calc_split_util(l_calc_util_slot, l_calc_util_port, l_calc_databus_port_max), - "Error splitting the utilization for target %s with slot utilizatio %d and port util %d", - mss::c_str(iv_target), - l_calc_util_slot, - l_calc_util_port); - - FAPI_TRY( calc_port_power(l_calc_databus_port_idle, - l_calc_databus_port_max, - l_port_power_idle, - l_port_power_max), - "Error calculating the port power value for %s. Slot value is %d, port value is %d", - mss::c_str(iv_target), - i_n_slot, - i_n_port); - - o_power = power_thermal::round_up (l_port_power_max); - -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Converts the port maximum databus to a dimm level based on powerslopes and dimms installed -/// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) -/// @param[out] o_databus_dimm_max array of dimm utilization values -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK -/// @note Called in p9_mss_bulk_pwr_throttles -/// @used to calculate the port power based off of DIMM power curves -/// -fapi2::ReturnCode throttle::calc_databus (const double i_databus_port_max, - double o_databus_dimm_max [MAX_DIMM_PER_PORT]) -{ - uint8_t l_count_dimms = count_dimm(iv_target); - - //No work for no dimms - if (l_count_dimms == 0) - { - return fapi2::FAPI2_RC_SUCCESS; - } - - for (const auto& l_dimm : mss::find_targets(iv_target)) - { - //Left early if count_dimms == 0 - o_databus_dimm_max[mss::index(l_dimm)] = i_databus_port_max / l_count_dimms; - } - - //If the power slopes aren't equal, set the dimm with the highest power slope - //Should be correct even if only one DIMM is installed - if (iv_pwr_slope[0] != iv_pwr_slope[1]) - { - o_databus_dimm_max[0] = (iv_pwr_slope[0] > iv_pwr_slope[1]) ? i_databus_port_max : 0; - o_databus_dimm_max[1] = (iv_pwr_slope[1] > iv_pwr_slope[0]) ? i_databus_port_max : 0; - } - - //Make sure both are not 0 - FAPI_ASSERT ( (o_databus_dimm_max[0] != 0) || (o_databus_dimm_max[1] != 0), - fapi2::MSS_NO_DATABUS_UTILIZATION() - .set_PORT_DATABUS_UTIL(i_databus_port_max) - .set_DIMM_COUNT(l_count_dimms), - "Failed to calculated databus utilization for target %s", - mss::c_str(iv_target)); - - return fapi2::FAPI2_RC_SUCCESS; -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed -/// @param[in] i_util_slot databus utilization for the slot -/// @param[in] i_util_port databus utilization for the port -/// @param[out] o_util_dimm_max array of dimm utilization values -/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK -/// @note determines worst case utilization per dimms, takes into account port and combine slot throttles -/// @note used in calculating the port power, not for calculating the slot and port utilization -/// -fapi2::ReturnCode throttle::calc_split_util( - const double i_util_slot, - const double i_util_port, - double o_util_dimm_max [MAX_DIMM_PER_PORT]) const -{ - uint8_t l_count_dimms = count_dimm (iv_target); - //The total utilization to be used is limited by either what the port can allow or what the dimms can use - FAPI_ASSERT( (i_util_slot <= i_util_port), - fapi2::MSS_SLOT_UTIL_EXCEEDS_PORT() - .set_SLOT_UTIL(i_util_slot) - .set_PORT_UTIL(i_util_port), - "The slot utilization (%f) exceeds the port's utilization (%f)", - i_util_slot, - i_util_port); - - if (l_count_dimms == 0) - { - return fapi2::FAPI2_RC_SUCCESS; - } - - //assumptions slot <= port, l_count_dimms <=2 - else if (i_util_slot * l_count_dimms > i_util_port) - { - FAPI_INF("In mss::power_thermal::calc_split i_util_slot is %f, i_util_port is %f, l_count_dimms is %d", - i_util_slot, - i_util_port, - l_count_dimms); - const uint8_t l_high_pos = (iv_pwr_slope[0] >= iv_pwr_slope[1]) ? 0 : 1; - - //Highest power_slope gets the higher utilization - o_util_dimm_max[l_high_pos] = std::min(i_util_slot, i_util_port); - //Set the other dimm to the left over utilization (i_util_port - i_util_slot) - o_util_dimm_max[(!l_high_pos)] = (l_count_dimms == 2) ? (i_util_port - o_util_dimm_max[l_high_pos]) : 0; - - FAPI_INF("Split utilization for target %s, DIMM in %d gets %f, DIMM in %d gets %f", - mss::c_str(iv_target), - l_high_pos, - o_util_dimm_max[l_high_pos], - !l_high_pos, - o_util_dimm_max[!l_high_pos]); - } - - else - { - //If only 1 dimm, i_util_port == i_util_slot - //If 2 dimms, 2*i_util_slot <= i_util_pot - //Either way, limit utilization by the slot value - for (const auto& l_dimm : mss::find_targets(iv_target)) - { - const size_t l_pos = mss::index(l_dimm); - o_util_dimm_max[l_pos] = i_util_slot; - } - } - - //make sure both are not 0 - FAPI_ASSERT ( (o_util_dimm_max[0] != 0) || (o_util_dimm_max[1] != 0), - fapi2::MSS_NO_DATABUS_UTILIZATION() - .set_PORT_DATABUS_UTIL(i_util_port) - .set_DIMM_COUNT(mss::count_dimm(iv_target)), - "Failed to calculated util utilization for target %s", - mss::c_str(iv_target)); -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Perform thermal calculations as part of the effective configuration -/// @param[in] i_target the MCS target in which the runtime throttles will be reset -/// @return FAPI2_RC_SUCCESS iff ok -/// -fapi2::ReturnCode restore_runtime_throttles( const fapi2::Target& i_target ) -{ - uint16_t l_run_throttles [MAX_DIMM_PER_PORT] = {}; - uint32_t l_max_databus = 0; - uint32_t l_throttle_m_clocks = {}; - - FAPI_TRY( mrw_mem_m_dram_clocks(l_throttle_m_clocks) ); - FAPI_TRY( mrw_max_dram_databus_util(l_max_databus) ); - - //Set runtime throttles to unthrottled value, using max dram utilization and M throttle - //Do I need to check to see if any DIMMS configured on the port? - for (const auto& l_mca : mss::find_targets(i_target)) - { - const auto l_pos = mss::index(l_mca); - - if (mss::count_dimm (l_mca) != 0) - { - l_run_throttles[l_pos] = mss::power_thermal::throttled_cmds (l_max_databus, l_throttle_m_clocks); - } - } - - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT, i_target, l_run_throttles) ); - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT, i_target, l_run_throttles) ); - -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values -/// @param[in] i_target the MCS target in which the runtime throttles will be set -/// @return FAPI2_RC_SUCCESS iff ok -/// -fapi2::ReturnCode update_runtime_throttles( const std::vector< fapi2::Target >& i_targets ) -{ - for (const auto& l_mcs : i_targets) - { - if (mss::count_dimm(l_mcs) == 0) - { - continue; - } - - uint16_t l_run_slot [PORTS_PER_MCS] = {}; - uint16_t l_run_port [PORTS_PER_MCS] = {}; - uint16_t l_calc_slot [PORTS_PER_MCS] = {}; - uint16_t l_calc_port [PORTS_PER_MCS] = {}; - - FAPI_TRY(runtime_mem_throttled_n_commands_per_slot(l_mcs, l_run_slot)); - FAPI_TRY(runtime_mem_throttled_n_commands_per_port(l_mcs, l_run_port)); - FAPI_TRY(mem_throttled_n_commands_per_slot(l_mcs, l_calc_slot)); - FAPI_TRY(mem_throttled_n_commands_per_port(l_mcs, l_calc_port)); - - for (const auto& l_mca : mss::find_targets(l_mcs)) - { - const auto l_pos = mss::index(l_mca); - //Choose the worst case between runtime and calculated throttles - //Have to make sure the calc_slot isn't equal to 0 though - l_run_slot[l_pos] = (l_calc_slot[l_pos] != 0) ? - std::min(l_run_slot[l_pos], l_calc_slot[l_pos]) : l_run_slot[l_pos]; - l_run_port[l_pos] = (l_calc_port[l_pos] != 0) ? - std::min(l_run_port[l_pos], l_calc_port[l_pos]) : l_run_port[l_pos]; - - FAPI_INF("New runtime throttles for %s for slot are %d, port are %d", - mss::c_str(l_mca), - l_run_slot[l_pos], - l_run_port[l_pos]); - } - - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT, l_mcs, l_run_port) ); - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_SLOT, l_mcs, l_run_slot) ); - } - -fapi_try_exit: - return fapi2::current_err; -} - /// /// @brief set ATTR_MSS_RUNTIME_MEM_M_DRAM_CLOCKS and ATTR_MSS_MEM_WATT_TARGET /// @param[in] i_targets vector of mcs targets all on the same vddr domain @@ -785,149 +112,5 @@ fapi_try_exit: return fapi2::current_err; } -/// -/// @brief Equalize the throttles and estimated power at those throttle levels -/// @param[in] i_targets vector of MCS targets all on the same VDDR domain -/// @param[in] i_throttle_type denotes if this was done for POWER (VMEM) or THERMAL (VMEM+VPP) throttles -/// @param[out] o_exceeded_power vector of MCA targets where the estimated power exceeded the maximum allowed -/// @return FAPI2_RC_SUCCESS iff ok -/// @note sets the throttles and power to the worst case -/// Called by p9_mss_bulk_pwr_throttles and by p9_mss_utils_to_throttle (so by IPL or by OCC) -/// -fapi2::ReturnCode equalize_throttles (const std::vector< fapi2::Target >& i_targets, - const throttle_type i_throttle_type, - std::vector< fapi2::Target >& o_exceeded_power) -{ - o_exceeded_power.clear(); - - //Set to max values so every compare will change to min value - uint16_t l_min_slot = ~(0); - uint16_t l_min_port = ~(0); - - //Loop through all of the MCS targets to find the worst case throttle value (lowest) for the slot and port - for (const auto& l_mcs : i_targets) - { - uint16_t l_calc_slot [mss::PORTS_PER_MCS] = {}; - uint16_t l_calc_port [mss::PORTS_PER_MCS] = {}; - uint16_t l_run_slot [mss::PORTS_PER_MCS] = {}; - uint16_t l_run_port [mss::PORTS_PER_MCS] = {}; - - FAPI_TRY(mem_throttled_n_commands_per_slot(l_mcs, l_calc_slot)); - FAPI_TRY(mem_throttled_n_commands_per_port(l_mcs, l_calc_port)); - FAPI_TRY(runtime_mem_throttled_n_commands_per_slot(l_mcs, l_run_slot)); - FAPI_TRY(runtime_mem_throttled_n_commands_per_port(l_mcs, l_run_port)); - - for (const auto& l_mca : mss::find_targets(l_mcs)) - { - if (mss::count_dimm(l_mca) == 0) - { - continue; - } - - const auto l_pos = mss::index(l_mca); - //Find the smaller of the three values (calculated slot, runtime slot, and min slot) - l_min_slot = (l_calc_slot[l_pos] != 0) ? std::min( std::min (l_calc_slot[l_pos], l_run_slot[l_pos]), - l_min_slot) : l_min_slot; - l_min_port = (l_calc_port[l_pos] != 0) ? std::min( std::min( l_calc_port[l_pos], l_run_port[l_pos]), - l_min_port) : l_min_port; - } - } - - FAPI_INF("Calculated min slot is %d, min port is %d for the system", l_min_slot, l_min_port); - - //Now set every port to have those values - { - for (const auto& l_mcs : i_targets) - { - uint16_t l_fin_slot [mss::PORTS_PER_MCS] = {}; - uint16_t l_fin_port [mss::PORTS_PER_MCS] = {}; - uint32_t l_fin_power [mss::PORTS_PER_MCS] = {}; - - for (const auto& l_mca : mss::find_targets(l_mcs)) - { - if (mss::count_dimm(l_mca) == 0) - { - continue; - } - - const auto l_pos = mss::index(l_mca); - // Declaring above to avoid fapi2 jump - uint64_t l_power_limit = 0; - - l_fin_slot[l_pos] = (mss::count_dimm(l_mca)) ? l_min_slot : 0; - l_fin_port[l_pos] = (mss::count_dimm(l_mca)) ? l_min_port : 0; - - //Need to create throttle object for each mca in order to get dimm configuration and power curves - //To calculate the slot/port utilization and total port power consumption - fapi2::ReturnCode l_rc; - - const auto l_dummy = mss::power_thermal::throttle(l_mca, l_rc); - FAPI_TRY(l_rc, "Failed creating a throttle object in equalize_throttles"); - - FAPI_TRY( l_dummy.calc_power_from_n(l_fin_slot[l_pos], l_fin_port[l_pos], l_fin_power[l_pos]), - "Failed calculating the power value for throttles: slot %d, port %d for target %s", - l_fin_slot[l_pos], - l_fin_port[l_pos], - mss::c_str(l_mca)); - - //Only calculate the power for ports that have dimms - l_fin_power[l_pos] = (mss::count_dimm(l_mca) != 0 ) ? l_fin_power[l_pos] : 0; - - // You may ask why this is not a variable within the throttle struct - // It's because POWER throttling is on a per port basis while the THERMAL throttle is per dimm - // Didn't feel like adding a variable just for this check - l_power_limit = (i_throttle_type == throttle_type::POWER) ? - l_dummy.iv_port_power_limit : (l_dummy.iv_dimm_thermal_limit[0] + l_dummy.iv_dimm_thermal_limit[1]); - - FAPI_INF("Calculated power is %d, limit is %d", l_fin_power[l_pos], l_power_limit); - - //If there's an error with calculating port power, the wrong watt target was passed in - //Returns an error but doesn't deconfigure anything. Calling function can log if it wants to - //Called by OCC and by p9_mss_eff_config_thermal, thus different ways for error handling - //Continue setting throttles to prevent a possible throttle == 0 - //The error will be the last bad port found - if (l_fin_power[l_pos] > l_power_limit) - { - //Need this because of pos traits and templating stuff - uint64_t l_fail = mss::fapi_pos(l_mca); - //Set the failing port. OCC just needs one failing port, doesn't need all of them - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_MEM_PORT_POS_OF_FAIL_THROTTLE, - fapi2::Target(), - l_fail) ); - - FAPI_ASSERT_NOEXIT( false, - fapi2::MSS_CALC_PORT_POWER_EXCEEDS_MAX() - .set_CALCULATED_PORT_POWER(l_fin_power[l_pos]) - .set_MAX_POWER_ALLOWED(l_power_limit) - .set_PORT_POS(mss::pos(l_mca)) - .set_MCA_TARGET(l_mca), - "Error calculating the final port power value for target %s, calculated power is %d, max value can be %d", - mss::c_str(l_mca), - l_fin_power[l_pos], - l_power_limit); - - o_exceeded_power.push_back(l_mca); - } - - FAPI_INF("%s Final throttles values for slot %d, for port %d, power value %d", - mss::c_str(l_mca), - l_fin_port[l_pos], - l_fin_slot[l_pos], - l_fin_power[l_pos]); - } - - //Even if there's an error, still calculate and set the throttles. - //OCC will set to safemode if there's an error - //Better to set the throttles than leave them 0, and potentially brick the memory - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_PORT, l_mcs, l_fin_port) ); - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, l_mcs, l_fin_slot) ); - FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_PORT_MAXPOWER, l_mcs, l_fin_power) ); - } - } - return fapi2::FAPI2_RC_SUCCESS; -fapi_try_exit: - FAPI_ERR("Error equalizing memory throttles"); - return fapi2::current_err; -} }//namespace power_thermal }//namespace mss diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.H b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.H index 189583cfa..20152398e 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2018 */ +/* Contributors Listed Below - COPYRIGHT 2016,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -39,312 +39,14 @@ #include #include #include +#include +#include +#include namespace mss { namespace power_thermal { -/// -/// @brief throttle constants used in the power_thermal functions -/// -enum throttle_const : size_t -{ - /// Dram data bus utilization is bus utilization / 4 - DRAM_BUS_UTILS = 4, - - /// 10000 to convert to and from c% - UTIL_CONVERSION = 10000, - - /// Conversion to percentage - PERCENT_CONVERSION = 100, - - /// MIN_UTIL is in c% - MIN_UTIL = 2500, - - /// IDLE_UTIL is in c% - IDLE_UTIL = 0, - - /// Minimum throttle allowed for the port and or slot. If we set to 0, we brick the port - MIN_THROTTLE = 1, -}; - -/// -/// @class throttle -/// @brief Determine power_thermal throttles for memory -/// -class throttle -{ - private: - /// - /// @brief Calculate the power (cW) of inputs and the power curve - /// @tparam T - /// @param[in] i_util the databus utilization that the power will be based on - /// @param[in] l_pos the dimm position for the power value being calculated. - /// @return Integral type T - /// - template - inline T calc_power (const T i_util, const size_t i_pos) const - { - return ((i_util / UTIL_CONVERSION) * iv_pwr_slope[i_pos]) + iv_pwr_int[i_pos]; - } - - /// - /// @brief Raise the o_value by the percent passed in - /// @param[in] i_uplift the percent the o_Value should be raised by - /// @param[out] o_value the value that will be modified - /// - inline void calc_power_uplift (const uint8_t i_uplift, double& o_value) const - { - o_value *= (1 + (static_cast(i_uplift) / PERCENT_CONVERSION)); - } - - public: - const fapi2::Target& iv_target; - - uint32_t iv_databus_port_max; - - uint8_t iv_power_uplift_idle; - uint8_t iv_power_uplift; - - uint16_t iv_runtime_n_slot; - uint16_t iv_runtime_n_port; - uint32_t iv_m_clocks; - uint32_t iv_dimm_thermal_limit[MAX_DIMM_PER_PORT] = {}; - uint16_t iv_pwr_slope[MAX_DIMM_PER_PORT] = {}; - uint16_t iv_pwr_int[MAX_DIMM_PER_PORT] = {}; - uint16_t iv_n_slot; - uint16_t iv_n_port; - uint32_t iv_port_power_limit; - uint32_t iv_calc_port_maxpower; - - //default ctor deleted - throttle() = delete; - - /// - /// @brief Constructor - /// @param[in] i_target MCA target to call power thermal stuff on - /// @param[out] o_rc fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ctor was successful - /// - throttle( const fapi2::Target& i_mca, fapi2::ReturnCode& o_rc); - - // - // @brief Destructor - // - ~throttle() = default; - - /// - /// @brief Calculates the min and max power usage for a port - /// @param[in] i_idle_util the utilization of the databus in idle mode - /// @param[in] i_max_util the utilization of the port at maximum possible (mrw or calculated) - /// @param[out] o_port_power_idle max value of port power in cW - /// @param[out] o_port_power_max max value of port power in cW - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK - /// @note Called twice in p9_mss_bulk_pwr_throttles - /// - fapi2::ReturnCode calc_port_power( const double i_idle_util [MAX_DIMM_PER_PORT], - const double i_max_util [MAX_DIMM_PER_PORT], - double& o_port_power_idle, - double& o_port_power_max) const; - /// - /// @brief Calculates max and min power usages based off of DIMM power curves - /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) - /// @param[in] i_port_power_calc_idle double of the port's power consumption at idle - /// @param[out] o_dimm_power_idle array of dimm power in cW - /// @param[out] o_dimm_power_max array of dimm power in cW - /// @note Called in p9_mss_bulk_pwr_throttles - /// @note used for the thermal throttles - /// - void calc_dimm_power(const double i_databus_idle, - const double i_databus_max, - double o_dimm_power_idle [MAX_DIMM_PER_PORT], - double o_dimm_power_max [MAX_DIMM_PER_PORT]) const; - - /// - /// @brief Calculate the power curve in order to calculate databus utilization - /// @param[in] i_power_idle double of the port's power consumption at idle - /// @param[in] i_power_max double of the port's power consumption at max utilization - /// @param[out] o_power_slope - /// @param[out] o_power_int - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK - /// @note Called in p9_mss_bulk_pwr_throttles - /// @note Power curve needed to calculate the utilization - /// - fapi2::ReturnCode calc_power_curve(const double i_power_idle, - const double i_power_max, - uint32_t& o_power_slope, - uint32_t& o_power_int) const; - /// - /// @brief Calculate the databus utilization given the power curve - /// @param[in] i_slope - /// @param[in] i_int - /// @param[in] i_power_limit either iv_port_power_limit or thermal_power_limit depending on throttle type - /// @param[out] o_port_util the port's databus utilization - /// @note Called in p9_mss_bulk_pwr_throttles - /// @note Chooses worst case between the maximum allowed databus utilization and the calculated value - /// - void calc_util_usage(const uint32_t i_slope, - const uint32_t i_int, - const uint32_t i_power_limit, - double& o_util) const; - /// - /// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower - /// @param[in] i_util_port pass in the calculated port databus utilization - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK - /// - fapi2::ReturnCode calc_slots_and_power (const double i_util_port); - - /// - /// @brief calculated the output power estimate from the calculated N throttle - /// @param[in] i_n_slot the N throttle per slot - /// @param[in] i_n_port the N throttle per port - /// @param[out] o_power the calculated power - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK - /// - fapi2::ReturnCode calc_power_from_n (const uint16_t i_n_slot, const uint16_t i_n_port, uint32_t& o_power) const; - - /// - /// @brief Converts the port maximum databus util to a dimm level based on powerslopes and dimms installed - /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) - /// @param[out] o_databus_dimm_max array of dimm utilization values - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK - /// @note Called in p9_mss_bulk_pwr_throttles - /// @used to calculate the port power based off of DIMM power curves - /// - fapi2::ReturnCode calc_databus( const double i_databus_port_max, - double o_databus_dimm_max [MAX_DIMM_PER_PORT]); - /// - /// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed - /// @param[in] i_util_slot databus utilization for the slot - /// @param[in] i_util_port databus utilization for the port - /// @param[out] o_util_dimm_max array of dimm utilization values - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK - /// @note determines worst case utilization per dimms, takes into account port and combine slot throttles - /// - fapi2::ReturnCode calc_split_util( - const double i_util_slot, - const double i_util_port, - double o_util_dimm_max [MAX_DIMM_PER_PORT]) const; - - - /// - /// @brief Calculate ATTR_MSS_CHANNEL_PAIR_MAXPOWER and ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK - /// @note Called in p9_mss_bulk_pwr_throttles - /// @note determines the throttle levels based off of the port's power curve, max databus utilization, - /// and memwat target. - /// @note currently sets the slot and port throttles to the same value - /// - fapi2::ReturnCode power_regulator_throttles (); - - /// - /// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, - /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK - /// @note Called in p9_mss_bulk_pwr_throttles - /// @note Sets the throttle levels based off of the dimm's thermal limits - /// @note both DIMM's on a port are set to the same throttle level - /// - fapi2::ReturnCode thermal_throttles (); -}; - -/// -/// @brief Calculate N (address operations) allowed within a window of M DRAM clocks -/// @param[in] i_databus_util databus utilization percentage (e.g. 5% = 5) -/// @param[in] i_num_dram_clocks window of M DRAM clocks -/// @return number of throttled commands allowed -/// @note Uses N/M Throttling. -/// Equation: N = (DRAM data bus utilization * M) / (4 * 10000) -/// -inline uint32_t throttled_cmds(const uint32_t i_databus_util, const uint32_t i_num_dram_clocks) -{ - constexpr uint64_t l_divisor = DRAM_BUS_UTILS * UTIL_CONVERSION; - const uint64_t l_dividend = i_databus_util * i_num_dram_clocks; - const uint64_t l_result = l_dividend / l_divisor; - - //Make sure N is not equal to 0, or we brick the dram until reboot - return ((l_result == 0) ? 1 : l_result); -} - -/// -/// @brief Calculate the port databus utilization based off of N throttles and M dram clocks -/// @tparam T output type -/// @param[in] i_n_throttles N (address operations) allowed within a window of M DRAM clocks -/// @param[in] i_num_dram_clocks window of M DRAM clocks -/// @param[out] o_calc_util -/// @return FAPI2_RC_SUCCESS iff method was a success -/// @note Uses N/M Throttling. -/// @note DRAM databus utilization = N * 4 * 10000 / M -/// -template -fapi2::ReturnCode calc_util_from_throttles(const uint16_t i_n_throttles, - const uint32_t i_num_dram_clocks, - T& o_calc_util) -{ - constexpr uint32_t l_multiplier = DRAM_BUS_UTILS * UTIL_CONVERSION; - FAPI_ASSERT( (i_num_dram_clocks != 0), - fapi2::MSS_M_DRAM_CLOCKS_EQUALS_ZERO(), - "ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero"); - - o_calc_util = (static_cast(i_n_throttles) * l_multiplier) / i_num_dram_clocks; - - // Best way to check for overflow if o_calc_util can be a double? - // If o_calc_util overflows, the value inside will be below the expected outcome - // So compare o_calc_util with the calculated value, but store calculated value in largest storage - // Compare ">=" because o_calc_util can be a double, and so we can't compare just equality due to truncation - FAPI_ASSERT( o_calc_util >= static_cast((static_cast(i_n_throttles) * l_multiplier) / - i_num_dram_clocks), - fapi2::MSS_OUTPUT_OVERFLOW_CALC_UTIL() - .set_RESULT((static_cast(i_n_throttles) * l_multiplier) / i_num_dram_clocks), - "Overflow of output variable in calc_util_from_throttles throttles: %d, multiplier %d, dram_clocks %d", - i_n_throttles, - l_multiplier, - i_num_dram_clocks); - - // Check for the minimum - if(o_calc_util < MIN_UTIL) - { - FAPI_INF("Calculated utilization (%f) is less than the minimum utilization: %lu. Setting to minimum value", - o_calc_util, MIN_UTIL); - o_calc_util = MIN_UTIL; - } - - FAPI_INF("In calc_util_from_throttles, calculated %f for output utilization", o_calc_util); - return fapi2::FAPI2_RC_SUCCESS; -fapi_try_exit: - return fapi2::current_err; -} - -/// -/// @brief Determines if the double has decimal digits and adds 1 and rounds if true -/// @param[in] i_val the double to be rounded up if trialing digits -/// @return the input value rounded up to the next whole digit -/// @note Called in p9_mss_bulk_pwr_throttles -/// -inline uint32_t round_up(double i_val) -{ - //convert to uint to truncate decimals and convert back to double for comparison - uint32_t temp = uint32_t (i_val); - - //if not equal, lost something from truncating, so add 1 - temp += (temp == i_val) ? 0 : 1; - - //Truncate final value - return temp; -} - -/// -/// @brief Perform thermal calculations as part of the effective configuration -/// @param[in] i_target the MCS target in which the runtime throttles will be reset -/// @return FAPI2_RC_SUCCESS iff ok -/// -fapi2::ReturnCode restore_runtime_throttles( const fapi2::Target& i_target ); - -/// -/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values -/// @param[in] i_target the MCS target in which the runtime throttles will be set -/// @return FAPI2_RC_SUCCESS iff ok -/// -fapi2::ReturnCode update_runtime_throttles(const std::vector< fapi2::Target >& i_targets); - /// /// @brief set ATTR_MSS_RUNTIME_MEM_M_DRAM_CLOCKS and ATTR_MSS_MEM_WATT_TARGET /// @param[in] i_targets vector of mcs targets all on the same vddr domain @@ -352,19 +54,8 @@ fapi2::ReturnCode update_runtime_throttles(const std::vector< fapi2::Target >& i_targets ); -/// -/// @brief Equalize the throttles and estimated power at those throttle levels -/// @param[in] i_targets vector of MCS targets all on the same VDDR domain -/// @param[in] i_throttle_type denotes if this was done for POWER (VMEM) or THERMAL (VMEM+VPP) throttles -/// @param[out] o_exceeded_power vector of MCA targets where the estimated power exceeded the maximum allowed -/// @return FAPI2_RC_SUCCESS iff ok -/// @note sets the throttles and power to the worst case -/// -fapi2::ReturnCode equalize_throttles (const std::vector< fapi2::Target >& i_targets, - const throttle_type i_throttle_type, - std::vector< fapi2::Target >& o_exceeded_power); - }//power_thermal + }// mss #endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle_traits.H b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle_traits.H index da277d6fb..15407e3a4 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle_traits.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/power_thermal/throttle_traits.H @@ -22,3 +22,125 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ +/// +/// @file p9_mss_utils_to_throttle.H +/// @brief throttle API +/// + +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_POWER_THROTTLE_TRAITS_ +#define _MSS_POWER_THROTTLE_TRAITS_ + +#include +#include + +namespace mss +{ +namespace power_thermal +{ + +/// +/// @class Traits and policy class for throttle code - specialization for the NIMBUS mc type +/// +template<> +class throttle_traits +{ + public: + ////////////////////////////////////////////////////////////// + // Target types + ////////////////////////////////////////////////////////////// + static constexpr fapi2::TargetType MC_TARGET_TYPE = fapi2::TARGET_TYPE_MCS; + static constexpr fapi2::TargetType PORT_TARGET_TYPE = fapi2::TARGET_TYPE_MCA; + + // MIN_UTIL is in c% + static const uint64_t MIN_UTIL = 2500; + // IDLE_UTIL is in c% + static const uint64_t IDLE_UTIL = 0; + // Minimum throttle allowed for the port and or slot. If we set to 0, we brick the port + static const uint64_t MIN_THROTTLE = 1; + + enum size_of_attrs : size_t + { + SIZE_OF_POWER_CURVES_ATTRS = 100, + SIZE_OF_THERMAL_ATTR = 10, + }; + + enum default_power + { + //Values are the worst case defaults for power curves + //They are the default/ catch-all values from the power curve attributes + //Shouldn't be used if system is set up correctly and attributes are available + //This will throttle the DIMMs to ~76% dram data bus utilization + //(using the mrw regulator power limit of 1700 cW and thermal power limit here of 1940 cW). + VDDR_SLOPE = 0x41A, + VDDR_INT = 0x384, + TOTAL_SLOPE = 0x44C, + TOTAL_INT = 0x44C, + THERMAL_LIMIT = 0x794, + }; + + enum + { + PORTS_PER_MC = 2, + DIMMS_PER_PORT = 2, + }; + + //Bit positions for different section of the attribute + //first 32 bits are the encoding, second are for values + enum DECODE_BUFFER_POS + { + ENCODING_START = 0, + ENCODING_LENGTH = 32, + VDDR_START = 32, + VDDR_LENGTH = 16, + TOTAL_START = 48, + TOTAL_LENGTH = 16, + THERMAL_POWER_START = 32, + THERMAL_POWER_LENGTH = 32, + }; + + //Positions and lengths of the encodings + enum ATTR_DECODE_INFO + { + DIMM_SIZE_START = 0, + DIMM_SIZE_LEN = 4, + + DRAM_GEN_START = 4, + DRAM_GEN_LEN = 2, + + DIMM_TYPE_START = 6, + DIMM_TYPE_LEN = 2, + + DRAM_WIDTH_START = 8, + DRAM_WIDTH_LEN = 3, + + DRAM_DENSITY_START = 11, + DRAM_DENSITY_LEN = 3, + + DRAM_STACK_TYPE_START = 14, + DRAM_STACK_TYPE_LEN = 2, + + DRAM_MFGID_START = 16, + DRAM_MFGID_LEN = 3, + + DIMMS_PER_PORT_START = 19, + DIMMS_PER_PORT_LEN = 2, + + // Invalid for Nimbus but compile will fail without them + DIMM_MODULE_HEIGHT_START = 0, + DIMM_MODULE_HEIGHT_LEN = 1, + }; + + + // Definition is in chip folder + static const std::vector< std::pair > DIMM_TYPE_MAP; +}; +}//power_thermal +}// mss + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H index 5f175b44c..1564d6f85 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H @@ -156,11 +156,6 @@ enum ffdc_function_codes // Used in rank.H MAP_RP_PRIMARY_TO_INIT_CAL = 60, - // Power thermal functions - SLOPE = 70, - INTERCEPT = 71, - POWER_LIMIT = 72, - // PDA function codes PDA_WR_VREF_LATCH_CONTAINER = 80, PDA_WR_VREF_LATCH_VECTOR = 81, diff --git a/src/import/chips/p9/procedures/hwp/memory/p9_mss_bulk_pwr_throttles.C b/src/import/chips/p9/procedures/hwp/memory/p9_mss_bulk_pwr_throttles.C index 84cc1c73e..a586eda62 100644 --- a/src/import/chips/p9/procedures/hwp/memory/p9_mss_bulk_pwr_throttles.C +++ b/src/import/chips/p9/procedures/hwp/memory/p9_mss_bulk_pwr_throttles.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2018 */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -32,6 +32,8 @@ // *HWP Team: Memory // *HWP Level: 3 // *HWP Consumed by: FSP:HB + +#include #include #include @@ -88,7 +90,7 @@ extern "C" const uint8_t l_pos = mss::index(l_mca); - mss::power_thermal::throttle l_pwr_struct(l_mca, l_rc); + mss::power_thermal::throttle<> l_pwr_struct(l_mca, l_rc); FAPI_TRY(l_rc, "Error constructing mss:power_thermal::throttle object for target %s", mss::c_str(l_mca)); diff --git a/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config_thermal.C b/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config_thermal.C index bd5819fd2..bac3836ff 100644 --- a/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config_thermal.C +++ b/src/import/chips/p9/procedures/hwp/memory/p9_mss_eff_config_thermal.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2018 */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -33,6 +33,7 @@ // *HWP Level: 3 // *HWP Consumed by: FSP:HB +#include #include #include #include @@ -52,12 +53,14 @@ extern "C" /// @note sets ATTR_MSS_MEM_WATT_TARGET, ATTR_MSS_RUNTIME_MEM_THROTTLED_N_COMMANDS_PER_PORT and _PER_SLOT, and ATTR_MSS_PORT_MAXPOWER fapi2::ReturnCode p9_mss_eff_config_thermal( const std::vector< fapi2::Target >& i_targets ) { + using TT = mss::power_thermal::throttle_traits; + FAPI_INF("Start effective config thermal"); fapi2::ReturnCode l_rc; - std::vector< uint64_t > l_slope (mss::power_thermal::SIZE_OF_POWER_CURVES_ATTRS, 0); - std::vector< uint64_t > l_intercept (mss::power_thermal::SIZE_OF_POWER_CURVES_ATTRS, 0); - std::vector< uint64_t > l_thermal_power_limit (mss::power_thermal::SIZE_OF_THERMAL_ATTR, 0); + std::vector< uint64_t > l_slope (TT::SIZE_OF_POWER_CURVES_ATTRS, 0); + std::vector< uint64_t > l_intercept (TT::SIZE_OF_POWER_CURVES_ATTRS, 0); + std::vector< uint64_t > l_thermal_power_limit (TT::SIZE_OF_THERMAL_ATTR, 0); uint16_t l_vddr_slope [mss::PORTS_PER_MCS][mss::MAX_DIMM_PER_PORT] = {}; uint16_t l_vddr_int [mss::PORTS_PER_MCS][mss::MAX_DIMM_PER_PORT] = {}; @@ -75,19 +78,21 @@ extern "C" // Return error if safemode throttle utilization is less than MIN_UTIL // This section needs to be in braces otherwise the compile will fail { + using TT = mss::power_thermal::throttle_traits<>; + const uint64_t l_min_util = TT::MIN_UTIL; uint16_t l_throttle_per_port = 0; uint32_t l_throttle_denominator = 0; FAPI_TRY(mss::mrw_mem_m_dram_clocks(l_throttle_denominator), "Error in p9_mss_eff_config_thermal" ); FAPI_TRY(mss::mrw_safemode_mem_throttled_n_commands_per_port(l_throttle_per_port), "Error in p9_mss_eff_config_thermal" ); - FAPI_ASSERT( (l_throttle_per_port >= (mss::power_thermal::MIN_UTIL * l_throttle_denominator / + FAPI_ASSERT( (l_throttle_per_port >= (TT::MIN_UTIL * l_throttle_denominator / mss::power_thermal::DRAM_BUS_UTILS / mss::power_thermal::UTIL_CONVERSION)), fapi2::MSS_MRW_SAFEMODE_THROTTLE_NOT_SUPPORTED() .set_MRW_SAFEMODE_N_VALUE(l_throttle_per_port) .set_MRW_DRAM_CLOCK_THROTTLE_M(l_throttle_denominator) - .set_MIN_UTIL_VALUE(mss::power_thermal::MIN_UTIL), + .set_MIN_UTIL_VALUE(l_min_util), "MRW safemode attribute (N=%d, M=%d) has less util than the min util allowed (%d centi percent)", - l_throttle_per_port, l_throttle_denominator, mss::power_thermal::MIN_UTIL); + l_throttle_per_port, l_throttle_denominator, l_min_util); } //Restore runtime_throttles from safemode setting diff --git a/src/import/chips/p9/procedures/hwp/memory/p9_mss_utils_to_throttle.C b/src/import/chips/p9/procedures/hwp/memory/p9_mss_utils_to_throttle.C index 076cd911c..56e2dbc42 100644 --- a/src/import/chips/p9/procedures/hwp/memory/p9_mss_utils_to_throttle.C +++ b/src/import/chips/p9/procedures/hwp/memory/p9_mss_utils_to_throttle.C @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2018 */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -37,6 +37,7 @@ // *HWP Level: 3 // *HWP Consumed by: FSP:HB +#include #include // fapi2 @@ -133,28 +134,30 @@ extern "C" l_input_databus_util[l_port_num] = l_calc_util_safemode; } + using TT = mss::power_thermal::throttle_traits<>; + const uint64_t l_min_util = TT::MIN_UTIL; //Make sure MIN_UTIL <= input_utilization <= max_utilization - const uint32_t l_databus_util = ( l_input_databus_util[l_port_num] >= mss::power_thermal::MIN_UTIL) ? + const uint32_t l_databus_util = ( l_input_databus_util[l_port_num] >= l_min_util) ? std::min(l_input_databus_util[l_port_num], l_max_databus_util) - : mss::power_thermal::MIN_UTIL; + : l_min_util; // Error if utilization is less than MIN_UTIL // Don't exit, let HWP finish and return error at end - if (l_input_databus_util[l_port_num] < mss::power_thermal::MIN_UTIL) + if (l_input_databus_util[l_port_num] < l_min_util) { FAPI_ASSERT_NOEXIT( false, fapi2::MSS_MIN_UTILIZATION_ERROR() .set_INPUT_UTIL_VALUE(l_input_databus_util[l_port_num]) - .set_MIN_UTIL_VALUE(mss::power_thermal::MIN_UTIL), + .set_MIN_UTIL_VALUE(l_min_util), "%s Input utilization (%d) less than minimum utilization allowed (%d)", - mss::c_str(l_mca), l_input_databus_util[l_port_num], mss::power_thermal::MIN_UTIL); + mss::c_str(l_mca), l_input_databus_util[l_port_num], l_min_util); l_util_error = true; } //Make a throttle object in order to calculate the port power fapi2::ReturnCode l_rc; - mss::power_thermal::throttle l_throttle (l_mca, l_rc); + mss::power_thermal::throttle<> l_throttle (l_mca, l_rc); FAPI_TRY(l_rc, "Error calculating mss::power_thermal::throttle constructor in p9_mss_utils_to_throttles"); FAPI_INF( "%s MRW dram clock window: %d, databus utilization: %d", diff --git a/src/import/chips/p9/procedures/xml/error_info/p9_memory_mss_eff_config_thermal.xml b/src/import/chips/p9/procedures/xml/error_info/p9_memory_mss_eff_config_thermal.xml index 5de51418f..a156088c2 100644 --- a/src/import/chips/p9/procedures/xml/error_info/p9_memory_mss_eff_config_thermal.xml +++ b/src/import/chips/p9/procedures/xml/error_info/p9_memory_mss_eff_config_thermal.xml @@ -5,7 +5,7 @@ - + @@ -34,182 +34,6 @@ - - RC_MSS_NO_POWER_THERMAL_ATTR_FOUND - - There was no match or value found in decoding the power thermal attributes - - GENERATED_KEY - FUNCTION - DIMM_TARGET - SIZE - DRAM_GEN - DIMM_TYPE - DRAM_WIDTH - DRAM_DENSITY - STACK_TYPE - MFGID - - CODE - HIGH - - - - - RC_MSS_POWER_THERMAL_ENCODE_ERROR - - There was no match or value found in encoding the power thermal attributes - - DIMM_TARGET - ATTR - - CODE - HIGH - - - - - RC_MSS_POWER_THERMAL_DECODE_ERROR - - There was no match or value found in decoding the power thermal attributes - - DIMM_TARGET - ATTR - - CODE - HIGH - - - - - RC_MSS_POWER_INTERCEPT_NOT_SET - - The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 - - - CODE - HIGH - - - - - RC_MSS_POWER_SLOPE_NOT_SET - - The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 - - - CODE - HIGH - - - - - RC_MSS_NO_DATABUS_UTILIZATION - - There are 2 DIMMS on the port but both have 0 databus utilization - - PORT_DATABUS_UTIL - DIMM_COUNT - - CODE - HIGH - - - - - RC_MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO - - Denominator equals 0 - - PORT_DATABUS_UTIL - UTIL_CONVERSION - IDLE_UTIL - RESULT - - CODE - HIGH - - - - - RC_MSS_NO_PORT_POWER_LIMIT - - Got 0 when calculating port power limit. - Either no dimms or attribute MEM_WATT_TARGET wasn't set - - COUNT_DIMMS - PORT_POWER_LIMIT - - CODE - HIGH - - - - - RC_MSS_NO_PORT_POWER - - Got 0 when calculating port power limits using the DIMMs databus utilization - - COUNT_DIMMS - MAX_UTILIZATION_DIMM_0 - MAX_UTILIZATION_DIMM_1 - - CODE - HIGH - - - - - RC_MSS_M_DRAM_CLOCKS_EQUALS_ZERO - - ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero - - - CODE - HIGH - - - - - RC_MSS_CALC_PORT_POWER_EXCEEDS_MAX - - The calculated port power from equalizing throttles exceeds the maximum allowed power - - CALCULATED_PORT_POWER - MAX_POWER_ALLOWED - PORT_POS - - CODE - HIGH - - - - MCA_TARGET - TARGET_TYPE_DIMM - - MEDIUM - - - - MCA_TARGET - TARGET_TYPE_DIMM - - - - - - RC_MSS_SLOT_UTIL_EXCEEDS_PORT - - The memory throttle per slot (DIMM) exceeds the allowed throttle for the port - - SLOT_UTIL - PORT_UTIL - - CODE - HIGH - - - RC_MSS_SPLIT_UTIL_CALC_ERROR @@ -225,17 +49,6 @@ - - RC_MSS_OUTPUT_OVERFLOW_CALC_UTIL - - Type of output variable is not large enough for the calculations - - RESULT - - CODE - HIGH - - RC_MSS_DIMM_COUNT_EXCEEDS_VMEM_REGULATOR_LIMIT diff --git a/src/import/generic/memory/lib/mss_generic_attribute_getters.H b/src/import/generic/memory/lib/mss_generic_attribute_getters.H index c8fd30b63..eb791e7f3 100644 --- a/src/import/generic/memory/lib/mss_generic_attribute_getters.H +++ b/src/import/generic/memory/lib/mss_generic_attribute_getters.H @@ -2206,6 +2206,53 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief ATTR_MEM_EFF_DRAM_MODULE_HEIGHT getter +/// @param[in] const ref to the TARGET_TYPE_DIMM +/// @param[out] uint8_t& reference to store the value +/// @note Generated by gen_accessors.pl generate_mc_port_params +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note ARRAY[DIMM] DRAM Modlue Height Decodes SPD Byte 193 +/// +inline fapi2::ReturnCode get_dram_module_height(const fapi2::Target& i_target, + uint8_t& o_value) +{ + uint8_t l_value[2] = {}; + const auto l_port = i_target.getParent(); + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MEM_EFF_DRAM_MODULE_HEIGHT, l_port, l_value) ); + o_value = l_value[mss::index(i_target)]; + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed getting ATTR_MEM_EFF_DRAM_MODULE_HEIGHT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief ATTR_MEM_EFF_DRAM_MODULE_HEIGHT getter +/// @param[in] const ref to the TARGET_TYPE_MEM_PORT +/// @param[out] uint8_t&[] array reference to store the value +/// @note Generated by gen_accessors.pl generate_mc_port_params +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note ARRAY[DIMM] DRAM Modlue Height Decodes SPD Byte 193 +/// +inline fapi2::ReturnCode get_dram_module_height(const fapi2::Target& i_target, + uint8_t (&o_array)[2]) +{ + uint8_t l_value[2] = {}; + + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_MEM_EFF_DRAM_MODULE_HEIGHT, i_target, l_value) ); + memcpy(o_array, &l_value, 2); + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR("failed getting ATTR_MEM_EFF_DRAM_MODULE_HEIGHT: 0x%lx (target: %s)", + uint64_t(fapi2::current_err), mss::c_str(i_target)); + return fapi2::current_err; +} + /// /// @brief ATTR_MEM_EFF_RCD_MFG_ID getter /// @param[in] const ref to the TARGET_TYPE_DIMM diff --git a/src/import/generic/memory/lib/utils/dimm/kind.H b/src/import/generic/memory/lib/utils/dimm/kind.H index bcb29264d..aecec90b9 100644 --- a/src/import/generic/memory/lib/utils/dimm/kind.H +++ b/src/import/generic/memory/lib/utils/dimm/kind.H @@ -22,3 +22,249 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file dimm.H +/// @brief Encapsulation for dimms of all types +/// +// *HWP HWP Owner: Louis Stermole +// *HWP HWP Backup: Andre Marin +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_DIMM_H_ +#define _MSS_DIMM_H_ + +#include + +#include +#include + +namespace mss +{ + +namespace dimm +{ + +/// +/// @class mss::dimm::kind +/// @brief A class containing information about a dimm like ranks, density, configuration - what kind of dimm is it? +/// +class kind +{ + public: + + /// + /// @brief Generate a vector of DIMM kind from a vector of DIMM + /// @param[in] i_dimm a vector of DIMM + /// @return std::vector of dimm::kind relating to the DIMM passed in + /// + static std::vector vector(const std::vector>& i_dimm) + { + std::vector l_kinds; + + for (const auto& d : i_dimm) + { + l_kinds.push_back( kind(d) ); + } + + return l_kinds; + } + + /// + /// @brief operator=() - assign kinds (needed to sort vectors of kinds) + /// @param[in] i_rhs the right hand side of the assignment statement + /// @return reference to this + /// + inline kind& operator=(const kind& i_rhs) + { + iv_target = i_rhs.iv_target; + iv_master_ranks = i_rhs.iv_master_ranks; + iv_total_ranks = i_rhs.iv_total_ranks; + iv_dram_density = i_rhs.iv_dram_density; + iv_dram_width = i_rhs.iv_dram_width; + iv_dram_generation = i_rhs.iv_dram_generation; + iv_dimm_type = i_rhs.iv_dimm_type; + iv_rows = i_rhs.iv_rows; + iv_size = i_rhs.iv_size; + iv_mfgid = i_rhs.iv_mfgid; + iv_stack_type = i_rhs.iv_stack_type; + iv_hybrid = i_rhs.iv_hybrid; + iv_hybrid_memory_type = i_rhs.iv_hybrid_memory_type; + iv_rcd_mfgid = i_rhs.iv_rcd_mfgid; + iv_module_height = i_rhs.iv_module_height; + return *this; + } + + /// + /// @brief operator==() - are two kinds the same? + /// @param[in] i_rhs the right hand side of the comparison statement + /// @return bool true iff the two kind are of the same kind + /// @warning this does not compare the targets (iv_target,) just the values + /// Also does not compare the mfgid as that's not really part of the DIMM kind but is additional information + /// + inline bool operator==(const kind& i_rhs) const + { + return ((iv_master_ranks == i_rhs.iv_master_ranks) && + (iv_total_ranks == i_rhs.iv_total_ranks) && + (iv_dram_density == i_rhs.iv_dram_density) && + (iv_dram_width == i_rhs.iv_dram_width) && + (iv_dram_generation == i_rhs.iv_dram_generation) && + (iv_dimm_type == i_rhs.iv_dimm_type) && + (iv_rows == i_rhs.iv_rows) && + (iv_size == i_rhs.iv_size) && + (iv_stack_type == i_rhs.iv_stack_type) && + (iv_hybrid == i_rhs.iv_hybrid) && + (iv_hybrid_memory_type == i_rhs.iv_hybrid_memory_type) && + (iv_rcd_mfgid == i_rhs.iv_rcd_mfgid) && + (iv_module_height == i_rhs.iv_module_height)); + } + + /// + /// @brief operator!=() - are two kinds different? + /// @param[in] i_rhs the right hand side of the comparison statement + /// @return bool true iff the two kind are of different + /// @warning this does not compare the targets (iv_target,) just the values + /// + inline bool operator!=(const kind& i_rhs) const + { + return !(this->operator==(i_rhs)); + } + + /// + /// @brief Construct a dimm::kind data structure - information about the kind of DIMM this is + /// @param[in] i_target a DIMM target + /// + kind(const fapi2::Target& i_target): + iv_target(i_target) + { + FAPI_TRY( mss::attr::get_dram_gen(i_target, iv_dram_generation) ); + FAPI_TRY( mss::attr::get_dimm_type(i_target, iv_dimm_type) ); + FAPI_TRY( mss::attr::get_dram_density(i_target, iv_dram_density) ); + FAPI_TRY( mss::attr::get_dram_width(i_target, iv_dram_width) ); + FAPI_TRY( mss::attr::get_num_master_ranks_per_dimm(i_target, iv_master_ranks) ); + FAPI_TRY( mss::attr::get_logical_ranks_per_dimm(i_target, iv_total_ranks) ); + FAPI_TRY( mss::attr::get_dram_row_bits(i_target, iv_rows) ); + FAPI_TRY( mss::attr::get_dimm_size(i_target, iv_size) ); + FAPI_TRY( mss::attr::get_dram_mfg_id(i_target, iv_mfgid) ); + FAPI_TRY( mss::attr::get_prim_stack_type( i_target, iv_stack_type) ); + FAPI_TRY( mss::attr::get_hybrid( i_target, iv_hybrid )); + FAPI_TRY( mss::attr::get_hybrid_memory_type( i_target, iv_hybrid_memory_type )); + FAPI_TRY( mss::attr::get_rcd_mfg_id(i_target, iv_rcd_mfgid) ); + FAPI_TRY( mss::attr::get_dram_module_height(i_target, iv_module_height) ); + return; + + fapi_try_exit: + // Not 100% sure what to do here ... + FAPI_ERR("error initializing DIMM structure: %s 0x%016lx", mss::c_str(i_target), uint64_t(fapi2::current_err)); + fapi2::Assert(false); + } + + /// + /// @brief Construct a DIMM kind used to identify this DIMM for tables. + /// @param[in] i_master_ranks number of master ranks on the DIMM + /// @param[in] i_total_ranks total number of ranks on the DIMM + /// @param[in] i_dram_density density of the DRAM + /// @param[in] i_dram_width width of the DRAM + /// @param[in] i_dram_generation DRAM generation + /// @param[in] i_dimm_type DIMM type (e.g. RDIMM) + /// @param[in] i_rows number of rows in the DRAM + /// @param[in] i_size the overal size of the DIMM in GB + /// @param[in] i_mfgid the dram manufacturer id of the dimm, defaulted to 0 + /// @param[in] i_stack_type dram die type, single die package or 3DS + /// @param[in] i_hybrid, default not hybrid + /// @param[in] i_hybrid_memory_type, defult none + /// @param[in] i_rcd_mfgid dimm register and data buffer manufacturer id, default 0 + /// @note can't be constexpr as fapi2::Target doesn't have a constexpr ctor. + /// + kind( const uint8_t i_master_ranks, + const uint8_t i_total_ranks, + const uint8_t i_dram_density, + const uint8_t i_dram_width, + const uint8_t i_dram_generation, + const uint8_t i_dimm_type, + const uint8_t i_rows, + const uint32_t i_size, + const uint16_t i_mfgid = 0, + const uint8_t i_stack_type = fapi2::ENUM_ATTR_EFF_PRIM_STACK_TYPE_SDP, + const uint8_t i_hybrid = fapi2::ENUM_ATTR_EFF_HYBRID_NOT_HYBRID, + const uint8_t i_hybrid_memory_type = fapi2::ENUM_ATTR_EFF_HYBRID_MEMORY_TYPE_NONE, + const uint16_t i_rcd_mfgid = 0, + const uint8_t i_module_height = 0 + ): + iv_target(0), + iv_master_ranks(i_master_ranks), + iv_total_ranks(i_total_ranks), + iv_dram_density(i_dram_density), + iv_dram_width(i_dram_width), + iv_dram_generation(i_dram_generation), + iv_dimm_type(i_dimm_type), + iv_rows(i_rows), + // TK consider calculating size rather than requiring it be set. + iv_size(i_size), + iv_mfgid(i_mfgid), + iv_stack_type(i_stack_type), + iv_hybrid(i_hybrid), + iv_hybrid_memory_type(i_hybrid_memory_type), + iv_rcd_mfgid(i_rcd_mfgid), + iv_module_height(i_module_height) + { + // Bit of an idiot-check to be sure a hand-crafted dimm::kind make sense wrt slaves, masters, packages, etc. + // Both of these are checked in eff_config. If they are messed up, they should be caught there + if (iv_master_ranks > iv_total_ranks) + { + FAPI_ERR("Not enough total ranks? master: %d total: %d", + iv_master_ranks, + iv_total_ranks); + fapi2::Assert(false); + } + + if ((iv_total_ranks % iv_master_ranks) != 0) + { + FAPI_ERR("total or master ranks seems incorrect. master: %d total: %d", + iv_master_ranks, + iv_total_ranks); + fapi2::Assert(false); + } + } + + fapi2::Target iv_target; + uint8_t iv_master_ranks; + uint8_t iv_total_ranks; + uint8_t iv_dram_density; + uint8_t iv_dram_width; + uint8_t iv_dram_generation; + uint8_t iv_dimm_type; + uint8_t iv_rows; + uint32_t iv_size; + uint16_t iv_mfgid; + uint8_t iv_stack_type; + uint8_t iv_hybrid; + uint8_t iv_hybrid_memory_type; + uint16_t iv_rcd_mfgid; + uint8_t iv_module_height; + + /// + /// @brief equal_config + /// @param[in] i_input_compare the i_kind to compare against + /// @return bool true iff the two kind are of the same kind for xlate purposes + /// @warning this does not compare the targets (iv_target,), mfgid, prim_stack_type nor hybrid type + /// + inline bool equal_config(const kind& i_input_compare) const + { + return ((iv_master_ranks == i_input_compare.iv_master_ranks) && + (iv_total_ranks == i_input_compare.iv_total_ranks) && + (iv_dram_density == i_input_compare.iv_dram_density) && + (iv_dram_width == i_input_compare.iv_dram_width) && + (iv_dram_generation == i_input_compare.iv_dram_generation) && + (iv_dimm_type == i_input_compare.iv_dimm_type) && + (iv_rows == i_input_compare.iv_rows) && + (iv_size == i_input_compare.iv_size)); + } +}; + +} + +} +#endif diff --git a/src/import/generic/memory/lib/utils/mss_math.H b/src/import/generic/memory/lib/utils/mss_math.H index ebb3da8e3..65fc0f898 100644 --- a/src/import/generic/memory/lib/utils/mss_math.H +++ b/src/import/generic/memory/lib/utils/mss_math.H @@ -108,6 +108,24 @@ fapi_try_exit: } +/// +/// @brief Determines if the double has decimal digits and adds 1 and rounds if true +/// @param[in] i_val the double to be rounded up if trialing digits +/// @return the input value rounded up to the next whole digit +/// @note Called in p9_mss_bulk_pwr_throttles +/// +inline uint32_t round_up(const double i_val) +{ + //convert to uint to truncate decimals and convert back to double for comparison + uint32_t temp = uint32_t (i_val); + + //if not equal, lost something from truncating, so add 1 + temp += (temp == i_val) ? 0 : 1; + + //Truncate final value + return temp; +} + }// mss #endif diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H b/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H index 7e2c47406..1c6365d37 100644 --- a/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H +++ b/src/import/generic/memory/lib/utils/power_thermal/gen_decoder.H @@ -22,3 +22,587 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_decoder.H +/// @brief Decoder for ATTR_MSS_MRW_PWR_CURVE_SLOPE and _INTERCEPT and THERMAL_POWER_LIMIT +/// +// *HWP HWP Owner: Louis Stermole +// *HWP HWP Backup: Stephen Glancy +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_GEN_POWER_DECODER__ +#define _MSS_GEN_POWER_DECODER__ + +#include +#include +#include +#include +#include + +namespace mss +{ + +namespace power_thermal +{ + +constexpr uint32_t ANY_SIZE = 0xFFFFFFFF; +constexpr uint8_t ANY_TYPE = 0xFF; +constexpr uint8_t ANY_GEN = 0xFF; +constexpr uint8_t ANY_WIDTH = 0xFF; +constexpr uint8_t ANY_DENSITY = 0xFF; +constexpr uint8_t ANY_STACK_TYPE = 0xFF; +constexpr uint16_t ANY_MFGID = 0xFFFF; +constexpr uint8_t ANY_HEIGHT = 0xFF; +constexpr uint8_t ANY_PORT = 0xFF; + +//Currently needs to be in sorted order for lookup to work +static const std::vector< std::pair > DIMM_SIZE_MAP = +{ + {4, 0b0000}, + {8, 0b0001}, + {16, 0b0010}, + {32, 0b0011}, + {64, 0b0100}, + {128, 0b0101}, + {256, 0b0110}, + {512, 0b0111}, + {ANY_SIZE, 0b1111} +}; + + + +static const std::vector< std::pair > DRAM_GEN_MAP = +{ + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_EMPTY, 0b00}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_DDR3, 0b01}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_GEN_DDR4, 0b10}, + {ANY_GEN, 0b11} +}; + +static const std::vector > DRAM_WIDTH_MAP = +{ + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X4, 0b000}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X8, 0b001}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X16, 0b010}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_WIDTH_X32, 0b011}, + {ANY_WIDTH, 0b111} +}; + +static const std::vector< std::pair > DRAM_DENSITY_MAP = +{ + {4, 0b000}, + {8, 0b001}, + {16, 0b010}, + {32, 0b011}, + {64, 0b100}, + {ANY_DENSITY, 0b111} +}; + +static const std::vector > DRAM_STACK_TYPE_MAP = +{ + {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_SDP, 0b00}, + {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_DDP_QDP, 0b01}, + {fapi2::ENUM_ATTR_MEM_EFF_PRIM_STACK_TYPE_3DS, 0b10}, + {ANY_STACK_TYPE, 0b11} +}; + +//Note, the first entries of the pairs need to be in sorted order!! +static const std::vector > DRAM_MFGID_MAP = +{ + //Kingston + {0x0198, 0b011}, + //A-data + {0x04CB, 0b101}, + //Micron + {0x802C, 0b000}, + //HYNIX + {0x80AD, 0b001}, + //SAMSUNG + {0x80CE, 0b010}, + //Innodisk + {0x86F1, 0b100}, + // ANY + {ANY_MFGID, 0b111} +}; + +static const std::vector< std::pair > DIMM_MODULE_HEIGHT_MAP = +{ + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_1U, 0b00}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_2U, 0b01}, + {fapi2::ENUM_ATTR_MEM_EFF_DRAM_MODULE_HEIGHT_4U, 0b10}, + {ANY_HEIGHT, 0b11} +}; + +static const std::vector < std::pair< uint8_t, uint8_t> > DIMMS_PORT_MAP = +{ + //Num dimms per MCA, only 1 or 2 possible options. 0 is no-op + {1, 0b00}, + {2, 0b01}, + {ANY_PORT, 0b11} +}; + +// Forward declaration +template> +fapi2::ReturnCode generate_wildcard_mask(const uint32_t i_hash, uint32_t& o_mask); + + +/// +///@brief a compare functor for the decoder::find_attr functions below +/// @tparam MC mss::mc_type +/// +template +struct is_match +{ + /// + ///@brief functor constructor + ///@param[in] i_gen_key the class object's constructed hash for the installed dimm, to be compared with the attr array + /// + is_match(const uint32_t i_gen_key) : iv_gen_key(i_gen_key) {} + const fapi2::buffer iv_gen_key; + + /// + ///@brief Boolean compare used for find_if function + /// + bool operator()(const uint64_t i_hash) + { + // l_this_hash is the first half of the i_hash's bits + const uint32_t l_this_hash = i_hash >> 32; + uint32_t l_wildcard_mask = 0; + + // Get wildcard mask. If the decoding fails(value to key), we should continue + generate_wildcard_mask(l_this_hash, l_wildcard_mask); + + // Mask the wildcard bits + return ((l_this_hash | l_wildcard_mask) == (iv_gen_key | l_wildcard_mask)); + } +}; + +/// +/// @brief Encode the attribute into a bit encoding +/// @tparam S *ATTR*_SIZE enum used for fapi2::buffer position +/// @tparam L *ATTR*_LEN enum used for fapi2::buffer position +/// @tparam T integral type of key +/// @tparam OT fapi2::buffer of some integral type +/// @param[in] i_target the DIMM the encoding is for +/// @param[in] i_attr the attribute key being used for the encoding +/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value, sorted +/// @param[out] o_buf the fapi2::buffer where the encoding is going into +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup +/// +template +inline fapi2::ReturnCode encode ( const fapi2::Target& i_target, + const T& i_attr, + const std::vector >& i_map, + fapi2::buffer& o_buf) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + //used to hold result from vector pair lookup + OT l_encoding = 0; + + //Failing out if we don't find an encoding. All suported types should be encoded above + FAPI_ASSERT( mss::find_value_from_key (i_map, i_attr, l_encoding), + fapi2::MSS_POWER_THERMAL_ENCODE_ERROR() + .set_ATTR(i_attr) + .set_DIMM_TARGET(i_target), + "Couldn't find encoding for power thermal encode for value: %x target: %s", i_attr, mss::c_str(i_target)); + o_buf.insertFromRight(l_encoding); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode the attribute into a bit encoding +/// @tparam S DRAM_GEN_SIZE enum used for fapi2::buffer position +/// @tparam L DRAM_GEN_LEN enum used for fapi2::buffer position +/// @tparam T integral type of key +/// @tparam OT fapi2::buffer of some integral type +/// @param[in] i_map a vector of pairs of the ATTR values and encodings for each value +/// @param[in] i_buf the fapi2::buffer that has the encoding to parse +/// @param[out] o_attr the attribute value from the encoding is going +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff attribute is found in map lookup +/// +template +inline fapi2::ReturnCode decode (const std::vector >& i_map, + fapi2::buffer& i_buf, + T& o_attr ) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + //used to hold result from vector pair lookup + OT l_encoding = 0; + i_buf.extractToRight(l_encoding); + + //Failing out if we don't find an decoding. All suported types should be encoded above + FAPI_ASSERT( mss::find_key_from_value (i_map, l_encoding, o_attr), + fapi2::MSS_POWER_THERMAL_DECODE_ERROR() + .set_ATTR(l_encoding), + "Couldn't find encoding for power thermal decode"); +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief generates wildcard mask for the hash value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @tparam MC mss::mc_type +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_hash The encoded value +/// @param[out] o_mask The wildcard mask +/// + +template> +fapi2::ReturnCode generate_wildcard_mask(const uint32_t i_hash, uint32_t& o_mask) +{ + + fapi2::buffer l_mask; + fapi2::buffer l_hash = i_hash; + uint8_t l_uint8_buf = 0; + uint16_t l_uint16_buf = 0; + uint32_t l_uint32_buf = 0; + + //DIMM_SIZE wildcard + FAPI_TRY(( decode + (DIMM_SIZE_MAP, l_hash, l_uint32_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DIMM_SIZE", l_hash ); + + if(ANY_SIZE == l_uint32_buf) + { + l_mask.setBit(TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN); + } + + //DRAM_GEN wildcard + FAPI_TRY(( decode + (DRAM_GEN_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DRAM_GEN", l_hash); + + if(ANY_GEN == l_uint8_buf) + { + l_mask.setBit(TT::DRAM_GEN_START, TT::DRAM_GEN_LEN); + } + + //DIMM_TYPE wildcard + FAPI_TRY(( decode + (TT::DIMM_TYPE_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DIMM_TYPE", l_hash); + + if(ANY_TYPE == l_uint8_buf) + { + l_mask.setBit(TT::DIMM_TYPE_START, TT::DIMM_TYPE_LEN); + } + + //DRAM_WIDTH wildcard + FAPI_TRY(( decode + (DRAM_WIDTH_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DRAM_WIDTH", l_hash); + + if(ANY_WIDTH == l_uint8_buf) + { + l_mask.setBit(TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN); + } + + //DRAM_DENSITY wildcard + FAPI_TRY(( decode + (DRAM_DENSITY_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DRAM_DENSITY", l_hash); + + if(ANY_DENSITY == l_uint8_buf) + { + l_mask.setBit(TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN); + } + + //DRAM_STACK_TYPE wildcard + FAPI_TRY(( decode + (DRAM_STACK_TYPE_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DRAM_STACK_TYPE", l_hash); + + if(ANY_STACK_TYPE == l_uint8_buf) + { + l_mask.setBit(TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN); + } + + //DRAM_MFGID wildcard + FAPI_TRY(( decode + (DRAM_MFGID_MAP, l_hash, l_uint16_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DRAM_MFG_ID", l_hash); + + if(ANY_MFGID == l_uint16_buf) + { + l_mask.setBit(TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN); + } + + + if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_MCS) + { + //NUM DROPS PER PORT wildcard + FAPI_TRY(( decode + (DIMMS_PORT_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DIMMS_PER_PORT", l_hash); + + if(ANY_PORT == l_uint8_buf) + { + l_mask.setBit(TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN); + } + } + + if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_OCMB_CHIP) + { + //MODUEL HEIGHT wildcard + FAPI_TRY(( decode + (DIMM_MODULE_HEIGHT_MAP, l_hash, l_uint8_buf)), + "Failed to generate power thermal decoding for %s val %d", + "DIMMS_MODULE_HEIGHT", l_hash); + + if(ANY_HEIGHT == l_uint8_buf) + { + l_mask.setBit(TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN); + } + } + + o_mask = l_mask; + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @class decoder +/// @brief Decodes the power curve and thermal power limit attributes for eff_config_thermal +/// @tparam MC mss::mc_type +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// +template> +class decoder +{ + public: + + //IVs for all of the attributes per MCS + const mss::dimm::kind iv_kind; + + //Left in here rather than calculating during encode for testing + uint8_t iv_dimms_per_port; + + //Power thermal attributes, both total and vddr versions will be used in eff_config_thermal + uint16_t iv_vddr_slope = 0; + uint16_t iv_vddr_intercept = 0; + uint16_t iv_total_slope = 0; + uint16_t iv_total_intercept = 0; + + // Valid for OCMB only + uint32_t iv_power_limit = 0 ; + + uint32_t iv_thermal_power_limit = 0; + + //Generated key, used to decode all three power curve attributes + fapi2::buffer iv_gen_key; + + /// + /// @brief Constructor + /// @param[in] dimm::kind to call power thermal stuff on + /// + decoder( const mss::dimm::kind& i_kind): + iv_kind(i_kind) + { + iv_dimms_per_port = mss::count_dimm (find_target(iv_kind.iv_target)); + }; + + // + // @brief Default destructor + // + ~decoder() = default; + + /// + /// @brief generates the 32 bit encoding for the power curve attributes + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful + /// @note populates iv_gen_key + /// + fapi2::ReturnCode generate_encoding (); + + /// + /// @brief Finds a value for the power curve slope attributes by matching the generated hashes + /// @param[in] i_array is a vector of the attribute values + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful + /// @note populates iv_vddr_slope, iv_total_slope + /// + fapi2::ReturnCode find_slope (const std::vector< const std::vector* >& i_slope); + + /// + /// @brief Finds a value for power curve intercept attributes by matching the generated hashes + /// @param[in] i_array is a vector of the attribute values + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful + /// @note populates iv_vddr_intercept, iv_total_intercept + /// + fapi2::ReturnCode find_intercept (const std::vector< const std::vector* >& i_intercept); + + /// + /// @brief Finds a value from ATTR_MSS_MRW_THERMAL_MEMORY_POWER_LIMIT and stores in iv variable + /// @param[in] i_array is a vector of the attribute values + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful + /// @note populates iv_thermal_power_limit + /// + fapi2::ReturnCode find_thermal_power_limit (const std::vector< const std::vector* >& i_thermal_limits); + + /// + /// @brief Helper function to find the value from attribute + /// @tparam FIELD_START the field start offset inside attribute + /// @tparam FIELD_LEN the field length to extract + /// @tparam FUNCTION the function of the field + /// @tparam OT output type + /// @param[in] i_array is a vector of the attribute values + /// @param[in] i_attr_description the attribute description + /// @param[out] o_value the output value + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful + /// @note populates iv_thermal_power_limit + /// + template + fapi2::ReturnCode get_power_thermal_value(const std::vector& i_array, + const char* const i_attr_description, + OT& o_value); +}; + +/// +/// @brief generates the 32 bit encoding for the power curve attributes +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @note populates iv_gen_keys +/// +template +fapi2::ReturnCode decoder::generate_encoding() +{ + //DIMM_SIZE + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_size, DIMM_SIZE_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DIMM_SIZE", iv_kind.iv_size, mss::c_str(iv_kind.iv_target) ); + + //DRAM_GEN + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_dram_generation, DRAM_GEN_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DRAM_GEN", iv_kind.iv_dram_generation, mss::c_str(iv_kind.iv_target) ); + + //DIMM_TYPE + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_dimm_type, TT::DIMM_TYPE_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DIMM_TYPE", iv_kind.iv_dimm_type, mss::c_str(iv_kind.iv_target) ); + + //DRAM WIDTH + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_dram_width, DRAM_WIDTH_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DRAM_WIDTH", iv_kind.iv_dram_width, mss::c_str(iv_kind.iv_target) ); + + //DRAM DENSITY + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_dram_density, DRAM_DENSITY_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DRAM_DENSITY", iv_kind.iv_dram_density, mss::c_str(iv_kind.iv_target) ); + + //DRAM STACK TYPE + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_stack_type, DRAM_STACK_TYPE_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DRAM_STACK_TYPE", iv_kind.iv_stack_type, mss::c_str(iv_kind.iv_target) ); + + //DRAM MFG ID + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_mfgid, DRAM_MFGID_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DRAM_MFG_ID", iv_kind.iv_mfgid, mss::c_str(iv_kind.iv_target) ); + + if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_MCS) + { + //NUM DROPS PER PORT + FAPI_TRY(( encode + (iv_kind.iv_target, iv_dimms_per_port, DIMMS_PORT_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DIMMS_PER_PORT", iv_dimms_per_port, mss::c_str(iv_kind.iv_target) ); + } + + if (TT::MC_TARGET_TYPE == fapi2::TARGET_TYPE_OCMB_CHIP) + { + //DIMM_MODULE_HEIGHT + FAPI_TRY(( encode + (iv_kind.iv_target, iv_kind.iv_module_height, DIMM_MODULE_HEIGHT_MAP, iv_gen_key)), + "Failed to generate power thermal encoding for %s val %d on target: %s", + "DIMM_MODULE_HEIGHT", iv_kind.iv_module_height, mss::c_str(iv_kind.iv_target) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find the value from attribute +/// @param[in] i_array is a vector of the attribute values +/// @param[in] i_attr_description the attribute description +/// @param[out] o_value the output value +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff the encoding was successful +/// @note populates iv_thermal_power_limit +/// +template +template +fapi2::ReturnCode decoder::get_power_thermal_value(const std::vector& i_array, + const char* const i_attr_description, + OT& o_value) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Find iterator to matching key (if it exists) + auto l_value_iterator = std::find_if(i_array.begin(), + i_array.end(), + is_match<>(iv_gen_key)); + + FAPI_ASSERT(l_value_iterator != i_array.end(), + fapi2::MSS_NO_POWER_THERMAL_ATTR_FOUND() + .set_GENERATED_KEY(iv_gen_key) + .set_FUNCTION(FUNCTION) + .set_DIMM_TARGET(iv_kind.iv_target) + .set_SIZE(iv_kind.iv_size) + .set_DRAM_GEN(iv_kind.iv_dram_generation) + .set_DIMM_TYPE(iv_kind.iv_dimm_type) + .set_DRAM_WIDTH( iv_kind.iv_dram_width) + .set_DRAM_DENSITY(iv_kind.iv_dram_density) + .set_STACK_TYPE(iv_kind.iv_stack_type) + .set_MFGID(iv_kind.iv_mfgid) + .set_MODULE_HEIGHT(iv_kind.iv_module_height), + "Couldn't find %s value for generated key:0x%08lx, for target %s. " + "DIMM values for generated key are " + "size is %d, gen is %d, type is %d, width is %d, density %d, stack %d, mfgid %d, dimms %d, height %d", + i_attr_description, + iv_gen_key, + mss::c_str(iv_kind.iv_target), + iv_kind.iv_size, + iv_kind.iv_dram_generation, + iv_kind.iv_dimm_type, + iv_kind.iv_dram_width, + iv_kind.iv_dram_density, + iv_kind.iv_stack_type, + iv_kind.iv_mfgid, + iv_dimms_per_port, + iv_kind.iv_module_height + ); + { + const fapi2::buffer l_temp(*l_value_iterator); + l_temp.extractToRight(o_value); + } + +fapi_try_exit: + return fapi2::current_err; +} + +} // power_thermal +} // mss +#endif diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H index 4d99317bf..d90c6bb01 100644 --- a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H +++ b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle.H @@ -22,3 +22,1207 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ +/// +/// @file gen_throttle.H +/// @brief throttle API +/// + +// *HWP HWP Owner: Andre Marin +// *HWP HWP Backup: Louis Stermole +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_GEN_POWER_THROTTLE_ +#define _MSS_GEN_POWER_THROTTLE_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mss +{ + +namespace power_thermal +{ + +/// +/// @brief throttle constants used in the power_thermal functions +/// +enum throttle_const : size_t +{ + /// Dram data bus utilization is bus utilization / 4 + DRAM_BUS_UTILS = 4, + + /// 10000 to convert to and from c% + UTIL_CONVERSION = 10000, + + /// Conversion to percentage + PERCENT_CONVERSION = 100, + +}; + +/// +/// @brief Calculate N (address operations) allowed within a window of M DRAM clocks +/// @param[in] i_databus_util databus utilization percentage (e.g. 5% = 5) +/// @param[in] i_num_dram_clocks window of M DRAM clocks +/// @return number of throttled commands allowed +/// @note Uses N/M Throttling. +/// Equation: N = (DRAM data bus utilization * M) / (4 * 10000) +/// +inline uint32_t throttled_cmds(const uint32_t i_databus_util, const uint32_t i_num_dram_clocks) +{ + constexpr uint64_t l_divisor = DRAM_BUS_UTILS * UTIL_CONVERSION; + const uint64_t l_dividend = i_databus_util * i_num_dram_clocks; + const uint64_t l_result = l_dividend / l_divisor; + + //Make sure N is not equal to 0, or we brick the dram until reboot + return ((l_result == 0) ? 1 : l_result); +} + +/// +/// @brief Calculate the port databus utilization based off of N throttles and M dram clocks +/// @tparam MC mss::mc_type +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @tparam T output type +/// @param[in] i_n_throttles N (address operations) allowed within a window of M DRAM clocks +/// @param[in] i_num_dram_clocks window of M DRAM clocks +/// @param[out] o_calc_util +/// @return FAPI2_RC_SUCCESS iff method was a success +/// @note Uses N/M Throttling. +/// @note DRAM databus utilization = N * 4 * 10000 / M +/// +template, typename T> +fapi2::ReturnCode calc_util_from_throttles(const uint16_t i_n_throttles, + const uint32_t i_num_dram_clocks, + T& o_calc_util) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + constexpr uint32_t l_multiplier = DRAM_BUS_UTILS * UTIL_CONVERSION; + const uint64_t l_calc_util_uint64 = static_cast((static_cast(i_n_throttles) * l_multiplier) / + i_num_dram_clocks); + FAPI_ASSERT( (i_num_dram_clocks != 0), + fapi2::MSS_M_DRAM_CLOCKS_EQUALS_ZERO(), + "ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero"); + + o_calc_util = (static_cast(i_n_throttles) * l_multiplier) / i_num_dram_clocks; + + // Best way to check for overflow if o_calc_util can be a double? + // If o_calc_util overflows, the value inside will be below the expected outcome + // So compare o_calc_util with the calculated value, but store calculated value in largest storage + // Compare ">=" because o_calc_util can be a double, and so we can't compare just equality due to truncation + FAPI_ASSERT( o_calc_util >= l_calc_util_uint64, + fapi2::MSS_OUTPUT_OVERFLOW_CALC_UTIL() + .set_RESULT(o_calc_util), + "Overflow of output variable in calc_util_from_throttles throttles: %d, multiplier %d, dram_clocks %d", + i_n_throttles, + l_multiplier, + i_num_dram_clocks); + + // Check for the minimum + if(o_calc_util < TT::MIN_UTIL) + { + FAPI_INF("Calculated utilization (%f) is less than the minimum utilization: %lu. Setting to minimum value", + o_calc_util, TT::MIN_UTIL); + o_calc_util = TT::MIN_UTIL; + } + + FAPI_INF("In calc_util_from_throttles, calculated %f for output utilization from throttles:%d, dram_clocks%d", + o_calc_util, i_n_throttles, i_num_dram_clocks); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform thermal calculations as part of the effective configuration +/// @tparam MC mss::mc_type +/// @tparam T the fapi2 target type of the target +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_target the MCS target in which the runtime throttles will be reset +/// @return FAPI2_RC_SUCCESS iff ok +/// +template> +fapi2::ReturnCode restore_runtime_throttles( const fapi2::Target& i_target ) +{ + + uint32_t l_max_databus = 0; + uint32_t l_throttle_m_clocks = 0; + + FAPI_TRY( mss::attr::get_mrw_mem_m_dram_clocks(l_throttle_m_clocks) ); + FAPI_TRY( mss::attr::get_mrw_max_dram_databus_util(l_max_databus) ); + + //Set runtime throttles to unthrottled value, using max dram utilization and M throttle + //Do I need to check to see if any DIMMS configured on the port? + for (const auto& l_port : mss::find_targets(i_target)) + { + uint16_t l_run_throttle = 0; + + if (mss::count_dimm (l_port) != 0) + { + l_run_throttle = mss::power_thermal::throttled_cmds (l_max_databus, l_throttle_m_clocks); + } + + FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_port( l_port, l_run_throttle) ); + FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_slot( l_port, l_run_throttle) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values +/// @tparam MC mss::mc_type +/// @tparam T the fapi2 target type of the target +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_target the MCS target in which the runtime throttles will be set +/// @return FAPI2_RC_SUCCESS iff ok +/// +template> +fapi2::ReturnCode update_runtime_throttle(const fapi2::Target& i_target) + +{ + + if (mss::count_dimm(i_target) == 0) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + for (const auto& l_port : mss::find_targets(i_target)) + { + + uint16_t l_run_slot = 0; + uint16_t l_run_port = 0; + uint16_t l_calc_slot = 0; + uint16_t l_calc_port = 0; + + FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot)); + FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port)); + FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_slot(l_port, l_calc_slot)); + FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_port(l_port, l_calc_port)); + + //Choose the worst case between runtime and calculated throttles + //Have to make sure the calc_slot isn't equal to 0 though + l_run_slot = (l_calc_slot != 0) ? + std::min(l_run_slot, l_calc_slot) : l_run_slot; + l_run_port = (l_calc_port != 0) ? + std::min(l_run_port, l_calc_port) : l_run_port; + + FAPI_INF("New runtime throttles for %s for slot are %d, port are %d for %s", + mss::c_str(l_port), + l_run_slot, + l_run_port, + mss::c_str(l_port)); + + FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port) ); + FAPI_TRY( mss::attr::set_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot) ); + } + + +fapi_try_exit: + return fapi2::current_err; +} + + + +/// +/// @brief Update the runtime throttles to the worst case of the general throttle values and the runtime values +/// @tparam MC mss::mc_type +/// @tparam T the fapi2 target type of the target +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_target the MCS target in which the runtime throttles will be set +/// @return FAPI2_RC_SUCCESS iff ok +/// +template> +fapi2::ReturnCode update_runtime_throttles(const std::vector< fapi2::Target >& i_targets) + +{ + for (const auto& l_mc : i_targets) + { + FAPI_TRY(update_runtime_throttle(l_mc)); + } + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @class throttle +/// @brief Determine power_thermal throttles for memory +/// @tparam MC mss::mc_type +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// +template> +class throttle +{ + private: + /// + /// @brief Calculate the power (cW) of inputs and the power curve + /// @tparam T the type of i_util and return value + /// @param[in] i_util the databus utilization that the power will be based on + /// @param[in] l_pos the dimm position for the power value being calculated. + /// @return Integral type T + /// + template + inline T calc_power (const T i_util, const size_t i_pos, fapi2::ReturnCode& o_rc ) const + { + o_rc = fapi2::FAPI2_RC_SUCCESS; + FAPI_ASSERT( (i_pos < TT::DIMMS_PER_PORT), + fapi2::MSS_POWER_THERMAL_DIMM_INDEX_OUT_OF_BOUND() + .set_INPUT_SIZE(i_pos) + .set_MAX_SIZE(TT::DIMMS_PER_PORT), + "The dimm is index is out of bound for the port index: %d, max: %d for port %s", + i_pos, TT::DIMMS_PER_PORT, mss::c_str(iv_target) ); + + return ((i_util / UTIL_CONVERSION) * iv_pwr_slope[i_pos]) + iv_pwr_int[i_pos]; + + fapi_try_exit: + o_rc = fapi2::current_err; + return 0; + } + + /// + /// @brief Raise the o_value by the percent passed in + /// @param[in] i_uplift the percent the o_Value should be raised by + /// @param[out] o_value the value that will be modified + /// + inline void calc_power_uplift (const uint8_t i_uplift, double& o_value) const + { + o_value *= (1 + (static_cast(i_uplift) / PERCENT_CONVERSION)); + } + + public: + const fapi2::Target& iv_target; + + uint32_t iv_databus_port_max; + + uint8_t iv_power_uplift_idle; + uint8_t iv_power_uplift; + + uint16_t iv_runtime_n_slot; + uint16_t iv_runtime_n_port; + uint32_t iv_m_clocks; + uint32_t iv_dimm_thermal_limit[TT::DIMMS_PER_PORT] = {}; + uint16_t iv_pwr_slope[TT::DIMMS_PER_PORT] = {}; + uint16_t iv_pwr_int[TT::DIMMS_PER_PORT] = {}; + uint16_t iv_n_slot; + uint16_t iv_n_port; + uint32_t iv_port_power_limit; + uint32_t iv_calc_port_maxpower; + + //default ctor deleted + throttle() = delete; + + /// + /// @brief Constructor + /// @param[in] i_target port target to call power thermal stuff on + /// @param[out] o_rc fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS iff ctor was successful + /// + throttle( const fapi2::Target& i_port, fapi2::ReturnCode& o_rc); + + // + // @brief Destructor + // + ~throttle() = default; + + /// + /// @brief Calculates the min and max power usage for a port + /// @param[in] i_idle_util the utilization of the databus in idle mode + /// @param[in] i_max_util the utilization of the port at maximum possible (mrw or calculated) + /// @param[out] o_port_power_idle max value of port power in cW + /// @param[out] o_port_power_max max value of port power in cW + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// @note Called twice in p9_mss_bulk_pwr_throttles + /// + fapi2::ReturnCode calc_port_power( const double i_idle_util [TT::DIMMS_PER_PORT], + const double i_max_util [TT::DIMMS_PER_PORT], + double& o_port_power_idle, + double& o_port_power_max) const; + /// + /// @brief Calculates max and min power usages based off of DIMM power curves + /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) + /// @param[in] i_port_power_calc_idle double of the port's power consumption at idle + /// @param[out] o_dimm_power_idle array of dimm power in cW + /// @param[out] o_dimm_power_max array of dimm power in cW + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note used for the thermal throttles + /// + fapi2::ReturnCode calc_dimm_power(const double i_databus_idle, + const double i_databus_max, + double o_dimm_power_idle [TT::DIMMS_PER_PORT], + double o_dimm_power_max [TT::DIMMS_PER_PORT]) const; + + /// + /// @brief Calculate the power curve in order to calculate databus utilization + /// @param[in] i_power_idle double of the port's power consumption at idle + /// @param[in] i_power_max double of the port's power consumption at max utilization + /// @param[out] o_power_slope + /// @param[out] o_power_int + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note Power curve needed to calculate the utilization + /// + fapi2::ReturnCode calc_power_curve(const double i_power_idle, + const double i_power_max, + uint32_t& o_power_slope, + uint32_t& o_power_int) const; + /// + /// @brief Calculate the databus utilization given the power curve + /// @param[in] i_slope the slope of power curve + /// @param[in] i_int the intercept of power curve + /// @param[in] i_power_limit either iv_port_power_limit or thermal_power_limit depending on throttle type + /// @param[out] o_port_util the port's databus utilization + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note Chooses worst case between the maximum allowed databus utilization and the calculated value + /// + void calc_util_usage(const uint32_t i_slope, + const uint32_t i_int, + const uint32_t i_power_limit, + double& o_util) const; + /// + /// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower + /// @param[in] i_util_port pass in the calculated port databus utilization + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK + /// + fapi2::ReturnCode calc_slots_and_power (const double i_util_port); + + /// + /// @brief calculated the output power estimate from the calculated N throttle + /// @param[in] i_n_slot the N throttle per slot + /// @param[in] i_n_port the N throttle per port + /// @param[out] o_power the calculated power + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// + fapi2::ReturnCode calc_power_from_n (const uint16_t i_n_slot, const uint16_t i_n_port, uint32_t& o_power) const; + + /// + /// @brief Converts the port maximum databus util to a dimm level based on powerslopes and dimms installed + /// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) + /// @param[out] o_databus_dimm_max array of dimm utilization values + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @used to calculate the port power based off of DIMM power curves + /// + fapi2::ReturnCode calc_databus( const double i_databus_port_max, + double o_databus_dimm_max [TT::DIMMS_PER_PORT]); + /// + /// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed + /// @param[in] i_util_slot databus utilization for the slot + /// @param[in] i_util_port databus utilization for the port + /// @param[out] o_util_dimm_max array of dimm utilization values + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK + /// @note determines worst case utilization per dimms, takes into account port and combine slot throttles + /// + fapi2::ReturnCode calc_split_util( + const double i_util_slot, + const double i_util_port, + double o_util_dimm_max [TT::DIMMS_PER_PORT]) const; + + + /// + /// @brief Calculate ATTR_MSS_CHANNEL_PAIR_MAXPOWER and ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note determines the throttle levels based off of the port's power curve, max databus utilization, + /// and memwat target. + /// @note currently sets the slot and port throttles to the same value + /// + fapi2::ReturnCode power_regulator_throttles (); + + /// + /// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT, + /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK + /// @note Called in p9_mss_bulk_pwr_throttles + /// @note Sets the throttle levels based off of the dimm's thermal limits + /// @note both DIMM's on a port are set to the same throttle level + /// + fapi2::ReturnCode thermal_throttles (); +}; + + +/// +/// @brief Constructor +/// @tparam MC mss::mc_type +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_target MCS target to call power thermal stuff on +/// @param[out] o_rc, a return code which determines the success of the constructor +/// +template +throttle::throttle( const fapi2::Target& i_port, fapi2::ReturnCode& o_rc) : + iv_target(i_port), + iv_databus_port_max(0), + iv_runtime_n_slot(0), + iv_runtime_n_port(0), + iv_n_slot(0), + iv_n_port(0), + iv_port_power_limit(0), + iv_calc_port_maxpower(0) +{ + FAPI_TRY( mss::attr::get_mrw_max_dram_databus_util(iv_databus_port_max), "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_mrw_dimm_power_curve_percent_uplift(iv_power_uplift), "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_mrw_dimm_power_curve_percent_uplift_idle(iv_power_uplift_idle), "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_dimm_thermal_limit( iv_target, iv_dimm_thermal_limit), "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_total_pwr_intercept( iv_target, iv_pwr_int), "%s Error in throttle ctor", mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_total_pwr_slope( iv_target, iv_pwr_slope), "%s Error in throttle ctor", mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_runtime_mem_throttled_n_commands_per_slot(iv_target, iv_runtime_n_slot ), + "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_runtime_mem_throttled_n_commands_per_port(iv_target, iv_runtime_n_port ), + "%s Error in throttle ctor", + mss::c_str(i_port) ); + FAPI_TRY( mss::attr::get_mrw_mem_m_dram_clocks(iv_m_clocks), "%s Error in throttle ctor", mss::c_str(i_port) ); + + //Port power limit = sum of dimm power limits + for ( const auto& l_dimm : mss::find_targets(iv_target) ) + { + uint32_t l_dimm_limit = 0; + FAPI_TRY( mss::attr::get_mem_watt_target( l_dimm, l_dimm_limit) ); + iv_port_power_limit += l_dimm_limit; + } + + FAPI_INF("Setting up throttle for target %s, Values are: max databus is %d, uplifts are %d %d, runtime throttles are %d %d for %s", + mss::c_str(iv_target), + iv_databus_port_max, + iv_power_uplift, + iv_power_uplift_idle, + iv_runtime_n_slot, + iv_runtime_n_port, + mss::c_str(iv_target)); + + FAPI_INF("The dimm power limit is %d, dram clocks are %d, dimm power curve slopes are %d %d for %s", + iv_port_power_limit, + iv_m_clocks, + iv_pwr_slope[0], + iv_pwr_slope[1], + mss::c_str(iv_target)); + + FAPI_INF("DIMM power curve intercepts are %d %d, DIMM power thermal limits are %d %d for %s", + iv_pwr_int[0], + iv_pwr_int[1], + iv_dimm_thermal_limit[0], + iv_dimm_thermal_limit[1], + mss::c_str(iv_target)); + + FAPI_ASSERT( (iv_databus_port_max != 0), + fapi2::MSS_NO_DATABUS_UTILIZATION() + .set_PORT_DATABUS_UTIL(iv_databus_port_max) + .set_DIMM_COUNT(mss::count_dimm(iv_target)), + "Failed to get max databus utilization for target %s", + mss::c_str(iv_target)); + + FAPI_ASSERT( (iv_port_power_limit != 0), + fapi2::MSS_NO_PORT_POWER_LIMIT() + .set_COUNT_DIMMS( mss::count_dimm(iv_target)) + .set_PORT_POWER_LIMIT( iv_port_power_limit), + "Error calculating port_power_limit on target %s with %d DIMMs installed", + mss::c_str(iv_target), + iv_port_power_limit); + + //Checking to make sure all of the attributes are valid + for ( const auto& l_dimm : mss::find_targets(iv_target) ) + { + const auto l_pos = mss::index(l_dimm); + FAPI_ASSERT( (iv_pwr_int[l_pos] != 0), + fapi2::MSS_POWER_INTERCEPT_NOT_SET(), + "The attribute ATTR_MSS_TOTAL_PWR_INTERCEPT equals 0 for %s", + mss::c_str(l_dimm)); + + FAPI_ASSERT( (iv_pwr_slope[l_pos] != 0), + fapi2::MSS_POWER_SLOPE_NOT_SET(), + "The attribute ATTR_MSS_TOTAL_PWR_SLOPE equals 0 for %s", + mss::c_str(l_dimm)); + } + +fapi_try_exit: + o_rc = fapi2::current_err; + return; +} + +/// +/// @brief Set ATTR_MSS_CHANNEL_PAIR_MAXPOWER, ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and _PER_PORT +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note determines the throttle levels based off of the port's power curve, +/// @note the _per_slot throttles are set to the _per_port values +/// @note throttles are all equalized and set to the worst case value +/// +template +fapi2::ReturnCode throttle::power_regulator_throttles () +{ + double l_port_power_calc_idle = 0; + double l_port_power_calc_max = 0; + uint32_t l_port_power_slope = 0; + uint32_t l_port_power_int = 0; + double l_calc_util_port = 0; + double l_databus_dimm_max[TT::DIMMS_PER_PORT] = {}; + double l_calc_databus_port_idle[TT::DIMMS_PER_PORT] = {TT::IDLE_UTIL, TT::IDLE_UTIL}; + + FAPI_INF("Starting power regulator throttles for %s", mss::c_str(iv_target)); + + //Decide utilization for each dimm based off of dimm count and power slopes + FAPI_TRY( calc_databus(iv_databus_port_max, l_databus_dimm_max), + "Failed to calculate each DIMMs' percentage of dram databus utilization for target %s, max port databus is %d", + mss::c_str(iv_target), + iv_databus_port_max); + + //Use the dimm utilizations and dimm power slopes to calculate port min and max power + FAPI_TRY( calc_port_power(l_calc_databus_port_idle, + l_databus_dimm_max, + l_port_power_calc_idle, + l_port_power_calc_max), + "Failed to calculate the max and idle power for port %s", + mss::c_str(iv_target)); + + FAPI_INF("POWER throttles: %s max port power is %f", mss::c_str(iv_target), l_port_power_calc_max); + + //Calculate the power curve slope and intercept using the port's min and max power values + FAPI_TRY(calc_power_curve(l_port_power_calc_idle, + l_port_power_calc_max, + l_port_power_slope, + l_port_power_int), + "Failed to calculate the power curve for port %s, calculated port power max is %d, idle is %d", + mss::c_str(iv_target), + l_port_power_calc_max, + l_port_power_calc_idle); + + FAPI_INF("%s POWER Port power limit is %d", mss::c_str(iv_target), iv_port_power_limit); + //Calculate the port's utilization to get under watt target using the port's calculated slopes + calc_util_usage(l_port_power_slope, + l_port_power_int, + iv_port_power_limit, + l_calc_util_port); + + FAPI_INF("%s POWER calc util port is %f", mss::c_str(iv_target), l_calc_util_port); + + //Calculate the new slot values and the max power value for the port + FAPI_TRY( calc_slots_and_power( l_calc_util_port), + "%s Error calculating the final throttles and power values for target with passed in port utilization %d", + mss::c_str(iv_target), + l_calc_util_port); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief set iv_n_port, iv_n_slot, iv_calc_port_maxpower +/// @param[in] i_util_port pass in the calculated port databus utilization +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// +template +fapi2::ReturnCode throttle::calc_slots_and_power (const double i_util_port) +{ + //Calculate the Port N throttles + iv_n_port = power_thermal::throttled_cmds(i_util_port, iv_m_clocks); + + //Set iv_n_slot to the lower value between the slot runtime and iv_n_port + iv_n_slot = (iv_runtime_n_slot != 0) ? std::min (iv_n_port, iv_runtime_n_slot) : iv_n_port; + + //Choose the lowest value of the runtime and the calculated + iv_n_port = (iv_runtime_n_port != 0) ? std::min (iv_n_port, iv_runtime_n_port) : iv_n_port; + + //Use the throttle value to calculate the power that gets to exactly that value + FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Set ATTR_MSS_MEM_THROTTLED_N_COMMANDS_PER_SLOT and PER_PORT +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note Sets the throttle levels based off of the dimm's thermal limits +/// @note both DIMM's on a port are set to the same throttle level +/// +template +fapi2::ReturnCode throttle::thermal_throttles () +{ + double l_dimm_power_idle [TT::DIMMS_PER_PORT] = {}; + double l_dimm_power_max [TT::DIMMS_PER_PORT] = {}; + uint32_t l_dimm_power_slope [TT::DIMMS_PER_PORT] = {}; + uint32_t l_dimm_power_int [TT::DIMMS_PER_PORT] = {}; + double l_calc_util [TT::DIMMS_PER_PORT] = {}; + const auto l_count = count_dimm (iv_target); + + //Calculate the dimm power range for each dimm at max utilization for each + FAPI_TRY( calc_dimm_power(TT::IDLE_UTIL, + iv_databus_port_max, + l_dimm_power_idle, + l_dimm_power_max)); + + //Let's calculate the N throttle for each DIMM + for ( const auto& l_dimm : mss::find_targets(iv_target) ) + { + uint16_t l_temp_n_slot = 0; + const uint8_t l_pos = mss::index(l_dimm); + //Calculate the power curve taking the thermal limit into account + FAPI_TRY( calc_power_curve(l_dimm_power_idle[l_pos], + l_dimm_power_max[l_pos], + l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos]), + "Failed to calculate the power curve for dimm %s, calculated dimm power curve slope is %d, intercept %d", + mss::c_str(l_dimm), + l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos]); + + //Calculate the databus utilization at the calculated power curve + calc_util_usage(l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos], + iv_dimm_thermal_limit[l_pos], + l_calc_util[l_pos]); + + FAPI_INF("THERMAL throttles: %s dram databus utilization is %f for %s", mss::c_str(l_dimm), l_calc_util[l_pos], + mss::c_str(l_dimm)); + + l_temp_n_slot = power_thermal::throttled_cmds (l_calc_util[l_pos], iv_m_clocks); + + //Set to the min between the two value + //If iv_n_slot == 0 (so uninitialized), set it to the calculated slot value + //The l_n_slot value can't be equal to 0 because there's a dimm installed + if ((l_temp_n_slot < iv_n_slot) || (iv_n_slot == 0)) + { + iv_n_slot = l_temp_n_slot; + } + } + + //Set to lowest value between calculated and runtime + FAPI_INF("THERMAL throttles: runtime slot is %d, calc n slot is %d for %s", iv_runtime_n_slot, iv_n_slot, + mss::c_str(iv_target)); + //Taking the min of the SLOT * (# of dimms on the port) and the iv_runtime_port throttle value + //Thermal throttling happens after the POWER calculations. the iv_runtime_n_port value shouldn't be set to 0 + iv_n_port = std::min(iv_runtime_n_port, static_cast(iv_n_slot * l_count)); + iv_n_port = (iv_n_port == 0) ? TT::MIN_THROTTLE : iv_n_port; + + iv_n_slot = std::min(iv_n_slot, iv_runtime_n_slot); + iv_n_slot = (iv_n_slot == 0) ? TT::MIN_THROTTLE : iv_n_slot; + + //Now time to get and set iv_calc_port_max from the calculated N throttle + FAPI_TRY( calc_power_from_n(iv_n_slot, iv_n_port, iv_calc_port_maxpower), + "Failed to calculate the final max port maxpower. Slot throttle value is %d, port value is %d for %s", + iv_n_slot, + iv_n_port, + mss::c_str(iv_target)); + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + FAPI_ERR("Error calculating mss::power_thermal::thermal_throttles() for %s", mss::c_str(iv_target)); + return fapi2::current_err; +} + +/// +/// @brief Calculates the min and max power usage for a port based off of power curves and utilizations +/// @param[in] i_idle_util the utilization of the databus in idle mode (0% most likely) +/// @param[in] i_max_util the utilization of the dimm at maximum possible percentage (mrw or calculated) +/// @param[out] o_port_power_idle max value of port power in cW +/// @param[out] o_port_power_max max value of port power in cW +/// @return fapi2::FAPI2_RC_SUCCESS iff the method was a success +/// @note Called twice in p9_mss_bulk_pwr_throttles +/// @note uses dimm power curves from class variables +/// +template +fapi2::ReturnCode throttle::calc_port_power(const double i_idle_util [TT::DIMMS_PER_PORT], + const double i_max_util [TT::DIMMS_PER_PORT], + double& o_port_power_idle, + double& o_port_power_max) const +{ + //Playing it safe + o_port_power_idle = 0; + o_port_power_max = 0; + fapi2::ReturnCode l_rc; + + //Calculate the port power curve info by summing the dimms on the port + for ( const auto& l_dimm : mss::find_targets(iv_target) ) + { + const auto l_pos = mss::index(l_dimm); + //Printing as decimals because HB messes up floats + FAPI_INF("%s max dram databus for DIMM in pos %d is %d, databus for idle is %d", + mss::c_str(iv_target), + l_pos, + static_cast( i_max_util[l_pos]), + static_cast( i_idle_util[l_pos]) ); + //Sum up the dimm's power to calculate the port power curve + o_port_power_idle += calc_power(i_idle_util[l_pos], l_pos, l_rc); + FAPI_TRY(l_rc, "calc_power failed"); + o_port_power_max += calc_power(i_max_util[l_pos], l_pos, l_rc); + FAPI_TRY(l_rc, "calc_power failed"); + } + + //Raise the powers by the uplift percent + calc_power_uplift(iv_power_uplift_idle, o_port_power_idle); + calc_power_uplift(iv_power_uplift, o_port_power_max); + + FAPI_ASSERT( (o_port_power_max > 0), + fapi2::MSS_NO_PORT_POWER() + .set_COUNT_DIMMS(mss::count_dimm(iv_target)) + .set_MAX_UTILIZATION_DIMM_0(i_max_util[0]) + .set_MAX_UTILIZATION_DIMM_1(i_max_util[1]), + "No Port Power limit was calculated for %s, %d DIMMs installed, utilizations: DIMM 0 %d, DIMM 1 %d", + mss::c_str(iv_target), + mss::count_dimm(iv_target), + i_max_util[0], + i_max_util[1]); + + //FAPI_ASSERTs don't set the current err to good + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Calculates max and min power usages based off of DIMM power curves +/// @param[in] i_databus_idle idle databus utilization (either calculated or mrw) +/// @param[in] i_databus_max max databus utilization (either calculated or mrw) +/// @param[out] o_dimm_power_idle array of dimm power in cW +/// @param[out] o_dimm_power_max array of dimm power in cW +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note used for the thermal throttles +/// +template +fapi2::ReturnCode throttle::calc_dimm_power(const double i_databus_idle, + const double i_databus_max, + double o_dimm_power_idle [TT::DIMMS_PER_PORT], + double o_dimm_power_max [TT::DIMMS_PER_PORT]) const +{ + for ( const auto& l_dimm : mss::find_targets(iv_target) ) + { + fapi2::ReturnCode l_rc; + const uint8_t l_pos = mss::index(l_dimm); + o_dimm_power_idle[l_pos] = calc_power(i_databus_idle, l_pos, l_rc); + FAPI_TRY(l_rc, "calc_power failed"); + o_dimm_power_max[l_pos] = calc_power(i_databus_max, l_pos, l_rc); + FAPI_TRY(l_rc, "calc_power failed"); + + //Raise the powers by the uplift percent + calc_power_uplift(iv_power_uplift_idle, o_dimm_power_idle[l_pos]); + calc_power_uplift(iv_power_uplift, o_dimm_power_max[l_pos]); + + FAPI_INF("Calc_dimm_power: dimm (%d) power max is %f, %f for dimm slope of %d, intercept of %d for %s", + l_pos, + o_dimm_power_max[l_pos], + o_dimm_power_max[l_pos], + iv_pwr_slope[l_pos], + iv_pwr_int[l_pos], + mss::c_str(l_dimm)); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + FAPI_INF("Error calculating mss::power_thermal::calc_dimm_power for %s", mss::c_str(iv_target)); + return fapi2::current_err; +} + +/// +/// @brief Calculate the port power curve in order to calculate the port utilization +/// @param[in] i_power_idle double of the port's power consumption at idle +/// @param[in] i_power_max double of the port's power consumption at max utilization +/// @param[out] o_slope +/// @param[out] o_int +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note Port power curve needed to calculate the port utilization +/// +template +fapi2::ReturnCode throttle::calc_power_curve(const double i_power_idle, + const double i_power_max, + uint32_t& o_slope, + uint32_t& o_int) const +{ + auto l_min_util = TT::MIN_UTIL; + const double l_divisor = ((static_cast(iv_databus_port_max) / UTIL_CONVERSION) - TT::IDLE_UTIL); + FAPI_ASSERT ((l_divisor > 0), + fapi2::MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO() + .set_PORT_DATABUS_UTIL(iv_databus_port_max) + .set_UTIL_CONVERSION(UTIL_CONVERSION) + .set_IDLE_UTIL(l_min_util) + .set_RESULT(l_divisor), + "Calculated zero for the divisor in calc_power_curve on target %s", + mss::c_str(iv_target) ); + + o_slope = (i_power_max - i_power_idle) / l_divisor; + o_int = i_power_idle - (o_slope * TT::IDLE_UTIL); + FAPI_INF("Calc_power_curve: power idle is %f, max is %f, slope is %d, int is %d for %s", + i_power_idle, + i_power_max, + o_slope, + o_int, + mss::c_str(iv_target)); + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + FAPI_INF("Error calculating mss::power_thermal::calc_power_curve for %s", mss::c_str(iv_target)); + return fapi2::current_err; + +} + +/// +/// @brief Calculate the databus utilization given the power curve +/// @param[in] i_slope +/// @param[in] i_int +/// @param[in] i_power_limit either the port_power_limit or the dimm thermal power limit +/// @param[out] o_port_util the port's databus utilization +/// @note Called in p9_mss_bulk_pwr_throttles +/// @note Chooses worst case between the maximum allowed databus utilization and the calculated value +/// +template +void throttle::calc_util_usage(const uint32_t i_slope, + const uint32_t i_int, + const uint32_t i_power_limit, + double& o_util) const +{ + o_util = ((static_cast(i_power_limit) - i_int) / i_slope ) * UTIL_CONVERSION; + + //Cast to uint32 for edge case where it has decimals + o_util = (static_cast(o_util) < iv_databus_port_max) ? static_cast(o_util) : iv_databus_port_max; + + // Check for the minimum threshnold and update if need be + if(o_util < TT::MIN_UTIL) + { + FAPI_INF("Calculated utilization (%lu) is less than the minimum utilization: %lu. Setting to minimum value for %s", + o_util, + TT::MIN_UTIL, mss::c_str(iv_target)); + o_util = TT::MIN_UTIL; + } +} + +/// +/// @brief calculated the output power estimate from the calculated N throttle +/// @param[in] i_n_slot the throttle per slot in terms of N commands +/// @param[in] i_n_port the throttle per port in terms of N commands +/// @param[out] o_power the calculated power +/// @return fapi2::ReturnCode iff it was a success +/// +template +fapi2::ReturnCode throttle::calc_power_from_n (const uint16_t i_n_slot, + const uint16_t i_n_port, + uint32_t& o_power) const +{ + double l_calc_util_port = 0; + double l_calc_util_slot = 0; + double l_calc_databus_port_max[TT::DIMMS_PER_PORT] = {}; + double l_calc_databus_port_idle[TT::DIMMS_PER_PORT] = {}; + double l_port_power_max = 0; + double l_port_power_idle = 0; + + FAPI_TRY( calc_util_from_throttles(i_n_slot, iv_m_clocks, l_calc_util_slot), + "%s Error calculating utilization from slot throttle %d and mem clocks %d", + mss::c_str(iv_target), + i_n_slot, + iv_m_clocks); + FAPI_TRY( calc_util_from_throttles(i_n_port, iv_m_clocks, l_calc_util_port), + "%s Error calculating utilization from port throttle %d and mem clocks %d", + mss::c_str(iv_target), + i_n_port, + iv_m_clocks); + + //Determine the utilization for each DIMM that will maximize the port power + FAPI_TRY( calc_split_util(l_calc_util_slot, l_calc_util_port, l_calc_databus_port_max), + "Error splitting the utilization for target %s with slot utilizatio %d and port util %d", + mss::c_str(iv_target), + l_calc_util_slot, + l_calc_util_port); + + FAPI_TRY( calc_port_power(l_calc_databus_port_idle, + l_calc_databus_port_max, + l_port_power_idle, + l_port_power_max), + "Error calculating the port power value for %s. Slot value is %d, port value is %d", + mss::c_str(iv_target), + i_n_slot, + i_n_port); + + o_power = mss::round_up (l_port_power_max); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Converts the port maximum databus to a dimm level based on powerslopes and dimms installed +/// @param[in] i_databus_port_max max databus utilization for the port (either calculated or mrw) +/// @param[out] o_databus_dimm_max array of dimm utilization values +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK +/// @note Called in p9_mss_bulk_pwr_throttles +/// @used to calculate the port power based off of DIMM power curves +/// +template +fapi2::ReturnCode throttle::calc_databus (const double i_databus_port_max, + double o_databus_dimm_max [TT::DIMMS_PER_PORT]) +{ + const uint8_t l_count_dimms = count_dimm(iv_target); + + //No work for no dimms + if (l_count_dimms == 0) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + for (const auto& l_dimm : mss::find_targets(iv_target)) + { + //Left early if count_dimms == 0 + o_databus_dimm_max[mss::index(l_dimm)] = i_databus_port_max / l_count_dimms; + } + + //If the power slopes aren't equal, set the dimm with the highest power slope + //Should be correct even if only one DIMM is installed + if (iv_pwr_slope[0] != iv_pwr_slope[1]) + { + o_databus_dimm_max[0] = (iv_pwr_slope[0] > iv_pwr_slope[1]) ? i_databus_port_max : 0; + o_databus_dimm_max[1] = (iv_pwr_slope[1] > iv_pwr_slope[0]) ? i_databus_port_max : 0; + } + + //Make sure both are not 0 + FAPI_ASSERT ( (o_databus_dimm_max[0] != 0) || (o_databus_dimm_max[1] != 0), + fapi2::MSS_NO_DATABUS_UTILIZATION() + .set_PORT_DATABUS_UTIL(i_databus_port_max) + .set_DIMM_COUNT(l_count_dimms), + "Failed to calculated databus utilization for target %s", + mss::c_str(iv_target)); + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Converts the port and slot util to a dimm level based on powerslopes and number of dimms installed +/// @param[in] i_util_slot databus utilization for the slot +/// @param[in] i_util_port databus utilization for the port +/// @param[out] o_util_dimm_max array of dimm utilization values +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff the split is OK +/// @note determines worst case utilization per dimms, takes into account port and combine slot throttles +/// @note used in calculating the port power, not for calculating the slot and port utilization +/// +template +fapi2::ReturnCode throttle::calc_split_util( + const double i_util_slot, + const double i_util_port, + double o_util_dimm_max [TT::DIMMS_PER_PORT]) const +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + const uint8_t l_count_dimms = count_dimm (iv_target); + //The total utilization to be used is limited by either what the port can allow or what the dimms can use + FAPI_ASSERT( (i_util_slot <= i_util_port), + fapi2::MSS_SLOT_UTIL_EXCEEDS_PORT() + .set_SLOT_UTIL(i_util_slot) + .set_PORT_UTIL(i_util_port), + "The slot utilization (%f) exceeds the port's utilization (%f) for %s", + i_util_slot, + i_util_port, + mss::c_str(iv_target)); + + if (l_count_dimms == 0) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + //assumptions slot <= port, l_count_dimms <=2 + if (i_util_slot * l_count_dimms > i_util_port) + { + FAPI_INF("In mss::power_thermal::calc_split i_util_slot is %f, i_util_port is %f, l_count_dimms is %d for %s", + i_util_slot, + i_util_port, + l_count_dimms, + mss::c_str(iv_target)); + const uint8_t l_high_pos = (iv_pwr_slope[0] >= iv_pwr_slope[1]) ? 0 : 1; + + //Highest power_slope gets the higher utilization + o_util_dimm_max[l_high_pos] = std::min(i_util_slot, i_util_port); + //Set the other dimm to the left over utilization (i_util_port - i_util_slot) + o_util_dimm_max[(!l_high_pos)] = (l_count_dimms == TT::DIMMS_PER_PORT) ? (i_util_port - o_util_dimm_max[l_high_pos]) : + 0; + + FAPI_INF("Split utilization for target %s, DIMM in %d gets %f, DIMM in %d gets %f for %s", + mss::c_str(iv_target), + l_high_pos, + o_util_dimm_max[l_high_pos], + !l_high_pos, + o_util_dimm_max[!l_high_pos], + mss::c_str(iv_target)); + } + else + { + //If only 1 dimm, i_util_port == i_util_slot + //If 2 dimms, 2*i_util_slot <= i_util_pot + //Either way, limit utilization by the slot value + for (const auto& l_dimm : mss::find_targets(iv_target)) + { + const size_t l_pos = mss::index(l_dimm); + o_util_dimm_max[l_pos] = i_util_slot; + } + } + + //make sure both are not 0 + FAPI_ASSERT ( (o_util_dimm_max[0] != 0) || (o_util_dimm_max[1] != 0), + fapi2::MSS_NO_DATABUS_UTILIZATION() + .set_PORT_DATABUS_UTIL(i_util_port) + .set_DIMM_COUNT(mss::count_dimm(iv_target)), + "Failed to calculated util utilization for target %s", + mss::c_str(iv_target)); +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Equalize the throttles and estimated power at those throttle levels +/// @tparam MC mss::mc_type +/// @tparam T the fapi2 MC target type of the target +/// @tparam TT throttle_traits throttle traits for the given mc_type +/// @param[in] i_targets vector of MCS targets all on the same VDDR domain +/// @param[in] i_throttle_type denotes if this was done for POWER (VMEM) or THERMAL (VMEM+VPP) throttles +/// @param[out] o_exceeded_power vector of MCA targets where the estimated power exceeded the maximum allowed +/// @return FAPI2_RC_SUCCESS iff ok +/// @note sets the throttles and power to the worst case +/// Called by p9_mss_bulk_pwr_throttles and by p9_mss_utils_to_throttle (so by IPL or by OCC) +/// +template> +fapi2::ReturnCode equalize_throttles (const std::vector< fapi2::Target >& i_targets, + const throttle_type i_throttle_type, + std::vector< fapi2::Target >& o_exceeded_power) + +{ + o_exceeded_power.clear(); + + //Set to max values so every compare will change to min value + uint16_t l_min_slot = ~(0); + uint16_t l_min_port = ~(0); + + //Loop through all of the MC targets to find the worst case throttle value (lowest) for the slot and port + for (const auto& l_mc : i_targets) + { + for (const auto& l_port : mss::find_targets(l_mc)) + { + uint16_t l_calc_slot = 0; + uint16_t l_calc_port = 0; + uint16_t l_run_slot = 0; + uint16_t l_run_port = 0; + + if (mss::count_dimm(l_port) == 0) + { + continue; + } + + FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_slot(l_port, l_calc_slot)); + FAPI_TRY(mss::attr::get_mem_throttled_n_commands_per_port(l_port, l_calc_port)); + FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_slot(l_port, l_run_slot)); + FAPI_TRY(mss::attr::get_runtime_mem_throttled_n_commands_per_port(l_port, l_run_port)); + + //Find the smaller of the three values (calculated slot, runtime slot, and min slot) + l_min_slot = (l_calc_slot != 0) ? std::min( std::min (l_calc_slot, l_run_slot), + l_min_slot) : l_min_slot; + l_min_port = (l_calc_port != 0) ? std::min( std::min( l_calc_port, l_run_port), + l_min_port) : l_min_port; + } + } + + FAPI_INF("Calculated min slot is %d, min port is %d for the system", l_min_slot, l_min_port); + + //Now set every port to have those values + { + for (const auto& l_mc : i_targets) + { + for (const auto& l_port : mss::find_targets(l_mc)) + { + uint16_t l_fin_slot = 0; + uint16_t l_fin_port = 0; + uint32_t l_fin_power = 0;; + + if (mss::count_dimm(l_port) == 0) + { + continue; + } + + // Declaring above to avoid fapi2 jump + uint64_t l_power_limit = 0; + + l_fin_slot = l_min_slot; + l_fin_port = l_min_port; + + //Need to create throttle object for each mca in order to get dimm configuration and power curves + //To calculate the slot/port utilization and total port power consumption + fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; + + const auto l_dummy = mss::power_thermal::throttle(l_port, l_rc); + FAPI_TRY(l_rc, "Failed creating a throttle object in equalize_throttles for %s", mss::c_str(l_port)); + + FAPI_TRY( l_dummy.calc_power_from_n(l_fin_slot, l_fin_port, l_fin_power), + "Failed calculating the power value for throttles: slot %d, port %d for target %s", + l_fin_slot, + l_fin_port, + mss::c_str(l_port)); + + // You may ask why this is not a variable within the throttle struct + // It's because POWER throttling is on a per port basis while the THERMAL throttle is per dimm + // Didn't feel like adding a variable just for this check + l_power_limit = (i_throttle_type == throttle_type::POWER) ? + l_dummy.iv_port_power_limit : (l_dummy.iv_dimm_thermal_limit[0] + l_dummy.iv_dimm_thermal_limit[1]); + + FAPI_INF("%s Calculated power is %d, limit is %ld", mss::c_str(l_port), l_fin_power, l_power_limit); + + //If there's an error with calculating port power, the wrong watt target was passed in + //Returns an error but doesn't deconfigure anything. Calling function can log if it wants to + //Called by OCC and by p9_mss_eff_config_thermal, thus different ways for error handling + //Continue setting throttles to prevent a possible throttle == 0 + //The error will be the last bad port found + if (l_fin_power > l_power_limit) + { + //Need this because of pos traits and templating stuff + uint64_t l_fail = mss::fapi_pos(l_port); + //Set the failing port. OCC just needs one failing port, doesn't need all of them + FAPI_TRY( FAPI_ATTR_SET( fapi2::ATTR_MSS_MEM_PORT_POS_OF_FAIL_THROTTLE, + fapi2::Target(), + l_fail) ); + + FAPI_ASSERT_NOEXIT( false, + fapi2::MSS_CALC_PORT_POWER_EXCEEDS_MAX() + .set_CALCULATED_PORT_POWER(l_fin_power) + .set_MAX_POWER_ALLOWED(l_power_limit) + .set_PORT_POS(mss::pos(l_port)) + .set_PORT_TARGET(l_port), + "Error calculating the final port power value for target %s, calculated power is %d, max value can be %d", + mss::c_str(l_port), + l_fin_power, + l_power_limit); + + o_exceeded_power.push_back(l_port); + } + + FAPI_INF("%s Final throttles values for slot %d, for port %d, power value %d", + mss::c_str(l_port), + l_fin_port, + l_fin_slot, + l_fin_power); + + //Even if there's an error, still calculate and set the throttles. + //OCC will set to safemode if there's an error + //Better to set the throttles than leave them 0, and potentially brick the memory + FAPI_TRY( mss::attr::set_mem_throttled_n_commands_per_port( l_port, l_fin_port) ); + FAPI_TRY( mss::attr::set_mem_throttled_n_commands_per_slot( l_port, l_fin_slot) ); + FAPI_TRY( mss::attr::set_port_maxpower( l_port, l_fin_power) ); + } + } + } + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + FAPI_ERR("Error equalizing memory throttles"); + return fapi2::current_err; +} + +} //ns power_thermal +}// mss + +#endif diff --git a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H index 17758adf1..d6d30fa66 100644 --- a/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H +++ b/src/import/generic/memory/lib/utils/power_thermal/gen_throttle_traits.H @@ -22,3 +22,36 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_throttle_traits.H +/// @brief Contains throttle traits information +/// +// *HWP HWP Owner: Stephen Glancy +// *HWP FW Owner: Andre Marin +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: CI + +#ifndef _GEN_THROTTLE_TRAITS_H_ +#define _GEN_THROTTLE_TRAITS_H_ + +#include +#include + +namespace mss +{ + +namespace power_thermal +{ + +/// +/// @class Traits for throttle +/// @tparam MC mss::mc_type memory controller type +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE > +class throttle_traits; + +} //ns power_thermal +} // ns mss +#endif diff --git a/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H b/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H index 2dee8fa53..275cb9e82 100644 --- a/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H +++ b/src/import/generic/memory/lib/utils/shared/mss_generic_consts.H @@ -104,6 +104,9 @@ enum conversions NIBBLES_PER_BYTE = 2, BITS_PER_NIBBLE = 4, BITS_PER_BYTE = 8, + + // Used by exp_decoder.C for dA to cA + DECI_TO_CENTI = 10, }; enum generic_sizes @@ -200,6 +203,11 @@ enum generic_ffdc_codes SET_PRIM_BUS_WIDTH = 0x1069, SET_PRIM_DIE_COUNT = 0x1070, SET_DRAM_DENSITY = 0x1071, + + // Power thermal functions + POWER_LIMIT = 0x1072, + SLOPE = 1073, + INTERCEPT = 1074, }; /// diff --git a/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml b/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml index 1b2e69ea4..60cf77d35 100644 --- a/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml +++ b/src/import/generic/procedures/xml/attribute_info/generic_memory_eff_attributes.xml @@ -620,6 +620,22 @@ dram_mfg_id + + ATTR_MEM_EFF_DRAM_MODULE_HEIGHT + TARGET_TYPE_MEM_PORT + + ARRAY[DIMM] + DRAM Modlue Height + Decodes SPD Byte 193 + + 1U = 0, 2U = 1, 4U = 2 + + uint8 + + 2 + dram_module_height + + ATTR_MEM_EFF_RCD_MFG_ID TARGET_TYPE_MEM_PORT diff --git a/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml b/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml index 31ac9b4c0..0230aa2e4 100644 --- a/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml +++ b/src/import/generic/procedures/xml/attribute_info/generic_memory_mrw_attributes.xml @@ -606,4 +606,136 @@ mrw_supported_dram_width + + ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT + TARGET_TYPE_SYSTEM + + Machine Readable Workbook Thermal Memory Power Limit + Used to calculate throttles to be at or under the power limit + Per DIMM basis + KEY (0-21): In order + DIMM_SIZE = bits 0-3, + DIMM_GEN = 4-5, + DIMM_TYPE = 6-8, + DIMM_WIDTH = 9-11, + DIMM_DENSITY = 12-14, + DIMM_STACK_TYPE = 15-16, + DRAM_MFGID = 17-19, + DIMM_HEIGHT = 20-21, + Bits 22-32: Not used + VALUE (bits 32-47) in cW: + OCMB+DRAM thermal power limit per DIMM = 32-47 + + uint64 + cW + 0xfffffc07940000 + 25 + + mrw_ocmb_thermal_memory_power_limit + + + + ATTR_MSS_MRW_OCMB_PWR_SLOPE + TARGET_TYPE_SYSTEM + + Machine Readable Workbook Power Curve Slope for DIMM + Used to get the OCMB+DRAM power curve for each DIMM + Per DIMM basis + KEY (0-21): In order + DIMM_SIZE = bits 0-3, + DIMM_GEN = 4-5, + DIMM_TYPE = 6-8, + DIMM_WIDTH = 9-11, + DIMM_DENSITY = 12-14, + DIMM_STACK_TYPE = 15-16, + DRAM_MFGID = 17-19, + DIMM_HEIGHT = 20-21, + Bits 22-32: Not used + VALUE (bits 32-47) in cW/utilization: + OCMB+DRAM thermal power limit per DIMM = 32-47 + + uint64 + cW + 0xfffffc00044C0000 + 50 + + mrw_ocmb_pwr_slope + + + + ATTR_MSS_MRW_OCMB_PWR_INTERCEPT + TARGET_TYPE_SYSTEM + + Machine Readable Workbook Power Curve Intercept for DIMM + Used to get the OCMB+DRAM power curve for each DIMM + Per DIMM basis + KEY (0-21): In order + DIMM_SIZE = bits 0-3, + DIMM_GEN = 4-5, + DIMM_TYPE = 6-8, + DIMM_WIDTH = 9-11, + DIMM_DENSITY = 12-14, + DIMM_STACK_TYPE = 15-16, + DRAM_MFGID = 17-19, + DIMM_HEIGHT = 20-21, + Bits 22-32: Not used + VALUE (bits 32-47) in cW/utilization: + OCMB+DRAM thermal power limit per DIMM = 32-47 + + uint64 + cW/utilization + 0xfffffc00044C0000 + 50 + + mrw_ocmb_pwr_intercept + + + + ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT + TARGET_TYPE_SYSTEM + + Machine Readable Workbook Power Curve Intercept and limit for DIMM + Used to get the PMIC power curve and limit for each DIMM + Per DIMM basis + KEY (0-21): In order + DIMM_SIZE = bits 0-3, + DIMM_GEN = 4-5, + DIMM_TYPE = 6-8, + DIMM_WIDTH = 9-11, + DIMM_DENSITY = 12-14, + DIMM_STACK_TYPE = 15-16, + DRAM_MFGID = 17-19, + DIMM_HEIGHT = 20-21, + Bits 22-32: Not used + VALUE (bits 32-39): Current limit (dA) + VALUE (bits 40-51): Current slope (cA/utilization) + VALUE (bits 52-63): Current intercept (cA) + + uint64 + dA, cA/utilization, cA + 0xfffffc0000000000 + 25 + + mrw_ocmb_current_curve_with_limit + + + + ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL + TARGET_TYPE_SYSTEM + + Machine Readable Workbook value for safe mode dram data bus utilization in centi percent (c%). + Set to below optimum value/ rate. + On a per port basis + Also used for emergency mode throttle + Used to thermally protect the system in all supported environmental conditions when OCC is not functional + Consumer: thermal_init, initfile + Default to 2500 c%% + + uint32 + 0x000009C4 + + + mrw_safemode_dram_databus_util + + diff --git a/src/import/generic/procedures/xml/error_info/generic_error.xml b/src/import/generic/procedures/xml/error_info/generic_error.xml index cec60fdfb..78d852568 100644 --- a/src/import/generic/procedures/xml/error_info/generic_error.xml +++ b/src/import/generic/procedures/xml/error_info/generic_error.xml @@ -440,4 +440,233 @@ + + RC_MSS_POWER_INTERCEPT_NOT_SET + + The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 + + + CODE + HIGH + + + + + RC_MSS_POWER_SLOPE_NOT_SET + + The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 + + + CODE + HIGH + + + + + RC_MSS_NO_DATABUS_UTILIZATION + + There are 2 DIMMS on the port but both have 0 databus utilization + + PORT_DATABUS_UTIL + DIMM_COUNT + + CODE + HIGH + + + + + RC_MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO + + Denominator equals 0 + + PORT_DATABUS_UTIL + UTIL_CONVERSION + IDLE_UTIL + RESULT + + CODE + HIGH + + + + + RC_MSS_NO_PORT_POWER_LIMIT + + Got 0 when calculating port power limit. + Either no dimms or attribute MEM_WATT_TARGET wasn't set + + COUNT_DIMMS + PORT_POWER_LIMIT + + CODE + HIGH + + + + + RC_MSS_NO_PORT_POWER + + Got 0 when calculating port power limits using the DIMMs databus utilization + + COUNT_DIMMS + MAX_UTILIZATION_DIMM_0 + MAX_UTILIZATION_DIMM_1 + + CODE + HIGH + + + + + RC_MSS_M_DRAM_CLOCKS_EQUALS_ZERO + + ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero + + + CODE + HIGH + + + + + RC_MSS_CALC_PORT_POWER_EXCEEDS_MAX + + The calculated port power from equalizing throttles exceeds the maximum allowed power + + CALCULATED_PORT_POWER + MAX_POWER_ALLOWED + PORT_POS + + CODE + HIGH + + + + PORT_TARGET + TARGET_TYPE_DIMM + + MEDIUM + + + + PORT_TARGET + TARGET_TYPE_DIMM + + + + + + RC_MSS_SLOT_UTIL_EXCEEDS_PORT + + The memory throttle per slot (DIMM) exceeds the allowed throttle for the port + + SLOT_UTIL + PORT_UTIL + + CODE + HIGH + + + + + RC_MSS_OUTPUT_OVERFLOW_CALC_UTIL + + Type of output variable is not large enough for the calculations + + RESULT + + CODE + HIGH + + + + + RC_MSS_POWER_THERMAL_DECODE_ERROR + + There was no match or value found in decoding the power thermal attributes + + DIMM_TARGET + ATTR + + CODE + HIGH + + + + + RC_MSS_MRW_SAFEMODE_UTIL_THROTTLE_NOT_SUPPORTED + + The MRW safemode utilization that is less than the minimum utilization supported. Check ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL. + + MRW_SAFEMODE_UTIL + MIN_UTIL_VALUE + + CODE + HIGH + + + + + RC_MSS_NO_POWER_THERMAL_ATTR_FOUND + + There was no match or value found in decoding the power thermal attributes + + GENERATED_KEY + FUNCTION + DIMM_TARGET + SIZE + DRAM_GEN + DIMM_TYPE + DRAM_WIDTH + DRAM_DENSITY + STACK_TYPE + MFGID + MODULE_HEIGHT + + CODE + HIGH + + + + + RC_MSS_POWER_THERMAL_ENCODE_ERROR + + There was no match or value found in encoding the power thermal attributes + + DIMM_TARGET + ATTR + + CODE + HIGH + + + + + RC_MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT + + The attributes vectors size is incorrect for find_xxx functions + + FUNCTION + INPUT_SIZE + EXPECTED_SIZE + + CODE + HIGH + + + + + RC_MSS_POWER_THERMAL_DIMM_INDEX_OUT_OF_BOUND + + The dimm index is out of bound for the port + + INPUT_SIZE + MAX_SIZE + + CODE + HIGH + + + -- cgit v1.2.1