Самореализованный UART в VHDL всегда пропускает второй символ

Я сейчас изучаю VHDL и пытался реализовать UART (1 стартовый бит, 8 бит данных, 1 стоповый бит) для периодической отправки жестко закодированной строки.

Все работает как положено - я получаю строку каждую 1 секунду. Однако второго персонажа нет.

Неважно, какой длины строка, какой это символ. Я проверил этот факт на осциллографе, и для этого конкретного символа нет формы сигнала. 1 стартовый бит, 8 бит для первого символа, стоповый бит, стартовый бит и 8 бит для третьего символа, а не для второго.

Следующий код предназначен для тактовой частоты 10 МГц, разделенной для отправки с ~ 38 400 бит в секунду, я также пробовал с 9600 бит в секунду, у обоих та же проблема.

Я использую плату разработки Altera MAX10: http://maximator-fpga.org/

Короткое видео о том, как это работает: https://gfycat.com/JoyousIllliterateGuillemot

UART.vhd:

LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    use ieee.numeric_std.ALL;
    use ieee.std_logic_arith.all;

entity UART is
    port (
        clk_10mhz: in STD_LOGIC;
        txPin: out STD_LOGIC
    );
end entity;

architecture Test of UART is
    signal txStart: STD_LOGIC;
    signal txIdle: STD_LOGIC;
    signal txData: STD_LOGIC_VECTOR(7 downto 0);

    component TX is
        port (
            clk_in: in STD_LOGIC;
            start: in STD_LOGIC;
            data: in STD_LOGIC_VECTOR(7 downto 0);
            tx: out STD_LOGIC;
            txIdle: out STD_LOGIC
        );
    end component TX;


