diff options
Diffstat (limited to 'src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/mttRdDqs2DTraining.c')
-rw-r--r-- | src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/mttRdDqs2DTraining.c | 1185 |
1 files changed, 1185 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/mttRdDqs2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/mttRdDqs2DTraining.c new file mode 100644 index 0000000000..0fc13f92d8 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Tech/mttRdDqs2DTraining.c @@ -0,0 +1,1185 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mttRdDqs2DTraining.c + * + * RD DQS 2 dimentional training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Tech) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + + + +#include "AGESA.h" +#include "amdlib.h" +#include "AdvancedApi.h" +#include "GeneralServices.h" +#include "Ids.h" +#include "heapManager.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "merrhdl.h" +#include "Filecode.h" +#include "OptionMemory.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_TECH_MTTRDDQS2DTRAINING_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ +#define MAX_DELAYS 9 /* 8 data bytes + 1 ECC byte */ +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +MemT2DRdDQSProcessConvolution ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[]; + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function executes 2D training for Rd DQS + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ + +BOOLEAN +MemTAmdRdDqs2DTraining ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + MEM_NB_BLOCK *NBPtr; + MEM_DATA_STRUCT *MemPtr; + UINT8 Dct; + UINT8 ChipSel; + MEM_2D_ENTRY Data; + UINT8 Lane; + UINT8 Vref; + BOOLEAN Status; + BOOLEAN DCT_x4Present; + UINT8 MaxLanes; + PSO_TABLE *PsoTable; + RD_DQS_2D *VrefPtr; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + NBPtr = TechPtr->NBPtr; + MemPtr = NBPtr->MemPtr; + AGESA_TESTPOINT (TpProcMem2dRdDqsTraining, &(MemPtr->StdHeader)); + PsoTable = MemPtr->ParameterListPtr->PlatformMemoryConfiguration; + // + // Set environment settings before training + // + IDS_HDT_CONSOLE (MEM_STATUS, "\n\nStart RD DQS 2D training.\n\n"); + MemTBeginTraining (TechPtr); + // + // Allocate heap for the 2D RdDqs/Vref Data structure + // + DCT_x4Present = FALSE; + // Check DCTs for x4 DIMMs + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + if (NBPtr->ChannelPtr->DimmNibbleAccess != 0) { + DCT_x4Present = TRUE; + break; + } + } + + NBPtr->SwitchDCT (NBPtr, 0); + // If DCT0 or DCT1 have x4 DIMMs, additonal allocate space + if (DCT_x4Present == TRUE) { + // Per Nibble + MaxLanes = TechPtr->NBPtr->MCTPtr->Status[SbEccDimms] ? 18 : 16; + } else { + // Per Byte + MaxLanes = TechPtr->NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8; + } + MaxLanes = 8; /// @todo Family specific hook + AllocHeapParams.RequestedBufferSize = MaxLanes * NBPtr->TotalMaxVrefRange * sizeof (RD_DQS_2D); + AllocHeapParams.BufferHandle = AMD_MEM_2D_RDQS_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) == AGESA_SUCCESS) { + VrefPtr = (RD_DQS_2D *) AllocHeapParams.BufferPtr; + } else { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + return TRUE; + } + for (Lane = 0; Lane < MaxLanes; Lane++) { + Data.Lane[Lane].Vref = &VrefPtr[Lane * NBPtr->TotalMaxVrefRange]; + } + // + // Setup hardware training engine + // + TechPtr->Direction = DQS_READ_DIR; + TechPtr->TrainingType = TRN_DQS_POSITION; + NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType); + + Data.Vnom = NBPtr->TotalMaxVrefRange / 2; // Set Nominal Vref + Data.MaxRdDqsSweep = NBPtr->TotalRdDQSDlyRange / 2; // Set Nominal Vref + ASSERT (NBPtr->TotalRdDQSDlyRange <= MAX_RD_DQS_ENTRIES); + // + // Execute 2d Rd DQS training for all Dcts/Chipselects + // + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); + NBPtr->SwitchDCT (NBPtr, Dct); + Status = FALSE; + if (MemTCheck2DTrainingPerConfig (TechPtr)) { + for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) { + if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + //Initialize storage + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + for (Vref = 0; Vref < NBPtr->TotalMaxVrefRange; Vref++) { + Data.Lane[Lane].Vref[Vref].PosRdDqsDly = 0; + Data.Lane[Lane].Vref[Vref].NegRdDqsDly = 0; + } + } + TechPtr->ChipSel = ChipSel; + IDS_HDT_CONSOLE (MEM_FLOW,"\tChip Select: %02x \n", TechPtr->ChipSel); + // + // 1. Sample the data eyes for each channel: + // + TechPtr->Local2DData = &Data; + if (TechPtr->NBPtr->MemN2DRdDQSDataCollection (NBPtr)) { + // + // 2. Process the array of results with a diamond convolution mask, summing the number passing sample points. + // + // Determine Diamond Mask Height + if (MemT2DRdDQSHeight (TechPtr, &Data)) { + // Apply Mask + if (MemT2DRdDQSApplyMask (TechPtr, &Data)) { + // Convolution + if (MemT2DRdDQSProcessConvolution (TechPtr, &Data)) { + // + // 3. Program the final DQS delay values. + // + if (MemT2DRdDQSPrograMaxRdDQSDly (TechPtr, &Data)) { + // + // Find the Smallest Positive or Negative Margin for current CS + // + if (MemT2DRdDQSFindCsVrefMargin (TechPtr, &Data)) { + Status = TRUE; + // + // DATAEYE - Allocate Temp storage for Generate Composite Eyes, + // Save composite eye for CS Pair into Allocated Storage. + TechPtr->TechnologySpecificHook[DataEyeSaveCompositeEyes] (TechPtr, &Data); + } + } + } + } + } + } + if (Status == FALSE) { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, MEM_ERROR_2D_DQS_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } // Chipselect + } // End of ChipSel for loop. + + // + // DATAEYE - Compress Eyes, Deallocate temp storage. Allocate memory for compressed eyes, + // copy eyes to new struct, fixup pointers. + // + TechPtr->TechnologySpecificHook[DataEyeCompressEyes] (TechPtr, &Data); + // + // Find the Max and Min Vref values for each DCT + // + if (Status == TRUE) { + if (MemT2DRdDQSFinalVrefMargin (TechPtr, &Data)) { + // + // Program the Max Vref value + // + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Final Vref for channel\n\n"); + MemT2DProgramVref (TechPtr, NBPtr->ChannelPtr->MaxVref); + Status = TRUE; + } else { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, MEM_ERROR_2D_DQS_VREF_MARGIN_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } + } + } + // + // Restore environment settings after training + // + if (HeapDeallocateBuffer (AMD_MEM_2D_RDQS_HANDLE, &MemPtr->StdHeader) != AGESA_SUCCESS) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + } + IDS_HDT_CONSOLE (MEM_STATUS, "\tEnd\n"); + MemTEndTraining (TechPtr); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\nEnd RD DQS 2D training\n\n"); + return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); +} +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the maximum number of lanes to program 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @return TRUE - Configuration valid + * FALSE - Configuration invalid + */ +BOOLEAN +MemTCheck2DTrainingPerConfig ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 i; + if (TechPtr->NBPtr->RefPtr->ForceTrainMode == FORCE_TRAIN_AUTO) { + i = 0; + while (memPlatSpecFlowArray[i] != NULL) { + if ((memPlatSpecFlowArray[i])->S2D (TechPtr->NBPtr, (memPlatSpecFlowArray[i])->EntryOfTables)) { + return TRUE; + } + i++; + } + } + return FALSE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the maximum number of lanes to program 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @return TRUE - Max Number of Lanes + */ +UINT8 +MemT2DGetMaxLanes ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + UINT8 MaxLanes; + if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) != 0) { + // Per Nibble + MaxLanes = TechPtr->NBPtr->MCTPtr->Status[SbEccDimms] ? 18 : 16; + } else { + // Per Byte + MaxLanes = TechPtr->NBPtr->MCTPtr->Status[SbEccDimms] ? 9 : 8; + } + MaxLanes = 8; + return MaxLanes; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref to internal or external control for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + */ +VOID +MemT2DProgramIntExtVrefSelect ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFVrefSel, (TechPtr->NBPtr->RefPtr->ExternalVrefCtl ? 0x0000 : 0x0001)); +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] Vref - Vref value + * + */ +VOID +MemT2DProgramVref ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 Vref + ) +{ + ID_INFO CallOutIdInfo; + CallOutIdInfo.IdField.SocketId = TechPtr->NBPtr->MCTPtr->SocketId; + CallOutIdInfo.IdField.ModuleId = TechPtr->NBPtr->MCTPtr->DieId; + if (TechPtr->NBPtr->RefPtr->ExternalVrefCtl == FALSE) { + // + // Internal vref control + /// @todo : Separate Family-Specific Funtionality + // + // + // This is 1/2 VrefDAC value Sign bit is shifted into place. + // + ASSERT (Vref < 32); + if (Vref < 15) { + Vref = (31 - Vref) << 1; + } else { + Vref = (Vref - 15) << 1; + } + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFVrefDAC, Vref << 2); + } else { + // External vref control + AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(TechPtr->NBPtr->MemPtr->StdHeader)); + TechPtr->NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref; + IDS_HDT_CONSOLE (MEM_FLOW, "\n2D training External Vref callout \n"); + // + /// @todo: Implement UEFI DXE Callout for AgesaExternal2dTrainVrefChange before uncommenting this + // + // AgesaExternal2dTrainVrefChange ((UINTN) CallOutIdInfo.IdInformation, TechPtr->NBPtr->MemPtr); + AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(TechPtr->NBPtr->MemPtr->StdHeader)); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs RdDQS for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] RdDQSDly - RdDqs value + * + */ +VOID +MemT2DPrograRdDQSDly ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN UINT8 RdDQSDly + ) +{ + UINT32 RdDqsTime; + // Program BL registers for both nibble (x4) and bytes (x8, x16) + RdDqsTime = 0; + RdDqsTime = (RdDQSDly & 0x1F) << 8; + RdDqsTime = RdDqsTime | (RdDQSDly & 0x1F); + if ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) == 0) { + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDataByteRxDqsDLLDimm0Broadcast, RdDqsTime); + } else if ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) == 1) { + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDataByteRxDqsDLLDimm1Broadcast, RdDqsTime); + } else if ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) == 2) { + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDataByteRxDqsDLLDimm2Broadcast, RdDqsTime); + } else if ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) == 3) { + TechPtr->NBPtr->SetBitField (TechPtr->NBPtr, BFDataByteRxDqsDLLDimm3Broadcast, RdDqsTime); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function stores data for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] *InPhaseResult[] - Array of inphase results + * @param[in] *PhaseResult180[] - Array of Phase 180 results + * + * @return TRUE - No Errors occurred + */ +VOID +StoreResult ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data, + IN UINT32 InPhaseResult[], + IN UINT32 PhaseResult180[] + ) +{ + UINT8 Lane; + UINT8 Vref; + UINT8 RdDqsDly; + UINT32 Result; + UINT32 Result180; + UINT8 Index; + Vref = TechPtr->NBPtr->Vref; + RdDqsDly = TechPtr->NBPtr->RdDqsDly; + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + for (RdDqsDly = 0; RdDqsDly < Data->MaxRdDqsSweep; RdDqsDly++) { + if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // x8, so combine "Nibble X" and "Nibble X+1" results + Index = Lane * 2; + Result = (InPhaseResult[RdDqsDly] >> Index) & 0x03; + Result180 = (PhaseResult180[RdDqsDly] >> Index) & 0x03; + } else { + // x4, so use "Nibble" results + Result = (InPhaseResult[RdDqsDly] >> Lane) & 0x01; + Result180 = (PhaseResult180[RdDqsDly] >> Lane) & 0x01; + } + Data->Lane[Lane].Vref[Vref].PosRdDqsDly |= (Result == 0) ? (1 << (Data->MaxRdDqsSweep - 1 - RdDqsDly)) : 0; + Data->Lane[Lane].Vref[Vref].NegRdDqsDly |= (Result180 == 0) ? (1 << (Data->MaxRdDqsSweep - 1 - RdDqsDly)) : 0; + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the height of data for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ +BOOLEAN +MemT2DRdDQSHeight ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 Lane; + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + Data->Lane[Lane].HalfDiamondHeight = 0x0F; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Lane: "); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tHeight: "); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (2*(Data->Lane[Lane].HalfDiamondHeight) + 1)); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the width for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return width + */ +UINT8 +MemGet2dRdDQSWidth ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + return TechPtr->NBPtr->DiamondWidthRd; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the step height for the dimond mask for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] Vref - current Vref value + * @param[in] Lane - current Lane + * + * @return TRUE - Step found and value should be updated + * FALSE - Step not found and value should not be updated + */ +BOOLEAN +MemCheck2dRdDQSDiamondMaskStep ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data, + IN UINT8 Vref, + IN UINT8 Lane + ) +{ + UINT8 M; + UINT8 VrefVal; + UINT8 width; + UINT8 i; + BOOLEAN status; + // m = -1 * height/width + // (y-b)/m = x + status = FALSE; + if (Vref > (Data->Vnom - 1)) { + VrefVal = (Vref + 1) - Data->Vnom; + } else { + VrefVal = Vref; + } + width = (MemGet2dRdDQSWidth (TechPtr, Data) - 1) / 2; + M = Data->Lane[Lane].HalfDiamondHeight / width; + i = 1; + while (i <= Data->Lane[Lane].HalfDiamondHeight) { + i = i + M; + if (VrefVal == i) { + status = TRUE; + } + } + return status; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function applies a mask fo 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ +BOOLEAN +MemT2DRdDQSApplyMask ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 RdDqsDly; + UINT8 Lane; + UINT8 Height; + UINT8 Width; + UINT32 PosNegData; + UINT8 Vref; + UINT8 count; + UINT8 Dly; + UINT8 endWidth; + UINT8 startWidth; + UINT8 origEndWidth; + UINT8 origStartWidth; + UINT8 maxOverLapWidth; + UINT8 startOverLapWidth; + BOOLEAN maxHeightExceeded; + BOOLEAN negVrefComplete; + BOOLEAN PosRdDqsToNegRdDqs; + BOOLEAN NegRdDqsToPosRdDqs; + MEM_NB_BLOCK *NBPtr; + NBPtr = TechPtr->NBPtr; + // + // Initialize Convolution + // + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + for (RdDqsDly = 0; RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + Data->Lane[Lane].Convolution[RdDqsDly] = 0; + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdDqsDly); + } + } + endWidth = 0; + startWidth = 0; + origEndWidth = 0; + origStartWidth = 0; + startOverLapWidth = 0; + maxOverLapWidth = 0; + maxHeightExceeded = FALSE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Width"); + // + // Get the Width of Diamond + // + Width = MemGet2dRdDQSWidth (TechPtr, Data); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWidth: %02x\n", Width); + ASSERT (Width != 0); + + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tExecuting convolution function\n"); + // + // Perform the convolution by sweeping the mask function centered at nominal Vref. Results in a one + // dimensional array with FOM values at each delay for each lane. Choose the delay setting at the peak + // FOM value. + // + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + Height = Data->Lane[Lane].HalfDiamondHeight; + ASSERT (Height < Data->Vnom); + // + // RdDqsDly is divided around "Data->MaxRdDqsSweep" into positive and negative directions + // Positive direction -> RdDqsDly = 0 to (Data->MaxRdDqsSweep - 1) + // Negative direction -> RdDqsDly = Data->MaxRdDqsSweep to (NBPtr->TotalRdDQSDlyRange - 1) + // + for (RdDqsDly = 0; RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + // Vref loop is divided around "Data->Vnom - 1" into positive and negative directions + // Negative direction -> Vref = 0 ("Data->Vnom - 1") to Height("Data->Vnom - 1" - Height) + // Positive direction -> Vref = "Data->Vnom" to Height("Data->Vnom" + Height) + // + negVrefComplete = FALSE; + PosRdDqsToNegRdDqs = FALSE; + NegRdDqsToPosRdDqs = FALSE; + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Initial negative direction where Vref = 0 ("Data->Vnom - 1"), so we need to set + // initial startWidth and endWidth for +/- RdDqs + // + // Create common delay based on +/- RdDqs + if (RdDqsDly > (Data->MaxRdDqsSweep - 1)) { + Dly = RdDqsDly - Data->MaxRdDqsSweep; + } else { + Dly = RdDqsDly; + } + if (Vref == 0 ) { + // Initialize -Vref + maxHeightExceeded = FALSE; // reset for start of -Vref + // Case 1: if +RdDqs - Check for lower bound (Width/2 > RdDqs > 0) + // : if -RdDqs - Check for lower bound (Width/2 + Data->MaxRdDqsSweep > RdDqs > Data->MaxRdDqsSweep) + if (Dly < Width / 2) { + endWidth = Dly + Width / 2 + 1; + startWidth = 0; + } else if ((Dly + Width / 2) > (Data->MaxRdDqsSweep - 1)) { + // Case 2: if +RdDqs - Check for upper bound ((Data->MaxRdDqsSweep - 1) < RdDqs < ((Data->MaxRdDqsSweep - 1) - Width/2)) + // : if -RdDqs - Check for lower bound ((DatNBPtra->TotalRdDQSDlyRange - 1) < RdDqs < ((NBPtr->TotalRdDQSDlyRange - 1) - Width/2)) + endWidth = Data->MaxRdDqsSweep; + startWidth = Dly - Width / 2; + } else { + // Set the initial "startWidth" and "endWidth" for +/- RdDqs + endWidth = Dly + Width / 2 + 1; + startWidth = Dly - Width / 2; + } + origEndWidth = endWidth; + origStartWidth = startWidth; + } else if (Vref == Data->Vnom) { + // Initialize +Vref + endWidth = origEndWidth; + startWidth = origStartWidth; + maxHeightExceeded = FALSE; // reset for start of +Vref + negVrefComplete = TRUE; + } else if ((Vref > (Data->Vnom + Height)) && negVrefComplete == TRUE) { + break; //switch to next RdDqs Dly if height exceeded for +vref and -vref complete + } else { + if (startWidth >= endWidth) { + if (RdDqsDly == (NBPtr->TotalRdDQSDlyRange - 1)) { + // Special condition for end of -RdDqs range + startWidth = Data->MaxRdDqsSweep - 1; + endWidth = Data->MaxRdDqsSweep; + } else { + // Width = 0, but Height not reached, + startWidth = Dly; + endWidth = Dly + 1; + } + } else { + // Check for Case 1 and Case 2 above + if ((RdDqsDly + Width / 2) > (NBPtr->TotalRdDQSDlyRange - 1)) { + endWidth = origEndWidth; + } + } + maxHeightExceeded = FALSE; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + if (Lane == 0) { + if (RdDqsDly == (Data->MaxRdDqsSweep - (Width / 2)) ) { + Data->DiamondLeft[Vref] = startWidth; + Data->DiamondRight[Vref] = endWidth - 1; + } + } + ); + // + // Determine the correct RdDqs (+/-) and Vref (+/-)direction + // + if (maxHeightExceeded == FALSE) { + if (RdDqsDly < Data->MaxRdDqsSweep) { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdDqsDly; // +RdDqs Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdDqsDly; // +RdDqs Dly, -Vref + } + } else { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdDqsDly; // -RdDqs Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdDqsDly; // -RdDqs Dly, -Vref + } + } + // + // Case 1: Non-overlap condition: + // Count the number of passes from "startWidth" to "endWidth" + // + for (count = startWidth; count < endWidth; count++) { + Data->Lane[Lane].Convolution[RdDqsDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdDqsDly]; + } + // Case 2: Overlay between +RdDqs and -RdDqs starting from +RdDqs + // Count the number of passes from "startWidth" to "endWidth" + // + if ((RdDqsDly <= (Data->MaxRdDqsSweep - 1) && (RdDqsDly > ((Data->MaxRdDqsSweep - 1) - Width / 2)))) { + startOverLapWidth = 0; + if (Vref == 0 || Vref == Data->Vnom) { + maxOverLapWidth = (RdDqsDly + Width / 2) - (Data->MaxRdDqsSweep - 1); // Initial overlap max width size + } else if (maxOverLapWidth == 0) { + maxOverLapWidth = startOverLapWidth; // Stop counting after overlap region complete + } + // Ensure that +/- vref is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdDqsDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdDqsDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdDqsDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdDqsDly]; + } + if (maxOverLapWidth > 0) { + if (MemCheck2dRdDQSDiamondMaskStep (TechPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + maxOverLapWidth--; // Reduce overlap width outside of diamond mask + } + PosRdDqsToNegRdDqs = TRUE; + } + } + if (((RdDqsDly - Data->MaxRdDqsSweep) < Width / 2) && (RdDqsDly > (Data->MaxRdDqsSweep - 1))) { + // + // Case 3: Overlay between -RdDqs and +RdDqs starting from -RdDqs + // Count the number of passes from "startWidth" to "endWidth" + // + maxOverLapWidth = Data->MaxRdDqsSweep; + if (Vref == 0 || Vref == Data->Vnom) { + startOverLapWidth = RdDqsDly - Width / 2; // Initial overlap start point + } else if (startOverLapWidth > maxOverLapWidth) { + maxOverLapWidth = maxOverLapWidth - 1; // Continue to count until MaxHeight excceded + } + // Ensure that vref + or - is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdDqsDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdDqsDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdDqsDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdDqsDly]; + } + if (startOverLapWidth < maxOverLapWidth) { + if (MemCheck2dRdDQSDiamondMaskStep (TechPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + startOverLapWidth++; // Reduce overlap width outside of diamond mask + } + NegRdDqsToPosRdDqs = TRUE; + } + } + } + if (MemCheck2dRdDQSDiamondMaskStep (TechPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + if (PosRdDqsToNegRdDqs) { + startWidth++; + endWidth = Data->MaxRdDqsSweep; + PosRdDqsToNegRdDqs = FALSE; + } else if (NegRdDqsToPosRdDqs) { + startWidth = 0; + endWidth--; + NegRdDqsToPosRdDqs = FALSE; + } else { + startWidth++; + endWidth--; + } + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdDqsDly); + } + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Diamond Shape: \n"); + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (RdDqsDly = (Data->MaxRdDqsSweep - Width); RdDqsDly < Data->MaxRdDqsSweep; RdDqsDly++) { + if (Vref < (Data->Vnom - 1)) { + if (RdDqsDly == Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdDqsDly == Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) - (Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } else { + if (RdDqsDly == Data->DiamondLeft[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdDqsDly == Data->DiamondRight[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[Vref - (Data->Vnom - 1)]) - (Data->DiamondLeft[Vref - (Data->Vnom - 1)])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t Convolution results after processing raw data:\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Delay: "); + for (RdDqsDly = 0; RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x ", RdDqsDly <= (Data->MaxRdDqsSweep - 1) ? (Data->MaxRdDqsSweep - 1) - RdDqsDly : (NBPtr->TotalRdDQSDlyRange - 1) - RdDqsDly); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tConv: "); + for (RdDqsDly = 0; RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%03x ", Data->Lane[Lane].Convolution[RdDqsDly]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + ); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function Examines the convolution function and determines the Max RDqs for + * 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ + +BOOLEAN +MemT2DRdDQSProcessConvolution ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 RdDqsDly; + UINT8 Lane; + UINT16 MaxFOM; + UINT8 MaxRange; + UINT8 CurrRange; + MEM_NB_BLOCK *NBPtr; + BOOLEAN status; + + NBPtr = TechPtr->NBPtr; + status = TRUE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining MaxRdDqs based on Convolution function\n"); + // Determine the MaxRdDqs Dly for the convolution function + // - Choose the delay setting at the peak FOM value. + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + // Find largest value as MaxFOM + MaxFOM = 0; + for (RdDqsDly = 0; RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + if (Data->Lane[Lane].Convolution[RdDqsDly] > MaxFOM) { + MaxFOM = Data->Lane[Lane].Convolution[RdDqsDly]; + } + } + status = MaxFOM > 0 ? TRUE : FALSE; // It is an error if all convolution points are zero + + // Then find the midpoint of the largest consecutive window w/ that MaxFOM + // In cases of an even number of consecutive points w/ that MaxFOM exists, + // choose the midpoint to the right + // All things being equal, favor the right side of a bi-modal eye + // Stressful SSO patterns shift the eye right! + MaxRange = 0; + CurrRange = 0; + for (RdDqsDly = 0; (MaxFOM > 0) && RdDqsDly < NBPtr->TotalRdDQSDlyRange; RdDqsDly++) { + if (Data->Lane[Lane].Convolution[RdDqsDly] == MaxFOM) { + CurrRange++; + if (CurrRange >= MaxRange) { + Data->Lane[Lane].MaxRdDqs = RdDqsDly - ((CurrRange - 1) / 2); + MaxRange = CurrRange; + } + } else { + CurrRange = 0; + } + } + + if (Data->Lane[Lane].MaxRdDqs > Data->MaxRdDqsSweep) { + status = FALSE; // Error + } + // Set Actual register value + if (Data->Lane[Lane].MaxRdDqs < Data->MaxRdDqsSweep) { + Data->Lane[Lane].MaxRdDqs = (Data->MaxRdDqsSweep - 1) - Data->Lane[Lane].MaxRdDqs; + } else { + status = FALSE; // Error + } + } + + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Lane: "); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tMaxRdDqs: "); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Data->Lane[Lane].MaxRdDqs); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + + if (status == FALSE) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_INVALID_2D_RDDQS_VALUE, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); + } + return status; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Max RDqs for 2D RdDQS training from convolution + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ +BOOLEAN +MemT2DRdDQSPrograMaxRdDQSDly ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 Lane; + UINT8 LaneHighRdDqs2dDlys; + UINT8 LaneLowRdDqs2dDlys; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming MaxRdDysDly per Lane\n\n"); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // Program Byte based for x8 and x16 + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdDqs); + TechPtr->NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = Data->Lane[Lane].MaxRdDqs; + } else { + // Program nibble based x4, so use "Nibble" + TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRdDqs2dDly, DIMM_NBBL_ACCESS ((TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdDqs); + TechPtr->NBPtr->ChannelPtr->RdDqs2dDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_NUMBER_LANES + Lane] = Data->Lane[Lane].MaxRdDqs; + // For each pair of nibbles (high (Odd Nibble) and Low (Even nibble)), find the largest and use that as the RdDqsDly value + if ((Lane & 0x1) == 0) { + LaneHighRdDqs2dDlys = Data->Lane[Lane + 1].MaxRdDqs; + LaneLowRdDqs2dDlys = Data->Lane[Lane].MaxRdDqs; + if (LaneHighRdDqs2dDlys > LaneLowRdDqs2dDlys) { + TechPtr->NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneHighRdDqs2dDlys; + } else { + TechPtr->NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneLowRdDqs2dDlys; + } + } + TechPtr->NBPtr->DctCachePtr->Is2Dx4 = TRUE; + } + } + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the Positive and negative Vref Margin for the current CS + * for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ +BOOLEAN +MemT2DRdDQSFindCsVrefMargin ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 SmallestMaxVrefNeg; + UINT8 Lane; + UINT8 RdDqsDly; + UINT8 Vref; + UINT8 MaxVrefPositive; + UINT8 MaxVrefNegative; + UINT8 SmallestMaxVrefPos; + UINT32 PosNegData; + SmallestMaxVrefPos = 0xFF; + SmallestMaxVrefNeg = 0; + MaxVrefPositive = 0; + MaxVrefNegative = 0xFF; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Smallest Max Positive and Negative Vref\n\n"); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + RdDqsDly = (Data->MaxRdDqsSweep - 1) - Data->Lane[Lane].MaxRdDqs; + for (Vref = 0; Vref < (Data->Vnom - 1); Vref++) { + // Neg Vref - (searching from top of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdDqsDly; + if ((UINT8) ((PosNegData >> RdDqsDly) & 0x1) == 1) { + MaxVrefNegative = Vref; + break; + } + TechPtr->NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (TechPtr->NBPtr, &Vref); + } + for (Vref = (Data->Vnom - 1); Vref < (TechPtr->NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Pos Vref - (searching from Vnom + 1 of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdDqsDly; + if ((UINT8) ((PosNegData >> RdDqsDly) & 0x1) == 0) { + // Convert to register setting + MaxVrefPositive = Vref - 1;// - Data->Vnom; + break; + } else { + // If Vref = 1F passes, then smallest Vref = 0x1F + if (Vref == ((TechPtr->NBPtr->TotalMaxVrefRange - 1) - 1)) { + MaxVrefPositive = 0x1E; + break; + } + } + TechPtr->NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (TechPtr->NBPtr, &Vref); + } + if (MaxVrefPositive < SmallestMaxVrefPos) { + // Find the smallest Max Pos Vref + SmallestMaxVrefPos = MaxVrefPositive; + } + if (MaxVrefNegative > SmallestMaxVrefNeg) { + // Find the largest Max Neg Vref + SmallestMaxVrefNeg = MaxVrefNegative; + } + } + if (SmallestMaxVrefPos != (Data->Vnom - 2)) { + Data->SmallestPosMaxVrefperCS[TechPtr->ChipSel] = SmallestMaxVrefPos - Data->Vnom + 1; + } else { + Data->SmallestPosMaxVrefperCS[TechPtr->ChipSel] = 0; + } + Data->SmallestNegMaxVrefperCS[TechPtr->ChipSel] = (Data->Vnom - 1) - SmallestMaxVrefNeg; + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "Smallest Max Positive Vref Offset from V-Nom for ChipSel %02x = + %02x\n", TechPtr->ChipSel, Data->SmallestPosMaxVrefperCS[TechPtr->ChipSel]); + if (Data->SmallestPosMaxVrefperCS[TechPtr->ChipSel] == 0) { + IDS_HDT_CONSOLE (MEM_FLOW, "Smallest Max Negative Vref Offset from V-Nom for ChipSel %02x = 00\n"); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "Smallest Max Negative Vref Offset from V-Nom for ChipSel %02x = - %02x\n", TechPtr->ChipSel, Data->SmallestNegMaxVrefperCS[TechPtr->ChipSel]); + } + ); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the final Vref Margin for 2D RdDQS training + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return TRUE - No Errors occurred + * @return FALSE - Errors occurred + */ +BOOLEAN +MemT2DRdDQSFinalVrefMargin ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + UINT8 ChipSel; + UINT8 SmallestMaxPosVref; + UINT8 SmallestMaxNegVref; + UINT8 OffsetFromVref; + UINT8 MaxRegVref; + SmallestMaxNegVref = 0x7F; + SmallestMaxPosVref = 0x7F; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Final Vref for channel\n\n"); + for (ChipSel = 0; ChipSel < TechPtr->NBPtr->CsPerChannel; ChipSel = ChipSel + TechPtr->NBPtr->CsPerDelay ) { + if ( (TechPtr->NBPtr->MCTPtr->Status[SbLrdimms]) ? ((TechPtr->NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((TechPtr->NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + if (Data->SmallestPosMaxVrefperCS[ChipSel] < SmallestMaxPosVref) { + SmallestMaxPosVref = Data->SmallestPosMaxVrefperCS[ChipSel]; + } + if (Data->SmallestNegMaxVrefperCS[ChipSel] < SmallestMaxNegVref) { + SmallestMaxNegVref = Data->SmallestNegMaxVrefperCS[ChipSel]; + } + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "Final Max Vref Offset From Vnom ="); + if (SmallestMaxPosVref > SmallestMaxNegVref) { + OffsetFromVref = (SmallestMaxPosVref - SmallestMaxNegVref) / 2; + if (OffsetFromVref != 0) { + IDS_HDT_CONSOLE (MEM_FLOW, " + "); + } + TechPtr->NBPtr->ChannelPtr->MaxVref = (Data->Vnom - 1) + OffsetFromVref; + } else { + OffsetFromVref = (SmallestMaxNegVref - SmallestMaxPosVref) / 2; + if (OffsetFromVref != 0) { + IDS_HDT_CONSOLE (MEM_FLOW, " - "); + } + TechPtr->NBPtr->ChannelPtr->MaxVref = (Data->Vnom - 1) - OffsetFromVref; + } + IDS_HDT_CONSOLE (MEM_FLOW, "%02x \n", OffsetFromVref); + MaxRegVref = TechPtr->NBPtr->ChannelPtr->MaxVref; + if (MaxRegVref <= ((TechPtr->NBPtr->TotalMaxVrefRange / 2) - 1)) { + MaxRegVref = (TechPtr->NBPtr->TotalMaxVrefRange - 1) - MaxRegVref; + } else { + MaxRegVref = MaxRegVref - (TechPtr->NBPtr->TotalMaxVrefRange / 2 - 1); + } + /// @todo: Need Family specific hook for MaxRegVref + MaxRegVref = ((MaxRegVref * 2) & 0x3F); + IDS_HDT_CONSOLE (MEM_FLOW, "Actual Max Vref programmed = %02x \n", MaxRegVref); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function displays ther results of the 2D search + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return + */ +VOID +MemT2DRdDqsDisplaySearch ( + IN OUT MEM_TECH_BLOCK *TechPtr, + IN MEM_2D_ENTRY *Data + ) +{ + IDS_HDT_CONSOLE_DEBUG_CODE ( + UINT8 Lane; + INT8 Vref; + // Display data collected + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDisplaying Data collected\n\n"); + for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Vref NegRdDqs PosRdDqs\n"); + for (Vref = TechPtr->NBPtr->TotalMaxVrefRange - 2; Vref >= 0; Vref--) { + if (Vref < (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t - "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", ((Data->Vnom -1) - Vref)); + } else if (Vref == (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t 00 "); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t + "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Vref - (Data->Vnom - 1)); + } + IDS_HDT_CONSOLE (MEM_FLOW, "%08x", TechPtr->Local2DData->Lane[Lane].Vref[Vref].NegRdDqsDly); // debug + IDS_HDT_CONSOLE (MEM_FLOW, "%08x \n", TechPtr->Local2DData->Lane[Lane].Vref[Vref].PosRdDqsDly); //debug + TechPtr->NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (TechPtr->NBPtr, &Vref); + } + } + ) +} |