portvhdlmeasureinout

VHDL INOUT Port does not provide a signal (I2C)


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;


Solution

  • 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.