/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.H $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2019 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ /// /// @file ccs.H /// @brief Run and manage the CCS engine /// // *HWP HWP Owner: Stephen Glancy // *HWP HWP Backup: Andre Marin // *HWP Team: Memory // *HWP Level: 3 // *HWP Consumed by: HB:FSP #ifndef _MSS_CCS_H_ #define _MSS_CCS_H_ #include #include #include #include #include #include #include #include // I have a dream that the CCS engine code can be shared among controllers. So, I drive the // engine from a set of traits. This might be folly. Allow me to dream. BRS /// /// @class ccsTraits /// @brief Nimbus CCS Engine traits /// template< fapi2::TargetType T > class ccsTraits; template<> class ccsTraits { public: }; template<> class ccsTraits { public: static constexpr uint64_t MODEQ_REG = MCBIST_CCS_MODEQ; static constexpr uint64_t MCB_CNTL_REG = MCBIST_MCB_CNTLQ; static constexpr uint64_t CNTLQ_REG = MCBIST_CCS_CNTLQ; static constexpr uint64_t STATQ_REG = MCBIST_CCS_STATQ; static constexpr fapi2::TargetType PORT_TARGET_TYPE = fapi2::TARGET_TYPE_MCA; enum { // Non address values that are needed for helper functions // ODT values used for beautification // Attribute locations ATTR_ODT_DIMM0_R0 = 0, ATTR_ODT_DIMM0_R1 = 1, ATTR_ODT_DIMM1_R0 = 4, ATTR_ODT_DIMM1_R1 = 5, // Right justified output - makes it so we can use insertFromRight CCS_ODT_DIMM0_R0 = 4, CCS_ODT_DIMM0_R1 = 5, CCS_ODT_DIMM1_R0 = 6, CCS_ODT_DIMM1_R1 = 7, // Default ODT cycle length is 5 - one for the preamble and 4 for the data DEFAULT_ODT_CYCLE_LEN = 5, // CCS MODEQ STOP_ON_ERR = MCBIST_CCS_MODEQ_STOP_ON_ERR, UE_DISABLE = MCBIST_CCS_MODEQ_UE_DISABLE, DATA_COMPARE_BURST_SEL = MCBIST_CCS_MODEQ_DATA_COMPARE_BURST_SEL, DATA_COMPARE_BURST_SEL_LEN = MCBIST_CCS_MODEQ_DATA_COMPARE_BURST_SEL_LEN, DDR_CAL_TIMEOUT_CNT = MCBIST_CCS_MODEQ_DDR_CAL_TIMEOUT_CNT, DDR_CAL_TIMEOUT_CNT_LEN = MCBIST_CCS_MODEQ_DDR_CAL_TIMEOUT_CNT_LEN, CFG_PARITY_AFTER_CMD = MCBIST_CCS_MODEQ_CFG_PARITY_AFTER_CMD, COPY_CKE_TO_SPARE_CKE = MCBIST_CCS_MODEQ_COPY_CKE_TO_SPARE_CKE, DISABLE_ECC_ARRAY_CHK = MCBIST_CCS_MODEQ_DISABLE_ECC_ARRAY_CHK, DISABLE_ECC_ARRAY_CORRECTION = MCBIST_CCS_MODEQ_DISABLE_ECC_ARRAY_CORRECTION, CFG_DGEN_FIXED_MODE = MCBIST_CCS_MODEQ_CFG_DGEN_FIXED_MODE, DDR_CAL_TIMEOUT_CNT_MULT = MCBIST_CCS_MODEQ_DDR_CAL_TIMEOUT_CNT_MULT, DDR_CAL_TIMEOUT_CNT_MULT_LEN = MCBIST_CCS_MODEQ_DDR_CAL_TIMEOUT_CNT_MULT_LEN, IDLE_PAT_ADDRESS_0_13 = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_0_13, IDLE_PAT_ADDRESS_0_13_LEN = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_0_13_LEN, IDLE_PAT_ADDRESS_17 = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_17, IDLE_PAT_BANK_GROUP_1 = MCBIST_CCS_MODEQ_IDLE_PAT_BANK_GROUP_1, IDLE_PAT_BANK_0_1 = MCBIST_CCS_MODEQ_IDLE_PAT_BANK_0_1, IDLE_PAT_BANK_0_1_LEN = MCBIST_CCS_MODEQ_IDLE_PAT_BANK_0_1_LEN, IDLE_PAT_BANK_GROUP_0 = MCBIST_CCS_MODEQ_IDLE_PAT_BANK_GROUP_0, IDLE_PAT_ACTN = MCBIST_CCS_MODEQ_IDLE_PAT_ACTN, IDLE_PAT_ADDRESS_16 = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_16, IDLE_PAT_ADDRESS_15 = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_15, IDLE_PAT_ADDRESS_14 = MCBIST_CCS_MODEQ_IDLE_PAT_ADDRESS_14, NTTM_MODE = MCBIST_CCS_MODEQ_NTTM_MODE, NTTM_RW_DATA_DLY = MCBIST_CCS_MODEQ_NTTM_RW_DATA_DLY, NTTM_RW_DATA_DLY_LEN = MCBIST_CCS_MODEQ_NTTM_RW_DATA_DLY_LEN, IDLE_PAT_BANK_2 = MCBIST_CCS_MODEQ_IDLE_PAT_BANK_2, DDR_PARITY_ENABLE = MCBIST_CCS_MODEQ_DDR_PARITY_ENABLE, IDLE_PAT_PARITY = MCBIST_CCS_MODEQ_IDLE_PAT_PARITY, // MCB_CNTRL MCB_CNTL_PORT_SEL = MCBIST_MCB_CNTLQ_MCBCNTL_PORT_SEL, MCB_CNTL_PORT_SEL_LEN = MCBIST_MCB_CNTLQ_MCBCNTL_PORT_SEL_LEN, // CCS CNTL CCS_START = MCBIST_CCS_CNTLQ_START, CCS_STOP = MCBIST_CCS_CNTLQ_STOP, // CCS STATQ CCS_IN_PROGRESS = MCBIST_CCS_STATQ_IP, // ARR0 ARR0_DDR_ADDRESS_0_13 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13, ARR0_DDR_ADDRESS_0_13_LEN = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13_LEN, ARR0_DDR_ADDRESS_0_9 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13, // Useful for rd/wr cmds ARR0_DDR_ADDRESS_0_9_LEN = 10, // CA bits are 9:0, total length of 10 ARR0_DDR_ADDRESS_10 = 10, // ADR10 is the 10th bit from the left in Nimbus ARR0 ARR0_DDR_ADDRESS_17 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_17, ARR0_DDR_BANK_GROUP_1 = MCBIST_CCS_INST_ARR0_00_DDR_BANK_GROUP_1, ARR0_DDR_RESETN = MCBIST_CCS_INST_ARR0_00_DDR_RESETN, ARR0_DDR_BANK_0_1 = MCBIST_CCS_INST_ARR0_00_DDR_BANK_0_1, ARR0_DDR_BANK_0_1_LEN = MCBIST_CCS_INST_ARR0_00_DDR_BANK_0_1_LEN, ARR0_DDR_BANK_GROUP_0 = MCBIST_CCS_INST_ARR0_00_DDR_BANK_GROUP_0, ARR0_DDR_ACTN = MCBIST_CCS_INST_ARR0_00_DDR_ACTN, ARR0_DDR_ADDRESS_16 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_16, ARR0_DDR_ADDRESS_15 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_15, ARR0_DDR_ADDRESS_14 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_14, ARR0_DDR_CKE = MCBIST_CCS_INST_ARR0_00_DDR_CKE, ARR0_DDR_CKE_LEN = MCBIST_CCS_INST_ARR0_00_DDR_CKE_LEN, ARR0_DDR_CSN_0_1 = MCBIST_CCS_INST_ARR0_00_DDR_CSN_0_1, ARR0_DDR_CSN_0_1_LEN = MCBIST_CCS_INST_ARR0_00_DDR_CSN_0_1_LEN, ARR0_DDR_CID_0_1 = MCBIST_CCS_INST_ARR0_00_DDR_CID_0_1, ARR0_DDR_CID_0_1_LEN = MCBIST_CCS_INST_ARR0_00_DDR_CID_0_1_LEN, ARR0_DDR_CSN_2_3 = MCBIST_CCS_INST_ARR0_00_DDR_CSN_2_3, ARR0_DDR_CSN_2_3_LEN = MCBIST_CCS_INST_ARR0_00_DDR_CSN_2_3_LEN, ARR0_DDR_CID_2 = MCBIST_CCS_INST_ARR0_00_DDR_CID_2, ARR0_DDR_ODT = MCBIST_CCS_INST_ARR0_00_DDR_ODT, ARR0_DDR_ODT_LEN = MCBIST_CCS_INST_ARR0_00_DDR_ODT_LEN, ARR0_DDR_CAL_TYPE = MCBIST_CCS_INST_ARR0_00_DDR_CAL_TYPE, ARR0_DDR_CAL_TYPE_LEN = MCBIST_CCS_INST_ARR0_00_DDR_CAL_TYPE_LEN, ARR0_DDR_PARITY = MCBIST_CCS_INST_ARR0_00_DDR_PARITY, ARR0_DDR_BANK_2 = MCBIST_CCS_INST_ARR0_00_DDR_BANK_2, ARR0_LOOP_BREAK_MODE = MCBIST_CCS_INST_ARR0_00_LOOP_BREAK_MODE, ARR0_LOOP_BREAK_MODE_LEN = MCBIST_CCS_INST_ARR0_00_LOOP_BREAK_MODE_LEN, // ARR1 ARR1_IDLES = MCBIST_CCS_INST_ARR1_00_IDLES, ARR1_IDLES_LEN = MCBIST_CCS_INST_ARR1_00_IDLES_LEN, ARR1_REPEAT_CMD_CNT = MCBIST_CCS_INST_ARR1_00_REPEAT_CMD_CNT, ARR1_REPEAT_CMD_CNT_LEN = MCBIST_CCS_INST_ARR1_00_REPEAT_CMD_CNT_LEN, ARR1_READ_OR_WRITE_DATA = MCBIST_CCS_INST_ARR1_00_READ_OR_WRITE_DATA, ARR1_READ_OR_WRITE_DATA_LEN = MCBIST_CCS_INST_ARR1_00_READ_OR_WRITE_DATA_LEN, ARR1_READ_COMPARE_REQUIRED = MCBIST_CCS_INST_ARR1_00_READ_COMPARE_REQUIRED, ARR1_DDR_CAL_RANK = MCBIST_CCS_INST_ARR1_00_DDR_CAL_RANK, ARR1_DDR_CAL_RANK_LEN = MCBIST_CCS_INST_ARR1_00_DDR_CAL_RANK_LEN, ARR1_DDR_CALIBRATION_ENABLE = MCBIST_CCS_INST_ARR1_00_DDR_CALIBRATION_ENABLE, ARR1_END = MCBIST_CCS_INST_ARR1_00_END, ARR1_GOTO_CMD = MCBIST_CCS_INST_ARR1_00_GOTO_CMD, ARR1_GOTO_CMD_LEN = MCBIST_CCS_INST_ARR1_00_GOTO_CMD_LEN, }; }; namespace mss { static constexpr uint64_t CKE_HIGH = 0b1111; static constexpr uint64_t CKE_LOW = 0b0000; // CKE setup for rank 0-7 to support // Currently only support 0, 1, 4, 5 // Not supported ranks will always get 0 // For self_refresh_entry_command() static constexpr uint64_t CKE_ARY_SRE[] = { // 0, 1, 2, 3, 0b0111, 0b1011, 0, 0, // 4, 5, 6, 7 0b0111, 0b1011, 0, 0 }; // For self_refresh_exit_command() static constexpr uint64_t CKE_ARY_SRX[] = { // 0, 1, 2, 3, 0b1000, 0b0100, 0, 0, // 4, 5, 6, 7 0b1000, 0b0100, 0, 0 }; namespace ccs { enum rank_configuration { DUAL_DIRECT = 0, QUAD_ENCODED = 1, // Note: we don't include QUAD_DIRECT in here // That's because it uses 4 CS and is board wiring dependent // Not sure if it would use CS23 or CID01 for CS2/3 }; /// /// @brief Enums for CCS return codes /// enum { // Success is defined as done-bit set, no others. STAT_QUERY_SUCCESS = 0x4000000000000000, // Bit positions 3:5 STAT_READ_MISCOMPARE = 0x1000000000000000, STAT_UE_SUE = 0x0800000000000000, STAT_CAL_TIMEOUT = 0x0400000000000000, // If the fail type isn't one of these, we're hung STAT_HUNG = 0x0ull, }; /// /// @class instruction_t /// @brief Class for ccs instructions /// @tparam T fapi2::TargetType representing the target of the CCS instructions /// @note A ccs instruction is data (array 0) and some control information (array 1)cc /// template< fapi2::TargetType T, typename TT = ccsTraits > class instruction_t { public: fapi2::buffer arr0; fapi2::buffer arr1; // The MCA indexed rank on which to operate. If this is invalid, all ranks will be disabled uint64_t iv_rank; // We want to have a switch to update rank or not. A user might want to setup CS in some weird way // In that case, they don't want us "fixing" their CS values // We'll default the rank to be updated - we want to send out CS properly bool iv_update_rank; /// /// @brief intstruction_t ctor /// @param[in] i_rank the rank this instruction is headed for /// @param[in] i_arr0 the initial value for arr0, defaults to 0 /// @param[in] i_arr1 the initial value for arr1, defaults to 0 /// @param[in] i_update_rank true if the rank should be updated before being sent, defaults to true /// instruction_t( uint64_t i_rank = NO_CHIP_SELECT_ACTIVE, const fapi2::buffer i_arr0 = 0, const fapi2::buffer i_arr1 = 0, const bool i_update_rank = true): arr0(i_arr0), arr1(i_arr1), iv_rank(i_rank), iv_update_rank(i_update_rank) { // Skip setting up the rank if hte user doesn't want us to if(iv_update_rank) { // Set the chip selects to be 1's (not active) // We'll fix these up before executing the instructions arr0.insertFromRight(0b11); arr0.insertFromRight(0b11); } } /// /// @brief Updates the rank based upon the passed in rank configuration encoding /// @param[in] i_target the port target for this instruction - for error logging /// @param[in] i_rank_config the rank configuration /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode configure_rank(const fapi2::Target& i_target, const rank_configuration i_rank_config ) { // If this instrunction is set to not update the rank, then don't update the rank if(!iv_update_rank) { return fapi2::FAPI2_RC_SUCCESS; } // Regardless of rank configurations, if we have NO_CHIP_SELECT_ACTIVE, deactivate all CS if(iv_rank == NO_CHIP_SELECT_ACTIVE) { arr0.insertFromRight(0b11); arr0.insertFromRight(0b11); return fapi2::FAPI2_RC_SUCCESS; } // First, check rank - we need to make sure that we have a valid rank FAPI_ASSERT(iv_rank < MAX_MRANK_PER_PORT, fapi2::MSS_INVALID_RANK() .set_MCA_TARGET(i_target) .set_RANK(iv_rank) .set_FUNCTION(ffdc_function_codes::CCS_INST_CONFIGURE_RANK), "%s rank out of bounds rank%u", mss::c_str(i_target), iv_rank); // Now the fun happens and we can deal with the actual encoding // If we're quad mode, setup the encoding accordingly if(i_rank_config == rank_configuration::QUAD_ENCODED) { // CS 0/1 are first, while CID0/1 are second // In quad enabled mode, CID acts as a "package select" // It selects R0/2 vs R1/3 // CS0 vs CS1 selects the low vs high rank in the package // CS0 will select rank 0/1 // CS1 will select rank 2/3 // Note: this is to workaround a Nimbus CCS bug // 1) Nimbus thinks that CID0 is a CID not part of the CS for encoded mode // 2) CID1 doesn't matter for quad encode // 3) we only access ranks 2,3 in encoded mode // 4) we can fake out parity by adding in another 1, so let's do that with CID1 // This is easier than calculating our own parity // CS0 H, CS1 L, CID0-> L => Rank 2 static const std::pair CS_N[mss::MAX_RANK_PER_DIMM] = { // CS0 L, CS1 H, CID0-> L => Rank 0 { 0b01, 0b00 }, // CS0 L, CS1 H, CID0-> H => Rank 1 { 0b01, 0b11 }, // CS0 H, CS1 L, CID0-> L => Rank 2 { 0b10, 0b00 }, // CS0 H, CS1 L, CID0-> H => Rank 3 { 0b10, 0b11 }, }; const auto l_dimm_rank = mss::index(iv_rank); const bool l_is_dimm0 = iv_rank < MAX_RANK_PER_DIMM; constexpr uint64_t NON_DIMM_CS = 0b11; // Assigns the CS based upon which DIMM we're at const auto CS01 = l_is_dimm0 ? CS_N[l_dimm_rank].first : NON_DIMM_CS; const auto CS23 = l_is_dimm0 ? NON_DIMM_CS : CS_N[l_dimm_rank].first; // Setup that rank arr0.insertFromRight(CS01); arr0.insertFromRight(CS23); arr0.insertFromRight(CS_N[l_dimm_rank].second); } // Otherwise, setup for dual-direct mode (our only other supported mode at the moment) else { // For DIMM0 .first is the CSN_0_1 setting, .second is the CSN_2_3 setting. // For DIMM1 .first is the CSN_2_3 setting, .second is the CSN_0_1 setting. static const std::pair CS_N[mss::MAX_RANK_PER_DIMM] = { // CS0 L CS1 H => CS2 => H CS3 => H Rank 0 { 0b01, 0b11 }, // CS0 H CS1 L => CS2 => H CS3 => H Rank 1 { 0b10, 0b11 }, // CS0 H CS1 H => CS2 => L CS3 => H Rank 2 { 0b11, 0b01 }, // CS0 H CS1 H => CS2 => H CS3 => L Rank 3 { 0b11, 0b10 }, }; const auto l_dimm_rank = mss::index(iv_rank); const bool l_is_dimm0 = iv_rank < MAX_RANK_PER_DIMM; // Assigns the CS based upon which DIMM we're at const auto CS01 = l_is_dimm0 ? CS_N[l_dimm_rank].first : CS_N[l_dimm_rank].second; const auto CS23 = l_is_dimm0 ? CS_N[l_dimm_rank].second : CS_N[l_dimm_rank].first; // Setup that rank arr0.insertFromRight(CS01); arr0.insertFromRight(CS23); // Check that we don't have a rank out of bounds case here // We can only have that if // 1) we are DIMM1 // 2) our DIMM rank is greater than the maximum allowed number of ranks on DIMM1 // So, we pass always if we're DIMM0, or if our DIMM rank is less than the maximum number of DIMM's on rank 1 FAPI_ASSERT(l_dimm_rank < MAX_RANKS_DIMM1 || l_is_dimm0, fapi2::MSS_INVALID_RANK() .set_MCA_TARGET(i_target) .set_RANK(iv_rank) .set_FUNCTION(ffdc_function_codes::CCS_INST_CONFIGURE_RANK), "%s rank out of bounds rank%u", mss::c_str(i_target), iv_rank); } return fapi2::FAPI2_RC_SUCCESS; fapi_try_exit: return fapi2::current_err; } /// /// @brief Equals comparison operator /// @param[in] i_rhs - the instruction to compare to /// @return True if both instructions are equal /// inline bool operator==( const instruction_t& i_rhs ) const { return arr0 == i_rhs.arr0 && arr1 == i_rhs.arr1 && iv_rank == i_rhs.iv_rank && iv_update_rank == i_rhs.iv_update_rank; } }; /// /// @brief Determines our rank configuration type across all ports /// @param[in] i_target the MCA target on which to operate /// @param[out] o_rank_config the rank configuration /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode get_rank_config(const fapi2::Target& i_target, std::vector& o_rank_config); /// /// @brief Determines our rank configuration type /// @param[in] i_target the MCA target on which to operate /// @param[out] o_rank_config the rank configuration /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS if ok /// fapi2::ReturnCode get_rank_config(const fapi2::Target& i_target, rank_configuration& o_rank_config); /// /// @brief A class representing a series of CCS instructions, and the /// CCS engine parameters associated with running the instructions /// @tparam T fapi2::TargetType representing the fapi2 target which /// @tparam P fapi2::TargetType representing the port /// contains the CCS engine (e.g., fapi2::TARGET_TYPE_MCBIST) template< fapi2::TargetType T, fapi2::TargetType P = fapi2::TARGET_TYPE_MCA > class program { public: // Setup our poll parameters so the CCS executer can see // whether to use the delays in the instruction stream or not program(): iv_poll(0, 0) {} // Vector of instructions std::vector< instruction_t > iv_instructions; poll_parameters iv_poll; // Vector of polling probes std::vector< poll_probe

