⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c_control.vhd

📁 Xilinx ISE 官方源代码盘第十章
💻 VHD
📖 第 1 页 / 共 2 页
字号:
-- i2c_control.vhd
--
-- Created: 12/30/99 ALS
--
-- 	This code implements the control of the i2c bus
--	created from code developed 6/99 - made minor changes
--	to reduce macrocell counts and changed system clock to 2Mhz
--	clock count only needs to be four bits since only counts half 
--	clock periods
--
-- Revised:	03/13/00 ALS
-- Revised:	06/29/00 ALS
-- Revised:	09/22/00 ALS


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

entity i2c_control is
  
  port(
	-- I2C bus signals
	sda : inout std_logic;
        scl : inout std_logic;
	
	-- interface signals from uP interface
	txak		: in		std_logic;	-- value for acknowledge when xmit
	msta		: in		std_logic; 	-- master/slave select
	msta_rst	: out		std_logic;	-- resets MSTA bit if arbitration is lost
	rsta		: in		std_logic;	-- repeated start 
	rsta_rst	: out		std_logic;	-- repeated start reset
	mtx		: in		std_logic;	-- master read/write 
	mbdr_micro	: in		std_logic_vector(7 downto 0);	-- uP data to output on I2C bus
	madr		: in		std_logic_vector(7 downto 0); -- I2C slave address
	mbb		: out		std_logic;	-- bus busy
	mcf		: inout		std_logic;	-- data transfer
	maas		: inout		std_logic;	-- addressed as slave
	mal		: inout		std_logic;	-- arbitration lost
	srw		: inout		std_logic;	-- slave read/write
	mif		: out		std_logic; 	-- interrupt pending
	rxak		: out		std_logic;	-- received acknowledge
	mbdr_i2c	: inout		std_logic_vector(7 downto 0); -- I2C data for uP
	mbcr_wr		: in		std_logic;	-- indicates that MCBR register was written
	mif_bit_reset 	: in		std_logic;	-- indicates that the MIF bit should be reset
	mal_bit_reset 	: in		std_logic;	-- indicates that the MAL bit should be reset

      	sys_clk : in std_logic;
	reset : in std_logic);

end i2c_control;

library IEEE;
use IEEE.std_logic_1164.all;

architecture behave of i2c_control is

constant	CNT_100KHZ	:	std_logic_vector(4 downto 0) := "10100";-- number of 2MHz clocks in 100KHz
constant	HIGH_CNT	: 	std_logic_vector(3 downto 0) := "1000";	-- number of 2MHz clocks in half 
										-- 100KHz period -1 since count from 0
										-- and -1 for "edge" state
constant	LOW_CNT		:	std_logic_vector(3 downto 0) := "1000";	-- number of 2Mhz clocks in half 
										-- 100KHZ period -1 since count from 0
										-- and -1 for "edge" state
constant	HIGH_CNT_2	:	std_logic_vector(3 downto 0) := "0100";	-- half of HIGH_CNT 

constant	TBUF		:	std_logic_vector(3 downto 0) := "1001"; -- number of 2MHz clocks in 4.7uS
constant	DATA_HOLD	:	std_logic_vector(3 downto 0) := "0001";	-- number of 2MHz clocks in 300ns
constant	START_HOLD	:	std_logic_vector(3 downto 0) := "1000";	-- number of 2MHz clocks in 4.0uS
constant	CLR_REG		:     	std_logic_vector (7 downto 0) := "00000000";
constant	START_CNT   	: 	std_logic_vector (3 downto 0) := "0000";
constant	CNT_DONE    	: 	std_logic_vector (3 downto 0) := "0111";
constant	ZERO_CNT	: 	std_logic_vector (3 downto 0) := "0000";  
constant	ZERO		: 	std_logic := '0'; 
constant 	RESET_ACTIVE 	:	std_logic := '0';

-- 8-bit serial load/parallel shift register
component SHIFT8
	port(
	     clk          : in std_logic;                        -- Clock
	     clr          : in std_logic;                        -- Active low clear
	     data_ld      : in std_logic;                        -- Data load enable
	     data_in      : in std_logic_vector (7 downto 0);     -- 8-bit data to load
	     shift_in     : in std_logic;                        -- Serial data in
	     shift_en     : in std_logic;                        -- Shift enable
	     shift_out    : out std_logic;                       -- Bit to shift out
	     data_out     : out std_logic_vector (7 downto 0));  -- 8-bit parallel out
		
end component;

