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

entity i2c_wrapper is
	generic ( prescale : std_logic_vector(15 downto 0) := x"00c8");
	port (
	wb_clk_i  : in    std_logic;
	wb_rst_i  : in    std_logic;
	wb_adr_i  : in    std_logic_vector(15 downto 0);
	wb_dat_i  : in    std_logic_vector(7 downto 0);
	wb_dat_o  : out   std_logic_vector(7 downto 0);
	wb_we_i   : in    std_logic;
	wb_stb_i  : in    std_logic;
	wb_ack_o  : out   std_logic;
	scl_i     : in    std_logic;
	scl_o     : out   std_logic;
	sda_i     : in    std_logic;
	sda_o     : out   std_logic;
	debug     : out   std_logic_vector(3 downto 0)
	);
end;

architecture i2c_wrapper of i2c_wrapper is

	signal wb_adr      : unsigned(2 downto 0);
	signal wb_dati     : std_logic_vector(7 downto 0);
	signal wb_dato     : std_logic_vector(7 downto 0);
	signal wb_we       : std_logic;
	signal wb_stb      : std_logic;
	signal wb_cyc      : std_logic;
	signal wb_ack      : std_logic;
	signal wb_inta     : std_logic;
	signal wb_dat_oq   : std_logic_vector(7 downto 0);
	signal cnt         : unsigned(3 downto 0);
	signal wb_dowrite  : std_logic;
	signal wb_doread   : std_logic;
	signal end_cnt     : integer range 0 to 11;

	type i2c_rec is record
		adr   : unsigned(2 downto 0);
		dat   : std_logic_vector(7 downto 0);
		int   : std_logic;
	end record;

	type i2c_mem is ARRAY(0 to 11) of i2c_rec;
	signal dat : i2c_mem;
	type   i2c_states is (IDLE, I2C_GO, I2C_WAIT_INT, I2C_GET_RXDAT, I2C_WB_ACK);
	signal i2c_sm : i2c_states;

	signal scl           : std_logic;
	signal sda           : std_logic;
	signal scl_oen       : std_logic;
	signal sda_oen       : std_logic;
	signal i2c_init_done : std_logic;
begin
	scl_o    <= scl when scl_oen = '0' else '1';
	sda_o    <= sda when sda_oen = '0' else '1';
	wb_dat_o <= wb_dat_oq;

	i2c_master : entity work.i2c_master_top
	generic map( ARST_LVL => '1' )
	port map (
		-- wishbone signals
		wb_clk_i  => wb_clk_i,
		wb_rst_i  => wb_rst_i,
		arst_i    => wb_rst_i,
		wb_adr_i  => std_logic_vector(wb_adr),
		wb_dat_i  => wb_dati,
		wb_dat_o  => wb_dato,
		wb_we_i   => wb_we,
		wb_stb_i  => wb_stb,
		wb_cyc_i  => wb_cyc,
		wb_ack_o  => wb_ack,
		wb_inta_o => wb_inta,

		-- i2c lines
		scl_pad_i    => scl_i,
		scl_pad_o    => scl,
		scl_padoen_o => scl_oen,
		sda_pad_i    => sda_i,
		sda_pad_o    => sda,
		sda_padoen_o => sda_oen
	);

	i2c_write: process (wb_clk_i, wb_rst_i)
	begin
	if rising_edge(wb_clk_i) then
	if wb_rst_i = '1' then
		wb_dat_oq     <= (others => '0');
		cnt           <= (others => '0');
		i2c_init_done <= '0';
		wb_dowrite    <= '0';
		wb_doread     <= '0';
		end_cnt       <= 0;
	else
		dat <= dat;

		if wb_ack = '1' and i2c_sm = I2C_GO then
			cnt <= cnt + "1";
		elsif i2c_sm = IDLE and i2c_init_done = '1' then
			cnt <= "0011";
		elsif i2c_sm = IDLE and i2c_init_done = '0' then
			cnt <= "0000";
		end if;

		if i2c_sm = IDLE and wb_stb_i = '1' then
	       		if wb_we_i = '1' then
				      --         PRESCALE Register                                            enable core and interrupt
				dat  <= (("000", prescale(7 downto 0),'0'), ("001", prescale(15 downto 8),'0'), ("010", x"c0",'0'),
				      --     ADDRESS           STA and WR bit           DATA/REG           STA and WR
					 ("011", x"EC", '0'), ("100", x"90",'1'), ("011", x"ff", '0'), ("100", x"90",'1'), 
				      --       DATA              STO and WR
					 ("011", x"ff",'0'), ("100", x"50", '1'));
				dat(3).dat <= wb_adr_i(15 downto 0);
				dat(5).dat <= wb_adr_i( 7 downto 0);
				dat(8).dat <= wb_dat_i;
				end_cnt    <= 9;
			else
				dat  <= (("000", prescale(7 downto 0),'0'), ("001", prescale(15 downto 8),'0'), ("010", x"c0",'0'),
				      --     ADDRESS           STA and WR bit           DATA/REG             WR           
					 ("011", x"EC", '0'), ("100", x"91",'1'), ("011", x"ff", '0'), ("100", x"11", '1'),
				      --     ADDRESS                STA,WR             STO, RD, NACK
				         ("011", x"ED", '0'), ("100", x"91",'1'), ("100", x"69", '1'), ("111", x"00", '0'), ("111", x"00", '0'));
				dat(3).dat <= wb_adr_i(15 downto 9) & '0'; -- address write
				dat(5).dat <= wb_adr_i( 7 downto 0);       -- memory location
				dat(7).dat <= wb_adr_i(15 downto 9) & '1'; -- address read
				end_cnt    <= 10;
			end if;
		end if;

		i2c_init_done <= i2c_init_done;
		if i2c_sm = I2C_GO and cnt = "0010" then
			i2c_init_done <= '1';
		end if;

		if i2c_sm = I2C_GET_RXDAT and wb_ack = '1' then
			wb_dat_oq <= wb_dato;
		end if;

		case i2c_sm is
		when IDLE =>
			if (wb_stb_i = '1') then
				i2c_sm <= I2C_GO;
			end if;
		when I2C_GO =>
			wb_dowrite <= '1';

			if wb_ack = '1' and dat(to_integer(cnt)).int = '1' then
				i2c_sm  <= I2C_WAIT_INT;
				wb_dowrite <= '0';
			elsif wb_ack = '1' and dat(to_integer(cnt)).int = '0' then
				if to_integer(cnt) = end_cnt then
					i2c_sm <= I2C_GET_RXDAT;
				end if;
				wb_dowrite <= '0';
			end if;
		when I2C_WAIT_INT =>
			wb_doread <= '1';

			if wb_ack = '1' and wb_dato(1) = '0' then
				wb_doread <= '0';
				if to_integer(cnt) = end_cnt then
					i2c_sm <= I2C_GET_RXDAT;
				else
					i2c_sm <= I2C_GO;
				end if;
			end if;
		when I2C_GET_RXDAT =>
			wb_doread <= '1';
			if wb_ack = '1' then
				wb_doread <= '0';
				i2c_sm <= I2C_WB_ACK;
			end if;
		when I2C_WB_ACK =>
			i2c_sm <= IDLE;
		end case;
	end if;
	end if;
	end process;

	wb_ack_o <= '1' when i2c_sm = I2C_WB_ACK else '0';
	wb_dati <= dat(to_integer(cnt)).dat;
	wb_adr  <= dat(to_integer(cnt)).adr when wb_dowrite = '1'       else
	                              "011" when i2c_sm = I2C_GET_RXDAT else
	                              "100";
	wb_cyc  <= wb_stb;
	wb_stb  <= '1' when (wb_dowrite = '1' or wb_doread = '1') and wb_ack = '0' else '0';
	wb_we   <= wb_dowrite;

	debug(3 downto 0) <= std_logic_vector(cnt);

end i2c_wrapper;
