-- See the file "LICENSE" for the full license governing this code. --

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.lt16x32_global.all;
use work.wishbone.all;
use work.config.all;

entity wb_timer is
	generic(
		memaddr		:	generic_addr_type; --:= CFG_BADR_TIMER;
		addrmask	:	generic_mask_type --:= CFG_MADR_TIMER;
	);
	port(
		clk		: in  std_logic;
		rst		: in  std_logic;
		wslvi	:	in	wb_slv_in_type;
		wslvo	:	out	wb_slv_out_type;

        interrupt : out std_logic
	);
end wb_timer;

architecture Behavioral of wb_timer is
	constant TARGET_COUNT_DEFAULT   : integer := 32 - 1;
	
	signal ack	: std_logic;

	signal counter : integer range 0 to 65535;
	signal counter_target_value : integer range 0 to 65535;

	signal enable : std_logic;
	signal repeat : std_logic;
	signal reset : std_logic;

	signal counter_vector : std_logic_vector(31 downto 0);
	signal status_register : std_logic_vector(31 downto 0);
	
	signal data_in : std_logic_vector(2 downto 0);
	signal data_in_changed : std_logic;

begin

	process(clk)
	begin
		if clk'event and clk='1' then
			if rst = '1' then
				counter <= 0;
			else
			    if reset = '1' then -- reset register
			         counter <= 0;
			    else			
                    if enable = '1' then
                        if counter = counter_target_value then
                            counter <= 0;
                        else
                            counter <= counter + 1;
                        end if;
                    end if;
				end if;
			end if;
		end if;
	end process;
	
	process(clk)
	begin
        if clk'event and clk='1' then
			if rst = '1' then
				enable <= '0';
				repeat <= '0';
				reset <= '0';
			else
			     reset <= '0';
			     
			     if data_in_changed = '1' then
                     enable <= data_in(0);
                     repeat <= data_in(1);
                     reset <= data_in(2);
			     end if;
			
			     -- Reset enable bit
			     if counter = counter_target_value and repeat = '0' then
			         enable <= '0';
			     end if;
		    end if;
		end if;
	end process;

	process(clk)
	begin
		if clk'event and clk='1' then
			if rst = '1' then
				ack	<= '0';
				data_in <= (others => '0');
				data_in_changed <= '0';
				counter_target_value <= TARGET_COUNT_DEFAULT;
			else
			    data_in <= (others => '0');
			    data_in_changed <= '0';
			    
				if wslvi.stb = '1' and wslvi.cyc = '1' then
				    if wslvi.we='1' and wslvi.adr(2) = '1' then
				        data_in <= dec_wb_dat(wslvi.sel,wslvi.dat)(2 downto 0);
				        data_in_changed <= '1';
				    end if;
				    
                    if wslvi.we='1' and wslvi.adr(2) = '0' then
				        counter_target_value <= to_integer(unsigned(dec_wb_dat(wslvi.sel,wslvi.dat)));
				    end if;
				
					if ack = '0' then
						ack	<= '1';
					else
						ack	<= '0';
					end if;
				else
					ack <= '0';
				end if;
			end if;
		end if;
	end process;

	counter_vector <= std_logic_vector(to_unsigned(counter_target_value, counter_vector'length));
	
	status_register(0) <= enable; -- enable
	status_register(1) <= repeat; -- repeat
	status_register(31 downto 2) <= (others => '0'); -- reset bit always reads as 0

	wslvo.dat <= counter_vector when wslvi.adr(2) = '0'
	   else status_register when wslvi.adr(2) = '1'
	   else (others => '0');
		
	interrupt <= '1' when counter = counter_target_value else '0';

	wslvo.ack	<= ack;
	wslvo.wbcfg	<= wb_membar(memaddr, addrmask);

end Behavioral;
