0
votes

I write simple vhdl code for Uart receiver. Simulation (iSIM) is fine but when implemented I have wrong reading behavior. When synthetized ISE tell me there are latches on state machine end on data_fill(x). have you any suggestion.

thanks in advance gian

here the code

library ieee;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity rx_uart is
port (
  clk : in STD_LOGIC;
  rst : in STD_LOGIC;
  rx_data : in STD_LOGIC;
  data_out: out STD_LOGIC_VECTOR(7 downto 0)
);
end rx_uart;

architecture fizzim of rx_uart is

-- state bits
subtype state_type is STD_LOGIC_VECTOR(2 downto 0);

constant idle: state_type:="000"; -- receive_en=0 load_en=0 cnt_en=0 
constant receive: state_type:="101"; -- receive_en=1 load_en=0 cnt_en=1 
constant stop_load: state_type:="010"; -- receive_en=0 load_en=1 cnt_en=0 

signal state,nextstate: state_type;
signal cnt_en_internal: STD_LOGIC;
signal load_en_internal: STD_LOGIC;
signal receive_en_internal: STD_LOGIC;
signal count : integer range 0 to 54686:=0;
signal cnt_en : STD_LOGIC;
signal load_en : STD_LOGIC;
signal receive_en : STD_LOGIC;
signal data_fill : STD_LOGIC_VECTOR(9 downto 0);

-- comb always block
begin


 COUNTER_EN : process(clk,rst,cnt_en) begin
   if (rst ='1') then
   count <= 0;
   elsif rising_edge(clk) then
        if (cnt_en ='1') then
        count <= count+1;
        else
        count <= 0;
        end if;
   end if;
 end process;
 LOADER: process(clk,rst,load_en) begin
    if (rst='1') then
    data_out <= (others =>'0');
    elsif (rising_edge(clk) and load_en='1')then
    data_out <= data_fill(8 downto 1);
    end if;
 end process;

 ASSIGNATION : process(clk,rst,receive_en) begin
 if (rst ='1') then
 data_fill <= (others =>'0');
 elsif (receive_en='1') then
    case count is
        when 7812 =>
        data_fill(1) <= rx_data;
        when 13020 =>
        data_fill(2) <= rx_data;
        when 18228 =>
        data_fill(3) <= rx_data;
        when 23436 =>
        data_fill(4) <= rx_data;
        when 28664 =>
        data_fill(5) <= rx_data;
        when 33852 =>
        data_fill(6) <= rx_data;
        when 39060 =>
        data_fill(7) <= rx_data;
        when 44268 =>
        data_fill(8) <= rx_data;
        when 49476 =>
        data_fill(9) <= rx_data;
        when others =>
        data_fill(0) <= '0';
     end case;
 end if;
 end process;

  COMB: process(state,clk,count,rst,rx_data) begin   
    case state is
      when idle      =>
        if (rx_data='0') then
          nextstate <= receive;
        elsif (rx_data='1') then
          nextstate <= idle;
        end if;

      when receive   =>
        if (count<=54685) then
          nextstate <= receive;
        elsif (count>54685) then
          nextstate <= stop_load;
        end if;

      when stop_load =>
        nextstate <= idle;

      when others =>

    end case;
  end process;

  -- Assign reg'd outputs to state bits
  cnt_en_internal <= state(0);
  load_en_internal <= state(1);
  receive_en_internal <= state(2);

  -- Port renames for vhdl
  cnt_en <= cnt_en_internal;
  load_en <= load_en_internal;
  receive_en <= receive_en_internal;

  -- sequential always block
  FF: process(clk,rst,nextstate) begin
    if (rst='1') then
      state <= idle;
    elsif (rising_edge(clk)) then
      state <= nextstate;
    end if;
  end process;
end fizzim;
2

2 Answers

0
votes

You need to understand when latches are generated. Latches are generated when you have an incomplete assignment in combinational logic. Your case statements are combinational. You do not completely assign all possibilities of your data signal and your state machine. When you have an incomplete assignment in a combinational process you will always generate a latch. Latches are bad!

Use your when others properly to assign all of your signals under all conditions. Your data_fill signal will always generate a latch because you don't handle all conditions for data 0:9 on all cases.

Read more about how to avoid latches in VHDL

Edit: It seems too that you don't consistently create sequential logic in VHDL. You need to be creating a clocked process or remove clk from your sensitivity list in a combinational process.

0
votes

your design has several bad code sites besides your latch problem. I'll number them for better reference.

(1) Xilinx XST won't recognize the implemented state machine as a FSM. See your XST report or the *.syr file. There won't be a FSM section. If XST finds no FSM it's not able to choose "the best" state encoding and optimize your FSM.

You should use an enum as a state-type and also initialize your state signal:

type t_state is (st_idle, st_receive, st_stop);
signal state     : t_state    := st_idle;
signal nextstate : t_state;

Your FSM process also needs default assignments (see Russell's explanation) like

nextstate <= state;

(2) asynchronous resets are no good design practice and complicate timing closure calculations.

(3) you are handling raw input signals from outside you FPGA without any timing information. to prevent meta stability problems place two D-flip-flops on the rx_data path. you should also add the async-reg and no-srl-extract attribute to these 2 registers to prevent XST optimizations

SIGNAL I_async  : STD_LOGIC         := '0';
SIGNAL I_sync   : STD_LOGIC         := '0';

-- Mark register "I_async" as asynchronous
ATTRIBUTE ASYNC_REG OF I_async      : SIGNAL IS "TRUE";

-- Prevent XST from translating two FFs into SRL plus FF
ATTRIBUTE SHREG_EXTRACT OF I_async  : SIGNAL IS "NO";
ATTRIBUTE SHREG_EXTRACT OF I_sync   : SIGNAL IS "NO";

[...]
BEGIN
[...]
I_async <= In       WHEN rising_edge(Clock);
I_sync  <= I_async  WHEN rising_edge(Clock);

Let 'In' be your asynchronous input. Now you can use I_sync in your clock domain. I choose to describe these 2 registers in a one-liner, you can also use a classic process :) The suffix '_async' allows you to define a matching timing rule in your *.xcf and *.ucf files.