  localparam GWMC_PRESCALER_PERIOD  = 200000;       // maintenance period (ps)
  localparam  APP_DATA_WIDTH = (ECC== "ON") ? nCK_PER_CLK*2*64 : nCK_PER_CLK*2*DQ_WIDTH;
  localparam  nDq_pre_Dqs = DRAM_WIDTH == 16 ? 8 : DRAM_WIDTH;
  localparam  DQS_WIDTH = DQ_WIDTH/nDq_pre_Dqs;
  localparam  DM_WIDTH = DQS_WIDTH;
  localparam  ADDR_WIDTH = RANK_WIDTH + BANK_WIDTH + ROW_WIDTH + COL_WIDTH;
  localparam  APP_MASK_WIDTH = APP_DATA_WIDTH/nDq_pre_Dqs;
  localparam RAM_WIDTH  = (nCK_PER_CLK == 4 ? 1 : 2 ) * (APP_DATA_WIDTH + APP_MASK_WIDTH);
  localparam  RANKS = 1;
  localparam  nCS_PER_RANK = 1; 
  localparam  nSLOTS = 1;
  localparam  WIDTH_CS          = 1;
  localparam  CS_WIDTH          = 1;
  localparam OUT_CMD_WIDTH = RANK_WIDTH + BANK_WIDTH + ROW_WIDTH + 1 + 1 + 1;

  // BANK Contrl State Machine
  localparam GWMCST_POWERON         = 30'b1<<0;
  localparam GWMCST_IDLE            = 30'b1<<1;
  localparam GWMCST_ACT_WR          = 30'b1<<2;
  localparam GWMCST_ACT2WR          = 30'b1<<3;
  localparam GWMCST_WRITE           = 30'b1<<4;
  localparam GWMCST_WRITE_P         = 30'b1<<5;
  localparam GWMCST_W2R             = 30'b1<<6;
  localparam GWMCST_ACTED_WR        = 30'b1<<7;
  localparam GWMCST_WP2AR           = 30'b1<<8;
  localparam GWMCST_WP2AW           = 30'b1<<9;
  localparam GWMCST_WP2REF          = 30'b1<<10;
  localparam GWMCST_WP2SRE          = 30'b1<<11;
  localparam GWMCST_ACT_RD          = 30'b1<<12;
  localparam GWMCST_ACT2RD          = 30'b1<<13;
  localparam GWMCST_READ            = 30'b1<<14;
  localparam GWMCST_READ_P          = 30'b1<<15;
  localparam GWMCST_R2W             = 30'b1<<16;
  localparam GWMCST_ACTED_RD        = 30'b1<<17;
  localparam GWMCST_RP2AR           = 30'b1<<18;
  localparam GWMCST_RP2AW           = 30'b1<<19;
  localparam GWMCST_RP2REF          = 30'b1<<20;
  localparam GWMCST_RP2SRE          = 30'b1<<21;
  localparam GWMCST_PD              = 30'b1<<22;
  localparam GWMCST_REF             = 30'b1<<23;
  localparam GWMCST_SREI            = 30'b1<<24;
  localparam GWMCST_SRE             = 30'b1<<25;
  localparam GWMCST_SREE            = 30'b1<<26;
  localparam GWMCST_ZQ              = 30'b1<<27;

  //***************************************************************************
  // Convert timing parameters from time to clock cycles
  //***************************************************************************
  localparam LAL                    = (DRAM_TYPE == "DDR3") ? ( (AL == "0") ? 0 : (AL == "CL-1") ? CL-1 : CL-2 ) :
                                                              ( (AL == "0") ? 0 : (AL == "1"   ) ?    1 :    2 ) ;

  localparam nCKE = cdiv(tCKE, tCK);
  localparam nRP  = cdiv(tRP, tCK);
  localparam nRCD = cdiv(tRCD, tCK) - LAL;
  localparam nRAS = cdiv(tRAS, tCK);
  localparam nFAW = cdiv(tFAW, tCK);
  localparam nRFC = cdiv(tRFC, tCK);

  localparam nWR_CK  = cdiv(15000, tCK);
  localparam nRRD_CK = cdiv(tRRD, tCK);
  localparam nWTR_CK = cdiv(tWTR, tCK);
  localparam nRTP_CK = cdiv(tRTP, tCK);
  // Convert tWR. As per specification, write recover for autoprecharge
  // cycles doesn't support values of 9 and 11. Round up 9 to 10 and 11 to 12
  localparam nWR = (nWR_CK == 9) ? 10 : (nWR_CK == 11) ? 12 : nWR_CK;

  // tRRD, tWTR at tRTP have a 4 cycle floor in DDR3 and 2 cycle floor in DDR2
  localparam nRRD = (DRAM_TYPE == "DDR3") ? (nRRD_CK < 4) ? 4 : nRRD_CK
                                          : (nRRD_CK < 2) ? 2 : nRRD_CK;

  localparam nWTR = (DRAM_TYPE == "DDR3") ? (nWTR_CK < 4) ? 4 : nWTR_CK
                                          : (nWTR_CK < 2) ? 2 : nWTR_CK;

  localparam nRTP = (DRAM_TYPE == "DDR3") ? (nRTP_CK < 4) ? 4 : nRTP_CK
                                          : (nRTP_CK < 2) ? 2 : nRTP_CK;

  // Add a cycle to CL/CWL for the register in RDIMM devices
  localparam nCWL = (REG_CTRL == "ON") ? CWL + 1 : CWL;
  localparam nCL  = (REG_CTRL == "ON") ? CL  + 1 : CL;
  
  localparam nWL  = (DRAM_TYPE == "DDR3") ? nCWL + LAL : nCL  + LAL - 1;
  localparam nRL  = nCL  + LAL;

  // Add combination cycle
  localparam nWTP  = nWL  + nWR  + (BURST_MODE == "4" ? 2 : 4);
  localparam nCRTW = nRL  - nWL  + (BURST_MODE == "4" ? 2 : 4) + 2;
  localparam nCWTR = nWL  + nWTR + (BURST_MODE == "4" ? 2 : 4);

  localparam nCKESR = nCKE + 1;



  localparam nXSD_MCCLKS = (nCK_PER_CLK == 1) ?  tDLLK: 
                           (nCK_PER_CLK == 2) ? ((tDLLK/2) + (tDLLK%2)) : 
                                                ((tDLLK/4) + ((tDLLK%4) ? 1 : 0));

  localparam nCKESR_MCCLKS = (nCK_PER_CLK == 1) ? nCKESR : 
                             (nCK_PER_CLK == 2) ? ((nCKESR/2) + (nCKESR%2)) : 
                                                  ((nCKESR/4) + ((nCKESR%4) ? 1 : 0));

  //***************************************************************************
  // Convert timing parameters from ddr clock cycles to MC clock cycles
  //***************************************************************************
  // precharge timer.
  localparam nRP_MCCLKS = (nCK_PER_CLK == 1) ? nRP : 
                          (nCK_PER_CLK == 2) ? ((nRP/2) + (nRP%2)) : 
                                               ((nRP/4) + ((nRP%4) ? 1 : 0));


