📄 smbus_control.vhd
字号:
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 <= smbus_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 smbus_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_SMBUS
mbdr_smbus_proc: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
mbdr_smbus <= (others => '0');
elsif sys_clk'event and sys_clk = '1' then
if (state = ACK_DATA) or (state = WAIT_ACK) then
mbdr_smbus <= shift_reg ;
else
mbdr_smbus <= mbdr_smbus;
end if;
end if;
end process;
-- ************************ uP Address Register ************************
addr_match <= '1' when smbus_header(7 downto 1) = madr(7 downto 1)
else '0';
-- ************************ Main State Machine Process ************************
-- The following process contains the main SMBUS 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 SMBUS_HEADER(0) for direction
if smbus_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 starts
if detect_start = '1' then
state <= HEADER;
-- Continue transmitting
elsif bit_cnt = CNT_DONE then
-- Send an acknowledge
state <= ACK_DATA;
end if;
------------ XMIT_DATA State --------------
when XMIT_DATA =>
--Check for repeated starts
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;
-- ************************ SMBUS Data Shift Register ************************
SMBUSDATA_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 );
smbusdata_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 smbus_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;
-- ************************ SMBUS Header Shift Register ************************
-- Header/Address Shift Register
SMBUSHEADER_REG: SHIFT8
port map (
clk => scl_not,
clr => reset,
data_ld => smbus_header_ld,
data_in => reg_clr,
shift_in => sda_in,
shift_en => smbus_header_en,
shift_out => smbus_shiftout,
data_out => smbus_header );
smbusheader_reg_ctrl: process(sys_clk, reset)
begin
if reset = RESET_ACTIVE then
smbus_header_en <= '0';
elsif sys_clk'event and sys_clk = '1' then
if (detect_start = '1') or (state = HEADER) then
smbus_header_en <= '1';
else
smbus_header_en <= '0';
end if;
end if;
end process;
smbus_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 + -