-- Up counter - 4 bit
component UPCNT4
	port(
	     data         : in std_logic_vector (3 downto 0);    -- Serial data in
	     cnt_en       : in std_logic;                        -- Count enable
	     load         : in std_logic;                        -- Load line enable
 	     clr          : in std_logic;                        -- Active low clear
	     clk          : in std_logic;                        -- Clock
	     qout         : inout std_logic_vector (3 downto 0));
		
end component;


type state_type is (IDLE, HEADER, ACK_HEADER, RCV_DATA, ACK_DATA, 
				XMIT_DATA, WAIT_ACK);
signal state 		: state_type;

type scl_state_type is (SCL_IDLE, START, SCL_LOW_EDGE, SCL_LOW, SCL_HIGH_EDGE, 
				SCL_HIGH, STOP_WAIT);
signal scl_state, next_scl_state 		: scl_state_type;

signal scl_in		: std_logic;	-- sampled version of scl
signal scl_out		: std_logic;	-- combinatorial scl output from scl generator state machine
signal scl_out_reg	: std_logic;	-- registered version of SCL_OUT
signal scl_not		: std_logic;	-- inverted version of SCL
signal sda_in		: std_logic;	-- sampled version of sda
signal sda_out		: std_logic;	-- combinatorial sda output from scl generator state machine
signal sda_out_reg	: std_logic;	-- registered version of SDA_OUT
signal sda_out_reg_d1	: std_logic;	-- delayed sda output for arbitration comparison
signal slave_sda		: std_logic;	-- sda value when slave
signal master_sda		: std_logic;	-- sda value when master

signal sda_oe		: std_logic;

signal master_slave	: std_logic;	-- 1 if master, 0 if slave

-- Shift Register and the controls	
signal shift_reg		: std_logic_vector(7 downto 0);	-- shift register that holds I2C data				
signal shift_out		: std_logic;
signal shift_reg_en, shift_reg_ld   : std_logic;
signal i2c_header		: std_logic_vector(7 downto 0);	-- shift register that holds I2C header
signal i2c_header_en, i2c_header_ld : std_logic;
signal i2c_shiftout	: std_logic;

-- Used to check slave address detected
signal addr_match  : std_logic;


signal arb_lost		: std_logic;	-- 1 if arbitration is lost
signal msta_d1		: std_logic;	-- delayed sample of msta

signal detect_start	: std_logic;	-- indicates that a START condition has been detected
signal detect_stop	: std_logic;	-- indicates that a STOP condition has been detected
signal sm_stop		: std_logic;	-- indicates that a STOP condition needs to be generated
							-- from state machine
signal bus_busy		: std_logic;	-- indicates that the bus is busy - set when START, cleared when STOP
signal bus_busy_d1	: std_logic;	-- delayed sample of bus busy used to determine MAL
signal gen_start		: std_logic;	-- indicates that the uP wants to generate a START
signal gen_stop		: std_logic;	-- indicates that the uP wants to generate a STOP
signal rep_start		: std_logic;	-- indicates that the uP wants to generate a repeated START
signal stop_scl		: std_logic;	-- signal in SCL state machine indicating a STOP
signal stop_scl_reg	: std_logic;	-- registered version of STOP_SCL

-- Bit counter 0 to 7
signal bit_cnt		: std_logic_vector(3 downto 0);
signal bit_cnt_ld, bit_cnt_clr, bit_cnt_en : std_logic; 

-- Clock Counter
signal clk_cnt		: std_logic_vector (3 downto 0);
signal clk_cnt_rst	: std_logic;
signal clk_cnt_en 	: std_logic;      

-- the following signals are only here because Viewlogic's VHDL compiler won't allow a constant
-- to be used in a component instantiation
signal reg_clr		: std_logic_vector(7 downto 0);	
signal zero_sig		: std_logic;
signal cnt_zero		: std_logic_vector(3 downto 0);
signal cnt_start		: std_logic_vector(3 downto 0);



begin

  -- set SDA and SCL
	sda <= '0' when sda_oe = '1' else 'Z';
	scl <= '0' when scl_out_reg = '0' else 'Z';
	scl_not <= not(scl);

  -- sda_oe is set when master and arbitration is not lost and data to be output = 0 or
  -- when slave and data to be output is 0

	sda_oe <= '1' when ((master_slave = '1' and arb_lost = '0' and sda_out_reg = '0') or
				 (master_slave = '0' and slave_sda = '0')
				  or stop_scl_reg = '1') else '0';

 
