📄 i2c_control.vhd
字号:
-- 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 + -