> iv_probes; }; /// /// @brief Common setup for all MRS/RCD instructions /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in,out] i_arr0 fapi2::buffer representing the ARR0 of the instruction /// template< fapi2::TargetType T, typename TT = ccsTraits > static void mrs_rcd_helper( fapi2::buffer& i_arr0 ) { // // Generic DDR4 MRS setup (RCD is an MRS) // // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS i_arr0.insertFromRight(CKE_HIGH); // ACT is high i_arr0.setBit(); // RAS, CAS, WE low i_arr0.clearBit() .template clearBit() .template clearBit(); } /// /// @brief Setup activate command instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t act_command( const uint64_t i_rank) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; // Set all CKE to high l_boilerplate_arr0.insertFromRight(CKE_HIGH); // ACT is high l_boilerplate_arr0.clearBit(); // RAS low, CAS low, WE low l_boilerplate_arr0.clearBit() .template clearBit() .template clearBit(); // Just leaving the row addresses to all 0 for now // row, bg, ba set to 0 l_boilerplate_arr0.clearBit(); l_boilerplate_arr0.clearBit(); l_boilerplate_arr0.clearBit(); l_boilerplate_arr0.clearBit(); l_boilerplate_arr0.clearBit(); l_boilerplate_arr0.clearBit(); return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Create, initialize an RCD (RCW - JEDEC) CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the DIMM this instruction is headed for /// @param[in] i_turn_on_cke flag that states whether we want CKE on for this RCW (defaulted to true) /// @return the RCD CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t rcd_command( const fapi2::Target& i_target, const bool i_sim, const bool i_turn_on_cke = true) { fapi2::buffer rcd_boilerplate_arr0; fapi2::buffer rcd_boilerplate_arr1; // // Generic DDR4 MRS setup (RCD is an MRS) // mrs_rcd_helper(rcd_boilerplate_arr0); // Not adding i_turn_on_cke in the mrs_rcd helper because we only need this // for RCWs and there is no need to complicate/change the MRS cmd API with // uneeded functionality. Little duplication, but this isolates the change. if( !i_sim ) { const uint64_t l_cke = i_turn_on_cke ? CKE_HIGH : CKE_LOW; rcd_boilerplate_arr0.insertFromRight(l_cke); } // // RCD setup // // DDR4: Set BG1 to 0 during an MRS. // BG0, BA1:BA0 to 0b111 selects RCW (aka MR7). rcd_boilerplate_arr0.clearBit() .template insertFromRight(0b11) .template setBit(); // RCD always goes to the 0th rank on the DIMM; either 0 or 4. return instruction_t((mss::index(i_target) == 0) ? 0 : 4, rcd_boilerplate_arr0, rcd_boilerplate_arr1); } /// /// @brief Create, initialize an MRS CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_mrs the specific MRS /// @return the MRS CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t mrs_command (const uint64_t i_rank, const uint64_t i_mrs ) { fapi2::buffer rcd_boilerplate_arr0; fapi2::buffer rcd_boilerplate_arr1; fapi2::buffer mrs(i_mrs); // // Generic DDR4 MRS setup (RCD is an MRS) // mrs_rcd_helper(rcd_boilerplate_arr0); // // MRS setup // // DDR4: Set BG1 to 0. BG0, BA1:BA0 to i_mrs rcd_boilerplate_arr0.clearBit(); mss::swizzle(mrs, rcd_boilerplate_arr0); FAPI_DBG("mrs rcd boiler 0x%llx 0x%llx", uint8_t(mrs), uint64_t(rcd_boilerplate_arr0)); return instruction_t(i_rank, rcd_boilerplate_arr0, rcd_boilerplate_arr1); } /// /// @brief Create, initialize a JEDEC Device Deselect CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_idle the idle time to the next command (default to 0) /// @return the Device Deselect CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t des_command(const uint16_t i_idle = 0) { fapi2::buffer rcd_boilerplate_arr0; fapi2::buffer rcd_boilerplate_arr1; // ACT is high. It's a no-care in the spec but it seems to raise questions when // people look at the trace, so lets set it high. rcd_boilerplate_arr0.setBit(); // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS rcd_boilerplate_arr0.insertFromRight(CKE_HIGH); // Insert idle rcd_boilerplate_arr1.template insertFromRight( i_idle ); // ACT is high no-care // RAS, CAS, WE no-care // Device Deslect wants CS_n always high (select nothing using rank NO_CHIP_SELECT_ACTIVE) return instruction_t( NO_CHIP_SELECT_ACTIVE, rcd_boilerplate_arr0, rcd_boilerplate_arr1); } /// /// @brief Converts an ODT attribute to CCS array input /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_attr_value ODT attribute value /// @return CCS value for the ODT's /// template< fapi2::TargetType T, typename TT = ccsTraits > inline uint8_t convert_odt_attr_to_ccs(const fapi2::buffer& i_attr_value) { // ODT value buffer fapi2::buffer l_ccs_value; l_ccs_value.template writeBit(i_attr_value.template getBit()) .template writeBit(i_attr_value.template getBit()) .template writeBit(i_attr_value.template getBit()) .template writeBit(i_attr_value.template getBit()) .template writeBit(i_attr_value.template getBit()); return uint8_t(l_ccs_value); } /// /// @brief Create, initialize a JEDEC Device Deselect CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_odt_values CCS defined ODT values /// @param[in] i_cycles the number of cycles to hold the ODT for - defaults to DEFAULT_ODT_CYCLE_LEN /// @return the Device Deselect CCS instruction /// @note This technically is not a JEDEC command, but is needed for CCS to hold the ODT cycles /// CCS by design does not repeat or latch ODT's appropriately /// As such, it's up to the programmers to hold the ODT's appropriately /// This "command" will greatly help us do that /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t odt_command(const uint8_t i_odt_values, const uint64_t i_cycles = TT::DEFAULT_ODT_CYCLE_LEN) { auto l_odt_cmd = des_command(); l_odt_cmd.arr0.template insertFromRight(i_odt_values); l_odt_cmd.arr1.template insertFromRight(i_cycles); return l_odt_cmd; } /// /// @brief Create, initialize a NTTM read CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @return the Device Deselect CCS instruction /// @note need to setup 4 cycles delay /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t nttm_read_command() { // setup 4 cycles delay constexpr uint64_t NTTM_READ_DELAY = 4; // No.. there's no field for this one. it's an undocumented bit constexpr uint64_t NTTM_MODE_FORCE_READ = 33; // get the des_command auto l_command = des_command(); // set to CCS_INST_ARR1 register l_command.arr1.template setBit(); l_command.arr1.template insertFromRight(NTTM_READ_DELAY); return l_command; } /// /// @brief Create, initialize a JEDEC Device Power Down Entry CCS command /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @return the Device Deselect CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t pde_command() { fapi2::buffer rcd_boilerplate_arr0; fapi2::buffer rcd_boilerplate_arr1; // Power Down Entry just like a DES, but we set CKE low instruction_t l_inst = des_command(); // CKE is low. Note: P8 set all 4 of these low - not sure if that's correct. l_inst.arr0.template insertFromRight(CKE_LOW); l_inst.arr1.template insertFromRight( mss::tcpded() ); return l_inst; } /// /// @brief Create, initialize an instruction which indicates an initial cal /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rp the rank-pair (rank) to cal /// @return the initial cal instruction /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t initial_cal_command(const uint64_t i_rp) { // An initial cal arr0 looks just like a DES, but we set the initial cal bits instruction_t l_inst = des_command(); // ACT is low - per Centaur spec (Shelton to confirm for Nimbus) BRS l_inst.arr0.template clearBit(); l_inst.arr0.template insertFromRight(0b1100); l_inst.arr1.template setBit(); #ifdef USE_LOTS_OF_IDLES // Idles is 0xFFFF - per Centaur spec (Shelton to confirm for Nimbus) BRS l_inst.arr1.template insertFromRight(0xFFFF); #else l_inst.arr1.template insertFromRight(0x0); #endif // The rank we're calibrating is enacoded - it's an int. So rank 3 is 0011 not 0001 l_inst.arr1.template insertFromRight(i_rp); return l_inst; } /// /// @brief Setup ZQ Long instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the DIMM this instruction is headed for /// @param[in] i_rank the rank on this dimm /// @param[in] i_idle the idle time to the next command (default to 0) /// @return the MRS CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t zqcl_command( const uint64_t i_rank, const uint16_t i_idle = 0 ) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS l_boilerplate_arr0.insertFromRight(CKE_HIGH); // ACT is high l_boilerplate_arr0.setBit(); // RAS/CAS high, WE low l_boilerplate_arr0.setBit() .template setBit() .template clearBit(); // ADDR10/AP is high l_boilerplate_arr0.setBit(); // Insert idle l_boilerplate_arr1.template insertFromRight( i_idle ); return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup read command helper function /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_bank_addr bank address bits [BG0:BG1] = [62:63] (right aligned) /// @param[in] i_bank_group_addr bank group address bits [BA0:BA1] = [62:63] (right aligned) /// @param[in] i_column_addr column address bits [A0:A9] = [54:63] (right aligned) /// @return the read command CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > static fapi2::buffer read_cmd_boilerplate( const uint64_t i_rank, const fapi2::buffer& i_bank_addr = 0, const fapi2::buffer& i_bank_group_addr = 0, const fapi2::buffer& i_column_addr = 0) { // TODO - RTC 166175 Encapsulate command truth table in a subclass for ccs.H fapi2::buffer l_boilerplate_arr0; // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. AAM l_boilerplate_arr0.insertFromRight(CKE_HIGH); // ACT is high l_boilerplate_arr0.setBit(); // RAS high, CAS low, WE high l_boilerplate_arr0.setBit() .template clearBit() .template setBit(); l_boilerplate_arr0.insertFromRight(i_bank_addr); // Bank Group takes a little effort - the bits aren't contiguous constexpr uint64_t BG0_BIT = 62; constexpr uint64_t BG1_BIT = 63; l_boilerplate_arr0.writeBit(i_bank_group_addr.getBit()) .template writeBit(i_bank_group_addr.getBit()); // CA is A[0:9] l_boilerplate_arr0.insertFromRight(i_column_addr); return l_boilerplate_arr0; } /// /// @brief Setup write command (Fixed BL8 or BC4) instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_bank_addr bank address bits [BA0:BA1] = [62:63] (right aligned) /// @param[in] i_bank_group_addr bank group address bits [BG0:BG1] = [62:63] (right aligned) /// @param[in] i_column_addr column address bits [A0:A9] = [54:63] (right aligned) /// @return the write command CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t wr_command( const uint64_t i_rank, const fapi2::buffer& i_bank_addr = 0, const fapi2::buffer& i_bank_group_addr = 0, const fapi2::buffer& i_column_addr = 0) { // WR's and RD's are very similar, so we just use the RD command boiler plate and modify the command to a WR fapi2::buffer l_boilerplate_arr0 = read_cmd_boilerplate(i_rank, i_bank_addr, i_bank_group_addr, i_column_addr); fapi2::buffer l_boilerplate_arr1; // RAS high, CAS low, WE low l_boilerplate_arr0.setBit() .template clearBit() .template clearBit(); return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup read command (Fixed BL8 or BC4) instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_bank_addr bank address bits [BA0:BA1] = [62:63] (right aligned) /// @param[in] i_bank_group_addr bank group address bits [BG0:BG1] = [62:63] (right aligned) /// @param[in] i_column_addr column address bits [A0:A9] = [54:63] (right aligned) /// @return the read command CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t rd_command( const uint64_t i_rank, const fapi2::buffer& i_bank_addr = 0, const fapi2::buffer& i_bank_group_addr = 0, const fapi2::buffer& i_column_addr = 0) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; l_boilerplate_arr0 = read_cmd_boilerplate(i_rank, i_bank_addr, i_bank_group_addr, i_column_addr); // Setup ADDR10/AP based on read type l_boilerplate_arr0.clearBit(); return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup read w/auto precharge command (Fixed BL8 or BC4) instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_bank_addr bank address bits [BG0:BG1] = [62:63] (right aligned) /// @param[in] i_bank_group_addr bank group address bits [BA0:BA1] = [62:63] (right aligned) /// @param[in] i_column_addr column address bits [A0:A9] = [54:63] (right aligned) /// @return the read command CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t rda_command( const uint64_t i_rank, const fapi2::buffer& i_bank_addr = 0, const fapi2::buffer& i_bank_group_addr = 0, const fapi2::buffer& i_column_addr = 0) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; l_boilerplate_arr0 = read_cmd_boilerplate(i_rank, i_bank_addr, i_bank_group_addr, i_column_addr); // Setup ADDR10/AP based on read type l_boilerplate_arr0.setBit(); return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup precharge all banks command instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_idle the idle time to the next command (default to 0) /// @return the precharge all banks command CCS instruction /// @note THIS IS DDR4 ONLY RIGHT NOW. We can (and possibly should) specialize this /// for the controller (Nimbus v Centaur) and then correct for DRAM generation (not included /// in this template definition) /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t precharge_all_command( const uint64_t i_rank, const uint16_t i_idle = 0 ) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. AAM l_boilerplate_arr0.insertFromRight(CKE_HIGH); // ACT is high l_boilerplate_arr0.setBit(); // RAS low, CAS high, WE low l_boilerplate_arr0.clearBit() .template setBit() .template clearBit(); // Setup ADDR10/AP high l_boilerplate_arr0.setBit(); // Insert idle l_boilerplate_arr1.template insertFromRight( i_idle ); // From DDR4 Spec table 17: // All other bits from the command truth table or 'V', for valid (1 or 0) return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup self-refresh entry command instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_idle the idle time to the next command (default to 0) /// @return the self-refresh entry command CCS instruction /// @note THIS IS FOR DDR4 NON-LRDIMM ONLY RIGHT NOW /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t self_refresh_entry_command( const uint64_t i_rank, const uint16_t i_idle = 0 ) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; // Set all CKE to high except the rank passed in l_boilerplate_arr0.insertFromRight(CKE_ARY_SRE[i_rank]); // ACT is high l_boilerplate_arr0.setBit(); // RAS low, CAS low, WE high l_boilerplate_arr0.clearBit() .template clearBit() .template setBit(); // Insert idle l_boilerplate_arr1.template insertFromRight( i_idle ); // From DDR4 Spec table 17: // All other bits from the command truth table are 'V', for valid (1 or 0) return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } /// /// @brief Setup self-refresh exit using NOP command instruction /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_rank the rank on this dimm /// @param[in] i_idle the idle time to the next command (default to 0) /// @return the self-refresh exit command CCS instruction /// @note Using NOP in case SDRAM is in gear down mode and max power saving mode exit /// @note THIS IS FOR DDR4 NON-LRDIMM ONLY RIGHT NOW /// template< fapi2::TargetType T, typename TT = ccsTraits > inline instruction_t self_refresh_exit_command( const uint64_t i_rank, const uint16_t i_idle = 0 ) { fapi2::buffer l_boilerplate_arr0; fapi2::buffer l_boilerplate_arr1; // Set all CKE to low except the rank passed in l_boilerplate_arr0.insertFromRight(CKE_ARY_SRX[i_rank]); // ACT is high l_boilerplate_arr0.setBit(); // RAS high, CAS high, WE high l_boilerplate_arr0.setBit() .template setBit() .template setBit(); // Insert idle l_boilerplate_arr1.template insertFromRight( i_idle ); // From DDR4 Spec table 17: // All other bits from the command truth table are 'V', for valid (1 or 0) return instruction_t(i_rank, l_boilerplate_arr0, l_boilerplate_arr1); } // // These functions are a little sugar to keep callers from doing the traits-dance to get the // appropriate bit field // /// /// @brief Select the port(s) to be used by the CCS /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in] i_ports the buffer representing the ports /// template< fapi2::TargetType T, typename TT = ccsTraits > inline fapi2::ReturnCode select_ports( const fapi2::Target& i_target, uint64_t i_ports) { fapi2::buffer l_data; fapi2::buffer l_ports; // Not handling multiple ports here, can't do that for CCS. BRS FAPI_TRY( l_ports.setBit(i_ports) ); FAPI_TRY( mss::getScom(i_target, TT::MCB_CNTL_REG, l_data) ); l_data.insert(l_ports); FAPI_TRY( mss::putScom(i_target, TT::MCB_CNTL_REG, l_data) ); fapi_try_exit: return fapi2::current_err; } /// /// @brief User sets to a '1'b to tell the Hdw to stop CCS whenever failure occurs. When a /// '0'b, Hdw will continue CCS even if a failure occurs. /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @param[in] i_value true iff stop whenever failure occurs. /// template< fapi2::TargetType T, typename TT = ccsTraits > inline void stop_on_err( const fapi2::Target&, fapi2::buffer& io_buffer, const states i_value) { io_buffer.writeBit(i_value); } /// /// @brief Disable ECC checking on the CCS arrays /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// template< fapi2::TargetType T, typename TT = ccsTraits > inline void disable_ecc( const fapi2::Target&, fapi2::buffer& io_buffer) { io_buffer.setBit() .template setBit(); } /// /// @brief User sets to a '1'b to force the Hdw to ignore any array ue or sue errors /// during CCS command fetching. /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @param[in] i_value true iff ignore any array ue or sue errors. /// template< fapi2::TargetType T, typename TT = ccsTraits > inline void ue_disable( const fapi2::Target&, fapi2::buffer& io_buffer, const states i_value) { io_buffer.writeBit(i_value); } /// /// @brief User sets to a '1'b to force the Hdw to delay parity a cycle /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @param[in] i_value mss::ON iff delay parity a cycle /// template< fapi2::TargetType T, typename TT = ccsTraits > inline void parity_after_cmd( const fapi2::Target&, fapi2::buffer& io_buffer, const states i_value) { io_buffer.writeBit(i_value); } /// /// @brief DDr calibration counter /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @param[in] i_count the count to wait for DDR cal to complete. /// @param[in] i_mult the DDR calibration time multiplaction factor /// template< fapi2::TargetType T, typename TT = ccsTraits > inline void cal_count( const fapi2::Target&, fapi2::buffer& io_buffer, const uint64_t i_count, const uint64_t i_mult) { io_buffer.insertFromRight(i_count); io_buffer.insertFromRight(i_mult); } /// /// @brief Copy CKE signals to CKE Spare on both ports NOTE: DOESN'T APPLY FOR NIMBUS. NO /// SPARE CHIPS TO COPY TO. 0 - Spare CKEs not copied with values from CKE(0:1) and /// CKE(4:5) 1 - Port A CKE(0:1) copied to Port A CKE(2:3), Port A CKE(4:5) copied /// to Port A CKE(6:7), Port B CKE(0:1) copied to Port B CKE(2:3) and Port B CKE(4:5) /// copied to Port B CKE(6:7) /// @tparam T the fapi2::TargetType - derived /// @tparam TT the ccsTraits associated with T - derived /// @param[in] i_target the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @param[in] i_value mss::ON iff Copy CKE signals to CKE Spare on both ports /// @note no-op for p9n /// template< fapi2::TargetType T, typename TT = ccsTraits > void copy_cke_to_spare_cke( const fapi2::Target&, fapi2::buffer& io_buffer, const states i_value); /// /// @brief Read the modeq register appropriate for this target /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in,out] io_buffer the buffer representing the mode register /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, typename TT = ccsTraits > inline fapi2::ReturnCode read_mode( const fapi2::Target& i_target, fapi2::buffer& io_buffer) { FAPI_DBG("read mode 0x%llx", TT::MODEQ_REG); return mss::getScom(i_target, TT::MODEQ_REG, io_buffer); } /// /// @brief Write the modeq register appropriate for this target /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in] i_buffer the buffer representing the mode register /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, typename TT = ccsTraits > inline fapi2::ReturnCode write_mode( const fapi2::Target& i_target, const fapi2::buffer& i_buffer) { return mss::putScom(i_target, TT::MODEQ_REG, i_buffer); } /// /// @brief config the NTTM /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_mcbist the target to operate /// @param[in] i_nttm_mode NTTM we need to turn on or off (i.e. ON, OFF) /// @return fapi2::ReturnCode fapi2::FAPI2_RC_SUCCESS if ok /// template< fapi2::TargetType T, typename TT = ccsTraits > inline fapi2::ReturnCode configure_nttm( const fapi2::Target& i_target, const mss::states i_nttm_mode) { fapi2::buffer l_data; FAPI_TRY(read_mode(i_target, l_data)); l_data.writeBit(i_nttm_mode); FAPI_TRY(write_mode(i_target, l_data)); fapi_try_exit: return fapi2::current_err; } /// /// @brief Execute a set of CCS instructions - multiple ports /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in] i_program the vector of instructions /// @param[in] i_ports the vector of ports /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, fapi2::TargetType P, typename TT = ccsTraits > fapi2::ReturnCode execute( const fapi2::Target& i_target, ccs::program& i_program, const std::vector< fapi2::Target