-- the following signals are only here because Viewlogic's VHDL compiler won't allow a constant
-- to be used in a component instantiation
reg_clr <= CLR_REG;
zero_sig <= ZERO;
cnt_zero <= ZERO_CNT;
cnt_start <= START_CNT;


-- ************************  Arbitration Process ************************
-- This process checks the master's outgoing SDA with the incoming SDA to determine
-- if control of the bus has been lost. SDA is checked only when SCL is high
-- and during the states IDLE, HEADER, and XMIT_DATA to insure that START and STOP 
-- conditions are not set when the bus is busy. Note that this is only done when Master. 
-- When arbitration is lost, a reset is generated for the MSTA bit

-- Note that when arbitration is lost, the mode is switched to slave and SCL continues
-- to be generated until the byte transfer is complete

-- arb_lost stays set until scl state machine goes to IDLE state

arbitration: process (sys_clk, reset)
  begin
	if reset = RESET_ACTIVE then
		arb_lost <= '0';
		msta_rst <= '0';
    	elsif (sys_clk'event and sys_clk = '1') then
		if scl_state = SCL_IDLE then
			arb_lost <= '0';
			msta_rst <= '0';
      	elsif (master_slave = '1') then
			-- only need to check arbitration in master mode
        		-- check for SCL high before comparing data and insure that arb_lost is 
			-- not already set
			if (scl_in = '1' and scl = '1' and arb_lost = '0' 
				and (state = HEADER or	state = XMIT_DATA or state = IDLE)) then
				-- when master, will check bus in all states except ACK_HEADER and WAIT_ACK
				-- this will insure that arb_lost is set if a start or stop condition
				-- is set at the wrong time
				if sda_out_reg_d1 = sda_in then 
					arb_lost <= '0';
					msta_rst <= '0';
				else
					arb_lost <= '1';
					msta_rst <= '1';
				end if;
			else
				arb_lost <= arb_lost;
				msta_rst <= '0';
			end if;
		end if;
      
    	end if;

  end process;

-- ************************  SCL_Generator Process ************************
-- This process generates SCL and SDA when in Master mode. It generates the START
-- and STOP conditions. If arbitration is lost, SCL will be generated until the
-- end of the byte transfer.

scl_generator_comb: process (scl_state, arb_lost, sm_stop, gen_stop, rep_start, 
					bus_busy, gen_start, master_slave, stop_scl_reg, 
					clk_cnt, bit_cnt, scl_in, state, sda_out,
					sda_out_reg, master_sda)

begin

-- state machine defaults
	scl_out <= '1';
	sda_out <= sda_out_reg;
	stop_scl <= stop_scl_reg;
	clk_cnt_en <= '0';
	clk_cnt_rst <= '1';
	next_scl_state <= scl_state;
	rsta_rst <= not(RESET_ACTIVE);

		case scl_state is 
	
			when SCL_IDLE =>
				sda_out <= '1';
				stop_scl <= '0';
				-- leave IDLE state when master, bus is idle, and gen_start
				if master_slave = '1' and bus_busy = '0' and gen_start = '1' then
					next_scl_state <= START;
				end if;

			when START =>
				-- generate start condition
				clk_cnt_en <= '1';
				clk_cnt_rst <= '0';
				sda_out <= '0';
				stop_scl <= '0';
				-- generate reset for repeat start bit if repeat start condition
				if rep_start = '1' then
					rsta_rst <= RESET_ACTIVE;
				end if;
				if clk_cnt = START_HOLD then
					next_scl_state <= SCL_LOW_EDGE;
				else
					next_scl_state <= START;
				end if;
			
			when SCL_LOW_EDGE =>
				clk_cnt_rst <= '1';
				scl_out <= '0';
				next_scl_state <= SCL_LOW;
				stop_scl <= '0';


			when SCL_LOW =>
				clk_cnt_en <= '1';
				clk_cnt_rst <= '0';
				scl_out <= '0';
				
				-- set SDA_OUT based on control signals
				if arb_lost = '1' then
					stop_scl <= '0';
				elsif ((sm_stop = '1' or gen_stop = '1') and
					(state /= ACK_DATA and state /= ACK_HEADER and state /= WAIT_ACK)) then
					sda_out <= '0';
					stop_scl <= '1';
				elsif rep_start = '1' then
					sda_out <= '1';
					stop_scl <= '0';
				elsif clk_cnt = DATA_HOLD then
					sda_out <= master_sda;
					stop_scl <= '0';
				else
					stop_scl <= '0';
				end if;
			
				-- determine next state
				if clk_cnt = LOW_CNT then

					if bit_cnt = CNT_DONE and arb_lost = '1' then
						next_scl_state <= SCL_IDLE;
					else
						next_scl_state <= SCL_HIGH_EDGE;
					end if;
				else
					next_scl_state <= SCL_LOW;
				end if;
				
			when SCL_HIGH_EDGE =>
				clk_cnt_rst <= '1';
				scl_out <= '1';
				if ((sm_stop = '1' or gen_stop = '1') and
				   (state /= ACK_DATA and state /= ACK_HEADER and state /= WAIT_ACK)) then
					stop_scl <= '1';
				else
					stop_scl <= '0';
				end if;

				
				-- this state sets SCL high
				-- stay in this state until SCL_IN = 1
				-- this will hold the counter in reset until all SCL drivers
				-- have released SCL to 1
				if scl_in = '0' then
					next_scl_state <= SCL_HIGH_EDGE;
				else
					next_scl_state <= SCL_HIGH;
				end if;

			when SCL_HIGH =>
				-- now all SCL drivers have set SCL to '1'
				-- begin count for high time
				clk_cnt_en <= '1';
				clk_cnt_rst <= '0';
				scl_out <= '1';	
				-- check to see if a repeated start or a stop needs to be 
				-- generated. If so, only hold SCL high for half of the high time
				if clk_cnt = HIGH_CNT_2 then
					
					if rep_start = '1' then
						next_scl_state <= START;
						clk_cnt_rst <= '1';
					elsif stop_scl_reg = '1' then
						next_scl_state <= STOP_WAIT;
						clk_cnt_rst <= '1';
					end if;
				elsif clk_cnt = HIGH_CNT then 
					next_scl_state <= SCL_LOW_EDGE;
				else
					next_scl_state <= SCL_HIGH;
				end if;

			when STOP_WAIT =>
				--this state gives the required free time between stop and start
				--conditions
				clk_cnt_en <='1';
				clk_cnt_rst <= '0';
				sda_out <= '1';
				stop_scl <= '0';
				if clk_cnt = TBUF then
					next_scl_state <= SCL_IDLE;
				else
					next_scl_state <= STOP_WAIT;
				end if;
		
		end case;
end process;

scl_generator_regs: process (sys_clk, reset)
begin
	if reset = RESET_ACTIVE then
		scl_state <= SCL_IDLE;
		sda_out_reg <= '1';
		scl_out_reg <= '1';
		stop_scl_reg <= '0';
	elsif sys_clk'event and sys_clk='1' then
		scl_state <= next_scl_state;
		sda_out_reg <= sda_out;
		scl_out_reg <= scl_out;
		stop_scl_reg <= stop_scl;
	end if;
end process;

-- ************************  Clock Counter Implementation ************************
-- The following code implements the counter that divides the sys_clock for 
-- creation of SCL. Control lines for this counter are set in SCL state machine

	CLKCNT : UPCNT4
	  port map( data      => cnt_zero,
		      cnt_en    => clk_cnt_en,
		      load      => clk_cnt_rst,
		      clr       => reset,  
		      clk       => sys_clk, 
		      qout      => clk_cnt );
		




-- ************************  Input Registers Process ************************
-- This process samples the incoming SDA and SCL with the system clock

input_regs: process(sys_clk,reset)
begin
	if reset = RESET_ACTIVE then
		sda_in <= '1';
		scl_in <= '1';
		msta_d1 <= '0';
		sda_out_reg_d1 <= '1';
	
	elsif sys_clk'event and sys_clk = '1' then

		-- the following if, then, else clauses are used
		-- because scl may equal 'H' or '1'
		if scl = '0' then
			scl_in <= '0';
		else	
			scl_in <= '1';
		end if;
		if sda = '0' then
			sda_in <= '0';
		else
			sda_in <= '1';
		end if;
		sda_out_reg_d1 <= sda_out_reg;
		msta_d1 <= msta;
	end if;
end process;

-- ************************  START/STOP Detect Process ************************
-- This process detects the start and stop conditions.
-- by using SDA as a clock.
start_det: process(sda, reset, state)
begin
	if reset = RESET_ACTIVE or state = HEADER then
		detect_start <= '0';
	elsif sda'event and sda = '0' then
		if scl /= '0' then 
			detect_start <= '1';
		else
			detect_start <= '0';
		end if;
	end if;
end process;

stop_det: process(sda, reset, detect_start)
begin
	if reset = RESET_ACTIVE or detect_start = '1' then

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -