diff options
Diffstat (limited to 'src/import/chips')
42 files changed, 13042 insertions, 0 deletions
diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.C b/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.C new file mode 100644 index 000000000..5753fd577 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.C @@ -0,0 +1,254 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/ccs/ccs.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file ccs.C +/// @brief Run and manage the CCS engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "ccs.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ +namespace ccs +{ + +/// +/// @brief Start or stop the CCS engine +/// @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::ReturnCode start_stop( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target, bool i_start_stop ) +{ + typedef ccsTraits<TARGET_TYPE_MCBIST> TT; + + fapi2::buffer<uint64_t> l_buf; + + // Do we need to read this? We are setting the only bit defined in the scomdef? BRS + FAPI_TRY(fapi2::getScom(i_target, TT::CNTLQ_REG, l_buf)); + + FAPI_TRY( fapi2::putScom(i_target, TT::CNTLQ_REG, + i_start_stop ? l_buf.setBit<TT::CCS_START>() : l_buf.setBit<TT::CCS_STOP>()) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Determine the CCS failure type +/// @tparam T the fapi2 target type of the target for this error +/// @param[in] the failure type +/// @return ReturnCode associated with the fail. +/// @note FFDC is handled here, caller doesn't need to do it +/// +template< fapi2::TargetType T > +fapi2::ReturnCode fail_type( const fapi2::Target<T>& i_target, const uint64_t& i_type ) +{ + FAPI_ASSERT(STAT_READ_MISCOMPARE != i_type, + fapi2::MSS_CCS_READ_MISCOMPARE().set_TARGET_IN_ERROR(i_target), + "CCS FAIL Read Miscompare"); + + FAPI_ASSERT(STAT_UE_SUE != i_type, + fapi2::MSS_CCS_UE_SUE().set_TARGET_IN_ERROR(i_target), + "CCS FAIL UE or SUE Error"); + + FAPI_ASSERT(STAT_CAL_TIMEOUT != i_type, + fapi2::MSS_CCS_CAL_TIMEOUT().set_TARGET_IN_ERROR(i_target), + "CCS FAIL Calibration Operation Time Out"); + + FAPI_ASSERT(STAT_HUNG != i_type, + fapi2::MSS_CCS_HUNG().set_TARGET_IN_ERROR(i_target), + "CCS appears hung"); +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Execute the contents of the CCS array +/// @param[in] i_target The MCBIST containing the array +/// @param[in] the MCBIST ccs program - to get the polling parameters +/// @return FAPI2_RC_SUCCESS iff success +/// +template<> +fapi2::ReturnCode execute_inst_array(const fapi2::Target<TARGET_TYPE_MCBIST>& i_target, + ccs::program<TARGET_TYPE_MCBIST>& i_program) +{ + typedef ccsTraits<TARGET_TYPE_MCBIST> TT; + + fapi2::buffer<uint64_t> status; + + FAPI_TRY(start_stop(i_target, mss::START)); + + mss::poll(i_target, TT::STATQ_REG, i_program.iv_poll, + [&status](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("ccs statq 0x%llx, remaining: %d", stat_reg, poll_remaining); + status = stat_reg; + return status.getBit<TT::CCS_IN_PROGRESS>() != 1; + }); + + // Check for done and success. DONE being the only bit set. + if (status == STAT_QUERY_SUCCESS) + { + FAPI_DBG("CCS Executed Successfully."); + goto fapi_try_exit; + } + + // So we failed or we're still in progress. Mask off the fail bits + // and run this through the FFDC generator. + FAPI_TRY( fail_type(i_target, status & 0x1C00000000000000) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Execute a set of CCS instructions +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the vector of instructions +/// @param[in] the vector of ports +/// @return FAPI2_RC_SUCCSS iff ok +/// @note assumes the CCS engine has been configured. +/// +template<> +fapi2::ReturnCode execute( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target, + ccs::program<TARGET_TYPE_MCBIST>& i_program, + const std::vector< fapi2::Target<TARGET_TYPE_MCA> >& i_ports) +{ + // Subtract one for the idle we insert at the end + static const size_t CCS_INSTRUCTION_DEPTH = 32 - 1; + static const uint64_t CCS_ARR0_ZERO = MCBIST_CCS_INST_ARR0_00; + static const uint64_t CCS_ARR1_ZERO = MCBIST_CCS_INST_ARR1_00; + + ccs::instruction_t<TARGET_TYPE_MCBIST> l_des = ccs::des_command<TARGET_TYPE_MCBIST>(); + + FAPI_DBG("loading ccs instructions (%d) for %s", i_program.iv_instructions.size(), mss::c_str(i_target)); + + auto l_inst_iter = i_program.iv_instructions.begin(); + + while (l_inst_iter != i_program.iv_instructions.end()) + { + size_t l_inst_count = 0; + + uint64_t l_total_delay = 0; + uint64_t l_delay = 0; + uint64_t l_repeat = 0; + + // Shove the instructions into the CCS engine, in 32 instruction chunks, and execute them + for (; l_inst_iter != i_program.iv_instructions.end() + && l_inst_count < CCS_INSTRUCTION_DEPTH; ++l_inst_count, ++l_inst_iter) + { + // Make sure this instruction leads to the next. Notice this limits this mechanism to pretty + // simple (straight line) CCS programs. Anything with a loop or such will need another mechanism. + l_inst_iter->arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_GOTO_CMD, + MCBIST_CCS_INST_ARR1_00_GOTO_CMD_LEN>(l_inst_count + 1); + FAPI_TRY( fapi2::putScom(i_target, CCS_ARR0_ZERO + l_inst_count, l_inst_iter->arr0) ); + FAPI_TRY( fapi2::putScom(i_target, CCS_ARR1_ZERO + l_inst_count, l_inst_iter->arr1) ); + + // arr1 contains a specification of the delay and repeat after this instruction, as well + // as a repeat. Total up the delays as we go so we know how long to wait before polling + // the CCS engine for completion + l_inst_iter->arr1.extractToRight<MCBIST_CCS_INST_ARR1_00_IDLES, MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(l_delay); + l_inst_iter->arr1.extractToRight<MCBIST_CCS_INST_ARR1_00_REPEAT_CMD_CNT, + MCBIST_CCS_INST_ARR1_00_REPEAT_CMD_CNT>(l_repeat); + + l_total_delay += l_delay * (l_repeat + 1); + + FAPI_DBG("css inst %d: 0x%016lX 0x%016lX (0x%lx, 0x%lx) delay: 0x%x (0x%x) %s", + l_inst_count, l_inst_iter->arr0, l_inst_iter->arr1, + CCS_ARR0_ZERO + l_inst_count, CCS_ARR1_ZERO + l_inst_count, + l_delay, l_total_delay, mss::c_str(i_target)); + } + + // Check our program for any delays. If there isn't a iv_initial_delay configured, then + // we use the delay we just summed from the instructions. + if (i_program.iv_poll.iv_initial_delay == 0) + { + i_program.iv_poll.iv_initial_delay = cycles_to_ns(i_target, l_total_delay); + } + + if (i_program.iv_poll.iv_initial_sim_delay == 0) + { + i_program.iv_poll.iv_initial_sim_delay = cycles_to_simcycles(l_total_delay); + } + + FAPI_DBG("executing ccs instructions (%d:%d, %d) for %s", + i_program.iv_instructions.size(), l_inst_count, i_program.iv_poll.iv_initial_delay, mss::c_str(i_target)); + + // Insert a DES as our last instruction. DES is idle state anyway and having this + // here as an instruction forces the CCS engine to wait the delay specified in + // the last instruction in this array (which it otherwise doesn't do.) + l_des.arr1.setBit<MCBIST_CCS_INST_ARR1_00_END>(); + FAPI_TRY( fapi2::putScom(i_target, CCS_ARR0_ZERO + l_inst_count, l_des.arr0) ); + FAPI_TRY( fapi2::putScom(i_target, CCS_ARR1_ZERO + l_inst_count, l_des.arr1) ); + + FAPI_DBG("css inst %d fixup: 0x%016lX 0x%016lX (0x%lx, 0x%lx) %s", + l_inst_count, l_des.arr0, l_des.arr1, + CCS_ARR0_ZERO + l_inst_count, CCS_ARR1_ZERO + l_inst_count, mss::c_str(i_target)); + + // Kick off the CCS engine - per port. No broadcast mode for CCS (per Shelton 9/23/15) + for (auto p : i_ports) + { + FAPI_DBG("executing CCS array for port %d (%s)", mss::pos(p), mss::c_str(p)); + FAPI_TRY( select_ports( i_target, mss::pos(p)) ); + FAPI_TRY( execute_inst_array(i_target, i_program) ); + } + } + +fapi_try_exit: + i_program.iv_instructions.clear(); + return fapi2::current_err; +} + +/// +/// @brief Nimbus specialization for modeq_copy_cke_to_spare_cke +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @param[in] bool, true iff Copy CKE signals to CKE Spare on both ports +/// @return void +/// @note no-op for p9n +/// +template<> +void copy_cke_to_spare_cke<TARGET_TYPE_MCBIST>( const fapi2::Target<TARGET_TYPE_MCBIST>&, fapi2::buffer<uint64_t>&, + bool ) +{ + return; +} + +} // namespace +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.H b/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.H new file mode 100644 index 000000000..d5008f660 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.H @@ -0,0 +1,648 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/ccs/ccs.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file ccs.H +/// @brief Run and manage the CCS engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_CCS_H_ +#define _MSS_CCS_H_ + +#include <fapi2.H> + +#include <p9_mc_scom_addresses.H> + +#include "../utils/poll.H" +#include "../port/port.H" +#include "../shared/mss_const.H" + +// 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 + +template< fapi2::TargetType T > +class ccsTraits; + +// Centaur CCS Engine traits +template<> +class ccsTraits<fapi2::TARGET_TYPE_MEMBUF_CHIP> +{ + public: +}; + +// Nimbus CCS Engine traits +template<> +class ccsTraits<fapi2::TARGET_TYPE_MCBIST> +{ + public: + static const uint64_t MODEQ_REG = MCBIST_CCS_MODEQ; + static const uint64_t MCB_CNTL_REG = MCBIST_MCB_CNTLQ; + static const uint64_t CNTLQ_REG = MCBIST_CCS_CNTLQ; + static const uint64_t STATQ_REG = MCBIST_CCS_STATQ; + + enum + { + // 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_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 +{ +namespace ccs +{ + +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, +}; + +// A ccs instruction is data (array 0) and some control information (array 1) +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +class instruction_t +{ + public: + fapi2::buffer<uint64_t> arr0; + fapi2::buffer<uint64_t> arr1; + + /// + /// @brief intstruction_t ctor + /// @param[in] the DIMM this instruction is headed for + /// @param[in] the rank this instruction is headed for + /// @param[in] the initial value for arr0, defaults to 0 + /// @param[in] the initial value for arr1, defaults to 0 + /// + instruction_t( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target = fapi2::Target<fapi2::TARGET_TYPE_DIMM>(), + uint64_t i_rank = 0xFF, + const fapi2::buffer<uint64_t> i_arr0 = 0, + const fapi2::buffer<uint64_t> i_arr1 = 0): + arr0(i_arr0), + arr1(i_arr1) + { + + static const uint64_t CS_N[mss::MAX_RANK_PER_DIMM] = + { + // DCS0 L DCS1 H => Rank 0 + 0b01, + // DCS0 H DCS1 L => Rank 1 + 0b10, + }; + + // Start be deselcting everything and we'll clear the bits we want. + arr0.insertFromRight<TT::ARR0_DDR_CSN_0_1, TT::ARR0_DDR_CSN_0_1_LEN>(0b11); + arr0.insertFromRight<TT::ARR0_DDR_CSN_2_3, TT::ARR0_DDR_CSN_2_3_LEN>(0b11); + + // If the rank indicates nothing selected (active low) then we're done. + if (i_rank == 0xFF) + { + return; + } + + // + // Note: This needs to be able to handle all DIMM, stacked, encoded CS_n, etc. This + // ain't gonna cut it. Turn this in to a dispatched funtion like c_str() and rcd_load() BRS + // + + // Direct CS mode - just clear the CS_N you're interested in. + // Setup the chip select based on which dimm in the slot and the rank + if (mss::index(i_target) == 0) + { + arr0.insertFromRight<TT::ARR0_DDR_CSN_0_1, + TT::ARR0_DDR_CSN_0_1_LEN>(CS_N[i_rank]); + } + else + { + arr0.insertFromRight<TT::ARR0_DDR_CSN_2_3, + TT::ARR0_DDR_CSN_2_3_LEN>(CS_N[i_rank]); + } + +#ifdef QUAD_ENCODED_CS + // Implement the Encoded QuadCS Mode DCS, DC mapping and stuff the resulting + // bits in to the proper location for the CCS instruction (perhaps we need + // to be a template - p9n CCS is different from Centaur ... make initializing + // the instruction a policy of the ccsTraits ... BRS) + + // Lookup table for CS_N and CID indexed by rank for Quad encoded CS modee + // First bits 0:1 is DCS1_n:DCS2_n. Second bits 0:1 are CID 0:1 bit 2 is CID 2 + static const std::pair< uint8_t, uint8_t > CS_CID[mss::MAX_RANK_PER_DIMM] = + { + // DCS0 L DCS1 H CID L:L => Rank 0 + { 0b01000000, 0b00000000 }, + // DCS0 L DCS1 H CID H:H => Rank 1 + { 0b01000000, 0b11000000 }, + // DCS0 H DCS1 L CID L:L => Rank 2 + { 0b10000000, 0b00000000 }, + // DCS0 H DCS1 L CID H:H => Rank 3 + { 0b10000000, 0b11000000 }, + }; + + // Setup the chip select based on which dimm in the slot and the rank + if (mss::index(i_target) == 0) + { + arr0.insert<TT::ARR0_DDR_CSN_0_1, + TT::ARR0_DDR_CSN_0_1_LEN>(CS_CID[i_rank].first); + } + else + { + arr0.insert<TT::ARR0_DDR_CSN_2_3, + TT::ARR0_DDR_CSN_2_3_LEN>(CS_CID[i_rank].first); + } + + arr0.insert<TT::ARR0_DDR_CID_0_1, + TT::ARR0_DDR_CID_0_1_LEN>(CS_CID[i_rank].second); + arr0.writeBit<TT::ARR0_DDR_CID_2>( + fapi2::buffer<uint8_t>(CS_CID[i_rank].second).getBit<2>()); +#endif + } +}; + +/// +/// @brief A class representing a series of CCS instructions, and the +/// CCS engine parameters associated with running the instructions +/// @tparam fapi2::TargetType T representing the fapi2 target which +/// contains the CCS engine (e.g., fapi2::TARGET_TYPE_MCBIST) +template< fapi2::TargetType T > +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<T> > iv_instructions; + poll_parameters iv_poll; +}; + +/// +/// @brief Common setup for all MRS/RCD instructions +/// @param[in,out] fapi2::buffer<uint64_t> representing the ARR0 of the instruction +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +static void mrs_rcd_helper( fapi2::buffer<uint64_t>& 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<TT::ARR0_DDR_CKE, TT::ARR0_DDR_CKE_LEN>(0b1111); + + // ACT is high + i_arr0.setBit<TT::ARR0_DDR_ACTN>(); + + // RAS, CAS, WE low + i_arr0.clearBit<TT::ARR0_DDR_ADDRESS_16>(); + i_arr0.clearBit<TT::ARR0_DDR_ADDRESS_15>(); + i_arr0.clearBit<TT::ARR0_DDR_ADDRESS_14>(); +} + +/// +/// @brief Create, initialize an RCD (RCW - JEDEC) CCS command +/// @tparam T, the fapi2 type of the unit which contains the CCS engine +/// @param[in] the DIMM this instruction is headed for +/// @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<T> > +inline instruction_t<T> rcd_command( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target ) +{ + fapi2::buffer<uint64_t> rcd_boilerplate_arr0; + fapi2::buffer<uint64_t> rcd_boilerplate_arr1; + + // + // Generic DDR4 MRS setup (RCD is an MRS) + // + mrs_rcd_helper<fapi2::TARGET_TYPE_MCBIST>(rcd_boilerplate_arr0); + + // + // RCD setup + // + // DDR4: Set BG1 to 0. BG0, BA1:BA0 to 0b111 + rcd_boilerplate_arr0.clearBit<TT::ARR0_DDR_BANK_GROUP_1>(); + rcd_boilerplate_arr0.insertFromRight<TT::ARR0_DDR_BANK_0_1, TT::ARR0_DDR_BANK_0_1_LEN>(0b11); + rcd_boilerplate_arr0.setBit<TT::ARR0_DDR_BANK_GROUP_0>(); + + // RCD always goes to rank 0. All we need to know is which DIMM we are on the port + return instruction_t<T>(i_target, 0, rcd_boilerplate_arr0, rcd_boilerplate_arr1); +} + +/// +/// @brief Create, initialize an MRS CCS command +/// @tparam T, the fapi2 type of the unit which contains the CCS engine +/// @param[in] the DIMM this instruction is headed for +/// @param[in] the rank on this dimm +/// @param[in] 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<T> > +inline instruction_t<T> mrs_command( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, const uint64_t i_rank, + const uint64_t i_mrs ) +{ + fapi2::buffer<uint64_t> rcd_boilerplate_arr0; + fapi2::buffer<uint64_t> rcd_boilerplate_arr1; + fapi2::buffer<uint8_t> mrs(i_mrs); + + // + // Generic DDR4 MRS setup (RCD is an MRS) + // + mrs_rcd_helper<fapi2::TARGET_TYPE_MCBIST>(rcd_boilerplate_arr0); + + // + // MRS setup + // + // DDR4: Set BG1 to 0. BG0, BA1:BA0 to i_mrs + rcd_boilerplate_arr0.clearBit<TT::ARR0_DDR_BANK_GROUP_1>(); + mss::swizzle<TT::ARR0_DDR_BANK_0_1, 3, 7>(mrs, rcd_boilerplate_arr0); + FAPI_DBG("mrs rcd boiler 0x%llx 0x%llx", uint8_t(mrs), uint64_t(rcd_boilerplate_arr0)); + return instruction_t<T>(i_target, i_rank, rcd_boilerplate_arr0, rcd_boilerplate_arr1); +} + +/// +/// @brief Create, initialize a JEDEC Device Deselect CCS command +/// @tparam T, the fapi2 type of the unit containing the CCS engine +/// @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<T> > +inline instruction_t<T> des_command() +{ + fapi2::buffer<uint64_t> rcd_boilerplate_arr0; + fapi2::buffer<uint64_t> 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<TT::ARR0_DDR_ACTN>(); + + // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS + rcd_boilerplate_arr0.insertFromRight<TT::ARR0_DDR_CKE, TT::ARR0_DDR_CKE_LEN>(0b1111); + + // ACT is high no-care + // RAS, CAS, WE no-care + + // Device Deslect wants CS_n always high (select nothing using rank 0xFF) + return instruction_t<T>(fapi2::Target<fapi2::TARGET_TYPE_DIMM>(), 0xFF, rcd_boilerplate_arr0, rcd_boilerplate_arr1); +} + +/// +/// @brief Create, initialize an instruction which indicates an initial cal +/// @param[in] the rank-pair (rank) to cal +/// @return the initial cal instruction +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline instruction_t<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<T> l_inst = des_command<T>(); + + // ACT is low - per Centaur spec (Shelton to confirm for Nimbus) BRS + l_inst.arr0.template clearBit<TT::ARR0_DDR_ACTN>(); + + l_inst.arr0.template insertFromRight<TT::ARR0_DDR_CAL_TYPE, TT::ARR0_DDR_CAL_TYPE_LEN>(0b1100); + l_inst.arr1.template setBit<TT::ARR1_DDR_CALIBRATION_ENABLE>(); + +#ifdef USE_LOTS_OF_IDLES + // Idles is 0xFFFF - per Centaur spec (Shelton to confirm for Nimbus) BRS + l_inst.arr1.template insertFromRight<TT::ARR1_IDLES, TT::ARR1_IDLES_LEN>(0xFFFF); +#else + l_inst.arr1.template insertFromRight<TT::ARR1_IDLES, TT::ARR1_IDLES_LEN>(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<TT::ARR1_DDR_CAL_RANK, TT::ARR1_DDR_CAL_RANK_LEN>(i_rp); + + return l_inst; +} + +// +// 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 fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the ports +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline fapi2::ReturnCode select_ports( const fapi2::Target<T>& i_target, uint64_t i_ports) +{ + fapi2::buffer<uint64_t> l_data; + fapi2::buffer<uint64_t> l_ports; + + // Not handling multiple ports here, can't do that for CCS. BRS + FAPI_TRY( l_ports.setBit(i_ports) ); + + FAPI_TRY( fapi2::getScom(i_target, TT::MCB_CNTL_REG, l_data) ); + l_data.insert<TT::MCB_CNTL_PORT_SEL, TT::MCB_CNTL_PORT_SEL_LEN>(l_ports); + FAPI_TRY( fapi2::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 fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @param[in] i_value, true iff stop whenever failure occurs. +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline void stop_on_err( const fapi2::Target<T>&, fapi2::buffer<uint64_t>& i_buffer, bool i_value) +{ + i_buffer.writeBit<TT::STOP_ON_ERR>(i_value); +} + +/// +/// @brief Disable ECC checking on the CCS arrays +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline void disable_ecc( const fapi2::Target<T>&, fapi2::buffer<uint64_t>& i_buffer) +{ + i_buffer.setBit<TT::DISABLE_ECC_ARRAY_CHK>(); + i_buffer.setBit<TT::DISABLE_ECC_ARRAY_CORRECTION>(); +} + +/// +/// @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 fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @param[in] i_value, true iff ignore any array ue or sue errors. +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline void ue_disable( const fapi2::Target<T>&, fapi2::buffer<uint64_t>& i_buffer, bool i_value) +{ + i_buffer.writeBit<TT::UE_DISABLE>(i_value); +} + +/// +/// @brief DDr calibration counter +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] 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 +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline void cal_count( const fapi2::Target<T>&, fapi2::buffer<uint64_t>& i_buffer, + const uint64_t i_count, const uint64_t i_mult) +{ + i_buffer.insertFromRight<TT::DDR_CAL_TIMEOUT_CNT, TT::DDR_CAL_TIMEOUT_CNT_LEN>(i_count); + i_buffer.insertFromRight<TT::DDR_CAL_TIMEOUT_CNT_MULT, TT::DDR_CAL_TIMEOUT_CNT_MULT_LEN>(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] the target to effect +/// @param[in] the buffer representing the mode register +/// @param[in] bool, true iff Copy CKE signals to CKE Spare on both ports +/// @note no-op for p9n +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +void copy_cke_to_spare_cke( const fapi2::Target<T>&, fapi2::buffer<uint64_t>& i_buffer, bool i_value); + +/// +/// @brief Read the modeq register appropriate for this target +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @erturn FAPI2_RC_SUCCSS iff ok +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline fapi2::ReturnCode read_mode( const fapi2::Target<T>& i_target, fapi2::buffer<uint64_t>& i_buffer) +{ + FAPI_DBG("read mode 0x%llx", TT::MODEQ_REG); + return fapi2::getScom(i_target, TT::MODEQ_REG, i_buffer); +} + +/// +/// @brief Write the modeq register appropriate for this target +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the buffer representing the mode register +/// @erturn FAPI2_RC_SUCCSS iff ok +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +inline fapi2::ReturnCode write_mode( const fapi2::Target<T>& i_target, const fapi2::buffer<uint64_t>& i_buffer) +{ + return fapi2::putScom(i_target, TT::MODEQ_REG, i_buffer); +} + +/// +/// @brief Execute a set of CCS instructions - multiple ports +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the vector of instructions +/// @param[in] the vector of ports +/// @erturn FAPI2_RC_SUCCSS iff ok +/// +template< fapi2::TargetType T, fapi2::TargetType P, typename TT = ccsTraits<T> > +fapi2::ReturnCode execute( const fapi2::Target<T>& i_target, + ccs::program<T>& i_program, + const std::vector< fapi2::Target<P> >& i_ports ); + +/// +/// @brief Execute a set of CCS instructions - single port +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the vector of instructions +/// @param[in] the port +/// @erturn FAPI2_RC_SUCCSS iff ok +/// +template< fapi2::TargetType T, fapi2::TargetType P, typename TT = ccsTraits<T> > +fapi2::ReturnCode execute( const fapi2::Target<T>& i_target, + ccs::program<T>& i_program, + const fapi2::Target<P>& i_port ) +{ + // Mmm. Might want to find a better way to do this - seems expensive. BRS + std::vector< fapi2::Target<P> > 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 fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] the target to effect +/// @param[in] the MCBIST ccs program - to get the polling parameters +/// @erturn FAPI2_RC_SUCCSS iff ok +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +fapi2::ReturnCode execute_inst_array(const fapi2::Target<T>& i_target, ccs::program<T>& i_program); +/// +/// @brief Start or stop the CCS engine +/// @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<T> > +fapi2::ReturnCode start_stop( const fapi2::Target<T>& i_target, bool i_start_stop ); + +/// +/// @brief Query the status of the CCS engine +/// @param[in] i_target The MCBIST containing the CCS engine +/// @param[out] The query result, first being the result, second the type +/// @return FAPI2_RC_SUCCESS iff success +/// +template< fapi2::TargetType T, typename TT = ccsTraits<T> > +fapi2::ReturnCode status_query( const fapi2::Target<T>& i_target, std::pair<uint64_t, uint64_t>& io_status ); + +} // ends namespace ccs +} + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.C new file mode 100644 index 000000000..7b93ab7ac --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.C @@ -0,0 +1,166 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mrs_load.C +/// @brief Run and manage the MRS_LOAD engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP FW Owner: Bill Hoffa <wghoffa@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "mrs_load.H" +#include "mrs_load_ddr4.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_DIMM; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ +/// +/// @brief Perform the mrs_load operations - TARGET_TYPE_MCBIST specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_MCBIST> +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode mrs_load<TARGET_TYPE_MCBIST>( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + // A vector of CCS instructions. We'll ask the targets to fill it, and then we'll execute it + ccs::program<TARGET_TYPE_MCBIST> l_program; + + for (auto c : i_target.getChildren<TARGET_TYPE_MCS>()) + { + for (auto p : c.getChildren<TARGET_TYPE_MCA>()) + { + for (auto d : p.getChildren<TARGET_TYPE_DIMM>()) + { + FAPI_DBG("mrs load for %s", mss::c_str(d)); + FAPI_TRY( perform_mrs_load(d, l_program.iv_instructions) ); + } + + // We have to configure the CCS engine to let it know which port these instructions are + // going out (or whether it's broadcast ...) so lets execute the instructions we presently + // have so that we kind of do this by port + FAPI_TRY( ccs::execute(i_target, l_program, p) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the mrs_load operations - unknown DIMM case +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to (unused) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_mrs_load<DEFAULT_KIND>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + uint8_t l_type = 0; + uint8_t l_gen = 0; + + FAPI_TRY( mss::eff_dimm_type(i_target, l_type) ); + FAPI_TRY( mss::eff_dram_gen(i_target, l_gen) ); + + // If we're here, we have a problem. The DIMM kind (type and/or generation) wasn't know + // to our dispatcher. We have a DIMM plugged in we don't know how to deal with. + FAPI_ASSERT(false, + fapi2::MSS_UNKNOWN_DIMM() + .set_DIMM_TYPE(l_type) + .set_DRAM_GEN(l_gen) + .set_DIMM_IN_ERROR(i_target), + "Unable to perform mrs load on %s: unknown type (%d) or generation (%d)", + mss::c_str(i_target), l_type, l_gen); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the mrs_load operations - RDIMM DDR4 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_mrs_load<KIND_RDIMM_DDR4>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_DBG("perform mrs_load for %s [expecting rdimm (ddr4)]", mss::c_str(i_target)); + FAPI_TRY( mrs_load_ddr4(i_target, i_inst) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the mrs_load operations - LRDIMM DDR4 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_mrs_load<KIND_LRDIMM_DDR4>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_DBG("perform mrs_load for %s [expecting lrdimm (ddr4)]", mss::c_str(i_target)); + FAPI_TRY( mrs_load_ddr4(i_target, i_inst) ); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Perform the mrs_load operations - start the dispatcher +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_mrs_load<FORCE_DISPATCH>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + uint8_t l_type = 0; + uint8_t l_gen = 0; + + FAPI_TRY( mss::eff_dimm_type(i_target, l_type) ); + FAPI_TRY( mss::eff_dram_gen(i_target, l_gen) ); + + return perform_mrs_load_dispatch<FORCE_DISPATCH>(dimm_kind( l_type, l_gen ), i_target, i_inst); + +fapi_try_exit: + FAPI_ERR("couldn't get dimm type, dram gen: %s", mss::c_str(i_target)); + return fapi2::current_err; +} + +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.H new file mode 100644 index 000000000..2f2f176d3 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.H @@ -0,0 +1,184 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/mrs_load.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mrs_load.H +/// @brief Code to support mrs_loads +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_MRS_LOAD_H_ +#define _MSS_MRS_LOAD_H_ + +#include <fapi2.H> +#include "../utils/c_str.H" +#include <p9_mc_scom_addresses.H> + +#include "../shared/mss_kind.H" + +namespace mss +{ + +/// +/// @brief A structure to represent an MRS operation +/// @tparam T, the target type of the CCS engine chiplet +/// +template< fapi2::TargetType T > +struct mrs_data +{ + // Which MRS# this is + fapi2::buffer<uint8_t> iv_mrs; + + // The attribute getter. For MRS we pass in the ARR0 of the CCS instruction + // as that allows us to encapsulate the attribute processing and the bit + // manipulation in one function. + fapi2::ReturnCode (*iv_func)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>&, ccs::instruction_t<T>&, const uint64_t); + fapi2::ReturnCode (*iv_dumper)(const ccs::instruction_t<T>&, const uint64_t); + + // The delay needed after this MRS word is written + uint64_t iv_delay; + + mrs_data( uint64_t i_mrs, + fapi2::ReturnCode (*i_func)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>&, ccs::instruction_t<T>&, const uint64_t), + fapi2::ReturnCode (*i_dumper)(const ccs::instruction_t<T>&, const uint64_t), + uint64_t i_delay ): + iv_mrs(i_mrs), + iv_func(i_func), + iv_dumper(i_dumper), + iv_delay(i_delay) + {} +}; + +/// +/// @brief Perform the mrs_load operations +/// @tparam T, the fapi2::TargetType of i_target +/// @param[in] i_target, a fapi2::Target +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode mrs_load( const fapi2::Target<T>& i_target ); + + +// +// Implement the polymorphism for mrs_load +// + +// -# Register the API. +/// -# Define the template parameters for the overloaded function +/// @note the first argument is the api name, and the rest are the api's template parameters. +/// @note this creates __api_name##_overload + +template< mss::kind_t K > +struct perform_mrs_load_overload +{ + static const bool available = false; +}; + +/// -# Register the specific overloads. The first parameter is the name +/// of the api, the second is the kind of the element which is being +/// overloaded - an RDIMM, an LRDIMM, etc. The remaining parameters +/// indicate the specialization of the api itself. +/// @note You need to define the "DEFAULT_KIND" here as an overload. This +/// allows the mechanism to find the "base" implementation for things which +/// have no specific overload. +template<> +struct perform_mrs_load_overload< DEFAULT_KIND > +{ + static const bool available = true; +}; + +template<> +struct perform_mrs_load_overload< KIND_RDIMM_DDR4 > +{ + static const bool available = true; +}; + +template<> +struct perform_mrs_load_overload< KIND_LRDIMM_DDR4 > +{ + static const bool available = true; +}; + +/// +/// -# Define the default case for overloaded calls. enable_if states that +/// if there is a DEFAULT_KIND overload for this TargetType, then this +/// entry point will be defined. Note the general case below is enabled if +/// there is no overload defined for this TargetType +/// + +/// +/// @brief Perform the mrs_load operations +/// @tparam K, the kind of DIMM we're operating on (derived) +/// @param[in] i_target, a fapi2::Target<fapi2::TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::kind_t K = FORCE_DISPATCH > +typename std::enable_if< perform_mrs_load_overload<DEFAULT_KIND>::available, fapi2::ReturnCode>::type +perform_mrs_load( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +// +// We know we registered overloads for perform_mrs_load, so we need the entry point to +// the dispatcher. Add a set of these for all TargetTypes which get overloads +// for this API +// +template<> +fapi2::ReturnCode perform_mrs_load<FORCE_DISPATCH>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +template<> +fapi2::ReturnCode perform_mrs_load<DEFAULT_KIND>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +// +// Boilerplate dispatcher +// +template< kind_t K, bool B = perform_mrs_load_overload<K>::available > +inline fapi2::ReturnCode perform_mrs_load_dispatch( const kind_t& i_kind, + const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst) +{ + // We dispatch to another kind if: + // We don't have an overload defined (B == false) + // Or, if we do have an overload (B == true) and this is not out kind. + if ((B == false) || ((B == true) && (K != i_kind))) + { + return perform_mrs_load_dispatch < (kind_t)(K - 1) > (i_kind, i_target, i_inst); + } + + // Otherwise, we call the overload. + return perform_mrs_load<K>(i_target, i_inst); +} + +// DEFAULT_KIND is 0 so this is the end of the recursion +template<> +inline fapi2::ReturnCode perform_mrs_load_dispatch<DEFAULT_KIND>(const kind_t&, + const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst) +{ + return perform_mrs_load<DEFAULT_KIND>(i_target, i_inst); +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.C new file mode 100644 index 000000000..94d14cb96 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.C @@ -0,0 +1,844 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mrs_load_ddr4.C +/// @brief Run and manage the DDR4 mrs loading +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP FW Owner: Bill Hoffa <wghoffa@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "mrs_load_ddr4.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_DIMM; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ + +// +// Each MRS has it's attributes encapsulated in it's little setter function. +// + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs00 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs00(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // Map from Write Recovery attribute value to bits in the MRS. + // Bit 4 is A13, bits 5:7 are A11:A9 + static const uint8_t wr_map[27] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0b0000, 0, 0b0001, 0, 0b0001, 0, 0b0011, + 0, 0b0100, 0, 0b0101, 0, 0b0111, 0, 0b0110, 0, 0b1000 + }; + + // Map from the CAS Latency attribute to the bits in the MRS + static const uint8_t cl_map[34] = + { + // 0 1 2 3 4 5 6 7 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 9 10 11 12 13 14 15 16 + 0b00000, 0b00001, 0b00010, 0b00011, 0b00100, 0b00101, 0b00110, 0b00111, + // 17, 18 19 20 21 22 23 24 + 0b01101, 0b01000, 0b01110, 0b01001, 0b01111, 0b01010, 0b01100, 0b01011, + // 25 26 27 28 29 30 31 32 33 + 0b10000, 0b10001, 0b10010, 0b10011, 0b10100, 0b10101, 0b10110, 0b10111, 0b11000 + }; + + uint8_t l_burst_length = 0; + uint8_t l_read_burst_type = 0; + uint8_t l_dll_reset = 0; + uint8_t l_test_mode = 0; + uint8_t l_write_recovery = 0; + uint64_t l_cas_latency = 0; + + fapi2::buffer<uint8_t> l_cl; + fapi2::buffer<uint8_t> l_wr; + + FAPI_TRY( mss::eff_dram_burst_length(i_target, l_burst_length) ); + FAPI_TRY( mss::eff_dram_read_burst_type(i_target, l_read_burst_type) ); + FAPI_TRY( mss::eff_dram_cas_latency(i_target, l_cas_latency) ); + FAPI_TRY( mss::eff_dram_dll_reset(i_target, l_dll_reset) ); + FAPI_TRY( mss::eff_dram_tm(i_target, l_test_mode) ); + FAPI_TRY( mss::eff_dram_write_recovery(i_target, l_write_recovery) ); + + FAPI_DBG("MR0 Attributes: BL: 0x%x, RBT: 0x%x, CL: 0x%x(0x%x), TM: 0x%x, DLL_RESET: 0x%x, WR: 0x%x(0x%x)", + l_burst_length, l_read_burst_type, l_cas_latency, cl_map[l_cas_latency], + l_test_mode, l_dll_reset, l_write_recovery, wr_map[l_write_recovery]); + + io_inst.arr0.insertFromRight<A0, 2>(l_burst_length); + io_inst.arr0.writeBit<A3>(l_read_burst_type); + io_inst.arr0.writeBit<A7>(l_test_mode); + io_inst.arr0.writeBit<A8>(l_dll_reset); + + // CAS Latency takes a little effort - the bits aren't contiguous + l_cl = cl_map[l_cas_latency]; + io_inst.arr0.writeBit<A12>(l_cl.getBit<3>()); + io_inst.arr0.writeBit<A6>(l_cl.getBit<4>()); + io_inst.arr0.writeBit<A5>(l_cl.getBit<5>()); + io_inst.arr0.writeBit<A4>(l_cl.getBit<6>()); + io_inst.arr0.writeBit<A2>(l_cl.getBit<7>()); + + // Write Recovery/Read to Precharge is not contiguous either. + l_wr = wr_map[l_write_recovery]; + io_inst.arr0.writeBit<A13>(l_wr.getBit<4>()); + io_inst.arr0.writeBit<A11>(l_wr.getBit<5>()); + io_inst.arr0.writeBit<A10>(l_wr.getBit<6>()); + io_inst.arr0.writeBit<A9>(l_wr.getBit<7>()); + + FAPI_DBG("MR0: 0x%016llx", uint64_t(io_inst.arr0)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS0, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs00_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + static const uint8_t wr_map[9] = { 10, 12, 14, 16, 18, 20, 24, 22, 26 }; + + uint8_t l_burst_length = 0; + uint8_t l_read_burst_type = 0; + uint8_t l_dll_reset = 0; + uint8_t l_test_mode = 0; + + fapi2::buffer<uint8_t> l_wr_index; + fapi2::buffer<uint8_t> l_cas_latency; + + i_inst.arr0.extractToRight<A0, 2>(l_burst_length); + l_read_burst_type = i_inst.arr0.getBit<A3>(); + l_test_mode = i_inst.arr0.getBit<A7>(); + l_dll_reset = i_inst.arr0.getBit<A8>(); + + // CAS Latency takes a little effort - the bits aren't contiguous + l_cas_latency.writeBit<3>(i_inst.arr0.getBit<A12>()); + l_cas_latency.writeBit<4>(i_inst.arr0.getBit<A6>()); + l_cas_latency.writeBit<5>(i_inst.arr0.getBit<A5>()); + l_cas_latency.writeBit<6>(i_inst.arr0.getBit<A4>()); + l_cas_latency.writeBit<7>(i_inst.arr0.getBit<A2>()); + + // Write Recovery/Read to Precharge is not contiguous either. + l_wr_index.writeBit<4>(i_inst.arr0.getBit<A13>()); + l_wr_index.writeBit<5>(i_inst.arr0.getBit<A11>()); + l_wr_index.writeBit<6>(i_inst.arr0.getBit<A10>()); + l_wr_index.writeBit<7>(i_inst.arr0.getBit<A9>()); + + FAPI_DBG("MR0 Decode BL: 0x%x, RBT: 0x%x, CL: 0x%x, TM: 0x%x, DLL_RESET: 0x%x, WR: (0x%x)0x%x", + l_burst_length, l_read_burst_type, uint8_t(l_cas_latency), l_test_mode, l_dll_reset, + wr_map[uint8_t(l_wr_index)], uint8_t(l_wr_index)); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs01 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs01(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // Little table to map Output Driver Imepdance Control. 34Ohm is index 0, + // 48Ohm is index 1 and we expect eff_config to make sure there's nothing + // else used here. + // Left bit is A2, right bit is A1 + static const uint8_t odic_map[2] = { 0b00, 0b01 }; + + // Indexed by denominator. So, if RQZ is 240, and you have OHM240, then you're looking + // for index 1. So this doesn't correspond directly with the table in the JEDEC spec, + // as that's not in "denominator order." + // 0 RQZ/1 RQZ/2 RQZ/3 RQZ/4 RQZ/5 RQZ/6 RQZ/7 + static const uint8_t rtt_nom_map[8] = { 0, 0b100, 0b010, 0b110, 0b001, 0b101, 0b011, 0b111 }; + + uint8_t l_dll_enable = 0; + uint8_t l_odic = 0; + uint8_t l_wl_enable = 0; + uint8_t l_tdqs = 0; + uint8_t l_qoff = 0; + uint8_t l_rtt_nom[MAX_RANK_PER_DIMM] = {0}; + + size_t l_rtt_nom_index = 0; + + fapi2::buffer<uint8_t> l_additive_latency; + fapi2::buffer<uint8_t> l_odic_buffer; + fapi2::buffer<uint8_t> l_rtt_nom_buffer; + + FAPI_TRY( mss::eff_dram_dll_enable(i_target, l_dll_enable) ); + FAPI_TRY( mss::eff_dram_ron(i_target, l_odic) ); + FAPI_TRY( mss::eff_dram_al(i_target, l_additive_latency) ); + FAPI_TRY( mss::eff_dram_wr_lvl_enable(i_target, l_wl_enable) ); + FAPI_TRY( mss::eff_dram_rtt_nom(i_target, &(l_rtt_nom[0])) ); + FAPI_TRY( mss::eff_dram_tdqs(i_target, l_tdqs) ); + FAPI_TRY( mss::eff_dram_output_buffer(i_target, l_qoff) ); + + // Map from impedance to bits in MRS1 + l_odic_buffer = (l_odic == fapi2::ENUM_ATTR_EFF_DRAM_RON_OHM34) ? odic_map[0] : odic_map[1]; + + // We have to be careful about 0 + l_rtt_nom_index = (l_rtt_nom[mss::index(i_rank)] == 0) ? + 0 : fapi2::ENUM_ATTR_EFF_DRAM_RTT_NOM_OHM240 / l_rtt_nom[mss::index(i_rank)]; + + // Map from RTT_NOM array to the value in the map + l_rtt_nom_buffer = rtt_nom_map[l_rtt_nom_index]; + + FAPI_INF("MR1 rank %d attributes: DLL_ENABLE: 0x%x, ODIC: 0x%x(0x%x), AL: 0x%x, WLE: 0x%x, " + "RTT_NOM: 0x%x(0x%x), TDQS: 0x%x, QOFF: 0x%x", i_rank, + l_dll_enable, l_odic, uint8_t(l_odic_buffer), uint8_t(l_additive_latency), l_wl_enable, + l_rtt_nom[mss::index(i_rank)], uint8_t(l_rtt_nom_buffer), l_tdqs, l_qoff); + + io_inst.arr0.writeBit<A0>(l_dll_enable); + mss::swizzle<A1, 2, 7>(l_odic_buffer, io_inst.arr0); + mss::swizzle<A3, 2, 7>(l_additive_latency, io_inst.arr0); + io_inst.arr0.writeBit<A7>(l_wl_enable); + mss::swizzle<A8, 3, 7>(l_rtt_nom_buffer, io_inst.arr0); + io_inst.arr0.writeBit<A11>(l_tdqs); + io_inst.arr0.writeBit<A12>(l_qoff); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS1, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs01_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + fapi2::buffer<uint8_t> l_odic; + fapi2::buffer<uint8_t> l_additive_latency; + fapi2::buffer<uint8_t> l_rtt_nom; + + uint8_t l_dll_enable = i_inst.arr0.getBit<A0>(); + uint8_t l_wrl_enable = i_inst.arr0.getBit<A7>(); + uint8_t l_tdqs = i_inst.arr0.getBit<A11>(); + uint8_t l_qoff = i_inst.arr0.getBit<A12>(); + + mss::swizzle<6, 2, A2>(i_inst.arr0, l_odic); + mss::swizzle<6, 2, A4>(i_inst.arr0, l_additive_latency); + mss::swizzle<5, 3, A10>(i_inst.arr0, l_rtt_nom); + + FAPI_INF("MR1 rank %d decode: DLL_ENABLE: 0x%x, ODIC: 0x%x, AL: 0x%x, WLE: 0x%x, " + "RTT_NOM: 0x%x, TDQS: 0x%x, QOFF: 0x%x", i_rank, + l_dll_enable, uint8_t(l_odic), uint8_t(l_additive_latency), l_wrl_enable, uint8_t(l_rtt_nom), + l_tdqs, l_qoff); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Given a uint32_t, which contains address bits with an encoded MRS1, +/// decode and trace the contents +/// @param[in] i_value, ADR 17:0 +/// @return void +/// +fapi2::ReturnCode ddr4_mrs01_decode(const uint32_t i_value) +{ + fapi2::buffer<uint32_t> l_data(i_value); + // Flip l_data so bit 0 is bit 0, bit 17 is bit 17 ... + reverse(l_data); + + fapi2::buffer<uint8_t> l_odic; + fapi2::buffer<uint8_t> l_additive_latency; + fapi2::buffer<uint8_t> l_rtt_nom; + + uint8_t l_dll_enable = l_data.getBit<A0>(); + uint8_t l_wrl_enable = l_data.getBit<A7>(); + uint8_t l_tdqs = l_data.getBit<A11>(); + uint8_t l_qoff = l_data.getBit<A12>(); + + mss::swizzle<6, 2, A2>(l_data, l_odic); + mss::swizzle<6, 2, A4>(l_data, l_additive_latency); + mss::swizzle<5, 3, A10>(l_data, l_rtt_nom); + + FAPI_INF("MR1 buffer decode: DLL_ENABLE: 0x%x, ODIC: 0x%x, AL: 0x%x, WLE: 0x%x, " + "RTT_NOM: 0x%x, TDQS: 0x%x, QOFF: 0x%x", + l_dll_enable, uint8_t(l_odic), uint8_t(l_additive_latency), l_wrl_enable, uint8_t(l_rtt_nom), + l_tdqs, l_qoff); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs02 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs02(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // Index this by subtracting 9 from the CWL attribute value. + static const uint64_t LOWEST_CWL = 9; + // 9 10 11 12 14 16 18 20 + static const uint8_t cwl_map[12] = { 0b000, 0b001, 0b010, 0b011, 0, 0b100, 0, 0b101, 0, 0b110, 0, 0b111 }; + + fapi2::buffer<uint8_t> l_lpasr; + uint8_t l_cwl = 0; + uint8_t l_dram_rtt_wr[MAX_RANK_PER_DIMM] = {0}; + uint8_t l_write_crc = 0; + + fapi2::buffer<uint8_t> l_cwl_buffer; + fapi2::buffer<uint8_t> l_rtt_wr_buffer; + + FAPI_TRY( mss::eff_dram_lpasr(i_target, l_lpasr) ); + FAPI_TRY( mss::eff_dram_cwl(i_target, l_cwl) ); + FAPI_TRY( mss::eff_dram_rtt_wr(i_target, &(l_dram_rtt_wr[0])) ); + FAPI_TRY( mss::eff_write_crc(i_target, l_write_crc) ); + + l_cwl_buffer = cwl_map[l_cwl - LOWEST_CWL]; + + // Arg. Change this. BRS + switch (l_dram_rtt_wr[i_rank]) + { + case fapi2::ENUM_ATTR_EFF_DRAM_RTT_WR_DISABLE: + l_rtt_wr_buffer = 0b000; + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_RTT_WR_HIGHZ: + l_rtt_wr_buffer = 0b011; + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_RTT_WR_OHM240: + l_rtt_wr_buffer = 0b010; + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_RTT_WR_OHM120: + l_rtt_wr_buffer = 0b001; + break; + + case fapi2::ENUM_ATTR_EFF_DRAM_RTT_WR_OHM60: + l_rtt_wr_buffer = 0b100; + break; + + default: + FAPI_ERR("unknown RTT_WR 0x%x (%s rank %d), dynamic odt off", + l_dram_rtt_wr[i_rank], mss::c_str(i_target), i_rank); + l_rtt_wr_buffer = 0b000; + break; + }; + + FAPI_INF("MR2 rank %d attributes: LPASR: 0x%x, CWL: 0x%x(0x%x), RTT_WR: 0x%x(0x%x), WRITE_CRC: 0x%x", i_rank, + uint8_t(l_lpasr), l_cwl, uint8_t(l_cwl_buffer), + l_dram_rtt_wr[i_rank], uint8_t(l_rtt_wr_buffer), l_write_crc); + + mss::swizzle<A3, 3, 7>(l_cwl_buffer, io_inst.arr0); + + mss::swizzle<A6, 2, 7>(l_lpasr, io_inst.arr0); + + mss::swizzle<A9, 3, 7>(l_rtt_wr_buffer, io_inst.arr0); + + io_inst.arr0.writeBit<A12>(l_write_crc); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS2, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs02_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + fapi2::buffer<uint8_t> l_lpasr; + fapi2::buffer<uint8_t> l_cwl; + fapi2::buffer<uint8_t> l_rtt_wr; + + uint8_t l_write_crc = i_inst.arr0.getBit<A12>(); + mss::swizzle<5, 3, A5>(i_inst.arr0, l_cwl); + mss::swizzle<6, 2, A7>(i_inst.arr0, l_lpasr); + mss::swizzle<5, 3, A11>(i_inst.arr0, l_rtt_wr); + + FAPI_INF("MR2 rank %d deocode: LPASR: 0x%x, CWL: 0x%x, RTT_WR: 0x%x, WRITE_CRC: 0x%x", i_rank, + uint8_t(l_lpasr), uint8_t(l_cwl), uint8_t(l_rtt_wr), l_write_crc); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs03 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs03(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // 4 5 6 R + static const uint8_t crc_wr_latency_map[8] = { 0, 0, 0, 0, 0, 1, 2, 3 }; + + uint8_t l_mpr_page = 0; + uint8_t l_geardown = 0; + uint8_t l_pda = 0; + uint8_t l_crc_wr_latency = 0; + uint8_t l_temp_readout = 0; + + fapi2::buffer<uint8_t> l_mpr_mode; + fapi2::buffer<uint8_t> l_fine_refresh; + fapi2::buffer<uint8_t> l_crc_wr_latency_buffer; + fapi2::buffer<uint8_t> l_read_format; + + FAPI_TRY( mss::eff_mpr_mode(i_target, l_mpr_mode) ); + FAPI_TRY( mss::eff_mpr_page(i_target, l_mpr_page) ); + FAPI_TRY( mss::eff_geardown_mode(i_target, l_geardown) ); + FAPI_TRY( mss::eff_per_dram_access(i_target, l_pda) ); + FAPI_TRY( mss::eff_temp_readout(i_target, l_temp_readout) ); + FAPI_TRY( mss::eff_fine_refresh_mode(i_target, l_fine_refresh) ); + FAPI_TRY( mss::eff_crc_wr_latency(i_target, l_crc_wr_latency) ); + FAPI_TRY( mss::eff_mpr_rd_format(i_target, l_read_format) ); + + l_crc_wr_latency_buffer = crc_wr_latency_map[l_crc_wr_latency]; + + FAPI_INF("MR3 rank %d attributes: MPR_MODE: 0x%x, MPR_PAGE: 0x%x, GD: 0x%x, PDA: 0x%x, " + "TEMP: 0x%x FR: 0x%x, CRC_WL: 0x%x(0x%x), RF: 0x%x", i_rank, + uint8_t(l_mpr_mode), l_mpr_page, l_geardown, l_pda, l_temp_readout, + uint8_t(l_fine_refresh), l_crc_wr_latency, uint8_t(l_crc_wr_latency_buffer), + uint8_t(l_read_format)); + + mss::swizzle<A0, 2, 7>(l_mpr_mode, io_inst.arr0); + io_inst.arr0.writeBit<A2>(l_mpr_page); + io_inst.arr0.writeBit<A3>(l_geardown); + io_inst.arr0.writeBit<A4>(l_pda); + io_inst.arr0.writeBit<A5>(l_temp_readout); + + mss::swizzle<A6 , 3, 7>(l_fine_refresh, io_inst.arr0); + mss::swizzle<A9 , 2, 7>(l_crc_wr_latency_buffer, io_inst.arr0); + mss::swizzle<A11, 2, 7>(l_read_format, io_inst.arr0); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS3, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs03_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + fapi2::buffer<uint8_t> l_mpr_mode; + + fapi2::buffer<uint8_t> l_fine_refresh; + fapi2::buffer<uint8_t> l_crc_wr_latency_buffer; + fapi2::buffer<uint8_t> l_read_format; + + uint8_t l_mpr_page = i_inst.arr0.getBit<A2>(); + uint8_t l_geardown = i_inst.arr0.getBit<A3>(); + uint8_t l_pda = i_inst.arr0.getBit<A4>(); + uint8_t l_temp_readout = i_inst.arr0.getBit<A5>(); + + mss::swizzle<6, 2, A1>(i_inst.arr0, l_mpr_mode); + mss::swizzle<5, 3, A7>(i_inst.arr0, l_fine_refresh); + mss::swizzle<6, 2, A10>(i_inst.arr0, l_crc_wr_latency_buffer); + mss::swizzle<6, 2, A12>(i_inst.arr0, l_read_format); + + FAPI_INF("MR3 rank %d decode: MPR_MODE: 0x%x, MPR_PAGE: 0x%x, GD: 0x%x, PDA: 0x%x, " + "TEMP: 0x%x FR: 0x%x, CRC_WL: 0x%x, RF: 0x%x", i_rank, + uint8_t(l_mpr_mode), l_mpr_page, l_geardown, l_pda, uint8_t(l_temp_readout), + uint8_t(l_fine_refresh), uint8_t(l_crc_wr_latency_buffer), uint8_t(l_read_format)); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs04 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs04(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // 0 3 4 5 6 8 + static const uint8_t cs_cmd_latency_map[9] = { 0b000, 0, 0, 0b001, 0b010, 0b011, 0b100, 0, 0b101 }; + + uint8_t l_max_pd_mode = 0; + uint8_t l_temp_ref_range = 0; + uint8_t l_temp_ref_mode = 0; + uint8_t l_vref_mon = 0; + uint8_t l_cs_cmd_latency = 0; + uint8_t l_ref_abort = 0; + uint8_t l_rd_pre_train_mode = 0; + uint8_t l_rd_preamble = 0; + uint8_t l_wr_preamble = 0; + uint8_t l_ppr = 0; + + fapi2::buffer<uint8_t> l_cs_cmd_latency_buffer; + + FAPI_TRY( mss::eff_max_powerdown_mode(i_target, l_max_pd_mode) ); + FAPI_TRY( mss::eff_temp_ref_range(i_target, l_temp_ref_range) ); + FAPI_TRY( mss::eff_temp_ref_mode(i_target, l_temp_ref_mode) ); + FAPI_TRY( mss::eff_int_vref_mon(i_target, l_vref_mon) ); + FAPI_TRY( mss::eff_cs_cmd_latency(i_target, l_cs_cmd_latency) ); + FAPI_TRY( mss::eff_self_ref_abort(i_target, l_ref_abort) ); + FAPI_TRY( mss::eff_rd_preamble_train(i_target, l_rd_pre_train_mode) ); + FAPI_TRY( mss::eff_rd_preamble(i_target, l_rd_preamble) ); + FAPI_TRY( mss::eff_wr_preamble(i_target, l_wr_preamble) ); + FAPI_TRY( mss::eff_dram_ppr(i_target, l_ppr) ); + + l_cs_cmd_latency_buffer = cs_cmd_latency_map[l_cs_cmd_latency]; + + FAPI_INF("MR4 rank %d attributes: MAX_PD: 0x%x, TEMP_REF_RANGE: 0x%x, TEMP_REF_MODE: 0x%x " + "VREF_MON: 0x%x, CSL: 0x%x(0x%x), REF_ABORT: 0x%x, RD_PTM: 0x%x, RD_PRE: 0x%x, " + "WR_PRE: 0x%x, PPR: 0x%x", i_rank, + l_max_pd_mode, l_temp_ref_range, l_temp_ref_mode, l_vref_mon, + l_cs_cmd_latency, uint8_t(l_cs_cmd_latency_buffer), l_ref_abort, + l_rd_pre_train_mode, l_rd_preamble, l_wr_preamble, l_ppr); + + io_inst.arr0.writeBit<A1>(l_max_pd_mode); + io_inst.arr0.writeBit<A2>(l_temp_ref_range); + io_inst.arr0.writeBit<A3>(l_temp_ref_mode); + io_inst.arr0.writeBit<A4>(l_vref_mon); + + mss::swizzle<A6, 3, 7>(l_cs_cmd_latency_buffer, io_inst.arr0); + io_inst.arr0.writeBit<A9>(l_ref_abort); + io_inst.arr0.writeBit<A10>(l_rd_pre_train_mode); + io_inst.arr0.writeBit<A11>(l_rd_preamble); + io_inst.arr0.writeBit<A12>(l_wr_preamble); + io_inst.arr0.writeBit<A13>(l_ppr); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS4, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs04_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + uint8_t l_max_pd_mode = i_inst.arr0.getBit<A1>(); + uint8_t l_temp_ref_range = i_inst.arr0.getBit<A2>(); + uint8_t l_temp_ref_mode = i_inst.arr0.getBit<A3>(); + uint8_t l_vref_mon = i_inst.arr0.getBit<A4>(); + + fapi2::buffer<uint8_t> l_cs_cmd_latency_buffer; + mss::swizzle<5, 3, A8>(i_inst.arr0, l_cs_cmd_latency_buffer); + + uint8_t l_ref_abort = i_inst.arr0.getBit<A9>(); + uint8_t l_rd_pre_train_mode = i_inst.arr0.getBit<A10>(); + uint8_t l_rd_preamble = i_inst.arr0.getBit<A11>(); + uint8_t l_wr_preamble = i_inst.arr0.getBit<A12>(); + uint8_t l_ppr = i_inst.arr0.getBit<A13>(); + + FAPI_INF("MR4 rank %d decode: MAX_PD: 0x%x, TEMP_REF_RANGE: 0x%x, TEMP_REF_MODE: 0x%x " + "VREF_MON: 0x%x, CSL: 0x%x, REF_ABORT: 0x%x, RD_PTM: 0x%x, RD_PRE: 0x%x, " + "WR_PRE: 0x%x, PPR: 0x%x", i_rank, + l_max_pd_mode, l_temp_ref_range, l_temp_ref_mode, l_vref_mon, + uint8_t(l_cs_cmd_latency_buffer), l_ref_abort, + l_rd_pre_train_mode, l_rd_preamble, l_wr_preamble, l_ppr); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs05 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs05(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // 0 4 5 6 8 + static const uint8_t ca_parity_latency_map[9] = { 0b000, 0, 0, 0, 0b001, 0b010, 0b011, 0, 0b100 }; + + // Indexed by denominator. So, if RQZ is 240, and you have OHM240, then you're looking + // for index 1. So this doesn't correspond directly with the table in the JEDEC spec, + // as that's not in "denominator order." + // 0 RQZ/1 RQZ/2 RQZ/3 RQZ/4 RQZ/5 RQZ/6 RQZ/7 + static const uint8_t rtt_park_map[8] = { 0, 0b100, 0b010, 0b110, 0b001, 0b101, 0b011, 0b111 }; + + uint8_t l_ca_parity_latency = 0; + uint8_t l_crc_error_clear = 0; + uint8_t l_ca_parity_error_status = 0; + uint8_t l_odt_input_buffer = 0; + uint8_t l_rtt_park[MAX_RANK_PER_DIMM] = {0}; + uint8_t l_ca_parity = 0; + uint8_t l_data_mask = 0; + uint8_t l_write_dbi = 0; + uint8_t l_read_dbi = 0; + + uint8_t l_rtt_park_index = 0; + + fapi2::buffer<uint8_t> l_ca_parity_latency_buffer; + fapi2::buffer<uint8_t> l_rtt_park_buffer; + + FAPI_TRY( mss::eff_ca_parity_latency(i_target, l_ca_parity_latency) ); + FAPI_TRY( mss::eff_crc_error_clear(i_target, l_crc_error_clear) ); + FAPI_TRY( mss::eff_ca_parity_error_status(i_target, l_ca_parity_error_status) ); + FAPI_TRY( mss::eff_odt_input_buff(i_target, l_odt_input_buffer) ); + + FAPI_TRY( mss::eff_rtt_park(i_target, &(l_rtt_park[0])) ); + + FAPI_TRY( mss::eff_ca_parity(i_target, l_ca_parity) ); + FAPI_TRY( mss::eff_data_mask(i_target, l_data_mask) ); + FAPI_TRY( mss::eff_write_dbi(i_target, l_write_dbi) ); + FAPI_TRY( mss::eff_read_dbi(i_target, l_read_dbi) ); + + l_ca_parity_latency_buffer = ca_parity_latency_map[l_ca_parity_latency]; + + // We have to be careful about 0 + l_rtt_park_index = (l_rtt_park[mss::index(i_rank)] == 0) ? + 0 : fapi2::ENUM_ATTR_EFF_RTT_PARK_240OHM / l_rtt_park[mss::index(i_rank)]; + + // Map from RTT_NOM array to the value in the map + l_rtt_park_buffer = rtt_park_map[l_rtt_park_index]; + + FAPI_INF("MR5 rank %d attributes: CAPL: 0x%x(0x%x), CRC_EC: 0x%x, CA_PES: 0x%x, ODT_IB: 0x%x " + "RTT_PARK: 0x%x(0x%x), CAP: 0x%x, DM: 0x%x, WDBI: 0x%x, RDBI: 0x%x", i_rank, + l_ca_parity_latency, uint8_t(l_ca_parity_latency_buffer), l_crc_error_clear, + l_ca_parity_error_status, l_odt_input_buffer, + l_rtt_park[mss::index(i_rank)], uint8_t(l_rtt_park_buffer), l_ca_parity, + l_data_mask, l_write_dbi, l_read_dbi); + + mss::swizzle<A0, 3, 7>(l_ca_parity_latency_buffer, io_inst.arr0); + io_inst.arr0.writeBit<A3>(l_crc_error_clear); + io_inst.arr0.writeBit<A4>(l_ca_parity_error_status); + io_inst.arr0.writeBit<A5>(l_odt_input_buffer); + mss::swizzle<A6, 3, 7>(l_rtt_park_buffer, io_inst.arr0); + io_inst.arr0.writeBit<A9>(l_ca_parity); + io_inst.arr0.writeBit<A10>(l_data_mask); + io_inst.arr0.writeBit<A11>(l_write_dbi); + io_inst.arr0.writeBit<A12>(l_read_dbi); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS5, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs05_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + fapi2::buffer<uint8_t> l_ca_parity_latency_buffer; + fapi2::buffer<uint8_t> l_rtt_park_buffer; + + mss::swizzle<5, 3, A2>(i_inst.arr0, l_ca_parity_latency_buffer); + mss::swizzle<5, 3, A8>(i_inst.arr0, l_rtt_park_buffer); + + uint8_t l_crc_error_clear = i_inst.arr0.getBit<A3>(); + uint8_t l_ca_parity_error_status = i_inst.arr0.getBit<A4>(); + uint8_t l_odt_input_buffer = i_inst.arr0.getBit<A5>(); + + uint8_t l_ca_parity = i_inst.arr0.getBit<A9>(); + uint8_t l_data_mask = i_inst.arr0.getBit<A10>(); + uint8_t l_write_dbi = i_inst.arr0.getBit<A11>(); + uint8_t l_read_dbi = i_inst.arr0.getBit<A12>(); + + FAPI_INF("MR5 rank %d decode: CAPL: 0x%x, CRC_EC: 0x%x, CA_PES: 0x%x, ODT_IB: 0x%x " + "RTT_PARK: 0x%x, CAP: 0x%x, DM: 0x%x, WDBI: 0x%x, RDBI: 0x%x", i_rank, + uint8_t(l_ca_parity_latency_buffer), l_crc_error_clear, l_ca_parity_error_status, + l_odt_input_buffer, uint8_t(l_rtt_park_buffer), l_ca_parity, l_data_mask, + l_write_dbi, l_read_dbi); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Configure the ARR0 of the CCS isntruction for mrs06 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in,out] the instruction to fixup +/// @param[in] ths rank in question +/// @return FAPI2_RC_SUCCESS iff OK +/// +static fapi2::ReturnCode ddr4_mrs06(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst, + const uint64_t i_rank) +{ + // 4 5 6 7 8 + static const uint8_t tccd_l_map[9] = { 0, 0, 0, 0, 0b000, 0b001, 0b010, 0b011, 0b100 }; + + uint8_t l_vrefdq_train_value[MAX_RANK_PER_DIMM] = {0}; + uint8_t l_vrefdq_train_range[MAX_RANK_PER_DIMM] = {0}; + uint8_t l_vrefdq_train_enable[MAX_RANK_PER_DIMM] = {0}; + uint8_t l_tccd_l = 0; + + fapi2::buffer<uint8_t> l_tccd_l_buffer; + fapi2::buffer<uint8_t> l_vrefdq_train_value_buffer; + + FAPI_TRY( mss::vref_dq_train_value(i_target, l_vrefdq_train_value) ); + FAPI_TRY( mss::vref_dq_train_range(i_target, l_vrefdq_train_range) ); + FAPI_TRY( mss::vref_dq_train_enable(i_target, l_vrefdq_train_enable) ); + FAPI_TRY( mss::tccd_l(i_target, l_tccd_l) ); + + l_tccd_l_buffer = tccd_l_map[l_tccd_l]; + l_vrefdq_train_value_buffer = l_vrefdq_train_value[mss::index(i_rank)]; + + FAPI_INF("MR6 rank %d attributes: TRAIN_V: 0x%x(0x%x), TRAIN_R: 0x%x, TRAIN_E: 0x%x, TCCD_L: 0x%x(0x%x)", i_rank, + l_vrefdq_train_value[mss::index(i_rank)], uint8_t(l_vrefdq_train_value_buffer), + l_vrefdq_train_range[mss::index(i_rank)], + l_vrefdq_train_enable[mss::index(i_rank)], l_tccd_l, uint8_t(l_tccd_l_buffer)); + + mss::swizzle<A0, 6, 7>(l_vrefdq_train_value_buffer, io_inst.arr0); + io_inst.arr0.writeBit<A6>(l_vrefdq_train_range[mss::index(i_rank)]); + io_inst.arr0.writeBit<A7>(l_vrefdq_train_enable[mss::index(i_rank)]); + mss::swizzle<A10, 3, 7>(l_tccd_l_buffer, io_inst.arr0); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Given a CCS instruction which contains address bits with an encoded MRS6, +/// decode and trace the contents +/// @param[in] i_inst, the CCS instruction +/// @param[in] ths rank in question +/// @return void +/// +static fapi2::ReturnCode ddr4_mrs06_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst, + const uint64_t i_rank) +{ + fapi2::buffer<uint8_t> l_tccd_l_buffer; + fapi2::buffer<uint8_t> l_vrefdq_train_value_buffer; + + mss::swizzle<2, 6, A5>(i_inst.arr0, l_vrefdq_train_value_buffer); + uint8_t l_vrefdq_train_range = i_inst.arr0.getBit<A6>(); + uint8_t l_vrefdq_train_enable = i_inst.arr0.getBit<A7>(); + mss::swizzle<5, 3, A12>(i_inst.arr0, l_tccd_l_buffer); + + FAPI_INF("MR6 rank %d decode: TRAIN_V: 0x%x, TRAIN_R: 0x%x, TRAIN_E: 0x%x, TCCD_L: 0x%x", i_rank, + uint8_t(l_vrefdq_train_value_buffer), l_vrefdq_train_range, + l_vrefdq_train_enable, uint8_t(l_tccd_l_buffer)); + + return FAPI2_RC_SUCCESS; +} + +/// +/// @brief Perform the mrs_load_ddr4 operations - TARGET_TYPE_DIMM specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +fapi2::ReturnCode mrs_load_ddr4( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_INF("mrs_load_ddr4 %s", mss::c_str(i_target)); + + // Per DDR4MRS02 table 104 - timing requirements + static const uint64_t tMRD = 8; + + static std::vector< mrs_data<TARGET_TYPE_MCBIST> > l_mrs_data = + { + { 0, ddr4_mrs00, ddr4_mrs00_decode, tMRD }, { 1, ddr4_mrs01, ddr4_mrs01_decode, tMRD }, + { 2, ddr4_mrs02, ddr4_mrs02_decode, tMRD }, { 3, ddr4_mrs03, ddr4_mrs03_decode, tMRD }, + { 4, ddr4_mrs04, ddr4_mrs04_decode, tMRD }, { 5, ddr4_mrs05, ddr4_mrs05_decode, tMRD }, + { 6, ddr4_mrs06, ddr4_mrs06_decode, tMRD }, + }; + + std::vector< uint64_t > l_ranks; + FAPI_TRY( mss::ranks(i_target, l_ranks) ); + + for (auto d : l_mrs_data) + { + for (auto r : l_ranks) + { + // Note: this isn't general - assumes Nimbus via MCBIST instruction here BRS + ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_a_side = ccs::mrs_command<TARGET_TYPE_MCBIST>(i_target, r, d.iv_mrs); + ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst_b_side; + + // Thou shalt send 2 MRS, one for the a-side and the other inverted for the b-side. + // If we're on an odd-rank then we need to mirror + // So configure the A-side, mirror if necessary and invert for the B-side + FAPI_TRY( d.iv_func(i_target, l_inst_a_side, r) ); + + FAPI_TRY( mss::address_mirror(i_target, r, l_inst_a_side) ); + l_inst_b_side = mss::address_invert(l_inst_a_side); + + // Not sure if we can get tricky here and only delay after the b-side MR. The question is whether the delay + // is needed/assumed by the register or is purely a DRAM mandated delay. We know we can't go wrong having + // both delays but if we can ever confirm that we only need one we can fix this. BRS + l_inst_a_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES, MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(d.iv_delay); + l_inst_b_side.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES, MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(d.iv_delay); + + // Dump out the 'decoded' MRS and trace the CCS instructions. + if (d.iv_dumper != NULL) + { + FAPI_TRY( d.iv_dumper(l_inst_a_side, r) ); + } + + FAPI_INF("MRS%02d (%d) 0x%016llx:0x%016llx %s:rank %d a-side", uint8_t(d.iv_mrs), d.iv_delay, + l_inst_a_side.arr0, l_inst_a_side.arr1, mss::c_str(i_target), r); + FAPI_INF("MRS%02d (%d) 0x%016llx:0x%016llx %s:rank %d b-side", uint8_t(d.iv_mrs), d.iv_delay, + l_inst_b_side.arr0, l_inst_b_side.arr1, mss::c_str(i_target), r); + + // Add both to the CCS program + i_inst.push_back(l_inst_a_side); + i_inst.push_back(l_inst_b_side); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.H new file mode 100644 index 000000000..ea11517f6 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.H @@ -0,0 +1,157 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/mrs_load_ddr4.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mrs_load_ddr4.H +/// @brief Code to support mrs_load_ddr4 +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP FW Owner: Bill Hoffa <wghoffa@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_MRS_LOAD_DDR4_H_ +#define _MSS_MRS_LOAD_DDR4_H_ + +#include <fapi2.H> +#include "../mss.H" + +namespace mss +{ + +// Map bits in the ARR0 register(s) to MRS address bits. Should be traits related to ARR0. BRS +enum address_bits +{ + A0 = 0, + A1 = 1, + A2 = 2, + A3 = 3, + A4 = 4, + A5 = 5, + A6 = 6, + A7 = 7, + A8 = 8, + A9 = 9, + A10 = 10, + A11 = 11, + A12 = 12, + A13 = 13, + A14 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_14, + A15 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_15, + A16 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_16, + A17 = MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_17, + + // Only kind of address bits ... <shrug> + BA0 = 17, + BA1 = 18, + BG0 = 19, + BG1 = 15, +}; + +/// +/// @brief Perform the mrs_load_ddr4 operations - TARGET_TYPE_DIMM specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +fapi2::ReturnCode mrs_load_ddr4( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +/// +/// @brief Mirror (front to back) the ADR bits of a CCS instruction - implementation +/// @tparam T, typename of the ccs::instruction_t +/// @param[in, out] io_inst, reference to a CCS instruction to be mirrored +/// @return FAPI2_RC_SUCESS iff ok +/// @note written this way so this is easier to test +/// +template<fapi2::TargetType T> +void address_mirror_impl(ccs::instruction_t<T>& io_inst) +{ + // Nothing fancy here, just mirror the bits we're told to mirror in Table 14 — Address Mirroring and Inversion + mss::template swap<A3, A4>(io_inst.arr0); + mss::template swap<A5, A6>(io_inst.arr0); + mss::template swap<A7, A8>(io_inst.arr0); + mss::template swap<A11, A13>(io_inst.arr0); + mss::template swap<BA0, BA1>(io_inst.arr0); + mss::template swap<BG0, BG1>(io_inst.arr0); +} + +/// +/// @brief Mirror (front to back) the ADR bits of a CCS instruction +/// @tparam T, typename of the ccs::instruction_t +/// @param[in] i_target, target to use to get mirroring attribute +/// @param[in] i_rank, the rank in question +/// @param[in, out] io_inst, reference to a CCS instruction to be mirrored +/// @return FAPI2_RC_SUCESS iff ok +/// @note assumes the input is from an even number rank +/// +template<fapi2::TargetType T> +fapi2::ReturnCode address_mirror(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + const uint64_t i_rank, ccs::instruction_t<T>& io_inst) +{ + // We only mirror if the mirroring attribute is set. + uint8_t l_mirror = 0; + FAPI_TRY( eff_dimm_rcd_mirror_mode(i_target, l_mirror) ); + + // We only mirror odd ranks. + if ((l_mirror == fapi2::ENUM_ATTR_EFF_DIMM_RCD_MIRROR_MODE_ON) && (i_rank & 0x1)) + { + address_mirror_impl(io_inst); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Invert (side to side) the ADR bits of a CCS instruction +/// @param[in] i_inst, const reference to a CCS instruction. +/// @return[out] ccs instruction with the ADR bits inverted (side-to-side) +/// +template<fapi2::TargetType T> +ccs::instruction_t<T> address_invert(const ccs::instruction_t<T>& i_inst) +{ + // Copy the input as the output doesn't all change. + ccs::instruction_t<T> i_out(i_inst); + + // Nothing fancy here, just negate the bits we're told to negate in Table 14 — Address Mirroring and Inversion + mss::template negate<A3>(i_out.arr0); + mss::template negate<A4>(i_out.arr0); + mss::template negate<A5>(i_out.arr0); + mss::template negate<A6>(i_out.arr0); + mss::template negate<A7>(i_out.arr0); + mss::template negate<A8>(i_out.arr0); + mss::template negate<A9>(i_out.arr0); + + mss::template negate<A11>(i_out.arr0); + mss::template negate<A13>(i_out.arr0); + mss::template negate<A17>(i_out.arr0); + + mss::template negate<BA0>(i_out.arr0); + mss::template negate<BA1>(i_out.arr0); + mss::template negate<BG0>(i_out.arr0); + mss::template negate<BG1>(i_out.arr0); + + return i_out; +} + +} // namespace +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.C new file mode 100644 index 000000000..8a618b813 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.C @@ -0,0 +1,320 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rank.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rank.C +/// @brief Manage dIMM ranks +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" + +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_DIMM; + +namespace mss +{ + +// +// Static table of rank pair assignments. Some of thoem won't be valid depending on +// the plug rules (which may be OpenPOWER, IBM, etc.) Some also won't make sense +// -- 3 rank DIMM? -- but it doesn't take up much space and lord knows stranger things +// have happened ... Won't hurt to have this defined JustInCase(tm). +// Index by [DIMM1 rank count][DIMM0 rank count] and first is the RANK_PAIR0 register, +// second is RANK_PAIR1. +// +static const std::vector< std::vector< std::pair< uint64_t, uint64_t > > > rank_pair_assignments = +{ + { {0x0000, 0x0000}, {0x1000, 0x0000}, {0x1030, 0x0000}, {0x1030, 0x5000}, {0x1030, 0x5070} }, + { {0x9000, 0x0000}, {0x1090, 0x0000}, {0x1090, 0x3000}, {0x1090, 0x3050}, {0x1090, 0x5730} }, + { {0x90B0, 0x0000}, {0x1090, 0xB000}, {0x1090, 0x30B0}, {0x1390, 0x50B0}, {0x1390, 0x57B0} }, + { {0x90B0, 0xD000}, {0x1090, 0xB0D0}, {0x109B, 0x30D0}, {0x139B, 0x50D0}, {0x139B, 0x57D0} }, + { {0x90B0, 0xD0F0}, {0x1090, 0xB0DF}, {0x109B, 0x30DF}, {0x139B, 0x50DF}, {0x139B, 0x57DF} }, +}; + +// +// Static table of vectors representing the master ranks, depending on the rank pair +// config. This table maps 1-1 to the table above, and allows us to skip the bit manipulation +// to figure out the master ranks. Note, no ranks means an empty vector ... +// +static const std::vector< std::vector< std::vector< uint64_t > > > primary_rank_pairs = +{ + { {}, {0}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3} }, + { {4}, {0, 4}, {0, 4, 1}, {0, 4, 2, 1}, {0, 4, 2, 1} }, + { {4, 5}, {0, 4, 5}, {0, 4, 1, 5}, {0, 4, 2, 5}, {0, 4, 2, 5} }, + { {4, 5}, {0, 4, 5}, {0, 4, 1, 5}, {0, 4, 2, 5}, {0, 4, 2, 5} }, + { {4, 5, 6, 7}, {0, 4, 5, 6}, {0, 4, 1, 6}, {0, 4, 2, 6}, {0, 4, 2, 6} }, +}; + +// +// Static table of vectors representing ranks on a single DIMM +// Note: std::vector< uint64_t > v = single_dimm_ranks[mss::index(dimm)][ranks on dimm]; +// +static const std::vector< std::vector< std::vector< uint64_t > > > single_dimm_ranks = +{ + { {}, {0}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3} }, + { {}, {4}, {4, 5}, {4, 5, 6}, {4, 5, 6, 7} }, +}; + +/// +/// @brief Return true iff this rank is on thie DIMM +/// @param[in] i_target representing the DIMM +/// @param[in] i_rank, the rank number. +/// @return true iff i_rank is a rank on i_target +/// +bool is_rank_on_dimm(const fapi2::Target<TARGET_TYPE_DIMM>& i_target, const uint64_t i_rank) +{ + // DIMM[0] has ranks 0:3, DIMM[1] has ranks 4:7 - if there are not 4 ranks + // on a DIMM, there are holes. That is, the first rank on DIMM[1] is always + // rank #4. + + if (i_rank > 7) + { + FAPI_ERR("seeing rank %d? %s", i_rank, mss::c_str(i_target)); + return false; + } + + if ((i_rank < RANK_MID_POINT) && (mss::index(i_target) == 0)) + { + return true; + } + + if ((i_rank >= RANK_MID_POINT) && (mss::index(i_target) == 1)) + { + return true; + } + + return false; +} + +/// +/// @brief Return the *port relative position* of the DIMM which posesses this rank +/// @param[in] i_rank, the rank number. +/// @return the relative position of the DIMM which contains this rank. +/// +size_t get_dimm_from_rank(const uint64_t i_rank) +{ + // DIMM[0] has ranks 0:3, DIMM[1] has ranks 4:7 - if there are not 4 ranks + // on a DIMM, there are holes. That is, the first rank on DIMM[1] is always + // rank #4. + + if (i_rank > 7) + { + FAPI_ERR("seeing rank %d?", i_rank); + fapi2::Assert(false); + } + + return (i_rank < RANK_MID_POINT) ? 0 : 1; +} + +/// +/// @brief Return a vector of rank numbers which represent the primary rank pairs for this dimm +/// @tparam T, the target type +/// @param[in] TARGET_TYPE_MCA +/// @param[out] a vector of rank_pairs +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template<> +fapi2::ReturnCode primary_ranks( const fapi2::Target<TARGET_TYPE_MCA>& i_target, std::vector< uint64_t >& o_rps ) +{ + FAPI_INF("get the primary ranks for %s", mss::c_str(i_target)); + + // Get the count of rank pairs for both DIMM on the port + std::vector<uint8_t> l_rank_count(MAX_DIMM_PER_PORT, 0); + + for (auto d : i_target.getChildren<TARGET_TYPE_DIMM>()) + { + FAPI_TRY( mss::eff_num_ranks_per_dimm(d, l_rank_count[mss::index(d)]) ); + } + + o_rps = primary_rank_pairs[l_rank_count[1]][l_rank_count[0]]; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return a vector of rank numbers which represent the primary rank pairs for this dimm +/// @tparam T, the target type +/// @param[in] TARGET_TYPE_DIMM +/// @param[out] a vector of rank_pairs +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template<> +fapi2::ReturnCode primary_ranks( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, std::vector< uint64_t >& o_rps ) +{ + FAPI_INF("get the primary ranks for %s", mss::c_str(i_target)); + + std::vector< uint64_t > l_prs; + FAPI_TRY( primary_ranks(i_target.getParent<TARGET_TYPE_MCA>(), l_prs) ); + + o_rps.clear(); + + for (auto r : l_prs) + { + if (is_rank_on_dimm(i_target, r)) + { + o_rps.push_back(r); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return a vector of rank numbers which represent the ranks for this dimm +/// @param[in] i_target, TARGET_TYPE_DIMM +/// @param[out] o_ranks, a vector of ranks (numbers) +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template<> +fapi2::ReturnCode ranks( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, std::vector< uint64_t >& o_ranks ) +{ + uint8_t l_ranks; + FAPI_TRY( eff_num_ranks_per_dimm(i_target, l_ranks) ); + + o_ranks = single_dimm_ranks[mss::index(i_target)][l_ranks]; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return a vector of rank numbers which represent the ranks for this dimm +/// @param[in] i_target, TARGET_TYPE_MCA +/// @param[out] o_ranks, a vector of ranks (numbers) +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template<> +fapi2::ReturnCode ranks( const fapi2::Target<TARGET_TYPE_MCA>& i_target, std::vector< uint64_t >& o_ranks ) +{ + // Note: Isn't there a better way to do this? + std::vector< uint64_t > l_ranks; + o_ranks.clear(); + + for (auto d : i_target.getChildren<TARGET_TYPE_DIMM>()) + { + FAPI_TRY( ranks(d, l_ranks) ); + o_ranks.insert(o_ranks.end(), l_ranks.begin(), l_ranks.end()); + } + + // Lets make sure the ranks are in order - we don't know if the child vector is in position + // order, but its easy to get the ranks in order by sorting them. + std::sort(o_ranks.begin(), o_ranks.end()); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Setup the rank information in the port +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode set_rank_pairs(const fapi2::Target<TARGET_TYPE_MCA>& i_target) +{ + // If a memory system consists of four or less ranks, each Rank Pair must contain one rank. Each rank has + // unique configuration registers, calibration registers, and registers to store delay values. When a system + // contains four or less ranks, each rank number used by the system must be loaded into one of the Primary + // Rank fields. + + // Set the CSID to all 'unused' and we'll reset them as we configure rank pairs. + // Note: Centaur configured this as 0xff00 all the time - it's unclear if we need + // to set only the bits for the rank pairs configured, or whether 0xff00 will suffice. BRS + fapi2::buffer<uint64_t> l_csid_data(0xFF00); + + fapi2::buffer<uint64_t> l_rp0_register; + fapi2::buffer<uint64_t> l_rp1_register; + + // Get the count of rank pairs for all DIMM on the port + std::vector<uint8_t> l_rank_count(MAX_DIMM_PER_PORT, 0); + + for (auto d : i_target.getChildren<TARGET_TYPE_DIMM>()) + { + FAPI_TRY( mss::eff_num_ranks_per_dimm(d, l_rank_count[mss::index(d)]) ); + } + + l_rp0_register = rank_pair_assignments[l_rank_count[1]][l_rank_count[0]].first; + l_rp1_register = rank_pair_assignments[l_rank_count[1]][l_rank_count[0]].second; + + FAPI_DBG("setting rank pairs for %s. [%d,%d] (0x%08llx, 0x%08llx) csid: 0x%016llx", + mss::c_str(i_target), l_rank_count[1], l_rank_count[0], l_rp0_register, l_rp1_register, l_csid_data); + + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_RANK_PAIR0_P0, l_rp0_register) ); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_RANK_PAIR1_P0, l_rp1_register) ); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_CSID_CFG_P0, l_csid_data) ); + + // HACK HACK HACK: put this in the code properly!! BRS + { + fapi2::buffer<uint64_t> l_fix_me; + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_RP1_PRI>(); + + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_A3_A4>(); + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_A5_A6>(); + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_A7_A8>(); + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_A11_A13>(); + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_BA0_BA1>(); + l_fix_me.setBit<MCA_DDRPHY_PC_RANK_GROUP_P0_ADDR_MIRROR_BG0_BG1>(); + FAPI_DBG("pc_rank_group: 0x%016llx", uint64_t(l_fix_me)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_RANK_GROUP_P0, l_fix_me) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Get a vector of configured rank pairs. +/// Returns a vector of ordinal values of the configured rank pairs. e.g., for a 2R DIMM, {0, 1} +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @param[out] std::vector of rank pairs configured +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode get_rank_pairs(const fapi2::Target<TARGET_TYPE_MCA>& i_target, std::vector<uint64_t>& o_pairs) +{ + std::vector< uint64_t > l_prs; + uint64_t l_index = 0; + + FAPI_TRY( primary_ranks(i_target, l_prs) ); + + o_pairs.clear(); + + // Can't use for (auto rp : l_prs) as rp is unused. BRS + for (auto rp_iter = l_prs.begin(); rp_iter != l_prs.end(); ++rp_iter) + { + o_pairs.push_back(l_index); + l_index += 1; + } + +fapi_try_exit: + return fapi2::current_err; +} + +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H new file mode 100644 index 000000000..66bcd6b5f --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rank.H @@ -0,0 +1,94 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rank.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rank.H +/// @brief Do things with or for ranks +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_RANK_H_ +#define _MSS_RANK_H_ + +#include <fapi2.H> +#include <mss_attribute_accessors.H> + +namespace mss +{ + +/// +/// @brief Return a vector of rank numbers which represent the ranks for this dimm +/// @tparam T, the target type you'd like the associated ranks for +/// @param[in] i_target +/// @param[out] o_ranks, a vector of ranks (numbers) +/// @return FAPI2_RC_SUCCESS iff all is ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode ranks( const fapi2::Target<T>& i_target, std::vector< uint64_t >& o_ranks ); + +/// +/// @brief Return a vector of rank numbers which represent the primary rank pairs for this port or dimm +/// @tparam T, the target type +/// @param[in] TARGET_TYPE_MCA or TARGET_TYPEDIMM +/// @param[out] a vector of rank_pairs +/// @return FAPI@_RC_SUCCESS iff all is ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode primary_ranks( const fapi2::Target<T>& i_target, std::vector< uint64_t >& o_rps ); + +/// +/// @brief Return true iff this rank is on thie DIMM +/// @param[in] i_target representing the DIMM +/// @param[in] i_rank, the rank number. +/// @return true iff i_rank is a rank on i_target +/// +bool is_rank_on_dimm(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, const uint64_t i_rank); + +/// +/// @brief Return the *port relative position* of the DIMM which posesses this rank +/// @param[in] i_rank, the rank number. +/// @return the relative position of the DIMM which contains this rank. +/// +size_t get_dimm_from_rank(const uint64_t i_rank); + +/// +/// @brief Setup the rank information in the port +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode set_rank_pairs(const fapi2::Target<T>& i_target); + +/// +/// @brief get the rank information related to the port +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @param[out] std::vector of rank pairs configured +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode get_rank_pairs(const fapi2::Target<T>& i_target, std::vector<uint64_t>& o_pairs); + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.C new file mode 100644 index 000000000..7598d4f27 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.C @@ -0,0 +1,165 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rcd_load.C +/// @brief Run and manage the RCD_LOAD engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "rcd_load.H" +#include "rcd_load_ddr4.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ +/// +/// @brief Perform the rcd_load operations - TARGET_TYPE_MCBIST specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_MCBIST> +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode rcd_load<TARGET_TYPE_MCBIST>( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + // A vector of CCS instructions. We'll ask the targets to fill it, and then we'll execute it + ccs::program<TARGET_TYPE_MCBIST> l_program; + + for (auto c : i_target.getChildren<TARGET_TYPE_MCS>()) + { + for (auto p : c.getChildren<TARGET_TYPE_MCA>()) + { + for (auto d : p.getChildren<TARGET_TYPE_DIMM>()) + { + FAPI_DBG("rcd load for %s", mss::c_str(d)); + FAPI_TRY( perform_rcd_load(d, l_program.iv_instructions) ); + } + + // We have to configure the CCS engine to let it know which port these instructions are + // going out (or whether it's broadcast ...) so lets execute the instructions we presently + // have so that we kind of do this by port + FAPI_TRY( ccs::execute(i_target, l_program, p) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the rcd_load operations - unknown DIMM case +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to (unused) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_rcd_load<DEFAULT_KIND>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + uint8_t l_type = 0; + uint8_t l_gen = 0; + + FAPI_TRY( mss::eff_dimm_type(i_target, l_type) ); + FAPI_TRY( mss::eff_dram_gen(i_target, l_gen) ); + + // If we're here, we have a problem. The DIMM kind (type and/or generation) wasn't know + // to our dispatcher. We have a DIMM plugged in we don't know how to deal with. + FAPI_ASSERT(false, + fapi2::MSS_UNKNOWN_DIMM() + .set_DIMM_TYPE(l_type) + .set_DRAM_GEN(l_gen) + .set_DIMM_IN_ERROR(i_target), + "Unable to perform rcd load on %s: unknown type (%d) or generation (%d)", + mss::c_str(i_target), l_type, l_gen); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the rcd_load operations - RDIMM DDR4 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_rcd_load<KIND_RDIMM_DDR4>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_DBG("perform rcd_load for %s [expecting rdimm (ddr4)]", mss::c_str(i_target)); + FAPI_TRY( rcd_load_ddr4(i_target, i_inst) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform the rcd_load operations - LRDIMM DDR4 +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_rcd_load<KIND_LRDIMM_DDR4>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_DBG("perform rcd_load for %s [expecting lrdimm (ddr4)]", mss::c_str(i_target)); + FAPI_TRY( rcd_load_ddr4(i_target, i_inst) ); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Perform the rcd_load operations - start the dispatcher +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode perform_rcd_load<FORCE_DISPATCH>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + uint8_t l_type = 0; + uint8_t l_gen = 0; + + FAPI_TRY( mss::eff_dimm_type(i_target, l_type) ); + FAPI_TRY( mss::eff_dram_gen(i_target, l_gen) ); + + return perform_rcd_load_dispatch<FORCE_DISPATCH>(dimm_kind( l_type, l_gen ), i_target, i_inst); + +fapi_try_exit: + FAPI_ERR("couldn't get dimm type, dram gen: %s", mss::c_str(i_target)); + return fapi2::current_err; +} + +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.H new file mode 100644 index 000000000..bc66ccd46 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.H @@ -0,0 +1,174 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rcd_load.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rcd_load.H +/// @brief Code to support rcd_loads +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_RCD_LOAD_H_ +#define _MSS_RCD_LOAD_H_ + +#include <fapi2.H> +#include "../utils/c_str.H" +#include <p9_mc_scom_addresses.H> + +#include "../shared/mss_kind.H" + +namespace mss +{ + +struct rcd_data +{ + // Which RC# this is + fapi2::buffer<uint8_t> iv_rcd; + + // The attribute getter + fapi2::ReturnCode (*iv_func)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>&, uint8_t&); + + // The delay needed after this RCD word is written + uint64_t iv_delay; + + rcd_data( uint64_t i_rcd, + fapi2::ReturnCode (*i_func)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>&, uint8_t&), + uint64_t i_delay ): + iv_rcd(i_rcd), + iv_func(i_func), + iv_delay(i_delay) + {} +}; + +/// +/// @brief Perform the rcd_load operations +/// @tparam T, the fapi2::TargetType of i_target +/// @param[in] i_target, a fapi2::Target +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode rcd_load( const fapi2::Target<T>& i_target ); + + +// +// Implement the polymorphism for rcd_load +// + +// -# Register the API. +/// -# Define the template parameters for the overloaded function +/// @note the first argument is the api name, and the rest are the api's template parameters. +/// @note this creates __api_name##_overload + +template< mss::kind_t K > +struct perform_rcd_load_overload +{ + static const bool available = false; +}; + +/// -# Register the specific overloads. The first parameter is the name +/// of the api, the second is the kind of the element which is being +/// overloaded - an RDIMM, an LRDIMM, etc. The remaining parameters +/// indicate the specialization of the api itself. +/// @note You need to define the "DEFAULT_KIND" here as an overload. This +/// allows the mechanism to find the "base" implementation for things which +/// have no specific overload. +template<> +struct perform_rcd_load_overload< DEFAULT_KIND > +{ + static const bool available = true; +}; + +template<> +struct perform_rcd_load_overload< KIND_RDIMM_DDR4 > +{ + static const bool available = true; +}; + +template<> +struct perform_rcd_load_overload< KIND_LRDIMM_DDR4 > +{ + static const bool available = true; +}; + +/// +/// -# Define the default case for overloaded calls. enable_if states that +/// if there is a DEFAULT_KIND overload for this TargetType, then this +/// entry point will be defined. Note the general case below is enabled if +/// there is no overload defined for this TargetType +/// + +/// +/// @brief Perform the rcd_load operations +/// @tparam K, the kind of DIMM we're operating on (derived) +/// @param[in] i_target, a fapi2::Target<fapi2::TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< mss::kind_t K = FORCE_DISPATCH > +typename std::enable_if< perform_rcd_load_overload<DEFAULT_KIND>::available, fapi2::ReturnCode>::type +perform_rcd_load( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +// +// We know we registered overloads for perform_rcd_load, so we need the entry point to +// the dispatcher. Add a set of these for all TargetTypes which get overloads +// for this API +// +template<> +fapi2::ReturnCode perform_rcd_load<FORCE_DISPATCH>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +template<> +fapi2::ReturnCode perform_rcd_load<DEFAULT_KIND>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); + +// +// Boilerplate dispatcher +// +template< kind_t K, bool B = perform_rcd_load_overload<K>::available > +inline fapi2::ReturnCode perform_rcd_load_dispatch( const kind_t& i_kind, + const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst) +{ + // We dispatch to another kind if: + // We don't have an overload defined (B == false) + // Or, if we do have an overload (B == true) and this is not out kind. + if ((B == false) || ((B == true) && (K != i_kind))) + { + return perform_rcd_load_dispatch < (kind_t)(K - 1) > (i_kind, i_target, i_inst); + } + + // Otherwise, we call the overload. + return perform_rcd_load<K>(i_target, i_inst); +} + +// DEFAULT_KIND is 0 so this is the end of the recursion +template<> +inline fapi2::ReturnCode perform_rcd_load_dispatch<DEFAULT_KIND>(const kind_t&, + const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst) +{ + return perform_rcd_load<DEFAULT_KIND>(i_target, i_inst); +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.C b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.C new file mode 100644 index 000000000..cbf370047 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.C @@ -0,0 +1,138 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rcd_load_ddr4.C +/// @brief Run and manage the DDR4 rcd loading +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "rcd_load_ddr4.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ + +/// +/// @brief Perform the rcd_load_ddr4 operations - TARGET_TYPE_DIMM specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +fapi2::ReturnCode rcd_load_ddr4( const fapi2::Target<TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<TARGET_TYPE_MCBIST> >& i_inst) +{ + FAPI_INF("rcd_load_ddr4 %s", mss::c_str(i_target)); + + // Per DDR4RCD02 table 104 - timing requirements + static const uint64_t tMRD = 8; + static const uint64_t tMRD_L = 16; + + // Per DDR4RCD02, tSTAB is 5us. We want this in cycles for the CCS. + const uint64_t tSTAB = mss::us_to_cycles(i_target, 5); + + static std::vector< rcd_data > l_rcd_4bit_data = + { + { 0, eff_dimm_ddr4_rc00, tMRD }, { 1, eff_dimm_ddr4_rc01, tMRD }, { 2, eff_dimm_ddr4_rc02, tSTAB }, + { 3, eff_dimm_ddr4_rc03, tMRD_L}, { 4, eff_dimm_ddr4_rc04, tMRD_L}, { 5, eff_dimm_ddr4_rc05, tMRD_L}, + { 6, eff_dimm_ddr4_rc67, tMRD }, { 8, eff_dimm_ddr4_rc08, tMRD }, { 9, eff_dimm_ddr4_rc09, tMRD }, + { 10, eff_dimm_ddr4_rc10, tSTAB }, { 11, eff_dimm_ddr4_rc11, tMRD }, { 12, eff_dimm_ddr4_rc12, tMRD }, + { 13, eff_dimm_ddr4_rc13, tMRD }, { 14, eff_dimm_ddr4_rc14, tMRD }, { 15, eff_dimm_ddr4_rc15, tMRD }, + }; + + static std::vector< rcd_data > l_rcd_8bit_data = + { + { 1, eff_dimm_ddr4_rc1x, tMRD }, { 2, eff_dimm_ddr4_rc2x, tMRD }, { 3, eff_dimm_ddr4_rc3x, tSTAB }, + { 4, eff_dimm_ddr4_rc4x, tMRD }, { 5, eff_dimm_ddr4_rc5x, tMRD }, { 6, eff_dimm_ddr4_rc6x, tMRD }, + { 7, eff_dimm_ddr4_rc7x, tMRD }, { 8, eff_dimm_ddr4_rc8x, tMRD }, { 9, eff_dimm_ddr4_rc9x, tMRD }, + { 10, eff_dimm_ddr4_rcax, tMRD }, { 11, eff_dimm_ddr4_rcbx, tMRD_L} + }; + + fapi2::buffer<uint8_t> l_value; + + // A little 4bit RCD love ... + for (auto d : l_rcd_4bit_data) + { + // Note: this isn't general - assumes Nimbus via MCBIST instruction here BRS + ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst = ccs::rcd_command<TARGET_TYPE_MCBIST>(i_target); + FAPI_TRY( d.iv_func(i_target, l_value) ); + + // If this control word would be set to 0, skip it - we can rely on the DIMM default + if (l_value != 0) + { + // Data to be written into the 4-bit configuration registers need to be presented on DA0 .. DA3 + mss::swizzle<MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13, 4, 7>(l_value, l_inst.arr0); + + // Selection of each word of 4-bit control bits is presented on inputs DA4 through DA12 + mss::swizzle < MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13 + 4, 4, 7 > (d.iv_rcd, l_inst.arr0); + + // For changes to the control word setting [...] the controller needs to wait tMRD[tSTAB] after + // the last control word access, before further access to the DRAM can take place. + l_inst.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES, MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(d.iv_delay); + + FAPI_INF("RCD%02d value 0x%x (%d) 0x%016llx:0x%016llx %s", uint8_t(d.iv_rcd), l_value, d.iv_delay, + l_inst.arr0, l_inst.arr1, mss::c_str(i_target)); + i_inst.push_back(l_inst); + } + } + + // 8bit's turn + for (auto d : l_rcd_8bit_data) + { + // Note: this isn't general - assumes Nimbus via MCBIST instruction here BRS + ccs::instruction_t<TARGET_TYPE_MCBIST> l_inst = ccs::rcd_command<TARGET_TYPE_MCBIST>(i_target); + FAPI_TRY( d.iv_func(i_target, l_value) ); + + // If this control word would be set to 0, skip it - we can rely on the DIMM default + if (l_value != 0) + { + // Data to be written into the 8-bit configuration registers need to be presented on DA0 .. DA7 + mss::swizzle<MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13, 8, 7>(l_value, l_inst.arr0); + + // Selection of each word of 8-bit control bits is presented on inputs DA8 through DA12. + mss::swizzle < MCBIST_CCS_INST_ARR0_00_DDR_ADDRESS_0_13 + 8, 5, 7 > (d.iv_rcd, l_inst.arr0); + + // For changes to the control word setting [...] the controller needs to wait tMRD[tSTAB] after + // the last control word access, before further access to the DRAM can take place. + l_inst.arr1.insertFromRight<MCBIST_CCS_INST_ARR1_00_IDLES, MCBIST_CCS_INST_ARR1_00_IDLES_LEN>(d.iv_delay); + + FAPI_INF("RCD%XX value 0x%x (%d) 0x%016llx:0x%016llx %s", uint8_t(d.iv_rcd), l_value, d.iv_delay, + l_inst.arr0, l_inst.arr1, mss::c_str(i_target)); + i_inst.push_back(l_inst); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.H b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.H new file mode 100644 index 000000000..640f43d89 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.H @@ -0,0 +1,48 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/dimm/rcd_load_ddr4.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file rcd_load_ddr4.H +/// @brief Code to support rcd_load_ddr4 +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_RCD_LOAD_DDR4_H_ +#define _MSS_RCD_LOAD_DDR4_H_ + +#include <fapi2.H> +#include "../utils/c_str.H" +#include <p9_mc_scom_addresses.H> + +namespace mss +{ +/// +/// @brief Perform the rcd_load_ddr4 operations - TARGET_TYPE_DIMM specialization +/// @param[in] i_target, a fapi2::Target<TARGET_TYPE_DIMM> +/// @param[in] a vector of CCS instructions we should add to +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +fapi2::ReturnCode rcd_load_ddr4( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, + std::vector< ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST> >& i_inst); +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C new file mode 100644 index 000000000..f2184f89b --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C @@ -0,0 +1,41 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mcbist.C +/// @brief Run and manage the MCBIST engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> +#include "mcbist.H" + +using fapi2::TARGET_TYPE_MCBIST; + +namespace mss +{ +namespace mcbist +{ + +} // namespace +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H new file mode 100644 index 000000000..1a6660c3c --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H @@ -0,0 +1,255 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/mcbist/mcbist.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mcbist.H +/// @brief Run and manage the MCBIST engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_MCBIST_H_ +#define _MSS_MCBIST_H_ + +#include <fapi2.H> + +#include <p9_mc_scom_addresses.H> +#include <p9_mc_scom_addresses_fld.H> + +#include "../utils/poll.H" +#include "../shared/mss_const.H" + +namespace mss +{ +namespace mcbist +{ + +// I have a dream that the MCBIST 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 mcbistTraits +/// @brief a collection of traits associated with the MCBIST engine or hardware +/// +template< fapi2::TargetType T > +class mcbistTraits; + +/// +/// @class mcbistTraits<fapi2::TARGET_TYPE_MEMBUF_CHIP> +/// @brief a collection of traits associated with the Centaur MCBIST engine or hardware +/// +template<> +class mcbistTraits<fapi2::TARGET_TYPE_MEMBUF_CHIP> +{ +}; + +/// +/// @class mcbistTraits<fapi2::TARGET_TYPE_MCBIST> +/// @brief a collection of traits associated with the Nimbus MCBIST engine or hardware +/// +template<> +class mcbistTraits<fapi2::TARGET_TYPE_MCBIST> +{ + public: + /// MCBIST "memory registers" - config for subtests. + static const uint64_t MCBMR0_REG = MCBIST_MCBMR0Q; + + enum + { + // Subtest control bits. These are the same in all '16 bit subtest' field + COMPL_1ST_CMD = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_COMPL_1ST_CMD, + COMPL_2ND_CMD = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_COMPL_2ND_CMD, + COMPL_3RD_CMD = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_COMPL_3RD_CMD, + ADDR_REV_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_REV_MODE, + ADDR_RAND_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_RAND_MODE, + ECC_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ECC_MODE, + DATA_MODE = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_DATA_MODE, + DATA_MODE_LEN = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_DATA_MODE_LEN, + ADDR_SEL = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_SEL, + ADDR_SEL_LEN = MCBIST_MCBMR0Q_MCBIST_CFG_TEST00_ADDR_SEL_LEN, + }; + +}; + +/// +/// @class subtest_t +/// @brief encapsulation of an MCBIST subtest. +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +class subtest_t +{ + public: + /// The mcbist 'memory register' for this subtest. + // Note that it is only 16 bits. + // Each 64b memory register contains multiple 16 bit subtest definitions. + // As we create a vector of subtests, we'll drop them in to their appropriate + // MCBMR register before executing. + fapi2::buffer<uint16_t> iv_mcbmr; +}; + +/// +/// @brief A class representing a series of MCBIST subtests, and the +/// MCBIST engine parameters associated with running the subtests +/// @tparam fapi2::TargetType T representing the fapi2 target which +/// contains the MCBIST engine (e.g., fapi2::TARGET_TYPE_MCBIST) +/// +template< fapi2::TargetType T > +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 subtests + std::vector< subtest_t<T> > iv_subtests; + poll_parameters iv_poll; +}; + +/// +/// @brief Complement the data for the first subcommand +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_compliment_1st_cmd( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::COMPL_1ST_CMD>(i_state); + return; +} + +/// +/// @brief Complement the data for the second subcommand +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_compliment_2nd_cmd( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::COMPL_2ND_CMD>(i_state); + return; +} + +/// +/// @brief Complement the data for the third subcommand +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_compliment_3rd_cmd( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::COMPL_3RD_CMD>(i_state); + return; +} + +/// +/// @brief Generate addresses in reverse order +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_addr_rev_mode( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::ADDR_REV_MODE>(i_state); + return; +} + +/// +/// @brief Generate addresses in random order +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_addr_rand_mode( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::ADDR_RAND_MODE>(i_state); + return; +} + +/// +/// @brief Generate and check data with ECC +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_state, the desired state of the function +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_ecc_mode( subtest_t<T>& io_subtest, const mss::states i_state ) +{ + io_subtest.iv_mcbmr.template writeBit<TT::ECC_MODE>(i_state); + return; +} + +/// +/// @brief Set the data mode for this subtest +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_mode, the desired mcbist::data_mode +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_data_mode( subtest_t<T>& io_subtest, const data_mode i_mode ) +{ + io_subtest.iv_mcbmr.template insertFromRight<TT::DATA_MODE, TT::DATA_MODE_LEN>(i_mode); + return; +} + +/// +/// @brief Configure which address registers to use for this subtest +/// @tparam T, fapi2::TargetType of the MCBIST engine +/// @tparam TT, the mssTraits associtated with T +/// @param[in,out] io_subtest, a subtest +/// @param[in] i_index, 0 = MCBSA0Q, 1 = MCBSA1Q, ... +/// @note wraps to 0-3 no matter what value you pass in. +/// @return void +/// +template< fapi2::TargetType T, typename TT = mcbistTraits<T> > +void change_addr_sel( subtest_t<T>& io_subtest, const uint16_t i_index ) +{ + // Roll the index around - tidy support for an index which is out of range. + static const uint16_t MAX_ADDRESS_START_END_REGISTERS = 3 + 1; + io_subtest.iv_mcbmr.template insertFromRight<TT::ADDR_SEL, TT::ADDR_SEL_LEN>(i_index % MAX_ADDRESS_START_END_REGISTERS); + return; +} + +} // namespace +} // namespace + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mss.H b/src/import/chips/p9/procedures/hwp/memory/lib/mss.H new file mode 100644 index 000000000..5ec6f752e --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mss.H @@ -0,0 +1,63 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/mss.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mss.H +/// @brief Main include file for the Memory Subsystem +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_H_ +#define _MSS_H_ + +#include <fapi2.H> +#include <p9_mc_scom_addresses.H> +#include <p9_mc_scom_addresses_fld.H> + +#include "shared/mss_const.H" +#include "shared/mss_kind.H" + +#include "utils/index.H" +#include "utils/c_str.H" + +#include "utils/pos.H" +#include "utils/swizzle.H" +#include "utils/conversions.H" +#include "utils/find.H" +#include "utils/poll.H" +#include "utils/checker.H" + +#include "ccs/ccs.H" + +#include "port/port.H" + + +#include "dimm/rcd_load.H" +#include "dimm/mrs_load.H" +#include "dimm/rank.H" + +#include "phy/ddr_phy.H" +#include "phy/dp16.H" +#include "phy/cal_timers.H" + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mss.mk b/src/import/chips/p9/procedures/hwp/memory/lib/mss.mk new file mode 100644 index 000000000..3f4e6c60f --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mss.mk @@ -0,0 +1,43 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: chips/p9/procedures/hwp/memory/lib/mss.mk $ +# +# IBM CONFIDENTIAL +# +# EKB Project +# +# COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# The source code for this program is not published or otherwise +# divested of its trade secrets, irrespective of what has been +# deposited with the U.S. Copyright Office. +# +# IBM_PROLOG_END_TAG + +# +# Makefile to build the MSS libraries. +# + +# Add common and generated parts to object list. + +MSS_PATH := $(ROOTPATH)/chips/p9/procedures/hwp/memory/lib + +MSS_SOURCE := $(shell find $(MSS_PATH) -name '*.C' -exec basename {} \;) +MSS_MODULE_OBJS += $(patsubst %.C,%.o,$(MSS_SOURCE)) + +MSS_SOURCE_DIRS := $(shell find $(MSS_PATH) -type d) + +# Define common source and include paths. +define MSS_MODULE_INCLUDES +$(foreach dir, $(MSS_SOURCE_DIRS), $(call ADD_MODULE_SRCDIR,$(1),$(dir))) +$(call ADD_MODULE_INCDIR,$(1),$(GENPATH)) +$(call ADD_MODULE_INCDIR,$(1),$(FAPI2_PLAT_INCLUDE)) +endef + +MODULE = mss +OBJS += $(MSS_MODULE_OBJS) +$(eval $(call MSS_MODULE_INCLUDES,$(MODULE))) +$(call BUILD_MODULE) diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/mss_utils.H b/src/import/chips/p9/procedures/hwp/memory/lib/mss_utils.H new file mode 100644 index 000000000..a20562e15 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/mss_utils.H @@ -0,0 +1,36 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/mss_utils.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mss_utils.h.H +/// @brief Main include file for the Memory Subsystem +/// +// *HWP HWP Owner: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP HWP Backup: Brian Silver <bsilver@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_P9_ATTR_UTILS_H_ +#define _MSS_P9_ATTR_UTILS_H_ + +#include "utils/index.H" +#include "utils/c_str.H" + +#endif // _MSS_P9_ATTR_UTILS_H_ diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/cal_timers.H b/src/import/chips/p9/procedures/hwp/memory/lib/phy/cal_timers.H new file mode 100644 index 000000000..4045ada50 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/cal_timers.H @@ -0,0 +1,245 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/phy/cal_timers.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file cal_timers.H +/// @brief Subroutines to calculate the duration of training operations +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_CAL_TIMERS_H_ +#define _MSS_CAL_TIMERS_H_ + +#include <fapi2.H> +#include "../shared/mss_const.H" +#include "../utils/conversions.H" +#include "../utils/poll.H" + +namespace mss +{ + +/// +/// @brief Return the cycles taken for write leveling on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for write leveling on a single rank +/// +template<fapi2::TargetType T> +uint64_t wr_lvl_cycles(const fapi2::Target<T>& i_target ) +{ + const uint64_t TWLO_TWLOE = mss::twlo_twloe(i_target); + + // This step runs for approximately (80 + TWLO_TWLOE) x NUM_VALID_SAMPLES x (384/(BIG_STEP + 1) + + // (2 x (BIG_STEP + 1))/(SMALL_STEP + 1)) + 20 memory clock cycles per rank. + + const uint64_t l_wr_lvl_cycles = (80 + TWLO_TWLOE) * WR_LVL_NUM_VALID_SAMPLES * (384 / (WR_LVL_BIG_STEP + 1) + + (2 * (WR_LVL_BIG_STEP + 1)) / (WR_LVL_SMALL_STEP + 1)) + 20; + FAPI_DBG("wr_lvl_cycles: %d(%dns) (%d, %d, %d, %d)", l_wr_lvl_cycles, mss::cycles_to_ns(i_target, l_wr_lvl_cycles), + TWLO_TWLOE, WR_LVL_NUM_VALID_SAMPLES, WR_LVL_BIG_STEP, WR_LVL_SMALL_STEP); + + return l_wr_lvl_cycles; +} + +/// +/// @brief Return the cycles taken for DQS align on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for DQS align on a single rank +/// +template<fapi2::TargetType T> +uint64_t dqs_align_cycles(const fapi2::Target<T>& i_target ) +{ + // This step runs for approximately 6 x 600 x 4 DRAM clocks per rank pair. + const uint64_t l_dqs_align_cycles = 6 * 600 * 4; + + FAPI_DBG("dqs_align_cycles: %d(%dns)", l_dqs_align_cycles, mss::cycles_to_ns(i_target, l_dqs_align_cycles)); + return l_dqs_align_cycles; +} + +/// +/// @brief Return the cycles taken for RD clock align on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for RD clock align on a single rank +/// +template<fapi2::TargetType T> +uint64_t rdclk_align_cycles(const fapi2::Target<T>& i_target ) +{ + // This step runs for approximately 24 x ((1024/COARSE_CAL_STEP_SIZE + 4 x COARSE_CAL_STEP_SIZE) x 4 + 32) DRAM + // clocks per rank pair + const uint64_t l_rdclk_align_cycles = 24 * ((1024 / COARSE_CAL_STEP_SIZE + 4 * COARSE_CAL_STEP_SIZE) * 4 + 32); + FAPI_DBG("rdclk_align_cycles: %d(%dns) (%d)", l_rdclk_align_cycles, + mss::cycles_to_ns(i_target, l_rdclk_align_cycles), COARSE_CAL_STEP_SIZE); + return l_rdclk_align_cycles; +} + +/// +/// @brief Return the cycles taken for read centering on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for read centering on a single rank +/// +template<fapi2::TargetType T> +uint64_t read_ctr_cycles(const fapi2::Target<T>& i_target ) +{ + // This step runs for approximately 6 x (512/COARSE_CAL_STEP_SIZE + 4 x (COARSE_CAL_STEP_SIZE + + // 4 x CONSEQ_PASS)) x 24 DRAM clocks per rank pair. + + const uint64_t l_read_ctr_cycles = 6 * (512 / COARSE_CAL_STEP_SIZE + 4 * (COARSE_CAL_STEP_SIZE + 4 * CONSEQ_PASS)) * 24; + FAPI_DBG("read_ctr_cycles %d(%dns) (%d, %d)", l_read_ctr_cycles, mss::cycles_to_ns(i_target, l_read_ctr_cycles), + COARSE_CAL_STEP_SIZE, CONSEQ_PASS); + return l_read_ctr_cycles; +} + +/// +/// @brief Return the cycles taken for write centering on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for write centering on a single rank +/// +template<fapi2::TargetType T> +uint64_t write_ctr_cycles(const fapi2::Target<T>& i_target ) +{ + // 1000 + (NUM_VALID_SAMPLES * (FW_WR_RD + FW_RD_WR + 16) * + // (1024/(SMALL_STEP +1) + 128/(BIG_STEP +1)) + 2 * (BIG_STEP+1)/(SMALL_STEP+1)) x 24 DRAM + // clocks per rank pair. + const uint64_t l_write_ctr_cycles = 1000 + (WR_LVL_NUM_VALID_SAMPLES * (WR_CNTR_FW_WR_RD + WR_CNTR_FW_RD_WR + 16) * + (1024 / (WR_LVL_SMALL_STEP + 1) + 128 / (WR_LVL_BIG_STEP + 1)) + 2 * + (WR_LVL_BIG_STEP + 1) / (WR_LVL_SMALL_STEP + 1)) * 24; + + FAPI_DBG("write_ctr_cycles: %d(%dns) (%d, %d, %d, %d, %d)", + l_write_ctr_cycles, mss::cycles_to_ns(i_target, l_write_ctr_cycles), + WR_LVL_NUM_VALID_SAMPLES, WR_CNTR_FW_WR_RD, WR_CNTR_FW_RD_WR, WR_LVL_BIG_STEP, WR_LVL_SMALL_STEP); + + return l_write_ctr_cycles; +} + +/// +/// @brief Return the cycles taken for coarse write on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for coarse write on a single rank +/// +template<fapi2::TargetType T> +uint64_t coarse_wr_cycles(const fapi2::Target<T>& i_target ) +{ + // The run length given here is the maximum run length for this calibration algorithm. + // This step runs for approximately 40 DRAM clocks per rank pair. + static const uint64_t l_coarse_wr_cycles = 40; + FAPI_DBG("coarse_wr_cycles: %d(%dns)", l_coarse_wr_cycles, mss::cycles_to_ns(i_target, l_coarse_wr_cycles)); + return l_coarse_wr_cycles; +} + +/// +/// @brief Return the cycles taken for coarse read on a single rank +/// @tparam T, the target type used to get attributes +/// @param[in] i_target, used to get attributes +/// @return uint64_t, the cycles taken for coarse rd on a single rank +/// +template<fapi2::TargetType T> +uint64_t coarse_rd_cycles(const fapi2::Target<T>& i_target ) +{ + // The run length given here is the maximum run length for this calibration algorithm. + // This step runs for approximately 32 DRAM clocks per rank pair. + static const uint64_t l_coarse_rd_cycles = 32; + FAPI_DBG("coarse_rd_cycles: %d(%dns)", l_coarse_rd_cycles, mss::cycles_to_ns(i_target, l_coarse_rd_cycles)); + return l_coarse_rd_cycles; +} + +/// +/// @brief Configure the polling intervals with the proper timeings based on the cal steps enabled +/// @tparam T, the fapi2::TargetType of the port +/// @param[in] i_target, the port target +/// @param[in] the poll_parameters to update +/// @param[in] a fapi2::buffer<uint8_t> representing the cal steps enabled +/// @return FAPI2_RC_SUCCESS iff everything is OK +/// +template<fapi2::TargetType T> +inline fapi2::ReturnCode cal_timer_setup(const fapi2::Target<T>& i_target, + poll_parameters& i_poll, + const fapi2::buffer<uint16_t>& i_cal_steps_enabled) +{ + // First, calculate the total number of cycles this cal should take if everything + // runs to completion + uint64_t l_total_cycles = i_cal_steps_enabled.getBit<WR_LEVEL>() ? wr_lvl_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<DQS_ALIGN>() ? dqs_align_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<RDCLK_ALIGN>() ? rdclk_align_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<READ_CTR>() ? read_ctr_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<WRITE_CTR>() ? write_ctr_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<COARSE_WR>() ? coarse_wr_cycles(i_target) : 0; + l_total_cycles += i_cal_steps_enabled.getBit<COARSE_RD>() ? coarse_rd_cycles(i_target) : 0; + + // Now we have to decide if we're going to abort on an error or keep going. If we keep going, + // then we want our initial delay to be the expected time to completion - we don't have much + // of a choice. If we abort on error then we want to have a small initial delay, and poll more + // times with longer durations between the polling. + +#define THRASH_CCS_IP_IN_SIM 1 +#ifdef THRASH_CCS_IP_IN_SIM + + i_poll.iv_initial_delay = mss::cycles_to_ns(i_target, 1); + i_poll.iv_initial_sim_delay = mss::cycles_to_simcycles(1); + + i_poll.iv_delay = DELAY_1US; + i_poll.iv_sim_delay = mss::cycles_to_simcycles(mss::ns_to_cycles(i_target, DELAY_1US)); + + i_poll.iv_poll_count = (mss::cycles_to_ns(i_target, l_total_cycles) - i_poll.iv_initial_delay) / i_poll.iv_delay; + + // Don't let the poll count be 0 - that just makes for a bad day. + i_poll.iv_poll_count = (i_poll.iv_poll_count == 0) ? 1 : i_poll.iv_poll_count; + +#else + + if (CAL_ABORT_ON_ERROR) + { + // We don't want to wait too long for the initial check, just some hueristics here + i_poll.iv_initial_delay = mss::cycles_to_ns(i_target, l_total_cycles / 8); + i_poll.iv_initial_sim_delay = mss::cycles_to_simcycles(l_total_cycles / 8); + + // Delay 1us between polls, and setup the poll count so + // iv_initial_delay + (iv_delay * iv_poll_count) == l_total_cycles; + i_poll.iv_delay = DELAY_1US; + i_poll.iv_sim_delay = mss::cycles_to_simcycles(mss::ns_to_cycles(i_target, DELAY_1US)); + + i_poll.iv_poll_count = (mss::cycles_to_ns(i_target, l_total_cycles) - i_poll.iv_initial_delay) / i_poll.iv_delay; + + // Don't let the poll count be 0 - that just makes for a bad day. + i_poll.iv_poll_count = (i_poll.iv_poll_count == 0) ? 1 : i_poll.iv_poll_count; + } + else + { + i_poll.iv_initial_delay = mss::cycles_to_ns(i_target, l_total_cycles); + i_poll.iv_initial_sim_delay = mss::cycles_to_simcycles(l_total_cycles); + } + +#endif + + FAPI_DBG("cal abort on error? %s. tc: %dc, id: %dns(%dsc), d: %d(%dsc), pc: %d", + (CAL_ABORT_ON_ERROR ? "yup" : "nope"), l_total_cycles, i_poll.iv_initial_delay, + i_poll.iv_initial_sim_delay, i_poll.iv_delay, i_poll.iv_sim_delay, i_poll.iv_poll_count); + + return fapi2::FAPI2_RC_SUCCESS; +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.C b/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.C new file mode 100644 index 000000000..cccb0b3e1 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.C @@ -0,0 +1,881 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file ddr_phy.C +/// @brief Subroutines to manipulate the phy, or used during phy procedures +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <vector> +#include <initializer_list> + +#include <fapi2.H> +#include "../mss.H" + +#include "ddr_phy.H" +#include "../utils/bit_count.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_PROC_CHIP; +using fapi2::TARGET_TYPE_SYSTEM; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ + +/// +/// @brief Blast one peice of data across a vector of addresses +/// @param[in] i_target, the target for the scom +/// @param[in] const std::vector<uint64_t>&, addresses +/// @param[in] const fapi2::buffer<uint64_t>&, the data to blast +/// @return FAPI2_RC_SUCCESS iff ok +/// @note Author is originally from Boston (Pahk mah cah in Havahd Yahd) + +/// @note std::transform might have been tidier, but because of the ReturnCode +/// and the FAPI_TRY mechanism, this is the simplest. +/// +template< fapi2::TargetType T > +fapi2::ReturnCode scom_blastah( const fapi2::Target<T>& i_target, const std::vector<uint64_t>& i_addrs, + const fapi2::buffer<uint64_t>& i_data ) +{ + size_t count(0); + + for (auto a : i_addrs) + { + FAPI_TRY( fapi2::putScom(i_target, a, i_data) ); + ++count; + } + + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR( "scom_blastah failed: %d of %d executed against %s", count, i_addrs.size(), mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief Blast one peice of data across a vector of targets +/// @param[in] the vector of targets for the scom +/// @param[in] the address +/// @param[in] const fapi2::buffer<uint64_t>&, the data to blast +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode scom_blastah( const std::vector<fapi2::Target<T> >& i_targets, const uint64_t i_addr, + const fapi2::buffer<uint64_t>& i_data ) +{ + size_t count(0); + + for (auto t : i_targets) + { + FAPI_TRY( fapi2::putScom(t, i_addr, i_data) ); + ++count; + } + + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR( "scom_blastah failed: %d of %d written to 0x%llx", count, i_targets.size(), i_addr); + return fapi2::current_err; +} + +/// +/// @brief Blast one peice of data across a vector of targets +/// @param[in] the vector of targets for the scom +/// @param[in] the vector of addresses +/// @param[in] const fapi2::buffer<uint64_t>&, the data to blast +/// @return FAPI2_RC_SUCCESS iff ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode scom_blastah( const std::vector<fapi2::Target<T> >& i_targets, + const std::vector<uint64_t>& i_addrs, + const fapi2::buffer<uint64_t>& i_data ) +{ + size_t count(0); + + for (auto t : i_targets) + { + FAPI_TRY( mss::scom_blastah(t, i_addrs, i_data) ); + ++count; + } + + return fapi2::current_err; + +fapi_try_exit: + FAPI_ERR( "scom_blastah failed: %d of %dx%d", count, i_targets.size(), i_addrs.size() ); + return fapi2::current_err; +} + +/// +/// @brief change resetn to the given state +/// @param[in] i_target, the mcbist +/// @param[in] the desired state +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode change_resetn( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target, states i_state ) +{ + fapi2::buffer<uint64_t> l_data; + + for (auto p : i_target.getChildren<TARGET_TYPE_MCA>()) + { + FAPI_DBG("Change reset to %s PHY: %s", (i_state == HIGH ? "high" : "low"), mss::c_str(p)); + + FAPI_TRY( fapi2::getScom(p, MCA_MBA_CAL0Q, l_data) ); + i_state == HIGH ? l_data.setBit<MCA_MBA_CAL0Q_RESET_RECOVER>() : l_data.clearBit<MCA_MBA_CAL0Q_RESET_RECOVER>(); + FAPI_TRY( fapi2::putScom(p, MCA_MBA_CAL0Q, l_data) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief perform the zctl toggle process +/// @param[in] i_target, the mcbist for the reset recover +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode toggle_zctl( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + + auto l_ports = i_target.getChildren<TARGET_TYPE_MCA>(); + // + // 4. Write 0x0010 to PC IO PVT N/P FET driver control registers to assert ZCTL reset and enable the internal impedance controller. + // (SCOM Addr: 0x8000C0140301143F, 0x8000C0140301183F, 0x8001C0140301143F, 0x8001C0140301183F) + FAPI_DBG("Write 0x0010 to PC IO PVT N/P FET driver control registers to assert ZCTL reset"); + l_data.setBit<59>(); + FAPI_TRY( mss::scom_blastah(l_ports, MCA_DDRPHY_PC_IO_PVT_FET_CONTROL_P0, l_data) ); + + // + // 5. Write 0x0018 to PC IO PVT N/P FET driver control registers to deassert ZCTL reset while impedance controller is still enabled. + // (SCOM Addr: 0x8000C0140301143F, 0x8000C0140301183F, 0x8001C0140301143F, 0x8001C0140301183F) + FAPI_DBG("Write 0x0018 to PC IO PVT N/P FET driver control registers to deassert ZCTL reset."); + l_data.setBit<59>().setBit<60>(); + FAPI_TRY( mss::scom_blastah(l_ports, MCA_DDRPHY_PC_IO_PVT_FET_CONTROL_P0, l_data) ); + + // + // 6. Write 0x0008 to PC IO PVT N/P FET driver control registers to deassert the impedance controller. + // (SCOM Addr: 0x8000C0140301143F, 0x8000C0140301183F, 0x8001C0140301143F, 0x8001C0140301183F) + + FAPI_DBG("Write 0x0008 to PC IO PVT N/P FET driver control registers to deassert the impedance controller"); + l_data.clearBit<59>().setBit<60>(); + FAPI_TRY( mss::scom_blastah(l_ports, MCA_DDRPHY_PC_IO_PVT_FET_CONTROL_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// @brief Change mclk low +/// @param[in] mcbist target +/// @param[in] mss::HIGH or mss::LOW - desired state. +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode change_force_mclk_low (const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target, + const mss::states i_state) +{ + + // Working with Goldade, we learned this: + // From John Bailas: 9:49:46 AM: you need to use that force_mclk_low signal, it needs to be asserted which + // forces RESETN on, and CK/CKN to 0, and all other address/cmd to Z. Then the MC has to establish valid + // values for all signals after PHY reset before force_mclk_low gets de-asserted + // And to celar up the ambiguity of "on" <grin> + // John S. Bialas Jr: Hi Brian, force_mclk_low should force RESETN, CK, and CK# to 0, and all + // other address/command signals to Z + // So, this should be enough to get us going. BRS + + // Additionally: from John Bailas + // The PHY should be reset and initialized such that it can synchronously control the RESETN, CK, and CKN + // to the DIMM, then force_mclk_low can be de-asserted and the control of those signals will be synchronously + // maintained. Beyond that the remainder of the DIMM init sequence can be performed, ie. start CK/CKN, + // de-assert RESETN, etc + + fapi2::buffer<uint64_t> l_data; + + FAPI_DBG("force mclk %s for all ports", (i_state == mss::LOW ? "low" : "high") ); + + // Might as well do this for all the ports while we're here. + for (auto p : i_target.getChildren<TARGET_TYPE_MCA>()) + { + FAPI_TRY(fapi2::getScom( p, MCA_MBA_FARB5Q, l_data)); + + if (i_state == mss::HIGH) + { + l_data.setBit<MCA_MBA_FARB5Q_CFG_FORCE_MCLK_LOW_N>(); + } + else + { + l_data.clearBit<MCA_MBA_FARB5Q_CFG_FORCE_MCLK_LOW_N>(); + } + + FAPI_TRY(fapi2::putScom( p, MCA_MBA_FARB5Q, l_data)); + } + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Unset the PLL and check to see that the PLL's have started +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode deassert_pll_reset( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + +#ifdef KNOW_ADR_DLL_PROCESS + static const uint64_t dp16_lock_mask = 0x000000000000FFFE; + static const uint64_t ad_lock_mask = fapi2::buffer<uint64_t>().setBit<48>().setBit<49>(); +#endif + + // + // Write 0x4000 into the PC Resets Registers. This deasserts the PLL_RESET and leaves the SYSCLK_RESET bit active + // (SCOM Addr: 0x8000C00E0301143F, 0x8001C00E0301143F, 0x8000C00E0301183F, 0x8001C00E0301183F) + FAPI_DBG("Write 0x4000 into the PC Resets Regs. This deasserts the PLL_RESET and leaves the SYSCLK_RESET bit active"); + + l_data.setBit<MCA_DDRPHY_PC_RESETS_P0_SYSCLK_RESET>(); + FAPI_TRY( mss::scom_blastah(i_target.getChildren<TARGET_TYPE_MCA>(), MCA_DDRPHY_PC_RESETS_P0, l_data) ); + + // + // Wait at least 1 millisecond to allow the PLLs to lock. Otherwise, poll the PC DP18 PLL Lock Status + // and the PC AD32S PLL Lock Status to determine if all PLLs have locked. + // PC DP18 PLL Lock Status should be 0xF800: (SCOM Addr: 0x8000C0000301143F, 0x8001C0000301143F, 0x8000C0000301183F, 0x8001C0000301183F) + // PC AD32S PLL Lock Status should be 0xC000: (SCOM Addr: 0x8000C0010301143F, 0x8001C0010301143F, 0x8000C0010301183F, 0x8001C0010301183F) + +#ifdef KNOW_ADR_DLL_PROCESS + // Poll for lock bits + FAPI_DBG("Poll until DP18 and AD32S PLLs have locked"); + + do + { + // Set in the CHECK_PLL macro + done_polling = true; + + fapi2::delay(DELAY_1US, cycles_to_simcycles(us_to_cycles(i_target, 1))); + + // Note: in the latest scomdef this is DP16 BRS + // Note: Not sure what the proper registers here are. I took the following old addresses from Ed's + // version of the code and mapped the addresses to the latests mc_scom_addresses.H. This needs to + // be fixed up when the extended addressing is fixed up. BRS + + // CONST_UINT64_T(DDRPHY_PC_DP18_PLL_LOCK_STATUS_P0_0x8000C0000701143F, ULL(0x8000C0000701143F) ); + CHECK_PLL( l_target_proc, MCA_0_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P0, l_data, dp16_lock_mask ); + + // CONST_UINT64_T(DDRPHY_PC_DP18_PLL_LOCK_STATUS_P1_0x8001C0000701143F, ULL(0x8001C0000701143F) ); + CHECK_PLL( l_target_proc, MCA_1_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P1, l_data, dp16_lock_mask ); + + // CONST_UINT64_T(DDRPHY_PC_DP18_PLL_LOCK_STATUS_P2_0x8000C0000701183F, ULL(0x8000C0000701183F) ); + CHECK_PLL( l_target_proc, MCA_2_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P2, l_data, dp16_lock_mask ); + + // CONST_UINT64_T(DDRPHY_PC_DP18_PLL_LOCK_STATUS_P3_0x8001C0000701183F, ULL(0x8001C0000701183F) ); + CHECK_PLL( l_target_proc, MCA_3_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P3, l_data, dp16_lock_mask ); + + // CONST_UINT64_T( DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P0_0x8000C0010701143F, ULL(0x8000C0010701143F) ); + CHECK_PLL( l_target_proc, MCA_0_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P0, l_data, ad_lock_mask ); + + // CONST_UINT64_T( DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P1_0x8001C0010701143F, ULL(0x8001C0010701143F) ); + CHECK_PLL( l_target_proc, MCA_1_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P1, l_data, ad_lock_mask ); + + // CONST_UINT64_T( DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P2_0x8000C0010701183F, ULL(0x8000C0010701183F) ); + CHECK_PLL( l_target_proc, MCA_2_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P2, l_data, ad_lock_mask ); + + // CONST_UINT64_T( DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P3_0x8001C0010701183F, ULL(0x8001C0010701183F) ); + CHECK_PLL( l_target_proc, MCA_3_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P3, l_data, ad_lock_mask ); + } + while (!done_polling && --max_poll_loops); + + // If we ran out of iterations, report we have a pll lock failure. + if (max_poll_loops == 0) + { + FAPI_ERR("DDR PHY PLL failed to lock for %s", mss::c_str(i_target)); + FFDC_PLL( l_target_proc, MCA_0_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P0, l_data, dp16_lock_mask, + fapi2::MSS_DP16_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_1_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P1, l_data, dp16_lock_mask, + fapi2::MSS_DP16_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_2_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P2, l_data, dp16_lock_mask, + fapi2::MSS_DP16_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_3_DDRPHY_PC_DP18_PLL_LOCK_STATUS_P3, l_data, dp16_lock_mask, + fapi2::MSS_DP16_PLL_FAILED_TO_LOCK() ); + + FFDC_PLL( l_target_proc, MCA_0_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P0, l_data, ad_lock_mask, + fapi2::MSS_AD32S_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_1_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P1, l_data, ad_lock_mask, + fapi2::MSS_AD32S_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_2_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P2, l_data, ad_lock_mask, + fapi2::MSS_AD32S_PLL_FAILED_TO_LOCK() ); + FFDC_PLL( l_target_proc, MCA_3_DDRPHY_PC_AD32S_PLL_LOCK_STATUS_P3, l_data, ad_lock_mask, + fapi2::MSS_AD32S_PLL_FAILED_TO_LOCK() ); + } + +#endif +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief Setup the phase rotator control registers +/// @param[in] the mcbist target +/// @param[in] the value for the registers +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode setup_phase_rotator_control_registers( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target, + const fapi2::buffer<uint64_t>& i_data ) +{ + FAPI_DBG("Write 0x%lx into the ADR SysClk Phase Rotator Control Regs and the DP18 SysClk Phase Rotator Control Regs", + i_data); + + std::vector<uint64_t> addrs( + { + MCA_DDRPHY_ADR_SYSCLK_CNTL_PR_P0_ADR32S0, MCA_DDRPHY_ADR_SYSCLK_CNTL_PR_P0_ADR32S1, + MCA_DDRPHY_DP16_WRCLK_PR_P0_0, MCA_DDRPHY_DP16_WRCLK_PR_P0_1, + MCA_DDRPHY_DP16_WRCLK_PR_P0_2, MCA_DDRPHY_DP16_WRCLK_PR_P0_3, MCA_DDRPHY_DP16_WRCLK_PR_P0_4, + } ); + + FAPI_TRY( mss::scom_blastah(i_target.getChildren<TARGET_TYPE_MCA>(), addrs, i_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Deassetr the sys clk reset +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode deassert_sysclk_reset( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + FAPI_DBG("Write 0x0000 into the PC Resets Register. This deasserts the SysClk Reset."); + FAPI_TRY( mss::scom_blastah(i_target.getChildren<TARGET_TYPE_MCA>(), MCA_DDRPHY_PC_RESETS_P0, 0) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Flush the DDR PHY +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode ddr_phy_flush( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + fapi2::buffer<uint64_t> l_mask; + + FAPI_INF( "Performing mss_ddr_phy_flush routine" ); + + FAPI_INF("ADR/DP18 FLUSH: 1) set PC_POWERDOWN_1 register, powerdown enable(48), flush bit(58)"); + // set MASTER_PD_CNTL bit set WR_FIFO_STAB bit + l_data.setBit<48>().setBit<58>(); + l_mask.setBit<48>().setBit<58>(); + + auto l_ports = i_target.getChildren<TARGET_TYPE_MCA>(); + + for (auto p : l_ports) + { + FAPI_TRY(fapi2::putScomUnderMask(p, MCA_DDRPHY_PC_POWERDOWN_1_P0, l_data, l_mask) ); + } + + fapi2::delay(DELAY_100NS, cycles_to_simcycles(ns_to_cycles(i_target, 100))); + + FAPI_INF("ADR/DP18 FLUSH: 2) clear PC_POWERDOWN_1 register, powerdown enable(48), flush bit(58)"); + + for (auto p : l_ports) + { + FAPI_TRY(fapi2::putScomUnderMask(p, MCA_DDRPHY_PC_POWERDOWN_1_P0, 0, l_mask) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Send a scom to all instances of a block on the phy. +/// @param[in] the MCA target +/// @param[in] the address +/// @param[in] the value +/// @note this iterates creating addresses - needs to change to use the +/// braodcast bits in the phy when we can scom it directly. +/// +static inline fapi2::ReturnCode phy_block_broadcast( const fapi2::Target<TARGET_TYPE_MCA>& i_target, + const uint64_t l_addr, + const fapi2::buffer<uint64_t> i_data) +{ +#ifndef BROADSIDE_SCOM_ONLY + static const size_t PHY_INSTANCE_COUNT = 5; + + // We have to use a dull knife since broadisde scom has to have the address in + // the scomdef. When we get a full system model (or the PIE driver supports scomming + // the PHY) we can use the broadcast bits and iterate over the DIMM ranks. + for (size_t i = 0; i < PHY_INSTANCE_COUNT; ++i) + { + FAPI_TRY( fapi2::putScom(i_target, l_addr | fapi2::buffer<uint64_t>().insertFromRight<18, 4>(i), i_data) ); + } + +fapi_try_exit: + return fapi2::current_err; +#endif +} + +/// +/// @brief Lock dphy_gckn and sysclk +/// @param[in] a port target +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode bang_bang_lock( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + fapi2::buffer<uint64_t> l_start_update(0x8024); + fapi2::buffer<uint64_t> l_stop_update(0x8020); + + // Per Bialas, we don't want to do true alignment in the cycle sim as we have + // a chnace of being off one-tick (which is detremental.) Per his recomendations, + // we write 0's to the control registers and then configure them with x8080. + uint8_t is_sim = 0; + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<TARGET_TYPE_SYSTEM>(), is_sim) ); + + if (is_sim) + { + l_start_update = 0x0; + l_stop_update = 0x8080; + } + + // 9. + FAPI_TRY( mss::setup_phase_rotator_control_registers(i_target, l_start_update), + "set up of phase rotator controls failed (in to cont update) %s", mss::c_str(i_target) ); + + // + // 10.Wait at least 5932 memory clock cycles to allow the clock alignment circuit to perform initial alignment. + // wait 2000 simcycles per Ed Maj, + FAPI_DBG("Wait at least 5932 memory clock cycles to allow the clock alignment circuit to perform initial alignment"); + FAPI_TRY( fapi2::delay(mss::cycles_to_ns(i_target, 5932), 2000) ); + + // + // 11.Write 0x0000 into the PC Resets Register. This deasserts the SysClk Reset + // (SCOM Addr: 0x8000C00E0301143F, 0x8001C00E0301143F, 0x8000C00E0301183F, 0x8001C00E0301183F) + FAPI_TRY( mss::deassert_sysclk_reset(i_target), "deassert_sysclk_reset failed for %s", mss::c_str(i_target) ); + + // 12. + FAPI_TRY( mss::setup_phase_rotator_control_registers(i_target, l_stop_update), + "set up of phase rotator controls failed (out of cont update) %s", mss::c_str(i_target) ); + + // 13. + FAPI_DBG("Wait at least 32 memory clock cycles"); + FAPI_TRY( fapi2::delay(mss::cycles_to_ns(i_target, 32), mss::cycles_to_simcycles(32)) ); + + // Don't bother in cycle sim, we don't do the actual aligment + if (!is_sim) + { + // Check for BB lock. + for (auto p : i_target.getChildren<TARGET_TYPE_MCA>()) + { + FAPI_DBG("Wait for BB lock in status register, bit %u", + MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0_ADR0_BB_LOCK); + + FAPI_ASSERT( mss::poll(p, MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0, mss::poll_parameters(DELAY_100NS), + [](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + FAPI_DBG("stat_reg 0x%llx, remaining: %d", stat_reg, poll_remaining); + return stat_reg.getBit<MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0_ADR0_BB_LOCK>(); + }), + fapi2::MSS_BANG_BANG_FAILED_TO_LOCK().set_MCA_IN_ERROR(p), + "MCA %s failed bang-bang alignment", mss::c_str(p) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Return the DIMM target for the primary rank in the specificed rank pair +/// @param[in] the MCA target +/// @param[in] the rank pair +/// @param[out] fapi2::Target<TARGET_TYPE_DIMM> +/// @return FAPI2_RC_SUCCESS iff ok +/// +template<> +fapi2::ReturnCode rank_pair_primary_to_dimm( + const fapi2::Target<TARGET_TYPE_MCA>& i_target, + const uint64_t i_rp, + fapi2::Target<TARGET_TYPE_DIMM>& o_dimm) +{ + fapi2::buffer<uint64_t> l_data; + fapi2::buffer<uint64_t> l_rank; + uint64_t l_rank_on_dimm; + auto l_dimms = i_target.getChildren<TARGET_TYPE_DIMM>(); + + // Sanity check the rank pair + FAPI_INF("%s rank pair: %d", mss::c_str(i_target), i_rp); + + fapi2::Assert(i_rp < MAX_RANK_PER_DIMM); + + // We need to get the register containing the specification for this rank pair, + // and fish out the primary rank for this rank pair + switch(i_rp) + { + case 0: + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_RANK_PAIR0_P0, l_data) ); + l_data.extractToRight<MCA_DDRPHY_PC_RANK_PAIR0_P0_PRI, + MCA_DDRPHY_PC_RANK_PAIR0_P0_PRI_LEN>(l_rank); + break; + + case 1: + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_RANK_PAIR0_P0, l_data) ); + l_data.extractToRight<MCA_DDRPHY_PC_RANK_PAIR0_P0_PAIR1_PRI, + MCA_DDRPHY_PC_RANK_PAIR0_P0_PAIR1_PRI_LEN>(l_rank); + break; + + case 2: + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_RANK_PAIR1_P0, l_data) ); + l_data.extractToRight<MCA_DDRPHY_PC_RANK_PAIR1_P0_PAIR2_PRI, + MCA_DDRPHY_PC_RANK_PAIR1_P0_PAIR2_PRI_LEN>(l_rank); + break; + + case 3: + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_RANK_PAIR1_P0, l_data) ); + l_data.extractToRight<MCA_DDRPHY_PC_RANK_PAIR1_P0_PAIR3_PRI, + MCA_DDRPHY_PC_RANK_PAIR1_P0_PAIR3_PRI_LEN>(l_rank); + break; + }; + + // Now we need to figure out which DIMM this rank is on. It's either on DIMM0 or DIMM1, and DIMM0 + // has ranks 0-3 and DIMM1 has ranks 4-7. Return the DIMM associated. + l_rank_on_dimm = get_dimm_from_rank(l_rank); + + // Sanity check the DIMM list + FAPI_INF("%s rank is on dimm: %d, number of dimms: %d", mss::c_str(i_target), l_rank_on_dimm, l_dimms.size()); + + fapi2::Assert(l_rank_on_dimm < l_dimms.size()); + + o_dimm = l_dimms[l_rank_on_dimm]; + + return FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief check and process initial cal errors +/// @param[in] the port in question +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff no error +/// +template<> +fapi2::ReturnCode process_initial_cal_errors( const fapi2::Target<TARGET_TYPE_MCA>& i_target ) +{ + static const uint64_t init_cal_err_mask = 0x7FF; + + uint64_t l_errors = 0; + uint64_t l_rank_pairs = 0; + + fapi2::buffer<uint64_t> l_fir_data; + fapi2::buffer<uint64_t> l_err_data; + + fapi2::Target<TARGET_TYPE_DIMM> l_failed_dimm; + + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_APB_FIR_ERR1_P0, l_fir_data) ); + + FAPI_DBG("initial cal FIR: 0x%016llx", uint64_t(l_fir_data)); + + if ((init_cal_err_mask & l_fir_data) == 0) + { + return FAPI2_RC_SUCCESS; + } + + // We have bits to check ... + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_INIT_CAL_ERROR_P0, l_err_data) ); + + l_err_data.extractToRight<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_WR_LEVEL, 11>(l_errors); + l_err_data.extractToRight<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_RANK_PAIR, + MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_RANK_PAIR_LEN>(l_rank_pairs); + FAPI_DBG("initial cal err: 0x%016llx, rp: 0x%016llx (0x%016llx)", l_errors, l_rank_pairs, uint64_t(l_err_data)); + + if ((l_rank_pairs == 0) || (l_errors == 0)) + { + FAPI_INF("Initial CAL FIR is 0x%016llx but missing info in the error register: 0x%016llx", + l_fir_data, l_err_data); + return FAPI2_RC_SUCCESS; + } + + // Get the DIMM which failed. We should only have one rank pair as we calibrate the + // rank pairs individually (we do this so we can see which DIMM failed if more than one + // fails ...) Note first_bit_set gives a bit position (0 being left most.) So, the rank + // in question is the bit postion minus the position of the 0th rank in the register. + // (the rank bits are bits 60:63, for example, so rank 0 is in position 60) + FAPI_TRY( mss::rank_pair_primary_to_dimm(i_target, + mss::first_bit_set(l_rank_pairs) - MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_RANK_PAIR, + l_failed_dimm) ); + FAPI_ERR("initial cal failed for %s", mss::c_str(l_failed_dimm)); + + // So we can do a few things here. If we're aborting on the first calibration error, + // we only expect to have one error bit set. If we ran all the calibrations, we can + // either have one bit set or more than one bit set. If we have more than one bit set + // the result is the same - a broken DIMM which will be deconfigured. So put enough + // information in the FFDC for the lab but we don't need one error for every cal fail. + FAPI_ASSERT(mss::bit_count(l_errors) == 1, + fapi2::MSS_DRAMINIT_TRAINING_MULTIPLE_ERRORS() + .set_FAILED_STEPS(uint64_t(l_err_data)) + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed multiple training steps. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_WR_LEVEL>(), + fapi2::MSS_DRAMINIT_TRAINING_WR_LVL_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed write leveling. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_INITIAL_PAT_WRITE>(), + fapi2::MSS_DRAMINIT_TRAINING_INITIAL_PAT_WRITE_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed initial pattern write. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_DQS_ALIGN>(), + fapi2::MSS_DRAMINIT_TRAINING_DQS_ALIGNMENT_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed DQS alignenment. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_RDCLK_ALIGN>(), + fapi2::MSS_DRAMINIT_TRAINING_RD_CLK_SYS_CLK_ALIGNMENT_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed read clk alignenment. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_READ_CTR>(), + fapi2::MSS_DRAMINIT_TRAINING_RD_CENTERING_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed read centering. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_WRITE_CTR>(), + fapi2::MSS_DRAMINIT_TRAINING_WR_CENTERING_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed write centering. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_INITIAL_COARSE_WR>(), + fapi2::MSS_DRAMINIT_TRAINING_INITIAL_COARSE_WR_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed initial coarse write. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_COARSE_RD>(), + fapi2::MSS_DRAMINIT_TRAINING_COARSE_RD_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed coarse read. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_CUSTOM_RD>(), + fapi2::MSS_DRAMINIT_TRAINING_CUSTOM_PATTERN_RD_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed custom read. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_CUSTOM_WR>(), + fapi2::MSS_DRAMINIT_TRAINING_CUSTOM_PATTERN_WR_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed custom write. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + + FAPI_ASSERT( ! l_err_data.getBit<MCA_DDRPHY_PC_INIT_CAL_ERROR_P0_DIGITAL_EYE>(), + fapi2::MSS_DRAMINIT_TRAINING_DIGITAL_EYE_ERROR() + .set_PORT_POSITION(mss::pos(i_target)) + .set_RANKGROUP_POSITION(l_rank_pairs) + .set_TARGET_IN_ERROR(l_failed_dimm), + "Initial CAL failed digital eye. dimm: %s, cal err: 0x%016llx", + mss::c_str(l_failed_dimm), uint64_t(l_err_data) + ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Setup the PC CONFIG0 register +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode set_pc_config0(const fapi2::Target<TARGET_TYPE_MCA>& i_target) +{ + fapi2::buffer<uint64_t> l_data; + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_CONFIG0_P0, l_data) ); + + // Note: This needs to get the DRAM gen from an attribute. - 0x1 is DDR4 Note for Nimbus PHY + // this is ignored and hard-wired to DDR4, per John Bialas 10/15 BRS + l_data.insertFromRight<MCA_DDRPHY_PC_CONFIG0_P0_PROTOCOL, MCA_DDRPHY_PC_CONFIG0_P0_PROTOCOL_LEN>(0x1); + + l_data.setBit<MCA_DDRPHY_PC_CONFIG0_P0_DDR4_CMD_SIG_REDUCTION>(); + l_data.setBit<MCA_DDRPHY_PC_CONFIG0_P0_DDR4_VLEVEL_BANK_GROUP>(); + + FAPI_DBG("phy pc_config0 0x%0llx", l_data); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_CONFIG0_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Setup the PC CONFIG1 register +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +fapi2::ReturnCode set_pc_config1(const fapi2::Target<TARGET_TYPE_MCA>& i_target) +{ + // Static table of PHY config values for MEMORY_TYPE. + // [EMPTY, RDIMM, CDIMM, or LRDIMM][EMPTY, DDR3 or DDR4] + static const uint64_t memory_type[4][3] = + { + { 0, 0, 0 }, // Empty, never really used. + { 0, 0b001, 0b101 }, // RDIMM + { 0, 0b000, 0b000 }, // CDIMM + { 0, 0b011, 0b111 }, // LRDIMM + }; + + fapi2::buffer<uint64_t> l_data; + + uint8_t l_rlo = 0; + uint8_t l_wlo = 0; + uint8_t l_dram_gen[MAX_DIMM_PER_PORT] = {0}; + uint8_t l_dimm_type[MAX_DIMM_PER_PORT] = {0}; + uint8_t l_custom_dimm[MAX_DIMM_PER_PORT] = {0}; + uint8_t l_type_index = 0; + uint8_t l_gen_index = 0; + + FAPI_TRY( mss::vpd_rlo(i_target, l_rlo) ); + FAPI_TRY( mss::vpd_wlo(i_target, l_wlo) ); + FAPI_TRY( mss::eff_dram_gen(i_target, &(l_dram_gen[0])) ); + FAPI_TRY( mss::eff_dimm_type(i_target, &(l_dimm_type[0])) ); + FAPI_TRY( mss::eff_custom_dimm(i_target, &(l_custom_dimm[0])) ); + + // There's no way to configure the PHY for more than one value. However, we don't know if there's + // a DIMM in one slot, the other or double drop. So we do a little gyration here to make sure + // we have one of the two values (and assume effective config caught a bad config.) + l_type_index = (l_custom_dimm[0] | l_custom_dimm[1]) == fapi2::ENUM_ATTR_EFF_CUSTOM_DIMM_YES ? + 2 : l_dimm_type[0] | l_dimm_type[1]; + l_gen_index = l_dram_gen[0] | l_dram_gen[1]; + + // FOR NIMBUS PHY (as the protocol choice above is) BRS + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_PC_CONFIG1_P0, l_data) ); + + l_data.insertFromRight<MCA_DDRPHY_PC_CONFIG1_P0_MEMORY_TYPE, + MCA_DDRPHY_PC_CONFIG1_P0_MEMORY_TYPE_LEN>(memory_type[l_type_index][l_gen_index]); + l_data.insertFromRight<MCA_DDRPHY_PC_CONFIG1_P0_READ_LATENCY_OFFSET, + MCA_DDRPHY_PC_CONFIG1_P0_READ_LATENCY_OFFSET_LEN>(l_rlo); + l_data.insertFromRight<MCA_DDRPHY_PC_CONFIG1_P0_WRITE_LATENCY_OFFSET, + MCA_DDRPHY_PC_CONFIG1_P0_WRITE_LATENCY_OFFSET_LEN>(l_wlo); + + l_data.setBit<MCA_DDRPHY_PC_CONFIG1_P0_DDR4_LATENCY_SW>(); + + FAPI_DBG("phy pc_config1 0x%0llx", l_data); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_CONFIG1_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Perform initializations for the PHY +/// @param[in] i_target, the MCBIST which has the PHYs to initialize +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode phy_scominit(const fapi2::Target<TARGET_TYPE_MCBIST>& i_target) +{ + // Returned from set_rank_pairs, it tells us how many rank pairs we configured on this port. + std::vector<uint64_t> l_pairs; + + for( auto p : i_target.getChildren<TARGET_TYPE_MCA>()) + { + // The following registers must be configured to the correct operating environment: + + // Undocumented, noted by Bialas + FAPI_TRY( mss::set_pc_config0(p) ); + FAPI_TRY( mss::set_pc_config1(p) ); + + // Section 5.2.1.3 PC Rank Pair 0 on page 177 + // Section 5.2.1.4 PC Rank Pair 1 on page 179 + FAPI_TRY( mss::set_rank_pairs(p) ); + + // Section 5.2.4.1 DP16 Data Bit Enable 0 on page 284 + // Section 5.2.4.2 DP16 Data Bit Enable 1 on page 285 + // Section 5.2.4.3 DP16 Data Bit Disable 0 on page 288 + // Section 5.2.4.4 DP16 Data Bit Disable 1 on page 289 + FAPI_TRY( mss::dp16::write_data_bit_enable(p) ); + FAPI_TRY( mss::dp16::set_bad_bits(p) ); + + FAPI_TRY( mss::get_rank_pairs(p, l_pairs) ); + + // Section 5.2.4.8 DP16 Write Clock Enable & Clock Selection on page 301 + FAPI_TRY( mss::dp16::write_clock_enable(p, l_pairs) ); + FAPI_TRY( mss::dp16::read_clock_enable(p, l_pairs) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +} diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.H b/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.H new file mode 100644 index 000000000..dd7ee3fa4 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.H @@ -0,0 +1,832 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/phy/ddr_phy.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file ddr_phy.H +/// @brief Subroutines to manipulate the phy, or used during phy procedures +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_DDR_PHY_H_ +#define _MSS_DDR_PHY_H_ + +#include <fapi2.H> +#include <mss_attribute_accessors.H> + +// Helper macro to condense the checking for PLL lock +#define CHECK_PLL( __target, __register, __buffer, __mask ) \ + FAPI_TRY(fapi2::getScom( __target, __register, __buffer)); \ + if ((__buffer & __mask) != __mask) \ + { \ + FAPI_INF("PLL 0x%lx failed to lock 0x%lx", __register, __buffer); \ + done_polling = false; \ + continue; \ + } + +// Helper macro to condense the checking for PLL lock +#define FFDC_PLL( __target, __register, __buffer, __mask, __ffdc_object ) \ + FAPI_ASSERT( ((__buffer & __mask) == __mask), \ + __ffdc_object.set_EXPECTED_STATUS(__mask) \ + .set_ACTUAL_STATUS(__buffer) \ + .set_REGISTER(__register) \ + .set_MCBIST_IN_ERROR(__target), \ + "PLL 0x%llx failed to lock 0x%llx", __register, __buffer); + +namespace mss +{ + +/// +/// @brief Perform initializations for the PHY +/// @param[in] i_target, the MCBIST which has the PHYs to initialize +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode phy_scominit(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target); + +/// +/// @brief change resetn to the given state +/// @param[in] i_target, the mcbist +/// @param[in] the desired state +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode change_resetn( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target, states i_state ); + +/// +/// @brief perform the zctl toggle process +/// @param[in] i_target, the mcbist for the reset recover +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode toggle_zctl( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// @brief Change mclk low +/// @param[in] mcbist target +/// @param[in] mss::HIGH or mss::LOW - desired state. +/// @return FAPI2_RC_SUCCESS iff ok +/// +fapi2::ReturnCode change_force_mclk_low (const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target, + const mss::states i_state); +/// +/// @brief Unset the PLL and check to see that the PLL's have started +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode deassert_pll_reset( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Setup the phase rotator control registers +/// @param[in] the mcbist target +/// @param[in] the value for the registers +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode setup_phase_rotator_control_registers( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target, + const fapi2::buffer<uint64_t>& i_data ); + +/// +/// @brief Deassetr the sys clk reset +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode deassert_sysclk_reset( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Flush the DDR PHY +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode ddr_phy_flush( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Reset the training delay configureation +/// @param[in] the mcbist target +/// @return FAPI2_RC_SUCCES iff ok +/// +fapi2::ReturnCode reset_delay_values( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Lock dphy_gckn and sysclk +/// @param[in] a MCBIST target +/// @return FAPI2_RC_SUCCESs iff ok +/// +fapi2::ReturnCode bang_bang_lock( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Configure the DP16 sysclk +/// @param[in] a MCBIST target +/// @return FAPI2_RC_SUCCESs iff ok +/// +fapi2::ReturnCode setup_dp16_sysclk( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief check and process initial cal errors +/// @tparam T, the type of the target in question +/// @param[in] the port in question +/// @return fapi2::ReturnCode, FAPI2_RC_SUCCESS iff no error +/// +template< fapi2::TargetType T > +fapi2::ReturnCode process_initial_cal_errors( const fapi2::Target<T>& i_target ); + +/// +/// @brief Dump all the cal config registers +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode dump_cal_registers( const fapi2::Target<T>& i_target ); + +/// +/// @brief Dump all the cal config registers +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode dump_cal_registers( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + // Note: Can we wrap this in an ifdef DEBUG or something? BRS + static const std::vector< std::pair<uint64_t, char const*> > l_registers = + { + { MCA_DDRPHY_PC_INIT_CAL_STATUS_P0, "PC_INIT_CAL_STATUS" }, + { MCA_DDRPHY_PC_INIT_CAL_ERROR_P0, "PC_INIT_CAL_ERROR" }, + { MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0, "PC_INIT_CAL_CONFIG0" }, + { MCA_DDRPHY_PC_INIT_CAL_CONFIG1_P0, "PC_INIT_CAL_CONFIG1" }, + { MCA_DDRPHY_PC_RANK_PAIR0_P0, "PC_RANK_PAIR0" }, + { MCA_DDRPHY_PC_RANK_PAIR1_P0, "PC_RANK_PAIR1" }, + { MCA_DDRPHY_PC_RANK_PAIR2_P0, "PC_RANK_PAIR2" }, + { MCA_DDRPHY_PC_RANK_PAIR3_P0, "PC_RANK_PAIR3" }, + + { MCA_DDRPHY_PC_CSID_CFG_P0, "PC_CSID_CFG" }, + + { MCA_DDRPHY_WC_CONFIG0_P0, "WC_CONFIG0" }, + { MCA_DDRPHY_WC_CONFIG1_P0, "WC_CONFIG1" }, + { MCA_DDRPHY_WC_CONFIG2_P0, "WC_CONFIG2" }, + { MCA_DDRPHY_WC_CONFIG3_P0, "WC_CONFIG3" }, + + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_0, "DP16_DATA_BIT_ENABLE0_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_1, "DP16_DATA_BIT_ENABLE0_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_2, "DP16_DATA_BIT_ENABLE0_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_3, "DP16_DATA_BIT_ENABLE0_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_4, "DP16_DATA_BIT_ENABLE0_4"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0, "DP16_DATA_BIT_ENABLE1_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_1, "DP16_DATA_BIT_ENABLE1_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_2, "DP16_DATA_BIT_ENABLE1_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_3, "DP16_DATA_BIT_ENABLE1_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_4, "DP16_DATA_BIT_ENABLE1_4"}, + + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_0, "DP16_DATA_BIT_DISABLE0_RP0_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_1, "DP16_DATA_BIT_DISABLE0_RP0_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_2, "DP16_DATA_BIT_DISABLE0_RP0_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_3, "DP16_DATA_BIT_DISABLE0_RP0_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP0_P0_4, "DP16_DATA_BIT_DISABLE0_RP0_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP1_P0_0, "DP16_DATA_BIT_DISABLE0_RP1_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP1_P0_1, "DP16_DATA_BIT_DISABLE0_RP1_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP1_P0_2, "DP16_DATA_BIT_DISABLE0_RP1_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP1_P0_3, "DP16_DATA_BIT_DISABLE0_RP1_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP1_P0_4, "DP16_DATA_BIT_DISABLE0_RP1_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP2_P0_0, "DP16_DATA_BIT_DISABLE0_RP2_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP2_P0_1, "DP16_DATA_BIT_DISABLE0_RP2_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP2_P0_2, "DP16_DATA_BIT_DISABLE0_RP2_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP2_P0_3, "DP16_DATA_BIT_DISABLE0_RP2_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP2_P0_4, "DP16_DATA_BIT_DISABLE0_RP2_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP3_P0_0, "DP16_DATA_BIT_DISABLE0_RP3_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP3_P0_1, "DP16_DATA_BIT_DISABLE0_RP3_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP3_P0_2, "DP16_DATA_BIT_DISABLE0_RP3_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP3_P0_3, "DP16_DATA_BIT_DISABLE0_RP3_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE0_RP3_P0_4, "DP16_DATA_BIT_DISABLE0_RP3_4"}, + + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP0_P0_0, "DP16_DATA_BIT_DISABLE1_RP0_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP0_P0_1, "DP16_DATA_BIT_DISABLE1_RP0_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP0_P0_2, "DP16_DATA_BIT_DISABLE1_RP0_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP0_P0_3, "DP16_DATA_BIT_DISABLE1_RP0_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP0_P0_4, "DP16_DATA_BIT_DISABLE1_RP0_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP1_P0_0, "DP16_DATA_BIT_DISABLE1_RP1_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP1_P0_1, "DP16_DATA_BIT_DISABLE1_RP1_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP1_P0_2, "DP16_DATA_BIT_DISABLE1_RP1_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP1_P0_3, "DP16_DATA_BIT_DISABLE1_RP1_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP1_P0_4, "DP16_DATA_BIT_DISABLE1_RP1_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP2_P0_0, "DP16_DATA_BIT_DISABLE1_RP2_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP2_P0_1, "DP16_DATA_BIT_DISABLE1_RP2_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP2_P0_2, "DP16_DATA_BIT_DISABLE1_RP2_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP2_P0_3, "DP16_DATA_BIT_DISABLE1_RP2_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP2_P0_4, "DP16_DATA_BIT_DISABLE1_RP2_4"}, + + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP3_P0_0, "DP16_DATA_BIT_DISABLE1_RP3_0"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP3_P0_1, "DP16_DATA_BIT_DISABLE1_RP3_1"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP3_P0_2, "DP16_DATA_BIT_DISABLE1_RP3_2"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP3_P0_3, "DP16_DATA_BIT_DISABLE1_RP3_3"}, + { MCA_DDRPHY_DP16_DATA_BIT_DISABLE1_RP3_P0_4, "DP16_DATA_BIT_DISABLE1_RP3_4"}, + + + + { MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_0, "DP16_WRCLK_EN_RP0_0" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_1, "DP16_WRCLK_EN_RP0_1" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_2, "DP16_WRCLK_EN_RP0_2" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_3, "DP16_WRCLK_EN_RP0_3" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_4, "DP16_WRCLK_EN_RP0_4" }, + + { MCA_DDRPHY_DP16_WRCLK_EN_RP1_P0_0, "DP16_WRCLK_EN_RP1_0" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP1_P0_1, "DP16_WRCLK_EN_RP1_1" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP1_P0_2, "DP16_WRCLK_EN_RP1_2" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP1_P0_3, "DP16_WRCLK_EN_RP1_3" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP1_P0_4, "DP16_WRCLK_EN_RP1_4" }, + + { MCA_DDRPHY_DP16_WRCLK_EN_RP2_P0_0, "DP16_WRCLK_EN_RP2_0" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP2_P0_1, "DP16_WRCLK_EN_RP2_1" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP2_P0_2, "DP16_WRCLK_EN_RP2_2" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP2_P0_3, "DP16_WRCLK_EN_RP2_3" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP2_P0_4, "DP16_WRCLK_EN_RP2_4" }, + + { MCA_DDRPHY_DP16_WRCLK_EN_RP3_P0_0, "DP16_WRCLK_EN_RP3_0" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP3_P0_1, "DP16_WRCLK_EN_RP3_1" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP3_P0_2, "DP16_WRCLK_EN_RP3_2" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP3_P0_3, "DP16_WRCLK_EN_RP3_3" }, + { MCA_DDRPHY_DP16_WRCLK_EN_RP3_P0_4, "DP16_WRCLK_EN_RP3_4" }, + + + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_0, "DP16_DQS_GATE_DELAY_RP0_0" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_1, "DP16_DQS_GATE_DELAY_RP0_1" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_2, "DP16_DQS_GATE_DELAY_RP0_2" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_3, "DP16_DQS_GATE_DELAY_RP0_3" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_4, "DP16_DQS_GATE_DELAY_RP0_4" }, + + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP1_P0_0, "DP16_DQS_GATE_DELAY_RP1_0" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP1_P0_1, "DP16_DQS_GATE_DELAY_RP1_1" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP1_P0_2, "DP16_DQS_GATE_DELAY_RP1_2" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP1_P0_3, "DP16_DQS_GATE_DELAY_RP1_3" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP1_P0_4, "DP16_DQS_GATE_DELAY_RP1_4" }, + + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP2_P0_0, "DP16_DQS_GATE_DELAY_RP2_0" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP2_P0_1, "DP16_DQS_GATE_DELAY_RP2_1" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP2_P0_2, "DP16_DQS_GATE_DELAY_RP2_2" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP2_P0_3, "DP16_DQS_GATE_DELAY_RP2_3" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP2_P0_4, "DP16_DQS_GATE_DELAY_RP2_4" }, + + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP3_P0_0, "DP16_DQS_GATE_DELAY_RP3_0" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP3_P0_1, "DP16_DQS_GATE_DELAY_RP3_1" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP3_P0_2, "DP16_DQS_GATE_DELAY_RP3_2" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP3_P0_3, "DP16_DQS_GATE_DELAY_RP3_3" }, + { MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP3_P0_4, "DP16_DQS_GATE_DELAY_RP3_4" }, + + + + + { MCA_DDRPHY_DP16_SYSCLK_PR0_P0_0, "DP16_SYSCLK_PR0_P0_0" }, + { MCA_DDRPHY_DP16_SYSCLK_PR0_P0_1, "DP16_SYSCLK_PR0_P0_1" }, + { MCA_DDRPHY_DP16_SYSCLK_PR0_P0_2, "DP16_SYSCLK_PR0_P0_2" }, + { MCA_DDRPHY_DP16_SYSCLK_PR0_P0_3, "DP16_SYSCLK_PR0_P0_3" }, + { MCA_DDRPHY_DP16_SYSCLK_PR0_P0_4, "DP16_SYSCLK_PR0_P0_4" }, + + + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_0, "DP16_READ_CLOCK_RANK_PAIR0_0" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_1, "DP16_READ_CLOCK_RANK_PAIR0_1" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_2, "DP16_READ_CLOCK_RANK_PAIR0_2" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_3, "DP16_READ_CLOCK_RANK_PAIR0_3" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_4, "DP16_READ_CLOCK_RANK_PAIR0_4" }, + + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR1_P0_0, "DP16_READ_CLOCK_RANK_PAIR1_0" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR1_P0_1, "DP16_READ_CLOCK_RANK_PAIR1_1" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR1_P0_2, "DP16_READ_CLOCK_RANK_PAIR1_2" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR1_P0_3, "DP16_READ_CLOCK_RANK_PAIR1_3" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR1_P0_4, "DP16_READ_CLOCK_RANK_PAIR1_4" }, + + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR2_P0_0, "DP16_READ_CLOCK_RANK_PAIR2_0" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR2_P0_1, "DP16_READ_CLOCK_RANK_PAIR2_1" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR2_P0_2, "DP16_READ_CLOCK_RANK_PAIR2_2" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR2_P0_3, "DP16_READ_CLOCK_RANK_PAIR2_3" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR2_P0_4, "DP16_READ_CLOCK_RANK_PAIR2_4" }, + + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR3_P0_0, "DP16_READ_CLOCK_RANK_PAIR3_0" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR3_P0_1, "DP16_READ_CLOCK_RANK_PAIR3_1" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR3_P0_2, "DP16_READ_CLOCK_RANK_PAIR3_2" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR3_P0_3, "DP16_READ_CLOCK_RANK_PAIR3_3" }, + { MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR3_P0_4, "DP16_READ_CLOCK_RANK_PAIR3_4" }, + + }; + + for (auto r : l_registers) + { + fapi2::buffer<uint64_t> l_data; + FAPI_TRY( fapi2::getScom(i_target, r.first, l_data) ); + FAPI_DBG("dump %s: 0x%llx 0x%llx", r.second, r.first, l_data); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Setup all the cal config register +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @param[in] the vector of currently configured rank pairs +/// @param[in] fapi2::buffer<uint8_t> representing the cal steps to enable +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode setup_cal_config( const fapi2::Target<T>& i_target, + const std::vector<uint64_t> i_rank_pairs, + const fapi2::buffer<uint16_t> i_cal_steps_enabled); + +/// +/// @brief Setup all the cal config register +/// @param[in] the MCA target associated with this cal setup +/// @param[in] the vector of currently configured rank pairs +/// @param[in] fapi2::buffer<uint8_t> representing the cal steps to enable +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode setup_cal_config( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + const std::vector<uint64_t> i_rank_pairs, + const fapi2::buffer<uint16_t> i_cal_steps_enabled) +{ + fapi2::buffer<uint64_t> l_cal_config; + + // This is the buffer which will be written to CAL_CONFIG0. It starts + // life assuming no cal sequences, no rank pairs - but we set the abort-on-error + // bit ahead of time. + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ABORT_ON_ERROR>(CAL_ABORT_ON_ERROR); + + // Sadly, the bits in the register don't align directly with the bits in the attribute. + // So, arrange the bits accordingly and write the config register. + { + // Skip EXT_ZQCAL as it's not in the config register - we do it outside. + // Loop (unrolled because static) over the remaining bits. + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_WR_LEVEL>( + i_cal_steps_enabled.getBit<WR_LEVEL>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_DQS_ALIGN>( + i_cal_steps_enabled.getBit<DQS_ALIGN>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_RDCLK_ALIGN>( + i_cal_steps_enabled.getBit<RDCLK_ALIGN>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_READ_CTR>( + i_cal_steps_enabled.getBit<READ_CTR>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_WRITE_CTR>( + i_cal_steps_enabled.getBit<WRITE_CTR>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_INITIAL_COARSE_WR>( + i_cal_steps_enabled.getBit<COARSE_WR>()); + l_cal_config.writeBit<MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_COARSE_RD>( + i_cal_steps_enabled.getBit<COARSE_RD>()); + } + + // Note: This rank encoding isn't used if the cal is initiated from the CCS engine + // as they use the recal inteface. + // Configure the rank pairs + for (auto rp : i_rank_pairs) + { + l_cal_config.setBit(MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0_ENA_RANK_PAIR + rp); + } + + FAPI_INF("cal_config for %s: 0x%llx", mss::c_str(i_target), l_cal_config); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0, l_cal_config) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Setup all the cal config register +/// @param[in] the target associated with this cal setup +/// @param[in] i_rank, one currently configured rank pairs +/// @param[in] fapi2::buffer<uint8_t> representing the cal steps to enable +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +inline fapi2::ReturnCode setup_cal_config( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + const uint64_t i_rank, + const fapi2::buffer<uint16_t> i_cal_steps_enabled) +{ + std::vector< uint64_t > l_ranks({i_rank}); + return setup_cal_config(i_target, l_ranks, i_cal_steps_enabled); +} + +/// +/// @brief Setup wc_config0 +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_wc_config0( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup wc_config0 +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_wc_config0( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + uint8_t l_is_sim = 0; + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_is_sim) ); + + // This is a simplification - in sim we don't have DQS wire delays so we don't acccount for them BRS + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG0_P0_TWLO_TWLOE, + MCA_DDRPHY_WC_CONFIG0_P0_TWLO_TWLOE_LEN>(mss::twlo_twloe(i_target)); + + // WL_ONE_DQS_PULSE = enable (one pulse) + l_data.setBit<MCA_DDRPHY_WC_CONFIG0_P0_WL_ONE_DQS_PULSE>(); + + // FW_WR_RD [same formula as RD_WR? max(tWTR+11,AL+tRTP+3), ATTR_EFF_DRAM_AL(0,1,2)] + // 57:62, 0b000000, (def_is_sim); # is this max? + // 57:62, 0b100000, any; # dd0 = 17 clocks, now 32 from SWyatt + { + const uint64_t FW_WR_RD = l_is_sim ? 0b000000 : 0b100000; + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG0_P0_FW_WR_RD, + MCA_DDRPHY_WC_CONFIG0_P0_FW_WR_RD_LEN>(FW_WR_RD); + } + + // 63, 0b0, any; # CUSTOM_INIT_WRITE + l_data.clearBit<MCA_DDRPHY_WC_CONFIG0_P0_CUSTOM_INIT_WRITE>(); + + FAPI_DBG("wc_config0 0x%llx (tWLO_tWLOE: %d)", l_data, mss::twlo_twloe(i_target)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_WC_CONFIG0_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + + + +/// +/// @brief Setup wc_config1 +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_wc_config1( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup wc_config1 +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_wc_config1( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + // Reset WC_CONFIG1 with the values directly from the PHY databook + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG1_P0_BIG_STEP, MCA_DDRPHY_WC_CONFIG1_P0_BIG_STEP_LEN>(WR_LVL_BIG_STEP); + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG1_P0_SMALL_STEP, MCA_DDRPHY_WC_CONFIG1_P0_SMALL_STEP_LEN>(WR_LVL_SMALL_STEP); + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG1_P0_WR_PRE_DLY, + MCA_DDRPHY_WC_CONFIG1_P0_WR_PRE_DLY_LEN>(WR_LVL_PRE_DLY); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_WC_CONFIG1_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + +/// +/// @brief Setup wc_config2 +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_wc_config2( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup wc_config2 +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_wc_config2( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG2_P0_NUM_VALID_SAMPLES, + MCA_DDRPHY_WC_CONFIG2_P0_NUM_VALID_SAMPLES_LEN>(WR_LVL_NUM_VALID_SAMPLES); + + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG2_P0_FW_RD_WR, MCA_DDRPHY_WC_CONFIG2_P0_FW_RD_WR_LEN>(WR_CNTR_FW_RD_WR); + + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_WC_CONFIG2_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + + +/// +/// @brief Setup wc_config3 +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_wc_config3( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup wc_config3 +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_wc_config3( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + uint8_t l_is_sim = 0; + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_is_sim) ); + + // MCA_DDRPHY_WC_CONFIG3_P0_MRS_CMD_DQ_ON is 0's + + // 55:60, 0b000000, (def_is_sim); # MRS_CMD_DQ_OFF !! + // 55:60, 0b111111, any ; # MRS_CMD_DQ_OFF !! + { + const uint64_t CMD_DQ_OFF = l_is_sim ? 0b000000 : 0b111111; + l_data.insertFromRight<MCA_DDRPHY_WC_CONFIG3_P0_MRS_CMD_DQ_OFF, + MCA_DDRPHY_WC_CONFIG3_P0_MRS_CMD_DQ_OFF_LEN>(CMD_DQ_OFF); + } + + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_WC_CONFIG3_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + + +/// +/// @brief Setup seq_config0 +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_seq_config0( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup seq_config0 +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_seq_config0( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + + // ATTR_VPD_DRAM_2N_MODE_ENABLED 49, 0b1, (def_2N_mode); # enable 2 cycle addr mode BRS + + FAPI_DBG("seq_config0 0x%llx", l_data); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_CONFIG0_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + + +/// +/// @brief Setup odt_wr/rd_config +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_odt_config( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup odt_wr/rd_config +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_odt_config( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + uint8_t l_odt_rd[MAX_DIMM_PER_PORT][MAX_RANK_PER_DIMM]; + uint8_t l_odt_wr[MAX_DIMM_PER_PORT][MAX_RANK_PER_DIMM]; + + FAPI_TRY( mss::eff_odt_rd(i_target, &(l_odt_rd[0][0])) ); + FAPI_TRY( mss::eff_odt_wr(i_target, &(l_odt_rd[0][0])) ); + + // Nimbus PHY is more or less hard-wired for 2 DIMM/port 4R/DIMM + // So there's not much point in looping over DIMM or ranks. + + // + // ODT Read + // + { + // DPHY01_DDRPHY_SEQ_ODT_RD_CONFIG0_P0 + // 48:55, ATTR_VPD_ODT_RD[0][0][0]; # when Read of Rank0 + // 56:63, ATTR_VPD_ODT_RD[0][0][1]; # when Read of Rank1 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG0_P0_VALUES0, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG0_P0_VALUES0_LEN>(l_odt_rd[0][0]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG0_P0_VALUES1, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG0_P0_VALUES1_LEN>(l_odt_rd[0][1]); + FAPI_DBG("odt_rd_config0: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_RD_CONFIG0_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_RD_CONFIG1_P0 + // 48:55, ATTR_VPD_ODT_RD[0][0][2]; # when Read of Rank2 + // 56:63, ATTR_VPD_ODT_RD[0][0][3]; # when Read of Rank3 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG1_P0_VALUES2, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG1_P0_VALUES2_LEN>(l_odt_rd[0][2]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG1_P0_VALUES3, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG1_P0_VALUES3_LEN>(l_odt_rd[0][3]); + FAPI_DBG("odt_rd_config1: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_RD_CONFIG1_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_RD_CONFIG2_P0 + // 48:55, ATTR_VPD_ODT_RD[0][1][0]; # when Read of Rank4 + // 56:63, ATTR_VPD_ODT_RD[0][1][1]; # when Read of Rank5 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG2_P0_VALUES4, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG2_P0_VALUES4_LEN>(l_odt_rd[1][0]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG2_P0_VALUES5, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG2_P0_VALUES5_LEN>(l_odt_rd[1][1]); + FAPI_DBG("odt_rd_config2: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_RD_CONFIG2_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_RD_CONFIG3_P0 + // 48:55, ATTR_VPD_ODT_RD[0][1][2]; # when Read of Rank6 + // 56:63, ATTR_VPD_ODT_RD[0][1][3]; # when Read of Rank7 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG3_P0_VALUES6, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG3_P0_VALUES6_LEN>(l_odt_rd[1][2]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_RD_CONFIG3_P0_VALUES7, + MCA_DDRPHY_SEQ_ODT_RD_CONFIG3_P0_VALUES7_LEN>(l_odt_rd[1][3]); + FAPI_DBG("odt_rd_config3: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_RD_CONFIG3_P0, l_data) ); + } + + // + // ODT Write + // + { + // DPHY01_DDRPHY_SEQ_ODT_WR_CONFIG0_P0 + // 48:55, ATTR_VPD_ODT_WR[0][0][0]; # when Read of Rank0 + // 56:63, ATTR_VPD_ODT_WR[0][0][1]; # when Read of Rank1 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG0_P0_VALUES0, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG0_P0_VALUES0_LEN>(l_odt_wr[0][0]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG0_P0_VALUES1, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG0_P0_VALUES1_LEN>(l_odt_wr[0][1]); + FAPI_DBG("odt_wr_config0: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_WR_CONFIG0_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_WR_CONFIG1_P0 + // 48:55, ATTR_VPD_ODT_WR[0][0][2]; # when Read of Rank2 + // 56:63, ATTR_VPD_ODT_WR[0][0][3]; # when Read of Rank3 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG1_P0_VALUES2, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG1_P0_VALUES2_LEN>(l_odt_wr[0][2]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG1_P0_VALUES3, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG1_P0_VALUES3_LEN>(l_odt_wr[0][3]); + FAPI_DBG("odt_wr_config1: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_WR_CONFIG1_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_WR_CONFIG2_P0 + // 48:55, ATTR_VPD_ODT_WR[0][1][0]; # when Read of Rank4 + // 56:63, ATTR_VPD_ODT_WR[0][1][1]; # when Read of Rank5 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG2_P0_VALUES4, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG2_P0_VALUES4_LEN>(l_odt_wr[1][0]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG2_P0_VALUES5, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG2_P0_VALUES5_LEN>(l_odt_wr[1][1]); + FAPI_DBG("odt_wr_config2: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_WR_CONFIG2_P0, l_data) ); + } + + { + // DPHY01_DDRPHY_SEQ_ODT_WR_CONFIG3_P0 + // 48:55, ATTR_VPD_ODT_WR[0][1][2]; # when Read of Rank6 + // 56:63, ATTR_VPD_ODT_WR[0][1][3]; # when Read of Rank7 + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG3_P0_VALUES6, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG3_P0_VALUES6_LEN>(l_odt_wr[1][2]); + l_data.insertFromRight<MCA_DDRPHY_SEQ_ODT_WR_CONFIG3_P0_VALUES7, + MCA_DDRPHY_SEQ_ODT_WR_CONFIG3_P0_VALUES7_LEN>(l_odt_wr[1][3]); + FAPI_DBG("odt_wr_config3: 0x%016llx", uint64_t(l_data)); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_ODT_WR_CONFIG3_P0, l_data) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + + + + + +/// +/// @brief Setup seq_rd_wr_data +/// @tparam T, the target type of the MCA/MBA +/// @param[in] the target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template< fapi2::TargetType T > +inline fapi2::ReturnCode reset_seq_rd_wr_data( const fapi2::Target<T>& i_target ); + +/// +/// @brief Setup seq_rd_wr_data +/// @param[in] the MCA target associated with this cal setup +/// @return FAPI2_RC_SUCCESS iff setup was successful +/// +template<> +inline fapi2::ReturnCode reset_seq_rd_wr_data( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ) +{ + // MPR_PATTERN_BIT of 0F0F0F0F pattern + static const uint64_t MPR_PATTERN = 0x5555; + fapi2::buffer<uint64_t> l_data; + + l_data.insertFromRight<MCA_DDRPHY_SEQ_RD_WR_DATA0_P0_DATA_REG0, + MCA_DDRPHY_SEQ_RD_WR_DATA0_P0_DATA_REG0_LEN>(MPR_PATTERN); + + FAPI_DBG("seq_rd_wr 0x%llx", l_data); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_RD_WR_DATA0_P0, l_data) ); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_SEQ_RD_WR_DATA1_P0, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + + + + + + +/// +/// @brief Setup the PC CONFIG0 register +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode set_pc_config0(const fapi2::Target<T>& i_target); + +/// +/// @brief Setup the PC CONFIG1 register +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode set_pc_config1(const fapi2::Target<T>& i_target); + +/// +/// @brief Return the DIMM target for the primary rank in the specificed rank pair +/// @tparam T, fapi2::TargetType of the port target +/// @param[in] the arget +/// @param[in] the rank pair +/// @param[out] fapi2::Target<fapi2::TARGET_TYPE_DIMM> +/// @return FAPI2_RC_SUCCESS iff ok +/// +template<fapi2::TargetType T> +fapi2::ReturnCode rank_pair_primary_to_dimm(const fapi2::Target<T>& i_target, const uint64_t i_rp, + fapi2::Target<fapi2::TARGET_TYPE_DIMM>& o_dimm); + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C new file mode 100644 index 000000000..81e50fc8c --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.C @@ -0,0 +1,330 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/phy/dp16.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file dp16.C +/// @brief Static data and subroutines to control the DP16 logic blocks +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> +#include <utility> +#include <vector> + +#include "dp16.H" + +#include <p9_mc_scom_addresses.H> +#include <p9_mc_scom_addresses_fld.H> + +#include "../utils/pos.H" +#include "../utils/c_str.H" + +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_SYSTEM; + +namespace mss +{ +namespace dp16 +{ + +typedef std::pair< uint64_t, uint64_t > register_data_pair; +typedef std::vector< register_data_pair > register_data_vector; + +// Why the tables you ask? Because these bits need to be controlled +// depending on phy, packaging, board swizzling and it's just eaiser +// to see the bits like this than in a more compact algorithmic arrangement. + +// Systems without Spare Bytes (or with deconfigured spares) +static std::vector< register_data_vector > data_bit_enable_no_spare = +{ + // DHPY01 + { + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_0, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_1, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_2, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_3, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_4, 0xFF00}, + + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_1, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_2, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_3, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_4, 0x0}, + }, +}; + +// Rank Pair will be added to the register address before writing +static std::vector< register_data_vector > wrclk_enable_no_spare_x4 = +{ + { + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_0, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_1, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_2, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_3, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_4, 0x8400}, + }, +}; + +// Rank Pair will be added to the register address before writing +static std::vector< register_data_vector > rdclk_enable_no_spare_x4 = +{ + { + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_0, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_1, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_2, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_3, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_4, 0x8400}, + }, +}; + +// Systems With Spare Bytes Enabled +static std::vector< register_data_vector > data_bit_enable_spare = +{ + // DHPY01 + { + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_0, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_1, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_2, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_3, 0xFFFF}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE0_P0_4, 0xFFFF}, + + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_1, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_2, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_3, 0x0}, + {MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_4, 0x0}, + }, +}; + +static std::vector< register_data_vector > wrclk_enable_spare_x4 = +{ + { + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_0, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_1, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_2, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_3, 0x8640}, + {MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_4, 0x8640}, + }, +}; + +static std::vector< register_data_vector > rdclk_enable_spare_x4 = +{ + { + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_0, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_1, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_2, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_3, 0x8640}, + {MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_4, 0x8640}, + }, +}; + +/// +/// @brief Write the data bit enable registers +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode write_data_bit_enable( const fapi2::Target<TARGET_TYPE_MCA>& i_target ) +{ + // Determine if we're running with spares or not. Once we know that, we can find the right vector to iterate over. + // Note: Is this ATTR_EFF_DIMM_SPARE? Because that's per DIMM, but this is a port-level statement, right? BRS + // Assume Nimbus for now - no spares ever. BRS + bool l_using_spares = false; + + // Since this code is the MCA specialization, we know we never deal with DPHY23 - Nimbus PHY are + // 4 copies of the same PHY. So we can use the DPHY01 data for all position. + auto l_reg_data = l_using_spares ? data_bit_enable_spare[0] : data_bit_enable_no_spare[0]; + + FAPI_DBG("reg/data vector %d", l_reg_data.size()); + + for (auto rdp : l_reg_data) + { + // This is probably important enough to be seen all the time, not just debug + FAPI_INF( "Setting up DATA_BIT_ENABLE 0x%llx (0x%llx) %s", rdp.first, rdp.second, mss::c_str(i_target) ); + FAPI_TRY( fapi2::putScom(i_target, rdp.first, rdp.second) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Write the clock enable registers +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode read_clock_enable( const fapi2::Target<TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ) +{ + // Just slam something in here for now - we know the 'RIT DIMM' is x4, lets assume no cross-coupling for now + bool l_using_spares = false; + auto l_reg_data = l_using_spares ? rdclk_enable_spare_x4[0] : rdclk_enable_no_spare_x4[0]; + + for (auto rp : l_rank_pairs) + { + for (auto rdp : l_reg_data) + { + // Grab the register and add the rank pair in + fapi2::buffer<uint64_t> l_address(rdp.first); + l_address.insertFromRight<22, 2>(rp); + + fapi2::buffer<uint64_t> l_data; + l_data.insertFromRight<MCA_DDRPHY_DP16_READ_CLOCK_RANK_PAIR0_P0_0_01_QUAD0_CLK16, 16>(rdp.second); + + FAPI_INF( "Setting up RDCLK RP%d 0x%llx (0x%llx) %s", rp, l_address, l_data, mss::c_str(i_target) ); + FAPI_TRY( fapi2::putScom(i_target, l_address, l_data) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Write the read clock enable registers +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCESs iff ok +/// +template<> +fapi2::ReturnCode write_clock_enable( const fapi2::Target<TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ) +{ + // Just slam something in here for now - we know the 'RIT DIMM' is x4, lets assume no cross-coupling for now + bool l_using_spares = false; + auto l_reg_data = l_using_spares ? wrclk_enable_spare_x4[0] : wrclk_enable_no_spare_x4[0]; + + for (auto rp : l_rank_pairs) + { + for (auto rdp : l_reg_data) + { + // Grab the register and add the rank pair in + fapi2::buffer<uint64_t> l_address(rdp.first); + l_address.insertFromRight<22, 2>(rp); + + fapi2::buffer<uint64_t> l_data; + l_data.insertFromRight<MCA_DDRPHY_DP16_WRCLK_EN_RP0_P0_0_01_QUAD0_CLK16, 16>(rdp.second); + + FAPI_INF( "Setting up WRCLK RP%d 0x%llx (0x%llx) %s", rp, l_address, l_data, mss::c_str(i_target) ); + FAPI_TRY( fapi2::putScom(i_target, l_address, l_data) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Reset the training delay configureation +/// @param[in] the port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode reset_delay_values( const fapi2::Target<TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ) +{ + std::vector<uint64_t> l_addrs( + { + MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_0, + MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_1, + MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_2, + MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_3, + MCA_DDRPHY_DP16_DQS_GATE_DELAY_RP0_P0_4, + } ); + + fapi2::buffer<uint64_t> l_data; + + // Reset the write level values + FAPI_INF( "Resetting write level values %s", mss::c_str(i_target) ); + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_WC_CONFIG2_P0, l_data) ); + l_data.setBit<MCA_DDRPHY_WC_CONFIG2_P0_EN_RESET_WR_DELAY_WL>(); + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_WC_CONFIG2_P0, l_data) ); + + for (auto rp : l_rank_pairs) + { + for (auto a : l_addrs) + { + // Add the rank pair into the register to get the actual address + fapi2::buffer<uint64_t> l_address(a); + l_address.insertFromRight<22, 2>(rp); + + FAPI_DBG( "Resetting DP16 gate delay 0x%llx %s", l_address, mss::c_str(i_target) ); + FAPI_TRY( fapi2::putScom(i_target, l_address, 0) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Configure the DP16 sysclk +/// @param[in] a MCBIST target +/// @return FAPI2_RC_SUCCESs iff ok +/// +template<> +fapi2::ReturnCode setup_sysclk( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target ) +{ + std::vector< std::pair<uint64_t, uint64_t> > l_addrs( + { + {MCA_DDRPHY_DP16_SYSCLK_PR0_P0_0, MCA_DDRPHY_DP16_SYSCLK_PR1_P0_0}, + {MCA_DDRPHY_DP16_SYSCLK_PR0_P0_1, MCA_DDRPHY_DP16_SYSCLK_PR1_P0_1}, + {MCA_DDRPHY_DP16_SYSCLK_PR0_P0_2, MCA_DDRPHY_DP16_SYSCLK_PR1_P0_2}, + {MCA_DDRPHY_DP16_SYSCLK_PR0_P0_3, MCA_DDRPHY_DP16_SYSCLK_PR1_P0_3}, + {MCA_DDRPHY_DP16_SYSCLK_PR0_P0_4, MCA_DDRPHY_DP16_SYSCLK_PR1_P0_4}, + } ); + + fapi2::buffer<uint64_t> l_data; + uint8_t is_sim = 0; + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<TARGET_TYPE_SYSTEM>(), is_sim) ); + + l_data.setBit<MCA_DDRPHY_DP16_SYSCLK_PR0_P0_0_01_ENABLE>(); + + if (is_sim) + { + l_data.setBit<MCA_DDRPHY_DP16_SYSCLK_PR0_P0_0_01_ROT_OVERRIDE_EN>(); + } + + for (auto p : i_target.getChildren<TARGET_TYPE_MCA>()) + { + FAPI_DBG("set dp16_sysclk for %s", mss::c_str(p)); + + for (auto a : l_addrs) + { + FAPI_TRY( fapi2::putScom(p, a.first, l_data) ); + FAPI_TRY( fapi2::putScom(p, a.second, l_data) ); + } + } + +fapi_try_exit: + return fapi2::current_err; +} + + +} +} diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H new file mode 100644 index 000000000..f5f962516 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/phy/dp16.H @@ -0,0 +1,165 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/phy/dp16.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file dp16.H +/// @brief Subroutines to control the DP16 logic blocks +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#ifndef _MSS_DP16_H_ +#define _MSS_DP16_H_ + +#include <fapi2.H> +#include <vector> + +namespace mss +{ +namespace dp16 +{ + +/// +/// @brief Configure the DP16 sysclk +/// @tparam T, the fapi2 target type +/// @param[in] a target +/// @return FAPI2_RC_SUCCESs iff ok +/// +template< fapi2::TargetType T > +fapi2::ReturnCode setup_sysclk( const fapi2::Target<T>& i_target ); + +/// +/// @brief Configure the DP16 sysclk +/// @param[in] a MCBIST target +/// @return FAPI2_RC_SUCCESs iff ok +/// +template<> +fapi2::ReturnCode setup_sysclk( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ); + +/// +/// @brief Reset the training delay configureation +/// @tparam T, the type of the port +/// @param[in] the port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode reset_delay_values( const fapi2::Target<T>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Reset the training delay configureation +/// @param[in] the port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode reset_delay_values( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Write the read clock enable registers +/// @tparam T, the type of the port +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode read_clock_enable( const fapi2::Target<T>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Write the clock enable registers +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode read_clock_enable( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Write the clock enable registers +/// @tparam T, the type of the port +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode write_clock_enable( const fapi2::Target<T>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Write the clock enable registers +/// @param[in] a port target +/// @param[in] vector of rank pairs +/// @return FAPI2_RC_SUCCES iff ok +/// +template<> +fapi2::ReturnCode write_clock_enable( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + const std::vector< uint64_t >& l_rank_pairs ); + +/// +/// @brief Write the data bit enable registers +/// @tparam T, the type of the port +/// @param[in] a port target +/// @param[in] vector of port pairs +/// @return FAPI2_RC_SUCCESs iff ok +/// +template< fapi2::TargetType T> +fapi2::ReturnCode write_data_bit_enable( const fapi2::Target<T>& i_target ); + +/// +/// @brief Write the data bit enable registers +/// @param[in] a port target +/// @param[in] vector of port pairs +/// @return FAPI2_RC_SUCCESs iff ok +/// +template<> +fapi2::ReturnCode write_data_bit_enable( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target ); + +/// +/// @brief Setup the bad-bits masks for a port +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T> +inline fapi2::ReturnCode set_bad_bits(const fapi2::Target<T>& i_target); + +/// +/// @brief Setup the bad-bits masks for a port +/// @tparam T the fapi2::TargetType +/// @param[in] the target (MCA or MBA?) +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template<> +inline fapi2::ReturnCode set_bad_bits(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target) +{ + // Note: We need to do this ... BRS + return fapi2::FAPI2_RC_SUCCESS; +} + +} +} + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/port/port.H b/src/import/chips/p9/procedures/hwp/memory/lib/port/port.H new file mode 100644 index 000000000..4b3f6637b --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/port/port.H @@ -0,0 +1,265 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/port/port.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file port.H +/// @brief Code to support ports +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_PORT_H_ +#define _MSS_PORT_H_ + +#include <fapi2.H> + +#include <p9_mc_scom_addresses.H> + +// I have a dream that port 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 + +template< fapi2::TargetType T > +class portTraits; + +// Centaur port traits +template<> +class portTraits<fapi2::TARGET_TYPE_MBA> +{ + public: +}; + +// Nimbus port traits +template<> +class portTraits<fapi2::TARGET_TYPE_MCA> +{ + public: + static const uint64_t FARB5Q_REG = MCA_MBA_FARB5Q; + static const uint64_t REFRESH_REG = MCA_MBAREF0Q; + static const uint64_t ECC_REG = MCA_RECR; + + enum + { + CFG_DDR_DPHY_NCLK = MCA_MBA_FARB5Q_CFG_DDR_DPHY_NCLK, + CFG_DDR_DPHY_NCLK_LEN = MCA_MBA_FARB5Q_CFG_DDR_DPHY_NCLK_LEN, + CFG_DDR_DPHY_PCLK = MCA_MBA_FARB5Q_CFG_DDR_DPHY_PCLK, + CFG_DDR_DPHY_PCLK_LEN = MCA_MBA_FARB5Q_CFG_DDR_DPHY_PCLK_LEN, + CFG_CCS_INST_RESET_ENABLE = MCA_MBA_FARB5Q_CFG_CCS_INST_RESET_ENABLE, + CFG_DDR_RESETN = MCA_MBA_FARB5Q_CFG_DDR_RESETN, + CFG_CCS_ADDR_MUX_SEL = MCA_MBA_FARB5Q_CFG_CCS_ADDR_MUX_SEL, + + REFRESH_ENABLE = MCA_MBAREF0Q_CFG_REFRESH_ENABLE, + + ECC_CHECK_DISABLE = MCA_RECR_MBSECCQ_DISABLE_MEMORY_ECC_CHECK_CORRECT, + ECC_CORRECT_DISABLE = MCA_RECR_MBSECCQ_DISABLE_MEMORY_ECC_CORRECT, + }; +}; + + +namespace mss +{ +/// +/// @brief Given a DIMM, return it's port number. +/// @param[in] the DIMM in question +/// @return the port number it's attached to +/// +// Note: Make this a template and dispatch at runtime to determine if the parent of +// the DIMM is an MBA or an MCA BRS +// +inline uint64_t port( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target ) +{ + // Our port is the position of our MCA + return mss::pos( i_target.getParent<fapi2::TARGET_TYPE_MCA>() ); +} + +/// +/// @brief Change the state of the addr_mux_sel bit +/// @param[in] the target +/// @param[in] the state +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode change_addr_mux_sel( const fapi2::Target<T>& i_target, states i_state ) +{ + fapi2::buffer<uint64_t> l_data; + + FAPI_DBG("Change addr_mux_sel to %s %s", (i_state == HIGH ? "high" : "low"), mss::c_str(i_target)); + FAPI_TRY( fapi2::getScom(i_target, TT::FARB5Q_REG, l_data) ); + l_data.writeBit<TT::CFG_CCS_ADDR_MUX_SEL>(i_state); + FAPI_TRY( fapi2::putScom(i_target, TT::FARB5Q_REG, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Change the state of the MC Refresh enable bit +/// @param[in] the target +/// @param[in] the state +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode change_refresh_enable( const fapi2::Target<T>& i_target, states i_state ) +{ + fapi2::buffer<uint64_t> l_data; + + FAPI_DBG("Change refresh enable to %s %s", (i_state == HIGH ? "high" : "low"), mss::c_str(i_target)); + FAPI_TRY( fapi2::getScom(i_target, TT::REFRESH_REG, l_data) ); + l_data.writeBit<TT::REFRESH_ENABLE>(i_state); + FAPI_TRY( fapi2::putScom(i_target, TT::REFRESH_REG, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Enable the MC Periodic calibration functionality +/// @param[in] the target +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode enable_periodic_cal( const fapi2::Target<T>& i_target ) +{ + FAPI_INF("Enable periodic cal NOOP"); + return fapi2::FAPI2_RC_SUCCESS; +} + +/// +/// @brief Enable Read ECC checking +/// @param[in] the target +/// @return FAPI2_RC_SUCCESS if and only if ok +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode enable_read_ecc( const fapi2::Target<T>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + + FAPI_DBG("Enable Read ECC %s", mss::c_str(i_target)); + + FAPI_TRY( fapi2::getScom(i_target, TT::ECC_REG, l_data) ); + l_data.clearBit<TT::ECC_CHECK_DISABLE>(); + l_data.clearBit<TT::ECC_CORRECT_DISABLE>(); + + // The preferred operating mode is 11 (INVERT_DATA_TOGGLE_CHECKS) which stores data complemented + // (because most bits are '0', and the dram bus pulls up, so transmitting 1s is least power) but + // still flips the inversion of check bits to aid RAS. Per Brad Michael 12/15 + l_data.insertFromRight<MCA_RECR_MBSECCQ_DATA_INVERSION, MCA_RECR_MBSECCQ_DATA_INVERSION_LEN>(0b11); + + // bits: 60 MBSTRQ_CFG_MAINT_RCE_WITH_CE + // cfg_maint_rce_with_ce - not implemented. Need to investigate if needed for nimbus. + + FAPI_TRY( fapi2::putScom(i_target, TT::ECC_REG, l_data) ); + +fapi_try_exit: + return fapi2::current_err; +} + +// +// We expect to come in to draminit with the following setup: +// 1. ENABLE_RESET_N (FARB5Q(6)) 0 +// 2. RESET_N (FARB5Q(4)) 1 - out of reset (done in draminit as a separate step) +// 3. CCS_ADDR_MUX_SEL (FARB5Q(5)) - 1 +// 4. CKE out of high impedence +// Note: Ignore resetn as it's taken care of as a separate step +// +/// +/// @brief Secure the entry criteria for draminit +/// @param[in] A target representing a port +/// @return FAPI2_RC_SUCCESS if and only if ok +// This is in this header as it's hoped to be able to be shared. Seems to make more +// Might make more sense in p9_mss_draminit.C ... BRS +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +inline fapi2::ReturnCode draminit_entry_invariant( const fapi2::Target<T>& i_target ) +{ + fapi2::buffer<uint64_t> l_data; + FAPI_TRY( fapi2::getScom(i_target, TT::FARB5Q_REG, l_data) ); + + if ((l_data.getBit<TT::CFG_CCS_ADDR_MUX_SEL>() != HIGH) || (l_data.getBit<TT::CFG_CCS_INST_RESET_ENABLE>() != LOW)) + { + // We have some bits not set correctly. Lets try to reset the register. + FAPI_INF("FARB5Q: 0x%llx, setting MUX_SEL, clearing RESET_ENABLE", uint64_t(l_data)); + l_data.setBit<TT::CFG_CCS_ADDR_MUX_SEL>(); + l_data.clearBit<TT::CFG_CCS_INST_RESET_ENABLE>(); + FAPI_TRY( fapi2::putScom(i_target, TT::FARB5Q_REG, l_data) ); + } + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Drive memory clocks +/// @param[in] A target representing a port +/// @param[in] phy p clock - right most 2 bits +/// @param[in] phy n clock - right most 2 bits +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note this might need a port id added for Centaur/MBA controllers +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode drive_mem_clks( const fapi2::Target<T>& i_target, uint64_t i_pclk, uint64_t i_nclk ) +{ + fapi2::buffer<uint64_t> l_data; + + FAPI_DBG("Drive mem clocks"); + FAPI_TRY( fapi2::getScom(i_target, TT::FARB5Q_REG, l_data) ); + + l_data.insertFromRight<TT::CFG_DDR_DPHY_NCLK, TT::CFG_DDR_DPHY_NCLK_LEN>(i_nclk); + l_data.insertFromRight<TT::CFG_DDR_DPHY_PCLK, TT::CFG_DDR_DPHY_PCLK_LEN>(i_pclk); + + FAPI_TRY( fapi2::putScom(i_target, TT::FARB5Q_REG, l_data) ); + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + FAPI_ERR("Unable to drive mem clocks: %s", mss::c_str(i_target)); + return fapi2::current_err; +} + +/// +/// @brief Set DDR resetn +/// @param[in] A target representing a port +/// @param[in] high or low +/// @return FAPI2_RC_SUCCESS if and only if ok +/// @note this might need a port id added for Centaur/MBA controllers +/// +template< fapi2::TargetType T, typename TT = portTraits<T> > +fapi2::ReturnCode ddr_resetn( const fapi2::Target<T>& i_target, bool i_state ) +{ + fapi2::buffer<uint64_t> l_data; + FAPI_TRY( fapi2::getScom(i_target, TT::FARB5Q_REG, l_data) ); + + if (l_data.getBit<TT::CFG_DDR_RESETN>() != i_state) + { + l_data.writeBit<TT::CFG_DDR_RESETN>(i_state); + FAPI_DBG("ddr_resetn transitioning to %d (0x%llx)", i_state, l_data); + FAPI_TRY( fapi2::putScom(i_target, TT::FARB5Q_REG, l_data) ); + } + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + FAPI_ERR("Unable to change resetn: %s (%d)", mss::c_str(i_target), i_state); + return fapi2::current_err; +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H new file mode 100644 index 000000000..b6b8c25aa --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_const.H @@ -0,0 +1,141 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/shared/mss_const.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @mss_const.H +/// @This file contains constants for the memory team. +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_CONST_H_ +#define _MSS_CONST_H_ + + +namespace mss +{ + +enum sizes +{ + PORTS_PER_MCS = 2, + PORTS_PER_MCBIST = 4, + MAX_DIMM_PER_PORT = 2, + MAX_RANK_PER_DIMM = 4, + RANK_MID_POINT = 4, ///< Which rank number indicates the switch to the other DIMM + DEFAULT_POLL_LIMIT = 10, ///< the number of poll attempts in the event we can't calculate another + MAX_NUM_IMP = 4, ///< number of impedances valid per slew type + MAX_NUM_CAL_SLEW_RATES = 4, ///< 3V/ns, 4V/ns, 5V/ns, 6V/n + MAX_SLEW_VALUE = 15, ///< 4 bit value + MAX_SUPPORTED_FREQUENCIES = 1, ///< Number of supported frequencies. Right now it's only 2400 + + // All need to be attributes - BRS + // 48:51, 0b1100, (def_is_sim); # BIG_STEP = 12 (changed from default for SIM) + // 48:51, 0b0000, any; # BIG_STEP = 0 SWyatt + // #48:51, 0b0010, any; # BIG_STEP = 2 (default) + // 52:54, 0b000, any; # SMALL_STEP = 0 (default) SWyatt + //#52:54, 0b001, any; # SMALL_STEP = 1 (!! recommend setting to 0) + // 55:60, 0b101010, any; # WR_PRE_DLY = 42 + WR_LVL_BIG_STEP = 0b1100, + WR_LVL_SMALL_STEP = 0b000, + WR_LVL_PRE_DLY = 0b101010, + WR_LVL_NUM_VALID_SAMPLES = 0x5, + + // THIS IS LIKELY INCORRECT - Should be defined in the DDR4 write centering protocol BRS + // This field must be set to the larger of the two values in number of memory clock cycles. + // FW_RD_WR = max(tWTR + 11, AL + tRTP + 3) + WR_CNTR_FW_RD_WR = 0x11 + 4, + WR_CNTR_FW_WR_RD = 0x0, + + // Attribute? BRS + COARSE_CAL_STEP_SIZE = 0x4, + CONSEQ_PASS = 0x8, +}; + +enum times +{ + CONVERT_PS_IN_A_NS = 1000, ///< 1000 pico in an nano + CONVERT_PS_IN_A_US = 1000000, ///< 1000000 picos in a micro + + DELAY_1NS = 1, + DELAY_10NS = 10 , ///< general purpose 10 ns delay for HW mode + DELAY_100NS = 100, ///< general purpose 100 ns delay for HW mode + DELAY_1US = 1000, ///< general purpose 1 usec delay for HW mode + DELAY_100US = 100000, ///< general purpose 100 usec delay for HW mode + + // From the DDR4spec 2400 speed - need to be changed to read attributes. BRS + tWLO = 10, + tWLOE = 2, +}; + +enum states +{ + LOW = 0, + HIGH = 1, + START = 1, + STOP = 0, + START_N = 0, + STOP_N = 1, + ON = 1, + OFF = 0, + ON_N = 0, + OFF_N = 1, + INVALID = 0xFF, + + // This needs to be an attribute I think - BRS. Used as a boolean. + CAL_ABORT_ON_ERROR = 1, +}; + +// Static consts describing the bits used in the cal_step_enable attribute +// These are bit positions. 0 is the left most bit. +enum cal_steps +{ + EXT_ZQCAL = 0, + WR_LEVEL = 1, + DQS_ALIGN = 2, + RDCLK_ALIGN = 3, + READ_CTR = 4, + READ_CTR_2D_VREF = 5, + WRITE_CTR = 6, + WRITE_CTR_2D_VREF = 7, + COARSE_WR = 8, + COARSE_RD = 9, +}; + +namespace mcbist +{ +enum data_mode +{ + // MCBIST test data modes + FIXED_DATA_MODE = 0b000, + RAND_FWD_MODE = 0b001, + RAND_REV_MODE = 0b010, + RAND_FWD_MAINT = 0b011, + RAND_REV_MAINT = 0b100, + DATA_EQ_ADDR = 0b101, + ROTATE_LEFT_MODE = 0b110, + ROTATE_RIGHT_MODE = 0b111, +}; +} // namespace mcbist + + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_kind.H b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_kind.H new file mode 100644 index 000000000..b89fc304c --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/shared/mss_kind.H @@ -0,0 +1,116 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/shared/mss_kind.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file mss_kind.H +/// @brief Implementation of mss specific types +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_KIND_H_ +#define _MSS_KIND_H_ + +#include <fapi2.H> + +/// +/// @brief Class to compile-time inspect whether an API has an overload +/// @note contains a static bool which defaults to false. Any overload +/// will specialize this template and replace the value with true. +/// +#define REGISTER_API( __api_name, ... ) \ + template< mss::kind_t K, __VA_ARGS__ > \ + struct __api_name##_overload \ + { static const bool available = false; }; + +// +// Overload definitions for the specializations +// +#define REGISTER_OVERLOAD( __api_name, __kind, ... ) \ + template<> \ + struct __api_name##_overload< __kind, __VA_ARGS__> \ + { static const bool available = true; }; + +namespace mss +{ + +// These must be contiguous and unique +enum kind_t +{ + // The default, base, etc. kind. Used to define the function + // which would be in a base class. Also used to stop the + // metaprogramming recursion of the dispatcher functions. + DEFAULT_KIND = 0, + + // DIMM type and DRAM Generation representation + KIND_RDIMM_EMPTY = 1, + KIND_RDIMM_DDR4 = 2, + KIND_LRDIMM_EMPTY = 3, + KIND_LRDIMM_DDR4 = 4, + + // Used to force dispatching looking for overloads + // This moust always be one more than that largest + // kind_t. + FORCE_DISPATCH = 4 + 1, +}; + +inline mss::kind_t dimm_kind( const uint64_t l_type, const uint64_t l_gen ) +{ + // This is the conditional needed to differentiate dimm type/generation + switch (l_type) + { + case fapi2::ENUM_ATTR_EFF_DIMM_TYPE_RDIMM: + if (l_gen == fapi2::ENUM_ATTR_EFF_DRAM_GEN_EMPTY) + { + return KIND_RDIMM_EMPTY; + } + + if (l_gen == fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR4) + { + return KIND_RDIMM_DDR4; + } + + return DEFAULT_KIND; + break; + + case fapi2::ENUM_ATTR_EFF_DIMM_TYPE_LRDIMM: + if (l_gen == fapi2::ENUM_ATTR_EFF_DRAM_GEN_EMPTY) + { + return KIND_LRDIMM_EMPTY; + } + + if (l_gen == fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR4) + { + return KIND_LRDIMM_DDR4; + } + + return DEFAULT_KIND; + break; + + default: + return DEFAULT_KIND; + break; + }; +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.C b/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.C new file mode 100644 index 000000000..d215773a0 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.C @@ -0,0 +1,3548 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file spd_decoder.C +/// @brief SPD decoder definitions +/// +// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP HWP Backup: Brian Silver <bsilver@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#include <fapi2.H> +#include "../mss.H" +#include "../utils/conversions.H" +#include "spd_decoder.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; + + +namespace mss +{ +namespace spd +{ + +// Note: IBM's implementation of std::maps are not thread safe + +// ========================================================= +// Byte 0 maps +// Item JC-45-2220.01x +// Page 14 +// DDR4 SPD Document Release 3 +// Byte 0 (0x000): Number of Bytes Used / Number of Bytes in SPD Device + +static const uint16_t bytes_used_map[] = +{ + // Shifting index by 1 first since first key value is undefined + // Key values < 1 and > 3 aren't supported or reserved + + // Undefined + 128, + 256, + 384, + 512, + //... + // All other bits reserved +}; + +static const uint16_t bytes_total_map[] = +{ + // Shifting index by 1 first since first key value is undefined + // Key values < 1 and > 2 aren't supported or reserved + + // Undefined + 256, + 512, + //... + // All other bits reserved +}; + +// ========================================================= +// Byte 2 maps +// Item JC-45-2220.01x +// Page 16 +// DDR4 SPD Document Release 3 +// Byte 2 (0x002): Key Byte / DRAM Device Type + +static const uint8_t dram_gen_map[] = +{ + // Initial and ending index values are not supported or reserved + // Shifting index by 11 since initial dram gen types aren't supported (by IBM) + // Key values < 12 and > 13 are not supported or reserved + + // Reserved + // Fast page mode is not supported + // EDO is not supported + // Pipelined Nibbleis not supported + // SDRAM + // ROM is not supported + // DDR SGRAM is not supported + // DDR SDRAM is not supported + // DDR2 SDRAM is not supported + // DDR2 SDRAM FB-DIMM is not supported + // DDR2 SDRAM FB-DIMM PROBE is not supported + fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR3, + fapi2::ENUM_ATTR_EFF_DRAM_GEN_DDR4, + //... + // All other bits reserved +}; + +// ========================================================= +// Byte 3 maps +// Item JC-45-2220.01x +// Page 17 +// DDR4 SPD Document Release 3 +// Byte 3 (0x003): Key Byte / Module Type + +static const uint8_t base_module_type_map[] = +{ + + // Initial and ending index values are not supported or reserved + // Index shifted by 1 since first module type isn't supported (by IBM) + // Key values < 1 and > 4 are not supported or reserved + + // Extending DIMM not supported + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_RDIMM, + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_UDIMM, + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_SO_DIMM, + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_LRDIMM, + // Mini-RDIMM not supported + // Mini-UDIMM not supported + // Reserved + // 72b-SO-RDIMM not supported + // 72b-SO-UDIMM not supported + // Reserved + // Reserved + // 16b-SO-DIMM + // 32b-SO-DIMM + // Reserved + // Reserved +}; + +// ========================================================= +// Byte 4 maps +// Item JC-45-2220.01x +// Page 18 +// DDR4 SPD Document Release 3 +// Byte 4 (0x004): SDRAM Density and Banks + +static const uint8_t sdram_capacity_map[] = +{ + // Initial and ending index values are not supported or reserved + // Index shifted by 2 since first module type isn't supported (by IBM) + // Key values < 2 and > 9 are not supported or reserved + + // 256 Mbs is not supported + // 512 Mbs is not supported + // // Units (Gigabits) + 1, // Gb + 2, // Gb + 4, // Gb + 8, // Gb + 16, // Gb + 32, // Gb + 12, // Gb + 24, // Gb + //... + //All others reserved +}; + +static const uint8_t sdram_banks_map[] = +{ + // Key values > 1 are not supported or reserved + + 4, // banks address bits (2 address bits) + 8, // banks address bits (3 address bits) + //... + // All others Reserved +}; + +static const uint8_t sdram_bankgroups_map[] = +{ + // Key values > 2 are not supported or reserved + + 0, // No bank groups (0 bank group bits) + 2, // 2 bank groups (1 bank group bit) + 4, // 4 bank groups (2 bank group bits) + //Reserved +}; + +// ========================================================= +// Byte 5 maps +// Item JC-45-2220.01x +// Page 18 +// DDR4 SPD Document Release 3 +// Byte 5 (0x005): SDRAM Addressing + +static const uint8_t column_address_map[] = +{ + 9, // Column address bits 000 + 10, // Column address bits 001 + 11, // Column address bits 010 + 12, // Column address bits 011 + // All others reserved +}; + +static const uint8_t row_address_map[] = +{ + 12, // Row address bits 000 + 13, // Row address bits 001 + 14, // Row address bits 010 + 15, // Row address bits 011 + 16, // Row address bits 100 + 17, // Row address bits 101 + 18, // Row address bits 110 + // All others reserved +}; + +// ========================================================= +// Byte 6 maps +// Item JC-45-2220.01x +// Page 19 +// DDR4 SPD Document Release 3 +// Byte 6 (0x006): Primary SDRAM Package Type + +static const uint8_t die_count_map[] = +{ + 1, // 000 = Single die + 2, // 001 = 2 die + 3, // 010 = 3 die + 4, // 011 = 4 die + 5, // 100 = 5 die + 6, // 101 = 6 die + 7, // 110 = 7 die + 8, // 111 = 8 die +}; + +static const uint8_t package_type_map[] = +{ + fapi2::ENUM_ATTR_EFF_STACK_TYPE_SDP, + uint8_t(~0), + uint8_t(~0), + uint8_t(~0), + uint8_t(~0), + fapi2::ENUM_ATTR_EFF_STACK_TYPE_DDP_QDP, + fapi2::ENUM_ATTR_EFF_STACK_TYPE_3DS, +}; + +// ========================================================= +// Byte 7 maps +// Item JC-45-2220.01x +// Page 20 +// DDR4 SPD Document Release 3 +// Byte 7 (0x007): SDRAM Optional Features + +uint16_t static const MAC_map[] = +{ + fapi2::ENUM_ATTR_EFF_DRAM_MAC_UNTESTED, // Untested MAC + fapi2::ENUM_ATTR_EFF_DRAM_MAC_700K, // 700K + fapi2::ENUM_ATTR_EFF_DRAM_MAC_600K, // 600K + fapi2::ENUM_ATTR_EFF_DRAM_MAC_500K, // 500K + fapi2::ENUM_ATTR_EFF_DRAM_MAC_400K, // 400K + fapi2::ENUM_ATTR_EFF_DRAM_MAC_300K, // 300K + fapi2::ENUM_ATTR_EFF_DRAM_MAC_200K, // 200K + uint16_t(~0), // Reserved + fapi2::ENUM_ATTR_EFF_DRAM_MAC_UNLIMITED,// Unlimited MAC + // All others reserved +}; + +uint16_t static const tMAW_map[] = +{ + 8192, + 4096, + 2048, +}; + +// ========================================================= +// Byte 9 maps +// Item JC-45-2220.01x +// Page 21 +// DDR4 SPD Document Release 3 +// Byte 9 (0x009): Other SDRAM Optional Features +static const uint8_t ppr_map[] +{ + fapi2::ENUM_ATTR_EFF_DRAM_PPR_NOT_SUPPORTED, + fapi2::ENUM_ATTR_EFF_DRAM_PPR_SUPPORTED, + //... + // Reserved +}; + +static const uint8_t soft_ppr_map[] +{ + fapi2::ENUM_ATTR_EFF_DRAM_SOFT_PPR_NOT_SUPPORTED, + fapi2::ENUM_ATTR_EFF_DRAM_SOFT_PPR_SUPPORTED, +}; + +// ========================================================= +// Byte 6 maps +// Item JC-45-2220.01x +// Page 19 +// DDR4 SPD Document Release 3 +// Byte 6 (0x006): Primary SDRAM Package Type + +static const uint8_t sec_die_count_map[] = +{ + 1, // 000 = Single die + 2, // 001 = 2 die + 3, // 010 = 3 die + 4, // 011 = 4 die + 5, // 100 = 5 die + 6, // 101 = 6 die + 7, // 110 = 7 die + 8, // 111 = 8 die +}; + +static const uint8_t sec_package_type_map[] = +{ + fapi2::ENUM_ATTR_EFF_STACK_TYPE_SDP, + uint8_t(~0), + uint8_t(~0), + uint8_t(~0), + uint8_t(~0), + fapi2::ENUM_ATTR_EFF_STACK_TYPE_DDP_QDP, + fapi2::ENUM_ATTR_EFF_STACK_TYPE_3DS, +}; + +// ========================================================= +// Byte 12 maps +// Item JC-45-2220.01x +// Page 23 +// DDR4 SPD Document Release 3 +// Byte 12 (0x00C): Module Organization +static const uint8_t device_type_map[] = +{ + // // Units + 4, // bits + 8, // bits + 16, // bits + 32, // bits + // All others reserved +}; + +static const uint8_t num_pkgs_ranks_per_dimm_map[] = +{ + // // Units + 1, // package rank + 2, // package ranks + 3, // package ranks + 4, // package ranks + 5, // package ranks + 6, // package ranks + 7, // package ranks + 8 // package ranks + // All others reserved +}; + +// ========================================================= +// Byte 12 maps +// Item JC-45-2220.01x +// Page 23 +// DDR4 SPD Document Release 3 +// Byte 13 (0x00D): Module Memory Bus Width +static const uint8_t prim_bus_width_map[] = +{ + // // Units + 8, // bits + 16, // bits + 32, // bits + 64, // bits + // All others reserved +}; + +static const uint8_t bus_width_ext_map[] = +{ + // // Units + 0, // bits + 8, // bits + // All others reserved +}; + +// ========================================================= +// Byte 17 maps +// Item JC-45-2220.01x +// Page 29 +// DDR4 SPD Document Release 3 +// Byte 17 (0x011): Timebases + +// Created a maps of a single value in case mapping expands to more values +static const uint8_t medium_timebase_map[] = +{ + // // Units + 125, // ps + // All others reserved +}; + +static const uint8_t fine_timebase_map[] = +{ + // // Units + 1, // ps + // All others reserved +}; + + +// ========================================================= +// Function implementations +// ========================================================= +namespace check +{ +/// +/// @brief Checks that stack type conforms to JEDEC table +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Consumed in (Byte 7) sdram_package_type(...) +/// Item JC-45-2220.01x +/// Page 20 (Terminology table) +/// DDR4 SPD Document Release 3 +bool stack_type(const uint8_t i_stack_type, const uint8_t i_die_count) +{ + const size_t STACK_SDP = 0; + const size_t STACK_DDP_QDP = 5; + const size_t STACK_3DS = 6; + + const size_t SINGLE_DIE_PACKAGE = 1; + const size_t DUAL_DIE_PACKAGE = 2; + const size_t QUAD_DIE_PACKAGE = 4; + + const size_t TWO_SDRAM_DIE = 2; + const size_t EIGHT_SDRAM_DIE = 8; + + // Use const or enums + switch(i_stack_type) + { + case STACK_SDP: + //SDP has single die + return i_die_count == SINGLE_DIE_PACKAGE; + break; + + case STACK_DDP_QDP: + //DDP has 2 die, QDP has 4 die + return (i_die_count == DUAL_DIE_PACKAGE) || (i_die_count == QUAD_DIE_PACKAGE); + break; + + case STACK_3DS: + //3DS has 2 - 8 dies + return (i_die_count >= TWO_SDRAM_DIE) && (i_die_count <= EIGHT_SDRAM_DIE); + break; + + default: + return false; // Doesn't meet JEDEC spec + } +}// stack_type() + +} // check namespace + +/// +/// @brief Calculates timing value +/// @param[in] const int64_t& i_spd_timing_mtb, +/// const int64_t& i_spd_timing_ftb, +/// const int64_t& multiplier_mtb, +/// const int64_t& multiplier_ftb +/// @return int64_t, (timing value) +inline int64_t calc_timing(const int64_t& i_spd_timing_mtb, + const int64_t& i_spd_timing_ftb, + const int64_t& multiplier_mtb, + const int64_t& multiplier_ftb) +{ + int64_t timing_val = i_spd_timing_mtb * multiplier_mtb; + int64_t offset = (i_spd_timing_ftb * multiplier_ftb); + int64_t remainder_val = timing_val % multiplier_mtb; + + if( remainder_val == 0) + { + // If the timing value can be expressed as an integer number + // of MTB units, return that + return timing_val; + } + else + { + // Else round up and incorporate correction factor + return (++timing_val) + offset; + } +} + +/// +/// @brief Decodes SPD number of bytes +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// size_t i_read_spd_size +/// @return fapi2::ReturnCode +/// @note Decodes SPD Byte 0 +/// +fapi2::ReturnCode decoder::number_of_bytes(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data, + const size_t i_read_spd_size) +{ + // Immutable constants + const size_t BYTE_INDEX = 0; + + // SPD Bytes used mapping limits + const size_t BYTES_USED_MAP_OFFSET = 1; + const size_t BYTES_USED_MIN_VALID_KEY = 1; // All previous keys are not supported or reserved + const size_t BYTES_USED_MAX_VALID_KEY = 4; // The rest are not supported or reserved + + // Total SPD bytes mapping limits + const size_t BYTES_TOTAL_MAP_OFFSET = 1; + const size_t BYTES_TOTAL_MIN_VALID_KEY = 1; // All previous keys are not supported or reserved + const size_t BYTES_TOTAL_MAX_VALID_KEY = 2; // The rest are not supported or reserved + + // Byte variables used for decoding + uint8_t l_spd_bytes_used = 0; + uint8_t l_spd_bytes_total = 0; + uint8_t l_reserved = 0; + uint16_t l_total_bytes_map_val = 0; + uint16_t l_used_bytes_map_val = 0; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace in the front assists w/ debug + FAPI_INF("%s SPD data at Byte 0: 0x%llX.", + mss::c_str(i_target_dimm), + i_spd_data[BYTE_INDEX]); + + // Decoding 1st nibble, bits 3~0 (SPD Bytes Used) + l_spd_buffer.extractToRight<BYTES_USED_START, BYTES_USED_LEN>(l_spd_bytes_used); + + // Check to assure SPD Bytes Used (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_spd_bytes_used >= BYTES_USED_MIN_VALID_KEY) && + (l_spd_bytes_used <= BYTES_USED_MAX_VALID_KEY), + BYTE_INDEX, + l_spd_bytes_used, + "Failed check on Used SPD bytes") ); + + // Decoding bits 6~4 (SPD Bytes Total) + l_spd_buffer.extractToRight<BYTES_TOTAL_START, BYTES_TOTAL_LEN>(l_spd_bytes_total); + + // Check to assure SPD Bytes Total (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_spd_bytes_total >= BYTES_TOTAL_MIN_VALID_KEY) && + (l_spd_bytes_total <= BYTES_TOTAL_MAX_VALID_KEY), + BYTE_INDEX, + l_spd_bytes_total, + "Failed check on total SPD bytes" ) ); + + // Decoding bit 7 (Reserved bit) + l_spd_buffer.extractToRight<BYTES_RESERVED_START, BYTES_RESERVED_LEN>(l_reserved); + + // Hold map values in temp variables + l_used_bytes_map_val = bytes_used_map[l_spd_bytes_used - BYTES_USED_MAP_OFFSET]; + l_total_bytes_map_val = bytes_total_map[l_spd_bytes_total - BYTES_TOTAL_MAP_OFFSET]; + + // Size of input SPD read should match size it claims to be based on the SPD spec + // "Used SPD bytes" wasn't used since manufacturers may not use all available bytes + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_total_bytes_map_val == i_read_spd_size, + BYTE_INDEX, + l_total_bytes_map_val, + "Failed SPD size check") ); + + FAPI_INF("%s. Bytes Used: %d, Total Bytes: %d, Reserved : %d", + mss::c_str(i_target_dimm), + l_used_bytes_map_val, + l_total_bytes_map_val, + l_reserved); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decodes SPD Revision +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @param[in, out] uint8_t io_revision_num +/// @return fapi2::ReturnCode +/// @note Decodes SPD Byte 1 +/// +fapi2::ReturnCode decoder::revision(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 1; + const size_t UNDEFINED = 0xFF; // per JEDEC spec + + // Byte variables used for decoding + uint8_t l_revision_num = 0; + + // Verify SPD revision is not undefined. Value is defined by the JEDEC spec + l_revision_num = i_spd_data[BYTE_INDEX]; + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_revision_num != UNDEFINED), + BYTE_INDEX, + l_revision_num, + "Failed check on SPD revision") ); + // Print decoded info + FAPI_INF("%s. SPD data at Byte %d: 0x%llX, Revision number : %d", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX], + l_revision_num); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decodes DRAM Device Type +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Decodes SPD Byte 2 +/// +fapi2::ReturnCode decoder::dram_device_type(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 2; + + // dram generation mapping limits + const size_t MIN_VALID_KEY = 12; // All previous keys are not supported or reserved + const size_t MAX_VALID_KEY = 13; // The rest are not supported or reserved + const size_t MAP_OFFSET = 11; // SPD DRAM device type map has an index offset of 11 (simplifies array) + // //since initial map values are not supported or reserved (ignored for now) + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_device_type = 0; + + // Attribute variables used to set decoded vals + uint8_t l_device_gen[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Check to assure SPD DRAM device type (map) wont be at invalid values + l_device_type = i_spd_data[BYTE_INDEX]; + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_device_type >= MIN_VALID_KEY) && + (l_device_type <= MAX_VALID_KEY), + BYTE_INDEX, + l_device_type, + "Unsupported/reserved key value retried from SPD") ); + + // Retrive entire MCS level attribute + FAPI_TRY(eff_dram_gen(l_target_mcs, &l_device_gen[0][0])); + + // Update attribute to decoded byte values + l_device_gen[PORT_NUM][DIMM_NUM] = dram_gen_map[l_device_type - MAP_OFFSET]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_GEN, l_target_mcs, l_device_gen)); + + // Print decoded info + FAPI_INF("%s SPD data at Byte %d: 0x%llX. Device type : %d", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX], + l_device_gen[PORT_NUM][DIMM_NUM]); + +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief Decodes SPD module type +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Decodes SPD Byte 3 +/// +fapi2::ReturnCode decoder::module_type(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 3; + + // Base module mapping limits // since first index is not supported + const size_t BASE_MODULE_MAP_OFFSET = 1; // base_module_type_map has an index offset of 1 + const size_t MIN_VALID_KEY = 1; // All previous keys are not supported or reserved + const size_t MAX_VALID_KEY = 4; // The rest are not supported or reserved + + // Hybrid Media limit + const size_t MAX_HYBRID_MEDIA_KEY = 1; // All other key values reserved + + // Hybrid + const size_t MAX_HYBRID_KEY = 1; // Nothing else exits afterwards + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_base_module_type = 0; + uint8_t l_hybrid_media = 0; + uint8_t l_hybrid = 0; + + // Attribute variables used to set decoded vals + uint8_t l_module_type[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace in the front assists w/ debug + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Decoding bits 3~0 + l_spd_buffer.extractToRight<BASE_MODULE_START, BASE_MODULE_LEN>(l_base_module_type); + + // Check to assure SPD DRAM base module type (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_base_module_type >= MIN_VALID_KEY) && + (l_base_module_type <= MAX_VALID_KEY), + BYTE_INDEX, + l_base_module_type, + "Failed check for SPD DRAM base module type") ); + + // Decoding bits 6~4 + l_spd_buffer.extractToRight<HYBRID_MEDIA_START, HYBRID_MEDIA_LEN>(l_hybrid_media); + + // Check to assure SPD DRAM hybrid media is valid + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_hybrid_media <= MAX_HYBRID_MEDIA_KEY, + BYTE_INDEX, + l_hybrid_media, + "Failed check for SPD DRAM hybrid media") ); + + // Decoding bit 7 + l_spd_buffer.extractToRight<HYBRID_START, HYBRID_LEN>(l_hybrid); + + + // Check to assure SPD DRAM hybrid media is valid + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_hybrid_media <= MAX_HYBRID_KEY, + BYTE_INDEX, + l_hybrid, + "Failed check for SPD DRAM hybrid media") ); + // Retrive entire MCS level attribute + FAPI_TRY(spd_module_type(l_target_mcs, &l_module_type[0][0])); + + // Update attribute to decoded byte values + l_module_type[PORT_NUM][DIMM_NUM] = base_module_type_map[l_base_module_type - BASE_MODULE_MAP_OFFSET]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_SPD_MODULE_TYPE, l_target_mcs, l_module_type)); + + FAPI_INF("%s. Base Module Type: %d, Hybrid Media: %d, Hybrid: %d", + c_str(i_target_dimm), + l_module_type[PORT_NUM][DIMM_NUM], + l_hybrid_media, + l_hybrid); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode SDRAM density +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 4 +/// +fapi2::ReturnCode decoder::sdram_density(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 4; + + // DRAM capacity mapping limits + const size_t CAPACITY_MAP_OFFSET = 1; // base_module_type_map has an index offset of 1 + const size_t CAPACITY_MIN_VALID_KEY = 2; // All previous keys are not supported or reserved + const size_t CAPACITY_MAX_VALID_KEY = 9; // The rest are not supported or reserved + + // DRAM banks mapping limits + const size_t BANKS_MAX_VALID_KEY = 1; // The rest are not supported or reserved + + // DRAM bank groups mapping limits + const size_t BANK_GRP_MAX_VALID_KEY = 2; // The rest are not supported or reserved + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_sdram_capacity = 0; + uint8_t l_sdram_banks = 0; + uint8_t l_sdram_bank_group = 0; + + // Attribute variables used to set decoded vals + uint8_t l_capacities[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_banks[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_bank_groups[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace in the front assists w/ debug + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Decoding bits 3~0 + l_spd_buffer.extractToRight<SDRAM_CAPACITY_START, SDRAM_CAPACITY_LEN>(l_sdram_capacity); + + // Check to assure SPD DRAM capacity (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sdram_capacity >= CAPACITY_MIN_VALID_KEY) && + (l_sdram_capacity <= CAPACITY_MAX_VALID_KEY), + BYTE_INDEX, + l_sdram_capacity, + "Failed check for SPD DRAM capacity") ); + // Decoding bits 5~4 + l_spd_buffer.extractToRight<SDRAM_BANKS_START, SDRAM_BANKS_LEN>(l_sdram_banks); + + // Check to assure SPD DRAM banks (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sdram_banks <= BANKS_MAX_VALID_KEY), + BYTE_INDEX, + l_sdram_banks, + "Failed check for SPD DRAM banks") ); + + // Decoding bits 7~6 + l_spd_buffer.extractToRight<BANK_GROUP_START, BANK_GROUP_LEN>(l_sdram_bank_group); + + // Check to assure SPD DRAM banks groups (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sdram_bank_group <= BANK_GRP_MAX_VALID_KEY), + BYTE_INDEX, + l_sdram_bank_group, + "Failed check for SPD DRAM bank groups") ); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_density(l_target_mcs, &l_capacities[0][0]) ); + FAPI_TRY( eff_dram_banks(l_target_mcs, &l_banks[0][0]) ); + FAPI_TRY( eff_dram_bank_groups(l_target_mcs, &l_bank_groups[0][0]) ); + + // Update attribute to decoded byte values + l_capacities[PORT_NUM][DIMM_NUM] = sdram_capacity_map[l_sdram_capacity - CAPACITY_MAP_OFFSET]; + l_banks[PORT_NUM][DIMM_NUM] = sdram_banks_map[l_sdram_banks]; + l_bank_groups[PORT_NUM][DIMM_NUM] = sdram_bankgroups_map[l_sdram_bank_group]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_DENSITY, l_target_mcs, l_capacities)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_BANKS, l_target_mcs, l_banks)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_BANK_GROUPS, l_target_mcs, l_bank_groups)); + + FAPI_INF("%s. SDRAM capacity: %d, banks: %d, bank groups: %d", + c_str(i_target_dimm), + sdram_capacity_map[l_sdram_capacity], + sdram_banks_map[l_sdram_banks], + sdram_bankgroups_map[l_sdram_bank_group]); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode SDRAM addressing +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 5 +/// +fapi2::ReturnCode decoder::sdram_addressing(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 5; + // DRAM column address mapping limits + const size_t COLUMN_MAX_VALID_KEY = 3; // The rest are not supported or reserved + // DRAM row address mapping limits + const size_t ROW_MAX_VALID_KEY = 6; // The rest are not supported or reserved + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_column_addr = 0; + uint8_t l_row_addr = 0; + uint8_t l_reserved = 0; + + // Attribute variables used to set decoded vals + uint8_t l_columns[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_rows[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace in the front assists w/ debug + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Decoding bits 2~0 (Column Address bits) + l_spd_buffer.extractToRight<COL_ADDRESS_START, COL_ADDRESS_LEN>(l_column_addr); + + // Check to assure SPD DRAM Column Address bits (map) wont be indexed at invalid values + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_column_addr <= COLUMN_MAX_VALID_KEY), + BYTE_INDEX, + l_column_addr, + "Failed check for SPD DRAM bank groups") ); + + // Decoding bits 3~0 (Row address bits) + l_spd_buffer.extractToRight<ROW_ADDRESS_START, ROW_ADDRESS_LEN>(l_row_addr); + + // Check to assure SPD DRAM Row address bits (map) wont be at invalid values + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_row_addr <= ROW_MAX_VALID_KEY), + BYTE_INDEX, + l_row_addr, + "Failed check for SPD DRAM bank groups") ); + + // Decoding bits 7~6 + l_spd_buffer.extractToRight<ADDRESS_RESERVED_START, ADDRESS_RESERVED_LEN>(l_reserved); + + // Check to assure SPD reserved bits are 0 as defined in JEDEC SPD spec + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_reserved == 0), + BYTE_INDEX, + l_reserved, + "Failed check for SPD DRAM bank groups") ); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_cols(l_target_mcs, &l_columns[0][0]) ); + FAPI_TRY( eff_dram_rows(l_target_mcs, &l_rows[0][0]) ); + + // Update attribute to decoded byte values + l_columns[PORT_NUM][DIMM_NUM] = column_address_map[l_column_addr]; + l_rows[PORT_NUM][DIMM_NUM] = row_address_map[l_row_addr]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_COLS, l_target_mcs, l_columns)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_ROWS, l_target_mcs, l_rows)); + + FAPI_INF("%s. Columns: %d, Rows: %d", + c_str(i_target_dimm), + l_columns[PORT_NUM][DIMM_NUM], + l_rows[PORT_NUM][DIMM_NUM]); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Decode SDRAM Package Type +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 6 +/// +fapi2::ReturnCode decoder::primary_package_type(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 6; + const size_t INVALID_VALUE = 0x11; //per JEDEC spec + const size_t RESERVED = 0; + + // DRAM die count mapping limits + const size_t DIE_COUNT_MAX_VALID_KEY = 7; // Nothing greater doesn't exist + const size_t INVALID_PACKAGE_TYPE = ~0; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_prim_signal_loading = 0; + uint8_t l_reserved = 0; + uint8_t l_prim_die_count = 0; + uint8_t l_prim_package_type = 0; + + // Attribute variables used to set decoded vals + uint8_t l_stack_type = 0; + uint8_t l_sdram_package_type[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_sdram_die_count[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Decoding bits 1~0 (Signal loading) + l_spd_buffer.extractToRight<PRIM_PRIM_SIGNAL_LOAD_START, PRIM_PRIM_SIGNAL_LOAD_LEN>(l_prim_signal_loading); + + // Check to assure SPD DRAM signal loading conforms to JEDEC SPEC + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_prim_signal_loading != INVALID_VALUE), + BYTE_INDEX, + l_prim_signal_loading, + "Failed check for SPD DRAM signal loading") ); + // Decoding bits 3~2 (Reserved) + l_spd_buffer.extractToRight<PACKAGE_RESERVE_START, PACKAGE_RESERVE_LEN>(l_reserved); + + // Check to assure SPD reserved bits are 0 as defined in JEDEC SPD spec + mss::check::spd::valid_value_warn(i_target_dimm, + (l_reserved == RESERVED), + BYTE_INDEX, + l_reserved, + "Failed check for SPD DRAM reserved bits"); + + // Decoding bits 6~4 (Die Count) + l_spd_buffer.extractToRight<PRIM_DIE_COUNT_START, PRIM_DIE_COUNT_LEN>(l_prim_die_count); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_prim_die_count <= DIE_COUNT_MAX_VALID_KEY), + BYTE_INDEX, + l_prim_die_count, + "Failed check for SPD DRAM die count") ); + // Decoding bit 7 + l_spd_buffer.extractToRight<PRIM_PACKAGE_TYPE_START, PRIM_PACKAGE_TYPE_LEN>(l_prim_package_type); + + // Manipulating and combining l_prim_package_type and l_prim_signal_loading to produce the following + // table for indexing the package_type_map[]. + // 0000 0000 = SDP (monolithic device) + // 0000 0101 = DDP/QDP (non-monolithic device) + // 0000 0110 = 3DS (non-monolithic device) + // What this essentially does is remove reserved bits. + // This was done to avoid having large gaps (of 0's) in the package_type_map (sparsed array) + // since we can't use std::map due to thread saftey issues + l_stack_type = (l_prim_package_type >> 5) | l_prim_signal_loading; + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + check::stack_type(l_stack_type, die_count_map[l_prim_die_count]) && + (package_type_map[l_stack_type] != INVALID_PACKAGE_TYPE), + BYTE_INDEX, + package_type_map[l_stack_type], + "Failed check for SPD DRAM stack type") ); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_stack_type(l_target_mcs, &l_sdram_package_type[0][0]) ); + FAPI_TRY( eff_prim_die_count(l_target_mcs, &l_sdram_die_count[0][0]) ); + + // Update attribute to decoded byte values + l_sdram_die_count[PORT_NUM][DIMM_NUM] = die_count_map[l_prim_die_count]; + l_sdram_package_type[PORT_NUM][DIMM_NUM] = package_type_map[l_stack_type]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_STACK_TYPE, l_target_mcs, l_sdram_package_type)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_PRIM_DIE_COUNT, l_target_mcs, l_sdram_die_count)); + + FAPI_INF("%s. Signal loading: %d, Die count: %d, Stack type: %d", + c_str(i_target_dimm), + l_prim_signal_loading, + die_count_map[l_prim_die_count], + l_sdram_package_type[PORT_NUM][DIMM_NUM]); + +fapi_try_exit: + return fapi2::current_err; +} + +//TODO +// Need to complete this function, map need tREF1 calculations + +/// +/// @brief Decode SDRAM Optional Features +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 7 +/// +fapi2::ReturnCode decoder::sdram_optional_features(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 7; + const size_t RESERVED = 0; + + // MAC mapping limits + const size_t MAC_RESERVED = 7; //per JEDEC spec + const size_t MAC_MAX_VALID_KEY = 8; + + // TMAW mappint limits + const size_t TMAW_MAX_VALID_KEY = 2; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint16_t l_MAC = 0; // Maximum Active Count + uint16_t l_tMAW = 0; // Maximum Active Window + uint8_t l_reserved = 0; + + // Attribute variables used to set decoded vals + uint16_t l_mac[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint16_t l_tmaw[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Decoding bits 3~0 (MAC) + l_spd_buffer.extractToRight<MAC_START, MAC_LEN>(l_MAC); + // Check to assure SPD DRAM signal loading conforms to JEDEC SPEC + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_MAC <= MAC_MAX_VALID_KEY) && + l_MAC != MAC_RESERVED, + BYTE_INDEX, + l_MAC, + "Failed check for Maximum Active Count (MAC)") ); + + // Decoding bits 5~4 (tMAW) + l_spd_buffer.extractToRight<TMAW_START, TMAW_LEN>(l_tMAW); + + // Check to assure SPD DRAM signal loading conforms to JEDEC SPEC + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_tMAW <= TMAW_MAX_VALID_KEY), + BYTE_INDEX, + l_tMAW, + "Failed check for Maximum Active Window (tMAW)") ); + + // Decoding bits 7~6 (Reserved) + l_spd_buffer.extractToRight<OPT_FEAT_RESERVED_START, OPT_FEAT_RESERVED_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + (l_reserved == RESERVED), + BYTE_INDEX, + l_reserved, + "Failed check for Reserved bits") ; + + // Retrive entire MCS level attribute + FAPI_TRY(eff_dram_mac(l_target_mcs, &l_mac[0][0])); + FAPI_TRY(eff_dram_tmaw(l_target_mcs, &l_tmaw[0][0])); + + // Update attribute to decoded byte values + l_mac[PORT_NUM][DIMM_NUM] = MAC_map[l_MAC]; + l_tmaw[PORT_NUM][DIMM_NUM] = tMAW_map[l_tMAW]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_MAC, l_target_mcs, l_mac)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_TMAW, l_target_mcs, l_tmaw)); + + // Print decoded info + FAPI_INF("%s. MAC: %d, tMAW: %d", + c_str(i_target_dimm), + l_tmaw[PORT_NUM][DIMM_NUM], + l_mac[PORT_NUM][DIMM_NUM]); + +fapi_try_exit: + return fapi2::current_err; +}// decode_sdram_optional_features() + +/// +/// @brief Decode SDRAM Thermal and Refresh Options +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 8, currently reserved +/// +fapi2::ReturnCode decoder::thermal_and_refresh_options(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 8; + const size_t RESERVED_BYTE = 0; // per JEDEC spec + + // Byte variable + uint8_t l_reserved = i_spd_data[BYTE_INDEX]; + + // Check to assure SPD reserved byte is 0 per JEDEC spec + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_reserved == RESERVED_BYTE), + BYTE_INDEX, + l_reserved, + "Failed check for SPD DRAM reserved byte") ); + + // Print decoded info + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + +fapi_try_exit: + return fapi2::current_err; +}// decode_thermal_and_refresh_options() + + +/// +/// @brief Decode Other SDRAM Optional Features +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 9 +/// +fapi2::ReturnCode decoder::other_optional_features(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 9; + const size_t RESERVED = 0; + + // Soft PPR mapping limits + const size_t SOFT_PPR_MAX_VALID_KEY = 2; // Nothing greater doesn't exist + + // PPR mapping limits + const size_t PPR_MAX_VALID_KEY = 2; // Nothing greater doesn't exist + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_reserved = 0; + uint8_t l_soft_ppr = 0;// Soft Post Package Repair + uint8_t l_ppr = 0; // Post Package Repair + + // Attribute variables used to set decoded vals + uint8_t l_soft_PPRs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_PPRs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding bits 4~0 + l_spd_buffer.extractToRight<PPR_RESERVED_START, PPR_RESERVED_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + (l_reserved == RESERVED), + BYTE_INDEX, + l_reserved, + "Failed check for reserved bits"); + + // Decoding bit 5 + l_spd_buffer.extractToRight<SOFT_PPR_START, SOFT_PPR_LEN>(l_soft_ppr); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_soft_ppr < SOFT_PPR_MAX_VALID_KEY), + BYTE_INDEX, + l_soft_ppr, + "Failed check for SOFT PPR") ); + + // Decoding bits 7~6 + l_spd_buffer.extractToRight<PPR_START, PPR_LEN>(l_ppr); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_ppr < PPR_MAX_VALID_KEY), + BYTE_INDEX, + l_ppr, + "Failed check for PPR") ); + + // Retrive entire MCS level attribute + FAPI_TRY(eff_dram_soft_ppr(l_target_mcs, &l_soft_PPRs[0][0])); + FAPI_TRY(eff_dram_ppr(l_target_mcs, &l_PPRs[0][0])); + + // Update attribute to decoded byte values + l_soft_PPRs[PORT_NUM][DIMM_NUM] = soft_ppr_map[l_soft_ppr]; + l_PPRs[PORT_NUM][DIMM_NUM] = ppr_map[l_ppr]; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_SOFT_PPR, l_target_mcs, l_soft_PPRs)); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_PPR, l_target_mcs, l_PPRs)); + + // Printed decoded info + FAPI_INF("%s. Soft PPR: %d, PPR: %d, Reserved: %d", + c_str(i_target_dimm), + l_soft_PPRs[PORT_NUM][DIMM_NUM], + l_PPRs[PORT_NUM][DIMM_NUM], + l_reserved); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Secondary SDRAM Package Type +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 10 +/// +fapi2::ReturnCode decoder::secondary_package_type(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 10; + const size_t SUPPORTED_VALUE = 0; + + // Byte variables used for decoding + uint8_t l_sec_signal_loading = 0; + uint8_t l_density_ratio = 0; + uint8_t l_sec_die_count = 0; + uint8_t l_sec_package_type = 0; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Currently we do not support asymmetrical assembly of multiple SDRAM package types + // According to the JEDEC spec, for modules with symmetrical assembly (which we do support), + // this byte must be coded as 0x00. Additional checks were added to isolate any corrupt data failure + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding bits 1~0 (Signal loading) + l_spd_buffer.extractToRight<SEC_SIGNAL_LOAD_START, SEC_SIGNAL_LOAD_LEN>(l_sec_signal_loading); + + // Check to assure SPD DRAM signal loading conforms to JEDEC SPEC + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sec_signal_loading == SUPPORTED_VALUE), + BYTE_INDEX, + l_sec_signal_loading, + "Failed check for SPD DRAM signal loading") ); + + // Decoding bits 3~2 (Density Ratio) + l_spd_buffer.extractToRight<DENSITY_RATIO_START, DENSITY_RATIO_LEN>(l_density_ratio); + + // Check to assure SPD density ratio bits are 0 to assure symmetical package + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_density_ratio == SUPPORTED_VALUE), + BYTE_INDEX, + l_density_ratio, + "Failed check for SPD DRAM density ratio") ); + + // Decoding bits 6~4 (Die Count) + l_spd_buffer.extractToRight<SEC_DIE_COUNT_START, SEC_DIE_COUNT_LEN>(l_sec_die_count); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sec_die_count == SUPPORTED_VALUE), + BYTE_INDEX, + l_sec_die_count, + "Failed check for SPD DRAM secondary die count") ); + + // Decoding bit 7 + l_spd_buffer.extractToRight<SEC_PACKAGE_TYPE_START, SEC_PACKAGE_TYPE_LEN>(l_sec_package_type); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + (l_sec_package_type == SUPPORTED_VALUE), + BYTE_INDEX, + l_sec_package_type, + "Failed check for SPD DRAM secondary package type") ); + + // Printed decoded info + FAPI_INF("Signal Loading: %d, DRAM Density Ratio: %d, Die Count: %d, SDRAM Package Type: %d", + l_sec_signal_loading, + l_density_ratio, + l_sec_die_count, + l_sec_package_type); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Module Nominal Voltage, VDD +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 11 +/// +fapi2::ReturnCode decoder::module_nominal_voltage(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 11; + const size_t RESERVED = 0; // per JEDEC sepc + const size_t SUPPORTED = 1; // per JEDEC sepc + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_operable = 0; + uint8_t l_endurant = 0; + uint8_t l_reserved = 0; + + // Attribute variables used to set decoded vals + uint64_t l_operable_attrs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding bits 0 (Operable) + l_spd_buffer.extractToRight<OPERABLE_START, OPERABLE_LEN>(l_operable); + + // DDR4 only supports 1.2 V, if not OPERABLE at this voltage than fail IPL + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_operable == SUPPORTED, + BYTE_INDEX, + l_operable, + "Failed check for OPERABLE module nominal voltage") ); + // Decoding bits 1 (Endurant) + l_spd_buffer.extractToRight<ENDURANT_START, ENDURANT_LEN>(l_endurant); + + // OPERABLE at 1.2V implies ENDURANT at 1.2V + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_endurant == SUPPORTED, + BYTE_INDEX, + l_endurant, + "Failed check for ENDURABLE module nominal voltage") ); + + // Decoding bits 7~2 (Reserved) + l_spd_buffer.extractToRight<NOM_VOLT_START, NOM_VOLT_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for module nominal voltage RESERVED bits"); + + // Retrive entire MCS level attribute + FAPI_TRY(spd_module_nominal_voltage(l_target_mcs, &l_operable_attrs[0][0])); + + // Update attribute to decoded byte values + l_operable_attrs[PORT_NUM][DIMM_NUM] = l_operable; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_SPD_MODULE_NOMINAL_VOLTAGE, l_target_mcs, l_operable_attrs) ); + + // Printed decoded info + FAPI_INF( "%s Operable: %d, Endurant: %d, Reserved: %d", + c_str(i_target_dimm), + l_operable, + l_endurant, + l_reserved ); + + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Module Organization +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 12 +/// +fapi2::ReturnCode decoder::module_organization(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 12; + const size_t RESERVED = 0; + + // SDRAM device width mapping limits + const size_t MAX_DEV_WIDTH_VALID_KEY = 3; // All others reserved + + // Number of package ranks per DIMM mapping limits + const size_t MAX_PKG_RANKS_VALID_KEY = 7; // max supported packages + + // Rank mix limits + const size_t SYMMETRICAL = 0; // asymmetical not supported + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_device_width = 0; + uint8_t l_num_pkgs_ranks = 0; + uint8_t l_rank_mix = 0; + uint8_t l_reserved = 0; + + // Attribute variables used to set decoded vals + uint8_t l_sdram_device_widths[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_num_pkg_ranks_per_dimm[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_rank_mixes[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding Bits 2~0 + l_spd_buffer.extractToRight<SDRAM_WIDTH_START, SDRAM_WIDTH_LEN>(l_device_width); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_device_width <= MAX_DEV_WIDTH_VALID_KEY, + BYTE_INDEX, + l_device_width, + "Failed check for SDRAM device width") ); + // Decoding Bits 5~3 + l_spd_buffer.extractToRight<PACKAGE_RANKS_START, PACKAGE_RANKS_LEN>(l_num_pkgs_ranks); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_num_pkgs_ranks <= MAX_PKG_RANKS_VALID_KEY, + BYTE_INDEX, + l_num_pkgs_ranks, + "Failed check for number of packages per DIMM") ); + // Decoding Bit 6 + l_spd_buffer.extractToRight<RANK_MIX_START, RANK_MIX_LEN>(l_rank_mix); + + // We only support symmetrical rank mix + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_rank_mix == SYMMETRICAL, + BYTE_INDEX, + l_rank_mix, + "Failed check for number of packages per DIMM") ); + // Decoding Bit 7 + l_spd_buffer.extractToRight<MODULE_ORG_RESERVED_START, MODULE_ORG_RESERVED_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for number of packages per DIMM"); + + // Retrive entire MCS level attribute + FAPI_TRY(eff_dram_width(l_target_mcs, &l_sdram_device_widths[0][0])); + FAPI_TRY(eff_num_packages_per_rank(l_target_mcs, &l_num_pkg_ranks_per_dimm[0][0])); + FAPI_TRY(eff_dram_rank_mix(l_target_mcs, &l_rank_mixes[0][0])); + + // Update attribute to decoded byte values + l_sdram_device_widths[PORT_NUM][DIMM_NUM] = device_type_map[l_device_width]; + l_num_pkg_ranks_per_dimm[PORT_NUM][DIMM_NUM] = num_pkgs_ranks_per_dimm_map[l_num_pkgs_ranks]; + l_rank_mixes[PORT_NUM][DIMM_NUM] = l_rank_mix; + + // Update MCS level attribute + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_WIDTH, l_target_mcs, l_sdram_device_widths) ); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_NUM_PACKAGES_PER_RANK, l_target_mcs, l_num_pkg_ranks_per_dimm) ); + FAPI_TRY( FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_RANK_MIX, l_target_mcs, l_rank_mixes) ); + + // Printed decoded info + FAPI_INF( "%s. Device Width: %d, Number of rank packages per DIMM: %d, Rank Mix: %d", + c_str(i_target_dimm), + l_sdram_device_widths[PORT_NUM][DIMM_NUM], + l_num_pkg_ranks_per_dimm[PORT_NUM][DIMM_NUM], + l_rank_mixes[PORT_NUM][DIMM_NUM] ); + +fapi_try_exit: + return fapi2::current_err; +} + +//TODO +// Returning correct stuff (bits) ?? Or am I supposed to calc "Module DRAM Capacity" pg 27 + +/// +/// @brief Decode Module Memory Bus Width +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 13 +/// +fapi2::ReturnCode decoder::module_memory_bus_width(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 13; + const size_t RESERVED = 0; + + // Primary bus width mapping limits + const size_t MAX_PRIM_BUS_WIDTH_KEY = 3; // All others reserved + + // Bus width extention mapping limits + const size_t MAX_VALID_BUS_WIDTH_EXT_KEY = 1; // All others reserved + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint8_t l_bus_width = 0; + uint8_t l_bus_width_ext = 0; + uint8_t l_reserved = 0; + + // Attribute variables used to set decoded vals + uint8_t l_module_bus_widths[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding Bits 2~0 + l_spd_buffer.extractToRight<BUS_WIDTH_START, BUS_WIDTH_LEN>(l_bus_width); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_bus_width <= MAX_PRIM_BUS_WIDTH_KEY, + BYTE_INDEX, + l_bus_width, + "Failed check on primary bus width") ); + // Decoding Bits 4~3 + l_spd_buffer.extractToRight<BUS_EXT_WIDTH_START, BUS_EXT_WIDTH_LEN>(l_bus_width_ext); + + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_bus_width_ext <= MAX_VALID_BUS_WIDTH_EXT_KEY, + BYTE_INDEX, + l_bus_width_ext, + "Failed check for bus width extension") ); + // Decoding bits Bits 7~5 + l_spd_buffer.extractToRight<BUS_WIDTH_RESERVED_START, BUS_WIDTH_RESERVED_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for bus width reserved bits"); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_module_bus_width(l_target_mcs, &l_module_bus_widths[0][0]) ); + + // Update attribute to decoded byte values + l_module_bus_widths[PORT_NUM][DIMM_NUM] = prim_bus_width_map[l_bus_width] + bus_width_ext_map[l_bus_width_ext]; + + // Update MCS level attribute + FAPI_ATTR_SET(fapi2::ATTR_EFF_DRAM_MODULE_BUS_WIDTH, l_target_mcs, l_module_bus_widths); + + // Printed decoded info + FAPI_INF( "%s Module Memory Bus Width (in bits): %d, Primary bus width: %d, Bus width extension: %d", + c_str(i_target_dimm), + l_module_bus_widths[PORT_NUM][DIMM_NUM], + prim_bus_width_map[l_bus_width], + bus_width_ext_map[l_bus_width_ext] ); + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Decode Module Thermal Sensor +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 14, no attribute found for this +/// +fapi2::ReturnCode decoder::module_thermal_sensor(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 14; + const size_t VALID_VALUE = 1; + const size_t RESERVED = 0; + + // Byte variables used for decoding + uint8_t l_reserved = 0; + uint8_t l_thermal_sensor = 0; + + // Attribute variables used to set decoded vals + uint8_t l_therm_sensors[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding Bits 6~0 + l_spd_buffer.extractToRight<THERM_SENSOR_RESERV_START, THERM_SENSOR_RESERV_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for thermal sensor reserved bits"); + // Decoding bits Bit 7 + l_spd_buffer.extractToRight<THERM_SENSOR_START, THERM_SENSOR_LEN>(l_thermal_sensor); + + // Length is a single bit (0 or 1), anything larger means corrupt data + FAPI_TRY( mss::check::spd::valid_value_fail(i_target_dimm, + l_thermal_sensor <= VALID_VALUE, + BYTE_INDEX, + l_thermal_sensor, + "Failed check for thermal sensor") ); + + // Retrive entire MCS level attribute + FAPI_TRY( spd_module_thermal_sensor(l_target_mcs, &l_therm_sensors[0][0]) ); + + // Update attribute to decoded byte values + l_therm_sensors[PORT_NUM][DIMM_NUM] = l_thermal_sensor; + + // Update MCS level attribute + FAPI_ATTR_SET(fapi2::ATTR_SPD_MODULE_THERMAL_SENSOR, l_target_mcs, l_therm_sensors); + + // Printed decoded info + FAPI_INF("%s. Thermal sensor: %d, Reserved: %d", + c_str(i_target_dimm), + l_thermal_sensor, + l_reserved ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Extended Module Type +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 15, no attribute for this byte +/// +fapi2::ReturnCode decoder::extended_module_type(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 15; + const size_t RESERVED = 0; + + // Byte variables used for decoding + uint8_t l_ext_module_type = 0; + uint8_t l_reserved = 0; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // Decoding Bits 3~0 + l_spd_buffer.extractToRight<EXT_MOD_TYPE_START, EXT_MOD_TYPE_LEN>(l_ext_module_type); + + // According to JEDEC spec this value should be coded as 0000 which is the value as the reserved bits + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + l_ext_module_type == RESERVED, + BYTE_INDEX, + l_ext_module_type, + "Failed check for extended base module type") ); + // Decoding Bit 7~4 + l_spd_buffer.extractToRight<EXT_MOD_TYPE_RESERV_START, EXT_MOD_TYPE_RESERV_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for extended module type reserved bits"); + + // Printed decoded info + FAPI_INF("%s. Extended Base Module Type: %d, Reserved: %d", + c_str(i_target_dimm), + l_ext_module_type, + l_reserved ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Timebases +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 17 +/// +fapi2::ReturnCode decoder::timebases(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 17; + const size_t RESERVED = 0; + + // Medium timebase mapping limits + const int64_t MAX_VALID_MTB_KEY = 1; // All others reserved + + // Fine timebase mapping limits + const int64_t MAX_VALID_FTB_KEY = 1; // All others reserved + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + int64_t l_fine_timebase = 0; + int64_t l_medium_timebase = 0; + int64_t l_reserved = 0; + + // Attribute variables used to set decoded vals + int64_t l_FTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_MTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Buffer used for bit manipulation + fapi2::buffer<uint8_t> l_spd_buffer(i_spd_data[BYTE_INDEX]); + + // TODO - update ENUMS to take account to int64_t + // Decoding Bits 1~0 + l_spd_buffer.extractToRight<FINE_TIMEBASE_START, FINE_TIMEBASE_LEN>(l_fine_timebase); + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + l_fine_timebase < MAX_VALID_FTB_KEY, + BYTE_INDEX, + l_fine_timebase, + "Failed check for fine timebase") ); + // Decoding Bits 3~2 + l_spd_buffer.extractToRight<MED_TIMEBASE_START, MED_TIMEBASE_LEN>(l_medium_timebase); + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + l_medium_timebase < MAX_VALID_MTB_KEY, + BYTE_INDEX, + l_medium_timebase, + "Failed check for medium timebase") ); + // Decoding bits Bits 7~4 + l_spd_buffer.extractToRight<TIMEBASE_RESERV_START, TIMEBASE_RESERV_LEN>(l_reserved); + + mss::check::spd::valid_value_warn(i_target_dimm, + l_reserved == RESERVED, + BYTE_INDEX, + l_reserved, + "Failed check for timebases reserved bits"); + + // Retrive entire MCS level attribute + FAPI_TRY( spd_fine_timebase(l_target_mcs, &l_FTBs[0][0]) ); + FAPI_TRY( spd_medium_timebase(l_target_mcs, &l_MTBs[0][0]) ); + + // Update attribute to decoded byte values + l_FTBs[PORT_NUM][DIMM_NUM] = fine_timebase_map[l_fine_timebase]; + l_MTBs[PORT_NUM][DIMM_NUM] = medium_timebase_map[l_medium_timebase]; + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_SPD_FINE_TIMEBASE, l_target_mcs, l_FTBs ); + FAPI_ATTR_SET( fapi2::ATTR_SPD_MEDIUM_TIMEBASE, l_target_mcs, l_MTBs ); + + // Printed decoded info + FAPI_INF( "%s. Fine Timebase: %d, Medium Timebase: %d", + c_str(i_target_dimm), + l_FTBs[PORT_NUM][DIMM_NUM], + l_MTBs[PORT_NUM][DIMM_NUM] ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode SDRAM Minimum Cycle Time +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 18 & Byte 125 +/// This byte depends on the fine & medium timebase values +/// obtained from Byte 17 +/// +fapi2::ReturnCode decoder::min_cycle_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MTB = 18; // min cycle medium timebase (mtb) + const size_t BYTE_INDEX_FTB = 125; // min cycle fine timebase (ftb) + + const int64_t MIN_CYCLE_TIME_MTB = 1; // from JEDEC + const int64_t MAX_CYCLE_TIME_MTB = 255; // from JEDEC + + const int64_t MIN_CYCLE_TIME_FTB = -128; // from JEDEC + const int64_t MAX_CYCLE_TIME_FTB = 127; // from JEDEC + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + int64_t l_tCKmin_mtb = 0; + int64_t l_tCKmin_ftb = 0; + + // Attribute variables + int64_t l_min_cycle_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_MTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_FTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_MTB, + i_spd_data[BYTE_INDEX_MTB]); + + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_FTB, + i_spd_data[BYTE_INDEX_FTB]); + + // Retrieve SDRAM Maximum Cycle Time + l_tCKmin_mtb = i_spd_data[BYTE_INDEX_MTB]; + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tCKmin_mtb <= MAX_CYCLE_TIME_MTB) && + (l_tCKmin_mtb >= MIN_CYCLE_TIME_MTB), + BYTE_INDEX_MTB, + l_tCKmin_mtb, + "Failed check for tCKmin (min cycle time) in MTB units") ); + + // Retrieve Fine Offset for SDRAM Minimum Cycle Time + // casted int8_t undoes 2's complement on the uint8_t spd data + l_tCKmin_ftb = int8_t(i_spd_data[BYTE_INDEX_FTB]); + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tCKmin_ftb <= MAX_CYCLE_TIME_FTB) && + (l_tCKmin_ftb >= MIN_CYCLE_TIME_FTB), + BYTE_INDEX_FTB, + l_tCKmin_ftb, + "Failed check for tCKmin (min cycle time) in FTB units") ); + + + // Retrieving medium timebase (MTB) multiplier used for timing calculation + FAPI_TRY( spd_medium_timebase(l_target_mcs, &l_MTBs[0][0]) ); + FAPI_TRY( spd_fine_timebase(l_target_mcs, &l_FTBs[0][0]) ); + + // Retrive entire MCS level attribute + FAPI_TRY( spd_timing_tckmin(l_target_mcs, &l_min_cycle_times[0][0]) ); + + // Update attribute to decoded byte values + // Calculating minimum cycle time in picosconds + l_min_cycle_times[PORT_NUM][DIMM_NUM] = calc_timing(l_tCKmin_mtb, + l_tCKmin_ftb, + l_MTBs[PORT_NUM][DIMM_NUM], + l_FTBs[PORT_NUM][DIMM_NUM]); + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_TIMING_TCKMIN, l_target_mcs, l_min_cycle_times ); + + // Printed decoded info + FAPI_INF("%s. tCKmin (min cycle time): %d (ps)", + c_str(i_target_dimm), + l_min_cycle_times[PORT_NUM][DIMM_NUM] ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode SDRAM Maximum Cycle Time +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 19 and 124 +/// +fapi2::ReturnCode decoder::max_cycle_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MTB = 19; // min cycle medium timebase (mtb) + const size_t BYTE_INDEX_FTB = 124; // min cycle fine timebase (ftb) + + const int64_t MIN_CYCLE_TIME_MTB = 1; // from JEDEC + const int64_t MAX_CYCLE_TIME_MTB = 255; // from JEDEC + + const int64_t MIN_CYCLE_TIME_FTB = -128; // from JEDEC + const int64_t MAX_CYCLE_TIME_FTB = 127; // from JEDEC + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + int64_t l_tCKmax_mtb = 0; + int64_t l_tCKmax_ftb = 0; + + // Attribute variable + int64_t l_max_cycle_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_MTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_FTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_MTB, + i_spd_data[BYTE_INDEX_MTB]); + + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_FTB, + i_spd_data[BYTE_INDEX_FTB]); + + // Retrieve SDRAM Maximum Cycle Time + l_tCKmax_mtb = i_spd_data[BYTE_INDEX_MTB]; + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tCKmax_mtb <= MAX_CYCLE_TIME_MTB) && + (l_tCKmax_mtb >= MIN_CYCLE_TIME_MTB), + BYTE_INDEX_MTB, + l_tCKmax_mtb, + "Failed check for tCKmin (min cycle time) in MTB units") ); + + // Retrieve Fine Offset for SDRAM Maximum Cycle Time + // casted int8_t undoes 2's complement on the uint8_t spd data + l_tCKmax_ftb = int8_t(i_spd_data[BYTE_INDEX_FTB]); + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tCKmax_ftb <= MAX_CYCLE_TIME_FTB) && + (l_tCKmax_ftb >= MIN_CYCLE_TIME_FTB), + BYTE_INDEX_FTB, + l_tCKmax_ftb, + "Failed check for tCKmin (min cycle time) in FTB units") ); + + // Retrieving medium timebase (MTB) multiplier used for timing calculation + FAPI_TRY( spd_medium_timebase(l_target_mcs, &l_MTBs[0][0]) ); + FAPI_TRY( spd_fine_timebase(l_target_mcs, &l_FTBs[0][0]) ); + + // Retrive entire MCS level attribute + FAPI_TRY(spd_timing_tckmax(l_target_mcs, &l_max_cycle_times[0][0])) + + // Update attribute to decoded byte values + l_max_cycle_times[PORT_NUM][DIMM_NUM] = calc_timing(l_tCKmax_mtb, + l_tCKmax_ftb, + l_MTBs[PORT_NUM][DIMM_NUM], + l_FTBs[PORT_NUM][DIMM_NUM]); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_TIMING_TCKMAX, l_target_mcs, l_max_cycle_times ); + + // Printed decoded info + FAPI_INF("%s. tCKmax (max cycle time): %d (ps)", + c_str(i_target_dimm), + l_max_cycle_times[PORT_NUM][DIMM_NUM] ); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Minimum CAS Latency Time +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 24 & 123 +/// +fapi2::ReturnCode decoder::min_cas_latency_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MTB = 24; // min cycle medium timebase (mtb) + const size_t BYTE_INDEX_FTB = 123; // min cycle fine timebase (ftb) + + const int64_t MIN_CYCLE_TIME_MTB = 1; // from JEDEC + const int64_t MAX_CYCLE_TIME_MTB = 255; // from JEDEC + + const int64_t MIN_CYCLE_TIME_FTB = -128; // from JEDEC + const int64_t MAX_CYCLE_TIME_FTB = 127; // from JEDEC + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + int64_t l_tAAmin_mtb = 0; + int64_t l_tAAmin_ftb = 0; + + // Attribute variable + int64_t l_min_cas_latency_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_MTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + int64_t l_FTBs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_MTB, + i_spd_data[BYTE_INDEX_MTB]); + + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX_FTB, + i_spd_data[BYTE_INDEX_FTB]); + + // Retrieve SDRAM Minimum CAS Latency Time + l_tAAmin_mtb = i_spd_data[BYTE_INDEX_MTB]; + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tAAmin_mtb <= MAX_CYCLE_TIME_MTB) && + (l_tAAmin_mtb >= MIN_CYCLE_TIME_MTB), + BYTE_INDEX_MTB, + l_tAAmin_mtb, + "Failed check for min CAS latency time (tAAmin) in MTB units") ); + + // Retrieve Fine Offset for Minimum CAS Latency Time + // casted int8_t undoes 2's complement on the uint8_t spd data + l_tAAmin_ftb = int8_t(i_spd_data[BYTE_INDEX_FTB]); + + FAPI_TRY(mss::check::spd::valid_value_fail(i_target_dimm, + (l_tAAmin_ftb <= MAX_CYCLE_TIME_FTB) && + (l_tAAmin_ftb >= MIN_CYCLE_TIME_FTB), + BYTE_INDEX_FTB, + l_tAAmin_ftb, + "Failed check for min CAS latency time (tAAmin) in FTB units") ); + + // Retrieving medium timebase (MTB) multiplier used for timing calculation + FAPI_TRY( spd_medium_timebase(l_target_mcs, &l_MTBs[0][0]) ); + FAPI_TRY( spd_fine_timebase(l_target_mcs, &l_FTBs[0][0]) ); + + // Retrive entire MCS level attribute + FAPI_TRY(spd_timing_taamin(l_target_mcs, &l_min_cas_latency_times[0][0])) + + // Update attribute to decoded byte values + l_min_cas_latency_times[PORT_NUM][DIMM_NUM] = calc_timing(l_tAAmin_mtb, + l_tAAmin_ftb, + l_MTBs[PORT_NUM][DIMM_NUM], + l_FTBs[PORT_NUM][DIMM_NUM]); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_TIMING_TAAMIN, l_target_mcs, l_min_cas_latency_times ); + + // Printed decoded info + FAPI_INF("%s. tAAmin (min cycle time): %d (ps)", + c_str(i_target_dimm), + l_min_cas_latency_times[PORT_NUM][DIMM_NUM] ); + +fapi_try_exit: + return fapi2::current_err; +} + +#if 0 + +/// +/// @brief Decode CAS Latencies Supported +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 20-23 +/// +fapi2::ReturnCode decoder::supported_cas_latencies(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t FIRST_BYTE = 20; + const size_t SEC_BYTE = 21; + const size_t THIRD_BYTE = 22; + const size_t FORTH_BYTE = 23; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding + uint64_t l_supported_cas_lat = 0; + + // Attribute variables used to set decoded vals + uint64_t l_supported_CLs[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_first_byte_buff(i_spd_data[FIRST_BYTE]); + fapi2::buffer<uint8_t> l_second_byte_buff(i_spd_data[SEC_BYTE]); + fapi2::buffer<uint8_t> l_third_byte_buff(i_spd_data[THIRD_BYTE]); + fapi2::buffer<uint8_t> l_forth_byte_buff(i_spd_data[FORTH_BYTE]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // TODO - update ENUMS to take account to int64_t + + // Combine Bytes 20 - 23 to create bitmap + l_first_byte_buff.extractToRight<CAS_BYTE_1_START, CAS_BYTE_1_LEN>(l_supported_cas_lat). + l_second_byte_buff.extractToRight<CAS_BYTE_2_START, CAS_BYTE_2_LEN>(l_supported_cas_lat). + l_third_byte_buff.extractToRight<CAS_BYTE_3_START, CAS_BYTE_3_LEN>(l_supported_cas_lat). + l_forth_byte_buff.extractToRight<CAS_BYTE_4_START, CAS_BYTE_4_LEN>(l_supported_cas_lat); + + // Retrive entire MCS level attribute + FAPI_TRY( cas_latencies_supported(l_target_mcs, &l_supported_CLs[0][0]) ); + + // Update attribute to decoded byte values + l_supported_CLs[PORT_NUM][DIMM_NUM] = l_supported_cas_lat; + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_SPD_CAS_LATENCIES_SUPPORTED, l_target_mcs, l_supported_CLs ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + + + +/// +/// @brief Decode Minimum RAS to CAS Delay Time +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 25 +/// +fapi2::ReturnCode decoder::min_ras_to_cas_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 25; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (rcd = ras to cas delay) + int64_t l_min_rcd_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trcd(l_target_mcs, &l_min_rcd_times[0][0]) ); + + // Update attribute to decoded byte values + l_min_rcd_times[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRCD, l_target_mcs, l_min_rcd_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Minimum Row Precharge Delay Time +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte 26 +/// +fapi2::ReturnCode decoder::min_row_precharge_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 26; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (rp = row to precharge) + int64_t l_min_rp_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trp(l_target_mcs, &l_min_rp_times[0][0]) ); + + // Update attribute to decoded byte values + l_min_rp_times[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRP, l_target_mcs, l_min_rp_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Decode Minimum Active to Precharge Delay Time (tRASmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 27 Bits 3~0 along with Byte 28 Bits 7~0 +/// +fapi2::ReturnCode decoder::min_activate_to_precharge_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSB = 27; // MSN = most significant byte + const size_t BYTE_INDEX_LSB = 28; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (tRAS = Active to Precharge Delay Time) + int64_t l_tRASmin = 0; + + // Attribute variable + int64_t l_min_ras_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSB]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // TODO - update ENUMS to take account to int64_t + + // Combining bits to create timing value + l_buffer_upper_nibble.extractToRight<TRASMIN_MSN_START, TRASMIN_MSN_LEN>(l_tRASmin); + l_buffer_lower_byte.extractToRight<TRASMIN_LSB_START, TRASMIN_LSB_LEN>(l_tRASmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_tras(l_target_mcs, &l_min_ras_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds) + l_min_ras_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tRASmin); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRAS, l_target_mcs, l_min_ras_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Active to Active/Refresh Delay Time (tRCmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte Byte 27 Bits 7~4 along with Byte 29 Bits 7~0 +/// +fapi2::ReturnCode +decoder::min_activate_to_activate_refresh_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSN = 27; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 28; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (RC = Activate to Activate/Refresh Delay) + uint16_t l_tRCmin = 0; + + // Attribute variable + uint16_t l_min_rc_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.extractToRight<TRCMIN_MSN_START, TRCMIN_MSN_LEN>(l_tRCmin); + l_buffer_lower_byte.extractToRight<TRCMIN_LSB_START, TRCMIN_LSB_LEN>(l_tRCmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trc(l_target_mcs, &l_min_rc_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_rc_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tRCmin); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRC, l_target_mcs, l_min_rc_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Refresh Recovery Delay Time 1 (tRFC1min) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte Byte 30 (LSB) along with Byte 31 (MSB) +/// +fapi2::ReturnCode decoder::min_refresh_recovery_delay_time_1(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSB = 31; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 30; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (RFC1 = Minimum Refresh Recovery Delay Time) + uint32_t l_tRFC1min = 0; + + // Attribute variable + uint32_t l_min_rfc1_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_MSB(i_spd_data[BYTE_INDEX_MSB]); + fapi2::buffer<uint8_t> l_buffer_LSB(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_MSB.extractToRight<, >(); + l_buffer_LSB.extractToRight<, >(); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trc(l_target_mcs, &l_min_rfc1_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_rfc1_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tRFC1min); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRFC1, l_target_mcs, l_min_rfc1_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Refresh Recovery Delay Time 2 (tRFC2min) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte Byte 32 (LSB) along with Byte 33 (MSB) +/// +fapi2::ReturnCode decoder::min_refresh_recovery_delay_time_2(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSB = 33; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 32; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (RFC1 = Minimum Refresh Recovery Delay 2) + uint32_t l_tRFC2min = 0; + + // Attribute variable + uint32_t l_min_rfc2_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_MSB(i_spd_data[BYTE_INDEX_MSB]); + fapi2::buffer<uint8_t> l_buffer_LSB(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_MSB.extractToRight<, >(); + l_buffer_LSB.extractToRight<, >(); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trc(l_target_mcs, &l_min_rfc2_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_rfc1_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tRFC2min); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRFC2, l_target_mcs, l_min_rfc2_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Refresh Recovery Delay Time 4 (tRFC4min) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note SPD Byte Byte 34 (LSB) along with Byte 5 (MSB) +/// +fapi2::ReturnCode decoder::min_refresh_recovery_delay_time_4(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSB = 35; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 34; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (RFC4 = Minimum Refresh Recovery Delay 4) + uint32_t l_tRFC4min = 0; + + // Attribute variable + uint32_t l_min_rfc4_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_MSB(i_spd_data[BYTE_INDEX_MSB]); + fapi2::buffer<uint8_t> l_buffer_LSB(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_MSB.extractToRight<, >(); + l_buffer_LSB.extractToRight<, >(); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trc(l_target_mcs, &l_min_rfc4_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_rfc1_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tRFC4min); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRFC2, l_target_mcs, l_min_rfc4_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Four Activate Window Delay Time (t FAW min) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 36 Bits 3 ~ 0 along with Byte 37 Bits 7 ~ 0 +/// +fapi2::ReturnCode decoder::min_four_activate_window_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSN = 36; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 37; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (FAW = Four Activate Window Delay) + uint16_t l_tFAW = 0; + + // Attribute variable + uint16_t l_min_faw_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.extractToRight<TRCMIN_MSN_START, TRCMIN_MSN_LEN>(l_tFAW); + l_buffer_lower_byte.extractToRight<TRCMIN_LSB_START, TRCMIN_LSB_LEN>(l_tFAW); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trc(l_target_mcs, &l_min_faw_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_faw_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tFAW); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRC, l_target_mcs, l_min_faw_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Activate to Activate Delay Time (tRRD_Smin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 38 +/// +fapi2::ReturnCode +decoder::min_act_to_act_delay_time_diff_bank_group(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 38; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (rrd_s = Activate to Activate Delay) + int64_t l_min_tRRD_S[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_tRRD_S[0][0]) ); + + // Update attribute to decoded byte values + l_min_tRRD_S[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRRD_S, l_target_mcs, l_min_tRRD_S ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Activate to Activate Delay Time (tRRD_Lmin), samebank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 39 +/// +fapi2::ReturnCode +decoder::min_act_to_act_delay_time_same_bank_group(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 39; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (rrd_s = Activate to Activate Delay) + int64_t l_min_tRRD_S[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_tRRD_S[0][0]) ); + + // Update attribute to decoded byte values + l_min_tRRD_S[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TRRD_L, l_target_mcs, l_min_tRRD_S ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum CAS to CAS Delay Time (tCCD_Lmin), same bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 40 +/// +fapi2::ReturnCode decoder::min_cas_to_cas_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 40; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TCCD_L, l_target_mcs, l_min_tCCD_L ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Write Recovery Time (tWRmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 41 Bits 3~0, Byte 42 Bits 7~0 +/// +fapi2::ReturnCode decoder::min_write_recovery_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSN = 41; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 42; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (FAW = Four Activate Window Delay) + uint16_t l_tWRmin = 0; + + // Attribute variable + int64_t l_min_wr_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.insert<, >(l_tWRmin); + l_buffer_lower_byte.insert<, >(l_tWRmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_twr(l_target_mcs, &l_min_wr_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_wr_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tWRmin); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TWR, l_target_mcs, l_min_wr_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Write to Read Time (tWTR_Smin), different bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 43 Bits 3~0, Byte 44 Bits 7~0 +/// +fapi2::ReturnCode decoder::min_write_to_read_time_diff_bank_group(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSN = 43; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 44; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (WRT = Write to Read Time) + int64_t l_tWRTmin = 0; + + // Attribute variable + int64_t l_min_wrt_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.insert<, >(l_tWRTmin); + l_buffer_lower_byte.insert<, >(l_tWRTmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_twrt(l_target_mcs, &l_min_wrt_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_wrt_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tWRTmin); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TWRT, l_target_mcs, l_min_wrt_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Minimum Write to Read Time (tWTR_Lmin), same bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 43 Bits 7~4, Byte 45 Bits 7~0 +/// +fapi2::ReturnCode +decoder::min_write_to_read_time_same_bank_group(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSN = 43; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 45; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (WRT = Write to Read Time) + int64_t l_tWRTmin = 0; + + // Attribute variable + int64_t l_min_wrt_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.insert<, >(l_tWRTmin); + l_buffer_lower_byte.insert<, >(l_tWRTmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_twrt(l_target_mcs, &l_min_wrt_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_wrt_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tWRTmin); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TWRT, l_target_mcs, l_min_wrt_times ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Connector to SDRAM Bit Mapping +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Bytes 60~77 +/// +fapi2::ReturnCode decoder::connector_to_sdram_bit_mapping(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ +// Retrive entire MCS level attribute +// Update attribute to decoded byte values +// Update MCS level attribute + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for Minimum CAS to CAS Delay Time (tCCD_Lmin), same bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 117 +/// +fapi2::ReturnCode decoder::offset_for_min_cas_to_cas_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 117; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +// Retrive entire MCS level attribute +// Update attribute to decoded byte values +// Update MCS level attribute + +fapi_try_exit: +return fapi2::current_err; +} + +/// +/// @brief Fine Offset for Minimum Activate to Activate Delay Time(tRRD_Lmin), same bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 118 +/// +fapi2::ReturnCode +decoder::offset_min_act_to_act_delay_time_diff_bank_gp(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 118; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief Fine Offset for Minimum Activate to Activate Delay Time (tRRD_Smin), different bank group +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 119 +/// +fapi2::ReturnCode +decoder::offset_min_act_to_act_delay_time_same_bank_gp(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 119; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for Minimum Active to Active/Refresh Delay Time (tRCmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 120 +/// +fapi2::ReturnCode +decoder::offset_for_min_act_to_act_refresh_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 120; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; + +// Retrive entire MCS level attribute +// Update attribute to decoded byte values +// Update MCS level attribute + + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for Minimum Row Precharge Delay Time (tRPmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 121 +/// +fapi2::ReturnCode +decoder::offset_for_min_row_precharge_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 121; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Fine Offset for Minimum RAS to CAS Delay Time (tRCDmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 122 +/// +fapi2::ReturnCode decoder::offset_for_min_ras_to_cas_delay_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 122; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for Minimum CAS Latency Time (tAAmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 123 +/// +fapi2::ReturnCode decoder::offset_for_min_cas_latency_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 123; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for SDRAM Maximum Cycle Time (tCKAVGmax) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 124 +/// +fapi2::ReturnCode decoder::offset_for_max_cycle_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 124; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Fine Offset for SDRAM Minimum Cycle Time (tCKAVGmin) +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 125 +/// +fapi2::ReturnCode decoder::offset_for_min_cycle_time(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX = 125; + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Attribute variable (CCD_L = Minimum CAS to CAS Delay Time, same bank group) + int64_t l_min_offset_tCCD_L[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_trrd(l_target_mcs, &l_min_offset_tCCD_L[0][0]) ); + + // Update attribute to decoded byte values + l_min_offset_tCCD_L[PORT_NUM][DIMM_NUM] = ns_to_ps( int64_t(i_spd_data[BYTE_INDEX]) ); + + // Update MCS level attribute + FAPI_ATTR_SET( fapi2::ATTR_EFF_OFFSET_DRAM_TCCD_L, l_target_mcs, l_min_offset_tCCD_L ); + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + + fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief Cyclical Redundancy Code (CRC) for Base Configuration Section +/// @param[in] const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, +/// uint8_t* i_spd_data +/// @return fapi2::ReturnCode +/// @note Byte 126 (LSB) along with Byte 127 (MSB) +/// +fapi2::ReturnCode decoder::crc_for_base_config_section(const fapi2::Target<TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data) +{ + // Immutable constants + const size_t BYTE_INDEX_MSB = 127; // MSN = most significant nibble + const size_t BYTE_INDEX_LSB = 126; // LSB = least significant byte + + // Targets + const auto l_target_mcs = find_target<TARGET_TYPE_MCS>(i_target_dimm); + const auto l_target_port = find_target<TARGET_TYPE_MCA>(i_target_dimm); + + // Current index + const auto PORT_NUM = index(l_target_port); + const auto DIMM_NUM = index(i_target_dimm); + + // Byte variables used for decoding (WRT = Write to Read Time) + int64_t l_tWRTmin = 0; + + // Attribute variable + int64_t l_min_wrt_times[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // TODO - update ENUMS to take account to int64_t + + // Buffers used for bit manipulation + fapi2::buffer<uint8_t> l_buffer_upper_nibble(i_spd_data[BYTE_INDEX_MSN]); + fapi2::buffer<uint8_t> l_buffer_lower_byte(i_spd_data[BYTE_INDEX_LSB]); + + // Trace print in the front assists w/ debug + FAPI_INF("%s SPD data at Byte %d: 0x%llX", + c_str(i_target_dimm), + BYTE_INDEX, + i_spd_data[BYTE_INDEX]); + + // Combining bits to create timing value + l_buffer_upper_nibble.insert<, >(l_tWRTmin); + l_buffer_lower_byte.insert<, >(l_tWRTmin); + + // Retrive entire MCS level attribute + FAPI_TRY( eff_dram_twrt(l_target_mcs, &l_min_wrt_times[0][0]) ); + + // Update attribute to decoded byte values (returning picoseconds, currently nanoseconds) + l_min_wrt_times[PORT_NUM][DIMM_NUM] = ns_to_ps(l_tWRTmin); + + // Update MCS level attribute (returning picoseconds, currently nanoseconds) + FAPI_ATTR_SET( fapi2::ATTR_EFF_DRAM_TWRT, l_target_mcs, l_min_wrt_times ); + + // Printed decoded info + FAPI_INF("%s", + c_str(i_target_dimm); + fapi_try_exit: + return fapi2::current_err; +} +#endif + +}//spd namespace +}// mss namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.H b/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.H new file mode 100644 index 000000000..c86dc8816 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.H @@ -0,0 +1,424 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/spd/spd_decoder.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file spd_decoder.H +/// @brief SPD decoder declarations +/// +// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP HWP Backup: Brian Silver <bsilver@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_SPD_DECODER_H_ +#define _MSS_SPD_DECODER_H_ + +#include <fapi2.H> +#include <cstdint> + +namespace mss +{ +namespace spd +{ + + +enum constants : uint64_t +{ + // Byte 0 + BYTES_USED_START = 4, + BYTES_USED_LEN = 4, + + BYTES_TOTAL_START = 1, + BYTES_TOTAL_LEN = 3, + + BYTES_RESERVED_START = 0, + BYTES_RESERVED_LEN = 1, + + // Byte 1 & 2 not used now + + // Byte 3 + BASE_MODULE_START = 4, + BASE_MODULE_LEN = 4, + + HYBRID_MEDIA_START = 1, + HYBRID_MEDIA_LEN = 3, + + HYBRID_START = 0, + HYBRID_LEN = 1, + + // Byte 4 + SDRAM_CAPACITY_START = 4, + SDRAM_CAPACITY_LEN = 4, + + SDRAM_BANKS_START = 2, + SDRAM_BANKS_LEN = 2, + + BANK_GROUP_START = 0, + BANK_GROUP_LEN = 2, + + // Byte 5 + COL_ADDRESS_START = 5, + COL_ADDRESS_LEN = 3, + + ROW_ADDRESS_START = 2, + ROW_ADDRESS_LEN = 3, + + ADDRESS_RESERVED_START = 0, + ADDRESS_RESERVED_LEN = 2, + + // Byte 6 + PRIM_PRIM_SIGNAL_LOAD_START = 5, + PRIM_PRIM_SIGNAL_LOAD_LEN = 2, + + PACKAGE_RESERVE_START = 4, + PACKAGE_RESERVE_LEN = 2, + + PRIM_DIE_COUNT_START = 1, + PRIM_DIE_COUNT_LEN = 3, + + PRIM_PACKAGE_TYPE_START = 0, + PRIM_PACKAGE_TYPE_LEN = 1, + + // Byte 7 + MAC_START = 4, + MAC_LEN = 4, + + TMAW_START = 2, + TMAW_LEN = 2, + + OPT_FEAT_RESERVED_START = 0, + OPT_FEAT_RESERVED_LEN = 2, + + // Byte 8 reserved + + // Byte 9 + PPR_RESERVED_START = 3, + PPR_RESERVED_LEN = 5, + + SOFT_PPR_START = 2, + SOFT_PPR_LEN = 1, + + PPR_START = 0, + PPR_LEN = 2, + + // Byte 10 + SEC_SIGNAL_LOAD_START = 5, + SEC_SIGNAL_LOAD_LEN = 2, + + DENSITY_RATIO_START = 4, + DENSITY_RATIO_LEN = 2, + + SEC_DIE_COUNT_START = 1, + SEC_DIE_COUNT_LEN = 3, + + SEC_PACKAGE_TYPE_START = 0, + SEC_PACKAGE_TYPE_LEN = 1, + + // Byte 11 + OPERABLE_START = 7, + OPERABLE_LEN = 1, + + ENDURANT_START = 6, + ENDURANT_LEN = 1, + + NOM_VOLT_START = 0, + NOM_VOLT_LEN = 6, + + // Byte 12 + SDRAM_WIDTH_START = 5, + SDRAM_WIDTH_LEN = 3, + + PACKAGE_RANKS_START = 2, + PACKAGE_RANKS_LEN = 3, + + RANK_MIX_START = 1, + RANK_MIX_LEN = 1, + + MODULE_ORG_RESERVED_START = 0, + MODULE_ORG_RESERVED_LEN = 1, + + // Byte 13 + BUS_WIDTH_START = 6, + BUS_WIDTH_LEN = 2, + + BUS_EXT_WIDTH_START = 3, + BUS_EXT_WIDTH_LEN = 2, + + BUS_WIDTH_RESERVED_START = 0, + BUS_WIDTH_RESERVED_LEN = 3, + + // Byte 14 + THERM_SENSOR_RESERV_START = 1, + THERM_SENSOR_RESERV_LEN = 7, + + THERM_SENSOR_START = 0, + THERM_SENSOR_LEN = 1, + + // Byte 15 + EXT_MOD_TYPE_START = 5, + EXT_MOD_TYPE_LEN = 3, + + EXT_MOD_TYPE_RESERV_START = 0, + EXT_MOD_TYPE_RESERV_LEN = 4, + + // Byte 16 - reserved + + // Byte 17 + FINE_TIMEBASE_START = 6, + FINE_TIMEBASE_LEN = 2, + + MED_TIMEBASE_START = 4, + MED_TIMEBASE_LEN = 2, + + TIMEBASE_RESERV_START = 0, + TIMEBASE_RESERV_LEN = 4, + + // Byte 18 - bits not decoded + // Byte 19 - bits not decoded + + // Byte 20-23 + CAS_BYTE_1_START = 55, + CAS_BYTE_1_LEN = 8, + CAS_BYTE_2_START = 47, + CAS_BYTE_2_LEN = 8, + CAS_BYTE_3_START = 39, + CAS_BYTE_3_LEN = 8, + CAS_BYTE_4_START = 31, + CAS_BYTE_4_LEN = 8, + + // Byte 24 - bits not decoded + // Byte 25 - bits not decoded + // Byte 26 - bits not decoded + + // Byte 27 + TRCMIN_MSN_START = 0, // MSN = most significant nibble + TRCMIN_MSN_LEN = 4, + + TRASMIN_MSN_START = 4, // MSN = most significant nibble + TRASMIN_MSN_LEN = 4, + + // Byte 28 + TRASMIN_LSB_START = 4, // LSB = least significant byte + TRASMIN_LSB_LEN = 8, + + // Byte 29 + TRCMIN_LSB_START = 4, // LSB = least significant byte + TRCMIN_LSB_LEN = 8, + + // Byte 30 + // TRFC1MIN_LSB_START + // TRFC1MIN_LSB_LEN + + // Byte 31 +// TRFC1MIN_MSB_START +// TRFC1MIN_MSB_LEN + + // Bytes 46 - 59 - reserved + + // Bytes 60 - 77 - Connector to SDRAM Bit Mapping ?? + + // Bytes 78 - 116 - reserved + + // Bytes 117 - 125 : bits not decoded + + // Bytes 128 ~ 191 Module-Specific Section ?? + + // Bytes 192 ~ 255 Hybrid Memory Architecture Specific Parameters ?? + + // Bytes 256 ~ 319 Extended Function Parameter Block ?? + + // Bytes 320 ~ 383 Module Supplier’s Data ?? +}; + + +class decoder +{ + + public: + // Constructor + decoder() = default; + // Deconstructor + virtual ~decoder() = default; + + // Methods + virtual fapi2::ReturnCode number_of_bytes(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data, + const size_t i_read_spd_size); + + virtual fapi2::ReturnCode revision(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode dram_device_type(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode module_type(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode sdram_density(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode sdram_addressing(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode primary_package_type(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode sdram_optional_features(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode thermal_and_refresh_options(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode other_optional_features(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode secondary_package_type(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode module_nominal_voltage(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode module_organization(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode module_memory_bus_width(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode module_thermal_sensor(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode extended_module_type(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode timebases(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_cycle_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode max_cycle_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_cas_latency_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + // Note: Yet to be implemented methods +#if 0 + + virtual fapi2::ReturnCode supported_cas_latencies(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + + virtual fapi2::ReturnCode min_ras_to_cas_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_row_precharge_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode upper_nibble_tRAS_tRC(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_activate_to_activate_refresh_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_activate_to_precharge_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_refresh_recovery_delay_time_1(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_refresh_recovery_delay_time_2(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_refresh_recovery_delay_time_4(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_four_activate_window_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_act_to_act_delay_time_diff_bank_group(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_act_to_act_delay_time_same_bank_group(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_cas_to_cas_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_write_recovery_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_write_to_read_time_diff_bank_group(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode min_write_recovery_time_same_bank_group(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode connector_to_sdram_bit_mapping(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_cas_to_cas_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_min_act_to_act_delay_time_diff_bank_gp(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_min_act_to_act_delay_time_same_bank_gp(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_act_to_act_refresh_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_row_precharge_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_ras_to_cas_delay_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& + i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_cas_latency_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_min_cycle_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode offset_for_max_cycle_time(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); + + virtual fapi2::ReturnCode crc_for_base_config_section(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + const uint8_t* i_spd_data); +#endif +}; + +}// spd +}// mss + +#endif //_MSS_SPD_DECODER_H_ diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.C b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.C new file mode 100644 index 000000000..906e8ced6 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.C @@ -0,0 +1,497 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/termination/slew_cal.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/// +/// @file slew_cal.C +/// @brief * This function runs the slew calibration engine to configure MSS_SLEW_DATA/ADR +/// attributes and calls config_slew_rate to set the slew rate in the registers. +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "slew_cal.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_SYSTEM; + +using fapi2::TARGET_STATE_FUNCTIONAL; + +using fapi2::FAPI2_RC_SUCCESS; + +// slew calibration control register + +static const uint64_t slew_cal_cntl[] = +{ + MCA_0_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0, + MCA_1_DDRPHY_ADR_SLEW_CAL_CNTL_P1_ADR32S0, + MCA_2_DDRPHY_ADR_SLEW_CAL_CNTL_P2_ADR32S0, + MCA_3_DDRPHY_ADR_SLEW_CAL_CNTL_P3_ADR32S0, +}; +// slew calibration status registers +static const uint64_t ENABLE_BIT = 48; +static const uint64_t slew_cal_stat[] = +{ + MCA_0_DDRPHY_ADR_SYSCLK_CNTL_PR_P0_ADR32S0, + MCA_1_DDRPHY_ADR_SYSCLK_CNTL_PR_P1_ADR32S0, + MCA_2_DDRPHY_ADR_SYSCLK_CNTL_PR_P2_ADR32S0, + MCA_3_DDRPHY_ADR_SYSCLK_CNTL_PR_P3_ADR32S0, +}; + +// big bang lock bit register +static const uint64_t bb_lock_stat[] = +{ + MCA_0_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0, + MCA_1_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P1_ADR32S0, + MCA_2_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P2_ADR32S0, + MCA_3_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P3_ADR32S0, +}; + +namespace mss +{ + +/// +/// @brief perform the slew calibration, store the result. +/// @tparam T, the type of the slew table +/// @param[in] MCA (port) target +/// @param[in] a vector of the steps which came before me +/// @param[in] the slew table to be operated on +/// @param[out] the array holding the results +/// @return FAPI2_RC_SUCCESS, iff ok +/// +template<typename T> +fapi2::ReturnCode perform_slew_cal( + const fapi2::Target<TARGET_TYPE_MCA>& i_target, + std::vector<tags_t>& i_where_am_i, T& l_table, + uint8_t (&o_cal_slew)[2][PORTS_PER_MCS][MAX_NUM_IMP][MAX_NUM_CAL_SLEW_RATES]) +{ + i_where_am_i.push_back(l_table.first); + + for (auto e : l_table.second) + { + FAPI_TRY( perform_slew_cal(i_target, i_where_am_i, e, o_cal_slew) ); + } + + i_where_am_i.pop_back(); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief perform the slew calibration, store the result. +/// @param[in] MCA (port) target +/// @param[in] a vector of the steps which came before me +/// @param[in] a slew_table_t +/// @param[out] the array holding the results +/// @bote Prunes recursion based on frequency. +/// @return FAPI2_RC_SUCCESS, iff ok +/// +template<> +fapi2::ReturnCode perform_slew_cal( + const fapi2::Target<TARGET_TYPE_MCA>& i_target, + std::vector<tags_t>& i_where_am_i, + std::pair<tags_t, slew_per_imp_t>& l_table, + uint8_t (&o_cal_slew)[2][PORTS_PER_MCS][MAX_NUM_IMP][MAX_NUM_CAL_SLEW_RATES]) +{ + uint64_t ddr_freq = 0; + const char* l_type = i_where_am_i[0] == TAG_ADR ? "adr" : "data"; + + // Check our speed. If this isn't our speed, we can get out of here. + FAPI_TRY( mss::freq(i_target.getParent<TARGET_TYPE_MCBIST>(), ddr_freq), + "Unable to get ddr freq for %s", mss::c_str(i_target) ); + + if (ddr_freq != l_table.first) + { + FAPI_DBG("Skipping slew %s for %s: invalid speed %d(%d)", + l_type, mss::c_str(i_target), l_table.first, ddr_freq); + return FAPI2_RC_SUCCESS; + } + + i_where_am_i.push_back(l_table.first); + + for (auto e : l_table.second) + { + FAPI_TRY( perform_slew_cal(i_target, i_where_am_i, e, o_cal_slew) ); + } + + i_where_am_i.pop_back(); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief perform the slew calibration, store the result. +/// @param[in] MCA (port) target +/// @param[in] a vector of the steps which came before me +/// @param[in] the slew rate to be calculated +/// @param[out] the array holding the results +/// @return FAPI2_RC_SUCCESS, iff ok +/// +template<> +fapi2::ReturnCode perform_slew_cal<slew_rate_t>( + const fapi2::Target<TARGET_TYPE_MCA>& i_target, + std::vector<tags_t>& i_where_am_i, + slew_rate_t& l_slew_rate, + uint8_t (&o_cal_slew)[2][PORTS_PER_MCS][MAX_NUM_IMP][MAX_NUM_CAL_SLEW_RATES]) +{ + i_where_am_i.push_back(l_slew_rate.first); + + fapi2::buffer<uint64_t> l_data; + + // Some short-hand for this calibration + const char* l_type = i_where_am_i[0] == TAG_ADR ? "adr" : "data"; + const uint64_t& l_speed = i_where_am_i[1]; + const uint64_t& l_ohm = i_where_am_i[2]; + const uint64_t& l_vns = i_where_am_i[3]; + + uint64_t cal_status = 0; + fapi2::buffer<uint64_t> status_register; + uint64_t calculated_slew = 0; + + FAPI_INF("Processing slew %s, %dmhz %dohm %dV/ns: %d", l_type, l_speed, l_ohm, l_vns, l_slew_rate.second); + +#ifdef NOT_TESTING_WITH_SUET + // Get out of here if we're in the simulator. Calibration fails in sim since bb_lock not possible in cycle + // simulator, putting initial to be cal'd value in output table + uint8_t is_sim = 0; + FAPI_TRY( FAPI_ATTR_GET(fapi2::ATTR_IS_SIMULATION, fapi2::Target<TARGET_TYPE_SYSTEM>(), is_sim) ); + + if (is_sim) + { + FAPI_INF("In SIM setting input slew value in array"); + calculated_slew = l_slew_rate.second; + goto perform_slew_cal_exit; + } + +#endif + + // This doesn't look right. There are 5 bits, but some of our values (134) are 8 bits. So, the + // left-most bits are truncated. Note this is the same in P8, it seems - this needs to be looked at BRS + l_data.insertFromRight<MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0_ADR0_TARGET_PR_OFFSET, + MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0_ADR0_TARGET_PR_OFFSET_LEN>(l_slew_rate.second); + l_data.setBit<MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0_ADR0_START>(); + + FAPI_TRY( fapi2::putScom(i_target, MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0, l_data) ); + + mss::poll( i_target, MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0, mss::poll_parameters(DELAY_100NS), + [&](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool + { + // Save off our claibration status + status_register = stat_reg; + stat_reg.extractToRight<MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0_ADR0_SLEW_DONE_STATUS, + MCA_DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR32S0_ADR0_SLEW_DONE_STATUS_LEN>(cal_status); + FAPI_DBG("slew calibration status 0x%llx, remaining: %d", cal_status, poll_remaining); + return cal_status != SLEW_CAL_NOT_DONE; + }); + + // This will kick us out if there's no further processing we can do. + FAPI_TRY( slew_cal_status(i_target, i_where_am_i, l_slew_rate.second, cal_status, status_register) ); + + // Get our calculated slew rate and process. + FAPI_TRY( fapi2::getScom(i_target, MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0, l_data) ); + l_data.extractToRight<MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0_ADR0_TARGET_PR_OFFSET, + MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0_ADR0_TARGET_PR_OFFSET_LEN>(calculated_slew); + +#ifdef NOT_TESTING_WITH_SUET +perform_slew_cal_exit: +#endif + FAPI_DBG("MSS_SLEW_RATE %s port %d %dohm slew 0x%lx(0x%lx)", + l_type, mss::pos(i_target), l_ohm, calculated_slew, uint64_t(status_register)); + + o_cal_slew[i_where_am_i[0]] // adr/data + [mss::index(i_target)] // port + [mss::index(i_where_am_i[0], i_where_am_i[2])] // imp + [mss::index(i_where_am_i[0], i_where_am_i[3])] = calculated_slew; + + i_where_am_i.pop_back(); + + return FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + + +/// +/// @brief Run the slew calibration engine +/// @param[in] i_target, the MCBIST +/// @return FAPI2_RC_SUCCESS iff OK +/// +fapi2::ReturnCode slew_cal(const fapi2::Target<TARGET_TYPE_MCBIST>& i_target) +{ + // freq index into lookup table. Fixed at 3 right now as we don't support + // dimm slower than 1866 - without more work below. + static const uint8_t freq_idx = 3; + + // current ddr freq + uint64_t ddr_freq = 0; + + // ddr type index into lookup table + // We only have one entry in the table right now, so this is fixed. + static const uint8_t ddr_idx = 0; + + // ATTR_EFF_DRAM_GEN{0=invalid, 1=ddr3, 2=ddr4} + // p9n only supprts ddr4 right now. So, fixed + static const uint8_t ddr_type = 2; + + fapi2::buffer<uint64_t> ctl_reg; + fapi2::buffer<uint64_t> stat_reg; + + // DD level 1.0-1.1, Version 1.0 + // [ddr3/4][dq/adr][speed][impedance][slew_rate] + // note: Assumes standard voltage for DDR3(1.35V), DDR4(1.2V), + // little endian, if >=128, lab only debug. + // + // ddr_type(1), ddr4=0 + // data/adr(2)data(dq/dqs)=0, adr(cmd/cntl)=1 + // speed(4)1066=0, 1333=1, 1600=2, 1866=3 + // imped(4)24ohms=0, 30ohms=1, 34ohms=2, 40ohms=3 for DQ/DQS + // imped(4)15ohms=0, 20ohms=1, 30ohms=2, 40ohms=3 for ADR driver + // slew(3)3V/ns=0, 4V/ns=1, 5V/ns=2, 6V/ns=3 + + // Too many in here for real-world. But this allows us to test out the + // logic to make sure we're in good shape as we add to this table. We + // can redice this table before flight BRS. + std::vector< std::pair< tags_t, slew_table_t> > slew_table = + { + { + TAG_ADR, { + { + TAG_1066MHZ, + { + { TAG_15OHM, {{TAG_3VNS, 142}, {TAG_4VNS, 139}, {TAG_5VNS, 136}, {TAG_6VNS, 134}} }, + { TAG_20OHM, {{TAG_3VNS, 140}, {TAG_4VNS, 136}, {TAG_5VNS, 134}, {TAG_6VNS, 133}} }, + { TAG_30OHM, {{TAG_3VNS, 138}, {TAG_4VNS, 134}, {TAG_5VNS, 131}, {TAG_6VNS, 131}} }, + { TAG_40OHM, {{TAG_3VNS, 133}, {TAG_4VNS, 131}, {TAG_5VNS, 131}, {TAG_6VNS, 131}} }, + } + }, + + + { + TAG_1333MHZ, + { + { TAG_15OHM, {{TAG_3VNS, 145}, {TAG_4VNS, 142}, {TAG_5VNS, 139}, {TAG_6VNS, 136}} }, + { TAG_20OHM, {{TAG_3VNS, 143}, {TAG_4VNS, 138}, {TAG_5VNS, 135}, {TAG_6VNS, 134}} }, + { TAG_30OHM, {{TAG_3VNS, 140}, {TAG_4VNS, 135}, {TAG_5VNS, 132}, {TAG_6VNS, 132}} }, + { TAG_40OHM, {{TAG_3VNS, 134}, {TAG_4VNS, 132}, {TAG_5VNS, 132}, {TAG_6VNS, 132}} }, + } + }, + + + { + TAG_1600MHZ, + { + { TAG_15OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 16}, {TAG_5VNS, 13}, {TAG_6VNS, 10}} }, + { TAG_20OHM, {{TAG_3VNS, 18}, {TAG_4VNS, 12}, {TAG_5VNS, 9}, {TAG_6VNS, 135}} }, + { TAG_30OHM, {{TAG_3VNS, 15}, {TAG_4VNS, 8}, {TAG_5VNS, 133}, {TAG_6VNS, 133}} }, + { TAG_40OHM, {{TAG_3VNS, 7}, {TAG_4VNS, 133}, {TAG_5VNS, 133}, {TAG_6VNS, 133}} }, + } + }, + + + { + TAG_1866MHZ, + { + { TAG_15OHM, {{TAG_3VNS, 24}, {TAG_4VNS, 19}, {TAG_5VNS, 15}, {TAG_6VNS, 11}} }, + { TAG_20OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 14}, {TAG_5VNS, 10}, {TAG_6VNS, 136}} }, + { TAG_30OHM, {{TAG_3VNS, 17}, {TAG_4VNS, 10}, {TAG_5VNS, 134}, {TAG_6VNS, 134}} }, + { TAG_40OHM, {{TAG_3VNS, 9}, {TAG_4VNS, 134}, {TAG_5VNS, 134}, {TAG_6VNS, 134}} }, + } + }, + + { + TAG_2400MHZ, + { + { TAG_15OHM, {{TAG_3VNS, 24}, {TAG_4VNS, 19}, {TAG_5VNS, 15}, {TAG_6VNS, 11}} }, + { TAG_20OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 14}, {TAG_5VNS, 10}, {TAG_6VNS, 136}} }, + { TAG_30OHM, {{TAG_3VNS, 17}, {TAG_4VNS, 10}, {TAG_5VNS, 134}, {TAG_6VNS, 134}} }, + { TAG_40OHM, {{TAG_3VNS, 9}, {TAG_4VNS, 134}, {TAG_5VNS, 134}, {TAG_6VNS, 134}} }, + } + }, + + }, + }, + + { + TAG_DATA, { + { + TAG_1066MHZ, + { + { TAG_24OHM, {{TAG_3VNS, 138}, {TAG_4VNS, 135}, {TAG_5VNS, 134}, {TAG_6VNS, 133}} }, + { TAG_30OHM, {{TAG_3VNS, 139}, {TAG_4VNS, 136}, {TAG_5VNS, 134}, {TAG_6VNS, 132}} }, + { TAG_34OHM, {{TAG_3VNS, 140}, {TAG_4VNS, 136}, {TAG_5VNS, 135}, {TAG_6VNS, 133}} }, + { TAG_40OHM, {{TAG_3VNS, 140}, {TAG_4VNS, 136}, {TAG_5VNS, 132}, {TAG_6VNS, 132}} }, + } + }, + + { + TAG_1333MHZ, + { + { TAG_24OHM, {{TAG_3VNS, 139}, {TAG_4VNS, 137}, {TAG_5VNS, 135}, {TAG_6VNS, 134}} }, + { TAG_30OHM, {{TAG_3VNS, 142}, {TAG_4VNS, 138}, {TAG_5VNS, 135}, {TAG_6VNS, 133}} }, + { TAG_34OHM, {{TAG_3VNS, 143}, {TAG_4VNS, 138}, {TAG_5VNS, 135}, {TAG_6VNS, 133}} }, + { TAG_40OHM, {{TAG_3VNS, 143}, {TAG_4VNS, 138}, {TAG_5VNS, 133}, {TAG_6VNS, 132}} }, + } + }, + + { + TAG_1600MHZ, + { + { TAG_24OHM, {{TAG_3VNS, 15}, {TAG_4VNS, 11}, {TAG_5VNS, 9}, {TAG_6VNS, 135}} }, + { TAG_30OHM, {{TAG_3VNS, 17}, {TAG_4VNS, 11}, {TAG_5VNS, 9}, {TAG_6VNS, 135}} }, + { TAG_34OHM, {{TAG_3VNS, 18}, {TAG_4VNS, 13}, {TAG_5VNS, 9}, {TAG_6VNS, 134}} }, + { TAG_40OHM, {{TAG_3VNS, 18}, {TAG_4VNS, 11}, {TAG_5VNS, 6}, {TAG_6VNS, 133}} }, + } + }, + + + { + TAG_1866MHZ, + { + { TAG_24OHM, {{TAG_3VNS, 18}, {TAG_4VNS, 13}, {TAG_5VNS, 10}, {TAG_6VNS, 137}} }, + { TAG_30OHM, {{TAG_3VNS, 19}, {TAG_4VNS, 13}, {TAG_5VNS, 10}, {TAG_6VNS, 136}} }, + { TAG_34OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 15}, {TAG_5VNS, 10}, {TAG_6VNS, 135}} }, + { TAG_40OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 13}, {TAG_5VNS, 8}, {TAG_6VNS, 134}} }, + } + }, + + { + TAG_2400MHZ, + { + { TAG_24OHM, {{TAG_3VNS, 18}, {TAG_4VNS, 13}, {TAG_5VNS, 10}, {TAG_6VNS, 137}} }, + { TAG_30OHM, {{TAG_3VNS, 19}, {TAG_4VNS, 13}, {TAG_5VNS, 10}, {TAG_6VNS, 136}} }, + { TAG_34OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 15}, {TAG_5VNS, 10}, {TAG_6VNS, 135}} }, + { TAG_40OHM, {{TAG_3VNS, 21}, {TAG_4VNS, 13}, {TAG_5VNS, 8}, {TAG_6VNS, 134}} }, + } + }, + + } + } + }; + + // Get the vector of ports. Note that we presently think there's a bit in the CCS_MODE + // register which needs to be twiddled during slew cal. That means our caller can't + // put each port's slew cal on a thread and do them in parallel. So, we interleave. + // If that requirement for that MCBIST register goes away, then these functions can turn + // in to per-port functions and the caller can decide to loop or thread as appropriate. + // Or maybe per MCS given the attribute work we need to do at the end ... + auto l_ports = i_target.getChildren<TARGET_TYPE_MCA>(TARGET_STATE_FUNCTIONAL); + + // Cache the name of our target. We can't just keep the pointer from c_str as + // it points to thread-local space and anything we call might change the string. + char l_name[fapi2::MAX_ECMD_STRING_LEN]; + strncpy(l_name, mss::c_str(i_target), fapi2::MAX_ECMD_STRING_LEN); + + // p9n is ddr4 only for now, so skip checking the dimm generation (effective config + // should have raised a rukus if this isn't a DDR4 dimm ... + + FAPI_TRY( mss::freq(i_target, ddr_freq), + "Unable to get ddr freq for %s", l_name ); + + // Note: this catches two cases. One where ddr_freq is 0 and the other + // where we put something slow in we don't (yet?) support + FAPI_ASSERT( ddr_freq > 1732, + fapi2::MSS_SLEW_CAL_INVALID_FREQ(), + "Invalid ATTR_MSS_FREQ (%d) on %s", ddr_freq, l_name ); + + // Get a list of functional ports. + // Note: If we need to use the functional vector to figure this out, + // change this to an mss call, and bury the attribute manipluation + // in that function so it can be shared and tests. BRS + + // + // This doesn't match teh Centaur workbook algorithm, but it matches the P8 code so + // I'm leaving it this way. The algorithm according to the Centaur book would be to + // configure ADR/MCLK detect and then wait for BB_LOCK. Then, enable the cal engine and + // wait 2K clocks. So to "fix" you'd move the polling for BB_LOCK above the putScom. BRS + // + + // Sanity check the DRAM generation + for (auto c : i_target.getChildren<TARGET_TYPE_MCS>()) + { + FAPI_TRY( check::dram_type(c) ); + } + + // Step A: Configure ADR registers and MCLK detect (done in ddr_phy_reset) + { + for (auto p : l_ports) + { + FAPI_INF("Enabling slew calibration engine on port %i: DDR%i(%u) %u(%u) in %s", + mss::pos(p), (ddr_type + 2), ddr_idx, ddr_freq, freq_idx, l_name); + + // Note: This is wrong. Cant' find the SLEW_CAL enable bit in n10_e9024, so leaving this here for now BRS + FAPI_TRY( fapi2::putScom(p, MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0, + fapi2::buffer<uint64_t>().setBit<ENABLE_BIT>()), + "Error enabling slew calibration engine in DDRPHY_ADR_SLEW_CAL_CNTL register."); + } + } + + // Note: must be 2000 nclks+ after setting enable bit + // normally 2000, but since cal doesn't work in SIM, setting to 1 + FAPI_TRY( fapi2::delay(cycles_to_ns(i_target, 2000), cycles_to_simcycles(1)) ); + + // Create calibrated slew settings, per MCS. We do this as the attributes are written per MCS, + // and this makes it easier to keep track of what's going where, etc. + for (auto c : i_target.getChildren<TARGET_TYPE_MCS>()) + { + // [adr or data][port on mcs][imp][slew] + uint8_t calibrated_slew[2][PORTS_PER_MCS][MAX_NUM_IMP][MAX_NUM_CAL_SLEW_RATES] = {0}; + + auto l_mcs_ports = c.getChildren<TARGET_TYPE_MCA>(); + + for (auto p : l_mcs_ports) + { + for (auto e : slew_table) + { + FAPI_INF("Starting %s, port %d slew calibration", (e.first == TAG_ADR ? "adr" : "data"), mss::pos(p)); + + for (auto this_table : e.second) + { + std::vector<tags_t> l_where_am_i; + l_where_am_i.push_back(e.first); + FAPI_TRY( perform_slew_cal(p, l_where_am_i, this_table, calibrated_slew) ); + } + } + } + + // We have the calibrated slew settings, disable the calibration engine and do the + // attribute dance. + for (auto p : l_mcs_ports) + { + // Note: This is wrong. Cant' find the SLEW_CAL enable bit in n10_e9024, so leaving this here for now BRS + FAPI_TRY( fapi2::putScom(p, MCA_DDRPHY_ADR_SLEW_CAL_CNTL_P0_ADR32S0, + fapi2::buffer<uint64_t>().clearBit<ENABLE_BIT>()), + "Error disabling slew calibration engine in DDRPHY_ADR_SLEW_CAL_CNTL register."); + } + +// FAPI_TRY( slew_cal_attributes() ); + } + +fapi_try_exit: + return fapi2::current_err; +} +} // namespace diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.H b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.H new file mode 100644 index 000000000..e74bb9ad3 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal.H @@ -0,0 +1,144 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/termination/slew_cal.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file slew_cal.H +/// @brief Runs the slew calibration engine +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_SLEW_CAL_H_ +#define _MSS_SLEW_CAL_H_ + +#include <fapi2.H> + +namespace mss +{ + +// bits: 58:59 DDRPHY_ADR_SYSCLK_PR_VALUE_RO_P0_ADR0_SLEW_DONE_STATUS(0:1) +enum slew_done_status +{ + SLEW_CAL_NOT_DONE = 0b00, ///< Not complete + SLEW_CAL_ERRORS = 0b01, ///< Complete, but with errors - didn't reach alignment + SLEW_CAL_WARNING = 0b10, ///< Complete, but with warnings - slew cntl is 0b0000 + SLEW_CAL_SUCCESS = 0b11, ///< OK +}; + +// Used for tagging slew rate information in the table. We use enums for tags rather than +// strings for a few reasons, not the least of which it simplifies putting the tags into error logs. +enum tags_t +{ + TAG_DATA = 0, + TAG_ADR = 1, + + TAG_3VNS = 3, + TAG_4VNS = 4, + TAG_5VNS = 5, + TAG_6VNS = 6, + TAG_MAXVNS = 7, + + TAG_15OHM = 15, + TAG_20OHM = 20, + TAG_24OHM = 24, + TAG_30OHM = 30, + TAG_34OHM = 34, + TAG_40OHM = 40, + + TAG_1066MHZ = 1066, + TAG_1333MHZ = 1333, + TAG_1600MHZ = 1600, + TAG_1866MHZ = 1866, + TAG_2400MHZ = 2400, +}; + +/// +/// @brief find the attribute array index of a tag +/// @parm[in] i_type, whether this is an adr or dq lookup +/// @parm[in] tags_t, the tag to find the index of +/// @return size_t, the attribute array index +/// +inline size_t index(const tags_t& i_type, const tags_t& i_tag) +{ + // Static table to map some tags to indexes. + // First are the indexes for DATA, second are the indexes for ADR + static const size_t map[2][41] = + { + // DATA + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + { + 0, 1, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3 + }, + + // ADR + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + { + 0, 1, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + // 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 + } + }; + + fapi2::Assert((i_type == TAG_ADR) || (i_type == TAG_DATA)); + fapi2::Assert(i_tag <= TAG_40OHM); + return map[i_type][i_tag]; +} + + +typedef std::pair<tags_t, uint64_t> slew_rate_t; + +// Some types to help us keep track of the slew information +///< slew(3)3V/ns=0, 4V/ns=1, 5V/ns=2, 6V/ns=3 +typedef std::vector< slew_rate_t > slew_rates_t; + +///< imped(4)24ohms=0, 30ohms=1, 34ohms=2, 40ohms=3 for DQ/DQS +///< imped(4)15ohms=0, 20ohms=1, 30ohms=2, 40ohms=3 for ADR driver +typedef std::vector< std::pair<tags_t, slew_rates_t> > slew_per_imp_t; + +///< speed(4)1066=0, 1333=1, 1600=2, 1866=3 +typedef std::vector< std::pair<tags_t, slew_per_imp_t> > slew_table_t; + +/// +/// @brief Run the sle calibration engine +/// @param[in] i_target, the MCBIST +/// @return FAPI2_RC_SUCCESS iff OK +/// +fapi2::ReturnCode slew_cal(const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target); + +/// +/// @brief Process FFDC for slew calibration +/// @param[in] MCA (port) target +/// @param[in] a vector of the steps which came before me +/// @param[in] the slew table to be operated on +/// @param[in] the calibration status +/// @param[in] the register value +/// @return A fapi2::ReturnCode, appropriate for the calibration status +/// +fapi2::ReturnCode slew_cal_status(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target, + std::vector<tags_t>& i_where_am_i, + const uint64_t l_slew_rate, + const uint64_t i_status, + const fapi2::buffer<uint64_t>& i_reg); +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal_status.C b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal_status.C new file mode 100644 index 000000000..4f62b626d --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/termination/slew_cal_status.C @@ -0,0 +1,110 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/termination/slew_cal_status.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ +/// +/// @file slew_cal_status.C +/// @brief Process the status from slew calibration. This is it's own function +/// and file as it gets messey considering there are FFDC object per port ... +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include <fapi2.H> + +#include "../mss.H" +#include "slew_cal.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_SYSTEM; + +using fapi2::TARGET_STATE_FUNCTIONAL; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ + +/// +/// @brief Process FFDC for slew calibration +/// @param[in] MCA (port) target +/// @param[in] a vector of the steps which came before me +/// @param[in] the slew table to be operated on +/// @param[in] the calibration status +/// @param[in] the register value +/// @return A fapi2::ReturnCode, appropriate for the calibration status +/// +fapi2::ReturnCode slew_cal_status(const fapi2::Target<TARGET_TYPE_MCA>& i_target, + std::vector<tags_t>& i_where_am_i, + const uint64_t l_slew_rate, + const uint64_t i_status, + const fapi2::buffer<uint64_t>& i_reg) +{ + // Some short-hand for this subroutine + const char* l_type = i_where_am_i[0] == TAG_ADR ? "adr" : "data"; + const uint64_t& l_speed = i_where_am_i[1]; + const uint64_t& l_ohm = i_where_am_i[2]; + const uint64_t& l_vns = i_where_am_i[3]; + + // Write up the message/error string once. + FAPI_INF("Slew calibration: %s slew %s, %lumhz %luohm %luV/ns: %d", + mss::c_str(i_target), l_type, l_speed, l_ohm, l_vns, l_slew_rate); + + switch (i_status) + { + case SLEW_CAL_SUCCESS: + FAPI_DBG("Slew calibration success"); + break; + + case SLEW_CAL_WARNING: + FAPI_ERR("Slew calibration warning"); + break; + + case SLEW_CAL_NOT_DONE: + FAPI_ASSERT(false, fapi2::MSS_SLEW_CAL_TIMEOUT() + .set_PORT(mss::pos(i_target)) + .set_DATA_ADR(i_where_am_i[0]) + .set_IMP(l_ohm) + .set_SLEW(l_slew_rate) + .set_STAT_REG(i_reg) + .set_TARGET_IN_ERROR(i_target), + "Slew calibration timeout"); + break; + + case SLEW_CAL_ERRORS: + FAPI_ASSERT(false, fapi2::MSS_SLEW_CAL_ERROR() + .set_PORT(mss::pos(i_target)) + .set_DATA_ADR(i_where_am_i[0]) + .set_IMP(l_ohm) + .set_SLEW(l_slew_rate) + .set_STAT_REG(i_reg) + .set_TARGET_IN_ERROR(i_target), + "Slew calibration error"); + break; + }; + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + +} diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/bit_count.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/bit_count.H new file mode 100644 index 000000000..6292100d0 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/bit_count.H @@ -0,0 +1,83 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/bit_count.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file bit_count.H +/// @brief count bits +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_BIT_COUNT_H_ +#define _MSS_BIT_COUNT_H_ + +#include <fapi2.H> + +namespace mss +{ + +/// +/// @brief Count the bits in a T which are set (1) +/// @tparam T, an integral type. e.g., uint64_t, uint8_t +/// @param[in] the value to check, count +/// @return uint64_t the number of bits set in the input +/// +template< typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> +inline uint64_t bit_count(const T& i_value) +{ + if (i_value == 0) + { + return 0; + } + else + { + return bit_count(i_value >> 1) + (i_value & 0x01); + } +} + +/// +/// @brief Return the bit position of the first bit set, from the left +/// @tparam T, an integral type. e.g., uint64_t, uint8_t +/// @param[in] the value to check +/// @return uint64_t the bit position of the first set bit +/// @bote assumes you checked to make sure there were bits set because it will return 0 +/// +template< typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> +inline uint64_t first_bit_set(const T& i_value, const uint64_t i_pos = 0) +{ + if (i_value == 0) + { + return 0; + } + + if (fapi2::buffer<T>(i_value).template getBit<0>()) + { + return i_pos; + } + else + { + return first_bit_set(i_value << 1, i_pos + 1); + } +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.C b/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.C new file mode 100644 index 000000000..657481c74 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.C @@ -0,0 +1,122 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/c_str.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file c_str.C +/// @brief Storage for the C-string name of a thing +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#include <mss_attribute_accessors.H> +#include <fapi2.H> +#include "c_str.H" + +using fapi2::TARGET_TYPE_MCBIST; +using fapi2::TARGET_TYPE_MCA; +using fapi2::TARGET_TYPE_MCS; +using fapi2::TARGET_TYPE_DIMM; + +using fapi2::FAPI2_RC_SUCCESS; + +namespace mss +{ + +// Thread local storage for the string we're going to create. +thread_local char c_str_storage[fapi2::MAX_ECMD_STRING_LEN]; + +template< fapi2::TargetType T > +char const* make_c_str_helper( char const* s, const fapi2::Target<T>& i_target); + +template<> +char const* c_str<DEFAULT_KIND, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + return make_c_str_helper(" unknown dimm", i_target); +} + +template<> +char const* c_str<KIND_RDIMM_DDR4, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + return make_c_str_helper(" rdimm (ddr4)", i_target); +} + +template<> +char const* c_str<KIND_RDIMM_EMPTY, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + return make_c_str_helper(" rdimm (empty)", i_target); +} + +template<> +char const* c_str<KIND_LRDIMM_DDR4, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + return make_c_str_helper(" lrdimm (ddr4)", i_target); +} + +template<> +char const* c_str<KIND_LRDIMM_EMPTY, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + return make_c_str_helper(" lrdimm (empty)", i_target); +} + +template<> +char const* c_str<FORCE_DISPATCH, TARGET_TYPE_DIMM>( const fapi2::Target<TARGET_TYPE_DIMM>& i_target ) +{ + uint8_t l_type = 0; + uint8_t l_gen = 0; + + uint8_t l_value[2][2]; + auto l_mca = i_target.getParent<TARGET_TYPE_MCA>(); + auto l_mcs = l_mca.getParent<TARGET_TYPE_MCS>(); + + // Had to unroll FAPI_TRY so that fapi2::current_err doesn't get overwritten, causes errors + // when calling c_str inside of a function that returns fapi2::ReturnCode + if (FAPI_ATTR_GET(fapi2::ATTR_EFF_DIMM_TYPE, l_mcs, l_value) != FAPI2_RC_SUCCESS) + { + goto fapi_try_exit; + } + + l_type = l_value[mss::index(l_mca)][mss::index(i_target)]; + + // Had to unroll FAPI_TRY so that fapi2::current_err doesn't get overwritten, causes errors + // when calling c_str inside of a function that returns fapi2::ReturnCode + if (FAPI_ATTR_GET(fapi2::ATTR_EFF_DRAM_GEN, l_mcs, l_value) != FAPI2_RC_SUCCESS) + { + goto fapi_try_exit; + } + + l_gen = l_value[mss::index(l_mca)][mss::index(i_target)]; + + return c_str_dispatch<FORCE_DISPATCH, TARGET_TYPE_DIMM>(dimm_kind( l_type, l_gen ), i_target); + +fapi_try_exit: + // Probably the best we're going to do ... + return "couldn't get dimm type, dram gen"; +} + +template< fapi2::TargetType T > +char const* make_c_str_helper( char const* s, const fapi2::Target<T>& i_target) +{ + fapi2::toString( i_target, c_str_storage, fapi2::MAX_ECMD_STRING_LEN ); + return strncat( c_str_storage, s, fapi2::MAX_ECMD_STRING_LEN - strlen(c_str_storage) ); +} + +} diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.H new file mode 100644 index 000000000..7a8855b04 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/c_str.H @@ -0,0 +1,154 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/c_str.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file c_str.H +/// @brief Function to return the C-string name of a thing +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_C_STR_H_ +#define _MSS_C_STR_H_ + +#include <fapi2.H> + +/// +/// @remark To create an overloaded function a few things need to be defined. +/// Sorry. +/// + +/// -# Document the function and define its signature *as a comment* Why as a comment? +/// We're going to use SFINAE to expose this signature if there has been no overload +/// defined for any of the template arguments. So, we don't know if this signature will +/// be the correct one until we instantiate the template. So this is here simply for +/// documentation. We do need a simple way for our callers to know how to call this +/// non-traditionally defined function - so we do that here. + +/// +/// @brief return the c-string name of the input, including any extension to the name defined by its type +/// @tparam T, the fapi2::TargetType of the input - derived. +/// @param[in] i_target, the fapi2::Target of interest +/// @return char const* a thread-safe c-string containing the element's name +/// @note to call: char* s = c_str(i_target); +/// +//template< fapi2::TargetType T> +//const char* c_str( const fapi2::Target<T>& i_target); + +/// +/// -# Include the header which defines all the macros which will +/// expand into our overload templates. Note that the pre-processor +/// is single-pass so all of the pre-requisites for the larger macros +/// need to be defined prior to including them. +/// +#include "../shared/mss_kind.H" + +namespace mss +{ + +// Thread local storage for the string we're going to create. +extern thread_local char c_str_storage[fapi2::MAX_ECMD_STRING_LEN]; + +/// -# Register the API. +/// -# Define the template parameters for the overloaded function +/// @note the first argument is the api name, and the rest are the api's template parameters. +/// @note this creates __api_name##_overload - so in this case it defines +/// c_str_overload which is used below to mame decisions. +REGISTER_API( c_str, fapi2::TargetType T ); + +/// -# Register the specific overloads. The first parameter is the name +/// of the api, the second is the kind of the element which is being +/// overloaded - an RDIMM, an LRDIMM, etc. The remaining parameters +/// indicate the specialization of the api itself. +/// @note You need to define the "DEFAULT_KIND" here as an overload. This +/// allows the mechanism to find the "base" implementation for things which +/// have no specific overload. +REGISTER_OVERLOAD( c_str, DEFAULT_KIND, fapi2::TARGET_TYPE_DIMM ); +REGISTER_OVERLOAD( c_str, KIND_RDIMM_DDR4, fapi2::TARGET_TYPE_DIMM ); +REGISTER_OVERLOAD( c_str, KIND_RDIMM_EMPTY, fapi2::TARGET_TYPE_DIMM ); +REGISTER_OVERLOAD( c_str, KIND_LRDIMM_DDR4, fapi2::TARGET_TYPE_DIMM ); +REGISTER_OVERLOAD( c_str, KIND_LRDIMM_EMPTY, fapi2::TARGET_TYPE_DIMM ); + +/// +/// -# Define the default case for overloaded calls. enable_if states that +/// if there is a DEFAULT_KIND overload for this TargetType, then this +/// entry point will be defined. Note the general case below is enabled if +/// there is no overload defined for this TargetType +/// + +template< mss::kind_t K = FORCE_DISPATCH, fapi2::TargetType T > +typename std::enable_if< c_str_overload<DEFAULT_KIND, T>::available, const char*>::type +c_str( const fapi2::template Target<T>& i_target ); + +/// +/// -# Register the handler for the API, not the overloads. This is the +/// code which implements the general case of the template. Because it +/// is not a full specialization of the template, it must be defiend in +/// this header. +/// +template< fapi2::TargetType T > +typename std::enable_if < ! c_str_overload<DEFAULT_KIND, T>::available, const char* >::type +c_str( const fapi2::template Target<T>& i_target ) +{ + fapi2::toString( i_target, c_str_storage, fapi2::MAX_ECMD_STRING_LEN ); + return c_str_storage; +} + +// +// We know we registered overloads for c_str, so we need the entry point to +// the dispatcher. Add a set of these for all TargetTypes which get overloads +// for this API +// +template<> +char const* c_str<FORCE_DISPATCH, fapi2::TARGET_TYPE_DIMM>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target ); + +template<> +char const* c_str<DEFAULT_KIND, fapi2::TARGET_TYPE_DIMM>( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target ); + +// +// Boilerplate dispatcher +// +template< kind_t K, fapi2::TargetType T, bool B = c_str_overload<K, T>::available > +inline char const * c_str_dispatch( const kind_t& i_kind, const fapi2::Target<T>& i_target ) +{ + // We dispatch to another kind if: + // We don't have an overload defined (B == false) + // Or, if we do have an overload (B == true) and this is not out kind. + if ((B == false) || ((B == true) && (K != i_kind))) + { + return c_str_dispatch < (kind_t)(K - 1), T > (i_kind, i_target); + } + + // Otherwise, we call the overload. + return c_str<K, T>(i_target); +} + +// DEFAULT_KIND is 0 so this is the end of the recursion +template<> +inline char const* c_str_dispatch<DEFAULT_KIND, fapi2::TARGET_TYPE_DIMM>(const kind_t&, + const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + return c_str<DEFAULT_KIND, fapi2::TARGET_TYPE_DIMM>(i_target); +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/checker.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/checker.H new file mode 100644 index 000000000..ff2a493f3 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/checker.H @@ -0,0 +1,298 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/checker.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file checker.H +/// @brief Contains common functions that perform checks +/// +// *HWP HWP Owner: Andre Marin <aamarin@us.ibm.com> +// *HWP FW Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#ifndef _CHECKER_H_ +#define _CHECKER_H_ + +#include <fapi2.H> +#include "../shared/mss_const.H" +#include <mss_attribute_accessors.H> + +namespace mss +{ +namespace check +{ +/// +/// @brief Checks homogenous DDR4 dimm configuration (e.g. DDR4) +/// @param[in] const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs +/// @return FAPI2_RC_SUCCESS iff ok +/// +inline fapi2::ReturnCode dram_type(const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs) +{ + uint8_t l_dram_gen[PORTS_PER_MCS][MAX_DIMM_PER_PORT] = {0}; + + // Retrieve DDR4 dimm attributes + FAPI_TRY(spd_dram_device_type(i_target_mcs, &(l_dram_gen[0][0]))); + + // Make sure all DRAMs are DDR4 or throw an error + // Iterate through MCA's/ports + for (auto p : i_target_mcs.getChildren<fapi2::TARGET_TYPE_MCA>()) + { + auto port_num = mss::index(p); + + // Iterate through DIMM targets + for (auto d : p.getChildren<fapi2::TARGET_TYPE_DIMM>()) + { + auto dimm_num = mss::index(d); + + FAPI_INF("%s DRAM device type is %llX", + mss::c_str(d), + l_dram_gen[port_num][dimm_num]); + + // Nimbus supports only DDR4 + FAPI_ASSERT(l_dram_gen[port_num][dimm_num] == fapi2::ENUM_ATTR_SPD_DRAM_DEVICE_TYPE_DDR4, + fapi2::MSS_UNSUPPORTED_DEV_TYPE(). + set_DEV_TYPE(l_dram_gen[port_num][dimm_num]), + "%s Incorrect DRAM device generation, DRAM generation is %llx", + mss::c_str(d), + l_dram_gen[port_num][dimm_num]); + } + + } + +fapi_try_exit: + return fapi2::current_err; +}// dram_type + + +/// +/// @brief Check if there is DIMM mixing (and deconfigures unsupported mixing <shrug> ?? - AAM) +/// @param[in] const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs +/// @param[out] uint64_t& o_module_type (returns something ?? - AAM ) +/// @return FAPI2_RC_SUCCESS iff ok +/// @note Functionality currently unknown +/// +inline fapi2::ReturnCode dimm_mixing(const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs, + uint64_t& o_module_type) +{ + //TODO + //Need to complete - AAM + + // Anuwat pushed Warren to say IBM systems will not support DIMM mixing + // Now DIMM deconfig rules? - AAM +#if 0 + + std::vector<uint8_t> l_module_type_vector; + uint8_t l_module_type[mss::PORTS_PER_MCS][mss::MAX_DIMM_PER_PORT] = {0}; + uint8_t lr_dimm_count; + uint8_t r_dimm_count; + uint8_t u_dimm_count; + + FAPI_TRY(mss::spd_module_type(i_target_mcs, &l_module_type[0][0]), + "Unable to read the SPD module type."); + + for(size_t l_port = 0; l_port < mss::PORTS_PER_MCS; l_port++) + { + for (size_t l_dimm = 0; l_dimm < mss::MAX_DIMM_PER_PORT; l_dimm++) + { + l_module_type_vector.push_back(l_module_type[l_port][l_dimm]); + } + } + + lr_dimm_count = std::count(l_module_type_vector.begin(), + l_module_type_vector.end(), + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_LRDIMM); + r_dimm_count = std::count(l_module_type_vector.begin(), + l_module_type_vector.end(), + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_RDIMM); + u_dimm_count = std::count(l_module_type_vector.begin(), + l_module_type_vector.end(), + fapi2::ENUM_ATTR_SPD_MODULE_TYPE_UDIMM); // Not supported? - AAM + + count(l_module_type_vector.begin(), l_module_type_vector.end(), fapi2::SO_DIMM); // Not supported? - AAM + + uint8_t temp = std::max({lr_dimm_count, r_dimm_count, u_dimm_count}); + + return (temp == r_dimm_count) ? fapi2::RDIMM : fapi2::LRDIMM + +#endif + + return fapi2::FAPI2_RC_SUCCESS; +}// dimm_mixing + +/// +/// @brief Checks what type of system is configured (i.e., single drop, dual drop) +/// @param[in] Unknown +/// @param[out] Unknown +/// @return FAPI2_RC_SUCCESS iff ok +/// @note Functionality currently unknown +/// +inline fapi2::ReturnCode system_drop_type(void) +{ + //TODO + //Update for P9, how will we check if a system is single/dual drop?? - AAM + +#if 0 + //Determining the cnfg for imposing any cnfg speed limitations + if ( (cur_dimm_spd_valid_u8array[0][0][0] == MSS_FREQ_VALID) + && (cur_dimm_spd_valid_u8array[0][0][1] == MSS_FREQ_EMPTY) ) + { + plug_config = MSS_FREQ_SINGLE_DROP; + num_ranks_total = num_ranks[0][0][0] + 1; + } + else if (((cur_dimm_spd_valid_u8array[1][0][0] == MSS_FREQ_VALID) + && (cur_dimm_spd_valid_u8array[1][0][1] == MSS_FREQ_EMPTY)) || + ((cur_dimm_spd_valid_u8array[1][0][1] == MSS_FREQ_VALID) && (cur_dimm_spd_valid_u8array[1][0][0] == MSS_FREQ_EMPTY))) + { + plug_config = MSS_FREQ_SINGLE_DROP; + num_ranks_total = num_ranks[1][0][0] + 1; + } + else if ((cur_dimm_spd_valid_u8array[0][0][0] == MSS_FREQ_VALID) + && (cur_dimm_spd_valid_u8array[0][0][1] == MSS_FREQ_VALID)) + { + plug_config = MSS_FREQ_DUAL_DROP; + num_ranks_total = (num_ranks[0][0][0] + 1) + (num_ranks[0][0][1] + 1); + } + else if ((cur_dimm_spd_valid_u8array[1][0][0] == MSS_FREQ_VALID) + && (cur_dimm_spd_valid_u8array[1][0][1] == MSS_FREQ_VALID)) + { + plug_config = MSS_FREQ_DUAL_DROP; + num_ranks_total = (num_ranks[1][0][0] + 1) + (num_ranks[1][0][1] + 1); + } + else + { + plug_config = MSS_FREQ_EMPTY; + } + + + FAPI_INF( "PLUG CONFIG(from SPD): %d, Type of Dimm(from SPD): 0x%02X, Num Ranks(from SPD): %d", + plug_config, module_type, num_ranks_total); +fapi_try_exit: + return fapi2::current_err; + +#endif + + return fapi2::FAPI2_RC_SUCCESS; +}// system_drop_type + +/// +/// @brief Checks nominal voltage is correct for all DIMMs +/// @param[in] const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs, +/// fapi2::TargetState dimm_state +/// @return ReturnCode +/// +inline fapi2::ReturnCode module_nominal_voltage(const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target_mcs, + fapi2::TargetState dimm_state) +{ + + uint64_t l_module_nom_voltage[mss::PORTS_PER_MCS][mss::MAX_DIMM_PER_PORT] = {0}; + + FAPI_TRY(mss::spd_module_nominal_voltage(i_target_mcs, &l_module_nom_voltage[0][0])); + + for (auto p : i_target_mcs.getChildren<fapi2::TARGET_TYPE_MCA>()) + { + auto port_num = mss::index(p); + + // Iterate through DIMM targets + for (auto d : p.getChildren<fapi2::TARGET_TYPE_DIMM>(dimm_state)) + { + auto dimm_num = mss::index(d); + + FAPI_INF("%s DRAM nominal voltage (in milliseconds) is %d", + mss::c_str(d), + l_module_nom_voltage[port_num][dimm_num]); + + // All DIMMs have to be operable at 1.2 V, else don't IPL (according to Warren) + FAPI_ASSERT( l_module_nom_voltage[port_num][dimm_num] == fapi2::ENUM_ATTR_SPD_MODULE_NOMINAL_VOLTAGE_OPERABLE, + fapi2::MSS_VOLT_DDR_TYPE_REQUIRED_VOLTAGE(). + set_DIMM_VOLTAGE(l_module_nom_voltage[port_num][dimm_num]), + "%s.Failed conditional, data returned:o %d.", + mss::c_str(d), + l_module_nom_voltage[port_num][dimm_num] ); + } + } + +fapi_try_exit: + return fapi2::current_err; +}// module_nominal_voltage + +namespace spd +{ +/// +/// @brief Checks conditional passes and implements traces & exits if it fails +/// @tparam T spd_data, of any size +/// @param[in] const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, +/// bool conditional +/// size_t spd_byte_inded +/// char* err_str +/// @return ReturnCode +/// +template< typename T > +inline fapi2::ReturnCode valid_value_fail(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + bool conditional, + size_t spd_byte_index, + T spd_data, + const char* err_str) +{ + FAPI_ASSERT(conditional, + fapi2::MSS_VALID_VALUE(). + set_VALUE(spd_data). + set_BYTE(spd_byte_index), + "%s %s Byte %d, Data returned: %d.", + c_str(i_target_dimm), + err_str, + spd_byte_index, + spd_data); + +fapi_try_exit: + return fapi2::current_err; + +} // invalid_value_fail() + +/// +/// @brief Checks conditional passes and implements traces if it fails +/// @tparam T spd_data, of any size +/// @param[in] const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, +/// bool conditional +/// size_t spd_byte_inded +/// char* err_str +/// @return void +/// +template< typename T > +inline void valid_value_warn(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target_dimm, + bool conditional, + size_t spd_byte_index, + T spd_data, + const char* err_str) +{ + if(!conditional) + { + FAPI_IMP("%s. %s. Byte %d, Data returned: %d.", + c_str(i_target_dimm), + err_str, + spd_byte_index, + spd_data ); + } +}// valid_value_warn + +}// spd +}// check +}// mss + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/conversions.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/conversions.H new file mode 100644 index 000000000..3989c172b --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/conversions.H @@ -0,0 +1,263 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/conversions.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file conversions.H +/// @brief Functions to convert units +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_CONVERSIONS_H_ +#define _MSS_CONVERSIONS_H_ + +#include <fapi2.H> +#include <mss_attribute_accessors.H> +#include "../shared/mss_const.H" +#include "find.H" + +// Mutiplication factor to go from clocks to simcycles. +// Is this just 2400 speed or does this hold for all? BRS +static const uint64_t SIM_CYCLES_PER_CYCLE = 8; + +namespace mss +{ + +/// +/// @brief Return the number of picoseconds taken by a certain mhz +/// @param[in] i_mhz - the mhz of interest +/// @return the picoseconds +/// +inline uint64_t mhz_to_ps(const uint64_t i_mhz) +{ + // Just used for indexs into the array below + enum freqs { FREQ_2400 = 0, }; + + // ATTR_MSS_FREQ is in mHZ, and we know us per clock is 1/(freq/2) + // We don't have many frequencies, so lets just make a little table. + static const uint64_t FREQ_TO_PS[MAX_SUPPORTED_FREQUENCIES] = + { + // 2400 is 833 picoseconds per clock + 833 + }; + + switch(i_mhz) + { + case 2400: + return FREQ_TO_PS[FREQ_2400]; + + default: + FAPI_ERR("unsupported frequency %d", i_mhz); + fapi2::Assert(false); + + // Keeps compiler happy + return 0; + } +} + +/// +/// @brief Translate from cycles to sim cycles +/// @param[in] i_cycles, the cycles to translate +/// @return uint64_t, the number of sim cycles. +/// +inline uint64_t cycles_to_simcycles( const uint64_t i_cycles ) +{ + // Is this always the case or do we need the freq to really figure this out? + return i_cycles * SIM_CYCLES_PER_CYCLE; +} + +/// +/// @brief Return the number of cycles contained in a count of picoseconds +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_ps, the number of picoseconds to convert +/// @return uint64_t, the number of cycles +/// +template< fapi2::TargetType T > +inline uint64_t ps_to_cycles(const fapi2::Target<T>& i_target, const uint64_t i_ps) +{ + // The frequency in mHZ + uint64_t l_freq; + uint64_t l_divisor = 0; + uint64_t l_quotient = 0; + uint64_t l_remainder = 0; + + FAPI_TRY( mss::freq( find_target<fapi2::TARGET_TYPE_MCBIST>(i_target), l_freq) ); + + // Hoping the compiler figures out how to do these together. + l_divisor = mhz_to_ps(l_freq); + l_quotient = i_ps / l_divisor; + l_remainder = i_ps % l_divisor; + + // Make sure we add a cycle if there wasn't an even number of cycles in the input + FAPI_DBG("converting %dps to %d cycles", i_ps, l_quotient + (l_remainder == 0 ? 0 : 1)); + + return l_quotient + (l_remainder == 0 ? 0 : 1); + +fapi_try_exit: + + // We simply can't work if we can't get the frequency - so this should be ok + FAPI_ERR("Can't get MSS_FREQ - stopping"); + fapi2::Assert(false); + + // Keeps compiler happy + return 0; +} + +/// +/// @brief Return the number of ps contained in a count of cycles +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_ps, the number of cycles to convert +/// @return uint64_t, the number of picoseconds +/// +template< fapi2::TargetType T > +inline uint64_t cycles_to_ps(const fapi2::Target<T>& i_target, const uint64_t i_cycles) +{ + // The frequency in mHZ + uint64_t l_freq; + + FAPI_TRY( mss::freq( find_target<fapi2::TARGET_TYPE_MCBIST>(i_target), l_freq) ); + + FAPI_DBG("converting %d cycles to %dps", i_cycles, i_cycles * mhz_to_ps(l_freq)); + return i_cycles * mhz_to_ps(l_freq); + +fapi_try_exit: + + // We simply can't work if we can't get the frequency - so this should be ok + FAPI_ERR("Can't get MSS_FREQ - stopping"); + fapi2::Assert(false); + + // Keeps compiler happy + return 0; +} + +/// +/// @brief Return the number of cycles contained in a count of microseconds +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_ps, the number of microseconds to convert +/// @return uint64_t, the number of cycles +/// +template< fapi2::TargetType T > +inline uint64_t us_to_cycles(const fapi2::Target<T>& i_target, const uint64_t i_us) +{ + return ps_to_cycles(i_target, i_us * CONVERT_PS_IN_A_US); +} + +/// +/// @brief Return the number of cycles contained in a count of nanoseconds +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_ps, the number of nanoseconds to convert +/// @return uint64_t, the number of cycles +/// +template< fapi2::TargetType T > +inline uint64_t ns_to_cycles(const fapi2::Target<T>& i_target, const uint64_t i_ns) +{ + return ps_to_cycles(i_target, i_ns * CONVERT_PS_IN_A_NS); +} + +/// +/// @brief Return the number of microseconds contained in a count of cycles +/// @tparam T, the target type +/// @tparam D, the time conversion (NS_IN_PS, etc) +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_ps, the number of cycles to convert +/// @return uint64_t, the number of microseconds +/// +template< fapi2::TargetType T, uint64_t D > +inline uint64_t cycles_to_time(const fapi2::Target<T>& i_target, const uint64_t i_cycles) +{ + // Hoping the compiler figures out how to do these together. + uint64_t l_dividend = cycles_to_ps(i_target, i_cycles); + uint64_t l_quotient = l_dividend / D; + uint64_t l_remainder = l_dividend % D; + + // Make sure we add time if there wasn't an even number of cycles + return l_quotient + (l_remainder == 0 ? 0 : 1); +} + +/// +/// @brief Return the number of nanoseconds contained in a count of cycles +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_cycles, the number of cycles to convert +/// @return uint64_t, the number of nanoseconds +/// +template< fapi2::TargetType T > +inline uint64_t cycles_to_ns(const fapi2::Target<T>& i_target, const uint64_t i_cycles) +{ + uint64_t l_ns = cycles_to_time<T, CONVERT_PS_IN_A_NS>(i_target, i_cycles); + FAPI_DBG("converting %d cycles to %dns", i_cycles, l_ns); + + return l_ns; +} + +/// +/// @brief Return the number of microseconds contained in a count of cycles +/// @param[in] i_target, target for the frequency attribute +/// @param[in] i_cycles, the number of cycles to convert +/// @return uint64_t, the number of microseconds +/// +template< fapi2::TargetType T > +inline uint64_t cycles_to_us(const fapi2::Target<T>& i_target, const uint64_t i_cycles) +{ + uint64_t l_us = cycles_to_time<T, CONVERT_PS_IN_A_US>(i_target, i_cycles); + FAPI_DBG("converting %d cycles to %dus", i_cycles, l_us); + + return l_us; +} + +/// +/// @brief Calculate TWLO_TWLOE - this needs to go in to eff_config and be an attribute +/// @tparam fapi2::TargetType T of the target used to calculate cycles from ns +/// @param[in] i_target, the target used to get DIMM clocks +/// @return uint64_t, TWLO_TWLOE in cycles +/// +template< fapi2::TargetType T > +inline uint64_t twlo_twloe(const fapi2::Target<T>& i_target) +{ + return 12 + mss::ns_to_cycles(i_target, tWLO - tWLOE); +} + +/// +/// @brief Convert nanoseconds to picoseconds +/// @param[in] int64_t i_time_in_ns, time in nanoseconds +/// @return int64_t, time in picoseconds +/// +inline int64_t ns_to_ps(const int64_t& i_time_in_ns) +{ + return i_time_in_ns * CONVERT_PS_IN_A_NS; +} + +/// +/// @brief Convert nanoseconds to picoseconds +/// @param[in] int64_t i_time_in_ps, time in picoseconds +/// @return int64_t, time in nanoseconds +/// +inline int64_t ps_to_ns(const int64_t& i_time_in_ps) +{ + int64_t remainder = i_time_in_ps % CONVERT_PS_IN_A_NS; + int64_t l_time_in_ns = i_time_in_ps / CONVERT_PS_IN_A_NS; + + // Round up if remainder isn't even + return l_time_in_ns + ( remainder == 0 ? 0 : 1 ); +} + +};// mss namespace +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.C b/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.C new file mode 100644 index 000000000..1fd1d1401 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.C @@ -0,0 +1,95 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/fake_spd.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file fake_spd.H +/// @brief A tool to return fake (fixed) DIMM SPD for testing, development +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#include "fake_spd.H" + +/// DIMM data was taken from /afs/awd.austin.ibm.com/projects/eclipz/lab/p8/gsiexe/ +/// 78P0AAM_DDR4_8G1RX8_mu/MICRON_SPD_8G_1Rx8_DDR4_2400_RDIMM.hex on 7/31/2015. + +static const uint8_t raw_spd[] = +{ + 0x23, 0x10, 0x0C, 0x01, 0x85, 0x21, 0x00, 0x08, 0x00, 0x60, 0x00, 0x03, 0x01, 0x0B, 0x80, 0x00, + 0x00, 0x00, 0x07, 0x0D, 0xF8, 0x7F, 0x00, 0x00, 0x6E, 0x6E, 0x6E, 0x11, 0x00, 0x6E, 0xF0, 0x0A, + 0x20, 0x08, 0x00, 0x05, 0x00, 0xA8, 0x1B, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x03, 0x15, 0x2C, + 0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x24, 0x03, 0x15, 0x2C, 0x24, 0x03, 0x15, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xB5, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD6, 0xCA, 0x91, + 0x11, 0x11, 0x23, 0x05, 0x00, 0x80, 0xB3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x41, 0x53, 0x46, 0x31, 0x47, 0x37, + 0x32, 0x50, 0x5A, 0x2D, 0x32, 0x47, 0x33, 0x41, 0x31, 0x20, 0x20, 0x20, 0x20, 0x31, 0x80, 0x2C, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +/// +/// @brief Return a blob of SPD data from a DIMM +/// @param[in] i_target, a DIMM target representing the DIMM in question +/// @param[out] o_blob, the blob of data from the DIMM - raw +/// @param[out] o_size, o_size, the size of the blob +/// @return FAPI2_RC_SUCCESS if there's no problem +/// @note passing nullptr for o_blob will return the size of the blob +/// size_t s; +/// FAPI_TRY(getSPD( dimm, nullptr, s )); +/// char blob[s]; +/// FAPI_TRY(getSPD( dimm, blob, s )); +/// blob now contains the SPD for the dimm. +/// +fapi2::ReturnCode getSPD(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, uint8_t* o_blob, size_t& o_size) +{ + o_size = sizeof( raw_spd ); + + if( o_blob != nullptr ) + { + memcpy(o_blob, raw_spd, o_size); + } + + return fapi2::FAPI2_RC_SUCCESS; +} + diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.H new file mode 100644 index 000000000..30161339e --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/fake_spd.H @@ -0,0 +1,51 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/fake_spd.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file fake_spd.H +/// @brief A tool to return fake (fixed) DIMM SPD for testing, development +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_FAKE_SPD_H_ +#define _MSS_FAKE_SPD_H_ + +#include <fapi2.H> + +/// +/// @brief Return a blob of SPD data from a DIMM +/// @param[in] i_target, a DIMM target representing the DIMM in question +/// @param[out] o_blob, the blob of data from the DIMM - raw +/// @param[out] o_size, o_size, the size of the blob +/// @return FAPI2_RC_SUCCESS if there's no problem +/// @note passing nullptr for o_blob will return the size of the blob +/// size_t s; +/// FAPI_TRY( getSPD(d->target(), nullptr, s) ); +/// { +/// uint8_t blob[s]; +/// FAPI_TRY( getSPD(d->target(), blob, s) ); +/// } +/// +fapi2::ReturnCode getSPD(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, uint8_t* o_blob, size_t& s); + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/find.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/find.H new file mode 100644 index 000000000..15c559d38 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/find.H @@ -0,0 +1,187 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/find.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file find.H +/// @brief Templates for finding things +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_FIND_H +#define _MSS_FIND_H + +#include <fapi2.H> + +namespace mss +{ + +/// +/// @brief find a set of elements based on a fapi2 target +/// @tparam M the target type to be returned +/// @tparam T the fapi2 target type of the argument +/// @param[in] the fapi2 target, T +/// @return a vector of M targets. +/// +template< fapi2::TargetType M, fapi2::TargetType T > +inline std::vector< fapi2::Target<M> > find_targets( const fapi2::Target<T>& ); + +/// +/// @brief find an element based on a fapi2 target +/// @tparam M the target type to be returned +/// @tparam T the fapi2 target type of the argument +/// @param[in] the fapi2 target, T +/// @return an M target. +/// +template< fapi2::TargetType M, fapi2::TargetType T > +inline fapi2::Target<M> find_target( const fapi2::Target<T>& ); + +/// +/// @brief find the McBIST given a McBIST +/// @param[in] the fapi2 target, mcBIST +/// @return a McBIST target. +/// +template<> +inline fapi2::Target<fapi2::TARGET_TYPE_MCBIST> find_target( const fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_self) +{ + return i_self; +} + +/// +/// @brief find the McBIST given a DIMM +/// @param[in] the fapi2 target, DIMM +/// @return a McBIST target. +/// +template<> +inline fapi2::Target<fapi2::TARGET_TYPE_MCBIST> find_target( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + return i_target.getParent<fapi2::TARGET_TYPE_MCA>().getParent<fapi2::TARGET_TYPE_MCBIST>(); +} + +/// +/// @brief find the McBIST given a MCA +/// @param[in] the fapi2 target, MCA +/// @return a McBIST target. +/// +template<> +inline fapi2::Target<fapi2::TARGET_TYPE_MCBIST> find_target( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target) +{ + return i_target.getParent<fapi2::TARGET_TYPE_MCBIST>(); +} + + +/// +/// @brief find all the dimm connected to an MCS +/// @param[in] a fapi2::Target MCS +/// @return a vector of fapi2::TARGET_TYPE_DIMM +/// +template<> +inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > find_targets( const fapi2::Target<fapi2::TARGET_TYPE_MCS>& + i_target ) +{ + std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > l_dimms; + + // At this time, fapi2 (cronus?) doesn't seem to recognize a DIMM is the child of an MCS. + for (auto p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>()) + { + auto l_these_dimms( p.getChildren<fapi2::TARGET_TYPE_DIMM>() ); + l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + } + + return l_dimms; +} + +/// +/// @brief find all the dimm connected to an MCBIST +/// @param[in] a fapi2::Target MCBIST +/// @return a vector of fapi2::TARGET_TYPE_DIMM +/// +template<> +inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > find_targets( const + fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ) +{ + std::vector< fapi2::Target<fapi2::TARGET_TYPE_DIMM> > l_dimms; + + for (auto p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>()) + { + auto l_these_dimms( p.getChildren<fapi2::TARGET_TYPE_DIMM>() ); + l_dimms.insert(l_dimms.end(), l_these_dimms.begin(), l_these_dimms.end()); + } + + return l_dimms; +} + +/// +/// @brief find all the MCS connected to an MCBIST +/// @param[in] a fapi2::Target MCBIST +/// @return a vector of fapi2::TARGET_TYPE_MCS +/// @note Cronus should support MCS children of an MCBIST - so this might be temporary +/// +template<> +inline std::vector< fapi2::Target<fapi2::TARGET_TYPE_MCS> > find_targets( const + fapi2::Target<fapi2::TARGET_TYPE_MCBIST>& i_target ) +{ + std::vector< fapi2::Target<fapi2::TARGET_TYPE_MCS> > l_mcses; + + // At this time, fapi2 (cronus?) doesn't seem to recognize a MCS is the child of an MCBIST + for (auto p : i_target.getChildren<fapi2::TARGET_TYPE_MCA>()) + { + fapi2::Target<fapi2::TARGET_TYPE_MCS> l_mcs = p.getParent<fapi2::TARGET_TYPE_MCS>(); + + if ( l_mcses.end() == std::find_if( l_mcses.begin(), l_mcses.end(), + [l_mcs](const fapi2::Target<fapi2::TARGET_TYPE_MCS>& c) + { + return l_mcs == c; + }) ) + { + l_mcses.push_back(l_mcs); + } + } + + return l_mcses; +} + +/// +/// @brief find the MCS given a DIMM +/// @param[in] the fapi2 target, DIMM +/// @return a MCS target. +/// +template<> +inline fapi2::Target<fapi2::TARGET_TYPE_MCS> find_target( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + return i_target.getParent<fapi2::TARGET_TYPE_MCA>().getParent<fapi2::TARGET_TYPE_MCS>(); +} + +/// +/// @brief find the MCA given a DIMM +/// @param[in] the fapi2 target, DIMM +/// @return a MCA target. +/// +template<> +inline fapi2::Target<fapi2::TARGET_TYPE_MCA> find_target( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + return i_target.getParent<fapi2::TARGET_TYPE_MCA>(); +} + +}// mss + +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/index.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/index.H new file mode 100644 index 000000000..aa9474895 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/index.H @@ -0,0 +1,71 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/index.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file index.H +/// @brief Tools to return attribute array index from a fapi2 target +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchamilt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_INDEX_H_ +#define _MSS_INDEX_H_ + +#include <fapi2.H> +#include "pos.H" +#include "../shared/mss_const.H" + +namespace mss +{ +/// +/// @brief Return an attribute array index from a fapi2 target +/// @tparam T, the fapi2::TargetType +/// @param[in] i_target, a DIMM target representing the DIMM in question +/// @return size_t, the attribute array index. +/// +template< fapi2::TargetType T > +inline size_t index(const fapi2::Target<T>& i_target); + +template<> +inline size_t index(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + return mss::pos(i_target) % MAX_DIMM_PER_PORT; +} + +template<> +inline size_t index(const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target) +{ + return mss::pos(i_target) % PORTS_PER_MCS; +} + +/// +/// @brief Return an attribute array index from a rank number +/// @param[in] uint64_t, a rank number DIMM0 {0, 1, 2, 3} DIMM1 {0, 1, 2, 3} +/// @return size_t, the attribute array index. +/// +inline size_t index(const uint64_t i_rank) +{ + return i_rank % RANK_MID_POINT; +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/poll.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/poll.H new file mode 100644 index 000000000..4fc9dab60 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/poll.H @@ -0,0 +1,134 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/poll.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file poll.H +/// @brief Poll a scom +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_POLL_H_ +#define _MSS_POLL_H_ + +#include <fapi2.H> +#include "../shared/mss_const.H" +#include "conversions.H" + +namespace mss +{ + +/// +/// @brief Poll paramter data structure. +/// Represents the parameters used to poll a register. +/// _delays are in ns, _sim_delays are in sim_cycles +/// See conversions.H for conversions. +struct poll_parameters +{ + // We need to decide how long to wait before pounding the compeletion SCOM. + // IN NS + uint64_t iv_initial_delay; + + // Same as the initial delay, but used when we're running in a simulator + // IN SIM_CYCLES + uint64_t iv_initial_sim_delay; + + // How many ns to wait in between poll attempts + // IN NS + uint64_t iv_delay; + + // Same as the interpoll delay, but used when we're running in a simulator + // IN SIM_CYCLES + uint64_t iv_sim_delay; + + // The count of polls to make + uint64_t iv_poll_count; + + poll_parameters( const uint64_t i_initial_delay = DELAY_10NS, + const uint64_t i_initial_sim_delay = 200, + const uint64_t i_delay = DELAY_10NS, + const uint64_t i_sim_delay = 200, + const uint64_t i_poll_count = DEFAULT_POLL_LIMIT): + iv_initial_delay(i_initial_delay), + iv_initial_sim_delay(i_initial_sim_delay), + iv_delay(i_delay), + iv_sim_delay(i_sim_delay), + iv_poll_count(i_poll_count) + {} +}; + +/// +/// @brief Poll a scom, return whether the poll croteria were met or not +/// @tparam T, the fapi2::TargetType +/// @tparam L, a lambda representing the completion criteria - returns true +/// iff the poll criteria have been met (and the polling can stop) +/// @param[in] i_target, target for the getScom +/// @param[in] i_addr, the address for the scom +/// @param[in] i_params, a poll_parameters structure +/// @param[in] i_fn, the function to call to check the poll criteria +/// [](const size_t poll_remaining, const fapi2::buffer<uint64_t>& stat_reg) -> bool +/// { +/// return true; +/// } +/// @return bool, true iff poll criteria was met before the number of iterations +/// ran out. +/// @warning If you want to handle a failure as an error, you need to wrap +/// the call to poll() in a FAPI_ASSERT. FAPI_TRY is *not* the right mechanism +/// as poll() does not return a fapi2::ReturnCode +/// +template< fapi2::TargetType T, typename L > +inline bool poll(const fapi2::Target<T>& i_target, const uint64_t i_addr, + const poll_parameters i_params, L i_fn) +{ + fapi2::buffer<uint64_t> l_reg; + + FAPI_DBG("polling 0x%llx: initial delay %dns(%dsc) poll %dns(%dsc) %d iters", + i_addr, i_params.iv_initial_delay, i_params.iv_initial_sim_delay, + i_params.iv_delay, i_params.iv_sim_delay, i_params.iv_poll_count); + + // We know to wait this long before polling + FAPI_TRY( fapi2::delay(i_params.iv_initial_delay, i_params.iv_initial_sim_delay) ); + + for ( size_t l_poll_limit = i_params.iv_poll_count; l_poll_limit > 0; --l_poll_limit ) + { + FAPI_TRY( fapi2::getScom(i_target, i_addr, l_reg) ); + + if (i_fn(l_poll_limit, l_reg) == true) + { + FAPI_DBG("polling finished. %d iterations remaining", l_poll_limit); + return true; + } + + FAPI_TRY( fapi2::delay(i_params.iv_delay, i_params.iv_sim_delay) ); + } + + // If we're here, we ran out of poll iterations + FAPI_INF("WARNING: Timeout on polling"); + return false; + +fapi_try_exit: + FAPI_ERR("mss::poll() hit an error in fapi2::delay or fapi2::getScom"); + return false; +} + +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/pos.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/pos.H new file mode 100644 index 000000000..18dadf701 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/pos.H @@ -0,0 +1,133 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/pos.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file pos.H +/// @brief Tools to return target's position from a fapi2 target +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Craig Hamilton <cchailt@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_POS_H_ +#define _MSS_POS_H_ + +#include <fapi2.H> + +namespace mss +{ + +/// +/// @brief Trait classes for the mss::pos functions +/// +template<fapi2::TargetType T> +class posTraits +{ + public: + // Needed as some targets have chip_unit_pos attributes, + // uint8_t and some attr_pos, uint32_t. We don't really care + // but the type checking in the macros sure does ... + typedef uint8_t pos_type; +}; + +template<> +class posTraits<fapi2::TARGET_TYPE_DIMM> +{ + public: + typedef uint32_t pos_type; +}; + +template<> +class posTraits<fapi2::TARGET_TYPE_PROC_CHIP> +{ + public: + typedef uint32_t pos_type; +}; + +/// +/// @brief Return a target's position from a fapi2 target +/// @tparam T, the fapi2::TargetType +/// @param[in] i_target, a DIMM target representing the DIMM in question +/// @return The position +/// +template< fapi2::TargetType T, typename TT = posTraits<T> > +inline typename TT::pos_type pos(const fapi2::Target<T>& i_target) +{ + typename TT::pos_type i_pos = 0; + + // Don't use FAPI_TRY as you'll mess up fapi2::current_err which + // lmits where this can be used. + if (FAPI_ATTR_GET(fapi2::ATTR_CHIP_UNIT_POS, i_target, i_pos) != fapi2::FAPI2_RC_SUCCESS) + { + goto fapi_try_exit; + } + + return i_pos; + +fapi_try_exit: + // If we can't get our unit position, we're in other trouble + FAPI_ERR("can't get our chip unit position"); + fapi2::Assert(false); + return 0; +} + +template<> +inline posTraits<fapi2::TARGET_TYPE_DIMM>::pos_type pos(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target) +{ + posTraits<fapi2::TARGET_TYPE_DIMM>::pos_type i_pos = 0; + + if (FAPI_ATTR_GET(fapi2::ATTR_POS, i_target, i_pos) != fapi2::FAPI2_RC_SUCCESS) + { + goto fapi_try_exit; + } + + return i_pos; + +fapi_try_exit: + // If we can't get our position, we're in other trouble + FAPI_ERR("can't get our position"); + fapi2::Assert(false); + return 0; + +} + +template<> +inline posTraits<fapi2::TARGET_TYPE_PROC_CHIP>::pos_type pos(const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& + i_target) +{ + posTraits<fapi2::TARGET_TYPE_PROC_CHIP>::pos_type i_pos = 0; + + if (FAPI_ATTR_GET(fapi2::ATTR_POS, i_target, i_pos) != fapi2::FAPI2_RC_SUCCESS) + { + goto fapi_try_exit; + } + + return i_pos; + +fapi_try_exit: + // If we can't get our position, we're in other trouble + FAPI_ERR("can't get our position"); + fapi2::Assert(false); + return 0; + +} +} +#endif diff --git a/src/import/chips/p9/procedures/hwp/memory/lib/utils/swizzle.H b/src/import/chips/p9/procedures/hwp/memory/lib/utils/swizzle.H new file mode 100644 index 000000000..57814b265 --- /dev/null +++ b/src/import/chips/p9/procedures/hwp/memory/lib/utils/swizzle.H @@ -0,0 +1,123 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: chips/p9/procedures/hwp/memory/lib/utils/swizzle.H $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* EKB Project */ +/* */ +/* COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/// +/// @file swizzle.H +/// @brief Swizzle bits +/// +// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com> +// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com> +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_SWIZZLE_H_ +#define _MSS_SWIZZLE_H_ + +#include <fapi2.H> + +namespace mss +{ + +/// +/// @brief Swap two bits in a buffer +/// @tparam TB, the bit in the buffer to move to SB +/// @tparam SB, the bit in the buffer to move to TB +/// @tparam T, integral type of the buffer +/// @param[in,out] io_data, reference to the buffer +/// @return reference to the input buffer (for chaining) +/// +template< uint64_t TB, uint64_t SB, typename T > +inline fapi2::buffer<T>& swap( fapi2::buffer<T>& i_data ) +{ + bool l_tb = i_data.template getBit<TB>(); + i_data.template writeBit<TB>(i_data.template getBit<SB>()); + i_data.template writeBit<SB>(l_tb); + + return i_data; +} + +/// +/// @brief Invert (negate) one bit in a buffer +/// @tparam TB, the bit in the buffer to negate +/// @tparam T, integral type of the buffer +/// @param[in,out] io_data, reference to the buffer +/// @return reference to the input buffer (for chaining) +/// +template< uint64_t TB, typename T > +inline fapi2::buffer<T>& negate( fapi2::buffer<T>& i_data ) +{ + // Use care when writeBit'ing a getBit as getBit returns a bool and writeBit + // checks if the input != 0. Negating it (~ getBit) was causing havok. So + // we do this - note the negation of the conditional to get the reverse. + i_data.template writeBit<TB>(i_data.template getBit<TB>() == true ? 0 : 1); + return i_data; +} + +/// +/// @brief Reverse the buffer +/// @param[in,out] io_buffer the buffer which to reverse +// +// @note from +// http://stackoverflow.com/questions/746171/best-algorithm-for-bit-reversal-from-msb-lsb-to-lsb-msb-in-c +/// +template< typename T > +static inline void reverse( T& io_buffer ) +{ + T l_result = io_buffer; + size_t l_s = sizeof(T) * 8 - 1; + + for( io_buffer >>= 1; io_buffer; io_buffer >>= 1) + { + l_result <<= 1; + l_result |= io_buffer & 1; + l_s--; + } + + l_result <<= l_s; + + io_buffer = l_result; +} + +/// +/// @brief Swizzle bits between two fapi2 buffers, and insert from source to destination +/// @tparam DS, the start bit in the destination buffer - swizzle will count up from here +/// @tparam L, how many bits to swizzle +/// @tparam SS, the start bit in the source buffer - swizzle will count down from here +/// @param[in] source buffer - these bits will be decremented +/// @param[in] destination buffer - these bits will be incremented +/// @return reference to the destination +/// +template<uint64_t DS, uint64_t L, uint64_t SS, typename SB, typename DB> +inline fapi2::buffer<DB>& swizzle( fapi2::buffer<SB> i_source, fapi2::buffer<DB>& o_destination ) +{ + // Reverse the destination, and then mangle the start bits to get things to line up + fapi2::buffer<SB> l_tmp(i_source); + reverse(i_source); + +#ifdef SWIZZLE_TRACE + FAPI_DBG("swizzle o: 0x%0X s: 0x%X d: 0x%llX ds: %d l: %d ss: %d", + l_tmp, i_source, o_destination, DS, L, (sizeof(SB) * 8) - (SS + 1)); +#endif + + return o_destination.template insert < DS, L, (sizeof(SB) * 8) - (SS + 1) > (SB(i_source)); +} + +} +#endif |