OpenCores
URL https://opencores.org/ocsvn/i2c/i2c/trunk

Subversion Repositories i2c

[/] [i2c/] [tags/] [first/] [I2C.VHD] - Rev 69

Go to most recent revision | Compare with Previous | Blame | View Log

--
-- Simple I2C controller
--
-- 1) No multimaster
-- 2) No slave mode
-- 3) No fifo's
--
-- notes:
-- Every command is acknowledged. Do not set a new command before previous is acknowledged.
-- Dout is available 1 clock cycle later as cmd_ack
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

package I2C is
        component simple_i2c is
        port (
                clk : in std_logic;
                ena : in std_logic;
                nReset : in std_logic;

                clk_cnt : in unsigned(7 downto 0);      -- 4x SCL 

                -- input signals
                start,
                stop,
                read,
                write,
                ack_in : std_logic;
                Din : in std_logic_vector(7 downto 0);

                -- output signals
                cmd_ack : out std_logic;
                ack_out : out std_logic;
                Dout : out std_logic_vector(7 downto 0);

                -- i2c signals
                SCL : inout std_logic;
                SDA : inout std_logic
        );
        end component simple_i2c;
end package I2C;


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity simple_i2c is
        port (
                clk : in std_logic;
                ena : in std_logic;
                nReset : in std_logic;

                clk_cnt : in unsigned(7 downto 0);      -- 4x SCL 

                -- input signals
                start,
                stop,
                read,
                write,
                ack_in : std_logic;
                Din : in std_logic_vector(7 downto 0);

                -- output signals
                cmd_ack : out std_logic;
                ack_out : out std_logic;
                Dout : out std_logic_vector(7 downto 0);

                -- i2c signals
                SCL : inout std_logic;
                SDA : inout std_logic
        );
end entity simple_i2c;

architecture structural of simple_i2c is
        component i2c_core is
        port (
                clk : in std_logic;
                nReset : in std_logic;

                clk_cnt : in unsigned(7 downto 0);

                cmd : in std_logic_vector(2 downto 0);
                cmd_ack : out std_logic;
                busy : out std_logic;

                Din : in std_logic;
                Dout : out std_logic;

                SCL : inout std_logic;
                SDA : inout std_logic
        );
        end component i2c_core;

        -- commands for i2c_core
        constant CMD_NOP        : std_logic_vector(2 downto 0) := "000";
        constant CMD_START      : std_logic_vector(2 downto 0) := "010";
        constant CMD_STOP       : std_logic_vector(2 downto 0) := "011";
        constant CMD_READ       : std_logic_vector(2 downto 0) := "100";
        constant CMD_WRITE      : std_logic_vector(2 downto 0) := "101";

        -- signals for i2c_core
        signal core_cmd : std_logic_vector(2 downto 0);
        signal core_ack, core_busy, core_txd, core_rxd : std_logic;

        -- signals for shift register
        signal sr : std_logic_vector(7 downto 0); -- 8bit shift register
        signal shift, ld : std_logic;

        -- signals for state machine
        signal go, host_ack : std_logic;
