Forum Discussion

CMcga's avatar
CMcga
Icon for New Contributor rankNew Contributor
6 years ago

i2c master/slave interface, can you implement both on a board? [SOLVED]

Right now, I have a master entity that is sending data to the slave, and the slave is recognizing it and changing the LED according to the bit-stream I send. Hopefully that's not a fluke and is actually working because of the protocol. Here is my problem, when I try to do RD and WR signals, one of them always has to be high or I get that an error saying so and so is getting driven by SDA which has multiple drivers, which makes sense because of the way i2c works. My question is, can I actually implement both so I can practice i2c, or can I only use a master and then use slaves that come with the board already(e.g. the eeprom, accelerometer)?

--synthesis VHDL_INPUT_VERSION VHDL_2008 
library ieee;
 
use ieee.std_logic_1164.all;
 
entity i2c_slave0 is
   generic(
				slvAddress : std_logic_vector(6 downto 0) := "0000001";
				regAddress : std_logic_vector(7 downto 0) := "10000101"
			 );
	port(
			clk50,
			SCL,
			reset   : in    std_logic := '0';
			SDA     : inout std_logic := '1';
			output  : out   std_logic_vector(7 downto 0) := (others => '0')
		 );
end i2c_slave0; 
 
architecture FSM of i2c_slave0 is
 
type slaveState is (Idle, Slave_Address, Send_Ack1, Send_Ack2, Register_Address, Read_Data, Write_Data, Wait_Stop);
 
signal prev_State, next_State : slaveState; 
 
signal i      : natural range 0 to 8 := 0;
 
 
signal addr    : std_logic_vector(7 downto 0) := (others => '0');
signal tempRam     : std_logic_vector(7 downto 0) := "00000000";	 
signal ram         : std_logic_vector(7 downto 0) := (others => '0');
 
 
signal start, stop : std_logic := '0';
 
signal syncFF_clk : std_logic_vector(1 downto 0) := "00";
signal syncFF_sda : std_logic_vector(1 downto 0) := "00";
 
signal edgeDetFF_clk : std_logic := '0';
signal edgeDetFF_SDA : std_logic := '0';
 
signal rising_edge_clk,
       falling_edge_clk     : std_logic := '0';
 
signal regAddr : std_logic_vector(7 downto 0) := (others => '0');	   
 
