------------------------------------------------------------------------------
--  This file is a part of the GRLIB VHDL IP LIBRARY
--  Copyright (C) 2003 - 2008, Gaisler Research
--  Copyright (C) 2008 - 2014, Aeroflex Gaisler
--  Copyright (C) 2015 - 2019, Cobham Gaisler
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; if not, write to the Free Software
--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
-----------------------------------------------------------------------------
-- Entity:      ddr2spax
-- File:        ddr2spax.vhd
-- Author:      Magnus Hjorth - Aeroflex Gaisler
-- Description: DDR2 memory controller with asynch AHB interface
--              Based on ddr2sp(16/32/64)a, generalized and expanded
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library grlib;
use grlib.stdlib.all;
use grlib.amba.all;
use grlib.devices.all;
library gaisler;
use gaisler.ddrpkg.all;
use gaisler.ddrintpkg.all;

entity ddr2spax_ddr is
   generic (
      ddrbits    : integer := 32;
      burstlen   : integer := 8;
      MHz        : integer := 100;
      TRFC       : integer := 130;
      col        : integer := 9;
      Mbyte      : integer := 8;
      pwron      : integer := 0;
      oepol      : integer := 0;
      readdly    : integer := 1;
      odten      : integer := 0;
      octen      : integer := 0;
      -- dqsgating  : integer := 0;
      nosync     : integer := 0;
      dqsgating  : integer := 0;
      eightbanks : integer range 0 to 1 := 0; -- Set to 1 if 8 banks instead of 4
      dqsse      : integer range 0 to 1 := 0;  -- single ended DQS
      ddr_syncrst: integer range 0 to 1 := 0;
      chkbits     : integer := 0;
      bigmem     : integer range 0 to 1 := 0;
      raspipe    : integer range 0 to 1 := 0;
      hwidthen   : integer range 0 to 1 := 0;
      phytech    : integer := 0;
      hasdqvalid : integer := 0;
      rstdel     : integer := 200;
      phyptctrl  : integer := 0;
      scantest   : integer := 0;
      dis_caslat : integer := 0;
      dis_init   : integer := 0;
      cke_rst    : integer := 0
   );
   port (
      ddr_rst  : in  std_ulogic;
      clk_ddr  : in  std_ulogic;
      request  : in  ddr_request_type;
      start_tog: in  std_logic;
      response : out ddr_response_type;
      sdi      : in  ddrctrl_in_type;
      sdo      : out ddrctrl_out_type;
      wbraddr  : out std_logic_vector(log2((16*burstlen)/ddrbits) downto 0);
      wbrdata  : in  std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwaddr  : out std_logic_vector(log2((16*burstlen)/ddrbits)-1 downto 0);
      rbwdata  : out std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwrite  : out std_logic;
      hwidth   : in std_ulogic;
      reqsel   : in std_ulogic;
      frequest : in  ddr_request_type;
      response2: out ddr_response_type;
      testen   : in std_ulogic;
      testrst  : in std_ulogic;
      testoen  : in std_ulogic
   );  
end ddr2spax_ddr;

