----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 07/23/2019 03:52:40 PM
-- Design Name: 
-- Module Name: axis_generator - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity axis_checker is
   port (
      CLK                  : in  std_logic;
      RESET                : in  std_logic;
      S_AXIS_TVALID        : in  std_logic;
      S_AXIS_TREADY        : out std_logic;
      S_AXIS_TLAST         : in  std_logic;
      S_AXIS_TKEEP         : in  std_logic_vector(63 downto 0);
      S_AXIS_TDATA         : in  std_logic_vector(511 downto 0);
      RECV_CNT             : out std_logic_vector(63 downto 0);
      ERR_CNT              : out std_logic_vector(63 downto 0);
      ERROR                : out std_logic
   );
end;

architecture Behavioral of axis_checker is

   -- Find first zero in 64bit vector
   function tkeep_mask_data (data : std_logic_vector; tkeep : std_logic_vector)
      return std_logic_vector is
      variable masked      : std_logic_vector(data'range) := (others => '0');
      variable mask        : std_logic_vector(7 downto 0) := (others => '0'); 
      begin
         for i in 0 to tkeep'length-1 loop
               mask := (others => tkeep(i));
               masked((i+1)*8 - 1 downto i*8) :=  data((i+1)*8 - 1 downto i*8) and mask;
         end loop;
         return masked;
      end tkeep_mask_data;

   signal error_cnt        : std_logic_vector(63 downto 0) := (others => '0');
   signal rcv_cnt          : std_logic_vector(63 downto 0) := (others => '0');
   signal prev_data        : std_logic_vector(S_AXIS_TDATA'range) := (others => '0');
   signal prev_tkeep       : std_logic_vector(S_AXIS_TKEEP'range) := (others => '0');
   
   constant LC_RCV_CNT_ZEROS  : std_logic_vector(rcv_cnt'range) := (others => '0');
   constant LC_RCV_CNT_MAX    : std_logic_vector(error_cnt'range) := (others => '1');
   constant LC_ERROR_CNT_MAX  : std_logic_vector(63 downto 0) := (others => '1');

begin


   process(CLK)
      variable prev_data_masked  : std_logic_vector(prev_data'range) := (others => '0');
      variable act_data_masked   : std_logic_vector(prev_data'range) := (others => '0');
      begin
         prev_data_masked := tkeep_mask_data(prev_data, prev_tkeep);
         act_data_masked := tkeep_mask_data(S_AXIS_TDATA, prev_tkeep);
         if rising_edge(CLK) then
            if RESET = '1' then
               error_cnt <= (others => '0');
               rcv_cnt <= (others => '0');
               ERROR <= '0';
            else
               if S_AXIS_TVALID = '1' then
                  -- save actual values of tkeep and tdata
                  prev_tkeep <= S_AXIS_TKEEP;
                  prev_data <= S_AXIS_TDATA;
                  -- update number of cycles with valid data
                  if rcv_cnt < LC_RCV_CNT_MAX then
                     rcv_cnt <= rcv_cnt + 1;
                  else
                     rcv_cnt <= LC_RCV_CNT_ZEROS;
                  end if;
                  -- update number of cycles with valid and incorrect data
                  if prev_data_masked + 1 /= act_data_masked and rcv_cnt /= LC_RCV_CNT_ZEROS then
                     if error_cnt < LC_ERROR_CNT_MAX then
                        error_cnt <= error_cnt + 1;
                     else
                        error_cnt <= (others => '0');
                     end if;
                     ERROR <= '1';
                  else
                     ERROR <= '0';
                  end if;
               end if;
            end if;
         end if;
      end process;


   S_AXIS_TREADY <= '1';
   RECV_CNT <= rcv_cnt;
   ERR_CNT <= error_cnt;
   
end Behavioral;
