mirror of
https://github.com/lcbcFoo/ReonV.git
synced 2025-04-24 21:47:05 -04:00
1398 lines
52 KiB
VHDL
1398 lines
52 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: 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;
|
|
|