diff options
author | Alvin Wang <wangat@tw.ibm.com> | 2019-04-16 10:57:56 -0500 |
---|---|---|
committer | Christian R. Geddes <crgeddes@us.ibm.com> | 2019-04-30 11:53:20 -0500 |
commit | 4ce53f71e9fcf0c22fdb83af31b450db975df233 (patch) | |
tree | 0c85bb228692dd78965a54f78795af407ff7209d /src/import/generic | |
parent | 902e166a98fe9cce8dbf6dc69534f45499c0ab1d (diff) | |
download | talos-hostboot-4ce53f71e9fcf0c22fdb83af31b450db975df233.tar.gz talos-hostboot-4ce53f71e9fcf0c22fdb83af31b450db975df233.zip |
Move power_thermal lib to generic
Change-Id: I2851b7fa990d7e8c5a2d726b650b4e2fc11f3fe7
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72525
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Dev-Ready: STEPHEN GLANCY <sglancy@us.ibm.com>
Tested-by: Hostboot CI <hostboot-ci+hostboot@us.ibm.com>
Tested-by: HWSV CI <hwsv-ci+hostboot@us.ibm.com>
Reviewed-by: Louis Stermole <stermole@us.ibm.com>
Reviewed-by: STEPHEN GLANCY <sglancy@us.ibm.com>
Reviewed-by: Jennifer A. Stofer <stofer@us.ibm.com>
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/72845
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Reviewed-by: Christian R. Geddes <crgeddes@us.ibm.com>
Diffstat (limited to 'src/import/generic')
10 files changed, 2517 insertions, 0 deletions
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 @@ -2207,6 +2207,53 @@ fapi_try_exit: } /// +/// @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<fapi2::TARGET_TYPE_DIMM>& i_target, + uint8_t& o_value) +{ + uint8_t l_value[2] = {}; + const auto l_port = i_target.getParent<fapi2::TARGET_TYPE_MEM_PORT>(); + + 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<fapi2::TARGET_TYPE_MEM_PORT>& 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 /// @param[out] uint16_t& reference to store the value 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 <stermole@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_DIMM_H_ +#define _MSS_DIMM_H_ + +#include <fapi2.H> + +#include <generic/memory/lib/mss_generic_attribute_getters.H> +#include <generic/memory/lib/utils/c_str.H> + +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<kind> vector(const std::vector<fapi2::Target<fapi2::TARGET_TYPE_DIMM>>& i_dimm) + { + std::vector<kind> 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<fapi2::TARGET_TYPE_DIMM>& 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<fapi2::TARGET_TYPE_DIMM> 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 <stermole@us.ibm.com> +// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_GEN_POWER_DECODER__ +#define _MSS_GEN_POWER_DECODER__ + +#include <fapi2.H> +#include <generic/memory/lib/utils/count_dimm.H> +#include <generic/memory/lib/utils/power_thermal/gen_throttle_traits.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#include <generic/memory/lib/mss_generic_attribute_getters.H> + +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<uint32_t , uint8_t> > 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<uint8_t , uint8_t> > 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 <std::pair<uint8_t, uint8_t> > 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<uint8_t , uint8_t> > DRAM_DENSITY_MAP = +{ + {4, 0b000}, + {8, 0b001}, + {16, 0b010}, + {32, 0b011}, + {64, 0b100}, + {ANY_DENSITY, 0b111} +}; + +static const std::vector <std::pair<uint8_t, uint8_t> > 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 <std::pair<uint16_t, uint8_t> > 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<uint8_t , uint8_t> > 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<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>> +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<mss::mc_type MC = DEFAULT_MC_TYPE> +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<uint32_t> 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<MC>(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<size_t S, size_t L, typename T, typename OT> +inline fapi2::ReturnCode encode ( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + const T& i_attr, + const std::vector<std::pair<T, OT> >& i_map, + fapi2::buffer<uint32_t>& 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<S, L>(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<size_t S, size_t L, typename T, typename OT> +inline fapi2::ReturnCode decode (const std::vector<std::pair<T, OT> >& i_map, + fapi2::buffer<uint32_t>& 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<S, L>(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<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>> +fapi2::ReturnCode generate_wildcard_mask(const uint32_t i_hash, uint32_t& o_mask) +{ + + fapi2::buffer<uint32_t> l_mask; + fapi2::buffer<uint32_t> 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<TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN> + (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<TT::DRAM_GEN_START, TT::DRAM_GEN_LEN> + (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_START, TT::DIMM_TYPE_LEN> + (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<TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN> + (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<TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN> + (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<TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN> + (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<TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN> + (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<TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN> + (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<TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN> + (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<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>> +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<uint32_t> 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<TT::PORT_TARGET_TYPE>(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<uint64_t>* >& 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<uint64_t>* >& 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<uint64_t>* >& 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<size_t FIELD_START, size_t FIELD_LEN, generic_ffdc_codes FUNCTION, typename OT> + fapi2::ReturnCode get_power_thermal_value(const std::vector<uint64_t>& 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<mss::mc_type MC, typename TT> +fapi2::ReturnCode decoder<MC, TT>::generate_encoding() +{ + //DIMM_SIZE + FAPI_TRY(( encode<TT::DIMM_SIZE_START, TT::DIMM_SIZE_LEN> + (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<TT::DRAM_GEN_START, TT::DRAM_GEN_LEN> + (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<TT::DIMM_TYPE_START, TT::DIMM_TYPE_LEN> + (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<TT::DRAM_WIDTH_START, TT::DRAM_WIDTH_LEN> + (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<TT::DRAM_DENSITY_START, TT::DRAM_DENSITY_LEN> + (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<TT::DRAM_STACK_TYPE_START, TT::DRAM_STACK_TYPE_LEN> + (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<TT::DRAM_MFGID_START, TT::DRAM_MFGID_LEN> + (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<TT::DIMMS_PER_PORT_START, TT::DIMMS_PER_PORT_LEN> + (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<TT::DIMM_MODULE_HEIGHT_START, TT::DIMM_MODULE_HEIGHT_LEN> + (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<mss::mc_type MC, typename TT> +template<size_t FIELD_START, size_t FIELD_LEN, generic_ffdc_codes FUNCTION, typename OT> +fapi2::ReturnCode decoder<MC, TT>::get_power_thermal_value(const std::vector<uint64_t>& 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<uint64_t> l_temp(*l_value_iterator); + l_temp.extractToRight<FIELD_START, FIELD_LEN>(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 <aamarin@us.ibm.com> +// *HWP HWP Backup: Louis Stermole <stermole@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_GEN_POWER_THROTTLE_ +#define _MSS_GEN_POWER_THROTTLE_ + +#include <fapi2.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#include <generic/memory/lib/utils/power_thermal/gen_throttle_traits.H> +#include <generic/memory/lib/utils/count_dimm.H> +#include <generic/memory/lib/mss_generic_system_attribute_getters.H> +#include <generic/memory/lib/mss_generic_attribute_setters.H> +#include <generic/memory/lib/utils/mss_math.H> +#include <generic/memory/lib/utils/pos.H> + +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<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>, 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<uint64_t>((static_cast<double>(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<double>(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<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>> +fapi2::ReturnCode restore_runtime_throttles( const fapi2::Target<T>& 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<TT::PORT_TARGET_TYPE>(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<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>> +fapi2::ReturnCode update_runtime_throttle(const fapi2::Target<T>& i_target) + +{ + + if (mss::count_dimm(i_target) == 0) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(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<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>> +fapi2::ReturnCode update_runtime_throttles(const std::vector< fapi2::Target<T> >& 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<mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = throttle_traits<MC>> +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<typename T> + 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<double>(i_uplift) / PERCENT_CONVERSION)); + } + + public: + const fapi2::Target<TT::PORT_TARGET_TYPE>& 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<TT::PORT_TARGET_TYPE>& 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<mss::mc_type MC, typename TT> +throttle<MC, TT>::throttle( const fapi2::Target<TT::PORT_TARGET_TYPE>& 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<fapi2::TARGET_TYPE_DIMM>(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<fapi2::TARGET_TYPE_DIMM>(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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<fapi2::TARGET_TYPE_DIMM>(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<uint16_t>(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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<fapi2::TARGET_TYPE_DIMM>(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<uint64_t>( i_max_util[l_pos]), + static_cast<uint64_t>( 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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<fapi2::TARGET_TYPE_DIMM>(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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<double>(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<mss::mc_type MC, typename TT> +void throttle<MC, TT>::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<double>(i_power_limit) - i_int) / i_slope ) * UTIL_CONVERSION; + + //Cast to uint32 for edge case where it has decimals + o_util = (static_cast<uint32_t>(o_util) < iv_databus_port_max) ? static_cast<uint32_t>(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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<fapi2::TARGET_TYPE_DIMM>(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<mss::mc_type MC, typename TT> +fapi2::ReturnCode throttle<MC, TT>::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<fapi2::TARGET_TYPE_DIMM>(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<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>> +fapi2::ReturnCode equalize_throttles (const std::vector< fapi2::Target<T> >& i_targets, + const throttle_type i_throttle_type, + std::vector< fapi2::Target<TT::PORT_TARGET_TYPE> >& 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<TT::PORT_TARGET_TYPE>(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<TT::PORT_TARGET_TYPE>(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<MC>(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<fapi2::TARGET_TYPE_SYSTEM>(), + 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 <sglancy@us.ibm.com> +// *HWP FW Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: CI + +#ifndef _GEN_THROTTLE_TRAITS_H_ +#define _GEN_THROTTLE_TRAITS_H_ + +#include <fapi2.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> + +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 @@ -621,6 +621,22 @@ </attribute> <attribute> + <id>ATTR_MEM_EFF_DRAM_MODULE_HEIGHT</id> + <targetType>TARGET_TYPE_MEM_PORT</targetType> + <description> + ARRAY[DIMM] + DRAM Modlue Height + Decodes SPD Byte 193 + </description> + <enum>1U = 0, 2U = 1, 4U = 2 </enum> + <initToZero></initToZero> + <valueType>uint8</valueType> + <writeable/> + <array>2</array> + <mssAccessorName>dram_module_height</mssAccessorName> + </attribute> + + <attribute> <id>ATTR_MEM_EFF_RCD_MFG_ID</id> <targetType>TARGET_TYPE_MEM_PORT</targetType> <description> 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 @@ <mssAccessorName>mrw_supported_dram_width</mssAccessorName> </attribute> + <attribute> + <id>ATTR_MSS_MRW_OCMB_THERMAL_MEMORY_POWER_LIMIT</id> + <targetType>TARGET_TYPE_SYSTEM</targetType> + <description> + 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 + </description> + <valueType>uint64</valueType> + <mssUnits>cW</mssUnits> + <default>0xfffffc07940000</default> + <array>25</array> + <platInit/> + <mssAccessorName>mrw_ocmb_thermal_memory_power_limit</mssAccessorName> + </attribute> + + <attribute> + <id>ATTR_MSS_MRW_OCMB_PWR_SLOPE</id> + <targetType>TARGET_TYPE_SYSTEM</targetType> + <description> + 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 + </description> + <valueType>uint64</valueType> + <mssUnits>cW</mssUnits> + <default>0xfffffc00044C0000</default> + <array>50</array> + <platInit/> + <mssAccessorName>mrw_ocmb_pwr_slope</mssAccessorName> + </attribute> + + <attribute> + <id>ATTR_MSS_MRW_OCMB_PWR_INTERCEPT</id> + <targetType>TARGET_TYPE_SYSTEM</targetType> + <description> + 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 + </description> + <valueType>uint64</valueType> + <mssUnits>cW/utilization</mssUnits> + <default>0xfffffc00044C0000</default> + <array>50</array> + <platInit/> + <mssAccessorName>mrw_ocmb_pwr_intercept</mssAccessorName> + </attribute> + + <attribute> + <id>ATTR_MSS_MRW_OCMB_CURRENT_CURVE_WITH_LIMIT</id> + <targetType>TARGET_TYPE_SYSTEM</targetType> + <description> + 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) + </description> + <valueType>uint64</valueType> + <mssUnits>dA, cA/utilization, cA</mssUnits> + <default>0xfffffc0000000000</default> + <array>25</array> + <platInit/> + <mssAccessorName>mrw_ocmb_current_curve_with_limit</mssAccessorName> + </attribute> + + <attribute> + <id>ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL</id> + <targetType>TARGET_TYPE_SYSTEM</targetType> + <description> + 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%% + </description> + <valueType>uint32</valueType> + <default>0x000009C4</default> + <platInit/> + <initToZero/> + <mssAccessorName>mrw_safemode_dram_databus_util</mssAccessorName> + </attribute> + </attributes> 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 @@ </callout> </hwpError> + <hwpError> + <rc>RC_MSS_POWER_INTERCEPT_NOT_SET</rc> + <description> + The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 + </description> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_POWER_SLOPE_NOT_SET</rc> + <description> + The attribute ATTR_MSS_TOTAL_POWER_INTERCEPT was not set and equals 0 + </description> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_NO_DATABUS_UTILIZATION</rc> + <description> + There are 2 DIMMS on the port but both have 0 databus utilization + </description> + <ffdc>PORT_DATABUS_UTIL</ffdc> + <ffdc>DIMM_COUNT</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_CALC_POWER_CURVE_DIVIDE_BY_ZERO</rc> + <description> + Denominator equals 0 + </description> + <ffdc>PORT_DATABUS_UTIL</ffdc> + <ffdc>UTIL_CONVERSION</ffdc> + <ffdc>IDLE_UTIL</ffdc> + <ffdc>RESULT</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_NO_PORT_POWER_LIMIT</rc> + <description> + Got 0 when calculating port power limit. + Either no dimms or attribute MEM_WATT_TARGET wasn't set + </description> + <ffdc>COUNT_DIMMS</ffdc> + <ffdc>PORT_POWER_LIMIT</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_NO_PORT_POWER</rc> + <description> + Got 0 when calculating port power limits using the DIMMs databus utilization + </description> + <ffdc>COUNT_DIMMS</ffdc> + <ffdc>MAX_UTILIZATION_DIMM_0</ffdc> + <ffdc>MAX_UTILIZATION_DIMM_1</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_M_DRAM_CLOCKS_EQUALS_ZERO</rc> + <description> + ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS was not set and equals zero + </description> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_CALC_PORT_POWER_EXCEEDS_MAX</rc> + <description> + The calculated port power from equalizing throttles exceeds the maximum allowed power + </description> + <ffdc>CALCULATED_PORT_POWER</ffdc> + <ffdc>MAX_POWER_ALLOWED</ffdc> + <ffdc>PORT_POS</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + <callout> + <childTargets> + <parent>PORT_TARGET</parent> + <childType>TARGET_TYPE_DIMM</childType> + </childTargets> + <priority>MEDIUM</priority> + </callout> + <deconfigure> + <childTargets> + <parent>PORT_TARGET</parent> + <childType>TARGET_TYPE_DIMM</childType> + </childTargets> + </deconfigure> + </hwpError> + + <hwpError> + <rc>RC_MSS_SLOT_UTIL_EXCEEDS_PORT</rc> + <description> + The memory throttle per slot (DIMM) exceeds the allowed throttle for the port + </description> + <ffdc>SLOT_UTIL</ffdc> + <ffdc>PORT_UTIL</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_OUTPUT_OVERFLOW_CALC_UTIL</rc> + <description> + Type of output variable is not large enough for the calculations + </description> + <ffdc>RESULT</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_POWER_THERMAL_DECODE_ERROR</rc> + <description> + There was no match or value found in decoding the power thermal attributes + </description> + <ffdc>DIMM_TARGET</ffdc> + <ffdc>ATTR</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_MRW_SAFEMODE_UTIL_THROTTLE_NOT_SUPPORTED</rc> + <description> + The MRW safemode utilization that is less than the minimum utilization supported. Check ATTR_MSS_MRW_SAFEMODE_DRAM_DATABUS_UTIL. + </description> + <ffdc>MRW_SAFEMODE_UTIL</ffdc> + <ffdc>MIN_UTIL_VALUE</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_NO_POWER_THERMAL_ATTR_FOUND</rc> + <description> + There was no match or value found in decoding the power thermal attributes + </description> + <ffdc>GENERATED_KEY</ffdc> + <ffdc>FUNCTION</ffdc> + <ffdc>DIMM_TARGET</ffdc> + <ffdc>SIZE</ffdc> + <ffdc>DRAM_GEN</ffdc> + <ffdc>DIMM_TYPE</ffdc> + <ffdc>DRAM_WIDTH</ffdc> + <ffdc>DRAM_DENSITY</ffdc> + <ffdc>STACK_TYPE</ffdc> + <ffdc>MFGID</ffdc> + <ffdc>MODULE_HEIGHT</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_POWER_THERMAL_ENCODE_ERROR</rc> + <description> + There was no match or value found in encoding the power thermal attributes + </description> + <ffdc>DIMM_TARGET</ffdc> + <ffdc>ATTR</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_POWER_THERMAL_ATTR_VECTORS_INCORRECT</rc> + <description> + The attributes vectors size is incorrect for find_xxx functions + </description> + <ffdc>FUNCTION</ffdc> + <ffdc>INPUT_SIZE</ffdc> + <ffdc>EXPECTED_SIZE</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + + <hwpError> + <rc>RC_MSS_POWER_THERMAL_DIMM_INDEX_OUT_OF_BOUND</rc> + <description> + The dimm index is out of bound for the port + </description> + <ffdc>INPUT_SIZE</ffdc> + <ffdc>MAX_SIZE</ffdc> + <callout> + <procedure>CODE</procedure> + <priority>HIGH</priority> + </callout> + </hwpError> + </hwpErrors> |