-- ============================================================================================= -- ** -- ============================================================================================= -- Title: phy_i2c.vhd -- Description: VHDL design file for control of an I2C interface -- -- Interfaces to the I2C bus to provide control of DP83865 PHY -- Notes -- a)Max clock speed for this device is 2.5 MHz, standard I2C is 100 kHz -- b)Data format is start (2), opcode(2), Phy Address(5), RegAddress(5), turnround (2) -- data bits (16) -- Mod Record -- =========== -- 1)23/01/2008 11:50 created -- -- -- (C) Copyright Alpha Data Parallel Systems Ltd. 1999-2008 -- -- ------------------------------------------------------------------------------------------------- --******************************************************************************************** --******************************************************************************************** -- Entity Definition -- -- Notes: --******************************************************************************************** library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; entity phy_i2c is port ( --Host controls reset :in std_logic; clock :in std_logic; ckdiv_cnt :in std_logic_vector(15 downto 0); rd_flag :in std_logic; wr_flag :in std_logic; ini_flag :in std_logic; busy :out std_logic; -- Params for I2C transfer phy_addr :in std_logic_vector(4 downto 0); reg_addr :in std_logic_vector(4 downto 0); wrdata_in :in std_logic_vector(15 downto 0); rddata_out :out std_logic_vector(15 downto 0); --I2C signals i2c_clock :out std_logic; i2c_data :inout std_logic ); end phy_i2c; --******************************************************************************************** --******************************************************************************************** -- Architecture Definition -- --******************************************************************************************** architecture synth of phy_i2c is --******************************************************************************************** -- Constant/Type Definitions --******************************************************************************************** --sense defined for CPLD control polarity ( 1 = i2c bus driven) constant I2C_WR :std_logic := '1'; constant I2C_RD :std_logic := '0'; constant I2C_DEFAULT :std_logic := '1'; type state_type is ( i2c_init, i2c_idle, i2c_start, i2c_addrbit, i2c_addrck, i2c_cmndbit, i2c_cmndck, i2c_wr_bit, i2c_wrck, i2c_rd_bit, i2c_stop ); --******************************************************************************************** --******************************************************************************************** -- Component Definitions --******************************************************************************************** --******************************************************************************************** --******************************************************************************************** -- Signal Definitions -- --******************************************************************************************** ------------------------------------------------------ --State machine variables ------------------------------------------------------ signal next_busy_sig :std_logic; signal next_optype_sig :std_logic; signal next_rcvd_data_sig :std_logic_vector(15 downto 0); signal next_bitcnt_sig :std_logic_vector( 7 downto 0); signal next_i2c_dirn_sig :std_logic; signal next_i2c_data_bit_out_sig :std_logic; signal next_i2c_clock_sig :std_logic; signal next_i2c_ack_sig :std_logic; signal next_i2c_state_sig :state_type; signal busy_sig :std_logic; signal bitcnt_sig :std_logic_vector(7 downto 0); signal rcvd_data_sig :std_logic_vector(15 downto 0); signal optype_sig :std_logic; signal i2c_dirn_sig :std_logic; signal i2c_data_bit_out_sig :std_logic; signal i2c_clock_sig :std_logic; signal i2c_ack_sig :std_logic; signal i2c_state_sig :state_type; ------------------------------------------------------ ------------------------------------------------------ --I2c data port input ------------------------------------------------------ signal i2c_data_bit_in_sig :std_logic; ------------------------------------------------------ ------------------------------------------------------ --Local bus i/f ------------------------------------------------------ signal reset_sig :std_logic; signal clock_sig :std_logic; signal data_in_sig :std_logic_vector(15 downto 0); signal addr_sig :std_logic_vector( 12 downto 0); ------------------------------------------------------ ------------------------------------------------------ --Control signals ------------------------------------------------------ signal ckdiv_cnt_sig :std_logic_vector(15 downto 0); signal cntr_sig :std_logic_vector( 15 downto 0); signal cntr_carry_sig :std_logic; signal rd_flag_sig :std_logic_vector( 1 downto 0); signal wr_flag_sig :std_logic_vector( 1 downto 0); signal rd_strobe_sig :std_logic; signal wr_strobe_sig :std_logic; signal rd_posedge_sig :std_logic; signal wr_posedge_sig :std_logic; signal ini_flag_sig :std_logic_vector( 1 downto 0); signal ini_strobe_sig :std_logic; signal ini_posedge_sig :std_logic; ------------------------------------------------------ --******************************************************************************************** --******************************************************************************************** -- Attributes --******************************************************************************************** --******************************************************************************************** --******************************************************************************************** -- Architectural Statements --******************************************************************************************** begin --============================================================================================== -- Map ports to sigs --============================================================================================== reset_sig <= reset; clock_sig <= clock; ckdiv_cnt_sig <= ckdiv_cnt; busy <= busy_sig; rddata_out <=rcvd_data_sig; --i2c interface i2c_data <= i2c_data_bit_out_sig when i2c_dirn_sig='1' else 'Z'; i2c_data_bit_in_sig <= i2c_data; i2c_clock<= i2c_clock_sig; ------------------------------------------------------------------------------------------------ --============================================================================================== -- Detect rising edge of read or write flag --============================================================================================== posedge:process ( reset_sig, clock_sig) begin if reset_sig = '1' then rd_flag_sig <=(others => '0'); rd_strobe_sig <= '0'; rd_posedge_sig <= '0'; wr_flag_sig <=(others => '0'); wr_strobe_sig <= '0'; wr_posedge_sig <= '0'; ini_flag_sig <=(others => '0'); ini_strobe_sig <= '0'; ini_posedge_sig <= '0'; elsif rising_edge( clock_sig) then rd_flag_sig(0) <=rd_flag; rd_flag_sig(1) <=rd_flag_sig(0); rd_posedge_sig <= rd_flag_sig(0) and not rd_flag_sig(1); wr_flag_sig(0) <=wr_flag; wr_flag_sig(1) <=wr_flag_sig(0); wr_posedge_sig <= wr_flag_sig(0) and not wr_flag_sig(1); ini_flag_sig(0) <=ini_flag; ini_flag_sig(1) <=ini_flag_sig(0); ini_posedge_sig <= ini_flag_sig(0) and not ini_flag_sig(1); --run the init sequence if ini_flag_sig(0)='1' and ini_flag_sig(1)='0' then ini_strobe_sig <= '1'; elsif busy_sig ='1' then ini_strobe_sig <= '0'; end if; --set the strobe on a rising edge, clear when state machine goes busy if rd_flag_sig(0)='1' and rd_flag_sig(1)='0' then rd_strobe_sig <= '1'; elsif busy_sig ='1' then rd_strobe_sig <= '0'; end if; --read is dominant if they occur simultaneously if wr_flag_sig(0)='1' and wr_flag_sig(1)='0' and rd_flag_sig ="00" then wr_strobe_sig <= '1'; elsif busy_sig ='1' then wr_strobe_sig <= '0'; end if; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- Register values on detection of read or write command --============================================================================================== regip: process ( reset_sig,clock_sig) begin if reset_sig = '1' then addr_sig <=(others =>'0'); data_in_sig <= (others =>'0'); elsif rising_edge( clock_sig) then if busy_sig ='0' then if wr_posedge_sig = '1' then addr_sig <="101"&phy_addr®_addr; end if; if rd_posedge_sig = '1' then addr_sig <="110"&phy_addr®_addr; end if; data_in_sig <= wrdata_in; end if; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- Clock generator to control i2c rate ( = half carry sig rate) --============================================================================================== i2c_ck: process (clock_sig,reset_sig,ckdiv_cnt_sig) begin if reset_sig = '1' then cntr_sig <= ( others => '0'); cntr_carry_sig <= '0'; elsif rising_edge(clock_sig) then if cntr_sig = ckdiv_cnt_sig then cntr_sig <= ( others => '0'); cntr_carry_sig <= '1'; else cntr_sig <= cntr_sig+ '1'; cntr_carry_sig <= '0'; end if; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- default for i2c data out is high on the bus -- default for i2c clock is high on the bus -- default for i2c dirn is master(FPGA or CPLD) driving -- i2c data in is an slv --============================================================================================== state_adv: process (clock_sig,reset_sig) begin if reset_sig = '1' then --handshaking busy_sig<= '0'; --internal sigs rcvd_data_sig <= (others =>'1'); bitcnt_sig <= (others =>'0'); optype_sig <=I2C_WR; --i2c ports i2c_clock_sig <= I2C_DEFAULT; i2c_data_bit_out_sig <= I2C_DEFAULT; i2c_dirn_sig <= I2C_WR; i2c_ack_sig <= I2C_DEFAULT; --state variable i2c_state_sig <= i2c_idle; elsif rising_edge(clock_sig) then if cntr_carry_sig ='1' then -- probably only needs to apply to the state update rcvd_data_sig <= next_rcvd_data_sig; --handshaking busy_sig<= next_busy_sig; optype_sig <=next_optype_sig; --internal sigs bitcnt_sig <= next_bitcnt_sig; --i2c ports i2c_clock_sig <= next_i2c_clock_sig; i2c_data_bit_out_sig <= next_i2c_data_bit_out_sig ; i2c_dirn_sig <= next_i2c_dirn_sig; i2c_ack_sig <= next_i2c_ack_sig; --state variable i2c_state_sig <= next_i2c_state_sig; end if; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- State mchine --============================================================================================== state_mux: process (i2c_clock_sig,i2c_state_sig, rcvd_data_sig, rd_strobe_sig,ini_strobe_sig, wr_strobe_sig, optype_sig, bitcnt_sig, i2c_data_bit_out_sig, i2c_ack_sig, i2c_data_bit_in_sig, data_in_sig,addr_sig) begin case i2c_state_sig is ------------------------------------------------ --Default state - all signals inactive -- ck and data out = high -- dirn = write ------------------------------------------------ when i2c_idle => -- wait for command next_busy_sig <= '0'; next_bitcnt_sig <= (others =>'0'); next_rcvd_data_sig <= rcvd_data_sig; next_i2c_clock_sig <= '0'; next_i2c_ack_sig <= '1'; next_i2c_dirn_sig <= I2C_WR; --default if ini_strobe_sig='1' then next_optype_sig <= I2C_WR; --default next_i2c_data_bit_out_sig <= '1'; next_i2c_state_sig <=i2c_init; elsif rd_strobe_sig='1' then next_optype_sig <= I2C_RD; next_i2c_data_bit_out_sig <= '0'; next_i2c_state_sig <=i2c_start; elsif wr_strobe_sig='1' then next_optype_sig <= I2C_WR; next_i2c_data_bit_out_sig <= '0'; next_i2c_state_sig <=i2c_start; else next_optype_sig <= I2C_WR; --default next_i2c_data_bit_out_sig <= '1'; next_i2c_state_sig <=i2c_idle; end if; ------------------------------------------------ --start is data hi to lo folowed by clock rising edge -- then clock low -- data and clock hi on entry ------------------------------------------------ when i2c_start => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_dirn_sig <= I2C_WR; next_i2c_ack_sig <= '1'; if bitcnt_sig = X"00" then next_i2c_data_bit_out_sig <= '0'; next_i2c_clock_sig <= '1'; next_bitcnt_sig <= bitcnt_sig + '1'; next_i2c_state_sig <=i2c_start; else next_i2c_data_bit_out_sig <= '0'; next_i2c_clock_sig <= '0'; next_bitcnt_sig <= (others =>'0'); next_i2c_state_sig <=i2c_addrbit; end if; ------------------------------------------------ -- clock is low on entry - op the address bits -- (incl. opmode bits and register addres) -- MSB first. Set the data bit and then pulse clock hi/lo -- ------------------------------------------------ when i2c_addrbit => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_bitcnt_sig <= bitcnt_sig; next_i2c_dirn_sig <= I2C_WR; next_i2c_clock_sig <= '0'; next_i2c_ack_sig <= '1'; next_i2c_data_bit_out_sig <= addr_sig((12-CONV_INTEGER(bitcnt_sig)) ); -- MSB first next_i2c_state_sig <=i2c_addrck; ------------------------------------------------ -- Update ------------------------------------------------ when i2c_addrck => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_dirn_sig <= I2C_WR; next_i2c_ack_sig <= '1'; next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig; if i2c_clock_sig = '1' then next_i2c_clock_sig <= '0'; if bitcnt_sig = X"0C" then next_i2c_state_sig <=i2c_cmndbit; next_bitcnt_sig <= (others =>'0'); else next_i2c_state_sig <=i2c_addrbit; next_bitcnt_sig <= bitcnt_sig + '1'; end if; else next_bitcnt_sig <= bitcnt_sig; next_i2c_clock_sig <= '1'; next_i2c_state_sig <=i2c_addrck; end if; ------------------------------------------------ --Set up the data line for the turn-around state --for a read, o/p is Z and data is don't care --for a write, o/p 10 ------------------------------------------------ when i2c_cmndbit => next_busy_sig <= '1'; next_optype_sig <= optype_sig; if optype_sig = I2C_RD then next_i2c_dirn_sig <= I2C_RD; else next_i2c_dirn_sig <= I2C_WR; end if; next_rcvd_data_sig <= rcvd_data_sig; next_bitcnt_sig <= bitcnt_sig; next_i2c_data_bit_out_sig <= not bitcnt_sig(0); next_i2c_clock_sig <= '0'; next_i2c_ack_sig <= '1'; next_i2c_state_sig <=i2c_cmndck; ------------------------------------------------ --Clock the turn-around state -- ------------------------------------------------ when i2c_cmndck => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig; next_i2c_ack_sig <= i2c_ack_sig; next_i2c_dirn_sig <= i2c_dirn_sig; if i2c_clock_sig = '1' then next_i2c_clock_sig <= '0'; if bitcnt_sig = X"01" then next_bitcnt_sig <= (others => '0'); -- done, pick read or wr path if optype_sig = I2C_RD then next_i2c_state_sig <=i2c_rd_bit; else next_i2c_state_sig <=i2c_wr_bit; end if; else next_bitcnt_sig <= bitcnt_sig + '1'; next_i2c_state_sig <=i2c_cmndbit; end if; else next_bitcnt_sig <= bitcnt_sig; next_i2c_clock_sig <= '1'; next_i2c_state_sig <=i2c_cmndck; end if; ------------------------------------------------ -- Write the data ------------------------------------------------ when i2c_wr_bit => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_bitcnt_sig <= bitcnt_sig; next_i2c_dirn_sig <= I2C_WR; next_i2c_data_bit_out_sig <= data_in_sig((15 -CONV_INTEGER(bitcnt_sig)) ); -- MSB first next_i2c_clock_sig <= '0'; next_i2c_ack_sig <= '1'; next_i2c_state_sig <=i2c_wrck; when i2c_wrck => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_dirn_sig <= I2C_WR; next_i2c_ack_sig <= '1'; --Hold the op data bit next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig; if i2c_clock_sig = '1' then next_i2c_clock_sig <= '0'; if bitcnt_sig = X"0F" then next_bitcnt_sig <= (others => '0'); next_i2c_state_sig <=i2c_stop; else next_bitcnt_sig <= bitcnt_sig + '1'; next_i2c_state_sig <=i2c_wr_bit; end if; else next_i2c_clock_sig <= '1'; next_bitcnt_sig <= bitcnt_sig; next_i2c_state_sig <=i2c_wrck; end if; ------------------------------------------------ --Shift data into received data latch --clock is low on entry --dirn = ip on entry ------------------------------------------------ when i2c_rd_bit => next_busy_sig <= '1'; next_optype_sig <=optype_sig; next_i2c_dirn_sig <= I2C_RD; next_i2c_data_bit_out_sig <= i2c_data_bit_out_sig; next_i2c_ack_sig <= '1'; if i2c_clock_sig = '1' then next_rcvd_data_sig <= rcvd_data_sig; next_i2c_clock_sig <= '0'; if bitcnt_sig = X"0F" then next_bitcnt_sig <= (others => '0'); next_i2c_state_sig <=i2c_stop; else next_bitcnt_sig <= bitcnt_sig + '1'; next_i2c_state_sig <=i2c_rd_bit; end if; else next_i2c_clock_sig <= '1'; next_bitcnt_sig <= bitcnt_sig; next_rcvd_data_sig(0) <= i2c_data_bit_in_sig; for i in 0 to 14 loop next_rcvd_data_sig(i+1) <= rcvd_data_sig(i); -- MSB first; end loop; next_i2c_state_sig <=i2c_rd_bit; end if; ------------------------------------------------ -- rd/wr complete, clock is low on entry -- stop = Z data followed by clock high ------------------------------------------------ when i2c_stop => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_ack_sig <= '1'; next_i2c_dirn_sig <= I2C_RD; if i2c_clock_sig = '0' then next_bitcnt_sig <= bitcnt_sig+'1'; if bitcnt_sig = X"01" then next_i2c_clock_sig <= '1'; else next_i2c_clock_sig <= '0'; end if; next_i2c_data_bit_out_sig <= '1'; next_i2c_state_sig <=i2c_stop; else next_bitcnt_sig <= (others =>'0'); next_i2c_clock_sig <= '0'; next_i2c_data_bit_out_sig <= '1'; next_i2c_state_sig <=i2c_idle; end if; ------------------------------------------------ -- Write min 32 clock pulses, data hi -- to re-sync after a reset ------------------------------------------------ when i2c_init => next_busy_sig <= '1'; next_optype_sig <= optype_sig; next_rcvd_data_sig <= rcvd_data_sig; next_i2c_ack_sig <= '1'; next_i2c_dirn_sig <= I2C_WR; next_i2c_data_bit_out_sig <= '1'; if i2c_clock_sig = '0' then next_i2c_clock_sig <= '1'; if bitcnt_sig = X"30" then next_i2c_state_sig <=i2c_idle; next_bitcnt_sig <= (others =>'0'); else next_i2c_state_sig <=i2c_init; next_bitcnt_sig <= bitcnt_sig; end if; else next_bitcnt_sig <= bitcnt_sig+'1'; next_i2c_clock_sig <= '0'; next_i2c_state_sig <=i2c_init; end if; ------------------------------------------------ --Catchall ------------------------------------------------ when others => next_busy_sig <= '1'; next_optype_sig <=optype_sig; next_i2c_ack_sig <= '1'; next_rcvd_data_sig <= rcvd_data_sig; next_bitcnt_sig <= (others =>'0'); next_i2c_dirn_sig <= I2C_WR; next_i2c_clock_sig <= '1'; next_i2c_data_bit_out_sig <= '1'; next_i2c_ack_sig <= '1'; next_i2c_state_sig <=i2c_idle; end case; end process; ------------------------------------------------------------------------------------------------ end; -- architecture i2c --********************************************************************************************