---------------------------------------------------------------------------
-- File           : ea_header_check.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : April 16th, 2019
---------------------------------------------------------------------------
-- Description    : This module check headers, input data streams, and 
-- packet crc
---------------------------------------------------------------------------
--
-- Commands for this module:- PRIORITY_OUTPUT
--                          - SET_FRAG       
--                          - NO_OF_FARGMENT 
--                          - PACKET_CRC_EN  
--                          - PREAMBLE_LENGTH
--                          - SFD            
--                          - SMD_I          
--                          - SMD_C          
--                          - TERMINATED   
---------------------------------------------------------------------------

library ieee; 
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_parameters_and_constants.all;
   use work.p_functions.all;
   use work.p_interfaces.all;
   use work.p_monitor_interface.all;
   use work.p_port_mon_interface.all;

entity ea_header_check is 
   port (clk           : in  std_logic;
         reset         : in  std_logic;
         -- from control port
         control_porti : in  TYPE_CONTROL_PORT_TO_HEADER_CHECK;
         control_porto : out TYPE_HEADER_CHECK_TO_CONTROL_PORT;
         -- to/from cam
         cami          : in  TYPE_CAM_TO_HEADER_CHECK;
         camo          : out TYPE_HEADER_CHECK_TO_CAM
   );
end ea_header_check;

architecture rtl of ea_header_check is

   -- NICE EXAMPLE: how VHDL is stupid
   -- constant C_BUFFER_CRC       : natural := 4;
   -- type TYPE_CRC_DATA_ARRAY is array (natural range <>) of std_logic_vector(C_BYTE_LENGTH-1 downto 0);
   -- TYPE_CRC_DATA_ARRAY(C_BUFFER_CRC-1 downto 0);
   -- std_logic_vector(C_CRC32_DATA_WIDTH-1 downto 0) = TYPE_CRC_DATA_ARRAY(C_BUFFER_CRC-1 downto 0)

   type TYPE_REGS is record
      frag_no                   : natural;
      frag_const                : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_i_const               : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_c_const               : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_i_count               : natural;
      smd_c_count               : natural;
      preamble_count            : natural;
      preamble_receiving        : boolean;
      check_const               : boolean;
      check_frag_no             : boolean;
      idle_error                : boolean;
      preamble_error            : boolean;
      sfd_error                 : boolean;
      smd_i_error               : boolean;
      smd_c_error               : boolean;
      crc_packet_error          : boolean;
      frag_no_error             : boolean;
      preamble_length_error     : boolean;
      correct_crc_pck_counter   : unsigned(C_BYTE_LENGTH-1 downto 0);
      smd_i_err_count           : unsigned(C_BYTE_LENGTH-1 downto 0);
      smd_c_err_count           : unsigned(C_BYTE_LENGTH-1 downto 0);
      crc_packet_err_count      : unsigned(C_BYTE_LENGTH-1 downto 0);
      frag_no_err_count         : unsigned(C_BYTE_LENGTH-1 downto 0);
      preamble_length_err_count : unsigned(C_BYTE_LENGTH-1 downto 0);

   end record;
   
   constant CONST_REGS_RESET : TYPE_REGS := (
      frag_no                   => 0,
      frag_const                => C_FRAG_NO_0,
      smd_i_const               => C_SMD_S_0,
      smd_c_const               => C_SMD_C_0,
      smd_i_count               => 0,
      smd_c_count               => 0,
      preamble_count            => 0,
      preamble_receiving        => false,
      check_const               => false,
      check_frag_no             => false,
      idle_error                => false,
      preamble_error            => false,
      sfd_error                 => false,
      smd_i_error               => false,
      smd_c_error               => false,
      crc_packet_error          => false,
      frag_no_error             => false,
      preamble_length_error     => false,
      correct_crc_pck_counter   => (others => '0'),
      smd_i_err_count           => (others => '0'),
      smd_c_err_count           => (others => '0'),
      crc_packet_err_count      => (others => '0'),
      frag_no_err_count         => (others => '0'),
      preamble_length_err_count => (others => '0')

   ); 
   
   signal r                    : TYPE_REGS := CONST_REGS_RESET;

