--
-- Downlink Testbench
--
--
-- Matt Warren  Aug 2016
--
--

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

library downlink;
use downlink.lcb_pkg_globals.all;

entity lcbtst_tester is
  port(
    cmdfrm_ready_o : out std_logic;
    cmd_gen_busy_i : in  std_logic;
    cmdfrm_o       : out std_logic_vector(48 downto 0);
    dut_hccid_o    : out std_logic_vector(4 downto 0);
    dut_abcid_o    : out std_logic_vector(3 downto 0);


    encoder_hccid_o : out std_logic_vector(4 downto 0);

    enc_frame_sync_i : in  std_logic_vector(1 downto 0);
    rb_busy_o        : out std_logic;



    ser_o    : out std_logic;
    l0a_o    : out std_logic;
    bcr_o    : out std_logic;
    tag_o    : out std_logic_vector(6 downto 0);
    clk160_o : out std_logic;
    clk40_o  : out std_logic;
    rstb_o   : out std_logic;
    rst_o    : out std_logic
    );

-- Declarations

end entity lcbtst_tester;

--
architecture sim of lcbtst_tester is

  constant POST_CLK_DELAY : time := 50 ps;

  signal clk    : std_logic := '0';
  signal clk160 : std_logic := '1';
  signal rst    : std_logic;


  signal ser : std_logic;
  signal l0a : std_logic;
  signal bcr : std_logic;
  signal tag : std_logic_vector(6 downto 0);

  signal dbg_l0a_map : std_logic_vector(3 downto 0);
  signal dbg_tag     : std_logic_vector(6 downto 0);
  signal dbg_l0a_sr  : std_logic_vector(3 downto 0);
  signal dbg_frame   : std_logic_vector(15 downto 0);
  signal dbg_data6b0 : std_logic_vector(5 downto 0);
  signal dbg_data6b1 : std_logic_vector(5 downto 0);
  signal dbg_n       : integer;
  signal dbg_x       : integer;
  signal dbg_y       : integer;
  signal dbg_cmdword : std_logic_vector(48 downto 0);


begin

  rst_o  <= rst;
  rstb_o <= not rst;

  clk     <= not(clk) after 12500 ps;
  clk40_o <= clk;

  clk160   <= not(clk160) after 3125 ps;
  clk160_o <= clk160;


  ser_o <= ser after 3125 ps;
  l0a_o <= l0a;
  bcr_o <= bcr;
  tag_o <= tag;


  dbg_l0a_map <= dbg_data6b0(4 downto 1);
  dbg_tag     <= dbg_data6b0(0) & dbg_data6b1;


  --======================================================================
  simulation : process
    --------------------------------------------------
    -- Procedures 
    --------------------------------------------------

    procedure WaitClks (nclks : in integer := 1) is
    begin
      for waitclkloops in 1 to nclks loop
        wait until rising_edge(clk);
        wait for POST_CLK_DELAY;
      end loop;
    end procedure;
    ----------------------------------------------------
    procedure WaitClk is
    begin
      WaitClks(1);
    end procedure;
    ------------------------------------------------------

    procedure WaitClk160 is
    begin
      wait until rising_edge(clk160);
      wait for POST_CLK_DELAY;
    end procedure;

    -----------------------------------------------------------------------

    -----------------------------------------------------------------------
    procedure TagIncrement is
    begin
      if (tag = "1111111") then
        tag <= "0000000";
      else
        tag <= tag + '1';
      end if;
    end TagIncrement;

    ------------------------------------------------------
    procedure SendRawFrame(frame : in std_logic_vector(15 downto 0)) is
    begin

      dbg_frame <= frame;
      for n in 0 to 15 loop
        ser <= frame(15-n);
        WaitClk160;
      end loop;
      dbg_frame <= (others => '0');

    end procedure;
    ----------------------------------------------------



    -----------------------------------------------------------------------
    ----------------------------------------------------
    procedure SendCmdFrame(cmdword : in std_logic_vector(48 downto 0) := '0' & x"000000000000";
                           hccid   : in std_logic_vector(4 downto 0)  := "11111"
                           ) is
      variable data6b0 : std_logic_vector(5 downto 0);
      variable data6b1 : std_logic_vector(5 downto 0);

    begin

      dbg_cmdword <= cmdword;

      --  All built around 7b per symbol needing 8 symbols = 56b commands

      SendRawFrame(MAP_K_8B(2) & MAP_6B_8B(conv_integer('0' & hccid)));

      data6b0(5 downto 1) := "00000";   -- aka this is a command frame

      for n in 0 to 6 loop

        data6b0(0) := cmdword(49-n*7+6);                  -- msb
        data6b1    := cmdword(49-n*7+5 downto 49-n*7+0);  -- lsbs

        dbg_data6b0 <= data6b0;
        dbg_data6b1 <= data6b1;

        SendRawFrame(MAP_6B_8B(conv_integer(data6b0)) &
                     MAP_6B_8B(conv_integer(data6b1)));

      end loop;

      SendRawFrame(MAP_K_8B(3) & SYMB_CMD_END);

      dbg_data6b0 <= (others => '0');
      dbg_data6b1 <= (others => '0');

    end procedure;
    ----------------------------------------------------
    ------------------------------------------------------
    procedure SendIdleFrames(nframes : in integer) is
    begin
      for n in 1 to nframes loop
        SendRawFrame(IDLE_FRAME);
      end loop;
    end procedure;

