UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Visitor sirranrap
Visitor
14,610 Views
Registered: ‎03-03-2013

I2C protocol In VHDL

So this is my second attempt to write the I2C protocol and I have learned a few important things. I believe I am very close to getting this working but have gotten to a point where I have no clue what I may be doing wrong. I have set up 3 indicators to test for slave acknowledgements and 3 indicators to display whether data is above or below nominal or zero. I get data being zero.

 

 

My code with test bench and breakdown to follow:

 

 

 

 

Library IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;


Entity I2C_Controller IS
			PORT(
					clk : in std_logic;
					scl : out std_logic;
					sda : inout std_logic;
					switch : in std_logic;
					LEDs : out std_logic_vector(7 downto 0)
				 );
END I2C_Controller;

Architecture fsmd of I2C_Controller IS

		signal slaveAddress_read : std_logic_vector(7 downto 0):= "01010001";
		signal slaveAddress_write : std_logic_vector(7 downto 0):= "01010000";
		signal registerSettings : std_logic_vector(7 downto 0):= "00101000";
		signal SCL_Clk : std_logic := '0';
		signal SCLout : std_logic := '0';
		signal clk_cnt : integer range 0 to 250 := 0;
		signal bitcount : integer range 0 to 20 := 0;
		signal data : std_logic_vector(11 downto 0) := "110000011000";
		signal DataIndicator : std_logic_vector(4 downto 0) := "00000";
		signal state : std_logic_vector(7 downto 0) := x"00";
		signal sda01 : std_logic := '0';
		signal slaveACK : std_logic := '0';
		signal errors : std_logic_vector(3 downto 0) := "0000";

BEGIN

sda <= 'Z' when sda01 = '1' else '0'; -- converts sda01 from 0 or 1 to 0 or Z
												  -- Z being high impedence
												  
scl <= SCLout;

process(clk)
begin
		If rising_edge(clk) then
			if clk_cnt < 250 then
				clk_cnt <= clk_cnt +1;
			else
				clk_cnt <= 0;
			end if;
			
			if clk_cnt < 125 or clk_cnt = 125 then
				SCL_Clk <= '0';
			elsif clk_cnt > 125 then
				SCL_Clk <= '1';
			end if;
		end if;
end process;


PROCESS(SCL_Clk)
BEGIN


	if(rising_edge(SCL_Clk)) then
		CASE state IS
--------------------------------------------IDLE CONDITION													
		when x"00" =>  -- idle
			SCLout <= '1';	-- SCL = 1
			sda01 <= '1'; -- SDA = 1
			state <= x"01";
--------------------------------------------START CONDITION			
		when x"01" =>  -- start condition
			SCLout <= '1';	-- SCL stays 1 while
			sda01 <= '0'; -- SDA transitions low
			bitcount <= 7;
			state <= x"02";
--------------------------------------------WRITE ADDRESS 
		when x"02" =>  -- sda transition state
			SCLout <= '0';	-- when scl low
			sda01 <= slaveAddress_write(bitcount);
			state <= x"03";

		when x"03" =>  -- write address state prt2
			SCLout <= '1';
			sda01 <= slaveAddress_write(bitcount);
				if bitcount - 1 >= 0 then
					bitcount <= bitcount -1;
					state <= x"02";
				else
					bitcount <= 7;
					state <= x"04";
				end if;
----------------------------------------------SLAVE ACK			
		when x"04" =>  -- slave ack bit prt1
			SCLout <= '0';	-- SCL = 1
			sda01 <= '1'; -- SDA = 1
			state <= x"05";		

		when x"05" =>  -- slave ack bit prt2
			SCLout <= '1';	-- SCL = 1
			slaveACK <= sda; -- 0 = ack, 1 = error
				if sda = '1' then
					state <= x"EE";
					errors <= "1000";
				else 
					state <= x"06";
				end if;
