UART 发送器仅在嵌入式逻辑分析器为 运行 时起作用

UART Transmitter only functions when embedded logic analyzer is running

我一直在尝试实现 UART,以便在我的 Lattice MachXO3D 板和我的计算机之间进行通信。目前我正在尝试从FPGA实现传输。

在测试硬件时,我遇到了一个很奇怪的问题。如果运行正常,它会运行几秒钟然后突然停止运行(连接到我的电脑的CH340会报告它正在接收包含0x0的消息)。但是,如果我通过 Lattice Diamond 软件将逻辑分析仪嵌入到 FPGA 中,并且我 运行 分析仪,它将在较长时间内完美运行。

遗憾的是,我没有逻辑分析仪,所以嵌入式逻辑分析仪是我了解实际传输内容的唯一机会。

这些是与我的实施相关的文件:

baud_gen

LIBRARY IEEE; 
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
-- Generates 16 ticks per bit
ENTITY baud_gen IS
  GENERIC(divider: INTEGER := 13 -- 24M/115200*16
          );
  PORT(
    clk, reset: IN STD_LOGIC;
    s_tick: OUT STD_LOGIC
    );
END baud_gen;

ARCHITECTURE working OF baud_gen IS
  
BEGIN
  PROCESS(clk)
    VARIABLE counter: UNSIGNED(3 DOWNTO 0) := to_unsigned(0,4);
  BEGIN
    IF clk'EVENT AND clk='1' THEN
      IF reset='1' THEN
        s_tick <= '0';
        counter := to_unsigned(0,4);
      ELSIF counter=to_unsigned(divider-1,4) then
        s_tick <= '1';
        counter:= to_unsigned(0,4);
      ELSE
        s_tick <= '0';
        counter := counter + 1;
      END IF;
    END IF;
  END PROCESS;
END working;

rs232_tx

LIBRARY IEEE; 
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

ENTITY rs232_tx IS
  PORT(clk: IN std_logic;
       tx: OUT std_logic;
       rst: IN std_logic;
       fifo_empty: IN std_logic;
       fifo_RdEn, fifo_RdClock: OUT std_logic;
       fifo_data: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
       Q: OUT STD_LOGIC_VECTOR(1 DOWNTO 0));
END rs232_tx;

ARCHITECTURE working OF rs232_tx IS
  TYPE state IS (idle,start,data);
  SIGNAL tx_pulse: STD_LOGIC := '1';
  SIGNAL s_tick: STD_LOGIC;
  SIGNAL pr_state, nx_state: state := idle;
  SIGNAL data_val: std_logic_vector(7 DOWNTO 0):=(others=>'0');
  SIGNAL data_count: unsigned(2 DOWNTO 0):=to_unsigned(0,3);
BEGIN
  process(s_tick, rst)
    VARIABLE count: unsigned(3 DOWNTO 0):= to_unsigned(0,4);
  BEGIN
    IF rising_edge(s_tick) THEN
      count := count + to_unsigned(1,4);

      IF count = to_unsigned(15,4) THEN
        tx_pulse <= '1';
      ELSE
        tx_pulse <='0';
      END IF; 
    END IF;
  END PROCESS;

  process(tx_pulse,rst)
  BEGIN
    IF rising_edge(tx_pulse) THEN
      IF rst='1' THEN
        pr_state <= idle;
        data_val <= (others=>'0');
        data_count <= to_unsigned(0,3);
      ELSE
        pr_state <= nx_state;
        CASE pr_state IS
          WHEN idle =>
            data_count <= to_unsigned(0,3);
          WHEN data =>
            data_count <= data_count + to_unsigned(1,3);
          WHEN start =>
            data_val <= fifo_data;
          WHEN OTHERS =>
        END case;
        
      END IF;
    END IF;
  END process;

  process(fifo_empty,rst,data_count,pr_state,data_count)
  BEGIN
    case pr_state is
      when idle =>
        Q <= ('1','1');
        fifo_RdEn <= '0';
        tx <= '1';
        IF fifo_empty='0' AND rst='0' THEN          
          nx_state <= start;
        ELSE          
          nx_state <= idle;
        END IF;
      WHEN start =>
        Q <= ('1','0');
        fifo_RdEn <= '1';
        tx <= '0';
        nx_state <= data;
      WHEN data =>
        Q <= ('0','1');
        fifo_RdEn <= '0';
        tx <= data_val(to_integer(data_count));
        if data_count=to_unsigned(7,3) then
          nx_state <= idle;
        ELSE
          nx_state <= data;
        end if;

    end case;
  END process;
  
  fifo_RdClock <= tx_pulse;
  baud_gen: ENTITY work.baud_gen
    PORT MAP(clk,reset=>'0',s_tick=>s_tick);
