LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY gpio_wb IS
	Port
	(
		CLK_I		: in  STD_LOGIC;
		RST_I		: in  STD_LOGIC;
		CYC_I		: in  STD_LOGIC;
		STB_I		: in  STD_LOGIC;
		SEL_I		: in  unsigned(3 downto 0);
		WE_I		: in  STD_LOGIC;
		ACK_O		: out STD_LOGIC;
		SRDY_O		: out STD_LOGIC;
		MRDY_I		: in  STD_LOGIC;
		ADDR_I		: in  unsigned(31 downto 0);
		DAT_I		: in  unsigned(31 downto 0);
		DAT_O		: out unsigned(31 downto 0);

		sys_gpo0	: out unsigned(31 downto 0);
		sys_gpo1	: out unsigned(31 downto 0);
		sys_gpi0	: in  unsigned(31 downto 0);
		sys_gpi1	: in  unsigned(31 downto 0)

	);
END gpio_wb;

ARCHITECTURE behavior OF gpio_wb IS

	signal gpo0_reg		: unsigned(31 downto 0);
	signal gpo1_reg		: unsigned(31 downto 0);
	signal gpi0_reg		: unsigned(31 downto 0);
	signal gpi1_reg		: unsigned(31 downto 0);
	-- Signals to form an timer generating an interrupt every microsecond
	subtype tick_usec_t	is natural range 0 to 99;
	signal tick_usec		: tick_usec_t;
	signal cnt_usec			: unsigned(31 downto 0);
	signal cnt_sec			: unsigned(31 downto 0);
	signal cnt_usec_preset	: unsigned(31 downto 0);
	signal cnt_sec_preset	: unsigned(31 downto 0);
	signal cnt_usec_en		: std_logic;
	signal cnt_usec_we		: std_logic;
	signal cnt_sec_en		: std_logic;
	signal cnt_sec_we		: std_logic;

begin

	SRDY_O	<= CYC_I;
	
------------------------------------------------------------------
led_out:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			sys_gpo0	<= gpo0_reg;
			sys_gpo1	<= gpo1_reg;
		end if;
	end process;	
		
------------------------------------------------------------------
btn_ps2_in:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			gpi0_reg	<= sys_gpi0;
			gpi1_reg	<= sys_gpi1;
		end if;
	end process;	
		
------------------------------------------------------------------
registers_write: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			cnt_usec_we		<= '0';
			cnt_sec_we		<= '0';
			if RST_I = '1' then
				gpo0_reg	<= (others => '0');
				gpo1_reg	<= (others => '0');
			elsif (STB_I and CYC_I and WE_I) = '1' then
				case ADDR_I(5 downto 2) is
				
				when "0000" =>
					if SEL_I(0) = '1' then
						gpo0_reg(7 downto 0) <= DAT_I(7 downto 0);
					end if;	
					if SEL_I(1) = '1' then
						gpo0_reg(15 downto 8) <= DAT_I(15 downto 8);
					end if;	
					if SEL_I(2) = '1' then
						gpo0_reg(23 downto 16) <= DAT_I(23 downto 16);
					end if;	
					if SEL_I(3) = '1' then
						gpo0_reg(31 downto 24) <= DAT_I(31 downto 24);
					end if;	
					
				when "0001" =>
					if SEL_I(0) = '1' then
						gpo1_reg(7 downto 0) <= DAT_I(7 downto 0);
					end if;	
					if SEL_I(1) = '1' then
						gpo1_reg(15 downto 8) <= DAT_I(15 downto 8);
					end if;	
					if SEL_I(2) = '1' then
						gpo1_reg(23 downto 16) <= DAT_I(23 downto 16);
					end if;	
					if SEL_I(3) = '1' then
						gpo1_reg(31 downto 24) <= DAT_I(31 downto 24);
					end if;	
					
				when "0010" =>
					if SEL_I(3) = '1' then
						cnt_usec_we		<= '1';
						cnt_usec_preset	<= DAT_I;
					end if;	

				when "0011" =>
					if SEL_I(3) = '1' then
						cnt_sec_we		<= '1';
						cnt_sec_preset	<= DAT_I;
					end if;	
					
				when others => null;
				end case;
			end if;	
		end if;	
	end process;

registers_read: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			ACK_O			<= '0';
			if (STB_I and CYC_I) = '1' then
				ACK_O		<= not WE_I;
				DAT_O		<= (others => '0');
				case ADDR_I(5 downto 2) is

				when "0000" =>
					DAT_O	<= gpi0_reg;

				when "0001" =>
					DAT_O	<= gpi1_reg;

				when "0010" =>
					DAT_O	<= cnt_usec;

				when "0011" =>
					DAT_O	<= cnt_sec;

				when others => null;
				end case;
			end if;
		end if;	
	end process;

tick_usec_timer:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			cnt_usec_en		<= '0';
			if tick_usec >= tick_usec_t'high then
				tick_usec		<= 0;
				cnt_usec_en		<= '1';
			else
				tick_usec		<= tick_usec + 1;
			end if;
		end if;
	end process;

cnt_usec_timer:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			cnt_sec_en		<= '0';
			if cnt_usec_we = '1' then
				cnt_usec		<= cnt_usec_preset;
			elsif cnt_usec_en = '1' then
				if cnt_usec >= to_unsigned(1E6 - 1, 32) then
					cnt_usec		<= (others => '0');
					cnt_sec_en		<= '1';
				else
					cnt_usec		<= cnt_usec + 1;
				end if;
			end if;	
		end if;
	end process;

cnt_sec_timer:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if cnt_sec_we = '1' then
				cnt_sec		<= cnt_sec_preset;
			elsif cnt_sec_en = '1' then
				cnt_sec		<= cnt_sec + 1;
			end if;	
		end if;
	end process;

------------------------------------------------------------------
end behavior;
