---------------------------------------------------------------------------
-- File           : ea_control_port.vhd
-- Author(s)      : Nikola Avramovic
-- Created        : March 16th, 2019
---------------------------------------------------------------------------
-- Description    : Control modul for port generator
---------------------------------------------------------------------------
--
-- Commands for this module:- SET_FRAG
--                          - PRIORITY_INPUT
--                          - NO_OF_FRAGMENT
--                          - PACKET_CRC_EN
--                          - FRAME_WAIT
--                          - PREAMBLE_LENGTH
--                          - SFD
--                          - TERMINATED
---------------------------------------------------------------------------

library ieee; 
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
   use work.p_parameters_and_constants.all;
   use work.p_generator_interface.all;
   use work.p_port_gen_interface.all;

entity ea_control_port is 
   generic(
      G_PORT_GEN_NO    : natural
      );
   port (clk           : in std_logic;
         reset         : in std_logic;
         -- to/from input command module
         cmd_decoderi  : in  TYPE_CMD_DECODER_TO_PORT_GEN;
         cmd_decodero  : out TYPE_PORT_GEN_TO_CMD_DECODER;
         -- to/from fragmentation multiplexer
         input_muxi     : in  TYPE_INPUT_MUX_TO_CONTROL_PORT;
         input_muxo     : out TYPE_CONTROL_PORT_TO_INPUT_MUX;
         -- to ethernet output
         eth_outputi    : in  TYPE_ETH_OUTPUT_TO_CONTROL_PORT;
         eth_outputo    : out TYPE_CONTROL_PORT_TO_ETH_OUTPUT;
         -- to stauts object
         statuso        : out TYPE_PORT_GEN_TO_STATUS
   );
end ea_control_port;