begin
 
	--Synchronize data--
	process(clk50)
	begin
	if(rising_edge(clk50)) then
	syncFF_clk(0) <= SCL;
	syncFF_clk(1) <= syncFF_clk(0);
	
	syncFF_sda(0) <= SDA;                            -- <----------------- This is where the error occurs
	syncFF_sda(1) <= syncFF_sda(0);
	end if;
	end process;
	--------------------------------
	
	--======Edge detection of SCL=======--
	--Pipelining SCL--
	process(clk50)
	begin
	if(rising_edge(clk50)) then edgeDetFF_clk <= syncFF_clk(1);
	end if;
	end process;
	---------------------------------------
	--Rising/Falling edge detection--
	process(clk50)
	begin
	if(falling_edge(clk50)) then
	rising_edge_clk  <= syncFF_clk(1) and not edgeDetFF_clk;
	falling_edge_clk <= not syncFF_clk(1) and edgeDetFF_clk;
	end if;
	end process;
	--------------------------------------------------------------
	
	--======Start/Stop detection of SDA=======--
	--Pipelining SDA--
	process(all)
	begin
	if(rising_edge(clk50)) then edgeDetFF_SDA <= syncFF_sda(1);
	end if;
	end process;
	--------------------------------------------------------------
	
	--Stop/Start detection--
	process(all)
	begin
	
	   start <= '0';
		stop  <= '0';
		if(syncFF_clk(1) = '1' and edgeDetFF_clk = '1') then
		
			start <= not syncFF_SDA(1) and edgeDetFF_SDA;
			stop  <= syncFF_SDA(1) and not edgeDetFF_SDA;
		end if;
	end process;
	---------------------------------------------------------------
 
	
	process(all)
	begin
		if   (reset = '1') 			     then prev_State <= Idle;
											  i <= 0;
											
		elsif(rising_edge(clk50)) then prev_State <= next_State;	
			
			if(prev_State = Idle or start = '1') then i <= 0;
		
			elsif(falling_edge_clk = '1') then  
				if(i < 8) then i <= i + 1;
				else           i <= 0;
				end if;
			end if;
											
		end if; 
	end process;
 
	process(all)
	begin
	
	SDA <= 'Z';
		case (prev_State) is
			when Idle =>
								
								if(start = '1') then	next_State <= Slave_Address; 
								else                    next_State <= Idle;
								end if;
						
			when Slave_Address => 	
			
		
								if(falling_edge_clk = '1') then	
									 
									if   (i < 8) 			  then next_State <= Slave_Address;
									elsif((i = 8) and (addr(7 downto 1) = slvAddress)) then next_State <= Send_Ack1;
									else                 		 						    next_State <= Idle;
									end if;
								else 														next_State <= Slave_Address;
								end if;
			
			when Send_Ack1 =>
								
								
								  SDA <= '0'; 
								if(falling_edge_clk = '1') then	  
									
									if(addr(0) = '0') then    next_State <= Register_Address;
									else             		  next_State <= Read_Data;
									end if;
								else                		  next_State <= Send_Ack1;
								end if;
			
			when Register_Address =>
			
									if(falling_edge_clk = '1') then
										if   (i < 8) 									      then next_State <= Register_Address;
										elsif((i = 8) and (regAddr(7 downto 0) = regAddress)) then next_State <= Send_Ack2;
										else                 		 						    next_State <= Idle;
										end if;		
									else                                                        next_State <= Register_Address;
									end if;
								 
			
			when Send_Ack2 => 
								
								SDA <= '0';
							
								if(falling_edge_clk = '1') then next_State <= Write_Data;
								else                      		  next_State <= Send_Ack2;
								end if;
								
								
			when Write_Data =>
								 if(falling_edge_clk = '1') then
									if   (i < 8) 					then next_State <= Write_Data;
									else                    		     next_State <= Send_Ack1;
									end if;
								else                                   next_State <= Write_Data;
								end if;
							
								
			when Read_Data => 
								
							   SDA <= ram(8 - i);	
								
							   if(falling_edge_clk = '1') then
									if   (i < 8) 					then next_State <= Read_Data;
									else                     		     next_State <= Wait_Stop;
									end if;
								else 						   	  		  next_State <= Read_Data;
								end if;	 
			when Wait_Stop =>
			
							next_State <= Wait_Stop;
							 
		end case;			
 
 
		if   (start = '1')  then next_State <= Slave_Address;
		elsif(stop  = '1')  then next_State <= Idle;
		end if;
	
	end process;
 
 
	process(all)
	begin
	if(rising_edge(clk50)) then
		if(reset = '1') then addr <= (others => '0');
									tempRam <= (others => '0');
									regAddr <= (others => '0');
		elsif(rising_edge_clk) then
			if   (prev_State = Slave_Address)    then addr(8 - i)    <= SDA; 
			elsif(prev_State = Write_Data) 		 then tempRam(8 - i)     <= SDA;
			elsif(prev_State = Register_Address) then regAddr(8 - i) <= SDA;	
			end if;
		end if;	
		
		if(reset = '1') then
			ram <= (others => '0');
		elsif(prev_State = Write_Data and i = 8) then ram <= tempRam;	 
		end if;
		
	
		if(reset = '1') then
			output <= (others => '0');
		elsif(falling_edge_clk = '1') then
			if(prev_State = Write_Data and i = 8) then output <= ram;
			end if;
		end if;
		
	end if;
	end process;
	
end FSM;
--synthesis VHDL_INPUT_VERSION VHDL_2008 
library ieee;
 
use ieee.std_logic_1164.all;
 
 
 
entity black_box is
	port(
			clk50,
			reset  : in  std_logic	:= '0';
			output : out std_logic_vector(7 downto 0) := (others => '0')
		 );
end black_box;
 
 
architecture structural of black_box is
 
component i2c_master is
	generic( 
				clk_freq  : integer := 50_000_000;
				i2c_freq  : integer := 300_000;
				slaveWR0  : std_logic_vector(8 downto 0) := "000000010";
				slaveRD0  : std_logic_vector(8 downto 0) := "000000011";
				reg0WR0   : std_logic_vector(8 downto 0) := "010000101";
				reg0RD0   : std_logic_vector(8 downto 0) := "010000101";
				slvData0  : std_logic_Vector(8 downto 0) := "001110101"
			  );
	port(
			clk50,
		--	slvAddress,
			WR, RD,
			reset   : in    std_logic;
			SCL     : out   std_logic;
			SDA     : inout std_logic
		--	output  : out   std_logic_vector(7 downto 0) := (others => '0')
		 );
