-- =============================================================================================
-- **
-- =============================================================================================
-- 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
--********************************************************************************************