Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Implement I2C in VHDL

Status
Not open for further replies.
There is nothing intrinsically better in the 2-process style. Why is the one process a failing?? And "aren't designed for HW development"?? That's EXACTLY what they were designed for; the H in HDL stands for hardware.

You have next state and next value logic directly in two process style. The one process style make combinatorial outputs -- even used within the same module -- difficult. My point is that the HDLs could support two process but also get rid of most of the boilerplate automatically. It might mean a special class of signals that don't support delays or multiple drivers, but those are mostly for simulation and not used internally.
 

You have next state and next value logic directly in two process style. The one process style make combinatorial outputs -- even used within the same module -- difficult. My point is that the HDLs could support two process but also get rid of most of the boilerplate automatically. It might mean a special class of signals that don't support delays or multiple drivers, but those are mostly for simulation and not used internally.
It would be nice and meaningful if we can stick to the OPs problem and related stuff.
 

I don't see a problem of HDL capabilities, you can write whatever you consider readable, e.g. have combinational and registered signal assignments, also different clock edges (using different signals, of course) in a single process. The question is, which hardware structure is appropriate and how can it be efficiently described.
 

OP here again bringing some news.

Yesterday I achieved to send some info to the I2C GPIO Expander. The problem was I had the "repeated start" continuosly, but I made the communication and it was outputing voltage through the GPIO pins, so the ports were configurated and the value too.

Today I fixed that and I have a working Stop Condition, but I see some glitches I will try to correct.
From now I will implement the read function, but at least I continue advancing on the project, so great. I will come back for more doubts or to post my work.

Cya soon and thanks for your help and support.

CapturaI2C_Envio.PNG
 

OP here again. I'm messing it with the tempos and I don't know why. Could anybody throw some light? I have been working on it but I don't realize what am I doing wrong.
From the last time I posted here I have generated a reader and mixed both modules into one, to create the I2C-master. Now I can write and read from the board in the same module, but my algorithm is not well implemented.
Here I post two chronograms I have taken.
1_.PNG
On this picture I marked those black edges. It is part of the ACK from the board. But it should be on more places, I marked it with an interrogation symbol (?).
2_.PNG
On this one I see the clock desynchronizes, I still don't know why.
I uploaded my code to github, just in case you want to see it.
Moderator action: deleted links to external severs
Thanks in advance.
 
Last edited by a moderator:

I posted the code on GitHub because I thought it could help the list of modifications, anyway, if it is forbidden to post links to external sites, I will respect that.
This is my code:

Master
Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;


entity masterI2C_RW is
    Port( 
		CLK_50: IN  STD_LOGIC;								--Reloj a 50Mhz
		RST 	: IN  STD_LOGIC;
		ADD	: IN  STD_LOGIC_VECTOR (6 DOWNTO 0); 	--Address: Dirección del dispositivo.
		COM	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0); 	--Command: Tipo de orden a enviar.
		DAT	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0); 	--Data: Información a enviar.
		GO		: IN  STD_LOGIC;								--Inicio.
		RW		: IN  STD_LOGIC;								--Bit de L/E. 0=Write; 1=Read;
		SPEED : IN  STD_LOGIC;								--Velocidad de reloj. 0=100kHz; 1=400kHz.
		RDNUM : IN  INTEGER;									--Numero de registros a leer.
		BUSY	: OUT STD_LOGIC;								--Ocupado. No puede atender nuevas peticiones.
		DOUT	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);	--Salida de datos. Cuando hay operación de lectura saca ese resultado.
		SCLK	: OUT STD_LOGIC;								--Señal de reloj para la sincronización del I2C. 100kHz.
		SDAT	: INOUT STD_LOGIC);							--Señal de datos generada. Trama a enviar al dispositivo.
end masterI2C_RW;


architecture a of masterI2C_RW is

	--Estados
	TYPE estados is (e0, e1, e2, e3, e4, e5, e6, e7, e8);
	SIGNAL ep : estados :=e0; 	--Estado Presente
	SIGNAL es : estados; 		--Estado Siguiente
	
	--Señales de control de la máquina de estados
	signal idle 	: std_logic := '0';
	signal start 	: std_logic := '0';
	signal config	: std_logic := '0';
	signal cmd		: std_logic := '0';
	signal data		: std_logic := '0';
	signal ack 		: std_logic := '0';
	signal stop		: std_logic := '0';
	signal conf_rdy: std_logic := '0';
	signal cmd_rdy	: std_logic := '0';
	signal data_rdy: std_logic := '0';
	signal ack_tx	: std_logic := '0';				--Acknowledge transmission. Estado de ACK en el que el MAESTRO tiene que responder.
	signal ack_rx	: std_logic := '0';				--Acknowledge reception. Estado de ACK en el que el ESCLAVO tiene que responder.
	signal cBits	: integer range -1 to 7 :=7; 	--Contador de bits.
	signal sec		: std_logic := '0';				--Senal para saber si es la segunda vez que pasa(RS-Repeated Start)
	

	--Generación de relojes
	signal scl2x 	: std_logic :='0'; 									--Reloj al doble de la frecuencia de scl1x. Se usa para hacer las condiciones de inicio/parada y trama.
	signal scl1x	: std_logic :='0'; 									--Reloj de 100 o 400 kHz. Para la sincronización. Señal SCL.
	signal cont		: std_logic_vector(7 downto 0) :="00000000";	--Lo uso para contar ciclos a la hora de hacer el reloj de 2x.
	signal halfCont: std_logic_vector(7 downto 0) :="01111101"; --Mitad del contador. Ciclo a '1'. Inicializado a 125.
	signal maxCont : std_logic_vector(7 downto 0) :="11111001"; --Máximo del contador. Indica cuando reiniciar. Inicializado a 249.
	
	--Señales de datos para la placa
	signal data_addr	: std_logic_vector(7 downto 0); --Dirección de la placa y bit de escritura.
	signal data_cmd	: std_logic_vector(7 downto 0); --Comando.
	signal data_info	: std_logic_vector(7 downto 0); --Datos asociados al comando.
	signal data_reg	: std_logic_vector(7 downto 0); --Datos que recibo del dispositivo.
	signal le			: std_logic;						  --Lectura/Escritura.
	
	--Otras señales
	signal reset 	 : std_logic;
	signal sdat_gen : std_logic:='1'; --Trama generada para SDAT.
	signal hold 	 : std_logic:='1'; --Para ir al ritmo de SCLK y no del CLK. Mantiene el valor en el canal SDAT lo que dure el ciclo de SCLK.
	signal stopped  : std_logic:='0';
	signal started  : std_logic:='0';
	signal terminate: std_logic:='0';
	signal ack_rdy  : std_logic:='0'; --Para detener el ack durante un ciclo.
	signal count100 : std_logic_vector(8 downto 0):="000000000";
	signal fullCont : std_logic_vector(8 downto 0):="111110011"; --499. El ciclo completo de 100kHz
	signal rdnReg	 : integer range 0 to 9:=0;		--"Readen Registers". Numero de registros leidos.

