0

我为 Uart 接收器编写了简单的 vhdl 代码。模拟(iSIM)很好,但在实施时我有错误的阅读行为。当综合 ISE 告诉我 data_fill(x) 上的状态机端有锁存器时。你有什么建议吗?

在此先感谢吉安

这里的代码

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;
4

2 回答 2

0

您需要了解何时生成闩锁。当您在组合逻辑中有不完整的分配时,会生成锁存器。您的案例陈述是组合的。您没有完全分配数据信号和状态机的所有可能性。当您在组合过程中有不完整的分配时,您将始终生成一个锁存器。闩锁很糟糕!

在所有条件下正确使用您when others的分配所有信号。您的 data_fill 信号将始终生成一个锁存器,因为您并非在所有情况下都处理数据 0:9 的所有条件。

阅读有关如何避免 VHDL 中的锁存器的更多信息

编辑:您似乎也没有始终如一地在 VHDL 中创建顺序逻辑。您需要在组合过程中创建时钟进程或从敏感度列表中删除 clk。

于 2014-07-08T19:11:05.017 回答
0

除了闩锁问题之外,您的设计还有几个错误的代码站点。我会给它们编号以便更好地参考。

(1) Xilinx XST 不会将实现的状态机识别为 FSM。查看您的 XST 报告或 *.syr 文件。不会有 FSM 部分。如果 XST 没有找到 FSM,它就无法选择“最佳”状态编码并优化您的 FSM。

您应该使用枚举作为状态类型并初始化您的状态信号:

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

您的 FSM 流程还需要默认分配(请参阅 Russell 的解释),例如

nextstate <= state;

(2) 异步复位不是好的设计实践,而且会使时序收敛计算复杂化。

(3) 您正在处理来自 FPGA 外部的原始输入信号,而没有任何时序信息。为了防止元稳定性问题,在 rx_data 路径上放置两个 D 触发器。您还应该将 async-reg 和 no-srl-extract 属性添加到这 2 个寄存器,以防止 XST 优化

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);

让“In”成为您的异步输入。现在您可以在时钟域中使用 I_sync。我选择单行描述这两个寄存器,您也可以使用经典流程:) 后缀“_async”允许您在 *.xcf 和 *.ucf 文件中定义匹配的时序规则。

于 2014-07-09T07:11:13.497 回答