architecture rtl of ea_control_port is  
 
   type TYPE_STATE is (IDLE, INIT, FRAME_WAIT, PRIORITY_SEND, FRAG_SEND);
   
   type TYPE_REGS is record   
      state                : TYPE_STATE;   
      input_mux_sel        : TYPE_INPUT_MUX_SELECT;
      busy                 : boolean;
      ready                : boolean;
      sop_p_wait_counter   : natural;
      sop_f_wait_counter   : natural;
      init_port            : natural;
      set_frag             : boolean;
      priority_input       : natural;
      priority_sending     : boolean;
      priority_waiting     : boolean;
      fragment_sending     : boolean;
      fragment_waiting     : boolean;
      no_of_fragment       : natural;
      packet_crc_en        : boolean;
      frame_wait_priority  : natural;
      send_priority_frame  : boolean;
      send_fragment_frame  : boolean;
      bridge_error         : boolean;
      output_error         : boolean;
      frame_wait_frag      : natural;
      preamble_length      : natural;
      terminated           : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      sfd                  : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_i                : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
      smd_c                : std_logic_vector(C_BYTE_LENGTH-1 downto 0);
   end record;
   
   constant CONST_REGS_RESET : TYPE_REGS := (
      state                => INIT,
      busy                 => false,
      ready                => false,
      input_mux_sel        => NONE,
      sop_p_wait_counter   => 0,
      sop_f_wait_counter   => 0,
      init_port            => 0,
      set_frag             => true,
      priority_input       => 0,
      priority_sending     => false,
      priority_waiting     => false,
      fragment_sending     => false,
      fragment_waiting     => false,
      no_of_fragment       => 0,
      packet_crc_en        => true, 
      frame_wait_priority  => 0,
      send_priority_frame  => false,
      send_fragment_frame  => false,
      bridge_error         => false,
      output_error         => false,
      frame_wait_frag      => 0,
      preamble_length      => 7,
      terminated           => X"FD",
      sfd                  => X"AB",
      smd_i                => X"E7",
      smd_c                => X"E6"
   ); 
   
   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;
         
         case r.state is
            when INIT =>
               s.busy                    := false;
               s.ready                   := false;

               if cmd_decoderi.init_port = G_PORT_GEN_NO then
                  s.state                := IDLE;
                  s.set_frag             := cmd_decoderi.set_frag;
                  s.priority_input       := cmd_decoderi.priority_input;
                  s.no_of_fragment       := cmd_decoderi.no_of_fragment;
                  s.packet_crc_en        := cmd_decoderi.packet_crc_en;
                  s.frame_wait_priority  := cmd_decoderi.frame_wait_priority;
                  s.frame_wait_frag      := cmd_decoderi.frame_wait_frag;
                  s.preamble_length      := cmd_decoderi.preamble_length;
                  s.sfd                  := cmd_decoderi.sfd;
                  s.smd_i                := cmd_decoderi.smd_i;
                  s.smd_c                := cmd_decoderi.smd_c;
                  s.terminated           := cmd_decoderi.terminated;

               end if;

            when IDLE =>
               s.busy                    := false;
               s.ready                   := false;
               s.input_mux_sel           := NONE;

               if cmd_decoderi.start_sending then
                  s.state                := FRAME_WAIT;
                  s.busy                 := true;
                  s.priority_sending     := false;
                  s.ready                := true;

               end if;

            when FRAME_WAIT =>
               -- do not send 
               s.input_mux_sel           := NONE;
               s.ready                   := true;
               s.send_priority_frame     := false;
               s.send_fragment_frame     := false;

               -- if sop sets on both inputs, "priority" input has priority
               if input_muxi.sop_priority and input_muxi.sop_fragment then
                  s.ready                := false;

                  -- priority pattern waiting for frame_wait_priority value
                  if r.priority_waiting then
                     s.priority_sending  := false;

                  else
                     s.priority_sending  := true;

                  end if;

                  if r.fragment_waiting then
                     s.fragment_sending  := false;

                  else
                     s.fragment_sending  := true;

                  end if;

               end if;

               if not input_muxi.priority_end_of_trans then
                  -- wait before send prority frame according to the command
                  if input_muxi.sop_priority and not r.fragment_sending then
                     if r.sop_p_wait_counter < r.frame_wait_priority then
                        s.sop_p_wait_counter:= r.sop_p_wait_counter + 1;
                        s.priority_waiting  := true;
                        s.ready             := false;

                     else
                        s.state             := PRIORITY_SEND;
                        s.input_mux_sel     := SET_PRIORITY;
                        s.priority_waiting  := false;
                        s.sop_p_wait_counter:= 0;
                        s.send_fragment_frame := false;
                        s.send_priority_frame := true;

                     end if;
                  end if;
               else
                  s.priority_sending     := false;

               end if;

               if not input_muxi.fragment_end_of_trans then
                  -- wait before send first frag according to the command
                  if input_muxi.sop_fragment and not r.priority_sending then
                     if r.sop_f_wait_counter < r.frame_wait_frag then
                        s.sop_f_wait_counter:= r.sop_f_wait_counter + 1;
                        s.fragment_waiting  := true;
                        s.ready             := false;

                     else
                        s.state               := FRAG_SEND;
                        s.input_mux_sel       := SET_FRAG;
                        s.fragment_waiting    := false;
                        s.sop_f_wait_counter  := 0;
                        s.send_fragment_frame := true;
                        s.send_priority_frame := false;

                     end if;
                  end if;
               else
                  s.fragment_sending     := false;

               end if;

               -- sent all frames wait for next commands
               if input_muxi.fragment_end_of_trans and input_muxi.priority_end_of_trans then
                  s.state                := INIT;
                  s.ready                := false;

               end if;

            when PRIORITY_SEND =>
               s.input_mux_sel           := SET_PRIORITY;
               s.ready                   := false;

               if eth_outputi.end_of_packet then
                  s.priority_sending     := false;
                  s.state                := FRAME_WAIT;
                  s.input_mux_sel        := NONE;
                  s.send_priority_frame  := false;
                  s.ready                := true;

               end if;

            when FRAG_SEND =>
               if r.set_frag then
                  s.input_mux_sel           := SET_FRAG;
                  s.priority_sending        := false; 
                  s.ready                   := false;

                  if eth_outputi.end_of_packet then
                     s.state                := FRAME_WAIT;
                     s.input_mux_sel        := NONE;
                     s.send_fragment_frame  := false;
                     s.ready                := true;

                  end if;
               else
                  s.state                   := FRAME_WAIT;
                  s.input_mux_sel           := NONE;
                  s.send_fragment_frame     := false;
                  s.ready                   := true;

               end if;
         end case;

         -- Assign the register record -------------------------------------------
         r                               <= s;

      end if;  
   end process;

   cmd_decodero <= (busy                 => r.busy);

   input_muxo   <= (input_mux_sel        => r.input_mux_sel,
                    priority_input       => r.priority_input,
                    ready                => r.ready,
                    send_priority_frame  => r.send_priority_frame,
                    send_fragment_frame  => r.send_fragment_frame);
   
   eth_outputo  <= (ready                => r.ready,
                    input_mux_sel        => r.input_mux_sel,
                    send_priority_frame  => r.send_priority_frame,
                    send_fragment_frame  => r.send_fragment_frame,
                    preamble_length      => r.preamble_length,
                    terminated           => r.terminated,
                    no_of_fragment       => r.no_of_fragment,
                    packet_crc_en        => r.packet_crc_en,
                    sfd                  => r.sfd,
                    smd_i                => r.smd_i,
                    smd_c                => r.smd_c);

   statuso      <= (bridge_error         => r.bridge_error,
                    output_error         => r.output_error);

end architecture rtl;