diff options
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c')
-rw-r--r-- | ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c | 9860 |
1 files changed, 9860 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c new file mode 100644 index 0000000..4086802 --- /dev/null +++ b/ReferenceCode/Chipset/SystemAgent/MemoryInit/Pei/Source/Services/MrcCrosser.c @@ -0,0 +1,9860 @@ +/** @file + These functions implement the crosser training algorithm. + +@copyright + Copyright (c) 1999 - 2013 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + This file contains an 'Intel Peripheral Driver' and uniquely + identified as "Intel Reference Module" and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement. + +**/ + +// +// Include files +// +#include "MrcCrosser.h" +#if SUPPORT_SODIMM == SUPPORT +#include "MrcSpdProcessing.h" +#endif //SUPPORT_SODIMM == SUPPORT + +/// +/// Module Definitions +/// +#define BIT_TX_XTALK_SWEEP_START (-3) +#define BIT_TX_XTALK_SWEEP_STOP (3) +#define BIT_TX_XTALK_SWEEP_LEN (BIT_TX_XTALK_SWEEP_STOP - BIT_TX_XTALK_SWEEP_START + 1) +#define BIT_TX_XTALK_RANGE (16) + +#define DIMM_ODT_DIMM_MASK_SHIFT (4) + +/// +/// Power optimizations Global Parameters +/// +#define OPT_PARAM_LOOP_COUNT (15) +#define OPT_PARAM_1D_LC (15) + +/// +/// UPM/PWR increment value if margins are at or below the retrain limit. +/// +#define MRC_UPM_PWR_INC_VAL (40) + +// +// Module Globals +// +const MrcUpmPwrRetrainLimits InitialLimits[MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS] = { + // + // UPM, PWR Retrain + // + {RdT, {240, 320, 90 }}, + {WrT, {220, 280, 90 }}, + {RdV, {400, 520, 160}}, + // For ULT DDR3L rcF the values are increased by 20 ticks, see UpmPwrLimitValue() + {WrV, {400, 520, 160}}, + {RdFan2, {240, 480, 0 }}, + {WrFan2, {240, 480, 0 }}, + {RdFan3, {240*4/5, 480*4/5, 0 }}, + {WrFan3, {240*4/5, 480*4/5, 0 }}, + // {650ps,750ps} * 64 pi ticks * 2 (for width) = 134 PI Ticks. ~1.3nsec for UPM,~1.5nsec for PWR + // We must subtract out the built in margin of 96 when shifting IO Lat. + // Margin function works in steps of 2, so we divide the margin by 2. + // Margin numbers are scaled by 10. + {RcvEnaX, {(((134 - 96) / 2) * 10), (((154 - 96) / 2) * 10), 0}} +}; + +const U8 ActualDimmOdt[6] = { 0, 120, 60, 40, 30, 20 }; + +#ifdef MRC_DEBUG_PRINT +const char *TOptParamOffsetString[] = { + "OptWrDS", + "OptRdOdt", + "OptSComp", + "OptTComp", + "OptTxEq", + "OptRxEq", + "OptRxBias", + "OptDimmOdt", + "OptDimmOdtWr", + "OptDimmRon", + "OptDefault" +}; + +const char *MarginTypesString[] = { + "RcvEna", + "RdT", + "WrT", + "WrDqsT", + "RdV", + "WrV", + "WrLevel", + "WrTBox", + "WrTBit", + "RdTBit", + "RdVBit", + "RcvEnaX", + "CmdT", + "CmdV" +}; + +/// +/// These strings match the OptResultPerByteDbgStr enum for indexing +/// the switch PrintCalcResultTableCh and PrintODTResultTable. +/// +const char *OptResultDbgStrings[] = { + "Best", + "GrdBnd", + "OffSel", + "Scale", + "Signal", + "Noise", + "Ratio", + "MaxPost", + "MinPost", + "Ticks", + "SNRTot." +}; + +#endif // MRC_DEBUG_PRINT + +/** + This function implements Sense Amp Offset training. + SenseAmp/ODT offset cancellation + Find the best "average" point for Vref Control + Test Vref point with SampOffset=-7 and Test Vref Point with SampOffset=+7 + Find Vref on per ch/byte basis where -7 samples all 1 and +7 samples all 0 + + @param[in,out] MrcData - Include all MRC global data. + + @retval MrcStatus - if it succeded return mrcSuccess +**/ +MrcStatus +MrcSenseAmpOffsetTraining ( + IN OUT MrcParameters *const MrcData + ) +{ + const MrcInput *Inputs; + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + MrcStatus Status; + U32 Offset; + S8 sumBits[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + S8 FirstBestPoint[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + S8 LastBestPoint[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + // + // additional bit for DQS per each byte + // + S8 firstZero[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS_FOR_OFFSET_TRAINING]; + // + // additional bit for DQS per each byte + // + S8 lastOne[MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_BITS_FOR_OFFSET_TRAINING]; + S8 sampOffset; + S8 vref; + S8 VrefWidth; + U8 HighMask[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + U8 LowMask[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + U8 Channel; + U8 Rank; + U8 byte; + U8 bit; + U8 MaxBits; + BOOL Lpddr; + DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0; + DDRDATA0CH0_CR_DDRCRDATACONTROL2_STRUCT DdrCrDataControl2; + DDRDATA0CH0_CR_DATATRAINFEEDBACK_STRUCT DataTrainFeedback; + DDRDATA_CR_RXOFFSETVDQ_STRUCT RxOffsetVdq; + + Inputs = &MrcData->SysIn.Inputs; + Debug = &Inputs->Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Status = mrcSuccess; + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); + + // + // Init LastBestPoint to 0, FirstBestPoint to -8, LowMask to 0xff and HighMask to 0 + // + MrcOemMemorySet ((U8 *) LastBestPoint, 0, sizeof (LastBestPoint)); + MrcOemMemorySet ((U8 *) FirstBestPoint, (U32) (-8), sizeof (FirstBestPoint)); + MrcOemMemorySet ((U8 *) LowMask, (U32) (-1), sizeof (LowMask)); + MrcOemMemorySet ((U8 *) HighMask, 0, sizeof (LowMask)); + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Stage 1: Vref Offset Training\nPlot Of SumOfBits across Vref settings\nChannel\t0 1\nByte\t" + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + (Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? "0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 7 8" : "0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7" + ); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + // + // Force RXAmp and Bias on -MUST use Per byte as preious DqControl2 values depended on byte number + // + for (byte = 0; byte < Outputs->SdramCount; byte++) { + DdrCrDataControl2.Data = ChannelOut->DqControl2[byte].Data; + DdrCrDataControl2.Bits.ForceBiasOn = 1; + DdrCrDataControl2.Bits.ForceRxOn = 1; + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * byte); + MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data); + } + } + } + // + // Sweep through vref settings and find point SampOffset of +/- 7 passes + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n1/2 Vref"); + for (vref = -8; vref <= 8; vref++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", vref); + + // + // Run Test and Record Results. + // + Status = ChangeMargin (MrcData, RdV, vref, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileRank); + + // + // Program settings for Vref and SampOffset = 7 + // + MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, 0xFFFFFFFF); // (8+7) + // + // To run test, enable Offset Cancel mode and Enable ODT + // Check Results and Update variables. Ideal result is all 0 + // Clear Offset Cancel mode at end test to enable writing RX_OffsetV + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + + // + // Propagate delay values (without a read command) + // + DdrCrDataControl0.Data = ChannelOut->DqControl0.Data; + DdrCrDataControl0.Bits.ReadRFRd = 1; + DdrCrDataControl0.Bits.ReadRFWr = 0; + DdrCrDataControl0.Bits.ReadRFRank = 0; + DdrCrDataControl0.Bits.ForceOdtOn = 1; + DdrCrDataControl0.Bits.SenseampTrainingMode = 1; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + + MrcWait (MrcData, HPET_1US); + + for (byte = 0; byte < Outputs->SdramCount; byte++) { + Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG + + ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) + + ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * byte); + DataTrainFeedback.Data = (U8) MrcReadCR (MrcData, Offset); + sumBits[Channel][byte] = -(MrcCountBitsEqOne (DataTrainFeedback.Bits.DataTrainFeedback)); + LowMask[Channel][byte] &= (U8) DataTrainFeedback.Bits.DataTrainFeedback; + } + + DdrCrDataControl0.Bits.SenseampTrainingMode = 0; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + } + } + // + // Program settings for SampOffset = -7 + // + MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, 0x11111111); // (8-7) + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(MrcChannelExist (Outputs, Channel))) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + (Channel != 0) ? "" : ((Outputs->SdramCount == MAX_SDRAM_IN_DIMM) ? " " : " ") + ); + } else { + ChannelOut = &ControllerOut->Channel[Channel]; + + // + // Propagate delay values (without a read command) + // + DdrCrDataControl0.Data = ChannelOut->DqControl0.Data; + DdrCrDataControl0.Bits.ReadRFRd = 1; + DdrCrDataControl0.Bits.ReadRFWr = 0; + DdrCrDataControl0.Bits.ReadRFRank = 0; + DdrCrDataControl0.Bits.ForceOdtOn = 1; + DdrCrDataControl0.Bits.SenseampTrainingMode = 1; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + + MrcWait (MrcData, HPET_1US); + for (byte = 0; byte < Outputs->SdramCount; byte++) { + Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG + + ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) + + ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * byte); + DataTrainFeedback.Data = (U8) MrcReadCR (MrcData, Offset); + sumBits[Channel][byte] += MrcCountBitsEqOne (DataTrainFeedback.Bits.DataTrainFeedback); + HighMask[Channel][byte] |= DataTrainFeedback.Bits.DataTrainFeedback; + + // + // Check if this point is better + // + if (sumBits[Channel][byte] > FirstBestPoint[Channel][byte]) { + FirstBestPoint[Channel][byte] = sumBits[Channel][byte]; + LastBestPoint[Channel][byte] = vref; + ChannelOut->RxVref[byte] = vref; + } else if (sumBits[Channel][byte] == FirstBestPoint[Channel][byte]) { + LastBestPoint[Channel][byte] = vref; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", sumBits[Channel][byte]); + } + + DdrCrDataControl0.Bits.SenseampTrainingMode = 0; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + } + } + } + +#ifdef MRC_DEBUG_PRINT + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\nHi-Lo\t"); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + for (byte = 0; byte < Outputs->SdramCount; byte++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "0x%x 0x%x ", HighMask[Channel][byte], LowMask[Channel][byte]); + // + // Exit with error if any bit did not change + // + if ((HighMask[Channel][byte] ^ LowMask[Channel][byte]) != 0xFF) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "\nERROR! At least one bit with unexpected results for Channel %u Byte %u\n", + Channel, + byte + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "A '0' in the following BitMask value represents the failing Bit(s) 0x%x\n", + (HighMask[Channel][byte] ^ LowMask[Channel][byte]) + ); + return mrcSenseAmpErr; + } + } + } + } + + // + // Display the selected Read Vref per byte + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nRdVref\t"); +#endif // MRC_DEBUG_PRINT + // + // Clear RdV offset + // + Status = ChangeMargin (MrcData, RdV, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileRank); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + // + // Upate RxVref delay center + // + for (byte = 0; byte < Outputs->SdramCount; byte++) { + VrefWidth = (S8) (LastBestPoint[Channel][byte] - ChannelOut->RxVref[byte]); + vref = (S8) (ChannelOut->RxVref[byte] + (VrefWidth / 2)); + + // + // Add 1 to Round Up and find the center + // + if (vref < 0) { + vref--; + } else { + vref++; + } + // + // step size for RxVref is about 7.8mv AND for RxVrefOffset is about 3.9mv + // + ChannelOut->RxVref[byte] = vref / 2; + + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if (MrcRankInChannelExist (MrcData, Rank, Channel)) { + UpdateRxT (MrcData, Channel, Rank, byte, 0xFF, 0); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", (S8) ChannelOut->RxVref[byte]); + } + } + } + // + // init firstZero and lastOne to 0 + // + MrcOemMemorySet ((U8 *) firstZero, 0, sizeof (firstZero)); + MrcOemMemorySet ((U8 *) lastOne, 0, sizeof (lastOne)); + + MaxBits = MAX_BITS_FOR_OFFSET_TRAINING - 1; +#ifdef ULT_FLAG + if (Lpddr) { + MaxBits++; // for ULT offset training done for 8 bits + DQS bit + } +#endif //ULT_FLAG + +#ifdef MRC_DEBUG_PRINT + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\nStage 2: SampOffset Training\n"); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0\t\t\t\t\t\t\t\t\t %s1\nByte ", Lpddr ? "\t" : ""); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + for (byte = 0; byte < Outputs->SdramCount; byte++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d %s", byte, Lpddr ? " " : ""); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nBits "); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + for (byte = 0; byte < Outputs->SdramCount; byte++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "01234567%s ", Lpddr ? "S" : ""); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n SAmp"); +#endif // MRC_DEBUG_PRINT + + for (sampOffset = 1; sampOffset <= 15; sampOffset++) { + // + // Display per Byte Feedback from REUT Registers + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n% 5d\t", sampOffset); + + // + // Program Offset and Propagate new value from RF + // + RxOffsetVdq.Data = 0; + for (bit = 0; bit < MAX_BITS; bit++) { + RxOffsetVdq.Data += (sampOffset & DDRDATA_CR_RXOFFSETVDQ_Lane0_MSK) << (DDRDATA_CR_RXOFFSETVDQ_Lane0_WID * bit); + } + + MrcWriteCrMulticast (MrcData, DDRDATA_CR_RXOFFSETVDQ_REG, RxOffsetVdq.Data); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!MrcChannelExist (Outputs, Channel)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t%s", Lpddr ? "\t" : ""); + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; +#ifdef ULT_FLAG + if (Lpddr) { + // + // write DQS offset to control2 reg sampOffset + // + for (byte = 0; byte < Outputs->SdramCount; byte++) { + DdrCrDataControl2.Data = ChannelOut->DqControl2[byte].Data; + + DdrCrDataControl2.Bits.RxDqsAmpOffset = sampOffset; + DdrCrDataControl2.Bits.ForceBiasOn = 1; + DdrCrDataControl2.Bits.ForceRxOn = 1; + DdrCrDataControl2.Bits.LeakerComp = 0; + + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * byte); + MrcWriteCR (MrcData, Offset, DdrCrDataControl2.Data); + } + } +#endif //ULT_FLAG + // + // Propagate delay values (without a read command) + // + DdrCrDataControl0.Data = ChannelOut->DqControl0.Data; + DdrCrDataControl0.Bits.ReadRFRd = 1; + DdrCrDataControl0.Bits.ReadRFWr = 0; + DdrCrDataControl0.Bits.ReadRFRank = 0; + DdrCrDataControl0.Bits.ForceOdtOn = 1; + DdrCrDataControl0.Bits.SenseampTrainingMode = 1; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + + MrcWait (MrcData, HPET_1US); + + for (byte = 0; byte < Outputs->SdramCount; byte++) { + Offset = DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG + + ((DDRDATA0CH1_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * Channel) + + ((DDRDATA1CH0_CR_DATATRAINFEEDBACK_REG - DDRDATA0CH0_CR_DATATRAINFEEDBACK_REG) * byte); + DataTrainFeedback.Data = MrcReadCR (MrcData, Offset); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "DataTrainFeedback = 0x%x, sampOffset = %d\n", DataTrainFeedback.Data, sampOffset); + + for (bit = 0; bit < MaxBits; bit++) { + if (DataTrainFeedback.Bits.DataTrainFeedback & (MRC_BIT0 << bit)) { + lastOne[Channel][byte][bit] = sampOffset; + } else { + if (firstZero[Channel][byte][bit] == 0) { + firstZero[Channel][byte][bit] = sampOffset; + } + } + // + // Display in bits + // + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + ((MRC_BIT0 << bit) & DataTrainFeedback.Bits.DataTrainFeedback) ? "1" : "0" + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " "); + } + + DdrCrDataControl0.Bits.SenseampTrainingMode = 0; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, DdrCrDataControl0.Data); + } // for Channel + } // for sampOffset + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nBitSAmp "); + + // + // Calculate and Program Offsets and display per bit SenseAmp Offset + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + for (byte = 0; byte < Outputs->SdramCount; byte++) { + RxOffsetVdq.Data = 0; + for (bit = 0; bit < MaxBits; bit++) { + // + // Find vref center, add 1 for Round Up + // + vref = (firstZero[Channel][byte][bit] + lastOne[Channel][byte][bit]) / 2; + + // + // Check for saturation conditions + // to make sure we are as close as possible to vdd/2 (750mv) + // + if (firstZero[Channel][byte][bit] == 0) { + vref = 15; + } + + if (lastOne[Channel][byte][bit] == 0) { + vref = 0; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%X", vref); +#ifdef ULT_FLAG + if (Lpddr) { + if (bit == 8) { + // + // Write the DQS sense amp offset value to the host struct. + // It will be written to the HW at the end of this routine. + // + // Add 8 to the center value, to better suppress DQS reflections before the read preamble. + // + ChannelOut->DqControl2[byte].Bits.RxDqsAmpOffset = MIN (vref + 8, DDRDATA_CR_DDRCRDATACONTROL2_RxDqsAmpOffset_MAX); + break; + } + } +#endif // ULT_FLAG + + RxOffsetVdq.Data += (vref & DDRDATA0CH0_CR_RXOFFSETVDQ_Lane0_MSK) << (DDRDATA0CH0_CR_RXOFFSETVDQ_Lane0_WID * bit); + ChannelOut->RxDqVrefPb[0][byte][bit].Center = vref; + } + + Offset = DDRDATA0CH0_CR_RXOFFSETVDQ_REG + + ((DDRDATA0CH1_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * Channel) + + ((DDRDATA1CH0_CR_RXOFFSETVDQ_REG - DDRDATA0CH0_CR_RXOFFSETVDQ_REG) * byte); + MrcWriteCR (MrcData, Offset, RxOffsetVdq.Data); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " "); + + // + // Propagate delay values (without a read command) + // + MrcDownloadRegFile (MrcData, Channel, 1, 0, MrcRegFileRank, 0, 1, 0); + } + } +#ifdef MRC_DEBUG_PRINT + else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t%s", Lpddr ? "\t" : ""); // Line up Channel 1 + } +#endif + } + // + // Clean up after test + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + + // + // Restore DataControl2 + // + for (byte = 0; byte < Outputs->SdramCount; byte++) { + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * Channel) + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL2_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL2_REG) * byte); + MrcWriteCR (MrcData, Offset, ChannelOut->DqControl2[byte].Data); + } + // + // Restore DataControl0 + // + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + + Status = IoReset (MrcData); + + return Status; +} + +/** + This function looks at the margin values stored in the global data structure and checks + WrT, WrV, RdT, and RdV to see if they are above the minimum margin required. + + @param[in, out] MrcData - MRC global data. + + @retval mrcSuccess if margins are acceptable. + @retval Otherwise, mrcRetrain. +**/ +MrcStatus +MrcRetrainMarginCheck ( + IN OUT MrcParameters *const MrcData + ) +{ + MrcDebug const *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcUpmPwrRetrainLimits *UpmPwrRetrainLimits; + MRC_MarginTypes MarginParam; + MrcMarginResult LastResultParam; + MrcStatus Status; + MRC_MARGIN_LIMIT_TYPE MarginLimitType; + U32 (*LastMargins)[MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; + U32 BERStats[4]; + U16 MinEdgeMargin[MAX_EDGES]; + U16 RetrainMarginLimit; + U16 CurrentMargin; + U8 Channel; + U8 ChannelMask; + U8 Rank; + U8 RankMask; + U8 Edge; + U8 Loopcount; + U8 MaxMargin; + BOOL RdWrMarginFail[2]; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + LastMargins = Outputs->MarginResult; + UpmPwrRetrainLimits = Outputs->UpmPwrRetrainLimits.Pointer; + Status = mrcSuccess; + Loopcount = 17; + MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats)); + RdWrMarginFail[0] = FALSE; + RdWrMarginFail[1] = FALSE; + + + // + // Loop is dependent on the order of MRC_MarginTypes. If this changes, pleas ensure functionality + // stays the same. + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Loopcount: %d\n", Loopcount); + SetupIOTestBasicVA (MrcData, Outputs->ValidChBitMask, Loopcount, NSOE, 0, 0, 8); + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = 1 << Rank; + ChannelMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelMask |= SelectReutRanks (MrcData, Channel, RankMask, 0); + if ((1 << Channel) & ChannelMask) { + MrcOemMemorySetDword (&ControllerOut->Channel[Channel].DataOffsetTrain[0], 0, Outputs->SdramCount); + } + } + + if (ChannelMask == 0) { + continue; + } + + for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) { + if (MarginParam == WrDqsT) { + continue; + } + + if (MarginParam == RdT) { + Outputs->DQPat = RdRdTA; + } else if (MarginParam == RdV) { + Outputs->DQPat = BasicVA; + } + + MaxMargin = ((MarginParam == RdV) || (MarginParam == WrV)) ? MAX_POSSIBLE_VREF : MAX_POSSIBLE_TIME; + + Status = MrcGetBERMarginCh ( + MrcData, + LastMargins, + ChannelMask, + 0xFF, + Rank, + MarginParam, + 0, + 1, + MaxMargin, + 0, + BERStats + ); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Margins\nParams: RdT\tWrT\tRdV\tWrV\n\tLft Rgt Lft Rgt Low Hi Low Hi"); + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!MrcRankInChannelExist (MrcData, Rank, Channel)) { + continue; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nC%dR%d\t", Channel, Rank); + for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) { + if (MarginParam == WrDqsT) { + continue; + } + + LastResultParam = GetMarginResultType (MarginParam); + RetrainMarginLimit = UpmPwrLimitValue (MrcData, MarginParam, RetrainLimit) / 10; + MrcOemMemorySetWord (MinEdgeMargin, (U16) (~0), MAX_EDGES); + + + for (Edge = 0; Edge < MAX_EDGES; Edge++) { + CurrentMargin = (U16) LastMargins[LastResultParam][Rank][Channel][0][Edge] / 10; + MinEdgeMargin[Edge] = MIN (MinEdgeMargin[Edge], CurrentMargin); + if ((CurrentMargin <= RetrainMarginLimit)) { + Status = mrcRetrain; + if ((MarginParam == RdT) || (MarginParam == RdV)) { + RdWrMarginFail[0] = TRUE; + } else if ((MarginParam == WrT) || (MarginParam == WrV)) { + RdWrMarginFail[1] = TRUE; + } + } + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%2d %2d\t", MinEdgeMargin[0], MinEdgeMargin[1]); + if ((RdWrMarginFail[0] == TRUE) && (RdWrMarginFail[1] == TRUE)) { + Rank = MAX_RANK_IN_CHANNEL; + Channel = MAX_CHANNEL; + break; + } + } + } + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table + + if (Status == mrcRetrain) { + // + // Loop is dependent on the order of MRC_MarginTypes. If this changes, please ensure functionality + // stays the same. + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** Margin Limit Check Failed! ***\nNew Limits:\nParam\tUPM\tPWR"); + for (MarginParam = RdT; MarginParam <= WrV; MarginParam++) { + if (((RdWrMarginFail[0] == FALSE) && ((MarginParam == RdT) || (MarginParam == RdV))) || + ((RdWrMarginFail[1] == FALSE) && ((MarginParam == WrT) || (MarginParam == WrV))) || + (MarginParam == WrDqsT)) { + continue; + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n%s", MarginTypesString[MarginParam]); + for (MarginLimitType = UpmLimit; MarginLimitType < RetrainLimit; MarginLimitType++) { + RetrainMarginLimit = MrcUpdateUpmPwrLimits (MrcData, MarginParam, MarginLimitType, MRC_UPM_PWR_INC_VAL); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t%d", RetrainMarginLimit); + } + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table. + } + + return Status; +} + + +/** + This function implements DIMM ODT training. + Adjust DIMM RTT_NOM/RTT_WR value to maximize read/write voltage/timing + + RdOdtPriority Needs to be an input parameter + option to prioritize the ReadODT setting and attempt to optimize that value first, + reducing CPU TDP power (as opposed to system power for the DRAM). + For this case, the base value for ReadODT is changed at the compensation block + by looking at the following values: + RdOdt Global: (50, 64, 84, 110) + + In the case of 2 dpc, the flow will first optimizing RttNom, while keeping RttWr fixed + at 60 Ohms (60 Ohms usually gives the best results). It will then try to reduce RttWr + to 120 Ohms if possible. + + In the case of 1 dpc, only RttNom is used and only a single pass is required. + However, it is important to note that the two channels are completely independent + and can have different numbers of dimms populated. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - if it succeed return mrcSuccess +**/ +MrcStatus +MrcDimmODTTraining ( + IN MrcParameters *const MrcData + ) +{ + const MrcDebug *Debug; + const MrcInput *Inputs; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + MrcStatus Status; + U8 Channel; + U8 byte; + U32 NumBytes; + U8 NumCh; + U8 RankMask; + U8 LocalRanks[MAX_CHANNEL]; + U8 ChMask; + U8 RttNomPoints; + U8 RdOdtPoints; + S8 GRdOdt; + U8 RttWr0; + U8 Dimm; + U8 RttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL]; + U8 RttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL]; + S8 RttNom0; + S8 RttNom1; + U8 BestRttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL]; + U8 BestRttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL]; + S8 BestRdOdt[MAX_CHANNEL]; + U8 offset; + U8 test; + U8 *TestList; + U8 TestListSize; + S32 RdOdtCodes[2]; // Store Comp Codes associated with each RdOdt + S8 RttNom1Off; + BOOL any2DPC; + BOOL any1DPC; + BOOL Lpddr; + S8 BestGRdOdt; + U8 RttOffset; + U8 OffsetPoints; + U8 loopcount; // for centering + S8 GRdOdtStep; + BOOL IncEnds; + BOOL SubPwrLimits; + BOOL skipSubOpt; + BOOL skipOptPrint; + BOOL ReCenterPoints; + U8 TestListTradRd[] = { OptRxBias }; + U8 TestListWr[] = { OptWrDS, OptTxEq }; + U8 TestListRdWr[] = { OptRxBias, OptWrDS, OptTxEq }; + U8 ScaleTest[] = { 1, 1, 1, 1, 1 }; // must specify scale=0 to unpopulated slots !! + U8 ScaleTest1DPC[] = { 1, 1, 1, 0, 0 }; // must specify scale=0 to unpopulated slots !! + U8 *Scale; + U16 PwrLimits[] = { 3000, 3000, 0, 0, 0 }; + S16 Best; + DimmOptPoint DimmOptPoints[MaxOptOff]; + U16 Points2calc[5][MaxOptOff]; + U8 PWRTrendSlope2D; + U8 NumTests; + U8 ArrayLength; + U8 localChMask; + U8 OdtTrainingDimmMask; + S32 OdtOff; + BOOL ForceCenter; + S8 GRdOdtOff; + U32 BestGRdOdtCode; + S8 Average; + OptResultsPerByte BestOff; +#ifdef ULT_FLAG + U8 TestListUltRd[] = { OptRxBias, OptRxEq }; + BOOL UltCpu; +#endif + + Inputs = &MrcData->SysIn.Inputs; + Debug = &Inputs->Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Status = mrcSuccess; + loopcount = 15; + GRdOdtStep = 16; + IncEnds = 1; + SubPwrLimits = 0; + skipSubOpt = 0; + skipOptPrint = 1; + ReCenterPoints = 0; + NumTests = 5; + OdtTrainingDimmMask = 0; + PWRTrendSlope2D = 65; + NumBytes = Outputs->SdramCount; + ArrayLength = sizeof (Points2calc) / sizeof (U16) / NumTests; +#ifdef ULT_FLAG + UltCpu = (Inputs->CpuModel == cmHSW_ULT); +#endif + + // + // Check if LPDDR3 memory is used + // + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); + + MrcOemMemorySet ((U8 *) &BestOff, 0, sizeof (BestOff)); + MrcOemMemorySet ((U8 *) DimmOptPoints, 0, sizeof (DimmOptPoints)); + MrcOemMemorySet ((U8 *) Points2calc, 0, sizeof (Points2calc)); + MrcOemMemorySet ((U8 *) LocalRanks, 0, sizeof (LocalRanks)); + MrcOemMemorySet ((U8 *) RdOdtCodes, 0, sizeof (RdOdtCodes)); + + TestList = TestListRdWr; + TestListSize = sizeof (TestListRdWr); + Scale = ScaleTest; + + // + // GOdt : [150,110, 84, 64, 50] + // 1 dpc: Search [Off, 120, 60] + // 2 dpc: Search [120, 60, 40] + // Dimm0/1 = [40/40, 40/30, 30/40, 30/30, 30/20, 20/30, 20/20] + // + ChMask = 0x3; + RankMask = 0xf; + + // + // Possible RttNom values to pick + // +#ifdef ULT_FLAG + if (UltCpu) { + RttOffset = 0; //In ULT no Rtt nom by definition + } else +#endif + { + RttOffset = 1; + } + RttNomPoints = 2; //[120, 60] + RdOdtPoints = 2; //[150,110] + ChMask &= Outputs->ValidChBitMask; + RankMask &= Outputs->ValidRankMask; + + any2DPC = FALSE; + any1DPC = FALSE; + // + // set channel and rank population + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + // + // Setup Dimm Masks for CalcPowerTrend so we don't access a Dimm that isn't present. + // + if (ChannelOut->DimmCount == 2) { + any2DPC |= 1; + OdtTrainingDimmMask |= 0x3 << (DIMM_ODT_DIMM_MASK_SHIFT * Channel); + } else { + any1DPC |= 1; + OdtTrainingDimmMask |= 0x1 << (DIMM_ODT_DIMM_MASK_SHIFT * Channel); + } + + LocalRanks[Channel] = RankMask & ChannelOut->ValidRankBitMask; + for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) { + // + // start with 60 Ohm by default + // +#ifdef ULT_FLAG + if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) { + RttWr[Channel][Dimm] = 0; + } else +#endif + { + RttWr[Channel][Dimm] = 2; + } + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "apply 2DPC optimization to ChMask %d\n", ChMask); + + // + // *** Read flow *** + // if both ch 1DPC RttNomPoints=1 and RttNom1Off=0 i.e. only GRdOdt loop + // + if (any2DPC == 0) { + // + // if no 2DPC ch + // + RttNomPoints = 1; +#ifdef ULT_FLAG + if (UltCpu && Inputs->TrainingEnables.RDEQT) { + TestList = TestListUltRd; + TestListSize = sizeof (TestListUltRd); + } else +#endif + { + TestList = TestListTradRd; + TestListSize = sizeof (TestListTradRd); + } + Scale = ScaleTest1DPC; + } + + OffsetPoints = 0; + // + // Walk Through RttNOM Settings - going from negative to positive + // + for (GRdOdt = 0; GRdOdt < RdOdtPoints; GRdOdt++) { + GRdOdtOff = -16 + GRdOdt * GRdOdtStep; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CalcRdOdt = %d, GRdOdt = %d GRdOdtOff=%d\n",CalcRdOdt(GRdOdt),GRdOdt,GRdOdtOff); + // + for (RttNom0 = RttOffset; RttNom0 < (RttNomPoints + RttOffset); RttNom0++) { + // + // Dimm0 RttNom Value + // + for (RttNom1Off = -1; RttNom1Off < 2; RttNom1Off++) { + // + // Dimm1 RttNom Value + // Calculate RttNom1 and check if out of range + // + RttNom1 = RttNom0 + RttNom1Off; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RttNom0 = %d, RttNom1 = %d, RttNom1Off = %d\n",RttNom0,RttNom1,RttNom1Off); + // + if ((RttNom1 == (RttNomPoints + RttOffset)) || (RttNom1 < RttOffset)) { + continue; + } + // + // if RttNom == 120ohm run recenter timing + // + if (((RttNom0 == 1) || (RttNom1 == 1)) && (any2DPC)) { + ForceCenter = 1; + } else { + ForceCenter = 0; + } + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + // + // set default opt params offset + // + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; // check if the ch exist + } + + RttNom[Channel][0] = RttNom0; + RttNom[Channel][1] = RttNom1; + for (byte = 0; byte < NumBytes; byte++) { + UpdateOptParamOffset (MrcData, Channel, 0, byte, OptWrDS, 0, 1); + UpdateOptParamOffset ( + MrcData, + Channel, + LocalRanks[Channel], + byte, + OptTxEq, + (S16) (3 * (TXEQFULLDRV >> 4) + 3), + 1 + ); + } + } + // + // Apply new settings and optimize various parameters + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TrainDimmOdtSetting: GRdOdt = %d, RdOdtOff = %d, RttNom0= %d, RttNom1= %d \n",GRdOdt,RdOdtOffsets[GRdOdt], RttNom0, RttNom1); + // + if (OffsetPoints < MaxOptOff) { + TrainDimmOdtSetting ( + MrcData, + &DimmOptPoints[OffsetPoints], + ChMask, + RankMask, + 0, + RttNom, + RttWr, + GRdOdtOff, + TestList, + TestListSize, + SubPwrLimits, + skipSubOpt, + skipOptPrint, + ReCenterPoints | ForceCenter, + ReCenterPoints, + Points2calc, + ArrayLength, + OffsetPoints + ); + OffsetPoints++; + } else { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: DimmOptPoints array out of bounds! %d > %d\n", + OffsetPoints, + MaxOptOff - 1 + ); + } + } + } + } + // + // for each channel apply Power Trend and find best point + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + BestRdOdt[Channel] = 0; + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; // check if the ch exist in this ch + } + + for (offset = 0; offset < OffsetPoints; offset++) { + // + // copy point for the FindOptTradeOff routing + // + for (test = 0; test < (DimmOptPoints->NumTests); test++) { + Points2calc[test][offset] = DimmOptPoints[offset].Points2Trade[test][Channel]; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "read channel=%d Points2calc[test=%d][offset=%d]=%d\n",Channel,test,offset,Points2calc[test][offset]); + // + } + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "FindOptimalTradeOff read\n"); + // LenMargin,TestList,TestListSize,noPwrCalc + // + CalcPowerTrend ( + MrcData, + Channel, + (OdtTrainingDimmMask >> (DIMM_ODT_DIMM_MASK_SHIFT * Channel)), + DimmOptPoints, + Points2calc, + MaxOptOff, + OffsetPoints, + DimmOptPoints->TestList, + Scale, + DimmOptPoints->NumTests, + 0, + PWRTrendSlope2D + ); + // + // senSq=0,AveN=1,caleM=1,powerOpHigh=0 + // + FindOptimalTradeOff ( + MrcData, + &BestOff, + Points2calc, + ArrayLength, + OffsetPoints, + Scale, + 0, + 1, + IncEnds, + 1, + PwrLimits, + 0, + 0 // GuardBand + ); + Best = BestOff.Best + BestOff.GuardBand; + UpdateOdtsValues ( + MrcData, + MRC_BIT0 << Channel, + &DimmOptPoints[Best], + 0, + 0, + skipSubOpt, + 1 + ); + // + // MrcWait (MrcData, 10 * HPET_1MS); + // MrcResetSequence (MrcData); + // + for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) { + BestRttNom[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttNom[Channel][Dimm]; + } + + BestRdOdt[Channel] = DimmOptPoints[Best].ODTSet.GRdOdt; + RdOdtCodes[Channel] = DimmOptPoints[Best].ODTSet.GRdOdtCode; + +#ifdef MRC_DEBUG_PRINT + // + // printing the results + // + PrintODTResultTable ( + MrcData, + &BestOff, + DimmOptPoints, + OffsetPoints, + 0, + 1, + OptDimmOdt, + Channel, + LocalRanks[Channel], + 1, + 0, + 1 + ); + // + // PrintODTResultTable(*MrcData,calcResultSummary,*TestList,NumTest,NumOffsets,MidPoint,IncEnds,OptParam,Channel,Ranks,TrendLine,Nibble,perCh); + // +#endif // MRC_DEBUG_PRINT + } // end ch loop + // + // Find Best "Average" value for Global RdOdt Offset + // + NumCh = 0; + Average = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; + } + + NumCh += 1; + Average += BestRdOdt[Channel]; + // + // RdOdtChOffset[Channel] = RdOdtCodes[BestRdOdt[Channel]]; //set comp code associated comp offset per ch + // + } + + BestGRdOdt = (NumCh != 0) ? (Average / NumCh) : Average; + // + // update average rdOdt offset (both ch) + // + BestGRdOdtCode = UpdateCompGlobalOffset (MrcData, RdOdt, (U8) BestGRdOdt, 1); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Calculated Average (both ch) GRdOdt is %d\n", BestGRdOdt); + + if (NumCh > 1) { + // + // adjust RdOdt to per channel/byte + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; + } + + RdOdtCodes[Channel] -= BestGRdOdtCode; + for (byte = 0; byte < Outputs->SdramCount; byte++) { + // + // Apply Best RdOdt in case we didnt run Godt. + // + OdtOff = RdOdtCodes[Channel] + ((Outputs->Controller[0].Channel[Channel].DataCompOffset[byte] >> 12) & 0x1f); + UpdateOptParamOffset (MrcData, Channel, 0xF, byte, OptRdOdt, (S16) OdtOff, 1); + } + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Voltage\n"); + Status = ReadVoltageCentering2D ( + MrcData, + Outputs->MarginResult, + ChMask, + RdV, + 0, + 0, + loopcount, + 0 + ); + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n"); + Status = DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + ChMask, + RdT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + loopcount, + 0 // En2D + ); + + if (!Lpddr) { + // + // DIMM ODT is disabled by default on LPDDR, so skip this section + // + + // + // *** Write flow ***// + // + RttOffset = (Inputs->MaxRttWr < 0x2) ? (Inputs->MaxRttWr) : (0x1); // Get user input for MaxRttWr, 0 = off, 1 = 120 ohms + if (any2DPC) { + // + // At least one 2DPC ch + // + if (!any1DPC) { + RttOffset = 1; // Start with 120ohm + } + } + + TestList = TestListWr; + TestListSize = sizeof (TestListWr); + Scale = ScaleTest1DPC; + + // + // option 1: Keep RttWr the same for both DIMMs (ie: train per Ch for both RttWr & WrDrv) + // option 2: Allow different RttWr for each DIMM and break WrDrv out of this optimization (ie: do it later). + // + for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) { + // + // check if dimm exist in any channel + // + OdtTrainingDimmMask = (0x3 << (Dimm * 2)); + if (!(Outputs->ValidRankMask & OdtTrainingDimmMask)) { + continue; + } + + localChMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (OdtTrainingDimmMask & LocalRanks[Channel]) { + localChMask |= MRC_BIT0 << Channel; // can run 1 or 2 ch + } + } + + localChMask &= ChMask; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "write odt train localChMask=%x\n", localChMask); + + OffsetPoints = 0; + for (RttWr0 = RttOffset; RttWr0 < 3; RttWr0++) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + RttWr[Channel][Dimm] = RttWr0; // start with 60 Ohm by default + if (!((MRC_BIT0 << Channel) & localChMask)) { + continue; // skip if the ch doesn't exist + } + } + + if ((RttWr0 == 0) && (any1DPC)) { + ForceCenter = 1; + } else { + ForceCenter = 0; + } + + TrainDimmOdtSetting ( + MrcData, + &DimmOptPoints[OffsetPoints], + localChMask, + OdtTrainingDimmMask, + 1, + BestRttNom, + RttWr, + BestGRdOdt, + TestList, + TestListSize, + SubPwrLimits, + skipSubOpt, + skipOptPrint, + ReCenterPoints, + ReCenterPoints | ForceCenter, + Points2calc, + ArrayLength, + OffsetPoints + ); + OffsetPoints++; + } + // + // for each channel apply Power Trend and find best point + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & localChMask)) { + continue; // check if the ch exist + } + + for (offset = 0; offset < OffsetPoints; offset++) { + // + // copy point for the FindOptTradeOff routing + // + for (test = 0; test < (DimmOptPoints->NumTests); test++) { + Points2calc[test][offset] = DimmOptPoints[offset].Points2Trade[test][Channel]; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Write channel=%d Points2calc[test=%d][offset=%d]=%d\n",Channel,test,offset,Points2calc[test][offset]); + // + } + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "FindOptimalTradeOff write\n"); + // LenMargin,TestList,TestListSize,noPwrCalc + // + CalcPowerTrend ( + MrcData, + Channel, + MRC_BIT0 << Dimm, + DimmOptPoints, + Points2calc, + MaxOptOff, + OffsetPoints, + DimmOptPoints->TestList, + Scale, + DimmOptPoints->NumTests, + 0, + PWRTrendSlope2D + ); + // + // senSq=0,AveN=1,caleM=1,powerOpHigh=0 + // + FindOptimalTradeOff ( + MrcData, + &BestOff, + Points2calc, + ArrayLength, + OffsetPoints, + Scale, + 0, + 1, + IncEnds, + 1, + PwrLimits, + 0, + 0 // GuardBand + ); + Best = BestOff.Best + BestOff.GuardBand; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "===============> BestOff=%d\n",BestOff.Best); + // skipGRdOdt=1 SkipDimmOdts=0, SkipBestOffsets,updateHost + // + UpdateOdtsValues (MrcData, MRC_BIT0 << Channel, &DimmOptPoints[Best], 1, 0, skipSubOpt, 1); + RttWr[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttWr[Channel][Dimm]; + BestRttWr[Channel][Dimm] = DimmOptPoints[Best].ODTSet.RttWr[Channel][Dimm]; // delete? + #ifdef MRC_DEBUG_PRINT // printing the results + PrintODTResultTable ( + MrcData, + &BestOff, + DimmOptPoints, + OffsetPoints, + 0, + 1, + OptDimmOdtWr, + Channel, + OdtTrainingDimmMask, + 1, + 0, + 1 + ); + // + // PrintODTResultTable(*MrcData,calcResultSummary,*TestList,NumTest,NumOffsets,MidPoint,IncEnds,OptParam,Channel,Ranks,TrendLine,Nibble,perCh); + // + #endif // MRC_DEBUG_PRINT + } // end of channel loop + } // end dimm loop + // + //set equal TxEq for all bytes before DS + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & Outputs->ValidChBitMask)) { + continue; // skip if the ch doesn't exist + } + + for (byte = 0; byte < NumBytes; byte++) { + UpdateOptParamOffset ( + MrcData, + Channel, + LocalRanks[Channel], + byte, + OptTxEq, + (S16) (3 * (TXEQFULLDRV >> 4) + 3), + 1 + ); + } + } + } // if (!Lpddr) + + // + // run WriteDS + // + Status = TrainWriteDriveStrength (MrcData, Outputs->ValidChBitMask, 0, OPT_PARAM_1D_LC, 0); + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Vref\n"); + Status = MrcWriteVoltageCentering2D (MrcData); + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing\n"); + Status = DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + Outputs->ValidChBitMask, + WrT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + loopcount, + 0 // En2D + ); + + return Status; +} + +/** + This function implements Read Equalization training. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - if it succeeds return mrcSuccess +**/ +MrcStatus +MrcReadEQTraining ( + IN MrcParameters *const MrcData + ) +{ +#ifdef ULT_FLAG + U8 RankMask; + U8 TestList[] = { RdV, RdT }; + U8 Scale[] = { 1, 2, 1, 0, 0 }; + U16 PwrLimits[] = { 1280, 1280, 0, 0, 0 }; + U16 GlobalPwrLimit; + OptOffsetChByte BestOff; + + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[0] = MAX (PwrLimits[0], GlobalPwrLimit); + GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + PwrLimits[1] = MAX (PwrLimits[1], GlobalPwrLimit); + + // + // Function Call for RxEQ Training + // + for (RankMask = 1; RankMask < (MRC_BIT0 << MAX_RANK_IN_CHANNEL); RankMask <<= 1) { + if (RankMask & MrcData->SysOut.Outputs.ValidRankMask) { + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, + RankMask, + OptRxEq, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + 0, // Start + 7, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOptUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + } + } + } +#endif + + return mrcSuccess; +} + +/** + This function implements Write (Transmitter) Equalization training. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - if it succeeds return mrcSuccess +**/ +MrcStatus +MrcWriteEQTraining ( + IN MrcParameters *const MrcData + ) +{ + MrcStatus Status; + U8 Rank; + U8 RankMask; + U8 TestList[] = { WrV, WrT }; + U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !! + U16 PwrLimits[5]; + OptOffsetChByte BestOff; + + MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0])); + PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + + // + // Function Call for RxEQ Training + // + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = MRC_BIT0 << Rank; + if (RankMask & MrcData->SysOut.Outputs.ValidRankMask) { + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, + RankMask, + OptTxEq, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + 0, // Start + 11, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 2 // GuardBand + ); + } + } + + DataTimeCentering2D ( + MrcData, + MrcData->SysOut.Outputs.MarginResult, // prev. margin results + 0x3, + WrT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + OPT_PARAM_1D_LC, + 0 // En2D + ); + + Status = mrcSuccess; + return Status; +} + +/** + This function implements Read Amplifier Power training. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - If it succeeds return mrcSuccess +**/ +MrcStatus +MrcReadAmplifierPower ( + IN MrcParameters *const MrcData + ) +{ + MrcStatus Status; + MrcOutput *Outputs; + const MrcDebug *Debug; + U8 TestList[] = { RdV, RdT }; + U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !! + U16 PwrLimits[5]; + OptOffsetChByte BestOff; + BOOL RdCenter; + U8 RecenterLC; + + RdCenter = 1; + RecenterLC = 15; + Status = mrcSuccess; + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + + MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0])); + PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + + // + // Function Call for RxBias + // + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, + 0xF, + OptRxBias, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + 0, // Start + 7, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + + if (RdCenter) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n"); + Status = DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + 0x3, + RdT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + RecenterLC, + 0 // En2D + ); + } + + return Status; +} + +/** + This function implements Dimm Ron training. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - if it succeeds return mrcSuccess +**/ +MrcStatus +MrcDimmRonTraining ( + IN MrcParameters *const MrcData + ) +{ +#ifdef ULT_FLAG + OptOffsetChByte BestOff; + U8 TestList[] = { RdV, RdT }; + U8 Scale[] = { 1, 2, 1, 0, 0 }; // must specify scale=0 to unpopulate slots !! + U16 PwrLimits[5]; + + MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0])); + PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + + // + // Check if LPDDR3 memory is used + // + if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) { + // + // Function Call for RxBias + // + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, // Channels + 0xF, // Ranks + OptDimmRon, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + 0, // Start + 2, // Stop + OPT_PARAM_1D_LC, // Loopcount + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + } +#endif // ULT_FLAG + + return mrcSuccess; + +} + +/** + This function implements Read ODT training and Write DS. + Optimize Read ODT strength for performance & power. + + @param[in,out] MrcData - Include all MRC global data. + @param[in,out] BestOff - Structure containg the best offest and margins for th Opt param. + @param[in] ChannelMask - Channels to train + @param[in] RankMask - Condenses down the results from multiple ranks + @param[in] OptParam - Defines the OptParam Offsets. + Supported OptParam = [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 3: TxEq, 4: RxEq, + 5: RxBias, 6: DimmOdt, 7: DimmOdtWr] + @param[in] TestList - List of margin params that will be tested (up to 4) + @param[in] NumTests - The length of TestList + @param[in] Scale - List of the relative importance between the 4 tests + @param[in] PwrLimitsABC - List of the values for each test margin, above which margin is "adequate" + @param[in] Start - Start point of sweeping the Comp values + @param[in] Stop - Stop point of sweeping the Comp values + @param[in] LoopCount - The number of loops to run in IO tests. + @param[in] Repeats - Number of times to repeat the test to average out any noise + @param[in] NoPrint - Switch to disable printing. + @param[in] SkipOptUpdate - Switch to train but not update Opt settings. + @param[in] RdRd2Test - Switch to run with different TA times: possible values are [0, RdRdTA, RdRdTA_All] + @param[in] GuardBand - Signed offset to apply to the Opt param best value. + + @retval Nothing +**/ +void +TrainDDROptParam ( + IN OUT MrcParameters *const MrcData, + IN OUT OptOffsetChByte *BestOff, + IN U8 ChannelMask, + IN U8 RankMask, + IN U8 OptParam, + IN U8 *TestList, + IN U8 NumTests, + IN U8 *Scale, + IN U16 *PwrLimitsABC, + IN S8 Start, + IN S8 Stop, + IN U8 LoopCount, + IN U8 Repeats, + IN BOOL NoPrint, + IN BOOL SkipOptUpdate, + IN U8 RdRd2Test, + IN S8 GuardBand + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + MrcControllerOut *ControllerOut; + MrcStatus Status; + U32 (*MarginByte)[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; + // + // TestParam X 24 Points X Ch X Byte X Hi/Lo + // + U32 BERStats[4]; + U16 SaveMargin[4][24][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; + U16 Test; + U16 MinEdge; + U16 Margins[5][24]; // TestParam X 24 Comp Points + U16 Mode; + S16 Best; + U8 ResultType; + U8 AveN; + U8 ChBitMask; + U8 Channel; + U8 Byte; + U8 Rank; + U8 Edge; + U8 FirstRank; + U8 OdtValue; + U8 NumBytes; + U8 BMap[9]; // Need by GetBERMarginByte + U8 Param; + U8 MaxMargin; + U8 localR[MAX_CHANNEL]; + U8 Rep; + void *NullPtr; + S8 CurrentComp; + S8 ReservedComp; + S8 MaxComp; + U16 OptPower[24]; + U8 PWRTrendSlope1D; + S8 Delta; + S8 Index; + S8 Off; + S8 LenMargin; + S8 Shift; + U16 NoiseTicks; + BOOL NoSignal; + BOOL IncEnds; + BOOL IncEndsForPrint; + BOOL CPUComp; + BOOL printPerCh; + DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0; + DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1; + DDRSCRAM_CR_DDRMISCCONTROL0_STRUCT DdrMiscControl0; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0; + DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1; + // + // result print summary: 5 columns per byte + // + OptResultsPerByte calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + + MaxComp = 63; + PWRTrendSlope1D = 65; + ResultType = 0; + NullPtr = 0; + CurrentComp = 0; + IncEnds = 0; + IncEndsForPrint = 0; + printPerCh = 0; + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + DdrCrCompCtl0.Data = Outputs->CompCtl0; + MarginByte = &Outputs->MarginResult; + ChannelMask &= Outputs->ValidChBitMask; + RankMask &= Outputs->ValidRankMask; + MrcOemMemorySet ((U8 *) calcResultSummary, 0, sizeof (calcResultSummary)); + MrcOemMemorySet ((U8 *) BestOff, 0xffff, sizeof (OptOffsetChByte)); + MrcOemMemorySet ((U8 *) Margins, 0, sizeof (Margins)); + MrcOemMemorySet ((U8 *) OptPower, 0, sizeof (OptPower)); + MrcOemMemorySet ((U8 *) localR, 0, sizeof (localR)); + MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats)); + for (Byte = 0; Byte < (sizeof (BMap) / sizeof (BMap[0])); Byte++) { + BMap[Byte] = Byte; + } + Outputs->EnDumRd = 0; + + if (RdRd2Test == RdRdTA) { + LoopCount -= 1; // 2 TA tests, so cut the loop count in half + } else if (RdRd2Test == RdRdTA_All) { + LoopCount -= 3; // 8 TA tests, so divide the loop count by 8 + } + + SetupIOTestBasicVA (MrcData, ChannelMask, LoopCount, 0, 0, 0, 8); // set test to all channels + + if (RdRd2Test != 0) { + Outputs->DQPat = RdRd2Test; + } + // + // Select All Ranks for REUT test + // + ChBitMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChannelMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + localR[Channel] = ChannelOut->ValidRankBitMask & RankMask; + // + // use ChBitMask from here down - if ch is set that mean at least 1 rank for testing, also remove ch w/o active ranks + // + ChBitMask |= SelectReutRanks (MrcData, Channel, localR[Channel], 0); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "reut ranks ChBitMask %x Local ranks=%x\n", ChBitMask,localR[Channel]); + // Clear any old state in DataTrain Offset + // + MrcOemMemorySet ((U8 *) &ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount); + } + + if (ChBitMask == 0) { + return ; + } + // + // Find the first selected rank + // + FirstRank = 0; + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((MRC_BIT0 << Rank) & RankMask) { + FirstRank = Rank; // could be in any channel + break; + } + } + // + // Store margin results for + // + NumBytes = (U8) Outputs->SdramCount; + + if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr) || (OptParam == OptDimmRon)) { + NumBytes = 1; + } + // + // Calculate Start/Stop Point for Comp Optimization + // + CPUComp = ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptTComp) || (OptParam == OptSComp)); + if (CPUComp) { + DdrCrDataComp0.Data = DdrCrDataComp1.Data = 0; + if (OptParam == OptRdOdt) { + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + } else { + DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG); + } + + switch (OptParam) { + case OptWrDS: + CurrentComp = (S8) DdrCrDataComp0.Bits.RcompDrvUp; + break; + + case OptRdOdt: + CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp; + break; + + case OptSComp: + CurrentComp = (S8) DdrCrDataComp0.Bits.SlewRateComp; + // + // For SCOMP we have a 5-bit register with the max value of 31. + // All other COMPs have 6-bit registers with the max value of 63 + // + MaxComp = 31; + break; + + case OptTComp: + CurrentComp = (S8) DdrCrDataComp0.Bits.TcoComp; + break; + + default: + CurrentComp = 0; + break; + } + + ReservedComp = 3; // Reserve 3 comp codes for adjustment range + Delta = CurrentComp - ReservedComp + Start; + if (Delta < 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "------------> warning offset range is clipped by %d\n", Delta); + Start -= Delta; + } + + Delta = MaxComp - CurrentComp - ReservedComp - Stop; + if (Delta < 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "------------> warning offset range is clipped by %d\n", Delta); + Stop += Delta; + } + + if (Stop < Start) { + Stop = Start; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CurrentComp = %d, Start = %d, Stop = %d, Delta = %d\n", CurrentComp, Start, Stop, Delta); + // + } + // + // Loop through all Test Params and Measure Margin + // + for (Test = 0; Test < NumTests; Test++) { + Param = TestList[Test]; // tl[0]=4 tl[1]=1 + ResultType = GetMarginResultType (Param); // rxv=0 rxt=1 + // + // Assign to last pass margin results by reference + // get lowest margin from all ch/rankS/byte save in FirstRank + // + Status = GetMarginByte (MrcData, Outputs->MarginResult, Param, FirstRank, RankMask); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n--- FirstRank = %d ResultType=%d Param=%d ranks=0x%x\n", FirstRank,ResultType,Param,RankMask); + // Calculate the MaxMargin for this test + // + MaxMargin = MAX_POSSIBLE_TIME; + if ((Param == RdV) || + (Param == RdFan2) || + (Param == RdFan3) || + (Param == WrV) || + (Param == WrFan2) || + (Param == WrFan3) + ) { + MaxMargin = MAX_POSSIBLE_VREF; + } + // + // No need to search too far + // + if (MaxMargin > (PwrLimitsABC[Test] / 20)) { + MaxMargin = (U8) (PwrLimitsABC[Test] / 20); + } + // + // Loop Through all Comp Codes + // + for (Off = Start; Off < Stop + 1; Off++) { + Index = Off - Start; + // + // Apply Code + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + // + // For DIMM ODT 2dpc, Start sweeping with RttNom = 40 and RttWr fixed at 60 + // For DIMM ODT 1dpc, Start sweeping with RttNom = Off and RttWr fixed at Off + // + if ((OptParam == OptDimmOdt) && (ChannelOut->DimmCount == 2)) { + Shift = 0x20; + } else { + Shift = 0; + } + + for (Byte = 0; Byte < NumBytes; Byte++) { + if (!SkipOptUpdate) { + // + // change OpParam offset for all ch/byte/LocalR + // + UpdateOptParamOffset (MrcData, Channel, localR[Channel], Byte, OptParam, (S16) (Off + Shift), 0); + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n--Channel=%d, localR[Channel]=%x Byte=%d OffsetComp=%d Off=%d\n",Channel,localR[Channel],Byte,OffsetComp,Off); + // + } // some are limited in range inside e.g: RdOdt +15:-16 + } + + for (Rep = 0; Rep < Repeats; Rep++) { + // + // Run Margin Test - margin_1d with chosen param + // run on all ranks but change param only for firstRank?? + // + Mode = 0; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " start MrcGetBERMarginByte \n"); + // + Status = MrcGetBERMarginByte ( + MrcData, + Outputs->MarginResult, + ChBitMask, + FirstRank, + FirstRank, + Param, + Mode, + BMap, + 1, + MaxMargin, + 0, + BERStats + ); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " finish MrcGetBERMarginByte \n"); + // Record Results + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + for (Edge = 0; Edge < MAX_EDGES; Edge++) { + MinEdge = 0xFFFF; + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if (Rep == 0) { + SaveMargin[Test][Index][Channel][Byte][Edge] = 0; + } + + SaveMargin[Test][Index][Channel][Byte][Edge] += + (U16) (*MarginByte)[ResultType][FirstRank][Channel][Byte][Edge]; + if (MinEdge > SaveMargin[Test][Index][Channel][Byte][Edge]) { + MinEdge = SaveMargin[Test][Index][Channel][Byte][Edge]; + } + } + + if (NumBytes == 1) { + SaveMargin[Test][Index][Channel][0][Edge] = MinEdge; // Todo:change Byte->0 + } + } + } + } + } // end of offset +#ifdef MRC_DEBUG_PRINT + PrintResultTableByte4by24 ( + MrcData, + ChBitMask, + SaveMargin, + Test, + Stop - Start + 1, + -Start, + 2, + OptParam, + TestList[Test], + PwrLimitsABC, + NoPrint + ); +#endif // MRC_DEBUG_PRINT + } // end of test list + // + // Calculate the best value for every byte + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n start calculate the the best margin \n"); + // + LenMargin = (Stop - Start) + 1; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + for (Byte = 0; Byte < NumBytes; Byte++) { + // + // Populate Margins array and asymetric penalty + // + for (Test = 0; Test < NumTests; Test++) { + for (Off = Start; Off < Stop + 1; Off++) { + Index = Off - Start; // 0:.. + // + // for now just get EW + // + if ((TestList[Test] == RdV) || + (TestList[Test] == WrV) || + (TestList[Test] == WrFan3) || + (TestList[Test] == RdFan3) + ) { + Margins[Test][Index] = EffectiveMargin ( + Scale[Test] * SaveMargin[Test][Index][Channel][Byte][0], + Scale[Test] * SaveMargin[Test][Index][Channel][Byte][1] + ); + } else { + Margins[Test][Index] = Scale[Test] * + (SaveMargin[Test][Index][Channel][Byte][0] + SaveMargin[Test][Index][Channel][Byte][1]); + } + } + } + // + // Special Cases for Running Average Filter + // + if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr) || (OptParam == OptDimmRon)) { + AveN = 1; + } else if (OptParam == OptRxBias) { + AveN = 3; + } else if (OptParam == OptRxEq) { + // + // Use special, 2D running average for RxEq + // + AveN = 1; + RunningAverage2D (Margins, 0, Stop - Start + 1, 5, 2, 1); + RunningAverage2D (Margins, 1, Stop - Start + 1, 5, 2, 1); // try Cscale=1 first. + } else { + AveN = 7; + if (LenMargin < AveN) { + AveN = LenMargin - 1; + } + } + // + // Use one of the Margin Arrays for fine grain power tradeoffs. This is only used if Scale[NumTests] is not 0 + // + for (Off = 0; Off < LenMargin; Off++) { + OptPower[Off] = (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptParam, Off + Start, CurrentComp, 0); + Margins[NumTests][Off] = OptPower[Off]; + if ((OptParam == OptDimmRon) || (OptParam == OptWrDS) || (OptParam == OptRdOdt)) { + // + // convert from Ohm to mW to pass for T-line calc : = (Vdd/2)^2/R ~ 562 / R + // + Margins[NumTests][Off] = 562 / Margins[NumTests][Off]; + } + + if ((OptParam == OptTxEq) && (ChannelOut->DimmCount == 1)) { + // + // find first rank: 0 or 2 + // + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((MRC_BIT0 << Rank) & ChannelOut->ValidRankBitMask) { + FirstRank = Rank; // could be in any channel + break; + } + } + + if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) { + OdtValue = 0; + } else { + Ddr3ModeRegister1.Data = ChannelOut->Dimm[FirstRank / 2].Rank[FirstRank % 2].MR[mrMR1]; + OdtValue = (U8) + ( + (Ddr3ModeRegister1.Bits.OdtRttValueHigh << 2) | + (Ddr3ModeRegister1.Bits.OdtRttValueMid << 1) | + Ddr3ModeRegister1.Bits.OdtRttValueLow + ); + } + + if (OdtValue == 0) { + Margins[NumTests][Off] = 1;//no power consideration + } + } + } + // + // need to provide set of power numbers depending on the OffsetComp codes (per byte)for trend line . + // + CalcPowerTrend ( + MrcData, + Channel, + localR[Channel], + NullPtr, + Margins, + 24, + LenMargin, + TestList, + Scale, + NumTests, + 1, + PWRTrendSlope1D + ); + // + // Use that value to create Margin Results based on power. + // Creates a smooth, linear function that goes from MaxSum to N/(N-1)*MaxSum + // RatioNum = FinePwrRatio[OptParam] * LenMargin; //e.g FinePwrRatio[RdOdt]=5 + // Find the Best Overall Setting + // senSq=0,caleM=1,powerOpHigh=0 + // + FindOptimalTradeOff ( + MrcData, + &calcResultSummary[Channel][Byte], + Margins, + 24, + LenMargin, + Scale, + 0, + AveN, + IncEnds, + 1, + PwrLimitsABC, + 0, + GuardBand + ); + // + // Get the best index considering the GuardBand + // + Best = calcResultSummary[Channel][Byte].Best + calcResultSummary[Channel][Byte].GuardBand; + NoiseTicks = 3; + NoSignal = FALSE; + for (Test = 0; Test < NumTests; Test++) { + if ((calcResultSummary[Channel][Byte].Ticks[Test] / 10) > NoiseTicks) { + NoSignal = FALSE; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n NumTests = %d Best =%d ch=%d byte=%d calcResultSummary[Channel][Byte].Ticks[Test]=%d NoiseTicks=%d\n",NumTests,Best,Channel,Byte,calcResultSummary[Channel][Byte].Ticks[Test],NoiseTicks); + // + break; + } + } + + if (NoSignal) { + Best = 0;//set to min + calcResultSummary[Channel][Byte].Best = 0; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n Best =%d ch=%d byte=%d \n",Best,Channel,Byte); + // Update CR + // + if ((OptParam == OptDimmOdt) && (ChannelOut->DimmCount == 2)) { + Shift = 0x20; + } else { + Shift = 0; + } + // + // Best += (Shift - Start); + // + Best -= (Shift - Start); // update take offset + if (!SkipOptUpdate) { + UpdateOptParamOffset (MrcData, Channel, localR[Channel], Byte, OptParam, Best, 1); + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " localR[Channel]=%x Best =%d ch=%d byte=%d \n",localR[Channel],Best,Channel,Byte); + // + BestOff->Offset[Channel][Byte] = Best; + } // end byte + } // End of Calculating best value (ch) +#ifdef MRC_DEBUG_PRINT + // + // printing the results + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if ((MrcChannelExist (Outputs, Channel))) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + if (!(ChannelOut->ValidRankBitMask & localR[Channel])) { + continue; + } + + IncEndsForPrint = + ( + OptParam == OptDimmOdt || + OptParam == OptDimmOdtWr || + OptParam == OptDimmRon || + OptParam == OptRxEq || + IncEnds + ); + printPerCh = (OptParam == OptDimmOdt || OptParam == OptDimmOdtWr || OptParam == OptDimmRon); + // + // lower bytes + // + PrintCalcResultTableCh ( + MrcData, + calcResultSummary, + TestList, + NumTests, + Stop - Start + 1, + -Start, + IncEndsForPrint, + OptParam, + OptPower, + Channel, + localR[Channel], + Scale[NumTests], + 0, + printPerCh, + NoPrint + ); + // + // higher bytes + // + if (!printPerCh) { + PrintCalcResultTableCh ( + MrcData, + calcResultSummary, + TestList, + NumTests, + Stop - Start + 1, + -Start, + IncEndsForPrint, + OptParam, + OptPower, + Channel, + localR[Channel], + Scale[NumTests], + 1, + printPerCh, + NoPrint + ); + } + } + } +#endif // MRC_DEBUG_PRINT + // + // Propgate new CR setting + // + // @todo: redundant :there is one inside updateComps + // + if (CPUComp) { + DdrMiscControl0.Data = Outputs->MiscControl0; + DdrMiscControl0.Bits.ForceCompUpdate = 1; + MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl0.Data); + } + // + // Update the LastPass points in host + // + for (Test = 0; Test < NumTests; Test++) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + ResultType = GetMarginResultType (TestList[Test]); + for (Byte = 0; Byte < NumBytes; Byte++) { + // + // save the margins in best offset point for each byte/ch in rank 0/1 + // + (*MarginByte)[ResultType][0][Channel][Byte][0] = + SaveMargin[Test][BestOff->Offset[Channel][Byte] - Start][Channel][Byte][0]; + (*MarginByte)[ResultType][0][Channel][Byte][1] = + SaveMargin[Test][BestOff->Offset[Channel][Byte] - Start][Channel][Byte][1]; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "best offset= %d ;byte=%d ;(*MarginByte)[ResultType][0][Channel][Byte][0] -%d (*MarginByte)[ResultType][0][Channel][Byte][1] -%d add=%d\n",BestOff->Offset[Channel][Byte],Byte,(U16) (*MarginByte)[ResultType][0][Channel][Byte][0] , (*MarginByte)[ResultType][0][Channel][Byte][1],((U16) (*MarginByte)[ResultType][0][Channel][Byte][0] + (U16)(*MarginByte)[ResultType][0][Channel][Byte][1])); + // + } + } + + Status = ScaleMarginByte (MrcData, Outputs->MarginResult, TestList[Test], 0); + } + + BestOff->NumTests = sizeof (TestList); + for (Test = 0; Test < NumTests; Test++) { + ResultType = GetMarginResultType (TestList[Test]); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + // + // track minimum eye width per ch + // + for (Byte = 0; Byte < NumBytes; Byte++) { + if (Byte == 0) { + BestOff->Margins[Test][Channel] = (U16) ((*MarginByte)[ResultType][0][Channel][0][0] + + (*MarginByte)[ResultType][0][Channel][0][1]); + } else if (BestOff->Margins[Test][Channel] > + ((*MarginByte)[ResultType][0][Channel][Byte][0] + (*MarginByte)[ResultType][0][Channel][Byte][1]) + ) { + BestOff->Margins[Test][Channel] = (U16) ((*MarginByte)[ResultType][0][Channel][Byte][0] + + (*MarginByte)[ResultType][0][Channel][Byte][1]); + } + } + + BestOff->TestList[Test][Channel] = TestList[Test]; + + // + // Normalize margins + // BestOff->Margins[Test][Channel] *= Scale[Test]; //Scale?? + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "after scale - BestOff->Margins[ch=%d][%s]= %d \n",Channel,MarginTypesString[TestList[Test]],BestOff->Margins[Test][Channel]); + // + } + // + // if (BestOff->Margins[Test][Channel]<=20) {//set OptParam to max in case off no eye + // for (Byte = 0; Byte < NumBytes; Byte++) { + // if(OptParam == OptRxBias) UpdateOptParamOffset (MrcData, Channel, 0, Byte, OptRxBias, 15, 1); + // if(OptParam == OptWrDS) UpdateOptParamOffset (MrcData, Channel, 0, Byte, OptWrDS, 7, 1); + // } + // } + // + } + // + // Clean up + // + Outputs->EnDumRd = 0; + + return; + +} + +/** + This function implements Read ODT training. + Optimize Read ODT strength for performance & power + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - If it succeded return mrcSuccess +**/ +MrcStatus +MrcReadODTTraining ( + IN MrcParameters *const MrcData + ) +{ + MrcStatus Status; + const MrcInput *Inputs; + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcCpuModel CpuModel; + U8 *TestList; + U8 TestListDdr3[] = { RdV, RdT }; + U8 TestListSize; + U8 ScaleDdr3[] = { 1, 2, 1, 0, 0 }; + U8 *Scale; + U16 PwrLimits[5]; // Eye width/height + S8 Start; + S8 Stop; + S16 OffLimit; + S16 OffLimitDn; + U16 OdtLimit; + U8 OdtLimitDn; + U16 Rleg; + S8 StatLegs; + U8 OdtLegsDis; + S8 CurrentVref; + S8 CurrentComp; + OptOffsetChByte BestOff; + BOOL RdCenter; + U8 RecenterLC; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0; + DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1; + U8 RdTATestType; + U8 Index; +#ifdef ULT_FLAG + U8 TestListLpddr[] = { RdV, RdT, RcvEnaX }; + U8 ScaleLpddr[] = { 1, 2, 2, 1, 0 }; + BOOL Lpddr; +#endif + + Inputs = &MrcData->SysIn.Inputs; + Outputs = &MrcData->SysOut.Outputs; + Debug = &Inputs->Debug; + CpuModel = Inputs->CpuModel; + Status = mrcSuccess; + RdCenter = 1; + RecenterLC = 17; + Start = -16; + MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0])); + + // + // find a start offset where we below 180ohm to protect against OS/US + // + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp; + OdtLimitDn = 30; //ohm + TestList = TestListDdr3; + TestListSize = sizeof (TestListDdr3); + Scale = ScaleDdr3; + RdTATestType = RdRdTA; + +#ifdef ULT_FLAG + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); + if (CpuModel == cmHSW_ULT) { + OdtLimit = 230; + if (Lpddr) { + RdTATestType = RdRdTA_All; + OdtLimitDn = 80; //ohm + TestList = TestListLpddr; + Scale = ScaleLpddr; + TestListSize = sizeof (TestListLpddr); + } + } else +#endif //ULT_FLAG + { + OdtLimit = 180; + } + + for (Index = 0; Index < TestListSize; Index++) { + PwrLimits[Index] = UpmPwrLimitValue (MrcData, TestList[Index], PowerLimit); + } + + DdrCrCompCtl0.Data = Outputs->CompCtl0; + OdtLegsDis = (U8) DdrCrCompCtl0.Bits.DisableOdtStatic; + CurrentVref = (S8) DdrCrCompCtl0.Bits.DqOdtVref; + StatLegs = 4 * 4; // we enable only 1/3 segment for odt + if (CurrentVref & 0x10) { + CurrentVref -= 0x20; // 2's complement + } + + Rleg = CalcRdOdt (MrcData, CurrentVref) * (StatLegs * (!OdtLegsDis) + CurrentComp); + OffLimit = (Rleg / OdtLimit) - StatLegs * (!OdtLegsDis) - CurrentComp; + + // + // Find max ODT offset + // + OffLimitDn = (Rleg / OdtLimitDn) - StatLegs * (!OdtLegsDis) - CurrentComp; + Stop = (U8) OffLimitDn; + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + " current code = %d OdtLegsDis = %d Rleg = %d CurrentVref = %d OffLimit = %d Start = %d stop = %d\n", + CurrentComp, + OdtLegsDis, + Rleg, + CurrentVref, + OffLimit, + Start, + Stop + ); + + if (OffLimit > Start) { + Start = (S8) OffLimit; + } + + if (Stop > (23 + Start)) { + Stop = (S8) (23 + Start); // Only 24 offsets in the margin array. + } + if (Stop > 15) { + Stop = 15; + } + + // + // Function Call for RdODT + // + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, + 0xF, + OptRdOdt, + TestList, + TestListSize, + Scale, + PwrLimits, + Start, + Stop, // Stop + 17, // Loopcount increased from 15 to better match RMT margins + 1, // Repeats + 0, // NoPrint + 0, // SkipOptUpdate + RdTATestType, // RdRd2Test + 0 // GuardBand + ); + + if (RdCenter) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing\n"); + Status = DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + 0x3, + RdT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + RecenterLC, + 0 // En2D + ); + } + + return Status; +} + +/** + This function implements Dimm Odt training. + Optimize Dimm Odt value for performance/power + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - If it succeded return mrcSuccess +**/ +MrcStatus +MrcDimmODT1dTraining ( + IN MrcParameters *const MrcData + ) +{ + MrcStatus Status; + MrcOutput *Outputs; + U8 TestList[] = { RdV, RdT, WrV, WrT }; // ie 4,1 + U8 TestListWr[] = { WrV, WrT }; // ie 4,1 + U8 Scale[] = { 1, 2, 1, 2, 0 }; + U8 ScaleWr[] = { 1, 2, 0, 0, 0 }; + U16 PwrLimits[] = { 2480, 2240, 2480, 2240, 0 }; // just margin consideration + U8 dimm; + U8 Channel; + U8 ChannelMask; + OptOffsetChByte BestOff; + + Outputs = &MrcData->SysOut.Outputs; + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if ((MrcChannelExist (Outputs, Channel))) { + ChannelMask = MRC_BIT0 << Channel; + if (Outputs->Controller[0].Channel[Channel].DimmCount == 2) { + // + // DimmODT Rtt Nom - 120,60,40,30 + // run Rtt nom with the Rtt write 0x20=60 ohm + // + TrainDDROptParam ( + MrcData, + &BestOff, + ChannelMask, + 0xF, + OptDimmOdt, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + 1, // Start + 4, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + for (dimm = 0; dimm < 2; dimm++) { + // + // Function Call for DimmODT Write - 120,60 + // + TrainDDROptParam ( + MrcData, + &BestOff, + ChannelMask, + 0x3 << (dimm * 2), + OptDimmOdtWr, + TestListWr, + sizeof (TestListWr), + ScaleWr, + PwrLimits, + 1, // Start + 2, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + } + } else { + // + // 1DPC (only write) - off,120,60 + // + TrainDDROptParam ( + MrcData, + &BestOff, + ChannelMask, + 0xF, + OptDimmOdt, + TestListWr, + sizeof (TestListWr), + ScaleWr, + PwrLimits, + 0, // Start + 2, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + } + } + } + + Status = mrcSuccess; + + return Status; +} + +/** + This function is the Write Drive Strength training entry point. + This step will optimize write drive strength for performance & power. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - If it succeeds return mrcSuccess +**/ +MrcStatus +MrcWriteDriveStrength ( + IN MrcParameters *const MrcData + ) +{ + MrcStatus Status; + U8 OptParamLC; + U8 RecenterLC; + BOOL Recenter; + + Status = mrcSuccess; + OptParamLC = OPT_PARAM_LOOP_COUNT; + RecenterLC = OPT_PARAM_1D_LC; + Recenter = 1; + + Status = TrainWriteDriveStrength (MrcData, 0x3, RecenterLC, OptParamLC, Recenter); + + return Status; +} + +/** + This function implements the Write Drive Strength optimization for performance and power. + + @param[in] MrcData - Include all MRC global data. + @param[in] ChBitMask - Channel mask to perform training on the Opt Param test list. + @param[in] RecenterLC - The loopcount for Write Time recentering. + @param[in] OptParamLC - The loopcount for training the Opt Param test list. + @param[in] Recenter - Switch which determines if the step recenters Write Timing. + + @retval If it succeeds return mrcSuccess +**/ +MrcStatus +TrainWriteDriveStrength ( + IN MrcParameters *const MrcData, + IN const U8 ChBitMask, + IN const U8 RecenterLC, + IN const U8 OptParamLC, + IN const BOOL Recenter + ) +{ + MrcStatus Status; + MrcOutput *Outputs; + const MrcDebug *Debug; + U8 TestList[] = { WrV, WrT }; + U8 Scale[] = { 1, 2, 1, 0, 0 }; + U16 PwrLimits[5]; + OptOffsetChByte BestOff; + + Status = mrcSuccess; + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + + MrcOemMemorySetWord (PwrLimits, 0, sizeof (PwrLimits) / sizeof (PwrLimits[0])); + PwrLimits[0] = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[1] = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + + TrainDDROptParam ( + MrcData, + &BestOff, + ChBitMask, + 0xf, + OptWrDS, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + -13, // Start + 10, // Stop + OptParamLC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 1 // GuardBand + ); + + if (Recenter) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing\n"); + Status = DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + 0x3, + WrT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + RecenterLC, + 0 // En2D + ); + } + + return Status; +} + +/** + This function implements Write Slew Rate training. + Optimize Write Slew Rate for performance & power + + @param[in] MrcData - Include all MRC global data. + + @retval mrcSuccess +**/ +MrcStatus +MrcWriteSlewRate ( + IN MrcParameters *const MrcData + ) +{ +#ifdef ULT_FLAG + U8 TestList[] = { WrV, WrT }; + U8 Scale[] = { 1, 2, 0, 0, 0 }; + U16 PwrLimits[] = { 2480, 2240, 0, 0, 0 }; // no power consideration + U16 GlobalPwrLimit; + OptOffsetChByte BestOff; + + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[0], PowerLimit); + PwrLimits[0] = MAX (PwrLimits[0], GlobalPwrLimit); + GlobalPwrLimit = UpmPwrLimitValue (MrcData, TestList[1], PowerLimit); + PwrLimits[1] = MAX (PwrLimits[1], GlobalPwrLimit); + + TrainDDROptParam ( + MrcData, + &BestOff, + 0x3, + 0xf, + OptSComp, + TestList, + sizeof (TestList), + Scale, + PwrLimits, + -15, // Start + 8, // Stop + OPT_PARAM_1D_LC, + 1, // Repeats + 0, // NoPrint + 0, // SkipOdtUpdate + 0, // RdRd2Test + 0 // GuardBand + ); + } +#endif + + return mrcSuccess; +} + +/** + Updates a given ch/Rank/byte combination with a new value for OptParam + OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias or DimmOdt + OptParam == OptDefault restore values from Host except Dimms Odt's + @param[in,out] MrcData - Include all MRC global data. + @param[in] Channel - Channel index to work on. + @param[in] Ranks - Condenses down the results from multiple ranks + @param[in] Byte - Byte index to work on. + @param[in] OptParam - Defines the OptParam Offsets. + Supported OptParam = [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 4: TxEq, + 5: RxEq, 6: RxBias, 7: DimmOdt, 8: DimmOdtWr] + @param[in] Off - Offset + @param[in] UpdateHost - Desides if MrcData has to be updated + + @retval Nothing +**/ +void +UpdateOptParamOffset ( + IN OUT MrcParameters *const MrcData, + IN const U8 Channel, + IN const U8 Ranks, + IN const U8 Byte, + IN const U8 OptParam, + IN S16 Off, + IN const U8 UpdateHost + ) +{ + const U16 RttNomMRSEncodingConst[] = {0x00, 0x10, 0x01, 0x11, 0x81, 0x80}; // RttNom Off,120,60,40,30,20 Ohms + const U16 RttWrMRSEncodingConst[] = {0x00, 0x02, 0x01}; // RttWr RttNom,120,60 Ohms + const U16 RttDimmRonEncodingConst[] = {0x00, 0x02}; // Dimm Ron 240/6,240/7 Oms + const MrcDebug *Debug; +#ifdef ULT_FLAG + const U8 LpddrRonEnc[] = {0x1,0x2,0x3}; //{34,40,48}; + const U8 LpddrOdtEnc[] = {0x0,0x2,0x3}; //{0,120,240}; + BOOL Lpddr; +#endif // ULT_FLAG + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + U16 *MrReg; + MrcStatus Status; + BOOL Type; + U8 Rank; + U8 RankMask; + U8 Value; + U8 Index; + U16 MRValue; + U16 RttNomMRSEncoding[sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0])]; + U16 RttWrMRSEncoding[sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0])]; + U16 RttWr, RttNom, RttNomMask; + U16 DimmRon; + U16 RttWrMask; + U16 DimmRonMask; + U32 Offset; + S16 OffCode; + S16 OffMin; + S16 OffMax; + DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp; + DDRSCRAM_CR_DDRMISCCONTROL0_STRUCT DdrMiscControl0; + DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ChannelOut = &Outputs->Controller[0].Channel[Channel]; + MrcOemMemoryCpy ((U8 *) RttNomMRSEncoding, (U8 *) RttNomMRSEncodingConst, sizeof (RttNomMRSEncoding)); + MrcOemMemoryCpy ((U8 *) RttWrMRSEncoding, (U8 *) RttWrMRSEncodingConst, sizeof (RttWrMRSEncoding)); +#ifdef ULT_FLAG + // + // Check if LPDDR3 memory is used + // + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); +#endif // ULT_FLAG + + // + // Compensation Offsets + // + Type = + ( + (OptParam == OptWrDS) || + (OptParam == OptRdOdt) || + (OptParam == OptTComp) || + (OptParam == OptSComp) || + (OptParam == OptDefault) + ); + if (Type) { + if (OptParam == OptWrDS) { + OffMin = -32; + OffMax = 31; + } else { + OffMin = -16; + OffMax = 15; + } + + if (Off > OffMax) { + Off = OffMax; + } else if (Off < OffMin) { + Off = OffMin; + } + + DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte]; + + if (OptParam == OptWrDS) { + DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset = Off; + DdrCrDataOffsetComp.Bits.DqDrvDownCompOffset = Off; + } else if (OptParam == OptRdOdt) { + DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset = Off; + DdrCrDataOffsetComp.Bits.DqOdtDownCompOffset = Off; + } else if (OptParam == OptTComp) { + DdrCrDataOffsetComp.Bits.DqTcoCompOffset = Off; + } else if (OptParam == OptSComp) { + DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset = Off; + } + + Offset = DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG + + ((DDRDATA1CH0_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Byte) + + ((DDRDATA0CH1_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Channel); + MrcWriteCR (MrcData, Offset, DdrCrDataOffsetComp.Data); + if (UpdateHost) { + ChannelOut->DataCompOffset[Byte] = DdrCrDataOffsetComp.Data; + } + // + // Propagate new value and force comp update + // + DdrMiscControl0.Data = Outputs->MiscControl0; + DdrMiscControl0.Bits.ForceCompUpdate = 1; + MrcWriteCR (MrcData, DDRSCRAM_CR_DDRMISCCONTROL0_REG, DdrMiscControl0.Data); + } + // + // Equalization Settings + // + Type = ((OptParam == OptTxEq) || (OptParam == OptRxEq) || (OptParam == OptDefault)); + if (Type) { + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && ((Ranks & (MRC_BIT0 << Rank)))) { + // + // TxEq[5:4] = Emphasize = [3, 6, 9, 12] legs + // TxEq[3:0] = Deemphasize = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 4*Rsvd] legs + // + if (OptParam == OptTxEq) { + if (Off > 11) { + Off = 11; + } + + if (Off < 0) { + Off = 0; + } + + OffCode = Off | TXEQFULLDRV; // Use 12 Emphasize legs (not trained) + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel=%d,Rank= %d update to %x \n",Channel,Rank,OffCode); + UpdateTxT (MrcData, Channel, Rank, Byte, 2, OffCode); + if (UpdateHost) { + ChannelOut->TxEq[Rank][Byte] = (S8) OffCode; + } + } + // + // RxEQ[4:0] CR Decoding (pF/kOhm) + // [2:0] + // [4:3] 0 1 2 3 4 5-7 + // 0 0.5/.02 0.5/1.0 0.5/.50 0.5/.25 0.5/.12 rsvd + // 1 1.0/.02 1.0/1.0 1.0/.50 1.0/.25 1.0/.12 rsvd + // 2 1.5/.02 1.5/1.0 1.5/.50 1.5/.25 1.5/.12 rsvd + // 3 2.0/.02 2.0/1.0 2.0/.50 2.0/.25 2.0/.12 rsvd + // Sweep = 0-19 [4:3] = (Sweep/5) [2:0] = (Sweep%5) + // + if (OptParam == OptRxEq) { + if (Off > 19) { + Off = 19; + } + + if (Off < 0) { + Off = 0; + } + + Value = (U8) (((Off / 5) << 3) + (Off % 5)); + UpdateRxT (MrcData, Channel, Rank, Byte, 2, Value); + if (UpdateHost) { + ChannelOut->RxEq[Rank][Byte] = Value; + } + } + + if (OptParam == OptDefault) { + UpdateTxT (MrcData, Channel, Rank, Byte, 0xff, 0); + UpdateRxT (MrcData, Channel, Rank, Byte, 0xff, 0); + } + } + } + } + // + // RX Amplifier BIAS + // + if ((OptParam == OptRxBias) || (OptParam == OptDefault)) { + if (Off > 7) { + Off = 7; + } + + if (Off < 0) { + Off = 0; + } + // + // Mapping: [0: 0.44, 1: 0.66, 2: 0.88, 3: 1.00, 4: 1.33, 5: 1.66, 6: 2.00, 7: 2.33] + // + DdrCrDataControl1.Data = ChannelOut->DqControl1[Byte].Data; + if (OptParam == OptRxBias) { + DdrCrDataControl1.Bits.RxBiasCtl = Off; + } + + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel); + MrcWriteCR (MrcData, Offset, DdrCrDataControl1.Data); + if (UpdateHost) { + ChannelOut->DqControl1[Byte].Data = DdrCrDataControl1.Data; + } + } + // + // Update Dimm Ron value + // + if ((OptParam == OptDimmRon)) { +#ifdef ULT_FLAG + // + // Check if LPDDR3 memory is used + // + if (Lpddr) { + DimmRonMask = (U16)~(MRC_BIT3 | MRC_BIT2 | MRC_BIT1 | MRC_BIT0); + Index = (U8) Off; + Index = MIN (Index, sizeof (LpddrRonEnc) / sizeof (LpddrRonEnc[0]) - 1); + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = MRC_BIT0 << Rank; + if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0]; + // + // Program Dimm Ron + // + DimmRon = LpddrRonEnc[Index]; + MRValue = (MrReg[mrMR3] & DimmRonMask) | DimmRon; + Status = MrcIssueMrw ( + MrcData, + Channel, + Rank, + mrMR3, + MRValue, + FALSE, // InitMrw + FALSE // ChipSelect2N + ); + if (UpdateHost) { + MrReg[mrMR3] = MRValue; + } + } + } + } else +#endif // ULT_FLAG + { + // + // DIMM Ron Encoding RttNom[A5,A1] + // + DimmRonMask = (U16)~(MRC_BIT5 | MRC_BIT1); + Index = (U8) Off; + Index = MIN (Index, 1); + // + // can be 0 or 1 + // + DimmRon = RttDimmRonEncodingConst[Index]; + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = MRC_BIT0 << Rank; + if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0]; + // + // Program Dimm Ron + // + MRValue = (MrReg[mrMR1] & DimmRonMask) | DimmRon; + Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, MRValue); + if (UpdateHost) { + MrReg[mrMR1] = MRValue; + } + } + } + } + } + // + // DIMM ODT Values + // + if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr)) { +#ifdef ULT_FLAG + if (Lpddr) { + // + // We have only Odt write + // + RttWrMask = (U16)~(MRC_BIT1 | MRC_BIT0); + Index = (U8) Off; + Index = MIN (Index, sizeof (LpddrOdtEnc) / sizeof (LpddrOdtEnc[0]) - 1); + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = MRC_BIT0 << Rank; + if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR11; + MRValue = *MrReg; + // + // Program Dimm DS + // + RttWr = LpddrOdtEnc[Index]; + MRValue = (MRValue & RttWrMask) | RttWr; + Status = MrcIssueMrw ( + MrcData, + Channel, + Rank, + mrMR11, + MRValue, + FALSE, // InitMrw + FALSE // ChipSelect2N + ); + if (UpdateHost) { + *MrReg = MRValue; + } + } + } + + return; + } +#endif + // + // DIMM ODT Encoding RttNom[A9,A6,A2] RttWr[A10, A9] + // + RttNomMask = (U16)~(MRC_BIT9 | MRC_BIT6 | MRC_BIT2); + RttWrMask = (U16)~(MRC_BIT10 | MRC_BIT9); + + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + RankMask = MRC_BIT0 << Rank; + if ((MrcRankInChannelExist (MrcData, Rank, Channel)) && (Ranks & RankMask)) { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0]; + + // + // Program RTT WR + // + Index = (U8) ((OptParam == OptDimmOdt) ? (Off >> 4) : Off); + Index = MIN (Index, 2); + RttWr = RttWrMRSEncoding[Index] << 9; + MRValue = (MrReg[mrMR2] & RttWrMask) | RttWr; + Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR2, MRValue); + if (UpdateHost) { + MrReg[mrMR2] = MRValue; + } + // + // Program RTT NOM + // + if (OptParam == OptDimmOdtWr) { + continue; + } + + Index = ((U8) Off & 0xF); + if (Index > 5) { + Index = 5; + } + + RttNom = RttNomMRSEncoding[Index] << 2; + MRValue = (MrReg[mrMR1] & RttNomMask) | RttNom; + Status = MrcWriteMRS (MrcData, Channel, RankMask, mrMR1, MRValue); + if (UpdateHost) { + MrReg[mrMR1] = MRValue; + } + } + } + } + + return; +} + +/** + Slightly penalize any Asymmetry in margin + + @param[in] NegEdge - Negative edge of the margin + @param[in] PosEdge - Positive edge of the margin + + @retval p2p - Width/Height reduced by the asymmetric difference in margin. +**/ +U16 +EffectiveMargin ( + IN const U16 NegEdge, + IN const U16 PosEdge + ) +{ + S16 p2p; + U16 p2pDiff; + + p2p = 2 * (PosEdge + NegEdge); + p2pDiff = PosEdge - NegEdge; + + if (PosEdge > NegEdge) { + p2p -= p2pDiff; + } else { + p2p += p2pDiff; + } + + return p2p / 2; +} + +/** + This function does a running average on Margins in two dimentional fashion. + + @param[in,out] Margins - Margins to average + @param[in] Test - Selects the Margins to average + @param[in] MLen - Determines the Y-Dimension lengths + @param[in] XDim - Determines the X-Dimension lengths + @param[in] XMin - Used to skip the first elements in the Margin when averaging. + @param[in] CScale - Used to place more weight on the center point. + + @retval Nothing +**/ +void +RunningAverage2D ( + IN OUT U16 Margins[2][24], + IN const U8 Test, + IN const U8 MLen, + IN const U8 XDim, + IN const U8 XMin, + IN const U8 CScale +) + +{ + U8 XMax; + U8 YMax; + U16 TMargins[24]; + U8 i; + U8 x; + U8 y; + U8 xo; + U8 yo; + U8 XOff; + S8 YOff; + + XMax = XDim - 1; + YMax = ((MLen + XDim - 1) / XDim) - 1; // Ceiling to int in case the matrix is not fully populated + + for (i = 0; i < MLen; i++) { + x = (i % XDim); + y = (i / XDim); + + // + // Center Point + // + TMargins[i] = Margins[Test][i] * (CScale - 1); // Also add margin at the centerpoint below + // + // Sum up surrounding results + // + for (xo = 0; xo < 3; xo++) { + XOff = x + xo - 1; + // + // Avoid negative numbers on XOff + // + if ((x == 0) && (xo == 0)) { + XOff = 0; + } + // + // (x < XMin) allows averaging across points (1;0) and (2;0) + // + if ((XOff < XMin) && (x < XMin)) { + XOff = x; // RxEq special case. Skip averaging on Col0/Col1 + } + + if (XOff > XMax) { + XOff = XMax; + } + + for (yo = 0; yo < 3; yo++) { + YOff = y + yo - 1; + if (YOff < 0) { + YOff = 0; + } + + if (YOff > YMax) { + YOff = YMax; + } + // + // Avoid averaging with unpopulated matrix elements when dealing with partially populated matrices + // + if ((XDim * YOff + XOff) > (MLen - 1)) { + YOff = YOff - 1; + } + + TMargins[i] += Margins[Test][XDim * YOff + XOff]; + } + } + } + // + // Copy TempMargins back over to real margins + // + for (i = 0; i < MLen; i++) { + Margins[Test][i] = TMargins[i] / (8 + CScale); // Added div to maintain margin scaling + } + + return; +} + +/** + Updates a given ch/Rank/byte combination with a new value for OptParam + OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias or DimmOdt + + # Margins: Upto 4 arrays that contain lenMargin elements + # Index to the array represents some arbitrary parameter value that we are optimizing + # Scale is 4 element array that scales the relative importance on Margins[0] vs. [1] ... + # ex: To make Margins[0] twice as important, set Scale = [1, 2, 2, 2] + # Since the search optimizes the lowest margin, increasing 1/2/3 makes 0 more important + # This function can be used to optimize only Margin[0] by setting Scale = [1, 0, 0, 0] + # EnSq = 1 uses a squared function to make the tradeoff between 0/1/2/3 steeper + # If AveN > 0, pre-processes the results with a N point running average filter + # IncEnds: By setting to 1, the running average will also include the end points + # ScaleM: Allows the middle point of the running average to be scaled up + # + # In addition to optimizing for margin, this function can also optimize for power + # PwrLimit is a 4 element array that sets level where pwr is more important than margin + # Find any points where ((Margin[0]>PwrLimit[0]) & (Margin[1]>PwrLimit[1]) & ... ) + # If such points exists and PwrOptHigh = 1, returns point with the highest X value + # If such points exists and PwrOptHigh = 0, returns point with the lowest X value + # If you don't want to optimize for power, set PwrLimitA and PwrLimitB to large number + # Power Optimize still uses the running average filter + # + # To avoid overflow, this function will automatic scale margins to fit in uint32 + + @param[in] MrcData - The global MRC data structure. + @param[in,out] OptResByte - Structure containing the optimized results. + @param[in] inputMargins - Margins we are optimizing + @param[in] MarginsLength - The length of inputMargins + @param[in] LenMargin - The length of inputMargins we are optimizing (0 - LenMargin -1). + @param[in] Scale - Controls the scaling of the input margin: 1-1, 1-2, ... and so on. + @param[in] EnSq - Enables the square root term in the optimization functions. + @param[in] AveN - The number of points used for the averaging filter. + @param[in] IncEnds - Controls if the endpoints are to be included. + @param[in] ScaleM - Controls the scaling of the middle point in 1-D average filter. + @param[in] PwrLimit - The power limit above which we only trade-off for power and not margin. + @param[in] PwrOptHigh - Controls returning the highest or lowest optimization point. + @param[in] GuardBand - Signed offest to check if margin drop is acceptable. Save good guardband + in OptResByte. + + @retval Nothing. +**/ +void +FindOptimalTradeOff ( + IN MrcParameters *const MrcData, + IN OUT OptResultsPerByte *OptResByte, + IN void *inputMargins, + IN U8 MarginsLength, + IN S8 LenMargin, + IN U8 *Scale, + IN U8 EnSq, + IN U8 AveN, + IN U8 IncEnds, + IN U8 ScaleM, + IN U16 *PwrLimit, + IN U8 PwrOptHigh, + IN S8 GuardBand + ) + +{ + const MrcDebug *Debug; + U8 NumArr; // Arrays to keep track of results + U32 PostMar[5][MaxOptOff]; // Margin array after scaling & averaging + U32 MaxPost[5]; // Variables for Results + U32 SMaxPost[5]; + U32 MinPost[5]; + U32 Signal[5]; + U32 Noise[5]; + U32 Ratio[5]; + U16 PwrLimitPost[5]; + U32 ScaleMin; + U8 Nby2; + U8 EqOrder; + U8 xArr; + U8 yArr; + U8 x; + U8 i; + U8 Off; + S8 xEff; + S32 n; + U8 NumBits; + U32 localY; + U8 Shift; + U8 Adder; + U8 Start; + U8 Stop; + U64 Result; + U64 rlocal; + U64 MaxR; + U64 MinR; + U64 SNRTotal; + U64 MarginLimit; + U8 BestX; + U8 PowerX; + U8 FoundPwrOpt; + U8 NumCalcArr; + S8 StepSize; + U8 MarginDropPercent; + U32 MinPost1; + BOOL GoodPower; + U16 *Margins; + OptResultsPerByte *calcResults; + + MarginDropPercent = 10; // 10% loss of margin is a bad guardband offset. + NumArr = 5; + Result = 0; + rlocal = 0; + MaxR = 0; + MinR = 0; + SNRTotal = 0; + Debug = &MrcData->SysIn.Inputs.Debug; + MrcOemMemorySetDword (MaxPost, 1, sizeof (MaxPost) / sizeof (U32)); + MrcOemMemorySetDword (SMaxPost, 1, sizeof (SMaxPost) / sizeof (U32)); + MrcOemMemorySetDword (MinPost, 0xFFFFFFFF, sizeof (MinPost) / sizeof (U32)); + MrcOemMemorySetDword (Signal, 0, sizeof (Signal) / sizeof (U32)); + MrcOemMemorySetDword (Noise, 0, sizeof (Noise) / sizeof (U32)); + MrcOemMemorySetDword (Ratio, 0, sizeof (Ratio) / sizeof (U32)); + MrcOemMemorySetWord (PwrLimitPost, 0, sizeof (PwrLimitPost) / sizeof (U16)); + + // + // Initialize PostMar with zeroes + // + MrcOemMemorySet ((U8 *) PostMar, 0, sizeof (PostMar)); + + calcResults = OptResByte; + Margins = (U16 *) inputMargins; + MrcOemMemorySet ((U8 *) calcResults, 0, sizeof (OptResultsPerByte)); + // + // Avoid division by zero. + // + if (AveN == 0) { + AveN = 1; + } + Nby2 = (AveN >> 1); + EqOrder = 0; // Is the optimization equation: X^1, X^2, X^5 + + // + // Process Raw Margins Results + // + for (xArr = 0; xArr < NumArr; xArr++) { + // + // Scale PwrLimit to match PostMar results + // + PwrLimitPost[xArr] = PwrLimit[xArr] * (AveN + ScaleM - 1) * Scale[xArr]; + + for (x = 0; x < LenMargin; x++) { + // + // Calculate the Running Average Filter + // + if (Scale[xArr] == 0) { + // + // not in the game + // + MinPost[xArr] = PostMar[xArr][x] = 1; + } else { + if (x == 0) { + // + // update EqOrder once for each xArr value with a non-zero scale factor e.g.:so for {RdT,RdV,0,0} it will be =2 + // + EqOrder += 1; + } + + for (Off = 0; Off < AveN; Off++) { + xEff = x + Off - Nby2; + if (xEff < 0) { + PostMar[xArr][x] += *(Margins + xArr * MarginsLength + 0); // Margins[xArr][0]; + } else if (xEff >= LenMargin) { + PostMar[xArr][x] += *(Margins + xArr * MarginsLength + LenMargin - 1); + } else if (x == xEff) { + PostMar[xArr][x] += ScaleM * *(Margins + xArr * MarginsLength + xEff); + } else { + PostMar[xArr][x] += *(Margins + xArr * MarginsLength + xEff); + } + } + + if (MaxPost[xArr] < PostMar[xArr][x]) { + MaxPost[xArr] = PostMar[xArr][x]; + } + + if (MinPost[xArr] > PostMar[xArr][x]) { + MinPost[xArr] = PostMar[xArr][x]; + } + // + // signal delta pre/post average filter + // + n = (PostMar[xArr][x] -*(Margins + xArr * MarginsLength + x) * (AveN + ScaleM - 1)); + Noise[xArr] += (n * n); + } + + calcResults->Margins[xArr][x].EW = PostMar[xArr][x] / (AveN + ScaleM - 1); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Margins[%d][%d] =%d\n",xArr,x,calcResults->Margins[xArr][x].EW); + // + } + + if (Scale[xArr] == 0) { + continue; + } + // + // Calculate SNR for this margin result + // For stdev, need sqrt function. Use log domain to change exponential to mult + // Make both Signal and Noise a % of (Max+Min)/2 + // *100 for signal&noise = not go to zero + // + Signal[xArr] = ((MaxPost[xArr] - MinPost[xArr]) * 200 * 100) / (MaxPost[xArr] + MinPost[xArr]); + Noise[xArr] /= LenMargin; + if (Noise[xArr] != 0) { + Noise[xArr] = Mrceexp (MrcNaturalLog (100 * Noise[xArr]) / 2); // result is 100x + } + + Noise[xArr] = (Noise[xArr] * 2 * 100) / (MaxPost[xArr] + MinPost[xArr]); + + if (Noise[xArr] == 0) { + Ratio[xArr] = (Signal[xArr] * 1000); + } else { + Ratio[xArr] = (Signal[xArr] * 1000) / Noise[xArr]; + } + + SMaxPost[xArr] = MaxPost[xArr]; + + // + // update global results + // + calcResults->Scale[xArr] = Scale[xArr]; + calcResults->Signal[xArr] = Signal[xArr]; + calcResults->Noise[xArr] = Noise[xArr]; + calcResults->Ratio[xArr] = Ratio[xArr]; + calcResults->MaxPost[xArr] = MaxPost[xArr] / (AveN + ScaleM - 1); + calcResults->MinPost[xArr] = MinPost[xArr] / (AveN + ScaleM - 1); + // + // 10x the tick diff. + // + calcResults->Ticks[xArr] = (U16) (MaxPost[xArr] - MinPost[xArr]) / (AveN + ScaleM - 1) / (Scale[xArr]); + } + // + // Sort Array + // + MrcBsort (SMaxPost, NumArr); + + // + // Calculate Number of Bits Required to represent this number. Make sure to handle care of EnSq + // + NumBits = 0; + + for (xArr = 0; xArr < NumArr; xArr++) { + if (xArr < (NumArr - 1)) { + // + // if EnSq we do Max^2 so the num get twice the bits... + // + localY = SMaxPost[xArr]; + if (EnSq) { + localY = (localY * localY); + } + + NumBits += MrcLog2 ((U32) localY); + } else { + NumBits += MrcLog2 ((U32) SMaxPost[xArr]); + } + } + + NumBits += 11; // reserved another 10 bits for division in order to format for printing ; 3 for adding - up to 8 + // + // EqOrder for square terms + // + if (EnSq) { + EqOrder = (EqOrder + (EqOrder - 1)); + } + // + // Handle Potential Saturation + // + if (NumBits > 64) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Warning number of bits exceeds 64 bit : %d \n", NumBits); + // + // Shift all numbers to reduce final result to be less than 32 bits. Round Up + // + Shift = (NumBits - 64 + EqOrder - 1) / EqOrder; + // + // RoundUp Adder + // + Adder = (1 << (Shift - 1)); + // + // Divide by (1<<Shift) and Round Up + // + for (xArr = 0; xArr < NumArr; xArr++) { + MaxPost[xArr] = (MaxPost[xArr] + Adder) >> Shift; + PwrLimitPost[xArr] = (PwrLimitPost[xArr] + Adder) >> Shift; + for (x = 0; x < LenMargin; x++) { + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "PostMar[%d][%d] before Shift : %d Adder : %d Shift : %d\n",xArr,x,PostMar[xArr][x],Shift,Adder); + // + PostMar[xArr][x] = (PostMar[xArr][x] + Adder) >> Shift; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "after: %d\n",PostMar[xArr][x]); + // + } + } + } + // + // Calculate Square terms: + // + if (EnSq) { + for (xArr = 0; xArr < NumArr; xArr++) { + MaxPost[xArr] = MaxPost[xArr] * MaxPost[xArr]; + } + } + // + // Set Limits for Search + // + Start = 0; + Stop = LenMargin; + if ((IncEnds == 0) && (LenMargin > AveN)) { + // + // most commonly + // + if (Nby2 > 0) { + Start++; + Stop--; + } + } + // + // Find the Best Point to Use + // + Result = 0; + MaxR = 0; + MinR = ~(0ULL); + BestX = 0; + PowerX = 0; + FoundPwrOpt = 0; + + for (x = Start; x < Stop; x++) { + // + // Find Optimal Point from Margin Point of View + // Combine the points using the formula: + // Max0*Max1*Max2*Post3 + Max1*Max2*Max3*Post0 + Max2*Max3*Max0*Post1 + + // Max3*Max0*Max1*Post2 + Scale*min(Post0,Post1,Post2,Post3)^EqOrder + // Scale = 1 + (10*(SMaxPost[0]-SMaxPost[1]))/SMaxPost[NumArr-1] + // + Result = 0; + MinPost1 = 0xFFFFFFFF; + GoodPower = 1; + for (xArr = 0; xArr < NumArr; xArr++) { + if (Scale[xArr] == 0) { + continue; // not need to calculate those + } + // + // Find Min of all PostMar at offset x + // Does this point meet the min power Margin requirements? + // + if (Scale[xArr] > 0) { + if (MinPost1 > PostMar[xArr][x]) { + MinPost1 = PostMar[xArr][x]; + } + + if (PostMar[xArr][x] < PwrLimitPost[xArr]) { + GoodPower = 0; // not ! //@todo: delete this power limit for this routing + } + } + // + // Calculate this portion of result + // + rlocal = 1; + for (yArr = 0; yArr < NumArr; yArr++) { + if (Scale[yArr] == 0) { + continue; // not need to calculate those + } + + if (xArr == yArr) { + continue; + } else { + rlocal = MrcOemMemoryMultiplyU64ByU32 (rlocal, MaxPost[yArr]); + } + } + + Result += MrcOemMemoryMultiplyU64ByU32 (rlocal, PostMar[xArr][x]); + } + + NumCalcArr = 0; + for (xArr = 0; xArr < NumArr; xArr++) { + // + // required for following step. + // + if (Scale[xArr] != 0) { + NumCalcArr++; + } + } + // + // Add in (MinPost ^ EqOrder) + // If NumCalcArr is 0, set it to 1 so that it still in the range of array size. + // + if (NumCalcArr == 0) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: wrong input parameter caused NumCalcArr = 0 when calling FindOptimalTradeOff()\n" + ); + NumCalcArr = 1; + } + + ScaleMin = 1 + (10 * (SMaxPost[0] - SMaxPost[1])) / SMaxPost[NumCalcArr - 1]; + if (ScaleMin > 5) { + ScaleMin = 5; + } + + ScaleMin = 1; + rlocal = ScaleMin; + for (i = 0; i < EqOrder; i++) { + rlocal = MrcOemMemoryMultiplyU64ByU32 (rlocal, MinPost1); + } + + Result += rlocal; + + if (Result < MinR) { + MinR = Result; + } + + if (Result > MaxR) { + MaxR = Result; + BestX = x; // save first highest function result offset + } + + calcResults->Result[x] = Result; + // + // Find Optimal Point from Power Point of View + // + if (GoodPower) { + // + // are all the point meet margins requirements for all Tests ? + // + if (FoundPwrOpt == 0) { + FoundPwrOpt = 1; // power optimization is possible + PowerX = x; // first point passing to power limits + } else { + if ((PwrOptHigh == 1) && (x > PowerX)) { + PowerX = x; // we take the less power save point + } + + if ((PwrOptHigh == 0) && (x < PowerX)) { + PowerX = x; // @todo: how can it be ? x is alwaye increasing + } + } + } + } // end shmoo offsets + if ((MaxR + MinR) == 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "warninig : MaxR+MinR are Zero !!!\n"); + } + // + // Record for debug purposes. + // more simple: 1000*(max-min)/((max+min)/2) + // + SNRTotal = MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 ((MaxR - MinR), 2000), (MaxR + MinR + 1)); + // + // Are we optimizing for Power or Margin? + // + if (FoundPwrOpt) { + if ((PwrOptHigh == 1) && (BestX < PowerX)) { + BestX = PowerX; + } + // + // if ((PwrOptHigh==0) && (BestX>PowerX)) BestX = PowerX;//give the more power saving offset that meet power limits + // + } + + calcResults->Best = BestX; + calcResults->SNRTotal = SNRTotal; + calcResults->MaxR = MaxR; + calcResults->MinR = MinR; + // + // Apply a guard band to the best setting, clamped at edges of the search. + // + if (GuardBand != 0) { + // + // Determine step direction and limit to the search edge. + // + if (GuardBand < 0) { + StepSize = 1; + Off = ((BestX + GuardBand) < Start) ? Start : (BestX + GuardBand); + } else { + StepSize = -1; + Off = ((BestX + GuardBand) >= Stop) ? (Stop - 1) : (BestX + GuardBand); + } + // + // Check each test for margin drop of MarginDropPercent. + // If any test fails, we step towards the original selection. + // + MarginLimit = MrcOemMemoryMultiplyU64ByU32 (calcResults->Result[BestX], (100 - MarginDropPercent)); + MarginLimit = MrcOemMemoryDivideU64ByU64 (MarginLimit, 100); + for(; (Off != BestX); Off += StepSize) { + if (calcResults->Result[Off] > MarginLimit) { + break; + } + } + + calcResults->GuardBand = Off - (S8) BestX; + } + + return; +} + +/** + This function implements Turn Around Timing training. + Optimize TA ODT Delay and Duration + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - If it succeeds return mrcSuccess. +**/ +MrcStatus +MrcTurnAroundTiming ( + IN MrcParameters *const MrcData + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcInput *Inputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + MrcStatus Status; + U8 Channel; + U8 RankMaskCh; + U8 RankMask; + BOOL RunDD; + BOOL RunDR; + U8 ParamList[4]; // List of parameters to margin + U8 TestListRd[2]; + U8 TestListWr[2]; + U8 GuardBand; + U8 NomWR2RD; + U8 Update; + U8 LoopCount; + S8 ClkShifts[2]; + U32 Offset; + + Status = mrcSuccess; + RankMaskCh = 0; + Update = 1; + LoopCount = 12; + RunDD = FALSE; + RunDR = FALSE; + NomWR2RD = 0; + RankMask = 0xF; + ParamList[0] = RdV; + ParamList[1] = RdT; + ParamList[2] = WrV; + ParamList[3] = WrT; + TestListRd[0] = RdV; + TestListRd[1] = RdT; + TestListWr[0] = WrV; + TestListWr[1] = WrT; + ClkShifts[0] = -7; + ClkShifts[1] = 7; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Inputs = &MrcData->SysIn.Inputs; + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + if (!ChannelOut->ValidRankBitMask) { + continue; + } + + RankMaskCh = ChannelOut->ValidRankBitMask; + RunDD = RunDD || (ChannelOut->DimmCount == 2); + RunDR = RunDR || ((RankMaskCh & 0xC) == 0xC) || ((RankMaskCh & 0x3) == 0x3); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Channel %d: RunDR = 0x%x, RunDD = 0x%x, RankMaskCh = 0x%x\n", + Channel, + RunDR, + RunDD, + RankMaskCh + ); + + // + // Use nominal values (previuosly programmed) +1 an -1 to test. + // + NomWR2RD = (U8) + ( + (ChannelOut->MchbarBANKRANKB & MCHBAR_CH0_CR_TC_BANK_RANK_B_tWRRD_dr_MSK) >> + MCHBAR_CH0_CR_TC_BANK_RANK_B_tWRRD_dr_OFF + ); + } + // + // Program SAFE values for ODT and SAmp + // + GuardBand = 1; + UpdateSampOdtTiming (MrcData, GuardBand); + + // + // Sweep ODT values but do not apply optimized value yet (Data Collection Only) + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running mcodts\n"); + Status = TrainDDROptParamCliff ( + MrcData, + mcodts, + TestListRd, + sizeof (TestListRd), + 0, + 2 + GuardBand, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + GuardBand + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running mcodtd\n"); + Status = TrainDDROptParamCliff ( + MrcData, + mcodtd, + TestListRd, + sizeof (TestListRd), + (-1 - GuardBand), + 0, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + GuardBand + ); + + // + // Restore SAFE values when ONLY collecting data + // + if (Update == 0) { + UpdateSampOdtTiming (MrcData, GuardBand); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + if (!ChannelOut->ValidRankBitMask) { + continue; + } + Offset = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel); + MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKD); + } + } + // + // Sweep DD Timing but do not apply optimized value yet (Data Collection Only) + // + if (RunDD) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDRD2RD\n"); + Status = TrainDDROptParamCliff ( + MrcData, + ddrd2rd, + TestListRd, + sizeof (TestListRd), + 6, + 7, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDWR2WR\n"); + Status = TrainDDROptParamCliff ( + MrcData, + ddwr2wr, + TestListWr, + sizeof (TestListWr), + 7, + 8, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DDWR2RD\n"); + Status = TrainDDROptParamCliff ( + MrcData, + ddwr2rd, + ParamList, + sizeof (ParamList), + NomWR2RD - 1, + NomWR2RD + 1, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + } + // + // Sweep DR Timing but do not apply optimized value yet (Data Collection Only) + // + if (RunDR) { + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRRD2RD\n"); + Status = TrainDDROptParamCliff ( + MrcData, + drrd2rd, + TestListRd, + sizeof (TestListRd), + 6, + 7, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRWR2WR\n"); + Status = TrainDDROptParamCliff ( + MrcData, + drwr2wr, + TestListWr, + sizeof (TestListWr), + 7, + 8, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n ##### Running DRWR2RD\n"); + Status = TrainDDROptParamCliff ( + MrcData, + drwr2rd, + ParamList, + sizeof (ParamList), + NomWR2RD - 1, + NomWR2RD + 1, + LoopCount, + Update, + Outputs->MarginResult, + ClkShifts, + sizeof (ClkShifts), + 0, + RankMask, + 0 + ); + } + // + // Restore SAFE values when ONLY collecting data + // + if (Update == 0) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + if (ChannelOut->ValidRankBitMask) { + Offset = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel); + MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKA); + + Offset = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel); + MrcWriteCR (MrcData, Offset, ChannelOut->MchbarBANKRANKB); + + } + } + } + + return Status; +} + +/** + General purpose function to optimize an abritray value, OptParam (see list above) + OptParam is generally some timing number that impacts performance or power + Expects that as OptParam gets smaller*, margins are flat until we hit a cliff + This procedure defines a cliff as a reducution of 4 ticks in eye height/width + * In the case of mcodts, higher values are actually worst + To stress out the timing, xxDDR_CLK is shifted by +/- 15 PI ticks + + @param[in] MrcData - Include all MRC global data. + @param[in] OptParam - Supports Turnaround Timings and ODT Start / Duration + @param[in] TestList - List of margin param to check to make sure timing are okay. + @param[in] NumTests - The size of TestList + @param[in] Start - Start point for this turn around time setting. + @param[in] Stop - Stop point for this turnaround time setting. + Note that the Start/Stop values are the real values, not the encoded value + @param[in] LoopCount - Length of a given test + @param[in] Update - Update the CRs and host structure with ideal values + @param[in] ClkShifts - Array of Pi clocks to be shifted + @param[in] MarginByte - Byte level margins + @param[in] NumR2RPhases - Number of PI clock phases + @param[in] rank - rank to work on + @param[in] RankMask - RankMask to be optimized + @param[in] GuardBand - GuardBand to be added to last pass value (to be a bit conservative). + + @retval MrcStatus - If it succeeds return mrcSuccess +**/ +MrcStatus +TrainDDROptParamCliff ( + IN MrcParameters *const MrcData, + IN U8 OptParam, + IN U8 TestList[], + IN U8 NumTests, + IN S8 Start, + IN S8 Stop, + IN U8 LoopCount, + IN U8 Update, + IN U32 MarginByte[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES], + IN S8 *ClkShifts, + IN U8 NumR2RPhases, + IN U8 rank, + IN U8 RankMask, + IN U8 GuardBand + ) +{ + const MRC_REUTAddress REUTAddressConst = { + {0, 0, 0, 0}, // Start + {7, 0, 0, 1023}, // Stop + {0, 0, 0, 0}, // Order + {0, 0, 0, 0}, // IncRate + {1, 0, 0, 1}}; // IncValue + const U8 OptParamDDType[13] = {1, 2, 1, 2, 1, 2, 1, 2, 3, 3, 3, 3, 0}; // Does this test run dr, dd or both? + const U8 RankMapping[16] = {15, 15, 15, 4, 15, 3, 15, 1, 15, 15, 15, 15, 5, 2, 15, 0}; + // Order of rank turnarounds for dr & dd. + const U32 RankOrder[2][6] = {{0x32320101, 0x20101010, 0x23232320, 0x20, 0x10, 0x23}, // RankOrder[0]: drsd - same DIMM + {0x21303120, 0x2120, 0x3020, 0x20, 0, 0}}; // RankOrder[1]: drdd - diff DIMM + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + MrcStatus Status; + MRC_REUTAddress REUTAddress; + MRC_WDBPattern WDBPattern; // For 8 bit VA, this walks through each WDB pointer ~ 2X + BOOL IsDual; + BOOL ODT; + BOOL PerByte; + BOOL NotRankTraining; + BOOL Lpddr; + BOOL FindFirstPass; + U32 BERStats[4]; // Track BER results + U32 RankList; + U32 Offset; + U32 CRValue; + U16 Margins[4][2][2][MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Tests X DR/DD x ClkPhases x Ch X Byte + U16 NumCL; // Number of cachelines per SubSeq + U16 m; + U16 SeqLC; + U16 MinMarginLimit; + U8 ShiftValue; + U8 Channel; + U8 ChannelMask; + U8 Byte; + U16 ByteMask; + U8 Rank; + U8 ChBitMask; + U8 RankCount; + U8 ChBitMaskdd; + U8 RankMaskCh; + U8 drddPresent[2]; // [0]: ChBitMask for dr, [1]: ChBitMask for dd + U8 CmdPat; + U8 BMap[9]; // Needed for GetBERMarginByte function + U8 MarginLimit; // Need to change it to 20%of eye heigth + U8 ResetDDR; + U8 SelfRefresh; + U8 RankInc; // Increment every cacheline (HW adds +1 automatically) + U16 ByteFailMask[MAX_CHANNEL]; // Per ch mask indicating which bytes have failed + U8 offs[MAX_CHANNEL]; + U8 Param; + U8 iparam; + U16 ByteDone; + U8 dd; + U8 test0; + U8 v0; + U8 Mode; + U8 RankOrderIndex; + U8 UpdateHostMargin; + U8 Done; + U8 MaxMargin; + U8 ResultType; + U8 WDBIncRate; // Number of cachelines between incrementing WDB. + U8 LoopEnd; + S8 Inc; + S8 Off; + S8 Index; + S8 LastPass[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; // Lass Pass Value for off + S8 Begin; + S8 End; + S8 ChLastPass; + S8 ActualGuardBand; +#ifdef MRC_DEBUG_PRINT + S8 ChLastPass1[MAX_CHANNEL]; +#endif // MRC_DEBUG_PRINT + MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_STRUCT ReutChSeqRankL2PMapping; + + Status = mrcSuccess; + Done = 0; + Rank = 0; + drddPresent[0] = 0; + drddPresent[1] = 0; + MarginLimit = (rtl == OptParam) ? 10 : 20; // Drop of X% in margin means failure + ResetDDR = 1; + SelfRefresh = 0; + WDBIncRate = 13; + NumCL = 128; + // + // For {8,5,4,3,2} ranks, this covers each rank ~ {3,5,6,8,12}X + // For 8 bit VA, this walks through each WDB pointer ~ 2X + // + WDBPattern.IncRate = 0; + WDBPattern.Start = 0; + WDBPattern.Stop = 7; + WDBPattern.DQPat = 0; + MrcOemMemorySetWord (ByteFailMask, 0, sizeof (ByteFailMask) / sizeof(ByteFailMask[0])); + MrcOemMemorySet (offs, 0, sizeof (offs)); + MrcOemMemorySet ((U8 *) BERStats, 0, sizeof (BERStats)); + MrcOemMemoryCpy ((U8 *) &REUTAddress, (U8 *) &REUTAddressConst, sizeof (REUTAddress)); + for (Byte = 0; Byte < (sizeof (BMap) / sizeof (BMap[0])); Byte++) { + BMap[Byte] = Byte; + } + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); + + NotRankTraining = (OptParam == rtl); + FindFirstPass = (OptParam == rtl); // FindFirstPass logic only works for RTL! + ODT = (OptParam == rdodtd) || (OptParam == wrodtd) || (OptParam == mcodtd) || (OptParam == mcodts); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "\nNotRankTraining = %u, ODT = %d\n", NotRankTraining, ODT); + + // + // Decide which channels need to be run and program NumCachelines + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + if (ChannelOut->ValidRankBitMask) { + ChannelMask = MRC_BIT0 << Channel; + RankMaskCh = ChannelOut->ValidRankBitMask; + IsDual = ((RankMaskCh & 0xC) == 0xC) || ((RankMaskCh & 0x3) == 0x3); + + // + // Continue if no ranks in this channel + // + if ((RankMaskCh & RankMask) == 0) { + continue; + } + + if ((OptParamDDType[OptParam] & 0x2) && (ChannelOut->DimmCount == 2)) { + drddPresent[1] |= ChannelMask; // dd parameter and channel has 2 DIMMs + } + + if (((OptParamDDType[OptParam] & 0x1) && IsDual) || NotRankTraining) { + drddPresent[0] |= ChannelMask; // dr parameter and channel has a dual rank + } + + if (ODT && ((drddPresent[0] & (1 << Channel)) == 0)) { + // + // ODT matters when Single rank + // dr parameter and channel has a dual rank + // + drddPresent[0] |= ChannelMask; + } + } + } + + ChBitMask = drddPresent[1] | drddPresent[0]; // Chanel is present if it has either a dr or dd + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "drddPresent[0] = 0x%x, drddPresent[1] = 0x%x, ChBitMask = 0x%x\n", + drddPresent[0], + drddPresent[1], + ChBitMask + ); + + // + // There is nothing to optimize for this parameter + // + if ((ChBitMask == 0) || (Stop <= Start)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ChBitMask = %d, Start = 0x%x, Stop = 0x%x\n", ChBitMask, Start, Stop); + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "No need to optimized TA, OptParam = %d\n", OptParam); + return mrcFail; + } + // + // Setup the REUT Test + // + SeqLC = LoopCount; + RankInc = 0; + Outputs->DQPat = TurnAround; + if ((OptParam == ddwr2rd) || (OptParam == drwr2rd)) { + CmdPat = PatWrRdTA; + Outputs->DQPat = TurnAroundWR; + RankInc = 1; + } else if (ODT) { + CmdPat = PatODTTA; + Outputs->DQPat = TurnAroundODT; + RankInc = 1; + } else if (OptParam == rtl) { + CmdPat = PatWrRd; + // + // Less optimistic values since we are updating values and RMT fails + // + WDBIncRate = 16; + NumCL = 4; + } else { + CmdPat = PatWrRd; + } + + WDBPattern.DQPat = Outputs->DQPat; + WDBPattern.IncRate = WDBIncRate; + REUTAddress.IncRate[0] = RankInc; + REUTAddress.IncRate[3] = RankInc; + + // + // SOE=0, EnCADB=0, EnCKE=0, SubSeqWait=0 + // + SetupIOTest (MrcData, ChBitMask, CmdPat, NumCL, (U8) SeqLC, &REUTAddress, NSOE, &WDBPattern, 0, 0, 0); + + Outputs->DQPatLC = MRC_BIT0 << (LoopCount - MrcLog2 ((U32) (NumCL - 1))); + if (Outputs->DQPatLC < 1) { + Outputs->DQPatLC = 1; + } + // + // Optimize parameter per byte. Everything else is per channel + // + PerByte = (OptParam == mcodts) || (OptParam == mcodtd); + + // + // Keep track of which bytes have failed and are we done yet + // + ByteDone = (1 << Outputs->SdramCount) - 1; + + // + // ########################################################### + // #### Loop through OptParam X DD X ClkPhases X Params and measure margin ##### + // ########################################################### + // + if (OptParam == mcodts) { + // + // In the case of mcodts, higher values are actually worst. + // + Begin = Start; + End = Stop; + Inc = 1; + } else { + Begin = Stop; + End = Start; + Inc = -1; + } + + ActualGuardBand = (Inc * GuardBand); + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Start = %d, Stop = %d, Begin = %d, End = %d, Inc = %d\n", + Start, + Stop, + Begin, + End, + Inc + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (OptParam == rtl) ? "Rank = %d\n" : "", rank); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel 0\t\t\t\t\t\t\t\t1\nByte\t"); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, ( + Outputs->SdramCount == MAX_SDRAM_IN_DIMM + ) ? "0\t1\t2\t3\t4\t\t5\t6\t7\t8\t0\t1\t2\t3\t4\t5\t6\t7\t8\n" : + "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7\n" + ); + + // + // Init Variables + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + LastPass[Channel][Byte] = Begin - ActualGuardBand; + for (iparam = 0; iparam < NumTests; iparam++) { + for (dd = 0; dd < 2; dd++) { + for (test0 = 0; test0 < NumR2RPhases; test0++) { + Margins[iparam][dd][test0][Channel][Byte] = 1280; + } + } + } + } + } + } + // + // Walk through different OptParam values + // + for (Off = (S8) Begin; Off != (S8) (End + Inc); Off += Inc) { + if (Done) { + break; + } + Index = (Off - Begin) * Inc; // Index = 0, 1, 2.. + // + // Inc can only take a value of +/- 1. + // + if ((Index == 1) && (TRUE == FindFirstPass)) { + Inc *= -1; + Off = End; + End = Begin - Inc; // One Inc less since we have already done Index 0. + Begin = Off - Inc; // One Inc less to get us starting at Index 1 + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Find First Pass - Walking backwards.\n Off = %d, Begin = %d, End = %d, Inc = %d, Index = %d\n", + Off, + Begin, + End, + Inc, + (Off - Begin) * Inc + ); + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Param^ Offset-> %d\n Actl\t", Off); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + // + // if nothing for this channel OR No Ranks in this channel + // + if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) { +#ifdef MRC_DEBUG_PRINT + if (Channel == 0) { + if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t"); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t"); + } + } +#endif // MRC_DEBUG_PRINT + continue; + } + // + // For debug purposes program Row start stop to OptParam + Offset value + // OptParam in upper BYTE + // + Offset = 4 + MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG + + ( + (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_START_MCMAIN_0_REG) * + Channel + ); + CRValue = MrcReadCR (MrcData, Offset); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "upper SEQ_BASE_ADDR_START BEFORE = 0x%x ", CRValue); + CRValue = MrcBitSwap (CRValue, OptParam, 0, 8); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue); + MrcWriteCR (MrcData, Offset, CRValue); + // + // Offset in Lower BYTE + // + Offset -= 4; + CRValue = MrcReadCR (MrcData, Offset); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "lower SEQ_BASE_ADDR_START BEFORE = 0x%x ", CRValue); + CRValue = MrcBitSwap (CRValue, Off, 24, 8); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue); + MrcWriteCR (MrcData, Offset, CRValue); + + // + // OptParam in upper BYTE + // + Offset = 4 + MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG + + ( + (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) * + Channel + ); + CRValue = MrcReadCR (MrcData, Offset); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "upper SEQ_BASE_ADDR_WRAP BEFORE = 0x%x ", CRValue); + CRValue = MrcBitSwap (CRValue, OptParam, 0, 8); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue); + MrcWriteCR (MrcData, Offset, CRValue); + // + // Offset in Lower BYTE + // + Offset -= 4; + CRValue = MrcReadCR (MrcData, Offset); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "lower SEQ_BASE_ADDR_WRAP BEFORE = 0x%x ", CRValue); + CRValue = MrcBitSwap (CRValue, Off, 24, 8); + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "After = 0x%x\n", CRValue); + MrcWriteCR (MrcData, Offset, CRValue); + + // + // No need to update MrcData host during this step even if not collecting data + // + LoopEnd = (U8) ((PerByte) ? Outputs->SdramCount : 1); + for (Byte = 0; Byte < LoopEnd; Byte++) { + UpdateTAParamOffset (MrcData, Channel, Byte, OptParam, Off, 0, 0, RankMaskCh); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + + // + // Test both: different dr and dd as required + // + for (dd = 0; dd < 2; dd++) { + if (Done) { + break; + } + // + // Check if this test type should be run + // + ChBitMaskdd = drddPresent[dd]; + if (ChBitMaskdd == 0) { + continue; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (dd == 0) ? "Dual Rank\n" : "Dual Dimm\n"); + // + // Select Ranks in the correct order based on the test type + // Need to re-order the ranks based on the value of ddw2r + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + if (RankMaskCh == 0) { + continue; // No Ranks in this channel + } + // + // Initialize variables and read out ordered rank list + // + ReutChSeqRankL2PMapping.Data = 0; + RankCount = 0; + + if (NotRankTraining) { + RankList = 0x00003210; + } else { + RankOrderIndex = RankMapping[RankMaskCh]; + if (RankOrderIndex == 15) { + RankList = 0x00003210; + } else { + RankList = RankOrder[dd][RankOrderIndex]; + } + } + + while (RankList > 0) { + Rank = (RankList & 0xF); // Nibble by Nibble + RankList = (RankList >> 4); + if (!(RankMaskCh & (MRC_BIT0 << Rank))) { + continue; + } + + ShiftValue = RankCount * + MRC_BIT0 << + MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_Logical_to_Physical_Rank0_Mapping_WID; + ReutChSeqRankL2PMapping.Data |= (Rank << ShiftValue); + RankCount++; + } + + Offset = MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG + + ( + ( + MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_1_REG - + MCDFXS_CR_REUT_CH_SEQ_RANK_LOGICAL_TO_PHYSICAL_MAPPING_MCMAIN_0_REG + ) * Channel + ); + MrcWriteCR (MrcData, Offset, ReutChSeqRankL2PMapping.Data); + Offset = MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG + + ( + (MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_1_REG - MCDFXS_CR_REUT_CH_SEQ_BASE_ADDR_WRAP_MCMAIN_0_REG) * + Channel + ) + 7; + MrcWriteCR8 (MrcData, Offset, RankCount - 1); + } + // + // ################################################### + // ### Walk through different sets of rank2rank timings ### + // ################################################### + // + for (test0 = 0; test0 < NumR2RPhases; test0++) { + if (Done) { + break; + } + + v0 = ClkShifts[test0]; + + // + // Program rank offsets differently for dd vs. dr + // + if (NotRankTraining) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) { + offs[Channel] = 0; + } else { + // + // Shift all signals in the channel(Clk/Ctl/Cmd/Dq) by v0 + // + offs[Channel] = v0; + } + } + // + // UpdateHost=0, SkipTx=0 + // + ShiftCh2Ch (MrcData, RankMask, offs, ResetDDR, SelfRefresh, 0, 0); + } else if (dd == 1) { + // + // For DD + // Shift Clk/DQ on one DIMM by v0 and Clk/DQ on other DIMM by -v0 + // @todo: CTL picode should be optionally shifted to improve margins + // + SetCmdMargin (MrcData, ChBitMaskdd, 0x3, WrT, v0, 0, ResetDDR, SelfRefresh); + SetCmdMargin (MrcData, ChBitMaskdd, 0xC, WrT, -v0, 0, ResetDDR, SelfRefresh); + } else { + // + // For DR + // Shift Clk/DQ on front side by v0 and Clk/DQ on backside by -v0 + // @todo: CTL picode should be optionally shifted to improve margins + // + SetCmdMargin (MrcData, ChBitMaskdd, 0x5, WrT, v0, 0, ResetDDR, SelfRefresh); + SetCmdMargin (MrcData, ChBitMaskdd, 0xA, WrT, -v0, 0, ResetDDR, SelfRefresh); + } + // + // Test different margin param + // + for (iparam = 0; iparam < NumTests; iparam++) { + Param = TestList[iparam]; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s ", MarginTypesString[Param]); + if (Param == 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, " WARNING! UNNECESSARY LOOPS. Param = %d \n", Param); + return mrcFail; + } + + ResultType = GetMarginResultType (Param); + + // + // Get the width/height limit for the parameter + // + MinMarginLimit = UpmPwrLimitValue (MrcData, Param, UpmLimit); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d ", MinMarginLimit); + // Calculate MaxMargin and Starting Point for margin search + // + MaxMargin = MAX_POSSIBLE_TIME; + if ((Param == RdV) || + (Param == RdFan2) || + (Param == RdFan3) || + (Param == WrV) || + (Param == WrFan2) || + (Param == WrFan3) + ) { + MaxMargin = MAX_POSSIBLE_VREF; + } + // + // Are we done yet or should we keep testing? + // + Done = 1; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(ChBitMaskdd & (MRC_BIT0 << Channel))) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + if (RankMaskCh == 0) { + continue; // No Ranks in this channel + } + + // + // When FindFirstPass is used, all Bytes have to have passed before we stop. + // We uses ByteFailMask[] to track the passing bytes in this case. + // + if (PerByte || FindFirstPass) { + if (ByteFailMask[Channel] != ByteDone) { + Done = 0; + } + } else { + if (ByteFailMask[Channel] == 0) { + Done = 0; + } + } + } + + if (Done) { + break; + } + + Status = GetMarginByte (MrcData, Outputs->MarginResult, Param, 0, 0xF); + if (Status != mrcSuccess) { + return Status; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 3d\t", (S8) v0); + + Mode = 0; + Status = MrcGetBERMarginByte ( + MrcData, + Outputs->MarginResult, + ChBitMaskdd, + rank, + 0xFF, + Param, + Mode, + BMap, + 1, + MaxMargin, + 0, + BERStats + ); + if (Status != mrcSuccess) { + return Status; + } + // + // Record Results + // + UpdateHostMargin = 1; + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + // + // if nothing for this channel OR No Ranks in this channel + // + if (!((MRC_BIT0 << Channel) & ChBitMaskdd) || (RankMaskCh == 0)) { +#ifdef MRC_DEBUG_PRINT + if (Channel == 0) { + if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t"); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t"); + } + } +#endif // MRC_DEBUG_PRINT + continue; + } + + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + // + // For this optimization, it makes more sense to look at the full sum + // + ByteMask = MRC_BIT0 << Byte; + m = EffectiveMargin ( + (U16) MarginByte[ResultType][rank][Channel][Byte][0], + (U16) MarginByte[ResultType][rank][Channel][Byte][1] + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d", m); + + // + // If previously failed, this is also a failure unless we are looking for + // the first passing offset. + // + if ((ByteFailMask[Channel] & ByteMask) && (FALSE == FindFirstPass)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "#\t"); + continue; + } + // + // Byte fails if margin is below MinMarginLimit at any time + // + if (m < MinMarginLimit) { + // + // If we are looking for pass, continue and do not update LastPass + // + if (TRUE == FindFirstPass) { + if (Index == 0) { + // + // When training from the most aggressive setting to the conservative setting, + // if we fail the first setting we stop. + // + ByteFailMask[Channel] = ByteDone; + } + UpdateHostMargin = 0; + } else { + ByteFailMask[Channel] |= ByteMask; + LastPass[Channel][Byte] = Off - Inc - ActualGuardBand; + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "#\t"); + continue; + } + + if (Index == 0) { + // + // Get the smallest marging at Index 0 + // + if (Margins[iparam][dd][test0][Channel][Byte] > m) { + Margins[iparam][dd][test0][Channel][Byte] = m; + } + } else { + // + // Check if we dropped more than the percent allowed + // + if (m < ((Margins[iparam][dd][test0][Channel][Byte] * (100 - MarginLimit)) / 100)) { + if (FALSE == FindFirstPass) { + ByteFailMask[Channel] |= ByteMask; + LastPass[Channel][Byte] = Off - Inc - ActualGuardBand; + } + UpdateHostMargin = 0; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "#-%d\t", + (ABS (m - Margins[iparam][dd][test0][Channel][Byte]) * 100) / Margins[iparam][dd][test0][Channel][Byte] + ); + continue; + } else { + if (TRUE == FindFirstPass) { + if ((ByteFailMask[Channel] & ByteMask) != ByteMask) { + LastPass[Channel][Byte] = Off - ActualGuardBand; + ByteFailMask[Channel] |= ByteMask; + } + } else { + LastPass[Channel][Byte] = Off - ActualGuardBand; + } + } + } + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + ".%c%d\t", + (m > Margins[iparam][dd][test0][Channel][Byte]) ? '+' : '-', + (ABS(m - Margins[iparam][dd][test0][Channel][Byte]) * 100) / Margins[iparam][dd][test0][Channel][Byte] + ); + } + } + + if (UpdateHostMargin) { + Status = ScaleMarginByte (MrcData, Outputs->MarginResult, Param, rank); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + + // + // Clean up + // + if (NotRankTraining) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + offs[Channel] = 0; + } + // + // UpdateHost=0, SkipTx=0 + // + ShiftCh2Ch (MrcData, RankMask, offs, ResetDDR, SelfRefresh, 0, 0); + } else { + SetCmdMargin (MrcData, ChBitMaskdd, RankMask, WrT, 0, 0, ResetDDR, SelfRefresh); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } + } + + // + // If we are sweeping agressive settings to conservative settings, we + // need to restore original Inc, Begin, and End values to select the + // proper offset if bytes have varying offsets values for a parameter + // that is NOT specified per Byte. + // + if (TRUE == FindFirstPass) { + Off = End; // Temp storage for swap + End = Begin + Inc; + Begin = Off + Inc; + Inc *= -1; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Find First Pass - Reverting Inc, Begin, and End\n Begin = %d, End = %d, Inc = %d,\n", + Begin, + End, + Inc + ); + } + +#ifdef MRC_DEBUG_PRINT + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Optimal offset per Byte\n\t"); + // + // Print OPTIMAL value + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + ChLastPass1[Channel] = End; + // + // if nothing for this channel OR No Ranks in this channel + // + if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) { + if (Channel == 0) { + if (Outputs->SdramCount == (MAX_SDRAM_IN_DIMM - 1)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t"); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t\t"); + } + } + + continue; + } + + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", LastPass[Channel][Byte]); + if ((Inc == 1) && (ChLastPass1[Channel] > LastPass[Channel][Byte])) { + ChLastPass1[Channel] = LastPass[Channel][Byte]; + } + + if ((Inc == -1) && (ChLastPass1[Channel] < LastPass[Channel][Byte])) { + ChLastPass1[Channel] = LastPass[Channel][Byte]; + } + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + // + // if nothing for this channel OR No Ranks in this channel + // + if (!((MRC_BIT0 << Channel) & ChBitMask) || (RankMaskCh == 0)) { + continue; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Optimal offset Channel %d = %d\n", Channel, ChLastPass1[Channel]); + } +#endif // MRC_DEBUG_PRINT + // + // Program new value + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChBitMask)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = ChannelOut->ValidRankBitMask & RankMask; + if (RankMaskCh == 0) { + continue; // No Ranks in this channel + } + // + // Start with the most aggressive setting + // + ChLastPass = End; + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if (Update == 0) { + LastPass[Channel][Byte] = Begin; + } + + if ((Inc == 1) && (ChLastPass > LastPass[Channel][Byte])) { + ChLastPass = LastPass[Channel][Byte]; + } + + if ((Inc == -1) && (ChLastPass < LastPass[Channel][Byte])) { + ChLastPass = LastPass[Channel][Byte]; + } + + if (PerByte) { + UpdateTAParamOffset (MrcData, Channel, Byte, OptParam, LastPass[Channel][Byte], Update, 1, RankMaskCh); + } + } + + if (PerByte == 0) { + UpdateTAParamOffset (MrcData, Channel, 0, OptParam, ChLastPass, Update, 1, RankMaskCh); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Selected Offset for channel %d is = %d\n", Channel, ChLastPass); + } + + return Status; +} + +/** + Sets commnad margins when moving WrT, WrTBox, or WrV + NOTE: ONLY one, ResetDDR or SelfRefresh can be set inside this function + + @param[in] MrcData - Include all MRC global data. + @param[in] ChBitMask - Bit mask of populated channels + @param[in] Ranks - Bit Mask of populated ranks + @param[in] Param - Input parameter to update + @param[in] Value0 - value to be added + @param[in] Value1 - value to be added + @param[in] ResetDDR - Do we reset DDR? + @param[in] SelfRefresh - Do we perform Self refresh? + + @retval MrcStatus - If it succeeds return mrcSuccess +**/ +void +SetCmdMargin ( + IN MrcParameters *const MrcData, + IN const U8 ChBitMask, + IN const U8 Ranks, + IN const U8 Param, + IN const U8 Value0, + IN const U8 Value1, + IN U8 ResetDDR, + IN const U8 SelfRefresh + ) +{ + MrcControllerOut *ControllerOut; + U8 Channel; + U8 RankMaskCh; + U8 Offset; + + ControllerOut = &MrcData->SysOut.Outputs.Controller[0]; + Offset = 0; + if (SelfRefresh && ResetDDR) { + MRC_DEBUG_MSG ( + &MrcData->SysIn.Inputs.Debug, + MSG_LEVEL_ERROR, + "WARNING SelfRefresh OR ResetDDR can be set at once...performing SelfRefresh\n" + ); + ResetDDR = 0; + } + + if (SelfRefresh) { + EnterSR (MrcData); + } + // + // Change Clock Timing + // + if ((Param == WrT) || (Param == WrTBox)) { + // + // Walk though all chs and ranks + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (ChBitMask & (MRC_BIT0 << Channel)) { + // + // determine which ranks from parameter "Ranks" exist in this channel + // + RankMaskCh = Ranks & ControllerOut->Channel[Channel].ValidRankBitMask; + if (RankMaskCh == 0) { + continue; // No Ranks in this channel + } + + ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankMaskCh, 3, Value0, 0); + } + } + } + + if ((Param == WrV) || (Param == (WrTBox))) { + if (Param == WrV) { + Offset = Value0; + } else { + if (Param == WrTBox) { + Offset = ((2 * Value1) - 1) * 8; + } + } + + UpdateVrefWaitTilStable (MrcData, 2, 0, Offset, 0); + } + + if (ResetDDR) { + MrcResetSequence (MrcData); + } else if (SelfRefresh) { + ExitSR (MrcData); + } + + return; +} + +/** + Updates the value for following OptParamCliff variables: + drrd2rd=0, ddrd2rd=1, drwr2wr=2, ddwr2wr=3, drrd2wr=4, ddrd2wr=5, drwr2rd=6, ddwr2rd=7, + rdodtd=8, wrodtd=9, mcodts=10, mcodtd=11, rtl=12} + + @param[in,out] MrcData - Include all MRC global data. + @param[in] Channel - Channel to update the specificed parameter. + @param[in] Byte - Byte to update the specified parameter. + @param[in] OptParam - Parameter to update. + @param[in] Off - Value to offset the current setting. + @param[in] UpdateHost - Switch to update the host structure with the new value. + @param[in] SkipPrint - Switch to skip debug prints. + @param[in] RankMask - Bit mask of Ranks to update. + + @retval Nothing +**/ +void +UpdateTAParamOffset ( + IN OUT MrcParameters *const MrcData, + IN const U8 Channel, + IN const U8 Byte, + IN const U8 OptParam, + IN const U8 Off, + IN const U8 UpdateHost, + IN const U8 SkipPrint, + IN const U8 RankMask + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + U8 Rank; + U8 IOLat; + S8 New; + U32 Offset1; + U32 Offset2; + MCHBAR_CH0_CR_TC_BANK_RANK_A_STRUCT TcBankRankA; + MCHBAR_CH0_CR_TC_BANK_RANK_B_STRUCT TcBankRankB; + MCHBAR_CH0_CR_TC_BANK_RANK_C_STRUCT TcBankRankC; + MCHBAR_CH0_CR_TC_BANK_RANK_D_STRUCT TcBankRankD; + DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrDataControl1; + MCHBAR_CH0_CR_SC_IO_LATENCY_STRUCT ScIoLatency; + MCHBAR_CH0_CR_SC_ROUNDT_LAT_STRUCT ScRoundtLat; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ChannelOut = &Outputs->Controller[0].Channel[Channel]; + + switch (OptParam) { + case drrd2rd: + // + // dr RD 2 RD Turn Around offsets + // + TcBankRankA.Data = ChannelOut->MchbarBANKRANKA; + TcBankRankA.Bits.tRDRD_dr = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankA.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKA = TcBankRankA.Data; + } + break; + + case ddrd2rd: + // + // dd RD 2 RD Turn Around offsets + // + TcBankRankA.Data = ChannelOut->MchbarBANKRANKA; + TcBankRankA.Bits.tRDRD_dd = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankA.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKA = TcBankRankA.Data; + } + break; + + case drwr2wr: + // + // dr WR 2 WR Turn Around offsets + // + TcBankRankB.Data = ChannelOut->MchbarBANKRANKB; + TcBankRankB.Bits.tWRWR_dr = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankB.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKB = TcBankRankB.Data; + } + break; + + case ddwr2wr: + // + // dd WR 2 WR Turn Around offsets + // + TcBankRankB.Data = ChannelOut->MchbarBANKRANKB; + TcBankRankB.Bits.tWRWR_dd = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankB.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKB = TcBankRankB.Data; + } + break; + + case drrd2wr: + // + // dr RD 2 WR Turn Around offsets + // + TcBankRankC.Data = ChannelOut->MchbarBANKRANKC; + TcBankRankC.Bits.tRDWR_dr = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankC.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKC = TcBankRankC.Data; + } + break; + + case ddrd2wr: + // + // dd RD 2 WR Turn Around offsets + // + TcBankRankC.Data = ChannelOut->MchbarBANKRANKC; + TcBankRankC.Bits.tRDWR_dd = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankC.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKC = TcBankRankC.Data; + } + break; + + case drwr2rd: + // + // dr WR 2 RD Turn Around offsets + // + TcBankRankB.Data = ChannelOut->MchbarBANKRANKB; + TcBankRankB.Bits.tWRRD_dr = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankB.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKB = TcBankRankB.Data; + } + break; + + case ddwr2rd: + // + // dd WR 2 RD Turn Around offsets + // + TcBankRankB.Data = ChannelOut->MchbarBANKRANKB; + TcBankRankB.Bits.tWRRD_dd = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_B_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_B_REG - MCHBAR_CH0_CR_TC_BANK_RANK_B_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankB.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKB = TcBankRankB.Data; + } + break; + + case rdodtd: + TcBankRankD.Data = ChannelOut->MchbarBANKRANKD; + TcBankRankD.Bits.Odt_Read_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankD.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKD = TcBankRankD.Data; + } + break; + + case wrodtd: + TcBankRankD.Data = ChannelOut->MchbarBANKRANKD; +#ifdef ULT_FLAG + if (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3) { + TcBankRankD.UltBits.Odt_Write_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs + } else +#endif // ULT_FLAG + { + TcBankRankD.Bits.Odt_Write_Duration = Off - 6; // Convert into Register values. 2'b00 = BL/2 + 2 (6 DCLKs + } + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_D_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_D_REG - MCHBAR_CH0_CR_TC_BANK_RANK_D_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankD.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKD = TcBankRankD.Data; + } + break; + + case mcodts: + // + // MC ODT delay + // + DdrDataControl1.Data = ChannelOut->DqControl1[Byte].Data; + New = MrcSE ((U8) DdrDataControl1.Bits.OdtDelay, 4, 8) + Off; // SignExtend + if (New < -4) { + New = -4; // RcvEnPi[8:6] - 5 qclk Min + } else if (New > 6) { + New = 6; // RcvEnPi[8:6] + 5 qclk Max + } + + DdrDataControl1.Bits.OdtDelay = New; + DdrDataControl1.Bits.SenseAmpDelay = New; + Offset1 = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel) + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte); + MrcWriteCR (MrcData, Offset1, DdrDataControl1.Data); + if (UpdateHost) { + ChannelOut->DqControl1[Byte].Data = DdrDataControl1.Data; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (SkipPrint) ? "" : "%d\t", New); + break; + + case mcodtd: + // + // Duration + // + DdrDataControl1.Data = ChannelOut->DqControl1[Byte].Data; + New = (U8) DdrDataControl1.Bits.OdtDuration + Off; + if (New < 0) { + New = 0; // 11 tQCK Min + } else if (New > DDRDATA0CH0_CR_DDRCRDATACONTROL1_OdtDuration_MAX) { + New = DDRDATA0CH0_CR_DDRCRDATACONTROL1_OdtDuration_MAX; // 18 tQCK Max + } + + DdrDataControl1.Bits.OdtDuration = New; + DdrDataControl1.Bits.SenseAmpDuration = New; + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "mcodtd CRValue = 0x%x\n", DdrDataControl1.Bits.OdtDuration); + // + Offset1 = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel) + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte); + MrcWriteCR (MrcData, Offset1, DdrDataControl1.Data); + if (UpdateHost) { + ChannelOut->DqControl1[Byte].Data = DdrDataControl1.Data; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, (SkipPrint) ? "" : "%d\t", DdrDataControl1.Bits.OdtDuration); + break; + + case rtl: + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if (RankMask & (MRC_BIT0 << Rank)) { + // + // Update IO Latency & RoundTrip + // + IOLat = ChannelOut->IoLatency[Rank] - (ChannelOut->RTLatency[Rank] - Off); + if ((S8) IOLat < 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_WARNING, "IOLatency reached the Saturation point \n"); + } else { + Offset1 = MCHBAR_CH0_CR_SC_IO_LATENCY_REG + + ((MCHBAR_CH1_CR_SC_IO_LATENCY_REG - MCHBAR_CH0_CR_SC_IO_LATENCY_REG) * Channel); + ScIoLatency.Data = MrcReadCR (MrcData, Offset1); + Offset2 = MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG + + ((MCHBAR_CH1_CR_SC_ROUNDT_LAT_REG - MCHBAR_CH0_CR_SC_ROUNDT_LAT_REG) * Channel); + ScRoundtLat.Data = MrcReadCR (MrcData, Offset2); + switch (Rank) { + case 0: + ScIoLatency.Bits.IOLAT_R0D0 = IOLat; + ScRoundtLat.Bits.Lat_R0D0 = Off; + break; + + case 1: + ScIoLatency.Bits.IOLAT_R1D0 = IOLat; + ScRoundtLat.Bits.Lat_R1D0 = Off; + break; + + case 2: + ScIoLatency.Bits.IOLAT_R0D1 = IOLat; + ScRoundtLat.Bits.Lat_R0D1 = Off; + break; + + case 3: + ScIoLatency.Bits.IOLAT_R1D1 = IOLat; + ScRoundtLat.Bits.Lat_R1D1 = Off; + break; + + default: + break; + } + + MrcWriteCR (MrcData, Offset1, ScIoLatency.Data); + MrcWriteCR (MrcData, Offset2, ScRoundtLat.Data); + + // + // Update host + // + if (UpdateHost) { + ChannelOut->RTLatency[Rank] = Off; + ChannelOut->IoLatency[Rank] = IOLat; + } + } + } + } + break; + + case srrd2rd: + // + // sr RD 2 RD Turn Around offsets + // + TcBankRankA.Data = ChannelOut->MchbarBANKRANKA; + TcBankRankA.Bits.tRDRD = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_A_REG + ((MCHBAR_CH1_CR_TC_BANK_RANK_A_REG - MCHBAR_CH0_CR_TC_BANK_RANK_A_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankA.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKA = TcBankRankA.Data; + } + break; + + case srrd2wr: + // + // sr RD 2 WR Turn Around offsets + // + TcBankRankC.Data = ChannelOut->MchbarBANKRANKC; + TcBankRankC.Bits.tRDWR = Off; + Offset1 = MCHBAR_CH0_CR_TC_BANK_RANK_C_REG + + ((MCHBAR_CH1_CR_TC_BANK_RANK_C_REG - MCHBAR_CH0_CR_TC_BANK_RANK_C_REG) * Channel); + MrcWriteCR (MrcData, Offset1, TcBankRankC.Data); + if (UpdateHost) { + ChannelOut->MchbarBANKRANKC = TcBankRankC.Data; + } + break; + + default: + break; + } + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + ((OptParam != mcodtd) && (OptParam != mcodts) && (!SkipPrint)) ? "%d\t" : "", + Off + ); + + return; +} + +/** + This function applies the new DRAM ODT settings + Walks through various optimizations to get the best result with new ODT values + This includes WrDS, RdODT, Eq, etc. + Updates Best* variables if this point if better than the prior points + chDone is both an input and output. Reports which channels have a good enough value + if SkipRd is high, it will skip the read related functions (RdODT, RdEq, RdTiming) + + @param[in] MrcData - Include all MRC global data. + @param[in,out] DimmOptPoints - Structure of all the DIMM ODT optimal settings. + @param[in] ChMask - Channel to work on. + @param[in] RankMask - Rank to work on. + @param[in] skipGRdOdt - Used to skip RdODT. + @param[in] RttNom - Rtt_Nom value for each DIMM. + @param[in] RttWr - Rtt_Wr value for each DIMM. + @param[in] GRdOdt - CPU Global Read ODT. + @param[in] OptParamTestList - List of Opt test(Drive Strength, RxBias, TxEq, RxEq) to run. + @param[in] OptParamTestListSize - Size of OptParamTestList. + @param[in] SubPwrLimits - Switch to apply power limits to the suboptimization. + @param[in] skipOptTests - Skips the suboptimization. + @param[in] skipOptPrint - Skip printing of the suboptimization steps. + @param[in] RdCenter - Switch to recenter read. + @param[in] WrCenter - Switch to recenter write. + @param[in] inputBestMargin - Array of the best margin for each test. + @param[in] MarginsLength - Length of inputBestMargin. + @param[in] OffsetPoint - Index inside inputBestMargin to start. + + @retval Nothing. +**/ +void +TrainDimmOdtSetting ( + IN MrcParameters *const MrcData, + IN OUT DimmOptPoint *DimmOptPoints, + IN U8 ChMask, + IN U8 RankMask, + IN U8 skipGRdOdt, + IN U8 RttNom[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL], + IN U8 RttWr[MAX_CHANNEL][MAX_DIMMS_IN_CHANNEL], + IN S8 GRdOdt, + IN U8 *OptParamTestList, + IN U8 OptParamTestListSize, + IN BOOL SubPwrLimits, + IN BOOL skipOptTests, + IN BOOL skipOptPrint, + IN BOOL RdCenter, + IN BOOL WrCenter, + IN void *inputBestMargin, + IN U8 MarginsLength, + IN U8 OffsetPoint + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + DimmOptPoint *PointResults; + OptOffsetChByte BestOffArr[SizeOfTCompOffset][MAX_RANK_IN_CHANNEL]; + TCompOffset OffsetType; + U8 dimm; + U8 rank; + U8 ValidRankMask; + U8 LocalRanks[MAX_CHANNEL]; + U8 ChBitMask; + U8 Channel; + U8 ParamList[] = { RdV, RdT, WrV, WrT }; // List of parameters to margin + U8 TestListRd[] = { RdV, RdT }; + U8 TestListWr[] = { WrV, WrT }; + U8 *TestList; + U8 TestListSize; + U8 TScale[] = { 1, 2, 1, 0, 0 }; + U8 GScale[] = { 1, 2, 0, 0, 0 }; + U16 GPwrLimits[] = { 520, 280, 0, 0, 0 }; + U16 noPwrLimits[] = { 2480, 2240, 0, 0, 0 }; + U8 *Scale; + U16 *PwrLimits; + S8 start; + S8 stop; + U8 i; + U8 t; + U8 ResultType; + U8 RecenterLC; + U8 OptParamLC; + BOOL clipPowerLmt; + U16 *BestMargin; + U8 TestResultType[4] = { 0, 0, 0, 0 }; + + TestListSize = 0; + RecenterLC = 15; + OptParamLC = OPT_PARAM_LOOP_COUNT; + clipPowerLmt = 1; + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + ControllerOut = &Outputs->Controller[0]; + + PointResults = DimmOptPoints; + BestMargin = (U16 *) inputBestMargin; + MrcOemMemorySet ((U8 *) BestOffArr, 0xffff, sizeof (BestOffArr)); + MrcOemMemorySet ((U8 *) PointResults, 0xffff, sizeof (DimmOptPoint)); + OffsetType = 0; + + if (SubPwrLimits) { + // + // Use power limits and Trendline + // + Scale = TScale; + PwrLimits = GPwrLimits; + } else { + // + // No power limits and no TrendLine + // + Scale = GScale; + PwrLimits = noPwrLimits; + } + // + // TrainDDROptParam already check the valid against host chRankBit mask + // Walk through channels, check if this point is redundant, set RttNom + // + ChMask &= Outputs->ValidChBitMask; + RankMask &= Outputs->ValidRankMask; + ValidRankMask = 0; + ChBitMask = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + LocalRanks[Channel] = 0; + if (((MRC_BIT0 << Channel) & ChMask)) { + LocalRanks[Channel] = RankMask & ControllerOut->Channel[Channel].ValidRankBitMask; + if (LocalRanks[Channel]) { + ChBitMask |= MRC_BIT0 << Channel; // remove ch with no "active" ranks + } + } + } + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + for (dimm = 0; dimm < MAX_DIMMS_IN_CHANNEL; dimm++) { + PointResults->ODTSet.RttNom[Channel][dimm] = RttNom[Channel][dimm]; + PointResults->ODTSet.RttWr[Channel][dimm] = RttWr[Channel][dimm]; + } + } + + PointResults->ODTSet.GRdOdt = GRdOdt; + UpdateOdtsValues (MrcData, ChBitMask, PointResults, skipGRdOdt, 0, 1, 1); + // + // update only DimmOdt and GROdt if not skipped. + // Recenter Timing + // + if (RdCenter) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Vref\n"); + ReadVoltageCentering2D ( + MrcData, + Outputs->MarginResult, + ChBitMask, + RdV, + 0, + 0, + RecenterLC, + 0 + ); + // + // We can add if status fail go to next point + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Read Timing ChBitMask=%x\n", ChBitMask); + DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + ChBitMask, + RdT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + RecenterLC, + 0 // En2D + ); + } + + if (WrCenter) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Vref\n"); + MrcWriteVoltageCentering2D (MrcData); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Re-center Write Timing ChBitMask=%x\n", ChBitMask); + DataTimeCentering2D ( + MrcData, + Outputs->MarginResult, // prev. margin results + ChBitMask, + WrT, + 0, // EnPerBit, + 0, // EnRxDutyCycle + 0, // ResetPerBit + RecenterLC, + 0 // En2D + ); + } + // + // @todo: we could check here if we have some reasonable amount of margin to play with + // + TestList = ParamList; + PointResults->OptParamTestListSize = OptParamTestListSize; + for (t = 0; t < OptParamTestListSize; t++) { + // + // also apply the best offset to hw and host and inside also best offset related margin is saved in host struct + // + PointResults->OptParamTestList[t] = OptParamTestList[t]; + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParamTestList[%d]=%d , %s\n",t,OptParamTestList[t],TOptParamOffsetString[OptParamTestList[t]]); + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParamTestList[%d]=%d OptParamTestListSize=%d\n",t,OptParamTestList[t],OptParamTestListSize); + switch (OptParamTestList[t]) { + case (OptWrDS): + start = -11; + stop = 12; + TrainDDROptParam ( + MrcData, + &BestOffArr[WrDSOfft][0], + ChBitMask, + RankMask, + OptWrDS, + TestListWr, + sizeof (TestListWr), + Scale, + PwrLimits, + start, + stop, + OptParamLC, + 1, // Repeats + skipOptPrint, + skipOptTests, + 0, // RdRd2Test + 1 // GuardBand + ); + TestList = TestListWr; + TestListSize = sizeof (TestListWr); + OffsetType = WrDSOfft; + PointResults->BestOptOff[WrDSOfft][0] = BestOffArr[WrDSOfft][0]; + break; + + case (OptRdOdt): + start = -10; + stop = 6; + TrainDDROptParam ( + MrcData, + &BestOffArr[RdOdtOfft][0], + ChBitMask, + RankMask, + OptRdOdt, + TestListRd, + sizeof (TestListRd), + Scale, + PwrLimits, + start, + stop, + OptParamLC, + 1, // Repeats + skipOptPrint, + skipOptTests, + RdRdTA, // RdRd2Test + 0 // GuardBand + ); + TestList = TestListRd; + TestListSize = sizeof (TestListRd); + OffsetType = RdOdtOfft; + PointResults->BestOptOff[RdOdtOfft][0] = BestOffArr[RdOdtOfft][0]; + break; + + case (OptSComp): + case (OptTComp): + break; + + case (OptTxEq): + start = 0; + stop = 11; + for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) { + if (!((MRC_BIT0 << rank) & RankMask)) { + continue; // check if rank at least on one channel + } + + TrainDDROptParam ( + MrcData, + &BestOffArr[TxEqOfft][rank], + ChBitMask, + (MRC_BIT0 << rank), + OptTxEq, + TestListWr, + sizeof (TestListWr), + Scale, + PwrLimits, + start, + stop, + OptParamLC, + 1, // Repeats + skipOptPrint, + skipOptTests, + 0, // RdRd2Test + 2 // GuardBand + ); + PointResults->BestOptOff[TxEqOfft][rank] = BestOffArr[TxEqOfft][rank]; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << rank) & LocalRanks[Channel])) { + continue; + // + // check if the rank exist in this ch + // + } + + for (i = 0; i < sizeof (TestListWr); i++) { + // + // track min margin per ch + // + if (BestOffArr[TxEqOfft][rank].Margins[i][Channel] < BestOffArr[TxEqOfft][0].Margins[i][Channel]) { + BestOffArr[TxEqOfft][0].Margins[i][Channel] = BestOffArr[TxEqOfft][rank].Margins[i][Channel]; + } + } + } + } + + TestList = TestListWr; + TestListSize = sizeof (TestListWr); + OffsetType = TxEqOfft; + break; + + case (OptRxEq): + start = 0; + stop = 14; + for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) { + if (!((MRC_BIT0 << rank) & RankMask)) { + continue; // check if rank at least on one channel + } + + TrainDDROptParam ( + MrcData, + &BestOffArr[RxEqOfft][rank], + ChBitMask, + (MRC_BIT0 << rank), + OptRxEq, + TestListRd, + sizeof (TestListRd), + Scale, + noPwrLimits, + start, + stop, + OptParamLC, + 1, // Repeats + skipOptPrint, + skipOptTests, + RdRdTA, // RdRd2Test + 0 // GuardBand + ); + PointResults->BestOptOff[RxEqOfft][rank] = BestOffArr[RxEqOfft][rank]; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((1 << rank) & LocalRanks[Channel])) { + continue; // check if the rank exist in this ch + } + + for (i = 0; i < sizeof (TestListRd); i++) { + // + // track min margin per ch and asign to rank0 + // + if (BestOffArr[RxEqOfft][rank].Margins[i][Channel] < BestOffArr[RxEqOfft][0].Margins[i][Channel]) { + BestOffArr[RxEqOfft][0].Margins[i][Channel] = BestOffArr[RxEqOfft][rank].Margins[i][Channel]; + } + } + } + } + + TestList = TestListRd; + TestListSize = sizeof (TestListRd); + OffsetType = RxEqOfft; + break; + + case (OptRxBias): + start = 0; + stop = 7; + TrainDDROptParam ( + MrcData, + &BestOffArr[RdSAmpOfft][0], + ChBitMask, + RankMask, + OptRxBias, + TestListRd, + sizeof (TestListRd), + Scale, + PwrLimits, + start, + stop, + OptParamLC, + 1, // Repeats + skipOptPrint, + skipOptTests, + RdRdTA, // RdRd2Test + 0 // GuardBand + ); + TestList = TestListRd; + TestListSize = sizeof (TestListRd); + OffsetType = RdSAmpOfft; + PointResults->BestOptOff[RdSAmpOfft][0] = BestOffArr[RdSAmpOfft][0]; + break; + + case (OptDimmOdt): + break; + + case (OptDimmOdtWr): + break; + + default: + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptParam Test not valid\n"); + + } + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(LocalRanks[Channel])) { + continue; // check if the active rank run this ch + } + // + // run through all BestOff[optParam][0] and track min[RdV,RdT,WrV,WrT] + // + for (i = 0; i < TestListSize; i++) { + ResultType = GetMarginResultType (TestList[i]); + TestResultType[ResultType] = TestList[i]; // indicate which test we run and create the reverse dic + // + //we need to update only last results + // + PointResults->Test[ResultType][Channel] = BestOffArr[OffsetType][0].Margins[i][Channel]; + } + } + } // end for OptParamTest + // + // assign the point for passing to the FindOptimalTradeOff function + // + i = 0; + PointResults->NumTests = 0; + for (t = 0; t < 4; t++) { + // + // ResultType=GetMarginResultType(TestList[i]); + // + if (TestResultType[t] == 0) { + continue; // can only be 1,2,4,5 + } else { + PointResults->TestList[i] = TestResultType[t]; + PointResults->NumTests++; + // + // *(BestMargin+i*MarginsLength+OffsetPoint)=PointResults->Test[t][Channel]; + // sorting test for TradeOff + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(LocalRanks[Channel])) { + continue; // check if the active rank run this ch + } + + if (clipPowerLmt) { + if (PointResults->Test[t][Channel] > UpmPwrLimitValue (MrcData, TestResultType[t], PowerLimit)) { + PointResults->Points2Trade[i][Channel] = UpmPwrLimitValue (MrcData, TestResultType[t], PowerLimit); + } else { + PointResults->Points2Trade[i][Channel] = PointResults->Test[t][Channel]; + } + } else { + PointResults->Points2Trade[i][Channel] = PointResults->Test[t][Channel]; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "PointResults->TestList[%d]=%d PointResults->Test[test index=%d][channel=%d] =%d\n",i,PointResults->TestList[i],t,Channel,PointResults->Test[t][Channel]); + // + } + + i++; + } + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "PointResults->NumTests =%d\n",PointResults->NumTests); + // + return; +} + +/** + This function applies an offset to the global compensation logic. + Reruns Compensation and returns the new comp value + + @param[in,out] MrcData - Include all MRC global data. + @param[in] param - Parameter defining the desired global compensation logic + @param[in] offset - Value to apply + @param[in] UpdateHost - Desides if MrcData has to be updated + + @retval Returns the new comp value. +**/ +U32 +UpdateCompGlobalOffset ( + IN OUT MrcParameters *const MrcData, + IN const U8 param, + IN const U32 offset, + IN const U8 UpdateHost + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0; + DDRCOMP_CR_DDRCRCOMPCTL1_STRUCT DdrCrCompCtl1; + PCU_CR_M_COMP_PCU_STRUCT PcuCrMComp; + DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0; + DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1; + DDRCOMP_CR_DDRCRCMDCOMP_STRUCT DdrCrCmdComp; + DDRCOMP_CR_DDRCRCTLCOMP_STRUCT DdrCrCtlComp; + DDRCOMP_CR_DDRCRCLKCOMP_STRUCT DdrCrClkComp; + DDRDATACH0_CR_DDRCRDATACONTROL0_STRUCT DdrCrDataControl0; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0_Temp; + U32 RegOffset; + U8 Channel; + + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Debug = &MrcData->SysIn.Inputs.Debug; + DdrCrCompCtl0.Data = Outputs->CompCtl0; + DdrCrCompCtl1.Data = Outputs->CompCtl1; + DdrCrDataControl0.Data = 0; + + // + // Update offset in local CR variable + // + switch (param) { + case RdOdt: + // + // Disable FixOdt feature before changing this param + // + DdrCrCompCtl0.Bits.FixOdtD = 0; + // + // Apply Comp Offset to RdOdt + // + DdrCrCompCtl0.Bits.DqOdtVref = offset; + break; + + case WrDS: + // + // Apply Comp Offset to WrDS-DQ + // + DdrCrCompCtl0.Bits.DqDrvVref = offset; + break; + + case WrDSCmd: + // + // Apply Comp Offset to WrDS-CMD + // + DdrCrCompCtl0.Bits.CmdDrvVref = offset; + break; + + case WrDSCtl: + // + // Apply Comp Offset to WrDS-CTL + // + DdrCrCompCtl0.Bits.CtlDrvVref = offset; + break; + + case WrDSClk: + // + // Apply Comp Offset to WrDS-CLK + // + DdrCrCompCtl0.Bits.ClkDrvVref = offset; + break; + + case SCompDq: + // + // Apply Comp Offset to Scomp-DQ + // + DdrCrCompCtl1.Bits.DqScompCells = offset; + DdrCrCompCtl1.Bits.DqScompPC = offset >> 4; + break; + + case SCompCmd: + // + // Apply Comp Offset to Scomp-CMD + // + DdrCrCompCtl1.Bits.CmdScompCells = offset; + DdrCrCompCtl1.Bits.CmdScompPC = offset >> 4; + break; + + case SCompCtl: + // + // Apply Comp Offset to Scomp-CTL + // + DdrCrCompCtl1.Bits.CtlScompCells = offset; + DdrCrCompCtl1.Bits.CtlScompPC = offset >> 4; + break; + + case SCompClk: + // + // Apply Comp Offset to Scomp-CLK + // + DdrCrCompCtl1.Bits.ClkScompCells = offset; + DdrCrCompCtl1.Bits.ClkScompPC = offset >> 4; + break; + + case DisOdtStatic: + // + // disable static read Otd legs + // + DdrCrCompCtl0.Bits.DisableOdtStatic = offset; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + DdrCrDataControl0.Data = ChannelOut->DqControl0.Data; + DdrCrDataControl0.Bits.DisableOdtStatic = offset; // apply to bytes fubs + RegOffset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, RegOffset, DdrCrDataControl0.Data); + if (UpdateHost) { + ChannelOut->DqControl0.Data = DdrCrDataControl0.Data; + } + } + break; + + default: + break; + } + // + // Update the Comp Offsets and Host Structure + // + MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data); + MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL1_REG, DdrCrCompCtl1.Data); + if (UpdateHost) { + Outputs->CompCtl0 = DdrCrCompCtl0.Data; + Outputs->CompCtl1 = DdrCrCompCtl1.Data; + } + // + // Run Compensation + // Start Comp Engine + // + PcuCrMComp.Data = 0; + PcuCrMComp.Bits.COMP_FORCE = 1; + PcuCrMComp.Bits.COMP_INTERVAL = MIN (COMP_INT, PCU_CR_M_COMP_PCU_COMP_INTERVAL_MAX); + PcuCrMComp.Bits.COMP_DISABLE = 1; + MrcWriteCR (MrcData, PCU_CR_M_COMP_PCU_REG, PcuCrMComp.Data); + MrcWait (MrcData, 8 * HPET_1US); // Wait for Comp to Complete + if (param == RdOdt) { + // + // we check if we close to saturation and try dis/en the static legs + // + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + if ((DdrCrDataComp1.Bits.RcompOdtUp < 16) || (DdrCrDataComp1.Bits.RcompOdtUp > 48)) { + // + // disable/enable static read Otd legs + // + if (DdrCrDataComp1.Bits.RcompOdtUp < 16) { + DdrCrCompCtl0.Bits.DisableOdtStatic = 1; + } else { + DdrCrCompCtl0.Bits.DisableOdtStatic = 0; + } + // + // Update the Comp Offsets and Host Structure + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &ControllerOut->Channel[Channel]; + ChannelOut->DqControl0.Bits.DisableOdtStatic = DdrCrCompCtl0.Bits.DisableOdtStatic; // apply to bytes fubs + RegOffset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "DdrCrDataControl0.Bits.DisableOdtStatic=%d\n",DdrCrDataControl0.Bits.DisableOdtStatic); + // + MrcWriteCrMulticast (MrcData, RegOffset, ChannelOut->DqControl0.Data); + } + + MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data); + // + // host need to always be updated with static state + // + DdrCrCompCtl0_Temp.Data = Outputs->CompCtl0; + DdrCrCompCtl0_Temp.Bits.DisableOdtStatic = DdrCrCompCtl0.Bits.DisableOdtStatic; + Outputs->CompCtl0 = DdrCrCompCtl0_Temp.Data; + // + // Run Compensation + // Start Comp Engine + // + MrcWriteCR (MrcData, PCU_CR_M_COMP_PCU_REG, PcuCrMComp.Data); + MrcWait (MrcData, 8 * HPET_1US); // Wait for Comp to Complete + } + + } + // + // Return the new comp code + // + switch (param) { + case DisOdtStatic: + case RdOdt: + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + // + // re-Enable FixOdt feature after changing this param + // + DdrCrCompCtl0.Bits.DqOdtUpDnOff = DdrCrDataComp1.Bits.RcompOdtDown - DdrCrDataComp1.Bits.RcompOdtUp; + DdrCrCompCtl0.Bits.FixOdtD = 1; + MrcWriteCR (MrcData, DDRCOMP_CR_DDRCRCOMPCTL0_REG, DdrCrCompCtl0.Data); + if (UpdateHost) { + Outputs->CompCtl0 = DdrCrCompCtl0.Data; + } + return DdrCrDataComp1.Bits.RcompOdtUp; + + case WrDS: + case SCompDq: + DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG); + return (param == WrDS) ? DdrCrDataComp0.Bits.RcompDrvUp : DdrCrDataComp0.Bits.SlewRateComp; + + case WrDSCmd: + case SCompCmd: + DdrCrCmdComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCMDCOMP_REG); + return (param == WrDSCmd) ? DdrCrCmdComp.Bits.RcompDrvUp : DdrCrCmdComp.Bits.Scomp; + + case WrDSCtl: + case SCompCtl: + DdrCrCtlComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCTLCOMP_REG); + return (param == WrDSCtl) ? DdrCrCtlComp.Bits.RcompDrvUp : DdrCrCtlComp.Bits.Scomp; + + case WrDSClk: + case SCompClk: + DdrCrClkComp.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRCLKCOMP_REG); + return (param == WrDSClk) ? DdrCrClkComp.Bits.RcompDrvUp : DdrCrClkComp.Bits.Scomp; + + default: + break; + } + + return 0; +} + +/** + Programs Delay/Duration for the SenseAmp and MCODT based on RcvEn timing + Provide GuardBand > 0 if needed to be more conservative in timing + Main goal is to optimize power + + @param[in,out] MrcData - Include all MRC global data. + @param[in] GuardBand - Input parameter with more conservative value + + @retval Nothing +**/ +void +UpdateSampOdtTiming ( + IN OUT MrcParameters *const MrcData, + IN const U8 GuardBand + ) + +{ + MrcOutput *Outputs; + MrcDebug *Debug; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + U16 *CurRcvEn; + U8 Channel; + U8 Byte; + U8 rank; + U16 MaxRcvEn; + U16 MinRcvEn; + U32 Offset; + U32 SWakeUp; + U32 SAWakeUppS; // Round up to nearest Qclk + S8 SOn; // SenseAmpDelay + S8 OOn; // OdtDelay + S32 SOff; // SenseAmpDuration + S32 OOff; // OdtDuration + DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT *DqControl1; + + SAWakeUppS = 1250; + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + ControllerOut = &Outputs->Controller[0]; + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "UpdateSampOdtTiming: GuardBand = %d\n", GuardBand); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Ch %d\tOdtOn\tOdtOff\tSAmpOn\tSAmpOff\n", Channel); + + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + MaxRcvEn = 0; + MinRcvEn = 512; + + for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) { + if (MrcRankInChannelExist (MrcData, rank, Channel)) { + CurRcvEn = &ChannelOut->RcvEn[rank][Byte]; + if (MaxRcvEn < *CurRcvEn) { + MaxRcvEn = *CurRcvEn; + } + + if (MinRcvEn > *CurRcvEn) { + MinRcvEn = *CurRcvEn; + } + } + } + // + // Round Max to nearest cycle + // + MaxRcvEn = (MaxRcvEn >> 6) + 1; + + // + // SENSE AMP CAN ONLY BE ON WHEN ODT IS ON FOR EOS REASONS. + // SWakeUp = (U32)( (SAWakeUppS + Outputs->Qclkps - 1) / Outputs->Qclkps ); + // SOn = MinRcvEn - SWakeUp - GuardBand; + // OOn = MinRcvEn - 2 - GuardBand; + // + SWakeUp = (U32) ((64 * SAWakeUppS) / Outputs->Qclkps); // Convert to PI codes + // + // Turn On ODT & Samp at least 2 Qclks before earlier RcvEn Rise + // + if (SWakeUp < 128) { + SWakeUp = 128; // at least 2-Qclks + } + + OOn = SOn = (S8) ((MinRcvEn - SWakeUp) >> 6) - GuardBand; + // + // SenseAmp Delay + // + if (SOn < -4) { + SOn = -4; // RcvEnPi[8:6] - 5 qclk + } else if (SOn > 6) { + SOn = 6; // RcvEnPi[8:6] + 5 qclk + } + // + // OdtDelay + // + if (OOn < -4) { + OOn = -4; // RcvEnPi[8:6] - 5 qclk + } else if (OOn > 6) { + OOn = 6; // RcvEnPi[8:6] + 5 qclk + } + // + // Turn Off Samp 1 qclk after postamble + // Turn Off ODT 1 qclk after postamble + // Program the duration to leave Odt/Samp On + // OnBeforeRcvEn BL+Post AfterPost CR Encoding + // + SOff = (MaxRcvEn - SOn) + (8 + 1) + 1 + GuardBand - 11; + OOff = (MaxRcvEn - OOn) + (8 + 1) + 1 + GuardBand - 11; + + if (SOff < 0) { + SOff = 0; // 11 tQCK Min + } else if (SOff > 7) { + SOff = 7; // 18 tQCK Max + } + + if (OOff < 0) { + OOff = 0; // 11 tQCK Min + } else if (OOff > 7) { + OOff = 7; // 18 tQCK mAx + } + + DqControl1 = &ChannelOut->DqControl1[Byte]; + DqControl1->Bits.OdtDelay = OOn; + DqControl1->Bits.OdtDuration = OOff; + DqControl1->Bits.SenseAmpDelay = SOn; + DqControl1->Bits.SenseAmpDuration = SOff; + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel); + MrcWriteCR (MrcData, Offset, DqControl1->Data); + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d:\t%d\t%d\t%d\t%d\n", + Byte, + DqControl1->Bits.OdtDelay, + DqControl1->Bits.OdtDuration, + DqControl1->Bits.SenseAmpDelay, + DqControl1->Bits.SenseAmpDuration + ); + } + } + } + + return; +} + +/** + Turns off unused portions of the slave DLL to save power + + @param[in,out] MrcData - Include all MRC global data. + + @retval Nothing +**/ +void +UpdateSlaveDLLLength ( + IN OUT MrcParameters *const MrcData + ) +{ + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + U8 *CurRxDqs; + U32 Offset; + U8 Channel; + U8 byte; + U8 rank; + U8 MaxPi; + + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + ChannelOut = &ControllerOut->Channel[Channel]; + for (byte = 0; byte < Outputs->SdramCount; byte++) { + MaxPi = 0; + for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) { + if (MrcRankInChannelExist (MrcData, rank, Channel)) { + CurRxDqs = &ChannelOut->RxDqsP[rank][byte]; + if (MaxPi < *CurRxDqs) { + MaxPi = *CurRxDqs; + } + + CurRxDqs = &ChannelOut->RxDqsN[rank][byte]; + if (MaxPi < *CurRxDqs) { + MaxPi = *CurRxDqs; + } + } + } + // + // Update SlaveDLL Length for power Savings + // Calculate which segments to turn off: + // NEW (OFF: 0, PI<48: 0x2, PI<32: 0x4, PI<16: 0x6) + // results are: 0, 2 , 4 or 6 + // + ChannelOut->DqControl1[byte].Bits.SdllSegmentDisable = ((7 - (MaxPi >> 3)) &~MRC_BIT0); + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * byte) + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel); + MrcWriteCR (MrcData, Offset, ChannelOut->DqControl1[byte].Data); + } + } + } + + return; +} + +#ifdef TRAD_FLAG +/** + Update Internal clocks on setting if needed. + + @param[in,out] MrcData - Include all MRC global data. + + @retval Nothing +**/ +void +UpdateInternalClksOn ( + IN OUT MrcParameters *const MrcData + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + U32 Offset; + U8 Channel; + U8 Byte; + S8 SOn; // SenseAmpDelay + S8 OOn; // OdtDelay + S32 SOff; // SenseAmpDuration + S32 OOff; // OdtDuration + U8 InternalClkOn; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + InternalClkOn = 0; + ChannelOut = &ControllerOut->Channel[Channel]; + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + OOn = MrcSE ((U8) ChannelOut->DqControl1[Byte].Bits.OdtDelay, 4, 8); + OOff = ChannelOut->DqControl1[Byte].Bits.OdtDuration; + SOn = MrcSE ((U8) ChannelOut->DqControl1[Byte].Bits.SenseAmpDelay, 4, 8); + SOff = ChannelOut->DqControl1[Byte].Bits.SenseAmpDuration; + + // + // Check if OdtDelay + OdtDuration >= 7 or if SADelay + SADuration >= 7 + // + if (((OOn + OOff) >= 7) || ((SOn + SOff) >= 7)) { + InternalClkOn = 1; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ODTOn = %d, ODTOff = %d\n", OOn, OOff); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "SAOn = %d, SAOff = %d\n", SOn, SOff); + break; + } + } + + ChannelOut->DqControl0.Bits.InternalClocksOn = InternalClkOn; + Offset = DDRDATACH0_CR_DDRCRDATACONTROL0_REG + + ((DDRDATACH1_CR_DDRCRDATACONTROL0_REG - DDRDATACH0_CR_DDRCRDATACONTROL0_REG) * Channel); + MrcWriteCrMulticast (MrcData, Offset, ChannelOut->DqControl0.Data); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "C%dDdrCrDataControl0.Data = 0x%x\n", + Channel, + ChannelOut->DqControl0.Data + ); + } + } + + return; +} +#endif // TRAD_FLAG + +/** + This function Shifts the CMD timing. + NOTE: ONLY one, ResetDDR or SelfRefresh can be set inside this function + + @param[in,out] MrcData - Include all MRC global data. + @param[in] Ranks - Parameter defining the desired global compensation logic + @param[in] offset - per channel Value to shift picode for + @param[in] ResetDDR - Do we reset DDR? + @param[in] SelfRefresh - Do we perform Self refresh? + @param[in] UpdateHost - Determines if MrcData has to be updated + @param[in] SkipTx - Determines if TX update should be skipped + @todo: SkipTx is NOT USED at this time and we don't skip it anyway + + @retval MrcStatus - If it succeeds return mrcSuccess +**/ +MrcStatus +ShiftCh2Ch ( + IN OUT MrcParameters *const MrcData, + IN const U8 Ranks, + IN const U8 *const offset, + IN U8 ResetDDR, + IN const U8 SelfRefresh, + IN const U8 UpdateHost, + IN const U8 SkipTx + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + MrcStatus Status; + U8 Channel; + U8 Rank; + U8 RankMaskCh; + S32 NewValue; + S32 Offset; + BOOL Lpddr; + + Status = mrcSuccess; + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Lpddr = (Outputs->DdrType == MRC_DDR_TYPE_LPDDR3); + + if (SelfRefresh && ResetDDR) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_ERROR, + "WARNING SelfRefresh OR ResetDDR can be set at once...performing SelfRefresh\n" + ); + ResetDDR = 0; + } + + if (SelfRefresh) { + EnterSR (MrcData); + } + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!MrcChannelExist (Outputs, Channel)) { + continue; + } + + ChannelOut = &ControllerOut->Channel[Channel]; + RankMaskCh = Ranks & ChannelOut->ValidRankBitMask; + + if (RankMaskCh == 0) { + continue; + } + + Offset = offset[Channel]; + + // + // Shift CLK (this will shift DQ PIs as well) + // + ShiftPIforCmdTraining (MrcData, Channel, MrcIterationClock, RankMaskCh, 3, Offset, UpdateHost); + + // + // Shift CTL + // + NewValue = 0; + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if (RankMaskCh & (1 << Rank)) { + NewValue = ChannelOut->CtlPiCode[Rank] + Offset; + break; + } + } + + ShiftPIforCmdTraining (MrcData, Channel, MrcIterationCtl, RankMaskCh, 1, NewValue, UpdateHost); + + // + // Shift CmdS + // + ShiftPIforCmdTraining ( + MrcData, + Channel, + MrcIterationCmdS, + RankMaskCh, + 1, + ChannelOut->CmdsCmdPiCode[0] + Offset, + UpdateHost + ); + + // + // Shift CmdN + // + ShiftPIforCmdTraining ( + MrcData, + Channel, + MrcIterationCmdN, + RankMaskCh, + 1, + ChannelOut->CmdnCmdPiCode[0] + Offset, + UpdateHost + ); + +#ifdef ULT_FLAG + if (Lpddr) { + // + // For LPDDR need to shift CmdS PiCode[1] separately. + // Host struct is not updated, so update PiCode[0] manually, and then restore back. + // + ChannelOut->CmdsCmdPiCode[0] = ChannelOut->CmdsCmdPiCode[0] + Offset; + ShiftPIforCmdTraining ( + MrcData, + Channel, + MrcIterationCmdS, + RankMaskCh, + 2, + ChannelOut->CmdsCmdPiCode[1] + Offset, + UpdateHost + ); + ChannelOut->CmdsCmdPiCode[0] = ChannelOut->CmdsCmdPiCode[0] - Offset; + } +#endif // ULT_FLAG + // + // Shift CKE + // + ShiftPIforCmdTraining ( + MrcData, + Channel, + MrcIterationCke, + RankMaskCh, + 1, + ChannelOut->CkeCmdPiCode[0] + Offset, + UpdateHost + ); + } // for Channel + // + // Reset DDR is required + // + if (ResetDDR) { + Status = MrcResetSequence (MrcData); + } else if (SelfRefresh) { + ExitSR (MrcData); + } + + return Status; +} + +/** + Returns the index into the array OptResult in the MrcOutput structure. + + @param[in] OptParam - Margin parameter + + @retval One of the following values: RdSAmpOfft(0), WrDSOfft (1), RxEqOfft(2), TxEqOfft (3), RdOdtOfft(4) +**/ +U8 +GetOptResultType ( + IN U8 OptParam + ) +{ + switch (OptParam) { + case OptRxBias: + return RdSAmpOfft; + + case OptWrDS: + return WrDSOfft; + + case OptRxEq: + return RxEqOfft; + + case OptTxEq: + return TxEqOfft; + + case OptRdOdt: + return RdOdtOfft; + + default: + return 0; // Return RdSAmpOfft to point to the beginning of the array + } +} + +/** + Program DimmOptPoint values on CPU and DIMM sides, such as DIMM ODT, CPU ODT, Ron, Slew Rate, Equalization. + + @param[in,out] MrcData - Include all MRC global data. + @param[in] ChMask - Channel to work on. + @param[in,out] BestDimmOptPoint - Best DIMM Opt settings used to update hardware + @param[in] SkipGRdOdt - Switch to skip updating CPU ODT + @param[in] SkipDimmOdts - Switch to skip updating DIMM ODT + @param[in] SkipBestOffsets - Switch to skip updating Opt settings + @param[in] UpdateHost - Switch to skip updating MRC host structure + + @retval Nothing +**/ +void +UpdateOdtsValues ( + IN OUT MrcParameters *const MrcData, + IN U8 ChMask, + IN OUT DimmOptPoint *BestDimmOptPoint, + IN BOOL SkipGRdOdt, + IN BOOL SkipDimmOdts, + IN BOOL SkipBestOffsets, + IN BOOL UpdateHost + ) +{ + MrcOutput *Outputs; + U8 byte; + U8 rank; + U8 Channel; + U8 offset; + U8 Dimm; + U8 test; + U8 TestArray[5]; + BOOL DebugPrint; + U8 OptParam; + U8 NumTests; + const MrcDebug *Debug; + + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + NumTests = BestDimmOptPoint->OptParamTestListSize; + DebugPrint = 0; + + MrcOemMemorySet (TestArray, 0, sizeof (TestArray)); + if (SkipBestOffsets) { + NumTests = 0; + } + // + // build tests array to update RdSAmpOfft(0), WrDSOfft (1), RxEqOfft(2), TxEqOfft (3), RdOdtOfft(4) + // + for (test = 0; test < NumTests; test++) { + OptParam = BestDimmOptPoint->OptParamTestList[test]; + TestArray[GetOptResultType (OptParam)] = 1; + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Test - %s : %d ,UpdateHost: %d\n", + TOptParamOffsetString[OptParam], + test, + UpdateHost + ); + } + + if (!SkipGRdOdt) { + // + // update GRdOdt + // + BestDimmOptPoint->ODTSet.GRdOdtCode = UpdateCompGlobalOffset ( + MrcData, + RdOdt, + (U8) BestDimmOptPoint->ODTSet.GRdOdt, + UpdateHost + ); + if (DebugPrint) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "best GRdODT aplly is : %d \n", + CalcRdOdt (MrcData, BestDimmOptPoint->ODTSet.GRdOdt) + ); + } + } + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!(MrcChannelExist (Outputs, Channel))) { + continue; // Not valid channel + } + + if (!((MRC_BIT0 << Channel) & ChMask)) { + continue; + } + + offset = 1; + if ((Outputs->Controller[0].Channel[Channel].DimmCount == 1)) { + offset = 0; // disable dynamic odt + for (Dimm = 0; Dimm < MAX_DIMMS_IN_CHANNEL; Dimm++) { + // + // set RttNom=write and RttWr=0 + // + BestDimmOptPoint->ODTSet.RttNom[Channel][Dimm] = BestDimmOptPoint->ODTSet.RttWr[Channel][Dimm]; + } + } +#ifdef ULT_FLAG + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + // + // On ULT (1DPC) DIMM ODT is connected to Vdd, so RttNom must be disabled + // + offset = 1; + BestDimmOptPoint->ODTSet.RttNom[Channel][0] = 0; + BestDimmOptPoint->ODTSet.RttNom[Channel][1] = 0; + BestDimmOptPoint->ODTSet.RttWr[Channel][1] = 0; + } +#endif //ULT_FLAG + + // + // Apply Best RTT Points + // + if (!SkipDimmOdts) { + UpdateOptParamOffset ( + MrcData, + Channel, + 0x3, + 0, + OptDimmOdt, + (S16) + ( + ((offset * BestDimmOptPoint->ODTSet.RttWr[Channel][0]) << 4) + + (BestDimmOptPoint->ODTSet.RttNom[Channel][0]) + ), + UpdateHost + ); + UpdateOptParamOffset ( + MrcData, + Channel, + 0xC, + 0, + OptDimmOdt, + (S16) + ( + ((offset * BestDimmOptPoint->ODTSet.RttWr[Channel][1]) << 4) + + (BestDimmOptPoint->ODTSet.RttNom[Channel][1]) + ), + UpdateHost + ); + if (DebugPrint) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "best RttNom0 aplly is : %d\t\n", + ActualDimmOdt[BestDimmOptPoint->ODTSet.RttNom[Channel][0]] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "best RttNom1 aplly is : %d\t\n", + ActualDimmOdt[BestDimmOptPoint->ODTSet.RttNom[Channel][1]] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "best RttWr0 aplly is : %d\t\n", + ActualDimmOdt[BestDimmOptPoint->ODTSet.RttWr[Channel][0]] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "best RttWr1 aplly is : %d\t\n", + ActualDimmOdt[BestDimmOptPoint->ODTSet.RttWr[Channel][1]] + ); + } + } + + if (NumTests) { + for (byte = 0; byte < Outputs->SdramCount; byte++) { + // + // Apply Best RdOdt and WrDS + // OdtOff = Off[RdOdtOfft][0][Channel][byte] + RdOdtChOffset[Channel]; + // + if (TestArray[RdSAmpOfft]) { + // + // OptRdOdt->OptRxBias + // + UpdateOptParamOffset ( + MrcData, + Channel, + 0xF, + byte, + OptRxBias, + BestDimmOptPoint->BestOptOff[RdSAmpOfft][0].Offset[Channel][byte], + UpdateHost + ); + } + + if (TestArray[WrDSOfft]) { + UpdateOptParamOffset ( + MrcData, + Channel, + 0xF, + byte, + OptWrDS, + BestDimmOptPoint->BestOptOff[WrDSOfft][0].Offset[Channel][byte], + UpdateHost + ); + } + + if (DebugPrint) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "enable=%d best WrDSOfft byte %d is : %d\t\n", + TestArray[WrDSOfft], + byte, + BestDimmOptPoint->BestOptOff[WrDSOfft][0].Offset[Channel][byte] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "enable=%d best RdSAmpOfft byte %d is : %d\t\n", + TestArray[RdSAmpOfft], + byte, + BestDimmOptPoint->BestOptOff[RdSAmpOfft][0].Offset[Channel][byte] + ); + } + + for (rank = 0; rank < MAX_RANK_IN_CHANNEL; rank++) { + if (!MrcRankInChannelExist (MrcData, rank, Channel)) { + continue; + } + // + // Apply Best Tx/Rx EQ Codes + // + if (TestArray[RxEqOfft]) { + UpdateOptParamOffset ( + MrcData, + Channel, + (MRC_BIT0 << rank), + byte, + OptRxEq, + BestDimmOptPoint->BestOptOff[RxEqOfft][rank].Offset[Channel][byte], + UpdateHost + ); + } + + if (TestArray[TxEqOfft]) { + UpdateOptParamOffset ( + MrcData, + Channel, + (MRC_BIT0 << rank), + byte, + OptTxEq, + BestDimmOptPoint->BestOptOff[TxEqOfft][rank].Offset[Channel][byte], + UpdateHost + ); + } + + if (DebugPrint) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "enable=%d best OptRxEq rank%d byte %d is : %d\t\n", + TestArray[RxEqOfft], + rank, + byte, + BestDimmOptPoint->BestOptOff[RxEqOfft][rank].Offset[Channel][byte] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "enable=%d best OptTxEq rank%d byte %d is : %d\t\n", + TestArray[TxEqOfft], + rank, + byte, + BestDimmOptPoint->BestOptOff[TxEqOfft][rank].Offset[Channel][byte] + ); + } + } + } + } + } +} + +/** + Calculate Power based on Ron and Rodt + Includes both static power from Ron/Rodt and dynamic power from Cpad/Cline + The power results here are not absolutely correct but give a reasonable estimate (ie: within 2x) with the proper trends + Getting absolutely correct power numbers with simple calculations is fairly difficult given the transmission line nature of the system + Driver power is calculated as the amount of power drawn from the CPU pin (do we want this to be thermal power instead?) based on the Ron and ODTeff + ODTeff is calculated as both the real, resistive ODT on the bus in parallel with the effective impendence of the cap on the line + This effective impedance is how AC power is included in the measurements + This better models the real system behavior where the power consumed due to dynamic power reduces as termination strength increases + ODT power is calculated as a purely DC term based on Ron and Rodt + The final power reported back is a scaled version of the CPU and DRAM power + This allows one to weight the CPU vs. DRAM power differently in the optimization function based on what is more important + CPU power is generally more important since it can be translated into additional performance + + @param[in] MrcData - Include all MRC global data. + @param[out] Results - Results of the Power power calculations + @param[in] RonCpu - RON CPU value (ohm) + @param[in] RonDimm - RON DIMM value (ohm) + @param[in] Rodtcpu - RODT CPU value + @param[in] Rodtdram - RODT DRAM value + @param[in] Wodtdram - WODT DRAM value + + @retval Nothing +**/ +void +CalcPower ( + IN MrcParameters *MrcData, + OUT MrcPower *Results, + IN U16 RonCpu, + IN U8 RonDimm, + IN U16 Rodtcpu, + IN U16 Rodtdram, + IN U16 Wodtdram + ) +{ + const MrcInput *Inputs; + const MrcDebug *Debug; + MrcOutput *Outputs; + U16 CapTotal; + U32 CapOdt; + U32 Rodt; + U32 Vx; + U32 Vy; + U32 Ix; + U32 Iy; + U32 DrvPwr; + U32 ACPowerRd; + U32 ACPowerWr; + // + // Power Results; + // + U16 ScaleCpuPwr; + U16 ScaleDramPwr; + U16 LineLength; // cm + U16 Cpad; // pF + U32 Derating; + U32 ACPower; + U32 Vswing; + U16 CapPerLength; // pF/cm + U16 Freq; // Ghz + U16 FreqEff; + U16 Pi; // 3.14; + U16 Vdd; // 1.5; mV + U16 SRDimm; // 15ohm serial resistance + U16 NormFactor; + + Vx = 0; + Vy = 0; + Ix = 0; + Iy = 0; + ScaleCpuPwr = 1; + ScaleDramPwr = 1; + LineLength = 10; + Cpad = 4; + CapPerLength = 2; + Pi = 3; + SRDimm = 15; + NormFactor = 100; // if 1000 we get mW + Inputs = &MrcData->SysIn.Inputs; + Debug = &Inputs->Debug; + Outputs = &MrcData->SysOut.Outputs; + Vdd = Outputs->VddVoltage[Inputs->MemoryProfile]; + Freq = (U16) (Outputs->Frequency); + Freq /= 100; // in 10xGhz + // + // capacitance for AC power + // Cut real cap in half and add 10pF offset to better match curves - results x100 pf + // Fixed frequency at 500 MHz(~Data Rate/4 assuming random 1100 type data) - resutls is 100x Ghz + // In general, most of the simulations show fairly flat AC power vs. frequency + // + CapTotal = (Cpad + LineLength * CapPerLength) * 45; + FreqEff = 50; + CapOdt = 10000000 / (2 * Pi * CapTotal * FreqEff); // Scale Up by 2.5x to better match curves + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "RonDimm=%d Rodtcpu=%d Wodtdram=%d Rodtdram=%d RonCpu=%d CapOdt=%d\n",RonDimm,Rodtcpu,Wodtdram,Rodtdram,RonCpu,CapOdt); + // for read + // + Rodt = (Rodtcpu * (Rodtdram + SRDimm)) / (Rodtcpu + (Rodtdram + SRDimm)); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "(Rodtdram+SRDimm)=%d Rodtcpu=%d Rodt=%d \n",(Rodtdram+SRDimm),Rodtcpu,Rodt); + Derating = 1000 * Rodt / (Rodt + CapOdt); // Derate ACPower based on ratio of Real ODT vs. "0DT" due to cap + Ix = Vdd / 2 / (RonDimm + SRDimm + Rodt); // mA + Vx = Vdd - Ix * (RonDimm + SRDimm); // voltage after dimm driver+15ohm + DrvPwr = Ix * Ix * (RonDimm + SRDimm) / NormFactor; // dimm Ron static power + Vswing = 2 * Vx - Vdd; // for ACpower= Vh-Vl + // + // Calculate power associated with swing that cap - mV/1000*pf/100*Ghz/100000 + // + ACPower = Vswing * Vswing / 1000 * CapTotal * FreqEff / 100 / 100 / NormFactor; + ACPowerRd = ACPower * Derating / 1000; // mW + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "read ACPowerRd=%d ACPower=%d Derating=%d Vswing=%d Rodt=%d Ix=%d,Vx=%d,PwrDrv=%d,FreqEff=%d CapOdt=%d Rodt=%d ,DrvPwr=%d\n",ACPowerRd,ACPower,Derating,Vswing,Rodt,Ix,Vx,DrvPwr,FreqEff,CapOdt,Rodt,DrvPwr); + Results->CpuPwrRd = ((Vdd - Vx) * (Vdd - Vx) + Vx * Vx) / (2 * Rodtcpu * NormFactor); // mW @todo:add RxBias? + Iy = (Vx - Vdd / 2) / (Rodtdram + SRDimm); // current in to the NT dimm + Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm + Results->DimmPwrRd = Iy * + Iy * + SRDimm / + NormFactor + + ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) / + (2 * Rodtdram * NormFactor) + + DrvPwr; // mW + // + // for write + // + Rodt = (Wodtdram + SRDimm) * (Rodtdram + SRDimm) / ((Wodtdram + SRDimm) + (Rodtdram + SRDimm)); + Derating = 1000 * Rodt / (Rodt + CapOdt); // De-rate ACPower based on ratio of Real ODT vs. "0DT" due to cap + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rodt=%d Derating=%d ",Rodt,Derating); + Ix = Vdd / 2 / (RonCpu + Rodt); // mA + Vx = Vdd - Ix * (RonCpu); // voltage after cpu driver + DrvPwr = Ix * Ix * (RonCpu) / NormFactor; // cpu Ron static power + Vswing = 2 * Vx - Vdd; // for ACpower + // + // Calculate power associated with swing that cap + // + ACPower = Vswing * Vswing / 1000 * CapTotal * FreqEff / 100 / 100 / NormFactor; + ACPowerWr = ACPower * Derating / 1000; // mW + //MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "write ACPowerWr=%d ACPower=%d Derating=%d Vswing=%d Rodt=%d Ix=%d,Vx=%d,PwrDrv=%d,FreqEff=%d CapOdt=%d Rodt=%d ,DrvPwr=%d\n",ACPowerWr,ACPower,Derating,Vswing,Rodt,Ix,Vx,DrvPwr,FreqEff,CapOdt,Rodt,DrvPwr); + Results->CpuPwrWr = DrvPwr; // mW + Iy = (Vx - Vdd / 2) / (Wodtdram + SRDimm); // current in to the T dimm + Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm + Results->DimmPwrWrT = Iy * Iy * SRDimm / NormFactor + ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) / (2 * Wodtdram * NormFactor); // mW + Iy = (Vx - Vdd / 2) / (Rodtdram + SRDimm); // current in to the NT dimm + Vy = Vx - Iy * (SRDimm); // voltage after 15 ohm inside the dimm + Results->DimmPwrWrNT = Iy * Iy * SRDimm / NormFactor + ((Vdd - Vy) * (Vdd - Vy) + Vy * Vy) / (2 * Rodtdram * NormFactor); // mW + + // + // ScaleCpuPwr and ScaleDramPwr allows one to tradeoff CPU vs. DRAM power + // + Results->ACPowerRd = ACPowerRd; + Results->ACPowerWr = ACPowerWr; + Results->TotPwr = (U16) + ( + 60 * (Results->CpuPwrRd * ScaleCpuPwr + Results->DimmPwrRd * ScaleDramPwr + ACPowerRd * ScaleDramPwr) + 40 * + ( + (Results->DimmPwrWrT + Results->DimmPwrWrNT) * + ScaleDramPwr + + Results->CpuPwrWr * + ScaleCpuPwr + + ACPowerWr * + ScaleCpuPwr + ) + ) / 100; + Results->ACPower = (60 * ACPowerRd + 40 * ACPowerWr) / 100; + Results->CpuPower = (U16) + ( + 60 * (Results->CpuPwrRd * ScaleCpuPwr ) + 40 * + ( + Results->CpuPwrWr * + ScaleCpuPwr + + ACPowerWr * + ScaleCpuPwr + ) + ) / 100; + + Results->DimmPwr = (U16) + ( + 60 * (Results->DimmPwrRd * ScaleDramPwr + ACPowerRd * ScaleDramPwr) + 40 * + ( + (Results->DimmPwrWrT + Results->DimmPwrWrNT) * + ScaleDramPwr + ) + ) / 100; + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " PwrTotal=%d DimmPwrR=%d DimmPwrWrT=%d DimmPwrWrNT=%d PwrDrvR=%d Rodt=%d PwrDrvW=%d\n",Results->TotPwr,Results->DimmPwrRd,Results->DimmPwrWrT,Results->DimmPwrWrNT,ACPowerRd,Rodt,ACPowerWr); + return; +} + +/** + Calculate Power Trend line based on Cpu and Dimms Ron and Odt's + + @param[in] MrcData - Include all MRC global data. + @param[in] Channel - Channel to work on. + @param[in] DimmMask - DIMMs to work on. + @param[in,out] DimmOptPoints - Structure of all the DIMM ODT optimal settings. + @param[in] Points2calc - Data to build the trendline on. + @param[in] ArrayLength - Array length of Points2calc. + @param[in] LenMargin - The length of inputMargins we are optimizing (0 - LenMargin -1). + @param[in] TestList - TestList index in Points2cal: WrVref, RdVref, WrT, RdT + @param[in] Scale - Scale to apply per test to Points2calc + @param[in] TestListSize - Size of TestList/Scale + @param[in] PwrCalc1d - Determines if the power test is 1-D or 2-D. + @param[in] PWRTrendSlope - Determines how aggressive the T-line will be.(0%-100%) + + @retval Nothing +**/ +void +CalcPowerTrend ( + IN MrcParameters *MrcData, + IN U8 Channel, + IN U8 DimmMask, + IN OUT void *DimmOptPoints, + IN void *Points2calc, + IN U8 ArrayLength, + IN U8 LenMargin, + IN U8 *TestList, + IN U8 *Scale, + IN U8 TestListSize, + IN BOOL PwrCalc1d, + IN U8 PWRTrendSlope + ) +{ + const MrcDebug *Debug; + MrcChannelOut *ChannelOut; + U16 MaxPoints[4]; + U16 MinPoints[4]; + U16 MaxPwr; + U16 MinPwr; + U8 off; + U8 test; + U8 dimm; + U8 TestParam; + MrcPower PwrRes; + U16 AveOfMax; + U16 X; + S16 MinRatio; + S16 Ratio; + U16 Slope; + U16 SlopeOver100; + U16 Rodtcpu; + U8 RonDimm; + U8 RonCpu; + U16 Rodtdram; + U16 Wodtdram; + U16 *Points; + U16 *PointsElement; + DimmOptPoint *DimmPoints; + U8 dimmcount; + BOOL is1DPC; + U16 AvgROdt; + + ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel]; + Debug = &MrcData->SysIn.Inputs.Debug; + Points = (U16 *) Points2calc; + is1DPC = (ChannelOut->DimmCount == 1); + MaxPwr = 0; + MinPwr = 0xffff; + Wodtdram = 0; + + MrcOemMemorySet ((U8 *) &PwrRes, 0, sizeof (PwrRes)); + MrcOemMemorySetWord (MaxPoints, 0, sizeof (MaxPoints) / sizeof (U16)); + MrcOemMemorySetWord (MinPoints, 0xFFFF, sizeof (MinPoints) / sizeof (U16)); + + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "TestListSize=%d\n",TestListSize); + // + for (off = 0; off < LenMargin; off++) { + // + // sorting the min max power points + // + for (test = 0; test < TestListSize; test++) { + // + // sorting the min max margin points for each test + // + PointsElement = (Points + ArrayLength * test + off); + if (MaxPoints[test] < *PointsElement) { + MaxPoints[test] = *PointsElement; + } + + if (MinPoints[test] > *PointsElement) { + MinPoints[test] = *PointsElement; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*(Points+ArrayLength*test+off)=%d (Points+ArrayLength*test+off)=%x\n",*(Points+ArrayLength*test+off),(Points+ArrayLength*test+off)); + // + } + + if (!PwrCalc1d) { + DimmPoints = (DimmOptPoint *) DimmOptPoints; + RonDimm = 0; + RonCpu = 30; + Rodtcpu = CalcRdOdt (MrcData, (DimmPoints + off)->ODTSet.GRdOdt); + dimmcount = 0; + AvgROdt = 0; + for (dimm = 0; dimm < MAX_DIMMS_IN_CHANNEL; dimm++) { + if (!((MRC_BIT0 << dimm) & DimmMask)) { + continue; + } + // + // read from MR1 the DimmRon + // + RonDimm += (U8) CalcOptPower (MrcData, Channel, 2 * dimm, 0, OptDimmRon, 0, 0, 1); + Rodtdram = ActualDimmOdt[(DimmPoints + off)->ODTSet.RttNom[Channel][dimm]]; + Wodtdram = ActualDimmOdt[(DimmPoints + off)->ODTSet.RttWr[Channel][dimm]]; + if (is1DPC) { // in 1DPC channel always use only one of the terminations + if (Wodtdram == 0) { + Wodtdram = Rodtdram; + } + Rodtdram = 0x3fff; // put 8k ohm as infinity + } + + if (Rodtdram == 0) { + Rodtdram = 0x3fff; + } + + if (Wodtdram == 0) { + Wodtdram = Rodtdram; // in 2DPC with RttW=0 + } + + AvgROdt += Rodtdram; + dimmcount++; + } + + AvgROdt = (dimmcount != 0) ? AvgROdt / dimmcount : AvgROdt; + RonDimm = (dimmcount != 0) ? RonDimm / dimmcount : RonDimm; + if ((120 < AvgROdt) && (AvgROdt < 0x3fff)) { + Rodtdram = 240; // the mix case of one open and one not + } else { + Rodtdram = AvgROdt; // for write average not needed because its by dimm + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "input for power calc Wodtdram=%d Rodtdram=%d RonDimm=%d \n",Wodtdram,Rodtdram,RonDimm); + // + PointsElement = (Points + ArrayLength * TestListSize + off); + CalcPower (MrcData, &PwrRes, RonCpu, RonDimm, Rodtcpu, Rodtdram, Wodtdram); + *PointsElement = PwrRes.TotPwr; + (DimmPoints + off)->PowerCalc = PwrRes; + + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rodtcpu=%d Wodtdram=%d EqRodtdram=%d Calcpower=%d\n",Rodtcpu,Wodtdram,Rodtdram,*(Points+ArrayLength*test+off)); + // + } else { + PointsElement = (Points + ArrayLength * TestListSize + off); + } + + if (MaxPwr < *PointsElement) { + MaxPwr = *PointsElement; + } + + if (MinPwr > *PointsElement) { + MinPwr = *PointsElement; + } + + if (LenMargin == 1) { + MaxPwr = *PointsElement; + MinPwr = 0; + } + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "MaxPwr=%d MinPwr=%d\n",MaxPwr,MinPwr); + // + AveOfMax = 0; + MinRatio = 0x7fff; + for (test = 0; test < TestListSize; test++) { + AveOfMax += MaxPoints[test]; + // + // map Test to TestParam + // + TestParam = TestList[test]; + Ratio = (100 * (MaxPoints[test] / Scale[test] - UpmPwrLimitValue (MrcData, TestParam, UpmLimit))) / + (UpmPwrLimitValue (MrcData, TestParam, PowerLimit) - UpmPwrLimitValue (MrcData, TestParam, UpmLimit)); + if (MinRatio > Ratio) { + MinRatio = Ratio; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "AveOfMax=%d MinRatio=%d MaxPoints[%d]=%d Scale[%d]=%d \n",AveOfMax,MinRatio,test,MaxPoints[test],test,Scale[test]); + // + } + + AveOfMax = AveOfMax / TestListSize; + // + // if MaxPoint > UPM Limit: PwrTrend should be flat + // if MaxPoints == PwrLimit: PwrTrend should have slope going from AveOfMax to (1-PWRTrendSlope/100)*AveOfMax + // PwrTrend will be a linear slope going from (MinPwr, (1- PWRTrendSlope/100)*AveOfMax) to (MaxPwr, AveOfMax) + // + Slope = (PWRTrendSlope * MinRatio) / 100; + SlopeOver100 = 0; + if (Slope > 100) { + // + // could only happen if no power limits + // + SlopeOver100 = Slope - 100; + Slope = 100; + } + + for (off = 0; off < LenMargin; off++) { + PointsElement = (Points + ArrayLength * TestListSize + off); + if (MinRatio < 0) { + *PointsElement = 1; + } else { + if (MaxPwr == MinPwr) { + X = 0; // no power consideration and not divide by zero + } else { + // + // % of where you are between Min and Max Pwr. X=0 should be MaxPwr and 100 should be MinPwr + // + X = 100 - 100 * (*PointsElement - MinPwr) / (MaxPwr); + } + // + // Create a linear line based on Power from (1 - PWRTrendSlope / 100) * AveOfMax to AveOfMax + // Adding a specicial case for TX XTalk: If PWRTrendSlope = 0 and ArrayLength = BIT_TX_XTALK_RANGE + // just multiply power numbers by AveOfMax. + // + if ((PWRTrendSlope == 0) && (ArrayLength == BIT_TX_XTALK_RANGE)) { + *PointsElement = *PointsElement * AveOfMax / 100; + } else { + *PointsElement = AveOfMax * (100 - Slope + (((Slope + SlopeOver100) * X) / 100)) / 100; + } + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "power trend Points[%d][%d]=%d\n",TestListSize,off,*(Points+ArrayLength*TestListSize+off)); + // + } + } +} + +#ifdef MRC_DEBUG_PRINT + +#if 0 // This function is not used right now +/** + Prints OptParam values from CRs and Host structure for all ch/Rank/byte as well as + the Best optimization value (if requested) + OptWrDS = 0 + OptRdOd = 1 + OptSCom = 2 + OptTComp = 3 + OptTxEq = 4 + OptRxEq = 5 + OptRxBias = 6 + OptDimmOdt = 7 + OptDimmOdtWr = 8 + OptDimmRon = 9 + OptDefault = 10 + + @param[in] MrcData - Include all MRC global data. + @param[in] ChMask - Channel Mask to print the summary for + @param[in] RankMask - Rank Mask to print the summary for (in case Rank is not applicable set RankMask = 0xF) + @param[in] OptParam - Defines the OptParam Offsets. OptDefault reports all parameters + @param[in] OptOff - Structure containg the best offest and margins for the OptParam. + If OptOffsetChByte is not available, NullPtr needs to be passed (void *NullPtr) + @param[in] OptResult - True/False: Whether to print the Best optimization value + + @retval Nothing +**/ +void +ReadOptParamOffsetSum ( + IN MrcParameters *const MrcData, + IN U8 ChMask, + IN U8 RankMask, + IN const U8 OptParam, + IN OptOffsetChByte *OptOff, + IN BOOL OptResult + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + U8 Channel; + U8 Rank; + U8 Byte; + U8 Param; + U8 NumBytes; + U8 ChannelMask; + S16 OffArr[2]; + S16 Best; + BOOL PerRank; + BOOL SkipByte; + + Outputs = &MrcData->SysOut.Outputs; + Debug = &MrcData->SysIn.Inputs.Debug; + ChannelMask = Outputs->ValidChBitMask & ChMask; + NumBytes = (U8) Outputs->SdramCount; + MrcOemMemorySetWord ((U16 *) OffArr, (U16) 0, sizeof (OffArr) / sizeof (OffArr[0])); + + for (Param = OptWrDS; Param < OptDefault; Param++) { + if (OptParam == Param || OptParam == OptDefault) { + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nOffsets for Optimization Parameter %s\n", TOptParamOffsetString[Param]); + PerRank = + ( + Param == OptTxEq || + Param == OptRxEq || + Param == OptDimmOdt || + Param == OptDimmOdtWr || + Param == OptDimmRon + ); + SkipByte = (Param == OptDimmRon || Param == OptDimmOdt || Param == OptDimmOdtWr); + + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (!((MRC_BIT0 << Channel) & ChannelMask)) { + continue; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Channel %d\n", Channel); + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((MRC_BIT0 << Rank) & RankMask) { + if (!(MrcRankInChannelExist (MrcData, Rank, Channel))) { + continue; + } + + if (PerRank) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Rank %d\n", Rank); + } else if (Rank > 0) { + continue; + } + + if (!SkipByte) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Byte\t"); + for (Byte = 0; Byte < NumBytes; Byte++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Byte); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } + + if (OptResult) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt/CR/Host\t"); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "CR/Host\t"); + } + + if (!SkipByte) { + for (Byte = 0; Byte < NumBytes; Byte++) { + ReadOptParamOffset (MrcData, &OffArr[0], Channel, Rank, Byte, Param); + + if (OptResult) { + Best = OptOff->Offset[Channel][Byte]; + if (Best != OffArr[0] || Best != OffArr[1]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\nError: Mismatch in Param %s in Channel %d Rank %d Byte %d is found: Best=%d CR=%d Host=%d\n", + TOptParamOffsetString[Param], + Channel, + Rank, + Byte, + Best, + OffArr[0], + OffArr[1] + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/%d/%d\t", Best, OffArr[0], OffArr[1]); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/%d\t", OffArr[0], OffArr[1]); + } + } + } else { + ReadOptParamOffset (MrcData, &OffArr[0], Channel, Rank, 0, Param); + + if (Param == OptDimmRon || Param == OptDimmOdtWr) { + if (OptResult) { + Best = OptOff->Offset[Channel][0]; + if (Best != OffArr[1]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\nError: Mismatch in Param %s in Channel %d Rank %d is found: Best=%d Host=%d\n", + TOptParamOffsetString[Param], + Channel, + Rank, + Best, + OffArr[1] + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/NA/%d", Best, OffArr[1]); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "NA/%d", OffArr[1]); + } + } else if (Param == OptDimmOdt) { + if (OptResult) { + Best = OptOff->Offset[Channel][0]; + if (Best != OffArr[0]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\nError: Mismatch in Param %s in Channel %d Rank %d is found: Best=%d Host=%d\n", + TOptParamOffsetString[Param], + Channel, + Rank, + Best, + OffArr[0] + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d/NA/%d", Best, OffArr[0]); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "NA/%d", OffArr[0]); + } + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } + } + } + } + } +} + +/** + Reads OptParam value from CRs and Host structure for a given ch/Rank/byte combination + OptParam can be: WrDS, RdOdt, TComp, SComp, RxEq, TxEq, RxBias, DIMM Ron, DIMM RttNom or DIMM RttWr + + @param[in] MrcData - Include all MRC global data. + @param[out] FinalVal - Pointer to the array consisting of CR value and Host value for a particular + OptParam and given ch/Rank/byte combination. + @param[in] Channel - Channel index to work on. + @param[in] Rank - Rank index to work on (valid only for TxEq and RxEq, for others is ignored) + @param[in] Byte - Byte index to work on. + @param[in] OptParam - Defines the OptParam Offsets. Supported OptParam = + [0: WrDS, 1: RdODT, 2: SComp, 3: TComp, 3: TxEq, + 4: RxEq, 5: RxBias, 6: DimmOdt, 7: DimmOdtWr] + + @retval Nothing +**/ +void +ReadOptParamOffset ( + IN MrcParameters *const MrcData, + OUT S16 *FinalVal, + IN const U8 Channel, + IN const U8 Rank, + IN const U8 Byte, + IN const U8 OptParam + ) +{ + const U16 RttNomMRSEncodingConst[] = {0x00, 0x10, 0x01, 0x11, 0x81, 0x80}; // RttNom Off,120,60,40,30,20 Ohms + const U16 RttWrMRSEncodingConst[] = {0x00, 0x02, 0x01}; // RttWr RttNom,120,60 Ohms + const MrcDebug *Debug; +#ifdef ULT_FLAG + const U8 LpddrRonEnc[] = {0x1,0x2,0x3}; //{34,40,48}; + const U8 LpddrOdtEnc[] = {0x0,0x2,0x3}; //{0,120,240}; + BOOL Lpddr; + U16 DimmRonMask; +#endif // ULT_FLAG + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + U16 *MrReg; + BOOL Type; + U8 Index; + U16 MRValue; + U16 RttNomMRSEncoding[sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0])]; + U16 RttWrMRSEncoding[sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0])]; + U16 RttWr; + U16 RttNom; + U16 RttNomMask; + U16 RttWrMask; + U32 Offset; + S16 UpOff; + S16 DnOff; + DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetCompCr; + DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetCompHost; + DDRDATA0CH0_CR_TXTRAINRANK0_STRUCT CrTxTrainRank; + DDRDATA0CH0_CR_RXTRAINRANK0_STRUCT CrRxTrainRank; + DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1Cr; + DDRDATA0CH0_CR_DDRCRDATACONTROL1_STRUCT DdrCrDataControl1Host; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + ChannelOut = &Outputs->Controller[0].Channel[Channel]; + MrcOemMemoryCpy ((U8 *) RttNomMRSEncoding, (U8 *) RttNomMRSEncodingConst, sizeof (RttNomMRSEncoding)); + MrcOemMemoryCpy ((U8 *) RttWrMRSEncoding, (U8 *) RttWrMRSEncodingConst, sizeof (RttWrMRSEncoding)); + +#ifdef ULT_FLAG + Lpddr = Outputs->DdrType == MRC_DDR_TYPE_LPDDR3; +#endif + // + // Compensation Offsets + // + Type = ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptTComp) || (OptParam == OptSComp)); + if (Type) { + + Offset = DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG + + ((DDRDATA1CH0_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Byte) + + ((DDRDATA0CH1_CR_DDRCRDATAOFFSETCOMP_REG - DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_REG) * Channel); + DdrCrDataOffsetCompCr.Data = MrcReadCR (MrcData, Offset); + DdrCrDataOffsetCompHost.Data = ChannelOut->DataCompOffset[Byte]; + + if (OptParam == OptWrDS) { + UpOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqDrvUpCompOffset; + DnOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqDrvDownCompOffset; + if (UpOff != DnOff) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "DqDrvUpCompOffset %d is not equal to DqDrvDownCompOffset for Channel=%d, Byte=%d\n", + UpOff, + DnOff, + Channel, + Byte + ); + } + + FinalVal[0] = UpOff; + UpOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqDrvUpCompOffset; + DnOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqDrvDownCompOffset; + if (UpOff != DnOff) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "DqDrvUpCompOffset %d is not equal to DqDrvDownCompOffset for Channel=%d, Byte=%d in Host Structure\n", + UpOff, + DnOff, + Channel, + Byte + ); + } + + FinalVal[1] = UpOff; + + if (FinalVal[0] & 0x20) { // 6-bit 2's complement + FinalVal[0] -= 0x40; + } + if (FinalVal[1] & 0x20) { // 6-bit 2's complement + FinalVal[1] -= 0x40; + } + } else if (OptParam == OptRdOdt) { + UpOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqOdtUpCompOffset; + DnOff = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqOdtDownCompOffset; + if (UpOff != DnOff) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "DqOdtUpCompOffset %d is not equal to DqOdtDownCompOffset for Channel=%d, Byte=%d\n", + UpOff, + DnOff, + Channel, + Byte + ); + } + + FinalVal[0] = UpOff; + UpOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqOdtUpCompOffset; + DnOff = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqOdtDownCompOffset; + if (UpOff != DnOff) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "DqOdtUpCompOffset %d is not equal to DqOdtDownCompOffset for Channel=%d, Byte=%d in Host Structure\n", + UpOff, + DnOff, + Channel, + Byte + ); + } + + FinalVal[1] = UpOff; + + if (FinalVal[0] & 0x10) { // 5-bit 2's complement + FinalVal[0] -= 0x20; + } + if (FinalVal[1] & 0x10) { // 5-bit 2's complement + FinalVal[1] -= 0x20; + } + } else if (OptParam == OptTComp) { + FinalVal[0] = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqTcoCompOffset; + FinalVal[1] = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqTcoCompOffset; + + if (FinalVal[0] & 0x10) { // 5-bit 2's complement + FinalVal[0] -= 0x20; + } + if (FinalVal[1] & 0x10) { // 5-bit 2's complement + FinalVal[1] -= 0x20; + } + } else if (OptParam == OptSComp) { + FinalVal[0] = (S16) (S32) DdrCrDataOffsetCompCr.Bits.DqSlewRateCompOffset; + FinalVal[1] = (S16) (S32) DdrCrDataOffsetCompHost.Bits.DqSlewRateCompOffset; + + if (FinalVal[0] & 0x10) { // 5-bit 2's complement + FinalVal[0] -= 0x20; + } + if (FinalVal[1] & 0x10) { // 5-bit 2's complement + FinalVal[1] -= 0x20; + } + } + + if (FinalVal[0] != FinalVal[1]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Byte=%d\n", + TOptParamOffsetString[OptParam], + FinalVal[0], + FinalVal[1], + Channel, + Byte + ); + } + } + // + // Equalization Settings + // + Type = ((OptParam == OptTxEq) || (OptParam == OptRxEq)); + if (Type) { + // + // TxEq[5:4] = Emphasize = [3, 6, 9, 12] legs + // TxEq[3:0] = Deemphasize = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 4*Rsvd] legs + // + if (OptParam == OptTxEq) { + + Offset = DDRDATA0CH0_CR_TXTRAINRANK0_REG + + ((DDRDATA0CH1_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Channel) + + ((DDRDATA0CH0_CR_TXTRAINRANK1_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Rank) + + ((DDRDATA1CH0_CR_TXTRAINRANK0_REG - DDRDATA0CH0_CR_TXTRAINRANK0_REG) * Byte); + + CrTxTrainRank.Data = MrcReadCR (MrcData, Offset); + FinalVal[0] = (S16) (S32) CrTxTrainRank.Bits.TxEqualization; + FinalVal[1] = (S16) (S32) ChannelOut->TxEq[Rank][Byte]; + FinalVal[0] &= 0xF; // Read Deemphasize portion only + FinalVal[1] &= 0xF; // Read Deemphasize portion only + } + // + // RxEQ[4:0] CR Decoding (pF/kOhm) + // [2:0] + // [4:3] 0 1 2 3 4 5-7 + // 0 0.5/.02 0.5/1.0 0.5/.50 0.5/.25 0.5/.12 rsvd + // 1 1.0/.02 1.0/1.0 1.0/.50 1.0/.25 1.0/.12 rsvd + // 2 1.5/.02 1.5/1.0 1.5/.50 1.5/.25 1.5/.12 rsvd + // 3 2.0/.02 2.0/1.0 2.0/.50 2.0/.25 2.0/.12 rsvd + // Sweep = 0-19 [4:3] = (Sweep/5) [2:0] = (Sweep%5) + // + if (OptParam == OptRxEq) { + Offset = DDRDATA0CH0_CR_RXTRAINRANK0_REG + + ((DDRDATA0CH1_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Channel) + + ((DDRDATA0CH0_CR_RXTRAINRANK1_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Rank) + + ((DDRDATA1CH0_CR_RXTRAINRANK0_REG - DDRDATA0CH0_CR_RXTRAINRANK0_REG) * Byte); + + CrRxTrainRank.Data = MrcReadCR (MrcData, Offset); + FinalVal[0] = (S16) (S32) CrRxTrainRank.Bits.RxEq; + FinalVal[1] = (S16) (S32) ChannelOut->RxEq[Rank][Byte]; + FinalVal[0] = ((FinalVal[0] >> 3) * 5) + (FinalVal[0] & 0x7); // Multiply Cap portion by 5 and add Res portion + FinalVal[1] = ((FinalVal[1] >> 3) * 5) + (FinalVal[1] & 0x7); // Multiply Cap portion by 5 and add Res portion + } + + if (FinalVal[0] != FinalVal[1]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Rank=%d, Byte=%d\n", + TOptParamOffsetString[OptParam], + FinalVal[0], + FinalVal[1], + Channel, + Rank, + Byte + ); + } + } + // + // RX Amplifier BIAS + // + if ((OptParam == OptRxBias)) { + // + // Mapping: [0: 0.44, 1: 0.66, 2: 0.88, 3: 1.00, 4: 1.33, 5: 1.66, 6: 2.00, 7: 2.33] + // + Offset = DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG + + ((DDRDATA1CH0_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Byte) + + ((DDRDATA0CH1_CR_DDRCRDATACONTROL1_REG - DDRDATA0CH0_CR_DDRCRDATACONTROL1_REG) * Channel); + + DdrCrDataControl1Cr.Data = MrcReadCR (MrcData, Offset); + DdrCrDataControl1Host.Data = ChannelOut->DqControl1[Byte].Data; + FinalVal[0] = (S16) (S32) DdrCrDataControl1Cr.Bits.RxBiasCtl; + FinalVal[1] = (S16) (S32) DdrCrDataControl1Host.Bits.RxBiasCtl; + + if (FinalVal[0] != FinalVal[1]) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: Param %s: CR value %d doesn't match Host value %d for Channel=%d, Byte=%d\n", + TOptParamOffsetString[OptParam], + FinalVal[0], + FinalVal[1], + Channel, + Byte + ); + } + } + // + // Dimm Ron value + // + if ((OptParam == OptDimmRon)) { + // + // DIMM Ron Encoding DriverImpCtrl[A5,A1] + // + if (MrcRankInChannelExist (MrcData, Rank, Channel)) { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[(Rank % 2)].MR[mrMR0]; +#ifdef ULT_FLAG + if (Lpddr) { + DimmRonMask = (MRC_BIT3 | MRC_BIT2 | MRC_BIT1 | MRC_BIT0); + MRValue = (MrReg[mrMR3] & DimmRonMask); + + for (Index = 0; Index < (sizeof (LpddrRonEnc) / sizeof (LpddrRonEnc[0])); Index++) { + if (MRValue == LpddrRonEnc[Index]) { + FinalVal[1] = (S16) (S8) Index; + } + } + } else +#endif + { + MRValue = MrReg[mrMR1]; + FinalVal[1] = (S16) ((MRValue >> 1) & 0x1); + } + } + } + // + // DIMM ODT Values + // + if ((OptParam == OptDimmOdt) || (OptParam == OptDimmOdtWr)) { + // + // DIMM ODT Encoding RttNom[A9,A6,A2] RttWr[A10, A9] LPDDR - No RttNom + // + if (MrcRankInChannelExist (MrcData, Rank, Channel)) { +#ifdef ULT_FLAG + if (Lpddr) { + RttWrMask = (MRC_BIT1 | MRC_BIT0); + MRValue = (ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR11 & RttWrMask); + + for (Index = 0; Index < (sizeof (LpddrOdtEnc) / sizeof (LpddrOdtEnc[0])); Index++) { + if (MRValue == LpddrOdtEnc[Index]) { + FinalVal[1] = (S16) (S8) Index; + } + } + + FinalVal[0] = 0; + } else +#endif + { + MrReg = &ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR0]; + RttNomMask = (MRC_BIT9 + MRC_BIT6 + MRC_BIT2); + RttWrMask = (MRC_BIT10 + MRC_BIT9); + RttWr = (MrReg[mrMR2] & RttWrMask) >> 9; + RttNom = (MrReg[mrMR1] & RttNomMask) >> 2; + + for (Index = 0; Index < sizeof (RttNomMRSEncodingConst) / sizeof (RttNomMRSEncodingConst[0]); Index++) { + if (RttNom == RttNomMRSEncoding[Index]) { + FinalVal[0] = (S16) (S8) Index; + } + } + + for (Index = 0; Index < sizeof (RttWrMRSEncodingConst) / sizeof (RttWrMRSEncodingConst[0]); Index++) { + if (RttWr == RttWrMRSEncoding[Index]) { + FinalVal[1] = (S16) (S8) Index; + } + } + } + } + } +} + +/** + This function will print out the last margin data collected of the Param passed in. + It will print both edges of all the requested bytes, Ranks and Channels. + NOTE: The function will not check to see if the Rank/Channel exists. It will print out the + values stored in the margin array regardless of population status. + + @param[in] MrcData - Global MRC data. + @param[in] Param - Parameter of MRC_MarginTypes of which to print the margin. + @param[in] ChannelMask - Bit mask of channels to print. + @param[in] RankMask - Bit mask of ranks to print. + @param[in] ByteMask - Bit mask of bytes to print. + + @retval Nothing. +**/ +void +MrcPrintLastMargins ( + IN MrcParameters *const MrcData, + IN const U8 Param, + IN const U8 ChannelMask, + IN const U8 RankMask, + IN const U16 ByteMask + ) +{ + MrcDebug const *Debug; + MrcOutput *Outputs; + char *EdgeString; + MrcMarginResult LastResultParam; + U32 (*LastMargins)[MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; + U8 Channel; + U8 Rank; + U8 Byte; + U8 Edge; + + LastResultParam = GetMarginResultType (Param); + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + LastMargins = Outputs->MarginResult; + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%s Last Margins:\n", + MarginTypesString[Param] + ); + + EdgeString = ((Param == RdV) || (Param == WrV)) ? "H" : "R"; + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Byte\t"); + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if ((1 << Byte) & ByteMask) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 10d", Byte); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nEdge\t"); + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if ((1 << Byte) & ByteMask) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " L %s", EdgeString); + } + } + + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((1 << Rank) & RankMask) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if ((1 << Channel) & ChannelMask) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\nR%d.C%d\t", Rank, Channel); + for(Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if ((1 << Byte) & ByteMask) { + for (Edge = 0; Edge < MAX_EDGES; Edge++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "% 5d", LastMargins[LastResultParam][Rank][Channel][Byte][Edge]); + } + } + } + } + } + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // End of table +} +#endif // #if 0 + +/** + This function implements switch to print the correct format and data for the + OptResultsPerByte struct members. + + @param[in] Debug - Debug pointer for printing. + @param[in] Data - Pointer to OptResultsPerByte struct. + @param[in] TypeIndex - Member of OptResultsPerByte to print. + @param[in] TestIndex - Some parameters store multiple test results to be printed. + @param[in] MidPoint - Used to convert from zero-based indexing to the selected value + + @retval Nothing. +**/ +void +MrcOptResultsPerBytePrint ( + IN const MrcDebug *const Debug, + IN OptResultsPerByte *Data, + IN U8 TypeIndex, + IN U8 TestIndex, + IN S8 MidPoint + ) +{ + switch (TypeIndex) { + case (MrcOptResultBest): + (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "=== %d ===", Data->Best - MidPoint) : + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); + break; + + case (MrcOptResultGrdBnd): + (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "*** %d ***", Data->GuardBand) : + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); + break; + + case(MrcOptResultOffSel): + (TestIndex == 0) ? MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "--> %d <--", Data->Best - MidPoint + Data->GuardBand) : + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); + break; + + case (MrcOptResultScale): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->Scale[TestIndex]); + break; + + case (MrcOptResultSignal): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d%s\t", Data->Signal[TestIndex] / 100, Data->Signal[TestIndex] % 100 / 10, "%"); + break; + + case (MrcOptResultNoise): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d%s\t", Data->Noise[TestIndex] / 100, Data->Noise[TestIndex] % 100 / 10, "%"); + break; + + case (MrcOptResultRatio): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d\t", Data->Ratio[TestIndex] / 1000, Data->Ratio[TestIndex] % 1000 / 100); + break; + + case (MrcOptResultMaxPost): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->MaxPost[TestIndex]); + break; + + case (MrcOptResultMinPost): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Data->MinPost[TestIndex]); + break; + + case (MrcOptResultTicks): + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d.%d\t", Data->Ticks[TestIndex] / 10, Data->Ticks[TestIndex] % 10); + break; + + case (MrcOptResultSnrTot): + (TestIndex == 0) ? MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + " %d.%d%s\t", + (U32) Data->SNRTotal / 100, + (U32) Data->SNRTotal % 100 / 10, + "%" + ) : MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); + break; // assuming we dont exceed 32 bits + + default: + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "OptResultPerByteDbgStr Switch exceeded number of cases defined\n"); + } +} + + +/** + This function prints the Optimize margin result table + e.g: calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM] + + @param[in] MrcData - MRC data structure + @param[in] calcResultSummary - The data array [MAX_CHANNEL][MAX_SDRAM_IN_DIMM] + @param[in] TestList - Test list + @param[in] NumTest - Number of test + @param[in] NumOffsets - Number of offsets + @param[in] MidPoint - Middle point + @param[in] IncEnds - Print ends points + @param[in] OptParam - Used to convert to the Opt param string for printing + @param[in] OptPower - Opt Power values to be printed + @param[in] Channel - Channel to print + @param[in] Ranks - Ranks to print + @param[in] TrendLine - Switch to print the trend line + @param[in] Nibble - take low/high bytes + @param[in] perCh - Switch to only print 1 Byte of data + @param[in] noPrint - Boolean used to disable printing of results + + @retval Nothing +**/ +void +PrintCalcResultTableCh ( + IN MrcParameters *const MrcData, + IN OptResultsPerByte calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM], + IN U8 *TestList, + IN U8 NumTest, + IN U8 NumOffsets, + IN S8 MidPoint, + IN BOOL IncEnds, + IN U8 OptParam, + IN U16 *OptPower, + IN U8 Channel, + IN U8 Ranks, + IN BOOL TrendLine, + IN U8 Nibble, + IN BOOL perCh, + IN BOOL noPrint + ) +{ + const MrcDebug *Debug; + OptResultsPerByte *data; + S8 Off; + S8 Start; + S8 Stop; + U8 i; + U8 j; + U8 b; + U8 FirstByte; + U8 NumBytes; + U8 NumTestPlus; + U32 Result; + BOOL Format64Results; + U8 Param; + + Format64Results = 1; + // + // Display result in %/Delta , 0-displat raw 64bit result in HEX + // + Debug = &MrcData->SysIn.Inputs.Debug; + Start = (!IncEnds); + Stop = NumOffsets - (!IncEnds); + if (noPrint) { + return ; + + } + + FirstByte = (Nibble) ? 4 : 0; + NumBytes = FirstByte + 4 + Nibble * MrcData->SysOut.Outputs.SdramCount % 8; + if (perCh) { + NumBytes = 1; + } + + NumTestPlus = (TrendLine) ? NumTest + 1 : NumTest; + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\n<======== optimize %s ========>Plot results ", + TOptParamOffsetString[OptParam] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "<Channel=%d><rank/s=0x%x><Nibble=%s> across settings :(Start=%d,Stop=%d)\n", + Channel, + Ranks, + (Nibble) ? "High" : "Low", + Start - MidPoint, + Stop - MidPoint - 1 + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Bytes\t"); + for (b = FirstByte; b < NumBytes; b++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", b); + for (i = 0; i < NumTestPlus + 1; i++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); // tab insertion + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here ! + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Offset\t"); // row starts here ! + if (OptPower[Stop - 1] != 0) {//WA: need to add param to enable this print + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", 3 + TOptParamOffsetString[OptParam]); + } + + for (b = FirstByte; b < NumBytes; b++) { + for (i = 0; i < NumTest; i++) { + // + // Test types header + // + Param = TestList[i]; + if (Param > CmdV) { + Param = (Param % 16) + 4; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", MarginTypesString[Param]); + } + + if (TrendLine) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", "T.line"); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt.func\t"); // more header.. + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n"); // row end here ! + for (Off = Start; Off < Stop; Off++) { + // + // row starts here ! + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Off - MidPoint); + if (OptPower[Stop - 1] != 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", OptPower[Off]); + } + + for (b = FirstByte; b < NumBytes; b++) { + if (b < MAX_SDRAM_IN_DIMM) { + data = &calcResultSummary[Channel][b]; + } else { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: calcResultSummary array out of bounds! %d > %d \n", + b, + MAX_SDRAM_IN_DIMM - 1 + ); + return; + } + + for (i = 0; i < NumTestPlus; i++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", data->Margins[i][Off].EW); + } + + if (Format64Results) { + Result = (U32) (MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 (data->Result[Off], 200), data->MaxR)); + Result /= 2; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t", Result); + } + + if (!Format64Results) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%08x-%08x\t\t", + (U32) MrcOemMemoryRightShiftU64 (data->Result[Off], + 32), + (U32) (data->Result[Off]) + ); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here ! + } + + for (i = 0; i < (sizeof (OptResultDbgStrings) / sizeof (*OptResultDbgStrings)); i++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OptResultDbgStrings[i]); + for (b = FirstByte; b < NumBytes; b++) { + if (b < MAX_SDRAM_IN_DIMM) { + data = &calcResultSummary[Channel][b]; + } else { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: calcResultSummary array out of bounds! %d > %d \n", + b, + MAX_SDRAM_IN_DIMM - 1 + ); + return; + } + + for (j = 0; j < NumTestPlus; j++) { + MrcOptResultsPerBytePrint (Debug, data, i, j, MidPoint); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t"); // tab insertion + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } // row end here ! + return; +} + +/** + This function prints the Optimize margin result table + e.g: calcResultSummary[MAX_CHANNEL][MAX_SDRAM_IN_DIMM] + + @param[in] MrcData - MRC data structure + @param[in] calcResultSummary - The data array [MAX_CHANNEL][MAX_SDRAM_IN_DIMM] + @param[in] DimmOptPoints - add argument and description to function comment + @param[in] TestList - Test list + @param[in] NumTest - Number of test + @param[in] NumOffsets - Number of offsets + @param[in] MidPoint - Middle point + @param[in] IncEnds - Print ends points + @param[in] OptParam - Used to convert to the Opt param string for printing + @param[in] Channel - Channel to print + @param[in] Ranks - Ranks to print + @param[in] TrendLine - Switch to print the trend line + @param[in] Nibble - take low/high bytes + @param[in] perCh - Switch to only print 1 Byte of data + + @retval Nothing +**/ +void +PrintODTResultTable ( + IN MrcParameters *const MrcData, + IN OptResultsPerByte *calcResultSummary, + IN DimmOptPoint *DimmOptPoints, + IN U8 NumOffsets, + IN S8 MidPoint, + IN BOOL IncEnds, + IN U8 OptParam, + IN U8 Channel, + IN U8 Ranks, + IN BOOL TrendLine, + IN U8 Nibble, + IN BOOL perCh + ) +{ + const char *OdtStrings[] = { + "RttNom0", + "RttNom1", + "RttWr0", + "RttWr1", + "RdOdt", + "Pwr[mW]", + "Cpu Rd", + "Dim Rd", + "Cpu Wr", + "DimW-T", + "DimW-NT", + "ACPower" + }; + const MrcDebug *Debug; + MrcOutput *Outputs; + OptResultsPerByte *data; + S8 Off; + S8 Start; + S8 Stop; + U8 i; + U8 j; + U8 b; + U8 r; + U8 FirstByte; + U8 NumBytes; + U8 NumTestPlus; + U8 *TestList; + U8 Param; + U32 Result; + U8 OptResultType; + BOOL Format64Results; // Display result in %/MaxR , 0-display raw 64bit result in HEX + BOOL printOptSetting; + U64 delta; + + Format64Results = 1; + printOptSetting = 1; + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + Start = (!IncEnds); + Stop = NumOffsets - (!IncEnds); + TestList = DimmOptPoints[0].TestList; + FirstByte = (Nibble) ? 4 : 0; + NumBytes = FirstByte + 4 + Nibble * MrcData->SysOut.Outputs.SdramCount % 8; + Ranks &= Outputs->Controller[0].Channel[Channel].ValidRankBitMask; + + if (perCh) { + NumBytes = 1; + } + + NumTestPlus = (TrendLine) ? DimmOptPoints[0].NumTests + 1 : DimmOptPoints[0].NumTests; + // + // RttNomOffset = (MrcData->Outputs.Channel[Channel].DimmCount == 1) ? 0 : RttOffset; // if 2DPC - RttNom 40,30,20 Ohms + // + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\n<======== optimize %s ========>Plot results ", + TOptParamOffsetString[OptParam] + ); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "<Channel=%d><rank/s=0x%x> across settings :(Start=%d,Stop=%d)\n", + Channel, + Ranks, + Start - MidPoint, + Stop - MidPoint - 1 + ); + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "ODT\t"); + // for (b = 0; b < (sizeof(OdtStrings)/sizeof(*OdtStrings)); b++) { + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t",OdtStrings[b] ); + // for (i = 0; i < NumTestPlus+1; i++) MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t");//tab insertion + // } + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");//row end here! + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Offset\t"); // row starts here! + for (b = 0; b < (sizeof (OdtStrings) / sizeof (*OdtStrings)); b++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OdtStrings[b]); + } + + for (i = 0; i < DimmOptPoints[0].NumTests; i++) { + // + // Test types header + // + Param = TestList[i]; + if (Param > CmdV) { + Param = (Param % 16) + 4; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", MarginTypesString[Param]); + } + + if (TrendLine) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", "T.line"); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Opt.func\t"); // more header + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n\n"); + + for (Off = Start; Off < Stop; Off++) { + // + // row starts here ! + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", Off - MidPoint); + for (b = 0; b < (sizeof (OdtStrings) / sizeof (*OdtStrings)); b++) { + if (b == 0) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttNom[Channel][0]]); + } + + if (b == 1) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttNom[Channel][1]]); + } + + if (b == 2) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttWr[Channel][0]]); + } + + if (b == 3) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ActualDimmOdt[DimmOptPoints[Off].ODTSet.RttWr[Channel][1]]); + } + + if (b == 4) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", CalcRdOdt (MrcData, DimmOptPoints[Off].ODTSet.GRdOdt)); + } + + if (b == 5) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.TotPwr / 10, + DimmOptPoints[Off].PowerCalc.TotPwr % 10 + ); + } + + if (b == 6) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.CpuPwrRd / 10, + DimmOptPoints[Off].PowerCalc.CpuPwrRd % 10 + ); + } + + if (b == 7) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.DimmPwrRd / 10, + DimmOptPoints[Off].PowerCalc.DimmPwrRd % 10 + ); + } + + if (b == 8) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.CpuPwrWr / 10, + DimmOptPoints[Off].PowerCalc.CpuPwrWr % 10 + ); + } + + if (b == 9) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.DimmPwrWrT / 10, + DimmOptPoints[Off].PowerCalc.DimmPwrWrT % 10 + ); + } + + if (b == 10) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.DimmPwrWrNT / 10, + DimmOptPoints[Off].PowerCalc.DimmPwrWrNT % 10 + ); + } + + if (b == 11) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d.%d\t", + DimmOptPoints[Off].PowerCalc.ACPower / 10, + DimmOptPoints[Off].PowerCalc.ACPower % 10 + ); + } + } + + data = calcResultSummary; + for (i = 0; i < NumTestPlus; i++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", data->Margins[i][Off].EW); + } + + delta = data->MaxR - data->MinR + 1; // +1 to not divide by 0 + if (Format64Results) { + Result = (U32) (MrcOemMemoryDivideU64ByU64 (MrcOemMemoryMultiplyU64ByU32 (data->Result[Off], 200), data->MaxR)); + Result /= 2; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t\t", Result); + } else { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%08x-%08x\t\t", + (U32) MrcOemMemoryRightShiftU64 (data->Result[Off], + 32), + (U32) (data->Result[Off]) + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here ! + if (printOptSetting) { + for (i = 0; i < DimmOptPoints[0].OptParamTestListSize; i++) { + OptResultType = GetOptResultType (DimmOptPoints[0].OptParamTestList[i]); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s \t", TOptParamOffsetString[DimmOptPoints[0].OptParamTestList[i]]); + if ((OptResultType == RxEqOfft) || (OptResultType == TxEqOfft)) { + // + // MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + // + for (r = 0; r < MAX_RANK_IN_CHANNEL; r++) { + if (!(Ranks & (0x1 << r))) { + continue; + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "<Rank %d>|", r); + for (b = 0; b < Outputs->SdramCount; b++) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%02d|", + DimmOptPoints[Off].BestOptOff[OptResultType][r].Offset[Channel][b] + ); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t"); + } + } else { + for (b = 0; b < Outputs->SdramCount; b++) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d|", + DimmOptPoints[Off].BestOptOff[OptResultType][0].Offset[Channel][b] + ); + } + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // row end here ! + } + } + + } + + for (i = 0; i < (sizeof (OptResultDbgStrings) / sizeof (*OptResultDbgStrings)); i++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s\t", OptResultDbgStrings[i]); + for (b = FirstByte; b < NumBytes; b++) { + data = calcResultSummary; + for (j = 0; j < NumTestPlus; j++) { + MrcOptResultsPerBytePrint (Debug, data, i, j, MidPoint); + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t"); // tab insertion + } + + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); + } // row end here ! +} + +/** + This function prints the Optimize margin result table + e.g: MarginResult[Test][Offset][Channel][Byte][sign] + + @param[in] MrcData - MRC data structure + @param[in] ChMask - Channels to print + @param[in] ResultArray - Array with saved margin results + @param[in] TestNum - Test index + @param[in] OffsetsNum - number of offsets + @param[in] MidPoint - Zero point + @param[in] Edges - 1 edge or 2 edge + @param[in] OptParam - Used to convert to the Opt param string for printing + @param[in] Param - Margin type to be printed. + @param[in] PowerLimits - Power limits to print. + @param[in] noPrint - Used to skip printing. + + @retval Nothing +**/ +void +PrintResultTableByte4by24 ( + IN MrcParameters *MrcData, + IN U8 ChMask, + IN U16 ResultArray[4][24][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES], + IN U16 TestNum, + IN U8 OffsetsNum, + IN U8 MidPoint, + IN U8 Edges, + IN U8 OptParam, + IN U8 Param, + IN U16 *PowerLimits, + IN BOOL noPrint + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + U8 Channel; + U8 Byte; + S8 Off; + S8 Start; + S8 Stop; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + Start = -MidPoint; + Stop = OffsetsNum - MidPoint - 1; + if (Param > CmdV) { + Param = (Param % 16) + 4; + } + + if (noPrint) { + return; + + } + + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\nTest number : %d - %s ,Plot results across OptParam=%s settings:(Start=%d,Stop=%d) w/ power limits(width): %d \nChannel\t0 1\nByte\t", + TestNum, + MarginTypesString[Param], + TOptParamOffsetString[OptParam], + Start, + Stop, + PowerLimits[TestNum] + ); + if (Outputs->SdramCount == 8) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7"); + } else if (Outputs->SdramCount == 9) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "0\t1\t2\t3\t4\t5\t6\t7\t0\t1\t2\t3\t4\t5\t6\t7\t8"); + } + // + // Sweep through OpParam settings + // + for (Off = Start; Off < Stop + 1; Off++) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n %d:\t", Off); + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + // + // spaces for non populated channel + // + if (!((0x1 << Channel) & ChMask)) { + if (Channel == 0) { + if (Outputs->SdramCount == 8) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t\t\t\t\t\t\t\t"); + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, " "); + } + } + + continue; + } + + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if (Edges > 1) { + if (Byte < MAX_SDRAM_IN_DIMM) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "%d-%d\t", + ResultArray[TestNum][Off - Start][Channel][Byte][0], + ResultArray[TestNum][Off - Start][Channel][Byte][1] + ); + } + } else { + if (Byte < MAX_SDRAM_IN_DIMM) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%d\t", ResultArray[TestNum][Off - Start][Channel][Byte][0]); + } + } + } + } + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n"); // New line after the end of the table + + return; +} +#endif // MRC_DEBUG_PRINT + +/** + This function returns the UPM or PWR limit value for the specified parameter + + @param[in] MrcData - Pointer to MRC global data. + @param[in] Param - Margin type + @param[in] LimitType - Type of limit: UpmLimit or PowerLimit + + @retval Returns the UPM or PWR limit +**/ +U16 +UpmPwrLimitValue ( + IN MrcParameters *const MrcData, + IN U8 Param, + IN U8 LimitType + ) +{ + MrcOutput *Outputs; + MrcUpmPwrRetrainLimits *MrcLimits; + U32 Index; + U16 Limit; +#ifdef ULT_FLAG + U8 Channel; +#endif // ULT_FLAG + + Limit = 0; + Outputs = &MrcData->SysOut.Outputs; + MrcLimits = Outputs->UpmPwrRetrainLimits.Pointer; + + for (Index = 0; Index < MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS; Index++) { + if (Param == MrcLimits[Index].Param) { + Limit = MrcLimits[Index].ParamLimit[LimitType]; + break; + } + } + +#ifdef ULT_FLAG + if ((MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) && + (Outputs->DdrType == MRC_DDR_TYPE_DDR3) && + (Param == WrV) && + (LimitType != RetrainLimit) + ) { + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if (MrcChannelExist (Outputs, Channel)) { + if (Outputs->Controller[0].Channel[Channel].Dimm[0].ReferenceRawCard == rcF) { + Limit += 200; // Add 20 ticks for WrV on HSW ULT with DDR3L and raw card F + break; + } + } + } + } +#endif // ULT_FLAG + + return Limit; +} + +/** + This function will adjust the requested Limit Type of the margin parameter by the signed offset passed in. + + @param[in] MrcData - MRC global data. + @param[in] Param - Margin parameter type to adjust. + @param[in] LimitType - MRC_MARGIN_LIMIT_TYPE to adjust. + @param[in] Offset - The adjustment value. + + @retval U16 - The new value of Param[MRC_MARGIN_LIMIT_TYPE] +**/ +U16 +MrcUpdateUpmPwrLimits ( + IN OUT MrcParameters * const MrcData, + IN U8 Param, + IN U8 LimitType, + IN S8 Offset + ) +{ + MrcUpmPwrRetrainLimits *MrcLimits; + U32 Index; + S32 UpdatedValue; + + MrcLimits = MrcData->SysOut.Outputs.UpmPwrRetrainLimits.Pointer; + UpdatedValue = 0; + + for (Index = 0; Index < MRC_NUMBER_UPM_PWR_RETRAIN_MARGINS; Index++) { + if (Param == MrcLimits[Index].Param) { + UpdatedValue = MrcLimits[Index].ParamLimit[LimitType]; + break; + } + } + + UpdatedValue += Offset; + UpdatedValue = MAX (UpdatedValue, 0); + UpdatedValue = MIN (UpdatedValue, 0xFFFF); + + MrcLimits[Index].ParamLimit[LimitType] = (U16) UpdatedValue; + + return (U16) UpdatedValue; +} + +/** + This function returns the Actual Cpu Driver Impedance (1 segment) in ohm. + + @param[in] MrcData - Pointer to MRC global data. + @param[in] Offset - Vref Offset (+-8). + + @retval Returns the CPU driver impedance value (for 1 segment) +**/ +U16 +CalcDrvImp ( + IN MrcParameters *const MrcData, + IN S8 Offset + ) +{ + U16 Result; + U8 Rext; + + Rext = 75; + +#ifdef ULT_FLAG + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + Rext = 120; // RCOMP1 resistor is 120 Ohm on HSW-ULT boards + } +#endif // ULT_FLAG + + // + // If Offset == -32, return 0; + // + if (Offset == -32) { + Result = 0; + } else { + Result = Rext * (32 - Offset) / (32 + Offset); + } + + return Result; +} + +/** + This function returns the Actual Cpu Odt termination in ohm. + + @param[in] MrcData - Pointer to MRC global data. + @param[in] Offset - Vref Offset (+-16). + + @retval Returns the Odt termination value. +**/ +U16 +CalcRdOdt ( + IN MrcParameters *const MrcData, + IN S8 Offset + ) +{ + U16 Result; + U8 Rext; + +#ifdef ULT_FLAG + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + Rext = 100; // 200 / 2 + } else +#endif //ULT_FLAG + { + Rext = 50; // 100 / 2 + } + + Result = (Rext * 96 / (Offset + 48) - Rext); + + return Result; +} + +/** + Calculate Power for the selected Opt param based on + + @param[in] MrcData - Include all MRC global data. + @param[in] Channel - Channel to work on + @param[in] Rank - Rank to work on + @param[in] Byte - Byte to work on + @param[in] OptParam - The Opt Parameter to work on + @param[in] Offset - The Offset to work on + @param[in] CurrentComp - The current Comp code for OptParam + @param[in] ReadHost - Switch to read current offset and CompCode from Host structure. + + @retval Calc power in mW +**/ +U32 +CalcOptPower ( + IN MrcParameters *MrcData, + IN U8 Channel, + IN U8 Rank, + IN U8 Byte, + IN U8 OptParam, + IN S8 Offset, + IN S8 CurrentComp, + IN BOOL ReadHost + ) +{ + U32 Power; + U16 Rleg; + S8 StatLegs; + U8 OdtLegsDis; + S8 CurrentVref; + U8 RxVselect; + U8 RxCBSelect; + S8 RxFselect; + U8 RxDefault; + extern const U8 RxBiasTable[2][5][4]; + U8 RxPowerScale[] = { 33, 66, 88, 100, 133, 166, 200, 233 }; + U32 Vcc; + U32 CPURXPower; + MrcVddSelect Vdd; + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + const MrcInput *Inputs; + const MrcDebug *Debug; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0; + DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp; + DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1; + DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0; + DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1; +#ifdef ULT_FLAG + BOOL Lpddr; + U16 DimmRon; + extern const U8 RxBiasTableUlt[2][3][4]; + + Lpddr = (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3); +#endif //ULT_FLAG + + Inputs = &MrcData->SysIn.Inputs; + Debug = &Inputs->Debug; + Outputs = &MrcData->SysOut.Outputs; + ChannelOut = &Outputs->Controller[0].Channel[Channel]; + DdrCrDataComp0.Data = 0; + DdrCrDataComp1.Data = 0; + DdrCrDataOffsetComp.Data = 0; + Power = 0; + Vdd = Outputs->VddVoltage[Inputs->MemoryProfile]; + + if ((OptParam == OptWrDS) || (OptParam == OptRdOdt) || (OptParam == OptSComp)) { + DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte]; + if (OptParam == OptRdOdt) { + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + } else { + DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG); + } + } + + if (OptParam == OptWrDS) { + DdrCrCompCtl0.Data = Outputs->CompCtl0; + // + // Added Driver RCOMP Vref for driver impedance calculation + // + CurrentVref = (S8) DdrCrCompCtl0.Bits.DqDrvVref; + if (CurrentVref & 0x8) { + CurrentVref -= 0x10; // 2's complement + } + + if (ReadHost) { + CurrentComp = (S8) DdrCrDataComp0.Bits.RcompDrvUp; + Offset = (S8) DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset; + + if (Offset & 0x20) { + Offset-= 0x40; // 2's complement + } + } + + StatLegs = 3 * 4 * 4; // seg*legs*4 - for calc set to 48 + Rleg = CalcDrvImp (MrcData, CurrentVref) / 3 * (StatLegs + 3 * CurrentComp); // RCOMP Vref added to Rleg calculation + Power = Rleg / (StatLegs + 3 * (CurrentComp + (Offset))); // in ohm + } + + if (OptParam == OptRdOdt) { + DdrCrCompCtl0.Data = Outputs->CompCtl0; + OdtLegsDis = (U8) DdrCrCompCtl0.Bits.DisableOdtStatic; + CurrentVref = (S8) DdrCrCompCtl0.Bits.DqOdtVref; + StatLegs = 4 * 4; // we enable only 1/3 segment for odt 4 legs time 4 + + if (CurrentVref & 0x10) { + CurrentVref -= 0x20; // 2's complement + } + + if (ReadHost) { + CurrentComp = (S8) DdrCrDataComp1.Bits.RcompOdtUp; + Offset = (S8) DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset; + if (Offset & 0x10) { + Offset-= 0x20; // 2's complement + } + } + // + // Avoid division by zero. + // + if (CurrentComp == 0) { + CurrentComp = 1; + } + Rleg = CalcRdOdt (MrcData, CurrentVref) * (StatLegs * (!OdtLegsDis) + CurrentComp); + Power = Rleg / (StatLegs * (!OdtLegsDis) + (CurrentComp + (Offset))); // in ohm + } + + if (OptParam == OptSComp) { + if (ReadHost) { + Offset = (S8) DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset; + if (Offset & 0x10) { + Offset -= 0x20; // 2's complement + } + } + Power = 50 + Offset; // simple linear T-line + } + + if (OptParam == OptTxEq) { + Power = Offset; // simple linear T-line + } + + if (OptParam == OptRxEq) { + Power = 100 + (5 * (Offset / 5)); // modulo 5 T-line + } + + if (OptParam == OptDimmRon) { + // + // calc the DimmRon [ohm] + // + if (ReadHost) { + if (Rank < MAX_RANK_IN_CHANNEL) { +#ifdef ULT_FLAG + if (Lpddr) { + DimmRon = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR3]; + Offset = (U8) 0xF & (DimmRon - 1); //{0x1,0x2,0x3}; //{34,40,48}; + } else +#endif //ULT_FLAG + { + Ddr3ModeRegister1.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR1]; + Offset = (U8) Ddr3ModeRegister1.Bits.ODImpedanceLow; + } + } else { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Error: ChannelOut->Dimm array out of bounds! %d > %d\n", + Rank / 2, + MAX_DIMMS_IN_CHANNEL - 1 + ); + return 0; + } + } + +#ifdef ULT_FLAG + if (Lpddr) { + if (Offset > 6) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Offset %d causes negative unsigned number or divide by 0. Dividing by 1.\n", + Offset + ); + Offset = 6; + } + + Power = 240 / (7 - Offset); + } else +#endif //ULT_FLAG + { + if (Offset < -5) { + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Offset %d causes negative unsigned number or divide by 0. Dividing by 1.\n", + Offset + ); + Offset = -5; + } + + Power = 240 / (6 + Offset); + } + } + + if (OptParam == OptRxBias) { + // + // RX BIAS calculations + // + Vcc = 1050; + RxVselect = 0; + if (Vdd > VDD_1_35) { + RxVselect = 1; // Set HiVdd bit if Vdd is over 1.35v + } + // + // RX BIAS calculations + // + GetRxFselect (MrcData, &RxFselect, &RxCBSelect); + +#ifdef ULT_FLAG + if (MrcData->SysIn.Inputs.CpuModel == cmHSW_ULT) { + RxFselect = MIN (RxFselect, RXF_SELECT_MAX_ULT); // Maximum 1600 MHz + RxDefault = RxBiasTableUlt[RxVselect][RxFselect][RxCBSelect]; // Read setting from array lookup table + } else +#endif // ULT_FLAG + { + RxDefault = RxBiasTable[RxVselect][RxFselect][RxCBSelect]; // Read setting from array lookup table + } + + CPURXPower = Vdd * 1200 / 1000 + Vcc * 1250 / 1000; // mW + CPURXPower /= 1000; + if (ReadHost) { + Offset = (U8) ChannelOut->DqControl1[Byte].Bits.RxBiasCtl; + } + + CPURXPower = (RxPowerScale[Offset] * CPURXPower) / RxPowerScale[RxDefault]; + Power = (U16) CPURXPower; + } + + return Power; +} + +/** + This function prints out the Margin eye diagram for ParamT/ParamV. + + + @param[in] MrcData - Include all MRC global data. + @param[in] Channel - Channel to margin. + @param[in] Ranks - Bit mask of Ranks to margin. + @param[in] ParamT - Time parameter to margin. + @param[in] ParamV - Voltage parameter to margin. + @param[in] Start - Starting point for margining. + @param[in] Stop - Stopping point for margining. + @param[in] Repeats - Number of times to repeat the test to average out any noise. + @param[in] NoPrint - Switch to skip printing. + + @retval Nothing +**/ +void +EyeMargin ( + IN MrcParameters *const MrcData, + IN U8 Channel, + IN U8 Ranks, + IN U8 ParamT, + IN U8 ParamV, + IN S8 Start, + IN S8 Stop, + IN U16 SearchLimits, + IN U8 LoopCount, + IN U8 Repeats, + IN BOOL NoPrint + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcChannelOut *ChannelOut; + MrcControllerOut *ControllerOut; + MrcStatus Status; + U32 (*MarginByte)[MAX_RESULT_TYPE][MAX_RANK_IN_CHANNEL][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; + U32 BERStats[4]; + U16 SaveMargin[63][MAX_CHANNEL][MAX_SDRAM_IN_DIMM][MAX_EDGES]; //40 Points X Ch X Byte X Hi/Lo + BOOL Eye[63][108]; + BOOL Lines[108]; + U8 MaxH=108; + U8 MaxW=63; + U8 i,j; + U16 MinEdge; + U16 Mode; + U8 ResultTypeV = 0; + U8 ChBitMask; + U8 Byte; + U8 Rank; + U8 Edge; + U8 FirstRank; + U8 NumBytes; + U8 BMap[9]; // Need by GetBERMarginByte + U8 MaxMarginV; + U8 localR[MAX_CHANNEL]; + U8 Rep; + S8 Index; + U8 IndexOff; + S8 Off; + U8 byteMax[MAX_CHANNEL]; + U32 Offset; + U64 CrValue64; + + Debug = &MrcData->SysIn.Inputs.Debug; + Outputs = &MrcData->SysOut.Outputs; + MarginByte = &Outputs->MarginResult; + Ranks &= Outputs->ValidRankMask; + ControllerOut = &Outputs->Controller[0]; + IndexOff = 0; + CrValue64 = 0x0ULL; //64 bit Data bit mask + + MrcOemMemorySet ((U8 *) localR, 0, sizeof(localR)); + MrcOemMemorySet ((U8 *) Eye, 0, sizeof(Eye)); + MrcOemMemorySet ((U8 *) Lines, 0, sizeof(Lines)); + MrcOemMemorySet ((U8 *) SaveMargin, 0, sizeof(SaveMargin)); + MrcOemMemorySetDword (BERStats, 0, sizeof(BERStats) / sizeof (U32)); + for (Byte = 0; Byte < sizeof (BMap) / sizeof (BMap[0]); Byte++) { + BMap[Byte] = Byte; + } + + Outputs->EnDumRd = 0; + SetupIOTestBasicVA(MrcData, 1<<Channel, LoopCount, 0, 0, 0,8); //set test to all channels + // + // Select All Ranks for REUT test + // + ChannelOut = &ControllerOut->Channel[Channel]; + localR[Channel] = ChannelOut->ValidRankBitMask & Ranks; + // + // use ChBitMask from here down - if ch is set that mean at least 1 rank for testing, also remove ch w/o active ranks + // + ChBitMask = SelectReutRanks (MrcData, Channel, localR[Channel], 0); + + if (ChBitMask == 0) { + return ; + } + + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + ChannelOut->DataOffsetTrain[Byte] = 0; + } + // + // Find the first selected rank + // + FirstRank = 0; + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + if ((1 << Rank) & localR[Channel]) { + FirstRank = Rank; // could be in any channel + break; + } + } + // + // Store margin results for + // + NumBytes = (U8) Outputs->SdramCount; + + // + // Loop through all Test Params and Measure Margin + // Find MaxMargin for this channel + // + byteMax[Channel] = Stop; + if (ParamT == RdT) { + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + byteMax[Channel] = MrcCalcMaxRxMargin (MrcData, Channel, Ranks, Byte, 0, byteMax[Channel]); + } + } + + Offset = MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG + + ((MCHBAR_CH1_CR_REUT_CH_ERR_DATA_MASK_REG - MCHBAR_CH0_CR_REUT_CH_ERR_DATA_MASK_REG) * Channel); + + MrcWriteCR64 (MrcData, Offset, CrValue64); + MaxMarginV = MAX_POSSIBLE_VREF; + if (MAX_POSSIBLE_TIME < Stop) { + Stop = MAX_POSSIBLE_TIME; + } + + if (-MAX_POSSIBLE_TIME > Start) { + Start = -MAX_POSSIBLE_TIME; + } + + IndexOff = MaxW / 2 + Start; + // + // No need to search too far + // + if (MaxMarginV > SearchLimits) { + MaxMarginV = (U8) (SearchLimits); + } + + for (Off = Start; Off < Stop + 1; Off++) { + Index = Off - Start; + // + // change margin ParamT + // + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + Status = ChangeMargin (MrcData, ParamT, Off, 0, 0, Channel, localR[Channel], Byte, 0, 1, 0, MrcRegFileStart); + } + ResultTypeV = GetMarginResultType (ParamV); // rxv=0 rxt=1 + // + // Assign to last pass margin results by reference + // get lowest margin from all ch/rankS/byte save in FirstRank + // + Status = GetMarginByte ( + MrcData, + Outputs->MarginResult, + ParamV, + FirstRank, + Ranks + ); + for (Rep = 0; Rep < Repeats; Rep++) { + // + // Run Margin Test - margin_1d with chosen param + // run on all ranks but change param only for firstRank?? + // + Mode = 0; + Status = MrcGetBERMarginByte ( + MrcData, + Outputs->MarginResult, + ChBitMask, + FirstRank, + FirstRank, + ParamV, + Mode, + BMap, + 1, + MaxMarginV, + 0, + BERStats + ); + // + // Record Results + // + for (Edge = 0; Edge < MAX_EDGES; Edge++) { + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if ((Index > 62) || (Index < 0)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: SaveMargin array out of bounds! %d", Index); + return; + } + + if (Rep == 0) { + SaveMargin[Index][Channel][Byte][Edge] = 0; + } + + SaveMargin[Index][Channel][Byte][Edge] += (U16) (*MarginByte)[ResultTypeV][FirstRank][Channel][Byte][Edge]; + } + } + } + + for (Edge = 0; Edge < MAX_EDGES; Edge++) { + MinEdge = 0xFFFF; + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + if ((Index > 62) || (Index < 0)) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: SaveMargin array out of bounds! %d", Index); + return; + } + + SaveMargin[Index][Channel][Byte][Edge] /= Repeats; + if (MinEdge > SaveMargin[Index][Channel][Byte][Edge]) { + MinEdge = SaveMargin[Index][Channel][Byte][Edge]; + } + } + + if (((Index + IndexOff) > 62) || + ((Index + IndexOff) < 0) || + ((MaxH / 2 - (MinEdge - 1) / 10) > 107) || + ((MaxH / 2 - (MinEdge - 1) / 10) < 0) || + ((MaxH / 2 + (MinEdge - 1) / 10) > 107) || + ((MaxH / 2 + (MinEdge - 1) / 10) < 0) + ) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: Eye or Lines array out of bounds!\n"); + return; + } + + if (Edge) { + Eye[Index + IndexOff][MaxH / 2 - (MinEdge - 1) / 10] = 1; + Lines[MaxH / 2 - (MinEdge - 1) / 10] = 1; + } else { + Eye[Index + IndexOff][MaxH / 2 + (MinEdge - 1) / 10] = 1; + Lines[MaxH / 2 + (MinEdge - 1) / 10] = 1; + } + } + }//end of offset + // + // Print the box + // + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "Plot Eye across ParamT = %s ParamV = %s settings:(Start=%d,Stop=%d) LC = %d Channel = %d Ranks = 0x%x\n", + MarginTypesString[ParamT], + MarginTypesString[ParamV], + Start, + Stop, + LoopCount, + Channel, + Ranks + ); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t------------------------------- +++++++++++++++++++++++++++++++\n"); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\t332222222222111111111100000000000000000001111111111222222222233\n"); + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Vref\t109876543210987654321098765432101234567890123456789012345678901\n"); + for (i = 0; i < MaxH; i++) { + if (Lines[i]) { + // + // print only fail lines + // + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%3d:\t", MaxH / 2 - i); // per ch + for (j = 0; j < MaxW; j++) { + if (Eye[j][i]) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s", "#"); // per ch + } else { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "%s", ((j == (MaxW) / 2) || (i == (MaxH) / 2)) ? "+" : " "); // per ch + } + } + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "\n");//per ch + } + } + // + // Clean up after test + // + ChannelOut = &ControllerOut->Channel[Channel]; + MrcOemMemorySetDword (&ChannelOut->DataOffsetTrain[0], 0, Outputs->SdramCount); + Status = ChangeMargin (MrcData, ParamT, 0, 0, 1, 0, 0, 0, 0, 0, 0, MrcRegFileCurrent); + +} + +/** + This function fill the input array (e.g array[ch][rank]) with the power calculation + per rank/ch for current sys. setting. + + @param[in] MrcData - MRC data struct; + @param[in,out] PwrChRank - Array to fill; + + @retval Nothing +**/ +void +CalcSysPower ( + IN MrcParameters *const MrcData, + IN OUT MrcPower PwrChRank[MAX_CHANNEL][MAX_RANK_IN_CHANNEL] + ) +{ + const MrcDebug *Debug; + MrcChannelOut *ChannelOut; + MrcOutput *Outputs; + MrcOdtPowerSaving *OdtPowerSaving; + U8 Rank; + U8 Byte; + U8 Channel; + BOOL is1DPC; + BOOL ChCalcDone; + U16 ROdtCpu; + U8 RonDimm; + U16 RonCpu; + U16 Rodtdram; + U16 Wodtdram; + U16 RxBiasPwr; + U8 TotalRankCount; + U32 PwrAvgRd; + U32 PwrAvgWr; + const U8 RttNomDic[6] = {0,60,120,40,20,30}; //accordingly to DDR3 spec + const U8 RttWrDic[3] = {0,60,120}; //accordingly to DDR3 spec + DDR3_MODE_REGISTER_1_STRUCT Ddr3ModeRegister1; + DDR3_MODE_REGISTER_2_STRUCT Ddr3ModeRegister2; +#ifdef ULT_FLAG + U16 LpddrMr3; //dimm DS + U16 LpddrMr11; //dimm ODT + const U8 LpddrRonDic[4] = {0,34,40,48}; + const U8 LpddrOdtDic[4] = {0,0,120,240}; +#endif // ULT_FLAG + + Outputs = &MrcData->SysOut.Outputs; + OdtPowerSaving = &Outputs->OdtPowerSavingData; + Debug = &MrcData->SysIn.Inputs.Debug; + RxBiasPwr = 0; + TotalRankCount = 0; + PwrAvgRd = 0; + PwrAvgWr = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChCalcDone = 0; + RonCpu = 0; + ROdtCpu = 0; + RxBiasPwr = 0; + for (Rank = 0; Rank < MAX_RANK_IN_CHANNEL; Rank++) { + ChannelOut = &Outputs->Controller[0].Channel[Channel]; + is1DPC = (ChannelOut->DimmCount == 1); + if (MrcRankInChannelExist (MrcData, Rank, Channel)) { +#ifdef ULT_FLAG + if (MrcData->SysOut.Outputs.DdrType == MRC_DDR_TYPE_LPDDR3) { + if (Rank >= MAX_RANK_IN_DIMM) { + MRC_DEBUG_MSG (Debug, MSG_LEVEL_NOTE, "Error: ChannelOut array out of bounds!\n"); + return ; + } + + LpddrMr3 = ChannelOut->Dimm[0].Rank[Rank].MR[mrMR3]; + LpddrMr11 = ChannelOut->Dimm[0].Rank[Rank].MR11; + RonDimm = LpddrRonDic[0x3 & LpddrMr3]; + Wodtdram = LpddrOdtDic[0x3 & LpddrMr11]; + Rodtdram = 0x3FFF; // put 8k ohm as infinity - in lpddr there is no nomOdt + } else +#endif // ULT_FLAG + { + Ddr3ModeRegister1.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR1]; + Ddr3ModeRegister2.Data = ChannelOut->Dimm[Rank / 2].Rank[Rank % 2].MR[mrMR2]; + RonDimm = 240 / (6 + (U8) Ddr3ModeRegister1.Bits.ODImpedanceLow); + Rodtdram = RttNomDic[(Ddr3ModeRegister1.Bits.OdtRttValueHigh << 2) | + (Ddr3ModeRegister1.Bits.OdtRttValueMid << 1) | + Ddr3ModeRegister1.Bits.OdtRttValueLow]; + Wodtdram = RttWrDic[Ddr3ModeRegister2.Bits.DynamicOdt]; + } + if (!ChCalcDone){ + // + //Ron CPU - take average all bytes only per ch + // + for (Byte = 0; Byte < Outputs->SdramCount; Byte++){ + RonCpu += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptWrDS, 0, 0, 1);//read from host + ROdtCpu += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptRdOdt, 0, 0, 1);//read from host + RxBiasPwr += (U16) CalcOptPower (MrcData, Channel, 0, Byte, OptRxBias, 0, 0, 1);//read from host + } + RonCpu /= (U16) Outputs->SdramCount; + ROdtCpu /= (U16) Outputs->SdramCount; + RxBiasPwr /= (U16) Outputs->SdramCount; + ChCalcDone = 1; + } + + if (is1DPC) { + // + // in 1DPC channel always use only one of the terminations + // + if (Wodtdram == 0) { + Wodtdram = Rodtdram; + Rodtdram = 0x3FFF; // put 8k ohm as infinity + } else { + Rodtdram = 0x3FFF; // put 8k ohm as infinity + } + } + + if (Rodtdram == 0) { + Rodtdram = 0x3FFF; + } + + if (Wodtdram == 0) { + Wodtdram = Rodtdram; // in 2DPC where RttW=0 + } + + CalcPower (MrcData, &PwrChRank[Channel][Rank], RonCpu, RonDimm, ROdtCpu, Rodtdram, Wodtdram); + // + // add RxBias to CPU and Total + // + PwrChRank[Channel][Rank].CpuPower += RxBiasPwr; + PwrChRank[Channel][Rank].TotPwr += RxBiasPwr; + PwrAvgRd += PwrChRank[Channel][Rank].CpuPwrRd + PwrChRank[Channel][Rank].DimmPwrRd + + PwrChRank[Channel][Rank].ACPowerRd; + PwrAvgWr += PwrChRank[Channel][Rank].CpuPwrWr + PwrChRank[Channel][Rank].DimmPwrWrT + + PwrChRank[Channel][Rank].DimmPwrWrNT + PwrChRank[Channel][Rank].ACPowerWr; + TotalRankCount++; + } + } + } + + if (TotalRankCount == 0) { + TotalRankCount = 1; // Prevent divide by 0 + } + + PwrAvgRd /= TotalRankCount; + PwrAvgRd += RxBiasPwr; + PwrAvgWr /= TotalRankCount; + // + // update Mrc struct with Base line numbers + // + if (OdtPowerSaving->BaseFlag == FALSE) { + OdtPowerSaving->BaseSavingRd = (U16) PwrAvgRd; + OdtPowerSaving->BaseSavingWr = (U16) PwrAvgWr; + OdtPowerSaving->BaseSavingCmd = 0; // currently no power train for CMD + } else { + OdtPowerSaving->MrcSavingRd = (U16) PwrAvgRd; + OdtPowerSaving->MrcSavingWr = (U16) PwrAvgWr; + OdtPowerSaving->MrcSavingCmd = 0; // currently no power train for CMD + } + + return; +} + +/** + This function optimize the digital offsets by reducing the digital + offset and apply the difference to the global one. + + @param[in] MrcData - Include all MRC global data. + @param[in] Param - Parameter defining the desired digital compensation offset. + @param[in] UpdateHost - Decides if MrcData is to be updated. + + @retval The new comp value. +**/ +U32 +OptimizeCompOffset ( + IN MrcParameters *const MrcData, + IN const U8 Param, + IN const U8 UpdateHost + ) +{ + const MrcDebug *Debug; + MrcOutput *Outputs; + MrcControllerOut *ControllerOut; + MrcChannelOut *ChannelOut; + DDRCOMP_CR_DDRCRDATACOMP0_STRUCT DdrCrDataComp0; + DDRCOMP_CR_DDRCRDATACOMP1_STRUCT DdrCrDataComp1; + DDRCOMP_CR_DDRCRCOMPCTL0_STRUCT DdrCrCompCtl0; + DDRCOMP_CR_DDRCRCOMPCTL1_STRUCT DdrCrCompCtl1; + DDRDATA0CH0_CR_DDRCRDATAOFFSETCOMP_STRUCT DdrCrDataOffsetComp; + U8 GlobalParam; + U8 CurrCompVref; + S8 NewCompVref; + U8 CurrentComp; + U8 NewComp; + S8 Sign; + U8 Done; + S16 AvgOffset; + U8 Offset; + U8 StartDelta; + U8 CurrDelta; + U8 MinDelta; + U8 Off; + U8 BestVrefOff; + U8 SignBit; + U8 Byte; + U8 Channel; + U8 NumCh; + U8 ReservedCodes; + S8 MaxCompVref; + S8 MinCompVref; + U8 DqSCompPC; + U8 CurrDqSCompPC; + U8 CompCodes[MAX_CHANNEL][MAX_SDRAM_IN_DIMM]; + + Outputs = &MrcData->SysOut.Outputs; + ControllerOut = &Outputs->Controller[0]; + Debug = &MrcData->SysIn.Inputs.Debug; + DdrCrCompCtl0.Data = Outputs->CompCtl0; + DdrCrCompCtl1.Data = Outputs->CompCtl1; + + DdrCrDataOffsetComp.Data = 0; + ReservedCodes = 3; + NewComp = 0; + Offset = 0; + SignBit = 0; + DqSCompPC = 0; + CurrDqSCompPC = 0; + + switch (Param) { + case OptWrDS: + DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG); + CurrentComp = (U8) DdrCrDataComp0.Bits.RcompDrvUp; + CurrCompVref = MrcSE ((U8) DdrCrCompCtl0.Bits.DqDrvVref, 4, 8); + MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqDrvVref_WID - 1)) - 1; + MinCompVref = (-1) * 1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqDrvVref_WID - 1); + GlobalParam = WrDS; + break; + + case OptRdOdt: + DdrCrDataComp1.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP1_REG); + CurrentComp = (U8) DdrCrDataComp1.Bits.RcompOdtUp; + CurrCompVref = MrcSE ((U8) DdrCrCompCtl0.Bits.DqOdtVref, 5, 8); + MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqOdtVref_WID - 1)) - 1; + MinCompVref = (-1) * 1 << (DDRCOMP_CR_DDRCRCOMPCTL0_DqOdtVref_WID - 1); + GlobalParam = RdOdt; + break; + + case OptSComp: + DdrCrDataComp0.Data = MrcReadCR (MrcData, DDRCOMP_CR_DDRCRDATACOMP0_REG); + CurrentComp = (U8) DdrCrDataComp0.Bits.SlewRateComp; + CurrDqSCompPC = (U8) DdrCrCompCtl1.Bits.DqScompPC; + CurrCompVref = (U8) DdrCrCompCtl1.Bits.DqScompCells; + MaxCompVref = (1 << (DDRCOMP_CR_DDRCRCOMPCTL1_DqScompCells_WID)) - 1; + MinCompVref = 4; + GlobalParam = SCompDq; + break; + + default: + CurrentComp = 0; + CurrCompVref = 0; + MaxCompVref = 0; + MinCompVref = 0; + GlobalParam = 0; + MRC_DEBUG_MSG (Debug, MSG_LEVEL_ERROR, "Invalid Param : %d", Param); + break; + } + + AvgOffset = 0; + NumCh = 0; + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + if ((MrcChannelExist (Outputs, Channel))) { + ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel]; + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + DdrCrDataOffsetComp.Data = ChannelOut->DataCompOffset[Byte]; + if (Param == OptWrDS) { + Offset = (U8) DdrCrDataOffsetComp.Bits.DqDrvUpCompOffset; + SignBit = 6; + } + + if (Param == OptRdOdt) { + Offset = (U8) DdrCrDataOffsetComp.Bits.DqOdtUpCompOffset; + SignBit = 5; + } + + if (Param == OptSComp) { + Offset = (U8) DdrCrDataOffsetComp.Bits.DqSlewRateCompOffset; + SignBit = 5; + } + + AvgOffset += (S8) MrcSE (Offset, SignBit, 8); + CompCodes[Channel][Byte] = CurrentComp + MrcSE (Offset, SignBit, 8); + } + + NumCh++; + } + } + + Sign = (AvgOffset < 0) ? -1 : 1; + // + // Calculate the average offset and round to the nearest integer. + // + AvgOffset = (AvgOffset + Sign * NumCh * ((U8) Outputs->SdramCount) / 2) / (NumCh * ((U8) Outputs->SdramCount)); + + if (AvgOffset == 0) { + return CurrentComp; + } + // + // Find the CompVref minimum of the delta between (CurrentComp + AvgOffset) to NewComp. + // Take care of keeping 3 code reserved. + // Exit if no vref range left. + // + Done = 0; + Off = 1; + BestVrefOff = CurrCompVref; + NewComp = CurrentComp; + DqSCompPC = CurrDqSCompPC; + StartDelta = ABS ((S8) AvgOffset); + MinDelta = StartDelta; + if (Param == OptSComp) { + Sign *= -1; + } + + while (!Done) { + NewCompVref = CurrCompVref + (Sign * Off); + if ((MinCompVref > NewCompVref) || (NewCompVref > MaxCompVref)) { + Done = 1; + } + // + // Reserve 3 comp codes + // + if ((ReservedCodes > NewComp) || (NewComp > (63 - ReservedCodes))) { + Done = 1; + } + + if (Param == OptSComp) { + if ((NewCompVref + 1) > 16) { + DqSCompPC = 0; + } + + NewCompVref = (DqSCompPC << 4) + NewCompVref; + } + + if (!Done) { + NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, NewCompVref, 0); + CurrDelta = ABS (CurrentComp + (S8) AvgOffset - NewComp); + if (CurrDelta < StartDelta) { + if (CurrDelta < MinDelta) { + MinDelta = CurrDelta; + BestVrefOff = NewCompVref; + if (MinDelta == 0) { + Done = 1; + } + } + } else { + Done = 1; + } + } + + Off++; + } + // + // update new compVref setting + // + if (BestVrefOff != CurrCompVref) { + NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, BestVrefOff, UpdateHost); + // + // Update all bytes with new offset: Offset + code - newcode = +newoffset + // + for (Channel = 0; Channel < MAX_CHANNEL; Channel++) { + ChannelOut = &MrcData->SysOut.Outputs.Controller[0].Channel[Channel]; + if ((MrcChannelExist (Outputs, Channel))) { + for (Byte = 0; Byte < Outputs->SdramCount; Byte++) { + UpdateOptParamOffset (MrcData, Channel, 0, Byte, Param, CompCodes[Channel][Byte] - NewComp, UpdateHost); + } + } + } + } else { + // + // Restore CompVref + // + if (Param == OptSComp) { + NewCompVref = (CurrDqSCompPC << 4) + CurrCompVref; + } + + NewComp = (U8) UpdateCompGlobalOffset (MrcData, GlobalParam, CurrCompVref, UpdateHost); + } + + return NewComp; +} + +/** + This step performs Comp Offset optimization on the param list defined in this function. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - mrcSuccess +**/ +MrcStatus +MrcOptimizeComp ( + IN MrcParameters *const MrcData + ) +{ + const U8 ParamList[] = { OptWrDS, OptRdOdt, OptSComp }; + U8 Param; + + for (Param = 0; Param < sizeof (ParamList); Param++) { + OptimizeCompOffset (MrcData, ParamList[Param], 1); + } + + return mrcSuccess; +} + +/** + This function calculates the percent of power saving from the power optimization steps and + updates the proper registers in the PCU. To get the correct base line for this calculation, + this routing needs to run first time early in the training in order to update the MrcStruct + with the base line. After the power training steps, it will run again to get the actual + percent of power saving. + + @param[in] MrcData - Include all MRC global data. + + @retval MrcStatus - mrcSuccess + +**/ +MrcStatus +MrcPowerSavingMeter ( + IN MrcParameters *const MrcData + ) +{ + MrcDebug const *Debug; + MrcPower PwrChRank[MAX_CHANNEL][MAX_RANK_IN_CHANNEL]; + MrcOdtPowerSaving *OdtPowerSaving; + U8 PercentRd; + U8 PercentWr; + U8 PercentCmd; + PCU_CR_MRC_ODT_POWER_SAVING_PCU_STRUCT CrMrcOdtPowerSavingPcu; + + Debug = &MrcData->SysIn.Inputs.Debug; + OdtPowerSaving = &MrcData->SysOut.Outputs.OdtPowerSavingData; + CalcSysPower (MrcData, PwrChRank); + MRC_DEBUG_MSG ( + Debug, + MSG_LEVEL_NOTE, + "\tBaseLine\tMrcSaving\nAvgRd\t%d\t\t%d\nAvgWr\t%d\t\t%d\n", + OdtPowerSaving->BaseSavingRd, + OdtPowerSaving->MrcSavingRd, + OdtPowerSaving->BaseSavingWr, + OdtPowerSaving->MrcSavingWr + ); + + if (OdtPowerSaving->BaseFlag) { + // + // Calculate power saving and update PCU regs + // + if (OdtPowerSaving->BaseSavingRd > OdtPowerSaving->MrcSavingRd) { + PercentRd = (U8) (((U32) (OdtPowerSaving->BaseSavingRd - OdtPowerSaving->MrcSavingRd) * 256) / OdtPowerSaving->BaseSavingRd); + } else { + PercentRd = 0; + } + + if (OdtPowerSaving->BaseSavingWr > OdtPowerSaving->MrcSavingWr) { + PercentWr = (U8) (((U32) (OdtPowerSaving->BaseSavingWr - OdtPowerSaving->MrcSavingWr) * 256) / OdtPowerSaving->BaseSavingWr); + } else { + PercentWr = 0; + } + + if (OdtPowerSaving->BaseSavingCmd > OdtPowerSaving->MrcSavingCmd) { + PercentCmd = (U8) (((U32) (OdtPowerSaving->BaseSavingCmd - OdtPowerSaving->MrcSavingCmd) * 256) / OdtPowerSaving->BaseSavingCmd); + } else { + PercentCmd = 0; + } + + CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Rd = PercentRd; + CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Wt = PercentWr; + CrMrcOdtPowerSavingPcu.Bits.MRC_Saving_Cmd = PercentCmd; + + MrcWriteCR (MrcData, PCU_CR_MRC_ODT_POWER_SAVING_PCU_REG, CrMrcOdtPowerSavingPcu.Data); + } else { + OdtPowerSaving->BaseFlag = TRUE; + } + + return mrcSuccess; +} |