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

library work;
    use work.reg32.all;
    use work.avalon_slave.all;

entity data_channel_control is
    port (
        clk : in std_logic;
        reset : in std_logic;

        address : in std_logic_vector( 3 downto 0 );
        read : in std_logic;
        readdata : out std_logic_vector( 31 downto 0 );
        write : in std_logic;
        writedata : in std_logic_vector( 31 downto 0 );

        sink_config : out std_logic;
        source_config : out std_logic;

        clear : out std_logic;
        empty : in std_logic;
        full : in std_logic;
        level : in std_logic_vector( 9 downto 0 );

        sink_write : out std_logic;
        sink_writedata : out std_logic_vector( 31 downto 0 );

        source_read : out std_logic;
        source_readdata : in std_logic_vector( 31 downto 0 )
    );
end entity data_channel_control;

architecture rtl of data_channel_control is

    type Registers is (
        REG_CONFIG,
        REG_EMPTY,
        REG_FULL,
        REG_LEVEL,
        REG_SINK,
        REG_SOURCE,
        REG_CLEAR
    );

    constant REG_CONFIG_POS : natural := Registers'pos( REG_CONFIG );
    constant REG_EMPTY_POS : natural := Registers'pos( REG_EMPTY );
    constant REG_FULL_POS : natural := Registers'pos( REG_FULL );
    constant REG_LEVEL_POS : natural := Registers'pos( REG_LEVEL );
    constant REG_SINK_POS : natural := Registers'pos( REG_SINK );
    constant REG_SOURCE_POS : natural := Registers'pos( REG_SOURCE );
    constant REG_CLEAR_POS : natural := Registers'pos( REG_CLEAR );

    constant REG_COUNT : natural := registers'pos( registers'right ) + 1;

    constant REG_ACCESS_TYPES : work.reg32.AccessArray( 0 to REG_COUNT - 1 ) := (
        READ_WRITE,
        READ_ONLY,
        READ_ONLY,
        READ_ONLY,
        WRITE_ONLY,
        READ_ONLY,
        WRITE_ONLY
    );

    signal reg_index : integer range  0 to REG_COUNT - 1;

    -- Internal registers
    signal current_state : work.avalon_slave.State;
    signal next_state : work.avalon_slave.State;
    signal reg_data : RegArray( 0 to REG_COUNT - 1 );
    signal fifo_read_req : std_logic;

begin

    u_avalon_slave_transitions: entity work.avalon_slave_transitions
        generic map (
            REG_COUNT => REG_COUNT,
            REG_ACCESS_TYPES => REG_ACCESS_TYPES
        )
        port map (
            address => address,
            read => read,
            write => write,

            current_state => current_state,
            next_state => next_state,
            reg_index => reg_index
        );

    sync : process ( clk, reset ) is
    begin
        if ( reset = '1' ) then
            current_state <= SLAVE_IDLE;
            reg_data( REG_CONFIG_POS ) <= ( others => '0' );
            fifo_read_req <= '0';
            clear <= '0';

        elsif ( rising_edge( clk ) ) then
            current_state <= next_state;
            sink_write <= '0';
            source_read <= '0';
            clear <= '0';

            case next_state is
            when SLAVE_IDLE =>
                null;

            when SLAVE_READ =>
                readdata <= ( others => '0' );
                if ( reg_index = REG_CONFIG_POS ) then
                    readdata( 1 downto 0 ) <= reg_data( reg_index )( 1 downto 0 );
                elsif ( reg_index = REG_EMPTY_POS ) then
                    readdata( 0 ) <= reg_data( reg_index )( 0 );
                elsif ( reg_index = REG_FULL_POS ) then
                    readdata( 0 ) <= reg_data( reg_index )( 0 );
                elsif ( reg_index = REG_LEVEL_POS ) then
                    readdata( 9 downto 0 ) <= reg_data( reg_index )( 9 downto 0 );
                elsif ( reg_index = REG_SINK_POS ) then
                    readdata <= reg_data( reg_index );
                elsif ( reg_index = REG_SOURCE_POS ) then
                    readdata <= source_readdata;
                    source_read <= '1';
                end if;
            when SLAVE_READ_DATA =>
                null;

            when SLAVE_WRITE =>
                if ( reg_index = REG_SINK_POS ) then
                    sink_write <= '1';
                    sink_writedata <= writedata;
                elsif ( reg_index = REG_CLEAR_POS ) then
                    clear <= '1';
                else
                    reg_data( reg_index ) <= writedata;
                end if;
            end case;

            reg_data( REG_EMPTY_POS )( 0 ) <= empty;
            reg_data( REG_FULL_POS )( 0 ) <= full;
            reg_data( REG_LEVEL_POS )( level'left downto level'right ) <= level;
        end if;
    end process sync;

    sink_config <= reg_data( REG_CONFIG_POS )( 0 );
    source_config <= reg_data( REG_CONFIG_POS )( 1 );

end architecture rtl;

