------------------------------------------------------------------------------ -- 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: ddr2spa_ahb -- File: ddr2spa_ahb.vhd -- Author: Magnus Hjorth - Aeroflex Gaisler -- Description: Asynch AHB interface for DDR memory controller -- 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_ahb is generic ( hindex : integer := 0; haddr : integer := 0; hmask : integer := 16#f00#; ioaddr : integer := 16#000#; iomask : integer := 16#fff#; burstlen : integer := 8; nosync : integer := 0; ahbbits : integer := ahbdw; revision : integer := 0; devid : integer := GAISLER_DDR2SP; ddrbits : integer := 32; regarea : integer := 0 ); port ( rst : in std_ulogic; clk_ahb : in std_ulogic; ahbsi : in ahb_slv_in_type; ahbso : out ahb_slv_out_type; request : out ddr_request_type; start_tog: out std_logic; response : in ddr_response_type; wbwaddr : out std_logic_vector(log2(burstlen) downto 0); wbwdata : out std_logic_vector(ahbbits-1 downto 0); wbwrite : out std_logic; wbwritebig: out std_logic; rbraddr : out std_logic_vector(log2(burstlen*32/ahbbits)-1 downto 0); rbrdata : in std_logic_vector(ahbbits-1 downto 0); hwidth : in std_logic; beid : in std_logic_vector(3 downto 0) ); end ddr2spax_ahb; architecture rtl of ddr2spax_ahb 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"; constant ramwt: integer := 0; constant hconfig : ahb_config_type := ( 0 => ahb_device_reg ( VENDOR_GAISLER, DEVID, 0, REVISION, 0), 4 => ahb_membar(haddr, '1', '1', hmask), 5 => ahb_iobar(ioaddr, iomask), others => zero32); 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 l2ahbw: integer := log2(ahbbits); constant l2ddrw: integer := log2(2*ddrbits); -- Write buffer dimensions -- Write buffer is addressable down to 32-bit level on write (AHB) side. constant wbuf_wabits: integer := 1+l2blen-5; -- log2(burstlen); constant wbuf_wdbits: integer := ahbbits; -- Read buffer dimensions constant rbuf_rabits: integer := l2blen-l2ahbw; -- log2(burstlen*32/ahbbits); constant rbuf_rdbits: integer := ahbbits; type ahbstate is (asnormal,asw1,asw2,asww1,asww2,aswr,aswwx); type ahb_reg_type is record s : ahbstate; start_tog : std_logic; ramaddr : std_logic_vector(l2blen-4 downto 2); -- These are sent to the DDR layer req : ddr_request_type; -- Posted write following current request nreq : ddr_request_type; -- Read flow control rctr_lin : std_logic_vector(3 downto 0); endpos : std_logic_vector(7 downto log2(ddrbits/4)); block_read: std_logic_vector(1 downto 0); -- Current AHB control signals haddr : std_logic_vector(31 downto 0); haddr_nonseq: std_logic_vector(9 downto 0); hio : std_logic; hsize : std_logic_vector(2 downto 0); hwrite : std_logic; hburst0 : std_logic; -- AHB slave outputs so_hready : std_logic; -- From DDR layer resp1,resp2: ddr_response_type; end record; signal ar,nar : ahb_reg_type; begin ahbcomb : process(ahbsi,rst,ar,response,rbrdata,hwidth,beid) variable av: ahb_reg_type; variable va2d: ddr_request_type; variable so: ahb_slv_out_type; variable vdone: std_logic; variable vresp: ddr_response_type; variable bigsize,midsize,canburst: std_logic; variable inc_ramaddr: std_logic; variable row: std_logic_vector(14 downto 0); variable wbwa: std_logic_vector(wbuf_wabits-1 downto 0); variable wbwd: std_logic_vector(wbuf_wdbits-1 downto 0); variable wbw,wbwb: std_logic; variable rbra: std_logic_vector(rbuf_rabits-1 downto 0); variable ha0: std_logic_vector(31 downto 0); variable rend,nrend: std_logic_vector(7 downto log2(ddrbits/4)); variable datavalid, writedone: std_logic; variable rctr_gray: std_logic_vector(3 downto 0); variable tog_start: std_logic; variable regdata: std_logic_vector(31 downto 0); begin ha0 := ahbsi.haddr; ha0(31 downto 20) := ha0(31 downto 20) and not std_logic_vector(to_unsigned(hmask,12)); av := ar; so := (hready => ar.so_hready, hresp => HRESP_OKAY, hrdata => (others => '0'), hsplit => (others => '0'), hirq => (others => '0'), hconfig => hconfig, hindex => hindex); wbw := '0'; wbwb := '0'; wbwa := ar.start_tog & ar.ramaddr; wbwd := ahbreaddata(ahbsi.hwdata,ar.haddr(4 downto 2), std_logic_vector(to_unsigned(log2(ahbbits/8),3))); rbra := ar.ramaddr(l2blen-4 downto l2ahbw-3); -- Determine whether the current hsize is a big (ahbbits-width) access bigsize := '0'; if (ahbbits = 256 and ar.hsize(2)='1' and ar.hsize(0)='1') or (ahbbits = 128 and ar.hsize(2)='1') or (ahbbits = 64 and ar.hsize="011") then bigsize := '1'; end if; midsize := '0'; if ( (ahbbits = 256 and ((ar.hsize(2)='1' and ar.hsize(0)='0') or (ar.hsize(1 downto 0)="11"))) or (ahbbits = 128 and ar.hsize="011") ) then midsize := '1'; end if; -- Determine whether sequential burst is allowed after current access canburst := '0'; if (bigsize='1' and ar.haddr(l2blen-4 downto l2ahbw-3)/=(not zerov(l2blen-l2ahbw))) or (ar.hsize="010" and ar.haddr(l2blen-4 downto 2)/=(not zerov(l2blen-5))) then canburst := '1'; end if; -- if canburst='1' then -- print("ar.hsize=" & tost(ar.hsize) & "ar.haddr: " & tost(ar.haddr(l2blen-4 downto 2)) & " /= " & tost(not zerov(l2blen-5))); -- end if; if ar.hio='1' then canburst := '0'; end if; if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then av.haddr := ha0; av.ramaddr := ha0(log2(4*burstlen)-1 downto 2); av.hio := ahbsi.hmbsel(1); av.hsize := ahbsi.hsize; av.hwrite := ahbsi.hwrite; av.hburst0 := ahbsi.hburst(0); if ahbsi.htrans(0)='0' or canburst='0' then av.haddr_nonseq := ha0(9 downto 0); end if; end if; -- Synchronize from DDR domain av.resp1:=response; av.resp2:=ar.resp1; vresp := ar.resp2; if nosync /= 0 then vresp := response; end if; vdone := vresp.done_tog; -- Determine whether we can read more data in burst datavalid := '0'; writedone := '0'; if ar.start_tog=vdone then datavalid := '1'; writedone := '1'; end if; if ar.rctr_lin="0000" then rend:=ar.haddr(7 downto l2ddrw-3); else rend:=ar.endpos; end if; nrend := std_logic_vector(unsigned(rend)+1); rctr_gray := lin2gray(ar.rctr_lin); if ar.start_tog/=vdone and rctr_gray /= vresp.rctr_gray and ar.block_read(0)='0' then av.rctr_lin := std_logic_vector(unsigned(ar.rctr_lin)+1); av.endpos := nrend; rend := nrend; end if; if 2*ddrbits > ahbbits then if rend /= ar.haddr(7 downto log2(ddrbits/4)) then datavalid := '1'; end if; else if rend(7 downto log2(ahbbits/8)) /= ar.haddr(7 downto log2(ahbbits/8)) then datavalid := '1'; end if; if 2*ddrbits < ahbbits and ahbbits > 32 then if ar.hsize="010" or ar.hsize="001" or ar.hsize="000" then if rend(log2(ahbbits/8)-1 downto log2(ddrbits/4)) /= ar.haddr(log2(ahbbits/8)-1 downto log2(ddrbits/4)) then datavalid := '1'; end if; end if; end if; end if; if ar.block_read(1)='1' or (ar.start_tog/=vdone and ar.block_read(0)='1') then datavalid := '0'; writedone := '0'; end if; if ar.block_read(1)='1' and ar.start_tog/=vdone then av.block_read(1) := '0'; end if; if ar.block_read(1)='0' and vresp.rctr_gray="0000" then av.block_read(0) := '0'; end if; -- FSM inc_ramaddr := '0'; tog_start := '0'; case ar.s is when asnormal => -- Idle and memory read state if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then -- Pass on address immediately to request for read case av.req := (startaddr => ha0, endaddr => ha0(9 downto 0), hsize => ahbsi.hsize, hwrite => ahbsi.hwrite, hio => ahbsi.hmbsel(1), burst => ahbsi.hburst(0), maskdata => '0', maskcb => '0'); if ahbsi.hwrite='0' then if ahbsi.htrans(0)='0' or canburst='0' then av.so_hready := '0'; tog_start := '1'; elsif datavalid='1' then inc_ramaddr := '1'; else av.so_hready := '0'; -- grlib.testlib.print("Going to waitstate!"); end if; else av.s := asw1; end if; end if; if ar.so_hready='0' and datavalid='1' then av.so_hready := '1'; inc_ramaddr := '1'; end if; when asw1 => -- Transfer data for write request wbw := '1'; if bigsize='1' or midsize='1' then wbwb:='1'; end if; av.so_hready := '1'; av.req.endaddr := ar.haddr(9 downto 0); if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then if ahbsi.htrans(0)='0' or canburst='0' then if ahbsi.hwrite='1' then av.s := asww1; else av.so_hready := '0'; av.s := aswr; end if; tog_start := '1'; end if; else av.s := asw2; tog_start := '1'; end if; when asw2 => -- Write request ongoing av.so_hready := '1'; if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then if ahbsi.hwrite='1' then av.s := asww1; else av.so_hready := '0'; av.s := aswr; end if; elsif writedone='1' then av.s := asnormal; end if; when asww1 => -- Transfer data for second write while write request ongoing wbw := '1'; if bigsize='1' or midsize='1' then wbwb:='1'; end if; av.so_hready := '1'; av.nreq := (startaddr => ar.haddr(31 downto 10) & ar.haddr_nonseq(9 downto 0), endaddr => ar.haddr(9 downto 0), hsize => ar.hsize, hwrite => ar.hwrite, hio => ar.hio, burst => ar.hburst0, maskdata => '0', maskcb => '0'); if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then if ahbsi.htrans(0)='0' or canburst='0' then av.so_hready := '0'; av.s := aswwx; end if; else av.s := asww2; end if; when asww2 => -- Second write enqueued, wait for first write to finish -- Any new request here will cause HREADY to go low av.so_hready := '1'; if ahbsi.hready='1' and ahbsi.hsel(hindex)='1' and ahbsi.htrans(1)='1' then av.so_hready := '0'; av.s := aswwx; elsif writedone='1' then av.req := ar.nreq; tog_start := '1'; av.s := asw2; end if; when aswr => -- Read request following ongoing write request -- HREADY is low in this state av.so_hready := '0'; if writedone='1' then av.req := (startaddr => ar.haddr(31 downto 10) & ar.haddr_nonseq(9 downto 0), endaddr => ar.haddr(9 downto 0), hsize => ar.hsize, hwrite => ar.hwrite, hio => ar.hio, burst => ar.hburst0, maskdata => '0', maskcb => '0'); av.hwrite := '0'; tog_start := '1'; av.s := asnormal; end if; when aswwx => -- Write ongoing + write posted + another AHB request (read or write) -- Keep HREADY low av.so_hready := '0'; if writedone='1' then tog_start := '1'; av.req := ar.nreq; if ar.hwrite='1' then av.nreq := (startaddr => ar.haddr(31 downto 10) & ar.haddr_nonseq(9 downto 0), endaddr => ar.haddr(9 downto 0), hsize => ar.hsize, hwrite => ar.hwrite, hio => ar.hio, burst => ar.hburst0, maskdata => '0', maskcb => '0'); av.so_hready := '1'; av.s := asww1; else av.s := aswr; end if; end if; end case; if tog_start='1' and (regarea=0 or av.req.hio='0' or av.req.startaddr(5)='0') then av.start_tog := not ar.start_tog; av.rctr_lin := "0000"; if ar.start_tog /= vdone then av.block_read(1) := '1'; end if; av.block_read(0) := '1'; end if; if inc_ramaddr='1' then if bigsize='1' then av.ramaddr(log2(4*burstlen)-1 downto log2(ahbbits/8)) := std_logic_vector(unsigned(ar.ramaddr(log2(4*burstlen)-1 downto log2(ahbbits/8)))+1); else av.ramaddr(log2(4*burstlen)-1 downto 2) := std_logic_vector(unsigned(ar.ramaddr(log2(4*burstlen)-1 downto 2))+1); end if; end if; -- Used only if regarea /= 0 regdata := (others => '0'); regdata(18 downto 16) := std_logic_vector(to_unsigned(log2(ddrbits/8),3)); if hwidth/='0' then regdata(18 downto 16) := std_logic_vector(to_unsigned(log2(ddrbits/16),3)); end if; regdata(15 downto 12) := beid; -- If we are using AMBA-compliant data muxing, nothing needs to be done to -- the hrdata vector. Otherwise, we need to duplicate 32-bit lanes if regarea/=0 and ar.req.hio='1' and ar.req.startaddr(5)='1' then so.hrdata := ahbdrivedata(regdata); elsif CORE_ACDM /= 0 then so.hrdata := ahbdrivedata(rbrdata); else so.hrdata := ahbselectdata(ahbdrivedata(rbrdata),ar.haddr(4 downto 2),ar.hsize); end if; if rst='0' then av.s := asnormal; av.block_read := "00"; av.start_tog := '0'; av.so_hready := '1'; so.hready := '1'; so.hresp := HRESP_OKAY; end if; if l2blen-l2ddrw < 4 then av.rctr_lin(3 downto l2blen-l2ddrw) := (others => '0'); end if; nar <= av; request <= ar.req; start_tog <= ar.start_tog; ahbso <= so; wbwrite <= wbw; wbwritebig <= wbwb; wbwaddr <= wbwa; wbwdata <= wbwd; rbraddr <= rbra; end process; ahbregs : process(clk_ahb) begin if rising_edge(clk_ahb) then ar <= nar; end if; end process; end;