-- TODO: This is only a placeholder for a "real" GPIO core.

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

library ip;
use ip.wishbone_package.all;

entity gpio is
	port(
		clk    : in    std_logic;
		rst    : in    std_logic;
		clr    : in    std_logic;
		-- Wishbone
		wb_in  : in    wishbone_v3_slave_in;
		wb_out : out   wishbone_v3_slave_out;
		-- Ports
		gpio   : inout std_logic_vector(31 downto 0)
	);
end entity gpio;

architecture RTL of gpio is
	signal reg_port : std_logic_vector(31 downto 0);
	signal reg_ddr  : std_logic_vector(31 downto 0);
begin
	gpio_p : process(clk, rst) is
		procedure default_state is
		begin
			null;
		end procedure default_state;

		procedure reset_state is
		begin
			default_state;
			reg_ddr  <= (others => '0');
			reg_port <= (others => '0');
		end procedure reset_state;
	begin
		if rst = '1' then
			reset_state;
		elsif rising_edge(clk) then
			default_state;
			if clr = '1' then
				reset_state;
			else
				if (wb_in.STB = '1' and wb_in.CYC = '1' and wb_in.WE = '1') then
					for i in 0 to 3 loop
					if (wb_in.SEL(i) = '1') then						
							case wb_in.ADR(3 downto 2) is
								when "00"   => reg_ddr((i + 1) * 8 - 1 downto i * 8) <= wb_in.DAT((i + 1) * 8 - 1 downto i * 8);
								when "01"   => reg_port((i + 1) * 8 - 1 downto i * 8) <= wb_in.DAT((i + 1) * 8 - 1 downto i * 8);
								when others => null;
							end case;
						end if;
					end loop;
				end if;
			end if;
		end if;
	end process gpio_p;

	wb_out.ERR <= '0';
	wb_out.RTY <= '0';
	wb_out.ACK <= (wb_in.STB and wb_in.CYC);

	read : with wb_in.ADR(3 downto 2) select wb_out.DAT <=
		reg_ddr when "00",
		reg_port when "01",
		gpio when others;

	writeport : for i in 0 to 31 generate
		gpio(i) <= reg_port(i) when reg_ddr(i) = '1' else 'Z';
	end generate writeport;
end architecture RTL;
