library IEEE; use IEEE.STD_LOGIC_1164.all; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; -- ******* I2C Implementation ******* -- -- WARNING: This is work in progress! -- -- Note: I2C master procedures must be called with -- 2*I2C clock, slave procedure significantly -- faster -- -- => I2C Slave is not implemented yet! -- -- ******************************************** package I2C is subtype tByte is std_logic_vector(7 downto 0); type tModeCfg is (MODE_I2C_MASTER, MODE_I2C_SLAVE); type tState is (LEADING, SETUP, TRAILING, SAMPLE); type tStartStopState is (START, ACT1, ACT2, ACT3, ACT4); type tI2Cdata is record -- config initial : boolean; -- registers cnt : integer range 0 to 7; state : tState; stateSS : tStartStopState; end record; constant tI2Cdata_Default : tI2CData := ( initial => True, cnt => 7, state => LEADING, stateSS => START ); -- setup initial signal state procedure initMaster ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic ); procedure ioMasterBase ( signal data : inout tI2Cdata; signal SCL : out std_logic; variable done : inout boolean ); procedure ioMasterSend ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal byteSend : in tByte; signal done : inout boolean ); procedure ioMasterRecv ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal byteRecv : out tByte; signal done : inout boolean ); procedure ioMasterAck ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal done : inout boolean; signal success : out boolean ); procedure ioMasterStart ( signal data : inout tI2Cdata; signal SDA : inout std_ulogic; signal done : inout boolean ); procedure ioMasterStop ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal done : inout boolean ); -- utility function to_std_logic( data : in std_logic ) return std_logic; -- function to_std_logic( data : in std_logic ) return std_logic; end I2C; package body I2C is procedure initMaster( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic ) is begin if data.initial then SCL <= '1'; SDA <= 'Z'; data.initial <= False; end if; end initMaster; procedure ioMasterBase ( signal data : inout tI2Cdata; signal SCL : out std_logic; variable done : inout boolean ) is begin -- update std_logic counter done := False; if data.state = SAMPLE then -- last state ? if data.cnt = 0 then -- last std_logic ? done := True; -- reset internal states data.cnt <= 7; else data.cnt <= data.cnt - 1; end if; end if; -- update clock if data.state = LEADING then SCL <= '0'; elsif data.state = TRAILING then SCL <= '1'; end if; -- update state case data.state is when LEADING => data.state <= SETUP; when SETUP => data.state <= TRAILING; when TRAILING => data.state <= SAMPLE; when SAMPLE => data.state <= LEADING; end case; end ioMasterBase; procedure ioMasterSend ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal byteSend : in tByte; signal done : inout boolean ) is variable doneVar : boolean := False; begin if not done then if data.state = SETUP then SDA <= to_std_logic(byteSend(data.cnt)); end if; ioMasterBase(data, SCL, doneVar); if doneVar then done <= True; end if; end if; end ioMasterSend; procedure ioMasterRecv ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal byteRecv : out tByte; signal done : inout boolean ) is variable doneVar : boolean := False; begin if not done then if data.state = SAMPLE then if SDA = '1' then byteRecv(data.cnt) <= '1'; else byteRecv(data.cnt) <= '0'; end if; end if; ioMasterBase(data, SCL, doneVar); if doneVar then done <= True; end if; end if; end ioMasterRecv; procedure ioMasterAck ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal done : inout boolean; signal success : out boolean ) is begin if not done then case data.state is when LEADING => SCL <= '0'; SDA <= 'Z'; data.state <= SETUP; when SETUP => data.state <= TRAILING; when TRAILING => SCL <= '1'; data.state <= SAMPLE; when SAMPLE => success <= (SDA = '0'); done <= True; data.state <= LEADING; end case; end if; end ioMasterAck; procedure ioMasterStart ( signal data : inout tI2Cdata; signal SDA : inout std_ulogic; signal done : inout boolean ) is begin if not done then case data.stateSS is when START => SDA <= '1'; data.stateSS <= ACT1; when ACT1 => SDA <= '0'; data.stateSS <= START; done <= True; when others => null; end case; end if; end ioMasterStart; procedure ioMasterStop ( signal data : inout tI2Cdata; signal SCL : out std_logic; signal SDA : inout std_ulogic; signal done : inout boolean ) is begin if not done then case data.stateSS is when START => -- leading edge SCL <= '0'; data.stateSS <= ACT1; when ACT1 => -- pull to low SDA <= '0'; data.stateSS <= ACT2; when ACT2 => -- trailing edge SCL <= '1'; data.stateSS <= ACT3; when ACT3 => -- pull to high SDA <= '1'; data.stateSS <= ACT4; when ACT4 => -- release SDA <= 'Z'; data.stateSS <= START; done <= True; end case; end if; end ioMasterStop; function to_std_logic( data : in std_logic ) return std_logic is begin if data = '1' then return '1'; else return '0'; end if; end to_std_logic; end I2C;