>& i_ports); /// /// @brief Execute a set of CCS instructions - single port /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam P the target of the CCS instruction (the port) /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in] i_program the vector of instructions /// @param[in] i_port The target that's being programmed by the array /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, fapi2::TargetType P, typename TT = ccsTraits > fapi2::ReturnCode execute( const fapi2::Target& i_target, ccs::program& i_program, const fapi2::Target

& i_port) { // Mmm. Might want to find a better way to do this - seems expensive. BRS std::vector< fapi2::Target

> l_ports{ i_port }; return execute(i_target, i_program, l_ports); } /// /// @brief Execute a CCS array already loaded in to the engine /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam P the target of the CCS instruction (the port) /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target the target to effect /// @param[in] i_program the MCBIST ccs program - to get the polling parameters /// @param[in] i_port the port associated with the MCBIST array /// @return FAPI2_RC_SUCCSS iff ok /// template< fapi2::TargetType T, fapi2::TargetType P, typename TT = ccsTraits > fapi2::ReturnCode execute_inst_array(const fapi2::Target& i_target, ccs::program& i_program, const fapi2::Target

& i_port); /// /// @brief Start or stop the CCS engine /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target The MCBIST containing the CCS engine /// @param[in] i_start_stop bool MSS_CCS_START for starting MSS_CCS_STOP otherwise /// @return FAPI2_RC_SUCCESS iff success /// template< fapi2::TargetType T, typename TT = ccsTraits > fapi2::ReturnCode start_stop( const fapi2::Target& i_target, const bool i_start_stop ); /// /// @brief Query the status of the CCS engine /// @tparam T the target type of the chiplet which executes the CCS instruction /// @tparam TT the CCS traits of the chiplet which executes the CCS instruction /// @param[in] i_target The MCBIST containing the CCS engine /// @param[out] io_status The query result first being the result, second the type /// @return FAPI2_RC_SUCCESS iff success /// template< fapi2::TargetType T, typename TT = ccsTraits > fapi2::ReturnCode status_query( const fapi2::Target& i_target, std::pair& io_status ); /// /// @brief Determine the CCS failure type /// @param[in] i_target MCBIST target /// @param[in] i_type the failure type /// @param[in] i_mca The port the CCS instruction is training /// @return ReturnCode associated with the fail. /// @note FFDC is handled here, caller doesn't need to do it /// fapi2::ReturnCode fail_type( const fapi2::Target& i_target, const uint64_t& i_type, const fapi2::Target& i_mca ); } // ends namespace ccs } #endif