--------------------------------------------------------------------------------
-- File       : axi_pat_check.v
-- Author     : Xilinx Inc.
-- Project    : Xilinx LogiCORE Virtex-6 Embedded Tri-Mode Ethernet MAC
-- File       : axi_pat_check.vhd
-- Version    : 2.2
-------------------------------------------------------------------------------
--
-- (c) Copyright 2010 Xilinx, Inc. All rights reserved.
--
-- This file contains confidential and proprietary information
-- of Xilinx, Inc. and is protected under U.S. and
-- international copyright and other intellectual property
-- laws.
--
-- DISCLAIMER
-- This disclaimer is not a license and does not grant any
-- rights to the materials distributed herewith. Except as
-- otherwise provided in a valid license issued to you by
-- Xilinx, and to the maximum extent permitted by applicable
-- law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND
-- WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES
-- AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING
-- BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-
-- INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and
-- (2) Xilinx shall not be liable (whether in contract or tort,
-- including negligence, or under any other theory of
-- liability) for any loss or damage of any kind or nature
-- related to, arising under or in connection with these
-- materials, including for any direct, or any indirect,
-- special, incidental, or consequential loss or damage
-- (including loss of data, profits, goodwill, or any type of
-- loss or damage suffered as a result of any action brought
-- by a third party) even if such damage or loss was
-- reasonably foreseeable or Xilinx had been advised of the
-- possibility of the same.
--
-- CRITICAL APPLICATIONS
-- Xilinx products are not designed or intended to be fail-
-- safe, or for use in any application requiring fail-safe
-- performance, such as life-support or safety devices or
-- systems, Class III medical devices, nuclear facilities,
-- applications related to the deployment of airbags, or any
-- other applications that could lead to death, personal
-- injury, or severe property or environmental damage
-- (individually and collectively, "Critical
-- Applications"). Customer assumes the sole risk and
-- liability of any use of Xilinx products in Critical
-- Applications, subject only to applicable laws and
-- regulations governing limitations on product liability.
--
-- THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS
-- PART OF THIS FILE AT ALL TIMES.
--
-- Description:  A simple pattern checker - expects the same data pattern as generated by the pat_gen
-- with the same DA/SA order (it is expected that the frames will pass through
-- two address swap blocks).  the checker will first sync to the data and then
-- identify any errors (this is a sticky output but the internal signal is
-- per byte)
--
--------------------------------------------------------------------------------

library unisim;
use unisim.vcomponents.all;

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


entity axi_pat_check is
   generic (
      DEST_ADDR            : bit_vector(47 downto 0) := X"da0102030405";
      SRC_ADDR             : bit_vector(47 downto 0) := X"5a0102030405";
      MAX_SIZE             : unsigned(11 downto 0) := X"1f4";
      MIN_SIZE             : unsigned(11 downto 0) := X"040"
   );
   port (
      axi_tclk             : in  std_logic;
      axi_tresetn          : in  std_logic;

      enable_pat_chk       : in  std_logic;

      tdata                : in  std_logic_vector(7 downto 0);
      tvalid               : in  std_logic;
      tlast                : in  std_logic;
      tready               : in  std_logic;

      frame_error          : out std_logic
   );
end axi_pat_check;

architecture rtl of axi_pat_check is

   -- State machine
   type state_typ is    (IDLE,
                         INFO,
                         LOOK,
                         PKT);


   signal errored_data        : std_logic;
   signal lut_data            : std_logic_vector(7 downto 0);
   signal expected_data       : unsigned(7 downto 0);
   signal packet_size         : unsigned(15 downto 0);
   signal packet_count        : unsigned(4 downto 0);
   signal next_rx_state       : state_typ;
   signal rx_state            : state_typ;
   constant dummy             : bit_vector(51 downto 0) := (others => '0');

begin

-- the pattern checker is a slave in all respects so has to look for ackowledged data
-- the first and 6 bytes of data are compared against the first byte of DEST_ADDR and SCR_ADDR
-- to allow for address swaps being removed

-- a simple state machine will keep track of the packet position
next_s : process(rx_state, tlast, tvalid, tready, enable_pat_chk, errored_data)
begin
   next_rx_state <= rx_state;
   case rx_state is
      -- cannot simply look for a rise on valid have to see a last first so
      -- we know the next asserted valid is new data
      when IDLE =>
         if tlast = '1' and tvalid = '1' and tready = '1' and enable_pat_chk = '1' then
            next_rx_state <= INFO;
         end if;
      -- since we don't know where the packet gen will be rx a frame to enable
      -- packet size to be initialised
      when INFO =>
         if tlast = '1' and tvalid = '1' and tready = '1' then
            next_rx_state <= LOOK;
         end if;
      -- have seen a last so now look for a start
      when LOOK =>
         if tlast = '0' and tvalid = '1' and tready = '1' then
            next_rx_state <= PKT;
         end if;
      -- rxd first byte of packet - now stay in this state until we see a last
      when PKT =>
         if enable_pat_chk = '0' or errored_data = '1' then
            next_rx_state <= IDLE;
         elsif tlast = '1' and tvalid = '1' and tready = '1' then
            next_rx_state <= LOOK;
         end if;
   end case;
end process;

state_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      if axi_tresetn = '0' then
         rx_state <= IDLE;
      else
         rx_state <= next_rx_state;
      end if;
   end if;
end process state_p;

-- now need a counter for packet size and packet position AND data
pkt_count_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      if axi_tresetn = '0' then
         packet_count <= (others => '0');
      else
         if (next_rx_state = PKT or rx_state = PKT or rx_state = INFO) and tvalid = '1' and
             tready = '1' and packet_count(4) = '0' then
            packet_count <= packet_count + "00001";
         elsif next_rx_state = LOOK or rx_state = IDLE then
            packet_count <= (others => '0');
         end if;
      end if;
   end if;
end process pkt_count_p;

-- need to get packet size info
-- this is first initialised during the info state (the assumption being that
-- the generate sends incrementing packet sizes (wrapping at MAX_SIZE)
pkt_size_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      if rx_state = INFO and packet_count = "01100" and tvalid = '1' and tready = '1' then
         packet_size(15 downto 8) <= unsigned(tdata);
      elsif rx_state = INFO and packet_count = "01101" and tvalid = '1' and tready = '1' then
         packet_size(7 downto 0) <= unsigned(tdata);
      -- when in the LOOK state we want to update the packet size to the next expected
      elsif rx_state /= LOOK and next_rx_state = LOOK then
         if packet_size(15 downto 12) = X"0" and packet_size(11 downto 0) = MAX_SIZE - X"012" then
            packet_size(11 downto 0) <= MIN_SIZE - X"012";
         else
            packet_size <= packet_size + X"0001";
         end if;
      end if;
   end if;
end process pkt_size_p;

exp_data_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      if rx_state = LOOK and next_rx_state = PKT then
         expected_data <= packet_size(7 downto 0);
      elsif rx_state = PKT and packet_count >= "01110" and tvalid = '1' and tready = '1' then
         expected_data <= expected_data - X"01";
      end if;
   end if;
end process exp_data_p;

-- store the parametised values in a lut (64 deep)
-- this should mean the values could be adjusted in fpga_editor etc..
LUT6_gen : for I in 0 to 7 generate
begin
   LUT6_inst : LUT6
   generic map (
      INIT           => dummy &
                        SRC_ADDR(I) & SRC_ADDR(I+8) & SRC_ADDR(I+16) &
                        SRC_ADDR(I+24) & SRC_ADDR(I+32) & SRC_ADDR(I+40) &
                        DEST_ADDR(I) & DEST_ADDR(I+8) & DEST_ADDR(I+16) &
                        DEST_ADDR(I+24) & DEST_ADDR(I+32) & DEST_ADDR(I+40)

   )
   port map (
     O    => lut_data(I),
     I0   => packet_count(0),
     I1   => packet_count(1),
     I2   => packet_count(2),
     I3   => packet_count(3),
     I4   => '0',
     I5   => '0'
   );
end generate;

-- finally the check states - errored_data is only high on the cycle after a mismatch
chk_data_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      errored_data <= '0';
      if packet_count < "01100" and tvalid = '1' and tready = '1' then
         if lut_data /= tdata then
            errored_data <= '1';
         end if;
      elsif packet_count = "01100" and tvalid = '1' and tready = '1' then
         if packet_size(15 downto 8) /= unsigned(tdata) then
            errored_data <= '1';
         end if;
      elsif packet_count = "01101" and tvalid = '1' and tready = '1' then
         if packet_size(7 downto 0) /= unsigned(tdata) then
            errored_data <= '1';
         end if;
      elsif packet_count >= "01110" and tvalid = '1' and tready = '1' and rx_state = PKT then
         if expected_data /= unsigned(tdata) then
            errored_data <= '1';
         end if;
      end if;
   end if;
end process chk_data_p;

-- capture the error and hold until reset
error_p : process (axi_tclk)
begin
   if axi_tclk'event and axi_tclk = '1' then
      if axi_tresetn = '0' then
         frame_error <= '0';
      elsif rx_state /= IDLE and rx_state /= INFO and errored_data = '1' then
         frame_error <= '1';
      end if;
   end if;
end process error_p;

end rtl;
