Передатчик UART работает только при запущенном встроенном логическом анализаторе

#vhdl #fpga #lattice-diamond

#vhdl #fpga #решетка-ромб

Вопрос:

Я пытался реализовать UART для связи между моей платой Lattice MachXO3D и моим компьютером. На данный момент я пытаюсь реализовать передачу с FPGA.

После тестирования на оборудовании я столкнулся с очень странной проблемой. При нормальной работе он будет работать несколько секунд, а затем внезапно перестанет функционировать (CH340, подключенный к моему компьютеру, сообщит, что получает сообщения, содержащие 0x0). Однако, если я встрою логический анализатор в FPGA с помощью программного обеспечения Lattice Diamond и запущу анализатор, он будет отлично функционировать в течение длительного периода времени.

К сожалению, у меня нет логического анализатора, поэтому встроенный логический анализатор — мой единственный шанс узнать, что на самом деле передается.

Это файлы, связанные с моей реализацией:

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;
  

testbench

 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 бит / с, и он выходит из строя таким же образом. Он может отправлять постоянный символ, в данном случае ‘a’, на терминал на моем компьютере, а затем внезапно перестает что-либо отправлять. Однако, если я начну прослушивать программный логический анализатор, который я создал в Lattice Diamond, он работает без проблем и не дает сбоев.

Комментарии:

1. Вы это имитировали?

2. @MatthewTaylor Конечно, при моделировании он работает отлично. На аппаратном обеспечении он выходит из строя примерно через 2 секунды. Я даже моделировал его в течение 5 секунд, что заняло довольно много времени с шагом 1fs, и даже тогда он работал хорошо. Проблема определенно возникает после синтеза, но не при моделировании

3. Извините — глупый вопрос — есть тестовый стенд. Как насчет STA и / или моделирования уровня вентиля?

4. Я отмечаю, что вы используете логически сгенерированные часы s_tick в качестве фактических часов. Это не рекомендуется в ПЛИС, поскольку логические тактовые сигналы могут вызывать проблемы с синхронизацией. Гораздо лучше использовать одни и те же часы для всей логики и вместо этого использовать s_tick, как включение часов.

5. @BrianDrummond Да, я это понимаю. Это потому, что я хочу, чтобы счетчик оставался на 0 в течение одного тика после изменения состояния на него. Должен ли я был спроектировать его по-другому?

Ответ №1:

Это похоже на классическую проблему синхронизации.

В комментариях другие объяснили, где в коде есть недостатки, которые могут косвенно привести к этому. Я собираюсь сосредоточиться на том, что происходит.

Как вы уже заявили, при моделировании ваш код работает так, как вы ожидаете. Однако это только половина истории. Для создания битового потока FPGA инструменты сборки берут ваш код и несколько других файлов, синтезируют и проводят Place amp; Route. Ваша проблема с синхронизацией возникает во время P amp; R. Вот почему ваша симуляция не обнаруживает никаких ошибок, поскольку я предполагаю, что это симуляция RTL (pre-place amp; route).

Во время P amp; R инструменты наилучшим образом настраивают логику в соответствии с моделью синхронизации устройства, чтобы все пути соответствовали их требованиям к синхронизации. Требования к синхронизации пути получены из явных инструкций в файле временных ограничений и выведены из вашего кода (вот почему ваш стиль кодирования имеет значение, кстати).

После завершения P amp; R инструменты передадут артефакт сборки через инструмент static timing analyzer (STA) и сообщат, не соответствует ли сборка требованиям синхронизации.

Это приводит к двум вопросам:

  1. Сообщает ли сборка об ошибке синхронизации?
  2. Есть ли у вас файл с временными ограничениями — если вы не уверены, ответ отрицательный.

Способ отладки вашей проблемы — использовать отчет о времени, созданный Lattice Diamond, чтобы увидеть, где находятся сбои. Если нет сообщений о сбоях, это означает, что ваша модель синхронизации неверна из-за отсутствия соответствующих временных ограничений. Как минимум, вам нужно будет ограничить все операции ввода-вывода в проекте и описать все тактовые частоты в вашем проекте.

Вот хороший документ, который вам поможет: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/RZ/Timing_Closure_Document.ashx?document_id=45588

Причина, по которой ваш проект работает при использовании встроенного анализатора логики, заключается в том, что в проект была добавлена дополнительная логика, которая изменяет модель синхронизации. Инструменты P amp; R по-разному разрабатывают дизайн и, по счастливой случайности, разместили дизайн таким образом, чтобы он соответствовал реальным требованиям к времени.

Как я часто говорю своим коллегам-программистам, языки программирования создают набор инструкций, HDL создает набор предложений.

Комментарии:

1. Мне удалось использовать FT2232 на плате вместо CH340, и теперь он работает. Единственное, что отличается, — это используемые I контакты, а также расстояние, которое должен пройти сигнал, поэтому я считаю, что это действительно была проблема с синхронизацией. Мне нужно будет найти способ моделирования синхронизации в Linux.