------------------------------------------------WRITE TO REGISTER				
		when x"06" =>  -- sda transition state
			SCLout <= '0';	-- when scl low
			sda01 <= registerSettings(bitcount);
			state <= x"07";

		when x"07" =>  -- write register state prt2
			SCLout <= '1';
				if bitcount - 1 >= 0 then
					bitcount <= bitcount -1;
					state <= x"06";
				else
					bitcount <= 7;
					state <= x"08";
				end if;
-----------------------------------------------SLAVE ACK
		when x"08" =>  -- slave ack bit prt1
			SCLout <= '0';	-- SCL = 1
			sda01 <= '1'; -- SDA = 1
			state <= x"09";		

		when x"09" =>  -- slave ack bit prt2
			SCLout <= '1';	-- SCL = 1
			slaveAck <= sda; -- 0 = ack, 1 = error
				if sda = '1' then
					state <= x"EE";
					errors <= "0100";
				else 
					state <= x"10";
				end if;	
-----------------------------------------------STOP CONDITION

		when x"10" =>  -- stop
			SCLout <= '0';	-- SCL = 1
			sda01 <= '0'; -- SDA = 1
			state <= x"11";
			
		when x"11" =>  -- 
			SCLout <= '1';	-- SCL  1 while
			sda01 <= '0'; -- SDA stays low
			state <= x"12";

		when x"12" =>  -- stop
			SCLout <= '1';	-- SCL stays 1
			sda01 <= '1'; -- SDA transitions to 1
			state <= x"13";
------------------------------------------------START CONDITION
		when x"13" =>  -- idle
			SCLout <= '1';	-- SCL = 1
			sda01 <= '1'; -- SDA = 1
			state <= x"14";
		
		when x"14" =>  -- start condition
			SCLout <= '1';	-- SCL stays 1 while
			sda01 <= '0'; -- SDA transitions low
			bitcount <= 7;
			state <= x"15";
--------------------------------------------WRITE ADDRESS 
		when x"15" =>  -- sda transition state
			SCLout <= '0';	-- when scl low
			sda01 <= slaveAddress_read(bitcount);
			state <= x"16";

		when x"16" =>  -- write address state prt2
			SCLout <= '1';
				if bitcount - 1 >= 0 then
					bitcount <= bitcount -1;
					state <= x"15";
				else
					bitcount <= 7;
					state <= x"17";
				end if;
----------------------------------------------SLAVE ACK			
		when x"17" =>  -- slave ack bit prt1
			SCLout <= '0';	-- SCL = 1
			sda01 <= '1'; -- SDA = 1
			state <= x"18";		

		when x"18" =>  -- slave ack bit prt2
			SCLout <= '1';	-- SCL = 1
			slaveAck <= sda; -- 0 = ack, 1 = error
			bitcount <= 3;
				if sda = '1' then
					state <= x"EE";
					errors <= "0010";
				else 
					state <= x"19";
				end if;
--------------------------------------------IDLE CONDITION	(4 bits -- 2 zeros, 2 channel indicators)												
		when x"19" =>  -- idle
			SCLout <= '0';	-- SCL = 1
			sda01 <= '1';
			state <= x"20";
		
		when x"20" =>  -- start condition
			SCLout <= '1';	-- SCL stays 1 while
			
				if bitcount - 1 >= 0 then
					bitcount <= bitcount -1;
					state <= x"19";
				else
					bitcount <= 3;
					state <= x"21";
				end if;
------------------------------------------------read 4 MSB from converter				
		when x"21" =>  -- sda transition state
			SCLout <= '0';	-- when scl low
			sda01 <= '1';
			state <= x"22";

		when x"22" =>  -- write register state prt2
			SCLout <= '1';
			sda01 <= '1';
				if bitcount - 1 >= 0 then
					data(8 + bitcount) <= sda;
					bitcount <= bitcount -1;
					state <= x"21";
				else
					data(8 + bitcount) <= sda;
					bitcount <= 7;
					state <= x"23";
				end if;
