RSS Git Download  Clone
Raw Blame History
-- =============================================================================================
--                             	                                                            **
-- =============================================================================================
--	Title: phy_i2c.vhd
--	Description:	VHDL  design file for control of an I2C interface
--
--	Interfaces to the I2C bus to provide control of DP83865 PHY
--	Notes
--	a)Max clock speed for this device is 2.5 MHz, standard I2C is 100 kHz
--  b)Data format is start (2), opcode(2), Phy Address(5), RegAddress(5), turnround (2)
--    data bits (16)
--	Mod Record
--	===========
--	1)23/01/2008 11:50		 created
--
--
-- (C) Copyright Alpha Data Parallel Systems Ltd. 1999-2008
--
--
-------------------------------------------------------------------------------------------------



--********************************************************************************************








--********************************************************************************************
--			Entity Definition
--
-- Notes:
--********************************************************************************************
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;





entity phy_i2c is
port
(
		--Host controls
		reset		:in		std_logic;
		clock		:in		std_logic;
		ckdiv_cnt	:in		std_logic_vector(15 downto 0);

		rd_flag	:in		std_logic;
		wr_flag	:in		std_logic;
		ini_flag	:in		std_logic;

		busy		:out		std_logic;

		--	Params for I2C transfer
		phy_addr		:in		std_logic_vector(4 downto 0);
		reg_addr		:in		std_logic_vector(4 downto 0);

		wrdata_in		:in		std_logic_vector(15 downto 0);
		rddata_out	:out		std_logic_vector(15 downto 0);

		--I2C signals
		i2c_clock	:out		std_logic;
		i2c_data  :inout		std_logic
	);
end phy_i2c;
--********************************************************************************************







--********************************************************************************************
--	Architecture Definition
--
--********************************************************************************************
architecture synth of phy_i2c is









--********************************************************************************************
--	Constant/Type Definitions
--********************************************************************************************

	--sense defined for CPLD control polarity ( 1 = i2c bus driven)
	constant	I2C_WR	    :std_logic := '1';
	constant	I2C_RD	    :std_logic := '0';
	constant 	I2C_DEFAULT	:std_logic := '1';





  type state_type is (
						i2c_init,
						i2c_idle,
						i2c_start,
						i2c_addrbit,
						i2c_addrck,
						i2c_cmndbit,
						i2c_cmndck,
						i2c_wr_bit,
						i2c_wrck,
						i2c_rd_bit,
						i2c_stop
					);

--********************************************************************************************


--********************************************************************************************
--	Component Definitions
--********************************************************************************************


--********************************************************************************************







--********************************************************************************************
--	Signal Definitions
--
--********************************************************************************************
------------------------------------------------------
--State machine variables
------------------------------------------------------
signal	next_busy_sig           :std_logic;
signal	next_optype_sig        	:std_logic;

signal	next_rcvd_data_sig		:std_logic_vector(15 downto 0);
signal	next_bitcnt_sig         :std_logic_vector( 7 downto 0);

signal	next_i2c_dirn_sig       :std_logic;
signal	next_i2c_data_bit_out_sig   :std_logic;
signal	next_i2c_clock_sig      :std_logic;
signal	next_i2c_ack_sig      :std_logic;
signal	next_i2c_state_sig	:state_type;



signal	busy_sig           :std_logic;

signal	bitcnt_sig         :std_logic_vector(7 downto 0);
signal	rcvd_data_sig		:std_logic_vector(15 downto 0);
signal	optype_sig        	:std_logic;

signal	i2c_dirn_sig       :std_logic;
signal	i2c_data_bit_out_sig   :std_logic;
signal	i2c_clock_sig      :std_logic;
signal	i2c_ack_sig      :std_logic;

signal	i2c_state_sig	:state_type;
------------------------------------------------------



------------------------------------------------------
--I2c data port input
------------------------------------------------------
signal	i2c_data_bit_in_sig   :std_logic;
------------------------------------------------------




------------------------------------------------------
--Local bus i/f
------------------------------------------------------
signal	reset_sig      :std_logic;
signal	clock_sig      :std_logic;
signal	data_in_sig        :std_logic_vector(15 downto 0);
signal	addr_sig      :std_logic_vector( 12 downto 0);
------------------------------------------------------