architecture rtl of ddr2spax_ddr is

  constant CMD_PRE  : std_logic_vector(2 downto 0) := "010";
  constant CMD_REF  : std_logic_vector(2 downto 0) := "100";
  constant CMD_LMR  : std_logic_vector(2 downto 0) := "110";
  constant CMD_EMR  : std_logic_vector(2 downto 0) := "111";
  
  function tosl(x: integer) return std_logic is
  begin
    if x /= 0 then return '1'; else return '0'; end if;
  end tosl;

  function zerov(w: integer) return std_logic_vector is
    constant r: std_logic_vector(w-1 downto 0) := (others => '0');
  begin
    return r;
  end zerov;

  constant l2blen: integer := log2(burstlen)+log2(32);
  constant l2ddrw: integer := log2(ddrbits*2);
  
  constant oepols: std_logic := tosl(oepol);

  -- Write buffer dimensions
  -- Write buffer is addressable down to 32-bit level on write (AHB) side.
  constant wbuf_rabits: integer := 1+l2blen-l2ddrw; -- log2((burstlen*32)/(2*ddrbits));
  constant wbuf_rdbits: integer := 2*ddrbits;
  -- Read buffer dimensions
  constant rbuf_wabits: integer := l2blen-l2ddrw; -- log2((burstlen*32)/(2*ddrbits));
  constant rbuf_wdbits: integer := 2*(ddrbits+chkbits);
  
  -- sdram configuration register
  type sdram_cfg_type is record
    command    : std_logic_vector(2 downto 0);
    csize      : std_logic_vector(1 downto 0);
    bsize      : std_logic_vector(3 downto 0);
    trcd       : std_logic_vector(2 downto 0);  -- tRCD : 2-9 clock cycles
    trfc       : std_logic_vector(7 downto 0);
    trp        : std_logic_vector(2 downto 0);  -- precharge to activate: 2-9 clock cycles
    refresh    : std_logic_vector(11 downto 0);
    renable    : std_ulogic;
    dllrst     : std_ulogic;
    refon      : std_ulogic;
    cke        : std_ulogic;
    cal_en     : std_logic_vector(7 downto 0);
    cal_inc    : std_logic_vector(7 downto 0);
    cbcal_en   : std_logic_vector(3 downto 0);
    cbcal_inc  : std_logic_vector(3 downto 0);    
    cal_pll    : std_logic_vector(1 downto 0);  -- *** ??? pll_reconf
    cal_rst    : std_logic;
    readdly    : std_logic_vector(3 downto 0);
    twr        : std_logic_vector(4 downto 0);
    emr        : std_logic_vector(1 downto 0); -- selects EM register
    ocd        : std_ulogic; -- enable/disable ocd
    dqsctrl    : std_logic_vector(7 downto 0);
    eightbanks : std_ulogic;
    caslat     : std_logic_vector(1 downto 0);  -- CAS latency 3-6
    odten      : std_logic_vector(1 downto 0);
    tras       : std_logic_vector(4 downto 0);  -- RAS-to-Precharge minimum
    trtp       : std_ulogic;
    regmem     : std_ulogic;            -- Registered memory (1 cycle extra latency)
    strength   : std_ulogic;            -- Drive strength 1=reduced, 0=normal
  end record;

  constant ddr_burstlen: integer := (burstlen*32)/(2*ddrbits);
  constant l2ddr_burstlen: integer := l2blen-l2ddrw;
  
  type ddrstate is (dsidle,dsrascas,dscaslat,dsreaddly,dsdata,dsdone,dsagain,dsreg,dsrefresh,dspreall);
  type ddrcmdstate is (dcrstdel,dcoff,dcinit1,dcinit2,dcinit3,dcinit4,dcinit5,dcinit6,dcinit7,dcinit8,dcon);
  
  type ddr_reg_type is record
    s             : ddrstate;
    cmds          : ddrcmdstate;
    response      : ddr_response_type;
    response1     : ddr_response_type;
    response2     : ddr_response_type;
    response_prev : ddr_response_type;
    cfg           : sdram_cfg_type;
    rowsel        : std_logic_vector(2 downto 0);
    endaddr       : std_logic_vector(l2blen-4 downto 2);
    addrlo        : std_logic_vector(l2ddrw-4 downto 0);
    col           : std_logic_vector(13 downto 0);
    hwrite        : std_logic;
    hsize         : std_logic_vector(2 downto 0);
    ctr           : std_logic_vector(7 downto 0);
    casctr        : std_logic_vector(l2ddr_burstlen-1 downto 0);
    datacas       : std_logic;
    prectr        : std_logic_vector(5 downto 0);
    rastimer      : std_logic_vector(4 downto 0);
    tras_met      : std_logic;
    pchpend       : std_logic;
    refctr        : std_logic_vector(16 downto 0);
    refpend       : std_logic;
    pastlast      : std_logic;
    sdo_csn       : std_logic_vector(1 downto 0);
    sdo_wen       : std_ulogic;
    wen_prev      : std_ulogic;
    sdo_rasn      : std_ulogic;
    rasn_pre      : std_ulogic;
    sdo_casn      : std_ulogic;
    sdo_dqm       : std_logic_vector(15 downto 0);
    dqm_prev      : std_logic_vector(15 downto 0);
    twr_plus_cl   : std_logic_vector(5 downto 0);
    request_row   : std_logic_vector(14 downto 0);
    request_bank  : std_logic_vector(2 downto 0);
    request_cs    : std_logic_vector(0 downto 0);
    row           : std_logic_vector(14 downto 0);
    setrow        : std_logic;
    samerow       : std_logic;
    start_tog_prev: std_logic;
    sdo_bdrive    : std_ulogic;
    sdo_qdrive    : std_ulogic;
    sdo_nbdrive   : std_ulogic;
    sdo_address   : std_logic_vector(14 downto 0);
    sdo_address_prev: std_logic_vector(14 downto 0);
    sdo_ba        : std_logic_vector(2 downto 0);
    sdo_data      : std_logic_vector(sdo.data'length-1 downto 0);
    sdo_cb        : std_logic_vector(sdo.cb'length-1 downto 0);
    sdo_odt       : std_logic;
    sdo_oct       : std_logic;
    rbwrite       : std_logic;
    rbwdata       : std_logic_vector(rbuf_wdbits-1 downto 0);
    ramaddr       : std_logic_vector(rbuf_wabits-1 downto 0);
    ramaddr_prev  : std_logic_vector(rbuf_wabits-1 downto 0);
    mr_twr        : std_logic_vector(2 downto 0);
    mr_tcl        : std_logic_vector(2 downto 0);
    read_pend     : std_logic_vector(15 downto 0);
    req1,req2     : ddr_request_type;
    start1,start2 : std_logic;
    hwidth1       : std_logic;
    hwidth        : std_logic;    
    hwcas         : std_logic;
    hwctr         : std_logic;
  end record;

  signal dr,ndr    : ddr_reg_type;

  signal muxsel2,muxsel1,muxsel0: std_ulogic;
  signal muxin4: std_logic_vector(31 downto 0);
  signal muxout4: std_logic_vector(3 downto 0);

  signal start_tog_delta1,start_tog_delta2: std_logic;
  signal arst: std_ulogic;
  
  attribute syn_keep: boolean;
  attribute syn_keep of muxsel2:signal is true;
  attribute syn_keep of muxsel1:signal is true;
  attribute syn_keep of muxsel0:signal is true;
  
begin

  arst <= testrst when (scantest/=0 and ddr_syncrst=0) and testen='1' else ddr_rst;
  
  start_tog_delta1 <= start_tog;
  start_tog_delta2 <= start_tog_delta1;
  
  muxsel2 <= dr.rowsel(2);
  muxsel1 <= dr.rowsel(1);
  muxsel0 <= dr.rowsel(0);
  
  muxproc : process(muxin4,muxsel2,muxsel1,muxsel0)
  begin
    muxout4(3) <= genmux((muxsel2 & muxsel1 & muxsel0),muxin4(31 downto 24));
    muxout4(2) <= genmux((muxsel2 & muxsel1 & muxsel0),muxin4(23 downto 16));
    muxout4(1) <= genmux((muxsel2 & muxsel1 & muxsel0),muxin4(15 downto 8));
    muxout4(0) <= genmux((muxsel2 & muxsel1 & muxsel0),muxin4(7 downto 0));
  end process;
  
  ddrcomb : process(ddr_rst,sdi,request,frequest,start_tog_delta2,dr,wbrdata,muxout4,hwidth,reqsel,testen,testoen)
    constant plmemwrite: boolean := false;
    constant plmemread: boolean := false;
    
    variable dv: ddr_reg_type;
    variable o: ddrctrl_out_type;
    variable bdrive,qdrive: std_logic;
    variable vreq,vreqf: ddr_request_type;
    variable resp,resp2: ddr_response_type;
    variable vstart: std_logic;
    variable acsn: std_logic_vector(1 downto 0);
    variable arow: std_logic_vector(14 downto 0);
    variable acol: std_logic_vector(13 downto 0);
    variable abank: std_logic_vector(2 downto 0);
    variable aendaddr: std_logic_vector(l2blen-4 downto 2);
    variable aloa: std_logic_vector(l2ddrw-4 downto 0);
    variable rbw: std_logic;
    variable rbwd: std_logic_vector(rbuf_wdbits-1 downto 0);
    variable rbwa: std_logic_vector(rbuf_wabits-1 downto 0);
    variable wbra: std_logic_vector(wbuf_rabits-1 downto 0);
    variable regdata: std_logic_vector(31 downto 0);
    variable regsd1   : std_logic_vector(31 downto 0);   -- data from registers
    variable regsd2   : std_logic_vector(31 downto 0);   -- data from registers
    variable regsd3   : std_logic_vector(31 downto 0);   -- data from registers
    variable regsd4   : std_logic_vector(31 downto 0);   -- data from registers
    variable regsd5   : std_logic_vector(31 downto 0);   -- data from registers
    variable mr       : std_logic_vector(14 downto 0);  -- DDR2 Mode register
    variable mask: std_logic_vector(15 downto 0);
    variable hio1: std_logic;
    variable w5: std_logic;
    variable precharge_next: std_logic;
    variable precharge_notras: std_logic;
    variable goto_caslat: std_logic;
    variable block_precharge: std_logic;
    variable regt0,regt1: std_logic_vector(ddrbits-1 downto 0);
    variable addrtemp3,addrtemp2,addrtemp1,addrtemp0: std_logic_vector(7 downto 0);
    variable expcsize: std_logic_vector(2 downto 0);
    variable caslat_reg: std_logic_vector(2 downto 0);
    variable addrlo32, endaddr32: std_logic_vector(3 downto 2);
    variable endaddr43: std_logic_vector(4 downto 3);
    variable endaddr42: std_logic_vector(4 downto 2);
    variable inc_rctr: std_logic;
    
  begin
    
    
    dv := dr;
    o := ddrctrl_out_none;
    o.sdcke     := (others => dr.cfg.cke);
    o.sdcsn     := dr.sdo_csn;
    o.sdwen     := dr.wen_prev;
    o.rasn      := dr.sdo_rasn and dr.rasn_pre;
    o.casn      := dr.sdo_casn and dr.datacas;
    o.dqm       := dr.dqm_prev;
    o.bdrive    := dr.sdo_bdrive;
    o.qdrive    := dr.sdo_qdrive;
    o.nbdrive   := dr.sdo_nbdrive;
    o.address   := dr.sdo_address;
    o.data      := dr.sdo_data;
    o.ba        := dr.sdo_ba;
    o.cal_en    := dr.cfg.cal_en;
    o.cal_inc   := dr.cfg.cal_inc;
    o.cal_pll   := dr.cfg.cal_pll;
    o.cal_rst   := dr.cfg.cal_rst;
    o.odt       := (others => dr.sdo_odt);
    o.oct       := dr.sdo_oct;
    o.cb        := dr.sdo_cb;
    o.cbcal_en  := dr.cfg.cbcal_en;
    o.cbcal_inc := dr.cfg.cbcal_inc;

    resp := ddr_response_none;
    resp2 := ddr_response_none;
    
    rbw  := dr.rbwrite;
    rbwd := dr.rbwdata;
    rbwa := (others => '0');
    w5 := '0';
    wbra := dr.response.done_tog & dr.ramaddr;

    dv.ramaddr_prev := dr.ramaddr;
    dv.dqm_prev := dr.sdo_dqm;
    dv.wen_prev := dr.sdo_wen;
    dv.response_prev := dr.response;
    dv.sdo_address_prev := dr.sdo_address;
    
    dv.cfg.cal_en := (others => '0');
    dv.cfg.cal_inc := (others => '0');
    dv.cfg.cal_pll := (others => '0');
    dv.cfg.cal_rst := '0';
    dv.cfg.cbcal_en := (others => '0');
    dv.cfg.cbcal_inc := (others => '0');
    
    dv.sdo_data := (others => '0');
    dv.sdo_data(2*ddrbits-1 downto ddrbits) := wbrdata(2*ddrbits+chkbits-1 downto ddrbits+chkbits);
    dv.sdo_data(ddrbits-1 downto 0) := wbrdata(ddrbits-1 downto 0);

    dv.sdo_cb := (others => '0');
    if chkbits > 0 then
      dv.sdo_cb(2*chkbits-1 downto chkbits) := wbrdata(2*ddrbits+2*chkbits-1 downto 2*ddrbits+chkbits);
      dv.sdo_cb(chkbits-1 downto 0) := wbrdata(ddrbits+chkbits-1 downto ddrbits);
    end if;
    
    if hwidthen/=0 and dr.hwidth='1' and dr.hwctr='1' then
      dv.sdo_data(ddrbits-1 downto 0) := dr.sdo_data(2*ddrbits-1 downto ddrbits);
      if chkbits > 0 then
        dv.sdo_cb(chkbits-1 downto 0) := dr.sdo_cb(2*chkbits-1 downto chkbits);
      end if;
    end if;

    if not (hwidthen/=0 and hasdqvalid/=0 and sdi.datavalid='0') then

      dv.rbwdata(2*ddrbits+chkbits-1 downto ddrbits+chkbits) := sdi.data(2*ddrbits-1 downto ddrbits);
      dv.rbwdata(ddrbits-1 downto 0) := sdi.data(ddrbits-1 downto 0);
      if chkbits > 0 then
        dv.rbwdata(2*ddrbits+2*chkbits-1 downto 2*ddrbits+chkbits) := sdi.cb(2*chkbits-1 downto chkbits);
        dv.rbwdata(ddrbits+chkbits-1 downto ddrbits) := sdi.cb(chkbits-1 downto 0);
      end if;
      
      -- Half-width input data muxing
      if hwidthen/=0 and dr.hwidth='1' and dr.hwctr='1' then      
        dv.rbwdata(2*ddrbits+chkbits-1 downto 2*ddrbits+chkbits-ddrbits/2) :=
          dr.rbwdata(2*ddrbits+chkbits-ddrbits/2-1 downto ddrbits+chkbits);      
        dv.rbwdata(2*ddrbits+chkbits-ddrbits/2-1 downto ddrbits+chkbits) :=
          dr.rbwdata(ddrbits/2-1 downto 0);      
        dv.rbwdata(ddrbits-1 downto ddrbits/2) :=
          sdi.data(ddrbits+ddrbits/2-1 downto ddrbits);
        
        if chkbits > 0 then
          dv.rbwdata(2*ddrbits+2*chkbits-1 downto 2*ddrbits+2*chkbits-chkbits/2) :=
            dr.rbwdata(2*ddrbits+2*chkbits-chkbits/2-1 downto 2*ddrbits+chkbits);
          dv.rbwdata(2*ddrbits+2*chkbits-chkbits/2-1 downto 2*ddrbits+chkbits) :=
            dr.rbwdata(ddrbits+chkbits/2-1 downto ddrbits);
          dv.rbwdata(ddrbits+chkbits-1 downto ddrbits+chkbits/2) :=
            sdi.cb(chkbits+chkbits/2-1 downto chkbits);
        end if;
        
      end if;
      
    end if;
    
    -- hwidth input should be constant but sample it for robustness
    -- then sample in one more stage to allow replication if necessary
    dv.hwidth1 := hwidth;
    dv.hwidth := dr.hwidth1;
    if hwidthen=0 then dv.hwidth:='0'; end if;
    
    -- Synchronize 1/2 stages
    dv.req1 := request; dv.req2 := dr.req1;
    dv.start1 := start_tog_delta2; dv.start2 := dr.start1;
    vstart := dr.start2;
    vreq := dr.req2;
    vreqf := dr.req1;
    if nosync /= 0 then vstart:=start_tog_delta2; vreq:=request; vreqf:=request; end if;
    if nosync > 1 then vreqf:=frequest; end if;
    dv.start_tog_prev := vstart;

    regsd1 := (others => '0');
    regsd1(31 downto 15) := dr.cfg.refon & dr.cfg.ocd & dr.cfg.emr & dr.cfg.bsize(3) & dr.cfg.trcd(0) &
                            dr.cfg.bsize(2 downto 0) & dr.cfg.csize & dr.cfg.command &
                            dr.cfg.dllrst & dr.cfg.renable & dr.cfg.cke;
    regsd1(11 downto 0) := dr.cfg.refresh;
    regsd2 := (others => '0');
    regsd2(25 downto 18) := std_logic_vector(to_unsigned(phytech,8));
    if bigmem /= 0 then regsd2(17):='1'; end if;
    if chkbits > 0 then regsd2(16):='1'; end if;
    regsd2(15 downto 0) := "1" &
                           std_logic_vector(to_unsigned(log2(ddrbits/8),3)) &
                           std_logic_vector(to_unsigned(MHz,12));
    if dr.hwidth='1' then
      regsd2(14 downto 12) := std_logic_vector(to_unsigned(log2((ddrbits/2)/8),3));
    end if;
    regsd3 := (others => '0');    
    regsd3(17 downto 16) := dr.cfg.readdly(1 downto 0);
    regsd3(22 downto 18) := dr.cfg.trfc(4 downto 0);
    regsd3(27 downto 23) := dr.cfg.twr;
    regsd3(28) := dr.cfg.trp(0);
    regsd4 := (others => '0');
    regsd4(23 downto 22) := dr.cfg.readdly(3 downto 2);
    regsd4(21) := dr.cfg.regmem;
    regsd4(13 downto 0) := dr.cfg.trtp & "00" & dr.cfg.caslat &
                           dr.cfg.eightbanks & dr.cfg.dqsctrl;
    regsd5 := (others => '0');
    regsd5(30 downto 28) := dr.cfg.trp;
    regsd5(25 downto 18) := dr.cfg.trfc;
    regsd5(17 downto 16) := dr.cfg.odten;
    regsd5(15)           := dr.cfg.strength;
    regsd5(10 downto 8)  := dr.cfg.trcd;
    regsd5(4 downto 0)   := dr.cfg.tras;

    case ddrbits is
      when 16 => o.regwdata := dr.sdo_data(31 downto 0) & dr.sdo_data(31 downto 0);
      when 32 => o.regwdata := dr.sdo_data(31 downto 0) & dr.sdo_data(63 downto 32);
      when 64 => o.regwdata := dr.sdo_data(31 downto 0) & dr.sdo_data(63 downto 32);
      when others => o.regwdata := dr.sdo_data(2*ddrbits-7*32-1 downto 2*ddrbits-8*32) &
                                   dr.sdo_data(2*ddrbits-6*32-1 downto 2*ddrbits-7*32);
    end case;

    if dr.cfg.regmem='1' then
      caslat_reg := std_logic_vector(unsigned('0' & dr.cfg.caslat)+1);
    else
      caslat_reg := '0' & dr.cfg.caslat;
    end if;
    
    -- Mode register
    dv.mr_twr := std_logic_vector(unsigned(dr.cfg.twr(2 downto 0))-3);
    if dv.mr_twr="110" or dv.mr_twr="111" or dv.mr_twr="000" then
      dv.mr_twr := "101";
    end if;
    dv.mr_tcl := std_logic_vector(unsigned('0' & dr.cfg.caslat)+3);
    
    mr := (others => '0');
    mr(12)          := '0';             -- Power down exit time
    mr(11 downto 9) := dr.mr_twr;       -- WR-1
    mr(8)           := dr.cfg.dllrst;   -- DLL Reset
    mr(7)           := '0';             -- Test mode
    mr(6 downto 4)  := dr.mr_tcl;       -- CL
    mr(3)           := '0';             -- Burst type, 0=seq 1=interl
    mr(2 downto 0)  := "010";           -- Burst len 010=4, 011=8
    
    -- Calculate address parts from a2ds.haddr and a2ds.startword
    expcsize := dr.hwidth & dr.cfg.csize;
    case expcsize is
      when         "011"   => arow := vreqf.startaddr(l2ddrw+22 downto l2ddrw+8);
      when "111" | "010"   => arow := vreqf.startaddr(l2ddrw+21 downto l2ddrw+7);                              
      when "110" | "001"   => arow := vreqf.startaddr(l2ddrw+20 downto l2ddrw+6);
      when "101" | "000"   => arow := vreqf.startaddr(l2ddrw+19 downto l2ddrw+5);                              
      when others          => arow := vreqf.startaddr(l2ddrw+18 downto l2ddrw+4);
    end case;

    dv.rowsel := dr.cfg.bsize(2 downto 0);
    if bigmem /= 0 and dr.cfg.bsize(3 downto 1)="000" then
      dv.rowsel := "010";
    end if;
    if bigmem = 0 and dr.cfg.bsize(3)='1' then
      dv.rowsel := "111";
    end if;
    
    addrtemp3 := vreqf.startaddr(30 downto 23);  --CS
    addrtemp2 := vreqf.startaddr(29 downto 22);  --BA2/1
    addrtemp1 := vreqf.startaddr(28 downto 21);  --BA1/0
    addrtemp0 := vreqf.startaddr(27 downto 20);  --BA0/-
    if bigmem=1 then
      addrtemp3(1 downto 0) := "0" & vreqf.startaddr(31);
      addrtemp2(1 downto 0) := vreqf.startaddr(31 downto 30);
      addrtemp1(1 downto 0) := vreqf.startaddr(30 downto 29);
      addrtemp0(1 downto 0) := vreqf.startaddr(29 downto 28);
    end if;
    muxin4 <= addrtemp3 & addrtemp2 & addrtemp1 & addrtemp0;
    
    abank := muxout4(2 downto 0);
    if dr.cfg.eightbanks='0' then
      abank := '0' & abank(2) & abank(1);
    end if;

    acol := vreqf.startaddr(log2(ddrbits/8)+13 downto log2(ddrbits/8));
    if ddrbits=16 then acol(0):='0'; end if;  -- Always align to at least 32 bits

    acsn(0) := muxout4(3);
    acsn(1) := not acsn(0);

    dv.setrow := '0';
    if dr.setrow='1' then
      dv.row := dr.sdo_address_prev;
    end if;
    
    dv.samerow := '0';
    if abank=dr.sdo_ba and acsn=dr.sdo_csn and arow=dr.row then
      dv.samerow := '1';
    end if;

    dv.request_row := arow;
    dv.request_cs  := acsn(0 downto 0);
    dv.request_bank := abank;

    hio1 := vreqf.hio;
    if raspipe /= 0 then
      vstart := dr.start_tog_prev;
      arow := dr.request_row;
      acsn := (not dr.request_cs) & dr.request_cs;
      abank := dr.request_bank;
      hio1 := vreq.hio;
    end if;
    
    aendaddr := vreq.endaddr(log2(4*burstlen)-1 downto 2);
    if vreq.hsize(1 downto 0)="11" and vreq.hio='0' then
      aendaddr(2):='1';
    end if;
    if ahbdw > 64 and vreqf.hsize(2)='1' then
      aendaddr(3 downto 2) := "11";
      if ahbdw > 128 and vreqf.hsize(0)='1' then
        aendaddr(4) := '1';
      end if;
    end if;

    aloa(l2ddrw-4 downto 0) := vreq.startaddr(l2ddrw-4 downto 0);
    if ddrbits > 32 then addrlo32 := dr.addrlo(3 downto 2);
    elsif ddrbits > 16 then addrlo32 := '0' & dr.addrlo(2);
    else addrlo32 := "00";
    end if;
    endaddr32 := dr.endaddr(3 downto 2);
    endaddr43 := dr.endaddr(4 downto 3);
    endaddr42 := dr.endaddr(4 downto 2);
    
    -- Calculate data mask    
    mask := (others => dr.pastlast);
    -- Set mask bits for <word access
    if dr.hsize="000" then
      if dr.addrlo(0)='1' then
        mask := mask or "1010101010101010";
      else
        mask := mask or "0101010101010101";
      end if;
    end if;
    if dr.hsize(2 downto 1)="00" then
      if dr.addrlo(1)='1' then
        mask := mask or "1100110011001100";
      else
        mask := mask or "0011001100110011";
      end if;
    end if;
    -- First access
    -- (this could be written in generic code instead)
    if dr.ctr=zerov(dr.ctr'length) then
      case ddrbits is
        when 16 =>
          null;
        when 32 =>
          if dr.addrlo(2)='1' then
            mask(7 downto 0) := mask(7 downto 0) or x"F0";
          end if;
        when 64 =>
          case addrlo32 is
            when "00"   => null;
            when "01"   => mask := mask or x"F000";
            when "10"   => mask := mask or x"FF00";
            when others => mask := mask or x"FFF0";
          end case;
        when others => null;
      end case;      
    end if;
    -- Last access
    if dr.ramaddr = dr.endaddr(log2(4*burstlen)-1 downto log2(2*ddrbits/8)) then
      if hwidthen=0 or dr.hwidth='0' or dr.hwctr='1' then
        dv.pastlast := '1';
      end if;
      case ddrbits is
        when 16 => null;
        when 32 =>
          if dr.endaddr(2)='0' then
            mask(7 downto 0) := mask(7 downto 0) or x"0F";
          end if;
        when 64 =>
          case endaddr32 is
            when "00"   => mask := mask or x"0FFF";
            when "01"   => mask := mask or x"00FF";
            when "10"   => mask := mask or x"000F";
            when others => null;
          end case;
        when others => null;
      end case;
    end if;
    -- Before first
    if dr.col(1)='1' and dr.ctr(0)='1' and dr.ctr(dr.ctr'high downto 1)=zerov(dr.ctr'length-1) then
      mask := mask or x"FFFF";
    end if;
    
    dv.sdo_rasn := '1'; dv.sdo_casn := '1'; dv.sdo_wen := '1';
    dv.sdo_odt := '0'; dv.sdo_oct := '0';
    dv.rbwrite := '0';
    
    dv.ctr := std_logic_vector(unsigned(dr.ctr)+1);

    if hwidthen/=0 and dr.hwidth='1' and dr.s=dsdata then
      dv.hwctr := not dr.hwctr;
      if dr.hwctr='0' then dv.ctr := dr.ctr; end if;
    end if;
    
    dv.rastimer := std_logic_vector(unsigned(dr.rastimer)+1);
    if dr.rastimer=dr.cfg.tras then dv.tras_met := '1'; end if;

    -- Calculate whether we would precharge the next cycle if Tras=0 
    precharge_notras := '0';
    if dr.casctr=zerov(dr.casctr'length) and dr.prectr="000000" and dr.pchpend='1' then
      precharge_notras := '1';
    end if;
    -- Calculate whether we should precharge the next cycle
    precharge_next := precharge_notras and dr.tras_met;
    block_precharge := '0';

    inc_rctr := '0';
    
    goto_caslat := '0';
    case dr.s is
      when dsidle =>
        dv.ctr := (others => '0');
        dv.hwctr := '0';
        dv.sdo_bdrive := not oepols;
        dv.sdo_qdrive := not oepols;
        dv.sdo_nbdrive := not oepols;
        dv.col := acol;
        dv.sdo_csn := (others => '1');
        dv.rastimer := (others => '0');
        dv.tras_met := '0';
        dv.response.rctr_gray := "0000";
        
        if dr.refpend='1' and dr.cfg.refon='1' then
          -- Periodic refresh
          dv.sdo_csn := (others => '0');
          dv.sdo_rasn := '0';
          dv.sdo_casn := '0';
          dv.refpend := '0';
          dv.s := dsrefresh;

        elsif vstart /= dr.response.done_tog and (dr.cmds=dcon or (dr.cmds=dcoff and dr.cfg.renable='0')) then
          -- R/W data
          dv.sdo_rasn := '0' or hio1;
          dv.sdo_csn := acsn;
          dv.sdo_address := arow;
          dv.sdo_ba := abank;
          dv.s := dsrascas;
          
        elsif dr.cfg.command /= "000" then
          -- Command
          dv.sdo_csn := (others => '0');
          if dr.cfg.command(2 downto 1)="11" then
            dv.sdo_wen:='0'; dv.sdo_casn:='0'; dv.sdo_rasn:='0';
            
            dv.sdo_ba := "00" & dr.cfg.command(0);
            if dr.cfg.command(0)='0' or dr.cfg.emr="00" then
              dv.sdo_ba := "000";
              dv.sdo_address := mr;
            else
              dv.sdo_ba := "0" & dr.cfg.emr;
              if dr.cfg.emr="01" then
                dv.sdo_address := "0000"&conv_std_logic(dqsse=1)&dr.cfg.ocd&dr.cfg.ocd&dr.cfg.ocd 
                                  & dr.cfg.odten(1)&"000"& dr.cfg.odten(0) & dr.cfg.strength & "0"; 
                
              else
                dv.sdo_address := (others => '0');
              end if;
            end if;            
          else
            dv.sdo_wen := dr.cfg.command(2);
            dv.sdo_casn := dr.cfg.command(1);
            dv.sdo_rasn := dr.cfg.command(0);
            dv.sdo_address(10) := '1';
            -- print("X Command: " & tost(dr.cfg.command) & " -> casn:" & tost(dv.sdo_casn) & ",rasn:" & tost(dv.sdo_rasn) & ",wen:" & tost(dv.sdo_wen));
            
          end if;
          dv.cfg.command := "000";
          if dr.cfg.command=CMD_REF then
            dv.s := dsrefresh;
          end if;
          if dr.cfg.command=CMD_PRE then
            dv.s := dspreall;
          end if;
        end if;
        
      when dsrascas =>
        if dr.ctr(2 downto 0)="000" then
          -- pragma translate_off
          assert dr.ctr="00000000" severity failure;
          -- pragma translate_on
          -- dv.row := dr.sdo_address;
          dv.setrow := '1';
        end if;
        dv.hwrite := vreq.hwrite;
        dv.hsize := vreq.hsize;
        dv.endaddr := aendaddr;
        dv.addrlo := aloa;
        dv.sdo_address := dr.col(13 downto 10) & '0' & dr.col(9 downto 1) & '0';
        if dr.hwidth='1' then
          dv.sdo_address := dr.col(12 downto 9) & '0' & dr.col(8 downto 1) & "00";
        end if;
        
        if vreq.hio='1' and dr.ctr(0)='1' then
          dv.s := dsreg;
          dv.ctr := (others => '0');          
          dv.hwctr := '0';
        elsif vreq.hio='0' and dr.ctr(2 downto 0)=dr.cfg.trcd then
          goto_caslat := '1';
        end if;

      when dscaslat =>
        dv.sdo_odt := dr.hwrite;
        dv.sdo_oct := not dr.hwrite;
        dv.pastlast := '0';
        if  (dis_caslat = 0 and dr.ctr(2 downto 0)=caslat_reg) or
            (dis_caslat /= 0 and dr.hwrite='0' and dr.ctr(2 downto 0)="000" ) or
            (dis_caslat /= 0 and dr.hwrite='1' and dr.ctr(2 downto 0)=std_logic_vector(unsigned(sdi.regrdata(2 downto 0)) -1)) then
          if dr.hwrite='1' then
            dv.s := dsdata;
          else
            dv.s := dsreaddly;
          end if;
          dv.ctr := (others => '0');
          dv.hwctr := '0';
          dv.sdo_qdrive := not (dr.hwrite xor oepols);
          dv.sdo_nbdrive := not (dr.hwrite xor oepols);
        end if;

      when dsreaddly =>
        dv.sdo_odt := dr.hwrite;
        dv.sdo_oct := not dr.hwrite;
        dv.pastlast := '0';
        if dr.ctr(3 downto 0)=dr.cfg.readdly then
          dv.s := dsdata;
          dv.ctr := (others => '0');
          dv.hwctr := '0';
        end if;
        
      when dsdata =>

        inc_rctr := '0';

        dv.sdo_odt := dr.hwrite;
        dv.sdo_oct := not dr.hwrite;
        dv.rbwrite := '1';
        dv.sdo_dqm := mask;
        dv.sdo_bdrive := not (dr.hwrite xor oepols);
        dv.sdo_qdrive := not (dr.hwrite xor oepols);
        dv.sdo_nbdrive := not (dr.hwrite xor oepols);
        
        -- If-case to handle pausing for half-width mode
        if hwidthen=0 or dr.hwidth='0' or dr.hwctr='1' then

          inc_rctr := '1';
          
          -- The first request may be on a 2-odd column to get the first data first
          -- Make sure following requests are on even mult of 4xcolumns
          if dr.ctr(0)='1' then
            dv.col(1) := '0';          
          end if;
          -- Make sure we don't advance read counter for the unwanted 3:rd/4:th
          -- word in the burst in this case
          if dr.ctr(0)='1' and dr.col(1)='1' then
            inc_rctr := '0';
          end if;
          
          -- Toggle done and change state after completed burst
          if dr.ctr(log2(ddr_burstlen)-1 downto 0)=(not zerov(l2ddr_burstlen)) then
            dv.sdo_nbdrive := not oepols;
            dv.s := dsdone;
            dv.response.done_tog := not dr.response.done_tog;
          end if;
        end if;

        -- Stall if not ready yet
        if hasdqvalid/=0 and sdi.datavalid='0' and dr.hwrite='0' then
          dv.ctr := dr.ctr;
          dv.hwctr := dr.hwctr;
          dv.response := dr.response;
          dv.s := dsdata;
          dv.col(1) := dr.col(1);
          dv.rbwrite := '0';
          inc_rctr := '0';
        end if;

        if inc_rctr='1' and dr.hwrite='0' then          
          dv.response.rctr_gray(l2ddr_burstlen-1 downto 0) :=
            nextgray(dr.response.rctr_gray(l2ddr_burstlen-1 downto 0));
        end if;
        
      when dsdone =>
        dv.response.rctr_gray := "0000";
        dv.sdo_bdrive := not oepols;
        if dr.ctr(0)='1' then
          dv.sdo_qdrive := not oepols;
        end if;
        if dr.pchpend='0' and dr.prectr=zerov(dr.prectr'length) then
          dv.s := dsidle;
        end if;
        -- Short circuit if request on same row and waiting for Tras to expire
        if precharge_notras='1' and precharge_next='0' and 
          dr.start_tog_prev /= dr.response.done_tog and dr.samerow='1' and vreq.hio='0' then 
          dv.col := acol;
          dv.endaddr := aendaddr;
          dv.addrlo := aloa;
          dv.hwrite := vreq.hwrite;
          dv.hsize := vreq.hsize;
          dv.s := dsagain;
          dv.sdo_qdrive := not oepols;
        end if;

      when dsagain =>
        block_precharge := '1';
        dv.sdo_address := dr.col(13 downto 10) & '0' & dr.col(9 downto 1) & '0';
        goto_caslat := '1';
        
      when dsreg =>
        -- This code assumes ddrbits>=16, needs to be changed slightly to support
        -- smaller widths
        dv.rbwrite := '1';
        
        -- DDR2CFG1-5,PHYCFG read
        regt0 := (others => '0'); regt1 := (others => '0');
        case ddrbits is
          when 16 =>
            case endaddr42 is
              when "000"  => regt0 := regsd1(31 downto 16); regt1 := regsd1(15 downto 0);
              when "001"  => regt0 := regsd2(31 downto 16); regt1 := regsd2(15 downto 0);
              when "010"  => regt0 := regsd3(31 downto 16); regt1 := regsd3(15 downto 0);
              when "011"  => regt0 := regsd4(31 downto 16); regt1 := regsd4(15 downto 0);
              when "100" | "101"  => regt0 := regsd5(31 downto 16); regt1 := regsd5(15 downto 0);
              when "110"  => regt0 := sdi.regrdata(31 downto 16); regt1 := sdi.regrdata(15 downto 0);
              when others => regt0 := sdi.regrdata(63 downto 48); regt1 := sdi.regrdata(47 downto 32);
            end case;
          when 32 =>
            case endaddr43 is
              when "00"   => regt0 := regsd1; regt1 := regsd2;
              when "01"   => regt0 := regsd3; regt1 := regsd4;
              when "10"   => regt0 := regsd5; regt1 := regsd2;
              when others => regt0 := sdi.regrdata(31 downto 0); regt1 := sdi.regrdata(63 downto 32);
            end case;
          when 64 =>
            case dr.endaddr(4) is
              when '0'    => regt0 := regsd1 & regsd2; regt1 := regsd3 & regsd4;
              when others => regt0 := regsd5 & regsd2; regt1 := sdi.regrdata(31 downto 0) & sdi.regrdata(63 downto 32);
            end case;
          when 128 =>
            regt0 := regsd1 & regsd2 & regsd3 & regsd4;
            regt1 := regsd5 & regsd2 & sdi.regrdata(31 downto 0) & sdi.regrdata(63 downto 32);
          when others =>
            regt0(ddrbits-1 downto ddrbits-255) := regsd1 & regsd2 & regsd3 & regsd4 &
                                                   regsd5 & x"00000000" & sdi.regrdata(31 downto 0) & sdi.regrdata(63 downto 32);
        end case;
        dv.rbwdata(ddrbits*2+chkbits-1 downto ddrbits+chkbits) := regt0;
        dv.rbwdata(ddrbits-1 downto 0) := regt1;
        
        -- Note write data is two cycles behind
        regt0 := dr.sdo_data(ddrbits*2-1 downto ddrbits);
        regt1 := dr.sdo_data(ddrbits-1 downto 0);
        if dr.hwrite='1' and dr.ctr(2 downto 0)="010" then
          w5 := '0';
          case ddrbits is
            when 16 =>
              case endaddr42 is
                when "000"  => regsd1 := regt0 & regt1;
                when "001"  => regsd2 := regt0 & regt1;
                when "010"  => regsd3 := regt0 & regt1;
                when "011"  => regsd4 := regt0 & regt1;
                when "100"  => regsd5 := regt0 & regt1;
                               w5 := '1';
                when "110"  => o.regwrite(0) := '1';
                when "111"  => o.regwrite(1) := '1';
                when others => null;
              end case;
            when 32 => 
              case endaddr42 is
                when "000"  => regsd1 := regt0;
                when "001"  => regsd2 := regt1;
                when "010"  => regsd3 := regt0;
                when "011"  => regsd4 := regt1;
                when "100"  => regsd5 := regt0;
                               w5 := '1';
                when "110"  => o.regwrite(0) := '1';
                when "111"  => o.regwrite(1) := '1';
                when others => null;
              end case;
            when 64 =>
              case endaddr42 is
                when "000"  => regsd1 := regt0(63 downto 32);
                when "001"  => regsd2 := regt0(31 downto 0);
                when "010"  => regsd3 := regt1(63 downto 32);
                when "011"  => regsd4 := regt1(31 downto 0);
                when "100"  => regsd5 := regt0(63 downto 32);
                               w5 := '1';
                when "110"  => o.regwrite(0) := '1';
                when "111"  => o.regwrite(1) := '1';
                when others => null;
              end case;
            when 128 =>
              case endaddr42 is
                when "000"  => regsd1 := regt0(127 downto 96);
                when "001"  => regsd2 := regt0(95 downto 64);
                when "010"  => regsd3 := regt0(63 downto 32);
                when "011"  => regsd4 := regt0(31 downto 0);
                when "100"  => regsd5 := regt1(127 downto 96);
                               w5 := '1';
                when "110"  => o.regwrite(0) := '1';
                when "111"  => o.regwrite(1) := '1';
                when others => null;                               
              end case;
            when others =>
              case endaddr42 is
                when "000"  => regsd1 := regt0(ddrbits-1 downto ddrbits-32);
                when "001"  => regsd2 := regt0(ddrbits-33 downto ddrbits-64);
                when "010"  => regsd3 := regt0(ddrbits-65 downto ddrbits-96);
                when "011"  => regsd4 := regt0(ddrbits-97 downto ddrbits-128);
                when "100"  => regsd5 := regt0(ddrbits-129 downto ddrbits-160);
                               w5 := '1';
                when "110"  => o.regwrite(0) := '1';
                when "111"  => o.regwrite(1) := '1';
                when others => null;                               
              end case;
          end case;
          -- Update lsb aliases for expanded fields in ddr2cfg5
          if w5='1' then
            regsd3(28) := regsd5(28);   -- TRP
            regsd3(22 downto 18) := regsd5(22 downto 18);  -- TRFC
            regsd1(26) := regsd5(8);    -- TRCD
          end if;
          
        end if;

        if (dr.hwrite='1' and dr.ctr(2 downto 1)="11") or dr.hwrite='0' then
          dv.s := dsidle;
          dv.response.done_tog := not dr.response.done_tog;
        end if;

        dv.cfg := (refon => regsd1(31), ocd => regsd1(30), emr => regsd1(29 downto 28),
                   trcd => regsd5(10 downto 9) & regsd1(26),
                   bsize => regsd1(27) & regsd1(25 downto 23), csize => regsd1(22 downto 21),
                   command => regsd1(20 downto 18), dllrst => regsd1(17), renable => regsd1(16),
                   cke => regsd1(15), refresh => regsd1(11 downto 0),
                   cal_pll => regsd3(30 downto 29), cal_rst => regsd3(31),
                   trp => regsd5(30 downto 29) & regsd3(28),
                   twr => regsd3(27 downto 23),
                   trfc => regsd5(25 downto 23) & regsd3(22 downto 18),
                   readdly => regsd4(23 downto 22) & regsd3(17 downto 16), cal_inc => regsd3(15 downto 8),
                   cal_en => regsd3(7 downto 0),
                   eightbanks => regsd4(8), dqsctrl => regsd4(7 downto 0),
                   caslat => regsd4(10 downto 9),
                   odten => regsd5(17 downto 16), tras => regsd5(4 downto 0), strength => regsd5(15),
                   trtp => regsd4(13), cbcal_inc => regsd4(31 downto 28), cbcal_en => regsd4(27 downto 24),
                   regmem => regsd4(21)
                   );

      when dsrefresh =>
        if dr.ctr(7 downto 0)=dr.cfg.trfc then
          dv.s := dsidle;
        end if;

      when dspreall =>
        -- Wait for tRP (eightbanks=0) or tRP+1 (eightbanks=1)
        if dr.ctr(3 downto 0)=std_logic_vector(("0" & unsigned(dr.cfg.trp)) + (2+eightbanks)) then
          dv.s := dsidle;
        end if;
        
    end case;

    if goto_caslat='1' then
      dv.s := dscaslat;
      -- Set counter to -4 for read and -1 for write to compensate
      -- write-read diff and pipelining.
      -- Only need lowest three bits so set highest 3 to '0' as usual
      dv.ctr(5 downto 3) := "000";
      dv.ctr(2 downto 0) := "100";
      if vreq.hwrite='1' then
        dv.ctr(2 downto 0) := "111";
      end if;
      dv.casctr := std_logic_vector(to_unsigned(ddr_burstlen/2, dv.casctr'length));
      dv.hwcas := '0';
      dv.pchpend := '1';      
    end if;
    
    -- CAS and precharge handling
    -- FSM above sets up casctr and pchpend
    if dis_caslat /= 0 then
      dv.twr_plus_cl := std_logic_vector(("0" & unsigned(dr.cfg.twr)) + ("000" & unsigned(sdi.regrdata(2 downto 0))) - 1); --  should be -2 instead of -1 but AFI might delay write data for an additional clock cycle, requiring an additional twr clock cycle
    else
      dv.twr_plus_cl := std_logic_vector(("0" & unsigned(dr.cfg.twr)) + ("0000" & unsigned(dr.cfg.caslat)));
    end if;

    if dr.prectr /= zerov(dr.prectr'length) then
      dv.prectr := std_logic_vector(unsigned(dr.prectr)-1);
    end if;

    dv.read_pend := '0' & dr.read_pend(dr.read_pend'high downto 1);
    
    dv.datacas := '1';
    if dr.casctr /= zerov(dr.casctr'length) then
      if dr.datacas='1' then
        dv.datacas := '0';
        -- dv.sdo_casn := '0';
        dv.sdo_wen := not dr.hwrite;
        if dr.hwrite='0' then
          if dis_caslat /= 0 then
            dv.read_pend(4 downto 3) := "11";
          else
            case dr.cfg.caslat is
              when "00"   => dv.read_pend(4 downto 3) := "11";
              when "01"   => dv.read_pend(5 downto 4) := "11";
              when "10"   => dv.read_pend(6 downto 5) := "11";
              when others => dv.read_pend(7 downto 6) := "11";
            end case;
          end if;
        end if;
      elsif dr.hwidth='1' then

        dv.hwcas := not dr.hwcas;
        if dr.hwcas='1' then          
          dv.casctr := std_logic_vector(unsigned(dr.casctr)-1);
          if l2blen-l2ddrw > 1 then
            dv.sdo_address(l2blen-l2ddrw+1 downto 3) :=
              std_logic_vector(unsigned(dr.sdo_address(l2blen-l2ddrw+1 downto 3)+1));
          end if;
          dv.sdo_address(2) := '0';
        else
          dv.sdo_address(2) := not dr.sdo_address(2);
        end if;
        
      else
        dv.casctr := std_logic_vector(unsigned(dr.casctr)-1);
        if l2blen-l2ddrw > 1 then
          dv.sdo_address(l2blen-l2ddrw downto 2) :=
            std_logic_vector(unsigned(dr.sdo_address(l2blen-l2ddrw downto 2)+1));
        end if;
        dv.sdo_address(1) := '0';
      end if;
      -- Set up precharge counter (will not run until casctr=0)
      if dr.hwrite='0' then
        dv.prectr := "00000" & dr.cfg.trtp;
      else
        dv.prectr := dr.twr_plus_cl;
      end if;
    end if;
    
    o.read_pend := dv.read_pend(7 downto 0);
    
    dv.rasn_pre := '1';
    if precharge_next='1' and block_precharge='0' then
      dv.pchpend := '0';
      dv.sdo_wen := '0';
      -- dv.sdo_rasn := '0';
      dv.rasn_pre := '0';
      dv.prectr := "000" & dr.cfg.trp;
    end if;
    
    -- Refresh and init handling    
    dv.refctr := std_logic_vector(unsigned(dr.refctr)+1);
    case dr.cmds is
      when dcrstdel =>
        if dr.refctr=std_logic_vector(to_unsigned(MHz*rstdel, dr.refctr'length)) then
          dv.cmds := dcoff;
        end if;
        -- Bypass reset delay by writing anything to regsd2
        if dr.start_tog_prev='1' and
          vreq.hio='1' and vreq.hwrite='1' and vreq.endaddr(4 downto 2)="001" then
          dv.cmds := dcoff;
        end if;
        
      when dcoff =>
        -- Wait for renable to be set high and phy to be locked
        dv.refctr := (others => '0');
        if dr.cfg.renable='1' then
          dv.cfg.cke := '1';
          dv.cfg.dllrst := '1';
          dv.cfg.ocd := '0';
          dv.cmds := dcinit1;
        elsif dis_init /= 0 then
          dv.cmds := dcon;
        end if;
      when dcinit1 =>
        -- Wait >=400 ns
        if dr.refctr=std_logic_vector(to_unsigned((MHz*4+9)/10, dr.refctr'length)) then
          dv.cmds := dcinit2;
          dv.cfg.command := CMD_PRE;
          dv.cfg.emr := "00";
        end if;
        
      when dcinit2 =>
        -- MR order 2,3,1,0
        -- 2xcycles per command
        if dr.cfg.command="000" then
          dv.cfg.command := CMD_EMR;
          dv.cfg.emr := (not dr.cfg.emr(0)) & dr.cfg.emr(1);  -- 00->10->11->01->00          
          if dr.cfg.emr="01" then
            dv.cmds := dcinit3;
            dv.refctr := (others => '0');
          end if;
        end if;

      when dcinit3 =>
        if dr.cfg.command="000" then
          dv.cfg.command := CMD_PRE;
          dv.cmds := dcinit4;
        end if;

      when dcinit4 =>
        if dr.cfg.command="000" then
          dv.cfg.command := CMD_REF;
          dv.cmds := dcinit5;
        end if;

      when dcinit5 =>
        if dr.cfg.command="000" then
          dv.cfg.command := CMD_REF;
          dv.cmds := dcinit6;
        end if;

      when dcinit6 =>
        if dr.cfg.command="000" then
          dv.cfg.command := CMD_EMR;
          dv.cfg.emr := "00";
          dv.cfg.dllrst := '0';
          dv.cmds := dcinit7;
          dv.refctr := (others => '0');
        end if;

      when dcinit7 =>
        if dr.refctr(7 downto 0)=std_logic_vector(to_unsigned(200,8)) then
          dv.cfg.command := CMD_EMR;
          dv.cfg.emr := "01";
          dv.cfg.ocd := '1';
          dv.cmds := dcinit8;
        end if;

      when dcinit8 =>        
        if dr.cfg.command="000" then
          if dr.cfg.ocd='1' then
            dv.cfg.ocd := '0';
            dv.cfg.command := CMD_EMR;
          else            
            dv.cmds := dcon;
            dv.cfg.renable := '0';
          end if;
        end if;
        dv.refctr := (others => '0');
      
      when dcon =>
        if dr.cfg.cke='0' then
          dv.cmds := dcoff;
        elsif dr.cfg.renable='1' then          
          dv.cmds := dcinit2;
          dv.refctr := (others => '0');
        elsif dr.refctr(11 downto 0)=dr.cfg.refresh then
          dv.refpend := '1';
          dv.refctr := (others => '0');
        end if;
        
    end case;

    if dis_init /= 0 then
      dv.cfg.renable := '0';
    end if;
    -- Calculate next address    
    dv.ramaddr(0) := dv.ctr(0) xor dv.col(1);
    if rbuf_wabits > 1 then
      dv.ramaddr(rbuf_wabits-1 downto 1) :=
        std_logic_vector(unsigned(dr.col(rbuf_wabits downto 2)) +
                         unsigned(dv.ctr(rbuf_wabits-1 downto 1)));
    end if;
    
      -- print("col: " & tost(dr.col) & ", dv.ctr: " & tost(dv.ctr) & ", res: " & tost(dv.ramaddr));
    
    
    if eightbanks=0 then dv.cfg.eightbanks:='0'; end if;    

    rbwd := dv.rbwdata;
    rbwa := dr.ramaddr;
    rbw  := dv.rbwrite;

    if plmemwrite then
      rbwd := dr.rbwdata;
      rbwa := dr.ramaddr_prev;
      rbw  := dr.rbwrite;
    end if;

    if not plmemread then
      o.dqm   := dr.sdo_dqm;
      o.sdwen := dr.sdo_wen;
      o.data  := dv.sdo_data;
      o.cb    := dv.sdo_cb;
    end if;

    -- half-width output data muxing, placed after (potential) pipeline regs.
    if hwidthen/=0 and dr.hwidth='1' then
      if dr.hwctr='1' then
        o.data(ddrbits/2-1 downto 0) := o.data(2*ddrbits-ddrbits/2-1 downto ddrbits);
        o.data(2*ddrbits-ddrbits/2-1 downto ddrbits) := o.data(2*ddrbits-1 downto 2*ddrbits-ddrbits/2);
        if chkbits > 0 then
          o.cb(chkbits/2-1 downto 0) := o.cb(2*chkbits-chkbits/2-1 downto chkbits);
          o.cb(2*chkbits-chkbits/2-1 downto chkbits) := o.cb(2*chkbits-1 downto 2*chkbits-chkbits/2);
        end if;
        o.dqm(ddrbits/16-1 downto 0) := o.dqm(ddrbits/4-ddrbits/16-1 downto ddrbits/8);
        o.dqm(ddrbits/4-ddrbits/16-1 downto ddrbits/8) := o.dqm(ddrbits/4-1 downto ddrbits/4-ddrbits/16);
      else
        o.data(2*ddrbits-ddrbits/2-1 downto ddrbits) := o.data(ddrbits-1 downto ddrbits/2);
        if chkbits > 0 then
          o.cb(2*chkbits-chkbits/2-1 downto chkbits) := o.cb(chkbits-1 downto chkbits/2);
        end if;
        o.dqm(ddrbits/4-ddrbits/16-1 downto ddrbits/8) := o.dqm(ddrbits/8-1 downto ddrbits/16);
      end if;
    end if;
    
    if ddr_rst='0' then
      dv.s := dsidle;
      dv.cmds := dcrstdel;
      dv.response := ddr_response_none;
      dv.casctr := (others => '0');
      dv.refctr := (others => '0');
      dv.pchpend := '0';
      dv.refpend := '0';
      dv.rbwrite := '0';
      dv.ctr := (others => '0');
      dv.hwctr := '0';
      dv.sdo_nbdrive := not oepols;
      dv.sdo_csn := (others => '1');
      dv.rastimer := (others => '0');
      dv.tras_met := '0';
      dv.cfg.command  := "000";
      dv.cfg.emr      := "00";
      dv.cfg.csize    := conv_std_logic_vector(col-9, 2);
      dv.cfg.bsize    := conv_std_logic_vector(log2(Mbyte/8), 4);
      dv.cfg.refon    :=  '0';
      dv.cfg.trfc     := conv_std_logic_vector(TRFC*MHz/1000-2, 8);
      dv.cfg.refresh  := conv_std_logic_vector(7800*MHz/1000, 12);
      dv.cfg.twr      := conv_std_logic_vector((15 * MHz + 999) / 1000 + 3, 5);
      dv.sdo_dqm      := (others => '1');
      dv.cfg.dllrst   := '0';
      dv.cfg.cke      := std_logic(to_unsigned(cke_rst, 1)(0));
      dv.cfg.ocd      := '0';
      dv.cfg.readdly  := conv_std_logic_vector(readdly, 4);
      dv.cfg.eightbanks := conv_std_logic_vector(eightbanks, 1)(0);
      dv.cfg.odten := std_logic_vector(to_unsigned(odten,2));
      dv.cfg.dqsctrl := (others => '0');      
      dv.cfg.strength := '0';
      if pwron = 1 and dis_init = 0 then dv.cfg.renable :=  '1'; else dv.cfg.renable:='0'; end if;
      -- Default to min 15 ns tRCD, 15 ns tRP, min(7.5 ns,2*tCK) tRTP      
      -- Use CL=3 for DDR2-400/533, 4 for DDR2-667, 5 for DDR2-800
      dv.cfg.trcd := "000";
      dv.cfg.trp := "000";
      dv.cfg.trtp := '0';
      dv.cfg.caslat := "00";
      dv.cfg.regmem := '0';
      if MHz > 130 then
        dv.cfg.trcd :=  "001";
        dv.cfg.trp := "001";
      end if;
      if MHz > 200 then
        -- Will work up to 600 MHz, then trcd/trp needs to be expanded
        dv.cfg.trcd := std_logic_vector(to_unsigned((15 * MHz + 999) / 1000 - 2, 3));
        dv.cfg.trp := std_logic_vector(to_unsigned((15 * MHz + 999) / 1000 - 2, 3));        
      end if;
      if MHz > 267 then
        -- Works up to 400 MHz, then trtp will need to be expanded
        dv.cfg.trtp := '1';
        dv.cfg.caslat := "01";
      end if;
      if MHz > 334 then
        dv.cfg.caslat := "10";
      end if;
      dv.cfg.cal_rst := '1'; -- Reset input delays            
      dv.sdo_ba := (others => '0');
      dv.sdo_address := (others => '0');
      -- Default to min 45 ns tRAS
      dv.cfg.tras := std_logic_vector(to_unsigned((45*MHz+999)/1000 - 2, 5));
      dv.read_pend := (others => '0');
      if ddr_syncrst /= 0 then 
        dv.sdo_bdrive := not oepols;
        dv.sdo_qdrive := not oepols;
        dv.sdo_odt := '0';
        if phyptctrl /= 0 then
          o.sdcke := "00";
          o.bdrive := not oepols;
          o.qdrive := not oepols;
          o.odt := (others => '0');
        end if;
      end if;
    end if;
    
    if dr.cfg.odten="00" then
      dv.sdo_odt := '0';
    end if;
    if octen=0 then
      dv.sdo_oct := '0';
    end if;
    
    for x in 0 to chkbits/4-1 loop
      o.cbdqm(x) := o.dqm(x*ddrbits/chkbits);
    end loop;

    if vreq.maskdata='1' then
      o.dqm := (others => '1');
    end if;
    if vreq.maskcb='1' then
      o.cbdqm := (others => '1');
    end if;
    
    if dr.cfg.command /= "000" then
      -- print("Command: " & tost(dr.cfg.command) & " -> casn:" & tost(dv.sdo_casn) & ",rasn:" & tost(dv.sdo_rasn) & ",wen:" & tost(dv.sdo_wen));
    end if;

    -- Dynamic nosync handling (nosync=2)
    if plmemwrite then
      dv.response1 := dr.response;
      dv.response2 := dr.response;
    else
      dv.response1 := dv.response;
      dv.response2 := dv.response;
    end if;
    if reqsel='1' then dv.response1 := ddr_response_none; end if;    
    if reqsel='0' then dv.response2 := ddr_response_none; end if;

    if nosync > 1 then
      resp := dr.response1;
    elsif plmemwrite then
      resp := dr.response_prev;
    else
      resp := dr.response;
    end if;
    resp2 := dr.response2;

    if scantest/=0 and phyptctrl/=0 then
      if testen='1' then
        o.bdrive := testoen;
        o.qdrive := testoen;
      end if;
    end if;
    
    rbwdata <= rbwd;
    rbwaddr <= rbwa;
    rbwrite <= rbw;
    wbraddr <= wbra;
    sdo     <= o;
    response  <= resp;
    response2 <= resp2;
    ndr     <= dv;
  end process;

  
  ddrregs: process(clk_ddr,arst)
  begin
    if rising_edge(clk_ddr) then
      dr <= ndr;
    end if;
    if ddr_syncrst=0 and arst='0' then
      dr.cfg.cke <= std_logic(to_unsigned(cke_rst, 1)(0));
      dr.sdo_bdrive <= not oepols;
      dr.sdo_qdrive <= not oepols;
      dr.sdo_odt <= '0';
    end if;
  end process;
    
end;

