------------------------------------------------------------------------------
--  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 - 2019, 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 
-----------------------------------------------------------------------------
-- Package:     ddrintpkg
-- File:        ddrintpkg.vhd
-- Author:      Magnus Hjorth - Aeroflex Gaisler
-- Description: Internal components and types for DDR SDRAM controllers
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library techmap;
use techmap.gencomp.all;
library grlib;
use grlib.amba.all;
use grlib.stdlib.all;
use grlib.devices.all;
library gaisler;
use gaisler.ddrpkg.all;

package ddrintpkg is

  -----------------------------------------------------------------------------
  -- DDR2SPA types and components
  -----------------------------------------------------------------------------
  component ddr2buf is
    generic (
      tech   : integer := 0;
      wabits : integer := 6;
      wdbits : integer := 8;
      rabits : integer := 6;
      rdbits : integer := 8;
      sepclk : integer := 0;
      wrfst  : integer := 0;
      testen : integer := 0);
    port (
      rclk     : in std_ulogic;
      renable  : in std_ulogic;
      raddress : in std_logic_vector((rabits -1) downto 0);
      dataout  : out std_logic_vector((rdbits -1) downto 0);
      wclk     : in std_ulogic;
      write    : in std_ulogic;
      writebig : in std_ulogic;
      waddress : in std_logic_vector((wabits -1) downto 0);
      datain   : in std_logic_vector((wdbits -1) downto 0);
      testin   : in std_logic_vector(TESTIN_WIDTH-1 downto 0));
  end component;

  type ddr_request_type is record
    startaddr : std_logic_vector(31 downto 0);
    endaddr   : std_logic_vector(9 downto 0);
    hsize     : std_logic_vector(2 downto 0);
    hwrite    : std_ulogic;
    hio       : std_ulogic;
    maskdata  : std_ulogic;
    maskcb    : std_ulogic;
    burst     : std_ulogic;
  end record;

  type ddr_response_type is record
    done_tog  : std_ulogic;
    rctr_gray : std_logic_vector(3 downto 0);
    readerr   : std_ulogic;
  end record;

  constant ddr_request_none: ddr_request_type :=
    ((others => '0'), (others => '0'), "000", '0','0','0','0','0');

  constant ddr_response_none: ddr_response_type := ('0',"0000",'0');

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

  component ft_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 := 64;
      bufbits    : integer := 96;
      ddrbits    : integer := 16;
      hwidthen   : integer := 0;
      revision   : integer := 0;
      devid      : integer := GAISLER_DDR2SP
   );
   port (
      rst        : in  std_ulogic;
      clk_ahb    : in  std_ulogic;
      ahbsi      : in  ahb_slv_in_type;
      ahbso      : out ahb_slv_out_type;
      ce         : out std_logic;
      request    : out ddr_request_type;
      start_tog  : out std_logic;
      response   : in  ddr_response_type;
      wbwaddr    : out std_logic_vector(log2(burstlen)-2 downto 0);
      wbwdata    : out std_logic_vector(bufbits-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(bufbits-1 downto 0);
      hwidth     : in  std_logic;
      synccfg    : in  std_logic;
      request2   : out ddr_request_type;
      start_tog2 : out std_logic;
      beid       : in  std_logic_vector(3 downto 0)
   );
  end component;

  constant FTFE_BEID_DDR2  : std_logic_vector(3 downto 0) := "0000";
  constant FTFE_BEID_SDR   : std_logic_vector(3 downto 0) := "0001";
  constant FTFE_BEID_DDR1  : std_logic_vector(3 downto 0) := "0010";
  constant FTFE_BEID_SSR   : std_logic_vector(3 downto 0) := "0011";
  constant FTFE_BEID_LPDDR2: std_logic_vector(3 downto 0) := "0100";

  component ddr2spax_ddr is
    generic (
      ddrbits    : integer := 32;
      burstlen   : integer := 8;
      MHz        : integer := 100;
      TRFC       : integer := 130;
      col        : integer := 9;
      Mbyte      : integer := 8;
      pwron      : integer := 0;
      oepol      : integer := 0;
      readdly    : integer := 1;
      odten      : integer := 0;
      octen      : integer := 0;
      dqsgating  : integer := 0;
      nosync     : integer := 0;
      eightbanks : integer range 0 to 1 := 0; -- Set to 1 if 8 banks instead of 4
      dqsse      : integer range 0 to 1 := 0;  -- single ended DQS
      ddr_syncrst: integer range 0 to 1 := 0;
      chkbits    : integer := 0;
      bigmem     : integer range 0 to 1 := 0;
      raspipe    : integer range 0 to 1 := 0;
      hwidthen   : integer range 0 to 1 := 0;
      phytech    : integer := 0;
      hasdqvalid : integer := 0;
      rstdel     : integer := 200;
      phyptctrl  : integer := 0;
      scantest   : integer := 0;
      dis_caslat : integer := 0;
      dis_init   : integer := 0;
      cke_rst    : integer := 0
   );
   port (
      ddr_rst  : in  std_ulogic;
      clk_ddr  : in  std_ulogic;
      request  : in  ddr_request_type;
      start_tog: in  std_logic;
      response : out ddr_response_type;
      sdi      : in  ddrctrl_in_type;
      sdo      : out ddrctrl_out_type;
      wbraddr  : out std_logic_vector(log2((16*burstlen)/ddrbits) downto 0);
      wbrdata  : in  std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwaddr  : out std_logic_vector(log2((16*burstlen)/ddrbits)-1 downto 0);
      rbwdata  : out std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwrite  : out std_logic;
      hwidth   : in  std_ulogic;
      -- dynamic sync (nosync=2)
      reqsel   : in  std_ulogic;
      frequest : in  ddr_request_type;
      response2: out ddr_response_type;
      testen   : in  std_ulogic;
      testrst  : in  std_ulogic;
      testoen  : in  std_ulogic
   );
  end component;

  -----------------------------------------------------------------------------
  -- DDRSPA types and components
  -----------------------------------------------------------------------------

  component ddr1spax_ddr is
    generic (
      ddrbits    : integer := 32;
      burstlen   : integer := 8;
      MHz        : integer := 100;
      col        : integer := 9;
      Mbyte      : integer := 8;
      pwron      : integer := 0;
      oepol      : integer := 0;
      mobile     : integer := 0;
      confapi    : integer := 0;
      conf0      : integer := 0;
      conf1      : integer := 0;
      nosync     : integer := 0;
      ddr_syncrst: integer range 0 to 1 := 0;
      chkbits    : integer := 0;
      hasdqvalid : integer := 0;
      readdly    : integer := 0;
      regoutput  : integer := 1;
      ddr400     : integer := 1;
      rstdel     : integer := 200;
      phyptctrl  : integer := 0;
      scantest   : integer := 0
      );
    port (
      ddr_rst  : in  std_ulogic;
      clk_ddr  : in  std_ulogic;
      request  : in  ddr_request_type;
      start_tog: in  std_logic;
      response : out ddr_response_type;
      sdi      : in  ddrctrl_in_type;
      sdo      : out ddrctrl_out_type;
      wbraddr  : out std_logic_vector(log2((16*burstlen)/ddrbits) downto 0);
      wbrdata  : in  std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwaddr  : out std_logic_vector(log2((16*burstlen)/ddrbits)-1 downto 0);
      rbwdata  : out std_logic_vector(2*(ddrbits+chkbits)-1 downto 0);
      rbwrite  : out std_logic;
      reqsel   : in std_ulogic;
      frequest : in  ddr_request_type;
      response2: out ddr_response_type;
      testen   : in std_ulogic;
      testrst  : in std_ulogic;
      testoen  : in std_ulogic
      );
  end component;

  -----------------------------------------------------------------------------
  -- Other components re-using sub-components above
  -----------------------------------------------------------------------------
  component ahb2avl_async_be is
    generic (
      avldbits  : integer := 32;
      avlabits  : integer := 20;
      ahbbits   : integer := ahbdw;
      burstlen  : integer := 8;
      nosync    : integer := 0
      );
    port (
      rst : in std_ulogic;
      clk : in std_ulogic;
      avlsi : out ddravl_slv_in_type;
      avlso : in  ddravl_slv_out_type;
      request: in ddr_request_type;
      start_tog: in std_ulogic;
      response: out ddr_response_type;
      wbraddr  : out std_logic_vector(log2((32*burstlen)/avldbits) downto 0);
      wbrdata  : in std_logic_vector(avldbits-1 downto 0);
      rbwaddr  : out std_logic_vector(log2((32*burstlen)/avldbits)-1 downto 0);
      rbwdata  : out std_logic_vector(avldbits-1 downto 0);
      rbwrite  : out std_logic
      );
  end component;

  -----------------------------------------------------------------------------
  -- Gray-code routines
  -----------------------------------------------------------------------------
  function lin2gray(l: std_logic_vector) return std_logic_vector;
  function gray2lin(g: std_logic_vector) return std_logic_vector;
  function nextgray(g: std_logic_vector) return std_logic_vector;

  -----------------------------------------------------------------------------
  -- Data-mask routines
  -----------------------------------------------------------------------------
  function maskfirst(addr: std_logic_vector(9 downto 0); ddrbits: integer) return std_logic_vector;
  function masklast(addr: std_logic_vector(9 downto 0); hsize: std_logic_vector(2 downto 0); ddrbits: integer) return std_logic_vector;
  function masksub32(addr: std_logic_vector(9 downto 0); hsize: std_logic_vector(2 downto 0); ddrbits: integer) return std_logic_vector;

end package;

package body ddrintpkg is

  function lin2gray(l: std_logic_vector) return std_logic_vector is
    variable lx,r: std_logic_vector(l'length-1 downto 0);
  begin
    lx := l;
    r(l'length-1) := lx(l'length-1);
    if l'length > 1 then
      r(l'length-2 downto 0) := lx(l'length-1 downto 1) xor lx(l'length-2 downto 0);
    end if;
    return r;
  end lin2gray;

  function gray2lin(g: std_logic_vector) return std_logic_vector is
    variable x: std_logic_vector(15 downto 0);
    variable r: std_logic_vector(g'length-1 downto 0);
  begin
    x := (others => '0');
    x(g'length-1 downto 0) := g;
    if g'length > 1 then
      x(14 downto 0) := x(14 downto 0) xor x(15 downto 1);
    end if;
    if g'length > 2 then
      x(13 downto 0) := x(13 downto 0) xor x(15 downto 2);
    end if;
    if g'length > 4 then
      x(11 downto 0) := x(11 downto 0) xor x(15 downto 4);
    end if;
    if g'length > 8 then
      x(7 downto 0) := x(7 downto 0) xor x(15 downto 8);
    end if;
    r := x(g'length-1 downto 0);
    return r;
  end gray2lin;

  function nextgray(g: std_logic_vector) return std_logic_vector is
    variable gx,r: std_logic_vector(g'length-1 downto 0);
    variable gx3,r3: std_logic_vector(2 downto 0) := "000";
    variable l,nl: std_logic_vector(g'length-1 downto 0);
  begin
    gx := g;
    if gx'length = 1 then
      r(0) := not gx(0);
    elsif gx'length = 2 then
      r(1) := gx(0);
      r(0) := not gx(1);
    elsif gx'length = 3 then
--      r(2) := (gx(1) or gx(0)) and (not gx(2) or not gx(0));
--      r(1) := (gx(1) or gx(0)) and (gx(2) or not gx(0));
--      r(0) := gx(2) xor gx(1);
      gx3 := gx(2 downto 0);
      case gx3 is
        when "000"  => r3 := "001";
        when "001"  => r3 := "011";
        when "011"  => r3 := "010";
        when "010"  => r3 := "110";
        when "110"  => r3 := "111";
        when "111"  => r3 := "101";
        when "101"  => r3 := "100";
        when others => r3 := "000";
      end case;
      r(2 downto 0) := r3;
    else
      l := gray2lin(g);
      nl := std_logic_vector(unsigned(l)+1);
      r := lin2gray(nl);
    end if;
    return r;
  end nextgray;

  function maskfirst(addr: std_logic_vector(9 downto 0); ddrbits: integer) return std_logic_vector is
    variable r: std_logic_vector(ddrbits/4-1 downto 0);
    variable a32: std_logic_vector(3 downto 2);
    variable a432: std_logic_vector(4 downto 2);
  begin
    r := (others => '0');
    a32 := addr(3 downto 2);
    a432 := addr(4 downto 2);
    case ddrbits is
      when 32 =>
        if addr(2)='0' then r := "00000000";
        else                r := "11110000";
        end if;
      when 64 =>
        case a32 is
          when "00"   => r := x"0000";
          when "01"   => r := x"F000";
          when "10"   => r := x"FF00";
          when others => r := x"FFF0";
        end case;
      when 128 =>
        case a432 is
          when "000"  => r := x"00000000";
          when "001"  => r := x"F0000000";
          when "010"  => r := x"FF000000";
          when "011"  => r := x"FFF00000";
          when "100"  => r := x"FFFF0000";
          when "101"  => r := x"FFFFF000";
          when "110"  => r := x"FFFFFF00";
          when others => r := x"FFFFFFF0";
        end case;
      when others =>
        --pragma translate_off
        assert ddrbits=16 report "Unsupported DDR width" severity failure;
        --pragma translate_on
        null;
    end case;
    return r;
  end maskfirst;

  function masklast(addr: std_logic_vector(9 downto 0);
                    hsize: std_logic_vector(2 downto 0); ddrbits: integer)
    return std_logic_vector is
    variable r: std_logic_vector(ddrbits/4-1 downto 0);
    variable xaddr: std_logic_vector(9 downto 0);
    variable a32: std_logic_vector(3 downto 2);
    variable a432: std_logic_vector(4 downto 2);
  begin
    xaddr := addr;
    if hsize(2)='1' then
      xaddr(3 downto 2) := "11";
      xaddr(3 downto 2) := "11";
    end if;
    if hsize(2)='1' and hsize(0)='1' then
      xaddr(4) := '1';
    end if;
    if hsize(1 downto 0)="11" then
      xaddr(2) := '1';
    end if;
    a32 := xaddr(3 downto 2);
    a432 := xaddr(4 downto 2);
    r := (others => '0');
    case ddrbits is
      when 32 =>
        if xaddr(2)='0' then r := "00001111";
        else                 r := "00000000";
        end if;
      when 64 =>
        case a32 is
          when "00"   => r := x"0FFF";
          when "01"   => r := x"00FF";
          when "10"   => r := x"000F";
          when others => r := x"0000";
        end case;
      when 128 =>
        case a432 is
          when "000"  => r := x"0FFFFFFF";
          when "001"  => r := x"00FFFFFF";
          when "010"  => r := x"000FFFFF";
          when "011"  => r := x"0000FFFF";
          when "100"  => r := x"00000FFF";
          when "101"  => r := x"000000FF";
          when "110"  => r := x"0000000F";
          when others => r := x"00000000";
        end case;
      when others =>
        --pragma translate_off
        assert ddrbits=16 report "Unsupported DDR width" severity failure;
        --pragma translate_on
        null;
    end case;
    return r;
  end masklast;

  function masksub32(addr: std_logic_vector(9 downto 0); hsize: std_logic_vector(2 downto 0); ddrbits: integer)
    return std_logic_vector is
    variable r: std_logic_vector(ddrbits/4-1 downto 0);
    variable r16: std_logic_vector(3 downto 0);
    variable a10: std_logic_vector(1 downto 0);
  begin
    r16 := (others => '0');
    if hsize(2 downto 1)="00" then
      r16 := addr(1) & addr(1) & (not addr(1)) & (not addr(1));
      if hsize(0)='0' then
        r16 := r16 or (addr(0) & (not addr(0)) & addr(0) & (not addr(0)));
      end if;
    end if;
    r := (others => '0');
    for x in 0 to ddrbits/16-1 loop
      r(x*4+3 downto x*4) := r16;
    end loop;
    return r;
  end masksub32;

end;

