-- --------------------------------------------------------------------
-- >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
-- --------------------------------------------------------------------
-- Copyright (c) 2005 by Lattice Semiconductor Corporation
-- --------------------------------------------------------------------
--
-- Permission:
--
--   Lattice Semiconductor grants permission to use this code for use
--   in synthesis for any Lattice programmable logic product.  Other
--   use of this code, including the selling or duplication of any
--   portion is strictly prohibited.
--
-- Disclaimer:
--
--   This VHDL or Verilog source code is intended as a design reference
--   which illustrates how these types of functions can be implemented.
--   It is the user's responsibility to verify their design for
--   consistency and functionality through the use of formal
--   verification methods.  Lattice Semiconductor provides no warranty
--   regarding the use or functionality of this code.
--
-- --------------------------------------------------------------------
--
--                     Lattice Semiconductor Corporation
--                     5555 NE Moore Court
--                     Hillsboro, OR 97214
--                     U.S.A
--
--                     TEL: 1-800-Lattice (USA and Canada)
--                          408-826-6000 (other locations)
--
--                     web: http://www.latticesemi.com/
--                     email: techsupport@latticesemi.com
--
-- --------------------------------------------------------------------
--
--  Project:           DDR memory test
--  File:              ddr_test.vhd
--  Title:             ddr_test
--  Description:       This module implements state machines that
--                     perform the DDR memory test through Lattice IP
--                     core DDRCT-GEN-E2-N1.
--
-- --------------------------------------------------------------------
--
-- Revision History :
-- --------------------------------------------------------------------
-- $Log: ddr_test.vhd,v $
-- Revision 1.0  2005-05-09 16:21:48-07  jhsin
-- Initial revision
--
--
-- --------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

library work;
  use work.ddrct_gen_e2_1_001_params.all;

entity ddr_test is
   port (
      -- INPUTS
      waited_200us             : in  std_logic;   
      init_done                : in  std_logic;
      pll_clk                  : in  std_logic;
      rst_n                    : in  std_logic;
      cmd_rdy                  : in  std_logic;
      data_rdy                 : in  std_logic;
      read_data_valid          : in  std_logic;
      read_data                : in  std_logic_vector(DSIZE-1 downto 0);
                           
      -- OUTPUTS           
      init_start               : inout std_logic;
      test_data                : inout std_logic_vector(7 downto 0);
      fsm2_out                 : out std_logic;
      datacheck                : inout std_logic_vector(1 downto 0);
      latched_data             : out std_logic_vector(15 downto 0);
      number_of_tests_passed   : inout std_logic_vector(3 downto 0);
      number_of_tests_failed   : inout std_logic_vector(3 downto 0);
      cmd                      : out std_logic_vector(3 downto 0);
      cmd_valid                : inout std_logic;
      data_mask                : out std_logic_vector(USER_DM-1 downto 0);
      addr                     : out std_logic_vector(ADDR_WIDTH-1 downto 0);
      write_data               : out std_logic_vector(DSIZE-1 downto 0)
   );
end ddr_test;

architecture ddr_test_a of ddr_test is

   constant CMD_READ     : std_logic_vector(3 downto 0) := "0001";
   constant CMD_WRITE    : std_logic_vector(3 downto 0) := "0010";
   constant CMD_READA    : std_logic_vector(3 downto 0) := "0011";
   constant CMD_WRITEA   : std_logic_vector(3 downto 0) := "0100";
   constant CMD_PDOWN    : std_logic_vector(3 downto 0) := "0101";
   constant CMD_LOAD_MR  : std_logic_vector(3 downto 0) := "0110";
   constant CMD_SELF_REF : std_logic_vector(3 downto 0) := "0111";

   -- State Machine Type Definition
   type FSM1_state_Typ is (fsm1_idle, fsm1_write, fsm1_verify, fsm1_change_test_data, fsm1_wait_for_cmd_rdy);
   type FSM2_state_Typ is (fsm2_wait_for_data_check, fsm2_check_readback_data);

   -- State Signal Declaration
   signal fsm1 : FSM1_state_Typ;
   signal fsm2 : FSM2_state_Typ;

   signal stop_init              : std_logic;
   signal read_data_valid_delay  : std_logic;
   signal data_rdy_delay         : std_logic;
   signal data_rdy_delay2        : std_logic;
   signal inc_test_addr          : std_logic;
   signal wait_count             : std_logic_vector(6 downto 0);
   signal test_addr              : std_logic_vector(ADDR_WIDTH-1 downto 0);
   signal end_of_test_addr       : std_logic;