begin

	--IDEA: Igual estas senales tienen que cambiar solo cuando la maquina este disponible, para que no se modifiquen en mitad de una operacion.
	reset	  	 <= RST;
	--data_addr <= ADD; Esto va mas abajo, tengo que cambiar el ultimo bit.
	data_cmd  <= COM;
	data_info <= DAT;
	le <= RW;

	--Maquina de estados
	PROCESS(ep, reset, GO, conf_rdy, cmd_rdy, data_rdy, ack, stopped, sec, started, ack_rdy, le)--He añadido ACK a la lista de sensibilidad.
	BEGIN
		IF(reset='1')then
			es<=e0;
		ELSE
			CASE ep IS
				WHEN e0 =>				--Estado de espera
					IF(GO='1')THEN
						es<=e1;
					ELSE
						es<=e0;
					END IF;
				WHEN e1=>					--Llamada para generar la condición de inicio
					IF(started='1')THEN	--Started se pone a '1' cuando ha hecho la condicion de inicio.
						es<=e2;
					ELSE
						es<=e1;
					END IF;
				WHEN e2=>				--Address+RW. Envio la direccion y la operacion a realizar (leer o escribir).
					IF(conf_rdy='1')THEN
						es<=e3;
					ELSE
						es<=e2;
					END IF;
				WHEN e3 =>				--ACK
					IF(ack_rdy='1')THEN
						--IF(ACK='0')THEN
							IF(le='0')THEN --Escritura
								es<=e4;
							ELSE				--Lectura
								IF(sec='0')THEN
									es<=e4;
								ELSE
									es<=e6;
								END IF;
							END IF;
						--ELSE
							--es<=e8;
						--END IF;
					ELSE
						es<=e3;
					END IF;
				WHEN e4=>				--CMD (Mas que comando, es el registro al que accedo). Convendria cambiar el nombre de estas variables.
					IF(cmd_rdy='1')THEN
						es<=e5;
					ELSE
						es<=e4;
					END IF;
				WHEN e5=>				--ACK
					--IF(ACK='0')THEN
						IF(ack_rdy='1')THEN
							IF(le='0')THEN --Escritura
								es<=e6;
							ELSE
								es<=e1;
							END IF;
						ELSE
							es<=e5;
						END IF;
					--ELSE
						--es<=e8;
					--END IF;
				WHEN e6=>				--DATA
					IF(data_rdy='1')THEN
						es<=e7;
					ELSE
						es<=e6;
					END IF;
				WHEN e7=>				--ACK
					--OJO. Hay que comprobar si quiero mas datos o si solo quiero recibir un Byte.
					IF(ack_rdy='1')THEN
						IF(le='0')THEN
							es<=e8;
						ELSE
							IF(ack='0')THEN--Quiero que siga devolviendome mas datos. Me pasa el siguiente registro.
								es<=e6;
							ELSE				--No quiero mas datos, quiero terminar la trama.
								es<=e8;
							END IF;
						END IF;
					ELSE
						es<=e7;
					END IF;
				WHEN e8=>				--Llamada para generar la condición de parada.
					if (stopped='1')then
						es<=e0;
					else
						es<=e8;
					end if;
			END CASE;
		END IF;
	END PROCESS;
	
	--Cambio de estados.
	PROCESS(scl2x)
	BEGIN
		if(rising_edge(scl2x))then
			ep<=es;
		end if;
	END PROCESS;
	
	--Señales de control
	idle		<='1' when ep=e0 else '0';
	start		<='1' when ep=e1 else '0';
	config 	<='1' when ep=e2 else '0';
	cmd		<='1' when ep=e4 else '0';
	data	 	<='1' when ep=e6 else '0';
	ack_tx	<='1' when ep=e7 else '0';
	ack_rx	<='1' when ep=e3 or ep=e5 else '0';
	stop		<='1' when ep=e8 else '0';

	--Reloj de 200kHz
	process (CLK_50)
	begin
		if (rising_edge(CLK_50)) then
			if (cont<halfCont)then --Si es menor que la mitad.
				scl2x<='0';
				cont<=cont+'1';
			else
				scl2x<='1';
				cont<=cont+'1';
			end if;
			if(cont=maxCont)then 
				cont<="00000000";
			end if;
		end if;
	end process;

	--Reloj de 100kHz (Va a enviarse a SCLK)
	process(scl2x)
	begin
		if(rising_edge(scl2x))then
			scl1x<=not(scl1x);
		end if;
	end process;
	
	--Unidad de Control (UC)
	process (CLK_50)	--En este proceso voy a manipular exclusivamente la señal SDAT.
	begin
		if(rising_edge(CLK_50))then
			if(idle='1')then
				sdat_gen<='1';
				stopped<='0';
				terminate<='0';
			elsif(start='1')then					--Comienzo de la transmision: SDAT a 0 antes que SCLK
				if(SPEED='0')then
					halfCont<="01111101";	--125.
					maxCont<="11111001";		--249.
					fullCont<="111110011";	--499. Ciclo completo de 100kHz
				else
					halfCont<="00100000";	--32.
					maxCont<="00111111";		--63.
					fullCont<="001111110";	--126. Ciclo completo de "400kHz". 390kHz reales.
				end if;
				ack_rdy<='0';
				--Preparo el siguiente dato a enviar.
				if(le='0')then
					data_addr<=ADD & le;
				else
					if(sec='0')then 					--Si es la segunda vez que pasa, le pongo el bit de escritura.
						data_addr<=ADD & '0';
					else
						conf_rdy<='0'; --Tengo que volver a ponerlo a 0 para que vuelva a enviar el address
						data_addr<=ADD & le;
					end if;
				end if;
				--Hago la condicion de inicio.
				if(scl1x='1')then
					if(scl2x='0')then
						sdat_gen<='0';
						started<='1';
					else
						sdat_gen<='1';
					end if;
				else
					sdat_gen<='1';
				end if;
				--Hay que inicializar las variables de rdy a 0.
			elsif(config='1')then
				ack_rdy<='0';
				if(scl1x='0')then
					if(scl2x='0')then
						if(cBits=-1)then--aqui he metido el clk,  de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
							cBits<=7;
							conf_rdy<='1';
						else
							sdat_gen<=data_addr(cBits);
							hold<='0';
						end if;
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
--				if(scl1x='0' and cBits=-1)then--aqui he metido el clk,  de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
--					cBits<=7;
--					conf_rdy<='1';
--				end if;
			elsif(cmd='1')then
			ack_rdy<='0';
				if(scl1x='0')then
					if(scl2x='0')then
						if(cBits=-1)then
							cBits<=7;
							if(le='1')then
								sec<='1';	--Activo sec para decir que ya ha pasado. En el siguiente estado e5 le toca ir al e0.
							else
								sec<='0';
							end if;
							cmd_rdy<='1';
						else
							sdat_gen<=data_cmd(cBits);
							hold<='0';
						end if;
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
--				if(scl1x='0'and cBits=-1)then--aqui he metido el clk, de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
--					cBits<=7;
--					--Solo es necesario que cambie sec para las lecturas.
--					if(le='1')then
--						sec<='1';	--Activo sec para decir que ya ha pasado. En el siguiente estado e5 le toca ir al e0.
--					else
--						sec<='0';
--					end if;
--					started<='0';
--					cmd_rdy<='1';
--				end if;
			elsif(data='1')then
				ack_rdy<='0';
				if(scl1x='0')then
					if(scl2x='0')then
						if(cBits=-1)then
							cBits<=7;
							data_rdy<='1';
						else
							if (le='0')then	--Si escribo, lo mando hacia sdat
								sdat_gen<=data_info(cBits);
							else			--Si leo, "pincho" la linea SDAT y lo almaceno en mi vector.
								sdat_gen<='1';
								data_reg(cBits)<=SDAT;
							end if;
							hold<='0';
						end if;	
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;

			elsif(ack_rx='1')then

				--Cuestion de tiempos, tienes que quedarte un ciclo. (Funciona, pero revisalo bien)
				sdat_gen<='1';
				--if(count100<('0' & fullCont(7 downto 1)))then --Espero un ciclo completo de reloj SCL.
				--	count100<=count100+'1';
				--else
					--count100<="000000000";
					--rdnReg<=rdnReg+1;
					--data_rdy<='0';--Tengo que coger nuevos datos, asi que no puede estar este flag a '1'
					ack_rdy<='1';
				--end if;
			elsif(ack_tx='1')then
				--Me estoy quedando un ciclo de 100kHz a '1'. El tx es solo para leer, y estoy considerando un unico caso, hacer solo una lectura.
				if(le='1')then
					if(rdnReg<RDNUM)then
							ack<='0';
					else
							ack<='1';
					end if;
					sdat_gen<=ack;
					if(count100<fullCont)then --Espero un ciclo completo de reloj SCL.
						count100<=count100+'1';
					else
						count100<="000000000";
						rdnReg<=rdnReg+1;
						data_rdy<='0';--Tengo que coger nuevos datos, asi que no puede estar este flag a '1'
						ack_rdy<='1';
					end if;
				else
					sdat_gen<='1';
					ack_rdy<='1';
				end if;
			elsif(stop='1')then				--Fin de la transmision: SDAT a 1 despues de SCLK
				--Pongo las variables de control a 0 para la proxima trama.
				conf_rdy<='0';
				cmd_rdy<='0';
				data_rdy<='0';
				ack_rdy<='0';
				sec<='0';
				sdat_gen<='0';--Lo pongo aqui para que despues del ack se quede a 0.
				--He cambiado de estado, pero estoy a la espera de que el ACK del dispositivo termine, espero al siguiente ciclo para poner SDA a 0.
				if(scl1x='0')then
					terminate<='1';
					sdat_gen<='0';
				end if;
				--Si todavía sigue en el ciclo anterior, el del ACK, lo mantengo a 1 para que haya alta impedancia y pueda escribir 0 el dispositivo.
				if(scl1x='1' and terminate='0')then
					--sdat_gen<='1';
					sdat_gen<='0';
				end if;
				--Si ya estoy en el siguiente ciclo, durante la bajada estaba 0, por lo que ahora hago la condición de parada subiendo SDA en el momento adecuado.
				if(scl1x='1' and scl2x='0' and terminate='1')then --Si estoy en el siguiente ciclo y en medio del bit de SCL que esta a 1, ya puedo subir SDA
					sdat_gen<='1';
					stopped<='1';
					started<='0';
				end if;
			end if;
		end if;
	end process;
		
	--Salidas
	SDAT <= 'Z' when sdat_gen = '1' else '0'; --En el bus I2C los '1' se representa con alta impedancia.
	SCLK 	<= '1' when ep=e0 else scl1x;
	BUSY	<= not(idle);
	DOUT <= data_reg;

