blob: 1706b7ef362e85d87e640ec9b8855f8e51e13cd0 [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mttEdgeDetect.c
6 *
7 * DQS R/W position training utilizing Data Eye Edge Detection for optimization
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/Tech)
12 * @e \$Revision: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $
13 *
14 **/
15/*****************************************************************************
16*
Siyuan Wang641f00c2013-06-08 11:50:55 +080017 * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
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.
30 *
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.
zbao7d94cf92012-07-02 14:19:14 +080041* ***************************************************************************
42*
43*/
44
45/*
46 *----------------------------------------------------------------------------
47 * MODULES USED
48 *
49 *----------------------------------------------------------------------------
50 */
51
52
53
54
55
56#include "AGESA.h"
57#include "amdlib.h"
58#include "AdvancedApi.h"
59#include "GeneralServices.h"
60#include "Ids.h"
61#include "heapManager.h"
62#include "mm.h"
63#include "mn.h"
64#include "mu.h"
65#include "mt.h"
66#include "mport.h"
67#include "mttEdgeDetect.h"
68#include "OptionMemory.h"
69#include "merrhdl.h"
70#include "Filecode.h"
71CODE_GROUP (G1_PEICC)
72RDATA_GROUP (G1_PEICC)
73
74#define FILECODE PROC_MEM_TECH_MTTEDGEDETECT_FILECODE
75/*----------------------------------------------------------------------------
76 * DEFINITIONS AND MACROS
77 *
78 *----------------------------------------------------------------------------
79 */
80
81
82#define LAST_DELAY (-128)
83#define INC_DELAY 1
84#define DEC_DELAY 0
85
86
87
88/*----------------------------------------------------------------------------
89 * TYPEDEFS AND STRUCTURES
90 *
91 *----------------------------------------------------------------------------
92 */
93
94/*----------------------------------------------------------------------------
95 * PROTOTYPES OF LOCAL FUNCTIONS
96 *
97 *----------------------------------------------------------------------------
98 */
99
100/**
101 * Sweep Table For Byte Training without insertion delay
102 *
103*/
Arthur Heymans8d3640d2022-05-16 12:27:36 +0200104CONST DQS_POS_SWEEP_TABLE SweepTableByte[] =
zbao7d94cf92012-07-02 14:19:14 +0800105{
106 // Begin End Inc/Dec Step EndResult Edge
107 { 0x00, 0x1F, INC_DELAY, 4, 0xFFFF, LEFT_EDGE}, /// For Left Edge, start from 0 and Increment to 0x1F by 4 until all PASS
108 { LAST_DELAY, 0x00, DEC_DELAY, -1, 0xFE00, LEFT_EDGE}, /// Then go back down to 0x00 by 1 until all FAIL
109 { 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
110 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
111};
112/**
113 * Sweep Table For Byte Training with insertion delay
114 *
115*/
Arthur Heymans8d3640d2022-05-16 12:27:36 +0200116CONST DQS_POS_SWEEP_TABLE InsSweepTableByte[] =
zbao7d94cf92012-07-02 14:19:14 +0800117{
118 // Begin End Inc/Dec Step EndResult Edge
119 { 0x00, -0x20, DEC_DELAY, -4, 0xFE00, LEFT_EDGE}, /// For Left Edge, start from 0 and Decrement to -0x20 by -4 until all FAIL
120 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFFFF, LEFT_EDGE}, /// Then go back up to 0x1F by 1 until all PASS
121 { 0x1F, 0x00, DEC_DELAY, -4, 0xFFFF, RIGHT_EDGE}, /// For Right Edge, start from 0x1F down to 0 until all PASS.
122 { LAST_DELAY, 0x1F, INC_DELAY, 1, 0xFE00, RIGHT_EDGE} /// Then go back up by 1 until all FAIL.
123};
124
125BOOLEAN
126STATIC
127MemTTrainDQSRdWrEdgeDetect (
128 IN OUT MEM_TECH_BLOCK *TechPtr
129 );
130
131BOOLEAN
132STATIC
133MemTInitTestPatternAddress (
134 IN OUT MEM_TECH_BLOCK *TechPtr,
135 IN OUT SWEEP_INFO *SweepPtr
136 );
137
138BOOLEAN
139STATIC
140MemTContinueSweep (
141 IN OUT MEM_TECH_BLOCK *TechPtr,
142 IN OUT SWEEP_INFO *SweepPtr
143 );
144
145BOOLEAN
146STATIC
147MemTSetNextDelay (
148 IN OUT MEM_TECH_BLOCK *TechPtr,
149 IN OUT SWEEP_INFO *SweepPtr
150 );
151
152UINT8
153STATIC
154MemTScaleDelayVal (
155 IN OUT MEM_TECH_BLOCK *TechPtr,
156 IN INT8 Delay
157 );
158
159BOOLEAN
160STATIC
161MemTDataEyeSave (
162 IN OUT MEM_TECH_BLOCK *TechPtr,
163 IN OUT SWEEP_INFO *SweepPtr,
164 IN UINT8 ByteLane
165 );
166
167/*----------------------------------------------------------------------------
168 * EXPORTED FUNCTIONS
169 *
170 *----------------------------------------------------------------------------
171 */
172extern MEM_FEAT_TRAIN_SEQ memTrainSequenceDDR3[];
173/* -----------------------------------------------------------------------------*/
174/**
175 *
176 * This function executes DQS position training for all a Memory channel using
177 * the Edge Detection algorithm.
178 *
179 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
180 *
181 */
182
183BOOLEAN
184MemTTrainDQSEdgeDetectSw (
185 IN OUT MEM_TECH_BLOCK *TechPtr
186 )
187{
188 MEM_NB_BLOCK *NBPtr;
189 BOOLEAN Status;
190
191 Status = FALSE;
192 NBPtr = TechPtr->NBPtr;
193 TechPtr->TrainingType = TRN_DQS_POSITION;
194 //
195 // Initialize the Pattern
196 //
197 if (AGESA_SUCCESS == NBPtr->TrainingPatternInit (NBPtr)) {
198 //
199 // Setup hardware training engine (if applicable)
200 //
201 NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType);
202 //
203 // Start Edge Detection
204 //
205 Status |= MemTTrainDQSRdWrEdgeDetect (TechPtr);
206 //
207 // Finalize the Pattern
208 //
209 Status &= (AGESA_SUCCESS == NBPtr->TrainingPatternFinalize (NBPtr));
210 }
211 return Status;
212}
213
214/*----------------------------------------------------------------------------
215 * LOCAL FUNCTIONS
216 *
217 *----------------------------------------------------------------------------
218 */
219
220/* -----------------------------------------------------------------------------*/
221/**
222 *
223 * This Executes Read DQS and Write Data Position training on a chip select pair
224 * using the Edge Detection algorithm.
225 *
226 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
227 *
228 * @return TRUE - No Errors occurred
229 * @return FALSE - Errors occurred
230
231 */
232
233BOOLEAN
234STATIC
235MemTTrainDQSRdWrEdgeDetect (
236 IN OUT MEM_TECH_BLOCK *TechPtr
237 )
238{
239 MEM_DATA_STRUCT *MemPtr;
240 MEM_NB_BLOCK *NBPtr;
241 UINT8 WrDqDelay;
242 UINT8 Dct;
243 UINT8 ChipSel;
244 UINT8 i;
245 BOOLEAN Status;
246 UINT8 TimesFail;
247 UINT8 TimesRetrain;
248
249 NBPtr = TechPtr->NBPtr;
250 MemPtr = NBPtr->MemPtr;
251 TimesRetrain = DEFAULT_TRAINING_TIMES;
252 IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader);
253 //
254 // Set environment settings before training
255 //
256 IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Read/Write Data Eye Edge Detection.\n");
257 MemTBeginTraining (TechPtr);
258 //
259 // Do Rd DQS /Wr Data Position training for all Dcts/Chipselects
260 //
261 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
262 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
263 NBPtr->SwitchDCT (NBPtr, Dct);
264 //
265 // Chip Select Loop
266 //
267 TechPtr->RestartChipSel = -1;
268 for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) {
269 //
270 // Init Bit Error Masks
271 //
272 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
273 0xFF,
274 (MAX_BYTELANES_PER_CHANNEL * NBPtr->CsPerDelay),
275 &MemPtr->StdHeader);
276 if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) :
277 ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) {
278
279 TechPtr->ChipSel = ChipSel;
280 IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel);
281 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tIncrease WrDat, Train RdDqs:\n");
282
283 TechPtr->DqsRdWrPosSaved = 0;
284 //
285 // Use a list of Approximate Write Data delay values and train Read DQS Position for
286 // each until a valid Data eye is found.
287 //
288 Status = FALSE;
289 TimesFail = 0;
290 NBPtr->FamilySpecificHook[InitializeRxEnSeedlessTraining] (NBPtr, NBPtr);
291 ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) {
292 i = 0;
293 while (NBPtr->GetApproximateWriteDatDelay (NBPtr, i, &WrDqDelay)) {
294 TechPtr->SmallDqsPosWindow = FALSE;
295 //
296 // Set Write Delay approximation
297 //
298 TechPtr->Direction = DQS_WRITE_DIR;
299 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrite Delay: %02x", WrDqDelay);
300 MemTSetDQSDelayAllCSR (TechPtr, WrDqDelay);
301 //
302 // Attempt Read Training
303 //
304 TechPtr->Direction = DQS_READ_DIR;
305 Status = memTrainSequenceDDR3[NBPtr->TrainingSequenceIndex].MemTechFeatBlock->RdPosTraining (TechPtr);
306 if (Status) {
307 //
308 // If Read DQS Training was successful, Train Write Data (DQ) Position
309 //
310 TechPtr->DqsRdWrPosSaved = 0;
311 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tTrain WrDat:\n\n");
312 TechPtr->Direction = DQS_WRITE_DIR;
313 if (NBPtr->FamilySpecificHook[BeforeWrDatTrn] (NBPtr, &ChipSel)) {
314 Status = MemTTrainDQSEdgeDetect (TechPtr);
315 }
316 break;
317 }
318 i++;
319 }
320 ERROR_HANDLE_RETRAIN_END ((Status == FALSE), TimesFail)
321 }
322
323 //
324 // If we went through the table, Fail.
325 //
326 if (Status == FALSE) {
327 // On training failure, check and record whether training fails due to small window or no window
328 if (TechPtr->SmallDqsPosWindow) {
329 NBPtr->MCTPtr->ErrStatus[EsbSmallDqs] = TRUE;
330 } else {
331 NBPtr->MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
332 }
333
334 SetMemError (AGESA_ERROR, NBPtr->MCTPtr);
335 if (TechPtr->Direction == DQS_READ_DIR) {
336 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_RD_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
337 } else {
338 PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_WR_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
339 }
340 NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ChipSel;
341 // If the even chip select failed training always fail the odd, if present.
342 if (((ChipSel & 0x01) == 0) && (NBPtr->CsPerDelay == 2)) {
343 if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << (ChipSel + 1))) {
344 NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << (ChipSel + 1);
345 }
346 }
347 if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
348 ASSERT (FALSE);
349 }
350 }
351 } else {
352 //
353 // Clear Bit Error Masks if these CS will not be trained.
354 //
355 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
356 0x00,
357 (MAX_BYTELANES_PER_CHANNEL * NBPtr->CsPerDelay),
358 &NBPtr->MemPtr->StdHeader);
359 }
360 }
361 }
362 //
363 // Restore environment settings after training
364 //
365 MemTEndTraining (TechPtr);
366 IDS_HDT_CONSOLE (MEM_FLOW, "End Read/Write Data Eye Edge Detection\n\n");
367 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
368}
369
370/* -----------------------------------------------------------------------------*/
371/**
372 *
373 * This function executes DQS position training for both read and write, using
374 * the Edge Detection Algorithm. This method searches for the beginning and end
375 * of the Data Eye with out scanning every DSQ delay value. The following is a
376 * detailed description of the algorithm:
377 *
378 * Four-Stage Data Eye Sweep
379 *
380 * -Search starts at Delay value of 0.
381 * -Search left in steps of 4/32UI looking for all Byte lanes Passing. Left from zero rolls over to a negative value.
382 * -Negative values are translated to the high end of the delay range, but using Insertion delay comparison.
383 * -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
384 * -Switch to search right in steps of 1/32UI looking for fail.
385 * -For each lane, starting delay for 1/32 sweep right is first passing delay from 4/32 sweep left.
386 * -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
387 * -Search right until all byte lanes have failed
388 * -For each lane, right edge used by BIOS will be first failing delay value minus 1/32
389
390 *
391 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
392 *
393 * @return TRUE - All bytelanes pass
394 * @return FALSE - Some bytelanes fail
395*/
396BOOLEAN
397MemTTrainDQSEdgeDetect (
398 IN OUT MEM_TECH_BLOCK *TechPtr
399 )
400{
401 MEM_NB_BLOCK *NBPtr;
402 DIE_STRUCT *MCTPtr;
Arthur Heymans8d3640d2022-05-16 12:27:36 +0200403 CONST DQS_POS_SWEEP_TABLE *SweepTablePtr;
zbao7d94cf92012-07-02 14:19:14 +0800404 UINT8 SweepTableSize;
405 SWEEP_INFO SweepData;
406 BOOLEAN Status;
407 UINT16 CurrentResult;
408 UINT16 AlignedResult;
409 UINT16 OffsetResult;
410 UINT8 StageIndex;
411 UINT8 CsIndex;
412 UINT8 i;
413
414 Status = TRUE;
415 //
416 // Initialize Object Pointers
417 //
418 NBPtr = TechPtr->NBPtr;
419 MCTPtr = NBPtr->MCTPtr;
420 //
421 // Initialize stack variables
422 //
423 LibAmdMemFill (&SweepData, 0, sizeof (SWEEP_INFO), &NBPtr->MemPtr->StdHeader);
424 //
425 /// Get Pointer to Sweep Table
426 //
427 if (TechPtr->Direction == DQS_READ_DIR) {
428 SweepTablePtr = InsSweepTableByte;
429 SweepTableSize = GET_SIZE_OF (InsSweepTableByte);
430 } else {
431 SweepTablePtr = SweepTableByte;
432 SweepTableSize = GET_SIZE_OF (SweepTableByte);
433 }
434
435 //
436 /// Set up the test Pattern, exit if no Memory
437 //
438 if (MemTInitTestPatternAddress (TechPtr, &SweepData) == FALSE) {
439 LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (TechPtr->ChipSel * MAX_BYTELANES_PER_CHANNEL) ],
440 0,
441 (MAX_BYTELANES_PER_CHANNEL * NBPtr->CsPerDelay),
442 &NBPtr->MemPtr->StdHeader);
443 return FALSE;
444 }
445 //
446 // Clear Error Flag
447 //
448 SweepData.Error = FALSE;
449 NBPtr->FamilySpecificHook[InitialzeRxEnSeedlessByteLaneError] (NBPtr, NBPtr);
450 //
451 /// Process Sweep table, using entries from the table to determine Starting and Ending Delays
452 /// as well as the Step size and criteria for evaluating whether the correct result is found.
453 ///
454 /// Delay values at this level are an abstract range of values which gets scaled to the actual value
455 /// before it is written to the hardware. This allows NB specific code to handle the scaling as a
456 /// function of frequency or other conditions.
457 //
458 for (StageIndex = 0; (StageIndex < SweepTableSize) && (SweepData.Error == FALSE); StageIndex++) {
459
460 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSTAGE: %d\t", StageIndex);
461 //
462 /// Initialize SweepData variables
463 //
464 SweepData.BeginDelay = SweepTablePtr->BeginDelay;
465 SweepData.EndDelay = SweepTablePtr->EndDelay;
466 SweepData.Step = 0; /// Step Value will be 0 to start.
467 SweepData.EndResult = SweepTablePtr->EndResult;
468 if (!(MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining])) {
469 SweepData.EndResult |= 0x0100;
470 }
471 SweepData.Edge = SweepTablePtr->MinMax;
472 SweepData.InsertionDelayMsk = 0;
473 SweepData.ResultFound = 0x0000;
474 //
475 // Set Training Delays Pointer.
476 //
477 if (TechPtr->Direction == DQS_READ_DIR) {
478 SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->RdDqsMaxDlys : NBPtr->ChannelPtr->RdDqsMinDlys);
479 } else {
480 SweepData.TrnDelays = (INT8 *) ((SweepData.Edge == RIGHT_EDGE) ? NBPtr->ChannelPtr->WrDatMaxDlys : NBPtr->ChannelPtr->WrDatMinDlys);
481 };
482 //
483 /// Set initial TrnDelay Values if necessary
484 //
485 IDS_HDT_CONSOLE (MEM_FLOW, "Sweeping %s DQS, %s from ", (TechPtr->Direction == DQS_READ_DIR) ?"Read":"Write", (SweepTablePtr->ScanDir == INC_DELAY) ? "incrementing":"decrementing");
486 if (SweepData.BeginDelay != LAST_DELAY) {
487 IDS_HDT_CONSOLE (MEM_FLOW, "%02x", (UINT16) MemTScaleDelayVal (TechPtr, SweepData.BeginDelay));
488 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); i++) {
489 SweepData.TrnDelays[i] = SweepData.BeginDelay;
490 }
491 } else {
492 IDS_HDT_CONSOLE (MEM_FLOW, "Current Delay");
493 SweepData.Step = SweepTablePtr->Step;
494 }
495 IDS_HDT_CONSOLE (MEM_FLOW, " by %02x, until all bytelanes %s.\n\n", (UINT16) MemTScaleDelayVal (TechPtr, ABS (SweepTablePtr->Step)), (SweepData.EndResult == 0xFFFF)?"PASS":"FAIL");
496
497 //-------------------------------------------------------------------
498 // Sweep DQS Delays
499 // MemTContinueSweep function returns false to break out of loop.
500 // There are no other breaks out of this loop.
501 //-------------------------------------------------------------------
502 while (MemTContinueSweep (TechPtr, &SweepData)) {
503 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tByte Lane : 08 07 06 05 04 03 02 01 00\n");
504 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tDQS Delays : %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
505 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[8]),
506 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[7]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[6]),
507 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[5]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[4]),
508 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[3]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[2]),
509 (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[1]), (UINT16) MemTScaleDelayVal (TechPtr, SweepData.TrnDelays[0])
510 );
511 //
512 /// Set Step Value
513 //
514 SweepData.Step = SweepTablePtr->Step;
515 CurrentResult = 0xFFFF;
516 //
517 /// Chip Select Loop: Test the Pattern for all populated CS that are controlled by the current delay registers
518 //
519 for (CsIndex = 0; CsIndex < NBPtr->CsPerDelay ; CsIndex++, TechPtr->ChipSel++) {
520 ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
521 ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
522 if (SweepData.CsAddrValid[CsIndex] == TRUE) {
523 //
524 /// If this is a Write Dqs sweep, Write the pattern now.
525 //
526 if (TechPtr->Direction == DQS_WRITE_DIR) {
527 NBPtr->WritePattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
528 }
529 //
530 /// Read the Pattern Back
531 //
532 NBPtr->ReadPattern (NBPtr, TechPtr->TestBufPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
533 //
534 /// Compare the Pattern and Merge the results using InsertionDelayMsk
535 //
536 AlignedResult = NBPtr->CompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
537 CurrentResult &= AlignedResult | SweepData.InsertionDelayMsk;
538 if (SweepData.InsertionDelayMsk != 0) {
539 OffsetResult = NBPtr->InsDlyCompareTestPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64);
540 CurrentResult &= (OffsetResult | (~SweepData.InsertionDelayMsk));
541 }
542 //
543 /// Flush the Test Pattern
544 //
545 NBPtr->FlushPattern (NBPtr, SweepData.TestAddrRJ16[CsIndex], TechPtr->PatternLength);
546 NBPtr->FamilySpecificHook[ResetRxFifoPtr] (NBPtr, NBPtr);
547 }
548 } /// End Chip Select Loop
549 TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
550 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResult : %c %c %c %c %c %c %c %c %c \n",
551 (SweepData.ResultFound & ((UINT16) 1 << (8))) ? ' ':(CurrentResult & ((UINT16) 1 << (8))) ? 'P':'.',
552 (SweepData.ResultFound & ((UINT16) 1 << (7))) ? ' ':(CurrentResult & ((UINT16) 1 << (7))) ? 'P':'.',
553 (SweepData.ResultFound & ((UINT16) 1 << (6))) ? ' ':(CurrentResult & ((UINT16) 1 << (6))) ? 'P':'.',
554 (SweepData.ResultFound & ((UINT16) 1 << (5))) ? ' ':(CurrentResult & ((UINT16) 1 << (5))) ? 'P':'.',
555 (SweepData.ResultFound & ((UINT16) 1 << (4))) ? ' ':(CurrentResult & ((UINT16) 1 << (4))) ? 'P':'.',
556 (SweepData.ResultFound & ((UINT16) 1 << (3))) ? ' ':(CurrentResult & ((UINT16) 1 << (3))) ? 'P':'.',
557 (SweepData.ResultFound & ((UINT16) 1 << (2))) ? ' ':(CurrentResult & ((UINT16) 1 << (2))) ? 'P':'.',
558 (SweepData.ResultFound & ((UINT16) 1 << (1))) ? ' ':(CurrentResult & ((UINT16) 1 << (1))) ? 'P':'.',
559 (SweepData.ResultFound & ((UINT16) 1 << (0))) ? ' ':(CurrentResult & ((UINT16) 1 << (0))) ? 'P':'.'
560 );
561 //
562 /// Merge current result into cumulative result and make it positive.
563 //
564 SweepData.ResultFound |= ~(CurrentResult ^ SweepData.EndResult);
565
566 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t\tResultFound : %c %c %c %c %c %c %c %c %c \n\n",
567 (SweepData.ResultFound & ((UINT16) 1 << (8))) ? 'Y':' ',
568 (SweepData.ResultFound & ((UINT16) 1 << (7))) ? 'Y':' ',
569 (SweepData.ResultFound & ((UINT16) 1 << (6))) ? 'Y':' ',
570 (SweepData.ResultFound & ((UINT16) 1 << (5))) ? 'Y':' ',
571 (SweepData.ResultFound & ((UINT16) 1 << (4))) ? 'Y':' ',
572 (SweepData.ResultFound & ((UINT16) 1 << (3))) ? 'Y':' ',
573 (SweepData.ResultFound & ((UINT16) 1 << (2))) ? 'Y':' ',
574 (SweepData.ResultFound & ((UINT16) 1 << (1))) ? 'Y':' ',
575 (SweepData.ResultFound & ((UINT16) 1 << (0))) ? 'Y':' '
576 );
577 } /// End of Delay Sweep
578 //
579 /// Place Final delay values at last passing delay.
580 //
581 if (SweepData.ResultFound == 0xFFFF) {
582 if ( ABS (SweepData.Step) == 1) {
583 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
584 if ((SweepData.EndResult & ((UINT16) (1 << i))) == 0) {
585 SweepData.TrnDelays[i] = SweepData.TrnDelays[i] - SweepData.Step;
586 }
587 }
588 }
589 }
590 //
591 // Update Pointer to Sweep Table
592 //
593 SweepTablePtr++;
594 }///End of Edge Detect loop
595 //
596 /// If No Errors are detected, Calculate Data Eye Width and Center
597 //
598 if (SweepData.Error == FALSE) {
599 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tData Eye Results:\n\n");
600 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tByte Left Right\n");
601 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane Edge Edge Width Center\n");
602 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
603 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t %0d", i);
604 TechPtr->Bytelane = i;
605 if (!MemTDataEyeSave (TechPtr, &SweepData, i)) {
606 break;
607 }
608 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
609 if (SweepData.Error == TRUE) {
610 Status = FALSE;
611 }
612 }
613 } else {
614 Status = FALSE;
615 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t--DATA EYE NOT FOUND--\n\n");
616 NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrNoWindBLError] (NBPtr, &SweepData);
617 }
618 return Status;
619}
620
621/* -----------------------------------------------------------------------------*/
622/**
623 *
624 * Initialize the Test Pattern Address for two chip selects and, if this
625 * is a Write Data Eye, write the initial test pattern.
626 *
627 * Test Address is stored in the Sweep info struct. If Memory is not present
628 * then return with False.
629 *
630 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
631 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
632 *
633 * @return BOOLEAN
634 * TRUE - Memory is present
635 * FALSE - No memory present on this Chip Select pair.
636 *
637**
638 */
639BOOLEAN
640STATIC
641MemTInitTestPatternAddress (
642 IN OUT MEM_TECH_BLOCK *TechPtr,
643 IN OUT SWEEP_INFO *SweepPtr
644 )
645{
646 MEM_NB_BLOCK *NBPtr;
647 UINT8 ChipSel;
648 UINT8 CsIndex;
649 BOOLEAN BanksPresent;
650
651 NBPtr = TechPtr->NBPtr;
652 BanksPresent = FALSE;
653 ChipSel = TechPtr->ChipSel;
654 for (CsIndex = 0; CsIndex < NBPtr->CsPerDelay; ChipSel++, CsIndex++, TechPtr->ChipSel++) {
655 ASSERT (CsIndex < MAX_CS_PER_CHANNEL);
656 ASSERT (ChipSel < MAX_CS_PER_CHANNEL);
657 ASSERT (TechPtr->ChipSel < MAX_CS_PER_CHANNEL);
658 //
659 /// If memory is present on this cs, get the test addr
660 //
661 if (NBPtr->GetSysAddr (NBPtr, ChipSel, &(SweepPtr->TestAddrRJ16[CsIndex]))) {
662 if (!(NBPtr->MCTPtr->Status[SbLrdimms]) || ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0)) {
663 BanksPresent = TRUE;
664 SweepPtr->CsAddrValid[CsIndex] = TRUE;
665 //
666 /// If this is a Read Dqs sweep, Write the pattern now.
667 //
668 if (TechPtr->Direction == DQS_READ_DIR) {
669 IDS_HDT_CONSOLE (MEM_FLOW, "\tTestAddr: %x0000\n", SweepPtr->TestAddrRJ16[CsIndex]);
670 NBPtr->WritePattern (NBPtr, SweepPtr->TestAddrRJ16[CsIndex], TechPtr->PatternBufPtr, TechPtr->PatternLength);
671 }
672 }
673 } else {
674 SweepPtr->CsAddrValid[CsIndex] = FALSE;
675 }
676 } /// End Chip Select Loop
677 TechPtr->ChipSel = TechPtr->ChipSel - CsIndex;
678 //
679 /// return FALSE if no ChipSelects present.
680 //
681 return BanksPresent;
682}
683
684/* -----------------------------------------------------------------------------*/
685/**
686 * Test Conditions for exiting the training loop, set the next delay value,
687 * and return status
688 *
689 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
690 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
691 *
692 * @return BOOLEAN
693 * TRUE - Continue to test with next delay setting
694 * FALSE - Exit training loop. Either the result has been found or
695 * end of delay range has been reached.
696*/
697BOOLEAN
698STATIC
699MemTContinueSweep (
700 IN OUT MEM_TECH_BLOCK *TechPtr,
701 IN OUT SWEEP_INFO *SweepPtr
702 )
703{
704 BOOLEAN Status;
705 Status = FALSE;
706 if (SweepPtr->ResultFound != 0xFFFF) {
707 Status = MemTSetNextDelay (TechPtr, SweepPtr);
708 }
709 TechPtr->NBPtr->FamilySpecificHook[RegAccessFence] (TechPtr->NBPtr, NULL);
710 return Status;
711}
712
713/* -----------------------------------------------------------------------------*/
714/**
715 *
716 * This function sets the next delay value for each bytelane that needs to
717 * be advanced. It checks the bounds of the delay to see if we are at the
718 * end of the range. If we are to close to advance a whole step value, but
719 * not at the boundary, then we set the delay to the boundary.
720 *
721 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
722 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
723 *
724 */
725
726BOOLEAN
727STATIC
728MemTSetNextDelay (
729 IN OUT MEM_TECH_BLOCK *TechPtr,
730 IN OUT SWEEP_INFO *SweepPtr
731 )
732{
733 DIE_STRUCT *MCTPtr;
734 UINT8 i;
735
736 MCTPtr = TechPtr->NBPtr->MCTPtr;
737 //
738 ///< Loop through bytelanes
739 //
740 for (i = 0; i < ((MCTPtr->Status[SbEccDimms] && TechPtr->NBPtr->IsSupported[EccByteTraining]) ? 9 : 8) ; i++) {
741 //
742 /// Skip Bytelanes that have already reached the desired result
743 //
744 if ( (SweepPtr->ResultFound & ((UINT16)1 << i)) == 0) {
745 //
746 /// If a bytelane has reached the end, flag an error and exit
747 //
748 if (SweepPtr->TrnDelays[i] == SweepPtr->EndDelay) {
749 if ((SweepPtr->EndResult & ((UINT16) (1 << i))) != 0) {
750 MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE;
751 SweepPtr->Error = TRUE;
752 }
753 return FALSE;
754 }
755 //
756 /// If the Current delay value is less than a step away from EndDelay,
757 //
758 if ( ABS (SweepPtr->EndDelay - SweepPtr->TrnDelays[i]) < ABS (SweepPtr->Step)) {
759 /// set to EndDelay.
760 //
761 SweepPtr->TrnDelays[i] = SweepPtr->EndDelay;
762 } else {
763 //
764 /// Otherwise, add the step value to it
765 SweepPtr->TrnDelays[i] = SweepPtr->TrnDelays[i] + SweepPtr->Step;
766 }
767 //
768 /// Set InsertionDelayMsk bit if Delay < 0 for this bytelane
769 //
770 if (SweepPtr->TrnDelays[i] < 0) {
771 SweepPtr->InsertionDelayMsk |= ((UINT16) 1 << i);
772 } else {
773 SweepPtr->InsertionDelayMsk &= ~((UINT16) 1 << i);
774 }
775 //
776 /// Write the scaled value to the Delay Register
777 //
778 TechPtr->SetDQSDelayCSR (TechPtr, i, MemTScaleDelayVal (TechPtr, SweepPtr->TrnDelays[i]));
779 }
780 }
781 return TRUE;
782}
783/* -----------------------------------------------------------------------------*/
784/**
785 *
786 * This function accepts a delay value in 32nd of a UI and converts it to an
787 * actual register value, taking into consideration NB type, rd/wr,
788 * and frequency.
789 *
790 * Delay = (Min + (Delay * ( (Max - Min) / TRN_DELAY_MAX) )) & Mask
791 *
792 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
793 * @param[in] *Delay - INT8 of delay value;
794 *
795 * @return UINT8 of the adjusted delay value
796*/
797UINT8
798STATIC
799MemTScaleDelayVal (
800 IN OUT MEM_TECH_BLOCK *TechPtr,
801 IN INT8 Delay
802 )
803{
804 MEM_NB_BLOCK *NBPtr;
805 TRN_DLY_PARMS Parms;
806 TRN_DLY_TYPE DelayType;
807 UINT8 NewDelay;
808 INT8 Factor;
809 INT8 ScaledDelay;
810
811 NBPtr = TechPtr->NBPtr;
812 //
813 // Determine Delay Type, Get Delay Parameters, and return scaled Delay value
814 //
815 DelayType = (TechPtr->Direction == DQS_WRITE_DIR) ? AccessWrDatDly : AccessRdDqsDly;
816 NBPtr->GetTrainDlyParms (NBPtr, DelayType, &Parms);
817 Factor = ((Parms.Max - Parms.Min) / TRN_DELAY_MAX);
818 ScaledDelay = Delay * Factor;
819 NewDelay = (Parms.Min + ScaledDelay) & Parms.Mask;
820 return NewDelay;
821}
822
823
824
825
826
827/* -----------------------------------------------------------------------------*/
828/**
829 *
830 * This function calculates the Center of the Data eye for the specified byte lane
831 * and stores its DQS Delay value for reference.
832 *
833 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
834 * @param[in,out] *SweepPtr - Pointer to SWEEP_INFO structure.
835 * @param[in] ByteLane - Bytelane number being targeted
836 *
837 */
838BOOLEAN
839STATIC
840MemTDataEyeSave (
841 IN OUT MEM_TECH_BLOCK *TechPtr,
842 IN OUT SWEEP_INFO *SweepPtr,
843 IN UINT8 ByteLane
844 )
845{
846 MEM_NB_BLOCK *NBPtr;
847 UINT8 EyeCenter;
848 UINT8 DlyMin;
849 UINT8 DlyMax;
850 UINT8 EyeWidth;
851 UINT8 Dimm;
852 CH_DEF_STRUCT *ChanPtr;
853
854 NBPtr = TechPtr->NBPtr;
855 ChanPtr = NBPtr->ChannelPtr;
856
857 ASSERT (ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8));
858 //
859 // Calculate Data Eye edges, Width, and Center in real terms.
860 //
861 if (TechPtr->Direction == DQS_READ_DIR) {
862 DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMinDlys[ByteLane]);
863 DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->RdDqsMaxDlys[ByteLane]);
864 EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->RdDqsMaxDlys[ByteLane] - ChanPtr->RdDqsMinDlys[ByteLane]));
865 EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->RdDqsMinDlys[ByteLane] + ChanPtr->RdDqsMaxDlys[ByteLane] + 1) / 2));
866 if (!NBPtr->FamilySpecificHook[RdDqsDlyRestartChk] (NBPtr, &EyeCenter)) {
867 return FALSE;
868 }
869 ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
870 ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
871 NBPtr->FamilySpecificHook[ForceRdDqsPhaseB] (NBPtr, &EyeCenter);
872 } else {
873 DlyMin = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMinDlys[ByteLane]);
874 DlyMax = MemTScaleDelayVal (TechPtr, ChanPtr->WrDatMaxDlys[ByteLane]);
875 EyeWidth = MemTScaleDelayVal (TechPtr, (ChanPtr->WrDatMaxDlys[ByteLane] - ChanPtr->WrDatMinDlys[ByteLane]));
876 EyeCenter = MemTScaleDelayVal (TechPtr, ((ChanPtr->WrDatMinDlys[ByteLane] + ChanPtr->WrDatMaxDlys[ByteLane] + 1) / 2));
877 ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
878 ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
879 }
880 //
881 // Flag error for small window.
882 //
883 if (EyeWidth < MemTScaleDelayVal (TechPtr, NBPtr->MinDataEyeWidth (NBPtr))) {
884 TechPtr->SmallDqsPosWindow = TRUE;
885 SweepPtr->Error = TRUE;
886 NBPtr->FamilySpecificHook[TrackRxEnSeedlessRdWrSmallWindBLError] (NBPtr, NULL);
887 }
888
889 IDS_HDT_CONSOLE (MEM_FLOW, " %02x %02x %02x %02x", DlyMin, DlyMax, EyeWidth, EyeCenter);
890
891 TechPtr->SetDQSDelayCSR (TechPtr, ByteLane, EyeCenter);
892 if (!SweepPtr->Error) {
893 TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
894 }
895 TechPtr->DqsRdWrPosSaved |= 0xFE00;
896
897 Dimm = (TechPtr->ChipSel / NBPtr->CsPerDelay) * TechPtr->DlyTableWidth () + ByteLane;
898 if (TechPtr->Direction == DQS_READ_DIR) {
899 ChanPtr->RdDqsDlys[Dimm] = EyeCenter;
900 } else {
901 ChanPtr->WrDatDlys[Dimm] = EyeCenter + ChanPtr->WrDqsDlys[Dimm];
902 }
903
904 return TRUE;
905}