From 85a561df01c8237bdaa48c62871b2cf0101abbbc Mon Sep 17 00:00:00 2001 From: Louis Stermole Date: Wed, 6 Jun 2018 13:13:09 -0500 Subject: Improve WR_VREF shmoo algorithm in p9c training_adv Change-Id: I0f07072b4e5e078c8ea3845abbf8933a03a8bf62 CQ:SW439234 Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/60047 Dev-Ready: Louis Stermole Reviewed-by: STEPHEN GLANCY Tested-by: Jenkins Server Tested-by: HWSV CI Reviewed-by: ANDRE A. MARIN Tested-by: Hostboot CI Reviewed-by: Thi N. Tran Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/60056 Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Tested-by: FSP CI Jenkins Disable-CI: Christian R. Geddes Reviewed-by: Christian R. Geddes --- .../procedures/hwp/memory/lib/shared/dimmConsts.H | 8 +- .../procedures/hwp/memory/p9c_mss_ddr4_pda.C | 13 +- .../memory/p9c_mss_draminit_training_advanced.C | 741 ++++++++++++++------- .../memory/p9c_mss_draminit_training_advanced.H | 48 +- .../procedures/hwp/memory/p9c_mss_generic_shmoo.C | 69 +- .../procedures/hwp/memory/p9c_mss_generic_shmoo.H | 21 +- .../procedures/hwp/memory/p9c_mss_mcbist_common.C | 2 +- .../xml/attribute_info/memory_attributes.xml | 18 +- ...emory_mss_draminit_training_advanced_errors.xml | 18 +- 9 files changed, 676 insertions(+), 262 deletions(-) (limited to 'src') diff --git a/src/import/chips/centaur/procedures/hwp/memory/lib/shared/dimmConsts.H b/src/import/chips/centaur/procedures/hwp/memory/lib/shared/dimmConsts.H index f8226ad6f..bd8770027 100644 --- a/src/import/chips/centaur/procedures/hwp/memory/lib/shared/dimmConsts.H +++ b/src/import/chips/centaur/procedures/hwp/memory/lib/shared/dimmConsts.H @@ -85,7 +85,13 @@ enum consts : size_t BITS_PER_PORT = (LANES_PER_BLOCK * DP18_INSTANCES), DATA_BYTES_PER_PORT = 8, - SP_BYTES_PER_PORT = 2, + SP_ECC_BYTES_PER_PORT = 2, + SP_ECC_BYTES_PER_PORT_ISDIMM = 1, + + MAX_DRAMS_PER_RANK_X8 = DATA_BYTES_PER_PORT + SP_ECC_BYTES_PER_PORT, + MAX_DRAMS_PER_RANK_X4 = MAX_DRAMS_PER_RANK_X8 * MAX_NIBBLES_PER_BYTE, + MAX_DRAMS_PER_RANK_X8_ISDIMM = DATA_BYTES_PER_PORT + SP_ECC_BYTES_PER_PORT_ISDIMM, + MAX_DRAMS_PER_RANK_X4_ISDIMM = MAX_DRAMS_PER_RANK_X8_ISDIMM * MAX_NIBBLES_PER_BYTE, }; diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_ddr4_pda.C b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_ddr4_pda.C index 6aa6db075..dc0ba80fd 100755 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_ddr4_pda.C +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_ddr4_pda.C @@ -1148,18 +1148,7 @@ extern "C" { } uint8_t num_ranks_array[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; //[port][dimm] - uint8_t dram_stack[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; - - FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_STACK_TYPE, i_target, dram_stack)); - - if(dram_stack[0][0] == fapi2::ENUM_ATTR_CEN_EFF_STACK_TYPE_STACK_3DS) - { - FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_RANKS_PER_DIMM, i_target, num_ranks_array)); - } - else - { - FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target, num_ranks_array); - } + FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target, num_ranks_array); //loops through all DIMMs all Ranks for(uint8_t l_dimm = 0; l_dimm < MAX_DIMM_PER_PORT; l_dimm++) diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.C b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.C index 1ddcb3090..c0f6f9195 100755 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.C +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.C @@ -45,6 +45,8 @@ // Includes - FAPI //---------------------------------------------------------------------- +#include +#include #include #include #include @@ -773,6 +775,454 @@ fapi_try_exit: } +/// +/// @brief set and latch new wr vref values +/// @param[in] i_target_mba Centaur input MBA +/// @param[in] i_vref_values the value to set for each rank +/// @return FAPI2_RC_SUCCESS iff successful +/// +fapi2::ReturnCode latch_wr_vref(const fapi2::Target& i_target_mba, + const uint8_t (&i_vref_values)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM]) +{ + constexpr uint8_t ENABLE_TRAINING = 0x01; + constexpr uint8_t DISABLE_TRAINING = 0x00; + + fapi2::buffer l_data_buffer_64; + uint8_t l_vrefdq_train_value[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM] = {0}; + uint8_t l_vrefdq_train_enable[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM] = {0}; + + // Turn off refresh + FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + l_data_buffer_64.clearBit(); + FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + + // Setup training attributes + std::fill(&l_vrefdq_train_enable[0][0][0], + &l_vrefdq_train_enable[0][0][0] + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM), + ENABLE_TRAINING); + memcpy(&l_vrefdq_train_value[0][0][0], &i_vref_values[0][0][0], + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM)); + + FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, l_vrefdq_train_enable)); + FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, i_target_mba, l_vrefdq_train_value)); + + // Setup and latch MRS6 to run training + FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "%s mrs_load failed", mss::c_str(i_target_mba)); + FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "%s mrs_load failed", mss::c_str(i_target_mba)); + + // Clear training enables + std::fill(&l_vrefdq_train_enable[0][0][0], + &l_vrefdq_train_enable[0][0][0] + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM), + DISABLE_TRAINING); + + FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, l_vrefdq_train_enable)); + + // ...and send them out + FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "%s mrs_load failed", mss::c_str(i_target_mba)); + + // Turn refresh back on + FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + l_data_buffer_64.setBit(); + FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + +fapi_try_exit: + return fapi2::current_err; + +} + +/// +/// @brief set average wr vref value on all ranks +/// @param[in] i_target_mba Centaur input MBA +/// @param[in] i_pda_nibble_table table of vref values from training +/// @return FAPI2_RC_SUCCESS iff successful +/// +fapi2::ReturnCode set_wr_vref_by_rank(const fapi2::Target& i_target_mba, + const uint32_t ( + &i_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) +{ + uint8_t num_ranks_per_dimm[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; + uint32_t l_avg_best_vref = 0; + uint8_t l_dimm_type = 0; + uint32_t l_max_nibbles = 0; + uint8_t l_vref_values[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM] = {0}; + + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target_mba, num_ranks_per_dimm)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba, l_dimm_type)); + + l_max_nibbles = (l_dimm_type == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? MAX_DRAMS_PER_RANK_X4 : + MAX_DRAMS_PER_RANK_X4_ISDIMM; + + // Calculate and set the Average V-Ref Value for each rank + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm = 0; l_dimm < MAX_DIMM_PER_PORT; l_dimm++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + l_avg_best_vref += i_pda_nibble_table[l_port_index][l_dimm][l_rank_index][l_nibble_index][0]; + } + + l_avg_best_vref = l_avg_best_vref / l_max_nibbles; + l_vref_values[l_port_index][l_dimm][l_rank_index] = l_avg_best_vref; + FAPI_INF("%s Port %d Dimm %d Rank:%d Best Avg V-Ref = %d", mss::c_str(i_target_mba), + l_port_index, l_dimm, l_rank_index, l_avg_best_vref); + } + } + } + + FAPI_TRY(latch_wr_vref(i_target_mba, l_vref_values)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief set new wr vref values on a DRAM basis (PDA) +/// @param[in] i_target_mba Centaur input MBA +/// @param[in] i_pda_nibble_table table of vref values from training +/// @return FAPI2_RC_SUCCESS iff successful +/// +fapi2::ReturnCode set_wr_vref_by_dram(const fapi2::Target& i_target_mba, + const uint32_t ( + &i_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) +{ + uint8_t num_ranks_per_dimm[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; + uint8_t dram_width = 0; + std::vector pda_enable; + std::vector pda_disable; + uint32_t max_vref; + uint8_t dram_num; + fapi2::buffer l_data_buffer_64; + uint8_t l_dimm_type = 0; + uint32_t l_max_nibbles = 0; + + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target_mba, num_ranks_per_dimm)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_DRAM_WIDTH, i_target_mba, dram_width)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba, l_dimm_type)); + + l_max_nibbles = (l_dimm_type == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? MAX_DRAMS_PER_RANK_X4 : + MAX_DRAMS_PER_RANK_X4_ISDIMM; + + // Issue PDA commands with train enable 1 (enabled) for all DRAMs with good VREF values + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + dram_num = l_nibble_index; + max_vref = i_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; + + // If we're x8 width, average the Vref for both nibbles together and update the DRAM index + if(dram_width == fapi2::ENUM_ATTR_CEN_EFF_DRAM_WIDTH_X8) + { + l_nibble_index++; + dram_num = dram_num / MAX_NIBBLES_PER_BYTE; + max_vref += i_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; + max_vref = max_vref / MAX_NIBBLES_PER_BYTE; + } + + FAPI_INF("%s Port %d Dimm %d Rank:%d Pda_Nibble: %d DRAM_num %d V-ref:%d Margin:%d", mss::c_str(i_target_mba), + l_port_index, l_dimm_index, l_rank_index, l_nibble_index, dram_num, + i_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0], + i_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); + + // Push the PDA enable MRS commands onto the enable list + pda_enable.push_back(PDA_MRS_Storage(0x01, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, dram_num, l_dimm_index, + l_rank_index, l_port_index)); + FAPI_INF("%s PDA STRING (training enable): %d %s", mss::c_str(i_target_mba), pda_enable.size() - 1, + pda_enable[pda_enable.size() - 1].c_str()); + pda_enable.push_back(PDA_MRS_Storage(max_vref, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, dram_num, l_dimm_index, + l_rank_index, l_port_index)); + FAPI_INF("%s PDA STRING (vref value): %d %s", mss::c_str(i_target_mba), pda_enable.size() - 1, + pda_enable[pda_enable.size() - 1].c_str()); + + // And the disable commands onto the disable list + pda_disable.push_back(PDA_MRS_Storage(0x00, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, dram_num, l_dimm_index, + l_rank_index, l_port_index)); + FAPI_INF("%s PDA STRING (training disable): %d %s", mss::c_str(i_target_mba), pda_disable.size() - 1, + pda_disable[pda_disable.size() - 1].c_str()); + pda_disable.push_back(PDA_MRS_Storage(max_vref, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, dram_num, l_dimm_index, + l_rank_index, l_port_index)); + FAPI_INF("%s PDA STRING (vref value): %d %s", mss::c_str(i_target_mba), pda_disable.size() - 1, + pda_disable[pda_disable.size() - 1].c_str()); + } + } //End of Rank Loop + } //end of dimm loop + } //End of Port Loop + + // Turn off refresh + FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + l_data_buffer_64.clearBit(); + FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + + FAPI_INF("%s RUNNING PDA FOR 1ST TIME", mss::c_str(i_target_mba)); + FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda_enable)); + FAPI_INF("%s FINISHED RUNNING PDA FOR 1ST TIME", mss::c_str(i_target_mba)); + + // Issue call to run PDA again (latching good value in train mode) + FAPI_INF("%s RUNNING PDA FOR 2ND TIME", mss::c_str(i_target_mba)); + FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda_enable)); + FAPI_INF("%s FINISHED RUNNING PDA FOR 2ND TIME", mss::c_str(i_target_mba)); + + // Next issue PDA commands with train enable 0 (disabled) for all DRAMs + FAPI_INF("%s RUNNING PDA FOR 3RD TIME", mss::c_str(i_target_mba)); + FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda_disable)); + FAPI_INF("%s FINISHED RUNNING PDA FOR 3RD TIME", mss::c_str(i_target_mba)); + + // Turn refresh back on + FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + l_data_buffer_64.setBit(); + FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief set new wr vref values and return margins +/// @param[in] i_target_mba Centaur input MBA +/// @param[in] i_vref_values Vref values to test +/// @param[in,out] io_pda_nibble_table table of vref values from training +/// @return FAPI2_RC_SUCCESS iff successful +/// +fapi2::ReturnCode wr_vref_test_helper(const fapi2::Target& i_target_mba, + const uint8_t (&i_vref_values)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4], + uint32_t (&io_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) +{ + uint8_t l_dimm_type = 0; + uint32_t l_max_nibbles = 0; + uint8_t num_ranks_per_dimm[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; + shmoo_type_t l_shmoo_type_valid = WR_EYE; + uint32_t l_left_total_margin = 0; + uint32_t l_right_total_margin = 0; + + // Set up the test point Vrefs and run a shmoo to get the margins + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target_mba, num_ranks_per_dimm)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba, l_dimm_type)); + + l_max_nibbles = (l_dimm_type == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? MAX_DRAMS_PER_RANK_X4 : + MAX_DRAMS_PER_RANK_X4_ISDIMM; + + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + io_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0] = + i_vref_values[l_port_index][l_dimm_index][l_rank_index][l_nibble_index]; + } + } + } + } + + FAPI_TRY(set_wr_vref_by_dram(i_target_mba, io_pda_nibble_table)); + + FAPI_TRY(delay_shmoo_ddr4_pda(i_target_mba, 0, l_shmoo_type_valid, + &l_left_total_margin, &l_right_total_margin, 0, io_pda_nibble_table)); + +fapi_try_exit: + return fapi2::current_err; +} + +/// +/// @brief search for best wr vref values by DRAM using ternary search algorithm +/// @param[in] i_target_mba Centaur input MBA +/// @param[in] io_pda_nibble_table table of best vref values +/// @return FAPI2_RC_SUCCESS iff successful +/// +fapi2::ReturnCode wr_vref_ternary_search(const fapi2::Target& i_target_mba, + uint32_t (&io_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) +{ + constexpr uint8_t MAX_VREF = 50; + + // Set the MCBIST end addresses to limit the duration of each delay_shmoo, and yield a low run-to-run variation + uint64_t l_mcbist_address_config = 0x0000001ffc000000ull; + + uint8_t l_vpd_wr_vref_value[2] = {0}; + uint8_t l_nominal_vref = 0; + uint8_t l_max_delta = 0; + uint8_t l_min_vref = 0; + uint8_t l_max_vref = 0; + uint8_t l_starting_ticks_from_nominal = 0; + uint8_t num_ranks_per_dimm[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; + uint8_t l_dimm_type = 0; + uint32_t l_max_nibbles = 0; + uint8_t l_attr_shmoo_test_type_u8 = fapi2::ENUM_ATTR_CEN_EFF_SCHMOO_TEST_VALID_WR_EYE; + bool l_search_complete = true; + + // Search boundaries + uint8_t l_low_bound[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4] = {0}; + uint8_t l_high_bound[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4] = {0}; + + // Test point + uint8_t l_test_vref[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4] = {0}; + + // Test results (margins) + uint32_t l_pda_table[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2] = {0}; + + // Test result storage (key is dram_key (port, dimm, rank, nibble), data is a map of vrefs to the margins found so far) + std::map> l_results; + + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + // Insert an empty map into l_results for each DRAM + const dram_key l_dram = {l_port_index, l_dimm_index, l_rank_index, l_nibble_index}; + l_results.insert(std::make_pair(l_dram, std::map())); + } + } + } + } + + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_MASTER_RANKS_PER_DIMM, i_target_mba, num_ranks_per_dimm)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_VPD_DRAM_WRDDR4_VREF, i_target_mba, l_vpd_wr_vref_value)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_VREF_CAL_DELTA_FROM_NOMINAL, i_target_mba, l_starting_ticks_from_nominal)); + FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba, l_dimm_type)); + + FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_EFF_SCHMOO_TEST_VALID, i_target_mba, l_attr_shmoo_test_type_u8)); + FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_MCBIST_END_ADDR, i_target_mba, l_mcbist_address_config)); + + // Make sure we do the MCBIST setup the fist time through so we get our custom MCBIST end address + FAPI_TRY(reset_attribute(i_target_mba)); + + l_max_nibbles = (l_dimm_type == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? MAX_DRAMS_PER_RANK_X4 : + MAX_DRAMS_PER_RANK_X4_ISDIMM; + + // Make sure our initial settings are ok + l_nominal_vref = l_vpd_wr_vref_value[0]; + l_max_delta = std::min((MAX_VREF - l_nominal_vref), l_nominal_vref); + FAPI_ASSERT((l_starting_ticks_from_nominal > 0) && + (l_nominal_vref - l_starting_ticks_from_nominal >= 0) && + (l_nominal_vref + l_starting_ticks_from_nominal <= MAX_VREF), + fapi2::CEN_MSS_WR_VREF_CAL_DELTA_INVALID_VALUE(). + set_MBA_TARGET(i_target_mba). + set_DELTA_VALUE(l_starting_ticks_from_nominal). + set_NOMINAL_VREF(l_nominal_vref). + set_DELTA_MAX(l_max_delta), + "%s Invalid value supplied for ATTR_CEN_MSS_VREF_CAL_DELTA_FROM_NOMINAL (%d). Nominal Vref is %d, so max delta is %d", + mss::c_str(i_target_mba), + l_starting_ticks_from_nominal, + l_nominal_vref, + l_max_delta); + + // Initial settings: start at the given delta from nominal + l_min_vref = l_nominal_vref - l_starting_ticks_from_nominal; + l_max_vref = l_nominal_vref + l_starting_ticks_from_nominal; + + std::fill(&l_low_bound[0][0][0][0], + &l_low_bound[0][0][0][0] + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM * l_max_nibbles), + l_min_vref); + std::fill(&l_high_bound[0][0][0][0], + &l_high_bound[0][0][0][0] + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM * l_max_nibbles), + l_max_vref); + + // Continue until we've converged the search range to the minumum boundary size + do + { + // Start with search complete true so &= operator switches us to false if any DRAM is not complete + l_search_complete = true; + + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + // The helper function will do the following: + // 1) Step min or max boundary inward (if both high and low results are available) + // 2) Give us the next Vref to test + // 3) Tell us if a given DRAM's search is complete + // Note: searches on all DRAMs may not complete at the same time due to skipping tests we've already done + bool l_dram_complete = false; + + const dram_key l_dram = {l_port_index, l_dimm_index, l_rank_index, l_nibble_index}; + FAPI_TRY(ternary_search_helper(l_results[l_dram], + l_low_bound[l_port_index][l_dimm_index][l_rank_index][l_nibble_index], + l_high_bound[l_port_index][l_dimm_index][l_rank_index][l_nibble_index], + l_test_vref[l_port_index][l_dimm_index][l_rank_index][l_nibble_index], + l_dram_complete)); + l_search_complete &= l_dram_complete; + } + } + } + } + + // Check margins at next test point + FAPI_TRY(wr_vref_test_helper(i_target_mba, l_test_vref, l_pda_table)); + + // Print them and store them in our results map + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + FAPI_INF("%s Port %d Dimm %d Rank:%d Nibble:%d Vref:%d margin=%d", + mss::c_str(i_target_mba), l_port_index, l_dimm_index, l_rank_index, l_nibble_index, + l_test_vref[l_port_index][l_dimm_index][l_rank_index][l_nibble_index], + l_pda_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); + + const dram_key l_dram = {l_port_index, l_dimm_index, l_rank_index, l_nibble_index}; + auto l_result = std::make_pair(l_test_vref[l_port_index][l_dimm_index][l_rank_index][l_nibble_index], + l_pda_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); + l_results[l_dram].insert(l_result); + } + } + } + } + + // Turn the attribute back on so we don't do the MCBIST setup after the fist time through + FAPI_TRY(set_attribute(i_target_mba)); + } + while (!l_search_complete); + + // Now that we've converged to the minimal search range, set the best vref for each nibble + for(uint8_t l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) + { + for(uint8_t l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) + { + for(uint8_t l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) + { + for(uint8_t l_nibble_index = 0; l_nibble_index < l_max_nibbles; l_nibble_index++) + { + // The best vref is set in the next test point, so copy that into the table + io_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0] = + l_test_vref[l_port_index][l_dimm_index][l_rank_index][l_nibble_index]; + + // Rather than running another shmoo, copy the margins in from the last shmoo + // which was tested at either the best vref or one tick away from it + io_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1] = + l_pda_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]; + } + } + } + } + + FAPI_TRY(set_wr_vref_by_dram(i_target_mba, io_pda_nibble_table)); + + return fapi2::FAPI2_RC_SUCCESS; + +fapi_try_exit: + return fapi2::current_err; +} + /// /// @brief perform binary wr vref shmoo on DDR4 dimms /// @param[in] i_target_mba Centaur input MBA @@ -791,16 +1241,14 @@ fapi2::ReturnCode wr_vref_shmoo_ddr4_bin(const fapi2::Target pda; - pda.clear(); uint32_t index_mul_print = 650; uint8_t l_attr_shmoo_test_type_u8 = 1; uint32_t vref_val_print = 0; uint8_t vpd_wr_vref_value[2] = {0}; const auto l_target_centaur1 = i_target_mba.getParent(); + uint32_t l_max_nibbles = 0; FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_DRAM_WIDTH, i_target_mba, dram_width)); FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba, l_attr_eff_dimm_type_u8)); @@ -834,6 +1281,10 @@ fapi2::ReturnCode wr_vref_shmoo_ddr4_bin(const fapi2::Target SET FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBSPAMSKQ, l_data_buffer_64)); @@ -922,10 +1373,38 @@ fapi2::ReturnCode wr_vref_shmoo_ddr4_bin(const fapi2::Target= imin) { + uint8_t l_vref_values[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM] = {0}; if(l_loop_count == 0) { @@ -963,62 +1443,12 @@ fapi2::ReturnCode wr_vref_shmoo_ddr4_bin(const fapi2::Target(); - FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < MAX_RANKS_PER_DIMM; l_rank_index++) - { - - vrefdq_train_enable[l_port_index][l_dimm_index][l_rank_index] = 0x01; - - } - } - } - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_RANGE, i_target_mba, vrefdq_train_range)); - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, vrefdq_train_enable)); - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) //Port - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) //Max dimms - { - for(l_rank_index = 0; l_rank_index < MAX_RANKS_PER_DIMM; l_rank_index++) //Ranks - { - - vrefdq_train_value[l_port_index][l_dimm_index][l_rank_index] = vref_val; - - } - } - } - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, i_target_mba, vrefdq_train_value)); - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - // Call it Twice to Latch (Steve) - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < MAX_RANKS_PER_DIMM; l_rank_index++) - { - vrefdq_train_enable[l_port_index][l_dimm_index][l_rank_index] = 0x00; - } - } - } - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, vrefdq_train_enable)); - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - - FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); - l_data_buffer_64.setBit(); - FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); + std::fill(&l_vref_values[0][0][0], + &l_vref_values[0][0][0] + (MAX_PORTS_PER_MBA * MAX_DIMM_PER_PORT * MAX_RANKS_PER_DIMM), + vref_val); + FAPI_TRY(latch_wr_vref(i_target_mba, l_vref_values)); FAPI_TRY(delay_shmoo_ddr4_pda(i_target_mba, l_port, l_shmoo_type_valid, &l_left_margin, &l_right_margin, vref_val, pda_nibble_table)); @@ -1050,7 +1480,7 @@ fapi2::ReturnCode wr_vref_shmoo_ddr4_bin(const fapi2::Target(); FAPI_TRY(fapi2::putScom( i_target_mba, CEN_MBA_MBAREF0Q, refresh_reg)); - if(cal_control == 2) + if(cal_control == fapi2::ENUM_ATTR_CEN_MSS_VREF_CAL_CNTL_P8_RANK) { FAPI_INF("%s CAL_CONTROL in RANK_Wise Mode!! ", mss::c_str(i_target_mba)); - FAPI_TRY(fapi2::getScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); - l_data_buffer_64.clearBit(); - FAPI_TRY(fapi2::putScom(i_target_mba, CEN_MBA_MBAREF0Q, l_data_buffer_64)); - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < MAX_RANKS_PER_DIMM; l_rank_index++) - { - - vrefdq_train_enable[l_port_index][l_dimm_index][l_rank_index] = 0x01; - - } - } - } - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, vrefdq_train_enable)); - //Calculate the Average V-Ref Value - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(uint8_t i_dimm = 0; i_dimm < MAX_DIMM_PER_PORT; i_dimm++) - { - for(l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][i_dimm]; l_rank_index++) - { - for(l_nibble_index = 0; l_nibble_index < DATA_BYTES_PER_PORT * MAX_NIBBLES_PER_BYTE; l_nibble_index++) - { - - avg_best_vref = best_pda_nibble_table[l_port_index][i_dimm][l_rank_index][l_nibble_index][0] + avg_best_vref; - } - - avg_best_vref = avg_best_vref / (DATA_BYTES_PER_PORT * MAX_NIBBLES_PER_BYTE); - FAPI_INF("++ RANK_Wise ++++ Best Avg V-Ref = %d !! ", avg_best_vref); - vrefdq_train_value[l_port_index][i_dimm][l_rank_index] = avg_best_vref; - - } //End of Rank Loop - } //end of dimm loop - } //End of Port Loop - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, i_target_mba, vrefdq_train_value)); - - //issue call to run_pda (entering into train mode) - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - - // Call it Twice to Latch (Steve) - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < MAX_RANKS_PER_DIMM; l_rank_index++) - { - - vrefdq_train_enable[l_port_index][l_dimm_index][l_rank_index] = 0x00; - - } - } - } - - FAPI_TRY(FAPI_ATTR_SET( fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, i_target_mba, vrefdq_train_enable)); - FAPI_TRY(p9c_mss_mrs6_DDR4(i_target_mba), "mrs_load failed"); - - } //end of RANK wise if + FAPI_TRY(set_wr_vref_by_rank(i_target_mba, best_pda_nibble_table)); + } else { - //1 - Issue PDA commands with enable train enable 1 for all DRAMs with good VREF values - uint32_t max_vref = 0; - uint8_t dram_num = 0; - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) - { - for(l_nibble_index = 0; l_nibble_index < (DATA_BYTES_PER_PORT * MAX_NIBBLES_PER_BYTE); l_nibble_index++) - { - FAPI_INF("%s Port %d Dimm %d Rank:%d Pda_Nibble: %d V-ref:%d Margin:%d", mss::c_str(i_target_mba), l_port_index, - l_dimm_index, l_rank_index, - l_nibble_index, - best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0], - best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); - - //if x8, averages the two nibbles together and, regardless, converts the DRAM over to the nibble - dram_num = l_nibble_index; - max_vref = best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; - - if(dram_width == fapi2::ENUM_ATTR_CEN_EFF_DRAM_WIDTH_X8) - { - l_nibble_index++; - dram_num = dram_num / MAX_NIBBLES_PER_BYTE; - max_vref += best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; - max_vref = max_vref / MAX_NIBBLES_PER_BYTE; - } - - FAPI_INF("%s Port %d Dimm %d Rank:%d Pda_Nibble: %d DRAM_num %d V-ref:%d Margin:%d", mss::c_str(i_target_mba), - l_port_index, l_dimm_index, - l_rank_index, l_nibble_index, - dram_num, best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0], - best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); - - pda.push_back(PDA_MRS_Storage(0x01, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, dram_num, l_dimm_index, l_rank_index, - l_port_index)); - FAPI_INF("%s PDA STRING: %d %s", mss::c_str(i_target_mba), pda.size() - 1, pda[pda.size() - 1].c_str()); - pda.push_back(PDA_MRS_Storage(max_vref, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, dram_num, l_dimm_index, l_rank_index, - l_port_index)); - FAPI_INF("%s PDA STRING: %d %s", mss::c_str(i_target_mba), pda.size() - 1, pda[pda.size() - 1].c_str()); - } - - - } //End of Rank Loop - } //end of dimm loop - } //End of Port Loop - - FAPI_INF("%s RUNNING PDA FOR 1ST TIME", mss::c_str(i_target_mba)); - FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda)); - FAPI_INF("%s FINISHED RUNNING PDA FOR 1ST TIME", mss::c_str(i_target_mba)); - - //issue call to run PDA again (latching good value in train mode) - FAPI_INF("%s RUNNING PDA FOR 2ND TIME", mss::c_str(i_target_mba)); - FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda)); - FAPI_INF("%s FINISHED RUNNING PDA FOR 2ND TIME", mss::c_str(i_target_mba)); - //clear the PDA vector - pda.clear(); - - //build PDA vector with good VREF values and train enable DISABLED - - for(l_port_index = 0; l_port_index < MAX_PORTS_PER_MBA; l_port_index++) - { - for(l_dimm_index = 0; l_dimm_index < MAX_DIMM_PER_PORT; l_dimm_index++) - { - for(l_rank_index = 0; l_rank_index < num_ranks_per_dimm[l_port_index][l_dimm_index]; l_rank_index++) - { - for(l_nibble_index = 0; l_nibble_index < (DATA_BYTES_PER_PORT * MAX_NIBBLES_PER_BYTE); l_nibble_index++) - { - //if x8, averages the two nibbles together and, regardless, converts the DRAM over to the nibble - dram_num = l_nibble_index; - max_vref = best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; - - if(dram_width == fapi2::ENUM_ATTR_CEN_EFF_DRAM_WIDTH_X8) - { - l_nibble_index++; - dram_num = dram_num / MAX_NIBBLES_PER_BYTE; - max_vref += best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0]; - max_vref = max_vref / MAX_NIBBLES_PER_BYTE; - } - - FAPI_INF("\n Port %d Dimm %d Rank:%d Pda_Nibble: %d DRAM_num %d V-ref:%d Margin:%d", l_port_index, l_dimm_index, - l_rank_index, l_nibble_index, - dram_num, best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][0], - best_pda_nibble_table[l_port_index][l_dimm_index][l_rank_index][l_nibble_index][1]); - - pda.push_back(PDA_MRS_Storage(0x00, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_ENABLE, dram_num, l_dimm_index, l_rank_index, - l_port_index)); - FAPI_INF("%s PDA STRING: %d %s", mss::c_str(i_target_mba), pda.size() - 1, pda[pda.size() - 1].c_str()); - pda.push_back(PDA_MRS_Storage(max_vref, fapi2::ATTR_CEN_EFF_VREF_DQ_TRAIN_VALUE, dram_num, l_dimm_index, l_rank_index, - l_port_index)); - FAPI_INF("%s PDA STRING: %d %s", mss::c_str(i_target_mba), pda.size() - 1, pda[pda.size() - 1].c_str()); - } - } //End of Rank Loop - } //end of dimm loop - } //End of Port Loop - - FAPI_INF("%s RUNNING PDA FOR 3RD TIME", mss::c_str(i_target_mba)); - //issue call to PDA command - FAPI_TRY(mss_ddr4_run_pda((fapi2::Target&)i_target_mba, pda)); - FAPI_INF("%s FINISHED RUNNING PDA FOR 3RD TIME", mss::c_str(i_target_mba)); - } //End of Else + FAPI_TRY(set_wr_vref_by_dram(i_target_mba, best_pda_nibble_table)); + } //turn on refresh then exit FAPI_TRY(fapi2::getScom( i_target_mba, CEN_MBA_MBAREF0Q, refresh_reg)); refresh_reg.setBit(); FAPI_TRY(fapi2::putScom( i_target_mba, CEN_MBA_MBAREF0Q, refresh_reg)); - } // end of if + } // end of P8 algorithms else //Skipping Shmoos ... Writing VPD data directly @@ -1510,14 +1776,15 @@ fapi_try_exit: /// @param[out] o_left_margin returns left margin delay (setup) in ps /// @param[out] o_right_margin returns right margin delay (hold) in ps /// @param[in] i_shmoo_param Shmoo -/// @param[in] i_pda_nibble_table +/// @param[in] i_pda_nibble_table table of Vref values and measured margins (hence the last [2] array parameter) /// @return FAPI2_RC_SUCCESS iff successful /// fapi2::ReturnCode delay_shmoo_ddr4_pda(const fapi2::Target& i_target_mba, uint8_t i_port, shmoo_type_t i_shmoo_type_valid, uint32_t* o_left_margin, uint32_t* o_right_margin, - uint32_t i_shmoo_param, uint32_t i_pda_nibble_table[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][16][2]) + uint32_t i_shmoo_param, + uint32_t (&i_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) { //need to use fapi allocator to avoid memory fragmentation issues in Hostboot diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.H b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.H index 5c30fcfd8..5d1eb9b17 100755 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.H +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_draminit_training_advanced.H @@ -5,7 +5,7 @@ /* */ /* OpenPOWER HostBoot Project */ /* */ -/* Contributors Listed Below - COPYRIGHT 2016,2017 */ +/* Contributors Listed Below - COPYRIGHT 2016,2018 */ /* [+] International Business Machines Corp. */ /* */ /* */ @@ -40,6 +40,7 @@ // Includes //---------------------------------------------------------------------- #include +#include #include typedef fapi2::ReturnCode (*p9c_mss_draminit_training_advanced_FP_t)(const fapi2::Target&); @@ -57,6 +58,40 @@ enum shmoo_param RCV_IMP = 0x20 }; +/// @brief Struct to contain WR_VREF shmoo test data map keys +struct dram_key +{ + uint8_t port; + uint8_t dimm; + uint8_t rank; + uint8_t nibble; + + /// + /// @brief dram_key less than operator (for use in std::map) + /// @param[in] k the dram_key to compare against + /// @return true if this dram_key is less than the given one, false otherwise + /// + bool operator<(const dram_key& k) const + { + if (port != k.port) + { + return (port < k.port); + } + + if (dimm != k.dimm) + { + return (dimm < k.dimm); + } + + if (rank != k.rank) + { + return (rank < k.rank); + } + + return (nibble < k.nibble); + } +}; + extern "C" { /// @@ -151,13 +186,16 @@ extern "C" /// @param[out] o_left_margin returns left margin delay (setup) in ps /// @param[out] o_right_margin returns right margin delay (hold) in ps /// @param[in] i_shmoo_param Shmoo - /// @param[in] i_pda_nibble_table + /// @param[in] i_pda_nibble_table table of Vref values and measured margins (hence the last [2] array parameter) /// @return FAPI2_RC_SUCCESS iff successful /// - fapi2::ReturnCode delay_shmoo_ddr4_pda(const fapi2::Target& i_target_mba, const uint8_t i_port, + fapi2::ReturnCode delay_shmoo_ddr4_pda(const fapi2::Target& i_target_mba, + const uint8_t i_port, const shmoo_type_t i_shmoo_type_valid, - uint32_t* o_left_margin, uint32_t* o_right_margin, - const uint32_t i_shmoo_param, uint32_t i_pda_nibble_table[2][2][4][16][2]); + uint32_t* o_left_margin, + uint32_t* o_right_margin, + const uint32_t i_shmoo_param, + uint32_t (&i_pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]); /// /// @brief Finds better timing margin and returns the index /// @param[in] i_shmoo_param_valid PARAM_NONE, DRV_IMP, SLEW_RATE, WR_VREF, RD_VREF, RCV_IMP diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.C b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.C index b67133fc3..4baaa528b 100755 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.C +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.C @@ -4436,7 +4436,7 @@ extern "C" /// @return FAPI2_RC_SUCCESS iff successful /// fapi2::ReturnCode generic_shmoo::get_nibble_pda(const fapi2::Target& i_target, - uint32_t pda_nibble_table[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][16][2]) + uint32_t (&pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]) { uint8_t l_dimm = 0; uint8_t num_ranks_per_dimm[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0}; @@ -4450,7 +4450,7 @@ extern "C" { for(uint8_t l_dq = 0; l_dq < 4; l_dq++) { - for(uint8_t l_n = 0; l_n < 16; l_n++) + for(uint8_t l_n = 0; l_n < (iv_MAX_BYTES * MAX_NIBBLES_PER_BYTE); l_n++) { pda_nibble_table[l_p][l_dimm][l_rnk][l_n][0] = iv_vref_mul; @@ -4841,3 +4841,68 @@ extern "C" } }//Extern C + +/// +/// @brief helper function for testing ternary search +/// @param[in] i_results map of test results that have been found so far +/// @param[in,out] io_low_bound lower search boundary's vref value +/// @param[in,out] io_high_bound higher search boundary's vref value +/// @param[out] o_test_vref next Vref value to test +/// @param[out] o_complete true if the search is complete, false otherwise +/// @return FAPI2_RC_SUCCESS if no errors encountered +/// +fapi2::ReturnCode ternary_search_helper(const std::map& i_results, + uint8_t& io_low_bound, + uint8_t& io_high_bound, + uint8_t& o_test_vref, + bool& o_complete) +{ + // This value is for termination of the search algorithm. + // When the search range gets below this, the answer is the average of the high/low bounds + constexpr uint8_t MIN_BOUNDS = 3; + + while(true) + { + // First calculate the next step value (how much to move bound on end with lowest margin) + const uint32_t l_step = (io_high_bound - io_low_bound) / 3; + + // See if we've already got results for each test point + auto l_low_margin_it = i_results.find(io_low_bound + l_step); + auto l_high_margin_it = i_results.find(io_high_bound - l_step); + + // If we don't have results for the next test points, return the next value to test + if (l_low_margin_it == i_results.end()) + { + o_test_vref = io_low_bound + l_step; + o_complete = false; + return fapi2::FAPI2_RC_SUCCESS; + } + + if (l_high_margin_it == i_results.end()) + { + o_test_vref = io_high_bound - l_step; + o_complete = false; + return fapi2::FAPI2_RC_SUCCESS; + } + + // Next, move in low or high bound, depending on which test point had the lowest margin + if (l_low_margin_it->second < l_high_margin_it->second) + { + io_low_bound += l_step; + } + else + { + io_high_bound -= l_step; + } + + // If we're at our minimum search range, return the average of the bounds + if ((io_high_bound - io_low_bound) < MIN_BOUNDS) + { + o_test_vref = (io_high_bound + io_low_bound) / 2; + o_complete = true; + return fapi2::FAPI2_RC_SUCCESS; + } + + // If not, loop around and continue the search + } +} diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.H b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.H index 8430bc2df..c4708b3c2 100755 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.H +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_generic_shmoo.H @@ -44,6 +44,7 @@ #define SHMOO_DEBUG2 0 #include #include +#include /// /// @brief MSS Generic Shmoo Class.. Inherits from PHY access class and the knob abstraction @@ -414,7 +415,7 @@ class generic_shmoo /// @return FAPI2_RC_SUCCESS iff successful /// fapi2::ReturnCode get_nibble_pda(const fapi2::Target& i_target, - uint32_t pda_nibble_table[2][2][4][16][2]); + uint32_t (&pda_nibble_table)[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT][MAX_RANKS_PER_DIMM][MAX_DRAMS_PER_RANK_X4][2]); /// /// @brief used to find right and left bound @@ -439,4 +440,22 @@ class generic_shmoo fapi2::ReturnCode print_report2(const fapi2::Target& i_target); }; + +// Below are various training_advanced helper functions, placed here so they can be unit tested in p9c_mss_generic_shmoo_ut.C + +/// +/// @brief helper function for testing ternary search +/// @param[in] i_results map of test results that have been found so far +/// @param[in,out] io_low_bound lower search boundary's vref value +/// @param[in,out] io_high_bound higher search boundary's vref value +/// @param[out] o_test_vref next Vref value to test +/// @param[out] o_complete true if the search is complete, false otherwise +/// @return FAPI2_RC_SUCCESS if no errors encountered +/// +fapi2::ReturnCode ternary_search_helper(const std::map& i_results, + uint8_t& io_low_bound, + uint8_t& io_high_bound, + uint8_t& o_test_vref, + bool& o_complete); + #endif diff --git a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_mcbist_common.C b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_mcbist_common.C index e55a8a4a1..bfcd583c5 100644 --- a/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_mcbist_common.C +++ b/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_mcbist_common.C @@ -1783,7 +1783,7 @@ extern "C" uint8_t l_attr_eff_dimm_type_u8 = 0; uint8_t l_dqBitmap[DIMM_DQ_RANK_BITMAP_SIZE] = {0}; uint8_t l_dq[DATA_BYTES_PER_PORT] = { 0 }; - uint8_t l_sp[SP_BYTES_PER_PORT] = { 0 }; + uint8_t l_sp[SP_ECC_BYTES_PER_PORT] = { 0 }; uint16_t l_index0 = 0; uint8_t l_index_sp = 0; uint16_t l_sp_isdimm = 0xff; diff --git a/src/import/chips/centaur/procedures/xml/attribute_info/memory_attributes.xml b/src/import/chips/centaur/procedures/xml/attribute_info/memory_attributes.xml index 77b49ddaa..d532c13e9 100644 --- a/src/import/chips/centaur/procedures/xml/attribute_info/memory_attributes.xml +++ b/src/import/chips/centaur/procedures/xml/attribute_info/memory_attributes.xml @@ -121,11 +121,11 @@ Set by: PLL settings written by Dave Cadigan ATTR_CEN_MSS_VREF_CAL_CNTL TARGET_TYPE_MEMBUF_CHIP - Training Control over IPL - ENUM - 0x00=DISABLE /Skip V-ref Train; 0x01=DRAM - Enable V-Ref Train DRAM Level; 0x02=RANK Level Training; 0x03=Box shmoo; + Training Control over IPL - ENUM - 0x00=DISABLE /Skip V-ref Train; 0x01=P8_DRAM - Enable V-Ref Train DRAM Level (P8 algorithm); 0x02=P8_RANK Level Training (P8 algorithm); 0x03=Box shmoo; 0x04=Ternary shmoo Default Value = 0x03 for box shmoo on all platforms uint8 - DISABLE = 0, DRAM = 1, RANK = 2, BOX = 3 + DISABLE = 0, P8_DRAM = 1, P8_RANK = 2, BOX = 3, TERNARY = 4 3 @@ -133,6 +133,20 @@ Set by: PLL settings written by Dave Cadigan + +ATTR_CEN_MSS_VREF_CAL_DELTA_FROM_NOMINAL + TARGET_TYPE_MBA + Controls search boundaries for WR_VREF Ternary shmoo. Value is a delta of register ticks from nominal + Default Value = 0x08 (taken from characterization values), Max value = 0x19 + + uint8 + 0x08 + + + + + + - + @@ -101,4 +101,20 @@ + + RC_CEN_MSS_WR_VREF_CAL_DELTA_INVALID_VALUE + + The attribute ATTR_CEN_MSS_VREF_CAL_DELTA_FROM_NOMINAL was overridden + with an invalid value + + MBA_TARGET + DELTA_VALUE + NOMINAL_VREF + DELTA_MAX + + CODE + HIGH + + + -- cgit v1.2.1