end a;

Test bench
Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;

ENTITY pruebaI2C_RW is
	PORT(
			FPGA_CLK1_50: IN STD_LOGIC;
			KEY			: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
			LED			: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
			SCL			: OUT STD_LOGIC;
			SDA			: INOUT STD_LOGIC);	
END pruebaI2C_RW;

architecture a of pruebaI2C_RW is

	COMPONENT masterI2C_RW is
   	 	Port( 
			CLK_50: IN  STD_LOGIC;
			RST 	: IN  STD_LOGIC;
			ADD	: IN  STD_LOGIC_VECTOR (6 DOWNTO 0);	--Address: Direccion del dispositivo.
			COM	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0);	--Command: Tipo de orden a enviar.
			DAT	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0);	--Data: Informacion a enviar.
			GO		: IN  STD_LOGIC;
			RW		: IN  STD_LOGIC;							 	--Bit de L/E. 0=Write; 1=Read;
			SPEED : IN  STD_LOGIC;							 	--Velocidad de reloj. 0=100kHz; 1=400kHz.
			RDNUM : IN  INTEGER;									--Numero de registros a leer.
			BUSY	: OUT STD_LOGIC;
			DOUT	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);	--Salida de datos. Cuando hay operación de lectura saca ese resultado.
			SCLK	: OUT STD_LOGIC;
			SDAT	: INOUT STD_LOGIC
			);
	end COMPONENT;


	--Estados
	TYPE estados is (e0, e1, e2, e3);
	SIGNAL ep : estados :=e0; 	--Estado Presente
	SIGNAL es : estados; 		--Estado Siguiente
	

	signal command, data : std_logic_vector(7 downto 0);
	
	--Senales de datos para la placa
	constant cmd_config	: std_logic_vector(7 downto 0) := "00000011"; 	--Configura los pines de la placa. Cuales son de entrada y cuales de salida. (03h)
	constant cmd_write	: std_logic_vector(7 downto 0) := "00000001"; 	--Dice que el dato siguiente es para escribir en la salida.
	constant data_addr	: std_logic_vector(6 downto 0) := "1110010"; 	--Direccion de la placa.
	constant data_ports	: std_logic_vector(7 downto 0) := "00001111"; 	--Digo los puertos que son entradas y los que son salidas. (0=out; 1=in;)
	constant data_out		: std_logic_vector(7 downto 0) := "10101010"; 	--Digo donde quiero escribir un uno. Solo lo hara en las que se configuren como salida.
	signal 	reset			: std_logic:='0';
	signal	wait_count 	: std_logic_vector(13 downto 0):="00000000000000";
	signal 	change		: std_logic;
	signal 	go				: std_logic;
	signal 	ocupado		: std_logic;
	signal 	cont			: std_logic_vector (6 downto 0):="0000000";
	signal 	clk100k		: std_logic;
	signal 	clk100k_z	: std_logic;
	signal	le				: std_logic:='0';
	signal	speed			: std_logic:='0';
	signal	rdnum			:integer range 0 to 9:=6; 		--OJO! Que he puesto 2 en la definicion solo para la prueba.

BEGIN

	reset<=NOT(KEY(0));

	PROCESS(clk100k, ep, reset)
	BEGIN
		IF(rising_edge(clk100k))THEN
			IF(reset='1')then
				es<=e0;
			ELSE
				CASE ep IS
					WHEN e0 =>				--Estado de conf
						command<=cmd_config;
						data<=data_ports;
						le<='0';
						es<=e1;
					WHEN e1 =>
						command<=cmd_write;
						data<=data_out;
						le<='0';
						es<=e2;
					WHEN e2 =>
						command<=cmd_write;
						le<='1';
						es<=e3;
						--speed<='0';
					WHEN e3 =>
						es<=e3;
				END CASE;
			END IF;
		END IF;
	END PROCESS;
	go			<='1' 	when (ocupado='0' and (ep=e0 or ep=e1 or ep=e2)) else '0'; --OJO! Esto es una PRUEBA. Solo queria transmitir una trama!!!
	speed		<='1'		when ep=e1 or ep=e2 else '0';
	
	PROCESS(clk100k, ocupado, es)
	BEGIN
		if(ocupado='0')then
			ep<=es;
		end if;
	END PROCESS;


	inst1: masterI2C_RW
    	Port Map( 
		CLK_50 	=> FPGA_CLK1_50,
		RST 	=> reset,
		ADD 	=> data_addr,
		COM	=> command,
		DAT 	=> data,
		GO	=> go,
		RW => le,
		SPEED => '0',
		RDNUM => rdnum,
		BUSY	=> ocupado,
		DOUT => LED,
		SCLK	=> clk100k,
		SDAT	=> SDA
	);
	clk100k_Z <= 'Z' when clk100k = '1' else '0';
	SCL <= clk100k_Z;
END a;

Simulating on ModelSim I see this glitch, but I can't find the trace. Both states are practically equal (e2 and e4), and they share the same ACK status on the "control unit". BTW, the FPGA_CLOCK is 50Mhz, so I force it to 20ns on modelsim
3_.PNG
 

The glitches are a result of generating "clock" with flip-flops and then using both of these new clocks in the 50MHz domain to detect edges of them. Doing this results in all the glitches due to the combinational logic that produces the signals.

Instead of making everything synchronous to 50 MHz and creating an enable signal that pulses 1 50 MHz clock cycle high at the sclk2x rate you've made your design much more difficult by introducing multiple clock domains with each new clock generated with more skew. There is a chance that the new FF clocks aren't even routed on clock networks in the device. Which will cause even more timing variation and potential functional failures.

The entire design approach is bad, using 1, 2, or 3 process style FSMs has no bearing on the root problem...a bad architecture for an FPGA design.
 

Sorry, but I'm getting quite desperated with this.
I am just looking at the chronogram and I can't comprehend what am I doing wrong. Thats why I asking for some light.
On this thread there's an evolution from a copy-pasted code to a semi-working I2C protocol, I learned a little bit more to program in VHDL.
Some of you are telling me that the way I'm doing it is wrong, but I still don't know how should I do it. I'm learning and it seems I am doing all this way alone. So please, if someone could just tell me in a few words which mistake I'm doing and not seeing I would be really glad.

As always, thanks in advance.
 

I already told you use a clock enable that is generated off the 50 MHz and pulses high once every half period of the I2C interface bit period. Your FSM then just bit bangs the interface signals out (both SCL and SDA, i.e. SCL <= '1' on one enable at the 50 MHz clock then SCL <= '0' on the next enable at the 50 MHz clock). This way the entire design is synchronous and there are no skew problems causing a bunch of glitches and all the I2C outputs can be generated from the outputs of flip-flops. If you can't draw up a detailed block diagram of this and/or a schematic of such a implementation then you should probably stick to simpler circuits. I2C isn't all that complicated, but it is also not entirely trivial.

