blob: 9bb94408c5005800a9afb4e968e8ddc721bfd3cd [file] [log] [blame]
/* $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_MTRRDDQS2DTRAINING_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);
} 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);
}
}
)
}