/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/generic/memory/lib/utils/mcbist/gen_mss_mcbist.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2019,2020 */ /* [+] 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.H /// @brief Run and manage the MCBIST engine /// // *HWP HWP Owner: Andre Marin // *HWP HWP Backup: Stephen Glancy // *HWP Team: Memory // *HWP Level: 3 // *HWP Consumed by: HB:FSP #ifndef _GEN_MSS_MCBIST_H_ #define _GEN_MSS_MCBIST_H_ #include #include #include #include #include #include #include #include #include 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& 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& 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 /// @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 > inline fapi2::ReturnCode read_mcbfirmask( const fapi2::Target& i_target, fapi2::buffer& 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 /// @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 > inline fapi2::ReturnCode write_mcbfirmask( const fapi2::Target& i_target, const fapi2::buffer& 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 /// @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 > inline fapi2::ReturnCode read_mcbfirq( const fapi2::Target& i_target, fapi2::buffer& 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 /// @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 > inline fapi2::ReturnCode write_mcbfirq( const fapi2::Target& i_target, const fapi2::buffer& 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 /// @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 > inline void set_mcbist_program_complete_mask( fapi2::buffer& io_data, const mss::states i_state ) { io_data.writeBit(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 /// @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 > inline void set_mcbist_wat_debug_attn_mask( fapi2::buffer& io_data, const mss::states i_state ) { io_data.writeBit(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 /// @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 > inline void clear_mcbist_program_complete( fapi2::buffer& io_data ) { io_data.writeBit(mss::states::OFF); io_data.writeBit(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 > inline void get_mcbist_program_complete_mask( const fapi2::buffer i_data, mss::states& o_state ) { o_state = i_data.getBit() ? 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 > inline void get_mcbist_wat_debug_attn_mask( const fapi2::buffer i_data, mss::states& o_state ) { o_state = i_data.getBit() ? 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_TARGET_TYPE , typename TT = mcbistTraits > 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(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(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(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(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(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(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(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() ? 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(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(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(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(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(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(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(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(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 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_TARGET_TYPE , typename TT = mcbistTraits > inline subtest_t write_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0000 - we want subtest type to be a Write (W) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t alter_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 1011 - we want subtest type to be a Alter l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t display_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 1100 - we want subtest type to be a Display l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, 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 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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t steer_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 1010 - we want subtest type to be a Steer l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t read_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0001 - we want subtest type to be a Read (R) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t read_write_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0010 - we want subtest type to be a Read Write (RW) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t write_read_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0011 - we want subtest type to be a Write Read (WR) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t read_write_read_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0100 - we want subtest type to be a Read Write Read (RWR) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t read_read_write_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 1000 - we want subtest type to be a Read Read Write (RRW) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t read_write_write_subtest() { // Starts life full of 0's subtest_t l_subtest; // 0:3 = 0101 - we want subtest type to be a Read Write Write (RWW) l_subtest.iv_mcbmr.template insertFromRight(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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t random_subtest() { // Starts life full of 0's subtest_t 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(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_TARGET_TYPE, 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 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_TARGET_TYPE, typename TT = mcbistTraits > inline subtest_t init_subtest() { return write_subtest(); } /// /// @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_TARGET_TYPE , typename TT = mcbistTraits > 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 struct broadcast_helper { /// /// @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& io_config ) { io_config.writeBit(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& io_config ) { io_config.insertFromRight(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& io_addr_gen ) { io_addr_gen.writeBit(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 struct broadcast_helper { /// /// @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& 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& 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& 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(0b0000); // By default if there's an error, we stop after the errored address iv_config.insertFromRight(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(); } } /// /// @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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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::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::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& 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(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(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(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(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(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(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(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(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(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(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(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(); iv_config.clearBit(); iv_config.clearBit(); iv_addr_gen.clearBit(); switch (i_end) { case end_boundary::STOP_AFTER_ADDRESS: iv_config.setBit(); break; case end_boundary::STOP_AFTER_SLAVE_RANK: iv_config.setBit(); iv_addr_gen.setBit(); break; case end_boundary::STOP_AFTER_MASTER_RANK: iv_config.setBit(); iv_addr_gen.clearBit(); break; case end_boundary::STOP_AFTER_SUBTEST: iv_config.setBit(); 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& 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& 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(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> get_port_list( const fapi2::Target& 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(i_end); const uint64_t l_detect_slave = fapi2::buffer(i_end).getBit(); iv_addr_gen.writeBit( 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(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(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(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(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(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(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(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(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(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(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(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 l_value; iv_addr_gen.extract(l_value); // Bit 0 enables counter mode for start/end address field 0 l_value.writeBit<0>(i_mode); iv_addr_gen.insert(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 l_value; iv_addr_gen.extract(l_value); // Bit 1 enables counter mode for start/end address field 1 l_value.writeBit<1>(i_mode); iv_addr_gen.insert(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 l_value; iv_addr_gen.extract(l_value); // Bit 2 enables counter mode for start/end address field 2 l_value.writeBit<2>(i_mode); iv_addr_gen.insert(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 l_value; iv_addr_gen.extract(l_value); // Bit 3 enables counter mode for start/end address field 3 l_value.writeBit<3>(i_mode); iv_addr_gen.insert(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(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::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(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(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 i_target ) const { // MCBIST error traits using ET = mcbistMCTraits; // Until reading the error array is documented, comparison errors 'just' result in // a flag indicating there was a problem on port. { fapi2::buffer 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(l_port); } l_data.extractToRight(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 l_read0; fapi2::buffer 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& 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 >iv_subtests; // Place to hold the value of the MCBIST Memory Parameter Register. We'll scom // it when we execute the program. fapi2::buffer iv_parameters; // Place to hold the value of the MCBIST Address Generation Config. We'll scom // it when we execute the program. fapi2::buffer 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 iv_addr_map0; fapi2::buffer iv_addr_map1; fapi2::buffer iv_addr_map2; fapi2::buffer iv_addr_map3; // Data Rotate Seed and Config Registers fapi2::buffer iv_data_rotate_cnfg; fapi2::buffer iv_data_rotate_seed; // Config register fapi2::buffer iv_config; // Control register fapi2::buffer 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 iv_compare_mask; // The error stop conditions, thresholds for the program stop_conditions 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 > inline fapi2::ReturnCode load_config( const fapi2::Target& i_target, const mcbist::program& 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 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(); l_config.template clearBit(); } #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 > inline fapi2::ReturnCode load_control( const fapi2::Target& i_target, const mcbist::program& 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 > inline fapi2::ReturnCode load_addr_gen( const fapi2::Target& i_target, const mcbist::program& 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 > inline fapi2::ReturnCode config_address_range( const fapi2::Target& 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 > inline fapi2::ReturnCode config_address_range0( const fapi2::Target& i_target, const uint64_t i_start, const uint64_t i_end ) { return config_address_range(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 > inline fapi2::ReturnCode config_address_range1( const fapi2::Target& i_target, const uint64_t i_start, const uint64_t i_end ) { return config_address_range(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 > inline fapi2::ReturnCode config_address_range2( const fapi2::Target& i_target, const uint64_t i_start, const uint64_t i_end ) { return config_address_range(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 > inline fapi2::ReturnCode config_address_range3( const fapi2::Target& i_target, const uint64_t i_start, const uint64_t i_end ) { return config_address_range(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 > inline fapi2::ReturnCode start_stop( const fapi2::Target& 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 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() : l_buf.setBit()) ); 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 > inline fapi2::ReturnCode resume( const fapi2::Target& i_target ) { fapi2::buffer 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()) ); 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 > inline fapi2::ReturnCode reset_errors( const fapi2::Target& i_target ) { fapi2::buffer 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()) ); 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 > inline fapi2::ReturnCode in_progress( const fapi2::Target& i_target, bool& o_in_progress ) { fapi2::buffer l_buf; FAPI_TRY(fapi2::getScom(i_target, TT::STATQ_REG, l_buf)); o_in_progress = l_buf.getBit(); 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 > fapi2::ReturnCode load_mcbmr( const fapi2::Target& i_target, const mcbist::program& 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 > fapi2::ReturnCode load_mcbamr( const fapi2::Target& i_target, const mcbist::program& 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 > inline fapi2::ReturnCode load_mcbparm( const fapi2::Target& i_target, const mcbist::program& 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 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 > inline fapi2::ReturnCode clear_errors( const fapi2::Target 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 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 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 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(generate_eccspare_pattern_helper(i_pattern[0].first, i_invert)); l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[0].second, i_invert)); // Beat 2/3 l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[1].first, i_invert)); l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[1].second, i_invert)); // Beat 4/5 l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[2].first, i_invert)); l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[2].second, i_invert)); // Beat 6/7 l_pattern.insertFromRight(generate_eccspare_pattern_helper(i_pattern[3].first, i_invert)); l_pattern.insertFromRight(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 > fapi2::ReturnCode load_eccspare_pattern( const fapi2::Target& 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 > inline fapi2::ReturnCode load_pattern( const fapi2::Target& 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 l_value_first = i_invert ? ~l_cache_line.first : l_cache_line.first; const fapi2::buffer 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( 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 > inline fapi2::ReturnCode load_pattern( const fapi2::Target& 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 > 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 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 > fapi2::ReturnCode load_maint_pattern( const fapi2::Target& i_target, const pattern& i_pattern, const bool i_invert ) { // The scom registers are in the port target. PT: port traits using PT = mcbistTraits; // Init the fapi2 return code fapi2::current_err = fapi2::FAPI2_RC_SUCCESS; // Array access control fapi2::buffer l_aacr; // Array access data fapi2::buffer 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(mss::states::OFF) .template insertFromRight(PT::MAINT_DATA_INDEX_START) .template writeBit(mss::states::ON) .template writeBit(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(i_target); // Init the port map for (const auto& p : l_ports) { l_aacr.template insertFromRight(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 l_value_first = i_invert ? ~l_cache_line.first : l_cache_line.first; fapi2::buffer 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::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 > inline fapi2::ReturnCode load_maint_pattern( const fapi2::Target& 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 > inline fapi2::ReturnCode load_maint_pattern( const fapi2::Target& i_target, const mcbist::program& 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 > fapi2::ReturnCode load_random24b_seeds( const fapi2::Target& 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 l_mcbrsd0q; fapi2::buffer 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 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(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(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(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 > inline fapi2::ReturnCode load_random24b_seeds( const fapi2::Target& 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 > inline fapi2::ReturnCode load_random24b_seeds( const fapi2::Target& i_target, const mcbist::program& 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 > inline fapi2::ReturnCode load_fifo_mode( const fapi2::Target& i_target, const mcbist::program& 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& 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 > inline fapi2::ReturnCode load_data_config( const fapi2::Target& i_target, const mcbist::program& 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 > fapi2::ReturnCode load_data_compare_mask( const fapi2::Target& i_target, const mcbist::program& 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(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 > inline fapi2::ReturnCode load_thresholds( const fapi2::Target& 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 > inline fapi2::ReturnCode load_thresholds( const fapi2::Target& i_target, const mcbist::program& 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 > fapi2::ReturnCode read_rmw_array(const fapi2::Target& 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 >& o_data, std::vector< fapi2::buffer >& o_ecc_data) { fapi2::buffer l_control_value; fapi2::buffer 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::SELECT_RMW_BUFFER) // set start address .template insertFromRight(l_array_addr) // enable the auto increment bit .template setBit() // set ecc mode bit off .template clearBit(); 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(); 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 > inline fapi2::ReturnCode read_rmw_array(const fapi2::Target& 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 >& o_data) { std::vector< fapi2::buffer > 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& i_target, mcbist::program& 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 > fapi2::ReturnCode read_rb_array(const fapi2::Target& i_target, const uint64_t i_start_addr, const uint64_t i_num_entries, std::vector< fapi2::buffer >& o_data, std::vector< fapi2::buffer >& 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 > fapi2::ReturnCode read_rb_array(const fapi2::Target& i_target, const uint64_t i_start_addr, const uint64_t i_num_entries, std::vector< fapi2::buffer >& o_data) { std::vector< fapi2::buffer > l_temp; return read_rb_array(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 > fapi2::ReturnCode poll( const fapi2::Target& i_target, const program& i_program ) { using ET = mss::mcbistMCTraits; fapi2::buffer l_status; const uint64_t l_done = fapi2::buffer().setBit(); const uint64_t l_fail = fapi2::buffer().setBit(); const uint64_t l_in_progress = fapi2::buffer().setBit(); // A small vector of addresses to poll during the polling loop const std::vector> 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& stat_reg) -> bool { FAPI_DBG("mcbist statq 0x%016lx, remaining: %d", stat_reg, poll_remaining); l_status = stat_reg; return l_status.getBit() != 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 > fapi2::ReturnCode clear_error_helper( const fapi2::Target& i_target, const program& 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, typename ET = mcbistMCTraits > fapi2::ReturnCode execute( const fapi2::Target& i_target, const program& i_program ) { fapi2::buffer 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(i_target, const_cast&>(i_program)) ); // Configures the write/read FIFO bit FAPI_TRY( load_fifo_mode( 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 configured address maps down FAPI_TRY( load_mcbamr( i_target, i_program) ); // Slam the config register down FAPI_TRY( load_config( i_target, i_program) ); // 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_data_config( 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)); // 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& 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() == true) || (l_status.getBit() == 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& i_target ) { return ( mss::mcbist::reset_errors(i_target) ); } } // namespace ecc } // namespace mss #endif