📄 i2c_control.vhd
字号:
detect_stop <= '0';
elsif sda'event and sda /= '0' then
if scl /= '0' then
detect_stop <= '1';
else
detect_stop <= '0';
end if;
end if;
end process;
-- ************************ Bus Busy Process ************************
-- This process detects the start and stop conditions and sets the bus busy bit
-- It also describes a delayed version of the bus busy bit which is used to determine
-- MAL. MAL should be set if a start is detected while the bus is busy, however, the code below
-- sets bus_busy as soon as START is detected which would always set MAL. Therefore, a delayed
-- version of bus_busy is generated and used to determine MAL.
set_bus_busy: process(sys_clk,reset)
begin
if reset = RESET_ACTIVE then
bus_busy <= '0';
bus_busy_d1 <= '0';
elsif sys_clk'event and sys_clk = '1' then
bus_busy_d1 <= bus_busy;
if detect_start = '1' then
bus_busy <= '1';
end if;
if detect_stop = '1' then
bus_busy <= '0';
end if;
end if;
end process;
-- ************************ uP Control Bits Process ************************
-- This process detects the rising and falling edges of MSTA and sets signals to
-- control generation of start and stop conditions
-- This process also sets the master slave bit based on MSTA if and only if it is not
-- in the middle of a cycle, i.e. bus_busy = '0'
control_bits: process (sys_clk,reset)
begin
if reset = RESET_ACTIVE then
gen_start <= '0';
gen_stop <= '0';
master_slave <= '0';
elsif sys_clk'event and sys_clk = '1' then
if msta_d1 = '0' and msta = '1' then
-- rising edge of MSTA - generate start condition
gen_start <= '1';
elsif detect_start = '1' then
gen_start <= '0';
end if;
if arb_lost = '0' and msta_d1 = '1' and msta = '0' then
-- falling edge of MSTA - generate stop condition only
-- if arbitration has not been lost
gen_stop <= '1';
elsif detect_stop = '1' then
gen_stop <= '0';
end if;
if bus_busy = '0' then
master_slave <= msta;
else
master_slave <= master_slave;
end if;
end if;
end process;
rep_start <= rsta; -- repeat start signal is RSTA control bit
-- ************************ uP Status Register Bits Processes ************************
-- The following processes and assignments set the bits of the MBUS status register MBSR
--
-- MCF - Data transferring bit
-- While one byte of data is being transferred, this bit is cleared. It is set by the falling edge
-- of the 9th clock of a byte transfer and is not cleared at reset
mcf_bit: process(scl, reset)
begin
if reset = RESET_ACTIVE then
mcf <= '0';
elsif scl'event and scl = '0' then
if bit_cnt = CNT_DONE then
mcf <= '1';
else
mcf <= '0';
end if;
end if;
end process;
-- MAAS - Addressed As Slave Bit
-- When its own specific address (MADR) matches the I2C Address, this bit is set. The CPU is
-- interrupted provided the MIEN is set. Then the CPU needs to check the SRW bit and set its
-- TX-RX mode accordingly. Writing to the MBCR clears this bit
maas_bit: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
maas <= '0';
elsif sys_clk'event and sys_clk = '1' then
if mbcr_wr = '1' then
maas <= '0';
elsif state = ACK_HEADER then
maas <= addr_match; -- the signal address match compares MADR with I2_ADDR
else
maas <= maas;
end if;
end if;
end process;
-- MBB - Bus Busy Bit
-- This bit indicates the status of the bus. This bit is set when a START signal is detected and
-- cleared when a stop signal is detected. It is also cleared on reset. This bit is identical to
-- the signal bus_busy set in the process set_bus_busy.
mbb <= bus_busy;
-- MAL - Arbitration Lost Bit
-- This bit is set when the arbitration procedure is lost. Arbitration is lost when:
-- 1. SDA is sampled low when the master drives high during addr or data transmit cycle
-- 2. SDA is sampled low when the master drives high during the acknowledge bit of a
-- data receive cycle
-- 3. A start cycle is attempted when the bus is busy
-- 4. A repeated start is requested in slave mode
-- 5. A stop condition is detected that the master did not request it.
-- This bit is cleared upon reset and when the software writes a '0' to it
-- Conditions 1 & 2 above simply result in SDA_IN not matching SDA_OUT while SCL is high. This
-- design will not generate a START condition while the bus is busy. When a START is detected, this hardware
-- will set the bus busy bit and gen_start stays set until detect_start asserts, therefore will have
-- to compare with a delayed version of bus_busy. Condition 3 is really just
-- a check on the uP software control registers as is condition 4. Condition 5 is also taken care
-- of by the fact that SDA_IN does not equal SDA_OUT, however, this process also tests for if a stop
-- condition has been detected when this master did not generate it
mal_bit: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
mal <= '0';
elsif sys_clk'event and sys_clk = '1' then
if mal_bit_reset = '1' then
mal <= '0';
elsif master_slave = '1' then
if (arb_lost = '1') or
(bus_busy_d1 = '1' and gen_start = '1') or
(detect_stop = '1' and gen_stop = '0' and sm_stop = '0') then
mal <= '1';
end if;
elsif rsta = '1' then
-- repeated start requested while slave
mal <= '1';
end if;
end if;
end process;
-- SRW - Slave Read/Write Bit
-- When MAAS is set, SRW indicates the value of the R/W command bit of the calling address sent
-- from the master. This bit is only valid when a complete transfer has occurred and no other
-- transfers have been initiated. The CPU uses this bit to set the slave transmit/receive mode.
-- This bit is reset by reset
srw_bit: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
srw <= '0';
elsif sys_clk'event and sys_clk = '1' then
if state = ACK_HEADER then
srw <= i2c_header(0);
else
srw <= srw;
end if;
end if;
end process;
-- MIF - M-bus Interrupt
-- The MIF bit is set when an interrupt is pending, which causes a processor interrupt
-- request provided MIEN is set. MIF is set when:
-- 1. Byte transfer is complete (set at the falling edge of the 9th clock
-- 2. MAAS is set when in slave receive mode
-- 3. Arbitration is lost
-- This bit is cleared by reset and software writting a '0'to it
mif_bit: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
mif <= '0';
elsif sys_clk'event and sys_clk = '1' then
if mif_bit_reset = '1' then
mif <= '0';
elsif mal = '1' or mcf = '1' or
(maas = '1' and i2c_header(0) = '0' and master_slave = '0') then
mif <= '1';
end if;
end if;
end process;
-- RXAK - Received Acknowledge
-- RXAK contains the value of SDA during the acknowledge bit of a bus cycle. If =0, then
-- an acknowledge signal has been received, if 1, then no acknowledge has been received.
-- This bit is not cleared at reset. The CPLD will reset this bit upon power-up
rxak_bit: process(scl)
begin
if scl'event and scl = '0' then
if state = ACK_HEADER or state = ACK_DATA or state = WAIT_ACK then
rxak <= sda_in;
end if;
end if;
end process;
-- ************************ uP Data Register ************************
-- Register for uP interface MBDR_I2C
mbdr_i2c_proc: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
mbdr_i2c <= (others => '0');
elsif sys_clk'event and sys_clk = '1' then
if (state = ACK_DATA) or (state = WAIT_ACK) then
mbdr_i2c <= shift_reg ;
else
mbdr_i2c <= mbdr_i2c;
end if;
end if;
end process;
-- ************************ uP Address Register ************************
addr_match <= '1' when i2c_header(7 downto 1) = madr(7 downto 1)
else '0';
-- ************************ Main State Machine Process ************************
-- The following process contains the main I2C state machine for both master and slave
-- modes. This state machine is clocked on the falling edge of SCL. DETECT_STOP must stay as
-- an asynchronous reset because once STOP has been generated, SCL clock stops.
state_machine: process (scl, reset, detect_stop)
begin
if reset = RESET_ACTIVE or detect_stop = '1' then
state <= IDLE;
sm_stop <= '0';
elsif scl'event and scl = '0' then
case state is
------------- IDLE STATE -------------
when IDLE =>
if detect_start = '1' then
state <= HEADER;
end if;
------------- HEADER STATE -------------
when HEADER =>
if bit_cnt = CNT_DONE then
state <= ACK_HEADER;
end if;
------------- ACK_HEADER STATE -------------
when ACK_HEADER =>
if arb_lost = '1' then
state <= IDLE;
elsif sda_in = '0' then
-- ack has been received, check for master/slave
if master_slave = '1' then
-- master, so check mtx bit for direction
if mtx = '0' then
-- receive mode
state <= RCV_DATA;
else
--transmit mode
state <= XMIT_DATA;
end if;
else
if addr_match = '1' then
--if maas = '1' then
-- addressed slave, so check I2C_HEADER(0) for direction
if i2c_header(0) = '0' then
-- receive mode
state <= RCV_DATA;
else
-- transmit mode
state <= XMIT_DATA;
end if;
else
-- not addressed, go back to IDLE
state <= IDLE;
end if;
end if;
else
-- no ack received, stop
state <= IDLE;
if master_slave = '1' then
sm_stop <= '1';
end if;
end if;
------------- RCV_DATA State --------------
when RCV_DATA =>
-- check for repeated start
if (detect_start = '1') then
state <= HEADER;
elsif bit_cnt = CNT_DONE then
-- Send an acknowledge
state <= ACK_DATA;
end if;
------------ XMIT_DATA State --------------
when XMIT_DATA =>
-- check for repeated start
if (detect_start = '1') then
state <= HEADER;
elsif bit_cnt = CNT_DONE then
-- Wait for acknowledge
state <= WAIT_ACK;
end if;
------------- ACK_DATA State --------------
when ACK_DATA =>
state <= RCV_DATA;
------------- WAIT_ACK State --------------
when WAIT_ACK =>
if arb_lost = '1' then
state <= IDLE;
elsif (sda = '0') then
state <= XMIT_DATA;
else
-- no ack received, generate a stop and return
-- to IDLE state
if master_slave = '1' then
sm_stop <= '1';
end if;
state <= IDLE;
end if;
end case;
end if;
end process;
-- ************************ Slave and Master SDA ************************
slv_mas_sda: process(reset, sys_clk)
begin
if reset = RESET_ACTIVE then
master_sda <= '1';
slave_sda <= '1';
elsif sys_clk'event and sys_clk = '1' then
if state = HEADER or state = XMIT_DATA then
master_sda <= shift_out;
elsif state = ACK_DATA then
master_sda <= TXAK;
else
master_sda <= '1';
end if;
-- For the slave SDA, address match (MAAS) only has to be checked when
-- state is ACK_HEADER because state
-- machine will never get to state XMIT_DATA or ACK_DATA
-- unless address match is a one.
if (maas = '1' and state = ACK_HEADER) or
(state = ACK_DATA) then
slave_sda <= TXAK;
elsif (state = XMIT_DATA) then
slave_sda <= shift_out;
else
slave_sda <= '1';
end if;
end if;
end process;
-- ************************ I2C Data Shift Register ************************
I2CDATA_REG: SHIFT8
port map (
clk => scl_not,
clr => reset,
data_ld => shift_reg_ld,
data_in => mbdr_micro,
shift_in => sda_in,
shift_en => shift_reg_en,
shift_out => shift_out,
data_out => shift_reg );
i2cdata_reg_ctrl: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
shift_reg_en <= '0';
shift_reg_ld <= '0';
elsif sys_clk'event and sys_clk = '1' then
if ((master_slave = '1' and state = HEADER)
or (state = RCV_DATA) or (state = XMIT_DATA)) then
shift_reg_en <= '1';
else
shift_reg_en <= '0';
end if;
if ((master_slave = '1' and state = IDLE) or (state = WAIT_ACK)
or (state = ACK_HEADER and i2c_header(0) = '1' and master_slave = '0')
or (state = ACK_HEADER and mtx = '1' and master_slave = '1')
or (detect_start = '1') ) then
shift_reg_ld <= '1';
else
shift_reg_ld <= '0';
end if;
end if;
end process;
-- ************************ I2C Header Shift Register ************************
-- Header/Address Shift Register
I2CHEADER_REG: SHIFT8
port map (
clk => scl_not,
clr => reset,
data_ld => i2c_header_ld,
data_in => reg_clr,
shift_in => sda_in,
shift_en => i2c_header_en,
shift_out => i2c_shiftout,
data_out => i2c_header );
i2cheader_reg_ctrl: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
i2c_header_en <= '0';
elsif sys_clk'event and sys_clk = '1' then
if (detect_start = '1') or (state = HEADER) then
i2c_header_en <= '1';
else
i2c_header_en <= '0';
end if;
end if;
end process;
i2c_header_ld <= '0';
-- ************************ Bit Counter ************************
BITCNT : UPCNT4
port map( data => cnt_start,
cnt_en => bit_cnt_en,
load => bit_cnt_ld,
clr => reset,
clk => scl_not,
qout => bit_cnt );
-- Counter control lines
bit_cnt_en <= '1' when (state = HEADER) or (state = RCV_DATA)
or (state = XMIT_DATA) else '0';
bit_cnt_ld <= '1' when (state = IDLE) or (state = ACK_HEADER)
or (state = ACK_DATA)
or (state = WAIT_ACK)
or (detect_start = '1') else '0';
end behave;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -