-- file I2cmaster_v1_0.vhd
-- I2cmaster_v1_0 module implementation
-- copyright: (C) 2016-2022 MPSI Technologies GmbH
-- author: Catherine Johnson (auto-generation)
-- date created: 17 Apr 2022
-- IP header --- ABOVE

-- module parameters:
-- threeNotInout (tri-state buffer configuration): true

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

use work.Dbecore.all;
use work.Tivsp.all;

entity I2cmaster_v1_0 is
	generic (
		fMclk: natural := 50000;
		clkFastNotStd: std_logic := '0';
		clkFastplusNotFast: std_logic := '0'
	);
	port (
		reset: in std_logic;
		mclk: in std_logic;

		req: in std_logic;
		ack: out std_logic;
		dne: out std_logic;

		devaddr: in std_logic_vector(7 downto 0);
		r16Not8: in std_logic;
		d16Not8: in std_logic;
		readNotWrite: in std_logic;
		readAradNotArd: in std_logic;
		writeAdNotArd: in std_logic;
		regaddr: in std_logic_vector(15 downto 0);
		send: in std_logic_vector(15 downto 0);
		recv: out std_logic_vector(15 downto 0);

		scl_in: in std_logic;
		scl_out: out std_logic;
		scl_tri: out std_logic;

		sda_in: in std_logic;
		sda_out: out std_logic;
		sda_tri: out std_logic
	);
end I2cmaster_v1_0;

architecture Rtl of I2cmaster_v1_0 is

	------------------------------------------------------------------------
	-- component declarations
	------------------------------------------------------------------------

	------------------------------------------------------------------------
	-- signal declarations
	------------------------------------------------------------------------

	---- transfer operation (xfer)
	type stateXfer_t is (
		stateXferInit,
		stateXferStartA, stateXferStartB,
		stateXferBitA, stateXferBitB,
		stateXferAckA, stateXferAckB,
		stateXferRestart,
		stateXferStopA, stateXferStopB, stateXferStopC,
		stateXferDone
	);
	signal stateXfer: stateXfer_t;

	signal recv_sig: std_logic_vector(15 downto 0);

	signal scl_sig: std_logic;
	signal sda_sig: std_logic;

