blob: d78a3fcfdfad47d93d7726b19c3976f9b9eacc0f [file] [log] [blame]
/* $NoKeywords:$ */
/**
* @file
*
* mttEdgeDetect.c
*
* DQS R/W position training utilizing Data Eye Edge Detection for optimization
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: AGESA
* @e sub-project: (Mem/Tech)
* @e \$Revision: 57884 $ @e \$Date: 2011-08-15 10:42:12 -0600 (Mon, 15 Aug 2011) $
*
**/
/*****************************************************************************
*
* Copyright (C) 2012 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 "mttEdgeDetect.h"
#include "OptionMemory.h"
#include "merrhdl.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G1_PEICC)
#define FILECODE PROC_MEM_TECH_MTTEDGEDETECT_FILECODE
/*----------------------------------------------------------------------------
* DEFINITIONS AND MACROS
*
*----------------------------------------------------------------------------
*/
#define LAST_DELAY (-128)
#define INC_DELAY 1
#define DEC_DELAY 0
/*----------------------------------------------------------------------------
* TYPEDEFS AND STRUCTURES
*
*----------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
* PROTOTYPES OF LOCAL FUNCTIONS
*
*----------------------------------------------------------------------------
*/
/**
* Sweep Table For Byte Training without insertion delay
*
*/
DQS_POS_SWEEP_TABLE SweepTableByte[] =
{
// Begin End Inc/Dec Step EndResult Edge
{ 0x00, 0x1F, INC_DELAY, 4, 0xFFFF, LEFT_EDGE}, /// For Left Edge, start from 0 and Increment to 0x1F by 4 until all PASS
{ LAST_DELAY, 0x00, DEC_DELAY, -1, 0xFE00, LEFT_EDGE}, /// Then go back down to 0x00 by 1 until all FAIL
{ 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
{ LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
};
/**
* Sweep Table For Byte Training with insertion delay
*
*/
DQS_POS_SWEEP_TABLE InsSweepTableByte[] =
{
// Begin End Inc/Dec Step EndResult Edge
{ 0x00, -0x20, DEC_DELAY, -4, 0xFE00, LEFT_EDGE}, /// For Left Edge, start from 0 and Decrement to -0x20 by -4 until all FAIL
{ LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFFFF, LEFT_EDGE}, /// Then go back up to 0x1F by 1 until all PASS
{ 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
{ LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
};
BOOLEAN
STATIC
MemTTrainDQSRdWrEdgeDetect (
IN OUT MEM_TECH_BLOCK *TechPtr
);
BOOLEAN
STATIC
MemTInitTestPatternAddress (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
);
BOOLEAN
STATIC
MemTContinueSweep (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
);
BOOLEAN
STATIC
MemTSetNextDelay (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
);
UINT8
STATIC
MemTScaleDelayVal (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN INT8 Delay
);
BOOLEAN
STATIC
MemTDataEyeSave (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr,
IN UINT8 ByteLane
);
/*----------------------------------------------------------------------------
* EXPORTED FUNCTIONS
*
*----------------------------------------------------------------------------
*/
extern MEM_FEAT_TRAIN_SEQ memTrainSequenceDDR3[];
/* -----------------------------------------------------------------------------*/
/**
*
* This function executes DQS position training for all a Memory channel using
* the Edge Detection algorithm.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
*
*/
BOOLEAN
MemTTrainDQSEdgeDetectSw (
IN OUT MEM_TECH_BLOCK *TechPtr
)
{
MEM_NB_BLOCK *NBPtr;
BOOLEAN Status;
Status = FALSE;
NBPtr = TechPtr->NBPtr;
TechPtr->TrainingType = TRN_DQS_POSITION;
//
// Initialize the Pattern
//
if (AGESA_SUCCESS == NBPtr->TrainingPatternInit (NBPtr)) {
//
// Setup hardware training engine (if applicable)
//
NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType);
//
// Start Edge Detection
//
Status |= MemTTrainDQSRdWrEdgeDetect (TechPtr);
//
// Finalize the Pattern
//
Status &= (AGESA_SUCCESS == NBPtr->TrainingPatternFinalize (NBPtr));
}
return Status;
}
/*----------------------------------------------------------------------------
* LOCAL FUNCTIONS
*
*----------------------------------------------------------------------------
*/
/* -----------------------------------------------------------------------------*/
/**
*
* This Executes Read DQS and Write Data Position training on a chip select pair
* using the Edge Detection algorithm.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
*
* @return TRUE - No Errors occurred
* @return FALSE - Errors occurred
*/
BOOLEAN
STATIC
MemTTrainDQSRdWrEdgeDetect (
IN OUT MEM_TECH_BLOCK *TechPtr
)
{
MEM_DATA_STRUCT *MemPtr;
MEM_NB_BLOCK *NBPtr;
UINT8 WrDqDelay;
UINT8 Dct;
UINT8 CSPerChannel;
UINT8 CsPerDelay;
UINT8 ChipSel;
UINT8 i;
BOOLEAN Status;
UINT8 TimesFail;
UINT8 TimesRetrain;
NBPtr = TechPtr->NBPtr;
MemPtr = NBPtr->MemPtr;
TimesRetrain = DEFAULT_TRAINING_TIMES;
IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader);
//
// Set environment settings before training
//
IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Read/Write Data Eye Edge Detection.\n");
MemTBeginTraining (TechPtr);
//
// Do Rd DQS /Wr Data Position 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);
//
// Chip Select Loop
//
CSPerChannel = NBPtr->CSPerChannel (NBPtr);
CsPerDelay = NBPtr->CSPerDelay (NBPtr);
for (ChipSel = 0; ChipSel < CSPerChannel; ChipSel = ChipSel + CsPerDelay ) {
//
// Init Bit Error Masks
//
LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
0xFF,
(MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
&MemPtr->StdHeader);
if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
TechPtr->ChipSel = ChipSel;
IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tIncrease WrDat, Train RdDqs:\n");
TechPtr->DqsRdWrPosSaved = 0;
//
// Use a list of Approximate Write Data delay values and train Read DQS Position for
// each until a valid Data eye is found.
//
Status = FALSE;
TimesFail = 0;
NBPtr->FamilySpecificHook[InitializeRxEnSeedlessTraining] (NBPtr, NBPtr);
ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) {
i = 0;
while (NBPtr->GetApproximateWriteDatDelay (NBPtr, i, &WrDqDelay)) {
TechPtr->SmallDqsPosWindow = FALSE;
//
// Set Write Delay approximation
//
TechPtr->Direction = DQS_WRITE_DIR;
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrite Delay: %02x", WrDqDelay);
MemTSetDQSDelayAllCSR (TechPtr, WrDqDelay);
//
// Attempt Read Training
//
TechPtr->Direction = DQS_READ_DIR;
Status = memTrainSequenceDDR3[NBPtr->TrainingSequenceIndex].MemTechFeatBlock->RdPosTraining (TechPtr);
if (Status) {
//
// If Read DQS Training was successful, Train Write Data (DQ) Position
//
TechPtr->DqsRdWrPosSaved = 0;
IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tTrain WrDat:\n\n");
TechPtr->Direction = DQS_WRITE_DIR;
if (NBPtr->FamilySpecificHook[BeforeWrDatTrn] (NBPtr, &ChipSel)) {
Status = MemTTrainDQSEdgeDetect (TechPtr);
}
break;
}
i++;
}
ERROR_HANDLE_RETRAIN_END ((Status == FALSE), TimesFail)
}
//
// If we went through the table, Fail.
//
if (Status == FALSE) {
// On training failure, check and record whether training fails due to small window or no window
if (TechPtr->SmallDqsPosWindow) {
NBPtr->MCTPtr->ErrStatus[EsbSmallDqs] = TRUE;
} else {
NBPtr->MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
}
SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
if (TechPtr->Direction == DQS_READ_DIR) {
PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_RD_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
} else {
PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_WR_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
}
NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ChipSel;
// If the even chip select failed training always fail the odd, if present.
if ((ChipSel & 0x01) == 0) {
if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << (ChipSel + 1))) {
NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << (ChipSel + 1);
}
}
if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
ASSERT (FALSE);
}
}
} else {
//
// Clear Bit Error Masks if these CS will not be trained.
//
LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
0x00,
(MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
&NBPtr->MemPtr->StdHeader);
}
}
}
//
// Restore environment settings after training
//
MemTEndTraining (TechPtr);
IDS_HDT_CONSOLE (MEM_FLOW, "End Read/Write Data Eye Edge Detection\n\n");
return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function executes DQS position training for both read and write, using
* the Edge Detection Algorithm. This method searches for the beginning and end
* of the Data Eye with out scanning every DSQ delay value. The following is a
* detailed description of the algorithm:
*
* Four-Stage Data Eye Sweep
*
* -Search starts at Delay value of 0.
* -Search left in steps of 4/32UI looking for all Byte lanes Passing. Left from zero rolls over to a negative value.
* -Negative values are translated to the high end of the delay range, but using Insertion delay comparison.
* -For each passing byte lane, freeze delay at first passing value, but set mask so next steps will not compare for byte lanes that previously passed
* -Switch to search right in steps of 1/32UI looking for fail.
* -For each lane, starting delay for 1/32 sweep right is first passing delay from 4/32 sweep left.
* -For each failing byte lane, freeze delay at first failing value, but set mask so next steps will not compare for byte lanes that previously failed
* -Search right until all byte lanes have failed
* -For each lane, right edge used by BIOS will be first failing delay value minus 1/32
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
*
* @return TRUE - All bytelanes pass
* @return FALSE - Some bytelanes fail
*/
BOOLEAN
MemTTrainDQSEdgeDetect (
IN OUT MEM_TECH_BLOCK *TechPtr
)
{
MEM_NB_BLOCK *NBPtr;
DIE_STRUCT *MCTPtr;
DQS_POS_SWEEP_TABLE *SweepTablePtr;
UINT8 SweepTableSize;
SWEEP_INFO SweepData;
BOOLEAN Status;
UINT16 CurrentResult;
UINT16 AlignedResult;
UINT16 OffsetResult;
UINT8 StageIndex;
UINT8 CsIndex;
UINT8 CsPerDelay;
UINT8 i;
Status = TRUE;
//
// Initialize Object Pointers
//
NBPtr = TechPtr->NBPtr;
MCTPtr = NBPtr->MCTPtr;
//
// Initialize stack variables
//
LibAmdMemFill (&SweepData, 0, sizeof (SWEEP_INFO), &NBPtr->MemPtr->StdHeader);
//
/// Get Pointer to Sweep Table
//
if (TechPtr->Direction == DQS_READ_DIR) {
SweepTablePtr = InsSweepTableByte;
SweepTableSize = GET_SIZE_OF (InsSweepTableByte);
} else {
SweepTablePtr = SweepTableByte;
SweepTableSize = GET_SIZE_OF (SweepTableByte);
}
//
// Get number of CS to train
//
CsPerDelay = NBPtr->CSPerDelay (NBPtr);
//
/// Set up the test Pattern, exit if no Memory
//
if (MemTInitTestPatternAddress (TechPtr, &SweepData) == FALSE) {
LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (TechPtr->ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
0,
(MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
&NBPtr->MemPtr->StdHeader);
return FALSE;
}
//
// Clear Error Flag
//
SweepData.Error = FALSE;
NBPtr->FamilySpecificHook[InitialzeRxEnSeedlessByteLaneError] (NBPtr, NBPtr);
//
/// Process Sweep table, using entries from the table to determine Starting and Ending Delays
/// as well as the Step size and criteria for evaluating whether the correct result is found.
///
/// Delay values at this level are an abstract range of values which gets scaled to the actual value
/// before it is written to the hardware. This allows NB specific code to handle the scaling as a
/// function of frequency or other conditions.
//
for (StageIndex = 0; (StageIndex < SweepTableSize) && (SweepData.Error == FALSE); StageIndex++) {
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSTAGE: %d\t", StageIndex);
//
/// Initialize SweepData variables
//
SweepData.BeginDelay = SweepTablePtr->BeginDelay;
SweepData.EndDelay = SweepTablePtr->EndDelay;
SweepData.Step = 0; /// Step Value will be 0 to start.
SweepData.EndResult = SweepTablePtr->EndResult;
if (!(MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining])) {
SweepData.EndResult |= 0x0100;
}
SweepData.Edge = SweepTablePtr->MinMax;
SweepData.InsertionDelayMsk = 0;
SweepData.ResultFound = 0x0000;
//
// Set Training Delays Pointer.
//
if (TechPtr->Direction == DQS_READ_DIR) {
SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->RdDqsMaxDlys : NBPtr->ChannelPtr->RdDqsMinDlys);
} else {
SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->WrDatMaxDlys : NBPtr->ChannelPtr->WrDatMinDlys);
};
//
/// Set initial TrnDelay Values if necessary
//
IDS_HDT_CONSOLE (MEM_FLOW, "Sweeping %s DQS, %s from ", (TechPtr->Direction == DQS_READ_DIR) ?"Read":"Write", (SweepTablePtr->ScanDir == INC_DELAY) ? "incrementing":"decrementing");
if (SweepData.BeginDelay != LAST_DELAY) {
IDS_HDT_CONSOLE (MEM_FLOW, "%02x", (UINT16) MemTScaleDelayVal (TechPtr, SweepData.BeginDelay));
for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); i++) {
SweepData.TrnDelays[i] = SweepData.BeginDelay;
}
} else {
IDS_HDT_CONSOLE (MEM_FLOW, "Current Delay");
SweepData.Step = SweepTablePtr->Step;
}
IDS_HDT_CONSOLE (MEM_FLOW, " by %02x, until all bytelanes %s.\n\n", (UINT16) MemTScaleDelayVal (TechPtr, ABS (SweepTablePtr->Step)), (SweepData.EndResult == 0xFFFF)?"PASS":"FAIL");
//-------------------------------------------------------------------
// Sweep DQS Delays
// MemTContinueSweep function returns false to break out of loop.
// There are no other breaks out of this loop.
//-------------------------------------------------------------------
while (MemTContinueSweep (TechPtr, &SweepData)) {
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : 08 07 06 05 04 03 02 01 00\n");
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDQS Delays : %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
(UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[8]),
(UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[7]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[6]),
(UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[5]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[4]),
(UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[3]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[2]),
(UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[1]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[0])
);
//
/// Set Step Value
//
SweepData.Step = SweepTablePtr->Step;
CurrentResult = 0xFFFF;
//
/// Chip Select Loop: Test the Pattern for all populated CS that are controlled by the current delay registers
//
for (CsIndex = 0; CsIndex < CsPerDelay ; CsIndex++, TechPtr->ChipSel++) {
ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
if (SweepData.CsAddrValid[CsIndex] == TRUE) {
//
/// If this is a Write Dqs sweep, Write the pattern now.
//
if (TechPtr->Direction == DQS_WRITE_DIR) {
NBPtr->WritePattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
}
//
/// Read the Pattern Back
//
NBPtr->ReadPattern (NBPtr, TechPtr->TestBufPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
//
/// Compare the Pattern and Merge the results using InsertionDelayMsk
//
AlignedResult = NBPtr->CompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
CurrentResult &= AlignedResult | SweepData.InsertionDelayMsk;
if (SweepData.InsertionDelayMsk != 0) {
OffsetResult = NBPtr->InsDlyCompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
CurrentResult &= (OffsetResult | (~SweepData.InsertionDelayMsk));
}
//
/// Flush the Test Pattern
//
NBPtr->FlushPattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
NBPtr->FamilySpecificHook[ResetRxFifoPtr] (NBPtr, NBPtr);
}
} /// End Chip Select Loop
TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResult : %c %c %c %c %c %c %c %c %c \n",
(SweepData.ResultFound & ((UINT16) 1 << (8))) ? ' ':(CurrentResult & ((UINT16) 1 << (8))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (7))) ? ' ':(CurrentResult & ((UINT16) 1 << (7))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (6))) ? ' ':(CurrentResult & ((UINT16) 1 << (6))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (5))) ? ' ':(CurrentResult & ((UINT16) 1 << (5))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (4))) ? ' ':(CurrentResult & ((UINT16) 1 << (4))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (3))) ? ' ':(CurrentResult & ((UINT16) 1 << (3))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (2))) ? ' ':(CurrentResult & ((UINT16) 1 << (2))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (1))) ? ' ':(CurrentResult & ((UINT16) 1 << (1))) ? 'P':'.',
(SweepData.ResultFound & ((UINT16) 1 << (0))) ? ' ':(CurrentResult & ((UINT16) 1 << (0))) ? 'P':'.'
);
//
/// Merge current result into cumulative result and make it positive.
//
SweepData.ResultFound |= ~(CurrentResult ^ SweepData.EndResult);
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResultFound : %c %c %c %c %c %c %c %c %c \n\n",
(SweepData.ResultFound & ((UINT16) 1 << (8))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (7))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (6))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (5))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (4))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (3))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (2))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (1))) ? 'Y':' ',
(SweepData.ResultFound & ((UINT16) 1 << (0))) ? 'Y':' '
);
} /// End of Delay Sweep
//
/// Place Final delay values at last passing delay.
//
if (SweepData.ResultFound == 0xFFFF) {
if ( ABS (SweepData.Step) == 1) {
for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
if ((SweepData.EndResult & ((UINT16) (1 << i))) == 0) {
SweepData.TrnDelays[i] = SweepData.TrnDelays[i] - SweepData.Step;
}
}
}
}
//
// Update Pointer to Sweep Table
//
SweepTablePtr++;
}///End of Edge Detect loop
//
/// If No Errors are detected, Calculate Data Eye Width and Center
//
if (SweepData.Error == FALSE) {
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tData Eye Results:\n\n");
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tByte Left Right\n");
IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane Edge Edge Width Center\n");
for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t %0d", i);
TechPtr->Bytelane = i;
if (!MemTDataEyeSave (TechPtr, &SweepData, i)) {
break;
}
IDS_HDT_CONSOLE (MEM_FLOW, "\n");
if (SweepData.Error == TRUE) {
Status = FALSE;
}
}
} else {
Status = FALSE;
IDS_HDT_CONSOLE (MEM_FLOW, "\t\t--DATA EYE NOT FOUND--\n\n");
NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrNoWindBLError] (NBPtr, &SweepData);
}
return Status;
}
/* -----------------------------------------------------------------------------*/
/**
*
* Initialize the Test Pattern Address for two chip selects and, if this
* is a Write Data Eye, write the initial test pattern.
*
* Test Address is stored in the Sweep info struct. If Memory is not present
* then return with False.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
*
* @return BOOLEAN
* TRUE - Memory is present
* FALSE - No memory present on this Chip Select pair.
*
**
*/
BOOLEAN
STATIC
MemTInitTestPatternAddress (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
)
{
MEM_NB_BLOCK *NBPtr;
UINT8 ChipSel;
UINT8 CsPerDelay;
UINT8 CsIndex;
BOOLEAN BanksPresent;
NBPtr = TechPtr->NBPtr;
BanksPresent = FALSE;
CsPerDelay = NBPtr->CSPerDelay (NBPtr);
ChipSel = TechPtr->ChipSel;
for (CsIndex = 0; CsIndex < CsPerDelay; ChipSel++, CsIndex++, TechPtr->ChipSel++) {
ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
ASSERT (ChipSel < MAX_CS_PER_CHANNEL);
ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
//
/// If memory is present on this cs, get the test addr
//
if (NBPtr->GetSysAddr (NBPtr, ChipSel, &(SweepPtr->TestAddrRJ16[CsIndex]))) {
if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
BanksPresent = TRUE;
SweepPtr->CsAddrValid[CsIndex] = TRUE;
//
/// If this is a Read Dqs sweep, Write the pattern now.
//
if (TechPtr->Direction == DQS_READ_DIR) {
IDS_HDT_CONSOLE (MEM_FLOW, "\tTestAddr: %x0000\n", SweepPtr->TestAddrRJ16[CsIndex]);
NBPtr->WritePattern (NBPtr, SweepPtr->TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
}
}
} else {
SweepPtr->CsAddrValid[CsIndex] = FALSE;
}
} /// End Chip Select Loop
TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
//
/// return FALSE if no ChipSelects present.
//
return BanksPresent;
}
/* -----------------------------------------------------------------------------*/
/**
* Test Conditions for exiting the training loop, set the next delay value,
* and return status
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
*
* @return BOOLEAN
* TRUE - Continue to test with next delay setting
* FALSE - Exit training loop. Either the result has been found or
* end of delay range has been reached.
*/
BOOLEAN
STATIC
MemTContinueSweep (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
)
{
BOOLEAN Status;
Status = FALSE;
if (SweepPtr->ResultFound != 0xFFFF) {
Status = MemTSetNextDelay (TechPtr, SweepPtr);
}
TechPtr->NBPtr->FamilySpecificHook[RegAccessFence] (TechPtr->NBPtr, NULL);
return Status;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function sets the next delay value for each bytelane that needs to
* be advanced. It checks the bounds of the delay to see if we are at the
* end of the range. If we are to close to advance a whole step value, but
* not at the boundary, then we set the delay to the boundary.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
*
*/
BOOLEAN
STATIC
MemTSetNextDelay (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr
)
{
DIE_STRUCT *MCTPtr;
UINT8 i;
MCTPtr = TechPtr->NBPtr->MCTPtr;
//
///< Loop through bytelanes
//
for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && TechPtr->NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
//
/// Skip Bytelanes that have already reached the desired result
//
if ( (SweepPtr->ResultFound & ((UINT16)1 << i)) == 0) {
//
/// If a bytelane has reached the end, flag an error and exit
//
if (SweepPtr->TrnDelays[i] == SweepPtr->EndDelay) {
if ((SweepPtr->EndResult & ((UINT16) (1 << i))) != 0) {
MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
SweepPtr->Error = TRUE;
}
return FALSE;
}
//
/// If the Current delay value is less than a step away from EndDelay,
//
if ( ABS (SweepPtr->EndDelay - SweepPtr->TrnDelays[i]) < ABS (SweepPtr->Step)) {
/// set to EndDelay.
//
SweepPtr->TrnDelays[i] = SweepPtr->EndDelay;
} else {
//
/// Otherwise, add the step value to it
SweepPtr->TrnDelays[i] = SweepPtr->TrnDelays[i] + SweepPtr->Step;
}
//
/// Set InsertionDelayMsk bit if Delay < 0 for this bytelane
//
if (SweepPtr->TrnDelays[i] < 0) {
SweepPtr->InsertionDelayMsk |= ((UINT16) 1 << i);
} else {
SweepPtr->InsertionDelayMsk &= ~((UINT16) 1 << i);
}
//
/// Write the scaled value to the Delay Register
//
TechPtr->SetDQSDelayCSR (TechPtr, i, MemTScaleDelayVal (TechPtr, SweepPtr->TrnDelays[i]));
}
}
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function accepts a delay value in 32nd of a UI and converts it to an
* actual register value, taking into consideration NB type, rd/wr,
* and frequency.
*
* Delay = (Min + (Delay * ( (Max - Min) / TRN_DELAY_MAX) )) & Mask
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in] *Delay - INT8 of delay value;
*
* @return UINT8 of the adjusted delay value
*/
UINT8
STATIC
MemTScaleDelayVal (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN INT8 Delay
)
{
MEM_NB_BLOCK *NBPtr;
TRN_DLY_PARMS Parms;
TRN_DLY_TYPE DelayType;
UINT8 NewDelay;
INT8 Factor;
INT8 ScaledDelay;
NBPtr = TechPtr->NBPtr;
//
// Determine Delay Type, Get Delay Parameters, and return scaled Delay value
//
DelayType = (TechPtr->Direction == DQS_WRITE_DIR) ? AccessWrDatDly : AccessRdDqsDly;
NBPtr->GetTrainDlyParms (NBPtr, DelayType, &Parms);
Factor = ((Parms.Max - Parms.Min) / TRN_DELAY_MAX);
ScaledDelay = Delay * Factor;
NewDelay = (Parms.Min + ScaledDelay) & Parms.Mask;
return NewDelay;
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function calculates the Center of the Data eye for the specified byte lane
* and stores its DQS Delay value for reference.
*
* @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
* @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
* @param[in] ByteLane - Bytelane number being targeted
*
*/
BOOLEAN
STATIC
MemTDataEyeSave (
IN OUT MEM_TECH_BLOCK *TechPtr,
IN OUT SWEEP_INFO *SweepPtr,
IN UINT8 ByteLane
)
{
MEM_NB_BLOCK *NBPtr;
UINT8 EyeCenter;
UINT8 DlyMin;
UINT8 DlyMax;
UINT8 EyeWidth;
UINT8 Dimm;
CH_DEF_STRUCT *ChanPtr;
NBPtr = TechPtr->NBPtr;
ChanPtr = NBPtr->ChannelPtr;
ASSERT (ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8));
//
// Calculate Data Eye edges, Width, and Center in real terms.
//
if (TechPtr->Direction == DQS_READ_DIR) {
DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMinDlys[ByteLane]);
DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMaxDlys[ByteLane]);
EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->RdDqsMaxDlys[ByteLane] - ChanPtr->RdDqsMinDlys[ByteLane]));
EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->RdDqsMinDlys[ByteLane] + ChanPtr->RdDqsMaxDlys[ByteLane] + 1) / 2));
if (!NBPtr->FamilySpecificHook[RdDqsDlyRestartChk] (NBPtr, &EyeCenter)) {
return FALSE;
}
ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
NBPtr->FamilySpecificHook[ForceRdDqsPhaseB] (NBPtr, &EyeCenter);
} else {
DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMinDlys[ByteLane]);
DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMaxDlys[ByteLane]);
EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->WrDatMaxDlys[ByteLane] - ChanPtr->WrDatMinDlys[ByteLane]));
EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->WrDatMinDlys[ByteLane] + ChanPtr->WrDatMaxDlys[ByteLane] + 1) / 2));
ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
}
//
// Flag error for small window.
//
if (EyeWidth < MemTScaleDelayVal (TechPtr, NBPtr->MinDataEyeWidth (NBPtr))) {
TechPtr->SmallDqsPosWindow = TRUE;
SweepPtr->Error = TRUE;
NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrSmallWindBLError] (NBPtr, NULL);
}
IDS_HDT_CONSOLE (MEM_FLOW, " %02x %02x %02x %02x", DlyMin, DlyMax, EyeWidth, EyeCenter);
TechPtr->SetDQSDelayCSR (TechPtr, ByteLane, EyeCenter);
if (!SweepPtr->Error) {
TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
}
TechPtr->DqsRdWrPosSaved |= 0xFE00;
Dimm = (TechPtr->ChipSel / 2) * TechPtr->DlyTableWidth () + ByteLane;
if (TechPtr->Direction == DQS_READ_DIR) {
ChanPtr->RdDqsDlys[Dimm] = EyeCenter;
} else {
ChanPtr->WrDatDlys[Dimm] = EyeCenter + ChanPtr->WrDqsDlys[Dimm];
}
return TRUE;
}