------------------------------------------------------
--Control signals
------------------------------------------------------
signal	ckdiv_cnt_sig      :std_logic_vector(15 downto 0);
signal	cntr_sig      :std_logic_vector( 15 downto 0);
signal	cntr_carry_sig      :std_logic;

signal	rd_flag_sig      :std_logic_vector( 1 downto 0);
signal	wr_flag_sig      :std_logic_vector( 1 downto 0);
signal	rd_strobe_sig      :std_logic;
signal	wr_strobe_sig      :std_logic;
signal	rd_posedge_sig      :std_logic;
signal	wr_posedge_sig      :std_logic;


signal	ini_flag_sig      :std_logic_vector( 1 downto 0);
signal	ini_strobe_sig      :std_logic;
signal	ini_posedge_sig      :std_logic;

------------------------------------------------------

--********************************************************************************************






--********************************************************************************************
--	Attributes
--********************************************************************************************










--********************************************************************************************



























--********************************************************************************************
--	Architectural Statements
--********************************************************************************************

begin
--==============================================================================================
-- Map ports to sigs
--==============================================================================================
	reset_sig <= reset;
	clock_sig <= clock;
	ckdiv_cnt_sig <= ckdiv_cnt;

	busy <= busy_sig;

	rddata_out <=rcvd_data_sig;



  --i2c interface
  i2c_data <= i2c_data_bit_out_sig when i2c_dirn_sig='1'
              else 'Z';
	i2c_data_bit_in_sig	<= i2c_data;

	i2c_clock<= i2c_clock_sig;
------------------------------------------------------------------------------------------------




--==============================================================================================
-- Detect rising edge of read or write flag
--==============================================================================================
posedge:process ( reset_sig, clock_sig)
begin
	if reset_sig = '1' then
		rd_flag_sig <=(others => '0');
		rd_strobe_sig <= '0';
		rd_posedge_sig <= '0';

		wr_flag_sig <=(others => '0');
		wr_strobe_sig <= '0';
		wr_posedge_sig <= '0';

		ini_flag_sig <=(others => '0');
		ini_strobe_sig <= '0';
		ini_posedge_sig <= '0';
	elsif rising_edge( clock_sig) then
		rd_flag_sig(0) <=rd_flag;
		rd_flag_sig(1) <=rd_flag_sig(0);
		rd_posedge_sig <= rd_flag_sig(0) and not rd_flag_sig(1);

		wr_flag_sig(0) <=wr_flag;
		wr_flag_sig(1) <=wr_flag_sig(0);
		wr_posedge_sig <= wr_flag_sig(0) and not wr_flag_sig(1);

		ini_flag_sig(0) <=ini_flag;
		ini_flag_sig(1) <=ini_flag_sig(0);
		ini_posedge_sig <= ini_flag_sig(0) and not ini_flag_sig(1);

		--run the init sequence
		if  ini_flag_sig(0)='1' and ini_flag_sig(1)='0' then
			ini_strobe_sig <= '1';
		elsif busy_sig ='1' then
			ini_strobe_sig <= '0';
		end if;

		--set the strobe on a rising edge, clear when state machine goes busy
		if  rd_flag_sig(0)='1' and rd_flag_sig(1)='0' then
			rd_strobe_sig <= '1';
		elsif busy_sig ='1' then
			rd_strobe_sig <= '0';
		end if;

		--read is dominant if they occur simultaneously
		if  wr_flag_sig(0)='1' and wr_flag_sig(1)='0' and rd_flag_sig ="00" then
			wr_strobe_sig <= '1';
		elsif busy_sig ='1' then
			wr_strobe_sig <= '0';
		end if;
	end if;
end process;
------------------------------------------------------------------------------------------------





--==============================================================================================
-- Register values on detection of read or write command
--==============================================================================================
regip:	process ( reset_sig,clock_sig)
begin
	if reset_sig = '1' then
		addr_sig <=(others =>'0');
		data_in_sig <= (others =>'0');
	elsif rising_edge( clock_sig) then
  	if busy_sig ='0' then
  		if wr_posedge_sig = '1' then
  			addr_sig <="101"&phy_addr&reg_addr;
  		end if;
  		if rd_posedge_sig = '1'  then
  			addr_sig <="110"&phy_addr&reg_addr;
  		end if;


  		data_in_sig <= wrdata_in;
  	end if;
	end if;
