-------------------------------------------------------------------------------
--
-- Project name: JESD204b Receiver core 
--
-- (C) Copyright 2017 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Marek Kvas (m.kvas@dfcdesign.cz)
--
-------------------------------------------------------------------------------
--
-- Testbench used to find out how xilinx core behaves.
--
--
-------------------------------------------------------------------------------


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.tb_print_pkg.all;
use work.axi_lite_sim_pkg.all;


entity tb_xilinx_jesd204 is
   generic (
      g_F            : integer := 2;   
      g_K            : integer := 32;
      g_lane_count   : integer := 1;

      g_stat_cnt_length    : integer := 16;

      g_axi_timeout        : integer := 10;
      C_S_AXI_DATA_WIDTH   : integer := ALSPKG_C_S_AXI_DATA_WIDTH;
      C_S_AXI_ADDR_WIDTH   : integer := ALSPKG_C_S_AXI_ADDR_WIDTH
           );
end entity;



architecture simulation of tb_xilinx_jesd204 is

   constant LC_K                 : std_logic_vector(11 downto 0) := x"1BC";
   constant LC_R                 : std_logic_vector(11 downto 0) := x"11C";
   constant LC_Q                 : std_logic_vector(11 downto 0) := x"19C";
   constant LC_A                 : std_logic_vector(11 downto 0) := x"17C";
   constant LC_F                 : std_logic_vector(11 downto 0) := x"1FC";

   signal axi_m2s_0     : axi_lite_m2s_type;
   signal axi_s2m_0     : axi_lite_s2m_type;
   signal axi_m2s_1     : axi_lite_m2s_type;
   signal axi_s2m_1     : axi_lite_s2m_type;
   signal read_data_0   : std_logic_vector(31 downto 0);

   signal gt0_rxdata             : std_logic_vector(31 DOWNTO 0);
   signal gt0_rxcharisk          : std_logic_vector(3 DOWNTO 0);
   signal gt0_rxdisperr          : std_logic_vector(3 DOWNTO 0);
   signal gt0_rxnotintable       : std_logic_vector(3 DOWNTO 0);
   signal gt1_rxdata             : std_logic_vector(31 DOWNTO 0);
   signal gt1_rxcharisk          : std_logic_vector(3 DOWNTO 0);
   signal gt1_rxdisperr          : std_logic_vector(3 DOWNTO 0);
   signal gt1_rxnotintable       : std_logic_vector(3 DOWNTO 0);
   signal rx_reset_done          : std_logic := '0';
   signal rxencommaalign_out     : std_logic;
   signal rx_reset_gt            : std_logic;
   signal rx_core_clk            : std_logic;
   signal s_axi_aclk             : std_logic;
   signal s_axi_aresetn          : std_logic;
   signal rx_reset               : std_logic;
   signal rx_aresetn             : std_logic;
   signal rx_tdata               : std_logic_vector(63 DOWNTO 0);
   signal rx_tvalid              : std_logic := '0';
   signal rx_start_of_frame      : std_logic_vector(3 DOWNTO 0);
   signal rx_end_of_frame        : std_logic_vector(3 DOWNTO 0);
   signal rx_start_of_multiframe : std_logic_vector(3 DOWNTO 0);
   signal rx_end_of_multiframe   : std_logic_vector(3 DOWNTO 0);
   signal rx_frame_error         : std_logic_vector(7 DOWNTO 0);
   signal rx_sysref              : std_logic;
   signal rx_sync                : std_logic;

   signal gt0_txdata             : STD_LOGIC_VECTOR(31 DOWNTO 0);
   signal gt0_txcharisk          : std_logic_vector(3 DOWNTO 0);
   signal gt1_txdata             : std_logic_vector(31 DOWNTO 0);
   signal gt1_txcharisk          : std_logic_vector(3 DOWNTO 0);
   signal tx_reset_done          : std_logic;
   signal gt_prbssel_out         : std_logic_vector(2 DOWNTO 0);
   signal tx_reset_gt            : std_logic;
   signal tx_core_clk            : std_logic;
   signal tx_reset               : std_logic;
   signal tx_sysref              : std_logic;
   signal tx_start_of_frame      : std_logic_vector(3 DOWNTO 0);
   signal tx_start_of_multiframe : std_logic_vector(3 DOWNTO 0);
   signal tx_aresetn             : std_logic;
   signal tx_tdata               : std_logic_vector(63 DOWNTO 0);
   signal tx_tready              : std_logic;
   signal tx_sync                : std_logic;

   signal clk                    : std_logic := '0'; 
   signal sclk                   : std_logic := '0'; 

   signal gtx_data               : std_logic_vector(31 downto 0);
   signal gtx_chisk              : std_logic_vector(3 downto 0);
   signal gtx_disperr            : std_logic_vector(3 downto 0);
   signal gtx_notintbl           : std_logic_vector(3 downto 0);

   signal dbg_mark               : std_logic := '0';
   signal dbg_multiframe         : std_logic := '0';

   signal dut_ctl_reinit            : std_logic;
   signal dut_ctl_cgs_done          : std_logic_vector(g_lane_count-1 downto 0);
   signal dut_ctl_reinit_req        : std_logic_vector(g_lane_count-1 downto 0);
   signal dut_ctl_err_report_req    : std_logic_vector(g_lane_count-1 downto 0);
   signal dut_ctl_ifa_done          : std_logic_vector(g_lane_count-1 downto 0);
   signal dut_ctl_release_data      : std_logic;
   signal dut_ctl_data_ready        : std_logic_vector(g_lane_count-1 downto 0);
   signal dut_phy_data              : std_logic_vector(31 downto 0);
   signal dut_phy_charisk           : std_logic_vector(3 downto 0);
   signal dut_phy_disperr           : std_logic_vector(3 downto 0);
   signal dut_phy_notintable        : std_logic_vector(3 downto 0);
   signal dut_phy_comma_align_en    : std_logic;
   signal dut_out_m_axis_tvalid     : std_logic;
   signal dut_out_m_axis_tdata      : std_logic_vector(31 downto 0);
   signal dut_out_sof               : std_logic_vector(3 downto 0);
   signal dut_out_eof               : std_logic_vector(3 downto 0);
   signal dut_out_somf              : std_logic_vector(3 downto 0);
   signal dut_out_eomf              : std_logic_vector(3 downto 0);
   signal dut_out_byte_err          : std_logic_vector(3 downto 0);
   signal dut_cfg_scrambling_en     : std_logic;
   signal dut_cfg_sysref_edge       : std_logic;
   signal dut_cfg_sysref_en         : std_logic;
   signal dut_cfg_f                 : std_logic_vector(7 downto 0);
   signal dut_cfg_k                 : std_logic_vector(4 downto 0);
   signal dut_cfg_fxk               : std_logic_vector(10 downto 0);
   signal dut_mon_phys_err_cnt      : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);
   signal dut_mon_phys_err_res_cnt  : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);
   signal dut_mon_align_f_err_cnt   : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);
   signal dut_mon_align_mf_err_cnt  : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);
   signal dut_mon_replace_f_cnt     : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);
   signal dut_mon_replace_a_cnt     : std_logic_vector(
                                          g_stat_cnt_length -1 downto 0);

   signal dut_sync                  : std_logic;
   
   -- LMFC dut
   constant LC_SUBCLASS_0           : std_logic_vector(1 downto 0) := "00";
   constant LC_SUBCLASS_1           : std_logic_vector(1 downto 0) := "01";
   constant LC_SUBCLASS_2           : std_logic_vector(1 downto 0) := "10";

   signal dut_ctl_ilas_err          : std_logic;
   signal dut_cfg_subclass          : std_logic_vector(1 downto 0) := "01";
   signal dut_cfg_sysref_delay      : std_logic_vector(3 downto 0) 
                                                         := (others => '0');
   signal dut_cfg_sysref_allresync  : std_logic;
   signal dut_cfg_sysref_polarity   : std_logic;
   signal dut_cfg_reinit_en         : std_logic := '0';
   signal dut_cfg_err_report_en     : std_logic;
   signal dut_stat_sysref_seen      : std_logic;
   signal dut_stat_sync_ok          : std_logic;
   signal dut_stat_err_lmfc_sync    : std_logic;
   signal dut_stat_err_lmfc_diff    : std_logic_vector(dut_cfg_fxk'range);
   signal dut_lmfc_out              : std_logic;
   signal dut_lmfc_prec_out         : std_logic_vector(3 downto 0);



   
   type gtx_out_type is record
      data     : std_logic_vector(7 downto 0);
      chisk    : std_logic;
      disperr  : std_logic;
      notintbl : std_logic;
   end record;

   type gtx_out_array_type is array (natural range <>) of gtx_out_type;

   type data_ctx_type is record
      value : integer;
      idx : integer;
      pos : integer;
   end record;

   type ila_data_ctx_type is record
      value : integer;
   end record;

   type err_type_type is (ERR_NONE, ERR_LANE_ALIGN);

   type error_injection_ctx_type is record
      err_type       : err_type_type;
      skip_frame     : integer;
      skip_frame_cnt : integer;
   end record;

   type frame_ctx_type is record
      F                    : integer; -- num of octets per frame
      scrambled            : boolean;

      last_in_prev_frame   : gtx_out_type;
      last_in_prev_frame_replaced : boolean;

      data_ctx             : data_ctx_type;
      ila_data_ctx         : ila_data_ctx_type;
   end record;

   type multiframe_ctx_type is record
      K                    : integer; -- num of frames per multiframes

      frame_ctx            : frame_ctx_type;

      err_injection_ctx    : error_injection_ctx_type;
   end record;   

   -- Return coplete gtx_out_type from symbol value s
   -- If s has 8 bits or 9th bit is '0', data symbol
   -- is created. If 9th bit is '1' K symbol is generated
   function symbol(s : std_logic_vector;
                   disperrnotintbl : std_logic_vector) return gtx_out_type is
      variable o : gtx_out_type;
   begin
      o.data := s(7 downto 0);
      if s'length > 8 then
         o.chisk := s(8);
      else
         o.chisk := '0';
      end if;
      o.disperr := disperrnotintbl(1);
      o.notintbl := disperrnotintbl(0);
      return o;
   end function;

   procedure gtx( signal clk : in std_logic;
                signal gtx : out gtx_out_type;
                constant s : in std_logic_vector(11 downto 0);
                constant disperrnotintbl : in std_logic_vector(1 downto 0)) is
   begin
      wait until rising_edge(clk);
      gtx <= symbol(s, disperrnotintbl);
   end procedure;


   -- Generate content of the frame
   -- Here first octet contains sequence number of the frame
   -- the rest is zeroed.
   -- If F is higher than 1, it causes generating of both
   -- frame and lane alignment characters (replacement)
   -- i is position of octed in the frame
   procedure gen_frame_data(variable ctx : inout data_ctx_type;
                            constant i : in integer;
                            variable octet : out std_logic_vector(7 downto 0)) is
      variable n_value : integer;
      variable idx_v : integer;
   begin
      if true then
         idx_v := ctx.idx mod 32;
         if ctx.pos = idx_v then
            n_value := 128;
         elsif ctx.pos + g_F = idx_v then
            n_value := 128;
         else
            n_value := ctx.idx mod 32;
            if n_value = 31 then
               ctx.pos := (ctx.pos + 1) mod 4;
            end if;
         end if;
         ctx.idx := ctx.idx + 1;
         octet := std_logic_vector(to_unsigned(n_value, 8));
      end if;

      if false then
         if i = 0 then
            n_value := ctx.value + 1;
            ctx.value := n_value;
         else
            n_value := 0; -- This causes generation of /A/ in all multiframes except
                          -- for the first one oafter ILAS 
            --n_value := ctx.value mod 2;
         end if;
         octet := std_logic_vector(to_unsigned(n_value/3, 8));
      end if;
   end procedure;

   -- Generate content of the ILA frames
   -- i is position of octet in the frame
   procedure gen_ila_frame_data(variable ctx : inout ila_data_ctx_type;
                            constant i : in integer;
                            variable octet : out std_logic_vector(7 downto 0)) is
      variable n_value : integer;
   begin
--      if i = 0 then
         n_value := ctx.value + 1;
         ctx.value := n_value;
--      else
--         n_value := 0;
--      end if;
      octet := std_logic_vector(to_unsigned(n_value, 8));
   end procedure;

   -- Generates whole frame to signal o
   -- content of frames is given by gen_frame_data function.
   -- This function performs character replacement for both
   -- frame and lane alignment.
   -- If no lane synchronization is used, just keep limf zero.
   -- ila parameter says whether ILA is being generated. It needs special frames
   -- If ila = 000  standard frame is generated.
   -- if ila(0) = 1  frame starts alway with /R/
   -- if ila(1) = 1  frame's second or second symbol based on F symbol is /Q/
   -- il ila(2) = 1  part of ila, use ila data and 
   --                if limf = 1 frame ends always with /A/
   procedure gen_frame(signal clk : in std_logic;
                       variable ctx : inout frame_ctx_type;
                       constant limf : in std_logic;
                       constant ila  : in std_logic_vector(2 downto 0);
                       signal o   : out gtx_out_type) is
      variable data_octed : std_logic_vector(7 downto 0);
      variable n_symb : gtx_out_type;
      variable d : boolean := false; -- determines whether to print
   begin
      cprint(d,"Entering gen_frame");
      
--      assert ctx.F > 0 anf_alignmentd ctx.F < 256 report "F must be 1 to 256" 
--         severity error;
      for i in 0 to ctx.F - 1 loop
         o.data <= x"ee";--(others => '0');
         o.chisk <= '0';
         o.disperr <= '0';
         o.notintbl <= '0';

         cprint(d,"Byte " & integer'image(i) & " of " & integer'image(ctx.F));
         if i = 0 and ila(0) = '1' then
            -- start of each ILA multiframe
            n_symb := symbol(LC_R, "00");
            cprint(d,"ILA mf_alignmentultiframe start /R/");
         elsif (ctx.F = 1 or i = 1) and ila(1) = '1' then
            -- Start of second ILA multiframe it must be the second symbol
            -- so if F = 1 it must be start of second frame.
            n_symb := symbol(LC_Q, "00");
            cprint(d,"ILA config start /Q/");
         else
            if ila(2) = '1' then               
               gen_ila_frame_data(ctx.ila_data_ctx, i, data_octed);
               cprint(d,"Fetched ILA data i= " & integer'image(i) &
                     " received= " & hstr(data_octed));
            else
               gen_frame_data(ctx.data_ctx, i, data_octed);
               cprint(d,"Fetched normal frame data i= " & integer'image(i) &
                     " received= " & hstr(data_octed));
            end if;
            n_symb := symbol(data_octed, "00");

            -- Character replacement takes place in the last octet of frame
            if i = ctx.F - 1 then
               if ila(2) = '1' then
                  if limf = '1' then
                     -- last in ILA multiframe is always /A/
                     n_symb := symbol(LC_A, "00");
                     cprint(d,"ILA /A/");
                  end if;
               elsif ctx.scrambled then
                  assert true report "Scrambling not implemented in TB as yet" 
                     severity error;
               else
                  -- replacement takes place only:
                  -- if octet matches the one from the last frame and
                  -- either octet was not replaced in the last frame or
                  -- it is the last frame of multiframe
                  if (not ctx.last_in_prev_frame_replaced  or limf = '1') and
                     ctx.last_in_prev_frame.data = data_octed then
                     if limf = '1' then
                        -- last in frame that is last in multiframe replace
                        -- with /A/ or lane alignment
                        n_symb := symbol(LC_A, "00");
                        cprint(d,"Lane alignment /A/");
                     else
                        n_symb := symbol(LC_F, "00");
                        cprint(d,"Frame alignment /F/");
                     end if;
                     ctx.last_in_prev_frame_replaced := true;
                  else
                     cprint(d,"Data octed insrted (last in frame)");
                     -- Store new value for next frame
                     ctx.last_in_prev_frame := n_symb;
                     cprint(d,"New last octed in prev frame stored " &
                        hstr(n_symb.data));
                     ctx.last_in_prev_frame_replaced := false;
                  end if;
               end if;
            else
               cprint(d,"Data octed insrted (not last in frame)");
            end if;
         end if;
         o <= n_symb;
         cprint(d,"Value output " & hstr(n_symb.data));
         wait until rising_edge(clk);
      end loop; -- loop through F
      cprint(d,"Leaving genf_alignment_frame");
   end procedure; -- gen frame;

   -- Generate multiframe
   -- if ila between 0-3 ILA frames are generated.
   procedure gen_multiframe(signal clk : in std_logic;
                       variable ctx : inout multiframe_ctx_type;
                       constant ila : integer;
                       signal o   : out gtx_out_type) is
      variable limf : std_logic := '0';
      variable f_ila  : std_logic_vector(2 downto 0) := (others => '0');
      variable d : boolean := false; -- determines whether to print
   begin
      for i in 0 to ctx.K - 1 loop
         f_ila := (others => '0');
         if ila < 4 then
            -- Ila is being f_alignment generated
            f_ila(2) := '1';

            if i = 0  then
               -- The first symbol of each ILA multiframe must be /R/
               f_ila(0) := '1';
            end if;
            if ila = 1 and (
               (ctx.frame_ctx.F = 1 and i = 1) or
               (ctx.frame_ctx.F > 1 and i = 0)) then
               -- second symbol of second multiframe must be Q
               f_ila(1) := '1';
            end if;
         end if; -- we are in ILA


         if i = ctx.K - 1 then
            limf := '1';
         end if;
         cprint(d, "Frame " & integer'image(i) & " limf " & std_logic'image(limf));
         if ctx.err_injection_ctx.err_type = ERR_LANE_ALIGN and
            i >= ctx.err_injection_ctx.skip_frame and
            i < ctx.err_injection_ctx.skip_frame +
                ctx.err_injection_ctx.skip_frame_cnt then
                   cprint(d, "Skipping frame for ERR_LANE_ALIGN");
                   null;
         else
            gen_frame(clk, ctx.frame_ctx, limf, f_ila, o);
         end if;
      end loop;
   end procedure;
                       
   
   signal gtxs    : gtx_out_type;
   signal gtxs_d  : gtx_out_type;
   signal gtxs_dl : gtx_out_array_type(1 downto 0); --1 downto 0 is nice


   constant LC_R_REV             : integer := 0;
   constant LC_R_RESET           : integer := 4;
   constant LC_R_SYNC_STAT       : integer := 16#38#;
   constant LC_R_DEBUG_STAT      : integer := 16#3C#;

begin

   assert g_F*g_K >=17 report "Minimal multiframe is 17 octets." severity error;

   sclk <= not sclk after 5 ns/4;
   clk <= not clk after 5 ns;

   rx_core_clk <= clk;
   s_axi_aclk <= clk;

   reset_proc : process
   begin
      s_axi_aresetn <= '0';
      rx_reset <= '1';
      wait for 100 ns;
      wait until rising_edge(clk);
      rx_reset <= '0';
      wait for 100 ns;
      wait until rising_edge(clk);

      s_axi_aresetn <= '1';

      wait;
   end process;

   axi_sim_proc : process
      variable v_data      : std_logic_vector(31 downto 0);
   begin
      init_m2s(axi_m2s_0);
      init_m2s(axi_m2s_1);
      wait until rising_edge(s_axi_aclk) and s_axi_aresetn = '1';
      wait until rising_edge(s_axi_aclk);
      for i in 0 to 20 loop
         wait until rising_edge(s_axi_aclk);
      end loop;

      print("Start of jesd204 core configuartion");
      axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_REV, v_data);
      print("Version register reads: " & hstr(v_data));  

      axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_RESET, v_data);
      print("Reset register reads: " & hstr(v_data));

      -- 0x008: Support ILA
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  2, x"00000001");

      -- 0x00C: Scrambling dissabled
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  3, x"00000000");

      -- 0x010: Sysref once
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  4, x"00000001");

      -- 0x014: Tx Only register

      -- 0x018: Test mode = Normal operation
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  6, x"00000000");

      -- 0x020: Octets per Frame F-1
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  8, 
         std_logic_vector(to_unsigned(g_F - 1, 32)));

      -- 0x024: Frames per Multiframe K-1
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 *  9, 
         std_logic_vector(to_unsigned(g_K - 1, 32)));

      -- 0x028: Lanes in use
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 * 10, x"00000003");

      -- 0x02C: Device subclass 1
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 * 11, x"00000001");

      -- 0x030: Rx buffer delay
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 * 12, x"00000000");

      -- 0x034: Error reporting via ~sync
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, 4 * 13, x"00000000");



      print("Triggering reset");
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_RESET,x"00000001");
      axi_lite_sim_mwr(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_RESET,x"00010001");
      print("Waiting for core to go out of reset...");
      axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_RESET, v_data);
      print("Reset register reads: " & hstr(v_data));
      while v_data(0) = '1' loop
         wait until rising_edge(s_axi_aclk);
         axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_RESET, v_data);
         print("Reset register reads: " & hstr(v_data));
         wait for 20 us;
      end loop;
      print("Core out of reset.");

      loop
         wait until rising_edge(s_axi_aclk);
         axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_SYNC_STAT, v_data);
         print("Sync stat register reads: " & hstr(v_data));
         axi_lite_sim_mrd(s_axi_aclk, axi_m2s_0, axi_s2m_0, LC_R_DEBUG_STAT, v_data);
         print("Debug stat register reads: " & hstr(v_data));
         wait for 10 us;
      end loop;

      wait;
   end process;

   rx_reset_done <= not rx_reset_gt after 100 ns;

   sysreq_gen_proc : process
      variable half_period : integer := g_K*g_F/2/4;
      variable cnt :  integer;
   begin
      cnt := 0;
      rx_sysref <= '0';
      wait for 50 ns;
      wait until rising_edge(clk) and rx_reset_gt = '0';

      loop
         rx_sysref <= '1';
         for i in 0 to half_period -1 loop
            wait until rising_edge(clk);
         end loop;
         rx_sysref <= '0';

         for i in 0 to half_period - 1 loop
            wait until rising_edge(clk);
         end loop;

         -- Insert shift in sysref
         if true then
            if cnt > 200 then
               if (cnt mod 110) = 0 then
                  wait for 10 ns;
               end if;
            end if;
         end if;
         cnt := cnt + 1;
      end loop;

      
      wait;
   end process;


   -- Generate stream to feed jesd core
   stimul_proc : process
      variable multiframe_cnt : integer := 0;
      variable multiframe_ctx   : multiframe_ctx_type;
   begin
      gtx(sclk, gtxs, x"000", "00");
      wait for 50 ns;
      wait until rising_edge(sclk) and rx_reset_gt = '0';
      
      -- Wait for CGS request and serve it
      wait until rising_edge(sclk) and rx_sync ='0';
      while rx_sync = '0' loop
         gtx(sclk, gtxs, LC_K, "00");
      end loop;
      
      multiframe_cnt := 0;

      -- SYNC is deasserted here so generate ILA and then data
      multiframe_ctx.K := g_K;
      
      multiframe_ctx.frame_ctx.F := g_F;
      multiframe_ctx.frame_ctx.scrambled := false;
      multiframe_ctx.frame_ctx.last_in_prev_frame := symbol(LC_K, "00");

      multiframe_ctx.frame_ctx.data_ctx.value := 0;
      multiframe_ctx.frame_ctx.data_ctx.idx := 0;
      multiframe_ctx.frame_ctx.data_ctx.pos := 0;
      
      multiframe_ctx.frame_ctx.ila_data_ctx.value := 0;

      multiframe_ctx.err_injection_ctx.err_type := ERR_NONE;

      -- LANE alignment error insertion
      while false loop
         if multiframe_cnt > 0 and 
           (multiframe_cnt mod 30) = 0 then
            multiframe_ctx.err_injection_ctx.err_type := ERR_LANE_ALIGN;
            multiframe_ctx.err_injection_ctx.skip_frame := 2;
            multiframe_ctx.err_injection_ctx.skip_frame_cnt := 1;
            dbg_mark <= '1';
         else
            multiframe_ctx.err_injection_ctx.err_type := ERR_NONE;
         end if;
         dbg_multiframe <= not dbg_multiframe;
         gen_multiframe(sclk, multiframe_ctx, multiframe_cnt, gtxs); 
         multiframe_cnt := multiframe_cnt + 1;
         dbg_mark <= '0';
      end loop;

      -- Error free operation (Errors may occur on physical layer)
      while true loop
         dbg_multiframe <= not dbg_multiframe;
         gen_multiframe(sclk, multiframe_ctx, multiframe_cnt, gtxs); 
         multiframe_cnt := multiframe_cnt + 1;
         dbg_mark <= '0';
         if rx_sync = '0' then
            exit;
         end if;
      end loop;

   end process;


   gtxs_delay_proc : process
      variable gtxs_v : gtx_out_type;
      variable cnt_v : integer := 0;
      variable err_pos : integer := 7005;--7004;--6278;
      variable err_len : integer := 2;
      variable err_hole_len: integer := 4;
      variable err_hole_pos : integer := 2;
   begin
      loop
         wait until rising_edge(sclk);
         gtxs_v := gtxs;

         if true then
            if  cnt_v >= err_pos and cnt_v < err_pos+err_len+err_hole_len then
               -- Disparity error
               if not (cnt_v>= err_pos+err_hole_pos and 
                       cnt_v < err_pos+err_hole_pos + err_hole_len) then
                  gtxs_v.disperr := '1';
               end if;
            end if;
            cnt_v := cnt_v + 1;
         end if; -- disp error insertion

         gtxs_d <= gtxs_dl(0);
         gtxs_dl <= gtxs_v & gtxs_dl(gtxs_dl'left downto 1);
      end loop;
   end process;

   -- create parallel data from symbol stream
   deser_proc : process
   begin
      loop
         wait until rising_edge(sclk);
         gtx_data <= gtxs_d.data & gtx_data(gtx_data'left downto 8);
         gtx_chisk <= gtxs_d.chisk & 
                      gtx_chisk(gtx_chisk'left downto 1);
         gtx_disperr <= gtxs_d.disperr & 
                      gtx_disperr(gtx_disperr'left downto 1);
         gtx_notintbl <= gtxs_d.notintbl & 
                      gtx_notintbl(gtx_notintbl'left downto 1);
      end loop;
   end process;

   -- provide parralel data to jesd core
   assigngtx_proc : process
   begin
      wait until rising_edge(clk);
      wait for 0.2 ns;

      gt0_rxdata <= gtx_data;
      gt0_rxcharisk <= gtx_chisk;
      gt0_rxdisperr <= gtx_disperr;
      gt0_rxnotintable <= gtx_notintbl;

      gt1_rxdata <= gtx_data;
      gt1_rxcharisk <= gtx_chisk;
      gt1_rxdisperr <= gtx_disperr;
      gt1_rxnotintable <= gtx_notintbl;

   end process;




   jesd204_0_inst : entity work.jesd204_0
   PORT MAP (
      gt0_rxdata              => gt0_rxdata,
      gt0_rxcharisk           => gt0_rxcharisk,
      gt0_rxdisperr           => gt0_rxdisperr,
      gt0_rxnotintable        => gt0_rxnotintable,
      gt1_rxdata              => gt1_rxdata,
      gt1_rxcharisk           => gt1_rxcharisk,
      gt1_rxdisperr           => gt1_rxdisperr,
      gt1_rxnotintable        => gt1_rxnotintable,
      rx_reset_done           => rx_reset_done,
      rxencommaalign_out      => rxencommaalign_out,
      rx_reset_gt             => rx_reset_gt,
      rx_core_clk             => rx_core_clk,
      s_axi_aclk              => s_axi_aclk,
      s_axi_aresetn           => s_axi_aresetn,
      S_AXI_AWADDR            => axi_m2s_0.awaddr,
      S_AXI_AWVALID           => axi_m2s_0.awvalid,
      S_AXI_AWREADY           => axi_s2m_0.awready,
      S_AXI_WDATA             => axi_m2s_0.wdata,
      S_AXI_WSTRB             => axi_m2s_0.wstrb,
      S_AXI_WVALID            => axi_m2s_0.wvalid,
      S_AXI_WREADY            => axi_s2m_0.wready,
      S_AXI_BRESP             => axi_s2m_0.bresp,
      S_AXI_BVALID            => axi_s2m_0.bvalid,
      S_AXI_BREADY            => axi_m2s_0.bready,
      S_AXI_ARADDR            => axi_m2s_0.araddr,
      S_AXI_ARVALID           => axi_m2s_0.arvalid,
      S_AXI_ARREADY           => axi_s2m_0.arready,
      S_AXI_RDATA             => axi_s2m_0.rdata,
      S_AXI_RRESP             => axi_s2m_0.rresp,
      S_AXI_RVALID            => axi_s2m_0.rvalid,
      S_AXI_RREADY            => axi_m2s_0.rready,
      rx_reset                => rx_reset,
      rx_aresetn              => rx_aresetn,
      rx_tdata                => rx_tdata,
      rx_tvalid               => rx_tvalid,
      rx_start_of_frame       => rx_start_of_frame,
      rx_end_of_frame         => rx_end_of_frame,
      rx_start_of_multiframe  => rx_start_of_multiframe,
      rx_end_of_multiframe    => rx_end_of_multiframe,
      rx_frame_error          => rx_frame_error,
      rx_sysref               => rx_sysref,
      rx_sync                 => rx_sync
  );


  jesd204_1_inst : entity work.jesd204_1
  PORT MAP (
      gt0_txdata              => gt0_txdata,
      gt0_txcharisk           => gt0_txcharisk,
      gt1_txdata              => gt1_txdata,
      gt1_txcharisk           => gt1_txcharisk,
      tx_reset_done           => tx_reset_done,
      gt_prbssel_out          => gt_prbssel_out,
      tx_reset_gt             => tx_reset_gt,
      tx_core_clk             => tx_core_clk,
      s_axi_aclk              => s_axi_aclk,
      s_axi_aresetn           => s_axi_aresetn,
      S_AXI_AWADDR            => axi_m2s_1.awaddr,
      S_AXI_AWVALID           => axi_m2s_1.awvalid,
      S_AXI_AWREADY           => axi_s2m_1.awready,
      S_AXI_WDATA             => axi_m2s_1.wdata,
      S_AXI_WSTRB             => axi_m2s_1.wstrb,
      S_AXI_WVALID            => axi_m2s_1.wvalid,
      S_AXI_WREADY            => axi_s2m_1.wready,
      S_AXI_BRESP             => axi_s2m_1.bresp,
      S_AXI_BVALID            => axi_s2m_1.bvalid,
      S_AXI_BREADY            => axi_m2s_1.bready,
      S_AXI_ARADDR            => axi_m2s_1.araddr,
      S_AXI_ARVALID           => axi_m2s_1.arvalid,
      S_AXI_ARREADY           => axi_s2m_1.arready,
      S_AXI_RDATA             => axi_s2m_1.rdata,
      S_AXI_RRESP             => axi_s2m_1.rresp,
      S_AXI_RVALID            => axi_s2m_1.rvalid,
      S_AXI_RREADY            => axi_m2s_1.rready,
      tx_reset                => tx_reset,
      tx_sysref               => tx_sysref,
      tx_start_of_frame       => tx_start_of_frame,
      tx_start_of_multiframe  => tx_start_of_multiframe,
      tx_aresetn              => tx_aresetn,
      tx_tdata                => tx_tdata,
      tx_tready               => tx_tready,
      tx_sync                 => tx_sync
  );
   tx_sysref <= rx_sysref;
   tx_tdata <= x"00112233aabbccdd";
   tx_sync <= rx_sync;
   tx_reset <= rx_reset;
   tx_core_clk <= rx_core_clk;
   tx_reset_done <= not tx_reset_gt after 100 ns;


   --dut_ctl_release_data <= dut_ctl_data_ready after 100 ns;

   jesd204b_rx_lane_inst: entity work.jesd204b_rx_lane
   generic map (
      g_stat_cnt_length    => g_stat_cnt_length,
      g_scrambler_incl     => false,
      g_lmfc_buffer_size   => 1024
           )
   port map (
      CLK                  => rx_core_clk,
      RST                  => not rx_aresetn, --rx_reset,
   
      -- JESD204B control signals
      CTL_REINIT           => dut_ctl_reinit,
      CTL_CGS_DONE         => dut_ctl_cgs_done(0),
      CTL_REINIT_REQ       => dut_ctl_reinit_req(0),
      CTL_ERR_REPORT_REQ   => dut_ctl_err_report_req(0),
      CTL_IFA_DONE         => dut_ctl_ifa_done(0),
      CTL_DATA_READY       => dut_ctl_data_ready(0),
      CTL_RELEASE_DATA     => dut_ctl_release_data,

      -- Connection to PHY (RX transceivers)
      PHY_DATA             => gt0_rxdata,--dut_phy_data,
      PHY_CHARISK          => gt0_rxcharisk,--dut_phy_charisk,
      PHY_DISPERR          => gt0_rxdisperr,--dut_phy_disperr,
      PHY_NOTINTABLE       => gt0_rxnotintable,--dut_phy_notintable,
      PHY_COMMA_ALIGN_EN   => dut_phy_comma_align_en,
      
      -- Output AXI4-Stream interface
      OUT_M_AXIS_TVALID    => dut_out_m_axis_tvalid,
      OUT_M_AXIS_TDATA     => dut_out_m_axis_tdata,
      OUT_SOF              => dut_out_sof,
      OUT_EOF              => dut_out_eof,
      OUT_SOMF             => dut_out_somf,
      OUT_EOMF             => dut_out_eomf,
      OUT_BYTE_ERR         => dut_out_byte_err,
      
      -- Configurations
      CFG_SCRAMBLING_EN    => dut_cfg_scrambling_en,
      CFG_ILAS             => '1',

      -- Both contain K/F -1 value as 0 is meaningless
      -- FxK is helper value - number of bytes in multiframe
      CFG_F                => dut_cfg_f,
      CFG_K                => dut_cfg_k,
      CFG_FxK              => dut_cfg_fxk,

      -- Monitoring - error, statistics
      MON_PHYS_ERR_CNT     => dut_mon_phys_err_cnt,
      MON_PHYS_ERR_RES_CNT => dut_mon_phys_err_res_cnt,
      MON_ALIGN_F_ERR_CNT  => dut_mon_align_f_err_cnt,
      MON_ALIGN_MF_ERR_CNT => dut_mon_align_mf_err_cnt,
      MON_REPLACE_F_CNT    => dut_mon_replace_f_cnt,
      MON_REPLACE_A_CNT    => dut_mon_replace_a_cnt
        );
   
   dut_cfg_f <= std_logic_vector(to_unsigned(g_F-1, dut_cfg_f'length));
   dut_cfg_k <= std_logic_vector(to_unsigned(g_K-1, dut_cfg_k'length));
   dut_cfg_fxk <= std_logic_vector(to_unsigned(g_K*g_F, dut_cfg_fxk'length));

   jesd204b_lmfc_inst : entity work.jesd204b_lmfc
   generic map (
      g_lane_count         => g_lane_count,
      g_stat_cnt_length    => g_stat_cnt_length
           )
   port map (
      CLK                  => rx_core_clk,
      RST                  => not rx_aresetn, --rx_reset,
   
      -- Lane control signals
      CTL_REINIT           => dut_ctl_reinit,
      CTL_CGS_DONE         => dut_ctl_cgs_done,
      CTL_REINIT_REQ       => dut_ctl_reinit_req,
      CTL_ERR_REPORT_REQ   => dut_ctl_err_report_req,
      CTL_IFA_DONE         => dut_ctl_ifa_done,
      CTL_RELEASE_DATA     => dut_ctl_release_data,
      CTL_DATA_READY       => dut_ctl_data_ready,

      -- External device control
      SYNC                 => dut_sync,
      SYSREF               => rx_sysref,

      -- Protocol parameters
      CFG_FxK              => dut_cfg_fxk,
      CFG_F                => dut_cfg_f,
      CFG_SUBCLASS         => dut_cfg_subclass,
      CFG_SYSREF_DELAY     => dut_cfg_sysref_delay,
      CFG_SYSREF_ALLRESYNC => dut_cfg_sysref_allresync,
      CFG_SYSREF_POLARITY  => dut_cfg_sysref_polarity,
      CFG_REINIT_EN        => dut_cfg_reinit_en,
      CFG_ERR_REPORT_EN    => dut_cfg_err_report_en,

      -- Status signals
      STAT_SYSREF_SEEN     => dut_stat_sysref_seen,
      STAT_SYNC_OK         => dut_stat_sync_ok,

      STAT_ERR_LMFC_SYNC   => dut_stat_err_lmfc_sync,
      STAT_ERR_LMFC_DIFF   => dut_stat_err_lmfc_diff,

      -- Debugging signals
      LMFC_OUT             => dut_lmfc_out,
      LMFC_PREC_OUT        => dut_lmfc_prec_out
      
        );



end architecture;