// RCD timer
//
// When CWL is even, CAS commands are issued on slot 0 and RAS commands are
// issued on slot 1. This implies that the RCD can never expire in the same
// cycle as the RAS (otherwise the CAS for a given transaction would precede
// the RAS). Similarly, this can also cause premature expiration for longer
// RCD. An offset must be added to RCD before translating it to the FPGA clock
// domain. In this mode, CAS are on the first DRAM clock cycle corresponding to
// a given FPGA cycle. In 2:1 mode add 2 to generate this offset aligned to
// the FPGA cycle. Likewise, add 4 to generate an aligned offset in 4:1 mode.
// 
// When CWL is odd, RAS commands are issued on slot 0 and CAS commands are
// issued on slot 1. There is a natural 1 cycle seperation between RAS and CAS
// in the DRAM clock domain so the RCD can expire in the same FPGA cycle as the
// RAS command. In 2:1 mode, there are only 2 slots so direct translation
// correctly places the CAS with respect to the corresponding RAS. In 4:1 mode,
// there are two slots after CAS, so 2 is added to shift the timer into the
// next FPGA cycle for cases that can't expire in the current cycle.
//
// In 2T mode, the offset from ROW to COL commands is fixed at 2. In 2:1 mode,
// It is sufficient to translate to the half-rate domain and add the remainder.
// In 4:1 mode, we must translate to the quarter-rate domain and add an
// additional fabric cycle only if the remainder exceeds the fixed offset of 2

  localparam nRCD_MCCLKS = nCK_PER_CLK == 1 ? nRCD :
                           nCK_PER_CLK == 2 ? nWL % 2 ? (nRCD/2)                        : (nRCD+2) / 2 :
                        //(nCK_PER_CLK == 4)
                                              nWL % 2 ? (nRCD-2 ? (nRCD-2) / 4 + 1 : 1) : nRCD/4 + 1;



  localparam nRAS_MCCLKS = (nCK_PER_CLK == 1)  ? nRAS  : 
                           (nCK_PER_CLK == 2) ? ((nRAS/2) + (nRAS % 2)) : 
                                                ((nRAS/4) + ((nRAS%4) ? 1 : 0));

  //localparam nWTP = CWL + ((BURST_MODE == "4") ? 2 : 4) + nWR;

