ReonV/lib/gaisler/ddr/ddr1spax_ddr.vhd

1147 lines
40 KiB
VHDL

------------------------------------------------------------------------------
-- 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 - 2017, 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: ddr1spax_ddr
-- File: ddr1spax_ddr.vhd
-- Author: Magnus Hjorth - Aeroflex Gaisler
-- Description: Merged 16/32/64-bit DDR/mobile-DDR backend
-- Based on ddrsp*a and ddr2spax_ddr
--------------------------------------------------------------------------------
-- Added features from the original ddrspa:
-- * Separated AHB,DDR parts of controller like for DDR2SPA
-- * 64/32/16 bit interfaces in the same entity
-- * Checkbit support for use with ft_ddr2spax_ahb front-end.
-- * Extended timing fields plus tRAS setting to meet DDR400 timing.
-- * Configurable burst length
-- * Support for PHY:s with read data valid signaling and extra latency
-- Incompatibility/differences to the original ddrspa:
-- * The mobile DDR had an undocumented feature that tRFC was extended with 8
-- cycles if the TRP bit was set. This is replaced by the extended
-- timing fields.
-- * ddrsp16a used a separate read-clock supplied only from the Spartan PHY.
-- * Reads/writes are made as multiple length-2 burst commands.
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 ddr1spax_ddr is
generic (
ddrbits : integer := 32;
burstlen : integer := 8;
MHz : integer := 100;
col : integer := 9;
Mbyte : integer := 8;
pwron : integer := 0;
oepol : integer := 0;
mobile : integer := 0;
confapi : integer := 0;
conf0 : integer := 0;
conf1 : integer := 0;
nosync : integer := 0;
ddr_syncrst: integer range 0 to 1 := 0;
chkbits : integer := 0;
hasdqvalid : integer := 0;
readdly : integer := 0;
regoutput : integer := 1;
ddr400 : integer := 1;
rstdel : integer := 200;
phyptctrl : integer := 0;
scantest : 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;
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 ddr1spax_ddr;
architecture rtl of ddr1spax_ddr is
constant l2blen: integer := log2(burstlen)+log2(32);
constant l2ddrw: integer := log2(ddrbits*2);
constant l2ddr_burstlen: integer := l2blen-l2ddrw;
-- 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);
type ddrstate is (dsidle,dsact1,dsact2,dsact3,dswr1,dswr2,dswr3,dswr4,dswr5,dswr6,
dsrd1,dsrd2,dsrd3,dsrd4,dsreg1,dsreg2,dscmd1,dscmd2,dspdown1,dspdown2,dsref1,
dssrr1,dssrr2);
type ddrinitstate is (disrstdel,disidle,disrun,disfinished);
type sdram_cfg_type is record
command : std_logic_vector(2 downto 0);
csize : std_logic_vector(1 downto 0);
bsize : std_logic_vector(2 downto 0);
trcd : std_ulogic; -- tCD : 2/3 clock cycles
trfc : std_logic_vector(4 downto 0);
trp : std_logic_vector(1 downto 0); -- precharge to activate: 2/3 clock cycles
refresh : std_logic_vector(11 downto 0);
renable : std_ulogic;
dllrst : std_ulogic;
refon : std_ulogic;
cke : std_ulogic;
pasr : std_logic_vector(5 downto 0); -- pasr(2:0) (pasr(5:3) used to detect update)
tcsr : std_logic_vector(3 downto 0); -- tcrs(1:0) (tcrs(3:2) used to detect update)
ds : std_logic_vector(5 downto 0); -- ds(1:0) (ds(3:2) used to detect update)
pmode : std_logic_vector(2 downto 0); -- Power-Saving mode
mobileen : std_logic; -- Mobile SD support, Mobile SD enabled
txsr : std_logic_vector(5 downto 0); -- Exit Self Refresh timing
txp : std_logic_vector(1 downto 0); -- Exit Power-Down timing
tcke : std_logic; -- Clock enable timing
cl : std_logic; -- CAS latency 2/3 (0/1)
conf : std_logic_vector(63 downto 0); -- PHY control
tras : std_logic_vector(1 downto 0); -- tRAS minimum (6-9 cycles)
twr : std_logic; -- tWR write recovery, 2/3 cycles
end record;
type ddr_reg_type is record
s : ddrstate;
initstate : ddrinitstate;
cfg : sdram_cfg_type;
resp,resp2 : ddr_response_type;
req1,req2 : ddr_request_type;
start1,start2 : std_logic;
start3 : std_logic;
ramaddr : std_logic_vector(rbuf_wabits-1 downto 0);
readpipe : std_logic_vector(4+readdly downto 0);
initpos : std_logic_vector(2 downto 0);
cmdctr : std_logic_vector(7 downto 0);
readdone : std_logic;
refctr : std_logic_vector(17 downto 0);
refpend : std_logic;
idlectr : std_logic_vector(3 downto 0);
pdowns : std_logic_vector(1 downto 0);
sdo_casn : std_logic;
sdo_rasn : std_logic;
sdo_wen : std_logic;
sdo_csn : std_logic_vector(1 downto 0);
sdo_ba : std_logic_vector(1 downto 0);
sdo_address : std_logic_vector(14 downto 0);
sdo_data : std_logic_vector(2*ddrbits-1 downto 0);
sdo_dqm : std_logic_vector(ddrbits/4-1 downto 0);
sdo_cb : std_logic_vector(2*chkbits downto 0);
sdo_ck : std_logic_vector(2 downto 0);
sdo_bdrive : std_logic;
sdo_qdrive : std_logic;
end record;
signal dr,ndr: ddr_reg_type;
constant onev: std_logic_vector(15 downto 0) := x"FFFF";
constant zerov: std_logic_vector(15 downto 0) := x"0000";
signal arst : std_ulogic;
begin
arst <= testrst when (scantest/=0 and ddr_syncrst=0) and testen='1' else ddr_rst;
ddrcomb: process(ddr_rst,sdi,request,frequest,start_tog,dr,wbrdata,testen,testoen,reqsel)
variable dv: ddr_reg_type;
variable o: ddrctrl_out_type;
variable rbw: std_logic;
variable rbwd: std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
variable vstart, vstartd, vdone, incdone: std_logic;
variable vrctr: std_logic_vector(3 downto 0);
variable vreq,vreqf: ddr_request_type;
variable regsd1 : std_logic_vector(31 downto 0);
variable regsd2 : std_logic_vector(31 downto 0);
variable regsd3 : std_logic_vector(31 downto 0);
variable lastreadcmd: std_logic;
variable lastwrite : std_logic;
variable vmaskfirst, vmasklast: std_logic_vector(ddrbits/4-1 downto 0);
variable ea: std_logic_vector(3 downto 2);
variable inc_sdoaddr, inc_ramaddr: std_logic;
variable datavalid: std_logic;
variable vcsf: std_logic_vector(1 downto 0);
variable vrowf: std_logic_vector(14 downto 0);
variable vbankf: std_logic_vector(1 downto 0);
variable vcol,vcoladdr: std_logic_vector(14 downto 1);
variable seqin,seqout: std_logic_vector(3 downto 0);
variable regrdata: std_logic_vector(2*ddrbits-1 downto 0);
variable regad: std_logic_vector(2 downto 0);
variable wrdreg1,wrdreg2,wrdreg3: std_logic_vector(31 downto 0);
variable reqselv: std_logic_vector(3 downto 0);
begin
---------------------------------------------------------------------------
-- Init vars
---------------------------------------------------------------------------
dv := dr;
o := ddrctrl_out_none;
o.bdrive := '1'; o.qdrive := '1';
vdone := dr.resp.done_tog or dr.resp2.done_tog;
vrctr := dr.resp.rctr_gray or dr.resp2.rctr_gray;
incdone := '0';
lastreadcmd := '0';
lastwrite := '0';
reqselv := reqsel & reqsel & reqsel & reqsel;
-- Config registers
regsd1 := (others => '0');
regsd1(31 downto 15) := dr.cfg.refon & dr.cfg.trp(0) & dr.cfg.trfc(2 downto 0) &
dr.cfg.trcd & dr.cfg.bsize & 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(8 downto 0) := conv_std_logic_vector(MHz, 9);
regsd2(14 downto 12) := conv_std_logic_vector(log2(ddrbits/8),3);
if mobile/=0 then regsd2(15):='1'; end if;-- Mobile DDR support
regsd2(19 downto 16) := conv_std_logic_vector(confapi, 4);
regsd3 := (others => '0');
regsd3(31) := dr.cfg.mobileen; -- Mobile DDR enable
regsd3(30) := dr.cfg.cl;
regsd3(24 downto 19) := dr.cfg.tcke & dr.cfg.txsr(3 downto 0) & dr.cfg.txp(0);
regsd3(18 downto 16) := dr.cfg.pmode;
regsd3( 7 downto 0) := dr.cfg.ds(2 downto 0) & dr.cfg.tcsr(1 downto 0)
& dr.cfg.pasr(2 downto 0);
-- Extended timing fields for DDR400
if ddr400 /= 0 then
regsd2(20) := '1'; -- Ext. fields available
regsd3(29 downto 28) := dr.cfg.tras;
regsd3(27 downto 26) := dr.cfg.txsr(5 downto 4);
regsd3(25) := dr.cfg.txp(1);
regsd3(11) := dr.cfg.twr;
regsd3(10) := dr.cfg.trp(1);
regsd3(9 downto 8) := dr.cfg.trfc(4 downto 3);
end if;
-- Data path
rbw := '0';
rbwd := (others => '0');
rbwd(ddrbits-1 downto 0) := sdi.data(ddrbits-1 downto 0);
rbwd(2*ddrbits+chkbits-1 downto ddrbits+chkbits) :=
sdi.data(2*ddrbits-1 downto ddrbits);
if chkbits > 0 then
rbwd(ddrbits+chkbits-1 downto ddrbits) := sdi.cb(chkbits-1 downto 0);
rbwd(2*(ddrbits+chkbits)-1 downto 2*ddrbits+chkbits) :=
sdi.cb(2*chkbits-1 downto chkbits);
end if;
dv.sdo_data(ddrbits-1 downto 0) := wbrdata(ddrbits-1 downto 0);
dv.sdo_data(2*ddrbits-1 downto ddrbits) :=
wbrdata(2*ddrbits+chkbits-1 downto ddrbits+chkbits);
dv.sdo_cb(chkbits) := '0'; -- dummy bit just to ensure length>0
if chkbits > 0 then
dv.sdo_cb(chkbits-1 downto 0) := wbrdata(ddrbits+chkbits-1 downto ddrbits);
dv.sdo_cb(2*chkbits-1 downto chkbits) :=
wbrdata(2*(ddrbits+chkbits)-1 downto 2*ddrbits+chkbits);
end if;
---------------------------------------------------------------------------
-- Request handling logic
---------------------------------------------------------------------------
-- Sync request inputs
dv.req1 := request;
dv.req2 := dr.req1;
dv.start1 := start_tog;
dv.start2 := dr.start1;
dv.start3 := dr.start2;
vstart := dr.start2;
vstartd := dr.start3;
vreq := dr.req2;
vreqf := dr.req1;
if nosync/=0 then
vstart:=start_tog;
vstartd:=start_tog;
vreq:=request;
vreqf:=request;
end if;
if nosync > 1 then
vreqf := frequest;
end if;
-- Address muxing
vcsf(0) := genmux(dr.cfg.bsize, vreqf.startaddr(30 downto 23));
vcsf(1) := not vcsf(0);
vbankf := genmux(dr.cfg.bsize, vreqf.startaddr(29 downto 22)) &
genmux(dr.cfg.bsize, vreqf.startaddr(28 downto 21));
case dr.cfg.csize is
when "00" => vrowf := vreqf.startaddr(19+l2ddrw downto 5+l2ddrw);
when "01" => vrowf := vreqf.startaddr(20+l2ddrw downto 6+l2ddrw);
when "10" => vrowf := vreqf.startaddr(21+l2ddrw downto 7+l2ddrw);
when others => vrowf := vreqf.startaddr(22+l2ddrw downto 8+l2ddrw);
end case;
vcol := vreq.startaddr(l2ddrw+10 downto l2ddrw-3);
-- vcoladdr==vcol when dr.ramaddr==lsb of vcol
vcoladdr := vcol(14 downto rbuf_wabits+1) & dr.ramaddr;
-- Generate data mask
-- Mask for 32-bit and larger bursts and single access
vmaskfirst := (others => '0');
vmasklast := (others => '0');
ea := vreq.endaddr(3 downto 2);
if vreq.hsize(1 downto 0)="11" then ea(2):='1'; end if;
if vreq.hsize(2)='1' then ea(3 downto 2):="11"; end if;
case ddrbits is
when 64 =>
-- 64-bit DDR width
case vreq.startaddr(3 downto 2) is
when "11" => vmaskfirst := "1111111111110000";
when "10" => vmaskfirst := "1111111100000000";
when "01" => vmaskfirst := "1111000000000000";
when others => vmaskfirst := "0000000000000000";
end case;
case ea(3 downto 2) is
when "11" => vmasklast := "0000000000000000";
when "10" => vmasklast := "0000000000001111";
when "01" => vmasklast := "0000000011111111";
when others => vmasklast := "0000111111111111";
end case;
if vreq.hsize(2 downto 1)="00" then
if vreq.startaddr(1)='1' then
vmaskfirst := vmaskfirst or "1100110011001100";
else
vmaskfirst := vmaskfirst or "0011001100110011";
end if;
end if;
if vreq.hsize="000" then
if vreq.startaddr(0)='1' then
vmaskfirst := vmaskfirst or "1010101010101010";
else
vmaskfirst := vmaskfirst or "0101010101010101";
end if;
end if;
when 32 =>
-- 32-bit DDR width
case vreq.startaddr(2) is
when '1' => vmaskfirst := "11110000";
when others => vmaskfirst := "00000000";
end case;
case ea(2) is
when '1' => vmasklast := "00000000";
when others => vmasklast := "00001111";
end case;
if vreq.hsize(2 downto 1)="00" then
if vreq.startaddr(1)='1' then
vmaskfirst := vmaskfirst or "11001100";
else
vmaskfirst := vmaskfirst or "00110011";
end if;
end if;
if vreq.hsize="000" then
if vreq.startaddr(0)='1' then
vmaskfirst := vmaskfirst or "10101010";
else
vmaskfirst := vmaskfirst or "01010101";
end if;
end if;
when others =>
-- 16-bit DDR width
if vreq.hsize(2 downto 1)="00" then
if vreq.startaddr(1)='1' then
vmaskfirst := vmaskfirst or "1100";
else
vmaskfirst := vmaskfirst or "0011";
end if;
end if;
if vreq.hsize="000" then
if vreq.startaddr(0)='1' then
vmaskfirst := vmaskfirst or "1010";
else
vmaskfirst := vmaskfirst or "0101";
end if;
end if;
end case;
-- Register read/write data muxing
regrdata := (others => '0');
case ddrbits is
when 64 =>
regad := vreq.startaddr(4 downto 2);
regrdata := regsd1 & regsd2 & regsd3 & x"00000000";
if confapi /= 0 and regad(2)='1' then
regrdata(95 downto 32) := dr.cfg.conf(31 downto 0) & dr.cfg.conf(63 downto 32);
end if;
wrdreg1 := wbrdata(128+chkbits-1 downto 96+chkbits);
wrdreg2 := wbrdata(96+chkbits-1 downto 64+chkbits);
wrdreg3 := wbrdata(63 downto 32);
when 32 =>
regad := dr.ramaddr(1 downto 0) & vreq.startaddr(2);
if regad(1)='0' then
regrdata := regsd1 & regsd2;
if confapi /= 0 and regad(2)='1' then
regrdata := regsd1 & dr.cfg.conf(31 downto 0);
end if;
else
regrdata := regsd3 & regsd2;
if confapi /= 0 and regad(2)='1' then
regrdata := dr.cfg.conf(63 downto 0);
end if;
end if;
wrdreg1 := wbrdata(64+chkbits-1 downto 32+chkbits);
wrdreg2 := wbrdata(31 downto 0);
wrdreg3 := wbrdata(64+chkbits-1 downto 32+chkbits);
when others =>
regad := dr.ramaddr(2 downto 0);
case regad is
when "000"|"100" => regrdata := regsd1;
when "001" => regrdata := regsd2;
when "010" => regrdata := regsd3;
when "101" =>
if confapi /= 0 then
regrdata := dr.cfg.conf(31 downto 0);
else
regrdata := regsd2;
end if;
when "110" =>
if confapi /= 0 then
regrdata := dr.cfg.conf(63 downto 32);
else
regrdata := regsd3;
end if;
when others => regrdata := regsd3;
end case;
wrdreg1 := wbrdata(31+chkbits downto 16+chkbits) & wbrdata(15 downto 0);
wrdreg2 := wbrdata(31+chkbits downto 16+chkbits) & wbrdata(15 downto 0);
wrdreg3 := wbrdata(31+chkbits downto 16+chkbits) & wbrdata(15 downto 0);
end case;
---------------------------------------------------------------------------
-- Main DDR-SDRAM access FSM
---------------------------------------------------------------------------
dv.sdo_ck := "111";
dv.sdo_rasn := '1';
dv.sdo_casn := '1';
dv.sdo_wen := '1';
dv.sdo_dqm := (others => '1');
dv.sdo_bdrive := '1';
dv.sdo_qdrive := '1';
inc_sdoaddr := '0';
inc_ramaddr := '0';
dv.readpipe := dr.readpipe(3+readdly downto 0) & '0';
datavalid := '0';
if hasdqvalid/=0 then
datavalid := sdi.datavalid;
if dr.s/=dsrd1 and dr.s/=dsrd2 and dr.s/=dsrd3 and dr.s/=dsrd4 and dr.s/=dssrr2 then
datavalid := '0';
end if;
end if;
if hasdqvalid=0 then
if dr.cfg.cl='0' then
datavalid := dr.readpipe(3+readdly);
else
datavalid := dr.readpipe(4+readdly);
end if;
end if;
if datavalid='1' and dr.s/=dsidle then
inc_ramaddr := '1';
rbw := '1';
vrctr(l2ddr_burstlen-1 downto 0) :=
nextgray(vrctr(l2ddr_burstlen-1 downto 0));
if dr.ramaddr=onev(dr.ramaddr'length-1 downto 0) then
dv.readdone := '1';
incdone:='1';
vrctr := "0000";
end if;
end if;
if dr.sdo_address((l2blen-l2ddrw) downto 1)=onev((l2blen-l2ddrw) downto 1) then
lastreadcmd := '1';
end if;
if dr.ramaddr=vreq.endaddr((l2blen-3)-1 downto (l2ddrw-3)) then
lastwrite := '1';
end if;
-- Update EMR when ds, tcsr or pasr change
if dr.cfg.command="000" and
( dr.cfg.ds(2 downto 0) /= dr.cfg.ds(5 downto 3) or
dr.cfg.tcsr(1 downto 0) /= dr.cfg.tcsr(3 downto 2) or
dr.cfg.pasr(2 downto 0) /= dr.cfg.pasr(5 downto 3) ) then
dv.cfg.command := "111";
end if;
-- Auto-refresh counter
dv.refctr := std_logic_vector(unsigned(dr.refctr)+1);
if (dr.refctr(11 downto 0)=dr.cfg.refresh and dr.cfg.refon='1') then
dv.refpend := '1';
dv.refctr := (others => '0');
end if;
if dr.initstate/=disrstdel and (dr.cfg.refon='0' or dr.cfg.pmode(1)='1') then
dv.refpend := '0';
dv.refctr := (others => '0');
end if;
dv.idlectr := "0000";
dv.pdowns(0) := '0';
if not (dr.cmdctr=(dr.cmdctr'range => '0')) and dr.pdowns(0)='0' then
dv.cmdctr := std_logic_vector(unsigned(dr.cmdctr)-1);
end if;
case dr.s is
when dsidle =>
vrctr := "0000";
dv.sdo_ck := "111";
if dr.cfg.pmode /= "000" then
dv.idlectr := std_logic_vector(unsigned(dr.idlectr)+1);
end if;
dv.sdo_csn := "11";
if dr.refpend='1' then
dv.sdo_csn := "00";
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
dv.s := dsref1;
dv.refpend := '0';
elsif vstart /= vdone and dr.cfg.renable='0' then
-- Transfer
dv.sdo_csn := vcsf;
dv.sdo_address := vrowf;
dv.sdo_ba := vbankf;
dv.sdo_rasn := '0' or vreqf.hio;
dv.s := dsact1;
elsif dr.cfg.command /= "000" then
dv.s := dscmd1;
elsif dr.idlectr="1111" then
dv.s := dspdown1;
end if;
when dsact1 =>
dv.ramaddr := vcol(rbuf_wabits downto 1);
if ddr400 /= 0 then
dv.cmdctr(2 downto 0) := "1" & dr.cfg.tras; -- t(RAS)-2t(CK) = TRAS+6-2 = TRAS+4
else
dv.cmdctr(2 downto 0) := "10" & dr.cfg.trcd;
end if;
dv.readdone := '0';
if dr.cfg.trcd='1' then
dv.s := dsact2;
else
dv.s := dsact3;
end if;
if vreq.hio='1' then
dv.s := dsreg1;
end if;
when dsact2 =>
dv.s := dsact3;
when dsact3 =>
dv.sdo_casn := '0';
dv.sdo_wen := not vreq.hwrite;
dv.sdo_qdrive := not vreq.hwrite;
-- dv.sdo_address := vcol(12 downto 10) & '0' & vcol(9 downto 1) & '0';
-- Since part of column is stored in ramaddr in dsact1, use that to
-- reduce fanout on vreq.startaddr
dv.sdo_address := vcoladdr(13 downto 10) & '0' & vcoladdr(9 downto 1) & '0';
if vreq.hwrite='1' then
dv.s := dswr1;
else
dv.s := dsrd1;
dv.readpipe(0) := '1';
end if;
when dswr1 =>
-- NOP,NOP,[WR]: issue either WR+D or NOP+D
dv.sdo_bdrive := '0';
dv.sdo_qdrive := '0';
inc_sdoaddr := '1';
inc_ramaddr := '1';
if lastwrite='1' then
dv.sdo_dqm := vmaskfirst or vmasklast;
dv.s := dswr3;
else
dv.sdo_casn := '0';
dv.sdo_wen := '0';
dv.sdo_dqm := vmaskfirst;
dv.s := dswr2;
end if;
when dswr2 =>
dv.sdo_dqm := (others => '0');
dv.sdo_bdrive := '0';
dv.sdo_qdrive := '0';
inc_sdoaddr := '1';
inc_ramaddr := '1';
if lastwrite='0' then
dv.sdo_casn := '0';
dv.sdo_wen := '0';
else
dv.s := dswr3;
dv.sdo_dqm := vmasklast;
end if;
when dswr3 =>
-- ...,WR+D,WR+D,[NOP+D]: issue NOP
dv.sdo_qdrive := '0';
dv.sdo_dqm := (others => '1');
dv.s := dswr4;
incdone := '1';
when dswr4 =>
-- Issue more NOP:s to meet tWR
dv.idlectr := std_logic_vector(unsigned(dr.idlectr)+1);
if dr.idlectr(0)=dr.cfg.twr then
dv.s := dswr5;
end if;
when dswr5 =>
-- Issue NOP:s until tRAS met.
if dr.cmdctr(2 downto 0)="000" then
dv.sdo_rasn := '0';
dv.sdo_wen := '0';
dv.s := dswr6;
end if;
when dswr6 =>
-- PRE: issue one or two NOP:s depending on trp setting
if dr.idlectr(1 downto 0)=dr.cfg.trp then
dv.s := dsidle;
else
dv.idlectr := std_logic_vector(unsigned(dr.idlectr)+1);
end if;
when dsrd1 =>
inc_sdoaddr := '1';
if lastreadcmd='0' then
dv.sdo_casn := '0';
dv.readpipe(0):='1';
elsif dr.cmdctr(2 downto 0)="000" then
dv.sdo_rasn := '0';
dv.sdo_wen := '0';
dv.s := dsrd3;
else
dv.s := dsrd2;
end if;
when dsrd2 =>
if dr.cmdctr(2 downto 0)="000" then
dv.sdo_rasn := '0';
dv.sdo_wen := '0';
dv.s := dsrd3;
end if;
when dsrd3 =>
if dr.idlectr(1 downto 0)=dr.cfg.trp then
if dv.readdone='1' then
dv.s := dsidle;
else
dv.s := dsrd4;
end if;
else
dv.idlectr := std_logic_vector(unsigned(dr.idlectr)+1);
end if;
when dsrd4 =>
if dv.readdone='1' then
dv.s := dsidle;
end if;
when dsreg1 =>
rbw := '1';
rbwd(2*ddrbits+chkbits-1 downto ddrbits+chkbits) := regrdata(2*ddrbits-1 downto ddrbits);
rbwd(ddrbits-1 downto 0) := regrdata(ddrbits-1 downto 0);
if vreq.hwrite='1' then
dv.s := dsreg2;
elsif regad="100" and dr.cfg.mobileen='1' then
dv.sdo_address := (others => '0');
dv.sdo_ba := "01";
dv.sdo_csn := "10";
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
dv.sdo_wen := '0';
dv.s := dssrr1;
dv.cmdctr(0) := '1';
null;
else
incdone := '1';
dv.s := dsidle;
end if;
when dsreg2 =>
case regad is
when "000" =>
dv.cfg.refon := wrdreg1(31);
dv.cfg.trp(0) := wrdreg1(30);
dv.cfg.trfc(2 downto 0) := wrdreg1(29 downto 27);
dv.cfg.trcd := wrdreg1(26);
dv.cfg.bsize := wrdreg1(25 downto 23);
dv.cfg.csize := wrdreg1(22 downto 21);
dv.cfg.command := wrdreg1(20 downto 18);
dv.cfg.dllrst := wrdreg1(17);
dv.cfg.renable := wrdreg1(16);
dv.cfg.cke := wrdreg1(15);
dv.cfg.refresh := wrdreg1(11 downto 0);
when "010" =>
dv.cfg.mobileen := wrdreg3(31);
dv.cfg.cl := wrdreg3(30);
dv.cfg.tcke := wrdreg3(24);
dv.cfg.txsr(3 downto 0) := wrdreg3(23 downto 20);
dv.cfg.txp(0) := wrdreg3(19);
dv.cfg.pmode := wrdreg3(18 downto 16);
dv.cfg.ds (5 downto 3) := wrdreg3(7 downto 5);
dv.cfg.tcsr(3 downto 2) := wrdreg3(4 downto 3);
dv.cfg.pasr(5 downto 3) := wrdreg3(2 downto 0);
-- Extended DDR400 fields
dv.cfg.tras := wrdreg3(29 downto 28);
dv.cfg.txsr(5 downto 4) := wrdreg3(27 downto 26);
dv.cfg.txp(1) := wrdreg3(25);
dv.cfg.twr := wrdreg3(11);
dv.cfg.trp(1) := wrdreg3(10);
dv.cfg.trfc(4 downto 3) := wrdreg3(9 downto 8);
when "101" =>
if confapi /= 0 then
dv.cfg.conf(31 downto 0) := wrdreg2;
end if;
when "110" =>
if confapi /= 0 then
dv.cfg.conf(63 downto 32) := wrdreg3;
end if;
when others =>
null;
end case;
incdone := '1';
dv.s := dsidle;
when dscmd1 =>
dv.sdo_csn := (others => '0');
dv.sdo_address(10) := '1';
dv.cfg.command := "000";
dv.s := dscmd2;
case dr.cfg.command is
when "010" => -- PRECHARGE ALL
dv.sdo_rasn := '0';
dv.sdo_wen := '0';
dv.cmdctr(1 downto 0) := "11";
when "100" => -- AUTO-REFRESH
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
dv.cmdctr(4 downto 0) := dr.cfg.trfc;
when "110" => -- MODE REGISTER
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
dv.sdo_wen := '0';
dv.sdo_ba := "00";
dv.sdo_address := "00000000" & "01" & dr.cfg.cl & "0001";
if dr.cfg.mobileen='0' then
dv.sdo_address(8) := dr.cfg.dllrst;
end if;
if dr.cfg.dllrst='1' then
dv.cmdctr := std_logic_vector(to_unsigned(200,dr.cmdctr'length));
end if;
when "111" => -- EXT. MODE REGISTER
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
dv.sdo_wen := '0';
if dr.cfg.mobileen='1' then
dv.sdo_ba := "10";
dv.sdo_address := "0000000" & dr.cfg.ds(5 downto 3) & dr.cfg.tcsr(3 downto 2)
& dr.cfg.pasr(5 downto 3);
else
dv.sdo_ba := "01";
dv.sdo_address := "000000000000000"; -- bit0=0 -> DLL enable
end if;
dv.cfg.pasr(2 downto 0) := dr.cfg.pasr(5 downto 3);
dv.cfg.ds(2 downto 0) := dr.cfg.ds(5 downto 3);
dv.cfg.tcsr(1 downto 0) := dr.cfg.tcsr(3 downto 2);
when others => null;
end case;
when dscmd2 =>
if dr.cmdctr=(dr.cmdctr'range => '0') then
dv.s := dsidle;
end if;
when dspdown1 =>
dv.sdo_csn := "00";
if dr.cfg.pmode(0)='1' or dr.cfg.pmode(1)='1' then
dv.cfg.cke := '0';
end if;
if dr.cfg.pmode(1)='1' then
dv.sdo_rasn := '0';
dv.sdo_casn := '0';
end if;
if dr.cfg.pmode(2)='1' and dr.cfg.pmode(0)='1' then
dv.sdo_wen := '0';
end if;
if dr.cfg.pmode(0)='1' then
dv.cmdctr(1 downto 0) := dr.cfg.txp;
end if;
if dr.cfg.pmode(1)='1' then
if dr.cfg.mobileen='1' then
dv.cmdctr(5 downto 0) := dr.cfg.txsr;
else
dv.cmdctr(7 downto 0) := std_logic_vector(to_unsigned(200,8));
end if;
end if;
dv.pdowns(1) := '0';
dv.s := dspdown2;
when dspdown2 =>
dv.pdowns(0) := '1';
if dr.pdowns(0)='0' and dr.cmdctr=(dr.cmdctr'range => '0') then
dv.pdowns(1):='1';
end if;
if dr.cfg.pmode(2)='1' and dr.cfg.pmode(0)='0' then
dv.sdo_ck := "000";
end if;
if dr.cfg.pmode(1)='1' then
dv.refpend := '1';
end if;
if (dr.refpend='1' and dr.cfg.pmode(1)='0') or vstart /= vdone then
if (dr.pdowns(0) or not dr.cfg.tcke)='1' then
dv.cfg.cke := '1';
if dr.pdowns(1)='1' then
dv.s := dsidle;
else
dv.s := dscmd2;
dv.pdowns(0) := '0';
end if;
end if;
end if;
when dsref1 =>
dv.s := dscmd2;
dv.cmdctr(4 downto 0) := dr.cfg.trfc;
when dssrr1 =>
if dr.cmdctr(0)='0' then
dv.sdo_casn := '0';
dv.readpipe(0):='1';
dv.s := dssrr2;
end if;
when dssrr2 =>
if datavalid='1' then
incdone := '1';
dv.s := dsidle;
end if;
end case;
if inc_sdoaddr='1' then
dv.sdo_address(l2blen-l2ddrw downto 1) :=
std_logic_vector(unsigned(dr.sdo_address(l2blen-l2ddrw downto 1))+1);
end if;
if inc_ramaddr='1' then
dv.ramaddr := std_logic_vector(unsigned(dr.ramaddr)+1);
end if;
-- Update the done flags
dv.resp.done_tog := (dr.resp.done_tog xor incdone) and (not reqsel);
dv.resp.rctr_gray := vrctr and (not reqselv);
dv.resp2.done_tog := (dr.resp2.done_tog xor incdone) and reqsel;
dv.resp2.rctr_gray := vrctr and reqselv;
---------------------------------------------------------------------------
-- DDR Init Sequence FSM
---------------------------------------------------------------------------
-- Command sequence lookup table
seqin := dr.cfg.mobileen & dr.initpos;
case seqin is
-- Mobile DDR
when "1100" => seqout := "0010"; -- PRECHARGE ALL
when "1011" => seqout := "0100"; -- AUTO REFRESH #1
when "1010" => seqout := "0100"; -- AUTO REFRESH #2
when "1001" => seqout := "0110"; -- MODE REG
when "1000" => seqout := "0111"; -- EXT MODE REG
-- Normal DDR
when "0110" => seqout := "0010"; -- PRECHARGE ALL
when "0101" => seqout := "0111"; -- EXT MODE REG En DLL
when "0100" => seqout := "1110"; -- MODE REG Rst DLL
when "0011" => seqout := "0010"; -- PRECHARGE ALL
when "0010" => seqout := "0100"; -- AUTO REFRESH #1
when "0001" => seqout := "0100"; -- AUTO REFRESH #2
when "0000" => seqout := "0110"; -- MODE REG NoRst DLL
when others => seqout := "0000";
end case;
case dr.initstate is
when disrstdel =>
if dr.refctr=std_logic_vector(to_unsigned(MHz*rstdel,dr.refctr'length)) then
dv.initstate := disidle;
if pwron=0 then dv.cfg.renable:='0'; end if;
end if;
-- Bypass reset delay by writing anything to regsd2
if vstartd='1' and (vreq.hio='1' and vreq.hwrite='1' and vreq.endaddr(4 downto 2)="001") then
dv.initstate := disidle;
if pwron=0 then dv.cfg.renable:='0'; end if;
end if;
when disidle =>
if dr.cfg.renable='1' then
dv.cfg.cke := '1';
if dr.cfg.cke='1' then
dv.initpos := "111";
dv.initstate := disrun;
end if;
end if;
when disrun =>
if dr.cfg.command="000" then
dv.cfg.dllrst := seqout(3);
dv.cfg.command := seqout(2 downto 0);
dv.initpos := std_logic_vector(unsigned(dr.initpos)-1);
if dr.initpos="000" then
dv.initstate := disfinished;
end if;
end if;
when disfinished =>
if dr.cfg.command="000" then
dv.cfg.renable := '0';
dv.cfg.refon := '1';
dv.initstate := disidle;
end if;
end case;
---------------------------------------------------------------------------
-- Reset
---------------------------------------------------------------------------
if ddr_rst='0' then
dv.s := dsidle;
dv.cmdctr := (others => '0');
dv.refctr := (others => '0');
dv.resp := ddr_response_none;
dv.resp2 := ddr_response_none;
dv.initstate := disrstdel;
dv.refpend := '0';
-- Reset cfg record
dv.cfg.command := "000";
dv.cfg.csize := conv_std_logic_vector(col-9, 2);
dv.cfg.bsize := conv_std_logic_vector(log2(Mbyte/8), 3);
dv.cfg.refon := '0';
dv.cfg.refresh := conv_std_logic_vector(7800*MHz/1000, 12);
dv.cfg.dllrst := '0';
dv.cfg.pasr := (others => '0');
dv.cfg.tcsr := (others => '0');
dv.cfg.ds := (others => '0');
dv.cfg.pmode := (others => '0');
dv.cfg.txsr := conv_std_logic_vector(120*MHz/1000, 6);
dv.cfg.txp := "01";
dv.cfg.cl := '0'; -- CL = 3/2 -- ****
dv.cfg.tcke := '1';
if MHz > 100 then
dv.cfg.trcd := '1';
else dv.cfg.trcd := '0';
end if;
if MHz > 100 then
dv.cfg.trp := "01";
else dv.cfg.trp := "00";
end if;
dv.cfg.renable := '1'; -- Updated in disrstdel state
if mobile >= 2 then
dv.cfg.mobileen := '1'; -- Default: Mobile DDR
else dv.cfg.mobileen := '0';
end if;
if mobile >= 2 then
dv.cfg.trfc := conv_std_logic_vector(98*MHz/1000-2, 5);
else dv.cfg.trfc := conv_std_logic_vector(75*MHz/1000-2, 5);
end if;
if ddr_syncrst /= 0 then
dv.sdo_ck := "000";
if mobile >= 2 then
dv.cfg.cke := '1';
else dv.cfg.cke := '0';
end if;
end if;
if confapi /= 0 then
dv.cfg.conf(31 downto 0) := conv_std_logic_vector(conf0, 32); --x"0000A0A0";
dv.cfg.conf(63 downto 32) := conv_std_logic_vector(conf1, 32); --x"00060606";
else
dv.cfg.conf := (others => '0');
end if;
if MHz > 175 then
dv.cfg.tras := "10";
elsif MHz > 150 then
dv.cfg.tras := "01";
else
dv.cfg.tras := "00";
end if;
if MHz > 133 then
dv.cfg.twr := '1';
else
dv.cfg.twr := '0';
end if;
dv.sdo_csn := "11";
dv.sdo_dqm := (others => '1');
dv.sdo_wen := '1';
dv.sdo_rasn := '1';
dv.sdo_casn := '1';
-- Extra reset for X-sensitive techs
dv.ramaddr := (others => '0');
end if;
---------------------------------------------------------------------------
-- Static logic/forced regs, etc
---------------------------------------------------------------------------
-- Force mobile disable/enabled
if mobile=0 then dv.cfg.mobileen := '0'; end if;
if mobile=3 then dv.cfg.mobileen := '1'; end if;
if mobile=0 then
dv.cfg.pasr := (others => '0');
dv.cfg.tcsr := (others => '0');
dv.cfg.ds := (others => '0');
dv.cfg.pmode := (others => '0');
dv.cfg.txp := "00";
dv.cfg.txsr := (others => '0');
dv.cfg.tcke := '0';
end if;
if ddr400=0 then
dv.cfg.tras := "00";
dv.cfg.txsr(5 downto 4) := "00";
dv.cfg.txp(1) := '0';
dv.cfg.trp(1) := '0';
dv.cfg.trfc(4 downto 3) := "00";
dv.cfg.twr := '0';
end if;
-- Assign sdo
o.bdrive := '1'; o.qdrive := '1'; --Temp.
o.sdck := dr.sdo_ck;
if ddr_syncrst/=0 and phyptctrl/=0 then
o.sdck := o.sdck and (o.sdck'range => ddr_rst);
end if;
if regoutput /= 0 then
o.casn := dr.sdo_casn;
o.rasn := dr.sdo_rasn;
o.sdwen := dr.sdo_wen;
o.sdcsn := dr.sdo_csn;
o.ba := '0' & dr.sdo_ba;
o.address := dr.sdo_address;
o.sdcke := (others => dr.cfg.cke);
if ddr_syncrst /= 0 and phyptctrl /= 0 then
if ddr_rst='0' then
if mobile >= 2 then o.sdcke := (others => '1');
else o.sdcke := (others => '0');
end if;
end if;
end if;
o.data(2*ddrbits-1 downto 0) := dr.sdo_data;
o.dqm(ddrbits/4-1 downto 0) := dr.sdo_dqm;
if chkbits > 0 then
o.cb(2*chkbits-1 downto 0) := dr.sdo_cb(2*chkbits-1 downto 0);
end if;
o.bdrive := dr.sdo_bdrive;
o.qdrive := dr.sdo_qdrive;
else
o.casn := dv.sdo_casn;
o.rasn := dv.sdo_rasn;
o.sdwen := dv.sdo_wen;
o.sdcsn := dv.sdo_csn;
o.ba := '0' & dv.sdo_ba;
o.address := dv.sdo_address;
o.sdcke := (others => dv.cfg.cke);
o.data(2*ddrbits-1 downto 0) := dv.sdo_data;
o.dqm(ddrbits/4-1 downto 0) := dv.sdo_dqm;
if chkbits > 0 then
o.cb(2*chkbits-1 downto 0) := dv.sdo_cb(2*chkbits-1 downto 0);
end if;
o.bdrive := dv.sdo_bdrive;
o.qdrive := dv.sdo_qdrive;
end if;
for x in 7 downto 0 loop
o.cbdqm(x) := o.dqm(2*x);
end loop;
-- Diag access
if vreq.maskcb='1' then
o.cbdqm := (others => '1');
end if;
if vreq.maskdata='1' then
o.dqm := (others => '1');
end if;
if scantest/=0 and phyptctrl/=0 then
if testen='1' then
o.bdrive := testoen;
o.qdrive := testoen;
end if;
end if;
---------------------------------------------------------------------------
-- Drive outputs
---------------------------------------------------------------------------
ndr <= dv;
sdo <= o;
response <= dr.resp;
response2 <= dr.resp2;
rbwrite <= rbw;
rbwaddr <= dr.ramaddr;
rbwdata <= rbwd;
wbraddr <= vdone & dv.ramaddr;
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.sdo_ck <= "000";
if mobile >= 2 then
dr.cfg.cke <= '1';
else dr.cfg.cke <= '0';
end if;
end if;
end process;
end;