/* $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; }