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 timer 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 )
    );
end entity timer;

architecture rtl of timer is

    type Registers is (
        REG_STATE,
        REG_CYCLE_COUNT
    );

    constant REG_STATE_POS : natural := Registers'pos( REG_STATE );
    constant REG_CYCLE_COUNT_POS : natural := Registers'pos( REG_CYCLE_COUNT );
    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
    );

    signal reg_index : integer range  0 to REG_COUNT - 1;

    signal current_avalon_state : work.avalon_slave.State;
    signal next_avalon_state : work.avalon_slave.State;

    signal running : std_logic;
    signal cycle_count : unsigned( 31 downto 0 );

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_avalon_state,
            next_state => next_avalon_state,
            reg_index => reg_index
        );

    sync : process ( clk, reset ) is
    begin
        if ( reset = '1' ) then
            current_avalon_state <= SLAVE_IDLE;
            running <= '0';

        elsif ( rising_edge( clk ) ) then
            current_avalon_state <= next_avalon_state;

            if ( running = '1' ) then
                cycle_count <= cycle_count + 1;
            end if;

            case next_avalon_state is
            when SLAVE_IDLE =>
                null;

            when SLAVE_READ =>
                readdata <= ( others => '0' );
                if ( reg_index = REG_STATE_POS ) then
                    readdata( 0 ) <= running;
                elsif ( reg_index = REG_CYCLE_COUNT_POS ) then
                    readdata <= std_logic_vector( cycle_count );
                end if;

            when SLAVE_READ_DATA =>
                null;

            when SLAVE_WRITE =>
                if ( reg_index = REG_STATE_POS ) then
                    running <= writedata( 0 );
                    if ( writedata( 0 ) = '1' ) then
                        cycle_count <= ( others => '0' );
                    end if;
                end if;

            end case;
        end if;
    end process sync;
end architecture rtl;