end component;
 
 
component i2c_slave0 is
   generic(
				slvAddress : std_logic_vector(6 downto 0) := "0000001";
				regAddress : std_logic_vector(7 downto 0) := "10000101"
			 );
	port(
			clk50,
			SCL,
			reset   : in    std_logic;
			SDA     : inout std_logic;
			output  : out   std_logic_vector(7 downto 0) := (others => '0')
		 );
end component;
 
signal SCL, SDA		: std_logic := '0';
 
signal oneSec : std_logic := '0';
 
signal wr, rd : std_logic := '0';
begin
 
	process(clk50) 
	variable counter : natural range 0 to 50_000_000;
	begin
	
	if(rising_edge(clk50)) then counter := counter + 1;
		if(counter = 50_000_000) then counter := 0;
												oneSec <= not oneSec;
		end if;
	end if;
	
	end process;
 
	process(oneSec)
	begin
	
	if(rising_edge(oneSec)) then 
	WR <= '0';
	RD <= '0';
	end if;
	end process;
	
	mstr: i2c_master port map (clk50, WR, RD, reset, SCL, SDA);
	slv : i2c_slave0 port map (clk50, SCL, reset, SDA, output);
end structural;

EDIT: So you can't do it within the FPGA, which makes sense, so what i ended up doing was using the I/O pins. Worked perfectly after that!