IMO you should have grabbed the design from Opencores and studied that and ran simulations of that to see how it works.
The problem with the copy paste comments and the lack of effort shown from before, was because you made no discernible effort to attempt to understand the design by writing a testbench and showing us waveforms of the simulation that didn't seem to be correct. Posting code snippets and such and saying you don't understand how it works isn't going to get any help without explaining why you can't understand how it works. e.g. I ran the following simulation and signal x doesn't go high like I think it should when y goes high... A description that allows us to see both what the problem is and if it is caused by misunderstanding how the code behaves or that you don't understand the logic involved. That is the kind of effort I want to see before I help, otherwise it's just another forum user that just wants to leach off the board and get free help on the simplest stuff, because they are too lazy to make any effort to learn.

The original design you posted used a clock enable and a single 50 MHz clock domain, you should have stuck with that and ran simulations.
 

sclk2x and sclk1x basically start with 2x and 1x going high. 2x goes low, then 1x high and 2x low, then the scl cycles ends with both low. The condition for changing the data is both clocks low. This is the last quarter of the scl cycle.

That said, the code should be rewritten with clk100k_rise/clk100_fall and any other relevant strobes. only rising_edge(clk50) should be used inside the synthesizable logic.

This type of project can also teach some bad practices because the delays often don't matter.
 

Hi,

I recommend to go through some good tutorials. Maybe one of the FPGA or CPLD manufacturers.
Especially focus on clock generation...

As ads-ee mentioned:
I already told you use a clock enable ...
I want to emphasize "enable".
You don't generate "new clock" signal that are use to control parts of the logic... you generate "enable" signals, but use always your high speed system clock.
..this is a general rule ... and for sure there are exceptions...

I'm no expert in writing HDL, but this was one of the first rule that I was told...

Klaus
 

Hello again, I fixed the mistakes with the tempo. But I have a doubt with reading the values from the registers.
My board has sequential read, so if I start reading in register 01h it should keep reading the 02h, 03h, etc.
I have configurated it writing on the registers and I tried to read them later separately, it works fine. But when I try to do the sequential read it returns me the same value all the time.
On the simulations the timings are right, you can see how it keeps the same stroke and the SDA line is on high-impedance in order to read, here's a picture:
sim.PNG
But when I connect the logic analyzer I get other kind of data. I should get AB, 0, F on the registers, but I only see AA all the time until de NACK and Stop condition. Why can this be happening?
real.PNG

The simulation goes on from the Repeated Start, and the real signal on logic simulator goes from start.

Thanks in advance, I update my code here so if anyone wants to run it.
Master
Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;


entity masterI2C_RW is
    Port( 
		CLK_50: IN  STD_LOGIC;								--Reloj a 50Mhz
		RST 	: IN  STD_LOGIC;
		ADD	: IN  STD_LOGIC_VECTOR (6 DOWNTO 0); 	--Address: Dirección del dispositivo.
		COM	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0); 	--Command: Tipo de orden a enviar.
		DAT	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0); 	--Data: Información a enviar.
		GO		: IN  STD_LOGIC;								--Inicio.
		RW		: IN  STD_LOGIC;								--Bit de L/E. 0=Write; 1=Read;
		SPEED : IN  STD_LOGIC;								--Velocidad de reloj. 0=100kHz; 1=400kHz.
		RDNUM : IN  INTEGER;									--Numero de registros a leer.
		BUSY	: OUT STD_LOGIC;								--Ocupado. No puede atender nuevas peticiones.
		DOUT	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);	--Salida de datos. Cuando hay operación de lectura saca ese resultado.
		SCLK	: OUT STD_LOGIC;								--Señal de reloj para la sincronización del I2C. 100kHz.
		SDAT	: INOUT STD_LOGIC);							--Señal de datos generada. Trama a enviar al dispositivo.
end masterI2C_RW;


architecture a of masterI2C_RW is

	--Estados
	TYPE estados is (e0, e1, e2, e3, e4, e5, e6, e7, e8);
	SIGNAL ep : estados :=e0; 	--Estado Presente
	SIGNAL es : estados; 		--Estado Siguiente
	
	--Señales de control de la máquina de estados
	signal idle 	: std_logic := '0';
	signal start 	: std_logic := '0';
	signal config	: std_logic := '0';
	signal cmd		: std_logic := '0';
	signal data		: std_logic := '0';
	signal ack 		: std_logic := '0';
	signal stop		: std_logic := '0';
	signal conf_rdy: std_logic := '0';
	signal cmd_rdy	: std_logic := '0';
	signal data_rdy: std_logic := '0';
	signal ack_tx	: std_logic := '0';				--Acknowledge transmission. Estado de ACK en el que el MAESTRO tiene que responder.
	signal ack_rx	: std_logic := '0';				--Acknowledge reception. Estado de ACK en el que el ESCLAVO tiene que responder.
	signal ack_txrx	: std_logic := '0';
	signal cBits	: integer range -1 to 7 :=7; 	--Contador de bits.
	signal sec		: std_logic := '0';				--Senal para saber si es la segunda vez que pasa(RS-Repeated Start)
	

	--Generación de relojes
	signal scl2x 	: std_logic :='0'; 									--Reloj al doble de la frecuencia de scl1x. Se usa para hacer las condiciones de inicio/parada y trama.
	signal scl1x	: std_logic :='0'; 									--Reloj de 100 o 400 kHz. Para la sincronización. Señal SCL.
	signal cont		: std_logic_vector(7 downto 0) :="00000000";	--Lo uso para contar ciclos a la hora de hacer el reloj de 2x.
	signal halfCont: std_logic_vector(7 downto 0) :="01111101"; --Mitad del contador. Ciclo a '1'. Inicializado a 125.
	signal maxCont : std_logic_vector(7 downto 0) :="11111001"; --Máximo del contador. Indica cuando reiniciar. Inicializado a 249.
	
	--Señales de datos para la placa
	signal data_addr	: std_logic_vector(7 downto 0); --Dirección de la placa y bit de escritura.
	signal data_cmd	: std_logic_vector(7 downto 0); --Comando.
	signal data_info	: std_logic_vector(7 downto 0); --Datos asociados al comando.
	signal data_reg	: std_logic_vector(7 downto 0); --Datos que recibo del dispositivo.
	signal le			: std_logic;						  --Lectura/Escritura.
	
	--Otras señales
	signal reset 	 : std_logic;
	signal sdat_gen : std_logic:='1'; --Trama generada para SDAT.
	signal hold 	 : std_logic:='1'; --Para ir al ritmo de SCLK y no del CLK. Mantiene el valor en el canal SDAT lo que dure el ciclo de SCLK.
	signal stopped  : std_logic:='0';
	signal started  : std_logic:='0';
	signal terminate: std_logic:='0';
	signal terminated: std_logic:='0';
	signal ack_rdy  : std_logic:='0'; --Para detener el ack durante un ciclo.
	signal count100 : std_logic_vector(8 downto 0):="000000000";
	signal fullCont : std_logic_vector(8 downto 0):="111110011"; --499. El ciclo completo de 100kHz
	signal rdnReg	 : integer range 0 to 9:=0;		--"Readen Registers". Numero de registros leidos.

