##// END OF EJS Templates
MiniSpartan6:...
MiniSpartan6: added ftdi chip config to switch between UART and Async FIFO. added few WIP designs with either spwlight core, FIFO_deom IP... Libs: added SpaceWire Light IP (Works really well!) started design of ahb_ftdi_fifo -> same protocol than AHBUART but over FTDI's Async FIFO interface. This might lead to much faster transfers UP to 12MB/s.

File last commit:

r681:9d85f9f8f05a default
r681:9d85f9f8f05a default
Show More
sdctrl16.vhd
1053 lines | 39.2 KiB | text/x-vhdl | VhdlLexer
------------------------------------------------------------------------------
-- 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 - 2016, 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: sdctrl16
-- File: sdctrl16.vhd
-- Author: Jiri Gaisler - Gaisler Research
-- Modified by: Daniel Bengtsson & Richard Fång
-- Description: 16- and 32-bit SDRAM memory controller.
------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
library grlib;
use grlib.amba.all;
use grlib.stdlib.all;
library gaisler;
use grlib.devices.all;
use gaisler.memctrl.all;
entity sdctrl16 is
generic (
hindex : integer := 0;
haddr : integer := 0;
hmask : integer := 16#f00#;
ioaddr : integer := 16#000#;
iomask : integer := 16#fff#;
wprot : integer := 0;
invclk : integer := 0;
fast : integer := 0;
pwron : integer := 0;
sdbits : integer := 16;
oepol : integer := 0;
pageburst : integer := 0;
mobile : integer := 0
);
port (
rst : in std_ulogic;
clk : in std_ulogic;
ahbsi : in ahb_slv_in_type;
ahbso : out ahb_slv_out_type;
sdi : in sdctrl_in_type;
sdo : out sdctrl_out_type
);
end;
architecture rtl of sdctrl16 is
constant WPROTEN : boolean := wprot = 1;
constant SDINVCLK : boolean := invclk = 1;
constant BUS16 : boolean := (sdbits = 16);
constant BUS32 : boolean := (sdbits = 32);
constant BUS64 : boolean := (sdbits = 64);
constant REVISION : integer := 1;
constant PM_PD : std_logic_vector(2 downto 0) := "001";
constant PM_SR : std_logic_vector(2 downto 0) := "010";
constant PM_DPD : std_logic_vector(2 downto 0) := "101";
constant std_rammask: Std_Logic_Vector(31 downto 20) :=
Conv_Std_Logic_Vector(hmask, 12);
constant hconfig : ahb_config_type := (
0 => ahb_device_reg ( VENDOR_GAISLER, GAISLER_SDCTRL, 0, REVISION, 0),
4 => ahb_membar(haddr, '1', '1', hmask),
5 => ahb_iobar(ioaddr, iomask),
others => zero32);
type mcycletype is (midle, active, leadout);
type sdcycletype is (act1, act2, act3, act3_16, rd1, rd2, rd3, rd4, rd4_16, rd5, rd6, rd7, rd8,
wr1, wr1_16, wr2, wr3, wr4, wr5, sidle,
sref, pd, dpd);
type icycletype is (iidle, pre, ref, lmode, emode, finish);
-- 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(2 downto 0);
casdel : std_ulogic; -- CAS to data delay: 2/3 clock cycles
trfc : std_logic_vector(2 downto 0);
trp : std_ulogic; -- precharge to activate: 2/3 clock cycles
refresh : std_logic_vector(14 downto 0);
renable : std_ulogic;
pageburst : std_ulogic;
mobileen : std_logic_vector(1 downto 0); -- Mobile SD support, Mobile SD enabled
ds : std_logic_vector(3 downto 0); -- ds(1:0) (ds(3:2) used to detect update)
tcsr : std_logic_vector(3 downto 0); -- tcrs(1:0) (tcrs(3:2) used to detect update)
pasr : std_logic_vector(5 downto 0); -- pasr(2:0) (pasr(5:3) used to detect update)
pmode : std_logic_vector(2 downto 0); -- Power-Saving mode
txsr : std_logic_vector(3 downto 0); -- Exit Self Refresh timing
cke : std_ulogic; -- Clock enable
end record;
-- local registers
type reg_type is record
hready : std_ulogic;
hsel : std_ulogic;
bdrive : std_ulogic;
nbdrive : std_ulogic;
burst : std_ulogic;
wprothit : std_ulogic;
hio : std_ulogic;
startsd : std_ulogic;
lhw : std_ulogic; --Lower halfword
mstate : mcycletype;
sdstate : sdcycletype;
cmstate : mcycletype;
istate : icycletype;
icnt : std_logic_vector(2 downto 0);
haddr : std_logic_vector(31 downto 0);
hrdata : std_logic_vector((sdbits-1)+((16/sdbits)*16) downto 0);
hwdata : std_logic_vector(31 downto 0);
hwrite : std_ulogic;
htrans : std_logic_vector(1 downto 0);
hresp : std_logic_vector(1 downto 0);
size : std_logic_vector(1 downto 0);
cfg : sdram_cfg_type;
trfc : std_logic_vector(3 downto 0);
refresh : std_logic_vector(14 downto 0);
sdcsn : std_logic_vector(1 downto 0);
sdwen : std_ulogic;
rasn : std_ulogic;
casn : std_ulogic;
dqm : std_logic_vector(7 downto 0);
address : std_logic_vector(16 downto 2); -- memory address
bsel : std_ulogic;
idlecnt : std_logic_vector(3 downto 0); -- Counter, 16 idle clock sycles before entering Power-Saving mode
sref_tmpcom : std_logic_vector(2 downto 0); -- Save SD command when exit sref
end record;
signal r, ri : reg_type;
signal rbdrive, ribdrive : std_logic_vector(31 downto 0);
attribute syn_preserve : boolean;
attribute syn_preserve of rbdrive : signal is true;
begin
ctrl : process(rst, ahbsi, r, sdi, rbdrive)
variable v : reg_type; -- local variables for registers
variable startsd : std_ulogic;
variable dataout : std_logic_vector(31 downto 0); -- data from memory
variable regsd : std_logic_vector(31 downto 0); -- data from registers
variable dqm : std_logic_vector(7 downto 0);
variable raddr : std_logic_vector(12 downto 0);
variable adec : std_ulogic;
variable rams : std_logic_vector(1 downto 0);
variable ba : std_logic_vector(1 downto 0);
variable haddr : std_logic_vector(31 downto 0);
variable dout : std_logic_vector(31 downto 0);
variable hsize : std_logic_vector(1 downto 0);
variable hwrite : std_ulogic;
variable htrans : std_logic_vector(1 downto 0);
variable hready : std_ulogic;
variable vbdrive : std_logic_vector(31 downto 0);
variable bdrive : std_ulogic;
variable lline : std_logic_vector(2 downto 0);
variable lineburst : boolean;
variable haddr_tmp : std_logic_vector(31 downto 0);
variable arefresh : std_logic;
variable hwdata : std_logic_vector(31 downto 0);
begin
-- Variable default settings to avoid latches
v := r; startsd := '0'; v.hresp := HRESP_OKAY; vbdrive := rbdrive; arefresh := '0';
if BUS16 then
if (r.lhw = '1') then --muxes read data to correct part of the register.
v.hrdata(sdbits-1 downto 0) := sdi.data(sdbits-1 downto 0);
else
v.hrdata((sdbits*2)-1 downto sdbits) := sdi.data(sdbits-1 downto 0);
end if;
else
v.hrdata(sdbits-1 downto sdbits-32) := sdi.data(sdbits-1 downto sdbits-32);
v.hrdata(31 downto 0) := sdi.data(31 downto 0);
end if;
hwdata := ahbreadword(ahbsi.hwdata, r.haddr(4 downto 2)); v.hwdata := hwdata;
lline := not r.cfg.casdel & r.cfg.casdel & r.cfg.casdel;
if (pageburst = 0) or ((pageburst = 2) and r.cfg.pageburst = '0') then
lineburst := true;
else lineburst := false; end if;
if ((ahbsi.hready and ahbsi.hsel(hindex)) = '1') then
v.size := ahbsi.hsize(1 downto 0); v.hwrite := ahbsi.hwrite;
v.htrans := ahbsi.htrans;
if ahbsi.htrans(1) = '1' then
v.hio := ahbsi.hmbsel(1);
v.hsel := '1'; v.hready := v.hio;
end if;
v.haddr := ahbsi.haddr;
-- addr must be masked since address range can be smaller than
-- total banksize. this can result in wrong chip select being
-- asserted
for i in 31 downto 20 loop
v.haddr(i) := ahbsi.haddr(i) and not std_rammask(i);
end loop;
end if;
if (r.hsel = '1') and (ahbsi.hready = '0') then
haddr := r.haddr; hsize := r.size;
htrans := r.htrans; hwrite := r.hwrite;
else
haddr := ahbsi.haddr; hsize := ahbsi.hsize(1 downto 0);
htrans := ahbsi.htrans; hwrite := ahbsi.hwrite;
-- addr must be masked since address range can be smaller than
-- total banksize. this can result in wrong chip select being
-- asserted
for i in 31 downto 20 loop
haddr(i) := ahbsi.haddr(i) and not std_rammask(i);
end loop;
end if;
if fast = 1 then haddr := r.haddr; end if;
if ahbsi.hready = '1' then v.hsel := ahbsi.hsel(hindex); end if;
-- main state
if BUS16 then
case r.size is
when "00" => --bytesize
case r.haddr(0) is
when '0' => dqm := "11111101";
when others => dqm := "11111110";
end case;
when others => dqm := "11111100"; --halfword, word
end case;
else
case r.size is
when "00" =>
case r.haddr(1 downto 0) is
when "00" => dqm := "11110111";
when "01" => dqm := "11111011";
when "10" => dqm := "11111101";
when others => dqm := "11111110";
end case;
when "01" =>
if r.haddr(1) = '0' then dqm := "11110011"; else dqm := "11111100"; end if;
when others => dqm := "11110000";
end case;
end if;
--
-- case r.size is
-- when "00" =>
-- case r.haddr(1 downto 0) is
-- when "00" => dqm := "11111101"; lhw := '0'; --lhv := r.haddr(1)
-- when "01" => dqm := "11111110"; lhw := '0';
-- when "10" => dqm := "11111101"; lhw := '1';
-- when others => dqm := "11111110"; lhw := '1';
-- end case;
-- when "01" =>
-- dqm := "11111100";
-- if r.haddr(1) = '0' then
-- lhw := '0';
-- else
-- lhw := '1';
-- end if;
-- when others => dqm := "11111100"; --remember when word: lhw first 0 then 1
-- end case;
--
if BUS64 and (r.bsel = '1') then dqm := dqm(3 downto 0) & "1111"; end if;
-- main FSM
case r.mstate is
when midle =>
if ((v.hsel and htrans(1) and not v.hio) = '1') then
if (r.sdstate = sidle) and (r.cfg.command = "000")
and (r.cmstate = midle) and (v.hio = '0')
then
if fast = 0 then startsd := '1'; else v.startsd := '1'; end if;
v.mstate := active;
elsif ((r.sdstate = sref) or (r.sdstate = pd) or (r.sdstate = dpd))
and (r.cfg.command = "000") and (r.cmstate = midle) and (v.hio = '0')
then
v.startsd := '1';
if r.sdstate = dpd then -- Error response when on Deep Power-Down mode
v.hresp := HRESP_ERROR;
else
v.mstate := active;
end if;
end if;
end if;
when others => null;
end case;
startsd := startsd or r.startsd;
-- generate row and column address size
if BUS16 then
case r.cfg.csize is
when "00" => raddr := haddr(21 downto 9);-- case to check for bursting over row limit, since 1 row is 512 byte.
when "01" => raddr := haddr(22 downto 10);
when "10" => raddr := haddr(23 downto 11);
when others =>
if r.cfg.bsize = "110" then raddr := haddr(25 downto 13); --tänk
else raddr := haddr(24 downto 12); end if;
end case;
else
case r.cfg.csize is
when "00" => raddr := haddr(22 downto 10);
when "01" => raddr := haddr(23 downto 11);
when "10" => raddr := haddr(24 downto 12);
when others =>
if r.cfg.bsize = "111" then raddr := haddr(26 downto 14);
else raddr := haddr(25 downto 13); end if;
end case;
end if;
-- generate bank address
-- if BUS16 then --011
-- ba := genmux(r.cfg.bsize, haddr(26 downto 19)) &
-- genmux(r.cfg.bsize, haddr(25 downto 18));
-- else
ba := genmux(r.cfg.bsize, haddr(28 downto 21)) &
genmux(r.cfg.bsize, haddr(27 downto 20));
-- end if;
-- generate chip select
if BUS64 then
adec := genmux(r.cfg.bsize, haddr(30 downto 23));
v.bsel := genmux(r.cfg.bsize, r.haddr(29 downto 22));
else
adec := genmux(r.cfg.bsize, haddr(29 downto 22)); v.bsel := '0';
end if;
-- elsif BUS32 then
-- adec := genmux(r.cfg.bsize, haddr(29 downto 22)); v.bsel := '0';
-- else
-- adec := genmux(r.cfg.bsize, haddr(27 downto 20)); v.bsel := '0';
-- end if;
rams := adec & not adec;
-- sdram access FSM
if r.trfc /= "0000" then v.trfc := r.trfc - 1; end if;
if r.idlecnt /= "0000" then v.idlecnt := r.idlecnt - 1; end if;
case r.sdstate is
when sidle =>
if (startsd = '1') and (r.cfg.command = "000") and (r.cmstate = midle) then
-- if BUS16 then
-- v.address(16 downto 2) := '0' & ba & raddr(11 downto 0); --since 1 bit lower row => tot adress field 14 bits
-- else
v.address(16 downto 2) := ba & raddr; -- ba(16-15) & raddr(14-2) (2+13= 15 bits)
-- end if;
v.sdcsn := not rams(1 downto 0); v.rasn := '0'; v.sdstate := act1;
v.startsd := '0';
elsif (r.idlecnt = "0000") and (r.cfg.command = "000")
and (r.cmstate = midle) and (r.cfg.mobileen(1) = '1') then
case r.cfg.pmode is
when PM_SR =>
v.cfg.cke := '0'; v.sdstate := sref;
v.sdcsn := (others => '0'); v.rasn := '0'; v.casn := '0';
v.trfc := (r.cfg.trp and r.cfg.mobileen(1)) & r.cfg.trfc; -- Control minimum duration of Self Refresh mode (= tRAS)
when PM_PD => v.cfg.cke := '0'; v.sdstate := pd;
when PM_DPD =>
v.cfg.cke := '0'; v.sdstate := dpd;
v.sdcsn := (others => '0'); v.sdwen := '0'; v.rasn := '1'; v.casn := '1';
when others =>
end case;
end if;
when act1 =>
v.rasn := '1'; v.trfc := (r.cfg.trp and r.cfg.mobileen(1)) & r.cfg.trfc;
if r.cfg.casdel = '1' then v.sdstate := act2; else
v.sdstate := act3;
if not BUS16 then -- needs if, otherwise it might clock in incorrect write data to state act3_16
v.hready := r.hwrite and ahbsi.htrans(0) and ahbsi.htrans(1);
end if;
end if;
if WPROTEN then
v.wprothit := sdi.wprot;
if sdi.wprot = '1' then v.hresp := HRESP_ERROR; end if;
end if;
when act2 =>
v.sdstate := act3;
if not BUS16 then
v.hready := r.hwrite and ahbsi.htrans(0) and ahbsi.htrans(1);
end if;
if WPROTEN and (r.wprothit = '1') then
v.hresp := HRESP_ERROR; v.hready := '0';
end if;
when act3 =>
v.casn := '0';
if BUS16 then --HW adress needed to memory
v.address(14 downto 2) := r.haddr(12 downto 11) & '0' & r.haddr(10 downto 1); --only allowed to use tot adressbits - ba bits
-- v.address(13 downto 2) := r.haddr(11) & '0' & r.haddr(10 downto 1); --only allowed to use tot adressbits - ba bits
v.lhw := r.haddr(1); -- 14-2 = 12 colummn bits => 13 downto 2
else
v.address(14 downto 2) := r.haddr(13 downto 12) & '0' & r.haddr(11 downto 2);
end if;
v.dqm := dqm; v.burst := r.hready; -- ??
if r.hwrite = '1' then
if BUS16 then --16 bit
if r.size(1) = '1' then --word
v.hready := ahbsi.htrans(0) and ahbsi.htrans(1); --delayed this check 1 state to keep write data correct in act3_16
v.burst := ahbsi.htrans(0) and ahbsi.htrans(1);
v.sdstate := act3_16; -- goto state for second part of word transfer
-- v.lhw := '0'; --write MSB 16 bits to AMBA adress that ends with 00
else --halfword or byte
v.sdstate := act3_16; v.hready := '1';
end if;
else --32 bit or 64
v.sdstate := wr1;
if ahbsi.htrans = "11" or (r.hready = '0') then v.hready := '1'; end if;
end if;
v.sdwen := '0'; v.bdrive := '0'; --write
if WPROTEN and (r.wprothit = '1') then
v.hresp := HRESP_ERROR; v.hready := '1';
if BUS16 then v.sdstate := act3_16; else v.sdstate := wr1; end if;
v.sdwen := '1'; v.bdrive := '1'; v.casn := '1'; --skip write, remember hready high in next state
end if;
else v.sdstate := rd1; end if;
when act3_16 => --handle 16 bit and WORD write
v.address(14 downto 2) := r.haddr(12 downto 11) & '0' & r.haddr(10 downto 2) & '1';
-- v.address(13 downto 2) := r.haddr(11) & '0' & r.haddr(10 downto 2) & '1';
v.lhw := '1';
if (r.hready and r.burst) = '1' and not (WPROTEN and (r.wprothit = '1')) then
v.hready := '0'; --kolla på transfertyp nonseq om vi vill delaya nedankoll.
if( ahbsi.htrans = "11" and
not ((r.haddr(5 downto 2) = "1111") and (r.cfg.command = "100")) and
not ((r.haddr(9) xor ahbsi.haddr(9)) = '1' and r.cfg.csize = "00") ) then
v.sdstate := wr1_16;
end if;
elsif r.burst = '1' or (r.hready and not r.burst) = '1' then --terminate burst or single write
v.sdstate := wr2; v.bdrive := '1'; v.casn := '1'; v.sdwen := '1';
v.dqm := (others => '1');
else -- complete single write
v.hready := '1';
v.sdstate := act3_16; --gick till wr1 förut
end if;
when wr1_16 =>
v.address(14 downto 2) := r.haddr(12 downto 11) & '0' & r.haddr(10 downto 1);
-- v.address(13 downto 2) := r.haddr(11) & '0' & r.haddr(10 downto 1);
v.lhw := r.haddr(1);
v.sdstate := act3_16;
v.hready := '1';
when wr1 =>
v.address(14 downto 2) := r.haddr(13 downto 12) & '0' & r.haddr(11 downto 2);
if (((r.burst and r.hready) = '1') and (r.htrans = "11"))
and not (WPROTEN and (r.wprothit = '1'))
then
v.hready := ahbsi.htrans(0) and ahbsi.htrans(1) and r.hready;
if ((r.haddr(5 downto 2) = "1111") and (r.cfg.command = "100")) then -- exit on refresh
v.hready := '0';
end if;
else
v.sdstate := wr2; v.bdrive := '1'; v.casn := '1'; v.sdwen := '1';
v.dqm := (others => '1');
end if;
when wr2 =>
if (r.cfg.trp = '0') then v.rasn := '0'; v.sdwen := '0'; end if;
v.sdstate := wr3;
when wr3 =>
if (r.cfg.trp = '1') then
v.rasn := '0'; v.sdwen := '0'; v.sdstate := wr4;
else
v.sdcsn := "11"; v.rasn := '1'; v.sdwen := '1'; v.sdstate := sidle;
v.idlecnt := (others => '1');
end if;
when wr4 =>
v.sdcsn := "11"; v.rasn := '1'; v.sdwen := '1';
if (r.cfg.trp = '1') then v.sdstate := wr5;
else v.sdstate := sidle; v.idlecnt := (others => '1'); end if;
when wr5 =>
v.sdstate := sidle; v.idlecnt := (others => '1');
when rd1 => --first read applied to sdram
v.casn := '1'; v.sdstate := rd7; --nop
if not BUS16 then --starting adress cannot be XXXX...111 since we have word burst in this case. and lowest bit always 0.
if lineburst and (ahbsi.htrans = "11") then
if r.haddr(4 downto 2) = "111" then
v.address(9 downto 5) := r.address(9 downto 5) + 1; --adds only within 1KB limit.
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
when rd7 =>
v.casn := '1'; --nop
if BUS16 then
if r.cfg.casdel = '1' then --casdel3
v.sdstate := rd2;
if lineburst and (ahbsi.htrans = "11") then
if r.haddr(3 downto 1) = "110" then
v.address(10 downto 5) := r.address(10 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
else --casdel2
v.sdstate := rd3;
if ahbsi.htrans /= "11" then
if (r.trfc(3 downto 1) = "000") then v.rasn := '0'; v.sdwen := '0'; end if;
elsif lineburst then
if r.haddr(3 downto 1) = "110" then
v.address(10 downto 5) := r.address(10 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
else -- 32 bit or larger
if r.cfg.casdel = '1' then --casdel3
v.sdstate := rd2;
if lineburst and (ahbsi.htrans = "11") then
if r.haddr(4 downto 2) = "110" then
v.address(9 downto 5) := r.address(9 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
else --casdel2
v.sdstate := rd3;
if ahbsi.htrans /= "11" then
if (r.trfc(3 downto 1) = "000") then v.rasn := '0'; v.sdwen := '0'; end if; --precharge
elsif lineburst then
if r.haddr(4 downto 2) = "110" then
v.address(9 downto 5) := r.address(9 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
end if;
when rd2 =>
v.casn := '1'; v.sdstate := rd3;
if BUS16 then
if ahbsi.htrans /= "11" then
v.rasn := '0'; v.sdwen := '0'; v.dqm := (others => '1'); --precharge & DQM
--note that DQM always has 2 cycle delay before blocking data. So NP if we fetch second HW
end if;
else
if ahbsi.htrans /= "11" then v.rasn := '0'; v.sdwen := '0'; v.dqm := (others => '1'); --precharge & DQM
elsif lineburst then
if r.haddr(4 downto 2) = "101" then
v.address(9 downto 5) := r.address(9 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
when rd3 => --first read data from sdram output v.lhw := r.haddr(1);
v.casn := '1'; --if read before cas makes nop else if pre => no difference
if BUS16 then
--note if read is for halfwor or byte we dont want to read a second time but exit.
--if the read is a word we need to change LHW to one since the next read should be muxed in next cylcle.
-- if r.size(1) = '1' then --word v.hready := not r.size(1)
-- v.sdstate := rd4_16; v.hready := '0'; --hready low since just first part of a word
-- v.lhw := '1'; -- read low 16 next state
-- else --HW or byte
-- v.sdstate := rd4_16; v.hready := '1';
-- end if;
v.sdstate := rd4_16;
v.lhw := not r.lhw; --r.lhw is 0 for word, we should invert for next half of word.For HW or Byte v.lhw does not matter.
v.hready := not r.size(1); --if word transfer the r.size(1) is 1 and hready goes low.If HW or byte r.size(1)=0 => hready=1
if r.sdwen = '0' then
v.rasn := '1'; v.sdwen := '1'; v.sdcsn := "11"; v.dqm := (others => '1'); -- make DSEL (NOP)
elsif lineburst and ((ahbsi.htrans = "11") and (r.cfg.casdel = '1')) then --only enter if cl3
if r.haddr(3 downto 1) = "100" then
v.address(10 downto 5) := r.address(10 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
else --32 bit or larger
v.sdstate := rd4; v.hready := '1';
if r.sdwen = '0' then
v.rasn := '1'; v.sdwen := '1'; v.sdcsn := "11"; v.dqm := (others => '1'); -- make DSEL (NOP)
elsif lineburst and (ahbsi.htrans = "11") and (r.casn = '1') then
if r.haddr(4 downto 2) = ("10" & not r.cfg.casdel) then
v.address(9 downto 5) := r.address(9 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
when rd4_16 => --enter as word (r.hready is still 0) else 1. If hready one next transfer sampled into v.
--v.hready := '1';
v.hready := not r.hready;-- if Byte or HW exit with hready low. If word flip bit, makes correct exit with hready low.
v.lhw := not r.lhw; --r.lhw is one the first time we enter (taking care of second part of word)
v.casn := '1';
--quit on: Single transfer CL 2/3 (prcharge if CL 2 and timer was not 0)
if (ahbsi.htrans /= "11" and (r.hready = '1')) or
((r.haddr(9) xor ahbsi.haddr(9)) = '1' and r.cfg.csize = "00" and r.hready = '1') or --probably dont have to check hready 1 since if 0 adresses equal.
((r.haddr(5 downto 2) = "1111") and (r.cfg.command = "100") and (r.hready = '1')) then --quit on: ST W/HW/BYTE OR
--v.hready := '0'; --if Byte or HW exit with hready low, if ST word exit with high.
v.dqm := (others => '1');
if r.sdcsn /= "11" then --not prechargeing
v.rasn := '0'; v.sdwen := '0'; v.sdstate := rd5; --precharge
else--exit
if r.cfg.trp = '1' then v.sdstate := rd6;
else v.sdstate := sidle; v.idlecnt := (others => '1'); end if;
end if;
elsif lineburst then --NOTE: r.casn = 1 makes sure its the first halfword of a word that is checked (hready low)
if r.cfg.casdel = '0' then
if (r.haddr(3 downto 1) = "100") and (r.casn = '1') then --lline = 011 if casdel =1, 100 if casdel= 0
v.address(10 downto 5) := r.address(10 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
else
if (r.haddr(3 downto 1) = "010") and (r.hready = '1') then --lline = 011 if casdel =1, 100 if casdel= 0
v.address(10 downto 5) := r.address(10 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
end if;
when rd4 =>
v.hready := '1'; v.casn := '1';
if (ahbsi.htrans /= "11") or (r.sdcsn = "11") or
((r.haddr(5 downto 2) = "1111") and (r.cfg.command = "100")) -- exit on refresh
then
v.hready := '0'; v.dqm := (others => '1');
if (r.sdcsn /= "11") then
v.rasn := '0'; v.sdwen := '0'; v.sdstate := rd5;
else
if r.cfg.trp = '1' then v.sdstate := rd6;
else v.sdstate := sidle; v.idlecnt := (others => '1'); end if;
end if;
elsif lineburst then
if (r.haddr(4 downto 2) = lline) and (r.casn = '1') then
v.address(9 downto 5) := r.address(9 downto 5) + 1;
v.address(4 downto 2) := "000"; v.casn := '0';
end if;
end if;
when rd5 =>
if r.cfg.trp = '1' then v.sdstate := rd6; else v.sdstate := sidle; v.idlecnt := (others => '1'); end if;
v.sdcsn := (others => '1'); v.rasn := '1'; v.sdwen := '1'; v.dqm := (others => '1');
v.casn := '1';
when rd6 =>
v.sdstate := sidle; v.idlecnt := (others => '1'); v.dqm := (others => '1');
v.sdcsn := (others => '1'); v.rasn := '1'; v.sdwen := '1';
when sref =>
if (startsd = '1' and (r.hio = '0'))
or (r.cfg.command /= "000") or r.cfg.pmode /= PM_SR then
if r.trfc = "0000" then -- Minimum duration (= tRAS)
v.cfg.cke := '1';
v.sdcsn := (others => '0'); v.rasn := '1'; v.casn := '1';
end if;
if r.cfg.cke = '1' then
if (r.idlecnt = "0000") then -- tXSR ns with NOP
v.sdstate := sidle;
v.idlecnt := (others => '1');
v.sref_tmpcom := r.cfg.command;
v.cfg.command := "100";
end if;
else
v.idlecnt := r.cfg.txsr;
end if;
end if;
when pd =>
if (startsd = '1' and (r.hio = '0'))
or (r.cfg.command /= "000") or r.cfg.pmode /= PM_PD then
v.cfg.cke := '1';
v.sdstate := sidle;
v.idlecnt := (others => '1');
end if;
when dpd =>
v.sdcsn := (others => '1'); v.sdwen := '1'; v.rasn := '1'; v.casn := '1';
v.cfg.renable := '0';
if (startsd = '1' and r.hio = '0') then
v.hready := '1'; -- ack all accesses with Error response
v.startsd := '0';
v.hresp := HRESP_ERROR;
elsif r.cfg.pmode /= PM_DPD then
v.cfg.cke := '1';
if r.cfg.cke = '1' then
v.sdstate := sidle;
v.idlecnt := (others => '1');
v.cfg.renable := '1';
end if;
end if;
when others =>
v.sdstate := sidle; v.idlecnt := (others => '1');
end case;
-- sdram commands
case r.cmstate is
when midle =>
if r.sdstate = sidle then
case r.cfg.command is
when "010" => -- precharge
v.sdcsn := (others => '0'); v.rasn := '0'; v.sdwen := '0';
v.address(12) := '1'; v.cmstate := active;
when "100" => -- auto-refresh
v.sdcsn := (others => '0'); v.rasn := '0'; v.casn := '0';
v.cmstate := active;
when "110" => -- Lodad Mode Reg
v.sdcsn := (others => '0'); v.rasn := '0'; v.casn := '0';
v.sdwen := '0'; v.cmstate := active;
if lineburst then
v.address(16 downto 2) := "0000010001" & r.cfg.casdel & "0011";
else
v.address(16 downto 2) := "0000010001" & r.cfg.casdel & "0111";
end if;
when "111" => -- Load Ext-Mode Reg
v.sdcsn := (others => '0'); v.rasn := '0'; v.casn := '0';
v.sdwen := '0'; v.cmstate := active;
v.address(16 downto 2) := "10000000" & r.cfg.ds(1 downto 0) & r.cfg.tcsr(1 downto 0)
& r.cfg.pasr(2 downto 0);
when others => null;
end case;
end if;
when active =>
v.sdcsn := (others => '1'); v.rasn := '1'; v.casn := '1';
v.sdwen := '1'; --v.cfg.command := "000";
v.cfg.command := r.sref_tmpcom; v.sref_tmpcom := "000";
v.cmstate := leadout; v.trfc := (r.cfg.trp and r.cfg.mobileen(1)) & r.cfg.trfc;
when leadout =>
if r.trfc = "0000" then v.cmstate := midle; end if;
end case;
-- sdram init
case r.istate is
when iidle =>
v.cfg.cke := '1';
if r.cfg.renable = '1' and r.cfg.cke = '1' then
v.cfg.command := "010"; v.istate := pre;
end if;
when pre =>
if r.cfg.command = "000" then
v.cfg.command := "100"; v.istate := ref; v.icnt := "111";
end if;
when ref =>
if r.cfg.command = "000" then
v.cfg.command := "100"; v.icnt := r.icnt - 1;
if r.icnt = "000" then v.istate := lmode; v.cfg.command := "110"; end if;
end if;
when lmode =>
if r.cfg.command = "000" then
if r.cfg.mobileen = "11" then
v.cfg.command := "111"; v.istate := emode;
else
v.istate := finish;
end if;
end if;
when emode =>
if r.cfg.command = "000" then
v.istate := finish;
end if;
when others =>
if r.cfg.renable = '0' and r.sdstate /= dpd then
v.istate := iidle;
end if;
end case;
if (ahbsi.hready and ahbsi.hsel(hindex) ) = '1' then
if ahbsi.htrans(1) = '0' then v.hready := '1'; end if;
end if;
if (r.hsel and r.hio and not r.hready) = '1' then v.hready := '1'; end if;
-- second part of main fsm
case r.mstate is
when active =>
if v.hready = '1' then
v.mstate := midle;
end if;
when others => null;
end case;
-- sdram refresh counter
-- pragma translate_off
if not is_x(r.cfg.refresh) then
-- pragma translate_on
if (r.cfg.renable = '1') and (r.istate = finish) and r.sdstate /= sref then
v.refresh := r.refresh - 1;
if (v.refresh(14) and not r.refresh(14)) = '1' then
v.refresh := r.cfg.refresh;
v.cfg.command := "100";
arefresh := '1';
end if;
end if;
-- pragma translate_off
end if;
-- pragma translate_on
-- AHB register access
-- if writing to IO space config regs. Just mapping write data to all config values in config reg
if (r.hsel and r.hio and r.hwrite and r.htrans(1)) = '1' then
if r.haddr(3 downto 2) = "00" then
if pageburst = 2 then v.cfg.pageburst := hwdata(17); end if;
v.cfg.command := hwdata(20 downto 18);
v.cfg.csize := hwdata(22 downto 21);
v.cfg.bsize := hwdata(25 downto 23);
v.cfg.casdel := hwdata(26);
v.cfg.trfc := hwdata(29 downto 27);
v.cfg.trp := hwdata(30);
v.cfg.renable := hwdata(31);
v.cfg.refresh := hwdata(14 downto 0);
v.refresh := (others => '0');
elsif r.haddr(3 downto 2) = "01" then
if r.cfg.mobileen(1) = '1' and mobile /= 3 then v.cfg.mobileen(0) := hwdata(31); end if;
if r.cfg.pmode = "000" then
v.cfg.cke := hwdata(30);
end if;
if r.cfg.mobileen(1) = '1' then
v.cfg.txsr := hwdata(23 downto 20);
v.cfg.pmode := hwdata(18 downto 16);
v.cfg.ds(3 downto 2) := hwdata( 6 downto 5);
v.cfg.tcsr(3 downto 2) := hwdata( 4 downto 3);
v.cfg.pasr(5 downto 3) := hwdata( 2 downto 0);
end if;
end if;
end if;
-- Disable CS and DPD when Mobile SDR is Disabled
if r.cfg.mobileen(0) = '0' then v.cfg.pmode(2) := '0'; end if;
-- Update EMR when ds, tcsr or pasr change
if r.cfg.command = "000" and arefresh = '0' and r.cfg.mobileen(0) = '1' then
if r.cfg.ds(1 downto 0) /= r.cfg.ds(3 downto 2) then
v.cfg.command := "111"; v.cfg.ds(1 downto 0) := r.cfg.ds(3 downto 2);
end if;
if r.cfg.tcsr(1 downto 0) /= r.cfg.tcsr(3 downto 2) then
v.cfg.command := "111"; v.cfg.tcsr(1 downto 0) := r.cfg.tcsr(3 downto 2);
end if;
if r.cfg.pasr(2 downto 0) /= r.cfg.pasr(5 downto 3) then
v.cfg.command := "111"; v.cfg.pasr(2 downto 0) := r.cfg.pasr(5 downto 3);
end if;
end if;
regsd := (others => '0');
--reads out config registers (r/w does not matter) according to manual depending on address, notice generic determines data width.
if r.haddr(3 downto 2) = "00" then
regsd(31 downto 18) := r.cfg.renable & r.cfg.trp & r.cfg.trfc &
r.cfg.casdel & r.cfg.bsize & r.cfg.csize & r.cfg.command;
if not lineburst then regsd(17) := '1'; end if;
regsd(16) := r.cfg.mobileen(1);
if BUS64 then regsd(15) := '1'; end if;
regsd(14 downto 0) := r.cfg.refresh;
elsif r.haddr(3 downto 2) = "01" then
regsd(31) := r.cfg.mobileen(0);
regsd(30) := r.cfg.cke;
regsd(23 downto 0) := r.cfg.txsr & '0' & r.cfg.pmode & "000000000" &
r.cfg.ds(1 downto 0) & r.cfg.tcsr(1 downto 0) & r.cfg.pasr(2 downto 0);
end if;
if (r.hsel and r.hio) = '1' then dout := regsd;
else
if BUS64 and r.bsel = '1' then dout := r.hrdata(63 downto 32);
else dout := r.hrdata(31 downto 0); end if;
end if;
v.nbdrive := not v.bdrive;
if oepol = 1 then bdrive := r.nbdrive; vbdrive := (others => v.nbdrive);
else bdrive := r.bdrive; vbdrive := (others => v.bdrive);end if;
-- reset
if rst = '0' then
v.sdstate := sidle;
v.mstate := midle;
v.istate := iidle;
v.cmstate := midle;
v.hsel := '0';
v.cfg.command := "000";
v.cfg.csize := "01";
v.cfg.bsize := "011";
v.cfg.casdel := '1';
v.cfg.trfc := "111";
if pwron = 1 then v.cfg.renable := '1';
else v.cfg.renable := '0'; end if;
v.cfg.trp := '1';
v.dqm := (others => '1');
v.sdwen := '1';
v.rasn := '1';
v.casn := '1';
v.hready := '1';
v.bsel := '0';
v.startsd := '0';
if (pageburst = 2) then
v.cfg.pageburst := '0';
end if;
if mobile >= 2 then v.cfg.mobileen := "11";
elsif mobile = 1 then v.cfg.mobileen := "10";
else v.cfg.mobileen := "00"; end if;
v.cfg.txsr := (others => '1');
v.cfg.pmode := (others => '0');
v.cfg.ds := (others => '0');
v.cfg.tcsr := (others => '0');
v.cfg.pasr := (others => '0');
if mobile >= 2 then v.cfg.cke := '0';
else v.cfg.cke := '1'; end if;
v.sref_tmpcom := "000";
v.idlecnt := (others => '1');
end if;
ri <= v;
ribdrive <= vbdrive;
ahbso.hready <= r.hready;
ahbso.hresp <= r.hresp;
ahbso.hrdata <= ahbdrivedata(dout);
end process;
--sdo.sdcke <= (others => '1');
sdo.sdcke <= (others => r.cfg.cke);
ahbso.hconfig <= hconfig;
ahbso.hirq <= (others => '0');
ahbso.hindex <= hindex;
ahbso.hsplit <= (others => '0');
-- Quick hack to get rid of undriven signal warnings. Check this for future
-- merge with main sdctrl.
drivehack : block
begin
sdo.qdrive <= '0';
sdo.nbdrive <= '0';
sdo.ce <= '0';
sdo.moben <= '0';
sdo.cal_rst <= '0';
sdo.oct <= '0';
sdo.xsdcsn <= (others => '1');
sdo.data(127 downto 16) <= (others => '0');
sdo.cb <= (others => '0');
sdo.ba <= (others => '0');
sdo.sdck <= (others => '0');
sdo.cal_en <= (others => '0');
sdo.cal_inc <= (others => '0');
sdo.cal_pll <= (others => '0');
sdo.odt <= (others => '0');
sdo.conf <= (others => '0');
sdo.vcbdrive <= (others => '0');
sdo.dqs_gate <= '0';
sdo.cbdqm <= (others => '0');
sdo.cbcal_en <= (others => '0');
sdo.cbcal_inc <= (others => '0');
sdo.read_pend <= (others => '0');
sdo.regwdata <= (others => '0');
sdo.regwrite <= (others => '0');
end block drivehack;
regs : process(clk, rst) begin
if rising_edge(clk) then
r <= ri; rbdrive <= ribdrive;
if rst = '0' then r.icnt <= (others => '0'); end if;
end if;
if (rst = '0') then
r.sdcsn <= (others => '1'); r.bdrive <= '1'; r.nbdrive <= '0';
if oepol = 0 then rbdrive <= (others => '1');
else rbdrive <= (others => '0'); end if;
end if;
end process;
rgen : if not SDINVCLK generate
sdo.address <= r.address;
sdo.bdrive <= r.nbdrive when oepol = 1 else r.bdrive;
sdo.vbdrive <= zero32 & rbdrive;
sdo.sdcsn <= r.sdcsn;
sdo.sdwen <= r.sdwen;
sdo.dqm <= "11111111" & r.dqm;
sdo.rasn <= r.rasn;
sdo.casn <= r.casn;
mux16_wrdata : if BUS16 generate --mux data depending on Low/High HW
sdo.data(15 downto 0) <= r.hwdata(15 downto 0) when r.lhw = '1' else r.hwdata(31 downto 16);
end generate;
wrdata : if not BUS16 generate
drivebus: for i in 0 to sdbits/64 generate
sdo.data(31+32*i downto 32*i) <= r.hwdata;
end generate;
end generate;
end generate;
ngen : if SDINVCLK generate
nregs : process(clk, rst) begin
if falling_edge(clk) then
sdo.address <= r.address;
if oepol = 1 then sdo.bdrive <= r.nbdrive;
else sdo.bdrive <= r.bdrive; end if;
sdo.vbdrive <= zero32 & rbdrive;
sdo.sdcsn <= r.sdcsn;
sdo.sdwen <= r.sdwen;
sdo.dqm <= "11111111" & r.dqm;
sdo.rasn <= r.rasn;
sdo.casn <= r.casn;
if BUS16 then --mux data depending on Low/High HW
if (r.lhw ='1') then
sdo.data(15 downto 0) <= r.hwdata(15 downto 0);
else
sdo.data(15 downto 0) <= r.hwdata(31 downto 16);
end if;
end if;
if not BUS16 then
for i in 0 to sdbits/64 loop
sdo.data(31+32*i downto 32*i) <= r.hwdata;
end loop;
end if;
end if;
if rst = '0' then sdo.sdcsn <= (others => '1'); end if;
end process;
end generate;
-- pragma translate_off
bootmsg : report_version
generic map ("sdctrl16" & tost(hindex) &
": PC133 SDRAM controller rev " & tost(REVISION));
-- pragma translate_on
end;