| /* $NoKeywords:$ */ |
| /** |
| * @file |
| * |
| * mttRdDqs2DEyeRimmSearch.c |
| * |
| * RD DQS 2 Dimentional Training using Eye Rim Sampling |
| * |
| * @xrefitem bom "File Content Label" "Release Content" |
| * @e project: AGESA |
| * @e sub-project: (Mem/Tech) |
| * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ |
| * |
| **/ |
| /***************************************************************************** |
| * |
| * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of Advanced Micro Devices, Inc. nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * *************************************************************************** |
| * |
| */ |
| /* |
| *---------------------------------------------------------------------------- |
| * MODULES USED |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| #include "AGESA.h" |
| #include "amdlib.h" |
| #include "AdvancedApi.h" |
| #include "GeneralServices.h" |
| #include "Ids.h" |
| #include "heapManager.h" |
| #include "mm.h" |
| #include "mn.h" |
| #include "mu.h" |
| #include "mt.h" |
| #include "mport.h" |
| #include "merrhdl.h" |
| #include "Filecode.h" |
| #include "OptionMemory.h" |
| CODE_GROUP (G1_PEICC) |
| RDATA_GROUP (G1_PEICC) |
| |
| #define FILECODE PROC_MEM_TECH_MTRRDDQS2DEYERIMSEARCH_FILECODE |
| /*---------------------------------------------------------------------------- |
| * DEFINITIONS AND MACROS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| |
| |
| /*---------------------------------------------------------------------------- |
| * TYPEDEFS AND STRUCTURES |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| /*---------------------------------------------------------------------------- |
| * PROTOTYPES OF LOCAL FUNCTIONS |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| |
| |
| BOOLEAN |
| MemTInitializeEyeRimSearch ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ); |
| |
| VOID |
| MemTEyeFill ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ); |
| |
| BOOLEAN |
| CheckSaveResAtEdge ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN INT8 xdir |
| ); |
| |
| UINT8 |
| DetermineSavedState ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ); |
| |
| UINT8 |
| GetPassFailValue ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ); |
| |
| VOID |
| SetPassFailValue ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN UINT8 result |
| ); |
| |
| VOID |
| SetSavedState ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN UINT8 result |
| ); |
| |
| INT8 MemTGet1DTrainedEyeCenter ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane |
| ); |
| |
| BOOLEAN |
| CheckForFail ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ); |
| |
| BOOLEAN |
| AllocateSaveLaneStorage ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ); |
| |
| INT8 |
| xlateY ( |
| IN INT8 y |
| ); |
| |
| BOOLEAN |
| ClearSampledPassResults ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ); |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Initialize Eye Rim Search |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return TRUE |
| */ |
| BOOLEAN |
| MemTInitializeEyeRimSearch ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| UINT8 lane; |
| UINT8 vref; |
| MEM_2D_ENTRY *Data; |
| MEM_2D_RIM_ENTRY *RimData; |
| Data = TechPtr->Local2DData; |
| RimData = TechPtr->SavedData; |
| // |
| // Set Boundaries |
| // |
| RimData->xMax = Data->MaxRdDqsSweep - 1; |
| RimData->yMax = (TechPtr->NBPtr->TotalMaxVrefRange / 2) - 1; |
| RimData->xMin = RimData->xMax * -1; |
| RimData->yMin = RimData->yMax * -1; |
| |
| RimData->ParallelSampling = EYERIM_PARALLEL_SAMPLING; |
| RimData->BroadcastDelays = EYERIM_BROADCAST_DELAYS; |
| RimData->Dirs[0] = 1; |
| RimData->Dirs[1] = -1; |
| |
| RimData->SampleCount = 0; |
| RimData->VrefUpdates = 0; |
| RimData->RdDqsDlyUpdates = 0; |
| for (lane = 0; lane < MemT2DGetMaxLanes (TechPtr); lane ++ ) { |
| for ( vref = 0; vref < TechPtr->NBPtr->TotalMaxVrefRange; vref ++ ) { |
| // 00b = state not saved |
| // 01b = state saved |
| RimData->LaneSaved[lane].Vref[vref].PosRdDqsDly = 0; |
| RimData->LaneSaved[lane].Vref[vref].NegRdDqsDly = 0; |
| // 00b = Fail |
| // 01b = Pass |
| Data->Lane[lane].Vref[vref].PosRdDqsDly = 0; |
| Data->Lane[lane].Vref[vref].NegRdDqsDly = 0; |
| } |
| } |
| return TRUE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * This function collects data for Eye Rim Search |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return TRUE - No Errors occurred |
| * @return FALSE - Errors occurred |
| */ |
| BOOLEAN |
| MemT2DRdDQSEyeRimSearch ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| UINT8 lane; |
| UINT8 j; |
| UINT8 k; |
| INT8 ydir; |
| INT8 xdir; |
| INT8 xi; |
| INT8 yi; |
| INT8 x; |
| INT8 y; |
| INT8 xmax; |
| INT8 ymax; |
| INT8 xmin; |
| INT8 ymin; |
| INT8 xo; |
| INT8 xt; |
| INT8 yo; |
| INT8 yt; |
| UINT8 slane; |
| UINT8 result; |
| INT8 states[2]; |
| UINT8 InitialCS; |
| UINT8 ChipSel; |
| UINT8 Aggr; |
| UINT8 SeedCount; |
| UINT32 InPhaseResult; |
| UINT32 PhaseResult180; |
| UINT32 Result; |
| MEM_2D_ENTRY *Data; |
| MEM_2D_RIM_ENTRY RimData; |
| MEM_NB_BLOCK *NBPtr; |
| RD_DQS_2D *VrefPtr; |
| ALLOCATE_HEAP_PARAMS AllocHeapParams; |
| NBPtr = TechPtr->NBPtr; |
| Data = TechPtr->Local2DData; |
| |
| //if (AllocateSaveLaneStorage(TechPtr)) { |
| // return FALSE; |
| //} |
| // Allocate Storage for Rim Search |
| AllocHeapParams.RequestedBufferSize = MemT2DGetMaxLanes (TechPtr) * TechPtr->NBPtr->TotalMaxVrefRange * sizeof (RD_DQS_2D); |
| AllocHeapParams.BufferHandle = AMD_MEM_2D_RDQS_RIM_HANDLE; |
| AllocHeapParams.Persist = HEAP_LOCAL_CACHE; |
| if (HeapAllocateBuffer (&AllocHeapParams, &TechPtr->NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) { |
| VrefPtr = (RD_DQS_2D *) AllocHeapParams.BufferPtr; |
| } else { |
| SetMemError (AGESA_FATAL, TechPtr->NBPtr->MCTPtr); |
| PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); |
| return FALSE; |
| } |
| for (lane = 0; lane < MemT2DGetMaxLanes (TechPtr); lane++) { |
| RimData.LaneSaved[lane].Vref = &VrefPtr[lane * TechPtr->NBPtr->TotalMaxVrefRange]; |
| } |
| TechPtr->SavedData = &RimData; |
| MemTInitializeEyeRimSearch (TechPtr); |
| MemT2DProgramIntExtVrefSelect (TechPtr); |
| InitialCS = TechPtr->ChipSel; |
| NBPtr->FamilySpecificHook[Adjust2DPhaseMaskBasedOnEcc] (NBPtr, &NBPtr); |
| NBPtr->InitializeRdDqs2dVictimContinuousWrites (NBPtr); |
| // |
| // EnableDisable continuous writes on the agressor channels |
| // |
| IDS_HDT_CONSOLE (MEM_FLOW,"\n\tEye Rim Search, ParallelSampling: %c, BroadcastDelays: %c \n", (RimData.ParallelSampling == TRUE) ? 'Y' : 'N', (RimData.BroadcastDelays == TRUE) ? 'Y' : 'N'); |
| |
| for (Aggr = 0; Aggr < (NBPtr->MaxAggressorDimms[(NBPtr->Dct + 1) & 1] > 0 ? NBPtr->MaxAggressorDimms[(NBPtr->Dct + 1) & 1] : 1) ; Aggr += (NBPtr->IsSupported[PerDimmAggressors2D] ? 2 : NBPtr->CsPerDelay) ) { |
| ClearSampledPassResults (TechPtr); |
| // |
| // Enable continuous writes on the aggressors |
| // |
| NBPtr->AgressorContinuousWrites (NBPtr, Aggr, TRUE); |
| for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { |
| xmax = RimData.xMax; |
| xmin = RimData.xMin; |
| ymax = RimData.yMax; |
| ymin = RimData.yMin; |
| if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) { |
| TechPtr->ChipSel = ChipSel; |
| for (lane = 0; lane < MemT2DGetMaxLanes (TechPtr); lane ++ ) { |
| // |
| // Two loops to handle each quadrant from the trained point |
| // |
| for ( j = 0; j < 2; j++) { |
| ydir = RimData.Dirs[j]; |
| for ( k = 0; k < 2; k++) { |
| xdir = RimData.Dirs[k]; |
| // |
| // Sample Loops - stay w/n the defined quadrant |
| // assume xmax = -1 * xmin, ymax = -1 * ymin |
| // initial point must always pass |
| // end point - boundary or two consecutive fails along the y-axis |
| // |
| // Initial dx, dy step state |
| // Starting dy at 8 to reduce samples necessary searching for the eye height |
| states[0] = 0; |
| states[1] = 8; |
| // xi and yi are inital trained points, assumed to be inside the eye |
| // These set the coordinate syst em for the quadrants |
| xi = MemTGet1DTrainedEyeCenter (TechPtr, lane); |
| yi = 0; // Initial center is always at Vref nominal |
| x = xi; |
| y = yi; |
| while ( |
| ((xdir > 0 && x >= xi && x <= xmax) || |
| (xdir < 0 && x <= xi && x >= xmin)) && |
| ((ydir > 0 && y >= yi && y <= ymax) || |
| (ydir < 0 && y <= yi && y >= ymin)) && |
| !(y == yi && (xdir * (xi - x) >= 2) && |
| CheckSaveResAtEdge (TechPtr, lane, y, x, xdir)) |
| ) { |
| // |
| // Decide if result is already sampled, or need to take the sample |
| // |
| if (0 == DetermineSavedState (TechPtr, lane, y, x)) { |
| RimData.SampleCount++; |
| // |
| // Result for this point is not in the cache, sample it |
| // |
| for (slane = 0; slane < MemT2DGetMaxLanes (TechPtr); slane ++ ) { |
| if (!RimData.ParallelSampling && ( slane != lane)) { |
| continue; |
| } |
| // |
| // Calculate the relative offset from the initial trained position for this lane |
| // |
| xo = MemTGet1DTrainedEyeCenter (TechPtr, slane); |
| yo = 0; |
| xt = xo + (x - xi); |
| yt = yo + (y - yi); |
| if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) { |
| continue; |
| } |
| // |
| // Update the vref and lane delays (yt, xt) |
| // |
| if (slane == lane) { |
| // |
| // For processors w/ a single Vref per channel, only set Vref once |
| // |
| if ( NBPtr->Vref != xlateY (yt)) { |
| // |
| // If not already set |
| // |
| TechPtr->SavedData->VrefUpdates++; |
| NBPtr->Vref = xlateY (yt); |
| MemT2DProgramVref (TechPtr, xlateY (yt)); |
| } |
| } |
| if (RimData.BroadcastDelays) { |
| // When BroadcastDelays, only set RdDqs once |
| if (slane == lane) { |
| // |
| // If current lane |
| // |
| if ( NBPtr->RdDqsDly != (UINT8) (xt & RimData.xMax)) { |
| // |
| // If the not already set |
| // Account for rollover. |
| // |
| TechPtr->SavedData->RdDqsDlyUpdates++; |
| NBPtr->RdDqsDly = (UINT8) (xt & RimData.xMax); |
| MemT2DPrograRdDQSDly (TechPtr, (UINT8) (xt & RimData.xMax)); |
| } |
| } |
| } else { |
| // |
| /// @todo: Set individual lanes |
| // |
| } |
| } |
| // |
| // Perform Memory RW test |
| // |
| InPhaseResult = 0; |
| PhaseResult180 = 0; |
| NBPtr->InitializeRdDqs2dVictimChipSelContinuousWrites (NBPtr); |
| for (SeedCount = 0; SeedCount < NBPtr->MaxSeedCount; SeedCount++) { |
| // |
| // Begin continuous reads and writes on the victim channels |
| // |
| NBPtr->StartRdDqs2dVictimContinuousWrites (NBPtr, SeedCount); |
| // |
| // Occasionally check if all trained lanes have already failed |
| // |
| if ((NBPtr->MaxSeedCount < 4) || ((SeedCount % (NBPtr->MaxSeedCount / 4)) == 0)) { |
| InPhaseResult |= NBPtr->InPhaseCompareRdDqs2DPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64); |
| PhaseResult180 |= NBPtr->Phase180CompareRdDqs2DPattern (NBPtr, TechPtr->TestBufPtr, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64); |
| if (((InPhaseResult & NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask) && ((PhaseResult180& NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask)) { |
| break; |
| } |
| } |
| } |
| // |
| // Obtain the results |
| // |
| for (slane = 0; slane < MemT2DGetMaxLanes (TechPtr); slane ++ ) { |
| if (!RimData.ParallelSampling && (slane != lane)) { |
| continue; |
| } |
| // |
| // Calculate the relative offset from legacy trained |
| // |
| xo = MemTGet1DTrainedEyeCenter (TechPtr, RimData.BroadcastDelays?lane:slane); |
| yo = 0; |
| xt = xo + (x - xi); |
| yt = yo + (y - yi); |
| if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) { |
| continue; |
| } |
| // |
| // In this example, data{}{}{} = 1 is a Fail, 0 is a Pass |
| // In-Phase Results |
| // |
| if (CheckForFail (TechPtr, slane, yt, xt)) { |
| // |
| // Don't overwrite a fail |
| // |
| if (xt > 0) { |
| if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { |
| // x8, so combine "Nibble X" and "Nibble X+1" results |
| Result = (InPhaseResult >> (slane * 2)) & 0x03; |
| } else { |
| // x4, so use "Nibble" results |
| Result = (InPhaseResult >> slane) & 0x01; |
| } |
| SetPassFailValue (TechPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); |
| SetSavedState (TechPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); |
| } else { |
| if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { |
| // x8, so combine "Nibble X" and "Nibble X+1" results |
| Result = (PhaseResult180 >> (slane * 2)) & 0x03; |
| } else { |
| // x4, so use "Nibble" results |
| Result = (PhaseResult180 >> slane) & 0x01; |
| } |
| SetPassFailValue (TechPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); |
| SetSavedState (TechPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); |
| } |
| } |
| } |
| } |
| // Decide the next sample point based on the result of the current lane |
| result = GetPassFailValue (TechPtr, lane, y, x); |
| InPhaseResult = 0; |
| PhaseResult180 = 0; |
| // States && comments are relative to the ++ Quadrant |
| if (result == 3) { |
| // Current Pass |
| if (states[0] > 0 || states[1] > 0) { |
| if (states[1] > 1 && y * ydir <= ymax - states[1]) { |
| // Current Pass, Continue searching up by leaps |
| states[0] = 0; |
| } else if (states[1] > 1 && y * ydir <= ymax - 4) { |
| // Current Pass, Continue searching up by leaps |
| states[0] = 0; |
| states[1] = 4; |
| } else if (states[1] > 1 && y * ydir <= ymax - 2) { |
| // Current Pass, Continue searching up by leaps |
| states[0] = 0; |
| states[1] = 2; |
| } else if (y * ydir <= ymax - 1) { |
| // Current Pass, Continue searching up by one |
| states[0] = 0; |
| states[1] = 1; |
| } else if (y * ydir == ymax) { |
| // Current Pass and at the top edge, Move right |
| states[0] = 1; |
| states[1] = 0; |
| } else { |
| ASSERT (FALSE); |
| } |
| } else { |
| // Move right one |
| states[0] = 1; |
| states[1] = 0; |
| } |
| } else if (result == 2) { |
| // Current Fail |
| if (states[0] > 0) { |
| // Search down and left |
| states[0] = -1; |
| states[1] = -1; |
| } else if (states[1] > 1 || states[1] < 0) { |
| // Search down |
| states[0] = 0; |
| states[1] = -1; |
| } else if (states[1] == 1) { |
| // Move down and right |
| states[0] = 1; |
| states[1] = -1; |
| } else { |
| ASSERT (FALSE); |
| } |
| } else { |
| ASSERT (FALSE); |
| } |
| // Update the coordinates based on the new state and quadrant direction |
| x += xdir * states[0]; |
| y += ydir * states[1]; |
| } |
| } |
| } |
| } |
| } |
| } |
| // |
| // Disable continuous writes on the agressor channels |
| // |
| NBPtr->AgressorContinuousWrites (NBPtr, Aggr, FALSE); |
| } |
| IDS_HDT_CONSOLE (MEM_FLOW,"\n\tSampleCount:%d",TechPtr->SavedData->SampleCount); |
| IDS_HDT_CONSOLE (MEM_FLOW,"\t\tVref Updates:%d",TechPtr->SavedData->VrefUpdates); |
| IDS_HDT_CONSOLE (MEM_FLOW,"\t\tRdDqs Dly Updates:%d\n",TechPtr->SavedData->RdDqsDlyUpdates); |
| // |
| // Finilazing Victim Continuous writes |
| // |
| NBPtr->FinalizeRdDqs2dVictimContinuousWrites (NBPtr); |
| TechPtr->ChipSel = InitialCS; |
| // |
| // Fill eye based on Rim Search results |
| // |
| MemTEyeFill (TechPtr); |
| // |
| // Display the results the completed eye |
| // |
| MemT2DRdDqsDisplaySearch (TechPtr, Data); |
| // |
| // Restore environment settings after training |
| // |
| if (HeapDeallocateBuffer (AMD_MEM_2D_RDQS_RIM_HANDLE, &TechPtr->NBPtr->MemPtr->StdHeader) != AGESA_SUCCESS) { |
| SetMemError (AGESA_FATAL, TechPtr->NBPtr->MCTPtr); |
| PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); |
| } |
| return TRUE; |
| } |
| |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * Fill the data eye |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return |
| */ |
| VOID |
| MemTEyeFill ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| INT8 x; |
| INT8 y; |
| UINT8 lane; |
| UINT8 result; |
| INT8 yLastPass; |
| UINT8 xMax; |
| UINT8 yMax; |
| UINT8 xMin; |
| UINT8 yMin; |
| BOOLEAN FirstPassFound; |
| |
| xMax = TechPtr->SavedData->xMax; |
| yMax = TechPtr->SavedData->yMax; |
| xMin = TechPtr->SavedData->xMin; |
| yMin = TechPtr->SavedData->yMin; |
| for (lane = 0; lane < MemT2DGetMaxLanes (TechPtr); lane++) { |
| for (x = xMin ; x <= xMax ; x++) { |
| FirstPassFound = FALSE; |
| yLastPass = yMin; |
| // |
| // Scan for the last passing value |
| // |
| for (y = yMax ; y >= yLastPass ; y--) { |
| result = GetPassFailValue (TechPtr, lane, y, x); |
| if (result == 0) { |
| // |
| // Not Saved, Mark it as FAIL. (Should already be cleared) |
| // |
| } else if (result == 2) { |
| // |
| // FAIL, so Mark as FAIL (Do nothing) |
| // |
| } else if (result == 3) { |
| // |
| // PASS, Mark it and save y value (This will end the loop) |
| // |
| SetPassFailValue (TechPtr, lane, y, x, result); |
| yLastPass = y; |
| } else { |
| ASSERT (FALSE); |
| } |
| } |
| // |
| // Scan for the first pass, the fill until the last pass |
| // |
| for (y = yMin ; y < yLastPass ; y++) { |
| result = GetPassFailValue (TechPtr, lane, y, x); |
| if (result == 0) { |
| // |
| // Not Saved, if we've already found a first Passing value, mark it as a PASS |
| // otherwise, mark it as FAIL. (Should already be cleared) |
| // |
| if (FirstPassFound == TRUE) { |
| SetPassFailValue (TechPtr, lane, y, x, result); |
| } |
| } else if (result == 2) { |
| // |
| // FAIL, so Mark as FAIL (Do nothing) |
| // |
| } else if (result == 3) { |
| // |
| // PASS, Mark it and set FirstPassFound |
| // |
| SetPassFailValue (TechPtr, lane, y, x, result); |
| FirstPassFound = TRUE; |
| } else { |
| ASSERT (FALSE); |
| } |
| } // y Loop |
| } //x Loop |
| } // Lane Loop |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Get the 1D trained center |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] lane - current lane |
| * |
| * @return |
| */ |
| INT8 |
| MemTGet1DTrainedEyeCenter ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane |
| ) |
| { |
| if ((TechPtr->NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { |
| // Program Byte based for x8 and x16 |
| return (INT8)TechPtr->NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + lane]; |
| } else { |
| return (INT8)TechPtr->NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + (lane >> 1)]; |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Determine if the saved value is at or close to the edge |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] *lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * @param[in] xdir - x-direction |
| * |
| * @return TRUE - close to edge, FALSE - not close to edge |
| */ |
| BOOLEAN |
| CheckSaveResAtEdge ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN INT8 xdir |
| ) |
| { |
| if (x > 0) { |
| if (((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-1 * xdir))) & 0x1) == 0) { |
| if (((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-1 * xdir))) & 0x1) == 1) { |
| if (((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-2 * xdir))) & 0x1) == 0) { |
| if (((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-2 * xdir))) & 0x1) == 1) { |
| return TRUE; |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } else { |
| if (((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-1 * xdir))) & 0x1) == 0) { |
| if (((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-1 * xdir))) & 0x1) == 1) { |
| if (((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-2 * xdir))) & 0x1) == 0) { |
| if (((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax) + (-2 * xdir))) & 0x1) == 1) { |
| return TRUE; |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } else { |
| return FALSE; |
| } |
| } |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * determine if a BL has been saved |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * |
| * @return 1 - value saved, 0 - value not saved |
| */ |
| UINT8 |
| DetermineSavedState ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ) |
| { |
| if (x > 0) { |
| return (UINT8) (TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1); |
| } else { |
| return (UINT8) (TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1); |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Determine if a failure has occured |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * |
| * @return 2 - Fail, 3 - Pass |
| */ |
| BOOLEAN |
| CheckForFail ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ) |
| { |
| if (x > 0) { |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // value not saved, so it is not fail |
| return TRUE; |
| } else { |
| // value saved, so examine result |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // result = fail |
| return FALSE; |
| } else { |
| // result = pass |
| return TRUE; |
| } |
| } |
| } else { |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // value not saved, so it is not fail |
| return TRUE; |
| } else { |
| // value saved, so examine result |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // result = fail |
| return FALSE; |
| } else { |
| // result = pass |
| return TRUE; |
| } |
| } |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Get pass fail state of lane |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * |
| * @return 0 - Value not saved, 2 - Fail, 3 - Pass |
| */ |
| UINT8 |
| GetPassFailValue ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x |
| ) |
| { |
| if (x > 0) { |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // value not saved |
| return 0; |
| } else { |
| // value saved, so return pass/fail |
| return ((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].PosRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) ? 2 : 3; |
| } |
| } else { |
| if ((TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) { |
| // value not saved |
| return 0; |
| } else { |
| // value saved, so return pass/fail |
| return ((TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].NegRdDqsDly >> (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax)) & 0x1) == 0) ? 2 : 3; |
| } |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Set the Pass/Fail state of lane |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] *lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * @param[in] result - result value |
| * |
| * @return Saved Value |
| */ |
| VOID |
| SetPassFailValue ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN UINT8 result |
| ) |
| { |
| if (x > 0) { |
| TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].PosRdDqsDly |= (result == 0) ? (1 << (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax))) : 0; |
| } else { |
| TechPtr->Local2DData->Lane[lane].Vref[xlateY (y)].NegRdDqsDly |= (result == 0) ? (1 << (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax))) : 0; |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Set the save state of lane |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * @param[in] *lane - current lane |
| * @param[in] y - vref value |
| * @param[in] x - RdDqs value |
| * @param[in] result - result value |
| * |
| * @return Saved Value |
| */ |
| VOID |
| SetSavedState ( |
| IN OUT MEM_TECH_BLOCK *TechPtr, |
| IN UINT8 lane, |
| IN INT8 y, |
| IN INT8 x, |
| IN UINT8 result |
| ) |
| { |
| if (x > 0) { |
| TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdDqsDly |= (1 << (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax))); |
| } else { |
| TechPtr->SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdDqsDly |= (1 << (TechPtr->SavedData->xMax - (x & TechPtr->SavedData->xMax))); |
| } |
| } |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Allocate data storage |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return FALSE - No Errors occurred |
| * @return TRUE - Errors occurred |
| */ |
| BOOLEAN |
| AllocateSaveLaneStorage ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| RD_DQS_2D *VrefPtr; |
| ALLOCATE_HEAP_PARAMS AllocHeapParams; |
| UINT8 Lane; |
| |
| AllocHeapParams.RequestedBufferSize = MemT2DGetMaxLanes (TechPtr) * TechPtr->NBPtr->TotalMaxVrefRange * sizeof (RD_DQS_2D); |
| AllocHeapParams.BufferHandle = AMD_MEM_2D_RDQS_RIM_HANDLE; |
| AllocHeapParams.Persist = HEAP_LOCAL_CACHE; |
| if (HeapAllocateBuffer (&AllocHeapParams, &TechPtr->NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) { |
| VrefPtr = (RD_DQS_2D *) AllocHeapParams.BufferPtr; |
| } else { |
| SetMemError (AGESA_FATAL, TechPtr->NBPtr->MCTPtr); |
| PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); |
| return TRUE; |
| } |
| for (Lane = 0; Lane < MemT2DGetMaxLanes (TechPtr); Lane++) { |
| TechPtr->SavedData->LaneSaved[Lane].Vref = &VrefPtr[Lane * TechPtr->NBPtr->TotalMaxVrefRange]; |
| } |
| return FALSE; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Translate Vref into a positive, linear value that can be used as an |
| * array index. |
| * |
| * @param[in] y - vref value |
| * |
| * @return Saved Value |
| */ |
| INT8 |
| xlateY ( |
| IN INT8 y |
| ) |
| { |
| ASSERT ( y > -0x10); |
| ASSERT ( y < 0x10); |
| return (y + 0xF) & 0x1F; |
| } |
| |
| /* -----------------------------------------------------------------------------*/ |
| /** |
| * |
| * Re-walking the eye rim for each aggressor combination, which invalidates previous Passes |
| * in the sample array. Previous Fails in the sample array remain valid. Knowledge of previous Fails |
| * and speeds sampling for the subsequent walks, esp. when used in conjunction w/ ParallelSampling |
| * |
| * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK |
| * |
| * @return TRUE |
| */ |
| BOOLEAN |
| ClearSampledPassResults ( |
| IN OUT MEM_TECH_BLOCK *TechPtr |
| ) |
| { |
| UINT8 lane; |
| UINT8 vref; |
| MEM_2D_ENTRY *Data; |
| MEM_2D_RIM_ENTRY *RimData; |
| Data = TechPtr->Local2DData; |
| RimData = TechPtr->SavedData; |
| for (lane = 0; lane < MemT2DGetMaxLanes (TechPtr); lane ++ ) { |
| for ( vref = 0; vref < TechPtr->NBPtr->TotalMaxVrefRange; vref ++ ) { |
| RimData->LaneSaved[lane].Vref[vref].PosRdDqsDly &= ~(Data->Lane[lane].Vref[vref].PosRdDqsDly); |
| Data->Lane[lane].Vref[vref].PosRdDqsDly = 0; |
| RimData->LaneSaved[lane].Vref[vref].NegRdDqsDly &= ~(Data->Lane[lane].Vref[vref].NegRdDqsDly); |
| Data->Lane[lane].Vref[vref].NegRdDqsDly = 0; |
| } |
| } |
| return TRUE; |
| } |