------------------------------------------------------------------------------ -- 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;