END working;

测试平台

LIBRARY IEEE; 
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

ENTITY rs232_tx_test IS
  GENERIC(clk_period: TIME := 41666666.7 fs;
          baud_period: TIME := 8680.55556 ns);
END rs232_tx_test;

ARCHITECTURE working OF rs232_tx_test IS
  SIGNAL clk: STD_LOGIC := '0';
  SIGNAL tx, rst, fifo_empty, fifo_RdEn, fifo_RdClock: STD_LOGIC;
  SIGNAL fifo_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
  clk <= NOT clk AFTER clk_period/2;
  rst <= '1', '0' AFTER 100 ns;
  PROCESS
  BEGIN
    fifo_empty <= '1';
    WAIT FOR baud_period;
    fifo_empty <= '0';
    WAIT FOR baud_period*16;
  END PROCESS;

  fifo_data <= ('1','1','0','0','1','0','1','1') WHEN fifo_RdEn='1' ELSE (others=>'0');
  dut: ENTITY work.rs232_tx
    PORT MAP(clk,tx,rst,fifo_empty,fifo_RdEn,fifo_RdClock,fifo_data);
END working;

编辑 我已经测试了我在网上找到的另一种 UART 设计 @ 9600 bps,但它以同样的方式失败了。它可以将一个常量字符(在本例中为 'a')发送到我计算机上的终端,然后它突然停止发送任何内容。但是,如果我开始收听我在 Lattice Diamond 中生成的软逻辑分析器,它可以正常工作并且不会失败。

这听起来像是一个经典的计时问题。

在评论中,其他人解释了代码中可能间接导致此问题的弱点。我将专注于正在发生的事情。

如您所述,在模拟中您的代码按预期工作。然而,这只是故事的一半。要创建 FPGA 比特流,构建工具会获取您的代码 和其他几个文件 ,进行综合,然后进行布局和布线。您的时间问题发生在 P&R 期间。这就是为什么您的模拟没有发现任何错误,因为我假设它是 RTL(pre-place 和路线)模拟。

在 P&R 期间,工具以最佳方式放置逻辑以适应设备的时序模型,因此所有路径都满足其时序要求。路径时序要求源自时序约束文件中的显式语句,从您的代码中推断(顺便说一句,这就是您的编码风格很重要的原因)。

一旦 P&R 完成,工具将通过静态时序分析器 (STA) 工具放置构建工件,并报告构建是否未能满足时序要求。

这引出了两个问题:

  1. 构建是否报时序错误?
  2. 您是否有时序约束文件 - 如果您不确定,答案是否定的。

调试问题的方法是使用 Lattice Diamond 生成的时序报告来查看故障所在。 如果没有报告失败,则意味着您的时序模型由于缺乏适当的时序约束而错误。至少,您需要约束设计中的所有 IO 并描述设计中的所有时钟。

这里有一份很好的文档可以帮助您解决问题: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/RZ/Timing_Closure_Document.ashx?document_id=45588

当您使用嵌入式逻辑分析器时,您的设计工作正常的原因是在设计中放置了额外的逻辑,这改变了时序模型。 P&R 工具以不同的方式布局设计,并且幸运地以满足实际时序要求的方式放置设计。

正如我经常对我的软件工程师同事说的那样,软件语言创建了一组指令,HDL 创建了一组建议。