library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity slaveIn is port( scl_i, sda_i : in std_logic; -- I2C SCL, SDA input scl_o, sda_o : out std_logic; -- I2C SCL, SDA output devAddr_i : in std_logic_vector (6 downto 0); -- I2C address for this device is16Bits_i : in std_logic; -- register size, 1: 16 bits, 0: 8 bits data_o : out unsigned(15 downto 0)); -- register data output ( (7,0) for 8-bit register ) -- dbg: out std_logic_vector (7 downto 0); -- for testing only -- dbg_start: out std_logic; -- for testing only -- dbg_start2: out std_logic_vector (3 downto 0)); -- for testing only end slaveIn; architecture slaveIn_impl of slaveIn is -- I2C communication states: -- idle: before receiving a S bit or after received a P bit -- start: receiving device address -- ack_start: Ack. master if the received address equal devAddr_i, otherwise, go to 'hold' state. -- read_a: receiving first byte -- ack_read_a: ack. master -- read_b: receiving second byte -- ack_read_b: ack. master -- hold: Wait until received a P bit. -- type states is (idle,start,ack_start,read_a,ack_a,read_b,ack_b,hold); -- c_state represents current state signal c_state : states := idle; -- internal buffer for receiving bits from master signal reg : std_logic_vector ( 7 downto 0) := "00000000"; signal regBit : std_logic; -- counter for shifting reg signal shiftCnt : unsigned(3 downto 0) := "0000"; -- signals for reg signal ldReg, rstReg : std_logic := '0'; -- Load reg into aReg(15,8) when assignAHigh = 1. -- Load reg into aReg(7,0) when assignALow = 1. signal assignAHigh, assignALow : std_logic := '0'; -- device register -- Only aReg(7,0) are used when is16Bits_i = 0. signal aReg : unsigned(15 downto 0) := X"0000"; -- I2C SDA output signal signal isda : std_logic := '1'; -- S bit, P bit signal signal started, doStop : std_logic := '0'; begin -- preserve input across low scl_i regBit <= sda_i when scl_i = '1'; -- manipulate internal buffer -- write always happening in scl_i's falling edge -- loadReg: process(scl_i, rstReg, regBit,reg) begin if(scl_i = '0' and scl_i'event) then if(rstReg = '1') then -- clear reg and shiftCnt reg <= "00000000"; shiftCnt <= "0000"; elsif(ldReg = '1') then -- shift reg and load regBit into reg(0) reg <= reg(6 downto 0) & regBit; shiftCnt <= shiftCnt + 1; end if; end if; end process loadReg; -- load reg into aReg assignAReg: process(assignAHigh, assignALow) begin if(assignAHigh = '1' and assignAHigh'event) then -- assign the first (high) byte for i in 15 downto 8 loop aReg(i) <= reg(i - 8); end loop; end if; if(assignALow = '1' and assignALow'event) then -- assign the second (low) bye for i in 7 downto 0 loop aReg(i) <= reg(i); end loop; end if; end process assignAReg; -- detect S bit and set started signal -- S bit: when sda_i is falling and scl_i is high -- -- note: sda_i never changes when scl_i is rising or high -- except the master transmiting S or P bit detectStart: process(scl_i, sda_i, doStop) begin if(doStop = '1') then started <= '0'; elsif(sda_i = '0' and sda_i'event) then if(scl_i = '1' and started = '0') then started <='1'; end if; end if; end process detectStart; -- detect P bit and set doStop signal -- P bit: when sda_i is rising and scl_i is high detectStop: process(scl_i, sda_i, started) begin if(started = '0') then doStop <= '0'; elsif(sda_i = '1' and sda_i'event) then if(scl_i = '1' and started = '1') then doStop <='1'; end if; end if; end process detectStop; -- send ack. bit -- isda must be low before next rising scl_i -- and hold low when scl_i is high. doAck: process(scl_i, c_state) begin if(c_state = idle or (c_state = hold and scl_i = '0')) then isda <= '1'; elsif(scl_i = '0' and scl_i'event) then case(c_state) is when ack_start => if(reg(6 downto 0) /= devAddr_i or regBit /= '0') then isda <= '1'; -- not for this device => don't ack. else isda <= '0'; end if; when ack_a => isda <= '0'; when ack_b => isda <= '0'; when others => isda <= '1'; end case; end if; end process doAck; -- state machine stateMachine: process(scl_i, c_state, started) begin if(started = '0') then c_state <= idle; elsif(started = '1' and c_state=idle) then c_state <= start; elsif(scl_i = '1' and scl_i'event) then case(c_state) is when idle => assignAHigh <= '0'; assignALow <= '0'; rstReg <= '1'; ldReg <= '0'; c_state <= idle; when start => assignAHigh <= '0'; assignALow <= '0'; rstReg <= '0'; ldReg <= '1'; if(shiftCnt = "0111") then -- receiving 8 bits before going to next state c_state <= ack_start; else c_state <= start; end if; when ack_start => assignAHigh <= '0'; assignALow <= '0'; ldReg <= '0'; rstReg <= '1'; if(reg(7 downto 1) /= devAddr_i or reg(0) /= '0') then c_state <= hold; -- hold until receiving P bit else if(is16Bits_i = '1') then c_state <= read_a; else c_state <= read_b; end if; end if; when read_a => assignAHigh <= '0'; assignALow <= '0'; rstReg <= '0'; ldReg <= '1'; if(shiftCnt = "0111") then c_state <= ack_a; else c_state <= read_a; end if; when ack_a => assignAHigh <= '1'; assignALow <= '0'; ldReg <= '0'; rstReg <= '1'; c_state <= read_b; when read_b => assignAHigh <= '0'; assignALow <= '0'; rstReg <= '0'; ldReg <= '1'; if(shiftCnt = "0111") then c_state <= ack_b; else c_state <= read_b; end if; when ack_b => assignAHigh <= '0'; assignALow <= '1'; ldReg <= '0'; rstReg <= '1'; c_state <= hold; when hold => -- do nothing assignAHigh <= '0'; assignALow <= '0'; ldReg <= '0'; rstReg <= '1'; c_state <= hold; when others => -- do nothing assignAHigh <= '0'; assignALow <= '0'; ldReg <= '0'; rstReg <= '0'; c_state <= c_state; end case; end if; end process stateMachine; -- assign output signals data_out: for i in 15 downto 0 generate data_o(i) <= aReg(i); end generate; sda_o <= isda; scl_o <= '1'; -- this device never pulling down SCL -- assign debug signals -- dbg <= reg; -- for testing only -- dbg_start <= started; -- for testing only -- dbg_start2 <= "000" & isda; -- for testing only end slaveIn_impl;