----------------------------------------------MASTER ACK			
		when x"23" =>  -- ack bit prt1
			SCLout <= '0';	-- SCL = 1
			sda01 <= '0'; -- SDA = 1
			state <= x"24";		

		when x"24" =>  -- ack bit prt2
			SCLout <= '1';	-- SCL = 1
			sda01 <= '0'; -- SDA = 
			bitcount <= 7;
			state <= x"25";
------------------------------------------------read 8 LSB from converter				
		when x"25" =>  -- sda transition state
			SCLout <= '0';	-- when scl low
			sda01 <= '1';
			state <= x"26";

		when x"26" =>  -- loops back to stop condition to take
			SCLout <= '1';	-- another 12 bits of data
			sda01 <= '1';
				if bitcount - 1 >= 0 then
					data(bitcount) <= sda;
					bitcount <= bitcount -1;
					state <= x"25";
				else
					data(bitcount) <= sda;
					bitcount <= 7;
					state <= x"10";
				end if;	
	
		when others =>
			SCLout <= '1';
			sda01 <= '1';
			state <= x"00";
		END CASE;
	end if;

end process;

  LEDs(7 downto 5) <= errors(3 downto 1);
  LEDs(4 downto 0) <= DataIndicator;
  
process(clk) 
 begin 	
		if(rising_edge(clk)) then 
			if data > "001100100000" then
				DataIndicator <= "00001";
			elsif data < "001100100000" and data > "000000000000" then
				DataIndicator <= "10000";
			else
				DataIndicator <= "00100";
			end if;
		end if; 
 end process;
 
end fsmd;

 

 

 

 

Phase One of I2C protocol.jpg

 

 

 

 

Phase Two of I2C protocol.jpg

 

I am hoping someone can see where I might be going wrong here and point me in the right direction

7 Replies
Instructor
Instructor
14,600 Views
Registered: ‎08-14-2007

Re: I2C protocol In VHDL

The one thing that pops up right away is that you don't seem to have any pull-up

in your test bench, so when SDA should be (weak) 1, it shows up as 'Z'.  Maybe

that causes your data to always read zero?

 

-- Gabor

-- Gabor
0 Kudos
Visitor sirranrap
Visitor
14,597 Views
Registered: ‎03-03-2013

Re: I2C protocol In VHDL

The slave is the PMOD AD2 which should have 5.6k pull up resistors, but I am not sure how to represent it in the simulation. Going through the test bench against the manuals for the I2C the timing seems to meet the specifications.

0 Kudos
Mentor hgleamon1
Mentor
14,594 Views
Registered: ‎11-14-2011

Re: I2C protocol In VHDL

In your testbench, you can set the data line, as a std_logic, to be 'H'. This is a pullup (as far as I know). This will allow the line to be driven low but will "override" the 'Z' assignment.

 

I have used this in a testbench that used two Xilinx I2C IPs, so I'm pretty confident it can work.

 

Regards,

 

Howard

----------
"That which we must learn to do, we learn by doing." - Aristotle
Historian
Historian
14,580 Views
Registered: ‎02-25-2008

Re: I2C protocol In VHDL


@sirranrap wrote:

The slave is the PMOD AD2 which should have 5.6k pull up resistors, but I am not sure how to represent it in the simulation. Going through the test bench against the manuals for the I2C the timing seems to meet the specifications.


You really need to have a model of the I2C device that lives on the PMOD,  otherwise your simulation is meaningless.

 

To model the pullup resistor you simply do the following assignment in the test bench:

 

scl <= 'H';

sda <= 'H';

 

which emulates a weak pullup. Since std_logic is a resolved type, for every assignment to scl and sda the resolution function will be called and whichever value is the strongest will win.

 

When your code does the 

 

sda <= '0' when sda_int = '0' else 'Z';

 

and sda_int is not '0', sda is assigned to 'Z', and the 'H' is a stronger value so when you look at the signal in the waveform you will never see 'Z', only 'H' or '0'. Of course the I2C slave can drive a '0' on the bus too so when the master floats the bus (drives 'Z', bus will be 'H'), that '0' wins and the bus will be '0' too.

 

You still haven't explained your goofy state encoding.