end process;
------------------------------------------------------------------------------------------------






--==============================================================================================
-- Clock generator to control i2c rate ( = half carry sig rate)
--==============================================================================================
i2c_ck:	process (clock_sig,reset_sig,ckdiv_cnt_sig)
begin
	if reset_sig = '1' then
		cntr_sig <= ( others => '0');
		cntr_carry_sig <= '0';
	elsif rising_edge(clock_sig) then
		if cntr_sig = ckdiv_cnt_sig then
			cntr_sig <= ( others => '0');
			cntr_carry_sig <= '1';
		else
			cntr_sig <= cntr_sig+ '1';
			cntr_carry_sig <= '0';
		end if;
	end if;
end process;
------------------------------------------------------------------------------------------------







--==============================================================================================
-- default for i2c data out is high on the bus
-- default for i2c clock is high on the bus
-- default for i2c dirn is master(FPGA or CPLD) driving
-- i2c data in is an slv
--==============================================================================================
state_adv:	process (clock_sig,reset_sig)
begin
	if reset_sig = '1' then
		--handshaking
		busy_sig<= '0';

		--internal sigs
		rcvd_data_sig <= (others =>'1');
		bitcnt_sig <= (others =>'0');
		optype_sig <=I2C_WR;

		--i2c ports
		i2c_clock_sig <= I2C_DEFAULT;
		i2c_data_bit_out_sig <= I2C_DEFAULT;
		i2c_dirn_sig <= I2C_WR;
		i2c_ack_sig <= I2C_DEFAULT;

		--state variable
		i2c_state_sig <= i2c_idle;

	elsif rising_edge(clock_sig) then


		if cntr_carry_sig ='1' then	-- probably only needs to apply to the state update
			rcvd_data_sig <= next_rcvd_data_sig;
			--handshaking
			busy_sig<= next_busy_sig;
			optype_sig <=next_optype_sig;

			--internal sigs
			bitcnt_sig <= next_bitcnt_sig;

			--i2c ports
			i2c_clock_sig <= next_i2c_clock_sig;
			i2c_data_bit_out_sig <= next_i2c_data_bit_out_sig ;
			i2c_dirn_sig <= next_i2c_dirn_sig;
			i2c_ack_sig <= next_i2c_ack_sig;

			--state variable
			i2c_state_sig <= next_i2c_state_sig;
		end if;
	end if;
end process;
------------------------------------------------------------------------------------------------






--==============================================================================================
-- State mchine
--==============================================================================================
state_mux:	process (i2c_clock_sig,i2c_state_sig,
					rcvd_data_sig, rd_strobe_sig,ini_strobe_sig,
					wr_strobe_sig, optype_sig,
					bitcnt_sig, i2c_data_bit_out_sig,
					i2c_ack_sig, i2c_data_bit_in_sig,
					data_in_sig,addr_sig)
