-- ============================================================================================= -- ** -- ============================================================================================= -- Title: phy_cntrl.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_cntrl 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; 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); --PHY signals phy_reset_l :out std_logic; i2c_clock :out std_logic; i2c_data :inout std_logic ); end phy_cntrl; --******************************************************************************************** --******************************************************************************************** -- Architecture Definition -- --******************************************************************************************** architecture synth of phy_cntrl is --******************************************************************************************** -- Constant/Type Definitions --******************************************************************************************** constant MSB_CNT :integer := 23; constant PHY0_ADDR :std_logic_vector := "10000"; constant PHY1_ADDR :std_logic_vector := "10001"; constant REG0_ADDR :std_logic_vector := "00000"; constant REG13_ADDR :std_logic_vector := "10011"; constant PWDN_CODE :std_logic_vector := x"0800"; constant LED_CODE :std_logic_vector := x"0c00"; --steady green type state_type is ( i2c_idle, i2c_reset, i2c_init, i2c_wait0, i2c_pwdn1, i2c_wait1, i2c_pwdn2, i2c_wait2, i2c_led1, i2c_wait3, i2c_led2, i2c_wait4, i2c_stop ); --******************************************************************************************** --******************************************************************************************** -- Component Definitions --******************************************************************************************** component 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 component phy_i2c; --******************************************************************************************** --******************************************************************************************** -- Signal Definitions -- --******************************************************************************************** signal if_wr_flag_sig : std_logic; signal if_rd_flag_sig : std_logic; signal if_phyaddr_sig : std_logic_vector(4 downto 0); signal if_regaddr_sig :std_logic_vector(4 downto 0); signal rd_data_sig : std_logic_vector(15 downto 0); signal if_wrdata_sig : std_logic_vector(15 downto 0); signal i2c_data_sig : std_logic; 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; ------------------------------------------------------ ------------------------------------------------------ --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; signal wr_flag_sig :std_logic; 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 cnt_flag_sig :std_logic_vector( 1 downto 0); signal ini_strobe_sig :std_logic; signal ini_posedge_sig :std_logic; ------------------------------------------------------ --******************************************************************************************** --******************************************************************************************** -- Attributes --******************************************************************************************** signal phy_inicnt_sig :std_logic_vector ( MSB_CNT downto 0); signal phy_iniflag_sig :std_logic_vector ( 3 downto 0); signal sm_phyaddr_sig : std_logic_vector(4 downto 0); signal sm_regaddr_sig : std_logic_vector(4 downto 0); signal next_sm_phyaddr_sig : std_logic_vector(4 downto 0); signal next_sm_regaddr_sig : std_logic_vector(4 downto 0); signal sm_wrdata_sig : std_logic_vector(15 downto 0); signal next_sm_wrdata_sig : std_logic_vector(15 downto 0); signal sm_busy_sig : std_logic; signal sm_wrflag_sig : std_logic; signal next_sm_busy_sig : std_logic; signal next_sm_wrflag_sig : std_logic; signal if_busy_sig : std_logic; signal if_busypl_sig : std_logic_vector(1 downto 0); signal sm_iniflag_sig : std_logic; signal next_sm_iniflag_sig : std_logic; signal phy_addr_sig : std_logic_vector(4 downto 0); signal reg_addr_sig : std_logic_vector(4 downto 0); signal wr_data_sig : std_logic_vector(15 downto 0); signal i2c_state_sig : state_type; signal next_i2c_state_sig : state_type; signal phy_reset_l_sig :std_logic; --******************************************************************************************** --******************************************************************************************** -- Architectural Statements --******************************************************************************************** begin --============================================================================================== -- Map ports to sigs --============================================================================================== reset_sig <= reset; clock_sig <= clock; ckdiv_cnt_sig <= ckdiv_cnt; rd_flag_sig <=rd_flag; wr_flag_sig <=wr_flag; phy_addr_sig <=phy_addr; reg_addr_sig <=reg_addr; wr_data_sig <= wrdata_in; rddata_out <= rd_data_sig; busy <= busy_sig; phy_reset_l <=phy_reset_l_sig; ------------------------------------------------------------------------------------------------ --============================================================================================== --Delay from end of reset for 1.2 ms min then init the phy interface. --1.2 ms at 80 MHz = 1e6 so 20 bits at least plus margin --Once complete, restore external control of the serial interface -- reset must be held low for a min of 150 us= 12000 (0x=2ee0) counts min at 80 MHz- us 0x3000 --============================================================================================== process(reset_sig, clock_sig) begin if reset_sig = '1' then phy_inicnt_sig <= (others=> '0'); phy_reset_l_sig <='0'; elsif rising_edge(clock_sig) then if phy_inicnt_sig(13 downto 12) ="11" then phy_reset_l_sig <= '1'; end if; if phy_inicnt_sig(MSB_CNT) ='0' then phy_inicnt_sig <= phy_inicnt_sig + '1'; end if; cnt_flag_sig(0)<=phy_inicnt_sig(MSB_CNT); cnt_flag_sig(1)<=cnt_flag_sig(0); end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== --Mux the control signals --============================================================================================== process(reset_sig, clock_sig) begin if reset_sig = '1' then busy_sig <='1'; if_phyaddr_sig <= PHY0_ADDR; if_regaddr_sig <= REG0_ADDR; if_wrdata_sig <=PWDN_CODE; if_rd_flag_sig <= '0'; if_wr_flag_sig <= '0'; elsif rising_edge(clock_sig) then if sm_busy_sig ='1' then busy_sig <='1'; if_phyaddr_sig <= sm_phyaddr_sig; if_regaddr_sig <= sm_regaddr_sig; if_wrdata_sig <=sm_wrdata_sig; if_rd_flag_sig <= '0'; if_wr_flag_sig <= sm_wrflag_sig; else busy_sig <=if_busy_sig; if_phyaddr_sig <= phy_addr_sig; if_regaddr_sig <= reg_addr_sig; if_wrdata_sig <=wr_data_sig; if_rd_flag_sig <= rd_flag_sig; if_wr_flag_sig <= wr_flag_sig; end if; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- Instatiate the serial interface --============================================================================================== physerif:phy_i2c port map ( --Host controls reset =>reset_sig, clock =>clock_sig, ckdiv_cnt =>ckdiv_cnt_sig, rd_flag =>if_rd_flag_sig, wr_flag =>if_wr_flag_sig, ini_flag =>sm_iniflag_sig, busy =>if_busy_sig, -- Params for I2C transfer phy_addr =>if_phyaddr_sig, reg_addr =>if_regaddr_sig, wrdata_in =>if_wrdata_sig, rddata_out =>rd_data_sig, --I2C signals i2c_clock =>i2c_clock, i2c_data =>i2c_data ); ------------------------------------------------------------------------------------------------ --============================================================================================== --Pipeline the busy signal for SM use --============================================================================================== process(reset_sig, clock_sig) begin if reset_sig = '1' then if_busypl_sig <= "00"; elsif rising_edge(clock_sig) then if_busypl_sig(0) <=if_busy_sig; if_busypl_sig(1) <=if_busypl_sig(0); 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 sm_iniflag_sig<= '0'; sm_wrflag_sig<= '0'; sm_regaddr_sig <= (others =>'0'); sm_phyaddr_sig <= (others =>'0'); sm_wrdata_sig <= (others =>'0'); sm_busy_sig <= '1'; --state variable i2c_state_sig <= i2c_idle; elsif rising_edge(clock_sig) then sm_iniflag_sig <= next_sm_iniflag_sig; sm_wrflag_sig <= next_sm_wrflag_sig; sm_regaddr_sig <= next_sm_regaddr_sig; sm_phyaddr_sig <= next_sm_phyaddr_sig; sm_wrdata_sig <= next_sm_wrdata_sig; sm_busy_sig <= next_sm_busy_sig; --state variable i2c_state_sig <= next_i2c_state_sig; end if; end process; ------------------------------------------------------------------------------------------------ --============================================================================================== -- State machine --============================================================================================== state_mux: process (i2c_state_sig ) begin case i2c_state_sig is ------------------------------------------------ --Wait for end of reset delay ------------------------------------------------ when i2c_idle => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; if cnt_flag_sig="01" then next_i2c_state_sig <=i2c_init; else next_i2c_state_sig <=i2c_idle; end if; ------------------------------------------------ --Trigger the initialisation sequence for the serial if ------------------------------------------------ when i2c_init => -- send command next_sm_iniflag_sig <= '1'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; next_i2c_state_sig <=i2c_wait0; ------------------------------------------------ --Wait for falling edge of busy from i/f ------------------------------------------------ when i2c_wait0 => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; if if_busypl_sig="10" then next_i2c_state_sig <=i2c_pwdn1; else next_i2c_state_sig <=i2c_wait0; end if; ------------------------------------------------ -- Write the PHY0 power down code -- reg 0, value 0x0800 ------------------------------------------------ when i2c_pwdn1 => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '1'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; next_i2c_state_sig <=i2c_wait1; ------------------------------------------------ --Wait for falling edge of busy from i/f ------------------------------------------------ when i2c_wait1 => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; if if_busypl_sig="10" then next_i2c_state_sig <=i2c_pwdn2; else next_i2c_state_sig <=i2c_wait1; end if; ------------------------------------------------ -- Write the PHY1 power down code -- reg 0, value 0x0800 ------------------------------------------------ when i2c_pwdn2 => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '1'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY1_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; next_i2c_state_sig <=i2c_wait2; ------------------------------------------------ --Wait for falling edge of busy from i/f ------------------------------------------------ when i2c_wait2 => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY1_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '1'; if if_busypl_sig="10" then next_i2c_state_sig <=i2c_led1; else next_i2c_state_sig <=i2c_wait2; end if; ------------------------------------------------ -- Set the green LED -- reg 0x13, value 0x0800 ------------------------------------------------ when i2c_led1 => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '1'; next_sm_regaddr_sig <= REG13_ADDR; next_sm_phyaddr_sig <= PHY1_ADDR; next_sm_wrdata_sig <= LED_CODE; next_sm_busy_sig <= '1'; next_i2c_state_sig <=i2c_wait3; ------------------------------------------------ --Wait for falling edge of busy from i/f ------------------------------------------------ when i2c_wait3 => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG13_ADDR; next_sm_phyaddr_sig <= PHY1_ADDR; next_sm_wrdata_sig <= LED_CODE; next_sm_busy_sig <= '1'; if if_busypl_sig="10" then next_i2c_state_sig <=i2c_led2; else next_i2c_state_sig <=i2c_wait3; end if; ------------------------------------------------ -- Write the PHY1 power down code -- ------------------------------------------------ when i2c_led2 => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '1'; next_sm_regaddr_sig <= REG13_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= LED_CODE; next_sm_busy_sig <= '1'; next_i2c_state_sig <=i2c_wait4; ------------------------------------------------ --Wait for falling edge of busy from i/f ------------------------------------------------ when i2c_wait4 => -- wait for command next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG13_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= LED_CODE; next_sm_busy_sig <= '1'; if if_busypl_sig="10" then next_i2c_state_sig <=i2c_stop; else next_i2c_state_sig <=i2c_wait4; end if; ------------------------------------------------ -- rd/wr complete, clock is low on entry -- stop = Z data followed by clock high ------------------------------------------------ when i2c_stop => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '0'; next_i2c_state_sig <=i2c_stop; ------------------------------------------------ --Catchall ------------------------------------------------ when others => next_sm_iniflag_sig <= '0'; next_sm_wrflag_sig <= '0'; next_sm_regaddr_sig <= REG0_ADDR; next_sm_phyaddr_sig <= PHY0_ADDR; next_sm_wrdata_sig <= PWDN_CODE; next_sm_busy_sig <= '0'; next_i2c_state_sig <=i2c_stop; end case; end process; ------------------------------------------------------------------------------------------------ end; -- architecture i2c --********************************************************************************************