begin
    process (clk_10mhz, txIdle) 
        variable clkDividerCounter : integer range 0 to 10000000;
        variable textToSend : string(1 to 31) := "Hello darkness my old friend!" & CR & LF; 
        variable currentCharacterIndex : integer range 0 to 31;
    begin       
        if (rising_edge(clk_10mhz)) then
            if (clkDividerCounter < 10000000) then
                clkDividerCounter := clkDividerCounter + 1;
            else
                clkDividerCounter := 0;
                currentCharacterIndex := 1;
            end if;


            if (txIdle = '1' and currentCharacterIndex > 0) then
                txData <= CONV_STD_LOGIC_VECTOR(character'pos(textToSend(currentCharacterIndex)),8);
                txStart <= '1';

                if (currentCharacterIndex < 31) then
                    currentCharacterIndex := currentCharacterIndex + 1;
                else
                    currentCharacterIndex := 0;
                    txStart <= '0';
                end if;
            end if;
        end if;
    end process;


    u1: TX port map (clk_10mhz, txStart, txData, txPin, txIdle);
end Test;

TX.vhd:

LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    use ieee.numeric_std.ALL;

entity TX is
    port (
        clk_in: in STD_LOGIC;
        start: in STD_LOGIC;
        data: in STD_LOGIC_VECTOR(7 downto 0);

        tx: out STD_LOGIC;
        txIdle: out STD_LOGIC
    );
end entity;

architecture Test of TX is
    signal idle: STD_LOGIC;
begin
    process (clk_in) 
        variable bitIndex : integer range 0 to 9;
        variable clkDividerCounter : integer range 0 to 260;

        variable dataFrame : STD_LOGIC_VECTOR(9 downto 0);
        variable dataFrameCurrentIndex : integer range 0 to 9;
    begin
        if (rising_edge(clk_in)) then
            if (start = '1' and idle = '1') then
                dataFrame(0) := '0';
                dataFrame(8 downto 1) := data;
                dataFrame(9) := '1';

                dataFrameCurrentIndex := 0;

                idle <= '0';
            end if;

            if (idle = '0') then                
                if (clkDividerCounter < 260) then
                    clkDividerCounter := clkDividerCounter + 1;
                else
                    if (dataFrameCurrentIndex <= 9) then
                        tx <= dataFrame(dataFrameCurrentIndex);
                        dataFrameCurrentIndex := dataFrameCurrentIndex + 1;
                    else
                        idle <= '1';
                    end if;

                    clkDividerCounter := 0;
                end if;
            end if; 

            txIdle <= idle; 
        end if;
    end process;
end Test;

person Adrian Adamczyk    schedule 21.05.2016    source источник
comment
А ваш стенд для моделирования?   -  person user_1818839    schedule 21.05.2016
comment
Я пробовал с ModelSim, но tx_pin всегда не инициализируется, даже после миллионов тактовых циклов (я также изменил задержку 1 с на доли секунды). Я искал решение этой другой проблемы. Что именно вы имели в виду под испытательным стендом моделирования?   -  person Adrian Adamczyk    schedule 21.05.2016
comment
Создайте свой UART в испытательном стенде (объект VHDL без портов), который подает свои входные сигналы (clk и т. д.) и контролирует свои выходы (в идеале, сообщая об ошибках, но это может подождать). Выложите этот файл сюда. Моделирование без тестового стенда усложняет задачу. Отладка напрямую в аппаратном обеспечении — это действительно трудный путь. О, и потеряйте библиотеку std_logic_arith.   -  person user_1818839    schedule 21.05.2016


Ответы (1)


Переместить линию

txIdle <= idle;

из TX.vhd вне процесса. Сигналы принимают свое новое значение после завершения процесса.

Например:

idle <= '0';
txIdle <= idle;

Устанавливает txIdle в '1', если idle было '1', когда два оператора были выполнены внутри процесса. Вы должны заметить, что это означает, что txIdle будет '1' в течение двух последовательных циклов и приведет к двойному увеличению currentCharacterIndex в начале.

Обратите внимание, что, в отличие от сигналов, переменная принимает свое новое значение, когда встречается оператор присваивания, а не в конце процесса, как это делают сигналы.

Хотя ваш код не так уж страшен для новичка, я рекомендую использовать только сигнал, когда вы начинаете изучать VHDL. Гораздо проще ошибиться с переменными или описать неоптимальную или сломанную реализацию.

Кроме того, как упомянул Брайан, не используйте std_logic_arith, особенно при использовании numeric_std. Они конфликтуют друг с другом (хотя некоторые инструменты с этим справляются), и std_logic_arith не является стандартом IEEE, а numeric_std — таковым.

Наконец, симуляция является важной частью проектирования аппаратного обеспечения. Чтобы избежать неинициализированного вывода, добавьте сброс в вашу схему, что обычно является хорошей идеей.

person Jonathan Drolet    schedule 22.05.2016
comment
Я сделал много изменений в коде после того, как нашел ModelSim очень полезным. Это указало мне на несколько ошибок, и я обнаружил эту проблему с двухтактным сигналом простоя, а также с неинициализированными значениями. Однако я не смог найти объяснения, которое вы знали. После некоторых изменений в коде, пытающихся избежать двухтактного сигнала простоя, форма сигнала выглядит следующим образом: i.imgur. com/c0J1mEG.png Так что все должно быть в порядке, к сожалению, на оборудовании теперь есть проблема с двойным H в начале строки. Я потерплю это какое-то время и замечу тебя. - person Adrian Adamczyk; 22.05.2016
comment
У меня кончились идеи, форма волны выглядит вот так, и вот HHello тьма, мой старый друг! полученная строка - двойная первая буква: i.imgur.com/7ysiatV.png UART.vhd: pastebin.com/bmkV2brB TX.vhd: pastebin.com/v4cAs1wc - person Adrian Adamczyk; 23.05.2016
comment
Я запустил SignalTap, и он подтвердил проблему: imgur.com/eb0SYC0 (ошибка в слове, должно быть перевернутое не перевернутое) - person Adrian Adamczyk; 24.05.2016
comment
У вас такая же проблема в симуляции. Первый символ отправляется дважды, а последний никогда не отправляется. Проблема в том, что модуль tx считывает ввод, когда idle равен 1, а модуль uart обновляет данные для отправки цикла после того, как он был прочитан tx. Я бы рекомендовал переключать start между отправленными символами, чтобы start и данные обновлялись в цикле после утверждения idle. - person Jonathan Drolet; 24.05.2016
comment
Все верно, я сделал, как вы сказали, и это сработало, спасибо. - person Adrian Adamczyk; 25.05.2016