begin

   --
   -- Wait 200µs after the power is applied and the clocks are running before
   -- asserting the "init_start" signal.
   -- Signal "init_start" should be kept asserted until the controller signaling
   -- the completion of the initialization endprocess asserts "init_done".
   --
   init_Proc : process (pll_clk, rst_n)
   begin
      if (rst_n='0') then
         init_start <= '0';
         stop_init <= '0';
      elsif rising_edge(pll_clk) then
         if (waited_200us='1') and (init_start='0') and (stop_init='0') then
            init_start <= '1';
            stop_init <= '1';
         elsif (init_done='1') then
            init_start <= '0';
         end if;
      end if;
   end process init_Proc;

   --
   -- Delays singnals
   --    (1) read_data_valid (used in the READs), and
   --    (2) data_rdy (used in the WRITEs)
   --
   delay_Proc : process (pll_clk, rst_n)
   begin
      if (rst_n='0') then
         read_data_valid_delay <= '0';
         data_rdy_delay <= '0';
         data_rdy_delay2 <= '0';
      elsif rising_edge(pll_clk) then
         read_data_valid_delay <= read_data_valid;
         data_rdy_delay <= data_rdy;
         data_rdy_delay2 <= data_rdy_delay;
      end if;
   end process delay_Proc;

   --
   -- Enable all bytes
   --
   data_mask <= (others => '0');

   --
   -- FSM1 : main state machine for DDR memory testing
   --
   FSM1_FSM: process(pll_clk, rst_n)
   begin
      if (rst_n='0') then
         cmd           <= (others => '0');
         cmd_valid     <= '0';
         addr          <= (others => '0');
         write_data    <= (others => '0');
         inc_test_addr <= '0';
         datacheck     <= (others => '0');
         latched_data  <= (others => '0');
         wait_count    <= (others => '0');
         fsm1          <= fsm1_idle;
      elsif rising_edge(pll_clk) then
         case fsm1 is
            when fsm1_idle =>
               if (init_done='1') then
                  fsm1 <= fsm1_wait_for_cmd_rdy;
               end if;
            when fsm1_wait_for_cmd_rdy =>
               if (cmd_rdy='1') then
                  addr <= test_addr;
                  write_data <= X"000000000000" & test_data & (test_data xor X"FF");
                  datacheck <= (others => '0');
                  cmd <= CMD_WRITE;
                  cmd_valid <= '1';
                  fsm1 <= fsm1_write; -- Switch to fsm1_write to start a new test
                  inc_test_addr <= '1';
               end if;
            when fsm1_write =>
               addr <= test_addr;
               write_data <= X"000000000000" & test_data & (test_data xor X"FF");
               datacheck <= (others => '0');
               if (cmd_rdy='1') and (end_of_test_addr='1') then
                  cmd <= CMD_READ; -- Switch to fsm1_verify to read data back and check if is correct
                  fsm1 <= fsm1_verify;
               end if;
            when fsm1_verify =>
               addr <= test_addr;
               write_data <= X"000000000000" & test_data & (test_data xor X"FF"); -- Even after switched to read, write_data still 
                                                                                  -- needs to be updated for the last few writes
               if (read_data_valid_delay='1') and (read_data_valid='0') then -- Checking readback data
                  latched_data <= read_data(15 downto 0);
                  datacheck <= ((read_data(15) xor test_data(7)) or (read_data(14) xor test_data(6)) or
                                (read_data(13) xor test_data(5)) or (read_data(12) xor test_data(4)) or
                                (read_data(11) xor test_data(3)) or (read_data(10) xor test_data(2)) or
                                (read_data( 9) xor test_data(1)) or (read_data( 8) xor test_data(0))) &
                               ((read_data( 7) xor (not test_data(7))) or (read_data( 6) xor (not test_data(6))) or
                                (read_data( 5) xor (not test_data(5))) or (read_data( 4) xor (not test_data(4))) or
                                (read_data( 3) xor (not test_data(3))) or (read_data( 2) xor (not test_data(2))) or
                                (read_data( 1) xor (not test_data(1))) or (read_data( 0) xor (not test_data(0))));
               end if;
               if (cmd_rdy='1') and (end_of_test_addr='1') then
                  cmd <= CMD_WRITE;
                  fsm1 <= fsm1_change_test_data; -- Switch to fsm1_change_test_data to change test_data for the next test
                  cmd_valid <= '0';
               end if;
            when fsm1_change_test_data =>
               write_data <= X"000000000000" & test_data & (test_data xor X"FF");
               if (cmd_rdy='1') and (wait_count(6)='1') then
                  datacheck <= (others => '0');
                  fsm1 <= fsm1_write; -- Switch to fsm1_write to start another new test
                  cmd_valid <= '1';
                  wait_count <= (others => '0');
               else
                  if (read_data_valid_delay='1') and (read_data_valid='0') then -- Even after switched to this state, we still need
                                                                                -- to check data for the last few reads
                     latched_data <= read_data(15 downto 0);
                     datacheck <= ((read_data(15) xor test_data(7)) or (read_data(14) xor test_data(6)) or
                                   (read_data(13) xor test_data(5)) or (read_data(12) xor test_data(4)) or
                                   (read_data(11) xor test_data(3)) or (read_data(10) xor test_data(2)) or
                                   (read_data( 9) xor test_data(1)) or (read_data( 8) xor test_data(0))) &
                                  ((read_data( 7) xor (not test_data(7))) or (read_data( 6) xor (not test_data(6))) or
                                   (read_data( 5) xor (not test_data(5))) or (read_data( 4) xor (not test_data(4))) or
                                   (read_data( 3) xor (not test_data(3))) or (read_data( 2) xor (not test_data(2))) or
                                   (read_data( 1) xor (not test_data(1))) or (read_data( 0) xor (not test_data(0))));
                  end if;
                  wait_count <= wait_count + 1;
               end if;
         end case;
      end if;
   end process FSM1_FSM;
   
   --
   -- Creating test_addr
   --
   test_addr_Proc : process (pll_clk, rst_n)
   begin
      if (rst_n='0') then
         test_addr <= (others => '0');
      elsif rising_edge(pll_clk) then
         if (inc_test_addr='1') and (cmd_rdy='1') and (cmd_valid='1') then
            -- Advance test_addr for the next READ command or WRITE command
            -- Default burst length = 4, therefore increase test_addr by 4
            test_addr <= test_addr + 4;
         end if;
      end if;
   end process test_addr_Proc;

   --
   -- Detecting end_of_test_addr
   --
   end_of_test_addr_Proc : process (pll_clk, rst_n)
   begin
      if (rst_n='0') then
         end_of_test_addr <= '0';
      elsif rising_edge(pll_clk) then
         if (test_addr="11111111111111111111100") then
            end_of_test_addr <= '1';
         else
            end_of_test_addr <= '0';
         end if;
      end if;
   end process end_of_test_addr_Proc;

   test_data_Proc : process (pll_clk, rst_n)
   begin
      if (rst_n='0') then
         test_data <= X"01";
      elsif rising_edge(pll_clk) then
         if ((data_rdy_delay2='1' and data_rdy_delay='0') or (read_data_valid_delay='1' and read_data_valid='0')) then
            -- Rotate test_data for every read command and write command
            -- During the same test, test_data will be rotated whenever
            --    (1) test_data is sampled by data_rdy durring the write, or
            --    (2) read_data_valid indicates data is read back (rotate test_data for data checking)
            test_data <= test_data(6 downto 0) & test_data(7);
         elsif (wait_count="0100000") then -- Change "test_data" for the next test
            --
            -- test #                =>  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 , 17 18 19 20 21 22 23 24 ...
            -- test_data(at addr  0) => 01 fd 04 f7 10 df 40 7f fe 02 fb 08 ef 20 bf 80 , 01 fd 04 f7 10 df 40 7f ...
            -- test_data(at addr  4) => 02 fb 08 ef 20 bf 80 fe fd 04 f7 10 df 40 7f 01 , 02 fb 08 ef 20 bf 80 fe ...
            -- test_data(at addr  8) => 04 f7 10 df 40 7f 01 fd fb 08 ef 20 bf 80 fe 02 , 04 f7 10 df 40 7f 01 fd ...
            -- test_data(at addr  c) => 08 ef 20 bf 80 fe 02 fb f7 10 df 40 7f 01 fd 04 , 08 ef 20 bf 80 fe 02 fb ...
            -- test_data(at addr 10) => 10 df 40 7f 01 fd 04 f7 ef 20 bf 80 fe 02 fb 08 , 10 df 40 7f 01 fd 04 f7 ...
            -- test_data(at addr 14) => 20 bf 80 fe 02 fb 08 ef df 40 7f 01 fd 04 f7 10 , 20 bf 80 fe 02 fb 08 ef ...
            -- test_data(at addr 18) => 40 7f 01 fd 04 f7 10 df bf 80 fe 02 fb 08 ef 20 , 40 7f 01 fd 04 f7 10 df ...
            -- test_data(at addr 1c) => 80 fe 02 fb 08 ef 20 bf 7f 01 fd 04 f7 10 df 40 , 80 fe 02 fb 08 ef 20 bf ...
            -- test_data(at addr 20) => 01 fd 04 f7 10 df 40 7f fe 02 fb 08 ef 20 bf 80 , 01 fd 04 f7 10 df 40 7f ...
            --         :
            --         :
            if ((test_data=X"80") or (test_data=X"7F")) then
               test_data <= test_data(6 downto 0) & test_data(7); -- Rotated test_data
            else
               test_data <= (test_data(6 downto 0) & test_data(7)) xor X"FF"; -- Rotate test_data and then invert it
            end if;
         end if;
      end if;
   end process test_data_Proc;

   --
   -- FSM2 for increaing success/failure counters during the data checking
   --
   FSM2_FSM: process(pll_clk, rst_n)
   begin
      if (rst_n='0') then
         number_of_tests_passed <= (others => '0');
         number_of_tests_failed <= (others => '0');
         fsm2 <= fsm2_wait_for_data_check;
      elsif rising_edge(pll_clk) then
         case fsm2 is
            when fsm2_wait_for_data_check =>
               if (cmd_rdy='1') and (fsm1=fsm1_write) and (end_of_test_addr='1') then
                  fsm2 <= fsm2_check_readback_data;
               end if;
            when fsm2_check_readback_data =>
               if (wait_count(5)='1') then
                  -- test_data will be changed whenever (wait_count == 7'h20), therefore,
                  -- if no error found when wait_count[5] goes high (i.e. wait_count >= 7'h20),
                  -- then increase correct counter, go back to fsm2_wait_for_data_check
                  -- and wait for next test
                  number_of_tests_passed <= number_of_tests_passed + 1;
                  fsm2 <= fsm2_wait_for_data_check;
               elsif (datacheck(1)='1') or (datacheck(0)='1') then
                  -- If there is any error (even just 1 error), increase error counter
                  -- and go to fsm2_wait_for_data_check for next test
                  number_of_tests_failed <= number_of_tests_failed + 1;
                  fsm2 <= fsm2_wait_for_data_check;
               end if;
         end case;
      end if;
   end process FSM2_FSM;

   fsm2_out <= '1' when (fsm2=fsm2_check_readback_data) else '0';

end ddr_test_a;
