-------------------------------------------------------------------------------
-- 
-- This VHDL design file is an open design; you can redistribute it and/or
-- modify it and/or implement it under the terms of the Openip General Public
-- License as it is going to be published by the OpenIP Organization and any
-- coming versions of this license.
-- You can check the draft license at
-- http://www.opencores.org/OIPC/license.shtml
--

 ------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 -- Title       : Parameterisable DRAM model
 -- Project     : Versatile Soft-Core Framework (VSCF)
 -------------------------------------------------------------------------------
 -- File        : d01bhv01.vhdl
 -- Author      : Damon P Thompson  
 -- Company     : The University of Edinburgh
 -- Last update : 1999/08/23
 -- Platform    : GENERIC
 -------------------------------------------------------------------------------
 -- Description :
 --      Parameterisable DRAM model, i.e. scalable data and address widths.
 --      Simulation assertions can be toggled on/off.
 --      Uses !RAS/!CAS control sequence for modelling DRAM activity.
 --      Refresh is monitored with data corrupted to "UU ... "
 --
 -------------------------------------------------------------------------------
 -- Modification history :
 --      Created about 2 years ago, and I can't really remember
 --
 -------------------------------------+-----------------------------------------
                                   -- |
 LIBRARY IEEE, ARITH_LIB, VSCF;    -- |
 USE IEEE.std_logic_1164.ALL;      -- |
                                   -- |
 -------------------------------------+-----------------------------------------

 ENTITY dram_cfg IS

    generic (
       assertions : boolean := true;     -- Toggle assertions ON/OFF
       data_width : integer := 8;        -- Data I/O bus width
       adrs_width : integer := 20        -- Address " " - must be EVEN
       );

    port (
       ras,
       cas,
       wrt      : in    std_logic;
       adrs_bus : in    std_logic_vector((((adrs_width)/2)-1) downto 0);
       data_bus : inout std_logic_vector((data_width-1) downto 0)
       );

 end dram_cfg;

 ARCHITECTURE simulate OF dram_cfg IS

    -- DRAM timing specification
    constant t_ref_max : time := 7.5 uS;  -- max. refresh cycle time
    constant t_cac_max : time := 30 nS;   -- max. access time after !CAS
    constant t_rac_max : time := 120 nS;  -- max. access time after !RAS
    constant t_off_max : time := 30 nS;   -- max. o/p diable after !CAS
    constant t_ras_min : time := 120 nS;  -- min. !RAS low pulse width
    constant t_ras_max : time := 10 uS;   -- max. !RAS low pulse width
    constant t_cas_min : time := 30 nS;   -- min. !CAS low pulse width
    constant t_cas_max : time := 10 uS;   -- max. !CAS low pulse width
    constant t_rps_min : time := 90 nS;   -- min. !RAS high pulse width
    constant t_cps_min : time := 35 nS;   -- min. !CAS high pulse width
    constant t_rcd_min : time := 25 nS;   -- min. !RAS low to !CAS low
    constant t_rcd_max : time := 90 nS;   -- max. !RAS low to !CAS low
    constant t_crp_min : time := 10 nS;   -- min. !CAS high to !RAS low
    constant t_csr_min : time := 10 nS;   -- min. !CAS low to !RAS low
    constant t_chr_min : time := 25 nS;   -- min. !RAS low to !CAS high

    -- Define memory array according to the 'adrs_width' GENERIC parameter
    type memory is array (0 to ((2**adrs_width)-1)) of std_logic_vector((data_width-1) downto 0);

    -- Define states for !RAS/!CAS DRAM model
    type dram_states is (
       idle_11,
       r_01,
       rc_00,
       rcc_01,
       c_10,
       cr_00,
       crc_01,
       crr_10
       );

    -- Some required type conversion procedures
    PROCEDURE int_to_vec( int_arg : IN INTEGER; vec_rslt : OUT STD_LOGIC_VECTOR) IS
       variable tmp_int           : integer;
       variable tmp_vec           : std_logic_vector(vec_rslt'range);
    begin
       tmp_int          := int_arg;
       for i in 0 to (vec_rslt'LENGTH - 1) loop
          if(tmp_int mod 2 = 1)
          then
             tmp_vec(i) := '1';
          else
             tmp_vec(i) := '0';
          end if;
          tmp_int       := tmp_int / 2;
       end loop;
       vec_rslt         := tmp_vec;
    end int_to_vec;

    PROCEDURE vec_to_int( vec_arg : IN STD_LOGIC_VECTOR; int_rslt : OUT INTEGER) IS
       variable tmp_int           : integer := 0;
    begin
       for i in vec_arg'range loop
          if(vec_arg(i) = '1')
          then
             tmp_int                        := tmp_int + 2**i;
          end if;
       end loop;
       int_rslt                             := tmp_int;
    end vec_to_int;

 begin

    ras_cas_control :
    process(ras, cas)

       variable dram_state    : dram_states;
       variable dram          : memory;
       variable refresh_adrs  : integer range 0 to ((2**adrs_width)-1) := 0;
       variable dram_vec_adrs : std_logic_vector((adrs_width-1) downto 0);
       variable dram_int_adrs : integer range 0 to ((2**adrs_width)-1) := 0;
       variable last_refresh,
          t_rh, t_rl,
          t_ch, t_cl          : time                                   := 0 ns;

    begin

       case dram_state is

          when idle_11 =>
             if(ras'EVENT and ras = '0' and cas = '1')
             then
                t_rl       := NOW;
                -- following read|write operation expected
                -- read row address from address bus
                if assertions then
                   assert(NOW - t_rh >= t_rps_min)
                      report "[idle_11] !RAS low too soon after !RAS high"
                      severity error;
                   assert(NOW - t_ch >= t_crp_min)
                      report "[idle_11] !RAS low too soon after !CAS high"
                      severity warning;
                end if;  -- assertions
                dram_vec_adrs((adrs_width-1) downto (adrs_width/2))
                           := adrs_bus;
                dram_state := r_01;
             elsif(cas'EVENT and ras = '1' and cas = '0')
             then
                t_cl       := NOW;
                -- following !CAS before !RAS refresh expected
                if assertions then
                   assert(NOW - t_ch >= t_cps_min)
                      report "[idle_11] !CAS low too soon after !CAS high"
                      severity warning;
                end if;  -- assertions
                dram_state := c_10;
             end if;

          when r_01 =>
             if(ras'EVENT and ras = '1' and cas = '1')
             then
                t_rh       := NOW;
                -- possible !CAS DRAM multiplexing occurred
                if assertions then
                   assert(NOW - t_rl >= t_ras_min)
                      report "[r_01] !RAS high too soon after !RAS low"
                      severity warning;
                   assert(NOW - t_rl <= t_ras_max)
                      report "[r_01] !RAS high too late after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := idle_11;
             elsif(cas'EVENT and ras = '0' and cas = '0')
             then
                t_cl       := NOW;
                -- read clomn address from address bus
                -- perform read|write operation
                if assertions then
                   assert(NOW - t_rl >= t_rcd_min)
                      report "[r_01] !CAS low too soon  after !RAS low"
                      severity warning;
                   assert(NOW - t_rl <= t_rcd_max)
                      report "[r_01] !CAS low too late  after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_vec_adrs(((adrs_width/2)-1) downto 0)
                           := adrs_bus;
                vec_to_int(dram_vec_adrs, dram_int_adrs);

                case wrt is

                   when '0' =>           -- perform write operation
                      dram(dram_int_adrs) := data_bus;

                   when '1' =>           -- perform read operation
                      data_bus <= dram(dram_int_adrs) after t_cac_max;

                   when others =>        -- !WRT input is undefined...!
                      if assertions then
                         assert false
                            report "[r_01] !WRT is undefined...!"
                            severity warning;
                      end if;  -- assertions

                end case;

                dram_state := rc_00;
             end if;

          when rc_00                =>
             if(ras'EVENT and ras = '1' and cas = '0')
             then
                -- ERROR...! Illegal !RAS event
                assert false
                   report "[rc_00] Illegal !RAS event...! STOP...!"
                   severity error;
             elsif(cas'EVENT and ras = '0' and cas = '1')
             then
                t_ch       := NOW;
                data_bus             <= (others => 'Z')  after t_off_max;
                if assertions then
                   assert(NOW - t_cl >= t_cas_min)
                      report "[rc_00] !CAS high too soon after !CAS low"
                      severity warning;
                   assert(NOW - t_cl <= t_cas_max)
                      report "[rc_00] !CAS high too late after !CAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := rcc_01;
             end if;

          when rcc_01 =>
             if(ras'EVENT and ras = '1' and cas = '1')
             then
                t_rh       := NOW;
                if assertions then
                   assert(NOW - t_rl >= t_ras_min)
                      report "[rcc_01] !RAS high too soon after !RAS low"
                      severity warning;
                   assert(NOW - t_rl <= t_ras_max)
                      report "[rcc_01] !RAS high too late after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := idle_11;
             elsif(cas'EVENT and ras = '0' and cas = '0')
             then
                -- ERROR...! Illegal !CAS event
                assert false
                   report "[rcc_01] Illegal !CAS event...! STOP...!"
                   severity error;
             end if;

          when c_10 =>
             if(ras'EVENT and ras = '0' and cas = '0')
             then
                t_rl         := NOW;
                -- perform !CAS before !RAS refresh operation
                refresh_adrs := refresh_adrs + 1;
                if assertions then
                   assert(NOW - t_cl >= t_csr_min)
                      report "[c_10] !RAS low too soon  after !CAS low"
                      severity warning;
                   assert(NOW - t_rh >= t_rps_min)
                      report "[c_10] !RAS low too soon  after !RAS high"
                      severity warning;
                end if;  -- assertions
                refresh_adrs := (refresh_adrs + 1) mod ((2**adrs_width)-1);
                dram_state   := cr_00;
             elsif(cas'EVENT and ras = '1' and cas = '1')
             then
                -- possible !RAS DRAM multiplexing occurred
                if assertions then
                   assert(NOW - t_cl >= t_cas_min)
                      report "[c_10] !CAS high too soon after !CAS low"
                      severity warning;
                   assert(NOW - t_cl <= t_cas_max)
                      report "[c_10]!CAS high too late  after !CAS low"
                      severity warning;
                end if;  -- assertions
                dram_state   := idle_11;
             end if;

          when cr_00 =>
             if(ras'EVENT and ras = '1' and cas = '0')
             then
                t_rh       := NOW;
                if assertions then
                   assert(NOW - t_rl >= t_ras_min)
                      report "[cr_00] !RAS high too soon after !RAS low"
                      severity warning;
                   assert(NOW - t_rl <= t_ras_max)
                      report "[cr_00] !RAS high too late after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := crr_10;
             elsif(cas'EVENT and ras = '0' and cas = '1')
             then
                t_ch       := NOW;
                if assertions then
                   assert(NOW - t_cl >= t_cas_min)
                      report "[cr_00] !CAS high too soon after !CAS low"
                      severity warning;
                   assert(NOW - t_cl <= t_cas_max)
                      report "[cr_00] !CAS high too late after !CAS low"
                      severity warning;
                   assert(NOW - t_rl >= t_chr_min)
                      report "[cr_00] !CAS high too soon after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := crc_01;
             end if;

          when crr_10 =>
             if(ras'EVENT and ras = '0' and cas = '0')
             then
                -- ERROR...! Illegal !RAS event
                assert false
                   report "[crr_01] Illegal !RAS event...! STOP...!"
                   severity error;
             elsif(cas'EVENT and ras = '1' and cas = '1')
             then
                t_ch       := NOW;
                if assertions then
                   assert(NOW - t_cl >= t_cas_min)
                      report "[crr_10] !CAS high too soon after !CAS low"
                      severity warning;
                   assert(NOW - t_cl <= t_cas_max)
                      report "[crr_10] !CAS high too late after !CAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := idle_11;
             end if;

          when crc_01 =>
             if(ras'EVENT and ras = '1' and cas = '1')
             then
                t_rh       := NOW;
                if assertions then
                   assert(NOW - t_rl >= t_ras_min)
                      report "[crc_01] !RAS high too soon after !RAS low"
                      severity warning;
                   assert(NOW - t_rl <= t_ras_max)
                      report "[crc_01] !RAS high too late after !RAS low"
                      severity warning;
                end if;  -- assertions
                dram_state := idle_11;
             elsif(cas'EVENT and ras = '0' and cas = '0')
             then
                -- ERROR...! Illegal !CAS event
                assert false
                   report "[crc_01] Illegal !CAS event...! STOP...!"
                   severity warning;
             end if;

          when others =>
             null;

       end case;  -- dram_state

       -- refresh monitor
       if(NOW - last_refresh > t_ref_max)
       then
          dram(refresh_adrs) := (others => 'U');
          refresh_adrs       := refresh_adrs + 1;
          if assertions then
             assert false
                report "[ref_mon] Too long since last refresh...! Amnesia...!"
                severity warning;
          end if;  -- assertions
          last_refresh       := NOW;     -- or at least it should have been
       end if;  -- refresh monitor

    end process ras_cas_control;

 end simulate;