// Unless 2T mode, add one to nWTP_CLKS for 2:1 mode.  This accounts for loss of
// one DRAM CK due to column command to row command fixed offset. In 2T mode,
// Add the remainder. In 4:1 mode, the fixed offset is -2. Add 2 unless in 2T
// mode, in which case we add 1 if the remainder exceeds the fixed offset.

  localparam nWTP_MCCLKS = (nCK_PER_CLK == 1) ? nWTP :
                           (nCK_PER_CLK == 2) ? (nWTP/2) + ((ADDR_CMD_MODE == "2T") ? nWTP%2 : 1) :
                                                (nWTP/4) + ((ADDR_CMD_MODE == "2T") ? (nWTP%4 > 2 ? 2 : 1) : 2);

// RFC ZQ XSDLL timer.  Generates delay from refresh, self-refresh exit or ZQ
// command until the end of the maintenance operation.

// Compute values for RFC, ZQ and XSDLL periods.
  localparam nRFC_MCCLKS =  (nCK_PER_CLK == 1) ? nRFC :
                            (nCK_PER_CLK == 2) ? ((nRFC/2) + (nRFC%2)) :
                                                 ((nRFC/4) + ((nRFC%4) ? 1 : 0));


  //***************************************************************************
  // RRD configuration.  The bank machines have a mechanism to prevent RAS to
  // RAS on adjacent fabric CLK states to the same rank.  When
  // nCK_PER_CLK == 1, this translates to a minimum of 2 for nRRD, 4 for nRRD
  // when nCK_PER_CLK == 2 and 8 for nRRD when nCK_PER_CLK == 4. Some of the
  // higher clock rate DDR3 DRAMs have nRRD > 4.  The additional RRD inhibit
  // is worked into the inhbt_faw signal.
  //***************************************************************************

  // divide by nCK_PER_CLK and add a cycle if there's a remainder
  localparam nRRD_MCCLKS = (nCK_PER_CLK == 1) ?  nRRD                    :
                           (nCK_PER_CLK == 2) ?  ((nRRD/2)+(nRRD%2)) :
                                                 ((nRRD/4)+((nRRD%4) ? 1 : 0));

  // take binary log to obtain counter width and add a tick for the idle cycle