begin

	--IDEA: Igual estas senales tienen que cambiar solo cuando la maquina este disponible, para que no se modifiquen en mitad de una operacion.
	reset	  	 <= RST;
	--data_addr <= ADD; Esto va mas abajo, tengo que cambiar el ultimo bit.
	data_cmd  <= COM;
	data_info <= DAT;
	le <= RW;

	--Maquina de estados
	PROCESS(ep, reset, GO, conf_rdy, cmd_rdy, data_rdy, ack, stopped, sec, started, ack_rdy, le)--He añadido ACK a la lista de sensibilidad.
	BEGIN
		IF(reset='1')then
			es<=e0;
		ELSE
			CASE ep IS
				WHEN e0 =>				--Estado de espera
					IF(GO='1')THEN
						es<=e1;
					ELSE
						es<=e0;
					END IF;
				WHEN e1=>					--Llamada para generar la condición de inicio
					IF(started='1')THEN	--Started se pone a '1' cuando ha hecho la condicion de inicio.
						es<=e2;
					ELSE
						es<=e1;
					END IF;
				WHEN e2=>				--Address+RW. Envio la direccion y la operacion a realizar (leer o escribir).
					IF(conf_rdy='1')THEN
						es<=e3;
					ELSE
						es<=e2;
					END IF;
				WHEN e3 =>				--ACK
					IF(ack_rdy='1')THEN
						--IF(ACK='0')THEN
							IF(le='0')THEN --Escritura
								es<=e4;
							ELSE				--Lectura
								IF(sec='0')THEN
									es<=e4;
								ELSE
									es<=e6;
								END IF;
							END IF;
						--ELSE
							--es<=e8;
						--END IF;
					ELSE
						es<=e3;
					END IF;
				WHEN e4=>				--CMD (Mas que comando, es el registro al que accedo). Convendria cambiar el nombre de estas variables.
					IF(cmd_rdy='1')THEN
						es<=e5;
					ELSE
						es<=e4;
					END IF;
				WHEN e5=>				--ACK
					--IF(ACK='0')THEN
						IF(ack_rdy='1')THEN
							IF(le='0')THEN --Escritura
								es<=e6;
							ELSE
								es<=e1;
							END IF;
						ELSE
							es<=e5;
						END IF;
					--ELSE
						--es<=e8;
					--END IF;
				WHEN e6=>				--DATA
					IF(data_rdy='1')THEN
						es<=e7;
					ELSE
						es<=e6;
					END IF;
				WHEN e7=>				--ACK
					--OJO. Hay que comprobar si quiero mas datos o si solo quiero recibir un Byte.
					IF(ack_rdy='1')THEN
						IF(le='0')THEN
							es<=e8;
						ELSE
							IF(ack='0')THEN--Quiero que siga devolviendome mas datos. Me pasa el siguiente registro.
								es<=e6;
							ELSE				--No quiero mas datos, quiero terminar la trama.
								es<=e8;
							END IF;
						END IF;
					ELSE
						es<=e7;
					END IF;
				WHEN e8=>				--Llamada para generar la condición de parada.
					if (stopped='1')then
						es<=e0;
					else
						es<=e8;
					end if;
			END CASE;
		END IF;
	END PROCESS;
	
	--Cambio de estados.
	PROCESS(CLK_50)
	BEGIN
		if(rising_edge(CLK_50))then
			ep<=es;
		end if;
	END PROCESS;
	
	--Señales de control
	idle		<='1' when ep=e0 else '0';
	start		<='1' when ep=e1 else '0';
	config 	<='1' when ep=e2 else '0';
	cmd		<='1' when ep=e4 else '0';
	data	 	<='1' when ep=e6 else '0';
	ack_txrx	<='1' when ep=e3 or ep=e5 or ep=e7 else '0';
	stop		<='1' when ep=e8 else '0';

	--Reloj de 200kHz
	process (CLK_50)
	begin
		if (rising_edge(CLK_50)) then
			if (cont<halfCont)then --Si es menor que la mitad.
				scl2x<='0';
				cont<=cont+'1';
			else
				scl2x<='1';
				cont<=cont+'1';
			end if;
			if(cont=maxCont)then 
				cont<="00000000";
			end if;
		end if;
	end process;

	--Reloj de 100kHz (Va a enviarse a SCLK)
	process(scl2x)
	begin
		if(rising_edge(scl2x))then
			scl1x<=not(scl1x);
		end if;
	end process;
	
	--Unidad de Control (UC)
	process (CLK_50)	--En este proceso voy a manipular exclusivamente la señal SDAT.
	begin
		if(rising_edge(CLK_50))then
			if(idle='1')then
				sdat_gen<='1';
				stopped<='0';
				terminate<='0';
				ack<='0';
				rdnReg<=0;
			elsif(start='1')then					--Comienzo de la transmision: SDAT a 0 antes que SCLK
				if(SPEED='0')then
					halfCont<="01111101";	--125.
					maxCont<="11111001";		--249.
					fullCont<="111110011";	--499. Ciclo completo de 100kHz
				else
					halfCont<="00100000";	--32.
					maxCont<="00111111";		--63.
					fullCont<="001111110";	--126. Ciclo completo de "400kHz". 390kHz reales.
				end if;
				ack_rdy<='0';
				--Preparo el siguiente dato a enviar.
				if(le='0')then
					data_addr<=ADD & le;
				else
					if(sec='0')then 					--Si es la segunda vez que pasa, le pongo el bit de escritura.
						data_addr<=ADD & '0';
					else
						conf_rdy<='0'; --Tengo que volver a ponerlo a 0 para que vuelva a enviar el address
						data_addr<=ADD & le;
					end if;
				end if;
				--Hago la condicion de inicio.
				if(scl1x='1')then
					if(scl2x='0')then
						sdat_gen<='0';
						terminated<='1';
					else
						sdat_gen<='1';
					end if;
				else
					if(terminated='1')then
						if(scl2x='0')then
							started<='1';
							terminated<='0';
						end if;
					else
						sdat_gen<='1';
					end if;
				end if;
				--Hay que inicializar las variables de rdy a 0.
			elsif(config='1')then
				terminated<='0';
				ack_rdy<='0';
				ack<='0';
				if(scl1x='0')then
					if(scl2x='1')then
						if(cBits=-1)then--aqui he metido el clk,  de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
							cBits<=7;
							conf_rdy<='1';
						end if;
							--sdat_gen<='1';
					else
						sdat_gen<=data_addr(cBits);
						hold<='0';
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
--				if(scl1x='0' and cBits=-1)then--aqui he metido el clk,  de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
--					cBits<=7;
--					conf_rdy<='1';
--				end if;
			elsif(cmd='1')then
			ack_rdy<='0';
			ack<='0';
				if(scl1x='0')then
					if(scl2x='1')then
						if(cBits=-1)then
							cBits<=7;
							--sdat_gen<='1';
							if(le='1')then
								sec<='1';	--Activo sec para decir que ya ha pasado. En el siguiente estado e5 le toca ir al e0.
							else
								sec<='0';
							end if;
							cmd_rdy<='1';
						end if;
					else
						sdat_gen<=data_cmd(cBits);
						hold<='0';
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
--				if(scl1x='0'and cBits=-1)then--aqui he metido el clk, de lo contrario el ultimo bit dura menos y cambia los tiempos de la trama
--					cBits<=7;
--					--Solo es necesario que cambie sec para las lecturas.
--					if(le='1')then
--						sec<='1';	--Activo sec para decir que ya ha pasado. En el siguiente estado e5 le toca ir al e0.
--					else
--						sec<='0';
--					end if;
--					started<='0';
--					cmd_rdy<='1';
--				end if;
			elsif(data='1')then
				ack_rdy<='0';
				ack<='0';
				if(scl1x='0')then
					if(scl2x='1')then
						if(cBits=-1)then
							cBits<=7;
							data_rdy<='1';
							--sdat_gen<='1';
						end if;
					else
						if (le='0')then	--Si escribo, lo mando hacia sdat
							sdat_gen<=data_info(cBits);
						else			--Si leo, "pincho" la linea SDAT y lo almaceno en mi vector.
							sdat_gen<='1';
							data_reg(cBits)<=SDAT;
						end if;
						hold<='0';
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
			elsif(ack_txrx='1')then
				started<='0';
				if(scl1x='0')then
					if(scl2x='1')then
						if(cBits=6)then
							cBits<=7;
							ack_rdy<='1';
							if(ep=e7 and le='1')then
								rdnReg<=rdnReg+1;
							end if;
						end if;
					else
						if(ep=e7)then
							data_rdy<='0';
							if (le='0')then	--Si escribo, lo mando hacia sdat
								ack<='1';
							else			--Si leo, "pincho" la linea SDAT y lo almaceno en mi vector.
								if(rdnReg<RDNUM)then
									ack<='0';
								else
									ack<='1';
								end if;
							end if;
						else
							ack<='1';
						end if;
						hold<='0';
					end if;
				elsif(scl1x='1' and cBits>-1 and hold='0')then
					cBits<=cBits-1;
					hold<='1';
				end if;
				if(config='0' and ack='0' and ep=e3)then
					sdat_gen<=data_addr(0);
				elsif(cmd='0' and ack='0' and ep=e5)then
					sdat_gen<=data_cmd(0);
				elsif(le='0' and data='0' and ack='0' and ep=e7)then
					sdat_gen<=data_info(0);
				elsif(le='1' and data_rdy='1' and ep=e7)then
					sdat_gen<='1';
				else
					sdat_gen<=ack;	--Antes solo esto sin condiciones.
				end if;		
			elsif(stop='1')then				--Fin de la transmision: SDAT a 1 despues de SCLK
				--Pongo las variables de control a 0 para la proxima trama.
				conf_rdy<='0';
				cmd_rdy<='0';
				data_rdy<='0';
				ack_rdy<='0';
				ack<='0';
				sec<='0';
				if(scl1x='0')then
					if(terminate='1')then
						stopped<='1';
						sdat_gen<='1';
					elsif(scl2x='1')then
						sdat_gen<='1';
					elsif(scl2x='0')then
						sdat_gen<='0';--Lo pongo aqui para que despues del ack se quede a 0.
					end if;
				else
					if(scl2x='0')then
						sdat_gen<='1';
						terminate<='1';
					end if;
				end if;
			end if;
		end if;
	end process;
		
	--Salidas
	SDAT <= 'Z' when sdat_gen = '1' or terminate='1' else '0'; --En el bus I2C los '1' se representa con alta impedancia.
	SCLK 	<= '1' when ep=e0 or terminate='1' else scl1x;
	BUSY	<= not(idle);
	DOUT <= data_reg;

