library ieee;
--library gold_lib;   --UNCOMMENT if you're using a GOLD model
use ieee.std_logic_1164.all;
--use gold_lib.all;   --UNCOMMENT if you're using a GOLD model

entity tb_controller is
generic (Period : Time := 4 ns);
end tb_controller;

architecture TEST of tb_controller is

  function INT_TO_STD_LOGIC( X: INTEGER; NumBits: INTEGER )
     return STD_LOGIC_VECTOR is
    variable RES : STD_LOGIC_VECTOR(NumBits-1 downto 0);
    variable tmp : INTEGER;
  begin
    tmp := X;
    for i in 0 to NumBits-1 loop
      if (tmp mod 2)=1 then
        res(i) := '1';
      else
        res(i) := '0';
      end if;
      tmp := tmp/2;
    end loop;
    return res;
  end;

  component controller
    PORT(
         CLK : in STD_LOGIC;
         nRST : in STD_LOGIC;
         ENABLE : in STD_LOGIC;
         OPCODE : in STD_LOGIC_VECTOR(3 downto 0);
         DATA : in STD_LOGIC_VECTOR(511 downto 0);
         SR_BUSY : in STD_LOGIC;
         SR_READ : out STD_LOGIC;
         MM0_ENABLE : out STD_LOGIC;
         MM0_OPCODE : out STD_LOGIC_VECTOR(2 downto 0);
         MM0_DATA : out STD_LOGIC_VECTOR(511 downto 0);
         MM0_RETURN : in STD_LOGIC_VECTOR(95 downto 0);
         MM1_ENABLE : out STD_LOGIC;
         MM1_OPCODE : out STD_LOGIC_VECTOR(2 downto 0);
         MM1_DATA : out STD_LOGIC_VECTOR(511 downto 0);
         MM1_RETURN : in STD_LOGIC_VECTOR(95 downto 0);
         MM_BUSY : in STD_LOGIC;
         TP0_ENABLE : out STD_LOGIC;
         TP0_OPCODE : out STD_LOGIC_VECTOR(2 downto 0);
         TP0_DATA : out STD_LOGIC_VECTOR(95 downto 0);
         TP0_RETURN : in STD_LOGIC_VECTOR(23 downto 0);
         TP1_ENABLE : out STD_LOGIC;
         TP1_OPCODE : out STD_LOGIC_VECTOR(2 downto 0);
         TP1_DATA : out STD_LOGIC_VECTOR(95 downto 0);
         TP1_RETURN : in STD_LOGIC_VECTOR(23 downto 0);
         TP_BUSY : in STD_LOGIC;
         LR_ENABLE : out STD_LOGIC;
         LR_OPCODE : out STD_LOGIC_VECTOR(1 downto 0);
         LR_DATA : out STD_LOGIC_VECTOR(47 downto 0);
         LR_BUSY : in STD_LOGIC;
         SB_ENABLE : out STD_LOGIC;
         SB_OPCODE : out STD_LOGIC_VECTOR(2 downto 0);
         SB_DATA : out STD_LOGIC_VECTOR(23 downto 0);
         SB_BUSY : in STD_LOGIC;
         LCD_FLIP : out STD_LOGIC;
         LCD_BUSY : in STD_LOGIC
    );
  end component;