begin
   sync: process (reset, clk) 
      variable s               : TYPE_REGS;

   begin

      if reset = '1' then
         r                     <= CONST_REGS_RESET;

      elsif rising_edge(clk) then
         -- initialize the variables ------------------------------------------
         s                     := r;

         -- provide checking, if control ready is set         
         if control_porti.ready then

            ---------------------------------------------------------------------------
            -- check preamble length. Set next smd_s/c
            ---------------------------------------------------------------------------
            -- first fragment has 7 bytes preable, others have 6 bytes preamble 
            if cami.preamble_receiving then
               s.preamble_receiving      := cami.preamble_receiving;
               s.preamble_count          := r.preamble_count + 1;
               s.check_const             := true;

            elsif (not cami.preamble_receiving) and r.check_const then
               s.preamble_count          := 0;
               s.preamble_receiving      := cami.preamble_receiving;
               s.check_const             := false;
               s.preamble_length_error   := false;

               if (r.preamble_count < control_porti.preamble_length or r.preamble_count > control_porti.preamble_length) 
               and (not cami.receiving_fragment) then
                  s.preamble_length_error:= true;

               -- first fragment has 7 bytes preambule
               elsif (r.preamble_count < control_porti.preamble_length or r.preamble_count > control_porti.preamble_length)
               and (cami.receiving_fragment) and (r.frag_no = 0) then
                  s.preamble_length_error:= true;

               -- other fragments have 6 bytes preambule
               elsif (r.preamble_count < control_porti.preamble_length-1 or r.preamble_count > control_porti.preamble_length-1)
               and (cami.receiving_fragment) and (r.frag_no /= 0) then
                  s.preamble_length_error:= true;

               end if;

               if (r.frag_no = control_porti.no_of_fragment-1) and cami.receiving_fragment then
                  s.smd_c_count          := 0;
                  -- smd i will be increment for every next frame (with modulo according to standard)
                  s.smd_i_count          := (r.smd_i_count + 1);
                  s.frag_no              := (r.frag_no + 1) mod control_porti.no_of_fragment;

                  -- according to the standard IEEE802.3Qbr (TSN-br):
                  -- modulo-4 counter containing the fragment count for the next outgoing continuation mPacket
                  s.smd_i_const          := smd_i_const(s.smd_i_count mod C_PREEMPTABLE_NO);

               elsif cami.receiving_fragment then
                  s.frag_no              := (r.frag_no + 1) mod control_porti.no_of_fragment;
                  if r.frag_no = 0 then
                     s.smd_c_count       := 0;

                  else
                     s.smd_c_count       := (r.smd_c_count + 1);

                  end if;

                  -- according to the standard IEEE802.3br (TSN-br):
                  -- modulo-4 counter containing the fragment count for the next outgoing continuation mPacket
                  s.smd_c_const          := smd_c_const(s.smd_c_count mod C_PREEMPTABLE_NO);

               end if;
            end if;

            -- according to the standard IEEE802.3br (TSN-br):
            -- modulo-4 counter containing the fragment count for the next outgoing continuation mPacket
            -- first fragment dose not have frag_no
            s.frag_const                 := frag_no_const(r.frag_no mod C_PREEMPTABLE_NO);

            ---------------------------------------------------------------------------
            -- check crc for whole packet
               ---------------------------------------------------------------------------
            if (cami.crc_mon_data /= cami.crc_received_data) and (not cami.crc32_en) and not cami.receiving_fragment
            and (r.frag_no = control_porti.no_of_fragment-1) then
               s.crc_packet_error        := true;

            else
               s.crc_packet_error        := false;

            end if;

            ---------------------------------------------------------------------------
            -- check constants
            ---------------------------------------------------------------------------
            -- check fragment number, one cycle after preambule end 
            if (r.preamble_receiving and not s.preamble_receiving) and cami.receiving_fragment and (r.frag_no /= 0) then
               s.check_frag_no           := true;

            else
               s.check_frag_no           := false;

            end if;

            if cami.idle /= C_IDLE then
               s.idle_error              := true;

            elsif cami.preamble /= C_PREAMBLE then
               s.preamble_error          := true;

            elsif cami.sfd /= control_porti.sfd then
               s.sfd_error               := true;

            -- check smd_s after faling edge of preamble_receiving
            elsif cami.smd_i /= r.smd_i_const and (r.preamble_receiving and not s.preamble_receiving) 
            and cami.receiving_fragment and (r.frag_no = control_porti.no_of_fragment-1) then
               s.smd_i_error             := true;

            -- check smd_c after faling edge of preamble_receiving
            elsif cami.smd_c /= r.smd_c_const and (r.preamble_receiving and not s.preamble_receiving) 
            and cami.receiving_fragment and (r.frag_no /= control_porti.no_of_fragment-1) then
               s.smd_c_error             := true;

            -- check fragment number
            elsif (cami.frag_const /= r.frag_const) and r.check_frag_no and (r.frag_no = control_porti.no_of_fragment-1) then
               s.check_frag_no           := false;
               s.frag_no_error           := true;

            else
               s.idle_error              := false;
               s.preamble_error          := false;
               s.sfd_error               := false;
               s.smd_i_error             := false;
               s.smd_c_error             := false;
               s.frag_no_error           := false;

            end if;

         end if;

         ---------------------------------------------------------------------------
         -- After reading status counter, clear counters
         ---------------------------------------------------------------------------
         if r.smd_i_error then
            s.smd_i_error                := false;
            s.smd_i_err_count            := r.smd_i_err_count + 1;

         elsif r.smd_c_error then
            s.smd_c_error                := false;
            s.smd_c_err_count            := r.smd_c_err_count + 1;


         elsif r.crc_packet_error then
            s.crc_packet_error           := false;
            s.crc_packet_err_count       := r.crc_packet_err_count + 1;
         


         elsif r.frag_no_error then
            s.frag_no_error              := false;
            s.frag_no_err_count          := r.frag_no_err_count + 1;


            
         elsif r.preamble_length_error then
            s.preamble_length_error      := false;
            s.preamble_length_err_count  := r.preamble_length_err_count + 1;


         elsif cami.receiving_fragment and (r.frag_no = control_porti.no_of_fragment-1)
         and r.check_const  and not s.check_const  then
            s.correct_crc_pck_counter    := r.correct_crc_pck_counter + 1;
    
         end if;

         if control_porti.smd_i_read then
            s.smd_i_err_count         := (others => '0');

         end if;

         if control_porti.smd_c_read then
            s.smd_i_err_count         := (others => '0');

         end if;

         if control_porti.crc_packet_read then
            s.smd_i_err_count         := (others => '0');

         end if;
         if control_porti.frag_no_read then
            s.smd_i_err_count         := (others => '0');

         end if;

         if control_porti.preamble_length_read then
            s.smd_i_err_count         := (others => '0');

         end if;

         -- Assign the register record -------------------------------------------
         r                     <= s;
      end if;  
   end process;   

                                   -- fatal errors
   control_porto               <= (idle_error                => r.idle_error,
                                   preamble_error            => r.preamble_error,
                                   sfd_error                 => r.sfd_error,
                                   -- errors per frame
                                   correct_crc_pck_counter   => r.correct_crc_pck_counter,
                                   smd_i_err_count           => r.smd_i_err_count,
                                   smd_c_err_count           => r.smd_c_err_count,
                                   crc_packet_err_count      => r.crc_packet_err_count,
                                   frag_no_err_count         => r.frag_no_err_count,
                                   preamble_length_err_count => r.preamble_length_err_count);

   camo                        <= (smd_i_const               => r.smd_i_const,
                                   smd_c_const               => r.smd_c_const,
                                   frag_const                => r.frag_const);


end architecture rtl;         