7 * DQS R/W position training utilizing Data Eye Edge Detection for optimization
9 * @xrefitem bom "File Content Label" "Release Content"
11 * @e sub-project: (Mem/Tech)
12 * @e \$Revision: 57884 $ @e \$Date: 2011-08-15 10:42:12 -0600 (Mon, 15 Aug 2011) $
15 /*****************************************************************************
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 * its contributors may be used to endorse or promote products derived
29 * from this software without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * ***************************************************************************
47 *----------------------------------------------------------------------------
50 *----------------------------------------------------------------------------
59 #include "AdvancedApi.h"
60 #include "GeneralServices.h"
62 #include "heapManager.h"
68 #include "mttEdgeDetect.h"
69 #include "OptionMemory.h"
73 RDATA_GROUP (G1_PEICC)
75 #define FILECODE PROC_MEM_TECH_MTTEDGEDETECT_FILECODE
76 /*----------------------------------------------------------------------------
77 * DEFINITIONS AND MACROS
79 *----------------------------------------------------------------------------
83 #define LAST_DELAY (-128)
89 /*----------------------------------------------------------------------------
90 * TYPEDEFS AND STRUCTURES
92 *----------------------------------------------------------------------------
95 /*----------------------------------------------------------------------------
96 * PROTOTYPES OF LOCAL FUNCTIONS
98 *----------------------------------------------------------------------------
102 * Sweep Table For Byte Training without insertion delay
105 DQS_POS_SWEEP_TABLE SweepTableByte[] =
107 // Begin End Inc/Dec Step EndResult Edge
108 { 0x00, 0x1F, INC_DELAY, 4, 0xFFFF, LEFT_EDGE}, /// For Left Edge, start from 0 and Increment to 0x1F by 4 until all PASS
109 { LAST_DELAY, 0x00, DEC_DELAY, -1, 0xFE00, LEFT_EDGE}, /// Then go back down to 0x00 by 1 until all FAIL
110 { 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
111 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
114 * Sweep Table For Byte Training with insertion delay
117 DQS_POS_SWEEP_TABLE InsSweepTableByte[] =
119 // Begin End Inc/Dec Step EndResult Edge
120 { 0x00, -0x20, DEC_DELAY, -4, 0xFE00, LEFT_EDGE}, /// For Left Edge, start from 0 and Decrement to -0x20 by -4 until all FAIL
121 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFFFF, LEFT_EDGE}, /// Then go back up to 0x1F by 1 until all PASS
122 { 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
123 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
128 MemTTrainDQSRdWrEdgeDetect (
129 IN OUT MEM_TECH_BLOCK *TechPtr
134 MemTInitTestPatternAddress (
135 IN OUT MEM_TECH_BLOCK *TechPtr,
136 IN OUT SWEEP_INFO *SweepPtr
142 IN OUT MEM_TECH_BLOCK *TechPtr,
143 IN OUT SWEEP_INFO *SweepPtr
149 IN OUT MEM_TECH_BLOCK *TechPtr,
150 IN OUT SWEEP_INFO *SweepPtr
156 IN OUT MEM_TECH_BLOCK *TechPtr,
163 IN OUT MEM_TECH_BLOCK *TechPtr,
164 IN OUT SWEEP_INFO *SweepPtr,
168 /*----------------------------------------------------------------------------
171 *----------------------------------------------------------------------------
173 extern MEM_FEAT_TRAIN_SEQ memTrainSequenceDDR3[];
174 /* -----------------------------------------------------------------------------*/
177 * This function executes DQS position training for all a Memory channel using
178 * the Edge Detection algorithm.
180 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
185 MemTTrainDQSEdgeDetectSw (
186 IN OUT MEM_TECH_BLOCK *TechPtr
193 NBPtr = TechPtr->NBPtr;
194 TechPtr->TrainingType = TRN_DQS_POSITION;
196 // Initialize the Pattern
198 if (AGESA_SUCCESS == NBPtr->TrainingPatternInit (NBPtr)) {
200 // Setup hardware training engine (if applicable)
202 NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType);
204 // Start Edge Detection
206 Status |= MemTTrainDQSRdWrEdgeDetect (TechPtr);
208 // Finalize the Pattern
210 Status &= (AGESA_SUCCESS == NBPtr->TrainingPatternFinalize (NBPtr));
215 /*----------------------------------------------------------------------------
218 *----------------------------------------------------------------------------
221 /* -----------------------------------------------------------------------------*/
224 * This Executes Read DQS and Write Data Position training on a chip select pair
225 * using the Edge Detection algorithm.
227 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
229 * @return TRUE - No Errors occurred
230 * @return FALSE - Errors occurred
236 MemTTrainDQSRdWrEdgeDetect (
237 IN OUT MEM_TECH_BLOCK *TechPtr
240 MEM_DATA_STRUCT *MemPtr;
252 NBPtr = TechPtr->NBPtr;
253 MemPtr = NBPtr->MemPtr;
254 TimesRetrain = DEFAULT_TRAINING_TIMES;
255 IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader);
257 // Set environment settings before training
259 IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Read/Write Data Eye Edge Detection.\n");
260 MemTBeginTraining (TechPtr);
262 // Do Rd DQS /Wr Data Position training for all Dcts/Chipselects
264 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
265 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
266 NBPtr->SwitchDCT (NBPtr, Dct);
270 CSPerChannel = NBPtr->CSPerChannel (NBPtr);
271 CsPerDelay = NBPtr->CSPerDelay (NBPtr);
272 for (ChipSel = 0; ChipSel < CSPerChannel; ChipSel = ChipSel + CsPerDelay ) {
274 // Init Bit Error Masks
276 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
278 (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
280 if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
281 ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
283 TechPtr->ChipSel = ChipSel;
284 IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
285 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tIncrease WrDat, Train RdDqs:\n");
287 TechPtr->DqsRdWrPosSaved = 0;
289 // Use a list of Approximate Write Data delay values and train Read DQS Position for
290 // each until a valid Data eye is found.
294 NBPtr->FamilySpecificHook[InitializeRxEnSeedlessTraining] (NBPtr, NBPtr);
295 ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) {
297 while (NBPtr->GetApproximateWriteDatDelay (NBPtr, i, &WrDqDelay)) {
298 TechPtr->SmallDqsPosWindow = FALSE;
300 // Set Write Delay approximation
302 TechPtr->Direction = DQS_WRITE_DIR;
303 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrite Delay: %02x", WrDqDelay);
304 MemTSetDQSDelayAllCSR (TechPtr, WrDqDelay);
306 // Attempt Read Training
308 TechPtr->Direction = DQS_READ_DIR;
309 Status = memTrainSequenceDDR3[NBPtr->TrainingSequenceIndex].MemTechFeatBlock->RdPosTraining (TechPtr);
312 // If Read DQS Training was successful, Train Write Data (DQ) Position
314 TechPtr->DqsRdWrPosSaved = 0;
315 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tTrain WrDat:\n\n");
316 TechPtr->Direction = DQS_WRITE_DIR;
317 if (NBPtr->FamilySpecificHook[BeforeWrDatTrn] (NBPtr, &ChipSel)) {
318 Status = MemTTrainDQSEdgeDetect (TechPtr);
324 ERROR_HANDLE_RETRAIN_END ((Status == FALSE), TimesFail)
328 // If we went through the table, Fail.
330 if (Status == FALSE) {
331 // On training failure, check and record whether training fails due to small window or no window
332 if (TechPtr->SmallDqsPosWindow) {
333 NBPtr->MCTPtr->ErrStatus[EsbSmallDqs] = TRUE;
335 NBPtr->MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
338 SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
339 if (TechPtr->Direction == DQS_READ_DIR) {
340 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_RD_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
342 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_WR_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
344 NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ChipSel;
345 // If the even chip select failed training always fail the odd, if present.
346 if ((ChipSel & 0x01) == 0) {
347 if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << (ChipSel + 1))) {
348 NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << (ChipSel + 1);
351 if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
357 // Clear Bit Error Masks if these CS will not be trained.
359 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
361 (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
362 &NBPtr->MemPtr->StdHeader);
367 // Restore environment settings after training
369 MemTEndTraining (TechPtr);
370 IDS_HDT_CONSOLE (MEM_FLOW, "End Read/Write Data Eye Edge Detection\n\n");
371 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
374 /* -----------------------------------------------------------------------------*/
377 * This function executes DQS position training for both read and write, using
378 * the Edge Detection Algorithm. This method searches for the beginning and end
379 * of the Data Eye with out scanning every DSQ delay value. The following is a
380 * detailed description of the algorithm:
382 * Four-Stage Data Eye Sweep
384 * -Search starts at Delay value of 0.
385 * -Search left in steps of 4/32UI looking for all Byte lanes Passing. Left from zero rolls over to a negative value.
386 * -Negative values are translated to the high end of the delay range, but using Insertion delay comparison.
387 * -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
388 * -Switch to search right in steps of 1/32UI looking for fail.
389 * -For each lane, starting delay for 1/32 sweep right is first passing delay from 4/32 sweep left.
390 * -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
391 * -Search right until all byte lanes have failed
392 * -For each lane, right edge used by BIOS will be first failing delay value minus 1/32
395 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
397 * @return TRUE - All bytelanes pass
398 * @return FALSE - Some bytelanes fail
401 MemTTrainDQSEdgeDetect (
402 IN OUT MEM_TECH_BLOCK *TechPtr
407 DQS_POS_SWEEP_TABLE *SweepTablePtr;
408 UINT8 SweepTableSize;
409 SWEEP_INFO SweepData;
411 UINT16 CurrentResult;
412 UINT16 AlignedResult;
421 // Initialize Object Pointers
423 NBPtr = TechPtr->NBPtr;
424 MCTPtr = NBPtr->MCTPtr;
426 // Initialize stack variables
428 LibAmdMemFill (&SweepData, 0, sizeof (SWEEP_INFO), &NBPtr->MemPtr->StdHeader);
430 /// Get Pointer to Sweep Table
432 if (TechPtr->Direction == DQS_READ_DIR) {
433 SweepTablePtr = InsSweepTableByte;
434 SweepTableSize = GET_SIZE_OF (InsSweepTableByte);
436 SweepTablePtr = SweepTableByte;
437 SweepTableSize = GET_SIZE_OF (SweepTableByte);
440 // Get number of CS to train
442 CsPerDelay = NBPtr->CSPerDelay (NBPtr);
444 /// Set up the test Pattern, exit if no Memory
446 if (MemTInitTestPatternAddress (TechPtr, &SweepData) == FALSE) {
447 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (TechPtr->ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
449 (MAX_BYTELANES_PER_CHANNEL * CsPerDelay),
450 &NBPtr->MemPtr->StdHeader);
456 SweepData.Error = FALSE;
457 NBPtr->FamilySpecificHook[InitialzeRxEnSeedlessByteLaneError] (NBPtr, NBPtr);
459 /// Process Sweep table, using entries from the table to determine Starting and Ending Delays
460 /// as well as the Step size and criteria for evaluating whether the correct result is found.
462 /// Delay values at this level are an abstract range of values which gets scaled to the actual value
463 /// before it is written to the hardware. This allows NB specific code to handle the scaling as a
464 /// function of frequency or other conditions.
466 for (StageIndex = 0; (StageIndex < SweepTableSize) && (SweepData.Error == FALSE); StageIndex++) {
468 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSTAGE: %d\t", StageIndex);
470 /// Initialize SweepData variables
472 SweepData.BeginDelay = SweepTablePtr->BeginDelay;
473 SweepData.EndDelay = SweepTablePtr->EndDelay;
474 SweepData.Step = 0; /// Step Value will be 0 to start.
475 SweepData.EndResult = SweepTablePtr->EndResult;
476 if (!(MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining])) {
477 SweepData.EndResult |= 0x0100;
479 SweepData.Edge = SweepTablePtr->MinMax;
480 SweepData.InsertionDelayMsk = 0;
481 SweepData.ResultFound = 0x0000;
483 // Set Training Delays Pointer.
485 if (TechPtr->Direction == DQS_READ_DIR) {
486 SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->RdDqsMaxDlys : NBPtr->ChannelPtr->RdDqsMinDlys);
488 SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->WrDatMaxDlys : NBPtr->ChannelPtr->WrDatMinDlys);
491 /// Set initial TrnDelay Values if necessary
493 IDS_HDT_CONSOLE (MEM_FLOW, "Sweeping %s DQS, %s from ", (TechPtr->Direction == DQS_READ_DIR) ?"Read":"Write", (SweepTablePtr->ScanDir == INC_DELAY) ? "incrementing":"decrementing");
494 if (SweepData.BeginDelay != LAST_DELAY) {
495 IDS_HDT_CONSOLE (MEM_FLOW, "%02x", (UINT16) MemTScaleDelayVal (TechPtr, SweepData.BeginDelay));
496 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); i++) {
497 SweepData.TrnDelays[i] = SweepData.BeginDelay;
500 IDS_HDT_CONSOLE (MEM_FLOW, "Current Delay");
501 SweepData.Step = SweepTablePtr->Step;
503 IDS_HDT_CONSOLE (MEM_FLOW, " by %02x, until all bytelanes %s.\n\n", (UINT16) MemTScaleDelayVal (TechPtr, ABS (SweepTablePtr->Step)), (SweepData.EndResult == 0xFFFF)?"PASS":"FAIL");
505 //-------------------------------------------------------------------
507 // MemTContinueSweep function returns false to break out of loop.
508 // There are no other breaks out of this loop.
509 //-------------------------------------------------------------------
510 while (MemTContinueSweep (TechPtr, &SweepData)) {
511 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : 08 07 06 05 04 03 02 01 00\n");
512 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDQS Delays : %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
513 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[8]),
514 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[7]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[6]),
515 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[5]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[4]),
516 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[3]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[2]),
517 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[1]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[0])
522 SweepData.Step = SweepTablePtr->Step;
523 CurrentResult = 0xFFFF;
525 /// Chip Select Loop: Test the Pattern for all populated CS that are controlled by the current delay registers
527 for (CsIndex = 0; CsIndex < CsPerDelay ; CsIndex++, TechPtr->ChipSel++) {
528 ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
529 ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
530 if (SweepData.CsAddrValid[CsIndex] == TRUE) {
532 /// If this is a Write Dqs sweep, Write the pattern now.
534 if (TechPtr->Direction == DQS_WRITE_DIR) {
535 NBPtr->WritePattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
538 /// Read the Pattern Back
540 NBPtr->ReadPattern (NBPtr, TechPtr->TestBufPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
542 /// Compare the Pattern and Merge the results using InsertionDelayMsk
544 AlignedResult = NBPtr->CompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
545 CurrentResult &= AlignedResult | SweepData.InsertionDelayMsk;
546 if (SweepData.InsertionDelayMsk != 0) {
547 OffsetResult = NBPtr->InsDlyCompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
548 CurrentResult &= (OffsetResult | (~SweepData.InsertionDelayMsk));
551 /// Flush the Test Pattern
553 NBPtr->FlushPattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
554 NBPtr->FamilySpecificHook[ResetRxFifoPtr] (NBPtr, NBPtr);
556 } /// End Chip Select Loop
557 TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
558 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResult : %c %c %c %c %c %c %c %c %c \n",
559 (SweepData.ResultFound & ((UINT16) 1 << (8))) ? ' ':(CurrentResult & ((UINT16) 1 << (8))) ? 'P':'.',
560 (SweepData.ResultFound & ((UINT16) 1 << (7))) ? ' ':(CurrentResult & ((UINT16) 1 << (7))) ? 'P':'.',
561 (SweepData.ResultFound & ((UINT16) 1 << (6))) ? ' ':(CurrentResult & ((UINT16) 1 << (6))) ? 'P':'.',
562 (SweepData.ResultFound & ((UINT16) 1 << (5))) ? ' ':(CurrentResult & ((UINT16) 1 << (5))) ? 'P':'.',
563 (SweepData.ResultFound & ((UINT16) 1 << (4))) ? ' ':(CurrentResult & ((UINT16) 1 << (4))) ? 'P':'.',
564 (SweepData.ResultFound & ((UINT16) 1 << (3))) ? ' ':(CurrentResult & ((UINT16) 1 << (3))) ? 'P':'.',
565 (SweepData.ResultFound & ((UINT16) 1 << (2))) ? ' ':(CurrentResult & ((UINT16) 1 << (2))) ? 'P':'.',
566 (SweepData.ResultFound & ((UINT16) 1 << (1))) ? ' ':(CurrentResult & ((UINT16) 1 << (1))) ? 'P':'.',
567 (SweepData.ResultFound & ((UINT16) 1 << (0))) ? ' ':(CurrentResult & ((UINT16) 1 << (0))) ? 'P':'.'
570 /// Merge current result into cumulative result and make it positive.
572 SweepData.ResultFound |= ~(CurrentResult ^ SweepData.EndResult);
574 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResultFound : %c %c %c %c %c %c %c %c %c \n\n",
575 (SweepData.ResultFound & ((UINT16) 1 << (8))) ? 'Y':' ',
576 (SweepData.ResultFound & ((UINT16) 1 << (7))) ? 'Y':' ',
577 (SweepData.ResultFound & ((UINT16) 1 << (6))) ? 'Y':' ',
578 (SweepData.ResultFound & ((UINT16) 1 << (5))) ? 'Y':' ',
579 (SweepData.ResultFound & ((UINT16) 1 << (4))) ? 'Y':' ',
580 (SweepData.ResultFound & ((UINT16) 1 << (3))) ? 'Y':' ',
581 (SweepData.ResultFound & ((UINT16) 1 << (2))) ? 'Y':' ',
582 (SweepData.ResultFound & ((UINT16) 1 << (1))) ? 'Y':' ',
583 (SweepData.ResultFound & ((UINT16) 1 << (0))) ? 'Y':' '
585 } /// End of Delay Sweep
587 /// Place Final delay values at last passing delay.
589 if (SweepData.ResultFound == 0xFFFF) {
590 if ( ABS (SweepData.Step) == 1) {
591 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
592 if ((SweepData.EndResult & ((UINT16) (1 << i))) == 0) {
593 SweepData.TrnDelays[i] = SweepData.TrnDelays[i] - SweepData.Step;
599 // Update Pointer to Sweep Table
602 }///End of Edge Detect loop
604 /// If No Errors are detected, Calculate Data Eye Width and Center
606 if (SweepData.Error == FALSE) {
607 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tData Eye Results:\n\n");
608 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tByte Left Right\n");
609 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane Edge Edge Width Center\n");
610 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
611 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t %0d", i);
612 TechPtr->Bytelane = i;
613 if (!MemTDataEyeSave (TechPtr, &SweepData, i)) {
616 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
617 if (SweepData.Error == TRUE) {
623 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t--DATA EYE NOT FOUND--\n\n");
624 NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrNoWindBLError] (NBPtr, &SweepData);
629 /* -----------------------------------------------------------------------------*/
632 * Initialize the Test Pattern Address for two chip selects and, if this
633 * is a Write Data Eye, write the initial test pattern.
635 * Test Address is stored in the Sweep info struct. If Memory is not present
636 * then return with False.
638 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
639 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
642 * TRUE - Memory is present
643 * FALSE - No memory present on this Chip Select pair.
649 MemTInitTestPatternAddress (
650 IN OUT MEM_TECH_BLOCK *TechPtr,
651 IN OUT SWEEP_INFO *SweepPtr
658 BOOLEAN BanksPresent;
660 NBPtr = TechPtr->NBPtr;
661 BanksPresent = FALSE;
662 CsPerDelay = NBPtr->CSPerDelay (NBPtr);
663 ChipSel = TechPtr->ChipSel;
664 for (CsIndex = 0; CsIndex < CsPerDelay; ChipSel++, CsIndex++, TechPtr->ChipSel++) {
665 ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
666 ASSERT (ChipSel < MAX_CS_PER_CHANNEL);
667 ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
669 /// If memory is present on this cs, get the test addr
671 if (NBPtr->GetSysAddr (NBPtr, ChipSel, &(SweepPtr->TestAddrRJ16[CsIndex]))) {
672 if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
674 SweepPtr->CsAddrValid[CsIndex] = TRUE;
676 /// If this is a Read Dqs sweep, Write the pattern now.
678 if (TechPtr->Direction == DQS_READ_DIR) {
679 IDS_HDT_CONSOLE (MEM_FLOW, "\tTestAddr: %x0000\n", SweepPtr->TestAddrRJ16[CsIndex]);
680 NBPtr->WritePattern (NBPtr, SweepPtr->TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
684 SweepPtr->CsAddrValid[CsIndex] = FALSE;
686 } /// End Chip Select Loop
687 TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
689 /// return FALSE if no ChipSelects present.
694 /* -----------------------------------------------------------------------------*/
696 * Test Conditions for exiting the training loop, set the next delay value,
699 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
700 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
703 * TRUE - Continue to test with next delay setting
704 * FALSE - Exit training loop. Either the result has been found or
705 * end of delay range has been reached.
710 IN OUT MEM_TECH_BLOCK *TechPtr,
711 IN OUT SWEEP_INFO *SweepPtr
716 if (SweepPtr->ResultFound != 0xFFFF) {
717 Status = MemTSetNextDelay (TechPtr, SweepPtr);
719 TechPtr->NBPtr->FamilySpecificHook[RegAccessFence] (TechPtr->NBPtr, NULL);
723 /* -----------------------------------------------------------------------------*/
726 * This function sets the next delay value for each bytelane that needs to
727 * be advanced. It checks the bounds of the delay to see if we are at the
728 * end of the range. If we are to close to advance a whole step value, but
729 * not at the boundary, then we set the delay to the boundary.
731 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
732 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
739 IN OUT MEM_TECH_BLOCK *TechPtr,
740 IN OUT SWEEP_INFO *SweepPtr
746 MCTPtr = TechPtr->NBPtr->MCTPtr;
748 ///< Loop through bytelanes
750 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && TechPtr->NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
752 /// Skip Bytelanes that have already reached the desired result
754 if ( (SweepPtr->ResultFound & ((UINT16)1 << i)) == 0) {
756 /// If a bytelane has reached the end, flag an error and exit
758 if (SweepPtr->TrnDelays[i] == SweepPtr->EndDelay) {
759 if ((SweepPtr->EndResult & ((UINT16) (1 << i))) != 0) {
760 MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
761 SweepPtr->Error = TRUE;
766 /// If the Current delay value is less than a step away from EndDelay,
768 if ( ABS (SweepPtr->EndDelay - SweepPtr->TrnDelays[i]) < ABS (SweepPtr->Step)) {
771 SweepPtr->TrnDelays[i] = SweepPtr->EndDelay;
774 /// Otherwise, add the step value to it
775 SweepPtr->TrnDelays[i] = SweepPtr->TrnDelays[i] + SweepPtr->Step;
778 /// Set InsertionDelayMsk bit if Delay < 0 for this bytelane
780 if (SweepPtr->TrnDelays[i] < 0) {
781 SweepPtr->InsertionDelayMsk |= ((UINT16) 1 << i);
783 SweepPtr->InsertionDelayMsk &= ~((UINT16) 1 << i);
786 /// Write the scaled value to the Delay Register
788 TechPtr->SetDQSDelayCSR (TechPtr, i, MemTScaleDelayVal (TechPtr, SweepPtr->TrnDelays[i]));
793 /* -----------------------------------------------------------------------------*/
796 * This function accepts a delay value in 32nd of a UI and converts it to an
797 * actual register value, taking into consideration NB type, rd/wr,
800 * Delay = (Min + (Delay * ( (Max - Min) / TRN_DELAY_MAX) )) & Mask
802 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
803 * @param[in] *Delay - INT8 of delay value;
805 * @return UINT8 of the adjusted delay value
810 IN OUT MEM_TECH_BLOCK *TechPtr,
816 TRN_DLY_TYPE DelayType;
821 NBPtr = TechPtr->NBPtr;
823 // Determine Delay Type, Get Delay Parameters, and return scaled Delay value
825 DelayType = (TechPtr->Direction == DQS_WRITE_DIR) ? AccessWrDatDly : AccessRdDqsDly;
826 NBPtr->GetTrainDlyParms (NBPtr, DelayType, &Parms);
827 Factor = ((Parms.Max - Parms.Min) / TRN_DELAY_MAX);
828 ScaledDelay = Delay * Factor;
829 NewDelay = (Parms.Min + ScaledDelay) & Parms.Mask;
837 /* -----------------------------------------------------------------------------*/
840 * This function calculates the Center of the Data eye for the specified byte lane
841 * and stores its DQS Delay value for reference.
843 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
844 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
845 * @param[in] ByteLane - Bytelane number being targeted
851 IN OUT MEM_TECH_BLOCK *TechPtr,
852 IN OUT SWEEP_INFO *SweepPtr,
862 CH_DEF_STRUCT *ChanPtr;
864 NBPtr = TechPtr->NBPtr;
865 ChanPtr = NBPtr->ChannelPtr;
867 ASSERT (ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8));
869 // Calculate Data Eye edges, Width, and Center in real terms.
871 if (TechPtr->Direction == DQS_READ_DIR) {
872 DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMinDlys[ByteLane]);
873 DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMaxDlys[ByteLane]);
874 EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->RdDqsMaxDlys[ByteLane] - ChanPtr->RdDqsMinDlys[ByteLane]));
875 EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->RdDqsMinDlys[ByteLane] + ChanPtr->RdDqsMaxDlys[ByteLane] + 1) / 2));
876 if (!NBPtr->FamilySpecificHook[RdDqsDlyRestartChk] (NBPtr, &EyeCenter)) {
879 ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
880 ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
881 NBPtr->FamilySpecificHook[ForceRdDqsPhaseB] (NBPtr, &EyeCenter);
883 DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMinDlys[ByteLane]);
884 DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMaxDlys[ByteLane]);
885 EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->WrDatMaxDlys[ByteLane] - ChanPtr->WrDatMinDlys[ByteLane]));
886 EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->WrDatMinDlys[ByteLane] + ChanPtr->WrDatMaxDlys[ByteLane] + 1) / 2));
887 ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
888 ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
891 // Flag error for small window.
893 if (EyeWidth < MemTScaleDelayVal (TechPtr, NBPtr->MinDataEyeWidth (NBPtr))) {
894 TechPtr->SmallDqsPosWindow = TRUE;
895 SweepPtr->Error = TRUE;
896 NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrSmallWindBLError] (NBPtr, NULL);
899 IDS_HDT_CONSOLE (MEM_FLOW, " %02x %02x %02x %02x", DlyMin, DlyMax, EyeWidth, EyeCenter);
901 TechPtr->SetDQSDelayCSR (TechPtr, ByteLane, EyeCenter);
902 if (!SweepPtr->Error) {
903 TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
905 TechPtr->DqsRdWrPosSaved |= 0xFE00;
907 Dimm = (TechPtr->ChipSel / 2) * TechPtr->DlyTableWidth () + ByteLane;
908 if (TechPtr->Direction == DQS_READ_DIR) {
909 ChanPtr->RdDqsDlys[Dimm] = EyeCenter;
911 ChanPtr->WrDatDlys[Dimm] = EyeCenter + ChanPtr->WrDqsDlys[Dimm];