-- Insert signals Declarations here
  signal CLK : STD_LOGIC;
  signal nRST : STD_LOGIC;
  signal ENABLE : STD_LOGIC;
  signal OPCODE : STD_LOGIC_VECTOR(3 downto 0);
  signal DATA : STD_LOGIC_VECTOR(511 downto 0);
  signal SR_BUSY : STD_LOGIC;
  signal SR_READ : STD_LOGIC;
  signal MM0_ENABLE : STD_LOGIC;
  signal MM0_OPCODE : STD_LOGIC_VECTOR(2 downto 0);
  signal MM0_DATA : STD_LOGIC_VECTOR(511 downto 0);
  signal MM0_RETURN : STD_LOGIC_VECTOR(95 downto 0);
  signal MM1_ENABLE : STD_LOGIC;
  signal MM1_OPCODE : STD_LOGIC_VECTOR(2 downto 0);
  signal MM1_DATA : STD_LOGIC_VECTOR(511 downto 0);
  signal MM1_RETURN : STD_LOGIC_VECTOR(95 downto 0);
  signal MM_BUSY : STD_LOGIC;
  signal TP0_ENABLE : STD_LOGIC;
  signal TP0_OPCODE : STD_LOGIC_VECTOR(2 downto 0);
  signal TP0_DATA : STD_LOGIC_VECTOR(95 downto 0);
  signal TP0_RETURN : STD_LOGIC_VECTOR(23 downto 0);
  signal TP1_ENABLE : STD_LOGIC;
  signal TP1_OPCODE : STD_LOGIC_VECTOR(2 downto 0);
  signal TP1_DATA : STD_LOGIC_VECTOR(95 downto 0);
  signal TP1_RETURN : STD_LOGIC_VECTOR(23 downto 0);
  signal TP_BUSY : STD_LOGIC;
  signal LR_ENABLE : STD_LOGIC;
  signal LR_OPCODE : STD_LOGIC_VECTOR(1 downto 0);
  signal LR_DATA : STD_LOGIC_VECTOR(47 downto 0);
  signal LR_BUSY : STD_LOGIC;
  signal SB_ENABLE : STD_LOGIC;
  signal SB_OPCODE : STD_LOGIC_VECTOR(2 downto 0);
  signal SB_DATA : STD_LOGIC_VECTOR(23 downto 0);
  signal SB_BUSY : STD_LOGIC;
  signal LCD_FLIP : STD_LOGIC;
  signal LCD_BUSY : STD_LOGIC;

-- signal <name> : <type>;

begin

  CLKGEN: process
    variable CLK_tmp: std_logic := '0';
  begin
    CLK_tmp := not CLK_tmp;
    CLK <= CLK_tmp;
    wait for Period/2;
  end process;

  DUT: controller port map(
                CLK => CLK,
                nRST => nRST,
                ENABLE => ENABLE,
                OPCODE => OPCODE,
                DATA => DATA,
                SR_BUSY => SR_BUSY,
                SR_READ => SR_READ,
                MM0_ENABLE => MM0_ENABLE,
                MM0_OPCODE => MM0_OPCODE,
                MM0_DATA => MM0_DATA,
                MM0_RETURN => MM0_RETURN,
                MM1_ENABLE => MM1_ENABLE,
                MM1_OPCODE => MM1_OPCODE,
                MM1_DATA => MM1_DATA,
                MM1_RETURN => MM1_RETURN,
                MM_BUSY => MM_BUSY,
                TP0_ENABLE => TP0_ENABLE,
                TP0_OPCODE => TP0_OPCODE,
                TP0_DATA => TP0_DATA,
                TP0_RETURN => TP0_RETURN,
                TP1_ENABLE => TP1_ENABLE,
                TP1_OPCODE => TP1_OPCODE,
                TP1_DATA => TP1_DATA,
                TP1_RETURN => TP1_RETURN,
                TP_BUSY => TP_BUSY,
                LR_ENABLE => LR_ENABLE,
                LR_OPCODE => LR_OPCODE,
                LR_DATA => LR_DATA,
                LR_BUSY => LR_BUSY,
                SB_ENABLE => SB_ENABLE,
                SB_OPCODE => SB_OPCODE,
                SB_DATA => SB_DATA,
                SB_BUSY => SB_BUSY,
                LCD_FLIP => LCD_FLIP,
                LCD_BUSY => LCD_BUSY
                );

--   GOLD: <GOLD_NAME> port map(<put mappings here>);

process

  begin

-- Insert TEST BENCH Code Here

    nRST <= '0';
    ENABLE <= '0';
    OPCODE <= "0000";
    DATA <= x"000F0000000100000002000000030000000400000005000000060000000700000008000000090000000A0000000B0000000C0000000D0000000E0000000F0000";
    SR_BUSY <= '0';
    MM_BUSY <= '0';
    TP_BUSY <= '0';
    LR_BUSY <= '0';
    SB_BUSY <= '0';
    LCD_BUSY <= '0';
    MM0_RETURN <= x"000000000000000000000000";
    MM1_RETURN <= x"000000000000000000000000";
    TP0_RETURN <= x"000000";
    TP1_RETURN <= x"000000";

    wait for 8 ns;
    
    nRST <= '1';

    wait for 8 ns;
    
    ENABLE <= '1';
    SR_BUSY <= '0';
    OPCODE <= "0000";

    -- running a manual test of Data as it will really be shifted in, to 
    -- show timing constraints of matrix operations

    DATA <= x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFF";

    wait for 4 ns;

    DATA <= x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEE";

    wait for 4 ns;

    DATA <= x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDD";

    wait for 4 ns;

    DATA <= x"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCC";

    wait for 4 ns;

    DATA <= x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBB";

    wait for 4 ns;

    DATA <= x"00000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA";

    wait for 4 ns;

    DATA <= x"000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA99999999";

    wait for 4 ns;

    DATA <= x"0000000000000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA9999999988888888";

    wait for 4 ns;

    DATA <= x"00000000000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA999999998888888877777777";

    wait for 4 ns;

    DATA <= x"000000000000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA99999999888888887777777766666666";

    wait for 4 ns;

    DATA <= x"0000000000000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA9999999988888888777777776666666655555555";

    wait for 4 ns;

    DATA <= x"00000000000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA999999998888888877777777666666665555555544444444";

    wait for 4 ns;

    DATA <= x"000000000000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA99999999888888887777777766666666555555554444444433333333";

    wait for 4 ns;

    DATA <= x"0000000000000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA9999999988888888777777776666666655555555444444443333333322222222";

    wait for 4 ns;