end a;

Slave:
Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;

ENTITY pruebaI2C_RW is
	PORT(
			FPGA_CLK1_50: IN STD_LOGIC;
			KEY			: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
			LED			: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
			SCL			: OUT STD_LOGIC;
			SDA			: INOUT STD_LOGIC);	
END pruebaI2C_RW;

architecture a of pruebaI2C_RW is

	COMPONENT masterI2C_RW is
   	 	Port( 
			CLK_50: IN  STD_LOGIC;
			RST 	: IN  STD_LOGIC;
			ADD	: IN  STD_LOGIC_VECTOR (6 DOWNTO 0);	--Address: Direccion del dispositivo.
			COM	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0);	--Command: Tipo de orden a enviar.
			DAT	: IN  STD_LOGIC_VECTOR (7 DOWNTO 0);	--Data: Informacion a enviar.
			GO		: IN  STD_LOGIC;
			RW		: IN  STD_LOGIC;							 	--Bit de L/E. 0=Write; 1=Read;
			SPEED : IN  STD_LOGIC;							 	--Velocidad de reloj. 0=100kHz; 1=400kHz.
			RDNUM : IN  INTEGER;									--Numero de registros a leer.
			BUSY	: OUT STD_LOGIC;
			DOUT	: OUT STD_LOGIC_VECTOR (7 DOWNTO 0);	--Salida de datos. Cuando hay operación de lectura saca ese resultado.
			SCLK	: OUT STD_LOGIC;
			SDAT	: INOUT STD_LOGIC
			);
	end COMPONENT;


	--Estados
	TYPE estados is (e0, e1, e2, e3, e4, e5, e6);
	SIGNAL ep : estados :=e0; 	--Estado Presente
	SIGNAL es : estados; 		--Estado Siguiente
	

	signal command, data : std_logic_vector(7 downto 0);
	
	--Senales de datos para la placa
	constant cmd_config	: std_logic_vector(7 downto 0) := "00000011"; 	--Configura los pines de la placa. Cuales son de entrada y cuales de salida. (03h)
	constant cmd_write	: std_logic_vector(7 downto 0) := "00000001"; 	--Dice que el dato siguiente es para escribir en la salida.
	constant data_addr	: std_logic_vector(6 downto 0) := "1110010"; 	--Direccion de la placa.
	constant data_ports	: std_logic_vector(7 downto 0) := "00001111"; 	--Digo los puertos que son entradas y los que son salidas. (0=out; 1=in;)
	constant data_out		: std_logic_vector(7 downto 0) := "10101010"; 	--Digo donde quiero escribir un uno. Solo lo hara en las que se configuren como salida.
	signal 	reset			: std_logic:='0';
	signal	wait_count 	: std_logic_vector(13 downto 0):="00000000000000";
	signal 	change		: std_logic;
	signal 	go				: std_logic;
	signal 	ocupado		: std_logic;
	signal 	cont			: std_logic_vector (6 downto 0):="0000000";
	signal 	clk100k		: std_logic;
	signal 	clk100k_z	: std_logic;
	signal	le				: std_logic:='0';
	signal	speed			: std_logic:='0';
	signal	rdnum			:integer range 0 to 9:=6; 		--OJO! Que he puesto 2 en la definicion solo para la prueba.