begin
        -- hookup i2c core
        u1: i2c_core port map (clk, nReset, clk_cnt, core_cmd, core_ack, core_busy, core_txd, core_rxd, SCL, SDA);

        -- generate host-command-acknowledge
        cmd_ack <= host_ack;
        
        -- generate go-signal
        go <= (read or write) and not host_ack;

        -- assign Dout output to shift-register
        Dout <= sr;

        -- assign ack_out output to core_rxd (contains last received bit)
        ack_out <= core_rxd;

        -- generate shift register
        shift_register: process(clk)
        begin
                if (clk'event and clk = '1') then
                        if (ld = '1') then
                                sr <= din;
                        elsif (shift = '1') then
                                sr <= (sr(6 downto 0) & core_rxd);
                        end if;
                end if;
        end process shift_register;

        --
        -- state machine
        --
        statemachine : block
                type states is (st_idle, st_start, st_read, st_write, st_ack, st_stop);
                signal state : states;
                signal dcnt : unsigned(2 downto 0);
        begin
                --
                -- command interpreter, translate complex commands into simpler I2C commands
                --
                nxt_state_decoder: process(clk, nReset, state)
                        variable nxt_state : states;
                        variable idcnt : unsigned(2 downto 0);
                        variable ihost_ack : std_logic;
                        variable icore_cmd : std_logic_vector(2 downto 0);
                        variable icore_txd : std_logic;
                        variable ishift, iload : std_logic;
                begin
                        -- 8 databits (1byte) of data to shift-in/out
                        idcnt := dcnt;

                        -- no acknowledge (until command complete)
                        ihost_ack := '0';

                        icore_txd := core_txd;

                        -- keep current command to i2c_core
                        icore_cmd := core_cmd;

                        -- no shifting or loading of shift-register
                        ishift := '0';
                        iload := '0';

                        -- keep current state;
                        nxt_state := state;
                        case state is
                                when st_idle =>
                                        if (go = '1') then
                                                if (start = '1') then
                                                        nxt_state := st_start;  
                                                        icore_cmd := CMD_START;
                                                elsif (read = '1') then
                                                        nxt_state := st_read;
                                                        icore_cmd := CMD_READ;
                                                        idcnt := "111";
                                                else
                                                        nxt_state := st_write;
                                                        icore_cmd := CMD_WRITE;
                                                        idcnt := "111";
                                                        iload := '1';
                                                end if;
                                        end if;

                                when st_start =>
                                        if (core_ack = '1') then
                                                if (read = '1') then
                                                        nxt_state := st_read;
                                                        icore_cmd := CMD_READ;
                                                        idcnt := "111";
                                                else
                                                        nxt_state := st_write;
                                                        icore_cmd := CMD_WRITE;
                                                        idcnt := "111";
                                                        iload := '1';
                                                end if;
                                        end if;

                                when st_write =>
                                        if (core_ack = '1') then
                                                idcnt := dcnt -1;       -- count down Data_counter
                                                icore_txd := sr(7);
                                                if (dcnt = 0) then
                                                        nxt_state := st_ack;
                                                        icore_cmd := CMD_READ;
                                                else
                                                        ishift := '1';
--                                                      icore_txd := sr(7);
                                                end if;
                                        end if;                 

                                when st_read =>
                                        if (core_ack = '1') then
                                                idcnt := dcnt -1;       -- count down Data_counter
                                                ishift := '1';
                                                if (dcnt = 0) then
                                                        nxt_state := st_ack;
                                                        icore_cmd := CMD_WRITE;
                                                        icore_txd := ack_in;
                                                end if;
                                        end if;                 

                                when st_ack =>
                                        if (core_ack = '1') then
                                                -- generate command acknowledge signal
                                                ihost_ack := '1';

                                                -- Perform an additional shift, needed for 'read' (store last received bit in shift register)
                                                ishift := '1';

                                                -- check for stop; Should a STOP command be generated ?
                                                if (stop = '1') then
                                                        nxt_state := st_stop;
                                                        icore_cmd := CMD_STOP;
                                                else
                                                        nxt_state := st_idle;
                                                        icore_cmd := CMD_NOP;
                                                end if;
                                        end if;

                                when st_stop =>
                                        if (core_ack = '1') then
                                                nxt_state := st_idle;
                                                icore_cmd := CMD_NOP;
                                        end if;

                                when others => -- illegal states
                                        nxt_state := st_idle;
                                        icore_cmd := CMD_NOP;
                        end case;

                        -- generate registers
                        if (nReset = '0') then
                                core_cmd <= CMD_NOP;
                                core_txd <= '0';
                                
                                shift <= '0';
                                ld <= '0';

                                dcnt <= "111";
                                host_ack <= '0';

                                state <= st_idle;
                        elsif (clk'event and clk = '1') then
                                if (ena = '1') then
                                        state <= nxt_state;

                                        dcnt <= idcnt;
                                        shift <= ishift;
                                        ld <= iload;

                                        core_cmd <= icore_cmd;
                                        core_txd <= icore_txd;

                                        host_ack <= ihost_ack;
                                end if;
                        end if;
                end process nxt_state_decoder;

        end block statemachine;

end architecture structural;


--
--
-- I2C Core
--
-- Translate simple commands into SCL/SDA transitions
-- Each command has 5 states, A/B/C/D/idle
--
-- start:       SCL     ~~~~~~~~~~\____
--      SDA     ~~~~~~~~\______
--               x | A | B | C | D | i
--
-- repstart     SCL     ____/~~~~\___
--      SDA     __/~~~\______
--               x | A | B | C | D | i
--
-- stop SCL     ____/~~~~~~~~
--      SDA     ==\____/~~~~~
--               x | A | B | C | D | i
--
--- write       SCL     ____/~~~~\____
--      SDA     ==X=========X=
--               x | A | B | C | D | i
--
--- read        SCL     ____/~~~~\____
--      SDA     XXXX=====XXXX
--               x | A | B | C | D | i
--

-- Timing:              Normal mode     Fast mode
-----------------------------------------------------------------
-- Fscl         100KHz          400KHz
-- Th_scl               4.0us           0.6us   High period of SCL
-- Tl_scl               4.7us           1.3us   Low period of SCL
-- Tsu:sta              4.7us           0.6us   setup time for a repeated start condition
-- Tsu:sto              4.0us           0.6us   setup time for a stop conditon
-- Tbuf         4.7us           1.3us   Bus free time between a stop and start condition
--

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity i2c_core is
        port (
                clk : in std_logic;
                nReset : in std_logic;

                clk_cnt : in unsigned(7 downto 0);

                cmd : in std_logic_vector(2 downto 0);
                cmd_ack : out std_logic;
                busy : out std_logic;

                Din : in std_logic;
                Dout : out std_logic;

                SCL : inout std_logic;
                SDA : inout std_logic
        );
end entity i2c_core;

architecture structural of i2c_core is
        constant CMD_NOP        : std_logic_vector(2 downto 0) := "000";
        constant CMD_START      : std_logic_vector(2 downto 0) := "010";
        constant CMD_STOP       : std_logic_vector(2 downto 0) := "011";
        constant CMD_READ       : std_logic_vector(2 downto 0) := "100";
        constant CMD_WRITE      : std_logic_vector(2 downto 0) := "101";

        type cmds is (idle, start_a, start_b, start_c, start_d, stop_a, stop_b, stop_c, rd_a, rd_b, rd_c, rd_d, wr_a, wr_b, wr_c, wr_d);
        signal state : cmds;
        signal SDAo, SCLo : std_logic;
        signal txd : std_logic;
        signal clk_en, slave_wait :std_logic;
        signal cnt : unsigned(7 downto 0) := clk_cnt;
begin
        -- whenever the slave is not ready it can delay the cycle by pulling SCL low
        slave_wait <= '1' when ((SCLo = '1') and (SCL = '0')) else '0';

        -- generate clk enable signal
        gen_clken: process(clk, nReset)
        begin
                if (nReset = '0') then
                        cnt <= (others => '0');
                        clk_en <= '1'; --'0';
                elsif (clk'event and clk = '1') then
                        if (cnt = 0) then
                                clk_en <= '1';
                                cnt <= clk_cnt;
                        else
                                if (slave_wait = '0') then
                                        cnt <= cnt -1;
                                end if;
                                clk_en <= '0';
                        end if;
                end if;
        end process gen_clken;

        -- generate statemachine
        nxt_state_decoder : process (clk, nReset, state, cmd, SDA)
                variable nxt_state : cmds;
                variable icmd_ack, ibusy, store_sda : std_logic;
                variable itxd : std_logic;
        begin

                nxt_state := state;

                icmd_ack := '0'; -- default no acknowledge
                ibusy := '1'; -- default busy

                store_sda := '0';

                itxd := txd;

                case (state) is
                        -- idle
                        when idle =>
                                case cmd is
                                        when CMD_START =>
                                                nxt_state := start_a;
                                                icmd_ack := '1'; -- command completed

                                        when CMD_STOP =>
                                                nxt_state := stop_a;
                                                icmd_ack := '1'; -- command completed

                                        when CMD_WRITE =>
                                                nxt_state := wr_a;
                                                icmd_ack := '1'; -- command completed
                                                itxd := Din;

                                        when CMD_READ =>
                                                nxt_state := rd_a;
                                                icmd_ack := '1'; -- command completed

                                        when others =>
                                                nxt_state := idle;
-- don't acknowledge NOP command                                                icmd_ack := '1'; -- command completed
                                                ibusy := '0';
                                end case;

                        -- start
                        when start_a =>
                                nxt_state := start_b;

                        when start_b =>
                                nxt_state := start_c;

                        when start_c =>
                                nxt_state := start_d;

                        when start_d =>
                                nxt_state := idle;
                                ibusy := '0'; -- not busy when idle


                        -- stop
                        when stop_a =>
                                nxt_state := stop_b;

                        when stop_b =>
                                nxt_state := stop_c;

                        when stop_c =>
--                              nxt_state := stop_d;

--                      when stop_d =>
                                nxt_state := idle;
                                ibusy := '0'; -- not busy when idle

                        -- read
                        when rd_a =>
                                nxt_state := rd_b;

                        when rd_b =>
                                nxt_state := rd_c;

                        when rd_c =>
                                nxt_state := rd_d;
                                store_sda := '1';

                        when rd_d =>
                                nxt_state := idle;
                                ibusy := '0'; -- not busy when idle

                        -- write
                        when wr_a =>
                                nxt_state := wr_b;

                        when wr_b =>
                                nxt_state := wr_c;

                        when wr_c =>
                                nxt_state := wr_d;

                        when wr_d =>
                                nxt_state := idle;
                                ibusy := '0'; -- not busy when idle

                end case;

                -- generate regs
                if (nReset = '0') then
                        state <= idle;
                        cmd_ack <= '0';
                        busy <= '0';
                        txd <= '0';
                        Dout <= '0';
                elsif (clk'event and clk = '1') then
                        if (clk_en = '1') then
                                state <= nxt_state;
                                busy <= ibusy;

                                txd <= itxd;
                                if (store_sda = '1') then
                                        Dout <= SDA;
                                end if;
                        end if;

                        cmd_ack <= icmd_ack and clk_en;
                end if;
        end process nxt_state_decoder;

        --
        -- convert states to SCL and SDA signals
        --
        output_decoder: process (clk, nReset, state)
                variable iscl, isda : std_logic;
        begin
                case (state) is
                        when idle =>
                                iscl := SCLo; -- keep SCL in same state
                                isda := SDA; -- keep SDA in same state

                        -- start
                        when start_a =>
                                iscl := SCLo; -- keep SCL in same state (for repeated start)
                                isda := '1'; -- set SDA high

                        when start_b =>
                                iscl := '1';    -- set SCL high
                                isda := '1'; -- keep SDA high

                        when start_c =>
                                iscl := '1';    -- keep SCL high
                                isda := '0'; -- sel SDA low

                        when start_d =>
                                iscl := '0'; -- set SCL low
                                isda := '0'; -- keep SDA low

                        -- stop
                        when stop_a =>
                                iscl := '0'; -- keep SCL disabled
                                isda := '0'; -- set SDA low

                        when stop_b =>
                                iscl := '1'; -- set SCL high
                                isda := '0'; -- keep SDA low

                        when stop_c =>
                                iscl := '1'; -- keep SCL high
                                isda := '1'; -- set SDA high

                        -- write
                        when wr_a =>
                                iscl := '0';    -- keep SCL low
--                              isda := txd; -- set SDA
                                isda := Din;

                        when wr_b =>
                                iscl := '1';    -- set SCL high
--                              isda := txd; -- set SDA
                                isda := Din;

                        when wr_c =>
                                iscl := '1';    -- keep SCL high
--                              isda := txd; -- set SDA
                                isda := Din;

                        when wr_d =>
                                iscl := '0'; -- set SCL low
--                              isda := txd; -- set SDA
                                isda := Din;

                        -- read
                        when rd_a =>
                                iscl := '0'; -- keep SCL low
                                isda := '1'; -- tri-state SDA

                        when rd_b =>
                                iscl := '1'; -- set SCL high
                                isda := '1'; -- tri-state SDA

                        when rd_c =>
                                iscl := '1'; -- keep SCL high
                                isda := '1'; -- tri-state SDA

                        when rd_d =>
                                iscl := '0'; -- set SCL low
                                isda := '1'; -- tri-state SDA
                end case;

                -- generate registers
                if (nReset = '0') then
                        SCLo <= '1';
                        SDAo <= '1';
                elsif (clk'event and clk = '1') then
                        if (clk_en = '1') then
                                SCLo <= iscl;
                                SDAo <= isda;
                        end if;
                end if;
        end process output_decoder;

        SCL <= '0' when (SCLo = '0') else 'Z'; -- since SCL is externally pulled-up convert a '1' to a 'Z'(tri-state)
        SDA <= '0' when (SDAo = '0') else 'Z'; -- since SDA is externally pulled-up convert a '1' to a 'Z'(tri-state)
--      SCL <= SCLo;
--      SDA <= SDAo;

end architecture structural;




Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.