---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 14:25:56 05/29/2008 -- Design Name: -- Module Name: i2c - Behavioral -- Project Name: -- Target Devices: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.std_logic_arith.all; library work; use work.usefuls.all; ---- Uncomment the following library declaration if instantiating ---- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity i2c_mux is generic ( slave_width : integer := 1; clk_ns : integer := 1000; -- @ 1MHz minimum_scl_low_period_ns : integer := 1250; -- @ 400KHz scl_stretch : boolean := true); port( scl : inout std_logic; sda : inout std_logic; clk : in std_logic; reset : in std_logic; scl_slave : inout std_logic_vector(slave_width - 1 downto 0); sda_slave : inout std_logic_vector(slave_width - 1 downto 0); slave_oe : in std_logic_vector(slave_width - 1 downto 0); transaction : out std_logic; -- keep '1' in the same transaction slave2master : out std_logic); -- M2S => '0', S2M => '1' end i2c_mux; architecture Behavioral of i2c_mux is type state_type is (POR, IDLE, ADDR_RW_M2S, ADDR_RW_ACK_S2M, DATA_M2S, DATA_ACK_S2M, DATA_S2M, DATA_ACK_M2S); signal current_state : state_type := POR; signal scl_buf, sda_buf : std_logic; signal scl_buf2, sda_buf2 : std_logic; signal sda_selected : std_logic; signal scl_fallen, scl_risen : boolean; signal start_cond, stop_cond : boolean; signal sda_oe : std_logic; signal scl_oe : std_logic; signal shift_register : std_logic_vector(7 downto 0); signal shift_count : std_logic_vector(3 downto 0); signal scl_slave_oe : std_logic; signal sda_slave_oe : std_logic; signal dir_from_slave : boolean; signal scl_slave_buf, sda_slave_buf : std_logic; constant minimum_scl_low_period_count : integer := (minimum_scl_low_period_ns + (clk_ns - 1)) / clk_ns; signal scl_low_counter : std_logic_vector(log2_ceil(minimum_scl_low_period_count) downto 0); begin process(clk) begin if (clk'event and clk = '1') then scl_buf <= scl; sda_buf <= sda; scl_buf2 <= scl_buf; sda_buf2 <= sda_buf; if (not scl_slave) /= (scl_slave'range => '0') then scl_slave_buf <= '0'; else scl_slave_buf <= '1'; end if; if (((not sda_slave) and slave_oe) /= (sda_slave'range => '0')) then sda_slave_buf <= '0'; else sda_slave_buf <= '1'; end if; end if; end process; scl_fallen <= true when ((scl_buf2 /= '0') and (scl_buf = '0')) else false; scl_risen <= true when ((scl_buf2 = '0') and (scl_buf /= '0')) else false; start_cond <= true when (scl_buf2 /= '0' and scl_buf /= '0' and sda_buf2 /= '0' and sda_buf = '0') else false; stop_cond <= true when (scl_buf2 /= '0' and scl_buf /= '0' and sda_buf2 = '0' and sda_buf /= '0') else false; dir_from_slave <= true when current_state = ADDR_RW_ACK_S2M or current_state = DATA_ACK_S2M or current_state = DATA_S2M else false; gen_stretch_scl : if scl_stretch generate -- clock stretch enabled when master receiving DATA/ACK/NACK scl_oe <= '1' when (scl_low_counter <= minimum_scl_low_period_count) or (scl_slave_buf = '0') else '0'; scl_slave_oe <= '1' when (scl_low_counter <= minimum_scl_low_period_count) or scl_fallen else '0'; end generate; gen_normal_scl : if not scl_stretch generate -- ignore clock stretch scl_oe <= '0'; scl_slave_oe <= '1' when scl_buf = '0' else '0'; end generate; sda_oe <= '1' when dir_from_slave and sda_slave_buf = '0' else '0'; sda_slave_oe <= '1' when (not dir_from_slave) and (sda_buf = '0') else '0'; scl <= '0' when scl_oe = '1' else 'Z'; sda <= '0' when sda_oe = '1' else 'Z'; gen_slave : for i in slave_width - 1 downto 0 generate scl_slave(i) <= '0' when scl_slave_oe = '1' and slave_oe(i) = '1' else 'Z'; sda_slave(i) <= '0' when sda_slave_oe = '1' and slave_oe(i) = '1' else 'Z'; end generate; sda_selected <= '1' when dir_from_slave and (sda_slave = not conv_std_logic_vector(0, slave_width)) else '1' when (not dir_from_slave) and (sda /= '0') else '0'; process(clk) begin if clk'event and clk = '1' then if start_cond then -- START bit shift_count <= (others => '0'); elsif (current_state = ADDR_RW_M2S) or (current_state = DATA_M2S) or (current_state = DATA_S2M) then if scl_risen then shift_count <= shift_count + 1; else shift_count <= shift_count; end if; else shift_count <= (others => '0'); end if; end if; end process; process(clk) begin if (clk'event and clk = '1') then if scl_risen and ((current_state = ADDR_RW_M2S) or (current_state = DATA_M2S) or (current_state = DATA_S2M)) then shift_register <= shift_register(6 downto 0) & sda_selected; else shift_register <= shift_register; end if; end if; end process; slave2master <= '1' when dir_from_slave else '0'; transaction <= '0' when ((current_state = POR) or (current_state = IDLE)) else '1'; process(clk, reset) begin if reset = '1' then scl_low_counter <= (others => '1'); elsif clk'event and clk = '1' then if current_state = POR or current_state = IDLE then scl_low_counter <= (others => '1'); elsif scl_fallen then scl_low_counter <= (others => '0'); elsif scl_low_counter < (scl_low_counter'range => '1') then scl_low_counter <= scl_low_counter + 1; else scl_low_counter <= scl_low_counter; end if; end if; end process; process(clk, reset) begin if reset = '1' then current_state <= POR; elsif (clk'event and clk = '1') then if stop_cond then -- STOP bit current_state <= IDLE; elsif start_cond then -- START bit current_state <= ADDR_RW_M2S; else current_state <= current_state; case current_state is when POR => current_state <= IDLE; when IDLE => null; when ADDR_RW_M2S => if scl_fallen and shift_count = conv_std_logic_vector(8, 4) then current_state <= ADDR_RW_ACK_S2M; else null; end if; when ADDR_RW_ACK_S2M => if scl_fallen then if shift_register(0) = '1' then current_state <= DATA_S2M; else current_state <= DATA_M2S; end if; else null; end if; when DATA_M2S => if scl_fallen and shift_count = conv_std_logic_vector(8, 4) then current_state <= DATA_ACK_S2M; else null; end if; when DATA_ACK_S2M => if scl_fallen then current_state <= DATA_M2S; else null; end if; when DATA_S2M => if scl_fallen and shift_count = conv_std_logic_vector(8, 4) then current_state <= DATA_ACK_M2S; else null; end if; when DATA_ACK_M2S => if scl_fallen then if sda_selected = '1' then current_state <= IDLE; else current_state <= DATA_S2M; end if; else null; end if; end case; end if; end if; end process; end Behavioral;