// RTP timer. Unless 2T mode, add one for 2:1 mode. This accounts for loss of
// one DRAM CK due to column command to row command fixed offset. In 2T mode,
// Add the remainder. In 4:1 mode, the fixed offset is -2. Add 2 unless in 2T
// mode, in which case we add 1 if the remainder exceeds the fixed offset.
  localparam nRTP_MCCLKS1 = (nCK_PER_CLK == 1) ? nRTP :
                           (nCK_PER_CLK == 2) ? (nRTP/2) + ((ADDR_CMD_MODE == "2T") ? nRTP%2 : 1) :
                                                (nRTP/4) + ((ADDR_CMD_MODE == "2T") ? (nRTP%4 > 2 ? 2 : 1) : 2);

  localparam nRTP_MCCLKS = ((nRTP_MCCLKS1-1) <= 0) ? 0 : nRTP_MCCLKS1-1;


  localparam nRTW_MCCLKS = (nCK_PER_CLK == 1) ?  nCRTW                    :
                           (nCK_PER_CLK == 2) ?  ((nCRTW/2)+(nCRTW%2))     :
                                                 ((nCRTW/4)+((nCRTW%4) ? 1 : 0));

  localparam nWTR_MCCLKS = (nCK_PER_CLK == 1) ?  nCWTR                    :
                           (nCK_PER_CLK == 2) ?  ((nCWTR/2)+(nCWTR%2))     :
                                                 ((nCWTR/4)+((nCWTR%4) ? 1 : 0));



  localparam nFAW_MCCLKS = (nCK_PER_CLK == 1) ? nFAW : 
                           (nCK_PER_CLK == 2) ? ((nFAW/2) + (nFAW%2)) : 
                                                ((nFAW/4) + ((nFAW%4) ? 1 : 0));

  localparam RAS_CNT_WIDTH   = clogb2(nRAS_MCCLKS   + 1);
  localparam RCD_CNT_WIDTH   = clogb2(nRCD_MCCLKS   + 1);
  localparam RFC_CNT_WIDTH   = clogb2(nRFC_MCCLKS   + 1);
  localparam RP_CNT_WIDTH    = clogb2(nRP_MCCLKS    + 1);
  localparam RRD_CNT_WIDTH   = clogb2(nRRD_MCCLKS   + 1);
  localparam RTP_CNT_WIDTH   = clogb2(nRTP_MCCLKS   + 1);
  localparam WTP_CNT_WIDTH   = clogb2(nWTP_MCCLKS   + 1);
  localparam RTW_CNT_WIDTH   = clogb2(nRTW_MCCLKS   + 1);
  localparam WTR_CNT_WIDTH   = clogb2(nWTR_MCCLKS   + 1);
  localparam FAW_CNT_WIDTH   = clogb2(nFAW_MCCLKS   + 1);
  localparam XSD_CNT_WIDTH   = clogb2(nXSD_MCCLKS   + 1);
  localparam CKESR_CNT_WIDTH = clogb2(nCKESR_MCCLKS + 1);

  // CK clock divisor to generate maintenance prescaler period (round down)
  localparam PRESCALER_DIV = (GWMC_PRESCALER_PERIOD/(tCK*nCK_PER_CLK)) - 1;

  // Maintenance prescaler divisor for refresh timer. Essentially, this is
  // just (tREFI / MAINT_PRESCALER_PERIOD), but we must account for the worst
  // case delay from the time we get a tick from the refresh counter to the
  // time that we can actually issue the REF command. Thus, subtract tRCD, CL,
  // data burst time and tRP for each implemented bank machine to ensure that
  // all transactions can complete before tREFI expires
  localparam REFRESH_TIMER_DIV = USER_REFRESH == "ON" ? 0 :
                                                       ((tREFI-(tRCD+((CL+4)*tCK)+tRP)) / ((PRESCALER_DIV+1)*tCK*nCK_PER_CLK)) - 1;


  localparam GWMC_PRESCALER_WIDTH     = clogb2(PRESCALER_DIV);
  localparam GWMC_REFRESH_TIMER_WIDTH = clogb2(REFRESH_TIMER_DIV);












  //***************************************************************************
  // Function cdiv
  //  Description:
  //    This function performs ceiling division (divide and round-up)
  //  Inputs:
  //    num: integer to be divided
  //    div: divisor
  // Outputs:
  //    cdiv: result of ceiling division (num/div, rounded up)
  //***************************************************************************

  function integer cdiv (input integer num, input integer div);
    begin
      // perform division, then add 1 if and only if remainder is non-zero
      cdiv = (num/div) + (((num%div)>0) ? 1 : 0);
    end
  endfunction // cdiv

  function integer clogb2 (input integer size); // ceiling logb2
    begin
      size = size - 1;
      for (clogb2=1; size>1; clogb2=clogb2+1)
            size = size >> 1;
    end
  endfunction // clogb2
