Reputation: 387
First of all, because of company disclosure agreement I am not allowed to post my code thus I will describe the symptoms of the behavior and hopefully it's enough.
I am using the IP for I2C master provided by Scott Larson on eewiki and I use it the same way in the example provided on the page. The difference is that I have several procedures as described in the example
WHEN get_data => --state for conducting this transaction
busy_prev <= i2c_busy; --capture the value of the previous i2c busy signal
IF(busy_prev = '0' AND i2c_busy = '1') THEN --i2c busy just went high
busy_cnt := busy_cnt + 1; --counts the times busy has gone from low to high during transaction
END IF;
CASE busy_cnt IS --busy_cnt keeps track of which command we are on
WHEN 0 => --no command latched in yet
i2c_ena <= '1'; --initiate the transaction
i2c_addr <= slave_addr; --set the address of the slave
i2c_rw <= '0'; --command 1 is a write
i2c_data_wr <= data_to_write; --data to be written
WHEN 1 => --1st busy high: command 1 latched, okay to issue command 2
i2c_rw <= '1'; --command 2 is a read (addr stays the same)
WHEN 2 => --2nd busy high: command 2 latched, okay to issue command 3
i2c_rw <= '0'; --command 3 is a write
i2c_data_wr <= new_data_to_write; --data to be written
IF(i2c_busy = '0') THEN --indicates data read in command 2 is ready
data(15 DOWNTO 8) <= i2c_data_rd; --retrieve data from command 2
END IF;
WHEN 3 => --3rd busy high: command 3 latched, okay to issue command 4
i2c_rw <= '1'; --command 4 is read (addr stays the same)
WHEN 4 => --4th busy high: command 4 latched, ready to stop
i2c_ena <= '0'; --deassert enable to stop transaction after command 4
IF(i2c_busy = '0') THEN --indicates data read in command 4 is ready
data(7 DOWNTO 0) <= i2c_data_rd; --retrieve data from command 4
busy_cnt := 0; --reset busy_cnt for next transaction
state <= home; --transaction complete, go to next state in design
END IF;
WHEN OTHERS => NULL;
END CASE;
For example at the end if the get_data
instead of going to home
I go to an initializing case to reset busy_cnt
and i2c_ena
and other signals then to another case for writing similar to get_data
.
Current Situation
i2c_busy
on and off and the behavior is as expected.routed using generic routing resource and might suffer from excessive delay and/or skew
The Problem
As I mentioned before the code is divided into several cases similar to get_data
in the example above and when executing it is stuck in an infinite loop in one of them. And when I change something in the case that is stuck another case before it misbehaves and also stuck in an infinite loop. Even changing a simple signal that has nothing to do with the algorithm (LED output for debugging) in one case might cause a case before it to misbehave.
The infinite loop behavior is also strange and shows state is not set at the end of the case.
Note that I am not using the same way in the example to update state rather I use a separate Process for the state update (curr_state <= next_state;
) and the next_state
is the one updated.
My Speculation I assumed that the problem could be because I have to set every output in every case. But even after setting all I could the behavior was similar.
For the sake of completion: The development environment is Lattice Diamond and the FPGA is MachXO2.
Upvotes: 2
Views: 503
Reputation: 3659
The posted code snippet has severe problems, if it's not part of a clocked process anymore according to the comments:
Me: Is the posted code still part of a clocked process?
mmahdich: @Martin In my code it's not, the clocked process only updates the curr_state <= next_state
If I embed the code from the question into the following test architecture, then the synthesis compiler XST (from ISE 14.7) reports warnings about latches for the signals busy_cnt
, busy_prev
and data
. The reason that the OP observes no latch warnings may result from further optimizations or interference from the undisclosed code parts. (I have no Lattice Diamond at hand.)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2c_test is
port (
clk : in std_logic;
i2c_busy : in std_logic;
slave_addr : in std_logic;
data_to_write : in std_logic_vector(7 downto 0);
new_data_to_write : in std_logic_vector(7 downto 0);
i2c_data_rd : in std_logic_vector(7 downto 0);
i2c_ena : out std_logic;
i2c_addr : out std_logic;
i2c_rw : out std_logic;
i2c_data_wr : out std_logic_vector(7 downto 0);
data : out std_logic_vector(15 downto 0));
end i2c_test;
architecture rtl of i2c_test is
type state_t is (get_data, home);
signal curr_state : state_t := home;
signal state : state_t; -- next state is named "state" in OP code
signal busy_prev : std_logic;
begin -- rtl
process (clk)
begin -- process
if rising_edge(clk) then
curr_state <= state;
end if;
end process;
process(curr_state, busy_prev, i2c_busy, slave_addr, data_to_write, new_data_to_write, i2c_data_rd)
variable busy_cnt : integer range 0 to 4 := 0;
begin
state <= curr_state; -- next state is named "state" in OP code
i2c_ena <= '0';
i2c_addr <= '-';
i2c_rw <= '-';
i2c_data_wr <= (others => '-');
-- no default assignments for busy_prev and data here, because the usage
-- below indicates that a register was intended
case curr_state is
when home => state <= get_data;
---------------------------------------------------------------
-- Add code from question here
---------------------------------------------------------------
end case;
end process;
end rtl;
First of all, the signal for the next state seems to be named state
.
Then, XST founds latches for the signals busy_prev
and data
. I have not added default assignments for these signals in the combinational process because the following assignments in the OP's code indicate that a register was intended:
busy_prev <= i2c_busy; --capture the value of the previous i2c busy signal
data(15 DOWNTO 8) <= i2c_data_rd; --retrieve data from command 2
data(7 DOWNTO 0) <= i2c_data_rd; --retrieve data from command 4
Finally, one cannot (easily) implement this wait counter without a clocked process:
WHEN get_data => --state for conducting this transaction
IF(busy_prev = '0' AND i2c_busy = '1') THEN --i2c busy just went high
busy_cnt := busy_cnt + 1; --counts the times busy has gone from low to high during transaction
END IF;
EDIT
Synthesizing the above would require flip-flops for busy_cnt
which is triggered by all signals listed in the process sensitivity list. Of course, a new state will be loaded into the flip-flops only if the condition (curr_state = get_data and busy_prev = '0' and i2c_busy = '1')
is true. For example, XST synthesizes a latch for this, which is enabled when the condition is true. But then, busy_cnt
forms a combinational loop during an enabled latch. This synthesized behaviour does not match the VHDL description.
The solution would be to implement the registers busy_prev
, data
and busy_cnt
in the clocked process.
Upvotes: 1