---------------------------------------------------------------------
----                                                             ----
----  WISHBONE revB2 compl. I2C Master Core; byte-controller     ----
----                                                             ----
----                                                             ----
----  Author: Richard Herveille                                  ----
----          richard@asics.ws                                   ----
----          www.asics.ws                                       ----
----                                                             ----
----  Downloaded from: http://www.opencores.org/projects/i2c/    ----
----                                                             ----
---------------------------------------------------------------------
----                                                             ----
---- Copyright (C) 2000 Richard Herveille                        ----
----                    richard@asics.ws                         ----
----                                                             ----
---- This source file may be used and distributed without        ----
---- restriction provided that this copyright statement is not   ----
---- removed from the file and that any derivative work contains ----
---- the original copyright notice and the associated disclaimer.----
----                                                             ----
----     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ----
---- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ----
---- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ----
---- FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ----
---- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ----
---- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ----
---- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ----
---- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ----
---- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ----
---- LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ----
---- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ----
---- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ----
---- POSSIBILITY OF SUCH DAMAGE.                                 ----
----                                                             ----
---------------------------------------------------------------------

--  CVS Log
--
--  $Id: i2c_master_byte_ctrl.vhd,v 1.1 2004/09/23 07:22:48 acroslan Exp $
--
--  $Date: 2004/09/23 07:22:48 $
--  $Revision: 1.1 $
--  $Author: acroslan $
--  $Locker:  $
--  $State: Exp $
--
-- Change History:
--               $Log: i2c_master_byte_ctrl.vhd,v $
--               Revision 1.1  2004/09/23 07:22:48  acroslan
--               I2C checked in
--
--               Revision 1.1  2001/11/05 12:02:33  rherveille
--               Split i2c_master_core.vhd into separate files for each entity; same layout as verilog version.
--               Code updated, is now up-to-date to doc. rev.0.4.
--               Added headers.
--




--
------------------------------------------
-- Byte controller section
------------------------------------------
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity i2c_master_byte_ctrl is
	generic(
		Tcq : time := 1 ns
	);
	port (
		clk    : in std_logic;
		rst    : in std_logic; -- synchronous active high reset (WISHBONE compatible)
		nReset : in std_logic;	-- asynchornous active low reset (FPGA compatible)
		ena    : in std_logic; -- core enable signal

		clk_cnt : in unsigned(15 downto 0);	-- 4x SCL 

		-- input signals
		start,
		stop,
		read,
		write,
		ack_in : std_logic;
		din    : in std_logic_vector(7 downto 0);

		-- output signals
		cmd_ack  : out std_logic;
		ack_out  : out std_logic;
		i2c_busy : out std_logic;
		dout     : out std_logic_vector(7 downto 0);

		-- i2c lines
		scl_i   : in std_logic;  -- i2c clock line input
		scl_o   : out std_logic; -- i2c clock line output
		scl_oen : out std_logic; -- i2c clock line output enable, active low
		sda_i   : in std_logic;  -- i2c data line input
		sda_o   : out std_logic; -- i2c data line output
		sda_oen : out std_logic  -- i2c data line output enable, active low
	);
end entity i2c_master_byte_ctrl;

architecture structural of i2c_master_byte_ctrl is
	component i2c_master_bit_ctrl is
		generic(
			Tcq : time := Tcq
		);
		port (
			clk    : in std_logic;
			rst    : in std_logic;
			nReset : in std_logic;
			ena    : in std_logic;				-- core enable signal

			clk_cnt : in unsigned(15 downto 0);		-- clock prescale value

			cmd     : in std_logic_vector(3 downto 0);
			cmd_ack : out std_logic;
			busy    : out std_logic;

			din  : in std_logic;
			dout : out std_logic;

			-- i2c lines
			scl_i   : in std_logic;  -- i2c clock line input
			scl_o   : out std_logic; -- i2c clock line output
			scl_oen : out std_logic; -- i2c clock line output enable, active low
			sda_i   : in std_logic;  -- i2c data line input
			sda_o   : out std_logic; -- i2c data line output
			sda_oen : out std_logic  -- i2c data line output enable, active low
		);
	end component i2c_master_bit_ctrl;

	-- commands for bit_controller block
	constant I2C_CMD_NOP  	: std_logic_vector(3 downto 0) := "0000";
	constant I2C_CMD_START	: std_logic_vector(3 downto 0) := "0001";
	constant I2C_CMD_STOP	 : std_logic_vector(3 downto 0) := "0010";
	constant I2C_CMD_READ	 : std_logic_vector(3 downto 0) := "0100";
	constant I2C_CMD_WRITE	: std_logic_vector(3 downto 0) := "1000";

	-- signals for bit_controller
	signal core_cmd : std_logic_vector(3 downto 0);
	signal core_ack, core_txd, core_rxd : std_logic;

	-- signals for shift register
	signal sr : std_logic_vector(7 downto 0); -- 8bit shift register
	signal shift, ld : std_logic;

	-- signals for state machine
	signal go, host_ack : std_logic;
	signal dcnt : unsigned(2 downto 0); -- data counter
	signal cnt_done : std_logic;

