diff options
Diffstat (limited to 'src/import/generic/memory/lib/utils')
41 files changed, 12455 insertions, 520 deletions
diff --git a/src/import/generic/memory/lib/utils/buffer_ops.H b/src/import/generic/memory/lib/utils/buffer_ops.H index 31e7f11a2..42b02b5d1 100644 --- a/src/import/generic/memory/lib/utils/buffer_ops.H +++ b/src/import/generic/memory/lib/utils/buffer_ops.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2015,2018 */ +/* Contributors Listed Below - COPYRIGHT 2015,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -103,6 +103,32 @@ static inline void reverse( T& io_buffer ) } /// +/// @brief Reverse a given bit range of a buffer +/// @tparam S start bit +/// @tparam L length of bits to reverse +/// @tparam T buffer type +/// @param[in] io_buffer +/// +template<uint64_t S, uint64_t L, typename T> +static inline void reverse_range(fapi2::buffer<T>& io_buffer) +{ + const auto target_length = fapi2::parameterTraits<T>::bit_length(); + + static_assert(S < target_length, + "reverse_range(): Start is out of bounds"); + + static_assert((S + L) <= target_length, + "reverse_range(): (Start + Len) is out of bounds"); + + fapi2::buffer<T> l_tmp; + + io_buffer.template extractToRight<S, L>(l_tmp); + + l_tmp.reverse(); + io_buffer.template insert<S, L>(l_tmp); +} + +/// /// @brief Swizzle bits between two fapi2 buffers, and insert from source to destination /// @tparam DS the start bit in the destination buffer - swizzle will count up from here /// @tparam L how many bits to swizzle diff --git a/src/import/generic/memory/lib/utils/conversions.H b/src/import/generic/memory/lib/utils/conversions.H index 5981fe7b5..dc4290ea5 100644 --- a/src/import/generic/memory/lib/utils/conversions.H +++ b/src/import/generic/memory/lib/utils/conversions.H @@ -101,7 +101,7 @@ static const std::vector<std::pair<uint64_t, uint64_t>> FREQ_TO_CLOCK_PERIOD = {DIMM_SPEED_4400, 454}, // DDR5 {DIMM_SPEED_4800, 416}, // DDR5 }; -} +} // ns dram_freq /// /// @brief Return the number of picoseconds diff --git a/src/import/generic/memory/lib/utils/count_dimm.H b/src/import/generic/memory/lib/utils/count_dimm.H index 87f54fb40..3ceea2fab 100644 --- a/src/import/generic/memory/lib/utils/count_dimm.H +++ b/src/import/generic/memory/lib/utils/count_dimm.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2018 */ +/* Contributors Listed Below - COPYRIGHT 2016,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -55,7 +55,6 @@ inline size_t count_dimm(const fapi2::Target<T>& i_target) // in the case of an over-ride and there are no DIMM in the config, we want to let // people know. Which is how we found we needed to add this code ... size_t l_dimm_count = find_targets<fapi2::TARGET_TYPE_DIMM>(i_target).size(); - FAPI_INF("%d DIMM on %s", l_dimm_count, mss::c_str(i_target)); return l_dimm_count; } @@ -71,5 +70,24 @@ inline size_t count_dimm(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) return 1; } +/// +/// @brief Return the count of the number of DIMM attached to a vector of targets +/// @tparam T the fapi2::TargetType +/// @param[in] i_target a vector of targets +/// @return size_t the count of DIMM attached +/// +template< fapi2::TargetType T > +inline size_t count_dimm(const std::vector<fapi2::Target<T>>& i_targets) +{ + size_t l_dimm_count = 0; + + for (const auto& l_target : i_targets) + { + l_dimm_count += count_dimm(l_target); + } + + return l_dimm_count; +} + } #endif diff --git a/src/import/generic/memory/lib/utils/dimm/kind.H b/src/import/generic/memory/lib/utils/dimm/kind.H index bcb29264d..cdabc493e 100644 --- a/src/import/generic/memory/lib/utils/dimm/kind.H +++ b/src/import/generic/memory/lib/utils/dimm/kind.H @@ -22,3 +22,252 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file kind.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 _GEN_MSS_KIND_H_ +#define _GEN_MSS_KIND_H_ + +#include <fapi2.H> + +#include <generic/memory/lib/mss_generic_attribute_getters.H> +#include <generic/memory/lib/utils/c_str.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> + +namespace mss +{ + +namespace dimm +{ + +/// +/// @class mss::dimm::kind +/// @tparam MC the MC type +/// @brief A class containing information about a dimm like ranks, density, configuration - what kind of dimm is it? +/// +template<mss::mc_type MC = DEFAULT_MC_TYPE> +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/dimm/mss_timing.H b/src/import/generic/memory/lib/utils/dimm/mss_timing.H new file mode 100644 index 000000000..2bc9805de --- /dev/null +++ b/src/import/generic/memory/lib/utils/dimm/mss_timing.H @@ -0,0 +1,912 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/dimm/mss_timing.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mss_timing.H +/// @brief Determine effective config for mss settings +/// +// *HWP HWP Owner: Louis Stermole <stermole@us.ibm.com> +// *HWP FW Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_GEN_TIMING_H_ +#define _MSS_GEN_TIMING_H_ + +#include <cstdint> +#include <fapi2.H> +#include <generic/memory/lib/mss_generic_attribute_getters.H> +#include <generic/memory/lib/utils/find.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#include <generic/memory/lib/utils/conversions.H> +#include <generic/memory/lib/spd/spd_utils.H> + +namespace mss +{ + +/// +/// @brief Enums for ffdc error callout so we know which function had the error +/// +enum timing_ffdc_codes +{ + TRAS = 0, + TFAW_HALF_KB_PAGE_HELPER = 1, + TFAW_ONE_KB_PAGE_HELPER = 2, + TFAW_TW_KB_PAGE_HELPER = 3, + TFAW_SLR_X4_HELPER = 4, + TFAW_SLR_X8_HELPER = 5, + TRRD_S_SLR = 6, + TRRD_L_SLR = 7, + TRRD_L_HALF_AND_1KB_PAGE_HELPER = 8, + TRRD_S_HALF_AND_1KB_PAGE_HELPER = 9, + TRRD_S_2KB_PAGE_HELPER = 10, + TRRD_S = 11, + TRRD_L = 12, + TFAW = 13, + TDLLK = 14, +}; + +enum refresh_rate : uint8_t +{ + REF1X = 1, ///< Refresh rate 1X + REF2X = 2, ///< Refresh rate 2X + REF4X = 4, ///< Refresh rate 4X +}; + +namespace spd +{ + +/// +/// @brief Returns clock cycles form picoseconds based on speed bin +/// Uses SPD rounding algorithm for DDR4 +/// @tparam OT the output type, derrived from the parameters +/// @param[in] i_freq frequency of the DIMM +/// @param[in] timing_in_ps timing parameter in ps +/// @return the clock cycles of timing parameter (provided in ps) +/// @note Uses DDR4 SPD Contents Rounding Algorithm +/// @note Item 2220.46 +/// +template<typename OT> +inline OT ps_to_nck( const uint64_t i_freq, const OT& i_timing_in_ps) +{ + OT l_tck_in_ps = 0; + OT l_temp_nck = 0; + + // No time if MT/s is 0 (well, infinite really but shut up) + if (i_freq == 0) + { + return 0; + } + + FAPI_TRY( freq_to_ps(i_freq, l_tck_in_ps), + "Failed freq() accessor" ); + FAPI_TRY( calc_nck(i_timing_in_ps, l_tck_in_ps, spd::INVERSE_DDR4_CORRECTION_FACTOR, l_temp_nck), + "Failed calc_nck()" ); + + return l_temp_nck; + +fapi_try_exit: + // We simply can't work if we get an unsupported value that can't be converted to a valid tCK (clock period) + // ...so this should be ok + FAPI_ERR("Obtained an invalid MSS_FREQ (%d), or overflow occurred - stopping", i_freq); + fapi2::Assert(false); + + // Keeps compiler happy + return 0; +} + +/// +/// @brief Returns clock cycles from nanoseconds +/// Uses SPD rounding algorithm for DDR4 +/// @tparam OT the output type, derrived from the parameters +/// @param[in] i_freq frequency of the DIMM +/// @param[out] o_value_nck the end calculation in nck +/// @return the clock cycles of timing parameter (provided in ps) +/// @note Uses DDR4 SPD Contents Rounding Algorithm +/// @note Item 2220.46 +/// +template<typename OT> +inline OT ns_to_nck( const uint64_t i_freq, const OT& i_timing_in_ns) +{ + return ps_to_nck(i_freq, i_timing_in_ns * CONVERT_PS_IN_A_NS); +} + +}// spd + +/// +/// @brief Calculates refresh interval time +/// @param[in] i_mode fine refresh rate mode +/// @param[in] i_refresh_request_rate refresh rate +/// @param[out] o_value timing val in ps +/// @return fapi2::ReturnCode +/// +inline fapi2::ReturnCode calc_trefi( const refresh_rate i_mode, + const uint8_t i_refresh_request_rate, + uint64_t& o_timing ) +{ + // Proposed DDR4 Full spec update(79-4B) + // Item No. 1716.78C + // pg.46 + // Table 24 - tREFI and tRFC parameters (in ps) + constexpr uint64_t TREFI_BASE = 7800000; + + uint64_t l_refresh_request = 0; + constexpr double TEN_PERCENT_FASTER = 0.90; + + switch(i_refresh_request_rate) + { + case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_SINGLE: + l_refresh_request = TREFI_BASE; + break; + + case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_DOUBLE: + // We are truncating but there is no remainder with TREFI_BASE, so we are okay + l_refresh_request = TREFI_BASE / 2; + break; + + case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_SINGLE_10_PERCENT_FASTER: + // We are truncating but there is no remainder with TREFI_BASE, so we are okay + // 10% faster so 100% - 10% = 90% + l_refresh_request = TREFI_BASE * TEN_PERCENT_FASTER; + break; + + case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_DOUBLE_10_PERCENT_FASTER: + // We are truncating but there is no remainder with TREFI_BASE, so we are okay + // 10% faster so 100% - 10% = 90% + l_refresh_request = (TREFI_BASE / 2) * TEN_PERCENT_FASTER; + break; + + default: + // Will catch incorrect MRW value set + FAPI_ASSERT(false, + fapi2::MSS_INVALID_REFRESH_RATE_REQUEST().set_REFRESH_RATE_REQUEST(i_refresh_request_rate), + "Incorrect refresh request rate received: %d ", i_refresh_request_rate); + break; + } + + o_timing = (l_refresh_request / i_mode); + + FAPI_INF( "tREFI (ps): %d, refresh request (ps): %d, tREFI_base (ps): %d, REF%dX", + o_timing, l_refresh_request, TREFI_BASE, i_mode ); + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// @brief Calculates Minimum Refresh Recovery Delay Time (different logical rank) +/// @param[in] i_mode fine refresh rate mode +/// @param[in] i_density SDRAM density +/// @param[out] o_trfc_in_ps timing val in ps +/// @return fapi2::FAPI2_RC_SUCCESS iff okay +/// +inline fapi2::ReturnCode calc_trfc_dlr(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + const uint8_t i_refresh_mode, + const uint8_t i_density, + uint64_t& o_trfc_in_ps) +{ + // Proposed DDR4 3DS Addendum + // Item No. 1727.58A + // pg. 69 - 71 + // Table 42 - Refresh parameters by logical rank density + const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR1 = + { + // { density in GBs, tRFC4(min) in picoseconds } + {4, 90000}, + {8, 120000}, + {16, 185000}, + }; + + // Proposed DDR4 3DS Addendum + // Item No. 1727.58A + // pg. 69 - 71 + // Table 42 - Refresh parameters by logical rank density + const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR2 = + { + // { density in GBs, tRFC4(min) in picoseconds } + {4, 55000}, + {8, 90000}, + {16, 120000}, + }; + + // Proposed DDR4 3DS Addendum + // Item No. 1727.58A + // pg. 69 - 71 + // Table 42 - Refresh parameters by logical rank density + const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR4 = + { + // { density in GBs, tRFC4(min) in picoseconds } + {4, 40000}, + {8, 55000}, + {16, 90000}, + }; + + bool l_is_val_found = 0; + + // Selects appropriate tRFC based on fine refresh mode + switch(i_refresh_mode) + { + case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_NORMAL: + l_is_val_found = find_value_from_key(TRFC_DLR1, i_density, o_trfc_in_ps); + break; + + case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FIXED_2X: + case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FLY_2X: + l_is_val_found = find_value_from_key(TRFC_DLR2, i_density, o_trfc_in_ps); + break; + + case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FIXED_4X: + case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FLY_4X: + l_is_val_found = find_value_from_key(TRFC_DLR4, i_density, o_trfc_in_ps); + break; + + default: + // Fine Refresh Mode will be a platform attribute set by the MRW, + // which they "shouldn't" mess up as long as use "attribute" enums. + // if openpower messes this up we can at least catch it + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FINE_REFRESH_MODE() + .set_FINE_REF_MODE(i_refresh_mode), + "Incorrect Fine Refresh Mode received: %d ", + i_refresh_mode); + break; + }// switch + + FAPI_ASSERT( l_is_val_found, + fapi2::MSS_FAILED_TO_FIND_TRFC() + .set_SDRAM_DENSITY(i_density) + .set_REFRESH_MODE(i_refresh_mode) + .set_DIMM_TARGET(i_target), + "%s: Unable to find tRFC (ps) from map with SDRAM density key %d with %d refresh mode", + mss::c_str(i_target), + i_density, + i_refresh_mode); + + // Again, FAPI_ASSERT doesn't set current_err to good, only to bad + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief tRTP *in ps* +/// @return constexpr value of RTP = 7500 ps +/// +constexpr uint64_t trtp() +{ + // Per JEDEC spec, defaults to 7500 ps for all frequencies. + // (technically max of 7.5 ns or 4 nclk, which is always 7.5ns for DDR4) + return 7500; +} + +/// +/// @brief Return the minimum allowable tRAS in picoseconds +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq freq for the DIMM +/// @return value in picoseconds +/// +inline uint64_t tras(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + const uint64_t i_freq) +{ + uint64_t l_tras = 0; + + switch(i_freq) + { + case 1866: + l_tras = 34000; + break; + + case 2133: + l_tras = 33000; + break; + + case 2400: + case 2666: + case 2933: + case 3200: + l_tras = 32000; + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TRAS) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + } + + return l_tras; + +fapi_try_exit: + + // We simply can't work if we can't get the frequency or + // if we get an unsupported value that can't be converted to a valid tCK (clock period) + // ...so this should be ok + FAPI_ERR("Obtained an invalid MSS_FREQ (%d) - stopping", i_freq); + fapi2::Assert(false); + + // Keeps compiler happy + return 0; +} + +/// +/// @brief Helper function to find tFAW based speed (MT/s) for 1/2 KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode tfaw_half_kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability. + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + // static_cast is needed for template deduction of std::max API + case 1866: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 17000) ); + break; + + case 2133: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 15000) ); + break; + + case 2400: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 13000) ); + break; + + case 2666: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 12000) ); + break; + + case 2933: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 10875) ); + break; + + case 3200: + o_output = std::max( 16, spd::ps_to_nck(i_freq, 10000) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TFAW_HALF_KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find tFAW based speed (MT/s) for 1KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode tfaw_1kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability (and ease of debug?). + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + o_output = std::max( 20, spd::ns_to_nck(i_freq, 23) ); + break; + + case 2133: + case 2400: + case 2666: + case 2933: + case 3200: + o_output = std::max( 20, spd::ns_to_nck(i_freq, 21) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TFAW_ONE_KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + break; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find tFAW based speed (MT/s) for 2KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode tfaw_2kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability. + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + case 2133: + case 2400: + case 2666: + case 2933: + case 3200: + o_output = std::max( 28, spd::ns_to_nck(i_freq, 30) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TFAW_TW_KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + break; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return the minimum allowable tFAW in nck +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_dram_width the page size +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_tFAW timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +// +template< fapi2::TargetType T > +fapi2::ReturnCode tfaw( const fapi2::Target<T>& i_target, + const uint8_t i_dram_width, + const uint64_t i_freq, + uint64_t& o_tFAW ) +{ + switch(i_dram_width) + { + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4: + FAPI_TRY( tfaw_half_kb_page_helper(i_target, i_freq, o_tFAW) ); + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8: + FAPI_TRY( tfaw_1kb_page_helper(i_target, i_freq, o_tFAW) ); + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16: + FAPI_TRY( tfaw_2kb_page_helper(i_target, i_freq, o_tFAW) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_DRAM_WIDTH() + .set_DRAM_WIDTH(i_dram_width) + .set_DIMM_TARGET(i_target), + "Invalid DRAM width with %d for target %s", + i_dram_width, + mss::c_str(i_target)); + break; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief tFAW_dlr *in nck* +/// @return 16nck +/// @note From DDR4 3DS Spec +/// 12.2 Timing Parameters by Speed Grade +/// +constexpr uint64_t tfaw_dlr() +{ + return 16; +} + +/// +/// @brief tRRD_dlr *in nck* +/// @return 4nck +/// @note From DDR4 3DS Spec +/// 12.2 Timing Parameters by Speed Grade +/// +constexpr uint64_t trrd_dlr() +{ + return 4; +} + +/// +/// @brief Helper function to find tRRD_L based speed (MT/s) for 1KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode trrd_l_half_and_1kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability (and ease of debug?). + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + case 2133: + // From the spec: Max(4nCK,5.3ns) + o_output = std::max( 4, spd::ps_to_nck(i_freq, 5300) ); + break; + + case 2400: + case 2666: + case 2933: + case 3200: + // Max(4nCK,4.9ns) + o_output = std::max( 4, spd::ps_to_nck(i_freq, 4900) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TRRD_L_HALF_AND_1KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find tRRD_L based speed (MT/s) for 2KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode trrd_l_2kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability (and ease of debug?). + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + case 2133: + case 2400: + case 2666: + case 2933: + case 3200: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 6400) ); + break; + + default: + FAPI_TRY(fapi2::FAPI2_RC_INVALID_PARAMETER, "%s Invalid frequency %lu", mss::c_str(i_target), i_freq); + break; + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return the minimum allowable tRRD_L in nck +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_dram_width the page size +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +fapi2::ReturnCode trrd_l( const fapi2::Target<T>& i_target, + const uint8_t i_dram_width, + const uint64_t i_freq, + uint64_t& o_tRRD_L ) +{ + switch(i_dram_width) + { + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4: + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8: + FAPI_TRY( trrd_l_half_and_1kb_page_helper(i_target, i_freq, o_tRRD_L), + "Error calculating trrd l for half and 1kb page" ); + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16: + FAPI_TRY( trrd_l_2kb_page_helper(i_target, i_freq, o_tRRD_L) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_PAGE_SIZE() + .set_DRAM_WIDTH(i_dram_width) + .set_DIMM_TARGET(i_target), + "%s Recieved an invalid page size: %lu", + mss::c_str(i_target), + i_dram_width); + break; + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find tRRD_S based speed (MT/s) for 1KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode trrd_s_half_and_1kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability (and ease of debug?). + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 4200) ); + break; + + case 2133: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 3700) ); + break; + + case 2400: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 3300) ); + break; + + case 2666: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 3000) ); + break; + + case 2933: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 2700) ); + break; + + case 3200: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 2500) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TRRD_S_HALF_AND_1KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper function to find tRRD_S based speed (MT/s) for 2KB page +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_output timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +static fapi2::ReturnCode trrd_s_2kb_page_helper(const fapi2::Target<T>& i_target, + const uint64_t i_freq, + uint64_t& o_output) +{ + // Values derived from DDR4 Spec (79-4A) + // 13.3 Timing Parameters by Speed Grade + // Table 132. Pg 240 + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // It could have been more "efficient" to hand-calculate the answer and + // use compile time constants to return the answer. To avoid magic + // numbers and to align (more closely) with the DDR4 JEDEC spec, + // we let the std library do the work for us for maintainability (and ease of debug?). + // Could have used compile-time constants to denote the numbers below + // but they are "random" and vary. + switch(i_freq) + { + case 1866: + case 2133: + case 2400: + case 2666: + case 2933: + case 3200: + o_output = std::max( 4, spd::ps_to_nck(i_freq, 5300) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_FREQ_PASSED_IN() + .set_FREQ(i_freq) + .set_FUNCTION(TRRD_S_2KB_PAGE_HELPER) + .set_DIMM_TARGET(i_target), + "%s Invalid frequency %lu", + mss::c_str(i_target), + i_freq); + break; + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return the minimum allowable tRRD_S in nck +/// @tparam T the fapi2::TargetType of a type from which we can get MT/s +/// @param[in] i_target the fapi2 target +/// @param[in] i_dram_width the page size +/// @param[in] i_freq the DRAM frequency +/// @param[out] o_tRRD_S timing in clocks (nck) +/// @return FAPI2_RC_SUCCESS iff okay +/// @note this is only for non-3DS DIMM +/// +template< fapi2::TargetType T > +fapi2::ReturnCode trrd_s( const fapi2::Target<T>& i_target, + const uint8_t i_dram_width, + const uint64_t i_freq, + uint64_t& o_tRRD_S ) +{ + switch(i_dram_width) + { + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4: + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8: + FAPI_TRY( trrd_s_half_and_1kb_page_helper(i_target, i_freq, o_tRRD_S), + "Error calculating trrd_s for half and 1kb page" ); + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16: + FAPI_TRY( trrd_s_2kb_page_helper(i_target, i_freq, o_tRRD_S) ); + break; + + default: + FAPI_ASSERT( false, + fapi2::MSS_INVALID_PAGE_SIZE() + .set_DRAM_WIDTH(i_dram_width) + .set_DIMM_TARGET(i_target), + "%s Recieved an invalid page size: %lu", + mss::c_str(i_target), + i_dram_width); + break; + } + + return fapi2::FAPI2_RC_SUCCESS; +fapi_try_exit: + return fapi2::current_err; +} + +} // mss +#endif diff --git a/src/import/generic/memory/lib/utils/dump_regs.H b/src/import/generic/memory/lib/utils/dump_regs.H index 91241cd70..f8d7e5b12 100644 --- a/src/import/generic/memory/lib/utils/dump_regs.H +++ b/src/import/generic/memory/lib/utils/dump_regs.H @@ -22,3 +22,34 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + + +/// +/// @file dump_regs.H +/// @brief Dump registers +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@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_DUMP_REGS_H_ +#define _MSS_DUMP_REGS_H_ + +#include <fapi2.H> + +namespace mss +{ + +/// +/// @brief Dump the registers of a target +/// @tparam T, the fapi2::TargetType +/// @param[in] i_target the target in question +/// @return fapi2::FAPI2_RC_SUCCESS if ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode dump_regs( const fapi2::Target<T>& i_target ); + +} +#endif diff --git a/src/import/generic/memory/lib/utils/endian_utils.H b/src/import/generic/memory/lib/utils/endian_utils.H index 20bfd7caa..94b9dbc0d 100644 --- a/src/import/generic/memory/lib/utils/endian_utils.H +++ b/src/import/generic/memory/lib/utils/endian_utils.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -37,9 +37,14 @@ #ifndef _ENDIAN_UTILS_H_ #define _ENDIAN_UTILS_H_ -#include <cstdint> #include <vector> -#include <generic/memory/lib/utils/shared/mss_generic_consts.H> + +#ifdef __PPE__ + #include <mss_generic_consts.H> +#else + #include <cstdint> + #include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#endif namespace mss { @@ -67,6 +72,7 @@ void forceLE(const T& i_input, std::vector<uint8_t>& io_data) } } +#ifndef __PPE__ /// /// @brief Forces native data into LE order for an array /// @tparam T the data type to process @@ -82,7 +88,7 @@ inline void forceLEArray(const T* i_input, const uint64_t i_size, std::vector<ui forceLE(i_input[i], io_data); } } - +#endif /// /// @brief Forces native data into BE order /// @tparam T the data type to process @@ -98,20 +104,31 @@ void forceBE(const T& i_input, std::vector<uint8_t>& io_data) std::vector<uint8_t> l_tempBuffer; // This loop will put i_input into l_tempBuffer in BE order - for(size_t i = 0; i < sizeof(i_input); i++) + + for(size_t i = sizeof(i_input); i > 0; i--) { // Grab the lowest order byte and add it to the front of the vector const uint8_t l_byte = l_temp & 0xFF; - l_tempBuffer.insert(l_tempBuffer.begin(), l_byte); + l_tempBuffer.push_back(l_byte); // Shift higher byte value into lowest no matter existing endianness l_temp >>= BITS_PER_BYTE; } // Put the new BE formatted data at the end of the input buffer - io_data.insert(io_data.end(), l_tempBuffer.begin(), l_tempBuffer.end()); + + std::vector<uint8_t>::iterator it = l_tempBuffer.end(); + --it; //Move iterator to the last element. + + for(uint8_t i = l_tempBuffer.size(); i > 0; --i) + { + io_data.push_back(*it); + --it; + } + } +#ifndef __PPE__ /// /// @brief Forces native data into BE order for an array /// @tparam T the data type to process @@ -127,6 +144,7 @@ inline void forceBEArray(const T* i_input, const uint64_t i_size, std::vector<ui forceBE(i_input[i], io_data); } } +#endif /// /// @brief Converts LE data into native order @@ -165,6 +183,7 @@ bool readLE(const std::vector<uint8_t>& i_input, uint32_t& io_idx, T& o_data) return true; } +#ifndef __PPE__ /// /// @brief Converts LE data into native order /// @tparam T the data type to output to @@ -188,6 +207,7 @@ bool readLEArray(const std::vector<uint8_t>& i_input, const uint32_t i_size, uin return l_passing; } +#endif /// /// @brief Converts BE data into native order @@ -225,6 +245,7 @@ bool readBE(const std::vector<uint8_t>& i_input, uint32_t& io_idx, T& o_data) return true; } +#ifndef __PPE__ /// /// @brief Converts BE data into native order /// @tparam T the data type to output to @@ -248,6 +269,7 @@ bool readBEArray(const std::vector<uint8_t>& i_input, const uint32_t i_size, uin return l_passing; } +#endif } diff --git a/src/import/generic/memory/lib/utils/find.H b/src/import/generic/memory/lib/utils/find.H index c22199b6d..297334ae7 100644 --- a/src/import/generic/memory/lib/utils/find.H +++ b/src/import/generic/memory/lib/utils/find.H @@ -38,9 +38,13 @@ #include <fapi2.H> #include <vector> +#include <generic/memory/lib/utils/c_str.H> +#include <generic/memory/lib/utils/index.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> namespace mss { + /// /// @brief Helper to find a set of elements based on a fapi2 target /// @tparam M the target type to be returned @@ -117,6 +121,7 @@ static inline fapi2::Target<M> find_target_impl( const fapi2::Target<T>& i_targe /// @tparam M the target type to be returned /// @tparam T the fapi2 target type of the argument /// @param[in] i_target the fapi2 target T +/// @param[in] std::true_type tag dispatch if T == M /// @return an M target. /// @note Only works for valid parent-child relationships /// @@ -145,30 +150,6 @@ inline fapi2::Target<M> find_target( const fapi2::Target<T>& i_target) } /// -/// @brief find the McBIST given a DIMM -/// @param[in] i_target the fapi2 target DIMM -/// @return a McBIST target. -/// -template<> -inline fapi2::Target<fapi2::TARGET_TYPE_MCBIST> find_target(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) -{ - return i_target.getParent<fapi2::TARGET_TYPE_MCA>().getParent<fapi2::TARGET_TYPE_MCBIST>(); -} - -/// -/// @brief find the PROC_CHIP given a MBA -/// @param[in] i_target the fapi2 target MBA -/// @return a DMI target. -/// -template<> -inline fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> find_target(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target) -{ - return i_target.getParent<fapi2::TARGET_TYPE_MEMBUF_CHIP>() - .getParent<fapi2::TARGET_TYPE_DMI>() - .getParent<fapi2::TARGET_TYPE_PROC_CHIP>(); -} - -/// /// @brief find the PROC_CHIP given a OCMB_CHIP /// @param[in] i_target the fapi2 target OCMB_CHIP /// @return a PROC_CHIP target. @@ -183,26 +164,16 @@ inline fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> find_target(const fapi2::Targ } /// -/// @brief find the DMI given an MBA -/// @param[in] i_target the fapi2 target MBA -/// @return a DMI target. -/// -template<> -inline fapi2::Target<fapi2::TARGET_TYPE_DMI> find_target(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target) -{ - return i_target.getParent<fapi2::TARGET_TYPE_MEMBUF_CHIP>().getParent<fapi2::TARGET_TYPE_DMI>(); -} - -/// -/// @brief find the PROC given a MEMBUF -/// @param[in] i_target the fapi2 target MEMBUF -/// @return a PROC target. +/// @brief find the MCC given a OCMB_CHIP +/// @param[in] i_target the fapi2 target OCMB_CHIP +/// @return a MCC target. /// template<> -inline fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> find_target(const fapi2::Target<fapi2::TARGET_TYPE_MEMBUF_CHIP>& +inline fapi2::Target<fapi2::TARGET_TYPE_MCC> find_target(const fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP>& i_target) { - return i_target.getParent<fapi2::TARGET_TYPE_DMI>().getParent<fapi2::TARGET_TYPE_PROC_CHIP>(); + return i_target.getParent<fapi2::TARGET_TYPE_OMI>() + .getParent<fapi2::TARGET_TYPE_MCC>(); } /// @@ -230,98 +201,93 @@ find_targets( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target, } /// -/// @brief find all the MBA connected to an DMI -/// @param[in] i_target a fapi2::Target DMI -/// @return a vector of fapi2::TARGET_TYPE_MBA -/// -template<> -inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_MBA> > -find_targets( const fapi2::Target<fapi2::TARGET_TYPE_DMI>& i_target, - fapi2::TargetState i_state ) -{ - std::vector< fapi2::Target<fapi2::TARGET_TYPE_MBA> > l_mbas; - - for (const auto& membuf_chip : i_target.getChildren<fapi2::TARGET_TYPE_MEMBUF_CHIP>(i_state)) - { - auto l_these_mbas( membuf_chip.getChildren<fapi2::TARGET_TYPE_MBA>(i_state) ); - l_mbas.insert(l_mbas.end(), l_these_mbas.begin(), l_these_mbas.end()); - } - - return l_mbas; -} - -/// -/// @brief find all the DIMM connected to a centaur -/// @param[in] i_target a fapi2::Target TARGET_TYPE_MEMBUF_CHIP +/// @brief find all the DIMMS connected to an MI +/// @param[in] i_target a fapi2::Target MI /// @return a vector of fapi2::TARGET_TYPE_DIMM /// template<> inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > -find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MEMBUF_CHIP>& i_target, +find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MI>& i_target, fapi2::TargetState i_state ) { std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > l_dimms; - for (const auto& l_mba : i_target.getChildren<fapi2::TARGET_TYPE_MBA>(i_state)) + for (const auto& l_omi : i_target.getChildren<fapi2::TARGET_TYPE_OMI>(i_state)) { - auto l_these_dimms( l_mba.getChildren<fapi2::TARGET_TYPE_DIMM>(i_state) ); - l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + for (const auto& l_ocmb : l_omi.getChildren<fapi2::TARGET_TYPE_OCMB_CHIP>(i_state)) + { + auto l_these_dimms( l_ocmb.getChildren<fapi2::TARGET_TYPE_DIMM>(i_state) ); + l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + } } return l_dimms; } /// -/// @brief find all the dimm connected to an MCS -/// @param[in] i_target a fapi2::Target MCS -/// @return a vector of fapi2::TARGET_TYPE_DIMM +/// @brief find all the OCMB_CHIPs connected to an MI +/// @param[in] i_target a fapi2::Target MI +/// @return a vector of fapi2::TARGET_TYPE_OCMB_CHIP /// template<> -inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > -find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target, +inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP> > +find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MI>& i_target, fapi2::TargetState i_state ) { - std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > l_dimms; + std::vector< fapi2::Target<fapi2::TARGET_TYPE_OCMB_CHIP> > l_ocmbs; - for (const auto& p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>(i_state)) + for (const auto& l_omi : i_target.getChildren<fapi2::TARGET_TYPE_OMI>(i_state)) { - auto l_these_dimms( p.getChildren<fapi2::TARGET_TYPE_DIMM>(i_state) ); - l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + auto l_these_ocmbs( l_omi.getChildren<fapi2::TARGET_TYPE_OCMB_CHIP>(i_state) ); + l_ocmbs.insert(l_ocmbs.end(), l_these_ocmbs.begin(), l_these_ocmbs.end()); } - return l_dimms; + return l_ocmbs; } /// -/// @brief find all the dimms connected to an MCBIST -/// @param[in] i_target a fapi2::Target MCBIST -/// @return a vector of fapi2::TARGET_TYPE_DIMM +/// @brief find all the MEM_PORTs connected to a PROC_CHIP +/// @param[in] i_target a fapi2::Target PROC_CHIP +/// @return a vector of fapi2::TARGET_TYPE_MEM_PORT /// template<> -inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > -find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target, +inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_MEM_PORT> > +find_targets( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& i_target, fapi2::TargetState i_state ) { - std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > l_dimms; + std::vector< fapi2::Target<fapi2::TARGET_TYPE_MEM_PORT> > l_ports; - for (const auto& p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>(i_state)) + for (const auto& l_mc : i_target.getChildren<fapi2::TARGET_TYPE_MC>(i_state)) { - auto l_these_dimms( p.getChildren<fapi2::TARGET_TYPE_DIMM>(i_state) ); - l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + for (const auto& l_omi : l_mc.getChildren<fapi2::TARGET_TYPE_OMI>(i_state)) + { + for (const auto& l_ocmb : l_omi.getChildren<fapi2::TARGET_TYPE_OCMB_CHIP>(i_state)) + { + auto l_these_ports( l_ocmb.getChildren<fapi2::TARGET_TYPE_MEM_PORT>(i_state) ); + l_ports.insert(l_ports.end(), l_these_ports.begin(), l_these_ports.end()); + } + } } - return l_dimms; + return l_ports; } /// -/// @brief find the MCS given a DIMM -/// @param[in] i_target the fapi2 target DIMM -/// @return a MCS target. +/// @brief Helper to find a set of elements based on a fapi2 target, then sort them +/// @tparam M the target type to be returned +/// @tparam T the fapi2 target type of the argument +/// @param[in] i_target the fapi2 target T +/// @param[in] i_state [optional] fapi2 target state (defaults to TARGET_STATE_FUNCTIONAL) +/// @return a vector of M targets sorted by mss::index. +/// @note this uses mss::index so the targets will be sorted via ATTR_REL_POS of their immediate parent /// -template<> -inline fapi2::Target<fapi2::TARGET_TYPE_MCS> find_target( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +template< fapi2::TargetType M, fapi2::TargetType T > +static inline std::vector< fapi2::Target<M> > find_targets_sorted_by_index( const fapi2::Target<T>& i_target, + fapi2::TargetState i_state = fapi2::TARGET_STATE_FUNCTIONAL ) { - return i_target.getParent<fapi2::TARGET_TYPE_MCA>().getParent<fapi2::TARGET_TYPE_MCS>(); + std::vector<fapi2::Target<M>> l_targets = find_targets<M, T>(i_target, i_state); + sort_targets_by_index(l_targets); + return l_targets; } /// @@ -432,6 +398,40 @@ bool find_value_from_key( const std::pair<T, OT> (&i_array)[N], return false; } +/// +/// @brief Mapping boilerplate check +/// @tparam T FAPI2 target type +/// @tparam IT map key type +/// @tparam OT map value type +/// @param[in] i_target the FAPI target +/// @param[in] i_map SPD to attribute data mapping +/// @param[in] i_ffdc_code FFDC function code +/// @param[in] i_key Key to query map +/// @param[out] o_output value from key +/// @return FAPI2_RC_SUCCESS iff okay +/// +template< fapi2::TargetType T, typename IT, typename OT > +inline fapi2::ReturnCode lookup_table_check(const fapi2::Target<T>& i_target, + const std::vector<std::pair<IT, OT>>& i_map, + const generic_ffdc_codes i_ffdc_code, + const IT i_key, + OT& o_output) +{ + const bool l_is_val_found = mss::find_value_from_key(i_map, i_key, o_output); + + FAPI_ASSERT( l_is_val_found, + fapi2::MSS_LOOKUP_FAILED() + .set_KEY(i_key) + .set_DATA(o_output) + .set_FUNCTION(i_ffdc_code) + .set_TARGET(i_target), + "Failed to find a mapped value for %d on %s", + i_key, + mss::spd::c_str(i_target) ); +fapi_try_exit: + return fapi2::current_err; +} + }// mss #endif diff --git a/src/import/generic/memory/lib/utils/find_magic.H b/src/import/generic/memory/lib/utils/find_magic.H deleted file mode 100644 index a67d4f2e8..000000000 --- a/src/import/generic/memory/lib/utils/find_magic.H +++ /dev/null @@ -1,164 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/find_magic.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ - -#ifndef _MSS_FIND_WITH_MAGIC_H -#define _MSS_FIND_WITH_MAGIC_H - -#include <fapi2.H> -#include <vector> -#include <generic/memory/lib/utils/pos.H> -#include <generic/memory/lib/utils/c_str.H> -#include <generic/memory/lib/utils/find.H> - -namespace mss -{ - -/// -/// @brief find the union of functionl targets and any magic targets -/// @note The PHY has a logic block which is only contained in the 0th PHY in the controller. -/// This makes the 0th PHY 'magic' in that it needs to always be present if not functional. -/// This function returns all functional targets and includes the magic target whether or not -/// it is truly functional. -/// @tparam M the target type to be returned -/// @tparam T the fapi2 target type of the argument -/// @param[in] i_target the fapi2 target T -/// @return a vector of M targets. -/// -template< fapi2::TargetType M, fapi2::TargetType T > -inline std::vector< fapi2::Target<M> > find_targets_with_magic( const fapi2::Target<T>& i_target); - -/// -/// @brief find a set of magic elements based on a fapi2 target -/// @note The PHY has a logic block which is only contained in the 0th PHY in the controller. -/// This makes the 0th PHY 'magic' in that it needs to always be present if not functional. -/// This function returns all magic targets whether or not it is truly functional. -/// It does not include other functional or present targets. -/// @tparam M the target type to be returned -/// @tparam T the fapi2 target type of the argument -/// @param[in] i_target the fapi2 target T -/// @return a vector of M targets. -/// -template< fapi2::TargetType M, fapi2::TargetType T > -inline std::vector< fapi2::Target<M> > find_magic_targets( const fapi2::Target<T>& i_target); - -/// -/// @brief find the magic MCA connected to an MCBIST -/// @param[in] i_target the fapi2::Target MCBIST -/// @return a vector of fapi2::TARGET_TYPE_MCA -/// -template<> -inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_MCA> > -find_magic_targets(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target) -{ - // The magic port is in position 0, relative to the MCBIST - constexpr uint64_t RELATIVE_MAGIC_POS = 0; - - // This is only one magic MCA on every MCBIST, so we only return a vector of one - std::vector<fapi2::Target<fapi2::TARGET_TYPE_MCA>> l_magic_ports; - - // Get all the present MCA children and find the target with the relative position of 0 - for (const auto& p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>(fapi2::TARGET_STATE_PRESENT)) - { - if (mss::relative_pos<fapi2::TARGET_TYPE_MCBIST>(p) == RELATIVE_MAGIC_POS) - { - l_magic_ports.push_back(p); - } - } - - // We don't care if the vector is empty. We don't know what the caller will do with this - // and they might not care if there is no magic port either ... - return l_magic_ports; -} - -/// -/// @brief find the union of functionl targets and any magic targets -/// @param[in] i_target the fapi2::Target MCBIST -/// @return a vector of i2::Target<fapi2::TARGET_TYPE_MCA> -/// -template<> -inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_MCA> > -find_targets_with_magic( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target) -{ - // We need the union of the functional target list and the magic target list. We can - // get a little tricky with the MCA's - we know there's only one magic port. - // So if the one magic port isn't in the list of functional ports, add it - auto l_magic_ports = find_magic_targets<fapi2::TARGET_TYPE_MCA>(i_target); - - if (l_magic_ports.size() != 1) - { - FAPI_ERR("Found wrong number of magic ports on %s (%d)", mss::c_str(i_target), l_magic_ports.size()); - fapi2::Assert(false); - } - - auto l_ports = mss::find_targets<fapi2::TARGET_TYPE_MCA>(i_target); - const auto l_magic_pos = mss::relative_pos<fapi2::TARGET_TYPE_MCBIST>(l_magic_ports[0]); - const auto l_magic_port = std::find_if(l_ports.begin(), l_ports.end(), - [&l_magic_pos](const fapi2::Target<fapi2::TARGET_TYPE_MCA>& t) - { - // Check ports by relative position. - const auto l_pos = mss::relative_pos<fapi2::TARGET_TYPE_MCBIST>(t); - FAPI_DBG("checking for magic port at %d candidate is %d", l_magic_pos, l_pos); - return l_magic_pos == l_pos; - }); - - if (l_magic_port == l_ports.end()) - { - // Add the magic port to the front of the port vector. - FAPI_DBG("inserting magic port %d", l_magic_pos); - l_ports.insert(l_ports.begin(), l_magic_ports[0]); - } - - // In either case, l_ports is the proper thing to return. Either the magic port was in - // l_ports or it is now because we inserted it. - return l_ports; -} - -/// -/// @brief Determine if a thing is functional -/// @tparam I, the type of the item we want to check for -/// @tparam P, the type of the parent which holds the things of interest -/// @param[in] i_target the parent containing the thing we're looking for -/// @param[in] i_rel_pos the relative position of the item of interest. -/// @return bool true iff the thing at i_rel_pos is noted as functional -/// -template< fapi2::TargetType I, fapi2::TargetType P > -bool is_functional( const fapi2::Target<P>& i_target, const uint64_t i_rel_pos ) -{ - // Not sure of a good way to do this ... we get all the functional - // children of the parent and look for our relative position ... - for (const auto& i : i_target.template getChildren<I>(fapi2::TARGET_STATE_FUNCTIONAL)) - { - if (mss::template relative_pos<P>(i) == i_rel_pos) - { - return true; - } - } - - return false; -} - -}// mss - -#endif diff --git a/src/import/generic/memory/lib/utils/fir/gen_mss_unmask.H b/src/import/generic/memory/lib/utils/fir/gen_mss_unmask.H index fa14534e5..a45b48202 100644 --- a/src/import/generic/memory/lib/utils/fir/gen_mss_unmask.H +++ b/src/import/generic/memory/lib/utils/fir/gen_mss_unmask.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -38,6 +38,8 @@ #include <fapi2.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> + namespace mss { @@ -46,40 +48,63 @@ namespace unmask /// /// @brief Unmask and setup actions performed after draminit_mc +/// @tparam MC the memory controller type /// @tparam T the fapi2::TargetType which hold the FIR bits /// @param[in] i_target the fapi2::Target /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok /// -template< mss::mc_type MC, fapi2::TargetType T > +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > fapi2::ReturnCode after_draminit_mc( const fapi2::Target<T>& i_target ); /// /// @brief Unmask and setup actions performed after draminit_training +/// @tparam MC the memory controller type /// @tparam T the fapi2::TargetType which hold the FIR bits /// @param[in] i_target the fapi2::Target /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok /// -template< mss::mc_type MC, fapi2::TargetType T > +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > fapi2::ReturnCode after_draminit_training( const fapi2::Target<T>& i_target ); /// /// @brief Unmask and setup actions performed after mss_scominit +/// @tparam MC the memory controller type /// @tparam T the fapi2::TargetType which hold the FIR bits -/// @param[in] i_target the fapi2::Target of the MCBIST +/// @param[in] i_target the fapi2::Target /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok /// -template< mss::mc_type MC, fapi2::TargetType T > +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > fapi2::ReturnCode after_scominit( const fapi2::Target<T>& i_target ); /// /// @brief Unmask and setup actions performed after mss_ddr_phy_reset +/// @tparam MC the memory controller type /// @tparam T the fapi2::TargetType which hold the FIR bits -/// @param[in] i_target the fapi2::Target of the MCBIST +/// @param[in] i_target the fapi2::Target /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok /// -template< mss::mc_type MC, fapi2::TargetType T > +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > fapi2::ReturnCode after_phy_reset( const fapi2::Target<T>& i_target ); +/// +/// @brief Unmask and setup actions for memdiags related FIR +/// @tparam MC the memory controller type +/// @tparam T the fapi2::TargetType which hold the FIR bits +/// @param[in] i_target the fapi2::Target +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode after_memdiags( const fapi2::Target<T>& i_target ); + +/// +/// @brief Unmask and setup actions for scrub related FIR +/// @tparam MC the memory controller type +/// @tparam T the fapi2::TargetType which hold the FIR bits +/// @param[in] i_target the fapi2::Target +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode after_background_scrub( const fapi2::Target<T>& i_target ); } } diff --git a/src/import/generic/memory/lib/utils/freq/cas_latency.H b/src/import/generic/memory/lib/utils/freq/cas_latency.H index 1b71481e2..0605189c2 100644 --- a/src/import/generic/memory/lib/utils/freq/cas_latency.H +++ b/src/import/generic/memory/lib/utils/freq/cas_latency.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2019 */ +/* Contributors Listed Below - COPYRIGHT 2016,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -571,7 +571,7 @@ class cas_latency l_temp); o_value = l_temp; - FAPI_INF( "%s. tAAmin (ps): %d", + FAPI_DBG( "%s. tAAmin (ps): %d", mss::spd::c_str(l_target), o_value); @@ -628,7 +628,7 @@ class cas_latency { FAPI_TRY( spd::calc_nck(i_taa, i_tck, spd::INVERSE_DDR4_CORRECTION_FACTOR, o_cas_latency) ); - FAPI_INF("%s. tAA (ps): %d, tCK (ps): %d, CL (nck): %d", + FAPI_DBG("%s. tAA (ps): %d, tCK (ps): %d, CL (nck): %d", mss::spd::c_str(iv_target), i_taa, i_tck, @@ -651,8 +651,8 @@ class cas_latency const uint64_t l_min_cl = get_min_cl(l_cl_mask); const uint64_t l_max_cl = get_max_cl(l_cl_mask); - FAPI_INF("%s. min CL %lu", mss::spd::c_str(iv_target), l_min_cl); - FAPI_INF("%s. max CL %lu", mss::spd::c_str(iv_target), l_max_cl); + FAPI_DBG("%s. min CL %lu", mss::spd::c_str(iv_target), l_min_cl); + FAPI_DBG("%s. max CL %lu", mss::spd::c_str(iv_target), l_max_cl); for(uint64_t l_cas_latency = l_min_cl; l_cas_latency <= l_max_cl; ++l_cas_latency) { @@ -688,7 +688,7 @@ class cas_latency { const bool l_found = std::binary_search(i_common_cls.begin(), i_common_cls.end(), i_cas_latency); - FAPI_INF("Found CL: %d in common CL mask: 0x%llX ? %s for %s", + FAPI_DBG("Found CL: %d in common CL mask: 0x%llX ? %s for %s", i_cas_latency, iv_common_cl_bitmap, l_found ? "yes" : "no", mss::spd::c_str(iv_target)); return l_found; @@ -709,7 +709,7 @@ class cas_latency const size_t l_taa_max = (iv_is_3ds == loading::NOT_3DS) ? TAA_MAX_DDR4 : TAA_MAX_DDR4_3DS; const bool l_is_cl_exceeding_taa = l_cas_latency_time > l_taa_max; - FAPI_INF("%s. CL (%d) * tCK (%d) = %d > %d ? %s", + FAPI_DBG("%s. CL (%d) * tCK (%d) = %d > %d ? %s", mss::spd::c_str(iv_target), i_cas_latency, i_tck, @@ -758,7 +758,7 @@ class cas_latency FAPI_TRY( freq_to_ps(l_proposed_freq, io_tck), "%s. Failed freq_to_ps()", mss::spd::c_str(iv_target) ); - FAPI_INF("%s Supported dimm speed override %d MT/s (Clock period %d in ps)", + FAPI_DBG("%s Supported dimm speed override %d MT/s (Clock period %d in ps)", mss::spd::c_str(iv_target), l_proposed_freq, io_tck); // Sanity check diff --git a/src/import/generic/memory/lib/utils/freq/gen_mss_freq.H b/src/import/generic/memory/lib/utils/freq/gen_mss_freq.H index 4df0eeb06..c681f2e59 100644 --- a/src/import/generic/memory/lib/utils/freq/gen_mss_freq.H +++ b/src/import/generic/memory/lib/utils/freq/gen_mss_freq.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018,2019 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -105,7 +105,7 @@ fapi2::ReturnCode get_dimm_type(const fapi2::Target<T>& i_target, /// @return FAPI2_RC_SUCCESS iff ok /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode callout_bad_freq_calculated(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +fapi2::ReturnCode callout_bad_freq_calculated(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const uint64_t i_final_freq); /// @@ -164,7 +164,7 @@ fapi2::ReturnCode is_vpd_config_supported( const fapi2::Target<TT::VPD_TARGET_TY set_MAX(VPD_KW_MAX). set_ACTUAL(io_vpd_info.iv_size). set_KEYWORD(VPD_BLOB). - set_MCS_TARGET(i_target), + set_VPD_TARGET(i_target), "VPD MR keyword size retrieved: %d, is larger than max: %d for %s", io_vpd_info.iv_size, TT::VPD_KEYWORD_MAX, mss::c_str(i_target)); } @@ -208,7 +208,7 @@ fapi2::ReturnCode check_freq_support_vpd( const fapi2::Target<TT::PORT_TARGET_TY /// @return FAPI2_RC_SUCCESS iff ok /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -inline fapi2::ReturnCode set_freq_attrs(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +inline fapi2::ReturnCode set_freq_attrs(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<uint64_t>& i_dimm_freq) { // Find the minimum (but non-0) freq in the vector. If we see all 0's we'll write a 0. However, @@ -245,7 +245,7 @@ fapi_try_exit: /// @return FAPI2_RC_SUCCESS iff okay /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -inline fapi2::ReturnCode spd_supported_freq(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +inline fapi2::ReturnCode spd_supported_freq(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, std::vector<uint32_t>& o_supported_freqs) { uint64_t l_largest_tck = 0; @@ -255,7 +255,17 @@ inline fapi2::ReturnCode spd_supported_freq(const fapi2::Target<TT::FREQ_TARGET_ // Get cached decoder std::vector< mss::spd::facade > l_spd_facades; - FAPI_TRY( get_spd_decoder_list(i_target, l_spd_facades), "%s get decoder - spd", mss::c_str(i_target) ); + + for (const auto& l_port : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target)) + { + std::vector< mss::spd::facade > l_these_spd_facades; + FAPI_TRY( get_spd_decoder_list(l_port, l_these_spd_facades), "%s get decoder - spd", mss::c_str(l_port) ); + + for (const auto& l_spd_facade : l_these_spd_facades) + { + l_spd_facades.push_back(l_spd_facade); + } + } // Looking for the biggest application period on an MC. // This will further reduce supported frequencies the system can run on. @@ -263,7 +273,7 @@ inline fapi2::ReturnCode spd_supported_freq(const fapi2::Target<TT::FREQ_TARGET_ { const auto l_dimm = l_cache.get_dimm_target(); const auto l_port = mss::find_target<TT::PORT_TARGET_TYPE>(l_dimm); - const auto l_port_pos = mss::relative_pos<TT::FREQ_TARGET_TYPE>(l_port); + const auto l_port_pos = mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(l_port); uint64_t l_tckmax_in_ps = 0; uint64_t l_tck_min_in_ps = 0; uint32_t l_dimm_freq = 0; @@ -279,7 +289,7 @@ inline fapi2::ReturnCode spd_supported_freq(const fapi2::Target<TT::FREQ_TARGET_ l_largest_tck = std::min(l_largest_tck, l_tckmax_in_ps); FAPI_TRY( mss::ps_to_freq(l_largest_tck, l_dimm_freq), "%s ps to freq %lu", mss::c_str(i_target), l_largest_tck ); - FAPI_INF("Biggest freq supported from SPD %d MT/s for %s", + FAPI_DBG("Biggest freq supported from SPD %d MT/s for %s", l_dimm_freq, mss::c_str(l_dimm)); o_supported_freqs[l_port_pos] = std::min(l_dimm_freq, o_supported_freqs[l_port_pos]); @@ -299,12 +309,12 @@ fapi_try_exit: /// // Pass in the syncronous target template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode vpd_supported_freqs( const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +fapi2::ReturnCode vpd_supported_freqs( const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, std::vector<std::vector<uint32_t>>& o_vpd_supported_freqs) { // This bitmap will keep track of the ports we visit. // Any we don't are not configured, so will support all frequencies in the scoreboard - fapi2::buffer<uint8_t> configured_ports; + fapi2::buffer<uint64_t> configured_ports; // Clearing output Just.In.Case o_vpd_supported_freqs.clear(); @@ -317,7 +327,8 @@ fapi2::ReturnCode vpd_supported_freqs( const fapi2::Target<TT::FREQ_TARGET_TYPE> // Just go to find target for the port level for( const auto& p : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target) ) { - const auto l_port_pos = mss::relative_pos<TT::FREQ_TARGET_TYPE>(p); + const auto l_port_pos = mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(p); + FAPI_TRY( configured_ports.setBit(l_port_pos) ); if( mss::count_dimm(p) == 0 ) @@ -342,7 +353,7 @@ fapi2::ReturnCode vpd_supported_freqs( const fapi2::Target<TT::FREQ_TARGET_TYPE> // Add supported freqs to our output if (l_supported) { - FAPI_INF("VPD supported freq added: %d for %s", freq, mss::c_str(p) ); + FAPI_DBG("VPD supported freq added: %d for %s", freq, mss::c_str(p) ); o_vpd_supported_freqs[l_port_pos].push_back(freq); } } @@ -381,7 +392,7 @@ fapi_try_exit: /// @return FAPI2_RC_SUCCESS iff okay /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -inline fapi2::ReturnCode find_min_dimm_freq(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +inline fapi2::ReturnCode find_min_dimm_freq(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<uint32_t>& i_supported_freqs, std::vector<uint64_t>& o_min_dimm_freq) { @@ -420,14 +431,14 @@ inline fapi2::ReturnCode find_min_dimm_freq(const fapi2::Target<TT::FREQ_TARGET_ // Find CAS latency using JEDEC algorithm FAPI_TRY( l_cas_latency.find_cl(l_desired_cl, l_tCKmin), "%s failed to find a cas latency", mss::c_str(i_target) ); - FAPI_INF("%s. Result from CL algorithm, CL (nck): %d, tCK (ps): %d", + FAPI_DBG("%s. Result from CL algorithm, CL (nck): %d, tCK (ps): %d", mss::c_str(l_port), l_desired_cl, l_tCKmin); // Find dimm transfer speed from selected tCK FAPI_TRY( mss::ps_to_freq(l_tCKmin, l_desired_freq), "%s. Failed ps_to_freq()", mss::c_str(l_port) ); - FAPI_INF("DIMM speed %d from selected tCK (ps): %d for %s", + FAPI_DBG("DIMM speed %d from selected tCK (ps): %d for %s", l_desired_freq, l_tCKmin, mss::c_str(l_port)); @@ -437,7 +448,7 @@ inline fapi2::ReturnCode find_min_dimm_freq(const fapi2::Target<TT::FREQ_TARGET_ }// end else FAPI_TRY(set_CL_attr<P>(l_port, l_desired_cl), "%s. Failed set_CL_attr()", mss::c_str(l_port) ); - } // mca + } // port fapi_try_exit: return fapi2::current_err; @@ -452,7 +463,7 @@ fapi_try_exit: /// @return FAPI2_RC_SUCCESS iff okay /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -inline fapi2::ReturnCode supported_freqs(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +inline fapi2::ReturnCode supported_freqs(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, std::vector<uint32_t>& o_freqs) { o_freqs.clear(); @@ -462,15 +473,18 @@ inline fapi2::ReturnCode supported_freqs(const fapi2::Target<TT::FREQ_TARGET_TYP std::vector<std::vector<uint32_t>> l_vpd_supported_freqs; std::vector<uint32_t> l_spd_supported_freq; std::vector<uint8_t> l_deconfigured = {0}; + std::vector<fapi2::Target<TT::PORT_TARGET_TYPE>> l_deconfigured_targets; // Retrieve system MRW, SPD, and VPD constraints FAPI_TRY( max_allowed_dimm_freq<P>(l_max_mrw_freqs.data()), "%s max_allowed_dimm_freq", mss::c_str(i_target) ); + FAPI_TRY( spd_supported_freq<P>(i_target, l_spd_supported_freq), "%s spd supported freqs", mss::c_str(i_target) ); + FAPI_TRY( vpd_supported_freqs<P>(i_target, l_vpd_supported_freqs), "%s vpd supported freqs", mss::c_str(i_target) ); - // Limits the frequency by the Nimbus processor constraints (sync mode) + // Limits the frequency by the processor constraints (sync mode) FAPI_TRY( limit_freq_by_processor<P>(i_target, l_scoreboard) ); // Limit frequency scoreboard according to MRW constraints @@ -486,6 +500,7 @@ inline fapi2::ReturnCode supported_freqs(const fapi2::Target<TT::FREQ_TARGET_TYP FAPI_TRY( l_scoreboard.template resolve<P>(i_target, l_vpd_supported_freqs, l_deconfigured, + l_deconfigured_targets, o_freqs) ); FAPI_INF("%s supported freqs:", mss::c_str(i_target)); @@ -510,7 +525,7 @@ namespace check /// @return FAPI2_RC_SUCCESS iff okay /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode final_freq(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target); +fapi2::ReturnCode final_freq(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target); } // check nameespace @@ -522,16 +537,16 @@ fapi2::ReturnCode final_freq(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target /// @return FAPI2_RC_SUCCESS iff okay /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode generate_freq(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target) +fapi2::ReturnCode generate_freq(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target) { std::vector<uint64_t> l_min_dimm_freq; std::vector<uint32_t> l_supported_freqs; - // Get supported freqs for this MCBIST + // Get supported freqs for this domain FAPI_TRY( mss::supported_freqs<P>(i_target, l_supported_freqs), "%s failed to get supported frequencies", mss::c_str(i_target) ); - // Finds the minimum supported DIMM frequencies for this MCBIST + // Finds the minimum supported DIMM frequencies for this domain FAPI_TRY(mss::find_min_dimm_freq<P>(i_target, l_supported_freqs, l_min_dimm_freq), "%s. Failed find_min_dimm_freq()", mss::c_str(i_target) ); diff --git a/src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.H b/src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.H index 3b2f7ded9..94bdcc980 100644 --- a/src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.H +++ b/src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018,2019 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -161,7 +161,6 @@ class freq_scoreboard if ( l_scoreboard_freq > i_freq_limit ) { - FAPI_INF("Removing freq %d on port %d since it's above the limit %d", l_scoreboard_freq, i_port_pos, i_freq_limit); l_port_supported_freqs[l_index] = false; } } @@ -229,7 +228,6 @@ class freq_scoreboard if (l_it == i_freq_list.end()) { - FAPI_INF("Removing freq %d on port %d since it's not supported", iv_freq_values[l_index], i_port_pos); iv_supported_port_freqs[i_port_pos][l_index] = false; } } @@ -308,13 +306,15 @@ class freq_scoreboard /// @param[in] i_target MCBIST target /// @param[in] i_vpd_supported_freqs vector of hardware supported freqs -- from VPD /// @param[out] o_deconfigured vector of port positions that were deconfigured by this function + /// @param[out] o_deconfigured_targets vector of port targets that were deconfigured by this function /// @param[out] o_freqs vector of frequencies supported by all ports /// @return FAPI2_RC_SUCCESS if successful /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> - fapi2::ReturnCode resolve(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, + fapi2::ReturnCode resolve(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<std::vector<uint32_t>>& i_vpd_supported_freqs, std::vector<uint8_t>& o_deconfigured, + std::vector<fapi2::Target<TT::PORT_TARGET_TYPE>>& o_deconfigured_targets, std::vector<uint32_t>& o_freqs) { const auto l_ports = mss::find_targets<TT::PORT_TARGET_TYPE>(i_target); @@ -358,10 +358,10 @@ class freq_scoreboard FAPI_TRY(callout_no_common_freq<P>(i_target, l_common_ports > l_empty_port_count, l_port_count)); // Now find and deconfigure all ports that don't support our selected frequency - FAPI_TRY(deconfigure_ports<P>(i_target, l_ports, l_best_freq_index, o_deconfigured)); + FAPI_TRY(deconfigure_ports<P>(i_target, l_ports, l_best_freq_index, o_deconfigured, o_deconfigured_targets)); // Now find all the frequencies supported by the ports that are left over - FAPI_TRY(this->template resolve<P>(i_target, i_vpd_supported_freqs, o_deconfigured, o_freqs)); + FAPI_TRY(this->template resolve<P>(i_target, i_vpd_supported_freqs, o_deconfigured, o_deconfigured_targets, o_freqs)); #ifndef __HOSTBOOT_MODULE @@ -417,7 +417,7 @@ class freq_scoreboard /// @returh l_supported_vector return the fector of supported frequencies /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> - std::vector<uint64_t> count_supported_frequencies(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, + std::vector<uint64_t> count_supported_frequencies(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<fapi2::Target<TT::PORT_TARGET_TYPE>>& i_ports, std::vector<uint32_t>& o_freqs) { @@ -434,7 +434,7 @@ class freq_scoreboard { if (l_supported[l_index]) { - FAPI_INF("%s Frequency %d is supported by port%d", mss::c_str(i_target), TT::SUPPORTED_FREQS[l_index], l_pos); + FAPI_DBG("%s Frequency %d is supported by port%d", mss::c_str(i_target), TT::SUPPORTED_FREQS[l_index], l_pos); ++l_support_counts[l_index]; } @@ -442,7 +442,7 @@ class freq_scoreboard // Note that deconfigured ports will support all frequencies due to the way the scoreboard is built if (l_support_counts[l_index] == iv_num_ports) { - FAPI_INF("%s Frequency %d is supported by all ports", mss::c_str(i_target), TT::SUPPORTED_FREQS[l_index]); + FAPI_DBG("%s Frequency %d is supported by all ports", mss::c_str(i_target), TT::SUPPORTED_FREQS[l_index]); o_freqs.push_back(TT::SUPPORTED_FREQS[l_index]); } @@ -461,15 +461,18 @@ class freq_scoreboard /// @param[in] i_ports vector of ports /// @param[in] i_best_freq_index index corresponding to the best case frequency /// @param[out] o_deconfigured vector of deconfigured port positions + /// @param[out] o_deconfigured_targets vector of deconfigured targets /// @return FAPI2_RC_SUCCESS iff ok /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> - fapi2::ReturnCode deconfigure_ports(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, + fapi2::ReturnCode deconfigure_ports(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<fapi2::Target<TT::PORT_TARGET_TYPE>>& i_ports, const uint64_t i_best_freq_index, - std::vector<uint8_t>& o_deconfigured) + std::vector<uint8_t>& o_deconfigured, + std::vector<fapi2::Target<TT::PORT_TARGET_TYPE>>& o_deconfigured_targets) { o_deconfigured.clear(); + o_deconfigured_targets.clear(); for ( size_t l_pos = 0; l_pos < iv_num_ports; ++l_pos ) { @@ -478,7 +481,7 @@ class freq_scoreboard i_ports.end(), [l_pos]( const fapi2::Target<TT::PORT_TARGET_TYPE>& i_rhs) -> bool { - return (mss::relative_pos<TT::FREQ_TARGET_TYPE>(i_rhs) != l_pos); + return (mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(i_rhs) == l_pos); }); // If we didn't find an port for a given position, there wasn't one configured there @@ -489,7 +492,7 @@ class freq_scoreboard // and call it out if it doesn't support the selected freq const auto& p = *l_it_port; - FAPI_INF("Checking if port %d (%s) supports common frequency", l_pos, mss::c_str(p)); + FAPI_DBG("Checking if port %d (%s) supports common frequency", l_pos, mss::c_str(p)); if (!iv_supported_port_freqs[l_pos][i_best_freq_index]) { @@ -497,6 +500,7 @@ class freq_scoreboard auto& l_port_supported_freqs = iv_supported_port_freqs[l_pos]; o_deconfigured.push_back(l_pos); + o_deconfigured_targets.push_back(p); FAPI_ASSERT_NOEXIT( false, fapi2::MSS_PORT_DOES_NOT_SUPPORT_MAJORITY_FREQ() .set_FREQ_TARGET(i_target) @@ -526,7 +530,7 @@ class freq_scoreboard /// @return FAPI2_RC_SUCCESS iff ok /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode limit_freq_by_processor(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +fapi2::ReturnCode limit_freq_by_processor(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, freq_scoreboard& io_scoreboard); /// @@ -541,7 +545,7 @@ fapi2::ReturnCode limit_freq_by_processor(const fapi2::Target<TT::FREQ_TARGET_TY /// have a path for testing /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -inline fapi2::ReturnCode limit_freq_by_mrw(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +inline fapi2::ReturnCode limit_freq_by_mrw(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<uint32_t>& i_max_mrw_freqs, freq_scoreboard& io_scoreboard) { @@ -563,18 +567,18 @@ inline fapi2::ReturnCode limit_freq_by_mrw(const fapi2::Target<TT::FREQ_TARGET_T fapi2::MSS_MAX_FREQ_ATTR_SIZE_CHANGED() .set_ACTUAL_SIZE(i_max_mrw_freqs.size()) .set_SUPPOSED_SIZE(NUM_MAX_FREQS) - .set_MCA_TARGET(i_target), + .set_PORT_TARGET(i_target), "%s Incorrect number of max frequencies in attribute for (%d)", mss::c_str(i_target), i_max_mrw_freqs.size()); - FAPI_INF("attribute supported max allowed dimm freqs %d %d %d %d %d for %s", + FAPI_DBG("attribute supported max allowed dimm freqs %d %d %d %d %d for %s", i_max_mrw_freqs[0], i_max_mrw_freqs[1], i_max_mrw_freqs[2], i_max_mrw_freqs[3], i_max_mrw_freqs[4], mss::c_str(i_target)); for( const auto& p : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target) ) { - const auto l_port_pos = mss::relative_pos<TT::FREQ_TARGET_TYPE>(p); + const auto l_port_pos = mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(p); const auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(p); const uint64_t l_dimms_on_port = l_dimms.size(); @@ -612,7 +616,7 @@ inline fapi2::ReturnCode limit_freq_by_mrw(const fapi2::Target<TT::FREQ_TARGET_T // If we have an LRDIMM, it's treated as a one rank DIMM from the memory controller's perspective l_rank_index = l_dimm_type == TT::LRDIMM_TYPE ? 0 : l_num_master_ranks - 1; l_index = l_indexes[l_dimms_on_port - 1][l_rank_index]; - FAPI_INF("%s is %s. rank_index%u index:%u", spd::c_str(d), l_dimm_type == TT::LRDIMM_TYPE ? "LRDIMM" : "RDIMM", + FAPI_DBG("%s is %s. rank_index%u index:%u", spd::c_str(d), l_dimm_type == TT::LRDIMM_TYPE ? "LRDIMM" : "RDIMM", l_rank_index, l_index); FAPI_ASSERT( (l_index < NUM_MAX_FREQS), @@ -625,7 +629,7 @@ inline fapi2::ReturnCode limit_freq_by_mrw(const fapi2::Target<TT::FREQ_TARGET_T l_num_master_ranks, mss::c_str(d)); - FAPI_INF("%s rank config %d drop %d yields max freq attribute index of %d (%d)", + FAPI_DBG("%s rank config %d drop %d yields max freq attribute index of %d (%d)", mss::c_str(d), l_num_master_ranks, l_dimms_on_port, l_indexes[l_dimms_on_port - 1][l_rank_index], i_max_mrw_freqs[l_index] ); @@ -655,7 +659,7 @@ fapi_try_exit: /// have a path for testing /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode limit_freq_by_vpd(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +fapi2::ReturnCode limit_freq_by_vpd(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<std::vector<uint32_t>>& i_hw_freqs, freq_scoreboard& io_scoreboard) { @@ -669,7 +673,7 @@ fapi2::ReturnCode limit_freq_by_vpd(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i for( const auto& p : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target) ) { - const auto l_port_pos = mss::relative_pos<TT::FREQ_TARGET_TYPE>(p); + const auto l_port_pos = mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(p); const auto& l_port_freqs = i_hw_freqs[l_port_pos]; // This is the list of supported frequencies for VPD @@ -709,13 +713,13 @@ fapi_try_exit: /// have a path for testing /// template<mss::proc_type P, typename TT = mss::frequency_traits<P>> -fapi2::ReturnCode limit_freq_by_spd(const fapi2::Target<TT::FREQ_TARGET_TYPE>& i_target, +fapi2::ReturnCode limit_freq_by_spd(const fapi2::Target<TT::FREQ_DOMAIN_TARGET_TYPE>& i_target, const std::vector<uint32_t>& i_hw_freqs, freq_scoreboard& io_scoreboard) { for( const auto& p : mss::find_targets<TT::PORT_TARGET_TYPE>(i_target) ) { - const auto l_port_pos = mss::relative_pos<TT::FREQ_TARGET_TYPE>(p); + const auto l_port_pos = mss::relative_pos<TT::FREQ_DOMAIN_TARGET_TYPE>(p); // Remove any frequencies that aren't in this port's list from the scoreboard io_scoreboard.remove_freqs_above_limit(l_port_pos, i_hw_freqs); diff --git a/src/import/generic/memory/lib/utils/index.H b/src/import/generic/memory/lib/utils/index.H index fde43f711..59b67c187 100644 --- a/src/import/generic/memory/lib/utils/index.H +++ b/src/import/generic/memory/lib/utils/index.H @@ -74,7 +74,24 @@ fapi_try_exit: } /// -/// @brief Return an attribute array index from a rank number +/// @brief Sort the provided target vector in order of index (low to high) +/// +/// @tparam T TargetType +/// @param[in,out] io_targets vector of targets to sort +/// +template <fapi2::TargetType T> +inline void sort_targets_by_index(std::vector<fapi2::Target<T>>& io_targets) +{ + std::sort(io_targets.begin(), io_targets.end(), [] ( + const fapi2::Target<T>& l_first_target, + const fapi2::Target<T>& l_second_target) -> bool + { + return mss::index(l_first_target) < mss::index(l_first_target); + }); +} + +/// +/// @brief Return a dimm rank / attribute array index from a port rank number /// @param[in] i_rank uint64_t a rank number DIMM0 {0, 1, 2, 3} DIMM1 {0, 1, 2, 3} /// @return size_t the attribute array index. /// diff --git a/src/import/generic/memory/lib/utils/mc/gen_mss_port.H b/src/import/generic/memory/lib/utils/mc/gen_mss_port.H index e44601ac2..6c4b4d4b1 100644 --- a/src/import/generic/memory/lib/utils/mc/gen_mss_port.H +++ b/src/import/generic/memory/lib/utils/mc/gen_mss_port.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -38,26 +38,138 @@ #define _GEN_MSS_PORT_H_ #include <fapi2.H> +#include <generic/memory/lib/mss_generic_attribute_getters.H> #include <generic/memory/lib/utils/shared/mss_generic_consts.H> #include <generic/memory/lib/utils/mc/gen_mss_port_traits.H> #include <generic/memory/lib/utils/scom.H> #include <generic/memory/lib/utils/c_str.H> +#include <generic/memory/lib/ecc/ecc.H> +#include <generic/memory/lib/utils/mss_bad_bits.H> +#include <generic/memory/lib/utils/mss_rank.H> namespace mss { /// +/// @brief Reads the farb0q register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[out] o_data data read from the register +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode read_farb0q( const fapi2::Target<T>& i_target, fapi2::buffer<uint64_t>& o_data ) +{ + FAPI_TRY( mss::getScom(i_target, TT::FARB0Q_REG, o_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Writes the farb0q register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[in] i_data data read from the register +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode write_farb0q( const fapi2::Target<T>& i_target, const fapi2::buffer<uint64_t>& i_data ) +{ + FAPI_TRY( mss::putScom(i_target, TT::FARB0Q_REG, i_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reads the farb6q register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[out] o_data data read from the register +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode read_farb6q( const fapi2::Target<T>& i_target, fapi2::buffer<uint64_t>& o_data ) +{ + FAPI_TRY( mss::getScom(i_target, TT::FARB6Q_REG, o_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Writes the farb6q register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[in] i_data data read from the register +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode write_farb6q( const fapi2::Target<T>& i_target, const fapi2::buffer<uint64_t>& i_data ) +{ + FAPI_TRY( mss::putScom(i_target, TT::FARB6Q_REG, i_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Gets the bandwidth window data +/// @tparam MC the memory controller type +/// @tparam TT the class traits for the port +/// @param[in] i_data data read from the register +/// @param[out] o_bw_window +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = portTraits<MC> > +void get_bw_window( const fapi2::buffer<uint64_t>& i_data, uint64_t& o_bw_window ) +{ + o_bw_window = 0; + i_data.extractToRight<TT::BW_WINDOW_SIZE, TT::BW_WINDOW_SIZE_LEN>(o_bw_window); +} + +/// +/// @brief Gets the bandwidth snapshot +/// @tparam MC the memory controller type +/// @tparam TT the class traits for the port +/// @param[in] i_data data read from the register +/// @param[out] o_bw_snapshot +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note Disable Port Fail after recurring RCD errors. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = portTraits<MC> > +void get_bw_snapshot( const fapi2::buffer<uint64_t>& i_data, uint64_t& o_bw_snapshot ) +{ + o_bw_snapshot = 0; + i_data.extractToRight<TT::BW_SNAPSHOT, TT::BW_SNAPSHOT_LEN>(o_bw_snapshot); +} + + +/// /// @brief ATTR_MSS_MVPD_FWMS getter declare /// @tparam MC the memory controller type /// @tparam T the fapi2 target type of the target /// @param[in] const ref to the fapi2::Target<fapi2::TargetType> /// @param[out] uint32_t* memory to store the value /// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK -/// @note Mark store records from MPVD Lx -/// keyword +/// @note Mark store records from MPVD Lx keyword /// template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T> -inline fapi2::ReturnCode mvpd_fwms(const fapi2::Target< T>& i_target, uint32_t (&o_array)[MARK_STORE_COUNT]); +fapi2::ReturnCode mvpd_fwms(const fapi2::Target< T>& i_target, uint32_t (&o_array)[MARK_STORE_COUNT]); /// /// @brief Enable power management @@ -111,9 +223,9 @@ fapi2::ReturnCode change_iml_complete( const fapi2::Target<T>& i_target, states FAPI_DBG("Change the IML init complete bit to high for %s %s", (i_state == HIGH ? "high" : "low"), mss::c_str(i_target)); - FAPI_TRY( mss::getScom(i_target, TT::FARB6Q_REG, l_data) ); + FAPI_TRY( mss::getScom(i_target, TT::PMU8Q_REG, l_data) ); l_data.writeBit<TT::CFG_INIT_COMPLETE>(i_state); - FAPI_TRY( mss::putScom(i_target, TT::FARB6Q_REG, l_data) ); + FAPI_TRY( mss::putScom(i_target, TT::PMU8Q_REG, l_data) ); fapi_try_exit: return fapi2::current_err; @@ -293,6 +405,19 @@ fapi_try_exit: /// +/// @brief Change the state of the force_str bit +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[in] i_state the state +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode change_force_str( const fapi2::Target<T>& i_target, const states i_state ); + + +/// /// @brief Change the state of the MC Refresh enable bit /// @tparam MC the memory controller type /// @tparam T the fapi2 target type of the target @@ -339,6 +464,19 @@ fapi_try_exit: } /// +/// @brief Set up memory controller specific settings for ECC registers (at the end of draminit_mc) +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the target +/// @param[in,out] io_data contents of RECR register +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode ecc_reg_settings_draminit_mc( const fapi2::Target<T>& i_target, + fapi2::buffer<uint64_t>& io_data ); + +/// /// @brief Enable Read ECC checking /// @tparam MC the memory controller type /// @tparam T the fapi2 target type of the target @@ -346,9 +484,11 @@ fapi_try_exit: /// @param[in] i_target the target /// @return FAPI2_RC_SUCCESS if and only if ok /// -template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +template< mss::mc_type MC, fapi2::TargetType T, typename TT = portTraits<MC> > fapi2::ReturnCode enable_read_ecc( const fapi2::Target<T>& i_target ) { + constexpr uint8_t RECR_MBSECCQ_DATA_INVERSION_NO_INVERSION = 0b00; + constexpr uint8_t RECR_MBSECCQ_DATA_INVERSION_INVERT_DATA_TOGGLE_CHECKS = 0b11; fapi2::buffer<uint64_t> l_data; uint8_t l_sim = 0; @@ -362,13 +502,17 @@ fapi2::ReturnCode enable_read_ecc( const fapi2::Target<T>& i_target ) // VBU tests assume good ECC and we don't have good ECC (since we're not writing everything) // so we can't run with address checking. Disable address checking in sim. - l_data.writeBit<TT::ECC_USE_ADDR_HASH>(l_sim ? 0 : 1); + l_data.writeBit<TT::ECC_USE_ADDR_HASH>(l_sim ? mss::states::LOW : mss::states::HIGH); - // The preferred operating mode is 11 (INVERT_DATA_TOGGLE_CHECKS) which stores data complemented - // (because most bits are '0', and the dram bus pulls up, so transmitting 1s is least power) but + // The preferred operating mode is 11 (INVERT_DATA_TOGGLE_CHECKS) which stores data complemented + // (because most bits are '0', and the dram bus pulls up, so transmitting 1s is least power) but // still flips the inversion of check bits to aid RAS. Per Brad Michael 12/15 // Leave un-inverted for sim. This allows the DIMM loader to write 0's and effect good ECC - l_data.insertFromRight<TT::RECR_MBSECCQ_DATA_INVERSION, TT::RECR_MBSECCQ_DATA_INVERSION_LEN>(l_sim ? 0b00 : 0b11); + l_data.insertFromRight<TT::RECR_MBSECCQ_DATA_INVERSION, TT::RECR_MBSECCQ_DATA_INVERSION_LEN>(l_sim ? + RECR_MBSECCQ_DATA_INVERSION_NO_INVERSION : + RECR_MBSECCQ_DATA_INVERSION_INVERT_DATA_TOGGLE_CHECKS); + + FAPI_TRY( ecc_reg_settings_draminit_mc<MC>(i_target, l_data) ); // bits: 60 MBSTRQ_CFG_MAINT_RCE_WITH_CE // cfg_maint_rce_with_ce - not implemented. Need to investigate if needed for nimbus. @@ -412,6 +556,800 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Configures the write reorder queue bit +/// @tparam MC the memory controller type +/// @tparam T, the mc +/// @tparam TT, the class traits for the port +/// @param[in] i_target the target to effect +/// @param[in] i_state to set the bit too +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +inline fapi2::ReturnCode configure_wrq(const fapi2::Target<T>& i_target, + const mss::states i_state) +{ + // Loops through all port targets, hitting all the registers + for( const auto& l_port : mss::find_targets<TT::PORT_TYPE>(i_target) ) + { + fapi2::buffer<uint64_t> l_data; + + // Gets the reg + FAPI_TRY(mss::getScom(l_port, TT::WRQ_REG, l_data), "%s failed to getScom from WRQ0Q", mss::c_str(l_port)); + + // Sets the bit + l_data.writeBit<TT::WRQ_FIFO_MODE>(i_state == mss::states::ON); + + // Sets the regs + FAPI_TRY(mss::putScom(l_port, TT::WRQ_REG, l_data), "%s failed to putScom to WRQ0Q", mss::c_str(l_port)); + } + + // In case we don't have any port's + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Configures the read reorder queue bit +/// @tparam MC the memory controller type +/// @tparam T, the mc +/// @tparam TT, the class traits for the port +/// @param[in] i_target the target to effect +/// @param[in] i_state to set the bit too +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +inline fapi2::ReturnCode configure_rrq(const fapi2::Target<T>& i_target, const mss::states i_state) +{ + // Loops through all port targets, hitting all the registers + for( const auto& l_port : mss::find_targets<TT::PORT_TYPE>(i_target) ) + { + fapi2::buffer<uint64_t> l_data; + + // Gets the reg + FAPI_TRY(mss::getScom(l_port, TT::RRQ_REG, l_data), "%s failed to getScom from RRQ0Q", mss::c_str(l_port)); + + // Sets the bit + l_data.writeBit<TT::RRQ_FIFO_MODE>(i_state == mss::states::ON); + + // Sets the regs + FAPI_TRY(mss::putScom(l_port, TT::RRQ_REG, l_data), "%s failed to putScom to RRQ0Q", mss::c_str(l_port)); + } + + // In case we don't have any port's + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Place a symbol mark in a Firmware Mark Store register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the DIMM target +/// @param[in] i_rank the rank +/// @param[in] i_dq the bad DQ bit +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +inline fapi2::ReturnCode place_symbol_mark(const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq) +{ + const auto& l_port = mss::find_target<TT::PORT_TYPE>(i_target); + const auto l_dimm_idx = mss::index(i_target); + const auto l_rank_idx = mss::index(i_rank); + + uint8_t l_galois = 0; + mss::mcbist::address l_addr; + + // For symbol marks, we set the appropriate Firmware Mark Store reg, with the symbol's + // Galois code, mark_type=SYMBOL, mark_region=MRANK, and the address of the DIMM+MRANK + // TODO RTC:165133 Remove static_cast once Galois API is updated to accept uint64_t input + FAPI_TRY( mss::ecc::dq_to_galois(static_cast<uint8_t>(i_dq), l_galois) ); + + l_addr.set_dimm(l_dimm_idx).set_master_rank(l_rank_idx); + + FAPI_INF("%s Setting firmware symbol mark on rank:%d dq:%d galois:0x%02x", + mss::c_str(i_target), i_rank, i_dq, l_galois); + FAPI_TRY( mss::ecc::set_fwms(l_port, i_rank, l_galois, + mss::ecc::fwms::mark_type::SYMBOL, + mss::ecc::fwms::mark_region::MRANK, + l_addr) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Place a chip mark in a Hardware Mark Store register +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @tparam TT the class traits for the port +/// @param[in] i_target the DIMM target +/// @param[in] i_rank the rank +/// @param[in] i_dq one of the bad DQ bits in the bad nibble +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +inline fapi2::ReturnCode place_chip_mark(const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq) +{ + const auto& l_port = mss::find_target<TT::PORT_TYPE>(i_target); + + uint8_t l_galois = 0; + uint8_t l_symbol = 0; + + // For chip marks, we set the appropriate Hardware Mark Store reg, with the Galois code + // of the first (smallest) symbol in the bad nibble, and both confirmed and exit1 bits set + FAPI_TRY( mss::ecc::dq_to_symbol(static_cast<uint8_t>(i_dq), l_symbol) ); + + // Round down to the nearest "nibble" to get the correct symbol, then get the Galois code for it + l_symbol = (l_symbol / BITS_PER_NIBBLE) * BITS_PER_NIBBLE; + FAPI_TRY( mss::ecc::symbol_to_galois(l_symbol, l_galois) ); + + FAPI_INF("%s Setting hardware (chip) mark on rank:%d galois:0x%02x", mss::c_str(i_target), i_rank, l_galois); + FAPI_TRY( mss::ecc::set_hwms(l_port, i_rank, l_galois) ); + +fapi_try_exit: + return fapi2::current_err; +} + +// Forward declaration for use in repair_state classes +template< fapi2::TargetType T > +class repair_state_machine; + +/// +/// @class mss::repair_state +/// @brief A class for keeping track of bad bit repair states in a repair_state_machine +/// @tparam T, the fapi2 target type of the DIMM +/// @note this is a base class +/// +template< fapi2::TargetType T > +class repair_state +{ + public: + /// @brief default contructor + repair_state() = default; + /// @brief default destructor + virtual ~repair_state() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + virtual fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) = 0; + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + virtual fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) = 0; + + protected: + /// + /// @brief Set a new state in the repair state machine + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_state pointer to the new state to set + /// + inline void set_state(repair_state_machine<T>& io_machine, std::shared_ptr<repair_state<T>> i_state) + { + io_machine.update_state(i_state); + } +}; + +/// +/// @class mss::chip_and_symbol_mark +/// @brief repair_state class for when both a chip mark and a symbol mark have been used +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class chip_and_symbol_mark : public repair_state<T> +{ + public: + /// @brief default contructor + chip_and_symbol_mark() = default; + /// @brief default destructor + ~chip_and_symbol_mark() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // repairs exceeded + FAPI_TRY( io_repairs_exceeded.setBit(mss::index(i_target)) ); + FAPI_INF("%s Repairs exceeded (chip mark and symbol mark exist, plus one bad DQ) on rank:%d DQ:%d", + mss::c_str(i_target), i_rank, i_dq); + return fapi2::FAPI2_RC_SUCCESS; + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // repairs exceeded + FAPI_TRY( io_repairs_exceeded.setBit(mss::index(i_target)) ); + FAPI_INF("%s Repairs exceeded (chip mark and symbol mark exist, plus one bad nibble) on rank:%d DQ:%d", + mss::c_str(i_target), i_rank, i_dq); + return fapi2::FAPI2_RC_SUCCESS; + fapi_try_exit: + return fapi2::current_err; + } +}; + +/// +/// @class mss::symbol_mark_plus_unrepaired_dq +/// @brief repair_state class for when only a symbol mark has been used, and one DQ bit remains unrepaired +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class symbol_mark_plus_unrepaired_dq : public repair_state<T> +{ + public: + /// @brief default contructor + symbol_mark_plus_unrepaired_dq() = default; + /// @brief default destructor + ~symbol_mark_plus_unrepaired_dq() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // repairs exceeded + FAPI_INF("%s Repairs exceeded (symbol mark and unrepaired DQ exist, plus bad DQ) on rank:%d DQ:%d", + mss::c_str(i_target), i_rank, i_dq); + FAPI_TRY( io_repairs_exceeded.setBit(mss::index(i_target)) ); + return fapi2::FAPI2_RC_SUCCESS; + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // place a chip mark, but also repairs exceeded + FAPI_TRY( place_chip_mark(i_target, i_rank, i_dq) ); + FAPI_TRY( io_repairs_applied.setBit(i_rank) ); + FAPI_TRY( io_repairs_exceeded.setBit(mss::index(i_target)) ); + FAPI_INF("%s Repairs exceeded (symbol mark and unrepaired DQ exist, plus bad nibble) on rank:%d DQ:%d", + mss::c_str(i_target), i_rank, i_dq); + { + const auto new_state = std::make_shared<chip_and_symbol_mark<fapi2::TARGET_TYPE_DIMM>>(); + mss::repair_state<T>::set_state(io_machine, new_state); + } + fapi_try_exit: + return fapi2::current_err; + } + +}; + +/// +/// @class mss::symbol_mark_only +/// @brief repair_state class for when only a symbol mark has been used +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class symbol_mark_only : public repair_state<T> +{ + public: + /// @brief default contructor + symbol_mark_only() = default; + /// @brief default destructor + ~symbol_mark_only() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // leave an unrepaired DQ + const auto new_state = std::make_shared< symbol_mark_plus_unrepaired_dq<T> >(); + mss::repair_state<T>::set_state(io_machine, new_state); + return fapi2::FAPI2_RC_SUCCESS; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // place a chip mark + FAPI_TRY( place_chip_mark(i_target, i_rank, i_dq) ); + FAPI_TRY( io_repairs_applied.setBit(i_rank) ); + { + const auto new_state = std::make_shared< chip_and_symbol_mark<T> >(); + mss::repair_state<T>::set_state(io_machine, new_state); + } + fapi_try_exit: + return fapi2::current_err; + } +}; + +/// +/// @class mss::chip_mark_only +/// @brief repair_state class for when only a chip mark has been used +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class chip_mark_only : public repair_state<T> +{ + public: + /// @brief default contructor + chip_mark_only() = default; + /// @brief default destructor + ~chip_mark_only() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // place a symbol mark + FAPI_TRY( place_symbol_mark(i_target, i_rank, i_dq) ); + FAPI_TRY( io_repairs_applied.setBit(i_rank) ); + { + const auto new_state = std::make_shared<chip_and_symbol_mark<fapi2::TARGET_TYPE_DIMM>>(); + mss::repair_state<T>::set_state(io_machine, new_state); + } + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // repairs exceeded + FAPI_TRY( io_repairs_exceeded.setBit(mss::index(i_target)) ); + FAPI_INF("%s Repairs exceeded (chip mark exists, plus bad nibble) on rank:%d DQ:%d", + mss::c_str(i_target), i_rank, i_dq); + return fapi2::FAPI2_RC_SUCCESS; + fapi_try_exit: + return fapi2::current_err; + } +}; + +/// +/// @class mss::no_fails +/// @brief repair_state class for no fails (no marks applied) +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class no_fails : public repair_state<T> +{ + public: + /// @brief default contructor + no_fails() = default; + /// @brief default destructor + ~no_fails() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // place a symbol mark + FAPI_TRY( place_symbol_mark(i_target, i_rank, i_dq) ); + FAPI_TRY( io_repairs_applied.setBit(i_rank) ); + { + const auto new_state = std::make_shared<symbol_mark_only<fapi2::TARGET_TYPE_DIMM>>(); + mss::repair_state<T>::set_state(io_machine, new_state); + } + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in,out] io_machine the repair state machine + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(repair_state_machine<T>& io_machine, + const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + // place a chip mark + FAPI_TRY( place_chip_mark(i_target, i_rank, i_dq) ); + FAPI_TRY( io_repairs_applied.setBit(i_rank) ); + { + const auto new_state = std::make_shared<chip_mark_only<fapi2::TARGET_TYPE_DIMM>>(); + mss::repair_state<T>::set_state(io_machine, new_state); + } + fapi_try_exit: + return fapi2::current_err; + } +}; + +/// +/// @class mss::repair_state_machine +/// @brief state machine class used in restore_repairs_helper +/// @tparam T, the fapi2 target type of the DIMM +/// +template< fapi2::TargetType T > +class repair_state_machine +{ + public: + /// @brief constructor + repair_state_machine() + : iv_repair_state(std::make_shared<no_fails<T>>()) {} + + /// @brief default destructor + ~repair_state_machine() = default; + + /// + /// @brief Perform a repair for a single bad DQ bit in a nibble + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq the DQ bit index + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repai:rs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode one_bad_dq(const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + FAPI_TRY( iv_repair_state->one_bad_dq(*this, i_target, i_rank, i_dq, io_repairs_applied, io_repairs_exceeded) ); + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Perform a repair for multiple bad DQ bits in a nibble + /// @param[in] i_target the DIMM target + /// @param[in] i_rank the rank + /// @param[in] i_dq one of the bad DQ bit indexes + /// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied + /// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired + /// @return FAPI2_RC_SUCCESS if and only if ok + /// + fapi2::ReturnCode multiple_bad_dq(const fapi2::Target<T>& i_target, + const uint64_t i_rank, + const uint64_t i_dq, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) + { + FAPI_TRY( iv_repair_state->multiple_bad_dq(*this, i_target, i_rank, i_dq, io_repairs_applied, io_repairs_exceeded) ); + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Update the state of the state machine + /// @param[in] i_state shared pointer to the new state + /// + void update_state(std::shared_ptr<repair_state<T>> i_state) + { + iv_repair_state = i_state; + } + + private: + std::shared_ptr<repair_state<T>> iv_repair_state; +}; + +/// @brief Get the attributes for the reorder queue setting +/// @tparam MC the memory controller type +/// @tparam T, the fapi2 target type of the target +/// @param[in] const ref to the mc target +/// @param[out] uint8_t& reference to store the value +/// @return fapi2::ReturnCode - FAPI2_RC_SUCCESS iff get is OK +/// @note Contains the settings for write/read reorder queue +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode reorder_queue_setting(const fapi2::Target<T>& i_target, uint8_t& o_value); + +/// +/// @brief Resets the write/read reorder queue values - needs to be called after MCBIST execution +/// @tparam MC the memory controller type +/// @tparam T, the mc +/// @tparam TT, the class traits for the port +/// @param[in] i_target the target to effect +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = portTraits<MC> > +inline fapi2::ReturnCode reset_reorder_queue_settings(const fapi2::Target<T>& i_target) +{ + uint8_t l_reorder_queue = 0; + FAPI_TRY(reorder_queue_setting<MC>(i_target, l_reorder_queue)); + + // Changes the reorder queue settings + { + // Two settings are FIFO and REORDER. FIFO is a 1 in the registers, while reorder is a 0 state + const mss::states l_state = ((l_reorder_queue == fapi2::ENUM_ATTR_MEM_REORDER_QUEUE_SETTING_FIFO) ? + mss::states::ON : mss::states::OFF); + FAPI_TRY(configure_rrq(i_target, l_state), "%s failed to reset read reorder queue settings", mss::c_str(i_target)); + FAPI_TRY(configure_wrq(i_target, l_state), "%s failed to reset read reorder queue settings", mss::c_str(i_target)); + } + + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Convert a bitmap from the BAD_DQ_BITMAP attribute to a vector of bad DQ indexes +/// @param[in] i_bad_bits an 8-bit bitmap of bad bits +/// @param[in] i_nibble which nibble of the bitmap to convert +/// @return std::vector of DQ bits marked as bad in the bitmap +/// +inline std::vector<uint64_t> bad_bit_helper(const uint8_t i_bad_bits, const size_t i_nibble) +{ + std::vector<uint64_t> l_output; + fapi2::buffer<uint8_t> l_bit_buffer(i_bad_bits); + + const size_t l_start = (i_nibble == 0) ? 0 : mss::conversions::BITS_PER_NIBBLE; + + for (size_t l_offset = 0; l_offset < mss::conversions::BITS_PER_NIBBLE; ++l_offset) + { + const size_t l_position_tmp = l_start + l_offset; + + if (l_bit_buffer.getBit(l_position_tmp)) + { + l_output.push_back(l_position_tmp); + } + } + + return l_output; +} + +// TODO: RTC: 157753 tparam R can be pulled from an PORT trait once we have it +// +/// @brief Restore symbol and chip marks according to BAD_DQ_BITMAP attribute, helper function for unit testing +/// @tparam MC the memory controller type +/// @tparam T, the fapi2 target type of the DIMM (derived) +/// @tparam R the maximum rank per DIMM +/// @tparam B the number of bytes per rank in the bad_dq_bitmap attribute +/// @param[in] i_target A target representing a DIMM +/// @param[in] i_bad_bits the bad bits values from the VPD, for the specified DIMM +/// @param[in,out] io_repairs_applied 8-bit mask, where a bit set means that rank had repairs applied +/// @param[in,out] io_repairs_exceeded 2-bit mask, where a bit set means that DIMM had more bad bits than could be repaired +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, uint64_t R, uint64_t B > +inline fapi2::ReturnCode restore_repairs_helper( const fapi2::Target<T>& i_target, + const uint8_t i_bad_bits[R][B], + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) +{ + FAPI_INF("%s Restore repair marks from bad DQ data", mss::c_str(i_target)); + + using MCT = mss::mcbistMCTraits<MC>; + using MT = mss::mcbistTraits<MC, MCT::MC_TARGET_TYPE>; + + std::vector<uint64_t> l_ranks; + const auto l_dimm_idx = index(i_target); + + // gets all of the ranks to loop over + FAPI_TRY( mss::rank::ranks_on_dimm_helper<MC>(i_target, l_ranks) ); + + // loop through ranks + for (const auto l_rank : l_ranks) + { + const auto l_rank_idx = index(l_rank); + + repair_state_machine<fapi2::TARGET_TYPE_DIMM> l_machine; + + for (uint64_t l_byte = 0; l_byte < (MT::MAX_DQ_NIBBLES / mss::conversions::NIBBLES_PER_BYTE); ++l_byte) + { + for (size_t l_nibble = 0; l_nibble < mss::conversions::NIBBLES_PER_BYTE; ++l_nibble) + { + const auto l_bad_dq_vector = bad_bit_helper(i_bad_bits[l_rank_idx][l_byte], l_nibble); + FAPI_DBG("Total bad bits on DIMM:%d rank:%d nibble%d: %d", + l_dimm_idx, l_rank, (l_byte * NIBBLES_PER_BYTE) + l_nibble, l_bad_dq_vector.size()); + + // apply repairs and update repair machine state + // if there are no bad bits (l_bad_dq_vector.size() == 0) no action is necessary + if (l_bad_dq_vector.size() == 1) + { + // l_bad_dq_vector is per byte, so multiply up to get the bad dq's index + const uint64_t l_dq = l_bad_dq_vector[0] + (l_byte * BITS_PER_BYTE); + FAPI_TRY( l_machine.one_bad_dq(i_target, l_rank, l_dq, io_repairs_applied, io_repairs_exceeded) ); + } + else if (l_bad_dq_vector.size() > 1) + { + // l_bad_dq_vector is per byte, so multiply up to get the bad dq's index + const uint64_t l_dq = l_bad_dq_vector[0] + (l_byte * BITS_PER_BYTE); + FAPI_TRY( l_machine.multiple_bad_dq(i_target, l_rank, l_dq, io_repairs_applied, io_repairs_exceeded) ); + } + + // if repairs have been exceeded, we're done + if (io_repairs_exceeded.getBit(l_dimm_idx)) + { + FAPI_INF("Repairs exceeded on DIMM %s", c_str(i_target)); + return fapi2::FAPI2_RC_SUCCESS; + } + } // end loop through nibbles + } // end loop through bytes + } // end loop through ranks + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Restore symbol and chip marks according to BAD_DQ_BITMAP attribute +/// @tparam MC, the memory controller type +/// @tparam T, the fapi2 target type of the port (derived) +/// @param[in] i_target A target representing a port +/// @param[in,out] io_repairs_applied bit mask, where a bit set means a rank had repairs applied (bit0 = rank0, etc) +/// @param[in,out] io_repairs_exceeded bit mask, where a bit set means a DIMM had more bad bits than could be repaired (bit0 = DIMM0 etc) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +inline fapi2::ReturnCode restore_repairs( const fapi2::Target<T>& i_target, + fapi2::buffer<uint8_t>& io_repairs_applied, + fapi2::buffer<uint8_t>& io_repairs_exceeded) +{ + uint8_t l_bad_bits[BAD_BITS_RANKS][BAD_DQ_BYTE_COUNT] = {}; + + io_repairs_applied = 0; + io_repairs_exceeded = 0; + + for (const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(i_target)) + { + FAPI_TRY( mss::get_bad_dq_bitmap<MC>(l_dimm, l_bad_bits) ); + + FAPI_TRY( (restore_repairs_helper<MC, fapi2::TARGET_TYPE_DIMM, BAD_BITS_RANKS, BAD_DQ_BYTE_COUNT>( + l_dimm, l_bad_bits, io_repairs_applied, io_repairs_exceeded)) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + }// ns mss #endif diff --git a/src/import/generic/memory/lib/utils/mcbist/address.H b/src/import/generic/memory/lib/utils/mcbist/address.H deleted file mode 100644 index af12f7144..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/address.H +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/address.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_address.H b/src/import/generic/memory/lib/utils/mcbist/gen_address.H deleted file mode 100644 index 45c4a4767..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/gen_address.H +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_address.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist.H index 0c5543750..667be0598 100644 --- a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist.H +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ +/* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -22,3 +22,3830 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist.H +/// @brief Run and manage the MCBIST engine +/// +// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_H_ +#define _GEN_MSS_MCBIST_H_ + +#include <fapi2.H> + +#include <generic/memory/lib/utils/poll.H> +#include <generic/memory/lib/utils/memory_size.H> +#include <generic/memory/lib/utils/mc/gen_mss_port.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H> +#include <generic/memory/lib/utils/conversions.H> +#include <generic/memory/lib/utils/num.H> + +namespace mss +{ + +/// +/// @brief Gets the attribute for freq +/// @tparam MC the memory controller type +/// @tparam T the fapi2 target type of the target +/// @param[in] const ref to the target +/// @param[out] uint64_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 Frequency of this memory channel in MT/s (Mega Transfers per second) +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T> +fapi2::ReturnCode freq(const fapi2::Target<T>& i_target, uint64_t& o_value); + +/// +/// @brief Return the estimated time an MCBIST subtest will take to complete +/// Useful for initial polling delays, probably isn't accurate for much else +/// as it doesn't take refresh in to account (which will necessarily slow down +/// the program.) +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType +/// @param[in] i_target the target from which to gather memory frequency +/// @param[in] i_bytes number of *bytes* in the address range +/// @param[in] i_64B_per mss::YES if the command is 64B, mss::NO if it's 128B. Defaults to mss::YES +/// @return the initial polling delay for this program in ns +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +inline uint64_t calculate_initial_delay(const fapi2::Target<T>& i_target, + const uint64_t i_bytes, + const bool i_64B_per = mss::YES) +{ + // TODO RTC: 164104 Update MCBIST delay calculator. As we learn more about what + // the lab really needs, we can probably make this function better. + const uint64_t l_bytes_per_cmd = (i_64B_per == mss::YES) ? 64 : 128; + + // Best case is a command takes 4 cycles. Given the number of commands and address space size + // we can get some idea of how long to wait before we start polling. + return cycles_to_ns(i_target, (i_bytes / l_bytes_per_cmd) * mss::CYCLES_PER_CMD); +} + + +/// +/// @brief Reads the contents of the MCBISTFIRMASK +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - derived +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in] i_target the target on which to operate +/// @param[out] o_data the register data +/// @return fapi2::fapi2_rc_success if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode read_mcbfirmask( const fapi2::Target<T>& i_target, fapi2::buffer<uint64_t>& o_data ) +{ + o_data = 0; + + FAPI_TRY( fapi2::getScom(i_target, TT::MCBFIRMASK_REG, o_data ), "%s failed to read MCBISTFIRMASK regiser", + mss::c_str(i_target)); + FAPI_DBG("%s MCBISTFIRMASK has data 0x%016lx", mss::c_str(i_target), o_data); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Writes the contents of the MCBISTFIRMASK +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - derived +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in] i_target the target on which to operate +/// @param[in] i_data the register data +/// @return fapi2::fapi2_rc_success if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode write_mcbfirmask( const fapi2::Target<T>& i_target, const fapi2::buffer<uint64_t>& i_data ) +{ + FAPI_TRY( fapi2::putScom(i_target, TT::MCBFIRMASK_REG, i_data ), "%s failed to write MCBISTFIRMASK regiser", + mss::c_str(i_target)); + FAPI_DBG("%s MCBISTFIRMASK has data 0x%016lx", mss::c_str(i_target), i_data); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reads the contents of the MCBISTFIRQ +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - derived +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in] i_target the target on which to operate +/// @param[out] o_data the register data +/// @return fapi2::fapi2_rc_success if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode read_mcbfirq( const fapi2::Target<T>& i_target, fapi2::buffer<uint64_t>& o_data ) +{ + o_data = 0; + + FAPI_TRY( fapi2::getScom(i_target, TT::MCBFIRQ_REG, o_data ), "%s failed to read MCBISTFIRQ regiser", + mss::c_str(i_target)); + FAPI_DBG("%s MCBISTFIRQ has data 0x%016lx", mss::c_str(i_target), o_data); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Writes the contents of the MCBISTFIRQ +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - derived +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in] i_target the target on which to operate +/// @param[in] i_data the register data +/// @return fapi2::fapi2_rc_success if ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode write_mcbfirq( const fapi2::Target<T>& i_target, const fapi2::buffer<uint64_t>& i_data ) +{ + FAPI_TRY( fapi2::putScom(i_target, TT::MCBFIRQ_REG, i_data ), "%s failed to write MCBISTFIRQ regiser", + mss::c_str(i_target)); + FAPI_DBG("%s MCBISTFIRQ has data 0x%016lx", mss::c_str(i_target), i_data); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Sets the mask for program complete +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - defaults to TARGET_TYPE_MCBIST +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in,out] io_data the value of the register +/// @param[in] i_state the state to write into the enable +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = fapi2::TARGET_TYPE_MCBIST, typename TT = mcbistTraits<MC, T> > +inline void set_mcbist_program_complete_mask( fapi2::buffer<uint64_t>& io_data, const mss::states i_state ) +{ + io_data.writeBit<TT::MCB_PROGRAM_COMPLETE_MASK>(i_state == mss::states::ON); + FAPI_DBG("set_mcbist_program_complete_mask to %d 0x%016lx", i_state, io_data); +} + +/// +/// @brief Sets the mask for WAT debug ATTN +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - defaults to TARGET_TYPE_MCBIST +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in,out] io_data the value of the register +/// @param[in] i_state the state to write into the enable +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = fapi2::TARGET_TYPE_MCBIST, typename TT = mcbistTraits<MC, T> > +inline void set_mcbist_wat_debug_attn_mask( fapi2::buffer<uint64_t>& io_data, const mss::states i_state ) +{ + io_data.writeBit<TT::MCB_WAT_DEBUG_ATTN_MASK>(i_state == mss::states::ON); + FAPI_DBG("set_mcbist_wat_debug_attn_mask to %d 0x%016lx", i_state, io_data); +} + +/// +/// @brief Clears the program complete and WAT debug ATTN +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type - defaults to TARGET_TYPE_MCBIST +/// @tparam TT traits type defaults to mcbistTraits<MC, T> +/// @param[in,out] io_data the value of the register +/// @param[in] i_state the state to write into the enable +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = fapi2::TARGET_TYPE_MCBIST, typename TT = mcbistTraits<MC, T> > +inline void clear_mcbist_program_complete( fapi2::buffer<uint64_t>& io_data ) +{ + io_data.writeBit<TT::MCB_PROGRAM_COMPLETE>(mss::states::OFF); + io_data.writeBit<TT::MCB_WAT_DEBUG_ATTN>(mss::states::OFF); + FAPI_DBG("clear_mcbist_program_complete to %d 0x%016lx", mss::states::OFF, io_data); +} + +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = fapi2::TARGET_TYPE_MCBIST, typename TT = mcbistTraits<MC, T> > +inline void get_mcbist_program_complete_mask( const fapi2::buffer<uint64_t> i_data, mss::states& o_state ) +{ + o_state = i_data.getBit<TT::MCB_PROGRAM_COMPLETE>() ? mss::states::HIGH : mss::states::LOW; + FAPI_DBG("get_mcbist_program_complete_mask %d", o_state); +} + +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = fapi2::TARGET_TYPE_MCBIST, typename TT = mcbistTraits<MC, T> > +inline void get_mcbist_wat_debug_attn_mask( const fapi2::buffer<uint64_t> i_data, mss::states& o_state ) +{ + o_state = i_data.getBit<TT::MCB_WAT_DEBUG_ATTN>() ? mss::states::HIGH : mss::states::LOW; + FAPI_DBG("mcbist_wat_debug_attn_mask %d", o_state); +} + + +namespace mcbist +{ + +/// +/// @class subtest_t +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType +/// @tparam TT the mcbistTraits associated with T +/// @brief encapsulation of an MCBIST subtest. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +class subtest_t +{ + public: + + /// + /// @brief Constructor + /// + subtest_t( const uint16_t i_data = 0 ): + iv_mcbmr(i_data) + {} + + /// + /// @brief Checks if the op type requires FIFO mode to be on + /// @return bool fifo_mode_requried - true if FIFO mode is required to be forced on + /// + inline bool fifo_mode_required() const + { + // Gets the op type for this subtest + uint64_t l_value_to_find = 0; + iv_mcbmr.extractToRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(l_value_to_find); + + // Finds if this op type is in the vector that stores the OP types that require FIFO mode to be run + const auto l_op_type_it = std::find(TT::FIFO_MODE_REQUIRED_OP_TYPES.begin(), TT::FIFO_MODE_REQUIRED_OP_TYPES.end(), + l_value_to_find); + + // If the op type is required (aka was found), it will be less than end + // std::find returns the ending iterator if it was not found, so this will return false in that case + return l_op_type_it != TT::FIFO_MODE_REQUIRED_OP_TYPES.end(); + } + + /// + /// @brief Convert to a 16 bit int + /// @return the subtest as a 16 bit integer, useful for testing + /// + inline operator uint16_t() + { + return uint16_t(iv_mcbmr); + } + + /// + /// @brief Complement the data for the first subcommand + /// @param[in] i_state the desired state (mss::ON or mss::OFF) + /// + inline void change_compliment_1st_cmd( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::COMPL_1ST_CMD>(i_state); + return; + } + + /// + /// @brief Complement the data for the second subcommand + /// @param[in] i_state the desired state (mss::ON or mss::OFF) + /// @return void + /// + inline void change_compliment_2nd_cmd( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::COMPL_2ND_CMD>(i_state); + return; + } + + /// + /// @brief Complement the data for the third subcommand + /// @param[in] i_state the desired state (mss::ON or mss::OFF) + /// @return void + /// + inline void change_compliment_3rd_cmd( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::COMPL_3RD_CMD>(i_state); + return; + } + + /// + /// @brief Enable a specific port for this test - maint address mode + /// @param[in] i_port the port desired to be enabled - int 0, 1, 2, 3 + /// @note The port number is relative to the MCBIST + /// @return void + /// + inline void enable_port( const uint64_t i_port ) + { + if (TT::MULTI_PORTS == mss::states::YES) + { + constexpr uint64_t l_len = (TT::COMPL_2ND_CMD - TT::COMPL_1ST_CMD) + 1; + iv_mcbmr.template insertFromRight<TT::COMPL_1ST_CMD, l_len>(i_port); + } + + return; + } + + /// + /// @brief Enable a specific dimm for this test - maint address mode + /// @param[in] i_dimm the dimm desired to be enabled - int 0, 1 + /// @return void + /// + inline void enable_dimm( const uint64_t i_dimm ) + { + iv_mcbmr.template writeBit<TT::COMPL_3RD_CMD>(i_dimm); + return; + } + + /// + /// @brief Get the port from this subtest + /// @note The port number is relative to the MCBIST + /// @return the port of the subtest + /// + inline uint64_t get_port() const + { + uint64_t l_port = 0; + + if (TT::MULTI_PORTS == mss::states::YES) + { + constexpr uint64_t l_len = (TT::COMPL_2ND_CMD - TT::COMPL_1ST_CMD) + 1; + iv_mcbmr.template extractToRight<TT::COMPL_1ST_CMD, l_len>(l_port); + } + + return l_port; + } + + /// + /// @brief Get the DIMM from this subtest + /// @return the DIMM this subtest has been configured for + /// + inline uint64_t get_dimm() const + { + + return iv_mcbmr.template getBit<TT::COMPL_3RD_CMD>() ? 1 : 0; + + return 0; + } + + /// + /// @brief Add the subtest to go to + /// @param[in] the subtest to jump to + /// @return void + /// + inline void change_goto_subtest( const uint64_t i_jmp_to ) + { + iv_mcbmr.template insertFromRight<TT::GOTO_SUBTEST, TT::GOTO_SUBTEST_LEN>(i_jmp_to); + FAPI_INF("changing subtest to jump to %d (0x%02x)", i_jmp_to, iv_mcbmr); + return; + } + + /// + /// @brief Generate addresses in reverse order + /// @param[in] i_state the desired state of the function; mss:ON, mss::OFF + /// @return void + /// + inline void change_addr_rev_mode( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::ADDR_REV_MODE>(i_state); + return; + } + + /// + /// @brief Generate addresses in random order + /// @param[in] i_state the desired state of the function; mss:ON, mss::OFF + /// @return void + /// + inline void change_addr_rand_mode( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::ADDR_RAND_MODE>(i_state); + return; + } + + /// + /// @brief Generate and check data with ECC + /// @param[in] i_state the desired state of the function; mss:ON, mss::OFF + /// @return void + /// + inline void change_ecc_mode( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::ECC_MODE>(i_state); + return; + } + + /// + /// @brief Set the 'done after this test' bit + /// @param[in] i_state the desired state of the function; mss:ON, mss::OFF + /// @return void + /// + inline void change_done( const mss::states i_state ) + { + iv_mcbmr.template writeBit<TT::DONE>(i_state); + return; + } + + /// + /// @brief Set the data mode for this subtest + /// @param[in] i_mode the desired mcbist::data_mode + /// @return void + /// + inline void change_data_mode( const data_mode i_mode ) + { + iv_mcbmr.template insertFromRight<TT::DATA_MODE, TT::DATA_MODE_LEN>(i_mode); + return; + } + + /// + /// @brief Set the operation type for this subtest + /// @param[in] i_mode the desired mcbist::op_type + /// @return void + /// + inline void change_op_type( const op_type i_type ) + { + iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(i_type); + return; + } + + /// + /// @brief Configure which address registers to use for this subtest + /// @param[in] i_index 0 = MCBSA0Q, 1 = MCBSA1Q, ... + /// @note wraps to 0-3 no matter what value you pass in. + /// @return void + /// + inline void change_addr_sel( const uint16_t i_index ) + { + // Roll the index around - tidy support for an index which is out of range. + iv_mcbmr.template insertFromRight<TT::ADDR_SEL, TT::ADDR_SEL_LEN>(i_index % TT::MAX_ADDRESS_START_END_REGISTERS); + FAPI_INF("changed address select to index %d (0x%x)", i_index, iv_mcbmr); + return; + } + + // + // @brief operator== for mcbist subtests + // @param[in] i_rhs the right hand side of the compare + // @return bool, true iff i_rhs == this + inline bool operator==(const subtest_t& i_rhs) const + { + return i_rhs.iv_mcbmr == iv_mcbmr; + } + + /// The mcbist 'memory register' for this subtest. + // Note that it is only 16 bits. + // Each 64b memory register contains multiple 16 bit subtest definitions. + // As we create a vector of subtests, we'll drop them in to their appropriate + // MCBMR register before executing. + fapi2::buffer<uint16_t> iv_mcbmr; +}; + +/// +/// @brief Return a write subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> write_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0000 - we want subtest type to be a Write (W) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::WRITE); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 12 = 1 - ecc + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + // 14:15 = 0 address select config registers 0 + + return l_subtest; +} + + +/// +/// @brief Return an alter subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> alter_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 1011 - we want subtest type to be a Alter + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::ALTER); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return an display subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> display_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 1100 - we want subtest type to be a Display + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::DISPLAY); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return an scrub subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> scrub_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 1001 - we want subtest type to be a Scrub + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::SCRUB_RRWR); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a steer subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> steer_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 1010 - we want subtest type to be a Steer + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::STEER_RW); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a read subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> read_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0001 - we want subtest type to be a Read (R) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::READ); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a read write subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> read_write_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0010 - we want subtest type to be a Read Write (RW) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::READ_WRITE); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + + +/// +/// @brief Return a write read subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> write_read_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0011 - we want subtest type to be a Write Read (WR) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::WRITE_READ); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a read write read subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> read_write_read_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0100 - we want subtest type to be a Read Write Read (RWR) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::READ_WRITE_READ); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a read read write subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> read_read_write_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 1000 - we want subtest type to be a Read Read Write (RRW) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::READ_READ_WRITE); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a read write write subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> read_write_write_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0101 - we want subtest type to be a Read Write Write (RWW) + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::READ_WRITE_WRITE); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a random subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> random_subtest() +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0110 - we want subtest type to be a Random Seq, a randomly chosen read or write + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::RAND_SEQ); + + // - Not a special subtest, so no other configs associated + // 4 = 0 - we don't want to complement data for our Writes + // 5:6 = 00 - don't know whether we complement 2nd and 3rd subcommand, caller to fix + // 7 = 0 - forward address generation + // 8 = 0 - non random address generation + // - Don't need to set up anything for LFSRs + // 9:11 = 000 - Fixed data mode + + // 14:15 = 0 address select config registers 0 + + // By default we want to turn on ECC. Caller can turn it off. + l_subtest.change_ecc_mode(mss::ON); + + return l_subtest; +} + +/// +/// @brief Return a goto subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] the subtest we should go to +/// @return mss::mcbist::subtest_t +/// @note Turns on ECC mode for the returned subtest - caller can turn it off +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> goto_subtest( const uint64_t i_jump_to ) +{ + // Starts life full of 0's + subtest_t<MC, T, TT> l_subtest; + + // 0:3 = 0111 - we want subtest type to be a Goto + l_subtest.iv_mcbmr.template insertFromRight<TT::OP_TYPE, TT::OP_TYPE_LEN>(op_type::GOTO_SUBTEST_N); + + // Plug in the subtest the user passed in + l_subtest.change_goto_subtest(i_jump_to); + return l_subtest; +} + +/// +/// @brief Return an init subtest - configured simply +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @return mss::mcbist::subtest_t +/// @note Configures for start/end address select bit as address config register 0 +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE, typename TT = mcbistTraits<MC, T> > +inline subtest_t<MC, T, TT> init_subtest() +{ + return write_subtest<MC, T, TT>(); +} + +/// +/// @brief A class representing a series of MCBIST subtests, and the +/// MCBIST engine parameters associated with running the subtests +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType representing the fapi2 target which +/// @tparam TT the mcbistTraits associated with T - derived +/// contains the MCBIST engine (e.g., fapi2::TARGET_TYPE_MCBIST) +/// @tparam TT, the mssTraits associtated with T +/// @note MCBIST Memory Parameter Register defaults to +/// - issue commands as fast as possible +/// - even weighting of read/write if random addressing +/// - disable clock monitoring +/// - random command gap is disabled +/// - BC4 disabled +/// - no selected ports +/// @note Address Generation Config Register defaults to +/// - 0 fixed slots +/// - All address counter modes on (so addr configs are start + len) +/// - maint address mode enabled +/// - maint broadcast mode disabled +/// - maint slave rank boundary detect disabled +/// @note Config register defaults to +/// - BROADCAST_SYNC_EN disabled +/// - BROADCAST_SYNC_WAIT 0 +/// - TIMEOUT_MODE - wait 524288 cycles until timeout is called +/// - RESET_KEEPER - 0 +/// - CURRENT_ADDR_TRAP_UPDATE_DIS - 0 +/// - CCS_RETRY_DIS - 0 +/// - RESET_CNTS_START_OF_RANK - 0 +/// - LOG_COUNTS_IN_TRACE - 0 +/// - SKIP_INVALID_ADDR_DIMM_DIS - 0 +/// - REFRESH_ONLY_SUBTEST_EN - 0 +/// - REFRESH_ONLY_SUBTEST_TIMEBASE_SEL(0:1) - 0 +/// - RAND_ADDR_ALL_ADDR_MODE_EN - 0 +/// - REF_WAIT_TIME(0:13) - 0 +/// - MCB_LEN64 - 1 +/// - PAUSE_ON_ERROR_MODE(0:1) - don't pause on error +/// - PAUSE_AFTER_CCS_SUBTEST - don't puase after CCS subtest +/// - FORCE_PAUSE_AFTER_ADDR - don't pause after current address +/// - FORCE_PAUSE_AFTER_SUBTEST - no pause after subtest +/// - ENABLE_SPEC_ATTN - disabled +/// - ENABLE_HOST_ATTN - enabled +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +class program +{ + private: + + /// + /// @class broadcast_helper + /// @brief Nested class to help specialize broadcast mode functionality + /// @tparam mss::states BC - YES if broadcast mode capable + /// @tparam B = true - here for a little compiler magic to allow some partial specializations + /// + template< mss::states BC, bool B = true > + struct broadcast_helper; + + /// + /// @class broadcast_helper - BC mode capable specialization + /// @tparam B = true - here for a little compiler magic to allow some partial specializations + /// + template<bool B > + struct broadcast_helper<mss::states::YES, B> + { + + /// + /// @brief Change the broadcast sync enable bit - broadcast capable specialization + /// @param[in] i_state mss::ON to enable the sync pulse, mss::OFF to disable + /// @param[in,out] io_config configuration register + /// + static inline void broadcast_sync_enable( const mss::states i_state, fapi2::buffer<uint64_t>& io_config ) + { + io_config.writeBit<TT::SYNC_EN>(i_state); + } + + /// + /// @brief Change the broadcast mode sync timbase count - broadcast capable specialization + /// @param[in] i_broadcast_timebase + /// @param[in,out] io_config configuration register + /// + static inline void change_broadcast_timebase( const mss::mcbist::broadcast_timebase i_broadcast_timebase, + fapi2::buffer<uint64_t>& io_config ) + { + io_config.insertFromRight<TT::SYNC_WAIT, TT::SYNC_WAIT_LEN>(i_broadcast_timebase); + } + + /// + /// @brief Enable or disable broadcast mode - broadcast capable specialization + /// @param[in] i_mode true if broadcast should be enabled + /// @param[in,out] io_addr_gen address generation register + /// @warn Maint address mode must be enabled for this to work + /// @return void + /// + static inline void change_maint_broadcast_mode( const bool i_mode, fapi2::buffer<uint64_t>& io_addr_gen ) + { + io_addr_gen.writeBit<TT::MAINT_BROADCAST_MODE_EN>(i_mode); + } + }; + + + /// + /// @class broadcast_helper - BC mode incapable specialization + /// @tparam B = true - here for a little compiler magic to allow some partial specializations + /// @note all functions here should be empty - if we don't have broadcast mode, we don't want to do anything for it + /// + template<bool B > + struct broadcast_helper<mss::states::NO, B> + { + + /// + /// @brief Change the broadcast sync enable bit - broadcast incapable specialization + /// @param[in] i_state mss::ON to enable the sync pulse, mss::OFF to disable + /// @param[in,out] io_config configuration register + /// + static inline void broadcast_sync_enable( const mss::states i_state, fapi2::buffer<uint64_t>& io_config ) + {} + + /// + /// @brief Change the broadcast mode sync timbase count - broadcast incapable specialization + /// @param[in] i_broadcast_timebase + /// @param[in,out] io_config configuration register + /// + static inline void change_broadcast_timebase( const mss::mcbist::broadcast_timebase i_broadcast_timebase, + fapi2::buffer<uint64_t>& io_config ) + {} + + /// + /// @brief Enable or disable broadcast mode - broadcast incapable specialization + /// @param[in] i_mode true if broadcast should be enabled + /// @param[in,out] io_addr_gen address generation register + /// @warn Maint address mode must be enabled for this to work + /// @return void + /// + static inline void change_maint_broadcast_mode( const bool i_mode, fapi2::buffer<uint64_t>& io_addr_gen ) + {} + }; + + public: + // Setup our poll parameters so the MCBIST executer can see + // whether to use the delays in the instruction stream or not + program(): + iv_parameters(0), + iv_addr_gen(0), + iv_test_type(CENSHMOO), // Used as default + iv_addr_map0(0), + iv_addr_map1(0), + iv_addr_map2(0), + iv_addr_map3(0), + iv_data_rotate_cnfg(0), + iv_data_rotate_seed(0), + iv_config(0), + iv_control(0), + iv_async(false), + iv_pattern(PATTERN_0), + iv_random24_data_seed(RANDOM24_SEEDS_0), + iv_random24_seed_map(RANDOM24_SEED_MAP_0), + iv_compare_mask(0) + { + // Enable the maintenance mode addressing + change_maint_address_mode(mss::ON); + + // Enable 64B lengths by default. Commands which need 128B (scrub, steer, alter, display) + // can change this to 128B (mss::OFF). + change_len64(mss::ON); + + // Turn off counting mode for all address configs + iv_addr_gen.insertFromRight<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(0b0000); + + // By default if there's an error, we stop after the errored address + iv_config.insertFromRight<TT::CFG_PAUSE_ON_ERROR_MODE, + TT::CFG_PAUSE_ON_ERROR_MODE_LEN>(end_boundary::STOP_AFTER_ADDRESS); + + // All mcbist attentions are host attentions, special attention bit is already clear + if(TT::CFG_ENABLE_ATTN_SUPPORT == mss::states::YES) + { + iv_config.setBit<TT::CFG_ENABLE_HOST_ATTN>(); + } + + } + + /// + /// @brief Change the DIMM select in the address mapping + /// @param[in] i_bitmap DIMM select bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_dimm_select_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_DIMM_SELECT, TT::CFG_AMAP_DIMM_SELECT_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the MRANK0 address mapping when not in 5D mode + /// @param[in] i_bitmap MRANK0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_mrank0_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_MRANK0, TT::CFG_AMAP_MRANK0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the MRANK0 address mapping when in 5D mode + /// @param[in] i_bitmap MRANK0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_mrank0_bit_5d( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK0, TT::CFG_AMAP_SRANK0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the MRANK1 address mapping when not in 5D mode + /// @param[in] i_bitmap MRANK1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_mrank1_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_MRANK1, TT::CFG_AMAP_MRANK1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the MRANK1 address mapping when in 5D mode + /// @param[in] i_bitmap MRANK1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_mrank1_bit_5d( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK1, TT::CFG_AMAP_SRANK1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the MRANK2 address mapping when in 5D mode + /// @param[in] i_bitmap MRANK2 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_mrank2_bit_5d( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK2, TT::CFG_AMAP_SRANK2_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the SRANK0 address mapping when in 5D mode + /// @param[in] i_bitmap SRANK0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_srank0_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK0, TT::CFG_AMAP_SRANK0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the SRANK1 address mapping + /// @param[in] i_bitmap SRANK1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_srank1_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK1, TT::CFG_AMAP_SRANK1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the SRANK2 address mapping + /// @param[in] i_bitmap SRANK2 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_srank2_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_SRANK2, TT::CFG_AMAP_SRANK2_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the BANK2 address mapping + /// @param[in] i_bitmap BANK2 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_bank2_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_BANK2, TT::CFG_AMAP_BANK2_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the BANK1 address mapping + /// @param[in] i_bitmap BANK1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_bank1_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_BANK1, TT::CFG_AMAP_BANK1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the BANK0 address mapping + /// @param[in] i_bitmap BANK0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_bank0_bit( const uint64_t i_bitmap ) + { + iv_addr_map0.insertFromRight<TT::CFG_AMAP_BANK0, TT::CFG_AMAP_BANK0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the BANK_GROUP1 address mapping + /// @param[in] i_bitmap BANK_GROUP1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_bank_group1_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_BANK_GROUP1, TT::CFG_AMAP_BANK_GROUP1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the BANK_GROUP0 address mapping + /// @param[in] i_bitmap BANK_GROUP0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_bank_group0_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_BANK_GROUP0, TT::CFG_AMAP_BANK_GROUP0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW17 address mapping + /// @param[in] i_bitmap ROW17 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row17_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW17, TT::CFG_AMAP_ROW17_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW16 address mapping + /// @param[in] i_bitmap ROW16 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row16_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW16, TT::CFG_AMAP_ROW16_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW15 address mapping + /// @param[in] i_bitmap ROW15 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row15_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW15, TT::CFG_AMAP_ROW15_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW14 address mapping + /// @param[in] i_bitmap ROW14 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row14_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW14, TT::CFG_AMAP_ROW14_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW13 address mapping + /// @param[in] i_bitmap ROW13 bit map in the address counter + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_row13_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW13, TT::CFG_AMAP_ROW13_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW12 address mapping + /// @param[in] i_bitmap ROW12 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row12_bit( const uint64_t i_bitmap ) + { + // CFG_AMAP_ROW12 = MCBIST_MCBAMR1A0Q_CFG_AMAP_ROW12 , + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW12, TT::CFG_AMAP_ROW12_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW11 address mapping + /// @param[in] i_bitmap ROW11 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row11_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW11, TT::CFG_AMAP_ROW11_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW10 address mapping + /// @param[in] i_bitmap ROW10 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row10_bit( const uint64_t i_bitmap ) + { + iv_addr_map1.insertFromRight<TT::CFG_AMAP_ROW10, TT::CFG_AMAP_ROW10_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW9 address mapping + /// @param[in] i_bitmap ROW9 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row9_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW9, TT::CFG_AMAP_ROW9_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW8 address mapping + /// @param[in] i_bitmap ROW8 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row8_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW8, TT::CFG_AMAP_ROW8_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW7 address mapping + /// @param[in] i_bitmap ROW7 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row7_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW7, TT::CFG_AMAP_ROW7_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW6 address mapping + /// @param[in] i_bitmap ROW6 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row6_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW6, TT::CFG_AMAP_ROW6_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW5 address mapping + /// @param[in] i_bitmap ROW5 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row5_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW5, TT::CFG_AMAP_ROW5_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW4 address mapping + /// @param[in] i_bitmap ROW4 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row4_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW4, TT::CFG_AMAP_ROW4_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW3 address mapping + /// @param[in] i_bitmap ROW3 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row3_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW3, TT::CFG_AMAP_ROW3_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW2 address mapping + /// @param[in] i_bitmap ROW2 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row2_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW2, TT::CFG_AMAP_ROW2_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW1 address mapping + /// @param[in] i_bitmap ROW1 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row1_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW1, TT::CFG_AMAP_ROW1_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the ROW0 address mapping + /// @param[in] i_bitmap ROW0 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_row0_bit( const uint64_t i_bitmap ) + { + iv_addr_map2.insertFromRight<TT::CFG_AMAP_ROW0, TT::CFG_AMAP_ROW0_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the COL9 address mapping + /// @param[in] i_bitmap COL9 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col9_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL9, TT::CFG_AMAP_COL9_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the COL8 address mapping + /// @param[in] i_bitmap COL8 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col8_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL8, TT::CFG_AMAP_COL8_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the COL7 address mapping + /// @param[in] i_bitmap COL7 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col7_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL7, TT::CFG_AMAP_COL7_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the COL6 address mapping + /// @param[in] i_bitmap COL6 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col6_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL6, TT::CFG_AMAP_COL6_LEN>(i_bitmap); + return; + } + + /// @brief Change the COL5 address mapping + /// @param[in] i_bitmap COL5 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col5_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL5, TT::CFG_AMAP_COL5_LEN>(i_bitmap); + return; + } + + /// @brief Change the COL4 address mapping + /// @param[in] i_bitmap COL4 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col4_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL4, TT::CFG_AMAP_COL4_LEN>(i_bitmap); + return; + } + + /// @brief Change the COL3 address mapping + /// @param[in] i_bitmap COL3 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col3_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL3, TT::CFG_AMAP_COL3_LEN>(i_bitmap); + return; + } + + /// @brief Change the COL2 address mapping + /// @param[in] i_bitmap COL2 bit map in the address counter + /// @note Assumes data is right-aligned + /// + inline void change_col2_bit( const uint64_t i_bitmap ) + { + iv_addr_map3.insertFromRight<TT::CFG_AMAP_COL2, TT::CFG_AMAP_COL2_LEN>(i_bitmap); + return; + } + + /// + /// @brief Change the mcbist 64/128 byte control + /// @param[in] i_state mss::ON if you want 64B, mss::OFF if you want 128B + /// @return void + /// + inline void change_len64( const mss::states i_state ) + { + iv_config.writeBit<TT::CFG_MCB_LEN64>(i_state); + return; + } + + /// + /// @brief Change the random address all address mode + /// @param[in] i_state mss::ON if you random addressing all addresses, mss::OFF if you don't + /// @return void + /// + inline void random_address_all( const mss::states i_state ) + { + iv_config.writeBit<TT::RAND_ADDR_ALL_ADDR_MODE_EN>(i_state); + return; + } + + /// + /// @brief Change the broadcast sync enable bit + /// @param[in] i_state mss::ON to enable the sync pulse, mss::OFF to disable + /// @return void + /// + inline void broadcast_sync_enable( const mss::states i_state ) + { + broadcast_helper<TT::BROADCAST_CAPABLE>::broadcast_sync_enable(i_state, iv_config); + } + + /// + /// @brief Change the broadcast mode sync timbase count + /// @param[in] i_broadcast_timebase + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_broadcast_timebase( const mss::mcbist::broadcast_timebase i_broadcast_timebase ) + { + broadcast_helper<TT::BROADCAST_CAPABLE>::change_broadcast_timebase(i_broadcast_timebase, iv_config); + return; + } + + /// + /// @brief Change the mcbist thresholds + /// @param[in] i_thresholds the new thresholds/stop conditions + /// @return void + /// + inline void change_thresholds( const stop_conditions<MC>& i_thresholds ) + { + iv_thresholds = i_thresholds; + return; + } + + /// + /// @brief Change the data rotate value + /// @param[in] i_data_rotate + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_data_rotate( mss::mcbist::data_rotate_mode i_data_rotate ) + { + iv_data_rotate_cnfg.insertFromRight<TT::CFG_DATA_ROT, TT::CFG_DATA_ROT_LEN>(i_data_rotate); + return; + } + + /// + /// @brief Get the data rotate value + /// @note Assumes data is right aligned + /// @return the data rotate value config + /// + inline uint64_t get_data_rotate() + { + uint64_t l_data_rotate = 0; + iv_data_rotate_cnfg.extractToRight<TT::CFG_DATA_ROT, TT::CFG_DATA_ROT_LEN>(l_data_rotate); + return l_data_rotate; + } + + /// + /// @brief Change the data seed mode value + /// @param[in] i_data_seed_mode + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_data_seed_mode( const mss::mcbist::data_seed_mode i_data_seed_mode ) + { + iv_data_rotate_cnfg.insertFromRight<TT::CFG_DATA_SEED_MODE, TT::CFG_DATA_SEED_MODE_LEN>(i_data_seed_mode); + return; + } + + /// + /// @brief Get the data seed mode value + /// @note Assumes data is right aligned + /// @return the data seed mode value + /// + inline uint64_t get_data_seed_mode() + { + uint64_t l_data_seed_mode = 0; + iv_data_rotate_cnfg.extractToRight<TT::CFG_DATA_SEED_MODE, TT::CFG_DATA_SEED_MODE_LEN>(l_data_seed_mode); + return l_data_seed_mode; + } + + /// + /// @brief Change the data rotate seed for data bits 0:63 + /// @param[in] i_width + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_data_rotate_seed1( const uint64_t i_data_rotate_seed1 ) + { + iv_data_rotate_seed.insertFromRight<TT::CFG_DATA_ROT_SEED1, TT::CFG_DATA_ROT_SEED1_LEN>(i_data_rotate_seed1); + return; + } + + /// + /// @brief Get the data rotate seed for data bits 0:63 + /// @note Assumes data is right aligned + /// @return the data rotate seed for data bits 0:63 + /// + inline uint64_t get_data_rotate_seed1() + { + uint64_t l_data_rotate_seed1 = 0; + iv_data_rotate_seed.extractToRight<TT::CFG_DATA_ROT_SEED1, TT::CFG_DATA_ROT_SEED1_LEN>(l_data_rotate_seed1); + return l_data_rotate_seed1; + } + + /// + /// @brief Change the data rotate seed for data bits 64:79 + /// @param[in] i_width + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_data_rotate_seed2( const uint64_t i_data_rotate_seed2 ) + { + iv_data_rotate_cnfg.insertFromRight<TT::CFG_DATA_ROT_SEED2, TT::CFG_DATA_ROT_SEED2_LEN>(i_data_rotate_seed2); + return; + } + + /// + /// @brief Get the data rotate seed for data bits 64:79 + /// @note Assumes data is right aligned + /// @return the data rotate seed for data bits 64:79 + /// + inline uint64_t get_data_rotate_seed2() + { + uint64_t l_data_rotate_seed2 = 0; + iv_data_rotate_cnfg.extractToRight<TT::CFG_DATA_ROT_SEED2, TT::CFG_DATA_ROT_SEED2_LEN>(l_data_rotate_seed2); + return l_data_rotate_seed2; + } + + /// + /// @brief Change the compare mask CE trap enable + /// @param[in] i_state mss::ON to enable the trap, mss::OFF to disable the trap + /// @return void + /// + inline void change_ce_trap_enable( const mss::states i_state ) + { + iv_compare_mask.writeBit<TT::CFG_TRAP_CE_ENABLE>(i_state); + return; + } + + /// + /// @brief Change the compare mask UE trap enable + /// @param[in] i_state mss::ON to enable the trap, mss::OFF to disable the trap + /// @return void + /// + inline void change_ue_trap_enable( const mss::states i_state ) + { + iv_compare_mask.writeBit<TT::CFG_TRAP_UE_ENABLE>(i_state); + return; + } + + /// + /// @brief Change the compare mask MPE trap enable + /// @param[in] i_state mss::ON to enable the trap, mss::OFF to disable the trap + /// @return void + /// + inline void change_mpe_trap_enable( const mss::states i_state ) + { + iv_compare_mask.writeBit<TT::CFG_TRAP_MPE_ENABLE>(i_state); + return; + } + + /// + /// @brief Change the forced pause state + /// @param[in] i_end the end_boundary to pause at + /// @return void + /// + inline void change_forced_pause( const end_boundary& i_end ) + { + if (i_end == end_boundary::DONT_CHANGE) + { + return; + } + + // Clear all the forced pause bits so we don't stack pauses + iv_config.clearBit<TT::MCBIST_CFG_FORCE_PAUSE_AFTER_ADDR>(); + iv_config.clearBit<TT::MCBIST_CFG_PAUSE_AFTER_RANK>(); + iv_config.clearBit<TT::MCBIST_CFG_FORCE_PAUSE_AFTER_SUBTEST>(); + iv_addr_gen.clearBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>(); + + switch (i_end) + { + case end_boundary::STOP_AFTER_ADDRESS: + iv_config.setBit<TT::MCBIST_CFG_FORCE_PAUSE_AFTER_ADDR>(); + break; + + case end_boundary::STOP_AFTER_SLAVE_RANK: + iv_config.setBit<TT::MCBIST_CFG_PAUSE_AFTER_RANK>(); + iv_addr_gen.setBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>(); + break; + + case end_boundary::STOP_AFTER_MASTER_RANK: + iv_config.setBit<TT::MCBIST_CFG_PAUSE_AFTER_RANK>(); + iv_addr_gen.clearBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>(); + break; + + case end_boundary::STOP_AFTER_SUBTEST: + iv_config.setBit<TT::MCBIST_CFG_FORCE_PAUSE_AFTER_SUBTEST>(); + break; + + // None is all set, we cleared the bits above + case end_boundary::NONE: + break; + + // Default is a no forced pause (as we cleared all the bits) + default: + FAPI_INF("no forced pause state - end state %d unknown", i_end); + break; + }; + + return; + } + + /// + /// @brief Calculate minimum command gap for BG_SCRUB + /// @param[in] i_target the target behind which the memory sits + /// @param[in] i_freq the DRAM frequency + /// @param[in] i_size the sum of all DIMM sizes + /// @param[out] o_min_cmd_gap the setting for MCBPARMQ_CFG_MIN_CMD_GAP + /// @param[out] o_timebase the setting for MCBPARMQ_CFG_MIN_GAP_TIMEBASE + /// @return FAPI2_RC_SUCCSS iff ok + /// + inline fapi2::ReturnCode calculate_min_cmd_gap( const fapi2::Target<T>& i_target, + const uint64_t i_freq, + const uint64_t i_size, + uint64_t& o_min_cmd_gap, + mss::states& o_timebase ) + { + constexpr uint64_t l_seconds = SEC_IN_HOUR * BG_SCRUB_IN_HOURS; + constexpr uint64_t MIN_CMD_GAP = 0x001; + uint64_t l_mem_cycles_per_sec; + uint64_t l_total_cycles; + uint64_t l_total_addresses; + uint64_t l_min_cmd_gap; + + // Sanity check our inputs, just assert if bad since they come directly from eff_config + // this will prevent us from any divide by zero problems + FAPI_ASSERT( (i_freq != 0) && (i_size != 0), + fapi2::MSS_ZERO_FREQ_OR_SIZE(). + set_FREQ(i_freq). + set_SIZE(i_size), + "%s received zero memory freq or size in calculate_min_cmd_gap", mss::c_str(i_target)); + + // MIN CMD GAP = TOTAL CYCLES / TOTAL ADDRESSES + // TOTAL CYCLES = 12 hours x 60 min/hr x 60 sec/min x [DRAM freq] cycles/sec x + // 1/2 (MEM logic runs half DRAM freq) + l_mem_cycles_per_sec = (i_freq * T_PER_MT) / 2; + l_total_cycles = l_seconds * l_mem_cycles_per_sec; + + // TOTAL ADDRESSES = sum over all dimms of ( [DIMM CAPACITY]/128B ) + l_total_addresses = i_size * BYTES_PER_GB / 128; + + l_min_cmd_gap = l_total_cycles / l_total_addresses; + + // If we're greater than the timebase, set the multiplier and divide down to get the gap setting + if (CMD_TIMEBASE < l_min_cmd_gap) + { + o_min_cmd_gap = l_min_cmd_gap / CMD_TIMEBASE; + o_timebase = mss::ON; + return fapi2::FAPI2_RC_SUCCESS; + } + + // If we're greater than the max gap setting, get as close to 12 hours as we can instead of just truncating + if (l_min_cmd_gap > MAX_CMD_GAP) + { + // work backwards to calculate what the total scrub time would be with the highest cmd gap with no multiplier... + const uint64_t l_scrub_time_fff = (l_total_addresses * MAX_CMD_GAP) / l_mem_cycles_per_sec; + // and with the lowest cmd gap with the multiplier + const uint64_t l_scrub_time_001 = (l_total_addresses * CMD_TIMEBASE) / l_mem_cycles_per_sec; + + if ((l_seconds - l_scrub_time_fff) > (l_scrub_time_001 - l_seconds)) + { + FAPI_INF("%s gap is greater than the field will allow. Setting to: 0x%03x", mss::c_str(i_target), MIN_CMD_GAP); + o_min_cmd_gap = MIN_CMD_GAP; + o_timebase = mss::ON; + } + else + { + FAPI_INF("%s gap is greater than the field will allow. Setting to: 0x%03x", mss::c_str(i_target), MAX_CMD_GAP); + o_min_cmd_gap = MAX_CMD_GAP; + o_timebase = mss::OFF; + } + + return fapi2::FAPI2_RC_SUCCESS; + } + + // Else, we're good to just set the calculated gap value directly + o_min_cmd_gap = l_min_cmd_gap; + o_timebase = mss::OFF; + return fapi2::FAPI2_RC_SUCCESS; + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Change MCBIST Speed + /// @param[in] i_target the target behind which the memory sits + /// @param[in] i_speed the speed eunmeration + /// @return FAPI2_RC_SUCCSS iff ok + /// + inline fapi2::ReturnCode change_speed( const fapi2::Target<T>& i_target, const speed i_speed ) + { + switch (i_speed) + { + case speed::LUDICROUS: + change_min_cmd_gap(0); + change_min_gap_timebase(mss::OFF); + return fapi2::FAPI2_RC_SUCCESS; + break; + + case speed::BG_SCRUB: + { + uint64_t l_freq = 0; + uint64_t l_size = 0; + uint64_t l_min_cmd_gap = 0; + mss::states l_timebase = mss::OFF; + + constexpr uint64_t l_seconds = SEC_IN_HOUR * BG_SCRUB_IN_HOURS; + + FAPI_TRY( mss::freq(i_target, l_freq) ); + FAPI_TRY( mss::eff_memory_size<MC>(i_target, l_size) ); + + FAPI_TRY( calculate_min_cmd_gap(i_target, l_freq, l_size, l_min_cmd_gap, l_timebase) ); + + FAPI_INF("%s setting bg scrub speed: %dMT/s, memory: %dGB, duration: %ds, gap: %d", + mss::c_str(i_target), l_freq, l_size, l_seconds, l_min_cmd_gap); + + change_min_cmd_gap(l_min_cmd_gap); + change_min_gap_timebase(l_timebase); + + return fapi2::FAPI2_RC_SUCCESS; + } + break; + + // Otherwise it's SAME_SPEED or something else in which case we do nothing + default: + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + break; + }; + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Get a list of ports involved in the program + /// @param[in] i_target the target for this program + /// @return vector of port targets + /// + std::vector<fapi2::Target<TT::PORT_TYPE>> get_port_list( const fapi2::Target<T>& i_target ) const; + + /// + /// @brief Change MCBIST Stop-on-error conditions (end boundaries) + /// @param[in] i_end the end boundary + /// @note By default the MCBIST is programmed to always stop after an errored address. This API + /// allows the caller to force a stop at a boundary or to force no stopping on errors + /// + inline void change_end_boundary( const end_boundary i_end ) + { + // If there's no change, just get outta here + if (i_end == DONT_CHANGE) + { + return; + } + + // The values of the enum were crafted so that we can simply insertFromRight into the register. + // We take note of whether to set the slave or master rank indicator and set that as well. + // The hardware has to have a 1 or a 0 - so there is no choice for the rank detection. So it + // doesn't matter that we're processing other end boundaries here - they'll just look like we + // asked for a master rank detect. + iv_config.insertFromRight<TT::CFG_PAUSE_ON_ERROR_MODE, TT::CFG_PAUSE_ON_ERROR_MODE_LEN>(i_end); + + const uint64_t l_detect_slave = fapi2::buffer<uint64_t>(i_end).getBit<TT::SLAVE_RANK_INDICATED_BIT>(); + iv_addr_gen.writeBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>( l_detect_slave ); + FAPI_INF("load MCBIST end boundaries 0x%016lx detect slave? %s", + i_end, (l_detect_slave == 1 ? "yes" : "no") ); + } + + /// + /// @brief Change the mcbist min command gap + /// @param[in] i_gap minimum number of cycles between commands when cfg_en_randcmd_gap is a 0 (disabled) + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_min_cmd_gap( const uint64_t i_gap ) + { + iv_parameters.insertFromRight<TT::MIN_CMD_GAP, TT::MIN_CMD_GAP_LEN>(i_gap); + return; + } + + /// + /// @brief Change the mcbist gap timebase + /// @param[in] i_tb When set to mss::ON and cfg_en_randcmd_gap is a 0, then the number of minimum + /// cycles between commands will be cfg_min_cmd_gap multiplied by 2^13. + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_min_gap_timebase( const bool i_tb ) + { + iv_parameters.writeBit<TT::MIN_GAP_TIMEBASE>(i_tb); + return; + } + + /// + /// @brief Change the mcbist min command gap blind steer + /// @param[in] i_gap min gap between commands when doing steering + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_min_cmd_gap_blind_steer( const uint64_t i_gap ) + { + iv_parameters.insertFromRight<TT::MIN_CMD_GAP_BLIND_STEER, TT::MIN_CMD_GAP_BLIND_STEER_LEN>(i_gap); + return; + } + + /// + /// @brief Change the mcbist gap timebase for blind steer + /// @param[in] i_tb When set to mss::ON and cfg_en_randcmd_gap is a 0, then the number of minimum + /// cycles between commands will be cfg_min_cmd_gap multiplied by 2^13. + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_min_gap_timebase_blind_steer( const bool i_tb ) + { + iv_parameters.writeBit<TT::MIN_GAP_TIMEBASE_BLIND_STEER>(i_tb); + return; + } + + /// + /// @brief Change the weights for random mcbist reads, writes + /// @param[in] i_weight + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_randcmd_wgt( const uint64_t i_weight ) + { + iv_parameters.insertFromRight<TT::RANDCMD_WGT, TT::RANDCMD_WGT_LEN>(i_weight); + return; + } + + /// + /// @brief Change the weights for random mcbist command gaps + /// @param[in] i_weight + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_randgap_wgt( const uint64_t i_weight ) + { + iv_parameters.insertFromRight<TT::RANDGAP_WGT, TT::RANDGAP_WGT_LEN>(i_weight); + return; + } + + /// + /// @brief Enable or disable mcbist clock monitoring + /// @param[in] i_monitor mss::ON to monitor + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_clock_monitor_en( const bool i_monitor ) + { + iv_parameters.writeBit<TT::CLOCK_MONITOR_EN>(i_monitor); + return; + } + + /// + /// @brief Enable or disable mcbist random command gaps + /// @param[in] i_rndgap mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_en_randcmd_gap( const bool i_rndgap ) + { + iv_parameters.writeBit<TT::EN_RANDCMD_GAP>(i_rndgap); + return; + } + + /// + /// @brief Enable or disable mcbist BC4 support + /// @param[in] i_bc4 mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_bc4_en( const bool i_bc4 ) + { + iv_parameters.writeBit<TT::BC4_EN>(i_bc4); + return; + } + + /// + /// @brief Change fixed width address generator config + /// @param[in] i_width + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_fixed_width( const uint64_t i_width ) + { + iv_addr_gen.insertFromRight<TT::FIXED_WIDTH, TT::FIXED_WIDTH_LEN>(i_width); + return; + } + + /// + /// @brief Get the fixed width address config + /// @note Assumes data is right aligned + /// @return the fixed width address config + /// + inline uint64_t get_fixed_width() const + { + uint64_t l_fixed_width = 0; + iv_addr_gen.extractToRight<TT::FIXED_WIDTH, TT::FIXED_WIDTH_LEN>(l_fixed_width); + return l_fixed_width; + } + + /// + /// @brief Enable or disable address counting mode for address config 0 + /// @param[in] i_mode mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_address_counter_mode0( const bool i_mode ) + { + fapi2::buffer<uint64_t> l_value; + iv_addr_gen.extract<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + // Bit 0 enables counter mode for start/end address field 0 + l_value.writeBit<0>(i_mode); + iv_addr_gen.insert<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + return; + } + + /// + /// @brief Enable or disable address counting mode for address config 1 + /// @param[in] i_mode mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_address_counter_mode1( const bool i_mode ) + { + fapi2::buffer<uint64_t> l_value; + iv_addr_gen.extract<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + // Bit 1 enables counter mode for start/end address field 1 + l_value.writeBit<1>(i_mode); + iv_addr_gen.insert<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + return; + } + + /// + /// @brief Enable or disable address counting mode for address config 2 + /// @param[in] i_mode mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_address_counter_mode2( const bool i_mode ) + { + fapi2::buffer<uint64_t> l_value; + iv_addr_gen.extract<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + // Bit 2 enables counter mode for start/end address field 2 + l_value.writeBit<2>(i_mode); + iv_addr_gen.insert<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + return; + } + + /// + /// @brief Enable or disable address counting mode for address config 3 + /// @param[in] i_program, the program in question + /// @param[in] i_mode mss::ON to enable + /// @note Assumes data is right-aligned + /// @return void + /// + inline void change_address_counter_mode3( const bool i_mode ) + { + fapi2::buffer<uint64_t> l_value; + iv_addr_gen.extract<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + // Bit 3 enables counter mode for start/end address field 3 + l_value.writeBit<3>(i_mode); + iv_addr_gen.insert<TT::ADDR_COUNTER_MODE, TT::ADDR_COUNTER_MODE_LEN>(l_value); + return; + } + + + /// + /// @brief Enable or disable maint address mode + /// @param[in] i_mode mss::ON to enable + /// @warn Address counter modes must be 0 for this to work. + /// @note When enabled subtest complement bits become 3-bit port-dimm selector field + /// (Note: when turning this off, make sure you clear or reprogram complement bits) + /// @return void + /// + inline void change_maint_address_mode( const bool i_mode ) + { + iv_addr_gen.writeBit<TT::MAINT_ADDR_MODE_EN>(i_mode); + return; + } + + /// + /// @brief Enable or disable broadcast mode + /// @param[in] i_program the program in question + /// @param[in] i_mode mss::ON to enable + /// @warn Maint address mode must be enabled for this to work + /// @return void + /// + inline void change_maint_broadcast_mode( const bool i_mode ) + { + broadcast_helper<TT::BROADCAST_CAPABLE>::change_maint_broadcast_mode(i_mode, iv_addr_gen); + return; + } + + + /// + /// @brief Enable or disable slave rank boundary detect + /// @param[in] i_program the program in question + /// @param[in] i_mode mss::ON to enable + /// @return void + /// + inline void change_srank_boundaries( const bool i_mode ) + { + iv_addr_gen.writeBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>(i_mode); + return; + } + + /// + /// @brief Enable or disable async mode + /// @param[in] i_program the program in question + /// @param[in] i_mode mss::ON to enable, programs will run async + /// @return void + /// + inline void change_async( const bool i_mode ) + { + iv_async = i_mode; + return; + } + + /// + /// @brief Select the port(s) to be used by the MCBIST + /// @param[in] i_ports uint64_t representing the ports. Multiple bits set imply broadcast + /// i_ports is a right-aligned uint64_t, of which only the right-most 4 bits are used. The register + /// field is defined such that the left-most bit in the field represents port 0, the right most + /// bit in the field represents port 3. So, to run on port 0, i_ports should be 0b1000. 0b0001 + /// (or 0x1) is port 3 - not port 0 + /// @return void + /// + inline void select_ports( const uint64_t i_ports ) + { + if (TT::MULTI_PORTS == mss::states::YES) + { + iv_control.insertFromRight<TT::PORT_SEL, TT::PORT_SEL_LEN>(i_ports); + FAPI_INF("mcbist select ports: iv_control 0x%016lx (ports: 0x%x)", iv_control, i_ports); + } + + return; + } + + /// + /// @brief Process mcbist errors + /// @param[in] i_target the target for this program + /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok + /// This shouldn't be called in firmware? Check with PRD + /// + inline fapi2::ReturnCode process_errors( const fapi2::Target<T> i_target ) const + { + // MCBIST error traits + using ET = mcbistMCTraits<MC>; + + // Until reading the error array is documented, comparison errors 'just' result in + // a flag indicating there was a problem on port. + { + fapi2::buffer<uint64_t> l_data; + uint64_t l_port = 0; + uint64_t l_subtest = 0; + FAPI_TRY( fapi2::getScom(i_target, TT::MCBSTATQ_REG, l_data), "%s Failed getScom", mss::c_str(i_target) ); + + if (TT::MULTI_PORTS == mss::states::YES) + { + l_data.extractToRight<TT::LOGGED_ERROR_ON_PORT_INDICATOR, TT::LOGGED_ERROR_ON_PORT_INDICATOR_LEN>(l_port); + } + + l_data.extractToRight<TT::SUBTEST_NUM_INDICATOR, TT::SUBTEST_NUM_INDICATOR_LEN>(l_subtest); + + FAPI_ASSERT( l_port == 0, + ET::memdiags_compare_error_in_last_pattern() + .set_MC_TARGET(i_target) + .set_PORT(mss::first_bit_set(l_port)) + .set_SUBTEST(l_subtest), + "%s MCBIST error on port %d subtest %d", mss::c_str(i_target), mss::first_bit_set(l_port), l_subtest ); + } + + // Check for UE errors + { + fapi2::buffer<uint64_t> l_read0; + fapi2::buffer<uint64_t> l_read1; + + FAPI_TRY( fapi2::getScom(i_target, TT::SRERR0_REG, l_read0), "%s Failed getScom", mss::c_str(i_target) ); + FAPI_TRY( fapi2::getScom(i_target, TT::SRERR1_REG, l_read1), "%s Failed getScom", mss::c_str(i_target) ); + + FAPI_ASSERT( ((l_read0 == 0) && (l_read1 == 0)), + ET::memdiags_error_in_last_pattern() + .set_MC_TARGET(i_target) + .set_STATUS0(l_read0) + .set_STATUS1(l_read1), + "%s MCBIST scrub/read error reg0: 0x%016lx reg1: 0x%016lx", mss::c_str(i_target), l_read0, l_read1 ); + } + + FAPI_INF("%s Execution success - no errors seen from MCBIST program", mss::c_str(i_target)); + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Store off the pattern index. We'll use this to write the patterns when we load the program + /// @param[in] i_index an index such as mss::mcbist::PATTERN_0 + /// @return fapi2::ReturnCode checks for bad pattern index + /// @warning if you give a pattern index which does not exist your pattern will not change. + /// @note patterns default to PATTERN_0 + /// + inline fapi2::ReturnCode change_pattern( const uint64_t i_pattern ) + { + FAPI_INF("change MCBIST pattern index %d", i_pattern); + + // Sanity check the pattern since they're just numbers. + FAPI_ASSERT( i_pattern <= mcbist::NO_PATTERN, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(i_pattern). + set_MC_TYPE(MC), + "Attempting to change a pattern which does not exist %d", i_pattern ); + + iv_pattern = i_pattern; + + return fapi2::FAPI2_RC_SUCCESS; + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Store off the random 24b data seed index. We'll use this to write the 24b random data seeds when we load the program + /// @param[in] i_index an index such as mss::mcbist::RANDOM24_SEEDS_0 + /// @return fapi2::ReturnCode checks for bad pattern index + /// @warning if you give a pattern index which does not exist your pattern will not change. + /// @note patterns default to PATTERN_0 + /// + inline fapi2::ReturnCode change_random_24b_seeds( const uint64_t i_random24_seed ) + { + FAPI_INF("change MCBIST 24b random data seeds index %d", i_random24_seed ); + + // TK Want a new RC for random 24 + // Sanity check the pattern since they're just numbers. + FAPI_ASSERT( i_random24_seed <= mcbist::NO_RANDOM24_SEEDS, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(i_random24_seed). + set_MC_TYPE(MC), + "Attempting to change to a 24b random data seed which does not exist %d", i_random24_seed ); + + iv_random24_data_seed = i_random24_seed; + + return fapi2::FAPI2_RC_SUCCESS; + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief Store off the random 24b data seed mapping index. We'll use this to write the 24b random data seed mappings when we load the program + /// @param[in] i_index an index such as mss::mcbist::RANDOM24_SEEDS_0 + /// @return fapi2::ReturnCode checks for bad pattern index + /// @warning if you give a pattern index which does not exist your pattern will not change. + /// @note patterns default to PATTERN_0 + /// + inline fapi2::ReturnCode change_random_24b_maps( const uint64_t i_random24_map ) + { + FAPI_INF("change MCBIST 24b random data seed mappings index %d", i_random24_map ); + + // TK Want a new RC for random 24 + // Sanity check the pattern since they're just numbers. + FAPI_ASSERT( i_random24_map <= mcbist::NO_RANDOM24_SEED_MAP, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(i_random24_map). + set_MC_TYPE(MC), + "Attempting to change to a random seed map which does not exist %d", i_random24_map ); + + iv_random24_seed_map = i_random24_map; + + return fapi2::FAPI2_RC_SUCCESS; + + fapi_try_exit: + return fapi2::current_err; + } + + /// + /// @brief checks if two programs are equal + /// @param[in] i_rhs program to compare + /// @return bool true if equal + /// + inline bool operator==( const program<MC>& i_rhs ) const + { + //checks the vector first, to save time if they're not equal (no sense in checking everything else) + if(iv_subtests != i_rhs.iv_subtests) + { + return false; + } + + //checks everything else + bool l_equal = iv_parameters == i_rhs.iv_parameters; + l_equal &= iv_addr_gen == i_rhs.iv_addr_gen; + l_equal &= iv_test_type == i_rhs.iv_test_type; + l_equal &= iv_poll == i_rhs.iv_poll; + l_equal &= iv_addr_map0 == i_rhs.iv_addr_map0; + l_equal &= iv_addr_map1 == i_rhs.iv_addr_map1; + l_equal &= iv_addr_map2 == i_rhs.iv_addr_map2; + l_equal &= iv_addr_map3 == i_rhs.iv_addr_map3; + l_equal &= iv_config == i_rhs.iv_config; + l_equal &= iv_control == i_rhs.iv_control; + l_equal &= iv_async == i_rhs.iv_async; + l_equal &= iv_pattern == i_rhs.iv_pattern; + l_equal &= iv_thresholds == i_rhs.iv_thresholds; + l_equal &= iv_data_rotate_cnfg == i_rhs.iv_data_rotate_cnfg; + l_equal &= iv_data_rotate_seed == i_rhs.iv_data_rotate_seed; + l_equal &= iv_random24_data_seed == i_rhs.iv_random24_data_seed; + l_equal &= iv_random24_seed_map == i_rhs.iv_random24_seed_map; + l_equal &= iv_data_rotate_cnfg == i_rhs.iv_data_rotate_cnfg; + l_equal &= iv_data_rotate_seed == i_rhs.iv_data_rotate_seed; + l_equal &= iv_compare_mask == i_rhs.iv_compare_mask; + + //returns result + return l_equal; + } + + // Vector of subtests. Note the MCBIST subtests are spread across + // 8 registers - 4 subtests fit in one 64b register + // (16 bits/test, 4 x 16 == 64, 4x8 = 32 subtests) + // We keep a vector of 16 bit subtests here, and we program the + // MCBIST engine (i.e., spread the subtests over the 8 registers) + // when we're told to execute the program. + std::vector< subtest_t<MC, T, TT> >iv_subtests; + + // Place to hold the value of the MCBIST Memory Parameter Register. We'll scom + // it when we execute the program. + fapi2::buffer<uint64_t> iv_parameters; + + // Place to hold the value of the MCBIST Address Generation Config. We'll scom + // it when we execute the program. + fapi2::buffer<uint64_t> iv_addr_gen; + + test_type iv_test_type; + + poll_parameters iv_poll; + + // Address Map Registers + // We might want to refactor to a vector ... BRS + // uint64_t iv_addr_map0; + // uint64_t iv_addr_map1; + // uint64_t iv_addr_map2; + // uint64_t iv_addr_map3; + //Perhaps this isn't the right approach, we can discuss and change if needed, leaving the above comments for now + fapi2::buffer<uint64_t> iv_addr_map0; + fapi2::buffer<uint64_t> iv_addr_map1; + fapi2::buffer<uint64_t> iv_addr_map2; + fapi2::buffer<uint64_t> iv_addr_map3; + + // Data Rotate Seed and Config Registers + fapi2::buffer<uint64_t> iv_data_rotate_cnfg; + fapi2::buffer<uint64_t> iv_data_rotate_seed; + + // Config register + fapi2::buffer<uint64_t> iv_config; + + // Control register + fapi2::buffer<uint64_t> iv_control; + + // True iff we want to run in asynchronous mode + bool iv_async; + + // The pattern for the pattern generator + uint64_t iv_pattern; + + // The pattern for the random 24b seeds + uint64_t iv_random24_data_seed; + + // The pattern for the random 24b data seed mapping + uint64_t iv_random24_seed_map; + + // The pattern for the pattern generator + fapi2::buffer<uint64_t> iv_compare_mask; + + // The error stop conditions, thresholds for the program + stop_conditions<MC> iv_thresholds; +}; + +/// +/// @brief Load the mcbist config register +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mssTraits associtated with T +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_config( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + FAPI_INF("%s loading MCBIST Config 0x%016lx", mss::c_str(i_target), i_program.iv_config); + + // Copy the program's config settings - we want to modify them if we're in sim. + fapi2::buffer<uint64_t> l_config = i_program.iv_config; + + // If we're running in Cronus, there is no interrupt so any attention bits will + // hang something somewhere. Make sure there's nothing in this config which can + // turn on attention bits unless we're running in hostboot +#ifndef __HOSTBOOT_MODULE + + if(TT::CFG_ENABLE_ATTN_SUPPORT == mss::states::YES) + { + l_config.template clearBit<TT::CFG_ENABLE_HOST_ATTN>(); + l_config.template clearBit<TT::CFG_ENABLE_SPEC_ATTN>(); + } + +#endif + + FAPI_TRY( fapi2::putScom(i_target, TT::CFGQ_REG, l_config) ); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Load the mcbist control register +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_control( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + FAPI_INF("loading MCBIST Control 0x%016lx for %s", i_program.iv_control, mss::c_str(i_target)); + return fapi2::putScom(i_target, TT::CNTLQ_REG, i_program.iv_control); +} + + +/// +/// @brief Load the address generator config +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_addr_gen( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + FAPI_INF("loading MCBIST Address Generation 0x%016lx for %s", i_program.iv_addr_gen, mss::c_str(i_target)); + return fapi2::putScom(i_target, TT::MCBAGRAQ_REG, i_program.iv_addr_gen); +} + +/// +/// @brief Configure address range based on index +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start 64b right-aligned address +/// @param[in] i_end 64b right-aligned address +/// @param[in] i_index which start/end pair to effect +/// @return FAPI2_RC_SUCCSS iff ok +/// @note Only the right-most 37 bits of the start/end are used. +/// @warn if address counting mode is enabled in the MCBIST program, these bits are start, len +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode config_address_range( const fapi2::Target<T>& i_target, + const uint64_t i_start, + const uint64_t i_end, + const uint64_t i_index ) +{ + FAPI_INF("config MCBIST address range %d start: 0x%016lx (0x%016lx), end/len 0x%016lx (0x%016lx)", + i_index, + i_start, (i_start << mss::mcbist::address::MAGIC_PAD), + i_end, (i_end << mss::mcbist::address::MAGIC_PAD), + mss::c_str(i_target)); + FAPI_ASSERT( i_index < TT::ADDRESS_PAIRS, + fapi2::MSS_MCBIST_INVALID_ADDRESS_PAIR_INDEX(). + set_INDEX(i_index). + set_MC_TYPE(MC). + set_TARGET(i_target), + "An invalid address pair index %d for %s", i_index, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::address_pairs[i_index].first, i_start << mss::mcbist::address::MAGIC_PAD) ); + FAPI_TRY( fapi2::putScom(i_target, TT::address_pairs[i_index].second, i_end << mss::mcbist::address::MAGIC_PAD) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Configure address range 0 +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start 64b right-aligned address +/// @param[in] i_end 64b right-aligned address +/// @return FAPI2_RC_SUCCSS iff ok +/// @note Only the right-most 37 bits of the start/end are used. +/// @warn if address counting mode is enabled in the MCBIST program, these bits are start, len +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode config_address_range0( const fapi2::Target<T>& i_target, + const uint64_t i_start, + const uint64_t i_end ) +{ + return config_address_range<MC>(i_target, i_start, i_end, 0); +} + + +/// +/// @brief Configure address range 1 +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start 64b right-aligned address +/// @param[in] i_end 64b right-aligned address +/// @return FAPI2_RC_SUCCSS iff ok +/// @note Only the right-most 37 bits of the start/end are used. +/// @warn if address counting mode is enabled in the MCBIST program, these bits are start, len +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode config_address_range1( const fapi2::Target<T>& i_target, + const uint64_t i_start, + const uint64_t i_end ) +{ + return config_address_range<MC>(i_target, i_start, i_end, 1); +} + + +/// +/// @brief Configure address range 2 +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start 64b right-aligned address +/// @param[in] i_end 64b right-aligned address +/// @return FAPI2_RC_SUCCSS iff ok +/// @note Only the right-most 37 bits of the start/end are used. +/// @warn if address counting mode is enabled in the MCBIST program, these bits are start, len +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode config_address_range2( const fapi2::Target<T>& i_target, + const uint64_t i_start, + const uint64_t i_end ) +{ + return config_address_range<MC>(i_target, i_start, i_end, 2); +} + + +/// +/// @brief Configure address range 3 +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start 64b right-aligned address +/// @param[in] i_end 64b right-aligned address +/// @return FAPI2_RC_SUCCSS iff ok +/// @note Only the right-most 37 bits of the start/end are used. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode config_address_range3( const fapi2::Target<T>& i_target, + const uint64_t i_start, + const uint64_t i_end ) +{ + return config_address_range<MC>(i_target, i_start, i_end, 3); +} + +/// +/// @brief Start or stop the MCBIST engine +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start_stop bool START for starting, STOP otherwise +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode start_stop( const fapi2::Target<T>& i_target, const bool i_start_stop ) +{ + // This is the same as the CCS start_stop ... perhaps we need one template for all + // 'engine' control functions? BRS + fapi2::buffer<uint64_t> l_buf; + FAPI_TRY(fapi2::getScom(i_target, TT::CNTLQ_REG, l_buf)); + + FAPI_TRY( fapi2::putScom(i_target, TT::CNTLQ_REG, + i_start_stop ? l_buf.setBit<TT::MCBIST_START>() : l_buf.setBit<TT::MCBIST_STOP>()) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Resume the MCBIST engine +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode resume( const fapi2::Target<T>& i_target ) +{ + fapi2::buffer<uint64_t> l_buf; + + FAPI_TRY( fapi2::getScom(i_target, TT::CNTLQ_REG, l_buf) ); + FAPI_TRY( fapi2::putScom(i_target, TT::CNTLQ_REG, l_buf.setBit<TT::MCBIST_RESUME>()) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reset the MCBIST error logs +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode reset_errors( const fapi2::Target<T>& i_target ) +{ + fapi2::buffer<uint64_t> l_buf; + + FAPI_TRY( fapi2::getScom(i_target, TT::CNTLQ_REG, l_buf) ); + FAPI_TRY( fapi2::putScom(i_target, TT::CNTLQ_REG, l_buf.setBit<TT::MCBIST_RESET_ERRORS>()) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return whether or not the MCBIST engine has an operation in progress +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[out] o_in_progress - false if no operation is in progress +/// @return FAPI2_RC_SUCCESS if getScom succeeded +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode in_progress( const fapi2::Target<T>& i_target, bool& o_in_progress ) +{ + fapi2::buffer<uint64_t> l_buf; + + FAPI_TRY(fapi2::getScom(i_target, TT::STATQ_REG, l_buf)); + o_in_progress = l_buf.getBit<TT::MCBIST_IN_PROGRESS>(); + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load a set of MCBIST subtests in to the MCBIST registers +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_mcbmr( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Leave if there are no subtests. + if (0 == i_program.iv_subtests.size()) + { + FAPI_INF("no subtests, nothing to do for %s", mss::c_str(i_target)); + return fapi2::current_err; + } + + // List of the 8 MCBIST registers - each holds 4 subtests. + const std::vector< uint64_t > l_memory_registers = + { + TT::MCBMR0_REG, TT::MCBMR1_REG, TT::MCBMR2_REG, TT::MCBMR3_REG, + TT::MCBMR4_REG, TT::MCBMR5_REG, TT::MCBMR6_REG, TT::MCBMR7_REG, + }; + + std::vector< uint64_t > l_memory_register_buffers = + { + 0, 0, 0, 0, 0, 0, 0, 0, + }; + + ssize_t l_bin = -1; + size_t l_register_shift = 0; + + // We'll shift this in to position to indicate which subtest is the last + const uint64_t l_done_bit( 0x8000000000000000 >> TT::DONE ); + + // For now limit MCBIST programs to 32 subtests. + const auto l_program_size = i_program.iv_subtests.size(); + FAPI_ASSERT( l_program_size <= TT::SUBTEST_PER_PROGRAM, + fapi2::MSS_MCBIST_PROGRAM_TOO_BIG(). + set_PROGRAM_LENGTH(l_program_size). + set_TARGET(i_target). + set_MC_TYPE(MC), + "mcbist program of length %d exceeds arbitrary maximum of %d", l_program_size, TT::SUBTEST_PER_PROGRAM ); + + // Distribute the program over the 8 MCBIST subtest registers + // We need the index, so increment thru i_program.iv_subtests.size() + for (size_t l_index = 0; l_index < l_program_size; ++l_index) + { + l_bin = (l_index % TT::SUBTEST_PER_REG) == 0 ? l_bin + 1 : l_bin; + l_register_shift = (l_index % TT::SUBTEST_PER_REG) * TT::BITS_IN_SUBTEST; + + l_memory_register_buffers[l_bin] |= + (uint64_t(i_program.iv_subtests[l_index].iv_mcbmr) << TT::LEFT_SHIFT) >> l_register_shift; + + FAPI_DBG("putting subtest %d (0x%x) in MCBMR%dQ shifted %d 0x%016llx", + l_index, i_program.iv_subtests[l_index].iv_mcbmr, l_bin, + l_register_shift, l_memory_register_buffers[l_bin]); + } + + // l_bin and l_register_shift are the values for the last subtest we'll tell the MCBIST about. + // We need to set that subtest's done-bit so the MCBIST knows it's the end of the line + l_memory_register_buffers[l_bin] |= l_done_bit >> l_register_shift; + FAPI_DBG("setting MCBMR%dQ subtest %llu as the last subtest 0x%016llx", + l_bin, l_register_shift, l_memory_register_buffers[l_bin]); + + // ... and slam the values in to the registers. + // Could just decrement l_bin, but that scoms the subtests in backwards and is confusing + for (auto l_index = 0; l_index <= l_bin; ++l_index) + { + FAPI_TRY( fapi2::putScom(i_target, l_memory_registers[l_index], l_memory_register_buffers[l_index]) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load a set of MCBIST address map registers +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_mcbamr( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + // Vector? Can decide when we fully understand the methods to twiddle the maps themselves. BRS + FAPI_INF("load MCBIST address map register 0: 0x%016lx for %s", i_program.iv_addr_map0, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::MCBAMR0A0Q_REG, i_program.iv_addr_map0) ); + + FAPI_INF("load MCBIST address map register 1: 0x%016lx for %s", i_program.iv_addr_map1, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::MCBAMR1A0Q_REG, i_program.iv_addr_map1) ); + + FAPI_INF("load MCBIST address map register 2: 0x%016lx for %s", i_program.iv_addr_map2, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::MCBAMR2A0Q_REG, i_program.iv_addr_map2) ); + + FAPI_INF("load MCBIST address map register 3: 0x%016lx for %s", i_program.iv_addr_map3, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::MCBAMR3A0Q_REG, i_program.iv_addr_map3) ); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Load MCBIST Memory Parameter Register +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_mcbparm( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + FAPI_INF("load MCBIST parameter register: 0x%016lx for %s", i_program.iv_parameters, mss::c_str(i_target)); + return fapi2::putScom(i_target, TT::MCBPARMQ_REG, i_program.iv_parameters); +} + +/// +/// @brief Clear mcbist errors +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target fapi2::Target<T> of the MCBIST +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode clear_errors( const fapi2::Target<T> i_target ) +{ + // TK: Clear the more detailed errors checked above + FAPI_INF("Clear MCBIST error state for %s", mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::MCBSTATQ_REG, 0) ); + FAPI_TRY( fapi2::putScom(i_target, TT::SRERR0_REG, 0) ); + FAPI_TRY( fapi2::putScom(i_target, TT::SRERR1_REG, 0) ); + FAPI_TRY( fapi2::putScom(i_target, TT::FIRQ_REG, 0) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Helper for assembling the ecc/spare pattern +/// @param[in] i_data the data pattern for a single beat +/// @param[in] i_invert true if the data should be inverted +/// @return The data pattern for this beat (a single byte of dataa) +/// +inline uint8_t generate_eccspare_pattern_helper(const uint64_t& i_data, const bool i_invert ) +{ + fapi2::buffer<uint64_t> l_data(i_invert ? ~i_data : i_data); + uint8_t l_byte = 0; + l_data.extractToRight<0, BITS_PER_BYTE>(l_byte); + return l_byte; +} + +/// +/// @brief Generates the pattern for the ECC and/or spare data +/// @param[in] i_pattern the pattern on which to operate +/// @param[in] i_invert true if the pattern should be inverted +/// @return ECC/spare pattern as needing to be put into the ECC/spare registers +/// +inline fapi2::buffer<uint64_t> generate_eccspare_pattern(const pattern& i_pattern, const bool i_invert ) +{ + constexpr uint64_t BYTE0 = BITS_PER_BYTE * 0; + constexpr uint64_t BYTE1 = BITS_PER_BYTE * 1; + constexpr uint64_t BYTE2 = BITS_PER_BYTE * 2; + constexpr uint64_t BYTE3 = BITS_PER_BYTE * 3; + constexpr uint64_t BYTE4 = BITS_PER_BYTE * 4; + constexpr uint64_t BYTE5 = BITS_PER_BYTE * 5; + constexpr uint64_t BYTE6 = BITS_PER_BYTE * 6; + constexpr uint64_t BYTE7 = BITS_PER_BYTE * 7; + + fapi2::buffer<uint64_t> l_pattern; + + // Pattern assembly is a tad weird for ECC/spare + // The pattern is stored in the same register by byte + // So we want to keep the same data as the rest of the data + // As such, we want to grab each piece of data on a byte by byte basis, flip as needed, and append it to the pattern + + // Beat 0/1 + l_pattern.insertFromRight<BYTE0, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[0].first, i_invert)); + l_pattern.insertFromRight<BYTE1, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[0].second, i_invert)); + + // Beat 2/3 + l_pattern.insertFromRight<BYTE2, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[1].first, i_invert)); + l_pattern.insertFromRight<BYTE3, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[1].second, i_invert)); + + // Beat 4/5 + l_pattern.insertFromRight<BYTE4, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[2].first, i_invert)); + l_pattern.insertFromRight<BYTE5, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[2].second, i_invert)); + + // Beat 6/7 + l_pattern.insertFromRight<BYTE6, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[3].first, i_invert)); + l_pattern.insertFromRight<BYTE7, BITS_PER_BYTE>(generate_eccspare_pattern_helper(i_pattern[3].second, i_invert)); + + return l_pattern; +} + +/// +/// @brief Load MCBIST ECC (and?) spare data pattern given a pattern +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_pattern an mcbist::patterns +/// @param[in] i_invert whether to invert the pattern or not +/// @note this overload disappears when we have real patterns. +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_eccspare_pattern( const fapi2::Target<T>& i_target, const pattern& i_pattern, + const bool i_invert ); + +/// +/// @brief Load MCBIST pattern given a pattern +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_pattern an mcbist::patterns +/// @param[in] i_invert whether to invert the pattern or not +/// @note this overload disappears when we have real patterns. +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_pattern( const fapi2::Target<T>& i_target, const pattern& i_pattern, const bool i_invert ) +{ + uint64_t l_address = TT::PATTERN0_REG; + + // Checks that the pattern is of the expected length, if not, error out + // Creates helpers for FAPI_ASSERT + const auto EXPECTED_SIZE = TT::EXPECTED_PATTERN_SIZE; + const auto ACTUAL_SIZE = i_pattern.size(); + FAPI_ASSERT(EXPECTED_SIZE == ACTUAL_SIZE, + fapi2::MSS_MCBIST_INCORRECT_PATTERN_LENGTH() + .set_TARGET(i_target) + .set_EXPECTED(EXPECTED_SIZE) + .set_ACTUAL(ACTUAL_SIZE), + "%s pattern expected size %u != actual size %u", + mss::c_str(i_target), EXPECTED_SIZE, ACTUAL_SIZE); + + // TK: algorithm for patterns which include ECC bits in them + // Loop over the cache lines in the pattern. We write one half of the cache line + // to the even register and half to the odd. + for (const auto& l_cache_line : i_pattern) + { + const fapi2::buffer<uint64_t> l_value_first = i_invert ? ~l_cache_line.first : l_cache_line.first; + const fapi2::buffer<uint64_t> l_value_second = i_invert ? ~l_cache_line.second : l_cache_line.second; + FAPI_INF("Loading cache line pattern 0x%016lx 0x%016lx for %s", l_value_first, l_value_second, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, l_address, l_value_first) ); + FAPI_TRY( fapi2::putScom(i_target, ++l_address, l_value_second) ); + ++l_address; + } + + FAPI_TRY(load_eccspare_pattern<MC>( i_target, i_pattern, i_invert )); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Check if patter is valid and get the index/invert +/// @param[in] i_pattern the pattern +/// @param[out] o_index the pattern index +/// @param[out] o_invert if pattern is inverted +/// @return FAPI2_RC_SUCCSS iff ok +/// +inline fapi2::ReturnCode get_pattern( uint64_t i_pattern, uint64_t& o_pattern_index, bool& o_invert) +{ + if (NO_PATTERN != i_pattern) + { + o_invert = false; + + // Sanity check the pattern since they're just numbers. + // Belt-and-suspenders FAPI_ASSERT as the sim-only uses this API directly. + FAPI_ASSERT( i_pattern <= mcbist::LAST_PATTERN, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(i_pattern), + "Attempting to load a pattern which does not exist %d", i_pattern ); + + // The indexes are split in to even and odd where the odd indexes don't really exist. + // They're just indicating that we want to grab the even index and invert it. So calculate + // the proper vector index and acknowledge the inversion if necessary. + if (mss::is_odd(i_pattern)) + { + o_invert = true; + i_pattern = i_pattern - 1; + } + + o_pattern_index = i_pattern / 2; + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load MCBIST pattern given an index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_index the pattern index +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_pattern( const fapi2::Target<T>& i_target, const uint64_t i_pattern ) +{ + if (NO_PATTERN != i_pattern) + { + bool l_invert = false; + uint64_t l_pattern_index = 0; + + FAPI_TRY(get_pattern(i_pattern, l_pattern_index, l_invert)); + return load_pattern(i_target, patterns[l_pattern_index], l_invert); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load MCBIST pattern given an index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_pattern( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + return load_pattern(i_target, i_program.iv_pattern); +} + +/// +/// @brief Load MCBIST maint pattern given a pattern +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @tparam MC the mc type of the T +/// @param[in] i_target the target to effect +/// @param[in] i_pattern an mcbist::patterns +/// @param[in] i_invert whether to invert the pattern or not +/// @note this overload disappears when we have real patterns. +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_maint_pattern( const fapi2::Target<T>& i_target, const pattern& i_pattern, const bool i_invert ) +{ + // The scom registers are in the port target. PT: port traits + using PT = mcbistTraits<MC, TT::PORT_TYPE>; + + // Init the fapi2 return code + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Array access control + fapi2::buffer<uint64_t> l_aacr; + // Array access data + fapi2::buffer<uint64_t> l_aadr; + + // first we must setup the access control register + // Setup the array address + // enable the auto increment bit + // set ecc mode bit on + l_aacr + .template writeBit<PT::RMW_WRT_BUFFER_SEL>(mss::states::OFF) + .template insertFromRight<PT::RMW_WRT_ADDRESS, PT::RMW_WRT_ADDRESS_LEN>(PT::MAINT_DATA_INDEX_START) + .template writeBit<PT::RMW_WRT_AUTOINC>(mss::states::ON) + .template writeBit<PT::RMW_WRT_ECCGEN>(mss::states::ON); + + // This loop will be run twice to write the pattern twice. Once per 64B write. + // When MCBIST maint mode is in 64B mode it will only use the first 64B when in 128B mode + // MCBIST maint will use all 128B (it will perform two consecutive writes) + const auto l_ports = mss::find_targets<TT::PORT_TYPE>(i_target); + // Init the port map + + for (const auto& p : l_ports) + { + l_aacr.template insertFromRight<PT::RMW_WRT_ADDRESS, PT::RMW_WRT_ADDRESS_LEN>(PT::MAINT_DATA_INDEX_START); + + for (auto l_num_writes = 0; l_num_writes < 2; ++l_num_writes) + { + FAPI_INF("Setting the array access control register for %s.", mss::c_str(p)); + FAPI_TRY( fapi2::putScom(p, PT::RMW_WRT_BUF_CTL_REG, l_aacr) ); + + for (const auto& l_cache_line : i_pattern) + { + fapi2::buffer<uint64_t> l_value_first = i_invert ? ~l_cache_line.first : l_cache_line.first; + fapi2::buffer<uint64_t> l_value_second = i_invert ? ~l_cache_line.second : l_cache_line.second; + FAPI_INF("Loading cache line pattern 0x%016lx 0x%016lx for %s", l_value_first, l_value_second, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(p, PT::RMW_WRT_BUF_DATA_REG, l_value_first)); + + // In order for the data to actually be written into the RMW buffer, we must issue a putscom to the MCA_AAER register + // This register is used for the ECC, we will just write all zero to this register. The ECC will be auto generated + // when the aacr MCA_WREITE_AACR_ECCGEN bit is set + FAPI_TRY( fapi2::putScom(p, PT::RMW_WRT_BUF_ECC_REG, 0) ); + + // No need to increment the address because the logic does it automatically when MCA_WREITE_AACR_AUTOINC is set + FAPI_TRY( fapi2::putScom(p, PT::RMW_WRT_BUF_DATA_REG, l_value_second) ); + + // In order for the data to actually be written into the RMW buffer, we must issue a putscom to the MCA_AAER register + // This register is used for the ECC, we will just write all zero to this register. The ECC will be auto generated + // when the aacr MCA_WREITE_AACR_ECCGEN bit is set + FAPI_TRY( fapi2::putScom(p, PT::RMW_WRT_BUF_ECC_REG, 0) ); + } + + l_aacr.template insertFromRight<PT::RMW_WRT_ADDRESS, PT::RMW_WRT_ADDRESS_LEN>(PT::MAINT_DATA_INDEX_END); + } + } + +fapi_try_exit: + return fapi2::current_err; +} +/// +/// @brief Load MCBIST maint pattern given an index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_index the pattern index +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_maint_pattern( const fapi2::Target<T>& i_target, uint64_t i_pattern ) +{ + if (NO_PATTERN != i_pattern) + { + bool l_invert = false; + uint64_t l_pattern_index = 0; + + FAPI_TRY(get_pattern(i_pattern, l_pattern_index, l_invert)); + return load_maint_pattern(i_target, patterns[l_pattern_index], l_invert); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load MCBIST Maint mode pattern given an index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_maint_pattern( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + return load_maint_pattern(i_target, i_program.iv_pattern); +} + + +/// +/// @brief Load MCBIST 24b random data seeds given a pattern index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_random24_data_seed mcbist::random24_data_seed +/// @param[in] i_random24_map mcbist::random24_seed_map +/// @param[in] i_invert whether to invert the pattern or not +/// @note this overload disappears when we have real patterns. +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_random24b_seeds( const fapi2::Target<T>& i_target, + const random24_data_seed& i_random24_data_seed, + const random24_seed_map& i_random24_map, + const bool i_invert ) +{ + // Init the fapi2 return code + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + const uint64_t l_random_addr0 = TT::RANDOM_DATA_SEED0; + const uint64_t l_random_addr1 = TT::RANDOM_DATA_SEED1; + uint64_t l_index = 0; + uint64_t l_map_index = 0; + uint64_t l_map_offset = 0; + + fapi2::buffer<uint64_t> l_mcbrsd0q; + fapi2::buffer<uint64_t> l_mcbrsd1q; + + + // We are going to loop through the random seeds and load them into the random seed registers + // Because the 24b random data seeds share the same registers as the 24b random data byte LFSR maps + // we will load those as well + + for (const auto& l_seed : i_random24_data_seed) + { + FAPI_INF("Loading 24b random seed index %ld for %s", l_index, mss::c_str(i_target)); + fapi2::buffer<uint64_t> l_value = i_invert ? ~l_seed : l_seed; + + // Print an informational message to indicate if a random seed is 0 + // TK Do we want an error here? 0 may be used on purpose to hold a byte at all 0 on purpose + if ( l_value == 0 ) + { + FAPI_INF("Warning: Random 24b data seed is set to 0 for seed index %d for %s", l_index, mss::c_str(i_target)); + } + + // If we are processing the first 24b random data seed we will add it to the fapi buffer + // we won't load it yet because the second 24b seed will be loaded into the same register + if ( l_index == 0 ) + { + l_mcbrsd0q.insertFromRight<TT::CFG_DGEN_RNDD_SEED0, TT::CFG_DGEN_RNDD_SEED0_LEN>(l_value); + } + // The second 24b random data seed is loaded into the same register as the first seed + // therefore we will add the second seed to the fapi buffer and then issue the putscom + else if (l_index == 1 ) + { + l_mcbrsd0q.insertFromRight<TT::CFG_DGEN_RNDD_SEED1, TT::CFG_DGEN_RNDD_SEED1_LEN>(l_value); + FAPI_INF("Loading 24b random seeds 0 and 1 0x%016lx for %s", l_mcbrsd0q, mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, l_random_addr0, l_mcbrsd0q) ); + } + // The third 24b random data seed occupies the same register as the random data byte maps. Therefore we first + // add the third random 24b data seed to the register and then loop through all of the byte mappings a total of + // 9. ach of the byte mappings associates a byte of the random data to a byte in the 24b random data LFSRs + // Each byte map is offset by 4 bits in the register. + else + { + l_mcbrsd1q.insertFromRight<TT::CFG_DGEN_RNDD_SEED2, TT::CFG_DGEN_RNDD_SEED2_LEN>(l_value); + + for (const auto& l_map : i_random24_map) + { + l_map_offset = TT::CFG_DGEN_RNDD_DATA_MAPPING + (l_map_index * RANDOM24_SEED_MAP_FIELD_LEN); + FAPI_TRY(l_mcbrsd1q.insertFromRight(l_map, l_map_offset, RANDOM24_SEED_MAP_FIELD_LEN)); + FAPI_INF("Loading 24b random seed map index %ld for %s", l_map_index, mss::c_str(i_target)); + FAPI_ASSERT( l_map_index < mss::mcbist::MAX_NUM_RANDOM24_MAPS, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(l_map_index). + set_MC_TYPE(MC), + "Attempting to load a 24b random data seed map which does not exist %d", l_map_index ); + ++l_map_index; + } + + FAPI_TRY( fapi2::putScom(i_target, l_random_addr1, l_mcbrsd1q) ); + } + + FAPI_ASSERT( l_index < MAX_NUM_RANDOM24_SEEDS, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(l_index). + set_MC_TYPE(MC), + "Attempting to load a 24b random data seed which does not exist %d", l_index ); + ++l_index; + } + +fapi_try_exit: + return fapi2::current_err; +} + + + + +/// +/// @brief Load MCBIST 24b Random data seeds given a pattern index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_data_seed the 24b random data seed index +/// @param[in] i_seed_map the 24b random data map index +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_random24b_seeds( const fapi2::Target<T>& i_target, uint64_t i_data_seed, + uint64_t i_seed_map ) +{ + if ((NO_RANDOM24_SEEDS != i_data_seed) && (NO_RANDOM24_SEED_MAP != i_seed_map)) + { + bool l_invert = false; + + // TK Want a new RC for random 24 + // Sanity check the pattern since they're just numbers. + // Belt-and-suspenders FAPI_ASSERT as the sim-only uses this API directly. + FAPI_ASSERT( i_data_seed <= mcbist::LAST_RANDOM24_SEEDS, + fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX(). + set_INDEX(i_data_seed). + set_MC_TYPE(MC), + "Attempting to load a 24b random data seed set which does not exist %d", i_data_seed ); + + // The indexes are split in to even and odd where the odd indexes don't really exist. + // They're just indicating that we want to grab the even index and invert it. So calculate + // the proper vector index and acknowledge the inversion if necessary. + if ((i_data_seed % 2) != 0) + { + l_invert = true; + i_data_seed -= 1; + } + + return load_random24b_seeds(i_target, random24_data_seeds[i_data_seed / 2], random24_seed_maps[i_seed_map], l_invert); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load MCBIST 24b Random data seeds given a program conatining a pattern index +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_random24b_seeds( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + return load_random24b_seeds(i_target, i_program.iv_random24_data_seed, i_program.iv_random24_seed_map); +} + +/// +/// @brief Loads the FIFO value if needed +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_fifo_mode( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + // Checks if FIFO mode is required by checking all subtests + const auto l_subtest_it = std::find_if(i_program.iv_subtests.begin(), + i_program.iv_subtests.end(), []( const mss::mcbist::subtest_t<MC, T, TT>& i_rhs) -> bool + { + return i_rhs.fifo_mode_required(); + }); + + // if the FIFO load is not needed (no subtest requiring it was found), just exit out + if(l_subtest_it == i_program.iv_subtests.end()) + { + return fapi2::FAPI2_RC_SUCCESS; + } + + // Turns on FIFO mode + constexpr mss::states FIFO_ON = mss::states::ON; + + FAPI_TRY( configure_wrq(i_target, FIFO_ON) ); + FAPI_TRY( configure_rrq(i_target, FIFO_ON) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Load MCBIST data patterns and configuration +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_data_config( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + // First load the data pattern registers + FAPI_INF("Loading the data pattern seeds for %s!", mss::c_str(i_target)); + FAPI_TRY( mss::mcbist::load_pattern(i_target, i_program.iv_pattern) ); + + // Load the 24b random data pattern seeds registers + FAPI_INF("Loading the 24b Random data pattern seeds for %s!", mss::c_str(i_target)); + FAPI_TRY( mss::mcbist::load_random24b_seeds(i_target, i_program.iv_random24_data_seed, + i_program.iv_random24_seed_map) ); + + // Load the maint data pattern into the Maint entry in the RMW buffer + // TK Might want to only load the RMW buffer if maint commands are present in the program + // The load takes 33 Putscoms to load 16 64B registers, might slow down mcbist programs that + // don't need the RMW buffer maint entry loaded + FAPI_INF("Loading the maint data pattern into the RMW buffer for %s!", mss::c_str(i_target)); + FAPI_TRY( mss::mcbist::load_maint_pattern(i_target, i_program.iv_pattern) ); + + FAPI_INF("Loading the data rotate config and seeds for %s!", mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::DATA_ROTATE_CNFG_REG, i_program.iv_data_rotate_cnfg) ); + FAPI_TRY( fapi2::putScom(i_target, TT::DATA_ROTATE_SEED_REG, i_program.iv_data_rotate_seed) ); + +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief Load MCBIST data compare mask registers +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode load_data_compare_mask( const fapi2::Target<T>& i_target, + const mcbist::program<MC>& i_program ) +{ + // Init the fapi2 return code + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Load the MCBCM Data compare masks + const auto l_ports = mss::find_targets<TT::PORT_TYPE>(i_target); + FAPI_INF("Loading the MCBIST data compare mask registers!"); + + for (const auto& p : l_ports) + { + FAPI_TRY( fapi2::putScom(p, TT::COMPARE_MASK, i_program.iv_compare_mask) ); + } + +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief Load MCBIST Thresholds +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_thresholds the thresholds +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_thresholds( const fapi2::Target<T>& i_target, const uint64_t i_thresholds ) +{ + FAPI_INF("load MCBIST threshold register: 0x%016lx for %s", i_thresholds, mss::c_str(i_target) ); + return fapi2::putScom(i_target, TT::THRESHOLD_REG, i_thresholds); +} + +/// +/// @brief Load MCBIST Threshold Register +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the program containing the thresholds +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode load_thresholds( const fapi2::Target<T>& i_target, const mcbist::program<MC>& i_program ) +{ + return load_thresholds(i_target, i_program.iv_thresholds); +} + +/// +/// @brief Read entries from MCBIST Read Modify Write (RMW) array +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start_addr the array address to read first +/// @param[in] i_num_entries the number of array entries to read +/// @param[in] i_roll_over_for_compare_mode set to true if only using first +/// NUM_COMPARE_INFO_ENTRIES of array, so array address rolls over at correct value +/// @param[out] o_data vector of output data +/// @param[out] o_ecc_data vector of ecc data +/// @return FAPI2_RC_SUCCSS iff ok +/// @note The number of entries in the array depends on i_roll_over_for_compare_mode parameter: +/// (NUM_COMPARE_LOG_ENTRIES for false, NUM_COMPARE_INFO_ENTRIES for true) but user may read more than +/// that since reads work in a circular buffer fashion +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode read_rmw_array(const fapi2::Target<T>& i_target, + const uint64_t i_start_addr, + const uint64_t i_num_entries, + const bool i_roll_over_for_compare_mode, + std::vector< fapi2::buffer<uint64_t> >& o_data, + std::vector< fapi2::buffer<uint64_t> >& o_ecc_data) +{ + fapi2::buffer<uint64_t> l_control_value; + fapi2::buffer<uint64_t> l_data; + uint64_t l_array_addr = i_start_addr; + + // Clear out any stale values from output vectors + o_data.clear(); + o_ecc_data.clear(); + + // Set the control register for the RMW array + l_control_value.template writeBit<TT::RMW_WRT_BUFFER_SEL>(TT::SELECT_RMW_BUFFER) + // set start address + .template insertFromRight<TT::RMW_WRT_ADDRESS, TT::RMW_WRT_ADDRESS_LEN>(l_array_addr) + // enable the auto increment bit + .template setBit<TT::RMW_WRT_AUTOINC>() + // set ecc mode bit off + .template clearBit<TT::RMW_WRT_ECCGEN>(); + + FAPI_INF("Setting the RMW array access control register for %s.", mss::c_str(i_target)); + FAPI_TRY( fapi2::putScom(i_target, TT::RMW_WRT_BUF_CTL_REG, l_control_value) ); + + for (uint8_t l_index = 0; l_index < i_num_entries; ++l_index) + { + // Read info out of RMW array and put into output vector + // Note that since we enabled AUTOINC above, reading ECC_REG will increment + // the array pointer so the next DATA_REG read will read the next array entry + FAPI_TRY( fapi2::getScom(i_target, TT::RMW_WRT_BUF_DATA_REG, l_data) ); + + FAPI_INF("RMW data index %d is: %016lx for %s", l_array_addr, l_data, mss::c_str(i_target)); + o_data.push_back(l_data); + + // Need to read ecc register to increment array index + FAPI_TRY( fapi2::getScom(i_target, TT::RMW_WRT_BUF_ECC_REG, l_data) ); + o_ecc_data.push_back(l_data); + ++l_array_addr; + + // Need to manually roll over address if we go beyond NUM_COMPARE_INFO_ENTRIES + // Since actual array is bigger than what is used for compare mode + if (i_roll_over_for_compare_mode && + (l_array_addr >= TT::NUM_COMPARE_INFO_ENTRIES)) + { + FAPI_INF("Rolling over the RMW array access control register address from %d to %d for %s.", (i_start_addr + l_index), + 0, mss::c_str(i_target)); + l_control_value.clearBit<TT::RMW_WRT_ADDRESS, TT::RMW_WRT_ADDRESS_LEN>(); + FAPI_TRY( fapi2::putScom(i_target, TT::RMW_WRT_BUF_CTL_REG, l_control_value) ); + l_array_addr = 0; + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Read entries from MCBIST Read Modify Write (RMW) array +/// Overload for the case where o_ecc_data is not needed +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start_addr the array address to read first +/// @param[in] i_num_entries the number of array entries to read +/// @param[in] i_roll_over_for_compare_mode set to true if only using first +/// NUM_COMPARE_INFO_ENTRIES of array, so array address rolls over at correct value +/// @param[out] o_data vector of output data +/// @return FAPI2_RC_SUCCSS iff ok +/// @note The number of entries in the array depends on i_roll_over_for_compare_mode parameter: +/// (NUM_COMPARE_LOG_ENTRIES for false, NUM_COMPARE_INFO_ENTRIES for true) but user may read more than +/// that since reads work in a circular buffer fashion +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +inline fapi2::ReturnCode read_rmw_array(const fapi2::Target<T>& i_target, + const uint64_t i_start_addr, + const uint64_t i_num_entries, + const bool i_roll_over_for_compare_mode, + std::vector< fapi2::buffer<uint64_t> >& o_data) +{ + std::vector< fapi2::buffer<uint64_t> > l_temp; + return read_rmw_array(i_target, i_start_addr, i_num_entries, i_roll_over_for_compare_mode, o_data, l_temp); +} + +/// +/// @brief Configures broadcast mode, if it is needed +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType +/// @param[in] i_target the target to effect +/// @param[in,out] io_program the mcbist::program +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode configure_broadcast_mode(const fapi2::Target<T>& i_target, mcbist::program<MC>& io_program); + +/// +/// @brief Read entries from MCBIST Read Buffer (RB) array +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start_addr the array address to read first +/// @param[in] i_num_entries the number of array entries to read +/// @param[out] o_data vector of output data +/// @param[out] o_ecc_data vector of ecc data +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode read_rb_array(const fapi2::Target<T>& i_target, + const uint64_t i_start_addr, + const uint64_t i_num_entries, + std::vector< fapi2::buffer<uint64_t> >& o_data, + std::vector< fapi2::buffer<uint64_t> >& o_ecc_data); + +/// +/// @brief Read entries from MCBIST Read Buffer (RB) array +/// Overload for the case where o_ecc_data is not needed +/// @tparam MC the mc type of the T +/// @tparam T, the fapi2::TargetType +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_start_addr the array address to read first +/// @param[in] i_num_entries the number of array entries to read +/// @param[out] o_data vector of output data +/// @return FAPI2_RC_SUCCSS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode read_rb_array(const fapi2::Target<T>& i_target, + const uint64_t i_start_addr, + const uint64_t i_num_entries, + std::vector< fapi2::buffer<uint64_t> >& o_data) +{ + std::vector< fapi2::buffer<uint64_t> > l_temp; + return read_rb_array<MC>(i_target, i_start_addr, i_num_entries, o_data, l_temp); +} + +/// +/// @brief Poll the mcbist engine and check for errors +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program, the mcbist program which is executing +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode poll( const fapi2::Target<T>& i_target, const program<MC>& i_program ) +{ + using ET = mss::mcbistMCTraits<MC>; + fapi2::buffer<uint64_t> l_status; + + const uint64_t l_done = fapi2::buffer<uint64_t>().setBit<TT::MCBIST_DONE>(); + const uint64_t l_fail = fapi2::buffer<uint64_t>().setBit<TT::MCBIST_FAIL>(); + const uint64_t l_in_progress = fapi2::buffer<uint64_t>().setBit<TT::MCBIST_IN_PROGRESS>(); + + // A small vector of addresses to poll during the polling loop + const std::vector<mss::poll_probe<T>> l_probes = + { + {i_target, "mcbist current address", TT::LAST_ADDR_REG}, + }; + + mss::poll(i_target, TT::STATQ_REG, i_program.iv_poll, + [&l_status](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("mcbist statq 0x%016lx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + return l_status.getBit<TT::MCBIST_IN_PROGRESS>() != 1; + }, + l_probes); + + // Check to see if we're still in progress - meaning we timed out. + FAPI_ASSERT((l_status & l_in_progress) != l_in_progress, + ET::mcbist_timeout().set_MC_TARGET(i_target), + "MCBIST timed out %s", mss::c_str(i_target)); + + // The control register has a bit for done-and-happy and a bit for done-and-unhappy + if ( ((l_status & l_done) == l_done) || ((l_status & l_fail) == l_fail) ) + { + FAPI_INF("MCBIST completed, processing errors for %s", mss::c_str(i_target)); + + // We're done. It doesn't mean that there were no errors. + FAPI_TRY( i_program.process_errors(i_target) ); + + // If we're here there were no errors, but lets report if the fail bit was set anyway. + FAPI_ASSERT( (l_status & l_fail) != l_fail, + ET::mcbist_unknown_failure() + .set_MC_TARGET(i_target) + .set_STATUS_REGISTER(l_status), + "%s MCBIST reported a fail, but process_errors didn't find it 0x%016llx", + mss::c_str(i_target), l_status ); + + // And if we're here all is good with the world. + return fapi2::current_err; + } + + FAPI_ASSERT(false, + ET::mcbist_data_fail() + .set_MC_TARGET(i_target) + .set_STATUS_REGISTER(l_status), + "%s MCBIST executed but we got corrupted data in the control register 0x%016llx", + mss::c_str(i_target), l_status ); + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Clear the errors helper. Chip can specialise this +/// function to put any necessary workaround in it. +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist program to execute +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode clear_error_helper( const fapi2::Target<T>& i_target, const program<MC>& i_program ) +{ + FAPI_TRY( clear_errors(i_target) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Execute the mcbist program +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target to effect +/// @param[in] i_program the mcbist program to execute +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff OK +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T>, typename ET = mcbistMCTraits<MC> > +fapi2::ReturnCode execute( const fapi2::Target<T>& i_target, const program<MC>& i_program ) +{ + fapi2::buffer<uint64_t> l_status; + bool l_poll_result = false; + poll_parameters l_poll_parameters; + + // Init the fapi2 return code + fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; + + // Before we go off into the bushes, lets see if there are any instructions in the + // program. If not, we can save everyone the hassle + FAPI_ASSERT(0 != i_program.iv_subtests.size(), + fapi2::MSS_MEMDIAGS_NO_MCBIST_SUBTESTS().set_MC_TARGET(i_target), + "Attempt to run an MCBIST program with no subtests on %s", mss::c_str(i_target)); + + FAPI_TRY( clear_error_helper<MC>(i_target, const_cast<program<MC>&>(i_program)) ); + + // Configures the write/read FIFO bit + FAPI_TRY( load_fifo_mode<MC>( i_target, i_program) ); + + // Slam the address generator config + FAPI_TRY( load_addr_gen<MC>(i_target, i_program) ); + + // Slam the parameters in to the mcbist parameter register + FAPI_TRY( load_mcbparm<MC>(i_target, i_program) ); + + // Slam the configured address maps down + FAPI_TRY( load_mcbamr( i_target, i_program) ); + + // Slam the config register down + FAPI_TRY( load_config<MC>( i_target, i_program) ); + + // Slam the control register down + FAPI_TRY( load_control<MC>( i_target, i_program) ); + + // Load the patterns and any associated bits for random, etc + FAPI_TRY( load_data_config<MC>( i_target, i_program) ); + + // Load the thresholds + FAPI_TRY( load_thresholds<MC>( i_target, i_program) ); + + // Slam the subtests in to the mcbist registers + // Always do this last so the action file triggers see the other bits set + FAPI_TRY( load_mcbmr<MC>(i_target, i_program) ); + + // Start the engine, and then poll for completion + FAPI_TRY(start_stop<MC>(i_target, mss::START)); + + // Verify that the in-progress bit has been set, so we know we started + // Don't use the program's poll as it could be a very long time. Use the default poll. + l_poll_result = mss::poll(i_target, TT::STATQ_REG, l_poll_parameters, + [&l_status](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("looking for mcbist start, mcbist statq 0x%llx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + // We're done polling when either we see we're in progress or we see we're done. + return (l_status.getBit<TT::MCBIST_IN_PROGRESS>() == true) || (l_status.getBit<TT::MCBIST_DONE>() == true); + }); + + // So we've either run/are running or we timed out waiting for the start. + FAPI_ASSERT( l_poll_result == true, + ET::memdiags_failed_to_start().set_MC_TARGET(i_target), + "The MCBIST engine failed to start its program" ); + + // If the user asked for async mode, we can leave. Otherwise, poll and check for errors + if (!i_program.iv_async) + { + return mcbist::poll(i_target, i_program); + } + +fapi_try_exit: + return fapi2::current_err; +} + +} // namespace mcbist + +namespace ecc +{ + +/// +/// @brief Clear all MAINT.ECC counters +/// @tparam T the fapi2::TargetType - derived +/// @param[in] i_target the fapi2 target +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS if ok +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode clear_all_counters( const fapi2::Target<T>& i_target ) +{ + return ( mss::mcbist::reset_errors(i_target) ); +} + +} // namespace ecc + +} // namespace mss + +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H new file mode 100644 index 000000000..37e35c48d --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H @@ -0,0 +1,441 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_address.H +/// @brief Class for mcbist related addresses (addresses below the hash translation) +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_ADDRESS_H_ +#define _GEN_MSS_MCBIST_ADDRESS_H_ + +#include <fapi2.H> +#include <utility> +#include <generic/memory/lib/ecc/ecc_traits.H> + +namespace mss +{ + +namespace mcbist +{ + +/// +/// @class address +/// @brief Represents a physical address in memory +/// @note +/// 0:1 port select +/// 2 dimm select +/// 3:4 mrank(0 to 1) +/// 5:7 srank(0 to 2) +/// 8:25 row(0 to 17) +/// 26:32 col(3 to 9) +/// 33:35 bank(0 to 2) +/// 36:37 bank_group(0 to 1) +/// +class address +{ + public: + + // How far over we shift to align the address in either the register or a buffer + static constexpr uint64_t MAGIC_PAD = 26; + + // first is the start bit of the field, second is the length + using field = std::pair<uint64_t, uint64_t>; + + constexpr static field PORT = {0, 2}; + constexpr static field DIMM = {2, 1}; + constexpr static field MRANK = {3, 2}; + constexpr static field SRANK = {5, 3}; + constexpr static field ROW = {8, 18}; + constexpr static field COL = {26, 7}; + constexpr static field BANK = {33, 3}; + constexpr static field BANK_GROUP = {36, 2}; + constexpr static field LAST_VALID = BANK_GROUP; + + /// + /// @brief default ctor + /// + address() = default; + + // Used when accessing an integral value containing a port and DIMM combination + static constexpr uint64_t DIMM_BIT = 63; + static constexpr uint64_t PORT_START = 61; + static constexpr uint64_t PORT_LEN = 2; + + /// + /// @brief Construct an address from a uint64_t + /// @param[in] i_value representing an address + /// + address( const uint64_t i_value ): + iv_address(i_value << MAGIC_PAD) + { + } + + /// + /// @brief Construct an address from the provided buffer + /// @param[in] i_address fapi2::buffer address + /// + address(const fapi2::buffer<uint64_t>& i_address): + iv_address(i_address) + { + } + + /// + /// @brief Conversion operator to uint64_t + /// @warn Right-aligns the address + /// + inline operator uint64_t() const + { + return iv_address >> MAGIC_PAD; + } + + /// + /// @brief Set a field for an address + /// @tparam F the field to set + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + template< const field& F > + inline address& set_field( const uint64_t i_value ) + { + iv_address.insertFromRight<F.first, F.second>(i_value); + return *this; + } + + /// + /// @brief Get a field from an address + /// @tparam F the field to get + /// @return right-aligned uint64_t representing the value + /// + template< const field& F > + inline uint64_t get_field() const + { + uint64_t l_value = 0; + iv_address.extractToRight<F.first, F.second>(l_value); + return l_value; + } + + /// + /// @brief Get a range of addresses. + /// @tparam[in] F the left-most valid field. So, if the address was for master rank, + /// the left-most valid field would be MRANK + /// @param[out] o_end representing an address to end at + /// @note this pointer is the start address + /// + template< const field& F > + inline void get_range( address& o_end ) const + { + constexpr uint64_t START = F.first + F.second; + constexpr uint64_t LEN = (LAST_VALID.first + LAST_VALID.second) - START; + + // All we need to do is fill in the bits to the right of the last valid field + o_end.iv_address = iv_address; + o_end.iv_address.setBit<START, LEN>(); + } + + + /// + /// @brief Get an end address for sim mode + /// @param[out] o_end representing an address to end at + /// @note this pointer is the start address + /// + inline void get_sim_end_address( address& o_end ) const + { + // This magic number represents a range of addresses which cover all + // cache lines the training algorithms touch. By effecting 0 - this end + // address you'll effect everything which has bad ECC in the sim. + // TODO: this will have to change for explorer. it's algorithm dependent. + // we'll need to talk to the microchip about this + constexpr uint64_t l_magic_sim_number = 0b1000000; + + get_range<COL>(o_end); + o_end.set_column(l_magic_sim_number); + return; + } + + /// + /// @brief Get a range of addresses given a master rank + /// @param[in] i_start representing an address to start from + /// @param[out] o_end representing an address to end at + /// + inline static void get_mrank_range( const address& i_start, address& o_end ) + { + i_start.get_range<MRANK>(o_end); + } + + /// + /// @brief Get a range of addresses given a master rank + /// @param[in] i_port representing the port for the starting address + /// @param[in] i_dimm representing the dimm for the starting address + /// @param[in] i_mrank representing the master rank for the starting address + /// @param[out] o_start representing an address to start from + /// @param[out] o_end representing an address to end at + /// + inline static void get_mrank_range( const uint64_t i_port, + const uint64_t i_dimm, + const uint64_t i_mrank, + address& o_start, + address& o_end ) + { + o_start.set_port(i_port).set_dimm(i_dimm).set_master_rank(i_mrank); + get_mrank_range(o_start, o_end); + } + + /// + /// @brief Get a range of addresses given a slave rank + /// @param[in] i_start representing an address to start from + /// @param[out] o_end representing an address to end at + /// + inline static void get_srank_range( const address& i_start, address& o_end ) + { + i_start.get_range<SRANK>(o_end); + } + + /// + /// @brief Get a range of addresses given a slave rank + /// @param[in] i_port representing the port for the starting address + /// @param[in] i_dimm representing the dimm for the starting address + /// @param[in] i_mrank representing the master rank for the starting address + /// @param[in] i_srank representing the slave rank for the starting address + /// @param[out] o_start representing an address to start from + /// @param[out] o_end representing an address to end at + /// + inline static void get_srank_range( const uint64_t i_port, const uint64_t i_dimm, + const uint64_t i_mrank, const uint64_t i_srank, + address& o_start, + address& o_end ) + { + o_start.set_port(i_port).set_dimm(i_dimm).set_master_rank(i_mrank).set_slave_rank(i_srank); + get_srank_range(o_start, o_end); + } + + /// + /// @brief Set the port value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_port( const uint64_t i_value ) + { + return set_field<PORT>(i_value); + } + + /// + /// @brief Get the port value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_port() const + { + return get_field<PORT>(); + } + + /// + /// @brief Set the DIMM value for an address + /// @param[in] i_value the value to set + /// @note 0 is the DIMM[0] != 0 is DIMM[1] + /// @return address& for method chaining + /// + inline address& set_dimm( const uint64_t i_value ) + { + return set_field<DIMM>(i_value); + } + + /// + /// @brief Get the DIMM value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_dimm() const + { + return get_field<DIMM>(); + } + + /// + /// @brief Set the port and DIMM value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// @note Useful for indexing all ports/DIMM on a controller + /// + inline address& set_port_dimm( const fapi2::buffer<uint64_t> i_value ) + { + uint64_t l_read_port = 0; + + i_value.extractToRight<PORT_START, PORT_LEN>(l_read_port); + return set_dimm(i_value.getBit<DIMM_BIT>()).set_port(l_read_port); + } + + /// + /// @brief Get the port and DIMM value for an address + /// @return right-aligned uint64_t representing the value + /// @note Useful for indexing all ports/DIMM on a controller + /// + inline uint64_t get_port_dimm() const + { + fapi2::buffer<uint64_t> l_value; + + l_value.insertFromRight<PORT_START, PORT_LEN>(get_port()); + l_value.writeBit<DIMM_BIT>(get_dimm()); + + return l_value; + } + + /// + /// @brief Set the master rank value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_master_rank( const uint64_t i_value ) + { + return set_field<MRANK>(i_value); + } + + /// + /// @brief Get the master rank value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_master_rank() const + { + return get_field<MRANK>(); + } + + + /// + /// @brief Set the slave rank value for an address + /// @param[in] i_value the value to set + /// + inline void set_slave_rank( const uint64_t i_value ) + { + set_field<SRANK>(i_value); + } + + /// + /// @brief Get the slave rank value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_slave_rank() const + { + return get_field<SRANK>(); + } + + + /// + /// @brief Set the row value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_row( const uint64_t i_value ) + { + return set_field<ROW>(i_value); + } + + /// + /// @brief Get the row value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_row() const + { + return get_field<ROW>(); + } + + + /// + /// @brief Set the column value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_column( const uint64_t i_value ) + { + return set_field<COL>(i_value); + } + + /// + /// @brief Get the column value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_column() const + { + return get_field<COL>(); + } + + + /// + /// @brief Set the bank value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_bank( const uint64_t i_value ) + { + return set_field<BANK>(i_value); + } + + /// + /// @brief Get the bank value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_bank() const + { + return get_field<BANK>(); + } + + /// + /// @brief Set the bank group value for an address + /// @param[in] i_value the value to set + /// @return address& for method chaining + /// + inline address& set_bank_group( const uint64_t i_value ) + { + return set_field<BANK_GROUP>(i_value); + } + + /// + /// @brief Get the bank group value for an address + /// @return right-aligned uint64_t representing the value + /// + inline uint64_t get_bank_group() const + { + return get_field<BANK_GROUP>(); + } + + /// + /// @brief Get the buffer object for bit operations + /// @return fapi2::buffer<uint64_t>& + /// + inline fapi2::buffer<uint64_t> get_address_buffer() const + { + return iv_address; + } + + private: + // We use a fapi2 buffer as it has static compile-time support + fapi2::buffer<uint64_t> iv_address; +}; + +} // close namespace mcbist +} // close namespace mss + +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_ecc_trap_address.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_ecc_trap_address.H new file mode 100644 index 000000000..ba8b1f47b --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_ecc_trap_address.H @@ -0,0 +1,176 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_ecc_trap_address.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_ecc_trap_address.H +/// @brief Class for ECC trap addresses (addresses below the hash translation) +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_ECC_TRAP_ADDRESS_H_ +#define _GEN_MSS_MCBIST_ECC_TRAP_ADDRESS_H_ + +#include <fapi2.H> +#include <utility> +#include <generic/memory/lib/ecc/ecc_traits.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H> + +namespace mss +{ + +namespace ecc +{ + +/// +/// @class trap_address +/// @brief Converts trap address into mcbist::address +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type defaults to fapi2::TARGET_TYPE_MCA or TARGET_TYPE_MEM_PORT +/// @tparam TT traits type defaults to eccTraits<T> +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::FWMS_ADDR_TARGET_TYPE, typename TT = mss::eccTraits<MC, T> > +class trap_address +{ + public: + // first is the start bit of the field, second is the length + using field = std::pair<uint64_t, uint64_t>; + + constexpr static field PORT = {TT::TRAP_ADDRESS_PORT, TT::TRAP_ADDRESS_PORT_LEN}; + constexpr static field DIMM = {TT::TRAP_ADDRESS_DIMM, TT::TRAP_ADDRESS_DIMM_LEN}; + constexpr static field MRANK = {TT::TRAP_ADDRESS_MRANK, TT::TRAP_ADDRESS_MRANK_LEN}; + constexpr static field SRANK = {TT::TRAP_ADDRESS_SRANK, TT::TRAP_ADDRESS_SRANK_LEN}; + constexpr static field ROW = {TT::TRAP_ADDRESS_ROW, TT::TRAP_ADDRESS_ROW_LEN}; + constexpr static field COL = {TT::TRAP_ADDRESS_COL, TT::TRAP_ADDRESS_COL_LEN}; + constexpr static field BANK = {TT::TRAP_ADDRESS_BANK, TT::TRAP_ADDRESS_BANK_LEN}; + constexpr static field BANK_GROUP = {TT::TRAP_ADDRESS_BANK_GROUP, TT::TRAP_ADDRESS_BANK_GROUP_LEN}; + + trap_address() = default; + + /// + /// @brief Construct an address from a uint64_t (scom'ed value) + /// @param[in] i_value representing raw value from FWMS register + /// + trap_address( const uint64_t& i_value ): + iv_value(i_value) + { + } + + /// + /// @brief Construct an address from an mcbist::address + /// @param[in] i_mcbist_address mcbist formatted address + /// @note Construction of mcbist::address from trap_address + /// @note located in mcbist::address class + /// + trap_address( const mcbist::address& i_mcbist_address ) + { + iv_value.insertFromRight<PORT.first, PORT.second>(i_mcbist_address.get_field<mcbist::address::PORT>()); + iv_value.insertFromRight<DIMM.first, DIMM.second>(i_mcbist_address.get_field<mcbist::address::DIMM>()); + iv_value.insertFromRight<MRANK.first, MRANK.second>(i_mcbist_address.get_field<mcbist::address::MRANK>()); + iv_value.insertFromRight<SRANK.first, SRANK.second>(i_mcbist_address.get_field<mcbist::address::SRANK>()); + iv_value.insertFromRight<ROW.first, ROW.second>(i_mcbist_address.get_field<mcbist::address::ROW>()); + iv_value.insertFromRight<COL.first, COL.second>(i_mcbist_address.get_field<mcbist::address::COL>()); + iv_value.insertFromRight<BANK.first, BANK.second>(i_mcbist_address.get_field<mcbist::address::BANK>()); + iv_value.insertFromRight<BANK_GROUP.first, BANK_GROUP.second> + (i_mcbist_address.get_field<mcbist::address::BANK_GROUP>()); + } + + /// + /// @brief Construct an mcbist::address from an ecc::trap_address + /// @return the mcbist::address + /// + inline operator mss::mcbist::address() const + { + mss::mcbist::address l_mcbist_address; + l_mcbist_address.set_port (this->get_field<PORT>()); + l_mcbist_address.set_dimm (this->get_field<DIMM>()); + l_mcbist_address.set_master_rank(this->get_field<MRANK>()); + l_mcbist_address.set_slave_rank (this->get_field<SRANK>()); + l_mcbist_address.set_row (this->get_field<ROW>()); + l_mcbist_address.set_column (this->get_field<COL>()); + l_mcbist_address.set_bank (this->get_field<BANK>()); + l_mcbist_address.set_bank_group (this->get_field<BANK_GROUP>()); + return l_mcbist_address; + } + + /// + /// @brief Conversion operator to uint64_t + /// + inline operator uint64_t() const + { + uint64_t l_temp = 0; + iv_value.extract<TT::TRAP_ADDRESS, TT::TRAP_ADDRESS_LEN, TT::TRAP_ADDRESS>(l_temp); + return l_temp; + } + + /// + /// @brief Get a field from an address + /// @tparam F the field to get + /// @return right-aligned uint64_t representing the value + /// + template< const field& F > + inline uint64_t get_field() const + { + uint64_t l_value = 0; + iv_value.extractToRight<F.first, F.second>(l_value); + return l_value; + } + + private: + fapi2::buffer<uint64_t> iv_value; +}; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::PORT; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::DIMM; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::MRANK; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::SRANK; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::ROW; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::COL; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::BANK; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename trap_address<MC, T, TT>::field trap_address<MC, T, TT>::BANK_GROUP; + + +} // close namespace ecc +} // close namespace mss +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_fwms_address.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_fwms_address.H new file mode 100644 index 000000000..2b1bda8fe --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_fwms_address.H @@ -0,0 +1,175 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_fwms_address.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_fwms_address.H +/// @brief Class for FWMS trap addresses (addresses below the hash translation) +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_FWMS_ADDRESS_H_ +#define _GEN_MSS_MCBIST_FWMS_ADDRESS_H_ + +#include <fapi2.H> +#include <utility> +#include <generic/memory/lib/ecc/ecc_traits.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H> + +namespace mss +{ + +namespace ecc +{ + +namespace fwms +{ + +/// +/// @class address +/// @brief Converts Firmware Mark Store ADDRESS field into mcbist::address +/// @tparam MC the mc type of the T +/// @tparam T fapi2 Target Type defaults to fapi2::TARGET_TYPE_MCA or TARGET_TYPE_MEM_PORT +/// @tparam TT traits type defaults to eccTraits<MC, T> +/// @note template argument defaults are in forward declaration in lib/mcbist/address.H +/// @note 12 = dimm +/// @note 13:14 = mrank +/// @note 15:17 = srank +/// @note 18:19 = bank group +/// @note 20:22 = bank +/// +// See declaration below +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::FWMS_ADDR_TARGET_TYPE, typename TT = mss::eccTraits<MC, T> > +class address +{ + public: + // first is the start bit of the field, second is the length + using field = std::pair<uint64_t, uint64_t>; + + constexpr static field DIMM = {TT::FIRMWARE_MS_ADDRESS, 1}; + constexpr static field MRANK = {TT::FIRMWARE_MS_ADDRESS + 1, 2}; + constexpr static field SRANK = {TT::FIRMWARE_MS_ADDRESS + 3, 3}; + constexpr static field BANK_GROUP = {TT::FIRMWARE_MS_ADDRESS + 6, 2}; + constexpr static field BANK = {TT::FIRMWARE_MS_ADDRESS + 8, 3}; + + address() = default; + + /// + /// @brief Construct an address from a uint64_t (scom'ed value) + /// @param[in] i_value representing raw value from FWMS register + /// + address( const uint64_t& i_value ): + iv_value(i_value) + { + } + + /// + /// @brief Construct an address from an mcbist::address + /// @param[in] i_mcbist_address mcbist formatted address + /// @note Construction of mcbist::address from address + /// @note located in mcbist::address class + /// + address( const mss::mcbist::address& i_mcbist_address ) + { + iv_value.insertFromRight<DIMM.first, DIMM.second>(i_mcbist_address.get_dimm()); + iv_value.insertFromRight<MRANK.first, MRANK.second>(i_mcbist_address.get_master_rank()); + iv_value.insertFromRight<SRANK.first, SRANK.second>(i_mcbist_address.get_slave_rank()); + iv_value.insertFromRight<BANK_GROUP.first, BANK_GROUP.second>(i_mcbist_address.get_bank_group()); + iv_value.insertFromRight<BANK.first, BANK.second>(i_mcbist_address.get_bank()); + } + + /// + /// @brief Construct an address from an ecc::fwms::address + /// @tparam MC the mc type + /// @param[in] i_address representing an address field from a firmware mark store register + /// + + /// + /// @brief Construct an mcbist::address from an ecc::fwms::address + /// @return the mcbist::address + /// + inline operator mss::mcbist::address() const + { + mss::mcbist::address l_mcbist_address; + l_mcbist_address.set_dimm (this->get_field<DIMM>()); + l_mcbist_address.set_master_rank(this->get_field<MRANK>()); + l_mcbist_address.set_slave_rank (this->get_field<SRANK>()); + l_mcbist_address.set_bank_group (this->get_field<BANK_GROUP>()); + l_mcbist_address.set_bank (this->get_field<BANK>()); + return l_mcbist_address; + } + + /// + /// @brief Conversion operator to uint64_t + /// + inline operator uint64_t() const + { + uint64_t l_temp = 0; + iv_value.extract<TT::FIRMWARE_MS_ADDRESS, TT::FIRMWARE_MS_ADDRESS_LEN, TT::FIRMWARE_MS_ADDRESS>(l_temp); + return l_temp; + } + + /// + /// @brief Get a field from an address + /// @tparam F the field to get + /// @return right-aligned uint64_t representing the value + /// + template< const field& F > + inline uint64_t get_field() const + { + uint64_t l_value = 0; + iv_value.extractToRight<F.first, F.second>(l_value); + return l_value; + } + + private: + fapi2::buffer<uint64_t> iv_value; + +}; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename address<MC, T, TT>::field address<MC, T, TT>::DIMM; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename address<MC, T, TT>::field address<MC, T, TT>::MRANK; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename address<MC, T, TT>::field address<MC, T, TT>::SRANK; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename address<MC, T, TT>::field address<MC, T, TT>::BANK_GROUP; + +template< mss::mc_type MC, fapi2::TargetType T , typename TT > +constexpr typename address<MC, T, TT>::field address<MC, T, TT>::BANK; + + +} // close namespace fwms +} // close namespace ecc +} // close namespace mss +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.C b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.C new file mode 100644 index 000000000..81988c3d9 --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.C @@ -0,0 +1,146 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +/// +/// @file gen_mss_mcbist_patterns.C +/// @brief Static definition of MCBIST patterns +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> +#include <vector> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H> + +namespace mss +{ + +namespace mcbist +{ + +/// Vector of cache lines, seaprated in to two 64B chunks +// TK Real patterns from Marc representing the proper bits for ECC checking +const std::vector< pattern > patterns = +{ + // Pattern index 0 (Pattern 1 is this inverted) + { {0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000}, + }, + + // Pattern index 2 (Pattern 3 is this inverted) + { {0x5555555555555555, 0x5555555555555555}, + {0xAAAAAAAAAAAAAAAA, 0xAAAAAAAAAAAAAAAA}, + {0x5555555555555555, 0x5555555555555555}, + {0xAAAAAAAAAAAAAAAA, 0xAAAAAAAAAAAAAAAA}, + }, + + // Pattern index 4 (Pattern 5 is this inverted) + { {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000}, + }, + + // Pattern index 6 (Pattern 7 is this inverted) + { {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0x0000000000000000}, + {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0x0000000000000000}, + }, + + // Pattern index 8 Random Seed + { {0x1234567887654321, 0x8765432112345678}, + {0x1234567887654321, 0x8765432112345678}, + {0x1234567887654321, 0x8765432112345678}, + {0x1234567887654321, 0x8765432112345678}, + }, + + // Pattern index 10 (Pattern 11 is this inverted), MPR pattern + { {0x0000000000000000, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0xFFFFFFFFFFFFFFFF}, + {0x0000000000000000, 0xFFFFFFFFFFFFFFFF}, + }, +}; + +// TK Want a new RC for random 24 +/// Vector of 24b random data seeds +const std::vector< random24_data_seed > random24_data_seeds = +{ + // 24 Bit Pattern index 0 (Pattern 1 is this inverted) + { {0x010203}, + {0x040506}, + {0x070809}, + }, + + // 24 Bit Pattern index 2 (Pattern 3 is this inverted) + { {0x112233}, + {0x445566}, + {0x778899}, + }, + +}; + +/// Vector of 24b random data seed mappings +// Not sure how many mapping we will want, for now it should be sufficient to +// have all bytes point to a different LFSR or all bytes point to the same LFSR +const std::vector< random24_seed_map > random24_seed_maps = +{ + // 8 Bit Pattern index 0 + // This selection maps every data byte to a different random LFSR + { {0x0}, + {0x1}, + {0x2}, + {0x3}, + {0x4}, + {0x5}, + {0x6}, + {0x7}, + {0x8}, + }, + + // 8 Bit Pattern index 1 + // This selection maps every data byte to random LFSR 0 + { {0x0}, + {0x0}, + {0x0}, + {0x0}, + {0x0}, + {0x0}, + {0x0}, + {0x0}, + {0x0}, + }, + +}; + +} //namespace mcbist +} //namespace mss diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H new file mode 100644 index 000000000..cfb4a532d --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H @@ -0,0 +1,103 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_patterns.H +/// @brief Static definition of MCBIST patterns +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_GEN_MCBIST_PATTERNS_ +#define _MSS_GEN_MCBIST_PATTERNS_ + +#include <vector> + +namespace mss +{ +namespace mcbist +{ + +/// Memory diagnostic Pattern indexes. +// Why not an enum? I want to do math on them to generate a proper index in to a vector of +// patterns and enums really don't like that. +// Couple of extra symbols in here to keep things easy if desired +constexpr uint64_t PATTERN_ZEROS = 0; +constexpr uint64_t PATTERN_0 = PATTERN_ZEROS; +constexpr uint64_t PATTERN_ONES = 1; +constexpr uint64_t PATTERN_1 = PATTERN_ONES; +constexpr uint64_t PATTERN_2 = 2; +constexpr uint64_t PATTERN_3 = 3; +constexpr uint64_t PATTERN_4 = 4; +constexpr uint64_t PATTERN_5 = 5; +constexpr uint64_t PATTERN_6 = 6; +constexpr uint64_t PATTERN_7 = 7; +constexpr uint64_t PATTERN_8 = 8; +constexpr uint64_t PATTERN_9 = 9; +constexpr uint64_t PATTERN_10 = 10; +constexpr uint64_t PATTERN_RANDOM = PATTERN_8; +constexpr uint64_t LAST_PATTERN = PATTERN_10; + +// Don't mess with the patterns +constexpr uint64_t NO_PATTERN = LAST_PATTERN + 1; + +//Pattern references for the 24b random data pattern seeds +constexpr uint64_t MAX_NUM_RANDOM24_SEEDS = 3; +constexpr uint64_t RANDOM24_SEEDS_0 = 0; +constexpr uint64_t RANDOM24_SEEDS_1 = 1; +constexpr uint64_t RANDOM24_SEEDS_2 = 2; +constexpr uint64_t RANDOM24_SEEDS_3 = 3; +constexpr uint64_t LAST_RANDOM24_SEEDS = RANDOM24_SEEDS_3; +constexpr uint64_t NO_RANDOM24_SEEDS = LAST_RANDOM24_SEEDS + 1; + +//Pattern references for the 24b random data pattern seeds +constexpr uint64_t MAX_NUM_RANDOM24_MAPS = 9; +constexpr uint64_t RANDOM24_SEED_MAP_0 = 0; +constexpr uint64_t RANDOM24_SEED_MAP_1 = 1; +constexpr uint64_t LAST_RANDOM24_SEED_MAP = RANDOM24_SEED_MAP_1; +constexpr uint64_t NO_RANDOM24_SEED_MAP = LAST_RANDOM24_SEED_MAP + 1; +constexpr uint64_t RANDOM24_SEED_MAP_FIELD_LEN = 4; + + +/// Vector of cache lines, separated in to two 64B chunks +using cache_line = std::pair<uint64_t, uint64_t> ; +using pattern = std::vector< cache_line > ; +extern const std::vector< pattern > patterns; + +// Vector of 24b random data seeds +using random24_data_seed = std::vector< uint64_t > ; +extern const std::vector< random24_data_seed > random24_data_seeds; + +// Vector of 24b random data seed maps +using random24_seed_map = std::vector< uint64_t >; +extern const std::vector< random24_seed_map > random24_seed_maps; + +}// mcbist + +}// mss +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H new file mode 100644 index 000000000..6f570fbad --- /dev/null +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H @@ -0,0 +1,815 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2019 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_settings.H +/// @brief MCBIST settings, like stop conditions, thresholds, etc +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_SETTINGS_H_ +#define _GEN_MSS_MCBIST_SETTINGS_H_ + +#include <fapi2.H> + +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H> +#include <generic/memory/lib/utils/bit_count.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H> + +namespace mss +{ + +namespace mcbist +{ +/// +/// @brief End boundaries for MCBIST programs - where to stop when stopping or pausing +/// +enum end_boundary : uint64_t +{ + // We're gonna get a little hacky here. The pause on error mode field + // is two bits, with another bit representing slave/master. So we craft + // the enum so that we can insertFromRight and get the proper vaules, and + // leave one bit out of that two-bit range to represent master or slave + NONE = 0b000, + STOP_AFTER_ADDRESS = 0b001, + STOP_AFTER_MASTER_RANK = 0b010, + STOP_AFTER_SLAVE_RANK = 0b110, + STOP_AFTER_SUBTEST = 0b011, + + DONT_CHANGE = 0xFF, +}; + +/// +/// @brief Speeds for performing MCBIST operations +/// +enum speed +{ + /// As fast as possible, often the default + LUDICROUS = 0, + + /// Background scrubbing speed. + BG_SCRUB = 1, + + /// Used to indicate to the continue current command to not change the speed of the commands + SAME_SPEED = 4, +}; + +/// +/// @class Memory diagnostic subsystem stop-on-error settings and thresholds +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// @note Matches Nimbus MBSTRQ, but might be changed later for Centaur, or mapped. +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mss::mcbistTraits<MC, T> > +class stop_conditions +{ + public: + + // Many of the config fields share a disable bit pattern, so we define it here + static constexpr uint64_t DISABLE = 0b1111; + static constexpr uint64_t MAX_THRESHOLD = 0b1110; + static constexpr uint64_t DONT_CHANGE = 0; + + private: + + /// + /// @brief Little helper to convert threshold inputs to exponents + /// @param[in] i_value, the value of the threshold (presumably) + /// @return a value n such that 2^n <= i_value && n < 15 + /// + uint64_t make_threshold_setting( const uint64_t i_value ) + { + // If the user passes in DISABLE, let it past. This prevents callers from having to + // do the conditional. Zero is none which is disable + if ((i_value == DISABLE) || (i_value == 0)) + { + return DISABLE; + } + + // Find the first bit set. This represents the largest power of 2 this input can represent + // The subtraction from 63 switches from a left-count to a right-count (e.g., 0 (left most + // bit) is really bit 63 if you start on the right.) + const uint64_t l_largest = 63 - first_bit_set(i_value); + + // If the first bit set is off in space and greater than 2^14, we just return 0b1110 + // Otherwise, l_largest is the droid we're looking for + return l_largest >= MAX_THRESHOLD ? MAX_THRESHOLD : l_largest; + } + + /// + /// @brief Generic pause on threshold + /// @tparam F, the bit field to manipulate + /// @tparam L, the length of F + /// @param[in] the state of the error - mss::ON or mss::OFF + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + template< uint64_t F, uint64_t L > + inline stop_conditions<MC, T, TT>& set_pause_on_threshold( const states i_on_or_off ) + { + if (i_on_or_off == mss::OFF) + { + iv_value.insertFromRight<F, L>(DISABLE); + return *this; + } + + uint64_t l_thresh = 0; + iv_value.extractToRight<F, L>(l_thresh); + + if (l_thresh == DISABLE) + { + // Note the threshold field is an exponent, so this is 2^0, or 1 count + iv_value.insertFromRight<F, L>(0); + } + + return *this; + } + + public: + /// + /// @brief Stop/Thresholds class ctor + /// + stop_conditions(): + iv_value(0) + { + // By default we want to start everything in 'don't stop' mode. This means disabling + // the errors which contain thresholds + set_thresh_nce_int(DISABLE) + .set_thresh_nce_soft(DISABLE) + .set_thresh_nce_hard(DISABLE) + .set_thresh_rce(DISABLE) + .set_thresh_ice(DISABLE) + .set_thresh_mce_int(DISABLE) + .set_thresh_mce_soft(DISABLE) + .set_thresh_mce_hard(DISABLE); + } + + /// + /// @brief Stop/Thresholds class ctor + /// @param[in] uint64_t representing the threshold register contents + /// + stop_conditions(const uint64_t i_value): + iv_value(i_value) + { + } + + /// + /// @brief Stop/Thresholds class dtor + /// + ~stop_conditions() = default; + + /// + /// @brief uint64_t conversion + /// + inline operator uint64_t() const + { + return uint64_t(iv_value); + } + + /// + /// @brief set_thresh_nce_int + /// @param[in] i_value the value of the field + /// NCE intermittent error threshold magnitude to trigger for triggering pause. If + /// 1111, then pause will never be triggered (disabled). Else, then MCBIST will + /// pause if it takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_nce_int( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_NCE_INT, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_INT_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_nce_int - enable NCE intermittent error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_nce_int( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_NCE_INT, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_INT_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_nce_soft + /// @param[in] i_value the value of the field + /// NCE soft error threshold magnitude to trigger for triggering pause. If 1111, + /// then pause will never be triggered (disabled). Else, then MCBIST will pause if it + /// takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_nce_soft( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_NCE_SOFT, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_SOFT_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_nce_int - enable NCE soft error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_nce_soft( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_NCE_SOFT, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_SOFT_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_nce_hard + /// @param[in] i_value the value of the field + /// NCE hard error threshold magnitude to trigger for triggering pause. If 1111, + /// then pause will never be triggered (disabled). Else, then MCBIST will pause if it + /// takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_nce_hard( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_NCE_HARD, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_HARD_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_nce_hard - enable NCE hard error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_nce_hard( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_NCE_HARD, + TT::MBSTRQ_CFG_THRESH_MAG_NCE_HARD_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_rce + /// @param[in] i_value the value of the field + /// RCE error threshold magnitude to trigger for triggering pause. If 1111, then + /// pause will never be triggered (disabled). Else, then MCBIST will pause if it takes + /// sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_rce( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_RCE, + TT::MBSTRQ_CFG_THRESH_MAG_RCE_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_rce - enable RCE error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_rce( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_RCE, + TT::MBSTRQ_CFG_THRESH_MAG_RCE_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_ice + /// @param[in] i_value the value of the field + /// ICE (IMPE) error threshold magnitude to trigger for triggering pause. If 1111, + /// then pause will never be triggered (disabled). Else, then MCBIST will pause if + /// it takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_ice( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_ICE, + TT::MBSTRQ_CFG_THRESH_MAG_ICE_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_ice - enable ICE (IMPE) error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_ice( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_ICE, + TT::MBSTRQ_CFG_THRESH_MAG_ICE_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_mce_int + /// @param[in] i_value the value of the field + /// MCE intermittent error threshold magnitude to trigger for triggering pause. If + /// 1111, then pause will never be triggered (disabled). Else, then MCBIST will + /// pause if it takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_mce_int( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_MCE_INT, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_INT_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_mce_int - enable MCE intermittent error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_mce_int( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_MCE_INT, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_INT_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_mce_soft + /// @param[in] i_value the value of the field + /// MCE soft error threshold magnitude to trigger for triggering pause. If 1111, + /// then pause will never be triggered (disabled). Else, then MCBIST will pause if it + /// takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_mce_soft( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_MCE_SOFT, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_SOFT_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_mce_soft - enable MCE soft error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_mce_soft( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_MCE_SOFT, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_SOFT_LEN>(i_on_or_off); + } + + /// + /// @brief set_thresh_mce_hard + /// @param[in] i_value the value of the field + /// MCE hard error threshold magnitude to trigger for triggering pause. If 1111, + /// then pause will never be triggered (disabled). Else, then MCBIST will pause if it + /// takes sees 2^[this value] number of errors of this type. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note The register field is actually an exponent. The hardware will count 2^n for the + /// threshold. However, the input represents a count - how many. Thus we need to convert + /// the input to a power of 2 to get a proper exponent. Your input will be rounded down + /// to the nearest power of 2 which is less than 2^15 before being set in the register. + /// + inline stop_conditions<MC, T, TT>& set_thresh_mce_hard( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_THRESH_MAG_MCE_HARD, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_HARD_LEN>(make_threshold_setting(i_value)); + return *this; + } + + /// + /// @brief set_pause_on_mce_hard - enable MCE hard error + /// @param[in] i_on_or_off - the desired state. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// @note If the input is mss::ON, this method enables the error, it's corresponding + /// threshold defines the threshold at which the engine will stop. If no threshold is + /// defined (the error is disabled) this method will set the threshold to 1. A previously + /// defined threshold (i.e., not disabled) will be left intact. If the input + /// is mss::OFF, this method will disable the error by setting the threshold to disabled. + /// + inline stop_conditions<MC, T, TT>& set_pause_on_mce_hard( const states i_on_or_off ) + { + return set_pause_on_threshold<TT::MBSTRQ_CFG_THRESH_MAG_MCE_HARD, + TT::MBSTRQ_CFG_THRESH_MAG_MCE_HARD_LEN>(i_on_or_off); + } + + /// + /// @brief set_pause_on_sce + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on SCE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_sce( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_SCE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_mce + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on MCE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_mce( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_MCE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_mpe + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on MPE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_mpe( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_MPE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_ue + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on UE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_ue( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_UE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_sue + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on SUE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_sue( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_SUE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_aue + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on AUE error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_aue( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_AUE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_on_rcd + /// @param[in] i_on_or_off - the desired state. + /// Enable pause on RCD error. When enabled, MCBIST will pause at the boundary + /// configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_on_rcd( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_ON_RCD>(i_on_or_off); + return *this; + } + + /// + /// @brief set_symbol_counter_mode + /// @param[in] i_value the value of the field + /// Selects which mode to use symbol counter latches: Mode 0) MAINT 8-bit error + /// counters for of 72 symbols Mode 1) MCBIST 4-bit error counters for 18 nibbles x 8 + /// ranks (port agnostic) Mode 2) MCBIST 4-bit error counters for 18 nibbles x 4 + /// ports (rank agnostic) and 1-bit error rank map for 18 nibbles x 4 ports + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_symbol_counter_mode( const uint64_t i_value ) + { + iv_value.insertFromRight<TT::MBSTRQ_CFG_SYMBOL_COUNTER_MODE, + TT::MBSTRQ_CFG_SYMBOL_COUNTER_MODE_LEN>(i_value); + return *this; + } + + /// + /// @brief set_nce_soft_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables soft NCEs to trigger per symbol NCE error counting Only applies to + /// scrub where we have different types of NCE. Non scrub counts all NCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_nce_soft_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_NCE_SOFT_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_nce_inter_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables intermittent NCEs to trigger per symbol NCE error counting Only applies + /// to scrub where we have different types of NCE. Non scrub counts all NCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_nce_inter_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_NCE_INTER_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_nce_hard_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables hard NCEs to trigger per symbol NCE error counting Only applies to + /// scrub where we have different types of NCE. Non scrub counts all NCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_nce_hard_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_NCE_HARD_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_mcb_error + /// @param[in] i_on_or_off - the desired state. + /// Enable pause when MCBIST error is logged. When enabled, MCBIST will pause at + /// the boundary configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_mcb_error( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_MCB_ERROR>(i_on_or_off); + return *this; + } + + /// + /// @brief set_pause_mcb_log_full + /// @param[in] i_on_or_off - the desired state. + /// Enable pause when MCBIST log is full. When enabled, MCBIST will pause at the + /// boundary configured if this error is seen. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_pause_mcb_log_full( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_PAUSE_MCB_LOG_FULL>(i_on_or_off); + return *this; + } + + /// + /// @brief set_maint_rce_with_ce + /// @param[in] i_on_or_off - the desired state. + /// cfg_maint_rce_with_ce - not implemented. Need to investigate if needed for nimbus. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_maint_rce_with_ce( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_MAINT_RCE_WITH_CE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_mce_soft_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables soft MCEs to trigger per symbol MCE error counting Only applies to + /// scrub where we have different types of MCE. Non scrub counts all MCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_mce_soft_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_MCE_SOFT_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_mce_inter_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables intermittent MCEs to trigger per symbol MCE error counting Only applies + /// to scrub where we have different types of MCE. Non scrub counts all MCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_mce_inter_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_MCE_INTER_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + /// + /// @brief set_mce_hard_symbol_count_enable + /// @param[in] i_on_or_off - the desired state. + /// Enables hard MCEs to trigger per symbol MCE error counting Only applies to + /// scrub where we have different types of MCE. Non scrub counts all MCE. + /// @return fapi2::buffer<uint64_t>& this->iv_value useful for method chaining + /// + inline stop_conditions<MC, T, TT>& set_mce_hard_symbol_count_enable( const states i_on_or_off ) + { + iv_value.writeBit<TT::MBSTRQ_CFG_MCE_HARD_SYMBOL_COUNT_ENABLE>(i_on_or_off); + return *this; + } + + private: + + fapi2::buffer<uint64_t> iv_value; +}; + +template< mss::mc_type MC, fapi2::TargetType T, typename TT> +constexpr uint64_t stop_conditions<MC, T, TT>::DISABLE; + +template< mss::mc_type MC, fapi2::TargetType T, typename TT> +constexpr uint64_t stop_conditions<MC, T, TT>::MAX_THRESHOLD; + +template< mss::mc_type MC, fapi2::TargetType T, typename TT> +constexpr uint64_t stop_conditions<MC, T, TT>::DONT_CHANGE; + +/// +/// @class memdiags operational constraints +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T - derived +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct constraints +{ + /// + /// @brief constraints default constructor + /// + constraints(): + iv_stop(), + iv_pattern(NO_PATTERN), + iv_end_boundary(NONE), + iv_speed(LUDICROUS), + iv_start_address(0), + iv_end_address(TT::LARGEST_ADDRESS) + { + } + + /// + /// @brief constraints constructor + /// @param[in] i_pattern a pattern to set + /// + constraints( const uint64_t i_pattern ): + constraints() + { + iv_pattern = i_pattern; + FAPI_INF("setting up constraints with pattern %d", i_pattern); + } + + /// + /// @brief constraints constructor + /// @param[in] i_stop stop conditions + /// + constraints( const stop_conditions<MC, T, TT>& i_stop ): + constraints() + { + iv_stop = i_stop; + FAPI_INF("setting up constraints with stop 0x%016lx", uint64_t(i_stop)); + } + + /// + /// @brief constraints constructor + /// @param[in] i_stop stop conditions + /// @param[in] i_start_address address to start from + /// + constraints( const stop_conditions<MC, T, TT>& i_stop, + const address& i_start_address ): + constraints(i_stop) + { + iv_start_address = i_start_address; + FAPI_INF("setting up constraints with start address 0x%016lx", uint64_t(i_start_address)); + } + + /// + /// @brief constraints constructor + /// @param[in] i_stop stop conditions + /// @param[in] i_speed the speed at which to run + /// @param[in] i_end_boundary the place to stop on error + /// @param[in] i_start_address address to start from + /// @param[in] i_end_address address to end at (optional, run to end) + /// + constraints( const stop_conditions<MC, T, TT>& i_stop, + const speed i_speed, + const end_boundary i_end_boundary, + const address& i_start_address, + const address& i_end_address = mcbist::address(TT::LARGEST_ADDRESS) ): + constraints(i_stop, i_start_address) + { + iv_end_boundary = i_end_boundary; + iv_speed = i_speed; + iv_end_address = i_end_address; + + FAPI_INF("setting up constraints with end boundary %d and speed 0x%x", i_end_boundary, i_speed); + + // If our end address is 'before' our start address, make the end address the same as the start. + if (iv_start_address > iv_end_address) + { + iv_end_address = iv_start_address; + } + } + + // Member variable declaration + stop_conditions<MC, T, TT> iv_stop; + uint64_t iv_pattern; + end_boundary iv_end_boundary; + speed iv_speed; + mcbist::address iv_start_address; + mcbist::address iv_end_address; +}; + + +} // namespace +} // namespace +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H index 54583ebd0..cd0bc5163 100644 --- a/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist_traits.H @@ -22,3 +22,44 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_mcbist_traits.H +/// @brief Run and manage the MCBIST engine +/// +// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP + +#ifndef _GEN_MSS_MCBIST_TRAITS_H_ +#define _GEN_MSS_MCBIST_TRAITS_H_ + +#include <fapi2.H> + +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H> + +namespace mss +{ + +/// +/// @class mcbistMCTraits +/// @tparam MC the mc type +/// @brief A MC to MC_TARGET_TYPE mapping +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE > +class mcbistMCTraits; + +/// +/// @class mcbistTraits +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @brief a collection of traits associated with the MCBIST engine or hardware +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE> +class mcbistTraits; + +}// mss +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_mss_memdiags.H b/src/import/generic/memory/lib/utils/mcbist/gen_mss_memdiags.H index 62af113ca..c67b51595 100644 --- a/src/import/generic/memory/lib/utils/mcbist/gen_mss_memdiags.H +++ b/src/import/generic/memory/lib/utils/mcbist/gen_mss_memdiags.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ +/* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -22,3 +22,1288 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file gen_mss_memdiags.H +/// @brief API for memory diagnostics +/// +// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com> +// *HWP HWP Backup: Marc Gollub <gollub@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 3 +// *HWP Consumed by: HB:FSP +// + +#ifndef _GEN_MSS_MEMDIAGS_H_ +#define _GEN_MSS_MEMDIAGS_H_ + +#include <fapi2.H> +#include <generic/memory/lib/mss_generic_system_attribute_getters.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_address.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_patterns.H> +#include <generic/memory/lib/utils/mcbist/gen_mss_mcbist_settings.H> +#include <generic/memory/lib/utils/fir/gen_mss_unmask.H> +#include <generic/memory/lib/utils/count_dimm.H> +#include <generic/memory/lib/utils/conversions.H> +#include <generic/memory/lib/utils/pos.H> +#include <generic/memory/lib/utils/count_dimm.H> +#include <generic/memory/lib/utils/poll.H> + + +namespace mss +{ + +/// +/// @brief Determine if a thing is functional +/// @tparam I, the type of the item we want to check for +/// @tparam P, the type of the parent which holds the things of interest +/// @param[in] i_target the parent containing the thing we're looking for +/// @param[in] i_rel_pos the relative position of the item of interest. +/// @return bool true iff the thing at i_rel_pos is noted as functional +/// +template< fapi2::TargetType I, fapi2::TargetType P > +inline bool is_functional( const fapi2::Target<P>& i_target, const uint64_t i_rel_pos ) +{ + // Not sure of a good way to do this ... we get all the functional + // children of the parent and look for our relative position ... + for (const auto& i : i_target.template getChildren<I>(fapi2::TARGET_STATE_FUNCTIONAL)) + { + if (mss::template relative_pos<P>(i) == i_rel_pos) + { + return true; + } + } + + return false; +} + +namespace mcbist +{ +namespace sim +{ + +/// @brief Perform a sim version of initializing memory +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T +/// @param T a fapi2::TargetType +/// @param[in] i_target +/// @param[in] i_pattern an index representing a pattern to use to initize memory (defaults to 0) +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode sf_init( const fapi2::Target<T>& i_target, const uint64_t i_pattern ) +{ + FAPI_INF("Start sim init for %s", mss::c_str(i_target)); + + // If we're running in the simulator, we want to only touch the addresses which training touched + + for (const auto& p : i_target.template getChildren<TT::PORT_TYPE>()) + { + std::vector<uint64_t> l_pr; + mss::mcbist::program<MC> l_program; + + mss::mcbist::address l_start; + mss::mcbist::address l_end; + + size_t l_rank_address_pair = 0; + + // No point in bothering if we don't have any DIMM + if (mss::count_dimm(p) == 0) + { + FAPI_INF("No DIMM on %s, not running sf_init", mss::c_str(p)); + continue; + } + + // In sim we know a few things ... + // Get the primary ranks for this port. We know there can only be 4, and we know we only trained the primary + // ranks. Therefore, we only need to clean up the primary ranks. And because there's 4 max, we can do it + // all using the 4 address range registers of tne MCBIST (broadcast currently not considered.) + // So we can write 0's to those to get their ECC fixed up. + FAPI_TRY( mss::rank::primary_ranks(p, l_pr) ); + fapi2::Assert( l_pr.size() <= mss::MAX_RANK_PER_DIMM ); + + for (auto r = l_pr.begin(); r != l_pr.end(); ++l_rank_address_pair, ++r) + { + FAPI_INF("sim init %s, rank %d", mss::c_str(p), *r); + + // Setup l_start to represent this rank, and then make the end address from that. + l_start.set_master_rank(*r); + + // Set C3 bit to get an entire cache line + l_start.get_sim_end_address(l_end); + + // By default we're in maint address mode, not address counting mode. So we give it a start and end, and ignore + // anything invalid - that's what maint address mode is all about + mss::mcbist::config_address_range<MC>(i_target, l_start, l_end, l_rank_address_pair); + + // Write + { + // Run in ECC mode, 64B writes (superfast mode) + + mss::mcbist::subtest_t<MC> l_fw_subtest = + mss::mcbist::write_subtest<MC>(); + + l_fw_subtest.enable_port(mss::relative_pos<T>(p)); + l_fw_subtest.change_addr_sel(l_rank_address_pair); + l_fw_subtest.enable_dimm(mss::rank::get_dimm_from_rank(*r)); + l_program.iv_subtests.push_back(l_fw_subtest); + FAPI_DBG("adding superfast write for %s rank %d (dimm %d)", mss::c_str(p), *r, mss::rank::get_dimm_from_rank(*r)); + } + + // Read - we do a read here as verification can use this as a tool as we do the write and then the read. + // If we failed to write properly the read would thow ECC errors. Just a write (which the real hardware would + // do) doesn't catch that. This takes longer, but it's not terribly long in any event. + { + // Run in ECC mode, 64B writes (superfast mode) + mss::mcbist::subtest_t<MC> l_fr_subtest = + mss::mcbist::read_subtest<MC>(); + + l_fr_subtest.enable_port(mss::relative_pos<T>(p)); + l_fr_subtest.change_addr_sel(l_rank_address_pair); + l_fr_subtest.enable_dimm(mss::rank::get_dimm_from_rank(*r)); + l_program.iv_subtests.push_back(l_fr_subtest); + FAPI_DBG("adding superfast read for %s rank %d (dimm %d)", mss::c_str(p), *r, mss::rank::get_dimm_from_rank(*r)); + } + } + + // Write pattern + FAPI_TRY( mss::mcbist::load_pattern<MC>(i_target, i_pattern) ); + + // Setup the sim polling based on a heuristic <cough>guess</cough> + // Looks like ~400ck per address for a write/read program on the sim-dimm, and add a long number of polls + // On real hardware wait 100ms and then start polling for another 5s + l_program.iv_poll.iv_initial_sim_delay = mss::cycles_to_simcycles(((l_end - l_start) * l_pr.size()) * 800); + l_program.iv_poll.iv_initial_delay = 100 * mss::DELAY_1MS; + l_program.iv_poll.iv_sim_delay = 100000; + l_program.iv_poll.iv_delay = 10 * mss::DELAY_1MS; + l_program.iv_poll.iv_poll_count = 500; + + // Just one port for now. Per Shelton we need to set this in maint address mode + // even tho we specify the port/dimm in the subtest. + fapi2::buffer<uint8_t> l_port; + l_port.setBit(mss::relative_pos<T>(p)); + l_program.select_ports(l_port >> 4); + + // Kick it off, wait for a result + FAPI_TRY( mss::mcbist::execute(i_target, l_program) ); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + FAPI_INF("End sim init for %s", mss::c_str(i_target)); + return fapi2::current_err; +} + +} // namespace sim +} // namespace mcbist + + +namespace memdiags +{ + +// Map some of the mcbist namespace here to make it easier for users of memdiags +// This is an intentional using statement in a header which is typically +// disallowed - I am intentionally pulling these into this namespace for all callers. +using mss::mcbist::constraints; +using mss::mcbist::speed; +using mss::mcbist::end_boundary; +using mss::mcbist::stop_conditions; +using mss::mcbist::cache_line; +using mss::mcbist::pattern; +using mss::mcbist::patterns; + +// Why not mss::mcbist::address? Because the fields can't be pulled in via using, +// and it seems even more confusing to have a memdiags address but have to use +// mcbist fields. So, we all use mcbist address until such time that its promoted +// to some other general namespace. + +using mss::mcbist::PATTERN_ZEROS; +using mss::mcbist::PATTERN_0; +using mss::mcbist::PATTERN_ONES; +using mss::mcbist::PATTERN_1; +using mss::mcbist::PATTERN_2; +using mss::mcbist::PATTERN_3; +using mss::mcbist::PATTERN_4; +using mss::mcbist::PATTERN_5; +using mss::mcbist::PATTERN_6; +using mss::mcbist::PATTERN_7; +using mss::mcbist::PATTERN_8; +using mss::mcbist::PATTERN_RANDOM; +using mss::mcbist::LAST_PATTERN; +using mss::mcbist::NO_PATTERN; + +/// +/// @brief Set up memory controller specific settings for pre-maint mode read +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the portTraits associated with the port +/// @param[in] i_target the memory controller target +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode pre_maint_read_settings( const fapi2::Target<T>& i_target ); + +/// +/// @brief Set up memory controller specific settings for pre-scrub +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the portTraits associated with the port +/// @param[in] i_target the memory controller target +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT = portTraits<MC> > +fapi2::ReturnCode pre_scrub_settings( const fapi2::Target<T>& i_target ); + +/// +/// @brief Stop the current command +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T> +fapi2::ReturnCode stop( const fapi2::Target<T>& i_target ) +{ + // Too long, make shorter + using TT = mss::mcbistTraits<MC, T>; + using ET = mss::mcbistMCTraits<MC>; + + // Poll parameters are defined as TK so that we wait a nice time for operations + // For now use the defaults + mss::poll_parameters l_poll_parameters; + fapi2::buffer<uint64_t> l_status; + fapi2::buffer<uint64_t> l_last_address; + bool l_poll_result = false; + + FAPI_INF("Stopping any mcbist operations which are in progress for %s", mss::c_str(i_target)); + + // TODO RTC:153951 Add masking of FIR when stopping + FAPI_TRY( mss::mcbist::start_stop<MC>(i_target, mss::STOP) ); + + // Poll waiting for the engine to stop + l_poll_result = mss::poll(i_target, TT::STATQ_REG, l_poll_parameters, + [&l_status](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("looking for mcbist not in-progress, mcbist statq 0x%llx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + // We're done polling when either we see we're in progress or we see we're done. + return l_status.getBit<TT::MCBIST_IN_PROGRESS>() == false; + }); + + // Pass or fail output the current address. This is useful for debugging when we can get it. + // It's in the register FFDC for memdiags so we don't need it below + FAPI_TRY( mss::getScom(i_target, TT::LAST_ADDR_REG, l_last_address) ); + FAPI_INF("MCBIST last address (during stop): 0x%016lx for %s", + l_last_address, mss::c_str(i_target)); + + // So we've either stopped or we timed out + FAPI_ASSERT( l_poll_result == true, + ET::memdiags_failed_to_stop() + .set_MC_TARGET(i_target) + .set_POLL_COUNT(l_poll_parameters.iv_poll_count), + "%s The MCBIST engine failed to stop its program", + mss::c_str(i_target) ); + +fapi_try_exit: + return fapi2::current_err; + +} + + +/// +/// @class Base class for memdiags operations +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +class operation +{ + public: + /// + /// @brief memdiags::operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_subtest the proper subtest for this operation + /// @param[in] i_const mss::constraint structure + /// + operation( const fapi2::Target<T>& i_target, + const mss::mcbist::subtest_t<MC> i_subtest, + const constraints<MC> i_const ): + iv_target(i_target), + iv_subtest(i_subtest), + iv_const(i_const) + { + FAPI_TRY( mss::attr::get_is_simulation (iv_sim) ); + return; + + fapi_try_exit: + // Seems like a safe risk to take ... + FAPI_ERR("Unable to get the attribute ATTR_IS_SIMULATION"); + return; + } + + operation() = delete; + + /// + /// @brief Execute the memdiags operation + /// @return FAPI2_RC_SUCCESS iff ok + /// + inline fapi2::ReturnCode execute() + { + return mss::mcbist::execute(iv_target, iv_program); + } + + /// + /// @brief memdiags::operation destructor + /// + virtual ~operation() = default; + + /// + /// @brief memdiags init helper + /// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes + /// in subclassed constructores more easily. + /// @return FAPI2_RC_SUCCESS iff everything ok + /// + fapi2::ReturnCode base_init(); + + /// + /// @brief Configures all subtests for a multiport init + /// @param[in] i_dimms a vector of DIMM targets + /// + void configure_multiport_subtests(const std::vector<fapi2::Target<fapi2::TARGET_TYPE_DIMM>>& i_dimms); + + /// + /// @brief memdiags multi-port init helper + /// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes + /// in subclassed constructores more easily. + /// @return FAPI2_RC_SUCCESS iff everything ok + /// + fapi2::ReturnCode multi_port_init(); + + /// + /// @brief memdiags multi-port init for specific chip + /// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes + /// in subclassed constructores more easily. + /// @return FAPI2_RC_SUCCESS iff everything ok + /// + fapi2::ReturnCode multi_port_init_internal(); + + + /// + /// @brief memdiags multi-port address config helper + /// Initializes the address configs needed for a multi port operation + /// @return FAPI2_RC_SUCCESS iff everything ok + /// + fapi2::ReturnCode multi_port_addr(); + + /// + /// @brief Single port initializer + /// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes + /// in subclassed constructores more easily. + /// @return FAPI2_RC_SUCCESS iff everything ok + /// + fapi2::ReturnCode single_port_init(); + + /// + /// @brief get the protected mcbist program - useful for testing + /// @return a reference to the iv_program member + /// @note Intentionally not const ref; allows getter to set. + /// + mss::mcbist::program<MC>& get_program() + { + return iv_program; + } + + /// + /// @brief get the protected mcbist subtest_t - useful for testing + /// @return a reference to the iv_subtest member + /// + const mss::mcbist::subtest_t<MC>& get_subtest() const + { + return iv_subtest; + } + + protected: + fapi2::Target<T> iv_target; + mss::mcbist::subtest_t<MC> iv_subtest; + constraints<MC> iv_const; + mss::mcbist::program<MC> iv_program; + uint8_t iv_sim; +}; + + + +/// +/// @brief memdiags init helper +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T +/// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes +/// in subclassed constructors more easily. +/// @return FAPI2_RC_SUCCESS iff everything ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT > +inline fapi2::ReturnCode operation<MC, T, TT>::base_init() +{ + FAPI_INF("memdiags base init for %s", mss::c_str(iv_target)); + + // Check the state of the MCBIST engine to make sure its OK that we proceed. + // Force stop the engine (per spec, as opposed to waiting our turn) + FAPI_TRY( memdiags::stop<MC>(iv_target) ); + + // Zero out cmd timebase - mcbist::program constructor does that for us. + // Load pattern + FAPI_TRY( iv_program.change_pattern(iv_const.iv_pattern) ); + + // Load end boundaries + iv_program.change_end_boundary(iv_const.iv_end_boundary); + + // Load thresholds + iv_program.change_thresholds(iv_const.iv_stop); + + // Setup the requested speed + FAPI_TRY( iv_program.change_speed(iv_target, iv_const.iv_speed) ); + + // Enable maint addressing mode - enabled by default in the mcbist::program ctor + + // Apparently the MCBIST engine needs the ports selected even though the ports are specified + // in the subtest. We can just select them all, and it adjusts when it executes the subtest + iv_program.select_ports(0b1111); + + // Kick it off, don't wait for a result + iv_program.change_async(mss::ON); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Single port initializer +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T +/// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes +/// in subclassed constructors more easily. +/// @return FAPI2_RC_SUCCESS iff everything ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT > +inline fapi2::ReturnCode operation<MC, T, TT>::single_port_init() +{ + using ET = mcbistMCTraits<MC>; + FAPI_INF("single port init for %s", mss::c_str(iv_target)); + + const uint64_t l_relative_port_number = iv_const.iv_start_address.get_port(); + const uint64_t l_dimm_number = iv_const.iv_start_address.get_dimm(); + + // Make sure the specificed port is functional + FAPI_ASSERT( mss::is_functional<TT::PORT_TYPE>(iv_target, l_relative_port_number), + ET::memdiags_port_not_functional() + .set_RELATIVE_PORT_POSITION(l_relative_port_number) + .set_ADDRESS( uint64_t(iv_const.iv_start_address) ) + .set_MC_TARGET(iv_target), + "Port with relative postion %d is not functional for %s", + l_relative_port_number, mss::c_str(iv_target)); + + // No broadcast mode for this one + // Push on a read subtest + { + mss::mcbist::subtest_t<MC> l_subtest = iv_subtest; + + l_subtest.enable_port(l_relative_port_number); + l_subtest.enable_dimm(l_dimm_number); + iv_program.iv_subtests.push_back(l_subtest); + FAPI_INF("%s adding subtest 0x%04x for port %d, DIMM %d", + mss::c_str(iv_target), l_subtest, l_relative_port_number, l_dimm_number); + } + + // The address should have the port and DIMM noted in it. All we need to do is calculate the + // remainder of the address + if (iv_sim) + { + iv_const.iv_start_address.get_sim_end_address(iv_const.iv_end_address); + } + else if (iv_const.iv_end_address == TT::LARGEST_ADDRESS) + { + // Only the DIMM range as we don't want to cross ports. + iv_const.iv_start_address.template get_range<mss::mcbist::address::DIMM>(iv_const.iv_end_address); + } + + // Configure the address range + FAPI_TRY( mss::mcbist::config_address_range0<MC>(iv_target, iv_const.iv_start_address, iv_const.iv_end_address) ); + + // Initialize the common sections + FAPI_TRY( base_init() ); + +fapi_try_exit: + return fapi2::current_err; +} + + + +/// +/// @brief memdiags multi-port init helper +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType - derived +/// @tparam TT the mcbistTraits associated with T +/// Initializes common sections. Broken out rather than the base class ctor to enable checking return codes +/// in subclassed constructors more easily. +/// @return FAPI2_RC_SUCCESS iff everything ok +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT > +inline fapi2::ReturnCode operation<MC, T, TT>::multi_port_init() +{ + FAPI_INF("multi-port init for %s", mss::c_str(iv_target)); + + const auto l_port = mss::find_targets<TT::PORT_TYPE>(iv_target); + + // Make sure we have ports, if we don't then exit out + if(l_port.size() == 0) + { + // Cronus can have no ports under an MCBIST, FW deconfigures by association + FAPI_INF("%s has no attached ports skipping setup", mss::c_str(iv_target)); + return fapi2::FAPI2_RC_SUCCESS; + } + + // Let's assume we are going to send out all subtest unless we are in broadcast mode, + // where we only send up to 2 subtests under an port ( 1 for each DIMM) which is why no const + auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target); + + if( l_dimms.size() == 0) + { + // Cronus can have no DIMMS under an MCBIST, FW deconfigures by association + FAPI_INF("%s has no attached DIMMs skipping setup", mss::c_str(iv_target)); + return fapi2::FAPI2_RC_SUCCESS; + } + + return multi_port_init_internal(); +} + + +/// +/// @class Class for memdiags' super-fast init +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct sf_init_operation : public operation<MC> +{ + + /// + /// @brief memdiags::sf_init_operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_const mss::constraint structure + /// @param[out] o_rc the fapi2::ReturnCode of the intialization process + /// + sf_init_operation( const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc): + operation<MC>(i_target, mss::mcbist::init_subtest<MC>(), i_const) + { + // If sf_init was passed the random data pattern, then modify the subtest to use the true random data + if(i_const.iv_pattern == PATTERN_RANDOM) + { + this->iv_subtest.change_data_mode(mss::mcbist::data_mode::RAND_FWD_MAINT); + } + + // We're a multi-port operation + o_rc = this->multi_port_init(); + } + + sf_init_operation() = delete; +}; + + + +/// +/// @class Class for memdiags' super-fast read +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct sf_read_operation : public operation<MC> +{ + + /// + /// @brief memdiags::sf_read_operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_const mss::constraint structure + /// @param[out] o_rc the fapi2::ReturnCode of the intialization process + /// + sf_read_operation( const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc): + operation<MC>(i_target, mss::mcbist::read_subtest<MC>(), i_const) + { + // We're a multi-port operation + o_rc = this->multi_port_init(); + } + + sf_read_operation() = delete; +}; + + +/// +/// @class Class for memdiags' super-fast read to end of port +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct sf_read_eop_operation : public operation<MC> +{ + /// + /// @brief memdiags::sf_read_operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_const mss::constraint structure + /// @param[out] o_rc the fapi2::ReturnCode of the intialization process + /// + sf_read_eop_operation( const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc ): + operation<MC>(i_target, mss::mcbist::read_subtest<MC>(), i_const) + { + // We're a single-port operation + o_rc = this->single_port_init(); + } + + sf_read_eop_operation() = delete; +}; + +/// +/// @class Class for memdiags' continuous scrub +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct continuous_scrub_operation : public operation<MC> +{ + + /// + /// @brief memdiags::continuous_scrub_operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_const the contraints of the operation + /// @param[out] o_rc the fapi2::ReturnCode of the intialization process + /// + continuous_scrub_operation( const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc ); + + continuous_scrub_operation() = delete; +}; + +/// +/// @brief memdiags::continuous_scrub_operation constructor +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// @param[in] i_target the target of the mcbist engine +/// @param[in] i_const the contraints of the operation +/// @param[out] o_rc the fapi2::ReturnCode of the intialization process +/// +template< mss::mc_type MC, fapi2::TargetType T, typename TT> +continuous_scrub_operation<MC, T, TT>::continuous_scrub_operation( + const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc ): + operation<MC>(i_target, mss::mcbist::scrub_subtest<MC>(), i_const) +{ + mss::mcbist::address l_generic_start_address; + mss::mcbist::address l_generic_end_address; + + FAPI_INF("setting up for continuous scrub for %s", mss::c_str(i_target)); + + // Scrub operations run 128B + operation<MC>::iv_program.change_len64(mss::OFF); + + // We build a little program here which allows us to restart the loop in the event of a pause. + // So we need to craft some of the address ranges and some of the subtests by hand. + + // Setup address config 0 to cover all the addresses for a port/dimm. + // We leverage the MCBIST's ability to skip invalid addresses, and just setup + // If we're running in the simulator, we want to only touch the addresses which training touched + // *INDENT-OFF* + operation<MC>::iv_sim ? + l_generic_start_address.get_sim_end_address(l_generic_end_address) : + l_generic_start_address.get_range<mss::mcbist::address::DIMM>(l_generic_end_address); + // *INDENT-ON* + + FAPI_TRY( mss::mcbist::config_address_range0<MC>(i_target, l_generic_start_address, l_generic_end_address) ); + + // We push on a fake subtest 0 and subtest 1. We fix them up after we fill in the + // rest of the subtests. + operation<MC>::iv_program.iv_subtests.push_back(operation<MC>::iv_subtest); + operation<MC>::iv_program.iv_subtests.push_back(operation<MC>::iv_subtest); + + // a generic 0 - DIMM address range. + // + // Subtests 2-9: One subtest per port/dimm each covering the whole range of that + // port/dimm. scrub_subtests by default are using address config 0, so each of + // these get their full address complement. + for (const auto& p : operation<MC>::iv_target.template getChildren<TT::PORT_TYPE>()) + { + for (const auto& d : p.template getChildren<fapi2::TARGET_TYPE_DIMM>()) + { + // Don't destroy the subtest passed in, copy it + auto l_subtest = operation<MC>::iv_subtest; + + l_subtest.enable_port(mss::relative_pos<T>(p)); + l_subtest.enable_dimm(mss::index(d)); + operation<MC>::iv_program.iv_subtests.push_back(l_subtest); + FAPI_INF("adding scrub subtest for %s (dimm %d) ( 0x%04x)", mss::c_str(d), mss::index(d), l_subtest); + } + } + + // + // Subtest 10: goto subtest 2. This causes us to loop back to the first port/dimm and go thru them all + // This subtest will be marked the last when the MCBMR registers are filled in. + // + operation<MC>::iv_program.iv_subtests.push_back(mss::mcbist::goto_subtest<MC>(2)); + FAPI_INF("last goto subtest (10) is going to subtest 2 ( 0x%04x) for %s", operation<MC>::iv_program.iv_subtests[2], + mss::c_str(operation<MC>::iv_target)); + + // Ok, now we can go back in to fill in the first two subtests. + + { + auto l_subtest = operation<MC>::iv_subtest; + auto l_port = operation<MC>::iv_const.iv_start_address.get_port(); + auto l_dimm = operation<MC>::iv_const.iv_start_address.get_dimm(); + size_t l_index = 2; + + // By default if we don't find our port/dimm in the subtests, we just go back to the beginning. + uint64_t l_goto_subtest = 2; + + // + // subtest 0 + // + + // load the start address given and calculate the end address. Stick this into address config 1 + // We don't need to account for the simulator here as the caller can do that when they setup the + // start address. + // *INDENT-OFF* + operation<MC>::iv_sim ? + operation<MC>::iv_const.iv_start_address.get_sim_end_address(operation<MC>::iv_const.iv_end_address) : + operation<MC>::iv_const.iv_start_address.template get_range<mss::mcbist::address::DIMM>(operation<MC>::iv_const.iv_end_address); + // *INDENT-ON* + + FAPI_TRY( mss::mcbist::config_address_range1(i_target, operation<MC>::iv_const.iv_start_address, + operation<MC>::iv_const.iv_end_address) ); + + // We need to use this address range. We know it's ok to write to element 0 as we pushed it on above + l_subtest.change_addr_sel(1); + l_subtest.enable_port(l_port); + l_subtest.enable_dimm(l_dimm); + + operation<MC>::iv_program.iv_subtests[0] = l_subtest; + FAPI_INF("adding scrub subtest 0 for port %d dimm %d (0x%04x) for %s", l_port, l_dimm, l_subtest, mss::c_str(i_target)); + + // + // subtest 1 + // + + // From the port/dimm specified in the start address, we know what subtest should execute next. The idea + // being that this 0'th subtest is a mechanism to allow the caller to start a scrub 'in the middle' and + // jump to the next port/dimm which would have been scrubbed. The hard part is that we don't know where + // in the subtest vector the 'next' port/dimm are placed. So we look for our port/dimm (skipping subtest 0 + // since we know that's us and skipping subtest 1 since it isn't there yet.) + for (; l_index < operation<MC>::iv_program.iv_subtests.size(); ++l_index) + { + auto l_my_dimm = operation<MC>::iv_program.iv_subtests[l_index].get_dimm(); + auto l_my_port = operation<MC>::iv_program.iv_subtests[l_index].get_port(); + + if ((l_dimm == l_my_dimm) && (l_port == l_my_port)) + { + l_goto_subtest = l_index + 1; + break; + } + } + + // Since we set l_goto_subtest up with a meaningful default, we can just make a subtest with the + // l_goto_subtest subtest specified and pop that in to index 1. + FAPI_INF("adding scrub subtest 1 to goto subtest %d (port %d, dimm %d, test 0x%04x) for %s", l_goto_subtest, + operation<MC>::iv_program.iv_subtests[l_goto_subtest].get_port(), + operation<MC>::iv_program.iv_subtests[l_goto_subtest].get_dimm(), + operation<MC>::iv_program.iv_subtests[l_goto_subtest], mss::c_str(i_target) ); + + operation<MC>::iv_program.iv_subtests[1] = mss::mcbist::goto_subtest<MC>(l_goto_subtest); + } + + // Initialize the common sections + FAPI_TRY( operation<MC>::base_init() ); + +fapi_try_exit: + o_rc = fapi2::current_err; + return; +} + + + +/// +/// @class Class for memdiags' targeted scrub +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +struct targeted_scrub_operation : public operation<MC> +{ + + /// + /// @brief memdiags::targeted_scrub_operation constructor + /// @param[in] i_target the target of the mcbist engine + /// @param[in] i_const the contraints of the operation + /// @param[out] o_rc the fapi2::ReturnCode of the intialization process + /// + targeted_scrub_operation( const fapi2::Target<T>& i_target, + const constraints<MC> i_const, + fapi2::ReturnCode& o_rc ): + operation<MC>(i_target, mss::mcbist::scrub_subtest<MC>(), i_const) + { + // Scrub operations run 128B + this->iv_program.change_len64(mss::OFF); + + // We're a single-port operation + o_rc = this->single_port_init(); + + // Targeted scrub needs to force a pause and the end boundary. So we make sure that happens here. + this->iv_program.change_forced_pause( i_const.iv_end_boundary ); + } + + targeted_scrub_operation() = delete; +}; + +/// +/// @brief Super Fast Init - used to init all memory behind a target with a given pattern +/// @note Uses broadcast mode if possible +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target behind which all memory should be initialized +/// @param[in] i_pattern an index representing a pattern to use to init memory (defaults to 0) +/// @return FAPI2_RC_SUCCESS iff everything ok +/// @note The function is asynchronous, and the caller should be looking for a done attention +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode sf_init( const fapi2::Target<T>& i_target, + const uint64_t i_pattern = PATTERN_0 ) +{ + using ET = mss::mcbistMCTraits<MC>; + FAPI_INF("superfast init start for %s", mss::c_str(i_target)); + + uint8_t l_sim = false; + FAPI_TRY( mss::attr::get_is_simulation( l_sim) ); + + if (l_sim) + { + // Use some sort of pattern in sim in case the verification folks need to look for something + // TK. Need a verification pattern. This is a not-good pattern for verification ... We don't really + // have a good pattern for verification defined. + FAPI_INF("running mss sim init in place of sf_init for %s", mss::c_str(i_target)); + return mss::mcbist::sim::sf_init<MC>(i_target, i_pattern); + } + else + { + fapi2::ReturnCode l_rc; + constraints<MC> l_const(i_pattern); + sf_init_operation<MC> l_init_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == fapi2::FAPI2_RC_SUCCESS, + ET::memdiags_sf_init_failed_init().set_MC_TARGET(i_target), + "Unable to initialize the MCBIST engine for a sf read %s", mss::c_str(i_target) ); + + return l_init_op.execute(); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Super Fast Read - used to run superfast read on all memory behind the target +/// Determines ability to braodcast to all ports behind a target, does so if possible. +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @tparam TT the mcbistTraits associated with T - derived +/// @param[in] i_target the target behind which all memory should be read +/// @param[in] i_stop stop conditions +/// @param[in] i_address mcbist::address representing the address from which to start. +// Defaults to the first address behind the target +/// @param[in] i_end whether to end, and where +/// Defaults to stop after slave rank +/// @param[in] i_end_address mcbist::address representing the address to end. +// Defaults to TT::LARGEST_ADDRESS +/// @return FAPI2_RC_SUCCESS iff everything ok +/// @note The function is asynchronous, and the caller should be looking for a done attention +/// @note The address is often the port, dimm, rank but this is not enforced in the API. +/// +template< mss::mc_type MC, fapi2::TargetType T = mss::mcbistMCTraits<MC>::MC_TARGET_TYPE , typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode sf_read( const fapi2::Target<T>& i_target, + const stop_conditions<MC>& i_stop, + const mss::mcbist::address& i_address = mss::mcbist::address(), + const end_boundary i_end = end_boundary::STOP_AFTER_SLAVE_RANK, + const mss::mcbist::address& i_end_address = mss::mcbist::address(TT::LARGEST_ADDRESS) ) +{ + using ET = mss::mcbistMCTraits<MC>; + FAPI_INF("superfast read - start for %s", mss::c_str(i_target)); + + FAPI_TRY( pre_maint_read_settings<MC>(i_target) ); + + { + fapi2::ReturnCode l_rc; + constraints<MC> l_const(i_stop, speed::LUDICROUS, i_end, i_address, i_end_address); + sf_read_operation<MC> l_read_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == fapi2::FAPI2_RC_SUCCESS, + ET::memdiags_sf_init_failed_init().set_MC_TARGET(i_target), + "Unable to initialize the MCBIST engine for a sf read %s", mss::c_str(i_target) ); + + return l_read_op.execute(); + } + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Scrub - continuous scrub all memory behind the target +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target behind which all memory should be scrubbed +/// @param[in] i_stop stop conditions +/// @param[in] i_speed the speed to scrub +/// @param[in] i_address mcbist::address representing the address from which to start. +/// @return FAPI2_RC_SUCCESS iff everything ok +/// @note The function is asynchronous, and the caller should be looking for a done attention +/// @note The address is often the port, dimm, rank but this is not enforced in the API. +/// +template< mss::mc_type MC, fapi2::TargetType T > +fapi2::ReturnCode background_scrub( const fapi2::Target<T>& i_target, + const stop_conditions<MC>& i_stop, + const speed i_speed, + const mss::mcbist::address& i_address ) +{ + using ET = mss::mcbistMCTraits<MC>; + FAPI_INF("continuous (background) scrub for %s", mss::c_str(i_target)); + + FAPI_TRY( pre_scrub_settings<MC>(i_target) ); + + { + fapi2::ReturnCode l_rc; + constraints<MC> l_const(i_stop, i_speed, end_boundary::STOP_AFTER_ADDRESS, i_address); + continuous_scrub_operation<MC> l_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == fapi2::FAPI2_RC_SUCCESS, + ET::memdiags_continuous_scrub_failed_init().set_MC_TARGET(i_target), + "Unable to initialize the MCBIST engine for a continuous scrub %s", mss::c_str(i_target) ); + + return l_op.execute(); + } + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Scrub - targeted scrub all memory described by the input address (rank, slave, etc.) +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target behind which all memory should be scrubbed +/// @param[in] i_stop stop conditions +/// @param[in] i_speed the speed to scrub +/// @param[in] i_start_address mcbist::address representing the address from which to start. +/// @param[in] i_end_address mcbist::address representing the address at which to end. +/// @param[in] i_end whether to end, and where +/// @return FAPI2_RC_SUCCESS iff everything ok +/// @note The function is asynchronous, and the caller should be looking for a done attention +/// @note The address is often the port, dimm, rank but this is not enforced in the API. +/// +template< mss::mc_type MC, fapi2::TargetType T > +fapi2::ReturnCode targeted_scrub( const fapi2::Target<T>& i_target, + const stop_conditions<MC>& i_stop, + const mss::mcbist::address& i_start_address, + const mss::mcbist::address& i_end_address, + const end_boundary i_end ) +{ + using ET = mss::mcbistMCTraits<MC>; + FAPI_INF("targeted scrub for %s", mss::c_str(i_target)); + + FAPI_TRY( pre_scrub_settings<MC>(i_target) ); + + { + fapi2::ReturnCode l_rc; + constraints<MC> l_const(i_stop, speed::LUDICROUS, i_end, i_start_address, i_end_address); + targeted_scrub_operation<MC> l_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == fapi2::FAPI2_RC_SUCCESS, + ET::memdiags_targeted_scrub_failed_init().set_MC_TARGET(i_target), + "Unable to initialize the MCBIST engine for a targeted scrub %s", mss::c_str(i_target) ); + + return l_op.execute(); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Continue current command on next address +/// The current commaand has paused on an error, so we can record the address of the error +/// and finish the current master or slave rank. +/// @tparam MC the mc type of the T +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target +/// @param[in] i_end whether to end, and where (default - don't stop at end of rank) +/// @param[in] i_stop stop conditions (default - 0 meaning 'don't change conditions') +/// @param[in] i_speed the speed to scrub (default - SAME_SPEED meaning leave speed untouched) +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T > +fapi2::ReturnCode continue_cmd( const fapi2::Target<T>& i_target, + const end_boundary i_end = end_boundary::DONT_CHANGE, + const stop_conditions<MC>& i_stop = stop_conditions<MC>(stop_conditions<MC>::DONT_CHANGE), + const speed i_speed = speed::SAME_SPEED ) +{ + // Too long, make shorter + using TT = mss::mcbistTraits<MC, T>; + using ET = mss::mcbistMCTraits<MC>; + + // We can use a local mcbist::program to help with the bit processing, and then write just the registers we touch. + mss::mcbist::program<MC> l_program; + fapi2::buffer<uint64_t> l_status; + + FAPI_INF("continue_cmd for %s", mss::c_str(i_target)); + + // TODO RTC:155518 Check for stop or in progress before allowing continue. Not critical + // as the caller should know and can check the in-progress bit in the event they don't + + if (i_end != end_boundary::DONT_CHANGE) + { + // Before we go too far, check to see if we're already stopped at the boundary we are asking to stop at + bool l_stopped_at_boundary = false; + uint64_t l_error_mode = 0; + bool l_detect_slave = false; + + FAPI_TRY( mss::getScom(i_target, TT::CFGQ_REG, l_program.iv_config) ); + FAPI_TRY( mss::getScom(i_target, TT::MCBAGRAQ_REG, l_program.iv_addr_gen) ); + l_program.iv_config.template extractToRight<TT::CFG_PAUSE_ON_ERROR_MODE, TT::CFG_PAUSE_ON_ERROR_MODE_LEN>(l_error_mode); + l_detect_slave = l_program.iv_addr_gen.template getBit<TT::MAINT_DETECT_SRANK_BOUNDARIES>(); + + + switch (i_end) + { + case end_boundary::STOP_AFTER_ADDRESS: + l_stopped_at_boundary = + l_program.iv_config.template getBit< TT::MCBIST_CFG_FORCE_PAUSE_AFTER_ADDR>() || + l_error_mode == end_boundary::STOP_AFTER_ADDRESS; + break; + + case end_boundary::STOP_AFTER_SLAVE_RANK: + // Note: we really want STOP_AFTER_MASTER_RANK here even though we're in the slave + // case because MASTER_RANK has the a 0 so that l_error_mode will check correctly + l_stopped_at_boundary = + l_program.iv_config.template getBit< TT::MCBIST_CFG_PAUSE_AFTER_RANK>() || + ((l_error_mode == end_boundary::STOP_AFTER_MASTER_RANK) && (l_detect_slave == false)); + break; + + case end_boundary::STOP_AFTER_MASTER_RANK: + l_stopped_at_boundary = + l_program.iv_config.template getBit< TT::MCBIST_CFG_PAUSE_AFTER_RANK>() || + ((l_error_mode == end_boundary::STOP_AFTER_MASTER_RANK) && (l_detect_slave == true)); + break; + + case end_boundary::STOP_AFTER_SUBTEST: + l_stopped_at_boundary = + l_program.iv_config.template getBit< TT::MCBIST_CFG_FORCE_PAUSE_AFTER_SUBTEST>() || + l_error_mode == end_boundary::STOP_AFTER_SUBTEST; + break; + + // By default we're not stopped at a boundary we're going to continue from + default: + break; + }; + + FAPI_ASSERT( l_stopped_at_boundary == false, + ET::memdiags_already_at_boundary().set_MC_TARGET(i_target).set_BOUNDARY(i_stop), + "Asked to stop at a boundary, but we're already there" ); + + // Ok, if we're here either we need to change the stop and boundary conditions. + // Read-modify-write the fields in the program. + FAPI_TRY( mss::getScom(i_target, TT::MCBAGRAQ_REG, l_program.iv_addr_gen) ); + + // Note: we are specifically not configuring broadcast mode here + // The continue command is called by PRD exclusively at mainline + // If we're at mainline, we can't run in broadcast mode + // If we ever need to call continue elsewhere, we'll need to do the following + // 1) add the function to configure broadcast mode + // 2) add in a switch to disable broadcast mode if we're at runtime + + l_program.change_end_boundary(i_end); + + FAPI_TRY( mss::mcbist::load_addr_gen(i_target, l_program) ); + + FAPI_TRY( mss::mcbist::load_config(i_target, l_program) ); + } + + // Thresholds + // According to API definition, 0 means don't change conditions + if( i_stop != stop_conditions<MC>::DONT_CHANGE) + { + FAPI_TRY( mss::mcbist::load_thresholds(i_target, i_stop) ); + } + + // Setup speed + FAPI_TRY( l_program.change_speed(i_target, i_speed) ); + + // Load new speed unless we aren't changing it + if( i_speed != speed::SAME_SPEED ) + { + FAPI_TRY( load_mcbparm(i_target, l_program) ); + } + + // Tickle the resume from pause + FAPI_TRY( mss::mcbist::resume(i_target) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Begin initialize memory +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MC engine +/// @tparam TT the mcbistTraits associated with T +/// @param[in] i_target MC +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode mss_initialize_memory(const fapi2::Target<T>& i_target ) +{ + using ET = mss::mcbistMCTraits<MC>; + FAPI_INF("Start mss_initialize_memory for %s", mss::c_str(i_target)); + + // If there are no DIMM we don't need to bother. In fact, we can't as we didn't setup + // attributes for the PHY, etc. + if (mss::count_dimm(i_target) == 0) + { + FAPI_INF("... skipping scrub for %s - no DIMM ...", mss::c_str(i_target)); + return fapi2::FAPI2_RC_SUCCESS; + } + + // If we're running in the simulator, we want to only touch the addresses which training touched + uint8_t l_sim = 0; + bool l_poll_results = false; + fapi2::buffer<uint64_t> l_status; + + // A small vector of addresses to poll during the polling loop + const std::vector<mss::poll_probe<T>> l_probes = + { + {i_target, "mcbist current address", TT::LAST_ADDR_REG}, + }; + + // We'll fill in the initial delay below + mss::poll_parameters l_poll_parameters(0, 200, 100 * mss::DELAY_1MS, 200, 10000); + uint64_t l_memory_size = 0; + + FAPI_TRY( mss::eff_memory_size<MC>(i_target, l_memory_size) ); + l_poll_parameters.iv_initial_delay = mss::calculate_initial_delay<MC>(i_target, (l_memory_size * mss::BYTES_PER_GB)); + + FAPI_TRY( mss::attr::get_is_simulation( l_sim) ); + + if (l_sim) + { + FAPI_INF("running mss sim init in place of scrub for %s", mss::c_str(i_target)); + + // Use some sort of pattern in sim in case the verification folks need to look for something + // TK. Need a verification pattern. This is a not-good pattern for verification ... We don't really + // have a good pattern for verification defined. + auto l_rc = mss::mcbist::sim::sf_init<MC>(i_target, mss::mcbist::PATTERN_0); + + // Unmask firs and turn off FIFO mode before returning + FAPI_TRY ( mss::unmask::after_memdiags<MC>( i_target ) ); + FAPI_TRY ( mss::reset_reorder_queue_settings<MC>(i_target) ); + + return l_rc; + } + + // In Cronus on hardware (which is how we got here - f/w doesn't call this) we want + // to call sf_init (0's) + // TK we need to check FIR given the way this is right now, we should adjust with better stop + // conditions when we learn more about what we want to find in the lab + FAPI_TRY( mss::memdiags::sf_init<MC>(i_target, mss::mcbist::PATTERN_0) ); + + // Poll for completion. + l_poll_results = mss::poll(i_target, TT::FIRQ_REG, l_poll_parameters, + [&l_status](const size_t poll_remaining, + const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("mcbist firq 0x%llx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + return l_status.getBit<TT::MCB_PROGRAM_COMPLETE>() == true; + }, + l_probes); + + FAPI_ASSERT( l_poll_results == true, + ET::memdiags_sf_init_failed_init().set_MC_TARGET(i_target), + "sf init for scrub/memdiags timedout %s", mss::c_str(i_target) ); + + // Unmask firs after memdiags and turn off FIFO mode + FAPI_TRY ( mss::unmask::after_memdiags<MC>( i_target ) ); + FAPI_TRY ( mss::reset_reorder_queue_settings<MC>(i_target) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Begin background scrub helper +/// @tparam MC the mc type of the T +/// @tparam T fapi2::TargetType of the MCBIST engine +/// @tparam TT the mcbistTraits associated with T +/// @param[in] i_target MC +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = mcbistTraits<MC, T> > +fapi2::ReturnCode mss_background_scrub_helper( const fapi2::Target<T>& i_target ) +{ + FAPI_INF("Start mss_background_scrub_helper b on: %s", mss::c_str( i_target )); + + // If there are no DIMM we don't need to bother. In fact, we can't as we didn't setup + // attributes for the PHY, etc. + if (mss::count_dimm(i_target) == 0) + { + FAPI_INF("... skipping background scrub for %s - no DIMM ...", mss::c_str(i_target)); + return fapi2::FAPI2_RC_SUCCESS; + } + + // If we're running in the simulator, we want to only touch the addresses which training touched + uint8_t l_sim = 0; + FAPI_TRY( mss::attr::get_is_simulation(l_sim) ); + + // Kick off background scrub if we are not running in sim + if (!(l_sim)) + { + // Start background scrub + FAPI_TRY ( mss::memdiags::background_scrub<MC>( i_target, + mss::mcbist::stop_conditions<MC>(), + mss::mcbist::speed::BG_SCRUB, + mss::mcbist::address() ) ); + } + + // Unmask firs after background scrub is started + FAPI_TRY ( mss::unmask::after_background_scrub<MC>( i_target ) ); + +fapi_try_exit: + return fapi2::current_err; +} + +} // namespace memdiags + +} // namespace mss +#endif diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_patterns.C b/src/import/generic/memory/lib/utils/mcbist/gen_patterns.C deleted file mode 100644 index 03053fc0f..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/gen_patterns.C +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_patterns.C $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_patterns.H b/src/import/generic/memory/lib/utils/mcbist/gen_patterns.H deleted file mode 100644 index f99518b7e..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/gen_patterns.H +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_patterns.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mcbist/gen_settings.H b/src/import/generic/memory/lib/utils/mcbist/gen_settings.H deleted file mode 100644 index 1d437f39c..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/gen_settings.H +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/gen_settings.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mcbist/settings.H b/src/import/generic/memory/lib/utils/mcbist/settings.H deleted file mode 100644 index 0d0b10883..000000000 --- a/src/import/generic/memory/lib/utils/mcbist/settings.H +++ /dev/null @@ -1,24 +0,0 @@ -/* IBM_PROLOG_BEGIN_TAG */ -/* This is an automatically generated prolog. */ -/* */ -/* $Source: src/import/generic/memory/lib/utils/mcbist/settings.H $ */ -/* */ -/* OpenPOWER HostBoot Project */ -/* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ -/* [+] International Business Machines Corp. */ -/* */ -/* */ -/* Licensed under the Apache License, Version 2.0 (the "License"); */ -/* you may not use this file except in compliance with the License. */ -/* You may obtain a copy of the License at */ -/* */ -/* http://www.apache.org/licenses/LICENSE-2.0 */ -/* */ -/* Unless required by applicable law or agreed to in writing, software */ -/* distributed under the License is distributed on an "AS IS" BASIS, */ -/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ -/* implied. See the License for the specific language governing */ -/* permissions and limitations under the License. */ -/* */ -/* IBM_PROLOG_END_TAG */ diff --git a/src/import/generic/memory/lib/utils/mss_bad_bits.H b/src/import/generic/memory/lib/utils/mss_bad_bits.H index e6d2cdfa5..b39d84859 100644 --- a/src/import/generic/memory/lib/utils/mss_bad_bits.H +++ b/src/import/generic/memory/lib/utils/mss_bad_bits.H @@ -46,6 +46,17 @@ namespace mss { /// +/// @brief A generic bad bits getter +/// @tparam MC type memory controller type +/// @param[in] i_target the fapi2 target oon which training was conducted +/// @param[out] o_array the bad bits array +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff success, else error code +/// +template <mss::mc_type MC> +fapi2::ReturnCode get_bad_dq_bitmap(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + uint8_t (&o_array)[BAD_BITS_RANKS][BAD_DQ_BYTE_COUNT]); + +/// /// @brief A generic bad bits setter /// @tparam MC type memory controller type /// @param[in] i_target the fapi2 target oon which training was conducted @@ -57,6 +68,23 @@ fapi2::ReturnCode set_bad_dq_bitmap(const fapi2::Target<fapi2::TARGET_TYPE_DIMM> uint8_t (&i_array)[BAD_BITS_RANKS][BAD_DQ_BYTE_COUNT]); /// +/// @brief combine the two bad bits arrays into the io_bad_bits array +/// @param[in] i_new_bad_bits bad bits to append +/// @param[in,out] io_bad_bits will contain the bitwise or of the original io_bad_bits and i_new_bad_bits +/// +inline void combine_bad_bits(const uint8_t (&i_new_bad_bits)[BAD_BITS_RANKS][BAD_DQ_BYTE_COUNT], + uint8_t (&io_bad_bits)[BAD_BITS_RANKS][BAD_DQ_BYTE_COUNT]) +{ + for (uint8_t l_rank = 0; l_rank < BAD_BITS_RANKS; ++ l_rank) + { + for (uint8_t l_bad_dq_byte = 0; l_bad_dq_byte < BAD_DQ_BYTE_COUNT; ++l_bad_dq_byte) + { + io_bad_bits[l_rank][l_bad_dq_byte] |= i_new_bad_bits[l_rank][l_bad_dq_byte]; + } + } +} + +/// /// @brief Records bad bits into the bad bits attribute /// @tparam MC MC type on which training was run /// @tparam T fapi2::TargetType on which training was conducted diff --git a/src/import/generic/memory/lib/utils/mss_field.H b/src/import/generic/memory/lib/utils/mss_field.H index e22b2fa47..c69bde04f 100644 --- a/src/import/generic/memory/lib/utils/mss_field.H +++ b/src/import/generic/memory/lib/utils/mss_field.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -37,8 +37,21 @@ #ifndef _MSS_FIELD_H_ #define _MSS_FIELD_H_ -#include <generic/memory/lib/utils/shared/mss_generic_consts.H> -#include <generic/memory/lib/utils/mss_generic_check.H> +#ifdef __PPE__ + #include <mss_generic_check.H> +#else + #include <generic/memory/lib/utils/shared/mss_generic_consts.H> + #include <generic/memory/lib/utils/mss_generic_check.H> +#endif + +//Macro +#ifdef __PPE__ + #define TARGIDFORMAT "0x%08X" + #define TARGTID i_target.get() +#else + #define TARGIDFORMAT "%s" + #define TARGTID spd::c_str(i_target) +#endif namespace mss { @@ -190,18 +203,18 @@ inline fapi2::ReturnCode get_field(const fapi2::Target<T>& i_target, .set_LIST_SIZE(i_data.size()) .set_FUNCTION(i_ffdc_codes) .set_TARGET(i_target), - "Out of bounds indexing (with %d) on a list of size %d for %s", + "Out of bounds indexing (with %d) on a list of size %d for " TARGIDFORMAT, BYTE, i_data.size(), - spd::c_str(i_target)); + TARGTID); { // Extracting desired bits const fapi2::buffer<OT> l_buffer(i_data[BYTE]); l_buffer.template extractToRight<F.get_start(), F.get_length()>(o_value); - FAPI_DBG("%s data[%d] = 0x%02x. Field with start bit %d, bit len %d, has data 0x%02x.", - spd::c_str(i_target), + FAPI_DBG(TARGIDFORMAT " data[%d] = 0x%02x. Field with start bit %d, bit len %d, has data 0x%02x.", + TARGTID, BYTE, i_data[BYTE], F.get_start(), @@ -214,6 +227,7 @@ inline fapi2::ReturnCode get_field(const fapi2::Target<T>& i_target, fapi_try_exit: return fapi2::current_err; } +#ifndef __PPE__ /// /// @brief Helper function to set byte field information @@ -274,6 +288,7 @@ inline fapi2::ReturnCode set_field(const fapi2::Target<T>& i_target, fapi_try_exit: return fapi2::current_err; } +#endif /// /// @brief byte field reader @@ -303,7 +318,7 @@ inline fapi2::ReturnCode get_field( const fapi2::Target<T>& i_target, { IT l_temp = 0; FAPI_TRY( (get_field<E, F>(i_target, i_data, i_ffdc_codes, l_temp)), - "Failed get_field() for %s", spd::c_str(i_target) ); + "Failed get_field() for " TARGIDFORMAT, TARGTID ); // Test if retrieved data seems valid FAPI_TRY( check::invalid_value(i_target, @@ -312,30 +327,33 @@ inline fapi2::ReturnCode get_field( const fapi2::Target<T>& i_target, typename TT::template COMPARISON_OP<IT>() ), F.get_byte(i_data), l_temp, - i_ffdc_codes), - "Failed fail_for_invalid_value() for %s", spd::c_str(i_target) ); + i_ffdc_codes, + TT::FIELD_STR), + "%s failed check::invalid_value() for %s", + TT::FIELD_STR, spd::c_str(i_target) ); // Output should only change if data check passes o_value = static_cast<OT>(l_temp); - FAPI_ASSERT( o_value == l_temp, fapi2::MSS_CONVERSION_ERROR() .set_ORIGINAL_VAL(l_temp) .set_CONVERTED_VAL(o_value) .set_TARGET(i_target) .set_FUNCTION(i_ffdc_codes), - "Conversion error between original %d to converted %d value for %s", - l_temp, o_value, spd::c_str(i_target) ); + "Conversion error between original %d to converted %d value for " TARGIDFORMAT, + l_temp, o_value, TARGTID); - FAPI_INF("%s: 0x%02x for %s", + FAPI_DBG("%s: 0x%02x for %s", TT::FIELD_STR, o_value, spd::c_str(i_target)); + fapi_try_exit: return fapi2::current_err; } +#ifndef __PPE__ /// /// @brief byte field writer /// @tparam E endian type @@ -378,7 +396,7 @@ inline fapi2::ReturnCode set_field( const fapi2::Target<T>& i_target, FAPI_TRY( (set_field<E, F>(i_target, i_setting, i_ffdc_codes, io_data)), "Failed set_field() for %s", spd::c_str(i_target) ); - FAPI_INF("%s: Set value of 0x%02x. Data for buffer at byte %d, is now 0x%02x for %s", + FAPI_DBG("%s: Set value of 0x%02x. Data for buffer at byte %d, is now 0x%02x for %s", TT::FIELD_STR, i_setting, BYTE, @@ -388,6 +406,7 @@ inline fapi2::ReturnCode set_field( const fapi2::Target<T>& i_target, fapi_try_exit: return fapi2::current_err; } +#endif }// mss diff --git a/src/import/generic/memory/lib/utils/mss_generic_check.H b/src/import/generic/memory/lib/utils/mss_generic_check.H index f31440e14..45428b8c3 100644 --- a/src/import/generic/memory/lib/utils/mss_generic_check.H +++ b/src/import/generic/memory/lib/utils/mss_generic_check.H @@ -37,15 +37,18 @@ #define _MSS_GENERIC_CHECK_H_ #include <fapi2.H> -#include <generic/memory/lib/utils/shared/mss_generic_consts.H> -#include <generic/memory/lib/utils/scom.H> -#include <generic/memory/lib/utils/c_str.H> + +#ifndef __PPE__ + #include <generic/memory/lib/utils/shared/mss_generic_consts.H> + #include <generic/memory/lib/utils/scom.H> + #include <generic/memory/lib/utils/c_str.H> +#endif namespace mss { namespace check { - +#ifndef __PPE__ /// /// @brief Checks whether any FIRs have lit up on a target /// @tparam MC MC type for which to check FIR's @@ -175,6 +178,7 @@ fapi2::ReturnCode fir_or_pll_fail( const fapi2::Target<T>& i_target, #endif } +#endif /// /// @brief Checks conditional passes and implements traces & exits if it fails /// @tparam T fapi2 target type @@ -196,6 +200,18 @@ inline fapi2::ReturnCode invalid_value(const fapi2::Target<T>& i_target, const FFDC i_ffdc_codes, const char* i_err_str = "") { +#ifdef __PPE__ + FAPI_ASSERT(i_conditional, + fapi2::MSS_FAILED_DATA_INTEGRITY_CHECK(). + set_VALUE(i_data). + set_BYTE(i_byte_index). + set_TARGET(i_target). + set_FFDC_CODE(i_ffdc_codes), + "%s Byte %d, Data returned: %d.", + i_err_str, + i_byte_index, + i_data); +#else FAPI_ASSERT(i_conditional, fapi2::MSS_FAILED_DATA_INTEGRITY_CHECK(). set_VALUE(i_data). @@ -208,6 +224,7 @@ inline fapi2::ReturnCode invalid_value(const fapi2::Target<T>& i_target, i_byte_index, i_data); +#endif return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: diff --git a/src/import/generic/memory/lib/utils/mss_math.H b/src/import/generic/memory/lib/utils/mss_math.H index ebb3da8e3..b3ff26fde 100644 --- a/src/import/generic/memory/lib/utils/mss_math.H +++ b/src/import/generic/memory/lib/utils/mss_math.H @@ -107,6 +107,35 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Round value to a multiple of another number +/// +/// @param[in] i_value value to round +/// @param[in] i_multiple multiple to round to +/// @return uint64_t rounded value +/// +inline uint32_t round_to_nearest_multiple(const uint32_t i_value, const uint32_t i_multiple) +{ + return ((i_value + i_multiple / 2) / i_multiple) * i_multiple; +} + +/// +/// @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 diff --git a/src/import/generic/memory/lib/utils/mss_rank.H b/src/import/generic/memory/lib/utils/mss_rank.H index b43e7e6ff..f4731df46 100644 --- a/src/import/generic/memory/lib/utils/mss_rank.H +++ b/src/import/generic/memory/lib/utils/mss_rank.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ +/* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -22,3 +22,327 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file mss_rank.H +/// @brief Tools to obtain rank info from DIMM or PORT target +/// +// *HWP HWP Owner: Mark Pizzutillo <Mark.Pizzutillo@ibm.com> +// *HWP HWP Backup: Stephen Glancy <sglancy@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_GENERIC_RANK_H_ +#define _MSS_GENERIC_RANK_H_ + +#include <fapi2.H> +#include <generic/memory/lib/utils/index.H> +#include <generic/memory/lib/utils/find.H> +#include <generic/memory/lib/mss_generic_attribute_getters.H> +#include <generic/memory/lib/utils/shared/mss_generic_consts.H> + +namespace mss +{ +namespace rank +{ + +/// +/// @brief Rank traits class for variations depending on MC +/// @tparam MC the memory controller type +/// +template <mss::mc_type MC = DEFAULT_MC_TYPE> +class rankTraits; + +/// +/// @brief Rank info class +/// @tparam MC the memory controller type +/// @tparam TT the class traits for the port +/// +template <mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = rankTraits<MC>> +class info +{ + + public: + // Delete default constructor + info() = delete; + + /// + /// @brief Constructor for info object + /// @param[in] i_target DIMM target + /// @param[in] i_index dimm rank index 0-3 + /// @param[out] o_rc return code, FAPI2_RC_SUCCESS if no error + /// + info(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_dimm_target, const uint8_t i_index, fapi2::ReturnCode& o_rc) + { + o_rc = fapi2::FAPI2_RC_SUCCESS; + + uint8_t l_master_ranks_per_dimm = 0; + FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_dimm_target, l_master_ranks_per_dimm)); + + FAPI_ASSERT((i_index < l_master_ranks_per_dimm), + fapi2::MSS_RANK_OUT_OF_RANGE() + .set_TARGET(i_dimm_target) + .set_RANK(i_index), + "Rank %u provided to info constructor for DIMM %s exceeded the number of master ranks per DIMM", + i_index, mss::c_str(i_dimm_target)); + // Targets + iv_dimm_target = i_dimm_target; + iv_port_target = mss::find_target<DEFAULT_MEM_PORT_TARGET>(i_dimm_target); + + // Ranks + iv_dimm_rank = i_index; + iv_port_rank = iv_dimm_rank + ((mss::index(i_dimm_target) * TT::RANK_INDEX_STEP)); + iv_phy_rank = iv_dimm_rank + ((mss::index(i_dimm_target) * TT::PHY_RANK_INDEX_STEP)); + fapi_try_exit: + o_rc = fapi2::current_err; + } + + /// + /// @brief Constructor for info object + /// @param[in] i_target PORT target + /// @param[in] i_index port rank index 0-7 + /// @param[out] o_rc return code, FAPI2_RC_SUCCESS if no error + /// + info(const fapi2::Target<DEFAULT_MEM_PORT_TARGET>& i_port_target, const uint8_t i_index, fapi2::ReturnCode& o_rc) + { + o_rc = fapi2::FAPI2_RC_SUCCESS; + + // Dimm Target + const uint8_t l_target_index = i_index / TT::RANK_INDEX_STEP; + const auto l_dimms = mss::find_targets<fapi2::TARGET_TYPE_DIMM>(i_port_target); + + // This assumes that the mc_type max # of dimms per port is correct + FAPI_ASSERT( !l_dimms.empty(), + fapi2::MSS_EMPTY_VECTOR(). + set_FUNCTION(INIT_RANK_INFO). + set_TARGET(i_port_target), + "Empty dimm vector received on port %s", + mss::spd::c_str(i_port_target)); + + { + // This will remain false in two cases: + // Either: + // 1. The corresponding DIMM index is out of range (caused by port index out of range) + // 2. The corresponding DIMMs master_ranks_per_dimm is less than the desired rank + bool l_valid_rank = false; + + for (const auto& l_dimm : l_dimms) + { + // Since we can't guarantee the order of the returned dimms, we iterate through until we (might) find + // the right one. There's a chance it may not exist for example given info(port, rank13) would give us + // a dimm index above 2, which may be out of range, so we could never find a matching dimm, so we will + // throw that error. + // If we do find the right dimm, we need to then make sure that the master_ranks_per_dimm attribute + // is greater than the expected dimm rank. + if (mss::index(l_dimm) == l_target_index) + { + uint8_t l_master_ranks_per_dimm = 0; + FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(l_dimm, l_master_ranks_per_dimm)); + + // The rank passed in matches one of a valid DIMM + l_valid_rank = (i_index % TT::RANK_INDEX_STEP) < l_master_ranks_per_dimm; + + iv_dimm_target = l_dimm; + } + } + + FAPI_ASSERT(l_valid_rank, + fapi2::MSS_RANK_OUT_OF_RANGE() + .set_TARGET(i_port_target) + .set_RANK(i_index), + "Rank %u provided to rank_info constructor for PORT %s was out of range", + i_index, mss::c_str(i_port_target)); + + // Port Target + iv_port_target = i_port_target; + + // Ranks + iv_dimm_rank = i_index % TT::RANK_INDEX_STEP; + iv_phy_rank = get_phy_rank_from_port_rank(i_index); + iv_port_rank = i_index; + } + + fapi_try_exit: + o_rc = fapi2::current_err; + } + + /// + /// @brief Accessor for port rank + /// @return the number (0-7) of the rank within its port + /// + inline const uint8_t get_port_rank() const + { + return iv_port_rank; + } + + /// + /// @brief Accessor for rank number of DIMM + /// @return the number (0-3) of the DIMM + /// + inline const uint8_t get_dimm_rank() const + { + return iv_dimm_rank; + } + + /// + /// @brief Accessor for phy rank + /// @return the phy rank value + /// @note in a hypothetical 2 4-rank dimm configuration (not possible), this value is not valid + /// + inline const uint8_t get_phy_rank() const + { + return iv_phy_rank; + } + + /// + /// @brief Accessor for DIMM target + /// @return the dimm target associated with the rank + /// + inline const fapi2::Target<fapi2::TARGET_TYPE_DIMM> get_dimm_target() const + { + return iv_dimm_target; + } + + /// + /// @brief Accessor for PORT target + /// @return the port target associated with the rank + /// + inline const fapi2::Target<DEFAULT_MEM_PORT_TARGET> get_port_target() const + { + return iv_port_target; + } + + /// + /// @brief Calculate phy rank given port rank (unit testable) + /// + /// @param[in] l_port_rank port rank + /// @return phy rank + /// + inline static uint8_t get_phy_rank_from_port_rank(const uint8_t i_port_rank) + { + const uint8_t l_dimm_rank = i_port_rank % TT::RANK_INDEX_STEP; + const uint8_t l_dimm_index = i_port_rank / TT::RANK_INDEX_STEP; + + // Dimm index * 2 sets the center point at 2 + // Add on the dimm rank + // Note that this logic does NOT apply in a hypothetical/impossible 2 4-rank dimm configuration + return ((l_dimm_index * TT::PHY_RANK_INDEX_STEP) + (l_dimm_rank)); + } + + private: + fapi2::Target<fapi2::TARGET_TYPE_DIMM> iv_dimm_target; + fapi2::Target<DEFAULT_MEM_PORT_TARGET> iv_port_target; + uint8_t iv_dimm_rank; + uint8_t iv_port_rank; + + // Note! a configuration of 2 4-rank dimms is not possible. + // In this hypothetical scenario, the value for phy-rank would not + // be valid / does not apply, as there will be some rollover. + // 1 rank-per-dimm: 0 dimm0 2 dimm1 + // 2 rank-per-dimm: 0/1 dimm0 2/3 dimm1. + // 2 rank single dimm 0/1 + // 4 rank (quad encoded) 0-3. + uint8_t iv_phy_rank; +}; + +/// +/// @brief Obtains all DIMM ranks on a given port target +/// @tparam MC the memory controller type +/// @tparam TT the class traits for the port +/// @param[in] i_port_target port target +/// @param[out] o_vect vector of ranks +/// @return FAPI2_RC_SUCCESS iff success +/// +template <mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = rankTraits<MC>> +fapi2::ReturnCode ranks_on_port(const fapi2::Target<DEFAULT_MEM_PORT_TARGET>& i_port_target, + std::vector<info<MC>>& o_vect) +{ + o_vect.clear(); + + uint8_t l_master_ranks_per_dimm[TT::MAX_DIMMS_PER_PORT] = {0}; + FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_port_target, l_master_ranks_per_dimm)); + + FAPI_ASSERT(((l_master_ranks_per_dimm[0] <= TT::MAX_RANKS_PER_DIMM) && + (l_master_ranks_per_dimm[1] <= TT::MAX_RANKS_PER_DIMM)), + fapi2::MSS_TOO_MANY_PRIMARY_RANKS_ON_PORT() + .set_PORT_TARGET(i_port_target), + "Primary ranks on PORT %s exceeded %u", + mss::c_str(i_port_target), + TT::MAX_RANKS_PER_DIMM); + + for (const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(i_port_target)) + { + const uint8_t l_dimm_index = mss::index(l_dimm); + const uint8_t l_port_index_start = l_dimm_index * TT::RANK_INDEX_STEP; + + fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; + + // For each rank index of the dimm from the port's perspective + for (uint8_t l_index = l_port_index_start; l_index < (l_port_index_start + l_master_ranks_per_dimm[l_dimm_index]); + ++l_index) + { + o_vect.push_back(mss::rank::info<>(i_port_target, l_index, l_rc)); + FAPI_TRY(l_rc); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Obtains all ranks on a given DIMM target +/// @tparam MC the memory controller type +/// @tparam TT the class traits for the port +/// @param[in] i_target DIMM target +/// @param[out] o_vect vector of ranks +/// @return FAPI2_RC_SUCCESS iff success +/// +template <mss::mc_type MC = DEFAULT_MC_TYPE, typename TT = rankTraits<MC>> +fapi2::ReturnCode ranks_on_dimm(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_dimm_target, + std::vector<info<MC>>& o_vect) +{ + o_vect.clear(); + uint8_t l_master_ranks_per_dimm = 0; + FAPI_TRY(mss::attr::get_num_master_ranks_per_dimm(i_dimm_target, l_master_ranks_per_dimm)); + + FAPI_ASSERT(l_master_ranks_per_dimm <= TT::MAX_RANKS_PER_DIMM, + fapi2::MSS_TOO_MANY_PRIMARY_RANKS_ON_DIMM() + .set_RANK_COUNT(l_master_ranks_per_dimm) + .set_DIMM_TARGET(i_dimm_target), + "Seeing %d primary ranks on DIMM %s", + l_master_ranks_per_dimm, + mss::c_str(i_dimm_target)); + { + // Return code for constructor call + fapi2::ReturnCode l_rc = fapi2::FAPI2_RC_SUCCESS; + + // For each rank index from the dimm's perspective + for (uint8_t l_index = 0; l_index < l_master_ranks_per_dimm; l_index++) + { + o_vect.push_back(mss::rank::info<>(i_dimm_target, l_index, l_rc)); + FAPI_TRY(l_rc, "ranks_on_dimm(): Error in rank::info constructor call for DIMM %s and rank %u", + mss::c_str(i_dimm_target), l_index); + } + } + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return a vector of rank numbers which represent the ranks for this dimm +/// @tparam MC the memory controller type +/// @param[in] i_dimm_target TARGET_TYPE_DIMM +/// @param[out] o_ranks a vector of ranks for dimm (numbers) +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template<mss::mc_type MC> +fapi2::ReturnCode ranks_on_dimm_helper(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_dimm_target, + std::vector<uint64_t>& o_ranks); + +} // rank +} // mss +#endif diff --git a/src/import/generic/memory/lib/utils/num.H b/src/import/generic/memory/lib/utils/num.H index 62332bb65..327aca84f 100644 --- a/src/import/generic/memory/lib/utils/num.H +++ b/src/import/generic/memory/lib/utils/num.H @@ -22,3 +22,34 @@ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ + +/// +/// @file num.H +/// @brief Miscellaneous number checking functions +/// +// *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: HB:FSP + +#ifndef _GEN_MSS_NUM_H_ +#define _GEN_MSS_NUM_H_ + +namespace mss +{ + +/// +/// @brief Return whether or not a number is odd +/// @param[in] i_number the number to check +/// @return true if i_number is odd +/// +template< typename T > +constexpr bool is_odd(const T i_number) +{ + return (i_number & 0x1); +} + + +} +#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..f2d6370be 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<MC> 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<MC>& 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..0b15b6ab2 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 @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2019 */ +/* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -22,3 +22,1309 @@ /* 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_system_attribute_setters.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 the N throttle for a given dram data bus utilization value +/// +/// @tparam T1 template parameter, type of input to be processed +/// @tparam T2 template parameter, type of input to be processed +/// @param[in] i_dram_util dram data bus utilization value +/// @param[in] i_throttle_m M throttle value in N/M throttling +/// +/// @return int32_t calculated N throttle value +/// +template<typename T1, typename T2> +inline uint32_t calc_n_from_dram_util(const T1 i_dram_util, const T2 i_throttle_m) +{ + constexpr uint64_t CONVERT_ADDR_UTIL_TO_DATA_UTIL = 4; + + return (static_cast<int>(static_cast<double>(i_dram_util) + * (i_throttle_m) + / (CONVERT_ADDR_UTIL_TO_DATA_UTIL) + / (PERCENT_CONVERSION) + ) + ); +} + +/// +/// @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", + mss::c_str(l_port), + l_run_slot, + l_run_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", + mss::c_str(iv_target), + iv_databus_port_max, + iv_power_uplift, + iv_power_uplift_idle, + iv_runtime_n_slot, + iv_runtime_n_port); + + FAPI_INF("The dimm power limit 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}; + + //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 %f, idle is %f", + 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 %f", + 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); + uint8_t l_found_ddimm = 0; + + //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); + mss::dimm::kind<MC> l_kind (l_dimm); + l_found_ddimm = (l_kind.iv_dimm_type == fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_DDIMM) ? 1 : l_found_ddimm; + //Calculate the power curve taking the thermal limit into account + FAPI_TRY( calc_power_curve(l_dimm_power_idle[l_pos], + l_dimm_power_max[l_pos], + l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos]), + "Failed to calculate the power curve for dimm %s, calculated dimm power curve slope is %d, intercept %d", + mss::c_str(l_dimm), + l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos]); + + //Calculate the databus utilization at the calculated power curve + calc_util_usage(l_dimm_power_slope[l_pos], + l_dimm_power_int[l_pos], + iv_dimm_thermal_limit[l_pos], + l_calc_util[l_pos]); + + FAPI_INF("THERMAL throttles: %s dram databus utilization is %f", mss::c_str(l_dimm), l_calc_util[l_pos]); + + l_temp_n_slot = power_thermal::throttled_cmds (l_calc_util[l_pos], iv_m_clocks); + + //Set to the min between the two value + //If iv_n_slot == 0 (so uninitialized), set it to the calculated slot value + //The l_n_slot value can't be equal to 0 because there's a dimm installed + if ((l_temp_n_slot < iv_n_slot) || (iv_n_slot == 0)) + { + iv_n_slot = l_temp_n_slot; + } + } + + //Set to lowest value between calculated and runtime + FAPI_INF("THERMAL throttles: runtime slot is %d, calc n slot is %d for %s", iv_runtime_n_slot, iv_n_slot, + mss::c_str(iv_target)); + //DDDIMMs: Taking the min of the SLOT and the iv_runtime_port throttle value + //ISDIMMs: 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 = (l_found_ddimm) ? + std::min(iv_runtime_n_port, static_cast<uint16_t>(iv_n_slot)) : + 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, dimm slope %d, intercept %d for %s", + 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 +{ + // Return 0 utilization if our intercept is above the power limit + o_util = (i_power_limit > i_int) ? (((static_cast<double>(i_power_limit) - i_int) / i_slope ) * UTIL_CONVERSION) : 0; + + // 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 (%f) 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 %f and port util %f", + 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 + // For DDIMM, set each virtual DIMM to the same utilization value since mrw slope/intercept/limit attributes + // are equally divided by number of virtual dimms (ie. mrw values are for whole DDIMM, not individual virtual DIMMs) + // For ISDIMM, divide utilization by number of DIMMs on the port + mss::dimm::kind<MC> l_kind (l_dimm); + o_databus_dimm_max[mss::index(l_dimm)] = (l_kind.iv_dimm_type == fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_DDIMM) ? + i_databus_port_max : + 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; + uint8_t l_found_ddimm = 0; + 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; + } + + for ( const auto& l_dimm : mss::find_targets<fapi2::TARGET_TYPE_DIMM>(iv_target) ) + { + mss::dimm::kind<MC> l_kind (l_dimm); + l_found_ddimm = (l_kind.iv_dimm_type == fapi2::ENUM_ATTR_MEM_EFF_DIMM_TYPE_DDIMM) ? 1 : l_found_ddimm; + } + + //assumptions slot <= port, l_count_dimms <=2 + if (i_util_slot * l_count_dimms > i_util_port) + { + FAPI_INF("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) if not a DDIMM, otherwise set to same value as above + o_util_dimm_max[(!l_high_pos)] = (l_found_ddimm) ? + 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", + mss::c_str(iv_target), + l_high_pos, + o_util_dimm_max[l_high_pos], + !l_high_pos, + o_util_dimm_max[!l_high_pos]); + } + else + { + //If only 1 dimm, i_util_port == i_util_slot + //If 2 dimms, 2*i_util_slot <= i_util_pot + //Either way, limit utilization by the slot value + for (const auto& l_dimm : mss::find_targets<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 set the safemode throttle register +/// @tparam MC mss::mc_type +/// @tparam T the fapi2 target type of the target +/// @tparam TT portTraits port traits for the given MC target type +/// @param[in] i_target the port target +/// @return fapi2::FAPI2_RC_SUCCESS if ok +/// @note sets FARB4Q +/// @note used to set throttle window (N throttles / M clocks) +/// +template<mss::mc_type MC = DEFAULT_MC_TYPE, fapi2::TargetType T, typename TT = throttle_traits<MC>> +fapi2::ReturnCode calc_utilization(const fapi2::Target<T>& i_target, + const uint32_t i_input_databus_util, + const uint32_t i_dram_clocks, + const uint16_t i_safemode_throttle_per_port, + const uint32_t i_max_databus_util, + uint32_t& o_util, + bool& o_util_error, + bool& o_safemode) +{ + constexpr uint64_t l_min_util = TT::MIN_UTIL; + + o_util = i_input_databus_util; + o_safemode = false; + + // Use MRW safemode throttle values if input utilization is zero + if (i_input_databus_util == 0) + { + FAPI_TRY(calc_util_from_throttles<MC>(i_safemode_throttle_per_port, + i_dram_clocks, + o_util), + "%s Error calculating utilization from safemode throttle %d and mem clocks %d", + mss::c_str(i_target), + i_safemode_throttle_per_port, + i_dram_clocks); + FAPI_INF("%s Safemode throttles being used since input util is zero: Using N=%d, Utilization %f", + mss::c_str(i_target), + i_safemode_throttle_per_port, + o_util); + o_safemode = true; + } + else if (i_input_databus_util < l_min_util) + { + o_util_error = true; + } + + // Make sure MIN_UTIL <= input_utilization <= max_utilization + o_util = (o_util >= l_min_util) ? std::min(o_util, i_max_databus_util) : l_min_util; + + FAPI_INF("%s MRW dram clock window: %d, databus utilization: %d", + mss::c_str(i_target), + i_dram_clocks, + o_util); + + return fapi2::FAPI2_RC_SUCCESS; + +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) + { + FAPI_INF("Seeing no DIMMs on %s -- skipping", mss::c_str(l_port)); + 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) + { + FAPI_INF("Seeing no DIMMs on %s -- skipping", mss::c_str(l_port)); + 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 port 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( mss::attr::set_port_pos_of_fail_throttle(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_slot, + l_fin_port, + 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 bcf4f1207..77f177c40 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 @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018,2019 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] Evan Lojewski */ /* [+] International Business Machines Corp. */ /* */ @@ -38,7 +38,9 @@ #ifndef _MSS_GENERIC_CONSTS_H_ #define _MSS_GENERIC_CONSTS_H_ -#include <cstdint> +#ifndef __PPE__ + #include <cstdint> +#endif namespace mss { @@ -52,6 +54,39 @@ enum common_consts MEMCMP_EQUAL = 0, ///< Equal comparison value for memcmp BAD_BITS_RANKS = 4, ///< Bad bit attribute's number of ranks BAD_DQ_BYTE_COUNT = 10, ///< Bad bit attribute's number of byte + ATTR_RANK0 = 0, ///< Attribute index for rank0 + ATTR_RANK1 = 1, ///< Attribute index for rank1 + ATTR_RANK2 = 2, ///< Attribute index for rank2 + ATTR_RANK3 = 3, ///< Attribute index for rank3 +}; + +/// +/// @brief Common mcbist constants +/// +enum mcbist_common_consts +{ + CYCLES_PER_CMD = 4, ///< Best case cycles per MCBIST command + MAX_RANK_PER_DIMM = 4, + + BYTES_PER_GB = 1000000000, ///< Multiplier to go from GB to B + T_PER_MT = 1000000, ///< Multiplier to go from MT/s to T/s + + // Number of double words in... + NUM_DW_IN_128B = 16, + NUM_DW_IN_64B = 8, + + BG_SCRUB_IN_HOURS = 12, + CMD_TIMEBASE = 8192, ///< Represents the timebase multiplier for the MCBIST inter cmd gap + MAX_CMD_GAP = 4095, ///< Represents the maximum (non-multplied) time for MCBIST inter cmd gap + + + // MCBIST polling constant for actual HW + // The specific value here is not important, only that it is very large to avoid polling timeouts, + // but not to avoid any actual hardware timeouts + // Note: ~0 is not used as that would cause MCBIST to never timeout even if the hardware is in an infinite loop + // You can't get greater than ~0, so you'd never timeout + // TODO RTC:166340 - Clean up MCBIST polling + OVERLY_LARGE_NUMBER_OF_POLLS = 5000000000000, }; /// @@ -60,12 +95,13 @@ enum common_consts enum common_timings { DELAY_1NS = 1, - DELAY_10NS = 10 , ///< general purpose 10 ns delay for HW mode - DELAY_100NS = 100, ///< general purpose 100 ns delay for HW mode - DELAY_1US = 1000, ///< general purpose 1 usec delay for HW mode - DELAY_10US = 10000, ///< general purpose 1 usec delay for HW mode - DELAY_100US = 100000, ///< general purpose 100 usec delay for HW mode - DELAY_1MS = 1000000, ///< general purpose 1 ms delay for HW mode + DELAY_10NS = 10 , ///< general purpose 10 ns delay for HW mode + DELAY_100NS = 100, ///< general purpose 100 ns delay for HW mode + DELAY_1US = 1000, ///< general purpose 1 usec delay for HW mode + DELAY_10US = 10000, ///< general purpose 1 usec delay for HW mode + DELAY_100US = 100000, ///< general purpose 100 usec delay for HW mode + DELAY_1MS = 1000000, ///< general purpose 1 ms delay for HW mode + DELAY_1S = 1000000000, ///< general purpose 1 sec delay for HW mode }; /// @@ -80,6 +116,17 @@ enum conversions NIBBLES_PER_BYTE = 2, BITS_PER_NIBBLE = 4, BITS_PER_BYTE = 8, + BIT_POS_FOR_BYTE0 = BITS_PER_BYTE * 0, + BIT_POS_FOR_BYTE1 = BITS_PER_BYTE * 1, + BIT_POS_FOR_BYTE2 = BITS_PER_BYTE * 2, + BIT_POS_FOR_BYTE3 = BITS_PER_BYTE * 3, + BIT_POS_FOR_BYTE4 = BITS_PER_BYTE * 4, + BIT_POS_FOR_BYTE5 = BITS_PER_BYTE * 5, + BIT_POS_FOR_BYTE6 = BITS_PER_BYTE * 6, + BIT_POS_FOR_BYTE7 = BITS_PER_BYTE * 7, + + // Used by exp_decoder.C for dA to cA + DECI_TO_CENTI = 10, }; enum generic_sizes @@ -141,8 +188,11 @@ enum generic_ffdc_codes SET_MRANKS = 0x102A, SET_HOST_TO_DDR_SPEED_RATIO = 0x102B, SET_ATTR_HOST_TO_DDR_SPEED_RATIO = 0x102C, + CCS_INST_CONFIGURE_RANK = 0x102D, SET_DIMM_RANKS_CNFG = 0x1039, DDIMM_RAWCARD_DECODE = 0x103a, + INIT_RANK_INFO = 0x103B, + BIAS_PMIC_FROM_SPD = 0x103C, SET_DRAM_WIDTH = 0x1040, SET_SI_VREF_DRAM_WR = 0x1041, @@ -170,6 +220,58 @@ enum generic_ffdc_codes SET_DRAM_GEN_METADATA = 0x1063, SET_DIMM_TYPE_METADATA = 0x1064, SET_DIMM_POS_METADATA = 0x1065, + SET_LOGICAL_RANKS = 0x1066, + SET_PRIM_STACK_TYPE = 0x1067, + SET_DIMM_SIZE = 0x1068, + SET_PRIM_BUS_WIDTH = 0x1069, + SET_PRIM_DIE_COUNT = 0x1070, + SET_DRAM_DENSITY = 0x1071, + + // Power thermal functions + POWER_LIMIT = 0x1072, + SLOPE = 0x1073, + INTERCEPT = 0x1074, + + SET_SI_RD_VREF_DQ = 0x1075, + SET_CAC_DELAY_A = 0x1076, + SET_CAC_DELAY_B = 0x1077, + EFD_CA_LATENCY_MODE = 0x1080, + EFD_CA_PL_MODE = 0x1081, + SET_COL_ADDR_BITS = 0x1082, + SET_ROW_ADDR_BITS = 0x1083, + SET_3DS_HEIGHT = 0x1084, + SET_DRAM_CWL = 0x1085, + SET_DRAM_TREFI = 0x1086, + SET_DRAM_TCCD_L = 0x1087, + SET_DRAM_TWTR_L = 0x1088, + SET_DRAM_TWTR_S = 0x1089, + SET_DRAM_TFAW = 0x108A, + SET_DRAM_TRCD = 0x108B, + SET_DRAM_TRP = 0x108C, + SET_DRAM_TRAS = 0x108D, + SET_DRAM_TWR = 0x108E, + SET_DRAM_TRTP = 0x108F, + SET_DRAM_TRRD_S = 0x1090, + SET_DRAM_TRRD_L = 0x1091, + SET_DRAM_TRFC = 0x1092, + SET_DRAM_TRFC_DLR = 0x1093, + SET_DRAM_MFG_ID = 0x1094, + + // Used in fw_mark_store.H for MSS_INVALID_RANK_PASSED + FWMS_READ = 0x1095, + FWMS_WRITE = 0x1096, + + // Used in hw_mark_store.H for MSS_INVALID_RANK_PASSED + HWMS_READ = 0x1097, + HWMS_WRITE = 0x1098, + + // MSS_INVALID_INDEX_PASSED + SYMBOL_COUNT_READ = 0x1099, + SYMBOL_COUNT_WRITE = 0x109A, + + SET_RCD_MFG_ID = 0x109B, + SET_DRAM_MODULE_HEIGHT = 0x109C, + SET_SPD_REVISION = 0x109D, }; /// @@ -242,6 +344,510 @@ enum states NO_CHIP_SELECT_ACTIVE = 0xFF, }; + +enum port_select +{ + // Port selects for MCBIST and CCS + // Select for 1 port + PORT0 = 0b1000, + PORT1 = 0b0100, + PORT2 = 0b0010, + PORT3 = 0b0001, + // Selects for 2 port combinations + PORT01 = PORT0 | PORT1, + PORT02 = PORT0 | PORT2, + PORT03 = PORT0 | PORT3, + PORT12 = PORT1 | PORT2, + PORT13 = PORT1 | PORT3, + PORT23 = PORT2 | PORT3, + // Selects for 3 port combinations + PORT012 = PORT0 | PORT1 | PORT2, + PORT013 = PORT0 | PORT1 | PORT3, + PORT023 = PORT0 | PORT2 | PORT3, + PORT123 = PORT1 | PORT2 | PORT3, + // Select all + PORT0123 = PORT0 | PORT1 | PORT2 | PORT3, + // Maybe a better name for disabling all + PORT_NONE = 0b0000, +}; + +enum dimm_select +{ + // Dimm selects for MCBIST and CCS + // Select for 1 dimm + DIMM0 = 0b10, + DIMM1 = 0b01, + // Selects for 2 dimm combinations + DIMM01 = DIMM0 | DIMM1, + // Maybe a better name for disabling all + DIMM_NONE = 0b00, +}; + +namespace mcbist +{ + +enum broadcast_timebase +{ + // Number of 1024 2:1 cycle timebases to wait starting MCBIST + // for SRQs to get synced for broadcast mode + TB_COUNT_2 = 0b0000001, + TB_COUNT_4 = 0b0000011, + TB_COUNT_8 = 0b0000111, + TB_COUNT_16 = 0b0001111, + TB_COUNT_32 = 0b0011111, + TB_COUNT_64 = 0b0111111, + TB_COUNT_128 = 0b1111111, +}; + +enum data_rotate_mode +{ + // MCBIST data rotate modes refer to register MCBDRCR bits 0:3 + ROTATE_0_BITS = 0b0000, + ROTATE_1_BITS = 0b0001, + ROTATE_2_BITS = 0b0010, + ROTATE_3_BITS = 0b0011, + ROTATE_4_BITS = 0b0100, + ROTATE_5_BITS = 0b0101, + ROTATE_6_BITS = 0b0110, + ROTATE_7_BITS = 0b0111, + ROTATE_8_BITS = 0b1000, + ROTATE_9_BITS = 0b1001, + ROTATE_10_BITS = 0b1010, + ROTATE_11_BITS = 0b1011, + ROTATE_12_BITS = 0b1100, + ROTATE_13_BITS = 0b1101, + ROTATE_14_BITS = 0b1110, + ROTATE_15_BITS = 0b1111, +}; + +enum data_seed_mode +{ + // MCBIST data seed modes refer to register MCBDRCR bits 21:22 + ALL_UNIQUE = 0b00, + REPEAT_SEED_0 = 0b01, + REPEAT_SEED_1 = 0b10, + REPEAT_SEED_2 = 0b11, +}; + +enum data_mode +{ + // MCBIST test data modes + FIXED_DATA_MODE = 0b000, + RAND_FWD_MODE = 0b001, + RAND_REV_MODE = 0b010, + RAND_FWD_MAINT = 0b011, + RAND_REV_MAINT = 0b100, + DATA_EQ_ADDR = 0b101, + ROTATE_LEFT_MODE = 0b110, + ROTATE_RIGHT_MODE = 0b111, +}; + +// 0:3 Operation Type +enum op_type +{ + WRITE = 0b0000, // fast, with no concurrent traffic + READ = 0b0001, // fast, with no concurrent traffic + READ_WRITE = 0b0010, + WRITE_READ = 0b0011, + READ_WRITE_READ = 0b0100, + READ_WRITE_WRITE = 0b0101, + RAND_SEQ = 0b0110, + READ_READ_WRITE = 0b1000, + SCRUB_RRWR = 0b1001, + STEER_RW = 0b1010, + ALTER = 0b1011, // (W) + DISPLAY = 0b1100, // (R, slow) + CCS_EXECUTE = 0b1111, + + // if bits 9:11 (Data Mode bits) = 000 (bits 4:8 used to specify which subtest to go to) + // Refresh only cmd if bits 9:11 (Data Mode bits) /= 000 + GOTO_SUBTEST_N = 0b0111, +}; + + +enum test_type +{ + USER_MODE = 0, + CENSHMOO = 1, + SUREFAIL = 2, + MEMWRITE = 3, + MEMREAD = 4, + CBR_REFRESH = 5, + MCBIST_SHORT = 6, + SHORT_SEQ = 7, + DELTA_I = 8, + DELTA_I_LOOP = 9, + SHORT_RAND = 10, + LONG1 = 11, + BUS_TAT = 12, + SIMPLE_FIX = 13, + SIMPLE_RAND = 14, + SIMPLE_RAND_2W = 15, + SIMPLE_RAND_FIXD = 16, + SIMPLE_RA_RD_WR = 17, + SIMPLE_RA_RD_R = 18, + SIMPLE_RA_FD_R = 19, + SIMPLE_RA_FD_R_INF = 20, + SIMPLE_SA_FD_R = 21, + SIMPLE_RA_FD_W = 22, + INFINITE = 23, + WR_ONLY = 24, + W_ONLY = 25, + R_ONLY = 26, + W_ONLY_RAND = 27, + R_ONLY_RAND = 28, + R_ONLY_MULTI = 29, + SHORT = 30, + SIMPLE_RAND_BARI = 31, + W_R_INFINITE = 32, + W_R_RAND_INFINITE = 33, + R_INFINITE1 = 34, + R_INFINITE_RF = 35, + MARCH = 36, + SIMPLE_FIX_RF = 37, + SHMOO_STRESS = 38, + SIMPLE_RAND_RA = 39, + SIMPLE_FIX_RA = 40, + SIMPLE_FIX_RF_RA = 41, + TEST_RR = 42, + TEST_RF = 43, + W_ONLY_INFINITE_RAND = 44, + MCB_2D_CUP_SEQ = 45, + MCB_2D_CUP_RAND = 46, + SHMOO_STRESS_INFINITE = 47, + HYNIX_1_COL = 48, + RMWFIX = 49, + RMWFIX_I = 50, + W_INFINITE = 51, + R_INFINITE = 52, +}; + + +} // namespace mcbist + +namespace omi +{ + +/// +/// @brief dl0 no forward progress timer +/// +enum no_forward_progress_timer +{ + NO_FORWARD_TIMER_1US = 0b0000, + NO_FORWARD_TIMER_2US = 0b0001, + NO_FORWARD_TIMER_4US = 0b0010, + NO_FORWARD_TIMER_8US = 0b0011, + NO_FORWARD_TIMER_16US = 0b0100, + NO_FORWARD_TIMER_32US = 0b0101, + NO_FORWARD_TIMER_64US = 0b0110, + NO_FORWARD_TIMER_128US = 0b0111, + NO_FORWARD_TIMER_256US = 0b1000, + NO_FORWARD_TIMER_512US = 0b1001, + NO_FORWARD_TIMER_1MS = 0b1010, + NO_FORWARD_TIMER_2MS = 0b1011, + NO_FORWARD_TIMER_4MS = 0b1100, + NO_FORWARD_TIMER_8MS = 0b1101, + NO_FORWARD_TIMER_16MS = 0b1110, + NO_FORWARD_TIMER_DISABLED = 0b1111, +}; + +/// +/// @brief dl0 PHY control mode - determines the amount of time needed to receive pattern A or pattern B +/// +enum phy_ctr_mode +{ + PHY_CTR_MODE_1US = 0b0000, + PHY_CTR_MODE_50US = 0b0001, + PHY_CTR_MODE_100US = 0b0010, + PHY_CTR_MODE_200US = 0b0011, + PHY_CTR_MODE_500US = 0b0100, + PHY_CTR_MODE_1MS = 0b0101, + PHY_CTR_MODE_2MS = 0b0110, + PHY_CTR_MODE_3MS = 0b0111, + PHY_CTR_MODE_4MS = 0b1000, + PHY_CTR_MODE_5MS = 0b1001, + PHY_CTR_MODE_6MS = 0b1010, + PHY_CTR_MODE_8MS = 0b1011, + PHY_CTR_MODE_10MS = 0b1100, + PHY_CTR_MODE_15MS = 0b1101, + PHY_CTR_MODE_30MS = 0b1110, + PHY_CTR_MODE_60MS = 0b1111, +}; + +/// +/// @brief dl0 supported link widths +/// +enum link_widths +{ + LINK_WIDTHS_X4PLUS1 = 0b1000, + LINK_WIDTHS_X16 = 0b0100, + LINK_WIDTHS_X8 = 0b0010, + LINK_WIDTHS_X4 = 0b0001, +}; + +/// +/// @brief dl0 train mode +/// +enum train_mode +{ + TX_ZEROS = 0b0000, + TX_PATTERN_A = 0b0001, + TX_PATTERN_B = 0b0010, + TX_SYNC_PATTERN = 0b0011, + TX_TRAINING_STATE1 = 0b0100, + TX_TRAINING_STATE2 = 0b0101, + TX_TRAINING_STATE3 = 0b0110, + TX_TRAINING_STATE0 = 0b0111, + ENABLE_AUTO_TRAINING = 0b1000, +}; + + +/// +/// @brief These values are the number of clock cycles and the times specified assume a 625ps period. +/// This timer value must be greater than the di/dt timer +/// +enum rx_cdr_timer +{ + CDR_TIMER_DISABLED = 0b0001, + CDR_TIMER_60NS = 0b0001, + CDR_TIMER_125NS = 0b0010, + CDR_TIMER_185NS = 0b0011, + CDR_TIMER_250NS = 0b0100, + CDR_TIMER_375NS = 0b0101, + CDR_TIMER_500NS = 0b0110, + CDR_TIMER_750NS = 0b0111, + CDR_TIMER_1US = 0b1000, + CDR_TIMER_2US = 0b1001, + CDR_TIMER_4US = 0b1010, + CDR_TIMER_8US = 0b1011, + CDR_TIMER_16US = 0b1100, + CDR_TIMER_32US = 0b1101, + CDR_TIMER_64US = 0b1110, + CDR_TIMER_128US = 0b1111, +}; + +/// +/// @brief Amount of time to wait after lane is turned on/off before another lane can be turned on/off +/// +enum didt_timer +{ + DIDT_TIMER_DISABLED = 0b0000, + DIDT_TIMER_5NS = 0b0001, + DIDT_TIMER_10NS = 0b0010, + DIDT_TIMER_15NS = 0b0011, + DIDT_TIMER_20NS = 0b0100, + DIDT_TIMER_30NS = 0b0101, + DIDT_TIMER_45NS = 0b0110, + DIDT_TIMER_60NS = 0b0111, + DIDT_TIMER_90NS = 0b1000, + DIDT_TIMER_125NS = 0b1001, + DIDT_TIMER_185NS = 0b1010, + DIDT_TIMER_250NS = 0b1011, + DIDT_TIMER_375NS = 0b1100, + DIDT_TIMER_500NS = 0b1101, + DIDT_TIMER_768NS = 0b1110, + DIDT_TIMER_1US = 0b1111, +}; + +/// +/// @brief Calibration timer - amount of time betweem re-calibration for a given lane +/// +enum recal_timer +{ + RECAL_TIMER_DISABLED = 0b000, + RECAL_TIMER_25MS = 0b001, + RECAL_TIMER_50MS = 0b010, + RECAL_TIMER_100MS = 0b011, + RECAL_TIMER_200MS = 0b100, + RECAL_TIMER_400MS = 0b101, + RECAL_TIMER_800MS = 0b110, + RECAL_TIMER_1600MS = 0b111, +}; + +/// +/// @brief PMU prescalar value +/// +enum pmu_prescalar +{ + PRESCALAR_16BIT = 0b000, + PRESCALAR_8BIT = 0b001, + PRESCALAR_20BIT = 0b100, +}; + + +/// +/// @brief PMU cntrx pair selector +/// +enum cntrl_pair_selector +{ + SEL_ODD = 0b00, + SEL_EVEN = 0b01, + SEL_BOTH_AND = 0b10, + SEL_BOTH_XOR = 0b11, +}; + +/// +/// @brief PMU cntrx event selector +/// +enum cntrl_event_selector +{ + SIG_7_6 = 0b00, + SIG_5_4 = 0b01, + SIG_3_2 = 0b10, + SIG_1_0 = 0b11, +}; + +/// +/// @brief Configuration override to select lane width for dynamic lane power down modes. +/// +enum lan_width_override +{ + TL_CTR_BY_SIDEBAND = 0b00, + DL_OVERRIDE_X2 = 0b01, + DL_OVERRIDE_X4 = 0b10, + DL_OVERRIDE_X8 = 0b11, +}; + +/// +/// @brief Number of consecutive pattern B seen before indicating received pattern B +/// +enum b_hysteresis +{ + B_HYSTERESIS_16 = 0b0000, + B_HYSTERESIS_24 = 0b0001, + B_HYSTERESIS_32 = 0b0010, + B_HYSTERESIS_40 = 0b0011, + B_HYSTERESIS_48 = 0b0100, + B_HYSTERESIS_56 = 0b0101, + B_HYSTERESIS_64 = 0b0110, + B_HYSTERESIS_72 = 0b0111, + B_HYSTERESIS_80 = 0b1000, + B_HYSTERESIS_96 = 0b1001, + B_HYSTERESIS_128 = 0b1010, + B_HYSTERESIS_256 = 0b1011, + B_HYSTERESIS_512 = 0b1100, + B_HYSTERESIS_1K = 0b1101, + B_HYSTERESIS_2K = 0b1110, + B_HYSTERESIS_4K = 0b1111, +}; + +/// +/// @brief Number of consecutive pattern A seen before indicating received pattern A. +/// +enum a_hysteresis +{ + A_HYSTERESIS_16 = 0b0000, + A_HYSTERESIS_24 = 0b0001, + A_HYSTERESIS_32 = 0b0010, + A_HYSTERESIS_48 = 0b0011, + A_HYSTERESIS_64 = 0b0100, + A_HYSTERESIS_96 = 0b0101, + A_HYSTERESIS_128 = 0b0110, + A_HYSTERESIS_256 = 0b0111, + A_HYSTERESIS_512 = 0b1000, + A_HYSTERESIS_1024 = 0b1001, + A_HYSTERESIS_2K = 0b1010, + A_HYSTERESIS_4K = 0b1011, + A_HYSTERESIS_8K = 0b1100, + A_HYSTERESIS_16K = 0b1101, + A_HYSTERESIS_32K = 0b1110, + A_HYSTERESIS_64K = 0b1111, +}; + +/// +/// @brief Lanes disabled +/// +enum +{ + LANE_DISABLED_NONE = 0b00000000, + LANE_DISABLED_7 = 0b00000001, + LANE_DISABLED_6 = 0b00000010, + LANE_DISABLED_5 = 0b00000100, + LANE_DISABLED_4 = 0b00001000, + LANE_DISABLED_3 = 0b00010000, + LANE_DISABLED_2 = 0b00100000, + LANE_DISABLED_1 = 0b01000000, + LANE_DISABLED_0 = 0b10000000, +}; + +/// +/// @brief dl0 inject crc direction +/// +enum crc_inject_dir +{ + CRC_DIR_RX = 0, + CRC_DIR_TX = 1, +}; + +/// +/// @brief dl0 crc injection rate +/// +enum crc_inject_rate +{ + CRC_INJ_RATE_1US = 0b0000, + CRC_INJ_RATE_8US = 0b0001, + CRC_INJ_RATE_64US = 0b0010, + CRC_INJ_RATE_512US = 0b0011, + CRC_INJ_RATE_4MS = 0b0100, + CRC_INJ_RATE_32MS = 0b0101, + CRC_INJ_RATE_256MS = 0b0110, + CRC_INJ_RATE_2S = 0b0111, +}; + +/// +/// @brief CFG_DL0_EDPL_TIME: dl0 edpl time window +/// +enum edpl_time_win +{ + EDPL_TIME_WIN_NO = 0b0000, + EDPL_TIME_WIN_4US = 0b0001, + EDPL_TIME_WIN_32US = 0b0010, + EDPL_TIME_WIN_256US = 0b0011, + EDPL_TIME_WIN_2MS = 0b0100, + EDPL_TIME_WIN_16MS = 0b0101, + EDPL_TIME_WIN_128MS = 0b0110, + EDPL_TIME_WIN_1S = 0b0111, + EDPL_TIME_WIN_8S = 0b1000, + EDPL_TIME_WIN_64S = 0b1001, + EDPL_TIME_WIN_512S = 0b1010, + EDPL_TIME_WIN_4KS = 0b1011, + EDPL_TIME_WIN_32KS = 0b1100, + EDPL_TIME_WIN_256KS = 0b1101, + EDPL_TIME_WIN_2MILLIONS = 0b1110, + EDPL_TIME_WIN_16MILLIONS = 0b1111, +}; + +/// +/// @brief CFG_DL0_EDPL_THRESHOLD: dl0 edpl threshold +/// +enum edpl_err_thres +{ + EDPL_ERR_THRES_DISABLED = 0b000, + EDPL_ERR_THRES_2 = 0b001, + EDPL_ERR_THRES_4 = 0b010, + EDPL_ERR_THRES_8 = 0b011, + EDPL_ERR_THRES_16 = 0b100, + EDPL_ERR_THRES_32 = 0b101, + EDPL_ERR_THRES_64 = 0b110, + EDPL_ERR_THRES_128 = 0b111, +}; + +/// +/// @brief CONFIG1_CFG_PREIPL_PRBS_TIME: config1 pre-ipl prbs time +/// +enum preipl_prbs_time +{ + PREIPL_PRBS_256US = 0b000, + PREIPL_PRBS_1US = 0b001, + PREIPL_PRBS_4MS = 0b010, + PREIPL_PRBS_16MS = 0b011, + PREIPL_PRBS_64MS = 0b100, + PREIPL_PRBS_256MS = 0b101, + PREIPL_PRBS_1S = 0b110, + PREIPL_PRBS_4S = 0b111, +}; + +} /// /// @brief Supported DIMM speed equality deliberations /// @@ -260,6 +866,7 @@ namespace spd enum rev : uint8_t { V0_0 = 0x00, ///< represents Rev 0.0 + V0_3 = 0x03, ///< represents Rev 0.3 V1_0 = 0x10, ///< represents Rev 1.0 V1_1 = 0x11, ///< represents Rev 1.1 V1_2 = 0x12, ///< represents Rev 1.2 @@ -269,7 +876,7 @@ enum rev : uint8_t GEN_SEC_MAX = V1_1, RDIMM_MAX = V1_1, LRDIMM_MAX = V1_2, - DDIMM_MAX = V0_0, + DDIMM_MAX = V0_3, }; /// @@ -356,6 +963,7 @@ enum class throttle_type }; + /// /// @brief Trait classes for mc_type /// @tparam MC the mc_type diff --git a/src/import/generic/memory/lib/utils/voltage/gen_mss_volt.H b/src/import/generic/memory/lib/utils/voltage/gen_mss_volt.H index d84c97dbe..9ff39109e 100644 --- a/src/import/generic/memory/lib/utils/voltage/gen_mss_volt.H +++ b/src/import/generic/memory/lib/utils/voltage/gen_mss_volt.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2018 */ +/* Contributors Listed Below - COPYRIGHT 2018,2020 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -115,8 +115,6 @@ inline fapi2::ReturnCode setup_voltage_rail_values(const fapi2::Target<TT::SPD_T FAPI_TRY( (mss::set_voltage_attributes<M, D>(l_voltage_target, l_dram_voltages)), "Failed to set volt attributes for %s", mss::c_str(l_voltage_target) ); - FAPI_INF("%s End setup_voltage_rail_values", mss::c_str(i_target)); - fapi_try_exit: return fapi2::current_err; } |