-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:           Patrick Lehmann
--
-- Entity:           TODO
--
-- Description:
-- 
-- .. TODO:: No documentation available.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany
--                     Chair of VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--    http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- =============================================================================

library IEEE;
use      IEEE.STD_LOGIC_1164.all;
use      IEEE.NUMERIC_STD.all;

library PoC;
use      PoC.config.all;
use      PoC.utils.all;
use      PoC.vectors.all;
use      PoC.net.all;


entity [docs]arp_BroadCast_Requester is
  generic (
    ALLOWED_PROTOCOL_IPV4         : boolean                        := TRUE;
    ALLOWED_PROTOCOL_IPV6         : boolean                        := FALSE
  );
  port (
    Clock                         : in  std_logic;                                  --
    Reset                         : in  std_logic;                                  --

    SendRequest                   : in  std_logic;
    Complete                      : out std_logic;

    Address_rst                   : out std_logic;
    SenderMACAddress_nxt          : out std_logic;
    SenderMACAddress_Data         : in  T_SLV_8;
    SenderIPv4Address_nxt         : out std_logic;
    SenderIPv4Address_Data        : in  T_SLV_8;
    TargetMACAddress_nxt          : out std_logic;
    TargetMACAddress_Data         : in  T_SLV_8;
    TargetIPv4Address_nxt         : out std_logic;
    TargetIPv4Address_Data        : in  T_SLV_8;

    TX_Valid                      : out std_logic;
    TX_Data                       : out T_SLV_8;
    TX_SOF                        : out std_logic;
    TX_EOF                        : out std_logic;
    TX_Ack                        : in  std_logic;
    TX_Meta_DestMACAddress_rst    : in  std_logic;
    TX_Meta_DestMACAddress_nxt    : in  std_logic;
    TX_Meta_DestMACAddress_Data   : out T_SLV_8
  );
end entity;


architecture [docs]rtl of arp_BroadCast_Requester is
  attribute FSM_ENCODING            : string;

  type T_STATE    is (
    ST_IDLE,
      ST_SEND_HARDWARE_type_0,  ST_SEND_HARDWARE_type_1,
      ST_SEND_PROTOCOL_type_0,  ST_SEND_PROTOCOL_type_1,
      ST_SEND_HARDWARE_ADDRESS_LENGTH, ST_SEND_PROTOCOL_ADDRESS_LENGTH,
      ST_SEND_OPERATION_0,      ST_SEND_OPERATION_1,
      ST_SEND_SENDER_MAC,        ST_SEND_SENDER_IP,
      ST_SEND_TARGET_MAC,        ST_SEND_TARGET_IP,
    ST_COMPLETE
  );

  signal State                          : T_STATE                                                        := ST_IDLE;
  signal NextState                      : T_STATE;
  attribute FSM_ENCODING of State        : signal is "gray";

  constant HARDWARE_ADDRESS_LENGTH      : positive                                                      := 6;      -- MAC -> 6 bytes
  constant PROTOCOL_IPV4_ADDRESS_LENGTH  : positive                                                      := 4;      -- IPv4 -> 4 bytes
  constant PROTOCOL_IPV6_ADDRESS_LENGTH  : positive                                                      := 16;    -- IPv6 -> 16 bytes
  constant PROTOCOL_ADDRESS_LENGTH      : positive                                                      := ite((ALLOWED_PROTOCOL_IPV6 = FALSE), PROTOCOL_IPV4_ADDRESS_LENGTH, PROTOCOL_IPV6_ADDRESS_LENGTH);    -- IPv4 -> 4 bytes; IPv6 -> 16 bytes

  signal IsIPv4_l                        : std_logic                                                      := '1';
  signal IsIPv6_l                        : std_logic                                                      := '0';

  constant READER_COUNTER_BITS          : positive                                                      := log2ceilnz(imax(HARDWARE_ADDRESS_LENGTH, PROTOCOL_ADDRESS_LENGTH));
  signal Reader_Counter_rst              : std_logic;
  signal Reader_Counter_en              : std_logic;
  signal Reader_Counter_us              : unsigned(READER_COUNTER_BITS - 1 downto 0)                    := (others => '0');

begin
  IsIPv4_l    <= '1';
  IsIPv6_l    <= '0';

  process(Clock)
  begin
    if rising_edge(Clock) then
      if (Reset = '1') then
        State      <= ST_IDLE;
      else
        State      <= NextState;
      end if;
    end if;
  end process;

  [docs]process(State,
          SendRequest,
          IsIPv4_l, IsIPv6_l,
          TX_Ack, TX_Meta_DestMACAddress_rst, TX_Meta_DestMACAddress_nxt,
          SenderMACAddress_Data, SenderIPv4Address_Data, TargetMACAddress_Data, TargetIPv4Address_Data,
          Reader_Counter_us)
  begin
    NextState                      <= State;

    Complete                      <= '0';

    TX_Valid                      <= '0';
    TX_Data                        <= (others => '0');
    TX_SOF                        <= '0';
    TX_EOF                        <= '0';
    TX_Meta_DestMACAddress_Data    <= x"FF";

    Address_rst                    <= '0';
    SenderMACAddress_nxt          <= '0';
    SenderIPv4Address_nxt          <= '0';
    TargetMACAddress_nxt          <= '0';
    TargetIPv4Address_nxt          <= '0';

    Reader_Counter_rst            <= '0';
    Reader_Counter_en              <= '0';

    case State is
      when ST_IDLE =>
        if (SendRequest = '1') then
          Address_rst              <= '1';
          NextState                <= ST_SEND_HARDWARE_type_0;
        end if;

      when ST_SEND_HARDWARE_type_0 =>
        TX_Valid                  <= '1';
        TX_Data                    <= x"00";
        TX_SOF                    <= '1';

