From 2dbce731a9de46f7f30df1eaa88b0b35a7797bc6 Mon Sep 17 00:00:00 2001 From: Brian Silver Date: Mon, 28 Dec 2015 11:26:59 -0600 Subject: Initial commit of memory subsystem Change-Id: Ia6375304adaa7e04dfa642e144341d7a5776673a Original-Change-Id: I6b63d2c4eec5d77585c91d905a464962a6153a0a Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/22978 Tested-by: Jenkins Server Reviewed-by: Craig C. Hamilton Reviewed-by: Brian Silver Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/22759 Tested-by: FSP CI Jenkins Reviewed-by: Daniel M. Crowell --- .../chips/p9/procedures/hwp/memory/lib/ccs/ccs.C | 254 ++++++++ .../chips/p9/procedures/hwp/memory/lib/ccs/ccs.H | 648 +++++++++++++++++++++ 2 files changed, 902 insertions(+) create mode 100644 src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.C create mode 100644 src/import/chips/p9/procedures/hwp/memory/lib/ccs/ccs.H (limited to 'src/import/chips/p9/procedures/hwp/memory/lib/ccs') 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 +// *HWP HWP Backup: Craig Hamilton +// *HWP Team: Memory +// *HWP Level: 2 +// *HWP Consumed by: FSP:HB + +#include + +#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& i_target, bool i_start_stop ) +{ + typedef ccsTraits TT; + + fapi2::buffer 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() : l_buf.setBit()) ); + +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& 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& i_target, + ccs::program& i_program) +{ + typedef ccsTraits TT; + + fapi2::buffer 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& stat_reg) -> bool + { + FAPI_DBG("ccs statq 0x%llx, remaining: %d", stat_reg, poll_remaining); + status = stat_reg; + return status.getBit() != 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& i_target, + ccs::program& i_program, + const std::vector< fapi2::Target >& 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 l_des = ccs::des_command(); + + 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(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(l_delay); + l_inst_iter->arr1.extractToRight(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(); + 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( const fapi2::Target&, fapi2::buffer&, + 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 +// *HWP HWP Backup: Craig Hamilton +// *HWP Team: Memory +// *HWP Level: 1 +// *HWP Consumed by: HB:FSP + +#ifndef _MSS_CCS_H_ +#define _MSS_CCS_H_ + +#include + +#include + +#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 +{ + public: +}; + +// Nimbus CCS Engine traits +template<> +class ccsTraits +{ + 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 > +class instruction_t +{ + public: + fapi2::buffer arr0; + fapi2::buffer 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& i_target = fapi2::Target(), + uint64_t i_rank = 0xFF, + const fapi2::buffer i_arr0 = 0, + const fapi2::buffer 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(0b11); + arr0.insertFromRight(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(CS_N[i_rank]); + } + else + { + arr0.insertFromRight(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(CS_CID[i_rank].first); + } + else + { + arr0.insert(CS_CID[i_rank].first); + } + + arr0.insert(CS_CID[i_rank].second); + arr0.writeBit( + fapi2::buffer(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 > iv_instructions; + poll_parameters iv_poll; +}; + +/// +/// @brief Common setup for all MRS/RCD instructions +/// @param[in,out] fapi2::buffer representing the ARR0 of the instruction +/// @return void +/// +template< fapi2::TargetType T, typename TT = ccsTraits > +static void mrs_rcd_helper( fapi2::buffer& i_arr0 ) +{ + // + // Generic DDR4 MRS setup (RCD is an MRS) + // + // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS + i_arr0.insertFromRight(0b1111); + + // ACT is high + i_arr0.setBit(); + + // RAS, CAS, WE low + i_arr0.clearBit(); + i_arr0.clearBit(); + i_arr0.clearBit(); +} + +/// +/// @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 > +inline instruction_t rcd_command( const fapi2::Target& i_target ) +{ + fapi2::buffer rcd_boilerplate_arr0; + fapi2::buffer rcd_boilerplate_arr1; + + // + // Generic DDR4 MRS setup (RCD is an MRS) + // + mrs_rcd_helper(rcd_boilerplate_arr0); + + // + // RCD setup + // + // DDR4: Set BG1 to 0. BG0, BA1:BA0 to 0b111 + rcd_boilerplate_arr0.clearBit(); + rcd_boilerplate_arr0.insertFromRight(0b11); + rcd_boilerplate_arr0.setBit(); + + // RCD always goes to rank 0. All we need to know is which DIMM we are on the port + return instruction_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 > +inline instruction_t mrs_command( const fapi2::Target& i_target, const uint64_t i_rank, + const uint64_t i_mrs ) +{ + fapi2::buffer rcd_boilerplate_arr0; + fapi2::buffer rcd_boilerplate_arr1; + fapi2::buffer mrs(i_mrs); + + // + // Generic DDR4 MRS setup (RCD is an MRS) + // + mrs_rcd_helper(rcd_boilerplate_arr0); + + // + // MRS setup + // + // DDR4: Set BG1 to 0. BG0, BA1:BA0 to i_mrs + rcd_boilerplate_arr0.clearBit(); + mss::swizzle(mrs, rcd_boilerplate_arr0); + FAPI_DBG("mrs rcd boiler 0x%llx 0x%llx", uint8_t(mrs), uint64_t(rcd_boilerplate_arr0)); + return instruction_t(i_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 > +inline instruction_t des_command() +{ + fapi2::buffer rcd_boilerplate_arr0; + fapi2::buffer rcd_boilerplate_arr1; + + // ACT is high. It's a no-care in the spec but it seems to raise questions when + // people look at the trace, so lets set it high. + rcd_boilerplate_arr0.setBit(); + + // CKE is high Note: P8 set all 4 of these high - not sure if that's correct. BRS + rcd_boilerplate_arr0.insertFromRight(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(fapi2::Target(), 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 > +inline instruction_t initial_cal_command(const uint64_t i_rp) +{ + // An initial cal arr0 looks just like a DES, but we set the initial cal bits + instruction_t l_inst = des_command(); + + // ACT is low - per Centaur spec (Shelton to confirm for Nimbus) BRS + l_inst.arr0.template clearBit(); + + l_inst.arr0.template insertFromRight(0b1100); + l_inst.arr1.template setBit(); + +#ifdef USE_LOTS_OF_IDLES + // Idles is 0xFFFF - per Centaur spec (Shelton to confirm for Nimbus) BRS + l_inst.arr1.template insertFromRight(0xFFFF); +#else + l_inst.arr1.template insertFromRight(0x0); +#endif + + // The rank we're calibrating is enacoded - it's an int. So rank 3 is 0011 not 0001 + l_inst.arr1.template insertFromRight(i_rp); + + return l_inst; +} + +// +// 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 > +inline fapi2::ReturnCode select_ports( const fapi2::Target& i_target, uint64_t i_ports) +{ + fapi2::buffer l_data; + fapi2::buffer l_ports; + + // Not handling multiple ports here, can't do that for CCS. BRS + FAPI_TRY( l_ports.setBit(i_ports) ); + + FAPI_TRY( fapi2::getScom(i_target, TT::MCB_CNTL_REG, l_data) ); + l_data.insert(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 > +inline void stop_on_err( const fapi2::Target&, fapi2::buffer& i_buffer, bool i_value) +{ + i_buffer.writeBit(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 > +inline void disable_ecc( const fapi2::Target&, fapi2::buffer& i_buffer) +{ + i_buffer.setBit(); + i_buffer.setBit(); +} + +/// +/// @brief User sets to a '1'b to force the Hdw to ignore any array ue or sue errors +/// during CCS command fetching. +/// @tparam T, the 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 > +inline void ue_disable( const fapi2::Target&, fapi2::buffer& i_buffer, bool i_value) +{ + i_buffer.writeBit(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 > +inline void cal_count( const fapi2::Target&, fapi2::buffer& i_buffer, + const uint64_t i_count, const uint64_t i_mult) +{ + i_buffer.insertFromRight(i_count); + i_buffer.insertFromRight(i_mult); +} + +/// +/// @brief Copy CKE signals to CKE Spare on both ports NOTE: DOESN'T APPLY FOR NIMBUS. NO +/// SPARE CHIPS TO COPY TO. 0 - Spare CKEs not copied with values from CKE(0:1) and +/// CKE(4:5) 1 - Port A CKE(0:1) copied to Port A CKE(2:3), Port A CKE(4:5) copied +/// to Port A CKE(6:7), Port B CKE(0:1) copied to Port B CKE(2:3) and Port B CKE(4:5) +/// copied to Port B CKE(6:7) +/// @tparam T, the fapi2::TargetType - derived +/// @tparam TT, the ccsTraits associated with T - derived +/// @param[in] 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 > +void copy_cke_to_spare_cke( const fapi2::Target&, fapi2::buffer& 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 > +inline fapi2::ReturnCode read_mode( const fapi2::Target& i_target, fapi2::buffer& 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 > +inline fapi2::ReturnCode write_mode( const fapi2::Target& i_target, const fapi2::buffer& 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 > +fapi2::ReturnCode execute( const fapi2::Target& i_target, + ccs::program& i_program, + const std::vector< fapi2::Target

>& i_ports ); + +/// +/// @brief Execute a set of CCS instructions - single port +/// @tparam T, the 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 > +fapi2::ReturnCode execute( const fapi2::Target& i_target, + ccs::program& i_program, + const fapi2::Target

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

> l_ports{ i_port }; + return execute(i_target, i_program, l_ports); +} + +/// +/// @brief Execute a CCS array already loaded in to the engine +/// @tparam T, the 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 > +fapi2::ReturnCode execute_inst_array(const fapi2::Target& i_target, ccs::program& 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 > +fapi2::ReturnCode start_stop( const fapi2::Target& 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 > +fapi2::ReturnCode status_query( const fapi2::Target& i_target, std::pair& io_status ); + +} // ends namespace ccs +} + +#endif -- cgit v1.2.1