2 Replies

  • CMcga's avatar
    CMcga
    Icon for New Contributor rankNew Contributor
    --synthesis VHDL_INPUT_VERSION VHDL_2008 
    library ieee;
     
    use ieee.std_logic_1164.all;
     
    entity i2c_master is
    	generic( 
    				clk_freq  : integer := 50_000_000;
    				i2c_freq  : integer := 300_000;
    				slaveWR0  : std_logic_vector(8 downto 0) := "000000010";
    				slaveRD0  : std_logic_vector(8 downto 0) := "000000011";
    				reg0WR0   : std_logic_vector(8 downto 0) := "010000101";
    				reg0RD0   : std_logic_vector(8 downto 0) := "010000101";
    				slvData0  : std_logic_Vector(8 downto 0) := "001100101"
    			  );
    	port(
    			clk50,
    		--	slvAddress,
    			WR, RD,
    			reset   : in    std_logic := '0';
    			SCL     : out   std_logic := '0';
    			SDA     : inout std_logic := '0'
    		--	output  : out   std_logic_vector(7 downto 0) := (others => '0')
    		 );
    end i2c_master; 
     
    architecture FSM of i2c_master is
     
    type masterState is (
    						  Idle, Start, Slave_Address, Ack1, Stop, Hold,
                        WR_Address, Ack2, Write_Data, Ack3,
    						  RD_Address, Ack4, restartL, restartR, Initiate_Read, Ack5, Read_Data, noAck
    						  );
     
    signal prev_State, next_State : masterState; 
     
    signal i2c_clk     : std_logic := '0';
     
    signal i, iReg     : natural range 0 to 8;
    signal j, jReg     : natural range 0 to 7;
     
    signal storeData : std_logic_vector(7 downto 0) := (others => '0');
    signal wr_Reg     : std_logic := '0';
     
    --constant slaveAddress0     : std_logic_vector(7 downto 0) := slaveWR0;
    begin
     
    	
    	process(i2c_clk, reset)
    	begin
    	
    		if   (reset = '1') 			   then prev_State <= Idle;
    													  iReg <= 0;
    													  jReg <= 0;
    											  
    		elsif(falling_edge(i2c_clk)) then prev_State <= next_State;
    												   iReg <= i;
    													jReg <= j;
    		
    		end if;
    	
     
    	end process;
     
    	process(all)
    	variable clk_counter : natural range 0 to clk_freq/(2*i2c_freq);
    	begin
    	
    		if(rising_edge(clk50)) then clk_counter := clk_counter + 1;
    			if(clk_counter = clk_freq/(2*i2c_freq)) then i2c_clk <= not i2c_clk;
    																		clk_counter := 0;
    			end if;
    		end if;
    	end process;
    	process(all)
    	begin
    	SCL <= i2c_clk;
    	
    	i <= 0;
    	j <= 0;
    	
    		case (prev_State) is
    			when Idle =>
    								SCL <= '1';
    								SDA <= '1';
    								
    								if(WR = '1') then next_State <= Start;
    								else              next_State <= Idle;
    								end if;
    						
    			when Start => 
    							
    								SCL <= '1';
    								SDA <= '0';
    								next_State <= Slave_Address;
    			
    			when Slave_Address =>
     
    								 
    								SDA <= slaveWR0(8-i); 
    							 
    								i <= iReg + 1;
    								
    								if(i = 8) then next_State <= Ack1; 
    								else           next_State <= Slave_Address;
    											   
    								end if;
    			
    			when Ack1 =>
    								
    								SDA <= 'Z';
    								
    								if(wr_Reg = '1') then next_State <= WR_Address;
    								else       		 	  next_State <= RD_Address;
    								end if;
    			
    			when Stop =>
    								
    								SDA <= '0';
    								
    								next_State <= Hold;
    								
    			when Hold => 		 
    								SCL <= '1';
    								SDA <= '1';
    								if(wr = '0' and rd = '0') then next_State <= Idle;
    								else                				 next_State <= Hold;
    								end if;
    			
    			when WR_Address =>
    			
    								 SDA <= reg0WR0(8-i);
    								 
    								
    								i <= iReg + 1;
    								
    								if(i = 8) then next_State <= Ack2;
    								else           next_State <= WR_Address;
    								end if;
    			
    			 when Ack2 =>
    								
    								SDA <= 'Z';
    								next_State <= Write_Data;
    			
    			 when Write_Data =>
    					
    								 SDA <= slvData0(8-i); 
    				 				 
    								
    								
    								i <= iReg + 1;
    								
    								if(i = 8) then next_State <= Ack3;
    								else           next_State <= Write_Data;
    								end if;
    			  
    			 when Ack3 =>
    							
    								SDA <= 'Z';
    								
    								next_State <= Stop;
    			
    				
    			 when RD_Address =>
    			 
    			 			
    			 					  SDA <= reg0RD0(8-i);
    				 			 
    								
    								
    								i <= iReg + 1;
    								
    								if(i = 8) then next_State <= Ack4;
    								else           next_State <= RD_Address;
    								end if;
    								
    			 when Ack4 =>
    			 
    							 SDA <= 'Z';
    							 
    							 next_State <= restartL;
    			
    			 when restartL =>
    			 
    							 SCL <= '0';
    							 SDA <= '1';
    							 
    							 next_State <= restartR;
    				
    			 when restartR =>
    			 
    							SCL <= '1';
    							SDA <= not i2c_clk;
    							
    							next_State <= Initiate_Read;
    							
    				
    			 when Initiate_Read =>
    			 
    						
    			 				SDA <= slaveRD0(8-i);
    						 
    							
    							i <= iReg + 1;
    							
    							if(i = 8) then next_State <= Ack5;
    							else           next_State <= Initiate_Read;
    							end if;
    							
    			when Ack5 =>
     
    							SDA <= 'Z';
    							next_State <= Read_Data;
    							
    			when Read_Data =>
    						
    							SDA <= 'Z';
    							
    							i <= iReg + 1;
    							
    							if(i = 8) then next_State <= noAck;
    							else           next_State <= Read_Data;
    							end if;
    		
    		 when noAck =>
    		 
    							SDA <= '1';
    							next_State <= Stop;				
    		end case;
    	end process;
    	
    	process(clk50)
    	begin
    		
    		if(rising_edge(clk50)) then 
    			if(prev_State = Start) then wr_Reg <= WR;
    			end if;
    		end if;
    	end process;
    	
    	process(i2c_clk)
    	begin
    	
    	if(rising_edge(i2c_clk)) then 
    		if(prev_State = Read_Data) then storeData(8-i) <= SDA;
    		end if;
    	end if;
    --	
    --	if(falling_edge(i2c_clk)) then
    --		if(prev_State = Read_Data and i = 8) then output <= storeData; 
    --		end if;
    --	end if;
    	end process;
    	
     
    end FSM;

    Here is the master just in case.

  • Hi, Your design is accessing SDA from different (unnamed) processes.

    So write SDA just in one process, in case signal from one process to other what to do on SDA.

    Curing this, can work anyway but you are not guaranteed external devices are high state. To avoid contention, never drive SDA nor SCK High, drive 'Z' or '0'.

    FPGA work better on syncronous design, avoid ripple carry or clock crossing domain, generate enable instead.

    it is optional but is better style name every process with a name. This can help identify logic and help read code:

    example U1_SCK_edge_detect : process(clk, reset)

    On my old books where written to avoid elsif rising_edge, goggling around also college site I seen a lot of example using

    if reset then
     
     elsif rising_edge(clk) then

    This was discouraged on old school with nested if then else instead of if then elsif:

    [processname:] process(clk,reset)
    begin
     if reset then
     
      {reset action}
    else
     if risng_edge(clk) then
       [if clk_enable then else end if]
     
    end if;
    end if;
    end process;

    Aout this no idea if exhibit side effect.

    If this cannot solve your issue ask again.

    Regards