--        Address_rst                <= TX_Meta_DestMACAddress_rst;
--        TargetMACAddress_nxt      <= TX_Meta_DestMACAddress_nxt;

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_HARDWARE_type_1;
        end if;

      when ST_SEND_HARDWARE_type_1 =>
        TX_Valid                  <= '1';
        TX_Data                    <= x"01";

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_PROTOCOL_type_0;
        end if;

      when ST_SEND_PROTOCOL_type_0 =>
        TX_Valid                  <= '1';

        if (IsIPv4_l = '1') then
          TX_Data                  <= x"08";
        elsif (IsIPv6_l = '1') then
          TX_Data                  <= x"86";
        end if;

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_PROTOCOL_type_1;
        end if;

      when ST_SEND_PROTOCOL_type_1 =>
        TX_Valid                  <= '1';

        if (IsIPv4_l = '1') then
          TX_Data                  <= x"00";
        elsif (IsIPv6_l = '1') then
          TX_Data                  <= x"DD";
        end if;

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_HARDWARE_ADDRESS_LENGTH;
        end if;

      when ST_SEND_HARDWARE_ADDRESS_LENGTH =>
        TX_Valid                  <= '1';
        TX_Data                    <= x"06";

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_PROTOCOL_ADDRESS_LENGTH;
        end if;

      when ST_SEND_PROTOCOL_ADDRESS_LENGTH =>
        TX_Valid                  <= '1';

        if (IsIPv4_l = '1') then
          TX_Data                  <= x"04";
        elsif (IsIPv6_l = '1') then
          TX_Data                  <= x"10";
        end if;

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_OPERATION_0;
        end if;

      when ST_SEND_OPERATION_0 =>
        TX_Valid                  <= '1';
        TX_Data                    <= x"00";

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_OPERATION_1;
        end if;

      when ST_SEND_OPERATION_1 =>
        TX_Valid                  <= '1';
        TX_Data                    <= x"01";

        Address_rst                <= '1';

        if (TX_Ack   = '1') then
          NextState                <= ST_SEND_SENDER_MAC;
        end if;

      when ST_SEND_SENDER_MAC =>
        TX_Valid                  <= '1';
        TX_Data                    <= SenderMACAddress_Data;

        if (TX_Ack   = '1') then
          SenderMACAddress_nxt    <= '1';
          Reader_Counter_en        <= '1';

          if (Reader_Counter_us = (HARDWARE_ADDRESS_LENGTH - 1)) then
            Reader_Counter_rst    <= '1';
            NextState              <= ST_SEND_SENDER_IP;
          end if;
        end if;

      when ST_SEND_SENDER_IP =>
        TX_Valid                  <= '1';
        TX_Data                    <= SenderIPv4Address_Data;

        if (TX_Ack   = '1') then
          SenderIPv4Address_nxt    <= '1';
          Reader_Counter_en        <= '1';

          if ((IsIPv4_l = '1') and (Reader_Counter_us = (PROTOCOL_IPV4_ADDRESS_LENGTH - 1))) then
            Reader_Counter_rst    <= '1';
            NextState              <= ST_SEND_TARGET_MAC;
          elsif ((IsIPv6_l = '1') and (Reader_Counter_us = (PROTOCOL_IPV6_ADDRESS_LENGTH - 1))) then
            Reader_Counter_rst    <= '1';
            NextState              <= ST_SEND_TARGET_MAC;
          end if;
        end if;

      when ST_SEND_TARGET_MAC =>
        TX_Valid                  <= '1';
        TX_Data                    <= TargetMACAddress_Data;

        if (TX_Ack   = '1') then
          TargetMACAddress_nxt    <= '1';
          Reader_Counter_en        <= '1';

          if (Reader_Counter_us = (HARDWARE_ADDRESS_LENGTH - 1)) then
            Reader_Counter_rst    <= '1';
            NextState              <= ST_SEND_TARGET_IP;
          end if;
        end if;

      when ST_SEND_TARGET_IP =>
        TX_Valid                  <= '1';
        TX_Data                    <= TargetIPv4Address_Data;

        if (TX_Ack   = '1') then
          TargetIPv4Address_nxt  <= '1';
          Reader_Counter_en        <= '1';

          if ((IsIPv4_l = '1') and (Reader_Counter_us = (PROTOCOL_IPV4_ADDRESS_LENGTH - 1))) then
            TX_EOF                <= '1';
            Reader_Counter_rst    <= '1';
            NextState              <= ST_COMPLETE;
          elsif ((IsIPv6_l = '1') and (Reader_Counter_us = (PROTOCOL_IPV6_ADDRESS_LENGTH - 1))) then
            TX_EOF                <= '1';
            Reader_Counter_rst    <= '1';
            NextState              <= ST_COMPLETE;
          end if;
        end if;

      when ST_COMPLETE =>
        Complete                  <= '1';
        NextState                  <= ST_IDLE;

    end case;
  end process;


  process(Clock)
  begin
    if rising_edge(Clock) then
      if ((Reset or Reader_Counter_rst) = '1') then
        Reader_Counter_us          <= (others => '0');
      elsif (Reader_Counter_en = '1') then
        Reader_Counter_us          <= Reader_Counter_us + 1;
      end if;
    end if;
  end process;
end architecture;