VHDL: модуль прийому випадковим чином виходить з ладу під час підрахунку бітів


9

Фон

Це особистий проект; що стосується підключення FPGA до N64, значення байтів, які отримує FPGA, потім надсилаються через UART на мій комп'ютер. Він насправді функціонує досить добре! На випадковий час, на жаль, пристрій вийде з ладу, після чого відновиться. За допомогою налагодження мені вдалося знайти проблему, проте я наткнувся на те, як її виправити, оскільки я досить некомпетентний із VHDL.

Я вже пару днів граю з ВГДЛ, і, можливо, я не можу це вирішити.

Проблема

У мене є осцилограф, що вимірює сигнал N64 в FPGA, а інший канал підключається до виходу FPGA. У мене також є цифрові штифти, які записують значення лічильника.

По суті, N64 посилає 9 бітів даних, включаючи біт STOP. Лічильник підраховує отримані біти даних, і коли я досягаю 9 біт, FPGA починає передавати через UART.

Ось правильна поведінка: введіть тут опис зображення

FPGA - синя форма хвилі, а помаранчева - це вхід N64. Під час прийому моя FPGA "відголошує" сигнал входу для цілей налагодження. Після того, як FPGA налічує до 9, вона починає передавати дані через UART. Зауважте, що цифрові штифти нараховують до 9, а вихід FPGA стає низьким відразу після завершення N64.

Ось приклад відмови:

введіть тут опис зображення

Зауважте, що лічильник пропускає біти 2 та 7! FPGA доходить до кінця, чекаючи наступного запуску біт від N64, але нічого. Таким чином, FPGA закінчується та відновлюється.

Це VHDL для модуля прийому N64. Він містить лічильник: s_bitCount.

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

entity N64RX is
     port(
         N64RXD : in STD_LOGIC;                    --Data input
         clk25 : in STD_LOGIC;
         clr : in STD_LOGIC; 
         tdre : in STD_LOGIC;                      --detects when UART is ready
         transmit : out STD_LOGIC;                 --Signal to UART to transmit  
         sel : out STD_LOGIC; 
         echoSig : out STD_LOGIC;
         bitcount : out STD_LOGIC_VECTOR(3 downto 0);
         data : out STD_LOGIC_VECTOR(3 downto 0)   --The significant nibble
         );
end N64RX;

--}} End of automatically maintained section

architecture N64RX of N64RX is 

type state_type is (start, delay2us, sigSample, waitForStop, waitForStart, timeout, count9bits, sendToUART);

signal state: state_type;
signal s_sel, s_echoSig, s_timeoutDetect : STD_LOGIC;
signal s_baudCount : STD_LOGIC_VECTOR(6 downto 0);  --Counting variable for baud rate in delay
signal s_bitCount : STD_LOGIC_VECTOR(3 downto 0);  --Counting variable for number of bits recieved 
signal s_data : STD_LOGIC_VECTOR(8 downto 0);   --Signal for data

constant delay : STD_LOGIC_VECTOR(6 downto 0) := "0110010";  --Provided 25MHz, 50 cycles is 2us 
constant delayLong : STD_LOGIC_VECTOR(6 downto 0) := "1100100";

begin 