BEGIN

	reset<=NOT(KEY(0));

	PROCESS(clk100k, ep, reset)
	BEGIN
		IF(rising_edge(clk100k))THEN
			IF(reset='1')then
				es<=e0;
			ELSE
				CASE ep IS
					WHEN e0 =>				--Estado de conf
						command<=cmd_config;
						data<=data_ports;
						le<='0';
						es<=e1;
					WHEN e1 =>
						command<=cmd_write;
						data<="10101011";
						le<='0';
						es<=e2;
					WHEN e2 =>
						command<=cmd_write;
						le<='1';
						es<=e3;
						rdnum<=0;
						--speed<='0';
					WHEN e3 =>
						command<="00000010";
						le<='1';
						rdnum<=0;
						--speed<='0';
						es<=e4;
					WHEN e4 =>
						command<="00000011";
						le<='1';
						es<=e5;
						rdnum<=0;
						--speed<='0';
					WHEN e5 =>
						command<="00000001";
						le<='1';
						es<=e6;
						rdnum<=3;
						--speed<='0';
					WHEN e6 =>
						es<=e6;
				END CASE;
			END IF;
		END IF;
	END PROCESS;
	go			<='1' 	when (ocupado='0' and (ep=e0 or ep=e1 or ep=e2 or ep=e3 or ep=e4 or ep=e5)) else '0'; --OJO! Esto es una PRUEBA. Solo queria transmitir una trama!!!
	speed		<='1'		when ep=e1 or ep=e2 or ep=e3 else '0';
	
	PROCESS(clk100k, ocupado, es)
	BEGIN
		if(ocupado='0')then
			ep<=es;
		end if;
	END PROCESS;


	inst1: masterI2C_RW
    	Port Map( 
		CLK_50 	=> FPGA_CLK1_50,
		RST 	=> reset,
		ADD 	=> data_addr,
		COM	=> command,
		DAT 	=> data,
		GO	=> go,
		RW => le,
		SPEED => '0',
		RDNUM => rdnum,
		BUSY	=> ocupado,
		DOUT => LED,
		SCLK	=> clk100k,
		SDAT	=> SDA
	);
	clk100k_Z <= 'Z' when clk100k = '1' else '0';
	SCL <= clk100k_Z;
END a;
 

Hello,

I´m no specialist with HDL,

But I assume you didn´t get the clock concept yet.
Example @ Slave:
Code:
IF(rising_edge(clk100k))THEN
I assume this is the I2C communication clock, but not your system clock. If so, then you can´t rely on this clok. It is neither continous, nor can it be expected to be reliable.
Further I assume "FPGA_CLK1_50" is your FPGA system clock. If so, thewn synchronize aa inputs (and outputs) to this clock (domain). This should be your continous and reliable clock.

Example @ Master:
Code:
	if(rising_edge(scl2x))then
But scl2x is a generated signal. It is no "clock".

***
There should be experts .. they can better verify whether this may cause problems. (I even may be wrong with my assumptions)

Klaus
 

No, I still don't comprehend the clock concept you are talking me about.
What I assume I'm doing (may I be wrong) is generating another clock signal from an original 50Mhz signal. FPGA_CLK1_50 is the main FPGA Clock.
In the simulation it seems that the time I let for the ACK bit is OK. But in the simulation it seems as it doesn't read the ACK or that it doesn't want to read. What surprisees me the most is that it gets the NACK signal right.
 

Hi,

Clock generation:
This is basic and important. Usually every FPGA manufacturer gives related documentation. Look for it.
Hopefully a more experiecnced user could give you the link to a good tutorial.
What surprisees me the most is that it gets the NACK signal right.
Nothing to be surprised. NACK is the default, because it is just a HIGH on SDA. And for a HIGH on SDA no bus member has to do anything (idle state) because of the pullup. SDA is HIGH when all members release the bus.

Klaus
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top