/* $NoKeywords:$ */
/**
 * @file
 *
 * mttdimmbt.c
 *
 * Technology Dimm Based Training
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project: AGESA
 * @e sub-project: (Mem/Tech)
 * @e \$Revision: 56279 $ @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
 *
 **/
/*****************************************************************************
*
* Copyright (C) 2012 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 "mt.h"
#include "GeneralServices.h"
#include "heapManager.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G2_PEI)

#define FILECODE PROC_MEM_TECH_MTTDIMBT_FILECODE

/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                           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
  );

UINT8
STATIC
MemTFindMinMaxGrossDlyByte (
  IN OUT   MEM_TECH_BLOCK *TechPtr,
  IN       TRN_DLY_TYPE TrnDlyType,
  IN       BOOLEAN IfMax
  );
/*----------------------------------------------------------------------------
 *                            EXPORTED FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */


/* -----------------------------------------------------------------------------*/
/**
 *
 *   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;
  TechPtr->GetMinMaxGrossDly = MemTFindMinMaxGrossDlyByte;
  // 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) +
                                           (MAX_DIMMS * MAX_NUMBER_LANES)
                                          )
                                         );

  if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) {
    AllocHeapParams.RequestedBufferSize *= 2;
  }

  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].RdDqs__Dlys = AllocHeapParams.BufferPtr;
        AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES);
        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);
        if (NBPtr->MemPstateStage == MEMORY_PSTATE_1ST_STAGE) {
          MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlysMemPs1 = (UINT16 *) AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS) * 2;
          MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].WrDatDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].RdDqs__DlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_NUMBER_LANES);
          MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlysMemPs1 = AllocHeapParams.BufferPtr;
          AllocHeapParams.BufferPtr += (MAX_DIMMS * MAX_DELAYS);
          MCTPtr->DctData[Dct].ChData[Channel].FailingBitMaskMemPs1 = 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 (MEM_FLOW, "\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 (MEM_FLOW, "\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;
  }

  Pass = 0xFFFF;
  IDS_HDT_CONSOLE (MEM_FLOW, " -");
  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 (MEM_FLOW, "  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
    j++;
  }

  IDS_HDT_CONSOLE_DEBUG_CODE (
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t        -");
    for (i = 0, j -= 8; i < 8; i++, j++) {
      IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]);
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t        -");
    for (i = 0, j -= 8; i < 8; i++, j++) {
      IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]);
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\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);
      if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) {
        ASSERT (FALSE);
      }
    }
  }
  (*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.CsEnabled & ((UINT16) 1 << (Dimm << 1))) != 0) {
      if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16) 3 << (Dimm << 1))) == 0) {
        // Only choose the dimm that does not fail training
        for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); 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 < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); 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 finds the DIMM that has the largest receiver enable delay + Read DQS Delay for UNB
 *
 *     @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
MemTFindMaxRcvrEnDlyRdDqsDlyByteUnb (
  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 < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); 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;
        if (TotalDly > MaxDly) {
          MaxDly = TotalDly;
          MaxDlyDimm = Dimm;
          RetVal = TRUE;
        }
      }
    }
  }

  TechPtr->MaxDlyForMaxRdLat = MaxDly;
  *ChipSel = (MaxDlyDimm * 2);
  return RetVal;
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function finds the minimum or maximum gross dly among all the bytes.
 *
 *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
 *     @param[in]       TrnDlyType - Target Dly type
 *     @param[in]       IfMax - If this is for maximum value or minimum
 *
 *     @return   minimum gross dly
 */
UINT8
STATIC
MemTFindMinMaxGrossDlyByte (
  IN OUT   MEM_TECH_BLOCK *TechPtr,
  IN       TRN_DLY_TYPE TrnDlyType,
  IN       BOOLEAN IfMax
  )
{
  UINT8 i;
  UINT8 ByteLane;
  UINT16 CsEnabled;
  UINT8 MinMaxGrossDly;
  UINT8 TrnDly;
  MEM_NB_BLOCK *NBPtr;

  NBPtr = TechPtr->NBPtr;
  CsEnabled = NBPtr->DCTPtr->Timings.CsEnabled;
  MinMaxGrossDly = IfMax ? 0 : 0xFF;

  for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
    if ((CsEnabled & (UINT16) (3 << (i << 1))) != 0) {
      for (ByteLane = 0; ByteLane < ((NBPtr->MCTPtr->Status[SbEccDimms] && NBPtr->IsSupported[EccByteTraining]) ? 9 : 8); ByteLane++) {
        TrnDly = (UINT8) (GetTrainDlyFromHeapNb (NBPtr, TrnDlyType, DIMM_BYTE_ACCESS (i, ByteLane)) >> 5);
        if ((IfMax && (TrnDly > MinMaxGrossDly)) || (!IfMax && (TrnDly < MinMaxGrossDly))) {
          MinMaxGrossDly = TrnDly;
        }
      }
    }
  }

  return MinMaxGrossDly;
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *       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;
  IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDelay[BL] -");
  for (i = 0; i < 8; i++) {
    IDS_HDT_CONSOLE (MEM_FLOW, " %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_DEBUG_CODE (
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tPass/Fail -");
    for (i = 0, j -= 8; i < 8; i++, j++) {
      IDS_HDT_CONSOLE (MEM_FLOW, "  %c", (Buffer[j] == Pattern[j]) ? 'P' : '.');
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Measured -");
    for (i = 0, j -= 8; i < 8; i++, j++) {
      IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[j]);
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Expected -");
    for (i = 0, j -= 8; i < 8; i++, j++) {
      IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[j]);
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\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)) {
        ASSERT (FALSE);
        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];
  }
}
