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

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

package test_hardware_task is

    procedure read_state( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable state : out std_logic_vector );

    procedure assert_state_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable state : std_logic_vector );

    procedure assert_cycle_count_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable cycle_count : std_logic_vector );

    procedure write_start( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request );

    procedure write_config( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        variable index : in integer;
        variable config : in std_logic_vector );

    procedure assert_config_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable index : in integer;
        variable config : in std_logic_vector );

    procedure write_and_assert_config_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable index : in integer;
        variable config : in std_logic_vector );

    procedure assert_output_steam_data_eq( signal clk : in std_logic;
        signal write : std_logic;
        signal writedata : std_logic_vector;
        signal expected : work.reg32.RegArray );

    procedure test_execute( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        signal write : in std_logic;
        signal writedata : in std_logic_vector );

end package test_hardware_task;

package body test_hardware_task is

    procedure read_state( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable state : out std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 1, address'length ) );

        work.test_avalon_slave.read( clk => clk,
            address => address,
            req => req,
            rsp => rsp,
            data => state );
    end procedure read_state;

    procedure assert_state_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable state : std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 1, address'length ) );

        work.test_avalon_slave.assert_readdata_eq( clk => clk,
            address => address,
            req => req,
            rsp => rsp,
            expected => state,
            message => TEST_FAIL & " assert_state_eq" );
    end procedure assert_state_eq;

    procedure assert_cycle_count_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable cycle_count : std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 2, address'length ) );

        work.test_avalon_slave.assert_readdata_eq( clk => clk,
            address => address,
            req => req,
            rsp => rsp,
            expected => cycle_count,
            message => TEST_FAIL & " assert_cycle_count_eq" );
    end procedure assert_cycle_count_eq;

    procedure write_start( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request ) is

        variable address : std_logic_vector( 3 downto 0 );
        variable start : std_logic_vector( 31 downto 0 ) := ( others => '0' );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 0, address'length ) );

        work.test_avalon_slave.write( clk => clk,
            address => address,
            req => req,
            data => START );

    end procedure write_start;

    procedure write_config( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        variable index : in integer;
        variable config : in std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 3 + index, address'length ) );

        work.test_avalon_slave.write( clk => clk,
            address => address,
            req => req,
            data => config );
    end procedure write_config;

    procedure assert_config_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable index : in integer;
        variable config : in std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        wait until falling_edge( clk );
        address := std_logic_vector( to_unsigned( 3 + index, address'length ) );

        work.test_avalon_slave.assert_readdata_eq( clk => clk,
            address => address,
            req => req,
            rsp => rsp,
            expected => config,
            message => TEST_FAIL & " assert_config_eq" );
    end procedure assert_config_eq;

    procedure write_and_assert_config_eq( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        variable index : in integer;
        variable config : in std_logic_vector ) is

        variable address : std_logic_vector( 3 downto 0 );
    begin
        write_config( clk => clk, req => req, index => index, config => config );
        assert_config_eq( clk => clk, req => req, rsp => rsp, index => index, config => config );
    end procedure write_and_assert_config_eq;

    procedure assert_output_steam_data_eq( signal clk : in std_logic;
        signal write : std_logic;
        signal writedata : std_logic_vector;
        signal expected : work.reg32.RegArray ) is
    begin
    end procedure assert_output_steam_data_eq;

    procedure test_execute( signal clk : in std_logic;
        signal req : out work.avalon_slave.Request;
        signal rsp : in work.avalon_slave.Response;
        signal write : in std_logic;
        signal writedata : in std_logic_vector ) is

        variable expected_readdata : std_logic_vector( 31 downto 0 );
        variable state : std_logic_vector( 31 downto 0 );
        variable index : integer := 0;
    begin
        std.textio.write( std.textio.OUTPUT, "    test_execute ... " );

        expected_readdata := to_std_logic_vector( TASK_IDLE, expected_readdata'length );
        assert_state_eq( clk => clk, req => req, rsp => rsp, state => expected_readdata );

        write_start( clk => clk, req => req );

        expected_readdata := to_std_logic_vector( TASK_RUNNING, expected_readdata'length );
        assert_state_eq( clk => clk, req => req, rsp => rsp, state => expected_readdata );

        while true loop
            work.test_hardware_task.read_state( clk => clk, req => req, rsp => rsp, state => state );
            if ( state = to_std_logic_vector( TASK_DONE, expected_readdata'length ) ) then
                exit;
            end if;
        end loop;

        std.textio.write( std.textio.OUTPUT, TEST_OK );
    end procedure test_execute;
end package body test_hardware_task;