--    DATA <= x"00000000FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA999999998888888877777777666666665555555544444444333333332222222211111111";
    DATA <= x"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";

    wait for 4 ns;

    SR_BUSY <= '1';
    
    wait for 4 ns;

    SR_BUSY <= '0';
    MM_BUSY <= '1';
    
    wait for 9 ns;

    MM_BUSY <= '0';
    OPCODE <= "0001";

    wait for 3 ns;
    
    -- 1100 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    MM_BUSY <= '1';
    
    wait for 4 ns;

    MM_BUSY <= '0';
    OPCODE <= "0010";

    wait for 4 ns;
    
    -- 1250 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    MM_BUSY <= '1';
    
    wait for 4 ns;

    MM_BUSY <= '0';

    OPCODE <= "0011";
    wait for 4 ns;
    
    -- 1400 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    TP_BUSY <= '1';
    
    wait for 4 ns;

    TP_BUSY <= '0';
    OPCODE <= "0100";

    wait for 4 ns;
    
    -- 1550 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    TP_BUSY <= '1';
    
    wait for 4 ns;

    TP_BUSY <= '0';
    OPCODE <= "0101";

    wait for 4 ns;

    -- 1700 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    MM_BUSY <= '1';
    
    wait for 4 ns;

    MM_BUSY <= '0';
    TP_BUSY <= '1';
    
    wait for 4 ns;
    TP_BUSY <= '0';
    SB_BUSY <= '1';
    
    --no particular reason for 100 ns, just showing that it will wait until
    -- the signals are lowered accordingly
    wait for 100 ns;
    SB_BUSY <= '0';
    OPCODE <= "0110";

    wait for 4 ns;
    --2000 ns

    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    MM_BUSY <= '1';
    
    wait for 4 ns;

    MM_BUSY <= '0';
    TP_BUSY <= '1';
    
    wait for 4 ns;
    TP_BUSY <= '0';
    SB_BUSY <= '1';

    wait for 4 ns;
    SB_BUSY <= '0';
    OPCODE <= "0111";

    wait for 4 ns;
    --2250 ns

    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    SB_BUSY <= '1';
    
    wait for 4 ns;
    SB_BUSY <= '0';
    OPCODE <= "1000";
    
    wait for 4 ns;
    --2400 ns
    
    wait for 4 ns;

    SR_BUSY <= '1';
    
    wait for 8 ns;
    SR_BUSY <= '0';
    SB_BUSY <= '1';
    
    -- in theory, this will take a LONG time...
    wait for 36 ns;

    SB_BUSY <='0';
    OPCODE <= "1001";
    
    wait for 4 ns;
    --3000 ns

    wait for 4 ns;

    SR_BUSY <= '1';
    
    wait for 4 ns;
    SR_BUSY <= '0';
    LCD_BUSY <= '1';
    
    wait for 4 ns;

    LCD_BUSY <='0';
    OPCODE <= "1010";
    
    wait for 4 ns;
    --3150 ns

    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    SB_BUSY <= '1';
    
    wait for 4 ns;

    SB_BUSY <= '0';
    
    OPCODE <= "1011";
    wait for 4 ns;
    --3300 ns

    wait for 4 ns;

    SR_BUSY <= '1';

    wait for 4 ns;
    SR_BUSY <= '0';
    LR_BUSY <= '1';
    
    wait for 40 ns;

    LR_BUSY <= '0';
    
    wait for 4 ns;
    --3900 ns
    wait for 5 ns;

  end process;
end TEST;