as a part of a project, I am trying to implement a very basic I2C module (not self written) into my project. The I2C module uses the data line as an inout port. I am trying to verify the Data "01010001", containing the 6 bit address + 'read' bit. I generated the bitstream of the below added code and I am trying to measure the the output of the port with an oscilloscope. I can verify the clock on SCL, but the data signal stays zero all the time. I created a top module including a clock divider, to provide the clock for the i2c module, and the i2c module. I will add both VHDL files below. My question is: Is there an error in the code that I can't figure out or do I have change the way of measuring the port?
I2C Module:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity i2c_neuer_ansatz is
Port
( CLK_800kHz_IN, RESET : in STD_LOGIC;
SCL : out STD_LOGIC;
SDA :inout STD_LOGIC
);
end i2c_neuer_ansatz;
architecture i2c_neuer_ansatz_arch of i2c_neuer_ansatz is
signal STATE: STD_LOGIC_VECTOR (7 DOWNTO 0);
signal SDAINT: STD_LOGIC;
signal BITCOUNT: integer range 0 to 8;
constant SLAVEADD_READ: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010001";
---- I2C slave address + read
constant SLAVEADD_WRITE: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010000";
---- I2C slave address + write
signal DATA_IN: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000";
---- Data to be written to slave
signal DATA_OUT: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000";
---- Data to be read from slave
-- ---- for 100 kHz clock
-- constant max200k: integer:= 250;
-- signal clocktick200k: integer range 0 to max200k;
signal CLK_800kHz: STD_LOGIC;
---- CLK_200KHz is a 200 kHz clock and SCL is a 100kHz clock for i2c
begin
---- SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
SDA <= 'Z' when SDAINT = '1' else '0';
---- process for implementing FSM for the I2C master controller
CLK_800kHz <= CLK_800kHz_IN;
Output: process (CLK_800kHz, RESET)
begin
if (RESET = '0') then
---- idle condition, both SDA and SCL = 1
SCL <= '1';
SDAINT <= '1';
State <= x"00"; ---- next state
-- elsif (CLK_800kHz'event and CLK_800kHz = '1')then
elsif rising_edge(CLK_800kHz) then
case STATE is
when x"00" =>
---- when idle, both SDA and SCL = 1
SCL <= '1';
SDAINT <= '1';
state <= x"01"; ---- next state
when x"01" =>
---- send start condition SDA goes from '1' to '0' with SCL = '1'
SCL <= '1';
SDAINT <= '0';
BITCOUNT <= 8; ---- starting bit count
state <= x"40"; ---- next state
when x"02" =>
---- send seven bit address of the slave followed by write/read bit
SCL <= '0';
SDAINT <= SLAVEADD_WRITE (BITCOUNT - 1);
---- sending the seven bit address and write bits on SDA line
state <= x"03"; ---- next state
when x"03" =>
SCL <= '1';
if (BITCOUNT)> 0 then
---- if BITOCUNT > 0, then there are more bits to be sent. Therefore, go to state x"02"
BITCOUNT <= BITCOUNT - 1;
State <= x"02"; ---- next state
else
---- If BITCOUNT = 0, then all bits have been sent. Go to state x"12" and Set the value of BITCOUNT to 8
BITCOUNT <= 8;
State <= x"12"; ---- next state
end if;
---- address and write bits have been sent and now get acknowledgement from slave
when x"12" =>
SCL <= '0';
state <= x"13"; ---- next state
when x"13" =>
SCL <= '1';
if SDA = '1' then
---- if no acknowledgement from slave, go to the idle state. The designer can create an error state if desired and send the FSM there.
state <= x"00"; ---- next state
else
---- if there is acknowledgement from slave, go to state x"40" for reading data to the slave
State <= x"40"; ---- next state
end if;
---- writing data to slave
when x"30" =>
SCL <= '0';
---- sending the data to be written on the SDA line
SDAINT <= DATA_IN (BITCOUNT - 1);
state <= x"31"; ---- next state
when x"31" =>
SCL <= '1';
if (BITCOUNT)> 0 then
---- if BITOCUNT > 0, then there are more bits to be sent. Therefore, go to state x"30"
BITCOUNT <= BITCOUNT - 1;
state <= x"30"; ---- next state
else
---- If BITCOUNT = 0, then all bits have been sent. Go to state x"32"
state <= x"32"; ---- next state
end if;
----get acknowledgement from slave
when x"32" =>
SCL <= '0';
state <= x"33"; ---- next state
when x"33" =>
BITCOUNT <= 8;
SCL <= '1';
if SDA = '1' then
state <= x"00"; ---- next state
else
SDAINT <= '0';
state <= x"34"; ---- next state
end if;
when x"34" =>
SCL <= '0';
-- SDA starts at 0 to prepare for the 0 to 1 transition
SDAINT <= '0';
state <= x"42"; ---- next state
---- read from slave
when x"40" =>
---- send seven bit address of the slave followed by read bit
SCL <= '0';
SDAINT <= SLAVEADD_READ (BITCOUNT - 1);
state <= x"41"; ---- next state
when x"41" =>
SCL <= '1';
if (BITCOUNT)> 0 then
BITCOUNT <= BITCOUNT - 1;
state <= x"40"; ---- next state
else
BITCOUNT <= 8;
state <= x"50";
end if;
---- get acknowledgement from slave
when x"50" =>
BITCOUNT <= 8;
SCL<= '0';
state <= x"51"; ---- next state
when x"51" =>
SCL <= '1';
if SDA = '1' then
state <= x"00";
else
BITCOUNT <= 8;
state <= x"52";
end if;
when x"52" =>
SCL <= '0';
state <= x"53";
when x"53" =>
SCL <= '1';
DATA_OUT(bitcount-1) <= SDA;
if (bitcount - 1) > 0 then
bitcount <= bitcount - 1;
state <= x"54";
else
bitcount <= 8;
state <= x"00";
end if;
when others =>
state <= x"00";
end case;
end if;
end process;
------ process for generating 200 kHz clock from 50 MHz input clock
--Clk200kHz: process
-- begin
-- wait until CLK_50MHz'event and CLK_50MHz = '1';
-- if clocktick200k < max200k then
-- clocktick200k <= clocktick200k + 1;
-- else
-- clocktick200k <= 0;
-- end if;
-- if clocktick200k < max200k/2 then
-- CLK_200KHz <= '0';
-- else
-- CLK_200KHz <= '1';
-- end if;
-- end process;
end i2c_neuer_ansatz_arch;
The module consists of this code:
entity i2cNew_clk is
Port
(
MAIN_CLK_IN : in std_logic;
MAIN_RESET_I2C : in std_logic;
MAIN_RESET_CLK : in std_logic;
MAIN_SDA : inout std_logic;
MAIN_SCL : out std_logic;
MAIN_CLK_TEST : out std_logic
);
end i2cNew_clk;
architecture i2cNew_clk_arch of i2cNew_clk is
component i2c_neuer_ansatz is
Port
( CLK_800kHz_IN, RESET : in STD_LOGIC;
SCL : out STD_LOGIC;
SDA :inout STD_LOGIC
);
end component i2c_neuer_ansatz;
signal sign_SCL : std_logic;
signal sign_SDA : std_logic;
signal sign_RESET : std_logic;
component clk_gen is
Generic
(
sysclk_generic : natural := 125000000; --250MHz vom Board
clk_out_generic : natural := 1000000; --1MHz für General
i2c_clk_generic : natural := 800000 --800kHz für I2C
);
Port
(
SYSCLK_IN_CLK_GEN : in std_logic;
RST_IN_CLK_GEN : in std_logic;
CLK_THRU : out std_logic;
CLK_OUT_GENERAL_CLK_GEN : out std_logic;
CLK_OUT_I2C_CLK_GEN : out std_logic
);
end component clk_gen;
signal sign_SYSCLK_IN_CLK_GEN : std_logic;
signal sign_RST_IN_CLK_GEN : std_logic;
signal sign_CLK_THRU : std_logic;
signal sign_CLK_OUT_GENERAL_CLK_GEN : std_logic;
signal sign_CLK_OUT_I2C_CLK_GEN : std_logic;
begin
sign_RESET <= MAIN_RESET_I2C;
sign_RST_IN_CLK_GEN <= MAIN_RESET_CLK;
sign_SYSCLK_IN_CLK_GEN <= MAIN_CLK_IN;
MAIN_SDA <= sign_SDA;
MAIN_SCL <= sign_SCL;
MAIN_CLK_TEST <= sign_CLK_OUT_I2C_CLK_GEN;
i2c_neuer_ansatz_inst : i2c_neuer_ansatz
port map
(
CLK_800kHz_IN => sign_CLK_OUT_I2C_CLK_GEN,
RESET => sign_RESET,
SCL => sign_SCL,
SDA => sign_SDA
);
clk_gen_inst : clk_gen
port map
(
SYSCLK_IN_CLK_GEN => sign_SYSCLK_IN_CLK_GEN,
RST_IN_CLK_GEN => sign_RST_IN_CLK_GEN,
CLK_THRU => sign_CLK_THRU,
CLK_OUT_GENERAL_CLK_GEN => sign_CLK_OUT_GENERAL_CLK_GEN,
CLK_OUT_I2C_CLK_GEN => sign_CLK_OUT_I2C_CLK_GEN
);
end i2cNew_clk_arch;
EDIT: Updated code for the i2c module. I commented the whole part for writing to the slave out
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity i2c_neuer_ansatz is
Port
( CLK_800kHz_IN, RESET : in STD_LOGIC;
SCL : out STD_LOGIC;
SDA :inout STD_LOGIC
);
end i2c_neuer_ansatz;
architecture i2c_neuer_ansatz_arch of i2c_neuer_ansatz is
signal STATE: STD_LOGIC_VECTOR (7 DOWNTO 0);
-- signal SDAINT: STD_LOGIC;
signal BITCOUNT: integer range 0 to 8;
constant SLAVEADD_READ: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010001";
---- I2C slave address + read
constant SLAVEADD_WRITE: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010000";
---- I2C slave address + write
signal DATA_IN: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000";
---- Data to be written to slave
signal DATA_OUT: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000";
---- Data to be read from slave
-- ---- for 100 kHz clock
-- constant max200k: integer:= 250;
-- signal clocktick200k: integer range 0 to max200k;
signal CLK_800kHz: STD_LOGIC;
---- CLK_200KHz is a 200 kHz clock and SCL is a 100kHz clock for i2c
signal SDA_OUT : std_logic;
signal SDA_IN : std_logic;
signal SDA_OE : std_logic;
begin
------ SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
-- SDA <= 'Z' when SDAINT = '1' else '0';
SDA <= SDA_OUT when SDA_OE = '1' else 'Z';
SDA_IN <= SDA when SDA_OE = '0' else '0';
---- process for implementing FSM for the I2C master controller
CLK_800kHz <= CLK_800kHz_IN;
Output: process (CLK_800kHz, RESET)
begin
if (RESET = '0') then
---- idle condition, both SDA and SCL = 1
SCL <= '1';
SDA_OUT <= '1';
SDA_OE <= '1';
State <= x"00"; ---- next state
-- elsif (CLK_800kHz'event and CLK_800kHz = '1')then
elsif rising_edge(CLK_800kHz) then
case STATE is
when x"00" =>
---- when idle, both SDA and SCL = 1
SCL <= '1';
SDA_OE <= '1';
SDA_OUT <= '1';
state <= x"01"; ---- next state
when x"01" =>
---- send start condition SDA goes from '1' to '0' with SCL = '1'
SCL <= '1';
SDA_OE <= '1';
SDA_OUT <= '0';
BITCOUNT <= 8; ---- starting bit count
state <= x"40"; ---- next state
-- when x"02" =>
-- ---- send seven bit address of the slave followed by write/read bit
-- SCL <= '0';
-- SDAINT <= SLAVEADD_WRITE (BITCOUNT - 1);
------ sending the seven bit address and write bits on SDA line
-- state <= x"03"; ---- next state
-- when x"03" =>
-- SCL <= '1';
-- if (BITCOUNT)> 0 then
------ if BITOCUNT > 0, then there are more bits to be sent. Therefore, go to state x"02"
-- BITCOUNT <= BITCOUNT - 1;
-- State <= x"02"; ---- next state
-- else
------ If BITCOUNT = 0, then all bits have been sent. Go to state x"12" and Set the value of BITCOUNT to 8
-- BITCOUNT <= 8;
-- State <= x"12"; ---- next state
-- end if;
------ address and write bits have been sent and now get acknowledgement from slave
-- when x"12" =>
-- SCL <= '0';
-- state <= x"13"; ---- next state
-- when x"13" =>
-- SCL <= '1';
-- if SDA = '1' then
------ if no acknowledgement from slave, go to the idle state. The designer can create an error state if desired and send the FSM there.
-- state <= x"00"; ---- next state
-- else
------ if there is acknowledgement from slave, go to state x"40" for reading data to the slave
-- State <= x"40"; ---- next state
-- end if;
-- ---- writing data to slave
-- when x"30" =>
-- SCL <= '0';
------ sending the data to be written on the SDA line
-- SDAINT <= DATA_IN (BITCOUNT - 1);
-- state <= x"31"; ---- next state
-- when x"31" =>
-- SCL <= '1';
-- if (BITCOUNT)> 0 then
------ if BITOCUNT > 0, then there are more bits to be sent. Therefore, go to state x"30"
-- BITCOUNT <= BITCOUNT - 1;
-- state <= x"30"; ---- next state
-- else
------ If BITCOUNT = 0, then all bits have been sent. Go to state x"32"
-- state <= x"32"; ---- next state
-- end if;
-- ----get acknowledgement from slave
-- when x"32" =>
-- SCL <= '0';
-- state <= x"33"; ---- next state
-- when x"33" =>
-- BITCOUNT <= 8;
-- SCL <= '1';
-- if SDA = '1' then
-- state <= x"00"; ---- next state
-- else
-- SDAINT <= '0';
-- state <= x"34"; ---- next state
-- end if;
-- when x"34" =>
-- SCL <= '0';
---- SDA starts at 0 to prepare for the 0 to 1 transition
-- SDAINT <= '0';
-- state <= x"42"; ---- next state
---- read from slave
when x"40" =>
---- send seven bit address of the slave followed by read bit
SCL <= '0';
SDA_OE <= '1';
SDA_OUT <= SLAVEADD_READ (BITCOUNT - 1);
state <= x"41"; ---- next state
when x"41" =>
SCL <= '1';
if (BITCOUNT)> 0 then
BITCOUNT <= BITCOUNT - 1;
state <= x"40"; ---- next state
else
BITCOUNT <= 8;
state <= x"50";
end if;
---- get acknowledgement from slave
when x"50" =>
BITCOUNT <= 8;
SDA_OE <= '0';
SCL<= '0';
state <= x"51"; ---- next state
when x"51" =>
SCL <= '1';
SDA_OE <= '0';
if SDA = '1' then
state <= x"00";
else
BITCOUNT <= 8;
state <= x"52";
end if;
when x"52" =>
SDA_OE <= '0';
SCL <= '0';
state <= x"53";
when x"53" =>
SCL <= '1';
SDA_OE <= '0';
DATA_OUT(bitcount-1) <= SDA;
if (bitcount - 1) > 0 then
bitcount <= bitcount - 1;
state <= x"54";
else
bitcount <= 8;
state <= x"00";
end if;
when others =>
state <= x"00";
end case;
end if;
end process;
------ process for generating 200 kHz clock from 50 MHz input clock
--Clk200kHz: process
-- begin
-- wait until CLK_50MHz'event and CLK_50MHz = '1';
-- if clocktick200k < max200k then
-- clocktick200k <= clocktick200k + 1;
-- else
-- clocktick200k <= 0;
-- end if;
-- if clocktick200k < max200k/2 then
-- CLK_200KHz <= '0';
-- else
-- CLK_200KHz <= '1';
-- end if;
-- end process;
end i2c_neuer_ansatz_arch;
Your tristate management is not good.
Your main problem is on this line and I think the comment is wrong :
---- SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
SDA <= 'Z' when SDAINT = '1' else '0';
In order to manage INOUT in VHDL, you should have three internal signals in your module i2c_neuer_ansatz :
This is the line you should write instead the one I have quoted :
SDA <= SDA_OUT when SDA_OE = '1' else 'Z';
And you can use this line to handle the input mode :
SDA_IN <= SDA when SDA_OE = '0' else '0';
But this seconde line is not requirement, you also use SDA directly as in input in your code (You already did it).
In your code, you just have to add output enable management.