aboutsummaryrefslogtreecommitdiffstats
path: root/usrp2/opencores/i2c/rtl/vhdl/I2C.VHD
diff options
context:
space:
mode:
Diffstat (limited to 'usrp2/opencores/i2c/rtl/vhdl/I2C.VHD')
-rw-r--r--usrp2/opencores/i2c/rtl/vhdl/I2C.VHD620
1 files changed, 620 insertions, 0 deletions
diff --git a/usrp2/opencores/i2c/rtl/vhdl/I2C.VHD b/usrp2/opencores/i2c/rtl/vhdl/I2C.VHD
new file mode 100644
index 000000000..64d1eb656
--- /dev/null
+++ b/usrp2/opencores/i2c/rtl/vhdl/I2C.VHD
@@ -0,0 +1,620 @@
+--
+-- 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;
+
+
+
+