Add the AMD Family10 Agesa code
[coreboot.git] / src / vendorcode / amd / agesa / f10 / Proc / Mem / Tech / mttdimbt.c
diff --git a/src/vendorcode/amd/agesa/f10/Proc/Mem/Tech/mttdimbt.c b/src/vendorcode/amd/agesa/f10/Proc/Mem/Tech/mttdimbt.c
new file mode 100755 (executable)
index 0000000..c0b787b
--- /dev/null
@@ -0,0 +1,1298 @@
+/**
+ * @file
+ *
+ * mttdimmbt.c
+ *
+ * Technology Dimm Based Training
+ *
+ * @xrefitem bom "File Content Label" "Release Content"
+ * @e project: AGESA
+ * @e sub-project: (Mem/Tech)
+ * @e \$Revision: 44323 $ @e \$Date: 2010-12-22 01:24:58 -0700 (Wed, 22 Dec 2010) $
+ *
+ **/
+/*****************************************************************************
+*
+* Copyright (c) 2011, 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 "GeneralServices.h"
+#include "heapManager.h"
+#include "Filecode.h"
+#define FILECODE PROC_MEM_TECH_MTTDIMBT_FILECODE
+
+/*----------------------------------------------------------------------------
+ *                          DEFINITIONS AND MACROS
+ *
+ *----------------------------------------------------------------------------
+ */
+#define MAX_BYTELANES 8   /* 8 byte lanes */
+#define MAX_DELAYS    9   /* 8 data bytes + 1 ECC byte */
+#define MAX_DIMMS     4   /* 4 DIMMs per channel */
+
+/*----------------------------------------------------------------------------
+ *                           TYPEDEFS AND STRUCTURES
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ *                        PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+VOID
+STATIC
+MemTInitDqsPos4RcvrEnByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  );
+
+VOID
+STATIC
+MemTSetRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly
+  );
+
+VOID
+STATIC
+MemTLoadRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  );
+
+BOOLEAN
+STATIC
+MemTSaveRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly,
+  IN       UINT16 CmpResultRank0,
+  IN       UINT16 CmpResultRank1
+  );
+
+VOID
+STATIC
+MemTResetDctWrPtrByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  );
+
+UINT16
+STATIC
+MemTCompare1ClPatternByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT8 Pattern[]
+  );
+
+VOID
+STATIC
+MemTSkipChipSelPass1Byte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN OUT   UINT8 *ChipSelPtr
+  );
+
+VOID
+STATIC
+MemTSkipChipSelPass2Byte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN OUT   UINT8 *ChipSelPtr
+  );
+
+UINT8
+STATIC
+MemTMaxByteLanesByte (VOID);
+
+UINT8
+STATIC
+MemTDlyTableWidthByte (VOID);
+
+VOID
+STATIC
+MemTSetDqsDelayCsrByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 ByteLane,
+  IN       UINT8 Dly
+  );
+
+VOID
+STATIC
+MemTDqsWindowSaveByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 ByteLane,
+  IN       UINT8 DlyMin,
+  IN       UINT8 DlyMax
+  );
+
+BOOLEAN
+STATIC
+MemTFindMaxRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+     OUT   UINT8 *ChipSel
+  );
+
+UINT16
+STATIC
+MemTCompare1ClPatternOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT8 Pattern[],
+  IN       UINT8 Side,
+  IN       UINT8 Receiver,
+  IN       BOOLEAN Side1En
+  );
+
+VOID
+STATIC
+MemTLoadRcvrEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  );
+
+VOID
+STATIC
+MemTSetRcvrEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly
+  );
+
+VOID
+STATIC
+MemTLoadInitialRcvEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  );
+/*----------------------------------------------------------------------------
+ *                            EXPORTED FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+BOOLEAN
+MemTFindMaxRcvrEnDlyRdDqsDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+     OUT   UINT8 *ChipSel
+  );
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *   This function enables byte based training if called
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *
+ */
+
+VOID
+MemTDimmByteTrainInit (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  )
+{
+  UINT8 Dct;
+  UINT8 Channel;
+  UINT8 DctCount;
+  UINT8 ChannelCount;
+  DIE_STRUCT *MCTPtr;
+  ALLOCATE_HEAP_PARAMS AllocHeapParams;
+  MEM_NB_BLOCK  *NBPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  MCTPtr = NBPtr->MCTPtr;
+
+  TechPtr->InitDQSPos4RcvrEn = MemTInitDqsPos4RcvrEnByte;
+  TechPtr->SetRcvrEnDly = MemTSetRcvrEnDlyByte;
+  TechPtr->LoadRcvrEnDly = MemTLoadRcvrEnDlyByte;
+  TechPtr->SaveRcvrEnDly = MemTSaveRcvrEnDlyByte;
+  TechPtr->SaveRcvrEnDlyFilter = MemTSaveRcvrEnDlyByteFilterOpt;
+  TechPtr->ResetDCTWrPtr = MemTResetDctWrPtrByte;
+  TechPtr->Compare1ClPattern = MemTCompare1ClPatternByte;
+  TechPtr->SkipChipSelPass1 = MemTSkipChipSelPass1Byte;
+  TechPtr->SkipChipSelPass2 = MemTSkipChipSelPass2Byte;
+  TechPtr->MaxByteLanes = MemTMaxByteLanesByte;
+  TechPtr->DlyTableWidth = MemTDlyTableWidthByte;
+  TechPtr->SetDQSDelayCSR = MemTSetDqsDelayCsrByte;
+  TechPtr->DQSWindowSave = MemTDqsWindowSaveByte;
+  TechPtr->FindMaxDlyForMaxRdLat = MemTFindMaxRcvrEnDlyByte;
+  TechPtr->Compare1ClPatternOpt = MemTCompare1ClPatternOptByte;
+  TechPtr->LoadRcvrEnDlyOpt = MemTLoadRcvrEnDlyOptByte;
+  TechPtr->SetRcvrEnDlyOpt = MemTSetRcvrEnDlyOptByte;
+  TechPtr->InitializeVariablesOpt = MemTInitializeVariablesOptByte;
+  TechPtr->GetMaxValueOpt = MemTGetMaxValueOptByte;
+  TechPtr->SetSweepErrorOpt = MemTSetSweepErrorOptByte;
+  TechPtr->CheckRcvrEnDlyLimitOpt = MemTCheckRcvrEnDlyLimitOptByte;
+  TechPtr->LoadInitialRcvrEnDlyOpt = MemTLoadInitialRcvEnDlyOptByte;
+  // Dynamically allocate buffers for storing trained timings.
+  DctCount = MCTPtr->DctCount;
+  ChannelCount = MCTPtr->DctData[0].ChannelCount;
+  AllocHeapParams.RequestedBufferSize = ((DctCount * ChannelCount) *
+                                         ((MAX_DIMMS * MAX_DELAYS * NUMBER_OF_DELAY_TABLES) +
+                                          (MAX_DELAYS * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES)
+                                          )
+                                         );
+  AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_TRN_DATA_HANDLE, MCTPtr->NodeId, 0, 0);
+  AllocHeapParams.Persist = HEAP_LOCAL_CACHE;
+  if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) {
+    for (Dct = 0; Dct < DctCount; Dct++) {
+      for (Channel = 0; Channel < ChannelCount; Channel++) {
+        MCTPtr->DctData[Dct].ChData[Channel].RowCount = MAX_DIMMS;
+        MCTPtr->DctData[Dct].ChData[Channel].ColumnCount = MAX_DELAYS;
+
+        MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlys = (UINT16 *) AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2;
+        MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].WrDatDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlys = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
+        MCTPtr->DctData[Dct].ChData[Channel].FailingBitMask = AllocHeapParams.BufferPtr;
+        AllocHeapParams.BufferPtr += (MAX_CS_PER_CHANNEL * MAX_DELAYS);
+      }
+    }
+  } else {
+    PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_DYN_STORING_OF_TRAINED_TIMINGS, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader);
+    SetMemError (AGESA_FATAL, MCTPtr);
+    ASSERT(FALSE);  // Could not dynamically allocate buffers for storing trained timings
+  }
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function initializes the DQS Positions in preparation for Receiver Enable Training.
+ *  Write Position is no delay, Read Position is 1/2 Memclock delay
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *
+ */
+
+VOID
+STATIC
+MemTInitDqsPos4RcvrEnByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  )
+{
+  UINT8 Dimm;
+  UINT8 ByteLane;
+  UINT8 WrDqs;
+
+  for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+    for (ByteLane = 0; ByteLane < MAX_DELAYS; ByteLane++) {
+      WrDqs = TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane];
+      TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqs);
+      TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), 0x3F);
+    }
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function programs DqsRcvEnDly to additional index for DQS receiver enabled training
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *     @param[in]       RcvEnDly  - receiver enable delay to be saved
+ */
+
+VOID
+STATIC
+MemTSetRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly
+  )
+{
+  UINT8 ByteLane;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
+    TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), RcvEnDly);
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function loads the DqsRcvEnDly from saved data and program to additional index
+ *  for DQS receiver enabled training
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *
+ */
+
+VOID
+STATIC
+MemTLoadRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  )
+{
+  UINT8 i;
+  UINT8 Dimm;
+  UINT16 Saved;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+
+  Dimm = Receiver >> 1;
+  Saved = TechPtr->DqsRcvEnSaved;
+  for (i = 0; i < MAX_BYTELANES; i++) {
+    if (Saved & 1) {
+      TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i),
+      ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]);
+    }
+    Saved >>= 1;
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function saves passing DqsRcvEnDly values to the stack
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *     @param[in]       RcvEnDly  - receiver enable delay to be saved
+ *     @param[in]       CmpResultRank0 - compare result for Rank 0
+ *     @param[in]       CmpResultRank1 - compare result for Rank 1
+ *
+ *     @return  TRUE - All bytelanes pass
+ *     @return  FALSE - Some bytelanes fail
+ */
+
+BOOLEAN
+STATIC
+MemTSaveRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly,
+  IN       UINT16 CmpResultRank0,
+  IN       UINT16 CmpResultRank1
+  )
+{
+  UINT8 i;
+  UINT8 Passed;
+  UINT8 Saved;
+  UINT8 Mask;
+  UINT8 Dimm;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+
+  Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF);
+
+  Saved = (UINT8) (TechPtr->DqsRcvEnSaved & Passed); //@attention - false passes filter (subject to be replaced with a better solution)
+  Dimm = Receiver >> 1;
+  Mask = 1;
+  for (i = 0; i < MAX_BYTELANES; i++) {
+    if (Passed & Mask) {
+      if (!(Saved & Mask)) {
+        ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20;     // @attention -1 pass only
+        IDS_HDT_CONSOLE ("\t\t\tBL %d = %02x", i, RcvEnDly + 0x20);
+      }
+      Saved |= Mask;
+    }
+    Mask <<= 1;
+  }
+  TechPtr->DqsRcvEnSaved = Saved;
+
+  if (Saved == 0xFF) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function performs a filtering functionality and saves passing DqsRcvEnDly
+ *  values to the stack
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *     @param[in]       RcvEnDly  - receiver enable delay to be saved
+ *     @param[in]       CmpResultRank0 - compare result for Rank 0
+ *     @param[in]       CmpResultRank1 - compare result for Rank 1
+ *
+ *     @return  TRUE - All bytelanes pass
+ *     @return  FALSE - Some bytelanes fail
+ */
+
+BOOLEAN
+MemTSaveRcvrEnDlyByteFilter (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly,
+  IN       UINT16 CmpResultRank0,
+  IN       UINT16 CmpResultRank1
+  )
+{
+  UINT8 i;
+  UINT8 Passed;
+  UINT8 Saved;
+  UINT8 Mask;
+  UINT8 Dimm;
+  UINT8 MaxFilterDly;
+  CH_DEF_STRUCT *ChannelPtr;
+  MEM_DCT_CACHE *DctCachePtr;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+  DctCachePtr = TechPtr->NBPtr->DctCachePtr;
+
+  MaxFilterDly = TechPtr->MaxFilterDly;
+  Passed = (UINT8) ((CmpResultRank0 & CmpResultRank1) & 0xFF);
+
+  Dimm = Receiver >> 1;
+  Saved = (UINT8) TechPtr->DqsRcvEnSaved;
+  Mask = 1;
+  for (i = 0; i < MAX_BYTELANES; i++) {
+    if ((Passed & Mask) != 0) {
+      DctCachePtr->RcvEnDlyCounts [i] += 1;
+      if ((Saved & Mask) == 0) {
+        ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i] = RcvEnDly + 0x20;
+        Saved |= Mask;
+        IDS_HDT_CONSOLE ("\t\t\tBL %d = %02x", i, RcvEnDly + 0x20);
+      }
+    } else {
+      if (DctCachePtr->RcvEnDlyCounts [i] <= MaxFilterDly) {
+        DctCachePtr->RcvEnDlyCounts [i] = 0;
+        Saved &= ~Mask;
+      }
+    }
+    Mask <<= 1;
+  }
+
+  //-----------------------
+  TechPtr->DqsRcvEnSaved = (UINT16) Saved;
+
+  Saved = 0;
+  for (i = 0; i < MAX_BYTELANES; i++) {
+    if (DctCachePtr->RcvEnDlyCounts [i] >= MaxFilterDly) {
+      Saved |= (UINT8) 1 << i;
+    }
+  }
+
+  if (Saved == 0xFF) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function compares test pattern with data in buffer and return a pass/fail bitmap
+ *       for 8 Bytes
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Buffer[]  -  Buffer data from DRAM (Measured data from DRAM) to compare
+ *     @param[in]       Pattern[]  - Pattern (Expected data in ROM/CACHE) to compare against
+ *
+ *     @return  PASS - Bit map of results of comparison
+ */
+
+UINT16
+STATIC
+MemTCompare1ClPatternByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT8 Pattern[]
+  )
+{
+  UINT16 i;
+  UINT16 j;
+  UINT16 Pass;
+  DIE_STRUCT *MCTPtr;
+
+  MCTPtr = TechPtr->NBPtr->MCTPtr;
+  if (MCTPtr->GangedMode && MCTPtr->Dct) {
+    j = 8;
+  } else {
+    j = 0;
+  }
+
+#if 0
+  Pass = 0xFFFF;
+  IDS_HDT_CONSOLE ("%v1 -");
+  IDS_HDT_CONSOLE ("%v2 -");
+  IDS_HDT_CONSOLE ("%v3 -");
+  for (i = 0; i < 8; i++) {
+    if (Buffer[j] != Pattern[j]) {
+      // if bytelane n fails
+      Pass &= ~((UINT16)1 << (j % 8));    // clear bit n
+    }
+    IDS_HDT_CONSOLE ("%v1  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
+    IDS_HDT_CONSOLE ("%v2 %02x", Buffer[j]);
+    IDS_HDT_CONSOLE ("%v3 %02x", Pattern[j]);
+    j++;
+  }
+#endif
+    Pass = 0xFFFF;
+    IDS_HDT_CONSOLE ("\n\t\t\tPass/Fail -");
+    for (i = 0; i < 8; i++, j++) { 
+      if (Buffer[j] != Pattern[j]) {
+        // if bytelane n fails 
+        Pass &= ~((UINT16)1 << (j % 8));    // clear bit n
+      }
+      IDS_HDT_CONSOLE ("  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
+    }
+    IDS_HDT_CONSOLE ("\n\t\t\t Measured -");
+    for (i = 0, j -= 8; i < 8; i++, j++) {
+      IDS_HDT_CONSOLE (" %02x", Buffer[j]);
+    }
+    IDS_HDT_CONSOLE ("\n\t\t\t Expected -");
+    for (i = 0, j -= 8; i < 8; i++, j++) {
+      IDS_HDT_CONSOLE (" %02x", Pattern[j]);
+    }
+    IDS_HDT_CONSOLE ("\n\n");
+
+  return Pass;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       The function resets the DCT input buffer write pointer.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver - Chip select
+ *
+ */
+
+VOID
+STATIC
+MemTResetDctWrPtrByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  )
+{
+  UINT8 i;
+  UINT16 RcvEnDly;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  for (i = 0; i < MAX_BYTELANES; i++) {
+    RcvEnDly = (UINT16) TechPtr->NBPtr->GetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i));
+    TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver / 2, i), RcvEnDly);
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function skips odd chip select if training at 800MT or above.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       *ChipSelPtr - Pointer to variable contains Chip select index
+ *
+ */
+
+VOID
+STATIC
+MemTSkipChipSelPass1Byte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN OUT   UINT8 *ChipSelPtr
+  )
+{
+  MEM_NB_BLOCK *NBPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  // if the even chip select failed training, need to set CsTrainFail for odd chip select if present.
+  if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ((*ChipSelPtr) + 1))) {
+    if (NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16)1 << *ChipSelPtr)) {
+      NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ((*ChipSelPtr) + 1);
+      NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader);
+    }
+  }
+  (*ChipSelPtr)++;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  MemTSkipChipSelPass2Byte:
+ *
+ *       This function skips odd chip select if training at 800MT or above.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in,out]   *ChipSelPtr - Pointer to variable contains Chip select index
+ *
+ */
+
+VOID
+STATIC
+MemTSkipChipSelPass2Byte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN OUT   UINT8 *ChipSelPtr
+  )
+{
+  if (*ChipSelPtr & 1) {
+    *ChipSelPtr = MAX_CS_PER_CHANNEL;    // skip all successions
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function determines the maximum number of byte lanes
+ *
+ *     @return  Max number of Bytelanes
+ */
+
+UINT8
+STATIC
+MemTMaxByteLanesByte (VOID)
+{
+  return MAX_BYTELANES;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function determines the width of the delay tables (eg. RcvEnDlys, WrDqsDlys,...)
+ *
+ *     @return  Delay table width in bytes
+ */
+
+UINT8
+STATIC
+MemTDlyTableWidthByte (VOID)
+{
+  return MAX_DELAYS;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function writes the Delay value to a certain byte lane
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       ByteLane  -  Bytelane number being targeted
+ *     @param[in]       Dly - Delay value
+ *
+ */
+
+VOID
+STATIC
+MemTSetDqsDelayCsrByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 ByteLane,
+  IN       UINT8 Dly
+  )
+{
+  UINT8 Reg;
+  UINT8 Dimm;
+
+  ASSERT (ByteLane <= MAX_BYTELANES);
+
+  if (!(TechPtr->DqsRdWrPosSaved & ((UINT8)1 << ByteLane))) {
+    Dimm = (TechPtr->ChipSel >> 1);
+
+    if (TechPtr->Direction == DQS_WRITE_DIR) {
+      Dly = Dly + ((UINT8) TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_DELAYS) + ByteLane]);
+      Reg = AccessWrDatDly;
+    } else {
+      Reg = AccessRdDqsDly;
+    }
+
+    TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, Reg, DIMM_BYTE_ACCESS (Dimm, ByteLane), Dly);
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function programs the trained DQS delay for the specified byte lane
+ *       and stores its DQS window for reference.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       ByteLane  -  Bytelane number being targeted
+ *     @param[in]       DlyMin - Minimum delay value
+ *     @param[in]       DlyMax- Maximum delay value
+ *
+ */
+
+VOID
+STATIC
+MemTDqsWindowSaveByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 ByteLane,
+  IN       UINT8 DlyMin,
+  IN       UINT8 DlyMax
+  )
+{
+  UINT8 DqsDelay;
+  UINT8 Dimm;
+  CH_DEF_STRUCT *ChanPtr;
+
+  ASSERT (ByteLane <= MAX_BYTELANES);
+  ChanPtr = TechPtr->NBPtr->ChannelPtr;
+
+  DqsDelay = ((DlyMin + DlyMax + 1) / 2) & 0x3F;
+  MemTSetDqsDelayCsrByte (TechPtr, ByteLane, DqsDelay);
+  TechPtr->DqsRdWrPosSaved |= (UINT8)1 << ByteLane;
+  TechPtr->DqsRdWrPosSaved |= 0xFF00;
+
+  Dimm = (TechPtr->ChipSel / 2) * MAX_DELAYS + ByteLane;
+  if (TechPtr->Direction == DQS_READ_DIR) {
+    ChanPtr->RdDqsDlys[Dimm] = DqsDelay;
+  } else {
+    ChanPtr->WrDatDlys[Dimm] = DqsDelay + ChanPtr->WrDqsDlys[Dimm];
+  }
+
+  if (TechPtr->Direction == DQS_READ_DIR) {
+    ChanPtr->RdDqsMinDlys[ByteLane] = DlyMin;
+    ChanPtr->RdDqsMaxDlys[ByteLane] = DlyMax;
+  } else {
+    ChanPtr->WrDatMinDlys[ByteLane] = DlyMin;
+    ChanPtr->WrDatMaxDlys[ByteLane] = DlyMax;
+  }
+
+}
+
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *      This function finds the DIMM that has the largest receiver enable delay.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[out]      *ChipSel   - Pointer to the Chip select that has the largest receiver enable delay.
+ *
+ *     @return   TRUE - A chip select can be found.
+ *     @return   FALSE - A chip select cannot be found.
+ */
+
+BOOLEAN
+STATIC
+MemTFindMaxRcvrEnDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+     OUT   UINT8 *ChipSel
+  )
+{
+  UINT8  Dimm;
+  UINT8  ByteLane;
+  UINT16 RcvEnDly;
+  UINT16 MaxDly;
+  UINT8  MaxDlyDimm;
+  BOOLEAN RetVal;
+
+  MEM_NB_BLOCK  *NBPtr;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  ChannelPtr = NBPtr->ChannelPtr;
+
+  RetVal = FALSE;
+  MaxDly = 0;
+  MaxDlyDimm = 0;
+  for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+    if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
+      // Only choose the dimm that does not fail training
+      for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
+        RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
+        if (RcvEnDly > MaxDly) {
+          MaxDly = RcvEnDly;
+          MaxDlyDimm = Dimm;
+          RetVal = TRUE;
+        }
+      }
+    }
+  }
+
+  if (NBPtr->MCTPtr->Status[Sb128bitmode] != 0) {
+    //The RcvrEnDlys of DCT1 DIMMs should also be considered while ganging.
+    NBPtr->SwitchDCT (NBPtr, 1);
+    ChannelPtr = NBPtr->ChannelPtr;
+    for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+      for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
+        RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
+        if (RcvEnDly > MaxDly) {
+          MaxDly = RcvEnDly;
+          MaxDlyDimm = Dimm;
+        }
+      }
+    }
+    NBPtr->SwitchDCT (NBPtr, 0);
+  }
+
+  TechPtr->MaxDlyForMaxRdLat = MaxDly;
+  *ChipSel = (MaxDlyDimm * 2);
+  return RetVal;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *      This function finds the DIMM that has the largest receiver enable delay + Read DQS Delay.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[out]      *ChipSel   - Pointer to the Chip select that has the largest receiver enable delay
+ *                                   + Read DQS Delay.
+ *
+ *     @return   TRUE - A chip select can be found.
+ *     @return   FALSE - A chip select cannot be found.
+ */
+
+BOOLEAN
+MemTFindMaxRcvrEnDlyRdDqsDlyByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+     OUT   UINT8 *ChipSel
+  )
+{
+  UINT8  Dimm;
+  UINT8  ByteLane;
+  UINT16 RcvEnDly;
+  UINT16 RdDqsDly;
+  UINT16 TotalDly;
+  UINT16 MaxDly;
+  UINT8  MaxDlyDimm;
+  BOOLEAN RetVal;
+
+  MEM_NB_BLOCK  *NBPtr;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  ChannelPtr = NBPtr->ChannelPtr;
+
+  RetVal = FALSE;
+  MaxDly = 0;
+  MaxDlyDimm = 0;
+  for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
+    if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
+      // Only choose the dimm that does not fail training
+      for (ByteLane = 0; ByteLane < MAX_BYTELANES; ByteLane++) {
+        RcvEnDly = ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + ByteLane];
+        // Before Dqs Position Training, this value is 0. So the maximum value for
+        // RdDqsDly needs to be added later when calculating the MaxRdLatency value
+        // after RcvEnDly training but before DQS Position Training.
+        RdDqsDly = ChannelPtr->RdDqsDlys[Dimm * MAX_DELAYS + ByteLane];
+        TotalDly = RcvEnDly + (RdDqsDly >> 1);
+        if (TotalDly > MaxDly) {
+          MaxDly = TotalDly;
+          MaxDlyDimm = Dimm;
+          RetVal = TRUE;
+        }
+      }
+    }
+  }
+
+  TechPtr->MaxDlyForMaxRdLat = MaxDly;
+  *ChipSel = (MaxDlyDimm * 2);
+  return RetVal;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *       This function compares test pattern with data in buffer and return a pass/fail bitmap
+ *       for 8 Bytes for optimized receiver enable training
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Buffer[]  -  Buffer data from DRAM (Measured data from DRAM) to compare
+ *     @param[in]       Pattern[]  - Pattern (Expected data in ROM/CACHE) to compare against
+ *     @param[in]       Side - current side being targeted
+ *     @param[in]       Receiver - Current receiver value
+ *     @param[in]       Side1En - Indicates if the second side of the DIMM is being used
+ *     @return  PASS - Bit map of results of comparison
+ */
+
+UINT16
+STATIC
+MemTCompare1ClPatternOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Buffer[],
+  IN       UINT8 Pattern[],
+  IN       UINT8 Side,
+  IN       UINT8 Receiver,
+  IN       BOOLEAN  Side1En
+  )
+{
+  UINT16 i;
+  UINT16 j;
+  UINT16 Pass;
+  DIE_STRUCT *MCTPtr;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+  MCTPtr = TechPtr->NBPtr->MCTPtr;
+
+  if (MCTPtr->GangedMode && MCTPtr->Dct) {
+    j = 8;
+  } else {
+    j = 0;
+  }
+
+  Pass = 0xFFFF;
+#if 0
+  IDS_HDT_CONSOLE ("%v1 -");
+  IDS_HDT_CONSOLE ("%v2 -");
+  IDS_HDT_CONSOLE ("%v3 -");
+  IDS_HDT_CONSOLE ("%v4 -");
+#endif
+  IDS_HDT_CONSOLE ("\t\t\tDelay[BL] -");
+  for (i = 0; i < 8; i++) {
+//    IDS_HDT_CONSOLE ("%v1 %02x", TechPtr->RcvrEnDlyOpt[i] & 0xFF);
+//    IDS_HDT_CONSOLE ("%v2  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
+//    IDS_HDT_CONSOLE ("%v3 %02x", Buffer[j]);
+//    IDS_HDT_CONSOLE ("%v4 %02x", Pattern[j]);
+    IDS_HDT_CONSOLE (" %02x", TechPtr->RcvrEnDlyOpt[i] & 0xFF);
+    if (Buffer[j] != Pattern[j]) {
+      // if bytelane n fails
+      Pass &= ~((UINT16)1 << (j % 8));    // clear bit n
+      TechPtr->DqsRcvEnFirstPassValOpt[i] = 0;
+      TechPtr->GetFirstPassValOpt[i] = FALSE;
+      TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
+      TechPtr->DqsRcvEnSavedOpt[i] = FALSE;
+      if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) {
+        if (Side == ((Side1En ? 4 : 2) - 1)) {
+          TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT;
+        }
+      }
+    } else {
+      if (TechPtr->FilterSidePassCountOpt[i] == ((Side1En ? 4 : 2) - 1)) {
+        //Only apply filter if all sides have passed
+        if (TechPtr->FilterStatusOpt[i] != DONE_FILTER) {
+          if (TechPtr->GetFirstPassValOpt[i] == FALSE) {
+            // This is the first Pass, mark the start of filter check
+            TechPtr->DqsRcvEnFirstPassValOpt[i] = TechPtr->RcvrEnDlyOpt[i];
+            TechPtr->GetFirstPassValOpt[i] = TRUE;
+            TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
+            TechPtr->RcvrEnDlyOpt[i]++;
+          } else {
+            if ((TechPtr->RcvrEnDlyOpt[i] - TechPtr->DqsRcvEnFirstPassValOpt[i]) < FILTER_WINDOW_SIZE) {
+              if (TechPtr->IncBy1ForNextCountOpt[i] == FALSE) {
+                TechPtr->RcvrEnDlyOpt[i] += FILTER_SECOND_STAGE_COUNT;
+                TechPtr->IncBy1ForNextCountOpt[i] = TRUE;
+              } else {
+                TechPtr->RcvrEnDlyOpt[i]++;
+                TechPtr->IncBy1ForNextCountOpt[i] = FALSE;
+              }
+            } else {
+              // End sweep and add offset to first pass
+              TechPtr->MaxRcvrEnDlyBlOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i];
+              TechPtr->RcvrEnDlyOpt[i] = TechPtr->DqsRcvEnFirstPassValOpt[i] + FILTER_OFFSET_VALUE;
+              TechPtr->FilterStatusOpt[i] = DONE_FILTER;
+              TechPtr->FilterCountOpt++;
+            }
+          }
+        } else {
+          TechPtr->FilterSidePassCountOpt[i]++;
+        }
+      } else {
+        if (TechPtr->GetFirstPassValOpt[i] == FALSE) {
+          if (Side == ((Side1En ? 4 : 2) - 1)) {
+            TechPtr->RcvrEnDlyOpt[i] += FILTER_FIRST_STAGE_COUNT;
+          }
+        }
+        TechPtr->FilterSidePassCountOpt[i]++;
+      }
+      TechPtr->DqsRcvEnSavedOpt[i] = TRUE;
+      ChannelPtr->RcvEnDlys[(Receiver >> 1) * MAX_DELAYS + i] = TechPtr->RcvrEnDlyOpt[i];
+    }
+    if (Side == ((Side1En ? 4 : 2) - 1)) {
+      TechPtr->FilterSidePassCountOpt[i] = 0;
+    }
+    if (TechPtr->RcvrEnDlyOpt[i] >= TechPtr->RcvrEnDlyLimitOpt[i]) {
+      TechPtr->FilterCountOpt++;
+    }
+
+    j++;
+  }
+    IDS_HDT_CONSOLE ("\n\t\t\tPass/Fail -");
+    for (i = 0, j -= 8; i < 8; i++, j++) {
+      IDS_HDT_CONSOLE ("  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
+    }
+    IDS_HDT_CONSOLE ("\n\t\t\t Measured -");
+    for (i = 0, j -= 8; i < 8; i++, j++) {
+      IDS_HDT_CONSOLE (" %02x", Buffer[j]);
+    }
+    IDS_HDT_CONSOLE ("\n\t\t\t Expected -");
+    for (i = 0, j -= 8; i < 8; i++, j++) {
+      IDS_HDT_CONSOLE (" %02x", Pattern[j]);
+    }
+    IDS_HDT_CONSOLE ("\n\n");
+
+  return Pass;
+}
+/*-----------------------------------------------------------------------------
+ *
+ *  This function initializes variables for optimized training.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *
+ * ----------------------------------------------------------------------------
+ */
+VOID
+MemTInitializeVariablesOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  )
+{
+  UINT8 ByteLane;
+  for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
+    TechPtr->RcvrEnDlyLimitOpt[ByteLane] = FILTER_MAX_REC_EN_DLY_VALUE;      // @attention - limit depends on proc type
+    TechPtr->DqsRcvEnSavedOpt[ByteLane] = FALSE;
+    TechPtr->RcvrEnDlyOpt[ByteLane] = FILTER_NEW_RECEIVER_START_VALUE;
+    TechPtr->GetFirstPassValOpt[ByteLane] = FALSE;
+    TechPtr->DqsRcvEnFirstPassValOpt[ByteLane] = 0;
+    TechPtr->RevertPassValOpt[ByteLane] = FALSE;
+    TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] = 0;
+    TechPtr->FilterStatusOpt[ByteLane] = START_FILTER;
+    TechPtr->FilterCountOpt = 0;
+    TechPtr->FilterSidePassCountOpt[ByteLane] = 0;
+    TechPtr->IncBy1ForNextCountOpt[ByteLane] = FALSE;
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function loads the DqsRcvEnDly from saved data and program to additional index
+ *  for optimized DQS receiver enabled training
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *
+ */
+
+VOID
+STATIC
+MemTLoadRcvrEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  )
+{
+  UINT8 i;
+  UINT8 Dimm;
+  CH_DEF_STRUCT *ChannelPtr;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+  ChannelPtr = TechPtr->NBPtr->ChannelPtr;
+
+  Dimm = Receiver >> 1;
+  for (i = 0; i < 8; i++) {
+    if (TechPtr->DqsRcvEnSavedOpt[i]) {
+      TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i),
+      ChannelPtr->RcvEnDlys[Dimm * MAX_DELAYS + i]);
+    }
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function programs DqsRcvEnDly to additional index for DQS receiver enabled training
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ *     @param[in]       RcvEnDly  - receiver enable delay to be saved
+ */
+
+VOID
+STATIC
+MemTSetRcvrEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT16 RcvEnDly
+  )
+{
+  UINT8 ByteLane;
+
+  ASSERT (Receiver < MAX_CS_PER_CHANNEL);
+
+  for (ByteLane = 0; ByteLane < 8; ByteLane++) {
+    if (TechPtr->FilterStatusOpt[ByteLane] != DONE_FILTER) {
+      TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), TechPtr->RcvrEnDlyOpt[ByteLane]);
+    }
+  }
+}
+/*-----------------------------------------------------------------------------
+ *
+ *  This sets any Errors generated from Dly sweep
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]   DCT   - current DCT
+ *     @param[in]   Receiver   - current receiver
+ *
+ *     @return     FALSE - Fatal error occurs.
+ *     @return     TRUE  - No fatal error occurs.
+ * ----------------------------------------------------------------------------
+ */
+BOOLEAN
+MemTSetSweepErrorOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver,
+  IN       UINT8 Dct,
+  IN       BOOLEAN ErrorCheck
+  )
+{
+  UINT8 ByteLane;
+  MEM_DATA_STRUCT *MemPtr;
+  DIE_STRUCT *MCTPtr;
+  DCT_STRUCT *DCTPtr;
+  MEM_NB_BLOCK  *NBPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  MemPtr = NBPtr->MemPtr;
+  MCTPtr = NBPtr->MCTPtr;
+  DCTPtr = NBPtr->DCTPtr;
+  for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
+    if (TechPtr->RcvrEnDlyOpt[ByteLane] == TechPtr->RcvrEnDlyLimitOpt[ByteLane]) {
+      // no passing window
+      if (ErrorCheck) {
+        return FALSE;
+      }
+      PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_NO_PASSING_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader);
+      SetMemError (AGESA_ERROR, MCTPtr);
+    }
+    if (TechPtr->RcvrEnDlyOpt[ByteLane] > (TechPtr->RcvrEnDlyLimitOpt[ByteLane] - 1)) {
+      // passing window too narrow, too far delayed
+      if (ErrorCheck) {
+        return FALSE;
+      }
+      PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_VALUE_TOO_LARGE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, ByteLane, &NBPtr->MemPtr->StdHeader);
+      SetMemError (AGESA_ERROR, MCTPtr);
+      DCTPtr->Timings.CsTrainFail |= (UINT16) (3 << Receiver) & DCTPtr->Timings.CsPresent;
+      MCTPtr->ChannelTrainFail |= (UINT32)1 << Dct;
+      if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, DCTPtr->Timings.CsTrainFail, &MemPtr->StdHeader)) {
+        return FALSE;
+      }
+    }
+  }
+  return TRUE;
+}
+
+/*-----------------------------------------------------------------------------
+ *
+ *  This function determines the maximum receiver delay value
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *
+ *     @retval  MaxRcvrValue - Maximum receiver delay value for all bytelanes
+ * ----------------------------------------------------------------------------
+ */
+
+UINT16
+MemTGetMaxValueOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  )
+{
+  UINT8 ByteLane;
+  UINT16 MaxRcvrValue;
+  MaxRcvrValue = 0;
+  for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
+    if (TechPtr->MaxRcvrEnDlyBlOpt[ByteLane] > MaxRcvrValue) {
+      MaxRcvrValue = TechPtr->MaxRcvrEnDlyBlOpt[ByteLane];
+    }
+  }
+  MaxRcvrValue += FILTER_OFFSET_VALUE;
+  return MaxRcvrValue;
+}
+/*-----------------------------------------------------------------------------
+ *
+ *  This function determines if the sweep loop should complete.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *
+ *     @retval  TRUE - All bytelanes pass
+ *              FALSE - Some bytelanes fail
+ * ----------------------------------------------------------------------------
+ */
+
+BOOLEAN
+MemTCheckRcvrEnDlyLimitOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr
+  )
+{
+  if (TechPtr->FilterCountOpt >= (UINT16)MAX_CS_PER_CHANNEL) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *  This function load the result of write levelization training into RcvrEnDlyOpt,
+ *  using it as the initial value for Receiver DQS training.
+ *
+ *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
+ *     @param[in]       Receiver  - Current Chip select value
+ */
+VOID
+STATIC
+MemTLoadInitialRcvEnDlyOptByte (
+  IN OUT   MEM_TECH_BLOCK *TechPtr,
+  IN       UINT8 Receiver
+  )
+{
+  UINT8 ByteLane;
+  MEM_NB_BLOCK *NBPtr;
+
+  NBPtr = TechPtr->NBPtr;
+  for (ByteLane = 0; ByteLane < MAX_BYTELANES_PER_CHANNEL; ByteLane++) {
+    TechPtr->RcvrEnDlyOpt[ByteLane] = NBPtr->ChannelPtr->WrDqsDlys[((Receiver >> 1) * TechPtr->DlyTableWidth ()) + ByteLane];
+  }
+}