----------------------------------------------------
    procedure SendL0AFrame(l0a_map : in std_logic_vector(3 downto 0) := "0001";
                           tag     : in integer                      := 16#12#;
                           bcr     :    integer                      := 0) is
      variable data6b0 : std_logic_vector(5 downto 0);
      variable data6b1 : std_logic_vector(5 downto 0);
      variable tag_slv : std_logic_vector(6 downto 0);

    begin

      tag_slv := conv_std_logic_vector(tag, 7);

      -- default
      data6b0(5) := '0';
      if (bcr = 1) then
        data6b0(5) := '1';
      end if;

      data6b0(4 downto 1) := l0a_map;
      data6b0(0)          := tag_slv(6);
      data6b1             := tag_slv(5 downto 0);

      dbg_data6b0 <= data6b0;
      dbg_data6b1 <= data6b1;


      SendRawFrame(MAP_6B_8B(conv_integer(data6b0)) &
                   MAP_6B_8B(conv_integer(data6b1)));

      dbg_data6b0 <= (others => '0');
      dbg_data6b1 <= (others => '0');


    end procedure;
    ----------------------------------------------------

    procedure SendL0APattern(pattern     : in std_logic_vector(15 downto 0) := x"0001";
                             bcr_pattern : in std_logic_vector(15 downto 0) := x"0000") is
    begin

      for a in 0 to 15 loop
        l0a <= pattern(15-a);
        bcr <= bcr_pattern(15-a);
        WaitClk;
        if (pattern(15-a) = '1') then
          TagIncrement;
        end if;
      end loop;

      l0a <= '0';
      bcr <= '0';

    end procedure;
    ----------------------------------------------------



    --===========================================================================
    --===========================================================================
    procedure DecoderTest is
    begin

      -- Align at center of BCO
      WaitClk;
      WaitClk160;
      WaitClk160;

      SendIdleFrames(32);

      --SendRawFrame(x"0000");

      SendIdleFrames(20);

      -- Need to recheck the below since moving to 7*7b
      -- 0081840A18388 =  2  3  4  5  6  7  8 in 7 bits per digit transposed to 8b  
      -- 02450B183470F =  9  a  b  c  d  e  f
      -- 1C7973E9D7B77 =  71 72 73 74 75 76 77



      SendCmdFrame('0' & x"081840A18388", "10001");
      SendIdleFrames(4);

      SendCmdFrame('0' & x"081840A18388", "00001");
      SendIdleFrames(4);

      SendCmdFrame('1' & x"C7973E9D7B77", "10001");
      SendIdleFrames(4);


      for n in 10 to 14 loop
        SendL0AFrame("0010", n);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

      for n in 10 to 14 loop
        SendL0AFrame("1010", n*2);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

      for n in 10 to 14 loop
        SendL0AFrame("0101", n*2);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

      for n in 10 to 14 loop
        SendL0AFrame("0011", n*2);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

      for n in 10 to 14 loop
        SendL0AFrame("0111", n*3);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

      for n in 0 to 4 loop
        SendL0AFrame("1111", n*4);
        for i in 0 to 1 loop
          SendRawFrame(IDLE_FRAME);
        end loop;
      end loop;

    end procedure;



  --============================================================================
  begin

    -- Init ----------------------------
    rst             <= '1';
    ser             <= '0';
    l0a             <= '0';
    bcr             <= '0';
    tag             <= "0000000";
    cmdfrm_ready_o  <= '0';
    cmdfrm_o        <= '0' & x"000000000000";
    encoder_hccid_o <= "10001";
    dut_hccid_o     <= "10001";
    dut_abcid_o     <= "1001";
    rb_busy_o       <= '0';

    WaitClks(10);

    rst <= '0';

    WaitClks(10);

    bcr <= '1';
    WaitClk;
    bcr <= '0';

    WaitClks(10);

    --------------------------------------

    ----Directly driving Decoder
    -- DecoderTest;



    -- Drive Encoder
    ------------------------------------------------------r

    bcr <= '1';
    WaitClk;
    bcr <= '0';

    WaitClks(9);  -- Setting alignment of L0As wrt BCR - makes for clearer debug


    WaitClks(8*20);  -- Enough IDLEs to achieve lock in HCC and ABC


    -- align to frame - so BCR is correct
    wait until enc_frame_sync_i = "11";
    WaitClk;

    SendL0APattern("1110000000000000", "0001000000000000");
    WaitClks(4*4);



    -- Send Commands
    -----------------------------------------------------------

    cmdfrm_ready_o <= '1';
    cmdfrm_o       <= '1' & x"fc9012345678";
    wait until cmd_gen_busy_i = '1';
    cmdfrm_ready_o <= '0';


    WaitClks(40);


    SendL0APattern("1110000000000000");
    WaitClks(4*4);



    encoder_hccid_o <= "00001";
    --wait until cmd_gen_busy_i = '0';
    cmdfrm_ready_o  <= '1';
    cmdfrm_o        <= '1' & x"fc9012345678";
    wait until cmd_gen_busy_i = '1';
    cmdfrm_ready_o  <= '0';

    WaitClks(40);

    SendL0APattern("1110000000000000");
    WaitClks(4*4);


    encoder_hccid_o <= "10001";
    --wait until cmd_gen_busy_i = '0';
    cmdfrm_ready_o  <= '1';
    cmdfrm_o        <= '1' & x"ac9012345678";
    wait until cmd_gen_busy_i = '1';
    cmdfrm_ready_o  <= '0';

    WaitClks(40);


    SendL0APattern("1000100010000000");
    WaitClks(4*4);


    -- overlap with l0A now ..
    encoder_hccid_o <= "10001";
   -- wait until cmd_gen_busy_i = '0';
    cmdfrm_ready_o  <= '1';
    cmdfrm_o        <= '1' & x"9c9012345678";
    wait until cmd_gen_busy_i = '1';
    cmdfrm_ready_o  <= '0';



    -- Send L0As (and BCRs, but this is automatic)
    --------------------------------------------------------------

    -- align to LCB frame
    wait until enc_frame_sync_i = "11";
    WaitClk;

    for n in 0 to 9 loop
      SendL0APattern("1110000000000000");
      WaitClks(4*4);
    end loop;

    for n in 0 to 9 loop
      SendL0APattern("1110000000000000", "0001000000000000");
      WaitClks(4*4);
    end loop;

    for n in 0 to 9 loop
      SendL0APattern("1110110010000000", x"1111");
      WaitClks(4*4);
    end loop;





    WaitClks(1000);

    wait;
  end process;
end architecture sim;