begin
	case i2c_state_sig is

		------------------------------------------------
		--Default state - all signals inactive
		-- ck and data out = high
		-- dirn = write
		------------------------------------------------
		when i2c_idle =>	-- wait for command
			next_busy_sig <= '0';

			next_bitcnt_sig <= (others =>'0');
			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_clock_sig <= '0';
			next_i2c_ack_sig <= '1';
			next_i2c_dirn_sig <= I2C_WR;	--default

			if ini_strobe_sig='1' then
				next_optype_sig <= I2C_WR;	--default
				next_i2c_data_bit_out_sig <= '1';
				next_i2c_state_sig <=i2c_init;
			elsif rd_strobe_sig='1' then
				next_optype_sig <= I2C_RD;
				next_i2c_data_bit_out_sig <= '0';
				next_i2c_state_sig <=i2c_start;
			elsif wr_strobe_sig='1' then
				next_optype_sig <= I2C_WR;
				next_i2c_data_bit_out_sig <= '0';
				next_i2c_state_sig <=i2c_start;
			else
				next_optype_sig <= I2C_WR;	--default
				next_i2c_data_bit_out_sig <= '1';
				next_i2c_state_sig <=i2c_idle;
			end if;

		------------------------------------------------
		--start is data hi to lo folowed by clock rising edge
		-- then clock low
		-- data and clock hi on entry
		------------------------------------------------
		when i2c_start =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_ack_sig <= '1';

			if bitcnt_sig = X"00" then
				next_i2c_data_bit_out_sig <= '0';
				next_i2c_clock_sig <= '1';
				next_bitcnt_sig <= bitcnt_sig + '1';

				next_i2c_state_sig <=i2c_start;
			else
				next_i2c_data_bit_out_sig <= '0';
				next_i2c_clock_sig <= '0';
				next_bitcnt_sig <= (others =>'0');

				next_i2c_state_sig <=i2c_addrbit;
			end if;

		------------------------------------------------
		-- clock is low on entry - op the address bits
		-- (incl. opmode bits and register addres)
		-- MSB first. Set the data bit and then pulse clock hi/lo
		--
		------------------------------------------------
		when i2c_addrbit =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;
			next_bitcnt_sig <= bitcnt_sig;

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_clock_sig <= '0';
			next_i2c_ack_sig <= '1';
			next_i2c_data_bit_out_sig <= addr_sig((12-CONV_INTEGER(bitcnt_sig)) );	-- MSB first


			next_i2c_state_sig <=i2c_addrck;

		------------------------------------------------
		-- Update
		------------------------------------------------
		when i2c_addrck =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_ack_sig <= '1';
			next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig;

			if i2c_clock_sig = '1' then
				next_i2c_clock_sig <= '0';
				if bitcnt_sig = X"0C" then
					next_i2c_state_sig <=i2c_cmndbit;
					next_bitcnt_sig <= (others =>'0');
				else
					next_i2c_state_sig <=i2c_addrbit;
					next_bitcnt_sig <= bitcnt_sig + '1';
				end if;
			else
				next_bitcnt_sig <= bitcnt_sig;
				next_i2c_clock_sig <= '1';
				next_i2c_state_sig <=i2c_addrck;
			end if;



		------------------------------------------------
		--Set up the data line for the turn-around state
		--for a read, o/p is Z and data is don't care
		--for a write, o/p 10
		------------------------------------------------
		when i2c_cmndbit =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			if optype_sig = I2C_RD then
				next_i2c_dirn_sig <= I2C_RD;
			else
				next_i2c_dirn_sig <= I2C_WR;
			end if;

			next_rcvd_data_sig <= rcvd_data_sig;
			next_bitcnt_sig <= bitcnt_sig;
			next_i2c_data_bit_out_sig <=  not bitcnt_sig(0);
			next_i2c_clock_sig <= '0';
			next_i2c_ack_sig <= '1';

			next_i2c_state_sig <=i2c_cmndck;


		------------------------------------------------
		--Clock the turn-around state
		--
		------------------------------------------------
		when i2c_cmndck =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig;
			next_i2c_ack_sig <= i2c_ack_sig;
			next_i2c_dirn_sig <= i2c_dirn_sig;


			if i2c_clock_sig = '1' then
				next_i2c_clock_sig <= '0';
				if bitcnt_sig = X"01" then
					next_bitcnt_sig <= (others => '0');
					-- done, pick read or wr path
					if optype_sig = I2C_RD then
						next_i2c_state_sig <=i2c_rd_bit;
					else
						next_i2c_state_sig <=i2c_wr_bit;
					end if;
				else
					next_bitcnt_sig <= bitcnt_sig + '1';
					next_i2c_state_sig <=i2c_cmndbit;
				end if;
			else
				next_bitcnt_sig <= bitcnt_sig;
				next_i2c_clock_sig <= '1';
				next_i2c_state_sig <=i2c_cmndck;
			end if;


















		------------------------------------------------
		--	Write the data
		------------------------------------------------
		when i2c_wr_bit =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;
			next_bitcnt_sig <= bitcnt_sig;

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_data_bit_out_sig <= data_in_sig((15 -CONV_INTEGER(bitcnt_sig)) );	-- MSB first
			next_i2c_clock_sig <= '0';
			next_i2c_ack_sig <= '1';

			next_i2c_state_sig <=i2c_wrck;

		when i2c_wrck =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_ack_sig <= '1';

			--Hold the op data bit
			next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig;

			if i2c_clock_sig = '1' then
				next_i2c_clock_sig <= '0';
				if bitcnt_sig = 			X"0F" then
					next_bitcnt_sig <= (others => '0');
					next_i2c_state_sig <=i2c_stop;
				else
					next_bitcnt_sig <= bitcnt_sig + '1';
					next_i2c_state_sig <=i2c_wr_bit;
				end if;
			else
				next_i2c_clock_sig <= '1';
				next_bitcnt_sig <= bitcnt_sig;
				next_i2c_state_sig <=i2c_wrck;
			end if;



		------------------------------------------------
		--Shift data into received data latch
		--clock is low on entry
		--dirn = ip on entry
		------------------------------------------------
		when i2c_rd_bit =>
			next_busy_sig <= '1';
			next_optype_sig <=optype_sig;


			next_i2c_dirn_sig <= I2C_RD;
			next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig;
			next_i2c_ack_sig <= '1';


			if i2c_clock_sig = '1' then
				next_rcvd_data_sig <= rcvd_data_sig;
				next_i2c_clock_sig <= '0';
				if bitcnt_sig = X"0F" then
					next_bitcnt_sig <= (others => '0');
					next_i2c_state_sig <=i2c_stop;
				else
					next_bitcnt_sig <= bitcnt_sig + '1';
					next_i2c_state_sig <=i2c_rd_bit;
				end if;
			else
				next_i2c_clock_sig <= '1';
				next_bitcnt_sig <= bitcnt_sig;
				next_rcvd_data_sig(0) <= i2c_data_bit_in_sig;
				for i in 0 to 14 loop
					next_rcvd_data_sig(i+1) <= rcvd_data_sig(i);	-- MSB first;
				end loop;
				next_i2c_state_sig <=i2c_rd_bit;
			end if;


		------------------------------------------------
		-- rd/wr complete, clock is low on entry
		-- stop = Z data followed by clock high
		------------------------------------------------
		when i2c_stop =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_ack_sig <= '1';
			next_i2c_dirn_sig <= I2C_RD;


			if i2c_clock_sig = '0' then
				next_bitcnt_sig <= bitcnt_sig+'1';
				if bitcnt_sig = X"01" then
					next_i2c_clock_sig <= '1';
				else
					next_i2c_clock_sig <= '0';
				end if;
				next_i2c_data_bit_out_sig <= '1';
				next_i2c_state_sig <=i2c_stop;
			else
				next_bitcnt_sig <= (others =>'0');
				next_i2c_clock_sig <= '0';
				next_i2c_data_bit_out_sig <= '1';
				next_i2c_state_sig <=i2c_idle;
			end if;

	------------------------------------------------
		-- Write min 32 clock pulses, data hi
		-- to re-sync after a reset
		------------------------------------------------
		when i2c_init =>
			next_busy_sig <= '1';
			next_optype_sig <= optype_sig;

			next_rcvd_data_sig <= rcvd_data_sig;

			next_i2c_ack_sig <= '1';
			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_data_bit_out_sig <= '1';


			if i2c_clock_sig = '0' then
				next_i2c_clock_sig <= '1';
				if bitcnt_sig = X"30" then
					next_i2c_state_sig <=i2c_idle;
					next_bitcnt_sig <= (others =>'0');
				else
					next_i2c_state_sig <=i2c_init;
					next_bitcnt_sig <= bitcnt_sig;
			end if;
			else
				next_bitcnt_sig <= bitcnt_sig+'1';
				next_i2c_clock_sig <= '0';
				next_i2c_state_sig <=i2c_init;
			end if;




		------------------------------------------------
		--Catchall
		------------------------------------------------
		when others =>
			next_busy_sig <= '1';
			next_optype_sig <=optype_sig;

			next_i2c_ack_sig <= '1';
			next_rcvd_data_sig <= rcvd_data_sig;
			next_bitcnt_sig <= (others =>'0');

			next_i2c_dirn_sig <= I2C_WR;
			next_i2c_clock_sig <= '1';
			next_i2c_data_bit_out_sig <= '1';
			next_i2c_ack_sig <= '1';

			next_i2c_state_sig <=i2c_idle;


	end case;
end process;

------------------------------------------------------------------------------------------------










end; -- architecture  i2c
--********************************************************************************************