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

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

entity hardware_task_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 );

        task_start : out std_logic;
        task_state : in work.task.State;
        task_config : out work.reg32.RegArray( 0 to 2 )
    );
end entity hardware_task_control;

architecture rtl of hardware_task_control is

    type Registers is (
        REG_START,
        REG_STATE,
        REG_CYCLE_COUNT,
        REG_CONFIG_0,
        REG_CONFIG_1,
        REG_CONFIG_2
    );

    constant REG_START_POS : natural := Registers'pos( REG_START );
    constant REG_STATE_POS : natural := Registers'pos( REG_STATE );
    constant REG_CYCLE_COUNT_POS : natural := Registers'pos( REG_CYCLE_COUNT );
    constant REG_CONFIG_0_POS : natural := Registers'pos( REG_CONFIG_0 );
    constant REG_CONFIG_1_POS : natural := Registers'pos( REG_CONFIG_1 );
    constant REG_CONFIG_2_POS : natural := Registers'pos( REG_CONFIG_2 );

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

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

    -- Internal control and data signals
    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 task_running : 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( Registers'pos( REG_CYCLE_COUNT ) ) <= ( others => '0' );
            reg_data( Registers'pos( REG_CONFIG_0 ) ) <= ( others => '0' );

        elsif ( rising_edge( clk ) ) then
            current_state <= next_state;
            task_start <= '0';

            if ( task_state = work.task.TASK_DONE ) then
                task_running <= '0';
            end if;

            case next_state is
            when SLAVE_IDLE =>
                null;

            when SLAVE_READ =>
                readdata <= ( others => '0' );
                if ( reg_index = REG_STATE_POS ) then
                    readdata <= to_std_logic_vector( task_state, work.reg32.word'length );
                elsif ( reg_index = REG_CYCLE_COUNT_POS ) then
                    readdata <= reg_data( REG_CYCLE_COUNT_POS );
                else
                    readdata <= reg_data( reg_index );
                end if;

            when SLAVE_READ_DATA =>
                null;

            when SLAVE_WRITE =>

                if ( reg_index = REG_START_POS ) then
                    task_start <= '1';
                    reg_data( REG_CYCLE_COUNT_POS ) <= ( others => '0' );
                    task_running <= '1';
                else
                    reg_data( reg_index ) <= writedata;
                end if;

            end case;

            if ( task_running = '1' ) then
                reg_data( REG_CYCLE_COUNT_POS ) <=
                    std_logic_vector(
                        unsigned(
                            reg_data( REG_CYCLE_COUNT_POS ) ) + 1 );
            end if;
        end if;
    end process sync;
    task_config <= reg_data( REG_CONFIG_0_POS to REG_CONFIG_2_POS );
end architecture rtl;