-- altera vhdl_input_version vhdl_2008
-- XXX: QSYS Fix (https://www.intel.com/content/www/us/en/support/programmable/articles/000079458.html)

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

use work.rtps_package.all;

entity test_fpga is
    port (
        -- SYSTEM
        clk         : in std_logic;
        reset       : in std_logic;
        -- INPUT
        empty       : in std_logic;
        read        : out std_logic;
        data_in     : in std_logic_vector(WORD_WIDTH-1 downto 0);
        -- OUTPUT
        full        : in std_logic;
        write       : out std_logic;
        data_out    : out std_logic_vector(WORD_WIDTH-1 downto 0)
    );
end entity;

architecture arch of test_fpga is
    
    --*****TYPE DECLARATION*****
    type STAGE_TYPE is (SRC_ADDR_IN,DEST_ADDR_IN,UDP_PORTS_IN,PACKET_LEN_IN,SRC_ADDR_OUT,DEST_ADDR_OUT,UDP_PORTS_OUT,PACKET_LEN_OUT,WRITE_MAGIC_WORD,WRITE_PACKET);
    
    --*****CONSTANT DECLARATION*****
    constant RES_IPv4_ADDRESS   : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0) := x"C0A8000A"; --192.168.0.10
    constant MAGIC_WORD         : std_logic_vector(WORD_WIDTH-1 downto 0) := x"DEADBEEF";
    
    --*****SIGNAL DECLARATION*****
    signal stage, stage_next : STAGE_TYPE;
    signal src_addr, src_addr_next : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0);
    signal dest_addr, dest_addr_next : std_logic_vector(IPv4_ADDRESS_WIDTH-1 downto 0);
    signal src_port, src_port_next : std_logic_vector(UDP_PORT_WIDTH-1 downto 0);
    signal dest_port, dest_port_next : std_logic_vector(UDP_PORT_WIDTH-1 downto 0);
    signal len, len_next : unsigned(WORD_WIDTH-1 downto 0);
    
begin
    
    main_prc : process(all)
    begin
        -- DEFAULT
        stage_next      <= stage;
        src_addr_next   <= src_addr;
        dest_addr_next  <= dest_addr;
        src_port_next   <= src_port;
        dest_port_next  <= dest_port;
        len_next        <= len;
        read            <= '0';
        write           <= '0';
        data_out        <= (others => '0');
        
        case (stage) is
            when SRC_ADDR_IN =>
                -- Input Guard
                if (empty = '0') then
                    read            <= '1';
                    src_addr_next   <= data_in;
                    
                    stage_next  <= DEST_ADDR_IN;
                end if;
            when DEST_ADDR_IN =>
                -- Input Guard
                if (empty = '0') then
                    read            <= '1';
                    dest_addr_next  <= data_in;
                    
                    stage_next  <= UDP_PORTS_IN;
                end if;
            when UDP_PORTS_IN =>
                -- Input Guard
                if (empty = '0') then
                    read            <= '1';
                    src_port_next   <= data_in(WORD_WIDTH-1 downto WORD_WIDTH-UDP_PORT_WIDTH);
                    dest_port_next  <= data_in(UDP_PORT_WIDTH-1 downto 0);
                    
                    stage_next  <= PACKET_LEN_IN;
                end if;
            when PACKET_LEN_IN =>
                -- Input Guard
                if (empty = '0') then
                    read        <= '1';
                    len_next    <= unsigned(data_in) + 1;
                    
                    stage_next  <= SRC_ADDR_OUT;
                end if;
            when SRC_ADDR_OUT =>
                -- Output Guard
                if (full = '0') then
                    write       <= '1';
                    data_out    <= dest_addr;
                    
                    stage_next  <= DEST_ADDR_OUT;
                end if;
            when DEST_ADDR_OUT =>
                -- Output Guard
                if (full = '0') then
                    write       <= '1';
                    data_out    <= src_addr;
                    
                    stage_next  <= UDP_PORTS_OUT;
                end if;
            when UDP_PORTS_OUT =>
                -- Output Guard
                if (full = '0') then
                    write       <= '1';
                    data_out    <= dest_port & src_port;
                    
                    stage_next  <= PACKET_LEN_OUT;
                end if;
            when PACKET_LEN_OUT =>
                -- Output Guard
                if (full = '0') then
                    write       <= '1';
                    data_out    <= std_logic_vector(len);
                    
                    stage_next  <= WRITE_MAGIC_WORD;
                end if;
            when WRITE_MAGIC_WORD =>
                -- Output Guard
                if (full = '0') then
                    write       <= '1';
                    data_out    <= MAGIC_WORD;
                    len_next    <= len - 1;
                    
                    stage_next  <= WRITE_PACKET;
                end if;
            when WRITE_PACKET =>
                if (len = 0) then
                    stage_next  <= SRC_ADDR_IN;
                else
                    -- I/O Guard
                    if (empty = '0' and full = '0') then
                        -- Passthrough
                        read        <= '1';
                        write       <= '1';
                        data_out    <= data_in;
                        len_next    <= len - 1;
                    end if;
                end if;
            when others =>
                null;
        end case;
    end process;
    
    sync_prc : process(clk)
    begin
        if rising_edge(clk) then
            if (reset = '1') then
                stage       <= SRC_ADDR_IN;
                src_addr    <= IPv4_ADDRESS_INVALID;
                dest_addr   <= IPv4_ADDRESS_INVALID;
                src_port    <= UDP_PORT_INVALID;
                dest_port   <= UDP_PORT_INVALID;
                len         <= (others => '0');
            else
                stage       <= stage_next;
                src_addr    <= src_addr_next;
                dest_addr   <= dest_addr_next;
                src_port    <= src_port_next;
                dest_port   <= dest_port_next;
                len         <= len_next;
            end if;
        end if;
    end process;
    
end architecture;