----------------------------Yes, I do this for a living.
Visitor sirranrap
Visitor
14,575 Views
Registered: ‎03-03-2013

Re: I2C protocol In VHDL

The state assignments could more than likely be alot more eliquent, but I was going for step by step jamming each series of bits so that I would fully understand what I was doing. But I'll go through each briefly:

 

all states lead directly to the next unless an indicated loop to control scl while transmitting or receiving data&colon;

 

State x"00" : initial condition sda and scl both 1

state x"01" : Start condition, sda transitions low while scl remains high

state x"02" and state x"03" : are a loop to control scl while I pump out slave_address

state x"04" prepares for and state x"05" receives SlaveAck and produces an error if none is had

state x"06" and x"07 : another loop  to control scl while I pump out the register settings

------------------------This is to control an adc, the register sets channels and ref voltage and such

state x"08" and x"09" again prepares for and receives SlaveAck and produces an error if none is had

states x"10" through x"12" produces the stop signal scl remains high while sda transitions from low to high

state x"13" and x"14" : another Start condition, sda transitions from high to low, while scl remains high

state x"15" and x"16" : another loop  to control scl while I pump out the slave_address again

state x"17" and x"18" again prepares for and receives SlaveAck and produces an error if none is had

state x"19" and x"20" idles for 4 bits while the adc sends 2 zeros and 2 bits of channel configuration data

------------------------This will be important when I want to set up multiple channels but while I am testing

------------------------I am only doing 1 channel and these two bits shouldnt be important

states x"21" and x"22" loops to receive 4 MSB

states x"23" and x"24"sends master Ack bit

states x"25" and x"26" loops to receive 8 LSB then sends FSM back to state 10 to stop and take another reading.

 

 

These can be condensed I am aware to alow fewer states but like I said just wanted everything to be step by step.

 

So I have implemented the weak high in the test bench: 

 

I2C with simulated pullup resistors.jpg

 

It seems to produce the same signal that I had before with 2 caveats:

1. I changed the register settings, nothing important

2 It produces a noACK for the I2C slave, which is to be expected.

 

0 Kudos
Mentor hgleamon1
Mentor
14,554 Views
Registered: ‎11-14-2011

Re: I2C protocol In VHDL

You really need to have a model of the I2C device that lives on the PMOD,  otherwise your simulation is meaningless.

 

--

 

Precisely. I have read (and re-read) this thread twice now and I can't find out exactly what the problem is. You say you get "data being zero" but I can't see where you mean this and, as pointed out, without the "other end" actually responding, what sort of data or response are you expecting?

 

Given your LEDs value is "00000001", your own code seems to suggest that the received data is always a 1 (or greater than "001100100000", anyway) which would be entirely expected in my opinion. As you haven't posted your testbench I can't comment on how you are driving the SDA line when the Master is expecting data in return ...

----------
"That which we must learn to do, we learn by doing." - Aristotle
0 Kudos
Historian
Historian
14,544 Views
Registered: ‎02-25-2008

Re: I2C protocol In VHDL


@sirranrap wrote:

The state assignments could more than likely be alot more eliquent, but I was going for step by step jamming each series of bits so that I would fully understand what I was doing.

 


You would more fully understand what you're doing if your state register was an enumerated type. Then, instead of having a state X"00" and a comment saying "initial condition sda scl both 1" and a state X"01" and a comment "start condition, SDA transitions low ..." you could have

 

    when I2C_IDLE => 

    when I2C_START =>

    when I2C_SENDADDR =>

    when I2C_ADDRACK =>

 

etc etc etc.

 

That's a lot more maintainable, and of course the bonus is that the waveform display will show the enumeration value, not just a hex number, so you know exactly what state you're in.

 

(BTW: your code would never pass a professional review for the reason I just presented.)

 

And AGAIN: if your test bench DOES NOT have a model of the slave device on the PMOD, you're wasting your time as well as ours trying to help you.

 

Come back to us when your test bench isn't a bunch of handwaving.

----------------------------Yes, I do this for a living.