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