library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity PADDING_BLOCK is
    port(
        CLK         : in std_logic;
        RESET       : in std_logic;
        DATA_IN     : in std_logic_vector(63 downto 0);
        DATA_VLD    : in std_logic;
        LAST_BLOCK  : in std_logic;
        BYTES_VLD   : in std_logic_vector(3 downto 0);
        BUFFER_FULL : out std_logic;
        PAD_DATA    : out std_logic_vector(575 downto 0);
        PAD_VLD     : out std_logic
    );
 end PADDING_BLOCK;

architecture FULL of PADDING_BLOCK is

    signal pad_data_tmp : std_logic_vector(63 downto 0);

    signal out_tmp : std_logic_vector(575 downto 0);

    signal calculate_pad : std_logic;

    signal start    : std_logic;
    signal data_tmp : std_logic_vector(63 downto 0);

    signal count_buf : std_logic_vector(3 downto 0);
    signal pad_vld_s : std_logic;
    signal buff_full : std_logic;

    signal all_vld   : std_logic;

begin

    -- assign input
    data_tmp <= DATA_IN;

    -- buffer full
    BUFFER_FULL <= buff_full;

    -- pad is valid 
    PAD_VLD <= pad_vld_s;
    
    -- calculate padding if data are valid or we have to  pad with nulls and padding is not done or we have to start padding
    calculate_pad <= (DATA_VLD or (start and (not(pad_vld_s))));
    
    count_buf_p : process(CLK)
    begin
        if (rising_edge(CLK)) then
            if (RESET = '1') then
                count_buf <= (others => '0');
                pad_vld_s <= '0';
                buff_full <= '0';
            elsif (calculate_pad = '1') then
                if (count_buf = "1111") then
                    count_buf <= (others => '0');
                else
                    if (count_buf = "1000") then
                        pad_vld_s <= '1';
                    elsif (count_buf = "0111") then
                        buff_full <= '1';
                    end if;
                    count_buf <= std_logic_vector(unsigned(count_buf) + 1);
                end if;
            end if;
        end if;
    end process;    

    -- start padding if data are valid or stop padding if reset is 1 or iterations are done
    start_p : process(CLK)
    begin
        if(rising_edge(CLK)) then
            if(RESET = '1') then
                start <= '0';
            else
                if(DATA_VLD = '1') then
                    start <= '1';
                end if;
            end if;
        end if;
    end process;

    was_all_p : process(CLK)
    begin
        if(rising_edge(CLK)) then
            if(RESET = '1') then
                all_vld <= '0';
            elsif(LAST_BLOCK = '1' and BYTES_VLD = "1000") then
                all_vld <= '1';
            else
                all_vld <= '0';
            end if;
        end if;
    end process;

    -- mux for padding data
    with BYTES_VLD select
        pad_data_tmp <= x"0" & '0' & ('0' or all_vld) & ('0' or all_vld) & '0' & x"000000000000" & ('0' or buff_full) & "0000000" when "0000",
                        (DATA_IN(63 downto 56) & x"060000000000" & ('0' or buff_full) & "0000000") when "0001",
                        (DATA_IN(63 downto 48) & x"0600000000" & ('0' or buff_full) & "0000000") when "0010",
                        (DATA_IN(63 downto 40) & x"06000000" & ('0' or buff_full) & "0000000") when "0011",
                        (DATA_IN(63 downto 32) & x"060000" & ('0' or buff_full) & "0000000") when "0100",
                        (DATA_IN(63 downto 24) & x"0600" & ('0' or buff_full) & "0000000") when "0101",
                        (DATA_IN(63 downto 16) & x"06" & ('0' or buff_full) & "0000000") when "0110",
                        (DATA_IN(63 downto 8) & ('0' or buff_full) & "0000110") when "0111",
                        DATA_IN when "1000",
                        (others => '0') when others;

    -- reset output signal out_tmp if reset is 1 or assign data if calculate padding
    out_tmp_p : process(CLK)
    begin
        if(rising_edge(CLK)) then
            if (calculate_pad = '1') then
                out_tmp <= out_tmp(575-64 downto 0) & pad_data_tmp;
            elsif (RESET = '1') then
                out_tmp <= (others => '0'); 
            end if;
        end if;
    end process;

    -- assign reordered padded data bytes
    pad_reorder_g1 : for w in 0 to 8 generate
        par_reorder_g2 : for b in 0 to 7 generate 
            PAD_DATA((w*64+b*8)+7 downto (w*64+b*8)) <= out_tmp((w*64+(7-b)*8)+7 downto (w*64+(7-b)*8));
        end generate;
    end generate;

end FULL;
