diff options
author | Siyuan Wang <wangsiyuanbuaa@gmail.com> | 2013-07-25 15:14:15 +0800 |
---|---|---|
committer | Bruce Griffith <Bruce.Griffith@se-eng.com> | 2013-08-04 05:40:37 +0200 |
commit | affe85fbc8a13d35960aa92ae87cbb6330ad253f (patch) | |
tree | 9c1ace69f12b06b6544faf041994aa4288fb2e45 /src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING | |
parent | ae8d06969bdde9b1250bc3c4ad93f5db408dae98 (diff) |
AMD Kabini: Add AGESA/PI code for new processor family
Change-Id: Icb6f64e2e3cfd678fb4fb4f13f0e4b678d5acc4a
Signed-off-by: Siyuan Wang <SiYuan.Wang@amd.com>
Signed-off-by: Siyuan Wang <wangsiyuanbuaa@gmail.com>
Reviewed-by: Nick Dill <nick.dill@se-eng.com>
Tested-by: Bruce Griffith <bruce.griffith@se-eng.com>
Reviewed-on: http://review.coreboot.org/3836
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martin.roth@se-eng.com>
Diffstat (limited to 'src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING')
6 files changed, 3633 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c new file mode 100644 index 0000000000..b355d6b093 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c @@ -0,0 +1,348 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DKb.c + * + * KB - Specific funtion for 2D Read and write training feature + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining/Kb) + * @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 "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_KB_MFRDWR2DKB_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +MemFRdWr2DTrainingInitKB ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ); + +BOOLEAN +STATIC +MemFRdWr2DScaleVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramIntExtVrefSelectKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramDataPatternKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ); + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function initializes the 2D Read/Write Training Feature Hooks for KB + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE - Function was implemented + * + */ + +BOOLEAN +MemFRdWr2DTrainingInitKB ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + ASSERT (NBPtr != NULL); + NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining; + NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig; + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelectKB; + NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVrefKB; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVrefKB; + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays; + NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch; + NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel; + NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim; + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase; + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPatternKB; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref for 2D Read/Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *VrefPtr - Pointer to Vref value + * + * @return BOOLEAN + * TRUE - Success + * FAIL (External Callout only) + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ) +{ + AGESA_STATUS Status; + MEM_DATA_STRUCT *MemPtr; + ID_INFO CallOutIdInfo; + VOLTAGE_ADJUST Va; + UINT8 Vref; + + ASSERT (NBPtr != NULL); + ASSERT (VrefPtr != NULL); + MemPtr = NBPtr->MemPtr; + Vref = *(UINT8*)VrefPtr; + CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId; + CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId; + LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader); + Va.MemData = MemPtr; + Status = AGESA_SUCCESS; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) { + // + // Internal vref control + // + ASSERT (Vref < 61); + if (Vref < 30) { + Vref = (62 - Vref); + } else { + Vref = (Vref - 30); + } + NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 3); + } else { + // + // External vref control + // + AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref; + IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n"); + Va.VoltageType = VTYPE_CPU_VREF; + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + } + } else { + // + // DIMM Vref Control + // + Va.VoltageType = VTYPE_DIMM_VREF; + // + // Offset by 15 and multiply by 2. + // + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDimm Vref = %c%d% ", (Va.AdjustValue < 0) ? '-':'+', (Va.AdjustValue < 0) ? (0 - Va.AdjustValue) : Va.AdjustValue ); + if (Status != AGESA_SUCCESS) { + IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *"); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + return (Status == AGESA_SUCCESS) ? TRUE : FALSE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function scales Vref from the range used in Data Collection to + * the range that is programmed into the register. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale. + * + * @return BOOLEAN + * TRUE Function was implemented + * + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ) +{ + *(UINT8*)Vref = *(UINT8*)Vref * 2; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref to internal or external control for 2D Read + * or Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - External Vref was selected + * FALSE - Internal Vref was selected + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramIntExtVrefSelectKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0002 : 0x0001)); + } + return NBPtr->RefPtr->ExternalVrefCtl; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Data Pattern that will be sent and compared + * against. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *PatternIndexPtr - Pointer to a generic index used to + * determine which pattern to program. + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramDataPatternKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ) +{ + UINT8 SeedCount; + UINT32 PrbsSeed; + CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251}; + + ASSERT (NBPtr != 0); + ASSERT (PatternIndexPtr != NULL); + SeedCount = *(UINT8*)PatternIndexPtr; + ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1)); + MemNSetBitFieldNb (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]); + PrbsSeed = 0x7EA05; + switch (SeedCount) { + case 0: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x01); + PrbsSeed = 0x7FFFF; + break; + case 1: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x04); + MemNSetBitFieldNb (NBPtr, BFXorPatOvr, 0xFF); + PrbsSeed = 0x7EA05; + break; + case 2: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0x55555549); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0x55555555); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0x55555555); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0x55555555); + break; + case 3: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0xA5A5A55A); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0xA5A5A5A5); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0xA5A5A5A5); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0xA5A5A5A5); + break; + default: + ASSERT (FALSE); + } + ASSERT (PrbsSeed != 0); + // + // Program the PRBS Seed + // + NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed); + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c new file mode 100644 index 0000000000..62bfb82366 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c @@ -0,0 +1,115 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdDqs2DTraining.c + * + * RD DQS 2 dimensional training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @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 "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDDQS2DTRAINING_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + /*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function executes 2D training for Read DQS + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + * + */ + +BOOLEAN +MemFAmdRdDqs2DTraining ( + IN OUT MEM_TECH_BLOCK *TechPtr + ); + +BOOLEAN +MemFAmdRdDqs2DTraining ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + IDS_HDT_CONSOLE (MEM_STATUS, "\n\nStart Read DQS 2D training.\n\n"); + TechPtr->Direction = DQS_READ_DIR; + return TechPtr->NBPtr->FamilySpecificHook[RdWr2DTraining] (TechPtr->NBPtr, NULL); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c new file mode 100644 index 0000000000..090a9379a9 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c @@ -0,0 +1,1112 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DEyeRimSearch.c + * + * Eye Rim Sampling Algorithm for use in 2D Read DQS or 2D Write Data Training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @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 "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DEYERIMSEARCH_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +STATIC +MemFInitializeEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +VOID +STATIC +MemTEyeFill ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT8 +STATIC +DetermineSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + +UINT8 +STATIC +GetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + +VOID +STATIC +SetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ); + +VOID +STATIC +SetSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ); + +INT8 +STATIC +Get1DTrainedEyeCenter ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane + ); + +BOOLEAN +STATIC +CheckForFail ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + + +INT8 +STATIC +xlateY ( + IN INT8 y + ); + +BOOLEAN +STATIC +ClearSampledPassResults ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT32 +STATIC +CompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT32 +STATIC +Compare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +VOID +STATIC +ProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Vref + ); + +VOID +STATIC +StartAggressors ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN BOOLEAN TurnOn + ); + +/* -----------------------------------------------------------------------------*/ +/** + * + * Initialize Eye Rim Search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +STATIC +MemFInitializeEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 lane; + UINT8 vref; + MEM_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY *RimData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + Data = TechPtr->RdWr2DData; + RimData = Data->SavedData; + // + // Set Boundaries + // + RimData->xMax = Data->MaxRdWrSweep - 1; + RimData->yMax = (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->RdWrDlyUpdates = 0; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) { + for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) { + // 00b = state not saved + // 01b = state saved + RimData->LaneSaved[lane].Vref[vref].PosRdWrDly = 0; + RimData->LaneSaved[lane].Vref[vref].NegRdWrDly = 0; + // 00b = Fail + // 01b = Pass + Data->Lane[lane].Vref[vref].PosRdWrDly = 0; + Data->Lane[lane].Vref[vref].NegRdWrDly = 0; + } + } + if (TechPtr->Direction == DQS_READ_DIR) { + NBPtr->MaxSeedCount = MAX_2D_RD_SEED_COUNT; + } else { + // + // Set the seed count in terms of the Total desired bit times + // + NBPtr->MaxSeedCount = (UINT8) (NBPtr->TotalBitTimes2DWrTraining / (256 * 8 * MIN ( 1, NBPtr->MaxAggressorDimms[NBPtr->Dct]))); + }; + TechPtr->DqsRdWrPosSaved = 0; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\tTotal Bit Times: %d Max Seed Count: %d\n",(TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalBitTimes2DRdTraining:NBPtr->TotalBitTimes2DWrTraining,NBPtr->MaxSeedCount); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * This function collects data for Eye Rim Search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Not Used + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + * + */ +BOOLEAN +MemFRdWr2DEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID * OptParam + ) +{ + 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_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY RimData; + MEM_TECH_BLOCK *TechPtr; + RD_WR_2D *VrefPtr; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + Data = TechPtr->RdWr2DData; + // + // Allocate Storage for Rim Search + // + AllocHeapParams.RequestedBufferSize = MemFRdWr2DGetMaxLanes (NBPtr) * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D); + AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_RIM_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) { + VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr; + } else { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + return FALSE; + } + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane++) { + RimData.LaneSaved[lane].Vref = &VrefPtr[lane * NBPtr->TotalMaxVrefRange]; + } + Data->SavedData = &RimData; + + MemFInitializeEyeRimSearch (NBPtr); + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] (NBPtr, NULL); + InitialCS = TechPtr->ChipSel; + NBPtr->FamilySpecificHook[Adjust2DPhaseMaskBasedOnEcc] (NBPtr, &NBPtr); + NBPtr->FamilySpecificHook[RdWr2DInitVictim] (NBPtr, NULL); + // + // EnableDisable continuous writes on the agressor channels + // + + for (Aggr = 0; Aggr < MAX (1 , NBPtr->MaxAggressorDimms[NBPtr->Dct]) ; Aggr += (NBPtr->IsSupported[PerDimmAggressors2D] ? 2 : NBPtr->CsPerDelay) ) { + ClearSampledPassResults (NBPtr); + // + // Enable continuous writes on the aggressors + // + StartAggressors (NBPtr, 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; + Data->RdWrDly = Data->MaxRdWrSweep; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); 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 = Get1DTrainedEyeCenter (NBPtr, 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)) + ) { + // + // Decide if result is already sampled, or need to take the sample + // + if (0 == DetermineSavedState (NBPtr, lane, y, x)) { + RimData.SampleCount++; + // + // Result for this point is not in the cache, sample it + // + for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) { + if (!RimData.ParallelSampling && ( slane != lane)) { + continue; + } + // + // Calculate the relative offset from the initial trained position for this lane + // + xo = Get1DTrainedEyeCenter (NBPtr, 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 + // + RimData.VrefUpdates++; + NBPtr->Vref = xlateY (yt); + ProgramVref (NBPtr, NBPtr->Vref); + } + } + if (RimData.BroadcastDelays) { + // + // When BroadcastDelays, only set RdDqs once + // + if (slane == lane) { + // + // If current lane + // + if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) { + // + // If the not already set + // Account for rollover. + // + RimData.RdWrDlyUpdates++; + Data->RdWrDly = (UINT8) (xt & RimData.xMax); + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] (NBPtr, &Data->RdWrDly); + } + } + } else { + // + // For Reads, Program all RdDqs Delay Values the same + // For Writes, Program all WrDat Values with same value offset by WrDqs for that lane. + // + if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) { + // + // If the not already set + // Account for rollover. + // + RimData.RdWrDlyUpdates++; + Data->RdWrDly = (UINT8) (xt & RimData.xMax); + MemTSetDQSDelayAllCSR (TechPtr, (UINT8) (xt & RimData.xMax)); + } + } + } + // + // Perform Memory RW test + // + InPhaseResult = 0; + PhaseResult180 = 0; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL); + for (SeedCount = 0; SeedCount < NBPtr->MaxSeedCount; SeedCount++) { + // + // Begin continuous reads and writes on the victim channels + // + NBPtr->FamilySpecificHook[RdWr2DStartVictim] (NBPtr, &SeedCount); + // + // Occasionally check if all trained lanes have already failed + // + if ((NBPtr->MaxSeedCount < 4) || ((SeedCount % (NBPtr->MaxSeedCount / 4)) == 0)) { + InPhaseResult |= CompareInPhase (NBPtr); + PhaseResult180 |= Compare180Phase (NBPtr); + if (((InPhaseResult & NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask) && + ((PhaseResult180& NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask)) { + break; + } + } + } + // + // Obtain the results + // + for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) { + if (!RimData.ParallelSampling && (slane != lane)) { + continue; + } + // + // Calculate the relative offset from legacy trained + // + xo = Get1DTrainedEyeCenter (NBPtr, 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 (NBPtr, slane, yt, xt)) { + // + // Don't overwrite a fail + // + if (xt >= 0) { + if ((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 (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + } else { + if ((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 (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + } + } + } + } + // Decide the next sample point based on the result of the current lane + result = GetPassFailValue (NBPtr, 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 + // + StartAggressors (NBPtr, FALSE); + } + IDS_HDT_CONSOLE (MEM_FLOW,"\n\tSampleCount:%d",RimData.SampleCount); + IDS_HDT_CONSOLE (MEM_FLOW,"\t\tVref Updates:%d",RimData.VrefUpdates); + IDS_HDT_CONSOLE (MEM_FLOW,"\t\tRdDqs Dly Updates:%d\n",RimData.RdWrDlyUpdates); + // + // Finilazing Victim Continuous writes + // + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] (NBPtr, NULL); + TechPtr->ChipSel = InitialCS; + // + // Fill eye based on Rim Search results + // + MemTEyeFill (NBPtr); + // + // Display the results the completed eye + // + MemFRdWr2DDisplaySearch (NBPtr, Data); + // + // Restore environment settings after training + // + if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_RIM_HANDLE, &NBPtr->MemPtr->StdHeader) != AGESA_SUCCESS) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + } + return TRUE; +} + + +/* -----------------------------------------------------------------------------*/ +/** + * Fill the data eye + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + */ +VOID +STATIC +MemTEyeFill ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + INT8 x; + INT8 y; + UINT8 lane; + UINT8 result; + INT8 yLastPass; + UINT8 xMax; + UINT8 yMax; + UINT8 xMin; + UINT8 yMin; + BOOLEAN FirstPassFound; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + xMax = SavedData->xMax; + yMax = SavedData->yMax; + xMin = SavedData->xMin; + yMin = SavedData->yMin; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); 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 (NBPtr, 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 (NBPtr, 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 (NBPtr, 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 (NBPtr, 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 (NBPtr, lane, y, x, result); + FirstPassFound = TRUE; + } else { + ASSERT (FALSE); + } + } // y Loop + } //x Loop + } // Lane Loop +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Get the 1D trained center + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] lane - UINT8 of current lane + * + * @return INT8 - Eye Center + */ +INT8 +STATIC +Get1DTrainedEyeCenter ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane + ) +{ + MEM_TECH_BLOCK *TechPtr; + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + if (TechPtr->Direction == DQS_READ_DIR) { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // Program Byte based for x8 and x16 + return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane]; + } else { + return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (lane >> 1)]; + } + } else { + return (INT8) (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane] - + NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane]); + } +} + + +/* -----------------------------------------------------------------------------*/ +/** + * + * Determine if a Byte Lane result has been saved + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay + * + * @return UINT8 + * 1 - value saved + * 0 - value not saved + */ +UINT8 +STATIC +DetermineSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1); + } else { + return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Determine if a failure has occured + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * + * @return BOOLEAN + * FALSE - Fail + * TRUE - Pass + */ +BOOLEAN +STATIC +CheckForFail ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved, so it is not fail + return TRUE; + } else { + // value saved, so examine result + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // result = fail + return FALSE; + } else { + // result = pass + return TRUE; + } + } + } else { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved, so it is not fail + return TRUE; + } else { + // value saved, so examine result + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // result = fail + return FALSE; + } else { + // result = pass + return TRUE; + } + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Get pass fail state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * + * @return UINT8 + * 0 - Value not saved, + * 2 - Fail, + * 3 - Pass + */ +UINT8 +STATIC +GetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_RD_WR_2D_ENTRY* RdWr2DData; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + RdWr2DData = TechPtr->RdWr2DData; + SavedData = RdWr2DData->SavedData; + + if (x >= 0) { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved + return 0; + } else { + // value saved, so return pass/fail + return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3; + } + } else { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved + return 0; + } else { + // value saved, so return pass/fail + return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3; + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Set the Pass/Fail state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * @param[in] result - result value + * + */ +VOID +STATIC +SetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_RD_WR_2D_ENTRY* RdWr2DData; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + RdWr2DData = TechPtr->RdWr2DData; + SavedData = RdWr2DData->SavedData; + + if (x >= 0) { + RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0; + } else { + RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0; + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Set the save state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * @param[in] result - result value + * + */ +VOID +STATIC +SetSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax))); + } else { + SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax))); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Translate Vref into a positive, linear value that can be used as an + * array index. + * + * @param[in] y - INT8 of the (signed) Vref value + * + * @return UINT8 - Translated Value + */ +INT8 +STATIC +xlateY ( + IN INT8 y + ) +{ + ASSERT ( y > -0x10); + ASSERT ( y < 0x10); + return (y + 0xF) & 0x1F; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Re-walk 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] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +STATIC +ClearSampledPassResults ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 lane; + UINT8 vref; + MEM_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY *RimData; + + ASSERT (NBPtr != NULL); + + Data = (MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData; + RimData = Data->SavedData; + + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) { + for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) { + RimData->LaneSaved[lane].Vref[vref].PosRdWrDly &= ~(Data->Lane[lane].Vref[vref].PosRdWrDly); + Data->Lane[lane].Vref[vref].PosRdWrDly = 0; + RimData->LaneSaved[lane].Vref[vref].NegRdWrDly &= ~(Data->Lane[lane].Vref[vref].NegRdWrDly); + Data->Lane[lane].Vref[vref].NegRdWrDly = 0; + } + } + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Perform in-phase comparison + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT32 - Bitmap of results of comparison + * 1 = FAIL + * 0 = PASS + */ + +UINT32 +STATIC +CompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT32 Result; + Result = 0; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] (NBPtr, &Result); + return Result; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Perform 180 Degree out-of-phase comparison + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT32 - Bitmap of results of comparison + * 1 = FAIL + * 0 = PASS + */ + +UINT32 +STATIC +Compare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT32 Result; + Result = 0; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] (NBPtr, &Result); + return Result; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Program Vref after scaling to accomodate the register definition + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] Vref - UINT8 value of Vref relative to the sample point + * + */ + +VOID +STATIC +ProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Vref + ) +{ + UINT8 ScaledVref; + ScaledVref = Vref; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &ScaledVref); + NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &ScaledVref); +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Turn Aggressor Channels On or Off + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] TurnOn - BOOLEAN + * TRUE - Turn On, False = Turn Off. + */ + +VOID +STATIC +StartAggressors ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN BOOLEAN TurnOn + ) +{ + NBPtr->FamilySpecificHook[TurnOnAggressorChannels] (NBPtr, &TurnOn); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c new file mode 100644 index 0000000000..b8d8429822 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c @@ -0,0 +1,424 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DPatternGeneration.c + * + * Common Northbridge features + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @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 "AdvancedApi.h" +#include "amdlib.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mport.h" +#include "PlatformMemoryConfiguration.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DPATTERNGENERATION_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + + /* -----------------------------------------------------------------------------*/ +/** + * + * This function Initializes the Victim for 2D RdDqs Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +MemFRdWr2DInitVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + RRW_SETTINGS *Rrw; + UINT8 InitialCS; + UINT8 ChipSel; + UINT8 SeedCount; + BOOLEAN OptPatWr2D; + + OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]) && (NBPtr->TechPtr->Direction == DQS_READ_DIR); + InitialCS = NBPtr->TechPtr->ChipSel; + Rrw = &NBPtr->RrwSettings; + // Program the Bubble Count and CmdStreamLen + NBPtr->SetBitField (NBPtr, BFBubbleCnt, 32); + NBPtr->SetBitField (NBPtr, BFBubbleCnt2, 0); + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63); + // Set Comparison Masks + NBPtr->SetBitField (NBPtr, BFDramDqMaskLow, Rrw->CompareMaskLow); + NBPtr->SetBitField (NBPtr, BFDramDqMaskHigh, Rrw->CompareMaskHigh); + // If All Dimms are ECC Capable Test ECC. Otherwise, mask it off + NBPtr->SetBitField (NBPtr, BFDramEccMask, (NBPtr->MCTPtr->Status[SbEccDimms] == TRUE) ? Rrw->CompareMaskEcc : 0xFF); + // Program the Starting Address + NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA); + NBPtr->SetBitField (NBPtr, BFTgtAddressA, Rrw->TgtColAddressA); + NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB); + NBPtr->SetBitField (NBPtr, BFTgtAddressB, Rrw->TgtColAddressB); + NBPtr->SetBitField (NBPtr, BFCmdTgt, CMD_TGT_AB); + // Wait for RRW Engine to be ready and turn it on + NBPtr->PollBitField (NBPtr, BFCmdSendInProg, 0, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 1); + for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { + // Ensure that Odd and Even CS are trained + if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) { + continue; + } + NBPtr->TechPtr->ChipSel = ChipSel; + for (SeedCount = 0; SeedCount < (OptPatWr2D ? NBPtr->MaxSeedCount : 1 ); SeedCount++) { + if (OptPatWr2D) { + // + // Set BankAddress according to seed + // + ASSERT (NBPtr->MaxSeedCount <= 4); + Rrw->TgtBankAddressA = (SeedCount * 2) + CPG_BANK_ADDRESS_A; + Rrw->TgtBankAddressB = (SeedCount * 2) + CPG_BANK_ADDRESS_B; + } + // + // Send ACTIVATE to all Banks + // + // Set Chip select + MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel)); + // Set Bank Address + MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressA); + // Set Row Address + MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressA); + // Send the command + MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1); + // Wait for command complete + MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); + // Set Chip select + MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel)); + // Set Bank Address + MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressB); + // Set Row Address + MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressB); + // Send the command + MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1); + // Wait for command complete + MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); + + if (OptPatWr2D) { + // + // Write the Pattern to the bank pairs for each seed. + // + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL); + NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA); + NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB); + // Program the PRBS Seed + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount); + // + // Enable continuous writes on the victim channels + // + // Set the Command Count + NBPtr->SetBitField (NBPtr, BFCmdCount, 256); + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0. + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + } + } + } + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + // + // Do not use LFSR Rollover during Wr Training + // + NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 1); + } + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function Initializes the Victim chipSelects for 2D Read or Write + * Training Continuous Writes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +MemFRdWr2DInitVictimChipSel ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + NBPtr->SetBitField (NBPtr, BFTgtChipSelectA, NBPtr->TechPtr->ChipSel); + NBPtr->SetBitField (NBPtr, BFTgtChipSelectB, NBPtr->TechPtr->ChipSel); + NBPtr->SetBitField (NBPtr, BFResetAllErr, 1); + return TRUE; +} + + + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function checks the In Phase Error status bits for comparison + * results for RD/WR 2D training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[out] *Result - Pointer to UINT32 for storing results + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DCompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ) +{ + *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErrSts); + return TRUE; +} + + /*-----------------------------------------------------------------------------*/ +/** + * + * This function checks the 180 Error status bits for RD/WR 2D training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[out] *Result - Pointer to UINT32 for storing results + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DCompare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ) +{ + *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErr180Sts); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function starts the Victim for 2D RdDqs Training Continuous Writes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] SeedCountPtr - UINT8 Pointer to Seed count + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DStartVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *SeedCountPtr + ) +{ + BOOLEAN OptPatWr2D; + UINT32 CommandCount; + UINT8 SeedCount; + + ASSERT (NBPtr != NULL); + ASSERT (SeedCountPtr != NULL); + + SeedCount = *(UINT8*)SeedCountPtr; + + OptPatWr2D = FALSE; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]); + CommandCount = NBPtr->TotalBitTimes2DRdTraining / (8 * NBPtr->MaxSeedCount * NBPtr->MaxAggressorDimms[NBPtr->Dct]); + } else { + CommandCount = 256; + } + + // Program the PRBS Seed + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount); + if (OptPatWr2D) { + // + // Set BankAddress according to seed + // + NBPtr->SetBitField (NBPtr, BFTgtBankA, (SeedCount * 2) + CPG_BANK_ADDRESS_A); + NBPtr->SetBitField (NBPtr, BFTgtBankB, (SeedCount * 2) + CPG_BANK_ADDRESS_B); + } else { + // + // Enable continuous writes on the victim channels + // + // Set the Command Count + NBPtr->SetBitField (NBPtr, BFCmdCount, 256); + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0. + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + } + // + // Enable continuous reads on the victim channels + // + // Set the Command Count + ASSERT (NBPtr->MaxAggressorDimms[NBPtr->Dct] != 0); + // + NBPtr->SetBitField (NBPtr, BFCmdCount, CommandCount ); + // Reset All Errors and Disable StopOnErr + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_READ); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0 + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + //} + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finalizes the Victim for 2D RdDqs Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - Success + */ +BOOLEAN +MemFRdWr2DFinalizeVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + UINT8 InitialCS; + UINT8 ChipSel; + InitialCS = NBPtr->TechPtr->ChipSel; + NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 0); + for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { + // Ensure that Odd and Even CS are precharged + if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) { + continue; + } + NBPtr->TechPtr->ChipSel = ChipSel; + // Send the Precharge All Command + MemNRrwPrechargeCmd (NBPtr, ChipSel, PRECHARGE_ALL_BANKS); + } + // Turn Off the RRW Engine + NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 0); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Data Pattern that will be sent and compared + * against. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *PatternIndexPtr - Pointer to a generic index used to + * determine which pattern to program. + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DProgramDataPattern ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ) +{ + UINT8 SeedCount; + UINT32 PrbsSeed; + CONST STATIC UINT32 PrbsSeedTbl[4] = {0x7ea05, 0x44443, 0x22b97, 0x3f167}; + CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251}; + + ASSERT (NBPtr != 0); + ASSERT (PatternIndexPtr != NULL); + SeedCount = *(UINT8*)PatternIndexPtr; + ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1)); + // + // Program the Command Stream Length + // + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]); + } else { + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63); + } + PrbsSeed = PrbsSeedTbl[SeedCount % GET_SIZE_OF (PrbsSeedTbl)]; + ASSERT (PrbsSeed != 0); + // + // Program the PRBS Seed + // + NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed); + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c new file mode 100644 index 0000000000..6a9e873e94 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c @@ -0,0 +1,1368 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DTraining.c + * + * Common Read/Write 2D Training Feature Function + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @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 "Ids.h" +#include "AdvancedApi.h" +#include "GeneralServices.h" +#include "heapManager.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" + +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DTRAINING_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[]; + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function initializes the 2D Read/Write Training Feature Hooks. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE - Function was implemented + */ + +BOOLEAN +MemFRdWr2DTrainingInit ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + ASSERT (NBPtr != NULL); + NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining; + NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig; + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelect; + NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVref; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVref; + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays; + NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch; + NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel; + NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim; + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase; + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPattern; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function executes 2D training for Read DQS or Write DQ + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + */ + +BOOLEAN +MemFAmdRdWr2DTraining ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID* OptParam + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_DATA_STRUCT *MemPtr; + MEM_RD_WR_2D_ENTRY Data; + RD_WR_2D *VrefPtr; + PSO_TABLE *PsoTable; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + UINT8 Dct; + UINT8 ChipSel; + UINT8 Lane; + UINT8 Vref; + UINT8 MaxLanes; + UINT8 TmpLanes; + UINT8 TotalDlyRange; + BOOLEAN Status; + // + // Initialize Pointers + // + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + MemPtr = NBPtr->MemPtr; + PsoTable = MemPtr->ParameterListPtr->PlatformMemoryConfiguration; + // + // Set environment settings before training + // + AGESA_TESTPOINT (TpProcMem2dRdDqsTraining, &(MemPtr->StdHeader)); + MemTBeginTraining (TechPtr); + // + // Allocate heap for the 2D RdWR/Vref Data structure + // + MaxLanes = 0; + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + TmpLanes = MemFRdWr2DGetMaxLanes (NBPtr); + MaxLanes = MAX (MaxLanes, TmpLanes); + } + + AllocHeapParams.RequestedBufferSize = MaxLanes * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D); + AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) == AGESA_SUCCESS) { + VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr; + } else { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + return TRUE; + } + for (Lane = 0; Lane < MaxLanes; Lane++) { + Data.Lane[Lane].Vref = &VrefPtr[Lane * NBPtr->TotalMaxVrefRange]; + } + // + // Setup hardware training engine + // + TechPtr->TrainingType = TRN_DQS_POSITION; + NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType); + + Data.Vnom = NBPtr->TotalMaxVrefRange / 2; // Set Nominal Vref + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + Data.MaxRdWrSweep = TotalDlyRange / 2; // Set Max Sweep Size + ASSERT (TotalDlyRange <= MAX_RD_WR_DLY_ENTRIES); + // + // Execute 2d Rd DQS training for all Dcts/Chipselects + // + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); + NBPtr->SwitchDCT (NBPtr, Dct); + NBPtr->Vref = 0xFF; + Status = FALSE; + if (NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] (NBPtr, NULL)) { + for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) { + if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + // + //Initialize storage + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (Vref = 0; Vref < NBPtr->TotalMaxVrefRange; Vref++) { + Data.Lane[Lane].Vref[Vref].PosRdWrDly = 0; + Data.Lane[Lane].Vref[Vref].NegRdWrDly = 0; + } + } + TechPtr->ChipSel = ChipSel; + IDS_HDT_CONSOLE (MEM_FLOW,"\tChip Select: %02x \n", TechPtr->ChipSel); + // + // 1. Sample the data eyes for each channel: + // + TechPtr->RdWr2DData = &Data; + if (NBPtr->FamilySpecificHook[RdWr2DDataCollection] (NBPtr, NULL)) { + // + // 2. Process the array of results with a diamond convolution mask, summing the number passing sample points. + // + // Determine Diamond Mask Height + if (MemFRdWr2DHeight (NBPtr, &Data)) { + // + // Apply Mask + // + if (MemFRdWr2DApplyMask (NBPtr, &Data)) { + // + // Convolution + // + if (MemFRdWr2DProcessConvolution (NBPtr, &Data)) { + // + // 3. Program the final DQS delay values. + // + if (MemFRdWr2DProgramMaxDelays (NBPtr, &Data)) { + // + // Find the Smallest Positive or Negative Margin for current CS + // + if (MemFRdWr2DFindCsVrefMargin (NBPtr, &Data)) { + Status = TRUE; + } + } + } + } + } + } + if (Status == FALSE) { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_ERROR : MEM_ERROR_2D_WRDAT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } + } + // + // Find the Max and Min Vref values for each DCT + // + if (Status == TRUE) { + if (MemFRdWr2DFinalVrefMargin (NBPtr, &Data)) { + // + // Program the Max Vref value + // + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Final Vref for channel\n"); + NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &NBPtr->ChannelPtr->MaxVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tActual Vref programmed = %02x\n", + ( NBPtr->GetBitField (NBPtr, BFVrefDAC) >> TSEFO_END (NBPtr->NBRegTable[BFVrefDAC])) ); + Status = TRUE; + } else { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_VREF_MARGIN_ERROR: MEM_ERROR_2D_WRDAT_VREF_MARGIN_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } + } + } + // + // Restore environment settings after training + // + if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_HANDLE, &MemPtr->StdHeader) != AGESA_SUCCESS) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + } + IDS_HDT_CONSOLE (MEM_STATUS, "\tEnd\n"); + MemTEndTraining (TechPtr); + + IDS_HDT_CONSOLE (MEM_FLOW, "\n\nEnd %s 2D training\n\n",(TechPtr->Direction == DQS_READ_DIR) ? "Read DQS":"Write DQ"); + return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); +} +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines whether the current configuration is a valid + * config for applying 2D Training + * @todo: Update to work for 2D WR and 2D RD training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - Configuration valid + * FALSE - Configuration invalid + * + */ +BOOLEAN +MemFCheckRdWr2DTrainingPerConfig ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + UINT8 i; + if (NBPtr->RefPtr->ForceTrainMode == FORCE_TRAIN_AUTO) { + i = 0; + while (memPlatSpecFlowArray[i] != NULL) { + if ((memPlatSpecFlowArray[i])->S2D (NBPtr, (memPlatSpecFlowArray[i])->EntryOfTables)) { + return TRUE; + } + i++; + } + } + return FALSE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the maximum number of lanes for puposes of 2D + * Read or Write training. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT8 - Max Number of Lanes + * + */ +UINT8 +MemFRdWr2DGetMaxLanes ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 MaxLanes; + + TechPtr = NBPtr->TechPtr; + if ((TechPtr->Direction == DQS_READ_DIR) && ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) != 0)) { + // Per Nibble + MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 18 : 16; + } else { + // Per Byte + MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 9 : 8; + } + return MaxLanes; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref to internal or external control for 2D Read + * or Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - External Vref was selected + * FALSE - Internal Vref was selected + * + */ +BOOLEAN +MemFRdWr2DProgramIntExtVrefSelect ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0000 : 0x0001)); + } + return NBPtr->RefPtr->ExternalVrefCtl; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function scales Vref from the range used in Data Collection to + * the range that is programmed into the register. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale. + * + * @return BOOLEAN + * TRUE Function was implemented + * + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ) +{ + *(UINT8*)Vref = *(UINT8*)Vref * 2; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref for 2D Read/Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *VrefPtr - Pointer to Vref value + * + * @return BOOLEAN + * TRUE - Success + * FAIL (External Callout only) + * + */ +BOOLEAN +MemFRdWr2DProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ) +{ + AGESA_STATUS Status; + MEM_DATA_STRUCT *MemPtr; + ID_INFO CallOutIdInfo; + VOLTAGE_ADJUST Va; + UINT8 Vref; + + ASSERT (NBPtr != NULL); + ASSERT (VrefPtr != NULL); + MemPtr = NBPtr->MemPtr; + Vref = *(UINT8*)VrefPtr; + CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId; + CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId; + LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader); + Va.MemData = MemPtr; + Status = AGESA_SUCCESS; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) { + // + // Internal vref control + // + // This is 1/2 VrefDAC value Sign bit is shifted into place. + // + ASSERT (Vref < 32); + if (Vref < 15) { + Vref = (31 - Vref) << 1; + } else { + Vref = (Vref - 15) << 1; + } + NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 2); + } else { + // External vref control + AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref; + IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n"); + Va.VoltageType = VTYPE_CPU_VREF; + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + } + } else { + // + // DIMM Vref Control + // + Va.VoltageType = VTYPE_DIMM_VREF; + // + // Offset by 15 and multiply by 2. + // + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + if (Status != AGESA_SUCCESS) { + IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *"); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + return (Status == AGESA_SUCCESS) ? TRUE : FALSE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Read DQS or Write DQ Delay values for Read/Write + * Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Delay - Pointer to UINT8 containing Delay value + * + * @return BOOLEAN + * TRUE + * + */ + +BOOLEAN +MemFRdWr2DProgramDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *Delay + ) +{ + UINT32 RdDqsTime; + UINT8 RdWrDly; + + ASSERT (NBPtr != 0); + ASSERT (Delay != 0); + RdWrDly = *(UINT8*) Delay; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + // This function should only be used for read training + ASSERT (NBPtr->TechPtr->Direction == DQS_READ_DIR); + // Program BL registers for both nibble (x4) and bytes (x8, x16) + RdDqsTime = 0; + RdDqsTime = (RdWrDly & 0x1F) << 8; + RdDqsTime = RdDqsTime | (RdWrDly & 0x1F); + if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 0) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm0Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 1) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm1Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 2) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm2Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 3) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm3Broadcast, RdDqsTime); + } + } else { + MemTSetDQSDelayAllCSR (NBPtr->TechPtr, RdWrDly); + } + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function stores data for 2D Read DQS and Write DQ Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] *InPhaseResult[] - Array of inphase results + * @param[in] *PhaseResult180[] - Array of Phase 180 results + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +VOID +MemFRdWr2DStoreResult ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT32 InPhaseResult[], + IN UINT32 PhaseResult180[] + ) +{ + UINT8 Lane; + UINT8 Vref; + UINT8 RdWrDly; + UINT32 Result; + UINT32 Result180; + UINT8 Index; + Vref = NBPtr->Vref; + RdWrDly = Data->RdWrDly; + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (RdWrDly = 0; RdWrDly < Data->MaxRdWrSweep; RdWrDly++) { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (NBPtr->TechPtr->ChipSel >> 1))) == 0) { + // x8, so combine "Nibble X" and "Nibble X+1" results + Index = Lane * 2; + Result = (InPhaseResult[RdWrDly] >> Index) & 0x03; + Result180 = (PhaseResult180[RdWrDly] >> Index) & 0x03; + } else { + // x4, so use "Nibble" results + Result = (InPhaseResult[RdWrDly] >> Lane) & 0x01; + Result180 = (PhaseResult180[RdWrDly] >> Lane) & 0x01; + } + Data->Lane[Lane].Vref[Vref].PosRdWrDly |= (Result == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0; + Data->Lane[Lane].Vref[Vref].NegRdWrDly |= (Result180 == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0; + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the height of data for 2D Read and Write training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + */ +BOOLEAN +MemFRdWr2DHeight ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 Lane; + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + Data->Lane[Lane].HalfDiamondHeight = 0x0F; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Lane: "); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tHeight: "); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (2*(Data->Lane[Lane].HalfDiamondHeight) + 1)); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the width for 2D RdDQS and WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return UINT8 Width + */ +UINT8 +MemFGetRdWr2DWidth ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + return NBPtr->DiamondWidthRd; + } else { + return NBPtr->DiamondWidthWr; + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the step height for the diamond mask for 2D RdDQS or + * WrDat Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] Vref - current Vref value + * @param[in] Lane - current Lane + * + * @return BOOLEAN + * TRUE - Step found and value should be updated + * FALSE - Step not found and value should not be updated + * + */ +BOOLEAN +MemFCheckRdWr2DDiamondMaskStep ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT8 Vref, + IN UINT8 Lane + ) +{ + UINT8 M; + UINT8 VrefVal; + UINT8 width; + UINT8 i; + BOOLEAN status; + // m = -1 * height/width + // (y-b)/m = x + status = FALSE; + if (Vref > (Data->Vnom - 1)) { + VrefVal = (Vref + 1) - Data->Vnom; + } else { + VrefVal = Vref; + } + width = (MemFGetRdWr2DWidth (NBPtr, Data) - 1) / 2; + M = Data->Lane[Lane].HalfDiamondHeight / width; + i = 1; + while (i <= Data->Lane[Lane].HalfDiamondHeight) { + i = i + M; + if (VrefVal == i) { + status = TRUE; + } + } + return status; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function applies a mask for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + * + */ +BOOLEAN +MemFRdWr2DApplyMask ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 RdWrDly; + UINT8 Lane; + UINT8 Height; + UINT8 Width; + UINT32 PosNegData; + UINT8 Vref; + UINT8 count; + UINT8 Dly; + UINT8 endWidth; + UINT8 startWidth; + UINT8 origEndWidth; + UINT8 origStartWidth; + UINT8 maxOverLapWidth; + UINT8 startOverLapWidth; + UINT8 TotalDlyRange; + BOOLEAN maxHeightExceeded; + BOOLEAN negVrefComplete; + BOOLEAN PosRdWrToNegRdWr; + BOOLEAN NegRdWrToPosRdWr; + + TechPtr = NBPtr->TechPtr; + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + // + // Initialize Convolution + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + Data->Lane[Lane].Convolution[RdWrDly] = 0; + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly); + } + } + endWidth = 0; + startWidth = 0; + origEndWidth = 0; + origStartWidth = 0; + startOverLapWidth = 0; + maxOverLapWidth = 0; + maxHeightExceeded = FALSE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Width"); + // + // Get the Width of Diamond + // + Width = MemFGetRdWr2DWidth (NBPtr, Data); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWidth: %02x\n", Width); + ASSERT (Width != 0); + + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tExecuting convolution function\n"); + // + // Perform the convolution by sweeping the mask function centered at nominal Vref. Results in a one + // dimensional array with FOM values at each delay for each lane. Choose the delay setting at the peak + // FOM value. + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + Height = Data->Lane[Lane].HalfDiamondHeight; + ASSERT (Height < Data->Vnom); + // + // RdWrDly is divided around "Data->MaxRdWrSweep" into positive and negative directions + // Positive direction -> RdWrDly = 0 to (Data->MaxRdWrSweep - 1) + // Negative direction -> RdWrDly = Data->MaxRdWrSweep to (TotalDlyRange - 1) + // + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + // Vref loop is divided around "Data->Vnom - 1" into positive and negative directions + // Negative direction -> Vref = 0 ("Data->Vnom - 1") to Height("Data->Vnom - 1" - Height) + // Positive direction -> Vref = "Data->Vnom" to Height("Data->Vnom" + Height) + // + negVrefComplete = FALSE; + PosRdWrToNegRdWr = FALSE; + NegRdWrToPosRdWr = FALSE; + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Initial negative direction where Vref = 0 ("Data->Vnom - 1"), so we need to set + // initial startWidth and endWidth for +/- RdDqs + // + // Create common delay based on +/- RdDqs + if (RdWrDly > (Data->MaxRdWrSweep - 1)) { + Dly = RdWrDly - Data->MaxRdWrSweep; + } else { + Dly = RdWrDly; + } + if (Vref == 0 ) { + // Initialize -Vref + maxHeightExceeded = FALSE; // reset for start of -Vref + // Case 1: if +RdDqs - Check for lower bound (Width/2 > RdDqs > 0) + // : if -RdDqs - Check for lower bound (Width/2 + Data->MaxRdDqsSweep > RdDqs > Data->MaxRdDqsSweep) + if (Dly < Width / 2) { + endWidth = Dly + Width / 2 + 1; + startWidth = 0; + } else if ((Dly + Width / 2) > (Data->MaxRdWrSweep - 1)) { + // Case 2: if +RdWr - Check for upper bound ((Data->MaxRdWrSweep - 1) < RdWr < ((Data->MaxRdWrSweep - 1) - Width/2)) + // : if -RdWr - Check for lower bound ((DatNBPtr->TotalRdWrDlyRange - 1) < RdWr < ((NBPtr->TotalRdWrDlyRange - 1) - Width/2)) + endWidth = Data->MaxRdWrSweep; + startWidth = Dly - Width / 2; + } else { + // Set the initial "startWidth" and "endWidth" for +/- RdDqs + endWidth = Dly + Width / 2 + 1; + startWidth = Dly - Width / 2; + } + origEndWidth = endWidth; + origStartWidth = startWidth; + } else if (Vref == Data->Vnom) { + // Initialize +Vref + endWidth = origEndWidth; + startWidth = origStartWidth; + maxHeightExceeded = FALSE; // reset for start of +Vref + negVrefComplete = TRUE; + } else if ((Vref > (Data->Vnom + Height)) && negVrefComplete == TRUE) { + break; //switch to next RdDqs Dly if height exceeded for +vref and -vref complete + } else { + if (startWidth >= endWidth) { + if (RdWrDly == (TotalDlyRange - 1)) { + // Special condition for end of -RdDqs range + startWidth = Data->MaxRdWrSweep - 1; + endWidth = Data->MaxRdWrSweep; + } else { + // Width = 0, but Height not reached, + startWidth = Dly; + endWidth = Dly + 1; + } + } else { + // Check for Case 1 and Case 2 above + if ((RdWrDly + Width / 2) > (TotalDlyRange - 1)) { + endWidth = origEndWidth; + } + } + maxHeightExceeded = FALSE; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + if (Lane == 0) { + if (RdWrDly == (Data->MaxRdWrSweep - (Width / 2)) ) { + Data->DiamondLeft[Vref] = startWidth; + Data->DiamondRight[Vref] = endWidth - 1; + } + } + ); + // + // Determine the correct Delay (+/-) and Vref (+/-)direction + // + if (maxHeightExceeded == FALSE) { + if (RdWrDly < Data->MaxRdWrSweep) { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; // +RdWr Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly; // +RdDqs Dly, -Vref + } + } else { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly; // -RdWr Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly; // -RdWr Dly, -Vref + } + } + // + // Case 1: Non-overlap condition: + // Count the number of passes from "startWidth" to "endWidth" + // + for (count = startWidth; count < endWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + // Case 2: Overlay between +RdWr and -RdWr starting from +RdWr + // Count the number of passes from "startWidth" to "endWidth" + // + if ((RdWrDly <= (Data->MaxRdWrSweep - 1) && (RdWrDly > ((Data->MaxRdWrSweep - 1) - Width / 2)))) { + startOverLapWidth = 0; + if (Vref == 0 || Vref == Data->Vnom) { + maxOverLapWidth = (RdWrDly + Width / 2) - (Data->MaxRdWrSweep - 1); // Initial overlap max width size + } else if (maxOverLapWidth == 0) { + maxOverLapWidth = startOverLapWidth; // Stop counting after overlap region complete + } + // Ensure that +/- vref is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + if (maxOverLapWidth > 0) { + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + maxOverLapWidth--; // Reduce overlap width outside of diamond mask + } + PosRdWrToNegRdWr = TRUE; + } + } + if (((RdWrDly - Data->MaxRdWrSweep) < Width / 2) && (RdWrDly > (Data->MaxRdWrSweep - 1))) { + // + // Case 3: Overlay between -RdDqs and +RdDqs starting from -RdDqs + // Count the number of passes from "startWidth" to "endWidth" + // + maxOverLapWidth = Data->MaxRdWrSweep; + if (Vref == 0 || Vref == Data->Vnom) { + startOverLapWidth = RdWrDly - Width / 2; // Initial overlap start point + } else if (startOverLapWidth > maxOverLapWidth) { + maxOverLapWidth = maxOverLapWidth - 1; // Continue to count until MaxHeight excceded + } + // Ensure that vref + or - is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + if (startOverLapWidth < maxOverLapWidth) { + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + startOverLapWidth++; // Reduce overlap width outside of diamond mask + } + NegRdWrToPosRdWr = TRUE; + } + } + } + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + if (PosRdWrToNegRdWr) { + startWidth++; + endWidth = Data->MaxRdWrSweep; + PosRdWrToNegRdWr = FALSE; + } else if (NegRdWrToPosRdWr) { + startWidth = 0; + endWidth--; + NegRdWrToPosRdWr = FALSE; + } else { + startWidth++; + endWidth--; + } + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly); + } + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Diamond Shape: \n"); + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (RdWrDly = (Data->MaxRdWrSweep - Width); RdWrDly < Data->MaxRdWrSweep; RdWrDly++) { + if (Vref < (Data->Vnom - 1)) { + if (RdWrDly == Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdWrDly == Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) - (Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } else { + if (RdWrDly == Data->DiamondLeft[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdWrDly == Data->DiamondRight[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[Vref - (Data->Vnom - 1)]) - (Data->DiamondLeft[Vref - (Data->Vnom - 1)])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t Convolution results after processing raw data:\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Delay: "); + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x ", RdWrDly <= (Data->MaxRdWrSweep - 1) ? (Data->MaxRdWrSweep - 1) - RdWrDly : (TotalDlyRange - 1) - RdWrDly); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tConv: "); + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%03x ", Data->Lane[Lane].Convolution[RdWrDly]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + ); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function Examines the convolution function and determines the Max Delay + * for 2D RdDQS and WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ + +BOOLEAN +MemFRdWr2DProcessConvolution ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 RdWrDly; + UINT8 Lane; + UINT16 MaxFOM; + UINT8 MaxRange; + UINT8 CurrRange; + UINT8 TotalDlyRange; + BOOLEAN status; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + status = TRUE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Delay based on Convolution function\n"); + // Determine the Max RdDqs or WrDat Dly for the convolution function + // - Choose the delay setting at the peak FOM value. + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + // Find largest value as MaxFOM + MaxFOM = 0; + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + if (Data->Lane[Lane].Convolution[RdWrDly] > MaxFOM) { + MaxFOM = Data->Lane[Lane].Convolution[RdWrDly]; + } + } + status = MaxFOM > 0 ? TRUE : FALSE; // It is an error if all convolution points are zero + + // Then find the midpoint of the largest consecutive window w/ that MaxFOM + // In cases of an even number of consecutive points w/ that MaxFOM exists, + // choose the midpoint to the right + // All things being equal, favor the right side of a bi-modal eye + // Stressful SSO patterns shift the eye right! + MaxRange = 0; + CurrRange = 0; + for (RdWrDly = 0; (MaxFOM > 0) && RdWrDly < TotalDlyRange; RdWrDly++) { + if (Data->Lane[Lane].Convolution[RdWrDly] == MaxFOM) { + CurrRange++; + if (CurrRange >= MaxRange) { + Data->Lane[Lane].MaxRdWrDly = RdWrDly - ((CurrRange - 1) / 2); + MaxRange = CurrRange; + } + } else { + CurrRange = 0; + } + } + + if (Data->Lane[Lane].MaxRdWrDly > Data->MaxRdWrSweep) { + status = FALSE; // Error + } + // Set Actual register value + if (Data->Lane[Lane].MaxRdWrDly < Data->MaxRdWrSweep) { + Data->Lane[Lane].MaxRdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly; + } else { + status = FALSE; // Error + } + } + + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Cs %d Lane: ", TechPtr->ChipSel); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Max %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Data->Lane[Lane].MaxRdWrDly); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t1D Trained %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + if (TechPtr->Direction == DQS_READ_DIR) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane]); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] - \ + NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane])); + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + + if (status == FALSE) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_INVALID_2D_RDDQS_VALUE, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); + } + return status; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Max Rd Dqs or Max Wr DQ for 2D training from + * convolution + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DProgramMaxDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 Lane; + UINT8 LaneHighRdDqs2dDlys; + UINT8 LaneLowRdDqs2dDlys; + UINT8 MaxWrDatDly; + TechPtr = NBPtr->TechPtr; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Max %s Delay per Lane\n\n", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + if ( TechPtr->Direction == DQS_WRITE_DIR || (NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // Program Byte based for x8 and x16 + if ( TechPtr->Direction == DQS_READ_DIR) { + // + // Read DQS Training + // + NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly); + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = Data->Lane[Lane].MaxRdWrDly; + } else { + // + // Write DQ Training + // + MaxWrDatDly = (UINT8) (Data->Lane[Lane].MaxRdWrDly + TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + Lane]); + NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), MaxWrDatDly); + NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = MaxWrDatDly; + } + } else { + ASSERT (TechPtr->Direction == DQS_READ_DIR); + // Program nibble based x4, so use "Nibble" + NBPtr->SetTrainDly (NBPtr, AccessRdDqs2dDly, DIMM_NBBL_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly); + NBPtr->ChannelPtr->RdDqs2dDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_NUMBER_LANES + Lane] = Data->Lane[Lane].MaxRdWrDly; + // For each pair of nibbles (high (Odd Nibble) and Low (Even nibble)), find the largest and use that as the RdDqsDly value + if ((Lane & 0x1) == 0) { + LaneHighRdDqs2dDlys = Data->Lane[Lane + 1].MaxRdWrDly; + LaneLowRdDqs2dDlys = Data->Lane[Lane].MaxRdWrDly; + if (LaneHighRdDqs2dDlys > LaneLowRdDqs2dDlys) { + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneHighRdDqs2dDlys; + } else { + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneLowRdDqs2dDlys; + } + } + NBPtr->DctCachePtr->Is2Dx4 = TRUE; + } + } + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the Positive and negative Vref Margin for the current CS + * for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DFindCsVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 SmallestMaxVrefNeg; + UINT8 Lane; + UINT8 RdWrDly; + UINT8 Vref; + UINT8 MaxVrefPositive; + UINT8 MaxVrefNegative; + UINT8 SmallestMaxVrefPos; + UINT32 PosNegData; + SmallestMaxVrefPos = 0xFF; + SmallestMaxVrefNeg = 0; + MaxVrefPositive = 0; + MaxVrefNegative = 0xFF; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Smallest Max Positive and Negative Vref\n\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + RdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly; + for (Vref = 0; Vref < (Data->Vnom - 1); Vref++) { + // Neg Vref - (searching from top of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 1) { + MaxVrefNegative = Vref; + break; + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + for (Vref = (Data->Vnom - 1); Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Pos Vref - (searching from Vnom + 1 of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 0) { + // Convert to register setting + MaxVrefPositive = Vref - 1;// - Data->Vnom; + break; + } else { + // If Vref = 1F passes, then smallest Vref = 0x1F + if (Vref == ((NBPtr->TotalMaxVrefRange - 1) - 1)) { + MaxVrefPositive = 0x1E; + break; + } + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + if (MaxVrefPositive < SmallestMaxVrefPos) { + // Find the smallest Max Pos Vref + SmallestMaxVrefPos = MaxVrefPositive; + } + if (MaxVrefNegative > SmallestMaxVrefNeg) { + // Find the largest Max Neg Vref + SmallestMaxVrefNeg = MaxVrefNegative; + } + } + if (SmallestMaxVrefPos != (Data->Vnom - 2)) { + Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = SmallestMaxVrefPos - Data->Vnom + 1; + } else { + Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = 0; + } + Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel] = (Data->Vnom - 1) - SmallestMaxVrefNeg; + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Positive Vref Offset from V-Nom for ChipSel %02x = + %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel]); + if (Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] == 0) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = 00\n"); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = - %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel]); + } + ); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the final Vref Margin for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DFinalVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 ChipSel; + UINT8 SmallestMaxPosVref; + UINT8 SmallestMaxNegVref; + UINT8 OffsetFromVref; + UINT8 Vnom; + SmallestMaxNegVref = 0x7F; + SmallestMaxPosVref = 0x7F; + Vnom = (Data->Vnom - 1); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Final Vref for channel\n\n"); + for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) { + if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + if (Data->SmallestPosMaxVrefperCS[ChipSel] < SmallestMaxPosVref) { + SmallestMaxPosVref = Data->SmallestPosMaxVrefperCS[ChipSel]; + } + if (Data->SmallestNegMaxVrefperCS[ChipSel] < SmallestMaxNegVref) { + SmallestMaxNegVref = Data->SmallestNegMaxVrefperCS[ChipSel]; + } + } + } + // + // Synchronize minimum and Maximums with other Vref values + // + if (NBPtr->TechPtr->Direction == DQS_WRITE_DIR) { + if (NBPtr->SharedPtr->CommonSmallestMaxNegVref < SmallestMaxNegVref) { + SmallestMaxNegVref = NBPtr->SharedPtr->CommonSmallestMaxNegVref; + } else { + NBPtr->SharedPtr->CommonSmallestMaxNegVref = SmallestMaxNegVref; + } + if (NBPtr->SharedPtr->CommonSmallestMaxPosVref < SmallestMaxPosVref) { + SmallestMaxPosVref = NBPtr->SharedPtr->CommonSmallestMaxPosVref; + } else { + NBPtr->SharedPtr->CommonSmallestMaxPosVref = SmallestMaxPosVref; + } + } + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxPosVref); + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxNegVref); + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &Vnom); + + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Positive = + %02x\n", SmallestMaxPosVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Negative =%s%02x\n", ((SmallestMaxNegVref != 0) ? " - " : " "), SmallestMaxNegVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Vnom = %02x\n", Vnom); + + if (SmallestMaxPosVref > SmallestMaxNegVref) { + OffsetFromVref = (SmallestMaxPosVref - SmallestMaxNegVref) / 2; + NBPtr->ChannelPtr->MaxVref = Vnom + OffsetFromVref; + } else { + OffsetFromVref = (SmallestMaxNegVref - SmallestMaxPosVref) / 2; + NBPtr->ChannelPtr->MaxVref = Vnom - OffsetFromVref; + } + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tFinal Vref Offset From Vnom =%s%02x\n", + ((OffsetFromVref != 0) ? ((SmallestMaxPosVref > SmallestMaxNegVref) ? " + ":" - "):" "), OffsetFromVref); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function displays ther results of the 2D search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + */ +VOID +MemFRdWr2DDisplaySearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + IDS_HDT_CONSOLE_DEBUG_CODE ( + UINT8 Lane; + INT8 Vref; + // Display data collected + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDisplaying Data collected\n\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Vref %s\n", (NBPtr->TechPtr->Direction == DQS_READ_DIR) ? "NegRdDqs PosRdDqs" : "PosWrDat"); + for (Vref = NBPtr->TotalMaxVrefRange - 2; Vref >= 0; Vref--) { + if (Vref < (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t - "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", ((Data->Vnom -1) - Vref)); + } else if (Vref == (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t 00 "); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t + "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Vref - (Data->Vnom - 1)); + } + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + IDS_HDT_CONSOLE (MEM_FLOW, "%08x", Data->Lane[Lane].Vref[Vref].NegRdWrDly); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + IDS_HDT_CONSOLE (MEM_FLOW, "%08x \n", Data->Lane[Lane].Vref[Vref].PosRdWrDly); + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + } + ) +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h new file mode 100644 index 0000000000..a2a4b02dda --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h @@ -0,0 +1,266 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mf2DRdWrTraining.h + * + * Common Definitions ot support 2D Read/Write Training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/2DRdWrTraining) + * @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. + * *************************************************************************** + * + */ + +#ifndef _MFRDWR2DTRAINING_H_ +#define _MFRDWR2DTRAINING_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ +#define MAX_2D_VREF_ENTRIES 0x20 ///< Maximum number of vref entries +#define MAX_RD_WR_DLY_ENTRIES 0x40 ///< Maximum number of RD/WR Delay Entries +#define VREF_ADDITIONAL_STEP_SIZE 0x0 ///< Vref Additional Step size +#define RDDQS_ADDITIONAL_STEP_SIZE 0x0 ///< RdDqs Additional Step size +#define MAX_2D_RD_SEED_COUNT 4 ///< Max Seed count +#define MAX_2D_RD_WR_CS_PER_CHANNEL 8 ///< Max CS per Rd Wr delay group + +#define EYERIM_PARALLEL_SAMPLING TRUE +#define EYERIM_BROADCAST_DELAYS TRUE + +/// Structure for RD DQS 2D training RDDQS delays. +typedef struct { + UINT32 PosRdWrDly; ///< Positive RdDQS Delay + UINT32 NegRdWrDly; ///< Negative RdDQS Delay +} RD_WR_2D; + +/// Structure for RD DQS 2D training Vref delays +typedef struct { + RD_WR_2D *Vref; ///< Pointer to Vref Entries + UINT16 Convolution[MAX_RD_WR_DLY_ENTRIES]; ///< Total number of passes in each mask + UINT8 PosHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Positive Vref height Height per Delay + UINT8 NegHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Negative Vref height Height per Delay + UINT8 HalfDiamondHeight; ///< Half of the Height per BL (height of pos/neg vref) + UINT8 MaxRdWrDly; ///< Max RdWrDly from convolution function +} VREF_RD_WR_2D; + +/// Structure for RD DQS 2D training Nibbles +typedef struct { + VREF_RD_WR_2D Lane[MAX_NUMBER_LANES]; ///< Bytelane or Nibble + UINT8 Vnom; ///< Nominal Vref value + UINT8 MaxRdWrSweep; ///< Maximum RdDqs or WrDat Sweep size + UINT8 SmallestPosMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Positive Max Vref per CS + UINT8 SmallestNegMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Negative Max Vref per CS + UINT8 DiamondLeft[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display + UINT8 DiamondRight[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display + UINT8 RdWrDly; ///< RdDQS or WrDat Dly setting + VOID* SavedData; ///< Algorithm-specific saved data +} MEM_RD_WR_2D_ENTRY; + +/// Structure used for RD WR 2D Eye Rim Search Algorithm +typedef struct { + VREF_RD_WR_2D LaneSaved[MAX_NUMBER_LANES]; ///< Bytelane or Nibble that was saved + INT8 xMin; ///< Minimum value for RdDqs delays + INT8 xMax; ///< Maximum value for RdDqs delays + INT8 yMin; ///< Minimum value for vref delays + INT8 yMax; ///< Maximum value for vref delays + BOOLEAN ParallelSampling; ///< Flag to indicate parallel sampling feature + BOOLEAN BroadcastDelays; ///< Flag to indicate if delays will be broadcast + UINT32 SampleCount; ///< Count of samples taken + UINT32 VrefUpdates; ///< Count of updates to Vref + UINT32 RdWrDlyUpdates; ///< Count of updates to RdWrDly + INT8 Dirs[2]; ///< List of directions to search for each axis +} MEM_RD_WR_2D_RIM_ENTRY; + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ +BOOLEAN +MemFAmdRdWr2DTraining ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID* OptParam + ); + +BOOLEAN +MemFCheckRdWr2DTrainingPerConfig ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramIntExtVrefSelect ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ); + +BOOLEAN +MemFRdWr2DProgramDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *Delay + ); + +VOID +MemFRdWr2DStoreResult ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT32 InPhaseResult[], + IN UINT32 PhaseResult180[] + ); + +BOOLEAN +MemFRdWr2DHeight ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +UINT8 +MemFGetRdWr2DWidth ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFCheckRdWr2DDiamondMaskStep ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT8 Vref, + IN UINT8 Lane + ); +BOOLEAN +MemFRdWr2DApplyMask ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DProcessConvolution ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DProgramMaxDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DFindCsVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DFinalVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +UINT8 +MemFRdWr2DGetMaxLanes ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +MemFRdWr2DTrainingInit ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +MemFRdWr2DEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *OptParam + ); + +VOID +MemFRdWr2DDisplaySearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +/******************************* +* Pattern Generation Functions +*******************************/ +BOOLEAN +MemFRdWr2DCompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ); + +BOOLEAN +MemFRdWr2DCompare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ); + +BOOLEAN +MemFRdWr2DInitVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DInitVictimChipSel ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DStartVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *SeedCountPtr + ); + +BOOLEAN +MemFRdWr2DFinalizeVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramDataPattern ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ); + +#endif /* _MFRDWR2DTRAINING_H_ */ |