From 3d485fbdab55175f716253b4fe4c52835c93a5a0 Mon Sep 17 00:00:00 2001 From: Brian Silver Date: Thu, 2 Jun 2016 06:28:57 -0500 Subject: Add memdiags scrub capability Change-Id: I1f42c836a8fc4ff3ca31401ec53f2a3aeda77513 Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/25376 Reviewed-by: Zane C. Shelley Tested-by: Jenkins Server Tested-by: Hostboot CI Reviewed-by: ANDRE A. MARIN Reviewed-by: STEPHEN GLANCY Reviewed-by: Jennifer A. Stofer Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/25377 Tested-by: FSP CI Jenkins Reviewed-by: Daniel M. Crowell --- .../hwp/memory/lib/eff_config/memory_size.C | 36 +- .../p9/procedures/hwp/memory/lib/mcbist/address.H | 18 + .../p9/procedures/hwp/memory/lib/mcbist/mcbist.C | 19 +- .../p9/procedures/hwp/memory/lib/mcbist/mcbist.H | 363 ++++++++++++++-- .../p9/procedures/hwp/memory/lib/mcbist/memdiags.C | 481 +++++++++++++++++---- .../p9/procedures/hwp/memory/lib/mcbist/memdiags.H | 267 +++++++----- .../p9/procedures/hwp/memory/lib/mcbist/settings.H | 31 +- .../p9/procedures/hwp/memory/lib/mcbist/sim.C | 3 +- .../procedures/hwp/memory/lib/shared/mss_const.H | 8 + .../chips/p9/procedures/hwp/memory/p9_mss_scrub.C | 4 +- .../procedures/hwp/memory/tests/mss_memdiags_ut.C | 264 ++++++++++- 11 files changed, 1271 insertions(+), 223 deletions(-) (limited to 'src/import/chips/p9/procedures/hwp') diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/eff_config/memory_size.C b/src/import/chips/p9/procedures/hwp/memory/lib/eff_config/memory_size.C index 48da0578f..6f6d50995 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/eff_config/memory_size.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/eff_config/memory_size.C @@ -33,8 +33,11 @@ #include #include +#include + namespace mss { + /// /// @brief Return the total memory size behind an MCA /// @param[in] i_target the MCA target @@ -44,12 +47,12 @@ namespace mss template<> fapi2::ReturnCode eff_memory_size( const fapi2::Target& i_target, uint64_t& o_size ) { - uint32_t l_sizes[PORTS_PER_MCS]; + uint32_t l_sizes[MAX_DIMM_PER_PORT]; o_size = 0; FAPI_TRY( mss::eff_dimm_size(i_target, &(l_sizes[0])) ); - for (size_t i = 0; i < PORTS_PER_MCS; ++i) + for (size_t i = 0; i < MAX_DIMM_PER_PORT; ++i) { o_size += l_sizes[i]; } @@ -58,5 +61,34 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Return the total memory size behind an MBIST +/// @param[in] i_target the MCBIST target +/// @param[out] o_size the size of memory in GB behind the target +/// @return FAPI2_RC_SUCCESS if ok +/// +template<> +fapi2::ReturnCode eff_memory_size( const fapi2::Target& i_target, uint64_t& o_size ) +{ + o_size = 0; + + for (const auto& mcs : mss::find_targets(i_target)) + { + uint32_t l_sizes[PORTS_PER_MCS][MAX_DIMM_PER_PORT]; + FAPI_TRY( mss::eff_dimm_size(mcs, &(l_sizes[0][0])) ); + + for (size_t i = 0; i < PORTS_PER_MCS; ++i) + { + for (size_t j = 0; j < MAX_DIMM_PER_PORT; ++j) + { + o_size += l_sizes[i][j]; + } + } + } + +fapi_try_exit: + return fapi2::current_err; +} + } diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/address.H b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/address.H index ff9cd9f24..707e27dad 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/address.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/address.H @@ -141,6 +141,24 @@ class address o_end.iv_address.setBit(); } + + /// + /// @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. + constexpr uint64_t l_magic_sim_number = 0b1000000; + + get_range(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 diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C index 7971d2248..7f1a08b6b 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C @@ -213,17 +213,14 @@ fapi2::ReturnCode execute( const fapi2::Target& i_target, bool l_poll_result = false; poll_parameters l_poll_parameters; - FAPI_TRY( i_program.clear_errors(i_target) ); + FAPI_TRY( clear_errors(i_target) ); - // Slam the subtests in to the mcbist registers - FAPI_TRY( load_mcbmr(i_target, i_program) ); + // Slam the address generator config + FAPI_TRY( load_addr_gen(i_target, i_program) ); // Slam the parameters in to the mcbist parameter register FAPI_TRY( load_mcbparm(i_target, i_program) ); - // Slam the address generator config - FAPI_TRY( load_addr_gen(i_target, i_program) ); - // Slam the configured address maps down FAPI_TRY( load_mcbamr( i_target, i_program) ); @@ -233,6 +230,16 @@ fapi2::ReturnCode execute( const fapi2::Target& i_target, // Slam the control register down FAPI_TRY( load_control( i_target, i_program) ); + // Load the patterns and any associated bits for random, etc + FAPI_TRY( load_pattern( i_target, i_program) ); + + // Load the thresholds + FAPI_TRY( load_thresholds( 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(i_target, i_program) ); + // Start the engine, and then poll for completion FAPI_TRY(start_stop(i_target, mss::START)); diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H index ca5b75cfc..b135f16b0 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -84,6 +85,7 @@ class mcbistTraits static constexpr uint64_t SRERR_REG = MCBIST_MBSEC1Q; static constexpr uint64_t THRESHOLD_REG = MCBIST_MBSTRQ; static constexpr uint64_t FIRQ_REG = MCBIST_MCBISTFIRQ; + static constexpr uint64_t LAST_ADDR_REG = MCBIST_MCBMCATQ; static constexpr uint64_t MCBAMR0A0Q_REG = MCBIST_MCBAMR0A0Q; static constexpr uint64_t MCBAMR1A0Q_REG = MCBIST_MCBAMR1A0Q; @@ -117,6 +119,11 @@ class mcbistTraits COMPL_3RD_CMD = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_COMPL_3RD_CMD, ADDR_REV_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_REV_MODE, ADDR_RAND_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_RAND_MODE, + + // Goto subtests use the compl_1st - rand_mode to define the subtest to jump to + GOTO_SUBTEST = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_COMPL_1ST_CMD, + GOTO_SUBTEST_LEN = 5, + ECC_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ECC_MODE, DATA_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_DATA_MODE, DATA_MODE_LEN = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_DATA_MODE_LEN, @@ -135,6 +142,7 @@ class mcbistTraits MCBIST_START = MCBIST_MCB_CNTLQ_START, MCBIST_STOP = MCBIST_MCB_CNTLQ_STOP, + MCBIST_RESUME = MCBIST_MCB_CNTLQ_RESUME_FROM_PAUSE, MCBIST_IN_PROGRESS = MCBIST_MCB_CNTLSTATQ_IP, MCBIST_DONE = MCBIST_MCB_CNTLSTATQ_DONE, @@ -266,6 +274,39 @@ class subtest_t 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() + { + uint64_t l_port = 0; + constexpr uint64_t l_len = (TT::COMPL_2ND_CMD - TT::COMPL_1ST_CMD) + 1; + iv_mcbmr.template extractToRight(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() + { + return iv_mcbmr.template getBit() ? 1 : 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(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 @@ -342,8 +383,9 @@ class subtest_t inline void change_addr_sel( const uint16_t i_index ) { // Roll the index around - tidy support for an index which is out of range. - static const uint16_t MAX_ADDRESS_START_END_REGISTERS = 3 + 1; + constexpr uint16_t MAX_ADDRESS_START_END_REGISTERS = 3 + 1; iv_mcbmr.template insertFromRight(i_index % MAX_ADDRESS_START_END_REGISTERS); + FAPI_INF("changed address select to index %d (0x%x)", i_index, iv_mcbmr); return; } @@ -397,6 +439,39 @@ inline subtest_t write_subtest() return l_subtest; } +/// +/// @brief Return a scrub subtest - configured simply +/// @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< fapi2::TargetType T, typename TT = mcbistTraits > +inline subtest_t scrub_subtest() +{ + // Starts life full of 0's + subtest_t l_subtest; + + // 0:3 = 1001 - we want subtest type to be a Scrub + l_subtest.iv_mcbmr.template insertFromRight(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 read subtest - configured simply /// @tparam T the fapi2::TargetType - derived @@ -430,6 +505,29 @@ inline subtest_t read_subtest() return l_subtest; } +/// +/// @brief Return a goto subtest - configured simply +/// @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< fapi2::TargetType T, typename TT = mcbistTraits > +inline subtest_t goto_subtest( const uint64_t i_jump_to ) +{ + // Starts life full of 0's + subtest_t l_subtest; + + // 0:3 = 0111 - we want subtest type to be a Goto + l_subtest.iv_mcbmr.template insertFromRight(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 T the fapi2::TargetType - derived @@ -501,7 +599,8 @@ class program iv_addr_map3(0), iv_config(0), iv_control(0), - iv_async(false) + iv_async(false), + iv_pattern(PATTERN_0) { // Enable the maintenance mode addressing change_maint_address_mode(mss::ON); @@ -512,6 +611,10 @@ class program // Turn off counting mode for all address configs iv_addr_gen.insertFromRight(0b0000); + + // By default if there's an error, we stop after the errored address + iv_config.insertFromRight( + stop_conditions::STOP_AFTER_ADDRESS); } /// @@ -525,20 +628,142 @@ class program return; } + /// + /// @brief Change the mcbist thresholds + /// @param[in] i_thresholds the new thresholds + /// @return void + /// + inline void change_thresholds( const thresholds& i_thresholds ) + { + iv_thresholds = i_thresholds; + return; + } + + /// + /// @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& 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; + + 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(i_target, l_size) ); + + // 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) + // TOTAL ADDRESSES = sum over all dimms of ( [DIMM CAPACITY]/128B ) + l_min_cmd_gap = ((l_seconds * (l_freq * T_PER_MT)) / 2) / ((l_size * BYTES_PER_GB) / 128); + + FAPI_INF("setting bg scrub speed: %dMT/s, memory: %dGB, duration: %ds, gap: %d", + l_freq, l_size, l_seconds, l_min_cmd_gap); + + if (CMD_TIMEBASE < l_min_cmd_gap) + { + change_min_cmd_gap(l_min_cmd_gap / CMD_TIMEBASE); + change_min_gap_timebase(mss::ON); + } + else + { + change_min_cmd_gap(l_min_cmd_gap); + change_min_gap_timebase(mss::OFF); + } + + return fapi2::FAPI2_RC_SUCCESS; + } + break; + + // Otherwise it's SAME_SPEED or something else in which case we do nothing + default: + break; + }; + + fapi_try_exit: + return fapi2::current_err; + } + /// /// @brief Change MCBIST Stop conditions /// @param[in] i_stops the stop conditions + /// @param[in] i_only_if_error forces the stop conditions to only be in effect if there is an error + /// if there is not an error the subtest continues. Defaults to mss::NO, meaning these are forced conditions /// @return FAPI2_RC_SUCCSS iff ok - /// @note if the user askes for stop-on-error we stop after the operation on the failed address has completed + /// @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_stops( const stop_conditions i_stops ) + inline void change_stops( const stop_conditions i_stops, const bool i_only_if_error = mss::NO ) { - if (i_stops != DONT_CHANGE) + // If there's no change, just get outta here + if (i_stops == DONT_CHANGE) + { + goto fapi_try_exit; + } + + // If these conditions only take effect when there's an error, set that up in the ERROR_MODE field + // NO_STOP_ON_ERROR is specific to the ERROR MODE field, so set that up here too + if ((i_only_if_error == mss::YES) || (i_stops == NO_STOP_ON_ERROR)) { iv_config.insertFromRight(i_stops); + goto fapi_try_exit; } + // Otherwise, these are forced conditions and they need to be put in their specific fields + switch(i_stops) + { + case STOP_AFTER_ADDRESS: + iv_config.setBit(); + break; + + case STOP_AFTER_RANK: + iv_config.setBit(); + break; + + case STOP_AFTER_SUBTEST: + iv_config.setBit(); + break; + + // Same as don't-change + default: + break; + }; + + fapi_try_exit: FAPI_INF("load MCBIST stops: 0x%016lx (0x%016lx)", i_stops, iv_config); + + return; + } + + /// + /// @brief Change MCBIST End boundaries + /// @param[in] i_end the end boundary + /// @return FAPI2_RC_SUCCSS iff ok + /// @note this really is just deciding if the stop conditions are master or slave rank boundaries + /// + inline void change_end_boundary( const end_boundary i_end ) + { + // This is a little funny ... the hardware has one bit representing 'master' and 'slave' + // So, NONE really represents a stop condition. We probably should combine the two, but the + // API doc from PRD asks for them separate. + uint64_t l_detect_slave = i_end == end_boundary::SLAVE_RANK ? 1 : 0; + FAPI_INF("load MCBIST end boundaries: detect slave? %s", (l_detect_slave == 1 ? "yes" : "no") ); + iv_addr_gen.writeBit(l_detect_slave); } /// @@ -849,23 +1074,29 @@ class program } /// - /// @brief Clear mcbist errors - /// @param[in] i_target fapi2::Target of the MCBIST - /// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok + /// @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 clear_errors( const fapi2::Target i_target ) const + inline fapi2::ReturnCode change_pattern( const uint64_t i_pattern ) { - // TK: Clear the more detailed errors checked above - FAPI_INF("Clear MCBIST error state"); - FAPI_TRY( mss::putScom(i_target, TT::MCBSTATQ_REG, 0) ); - FAPI_TRY( mss::putScom(i_target, TT::SRERR_REG, 0) ); - FAPI_TRY( mss::putScom(i_target, TT::FIRQ_REG, 0) ); + 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), + "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; } - // 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) @@ -901,6 +1132,12 @@ class program // True iff we want to run in asynchronous mode bool iv_async; + + // The pattern for the pattern generator + uint64_t iv_pattern; + + // The error thresholds for the program + thresholds iv_thresholds; }; /// @@ -1072,6 +1309,25 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Resume the MCBIST engine +/// @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< fapi2::TargetType T, typename TT = mcbistTraits > +inline fapi2::ReturnCode resume( const fapi2::Target& i_target ) +{ + fapi2::buffer l_buf; + + FAPI_TRY( mss::getScom(i_target, TT::CNTLQ_REG, l_buf) ); + FAPI_TRY( mss::putScom(i_target, TT::CNTLQ_REG, l_buf.setBit()) ); + +fapi_try_exit: + return fapi2::current_err; +} + /// /// @brief Return whether or not the MCBIST engine has an operation in progress /// @tparam T the fapi2::TargetType - derived @@ -1158,6 +1414,27 @@ inline fapi2::ReturnCode load_mcbparm( const fapi2::Target& i_target, const m return mss::putScom(i_target, TT::MCBPARMQ_REG, i_program.iv_parameters); } +/// +/// @brief Clear mcbist errors +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the mcbistTraits associated with T - derived +/// @param[in] i_target fapi2::Target of the MCBIST +/// @return fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok +/// +template< fapi2::TargetType T, typename TT = mcbistTraits > +inline fapi2::ReturnCode clear_errors( const fapi2::Target i_target ) +{ + // TK: Clear the more detailed errors checked above + FAPI_INF("Clear MCBIST error state"); + FAPI_TRY( mss::putScom(i_target, TT::MCBSTATQ_REG, 0) ); + FAPI_TRY( mss::putScom(i_target, TT::SRERR_REG, 0) ); + FAPI_TRY( mss::putScom(i_target, TT::FIRQ_REG, 0) ); + +fapi_try_exit: + return fapi2::current_err; +} + + /// /// @brief Load MCBIST pattern given a pattern /// @tparam T, the fapi2::TargetType - derived @@ -1165,6 +1442,7 @@ inline fapi2::ReturnCode load_mcbparm( const fapi2::Target& i_target, const m /// @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< fapi2::TargetType T, typename TT = mcbistTraits > @@ -1172,11 +1450,9 @@ inline fapi2::ReturnCode load_pattern( const fapi2::Target& i_target, const p { uint64_t l_address = TT::PATTERN0_REG; - // Sanity check the pattern as the user can pass in anything they like - FAPI_ASSERT( i_pattern.size() == TT::PATTERN_COUNT, - fapi2::MSS_MEMDIAGS_INVALID_PATTERN_SIZE().set_SIZE(i_pattern.size()).set_TARGET(i_target), - "Attempting to load a pattern which is not %d elements (%d)", TT::PATTERN_COUNT, i_pattern.size() ); + // TODO RTC:155561 Add random pattern support. + // 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) @@ -1198,33 +1474,32 @@ fapi_try_exit: /// @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 an index representing the pattern you'd like +/// @param[in] i_index the pattern index /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, typename TT = mcbistTraits > -inline fapi2::ReturnCode load_pattern( const fapi2::Target& i_target, uint64_t i_index ) +inline fapi2::ReturnCode load_pattern( const fapi2::Target& i_target, uint64_t i_pattern ) { - if (NO_PATTERN != i_index) + if (NO_PATTERN != i_pattern) { bool l_invert = false; - FAPI_INF("%s load MCBIST pattern index %d", mss::c_str(i_target), i_index); - // Sanity check the pattern since they're just numbers. - FAPI_ASSERT( i_index <= mcbist::LAST_PATTERN, - fapi2::MSS_MEMDIAGS_INVALID_PATTERN_INDEX().set_INDEX(i_index).set_TARGET(i_target), - "Attempting to load a pattern which does not exist %d", i_index ); + // 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 ((i_index % 2) != 0) + if ((i_pattern % 2) != 0) { l_invert = true; - i_index -= 1; + i_pattern -= 1; } - return load_pattern(i_target, patterns[i_index / 2], l_invert); + return load_pattern(i_target, patterns[i_pattern / 2], l_invert); } return fapi2::FAPI2_RC_SUCCESS; @@ -1233,6 +1508,20 @@ fapi_try_exit: return fapi2::current_err; } +/// +/// @brief Load MCBIST pattern given an index +/// @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< fapi2::TargetType T, typename TT = mcbistTraits > +inline fapi2::ReturnCode load_pattern( const fapi2::Target& i_target, const mcbist::program& i_program ) +{ + return load_pattern(i_target, i_program.iv_pattern); +} + /// /// @brief Load MCBIST Threshold Register /// @tparam T, the fapi2::TargetType - derived @@ -1248,6 +1537,20 @@ inline fapi2::ReturnCode load_thresholds( const fapi2::Target& i_target, cons return mss::putScom(i_target, TT::THRESHOLD_REG, i_thresholds); } +/// +/// @brief Load MCBIST Threshold Register +/// @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< fapi2::TargetType T, typename TT = mcbistTraits > +inline fapi2::ReturnCode load_thresholds( const fapi2::Target& i_target, const mcbist::program& i_program ) +{ + return load_thresholds(i_target, i_program.iv_thresholds); +} + } // namespace } // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.C b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.C index 4318e111c..f3e4a3908 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.C @@ -38,11 +38,14 @@ #include #include +#include + using fapi2::TARGET_TYPE_MCBIST; using fapi2::TARGET_TYPE_MCA; using fapi2::TARGET_TYPE_DIMM; using fapi2::TARGET_TYPE_SYSTEM; using fapi2::FAPI2_RC_SUCCESS; +using fapi2::FAPI2_RC_INVALID_PARAMETER; namespace memdiags { @@ -55,19 +58,54 @@ namespace memdiags template<> fapi2::ReturnCode stop( const fapi2::Target& i_target ) { + // Too long, make shorter + using TT = mss::mcbist::mcbistTraits; + + // 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 l_status; + fapi2::buffer l_last_address; + bool l_poll_result = false; + FAPI_INF("Stopping any mcbist operations which are in progress"); // TODO RTC:153951 Add masking of FIR when stopping - return mss::mcbist::start_stop(i_target, mss::STOP); + FAPI_TRY( mss::mcbist::start_stop(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& 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() == 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", l_last_address); + + // So we've either stopped or we timed out + FAPI_ASSERT( l_poll_result == true, + fapi2::MSS_MEMDIAGS_MCBIST_FAILED_TO_STOP().set_TARGET(i_target), + "The MCBIST engine failed to stop its program" ); + +fapi_try_exit: + return fapi2::current_err; + } /// -/// @brief memdiags::base initializer +/// @brief memdiags init helper +/// 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 -/// @note specialized for Nimbus as the port select mechanism is different /// template<> -fapi2::ReturnCode memdiags::base::init() +fapi2::ReturnCode operation::base_init() { FAPI_INF("memdiags base init"); @@ -77,23 +115,21 @@ fapi2::ReturnCode memdiags::base::init() // Zero out cmd timebase - mcbist::program constructor does that for us. // Load pattern - FAPI_TRY( mss::mcbist::load_pattern(iv_target, iv_const.iv_pattern) ); + FAPI_TRY( iv_program.change_pattern(iv_const.iv_pattern) ); // Load stop conditions iv_program.change_stops(iv_const.iv_stop); + // Load end boundaries + iv_program.change_end_boundary(iv_const.iv_end_boundary); + // Load thresholds - FAPI_TRY( mss::mcbist::load_thresholds(iv_target, iv_const.iv_thresholds) ); + iv_program.change_thresholds(iv_const.iv_thresholds); - // A superfast operation which has an end address of 0 means 'to the end' - if (iv_const.iv_end_address == 0) - { - iv_const.iv_start_address.get_range(iv_const.iv_end_address); - } + // 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 - // Configure the address range - FAPI_TRY( mss::mcbist::config_address_range0(iv_target, iv_const.iv_start_address, iv_const.iv_end_address) ); // Apparently the MCBIST engine needs the ports selected even tho the ports are specified // in the subtest. We can just select them all, and it adjusts when it executes the subtest @@ -107,17 +143,15 @@ fapi_try_exit: } /// -/// @brief Super Fast Base initializer -/// @note Uses broadcast mode if possible +/// @brief memdiags multi-port init helper +/// 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<> -fapi2::ReturnCode memdiags::sf_operation::init() +fapi2::ReturnCode operation::multi_port_init() { - FAPI_INF("superfast init"); - - // Initialize the base class - FAPI_TRY( base::init() ); + FAPI_INF("multi-port init"); // Deterimine which ports are functional and whether we can broadcast to them // TK on the broadcast BRS @@ -129,7 +163,7 @@ fapi2::ReturnCode memdiags::sf_operation::init() for (const auto& d : mss::find_targets(p)) { // Don't destroy the subtest passed in, copy it - mss::mcbist::subtest_t l_subtest = iv_subtest; + auto l_subtest = iv_subtest; l_subtest.enable_port(mss::relative_pos(p)); l_subtest.enable_dimm(mss::index(d)); @@ -138,26 +172,36 @@ fapi2::ReturnCode memdiags::sf_operation::init() } } + // A operation which has an end address of 0 means 'to the end' + if (iv_const.iv_end_address == 0) + { + iv_const.iv_start_address.get_range(iv_const.iv_end_address); + } + + // Configure the address range + FAPI_TRY( mss::mcbist::config_address_range0(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 Super Fast Read (End of Port) initializer -/// @note Uses broadcast mode if possible +/// @brief Single port initializer +/// 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<> -fapi2::ReturnCode memdiags::sf_read_operation::end_of_port_init() +fapi2::ReturnCode operation::single_port_init() { - FAPI_INF("superfast read (end of port) init"); + FAPI_INF("single port init"); 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(); - // Initialize the base class - FAPI_TRY( base::init() ); - // Make sure the specificed port is functional FAPI_ASSERT( mss::is_functional(iv_target, l_relative_port_number), fapi2::MSS_MEMDIAGS_PORT_NOT_FUNCTIONAL() @@ -166,10 +210,6 @@ fapi2::ReturnCode memdiags::sf_read_operation::end_of_port_i .set_TARGET(iv_target), "Port with relative postion %d is not functional", l_relative_port_number ); - // The address should have the port and DIMM noted in it. All we need to do is calculate the - // remainder of the address - iv_const.iv_start_address.get_range(iv_const.iv_end_address); - // No broadcast mode for this one // Push on a read subtest { @@ -178,15 +218,169 @@ fapi2::ReturnCode memdiags::sf_read_operation::end_of_port_i 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("adding superfast read remainder of port %d, DIMM %d", l_relative_port_number, l_dimm_number); + FAPI_INF("adding subtest 0x%x for port %d, DIMM %d", l_subtest, l_relative_port_number, l_dimm_number); } - return FAPI2_RC_SUCCESS; + // The address should have the port and DIMM noted in it. All we need to do is calculate the + // remainder of the address, assuming the caller didn't setup an end address already. + // *INDENT-OFF* + if (iv_const.iv_end_address == 0) + { + iv_is_sim ? + iv_const.iv_start_address.get_sim_end_address(iv_const.iv_end_address) : + iv_const.iv_start_address.get_range(iv_const.iv_end_address); + } + // *INDENT-ON* + + // Configure the address range + FAPI_TRY( mss::mcbist::config_address_range0(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::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 +/// +template<> +continuous_scrub_operation::continuous_scrub_operation( + const fapi2::Target& i_target, + const constraints i_const, + fapi2::ReturnCode& o_rc ): + operation(i_target, mss::mcbist::scrub_subtest(), i_const) +{ + mss::mcbist::address l_generic_start_address; + mss::mcbist::address l_generic_end_address; + + FAPI_INF("setting up for continuous scrub"); + + // Scrub operations run 128B + 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* + iv_is_sim ? + l_generic_start_address.get_sim_end_address(l_generic_end_address) : + l_generic_start_address.get_range(l_generic_end_address); + // *INDENT-ON* + + FAPI_TRY( mss::mcbist::config_address_range0(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. + iv_program.iv_subtests.push_back(iv_subtest); + iv_program.iv_subtests.push_back(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 : iv_target.template getChildren()) + { + for (const auto& d : p.template getChildren()) + { + // Don't destroy the subtest passed in, copy it + auto l_subtest = iv_subtest; + + l_subtest.enable_port(mss::relative_pos(p)); + l_subtest.enable_dimm(mss::index(d)); + iv_program.iv_subtests.push_back(l_subtest); + FAPI_INF("adding scrub subtest for %s (dimm %d) (0x%x)", 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. + // + iv_program.iv_subtests.push_back(mss::mcbist::goto_subtest(2)); + FAPI_INF("last goto subtest (10) is going to subtest 2 (0x%x)", iv_program.iv_subtests[2]); + + // Ok, now we can go back in to fill in the first two subtests. + + { + auto l_subtest = iv_subtest; + auto l_port = iv_const.iv_start_address.get_port(); + auto l_dimm = 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* + iv_is_sim ? + iv_const.iv_start_address.get_sim_end_address(iv_const.iv_end_address) : + iv_const.iv_start_address.get_range(iv_const.iv_end_address); + // *INDENT-ON* + + FAPI_TRY( mss::mcbist::config_address_range1(i_target, iv_const.iv_start_address, 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); + + iv_program.iv_subtests[0] = l_subtest; + FAPI_INF("adding scrub subtest 0 for port %d dimm %d (0x%02x)", l_port, l_dimm, l_subtest); + + // + // 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 < iv_program.iv_subtests.size(); ++l_index) + { + auto l_my_dimm = iv_program.iv_subtests[l_index].get_dimm(); + auto l_my_port = 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%02x)", l_goto_subtest, + iv_program.iv_subtests[l_goto_subtest].get_port(), + iv_program.iv_subtests[l_goto_subtest].get_dimm(), + iv_program.iv_subtests[l_goto_subtest] ); + + iv_program.iv_subtests[1] = mss::mcbist::goto_subtest(l_goto_subtest); + } + + // Initialize the common sections + FAPI_TRY( base_init() ); + +fapi_try_exit: + o_rc = fapi2::current_err; + return; +} + /// /// @brief Super Fast Read Init - used to init all memory behind a target with a given pattern /// @note Uses broadcast mode if possible @@ -201,8 +395,7 @@ fapi2::ReturnCode sf_init( const fapi2::Target& i_target, { FAPI_INF("superfast init start"); - // If we're running in the simulator, we want to only touch the addresses which training touched - uint8_t is_sim = 0; + uint8_t is_sim = false; FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target(), is_sim) ); if (is_sim) @@ -216,7 +409,8 @@ fapi2::ReturnCode sf_init( const fapi2::Target& i_target, else { fapi2::ReturnCode l_rc; - sf_init_operation l_init_op(i_target, i_pattern, l_rc); + constraints l_const(i_pattern); + sf_init_operation l_init_op(i_target, l_const, l_rc); FAPI_ASSERT( l_rc == FAPI2_RC_SUCCESS, fapi2::MSS_MEMDIAGS_SUPERFAST_INIT_FAILED_TO_INIT().set_TARGET(i_target), @@ -240,13 +434,14 @@ fapi_try_exit: /// template<> fapi2::ReturnCode sf_read( const fapi2::Target& i_target, - const memdiags::stop_conditions i_stop, - const memdiags::thresholds& i_thresholds ) + const stop_conditions i_stop, + const thresholds& i_thresholds ) { FAPI_INF("superfast read start"); fapi2::ReturnCode l_rc; - sf_read_operation l_read_op(i_target, i_stop, i_thresholds, l_rc); + constraints l_const(i_stop, i_thresholds); + sf_read_operation l_read_op(i_target, l_const, l_rc); FAPI_ASSERT( l_rc == FAPI2_RC_SUCCESS, fapi2::MSS_MEMDIAGS_SUPERFAST_READ_FAILED_TO_INIT().set_TARGET(i_target), @@ -269,14 +464,15 @@ fapi_try_exit: /// template<> fapi2::ReturnCode sf_read( const fapi2::Target& i_target, - const memdiags::stop_conditions i_stop, - const memdiags::thresholds& i_thresholds, - const memdiags::address& i_address ) + const stop_conditions i_stop, + const thresholds& i_thresholds, + const mss::mcbist::address& i_address ) { FAPI_INF("superfast read - end of port"); fapi2::ReturnCode l_rc; - sf_read_operation l_read_op(i_target, i_stop, i_thresholds, i_address, l_rc); + constraints l_const(i_stop, i_thresholds, i_address); + sf_read_eop_operation l_read_op(i_target, l_const, l_rc); FAPI_ASSERT( l_rc == FAPI2_RC_SUCCESS, fapi2::MSS_MEMDIAGS_SUPERFAST_READ_FAILED_TO_INIT().set_TARGET(i_target), @@ -289,71 +485,210 @@ fapi_try_exit: } /// -/// @brief Scrub - scrub all memory behind the target +/// @brief Scrub - continuous scrub all memory behind the target /// @param[in] i_target the target behind which all memory should be scrubbed /// @param[in] i_stop stop conditions /// @param[in] i_thresholds thresholds /// @param[in] i_speed the speed to scrub /// @param[in] i_address mcbist::address representing the port, dimm, rank -/// @param[in] i_end whether to end, and where (default = continuous scrub) /// @return FAPI2_RC_SUCCESS iff everything ok /// @note The function is asynchronous, and the caller should be looking for a done attention /// template<> -fapi2::ReturnCode scrub( const fapi2::Target& i_target, - const memdiags::stop_conditions i_stop, - const memdiags::thresholds& i_thresholds, - const memdiags::speed i_speed, - const memdiags::address& i_address, - const memdiags::end_boundary i_end ) +fapi2::ReturnCode background_scrub( const fapi2::Target& i_target, + const stop_conditions i_stop, + const thresholds& i_thresholds, + const speed i_speed, + const mss::mcbist::address& i_address ) { - // TK implementation (because Glancy couldn't figure that out ) - FAPI_INF("scrub"); - return FAPI2_RC_SUCCESS; + FAPI_INF("continuous (background) scrub"); + + fapi2::ReturnCode l_rc; + constraints l_const(i_stop, i_thresholds, i_speed, end_boundary::NONE, i_address); + continuous_scrub_operation l_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == FAPI2_RC_SUCCESS, + fapi2::MSS_MEMDIAGS_CONTINUOUS_SCRUB_FAILED_TO_INIT().set_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 Continue current command on next address +/// @brief Scrub - targeted scrub all memory behind the target +/// @param[in] i_target the target behind which all memory should be scrubbed +/// @param[in] i_stop stop conditions +/// @param[in] i_thresholds thresholds +/// @param[in] i_speed the speed to scrub +/// @param[in] i_address mcbist::address representing the port, dimm, rank +/// @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 +/// +template<> +fapi2::ReturnCode targeted_scrub( const fapi2::Target& i_target, + const stop_conditions i_stop, + const thresholds& i_thresholds, + const speed i_speed, + const mss::mcbist::address& i_address, + const end_boundary i_end ) +{ + FAPI_INF("targeted scrub"); + + if (i_stop == memdiags::stop_conditions::DONT_STOP) + { + FAPI_ERR("targeted scrub must have stop conditions"); + return FAPI2_RC_INVALID_PARAMETER; + } + + fapi2::ReturnCode l_rc; + constraints l_const(i_stop, i_thresholds, i_speed, i_end, i_address); + targeted_scrub_operation l_op(i_target, l_const, l_rc); + + FAPI_ASSERT( l_rc == FAPI2_RC_SUCCESS, + fapi2::MSS_MEMDIAGS_TARGETED_SCRUB_FAILED_TO_INIT().set_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 - change thresholds /// 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. /// @param[in] i_target the target +/// @param[in] i_thresholds new thresholds /// @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 - NO_CHANGE meaning leave speed untouched) /// @return FAPI2_RC_SUCCESS iff ok +/// @note overloaded as there's no 'invalid' state for thresholds. /// template<> fapi2::ReturnCode continue_cmd( const fapi2::Target& i_target, - const memdiags::end_boundary i_end, - const memdiags::stop_conditions i_stop, - const memdiags::speed i_speed ) + const thresholds& i_thresholds, + const end_boundary i_end, + const stop_conditions i_stop, + const speed i_speed ) { - // TK implementation (because Glancy couldn't figure that out ) + // Too long, make shorter + using TT = mss::mcbist::mcbistTraits; + + // We can use a local mcbist::program to help with the bit processing, and then write just the registers we touch. + mss::mcbist::program l_program; + fapi2::buffer l_status; + FAPI_INF("continue_cmd"); - return FAPI2_RC_SUCCESS; + + // 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 + + // This is OK as because the i_end is optional, for the caller to specify a + // stop condition other than DONT_CHANGE, they'd have to specify the end too + // It doean't make any sense to have a 'don't stop' in the end boundaries as + // you need to tell the stop conditions that. + if (i_stop != stop_conditions::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; + + FAPI_TRY( mss::getScom(i_target, MCBIST_MCBCFGQ, l_program.iv_config) ); + l_program.iv_config.extractToRight(l_error_mode); + + switch (i_stop) + { + case stop_conditions::STOP_AFTER_ADDRESS: + l_stopped_at_boundary = + l_program.iv_config.getBit() || + l_error_mode == stop_conditions::STOP_AFTER_ADDRESS; + break; + + case stop_conditions::STOP_AFTER_RANK: + l_stopped_at_boundary = + l_program.iv_config.getBit() || + l_error_mode == stop_conditions::STOP_AFTER_RANK; + break; + + case stop_conditions::STOP_AFTER_SUBTEST: + l_stopped_at_boundary = + l_program.iv_config.getBit() || + l_error_mode == stop_conditions::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, + fapi2::MSS_MEMDIAGS_ALREADY_AT_BOUNDARY().set_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) ); + + l_program.change_stops(i_stop); + + 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 + FAPI_TRY( mss::mcbist::load_thresholds(i_target, i_thresholds) ); + + // Setup speed + FAPI_TRY( l_program.change_speed(i_target, i_speed) ); + + // Clear the program complete FIR + FAPI_TRY( mss::putScom(i_target, MCBIST_MCBISTFIRQ_AND, + fapi2::buffer().setBit().invert()) ); + + // Tickle the resume from pause + FAPI_TRY( mss::mcbist::resume(i_target) ); + +fapi_try_exit: + return fapi2::current_err; } /// -/// @brief Continue current command on next address - change thresholds +/// @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. /// @param[in] i_target the target -/// @param[in] i_thresholds new thresholds -/// @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 - NO_CHANGE meaning leave speed untouched) +/// @param[in] i_end where to end, if stop conditions change (defaults to Master Rank) +/// @param[in] i_stop stop conditions (default - DONT_CHANGE meaning 'don't change conditions') +/// @param[in] i_speed the speed to scrub (default - SAM_SPEED meaning leave speed untouched) /// @return FAPI2_RC_SUCCESS iff ok /// template<> fapi2::ReturnCode continue_cmd( const fapi2::Target& i_target, - const memdiags::thresholds& i_thresholds, - const memdiags::end_boundary i_end, - const memdiags::stop_conditions i_stop, - const memdiags::speed i_speed ) + const end_boundary i_end, + const stop_conditions i_stop, + const speed i_speed ) { - // TK implementation (because Glancy couldn't figure that out ) - FAPI_INF("continue_cmd - change thresholds"); - return FAPI2_RC_SUCCESS; + FAPI_INF("continue_cmd - no change thresholds"); + + // Read current thresholds and pass them as if they're changed. + fapi2::buffer i_thresholds; + FAPI_TRY( mss::getScom(i_target, mss::mcbist::mcbistTraits::THRESHOLD_REG, i_thresholds) ); + + return continue_cmd( i_target, thresholds(i_thresholds), i_end, i_stop, i_speed ); + +fapi_try_exit: + return fapi2::current_err; } + + } diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.H b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.H index 92d01e3a4..961fc5f73 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/memdiags.H @@ -48,11 +48,15 @@ using mss::mcbist::constraints; using mss::mcbist::speed; using mss::mcbist::end_boundary; using mss::mcbist::stop_conditions; -using mss::mcbist::address; 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; @@ -68,36 +72,46 @@ using mss::mcbist::PATTERN_RANDOM; using mss::mcbist::LAST_PATTERN; using mss::mcbist::NO_PATTERN; +/// +/// @brief Stop the current command +/// @tparam T the fapi2::TargetType of the target +/// @param[in] i_target the target +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode stop( const fapi2::Target& i_target ); + /// /// @class Base class for memdiags operations /// @tparam T fapi2::TargetType of the MCBIST engine /// template< fapi2::TargetType T > -class base +class operation { public: /// - /// @brief memdiags::base constructor + /// @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 /// - base( const fapi2::Target& i_target, - const memdiags::constraints i_const ): + operation( const fapi2::Target& i_target, + const mss::mcbist::subtest_t i_subtest, + const constraints i_const ): iv_target(i_target), + iv_subtest(i_subtest), iv_const(i_const) { + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target(), iv_is_sim) ); + return; + + fapi_try_exit: + // Seems like a safe risk to take ... + FAPI_ERR("Unable to get the attribute ATTR_IS_SIMULATION"); + return; } - base() = delete; - - /// - /// @brief memdiags::base initializer - /// @return FAPI2_RC_SUCCESS iff everything ok - /// @note specialized for Nimbus as the port select mechanism is different - // Needed because of the scom operations done in the initialization. There - // is no good way to handle these errors in a constructor - /// - fapi2::ReturnCode init(); + operation() = delete; /// /// @brief Execute the memdiags operation @@ -109,134 +123,177 @@ class base } /// - /// @brief memdiags::base destructor + /// @brief memdiags::operation destructor /// - virtual ~base() = default; - - protected: - fapi2::Target iv_target; - constraints iv_const; - mss::mcbist::program iv_program; -}; + virtual ~operation() = default; -/// -/// @class Base class for memdiags super-fast operations -/// @tparam T fapi2::TargetType of the MCBIST engine -/// -template< fapi2::TargetType T > -class sf_operation : public base -{ - public: /// - /// @brief memdiags::sf_operation constructor - /// @param[in] i_target the target of the mcbist engine - /// @param[in] i_const mss::constraint structure - /// @param[in] i_subtest the appropriate subtest for this operation + /// @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 /// - sf_operation( const fapi2::Target& i_target, - const memdiags::constraints i_const, - const mss::mcbist::subtest_t i_subtest ): - base(i_target, i_const), - iv_subtest(i_subtest) - { - } + fapi2::ReturnCode base_init(); - sf_operation() = delete; + /// + /// @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::sf_operation initializer + /// @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 - // Needed because of the scom operations done in the initialization. There - // is no good way to handle these errors in a constructor /// - fapi2::ReturnCode init(); + fapi2::ReturnCode single_port_init(); /// - /// @brief memdiags::sf_operation destructor + /// @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. /// - virtual ~sf_operation() = default; + mss::mcbist::program& get_program() + { + return iv_program; + } protected: + fapi2::Target iv_target; mss::mcbist::subtest_t iv_subtest; + constraints iv_const; + mss::mcbist::program iv_program; + uint8_t iv_is_sim; }; /// -/// @class Base class for memdiags' super-fast init +/// @class Class for memdiags' super-fast init /// @tparam T fapi2::TargetType of the MCBIST engine /// template< fapi2::TargetType T > -struct sf_init_operation : public sf_operation +struct sf_init_operation : public operation { /// /// @brief memdiags::sf_init_operation constructor /// @param[in] i_target the target of the mcbist engine - /// @param[in] i_pattern an index representing a pattern to use to initize memory + /// @param[in] i_const mss::constraint structure /// @param[out] o_rc the fapi2::ReturnCode of the intialization process /// sf_init_operation( const fapi2::Target& i_target, - const uint64_t i_pattern, + const constraints i_const, fapi2::ReturnCode& o_rc ): - sf_operation(i_target, constraints(i_pattern), mss::mcbist::init_subtest()) + operation(i_target, mss::mcbist::init_subtest(), i_const) { - o_rc = sf_operation::init(); + // We're a multi-port operation + o_rc = this->multi_port_init(); } sf_init_operation() = delete; }; /// -/// @class Base class for memdiags' super-fast read +/// @class Class for memdiags' super-fast read /// @tparam T fapi2::TargetType of the MCBIST engine /// template< fapi2::TargetType T > -struct sf_read_operation : public sf_operation +struct sf_read_operation : public operation { /// /// @brief memdiags::sf_read_operation constructor /// @param[in] i_target the target of the mcbist engine - /// @param[in] i_stop stop conditions - /// @param[in] i_thresholds thresholds + /// @param[in] i_const mss::constraint structure /// @param[out] o_rc the fapi2::ReturnCode of the intialization process /// sf_read_operation( const fapi2::Target& i_target, - const memdiags::stop_conditions i_stop, - const memdiags::thresholds& i_thresholds, + const constraints i_const, fapi2::ReturnCode& o_rc ): - sf_operation(i_target, constraints(i_stop, i_thresholds), mss::mcbist::read_subtest()) + operation(i_target, mss::mcbist::read_subtest(), i_const) { - o_rc = sf_operation::init(); + // 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 T fapi2::TargetType of the MCBIST engine +/// +template< fapi2::TargetType T > +struct sf_read_eop_operation : public operation +{ /// - /// @brief memdiags::sf_read_operation constructor - given starting address + /// @brief memdiags::sf_read_operation constructor /// @param[in] i_target the target of the mcbist engine - /// @param[in] i_stop stop conditions - /// @param[in] i_thresholds thresholds - /// @param[in] i_address start address of read operation - /// @note the address indicates this operation is performed on one port, one dimm until the end + /// @param[in] i_const mss::constraint structure /// @param[out] o_rc the fapi2::ReturnCode of the intialization process /// - sf_read_operation( const fapi2::Target& i_target, - const memdiags::stop_conditions i_stop, - const memdiags::thresholds& i_thresholds, - const memdiags::address& i_start_address, - fapi2::ReturnCode& o_rc ): - sf_operation(i_target, constraints(i_stop, i_thresholds, i_start_address), mss::mcbist::read_subtest()) + sf_read_eop_operation( const fapi2::Target& i_target, + const constraints i_const, + fapi2::ReturnCode& o_rc ): + operation(i_target, mss::mcbist::read_subtest(), i_const) { - o_rc = sf_read_operation::end_of_port_init(); + // We're a single-port operation + o_rc = this->single_port_init(); } - sf_read_operation() = delete; + sf_read_eop_operation() = delete; +}; + +/// +/// @class Class for memdiags' continuous scrub +/// @tparam T fapi2::TargetType of the MCBIST engine +/// +template< fapi2::TargetType T > +struct continuous_scrub_operation : public operation +{ /// - /// @brief memdiags::sf_read_operation end-of-port initializer - /// @return FAPI2_RC_SUCCESS iff everything ok - // Needed because of the scom operations done in the initialization. There - // is no good way to handle these errors in a constructor + /// @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 /// - fapi2::ReturnCode end_of_port_init(); + continuous_scrub_operation( const fapi2::Target& i_target, + const constraints i_const, + fapi2::ReturnCode& o_rc ); + + continuous_scrub_operation() = delete; +}; + +/// +/// @class Class for memdiags' targeted scrub +/// @tparam T fapi2::TargetType of the MCBIST engine +/// +template< fapi2::TargetType T > +struct targeted_scrub_operation : public operation +{ + + /// + /// @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& i_target, + const constraints i_const, + fapi2::ReturnCode& o_rc ): + operation(i_target, mss::mcbist::scrub_subtest(), 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_operation() = delete; }; /// @@ -281,27 +338,45 @@ template< fapi2::TargetType T > fapi2::ReturnCode sf_read( const fapi2::Target& i_target, const stop_conditions i_stop, const thresholds& i_thresholds, - const address& i_address ); + const mss::mcbist::address& i_address ); + +/// +/// @brief Scrub - continuous scrub all memory behind the target +/// @param[in] i_target the target behind which all memory should be scrubbed +/// @param[in] i_stop stop conditions +/// @param[in] i_thresholds thresholds +/// @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< fapi2::TargetType T > +fapi2::ReturnCode background_scrub( const fapi2::Target& i_target, + const stop_conditions i_stop, + const thresholds& i_thresholds, + const speed i_speed, + const mss::mcbist::address& i_address ); /// -/// @brief Scrub - scrub all memory behind the target +/// @brief Scrub - targeted scrub all memory described by the input address (rank, slave, etc.) /// @param[in] i_target the target behind which all memory should be scrubbed /// @param[in] i_stop stop conditions /// @param[in] i_thresholds thresholds /// @param[in] i_speed the speed to scrub /// @param[in] i_address mcbist::address representing the address from which to start. -/// @param[in] i_end whether to end, and where (default = continuous scrub) +/// @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< fapi2::TargetType T > -fapi2::ReturnCode scrub( const fapi2::Target& i_target, - const stop_conditions i_stop, - const thresholds& i_thresholds, - const speed i_speed, - const address& i_address, - const end_boundary i_end = end_boundary::NEVER ); +fapi2::ReturnCode targeted_scrub( const fapi2::Target& i_target, + const stop_conditions i_stop, + const thresholds& i_thresholds, + const speed i_speed, + const mss::mcbist::address& i_address, + const end_boundary i_end ); /// /// @brief Continue current command on next address @@ -316,7 +391,7 @@ fapi2::ReturnCode scrub( const fapi2::Target& i_target, /// template< fapi2::TargetType T > fapi2::ReturnCode continue_cmd( const fapi2::Target& i_target, - const end_boundary i_end = end_boundary::DONT_STOP, + const end_boundary i_end = end_boundary::NONE, const stop_conditions i_stop = stop_conditions::DONT_CHANGE, const speed i_speed = speed::SAME_SPEED ); @@ -335,18 +410,10 @@ fapi2::ReturnCode continue_cmd( const fapi2::Target& i_target, template< fapi2::TargetType T > fapi2::ReturnCode continue_cmd( const fapi2::Target& i_target, const thresholds& i_thresholds, - const end_boundary i_end = end_boundary::DONT_STOP, + const end_boundary i_end = end_boundary::NONE, const stop_conditions i_stop = stop_conditions::DONT_CHANGE, const speed i_speed = speed::SAME_SPEED ); -/// -/// @brief Stop the current command -/// @tparam T the fapi2::TargetType of the target -/// @param[in] i_target the target -/// @return FAPI2_RC_SUCCESS iff ok -/// -template< fapi2::TargetType T > -fapi2::ReturnCode stop( const fapi2::Target& i_target ); } // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/settings.H b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/settings.H index d9e564c62..4ab89090c 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/settings.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/settings.H @@ -47,6 +47,7 @@ namespace mcbist enum stop_conditions { NO_STOP_ON_ERROR = 0b00, + DONT_STOP = NO_STOP_ON_ERROR, STOP_AFTER_ADDRESS = 0b01, STOP_AFTER_RANK = 0b10, STOP_AFTER_SUBTEST = 0b11, @@ -61,7 +62,6 @@ enum end_boundary NONE = 0, CONTINUOUS = NONE, NEVER = NONE, - DONT_STOP = NONE, MASTER_RANK = 1, MRANK = MASTER_RANK, SLAVE_RANK = 2, @@ -98,6 +98,15 @@ class thresholds iv_value(0) { } + + /// + /// @brief Thresholds class ctor + /// @param[in] uint64_t representing the threshold register contents + /// + thresholds(const uint64_t i_value): + iv_value(i_value) + { } + /// /// @brief Thresholds class dtor /// @@ -414,6 +423,7 @@ struct constraints iv_thresholds(), iv_pattern(NO_PATTERN), iv_end_boundary(NONE), + iv_speed(LUDICROUS), iv_start_address(0), iv_end_address(0) { @@ -460,10 +470,29 @@ struct constraints i_stop, uint64_t(i_thresholds), uint64_t(i_start_address)); } + /// + /// @brief constraints constructor + /// @param[in] i_stop stop conditions + /// @param[in] i_thresholds thresholds + /// @param[in] i_start_address address to start from + /// + constraints( const stop_conditions i_stop, + const thresholds& i_thresholds, + const speed i_speed, + const end_boundary i_end_boundary, + const address& i_start_address ): + constraints(i_stop, i_thresholds, i_start_address) + { + iv_end_boundary = i_end_boundary; + iv_speed = i_speed; + FAPI_INF("setting up constraints with end boundary %d and speed 0x%x", i_end_boundary, i_speed); + } + stop_conditions iv_stop; thresholds iv_thresholds; uint64_t iv_pattern; end_boundary iv_end_boundary; + speed iv_speed; mcbist::address iv_start_address; mcbist::address iv_end_address; }; diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/sim.C b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/sim.C index 029072fe9..8e6aca12b 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/sim.C +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/sim.C @@ -88,9 +88,8 @@ fapi2::ReturnCode sf_init( const fapi2::Target& i_target, // Setup l_start to represent this rank, and then make the end address from that. l_start.set_master_rank(*r); - l_start.get_range(l_end); // Set C3 bit to get an entire cache line - l_end.set_field(0b1000000); + 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 diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H index 5a9a53037..5c32022a5 100644 --- a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H +++ b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H @@ -49,6 +49,9 @@ enum sizes MAX_SLEW_VALUE = 15, ///< 4 bit value MAX_SUPPORTED_FREQUENCIES = 1, ///< Number of supported frequencies. Right now it's only 2400 + BYTES_PER_GB = 1000000000, ///< Multiplier to go from GB to B + T_PER_MT = 1000000, ///< Multiplier to go from MT/s to T/s + // All need to be attributes - BRS // 48:51, 0b1100, (def_is_sim); # BIG_STEP = 12 (changed from default for SIM) // 48:51, 0b0000, any; # BIG_STEP = 0 SWyatt @@ -87,6 +90,11 @@ enum times // From the DDR4spec 2400 speed - need to be changed to read attributes. BRS tWLO = 10, tWLOE = 2, + + SEC_IN_HOUR = 60 * 60, ///< seconds in an hour, used for scrub times + BG_SCRUB_IN_HOURS = 12, + + CMD_TIMEBASE = 8192, ///< Represents the timebase multiplier for the MCBIST inter cmd gap }; enum states diff --git a/src/import/chips/p9/procedures/hwp/memory/p9_mss_scrub.C b/src/import/chips/p9/procedures/hwp/memory/p9_mss_scrub.C index 18d3e74dc..20fed5c89 100644 --- a/src/import/chips/p9/procedures/hwp/memory/p9_mss_scrub.C +++ b/src/import/chips/p9/procedures/hwp/memory/p9_mss_scrub.C @@ -65,8 +65,8 @@ fapi2::ReturnCode p9_mss_scrub( const fapi2::Target& i_targe } // TK do we want these to be arguments to the wrapper? - return memdiags::scrub(i_target, mss::mcbist::stop_conditions::NO_STOP_ON_ERROR, mss::mcbist::thresholds(), - mss::mcbist::speed::LUDICROUS, mss::mcbist::address()); + return memdiags::background_scrub(i_target, mss::mcbist::stop_conditions::NO_STOP_ON_ERROR, mss::mcbist::thresholds(), + mss::mcbist::speed::LUDICROUS, mss::mcbist::address()); fapi_try_exit: return fapi2::current_err; diff --git a/src/import/chips/p9/procedures/hwp/memory/tests/mss_memdiags_ut.C b/src/import/chips/p9/procedures/hwp/memory/tests/mss_memdiags_ut.C index a5a167a78..a16e3230c 100644 --- a/src/import/chips/p9/procedures/hwp/memory/tests/mss_memdiags_ut.C +++ b/src/import/chips/p9/procedures/hwp/memory/tests/mss_memdiags_ut.C @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ using fapi2::FAPI2_RC_SUCCESS; using fapi2::TARGET_TYPE_MCBIST; using fapi2::TARGET_TYPE_MCA; using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; namespace mss { @@ -64,6 +66,7 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" // Loops over MCBIST targets that were defined in the associated config for_each_target([l_fir_mask](const fapi2::Target& i_target) { + SECTION("Test thresholds structure") { mss::mcbist::thresholds l_t; @@ -194,8 +197,8 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" // of polling on an AWAN, but not so much that we run the risk of timing out mss::mcbist::address().get_range(l_const.iv_end_address); l_const.iv_end_address.set_column(0b111111); - memdiags::sf_operation l_bob(i_target, l_const, mss::mcbist::init_subtest()); - REQUIRE_FALSE( l_bob.init() ); + memdiags::operation l_bob(i_target, mss::mcbist::init_subtest(), l_const); + REQUIRE_FALSE( l_bob.multi_port_init() ); REQUIRE_FALSE( l_bob.execute() ); // Check the things we default to so that we have a canary in case the defaults change @@ -239,6 +242,7 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" // Poll for the fir bit. We expect this to be set ... fapi2::buffer l_status; + fapi2::buffer l_last_address; // A small vector of addresses to poll during the polling loop static const std::vector> l_probes = @@ -256,6 +260,10 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" }, l_probes); + // Pass or fail output the current address. This is useful for debugging when we can get it. + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMCATQ, l_last_address) ); + FAPI_INF("MCBIST last address: 0x%016lx", l_last_address); + REQUIRE( l_poll_results == true ); // Check for errors @@ -282,8 +290,8 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" // of polling on an AWAN, but not so much that we run the risk of timing out mss::mcbist::address().get_range(l_const.iv_end_address); l_const.iv_end_address.set_column(0b111111); - memdiags::sf_operation l_bob(i_target, l_const, mss::mcbist::read_subtest()); - REQUIRE_FALSE( l_bob.init() ); + memdiags::operation l_bob(i_target, mss::mcbist::read_subtest(), l_const); + REQUIRE_FALSE( l_bob.multi_port_init() ); REQUIRE_FALSE( l_bob.execute() ); // Check the things we default to so that we have a canary in case the defaults change @@ -327,6 +335,7 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" // Poll for the fir bit. We expect this to be set ... fapi2::buffer l_status; + fapi2::buffer l_last_address; // A small vector of addresses to poll during the polling loop static const std::vector> l_probes = @@ -344,6 +353,10 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" }, l_probes); + // Pass or fail output the current address. This is useful for debugging when we can get it. + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMCATQ, l_last_address) ); + FAPI_INF("MCBIST last address: 0x%016lx", l_last_address); + REQUIRE( l_poll_results == true ); // Check for errors @@ -386,7 +399,7 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" { fapi2::buffer l_read; REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBCFGQ, l_read) ); - REQUIRE( 0x00000000000000a0 == l_read ); + REQUIRE( 0x00000000000000a8 == l_read ); } // Load thresholds - default state (expecting 0's) @@ -408,13 +421,14 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" fapi2::buffer l_read; REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBSA0Q, l_read) ); - REQUIRE(l_read == 0x1FFFFFC000000000); + REQUIRE(l_read == 0x1fffffc000000000); REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBEA0Q, l_read) ); - REQUIRE(l_read == 0x1FFFFFFFFC000000); + REQUIRE(l_read == 0x1fffffe07c000000); } // Poll for the fir bit. We expect this to be set ... fapi2::buffer l_status; + fapi2::buffer l_last_address; // A small vector of addresses to poll during the polling loop static const std::vector> l_probes = @@ -432,6 +446,10 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" }, l_probes); + // Pass or fail output the current address. This is useful for debugging when we can get it. + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMCATQ, l_last_address) ); + FAPI_INF("MCBIST last address: 0x%016lx", l_last_address); + REQUIRE( l_poll_results == true ); // Check for errors @@ -447,6 +465,238 @@ TEST_CASE_METHOD(mss::test::mcbist_target_test_fixture, "memdiags", "[memdiags]" } + SECTION("Test continuous scrub") + { + // How many DIMM do we have? This effects the subtests we create + const auto l_dimm_count = mss::find_targets(i_target).size(); + FAPI_INF("seeing %d DIMM", l_dimm_count); + + // The addresses here are calculated so that we get a few iterations + // of polling on an AWAN, but not so much that we run the risk of timing out + mss::mcbist::address l_start; + mss::mcbist::address().get_range(l_start); + l_start.set_bank(0); + l_start.set_bank_group(0); + l_start.set_column(0); + + REQUIRE_FALSE( memdiags::background_scrub(i_target, memdiags::stop_conditions::NO_STOP_ON_ERROR, memdiags::thresholds(), + memdiags::speed::BG_SCRUB, l_start) ); + + // check the state of the mcbist engine + + // Check the iv_parameters + { + fapi2::buffer l_read; + uint64_t l_size; + REQUIRE_FALSE( mss::eff_memory_size(i_target, l_size) ); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBPARMQ, l_read) ); + + FAPI_INF("seeing memory size %d", l_size); + + switch (l_size) + { + case 128: + REQUIRE(l_read == 0x68000000000000); + break; + + case 64: + REQUIRE(l_read == 0xC8000000000000); + break; + + default: + FAIL("Memory size not supported"); + break; + }; + } + + // Check the address registers + { + // Address config 0 should have the start and end for a complete DIMM (in sim) + fapi2::buffer l_read; + + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBSA0Q, l_read) ); + REQUIRE(l_read == 0x0); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBEA0Q, l_read) ); + REQUIRE(l_read == 0x000000207C000000); + } + { + // Address 1 should have the start we configured and an end which is the + // real end of the DIMM address range + fapi2::buffer l_read; + + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBSA1Q, l_read) ); + REQUIRE(l_read == 0x1fffffc000000000); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBEA1Q, l_read) ); + REQUIRE(l_read == 0x1fffffe07c000000); + } + + // Check the subtests + { + if (l_dimm_count == 8) + { + fapi2::buffer l_read; + + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMR0Q, l_read) ); + REQUIRE(l_read == 0x9009718090089208); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMR1Q, l_read) ); + REQUIRE(l_read == 0x9408960898089a08); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMR2Q, l_read) ); + REQUIRE(l_read == 0x9c089e0871040000); + } + } + + // Make sure continuous scrub doesn't set the FIR bit. More for verifying our actions than + // anything else + { + fapi2::buffer l_read; + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBISTFIRQ, l_read) ); + REQUIRE( l_read.getBit() == false ); + } + + // We should have the LEN64 bit turned off + { + fapi2::buffer l_read; + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBCFGQ, l_read) ); + REQUIRE( 0x0000000000000000 == l_read ); + } + + + } + + SECTION("Test targeted scrub") + { + // Test that passing in no-stop is a bug + REQUIRE( memdiags::targeted_scrub(i_target, memdiags::stop_conditions::DONT_STOP, memdiags::thresholds(), + memdiags::speed::LUDICROUS, mss::mcbist::address(), + memdiags::end_boundary::MASTER_RANK) ); + + REQUIRE_FALSE( memdiags::targeted_scrub(i_target, memdiags::stop_conditions::STOP_AFTER_RANK, memdiags::thresholds(), + memdiags::speed::LUDICROUS, mss::mcbist::address(), + memdiags::end_boundary::MASTER_RANK) ); + + // Make sure targeted scrub sets the FIR bit. More for verifying our actions than + // anything else + { + fapi2::buffer l_read; + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBISTFIRQ, l_read) ); + REQUIRE( l_read.getBit() == true ); + } + + // Check the address registers + { + // Address config 0 should have the start and end for a complete DIMM (in sim) + fapi2::buffer l_read; + + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBSA0Q, l_read) ); + REQUIRE(l_read == 0x0); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBEA0Q, l_read) ); + REQUIRE(l_read == 0x000000207C000000); + } + + // Check the subtests + { + fapi2::buffer l_read; + + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMR0Q, l_read) ); + REQUIRE(l_read == 0x900c000000000000); + } + + // We should have the LEN64 bit turned off but we turned on pause-on-rank boundary + { + fapi2::buffer l_read; + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBCFGQ, l_read) ); + REQUIRE( 0x0000000020000020 == l_read ); + } + + } + + SECTION("Test what happens on stop-after-rank") + { + mss::mcbist::address l_start_address; + mss::mcbist::address l_end_address; + fapi2::ReturnCode l_rc; + + // Make our start be really close to the end of the rank an our end be the end so we + // can check to see what happens at the end of the rank + l_start_address.get_range(l_end_address); + l_start_address = l_end_address - 10; + + memdiags::constraints l_const(memdiags::stop_conditions::STOP_AFTER_RANK, memdiags::thresholds(), + memdiags::speed::LUDICROUS, memdiags::end_boundary::MASTER_RANK, + l_start_address); + + l_const.iv_end_address = l_end_address; + + memdiags::targeted_scrub_operation l_op(i_target, l_const, l_rc); + + REQUIRE_FALSE( l_rc ); + + // Add a goto on to the end of this program so that if it stops we know it stopped because + // of the rank boundary. + l_op.get_program().iv_subtests.push_back(mss::mcbist::goto_subtest(0)); + + REQUIRE_FALSE( l_op.execute() ); + + // A small vector of addresses to poll during the polling loop + static const std::vector> l_probes = + { + {i_target, "mcbist current address", MCBIST_MCBMCATQ}, + }; + + poll_parameters l_poll_parameters; + fapi2::buffer l_status; + fapi2::buffer l_last_address; + + bool l_poll_results = mss::poll(i_target, MCBIST_MCBISTFIRQ, l_poll_parameters, + [&l_status](const size_t poll_remaining, const fapi2::buffer& stat_reg) -> bool + { + FAPI_DBG("mcbist firq 0x%llx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + return l_status.getBit() == true; + }, + l_probes); + + // Pass or fail output the current address. This is useful for debugging when we can get it. + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMCATQ, l_last_address) ); + FAPI_INF("MCBIST last address: 0x%016lx", l_last_address); + + REQUIRE( l_poll_results == true ); + + // Check for errors + { + fapi2::buffer l_read; + + REQUIRE( 0x20000000000000 == (l_status & l_fir_mask) ); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBSTATQ, l_read) ); + REQUIRE(l_read == 0); + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MBSEC1Q, l_read) ); + REQUIRE(l_read == 0); + } + + // We asked to stop at the end of a rank, so we should not be able to continue to the end of the rank + REQUIRE( memdiags::continue_cmd(i_target, memdiags::end_boundary::MRANK, memdiags::stop_conditions::STOP_AFTER_RANK) ); + + // Continue but ask to stop at the end of the subtest + REQUIRE_FALSE( memdiags::continue_cmd(i_target, memdiags::end_boundary::NONE, + memdiags::stop_conditions::STOP_AFTER_SUBTEST) ); + + l_poll_results = mss::poll(i_target, MCBIST_MCBISTFIRQ, l_poll_parameters, + [&l_status](const size_t poll_remaining, const fapi2::buffer& stat_reg) -> bool + { + FAPI_DBG("mcbist firq 0x%llx, remaining: %d", stat_reg, poll_remaining); + l_status = stat_reg; + return l_status.getBit() == true; + }, + l_probes); + + // Pass or fail output the current address. This is useful for debugging when we can get it. + REQUIRE_FALSE( mss::getScom(i_target, MCBIST_MCBMCATQ, l_last_address) ); + FAPI_INF("MCBIST last address: 0x%016lx", l_last_address); + + REQUIRE( l_poll_results == true ); + } + + return 0; }); } -- cgit v1.2.1