ReonV/lib/gaisler/ddr/ddr2spax_ahb.vhd

487 lines
16 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: 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;