begin
	-- hookup bit_controller
	u1: i2c_master_bit_ctrl port map(
		clk     => clk,
		rst     => rst,
		nReset  => nReset,
		ena     => ena,
		clk_cnt => clk_cnt,
		cmd     => core_cmd,
		cmd_ack => core_ack,
		busy    => i2c_busy,
		din     => core_txd,
		dout    => core_rxd,
		scl_i   => scl_i,
		scl_o   => scl_o,
		scl_oen => scl_oen,
		sda_i   => sda_i,
		sda_o   => sda_o,
		sda_oen => sda_oen
	);

	-- generate host-command-acknowledge
	cmd_ack <= host_ack;
	
	-- generate go-signal
	go <= (read or write or stop) and not host_ack;

	-- assign Dout output to shift-register
	dout <= sr;

	-- generate shift register
	shift_register: process(clk, nReset)
	begin
		if (nReset = '0') then
			sr <= (others => '0') after Tcq;
		elsif (clk'event and clk = '1') then
			if (rst = '1') then
				sr <= (others => '0') after Tcq;
			elsif (ld = '1') then
				sr <= din after Tcq;
			elsif (shift = '1') then
				sr <= (sr(6 downto 0) & core_rxd) after Tcq;
			end if;
		end if;
	end process shift_register;

	-- generate data-counter
	data_cnt: process(clk, nReset)
	begin
		if (nReset = '0') then
			dcnt <= (others => '0') after Tcq;
		elsif (clk'event and clk = '1') then
			if (rst = '1') then
				dcnt <= (others => '0') after Tcq;
			elsif (ld = '1') then
				dcnt <= (others => '1') after Tcq; -- load counter with 7
			elsif (shift = '1') then
				dcnt <= dcnt -1 after Tcq;
			end if;
		end if;
	end process data_cnt;

	cnt_done <= '1' when (dcnt = 0) else '0';

	--
	-- state machine
	--
	statemachine : block
		type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop);
		signal c_state : states;
	begin
		--
		-- command interpreter, translate complex commands into simpler I2C commands
		--
		nxt_state_decoder: process(clk, nReset)
		begin
			if (nReset = '0') then
				core_cmd <= I2C_CMD_NOP after Tcq;
				core_txd <= '0' after Tcq;
				
				shift    <= '0' after Tcq;
				ld       <= '0' after Tcq;

				host_ack <= '0' after Tcq;
				c_state  <= st_idle after Tcq;

				ack_out  <= '0' after Tcq;
			elsif (clk'event and clk = '1') then
				if (rst = '1') then
					core_cmd <= I2C_CMD_NOP after Tcq;
					core_txd <= '0' after Tcq;
				
					shift    <= '0' after Tcq;
					ld       <= '0' after Tcq;

					host_ack <= '0' after Tcq;
					c_state  <= st_idle after Tcq;

					ack_out  <= '0' after Tcq;
				else
					-- initialy reset all signal
					core_txd <= sr(7) after Tcq;

					shift    <= '0' after Tcq;
					ld       <= '0' after Tcq;

					host_ack <= '0' after Tcq;

					case c_state is
						when st_idle =>
							if (go = '1') then
								if (start = '1') then
									c_state  <= st_start after Tcq;
									core_cmd <= I2C_CMD_START after Tcq;
								elsif (read = '1') then
									c_state  <= st_read after Tcq;
									core_cmd <= I2C_CMD_READ after Tcq;
								elsif (write = '1') then
									c_state  <= st_write after Tcq;
									core_cmd <= I2C_CMD_WRITE after Tcq;
								else -- stop
									c_state  <= st_stop after Tcq;
									core_cmd <= I2C_CMD_STOP after Tcq;

									host_ack <= '1' after Tcq; -- generate acknowledge signal
								end if;

								ld <= '1' after Tcq;
							end if;

						when st_start =>
							if (core_ack = '1') then
								if (read = '1') then
									c_state  <= st_read after Tcq;
									core_cmd <= I2C_CMD_READ after Tcq;
								else
									c_state  <= st_write after Tcq;
									core_cmd <= I2C_CMD_WRITE after Tcq;
								end if;

								ld <= '1' after Tcq;
							end if;

						when st_write =>
							if (core_ack = '1') then
								if (cnt_done = '1') then
									c_state  <= st_ack after Tcq;
									core_cmd <= I2C_CMD_READ after Tcq;
								else
									c_state  <= st_write after Tcq;       -- stay in same state
									core_cmd <= I2C_CMD_WRITE after Tcq;  -- write next bit

									shift    <= '1' after Tcq;
								end if;
							end if;			

						when st_read =>
							if (core_ack = '1') then
								if (cnt_done = '1') then
									c_state  <= st_ack after Tcq;
									core_cmd <= I2C_CMD_WRITE after Tcq;
								else
									c_state  <= st_read after Tcq;      -- stay in same state
									core_cmd <= I2C_CMD_READ after Tcq; -- read next bit
								end if;

								shift    <= '1' after Tcq;
								core_txd <= ack_in after Tcq;
							end if;			

						when st_ack =>
							if (core_ack = '1') then
								-- check for stop; Should a STOP command be generated ?
								if (stop = '1') then
									c_state  <= st_stop after Tcq;
									core_cmd <= I2C_CMD_STOP after Tcq;
								else
									c_state  <= st_idle after Tcq;
									core_cmd <= I2C_CMD_NOP after Tcq;
								end if;

								-- assign ack_out output to core_rxd (contains last received bit)
								ack_out  <= core_rxd after Tcq;

								-- generate command acknowledge signal
								host_ack <= '1' after Tcq;

								core_txd <= '1' after Tcq;
							else
								core_txd <= ack_in after Tcq;
							end if;

						when st_stop =>
							if (core_ack = '1') then
								c_state  <= st_idle after Tcq;
								core_cmd <= I2C_CMD_NOP after Tcq;
							end if;

						when others => -- illegal states
							c_state  <= st_idle after Tcq;
							core_cmd <= I2C_CMD_NOP after Tcq;
							report ("Byte controller entered illegal state.");

					end case;

				end if;
			end if;
		end process nxt_state_decoder;

	end block statemachine;

end architecture structural;

