VHDL I2C Example 2

------------------------------------------------------------------------------
-- i2c slave receiver
------------------------------------------------------------------------------
-- Intended target: Xilinx CoolRunner-II CPLD  (XC2C64A)
-- Development tools: XILINX ISE 7.1i webpack
-- Author:  DMITRY PETROV
-- Notes:
-- Date:     11-09-2005
-- Revision: 1.0
------------------------------------------------------------------------------
-- This code implements i2c slave which is able to receive a data byte.
 
-- i2c message has 3 parts:
--  22h
--  00h
--  XX 
-- If Device and Sub Addresses are matched the data byte will be accepted.

-- Because of SCL line used as clock for i2c state machine, slow SCL changes 
-- will make noise and invalid data reception. 

-- To avoid noise of slow SCL - usualy used an external CLOCK, for 
-- clocking all modules but it will take some amount of CPLD's macrocells.

-- Another way is to use an SCHMITT TRIGGER on SCL and SDA. 
-- Forexample XILINX CoolRunner-II CPLD, has SCHMITT TRIGGER on it's I/O.
-- By default this function is deactivated, PLEASE ACTIVATE IT !

-- If your PLD have no SCHMITT TRIGGER function, you may use solution wich 
-- require 2 resistors and aditional output pin. 
-- Here's an old Xilinx app note about it: 
-- http://www.xilinx.com/xcell/xl19/xl19-34.pdf 
-- ===========================================================================
-- DISCLAIMER: This code is FREEWARE which is provided on an ìas isî basis, 
-- YOU MAY USE IT ON YOUR OWN RISK, WITHOUT ANY WARRANTY. 
-- ===========================================================================d

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

------------------------------------------------------------------------------
entity i2cs_rx is
	generic(
		WR       : std_logic:='0';
		DADDR		: std_logic_vector(6 downto 0); --:= "0010001";		   -- 11h (22h) device address
		ADDR		: std_logic_vector(7 downto 0)  --:= "00000000"		   -- 00h	    sub address		
	);
	port(
		RST		: in std_logic;
		SCL		: in std_logic;
		SDA		: inout std_logic;
		DOUT 		: out std_logic_vector(7 downto 0)			   -- Recepted over i2c data byte
	);
	--SCHMITT TRIGGER activation (folowing 3 strings should be uncommented)
	--attribute SCHMITT_TRIGGER: string; 
	--attribute SCHMITT_TRIGGER of SCL: signal is "true"; 
	--attribute SCHMITT_TRIGGER of SDA: signal is "true";
end i2cs_rx;

------------------------------------------------------------------------------
architecture Behavioral of i2cs_rx is
	signal DOUT_S: std_logic_vector(7 downto 0);
	signal SDA_IN, START, START_RST, STOP, ACTIVE, ACK	: std_logic;
	signal SHIFTREG	: std_logic_vector(8 downto 0);
	signal STATE : std_logic_vector(1 downto 0);		-- 00 - iddle state	
																			-- 01 - DADDR  compare
																			-- 10 - ADDR compare
																			-- 11 - DATA read
begin

------------------------------------------------------------------------------
-- start condition detection, method 1 ( good noise tolerance )

   process (SDA_IN, START_RST)
   begin
      if (START_RST = '1') then
         START <= '0';    
      elsif (SDA_IN'event and SDA_IN = '0') then
         START <= scl;    
      end if;
   end process;

   process (SCL, START, STOP)
   begin
		if (SCL'event and SCL = '0') then
         START_RST <= START;    
      end if;
   end process;

------------------------------------------------------------------------------
-- start condition detection, method 2 ( simple - but week against noise )
--process (RST, SCL, SDA_IN)
--begin
--	if RST = '0' or SCL = '0' then
--		START <= '0';
--	elsif SCL = '1' and SDA_IN = '0' and SDA_IN'event then
--		START <= '1';
--	end if;
--end process;

------------------------------------------------------------------------------
-- stop condition detection
process (RST, SCL, SDA_IN, START)
begin
	if RST = '0' or SCL = '0' or START='1' then
		STOP <= '0';
	elsif SCL = '1' and SDA_IN = '1' and SDA_IN'event then
		STOP <= '1';
	end if;
end process;

------------------------------------------------------------------------------
-- "active communication" signal 
process (RST, STOP, START)
begin
	if RST = '0' or STOP = '1' then	 --or (SHIFTREG="000000001" and ACK = '0' and SCL='1' and SCL'event) 
		ACTIVE <= '0';
	elsif START = '0' and START'event then
		ACTIVE <= '1';
	end if;
end process;

------------------------------------------------------------------------------
-- RX data shifter
process (RST, ACTIVE, ACK, SCL, SDA_IN)
begin 
if RST = '0' or ACTIVE = '0' then
	SHIFTREG <= "000000001";	
elsif SCL'event and SCL = '1' then
	if ACK = '1' then
		SHIFTREG <= "000000001";
	else
		SHIFTREG(8 downto 0) <= SHIFTREG(7 downto 0) & SDA_IN;
	end if;
end if;							  
end process;

------------------------------------------------------------------------------
-- I2C data read
process (RST, STATE, ACK, SHIFTREG)
begin
if RST = '0' then
	DOUT_S <= "00000000";
elsif STATE="11" and ACK='1' and ACK'event then 
	DOUT_S <= SHIFTREG(7 downto 0);
end if;
end process; 

------------------------------------------------------------------------------
-- ACK
process (RST, SCL, SHIFTREG, STATE, ACTIVE)
begin
if RST = '0' or ACTIVE = '0' then
	ACK <= '0';
	STATE <= "00";
elsif SCL='0' and SCL'event then 
	if SHIFTREG(8) = '1' and STATE/="11" then
		STATE <= STATE + 1;
		if ((STATE="00" and SHIFTREG(7 downto 0) = DADDR & WR) or (STATE="01" and SHIFTREG(7 downto 0) = ADDR) or STATE="10") then 
			ACK <= '1';
		else
			STATE <= "11";
		end if;
	else
	   ACK <= '0';
	end if;
end if;
end process;

------------------------------------------------------------------------------
-- ACK responce
SDA_IN <= SDA;
SDA <= '0' when ACK = '1' else 'Z';

------------------------------------------------------------------------------
DOUT(7 downto 0) <= DOUT_S(7 downto 0);

end Behavioral;
------------------------------------------------------------------------------

Leave a Reply

Your email address will not be published. Required fields are marked *