Фон
Это личный проект; это касается подключения FPGA к N64, байтовые значения, которые получает FPGA, затем отправляются через UART на мой компьютер. Это на самом деле работает довольно хорошо! К сожалению, в случайное время устройство выйдет из строя, а затем восстановится. Благодаря отладке мне удалось найти проблему, однако я нахожусь в тупике, как ее исправить, потому что я довольно некомпетентен с VHDL.
Я играю с VHDL уже пару дней и, возможно, я не в состоянии решить эту проблему.
Проблема
У меня есть осциллограф, который измеряет сигнал N64 в ПЛИС, а другой канал подключается к выходу ПЛИС. У меня также есть цифровые контакты, записывающие значение счетчика.
По сути, N64 отправляет 9 бит данных, включая бит STOP. Счетчик считает полученные биты данных, и когда я достигну 9 бит, FPGA начнет передачу через UART.
Вот правильное поведение:
ПЛИС - это синяя форма волны, а оранжевая форма - это вход N64. На время приема моя ПЛИС "эхом" передает сигнал ввода для целей отладки. После того, как 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;
Итак, есть идеи? Советы по отладке? Советы по кодированию конечных автоматов?
В то же время, я буду продолжать играть с этим (у меня это будет в конечном итоге)! Помоги мне Stack Exchange, ты моя единственная надежда!
редактировать
Еще одно открытие в моей отладке - состояния переместятся с waitForStart обратно на waitForStop. Каждому состоянию я присвоил значение с waitForStart, равным «5», и waitForStop, равным «4». Смотрите изображение ниже:
Ответы:
Я не вижу синхронизатора в строке данных rx.
Все асинхронные входы должны быть синхронизированы с тактовой частотой дискретизации. Для этого есть несколько причин: метастабильность и маршрутизация. Это разные проблемы, но они взаимосвязаны.
Для распространения сигналов через матрицу ПЛИС требуется время. Сеть часов внутри ПЛИС предназначена для компенсации этих задержек "путешествия", так что все триггеры в ПЛИС видят часы в один и тот же момент. Обычная сеть маршрутизации не имеет этого, и вместо этого полагается на правило, согласно которому все сигналы должны быть стабильными в течение небольшого времени до изменения часов и оставаться стабильными в течение небольшого времени после изменения часов. Эти маленькие кусочки времени известны как время установки и удержания для данного триггера. Компонент place и route в цепочке инструментов очень хорошо понимает задержки маршрутизации для конкретного устройства и делает базовое предположение, что сигнал не нарушает настройки и время удержания триггеров в FPGA.
Если у вас есть сигналы, которые не синхронизированы с тактовыми импульсами, вы можете оказаться в ситуации, когда один триггер видит «старое» значение сигнала, поскольку новое значение не успело распространиться. Теперь вы находитесь в нежелательной ситуации, когда логика, смотрящая на один и тот же сигнал, видит два разных значения. Это может привести к неправильной работе, сбоям конечных автоматов и всевозможным ошибкам в диагностике.
Другая причина, по которой вы должны синхронизировать все ваши входные сигналы, - это метастабильность. Существуют тома, написанные на эту тему, но в двух словах, цифровая логическая схема на самом базовом уровне является аналоговой схемой. Когда ваша линия тактовых импульсов поднимается, состояние входной линии фиксируется, и если этот вход не является стабильным высоким или низким уровнем в это время, неизвестный «промежуточный» показатель может быть зафиксирован триггером сэмплирования.
Как вы знаете, FPGA являются цифровыми зверями и плохо реагируют на сигнал, который не является ни высоким, ни низким. Хуже того, если это неопределенное значение пробивается через триггер сэмплирования и попадает в ПЛИС, это может вызвать все виды странностей, поскольку большие части логики теперь видят неопределенное значение и пытаются его понять.
Решение заключается в синхронизации сигнала. На самом базовом уровне это означает, что вы используете цепочку триггеров для захвата ввода. Любой метастабильный уровень, который мог быть захвачен первым триггером и каким-то образом его удалось разобрать, получает еще один шанс для разрешения, прежде чем он достигнет вашей сложной логики. Два триггера обычно более чем достаточно для синхронизации входов.
Базовый синхронизатор выглядит так:
Подключите физический вывод для строки данных rx контроллера N64 ко входу async_in синхронизатора и подключите сигнал sync_out к входу rxd вашего UART.
Несинхронизированные сигналы могут вызвать странные проблемы. Убедитесь, что любой вход, подключенный к элементу FPGA, который не синхронизирован с часами процесса, считывающего сигнал, синхронизирован. Это включает в себя кнопки, сигналы UART «rx» и «cts» ... все, что не синхронизировано с часами, которые FPGA использует для выборки сигнала.
(За исключением: я написал страницу по адресу www.mixdown.ca/n64dev много лет назад. Я только что понял, что сломал ссылку, когда последний раз обновлял сайт, и исправлю ее утром, когда вернусь за компьютер. Я понятия не имел, так много людей использовали эту страницу!)
источник
Ваша проблема в том, что вы используете несинхронизированные сигналы для принятия решений в вашем автомате. Вы должны подавать все эти внешние сигналы через синхронизаторы с двойным FF, прежде чем использовать их в автомате.
Это тонкая проблема с конечными автоматами, которая может возникнуть при любом переходе состояния, который включает в себя изменение двух или более битов в переменной состояния. Если вы используете несинхронизированный вход, один из битов может измениться, а другой не изменится. Это приведет вас к другому состоянию, чем предполагалось, и это может быть или не быть законным государством.
Это последнее утверждение является причиной того, что у вас также всегда должен быть регистр по умолчанию (в VHDL,
when others => ...
) в вашем выражении состояния машины состояний, которое переводит вас из любого недопустимого состояния в легальное.источник
when others =>
это помогает, но оказывается, что оно не даст вам того, на что вы претендуете (под любым синтезатором, который я использовал), если вы не добавите атрибуты, чтобы гарантировать, что синтезатор понимает, что вы хотите «безопасный» конечный автомат. Нормальным поведением является оптимизация до «горячего» представления, а не обеспечение логики восстановления. См xilinx.com/support/answers/40093.html и synopsys.com/Company/Publications/SynopsysInsight/Pages/... , например.