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