n64RX: process(clk25, N64RXD, clr, tdre)
begin
    if clr = '1' then
        s_timeoutDetect <= '0';
        s_echoSig <= '1';
        s_sel <= '0';
        state <= start;
        s_data <= "000000000";
        transmit <= '0'; 
        s_bitCount <= "0000";
        s_baudCount <= "0000000";  
    elsif (clk25'event and clk25 = '1') then    --on rising edge of clock input
        case state is
            when start =>   
                --s_timeoutDetect <= '0';
                s_sel <= '0';
                transmit <= '0';        --Don't request UART to transfer   
                s_data <= "000000000";
                s_bitCount <= X"0";   
                if N64RXD = '1' then
                    state <= start;
                elsif N64RXD = '0' then     --if Start bit detected
                    state <= delay2us;
                end if;    

            when delay2us =>                 --wait two microseconds to sample
                --s_timeoutDetect <= '0';
                s_sel <= '1';
                s_echoSig <= '0';
                if s_baudCount >= delay then    
                    state <= sigSample;
                else
                    s_baudCount <= s_baudCount + 1;
                    state <= delay2us;
                end if;  

            when sigSample => 
                --s_timeoutDetect <= '1';
                s_echoSig <= N64RXD;
                s_bitCount <= s_bitCount + 1;
                s_baudcount <= "0000000";
                s_data <= s_data(7 downto 0) & N64RXD;      
                state <= waitForStop;   

            when waitForStop => 
                s_echoSig <= N64RXD;
                if N64RXD = '0' then
                    state <= waitForStop;
                elsif N64RXD = '1' then
                    state <= waitForStart;
                end if;   

            when waitForStart => 
                s_echoSig <= '1';
                s_baudCount <= s_baudCount + 1; 
                if N64RXD = '0' then 
                    s_baudCount <= "0000000";
                    state <= delay2us;
                elsif N64RXD = '1' then 
                    if s_baudCount >= delayLong then
                        state <= timeout;
                    elsif s_bitCount >= X"9" then
                        state <= count9bits;
                    else
                        state <= waitForStart;
                    end if;
                end if;     

            when count9bits =>  
                s_sel <= '0';
                if tdre = '0' then
                    state <= count9bits;
                elsif tdre = '1' then
                    state <= sendToUART;
                end if;   

            when sendToUART =>
                transmit <= '1';
                if tdre = '0' then
                    state <= start;
                else
                    state <= sendToUART;
                end if;

            when timeout =>
                --s_timeoutDetect <= '1';
                state <= start;

        end case;   
    end if;
end process n64RX;  
--timeoutDetect <= s_timeoutDetect;
bitcount <= s_bitCount;
echoSig <= s_echoSig;
sel <= s_sel;
data <= s_data(4 downto 1);

end N64RX;

Отже, якісь ідеї? Поради щодо налагодження? Поради щодо кодування машин кінцевого стану?

Тим часом я продовжую грати з цим (у мене це буде врешті-решт)! Допоможи мені обмінятися стеками, ти єдина моя надія!

Редагувати

Подальше відкриття в моїй налагодженні, держави перестрибнуть з waitForStart назад до waitForStop. Я дав кожному державі значення, у якому функція waitForStart дорівнює "5", а waitForStop дорівнює "4". Дивіться зображення нижче: введіть тут опис зображення


1
У вашому першому блоці випадків є рядок "s_bitCount <= X" 0 ";" Це X друкарська помилка?
travisbartley

@ trav1s Ні, що "X" позначає шістнадцятковий. Отже X "0" насправді "0000" у двійковій формі.
Нік Вільямс

1
У мене виникла кілька помилок під час запуску коду через лайнер. Сигнали N64RXD та tdre не повинні використовуватися у списку чутливості послідовного процесу, рядок 36.
travisbartley

1
@ trav1s Спасибі за вказівник, я видалив ці параметри; Ви маєте рацію, вони не потрібні. На жаль, у мене все ще є питання. Завдяки області застосування я додав сигнали, щоб визначити, у якому стані я перебуваю. Чомусь FPGA стрибає з "waitForStart" назад на "waitForStop" без стану між ними! Ось чому це не рахується, оскільки FPGA не доходить до стану, де він рахує біт. Проблема "стрибка назад" є проблемою.
Нік Вільямс

1
Але перехід "waitForStart" -> "waitForStop" недійсний. Немає можливості здійснити цей стрибок за один цикл. Перевірте дуже уважно, щоб переконатися, що між ними не дуже короткий стан. В іншому випадку повинна бути помилка апаратури / часу.
travisbartley

Відповіді:


9

Я не бачу синхронізатора в рядку даних rx.

Усі асинхронні входи повинні бути синхронізовані з тактовою частотою вибірки. Для цього є кілька причин: метастабільність та маршрутизація. Це різні проблеми, але взаємопов'язані.

Потрібен час, щоб сигнали поширювалися через тканину FPGA. Мережа годинника всередині FPGA призначена для компенсації цих затримок "подорожі", щоб усі фліп-флопи в межах FPGA бачили годинник в той самий момент. У звичайній мережі маршрутизації цього немає, і натомість покладається на правило, що всі сигнали повинні бути стабільними протягом певного часу, перш ніж зміни годинника і залишатися стабільними протягом невеликого часу після зміни годин. Ці невеликі шматочки часу відомі як час налаштування і час утримування для даного фліп-флопу. Компонент місця та маршруту ланцюжка інструментів дуже добре розуміє затримки маршрутизації для конкретного пристрою і робить основне припущення, що сигнал не порушує час установки та утримування тригерів у FPGA.

Якщо у вас є сигнали, які не синхронізовані з тактовою частотою вибірки, ви можете опинитися в ситуації, коли один перевернутий флоп бачить "старе" значення сигналу, оскільки нове значення не встигло поширитись. Тепер ви опинилися в небажаній ситуації, коли логіка, дивлячись на один і той же сигнал, бачить два різні значення. Це може призвести до неправильної роботи, аварійних машин і всіляких труднощів діагностувати.

Інша причина, чому потрібно синхронізувати всі вхідні сигнали, - це те, що називається метастабільністю. Існують томи, написані на цю тему, але в двох словах, цифрова логічна схема є на самому базовому рівні аналоговою схемою. Коли ваша тактова лінія піднімається, стан вхідної лінії вловлюється і якщо цей вхід не є стабільно високим або низьким рівнем на той момент, невідоме значення "проміжки" може бути зафіксовано фліп-дискюром вибірки.

Як відомо, FPGA - це цифрові звірі і не реагують добре на сигнал, який не є ані високим, ані низьким. Гірше, якщо це невизначене значення пробивається повз дискретизацію фліп-флопу і в FPGA, це може викликати всі види дивацтва, оскільки більші частини логіки тепер бачать невизначене значення і намагаються зрозуміти це.

Рішення полягає в синхронізації сигналу. На самому базовому рівні це означає, що ви використовуєте ланцюжок шльопанок для збору вводу. Будь-який метастабільний рівень, який, можливо, був захоплений першим фліп-флопом і встиг вийти з нього, отримує ще один шанс вирішитись, перш ніж він потрапить у вашу складну логіку. Двох фліп-флопів зазвичай більш ніж достатньо для синхронізації входів.

Основний синхронізатор виглядає так:

entity sync_2ff is
port (
    async_in : in std_logic;
    clk : in std_logic;
    rst : in std_logic;
    sync_out : out std_logic
);
end;

architecture a of sync_2ff is
begin

signal ff1, ff2: std_logic;

-- It's nice to let the synthesizer know what you're doing. Altera's way of doing it as follows:
ATTRIBUTE altera_attribute : string;
ATTRIBUTE altera_attribute OF ff1 : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS""";
ATTRIBUTE altera_attribute OF a : architecture is "-name SDC_STATEMENT ""set_false_path -to *|sync_2ff:*|ff1 """;

-- also set the 'preserve' attribute to ff1 and ff2 so the synthesis tool doesn't optimize them away
ATTRIBUTE preserve: boolean;
ATTRIBUTE preserve OF ff1: signal IS true;
ATTRIBUTE preserve OF ff2: signal IS true;

synchronizer: process(clk, rst)
begin
if rst = '1' then
    ff1 <= '0';
    ff2 <= '0';
else if rising_edge(clk) then
    ff1 <= async_in;
    ff2 <= ff1;
    sync_out <= ff2;
end if;
end process synchronizer;
end sync_2ff;

Підключіть фізичний штифт для rx лінії передачі даних контролера N64 до входу async_in синхронізатора і підключіть сигнал sync_out до входу rxd вашого UART.

Несинхронізовані сигнали можуть спричинити дивні проблеми. Переконайтесь, що будь-який вхід, підключений до елемента FPGA, який не синхронізований з годинником процесу зчитування сигналу, синхронізований. Сюди входять кнопкові кнопки, UART 'rx' та 'cts' сигнали ... все, що не синхронізується з тактовою частотою, яку FPGA використовує для вибірки сигналу.

(Вбік: я написав сторінку на www.mixdown.ca/n64dev багато років тому. Я щойно зрозумів, що перервав посилання, коли я востаннє оновив сайт, і виправлю його вранці, коли я знову за комп’ютером. Я не мав уявлення, що багато людей використовували цю сторінку!)


Дякуємо за чудову та вичерпну відповідь! Я спробую це спробувати зробити свою машину більш надійною.
Нік Вільямс

2
Це насправді має дуже мало спільного з метастабільністю (хоча це також викликає занепокоєння), і все, що стосується різного затримки шляху від асинхронного введення до різних ФФ, що містять біти змінної стану.
Дейв Твід

Ви маєте рацію, @DaveTweed; Я схильний збивати двох разом, і це неправильне мислення.
akohlsmith

Я відредагував свою відповідь, щоб взяти до уваги коментарі @ DaveTweed.
akohlsmith

1
@akohlsmith Дивовижно! Я додав синхронізатор, і це було рішення. Крім того, це неймовірний збіг випадків, коли ви написали сторінку змішання; Я знайшов купу ресурсів у протоколі N64, який посилався на цю статтю, і я був розчарований, що посилання було розірвано. Дякуємо, що виправили це.
Нік Вільямс

6

Ваша проблема полягає в тому, що ви використовуєте несинхронізовані сигнали для прийняття рішень у своєму стані. Вам слід подавати всі ці зовнішні сигнали через синхронізатори подвійного FF, перш ніж використовувати їх у державному апараті.

Це тонка проблема зі стаціонарними машинами, яка може виникнути при будь-якому переході стану, який передбачає зміну двох або більше біт змінної стану. Якщо ви використовуєте несинхронізований вхід, один з бітів може змінитися, а інший не вдасться змінити. Це переводить вас у інший стан від запланованого, а може бути, а може і не бути правовим.

Останнє твердження, тому ви також завжди повинні мати випадок за замовчуванням (у VHDL, when others => ...) у своїй заяві справи про державну машину, яка переводить вас із будь-якого незаконного стану в законний.


Так, це був висновок, який я збирався ізолювати, але я не хотів стрибати до нього, перш ніж отримати достатньо інформації ...
travisbartley

1
Чорт, ти побив мене до цього. Я звинувачую в тому, що я все це надрукував на планшеті. :-)
akohlsmith

@akohlsmith - найшвидша зброя на сході не єдине, що має значення у відповіді. Ваша відповідь корисна, і, очевидно, не була обманом, оскільки ви опублікували так скоро після цього.
travisbartley

Раніше я думав, що when others =>це допомагає, але виявляється, що ви не отримаєте те, про що ви заявляєте (під будь-яким синтезатором, яким я користувався), якщо ви не додасте атрибути, щоб синтез зрозумів, що ви хочете "безпечного" стану машини. Нормальна поведінка полягає в оптимізації до гарячого подання і не в наданні логіки відновлення. Див xilinx.com/support/answers/40093.html і synopsys.com/Company/Publications/SynopsysInsight/Pages / ... , наприклад.
Мартін Томпсон

Оце Так! Це чудова порада, і вона спрацювала як шарм.
Нік Вільямс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.