begin

	------------------------------------------------------------------------
	-- sub-module instantiation
	------------------------------------------------------------------------

	------------------------------------------------------------------------
	-- implementation: transfer operation (xfer)
	------------------------------------------------------------------------

	ack <= '0' when stateXfer=stateXferInit else '1';
	dne <= '1' when stateXfer=stateXferDone else '0';

	recv <= recv_sig;

	scl_sig <= '0' when (stateXfer=stateXferBitA or stateXfer=stateXferAckA or stateXfer=stateXferRestart
				 or stateXfer=stateXferStopA) else '1';

	scl_out <= '0';
	scl_tri <= not scl_sig;

	sda_out <= '0';
	sda_tri <= not sda_sig;

	process (reset, mclk, stateXfer)
		type elem_t is (elemAddrA, elemRegMsb, elemRegLsb, elemAddrB, elemDataMsb, elemDataLsb, elemFinalize);
		variable elem: elem_t;

		variable recvraw: std_logic_vector(15 downto 0);

		variable bitcnt: natural range 0 to 8;

		variable imax: natural range 0 to (fMclk/100)/2;
		variable i: natural range 0 to (fMclk/100)/2;

	begin
		if reset='1' then
			stateXfer <= stateXferInit;
			recv_sig <= (others => '0');
			sda_sig <= '1';
			
			elem := elemAddrA;
			recvraw := (others => '0');
			bitcnt := 0;
			imax := (fMclk/100)/2;
			i := 0;

		elsif rising_edge(mclk) then
			if (stateXfer=stateXferInit or req='0') then
				recv_sig <= (others => '0');
				sda_sig <= '1';

				elem := elemAddrA;

				recvraw := (others => '0');

				bitcnt := 0;

				if clkFastNotStd='0' then
					imax := (fMclk/100)/2;
				else
					if clkFastplusNotFast='1' then
						imax := (fMclk/1000)/2;
					else
						imax := (fMclk/400)/2;
					end if;
				end if;

				i := 0;

				if req='0' then
					stateXfer <= stateXferInit;

				else
					stateXfer <= stateXferStartA;
				end if;

			elsif stateXfer=stateXferStartA then
				i := i + 1;

				if i=imax then
					i := 0;

					sda_sig <= '0';

					stateXfer <= stateXferStartB;
				end if;

			elsif stateXfer=stateXferStartB then
				i := i + 1;

				if i=imax then
					i := 0;

					bitcnt := 0;

					stateXfer <= stateXferBitA;
				end if;

			elsif stateXfer=stateXferBitA then
				if elem=elemAddrA or elem=elemAddrB then
					if bitcnt<7 then
						sda_sig <= devaddr(6-bitcnt);
					else
						if elem=elemAddrA and readNotWrite='1' and readAradNotArd='1' then
							sda_sig <= '0';
						else
							sda_sig <= readNotWrite;
						end if;
					end if;

				elsif elem=elemRegMsb then
					sda_sig <= regaddr(15-bitcnt);

				elsif elem=elemRegLsb then
					sda_sig <= regaddr(7-bitcnt);

				elsif elem=elemDataMsb and readNotWrite='0' then
					sda_sig <= send(15-bitcnt);

				elsif elem=elemDataLsb and readNotWrite='0' then
					sda_sig <= send(7-bitcnt);

				else
					sda_sig <= '1';
				end if;

				i := i + 1;

				if i=imax then
					i := 0;

					stateXfer <= stateXferBitB;
				end if;

			elsif stateXfer=stateXferBitB then
				if elem=elemDataMsb and readNotWrite='1' then
					recvraw(15-bitcnt) := sda_in;
				elsif elem=elemDataLsb and readNotWrite='1' then
					recvraw(7-bitcnt) := sda_in;
				end if;

				i := i + 1;

				if i=imax then
					i := 0;

					bitcnt := bitcnt + 1;

					if bitcnt=8 then
						stateXfer <= stateXferAckA;

					else
						stateXfer <= stateXferBitA;
					end if;
				end if;

			elsif stateXfer=stateXferAckA then
				if elem=elemDataMsb and readNotWrite='1' then
					sda_sig <= '0';
				else
					sda_sig <= '1';
				end if;

				i := i + 1;

				if i=imax then
					i := 0;

					stateXfer <= stateXferAckB;
				end if;

			elsif stateXfer=stateXferAckB then
				i := i + 1;

				if i=imax then
					i := 0;

					bitcnt := 0;

					if elem=elemAddrA then
						if readNotWrite='0' and writeAdNotArd='1' then
							if d16Not8='1' then
								elem := elemDataMsb;
							else
								elem := elemDataLsb;
							end if;

						elsif r16Not8='1' then
							elem := elemRegMsb;
						else
							elem := elemRegLsb;
						end if;

					elsif elem=elemRegMsb then
						elem := elemRegLsb;

					elsif elem=elemRegLsb then
						if readNotWrite='1' and readAradNotArd='1' then
							elem := elemAddrB;
						elsif d16Not8='1' then
							elem := elemDataMsb;
						else
							elem := elemDataLsb;
						end if;
	
					elsif elem=elemAddrB then
						if d16Not8='1' then
							elem := elemDataMsb;
						else
							elem := elemDataLsb;
						end if;

					elsif elem=elemDataMsb then
						elem := elemDataLsb;

					elsif elem=elemDataLsb then
						elem := elemFinalize;
					end if;

					if elem=elemAddrB then
						stateXfer <= stateXferRestart;

					elsif elem=elemFinalize then
						sda_sig <= '0';

						stateXfer <= stateXferStopA;

					else
						stateXfer <= stateXferBitA;
					end if;
				end if;

			elsif stateXfer=stateXferRestart then
				i := i + 1;

				if i=imax then
					i := 0;

					stateXfer <= stateXferStartA;
				end if;

			elsif stateXfer=stateXferStopA then
				i := i + 1;

				if i=imax then
					i := 0;

					stateXfer <= stateXferStopB;
				end if;

			elsif stateXfer=stateXferStopB then
				i := i + 1;

				if i=imax then
					i := 0;
					sda_sig <= '1';

					stateXfer <= stateXferStopC;
				end if;

			elsif stateXfer=stateXferStopC then
				i := i + 1;

				if i=imax then
					if readNotWrite='1' then
						recv_sig <= recvraw;
					end if;

					stateXfer <= stateXferDone;
				end if;

			elsif stateXfer=stateXferDone then
				if req='0' then
					stateXfer <= stateXferInit;
				end if;
			end if;
		end if;
	end process;

end Rtl;
