Latar Belakang
Ini adalah proyek pribadi; itu tentang menghubungkan FPGA ke N64, nilai byte yang diterima FPGA kemudian dikirim melalui UART ke komputer saya. Ini sebenarnya berfungsi cukup baik! Sayangnya, secara acak, perangkat akan gagal, lalu pulih. Melalui debugging, saya sudah berhasil menemukan masalah, namun saya bingung bagaimana cara memperbaikinya karena saya cukup tidak kompeten dengan VHDL.
Saya sudah bermain-main dengan VHDL selama beberapa hari sekarang dan saya mungkin tidak mampu menyelesaikan ini.
Masalah
Saya memiliki osiloskop yang mengukur sinyal N64 ke dalam FPGA, dan saluran lainnya terhubung ke output FPGA. Saya juga memiliki pin digital yang merekam nilai penghitung.
Pada dasarnya, N64 mengirimkan 9 bit data, termasuk bit STOP. Penghitung menghitung bit data yang diterima dan ketika saya mencapai 9 bit, FPGA mulai mentransmisikan melalui UART.
Inilah perilaku yang benar:
FPGA adalah bentuk gelombang biru dan bentuk gelombang oranye adalah input dari N64. Selama menerima, FPGA saya "menggema" sinyal input untuk keperluan debugging. Setelah jumlah FPGA menjadi 9, FPGA mulai mengirimkan data melalui UART. Perhatikan bahwa pin digital dihitung hingga 9 dan output FPGA menjadi RENDAH segera setelah N64 selesai.
Berikut ini contoh kegagalan:
Perhatikan bahwa penghitung melompati bit 2 dan 7! FPGA mencapai akhir, menunggu bit awal berikutnya dari N64 tetapi tidak ada. Jadi, FPGA mati dan pulih.
Ini adalah VHDL untuk modul penerimaan N64. Ini mengandung penghitung: 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;
Jadi, ada ide? Kiat debugging? Tips tentang pengkodean Mesin Hingga Negara?
Sementara itu, saya akan terus bermain-main dengannya (saya akan memilikinya pada akhirnya)! Bantu saya Stack Exchange, Anda satu-satunya harapan saya!
Edit
Penemuan lebih lanjut dalam debugging saya, negara akan melompat dari waitForStart kembali ke waitForStop. Saya memberi setiap negara nilai dengan waitForStart sama dengan '5' dan waitForStop sama dengan '4'. Lihat gambar di bawah ini:
Jawaban:
Saya tidak melihat sinkronisasi pada baris data rx.
Semua input asinkron harus disinkronkan ke jam pengambilan sampel. Ada beberapa alasan untuk ini: metastabilitas dan perutean. Ini adalah masalah yang berbeda tetapi saling terkait.
Butuh waktu bagi sinyal untuk menyebar melalui fabric FPGA. Jaringan jam di dalam FPGA dirancang untuk mengkompensasi keterlambatan "perjalanan" ini sehingga semua sandal jepit dalam FPGA melihat jam pada saat yang sama. Jaringan perutean normal tidak memiliki ini, dan sebaliknya bergantung pada aturan bahwa semua sinyal harus stabil untuk sedikit waktu sebelum jam berubah dan tetap stabil untuk sedikit waktu setelah jam berubah. Ini sedikit waktu dikenal sebagai pengaturan dan tahan waktu untuk flip flop yang diberikan. Komponen tempat dan rute dari toolchain memiliki pemahaman yang sangat baik tentang keterlambatan perutean untuk perangkat tertentu dan membuat asumsi dasar bahwa sinyal tidak melanggar pengaturan dan waktu penahanan flip flop di FPGA.
Ketika Anda memiliki sinyal yang tidak disinkronkan ke jam pengambilan sampel, Anda dapat berakhir dalam situasi di mana satu flip flop melihat nilai "lama" dari sinyal karena nilai baru belum punya waktu untuk merambat. Sekarang Anda berada dalam situasi yang tidak diinginkan di mana logika melihat sinyal yang sama melihat dua nilai berbeda. Ini dapat menyebabkan operasi yang salah, mesin negara macet dan semua jenis sulit untuk mendiagnosis kekacauan.
Alasan lain mengapa Anda harus menyinkronkan semua sinyal input Anda adalah sesuatu yang disebut metastabilitas. Ada banyak volume yang ditulis tentang hal ini tetapi singkatnya, sirkuit logika digital pada tingkat paling dasar adalah sirkuit analog. Ketika garis jam Anda naik, status garis input ditangkap dan jika input itu bukan level tinggi atau rendah yang stabil pada saat itu, nilai "di antara" yang tidak diketahui dapat ditangkap oleh flip flop pengambilan sampel.
Seperti yang Anda ketahui, FPGA adalah binatang digital dan tidak bereaksi dengan baik terhadap sinyal yang tidak tinggi atau rendah. Lebih buruk lagi, jika nilai tak tentu itu melewati sampul flip sampel dan masuk ke dalam FPGA, hal itu dapat menyebabkan semua jenis keanehan karena bagian-bagian yang lebih besar dari logika sekarang melihat nilai tak tentu dan mencoba memahaminya.
Solusinya adalah menyinkronkan sinyal. Pada tingkat paling dasar ini berarti Anda menggunakan rantai sandal jepit untuk menangkap input. Setiap tingkat metastable yang mungkin telah ditangkap oleh flip flop pertama dan berhasil keluar mendapat peluang lain untuk diselesaikan sebelum menyentuh logika kompleks Anda. Dua sandal jepit biasanya lebih dari cukup untuk menyinkronkan input.
Sinkronisasi dasar terlihat seperti ini:
Hubungkan pin fisik untuk baris data rx pengontrol N64 ke input async_in sinkronisasi, dan hubungkan sinyal sync_out ke input rxd UART Anda.
Sinyal yang tidak disinkronkan dapat menyebabkan masalah aneh . Pastikan setiap input yang terhubung ke elemen FPGA yang tidak disinkronkan ke jam proses membaca sinyal disinkronkan. Ini termasuk tombol tekan, sinyal 'rx' dan 'cts' UART ... apa pun yang tidak disinkronkan dengan jam yang digunakan FPGA untuk mengambil sampel sinyal.
(Di samping itu: saya menulis halaman di www.mixdown.ca/n64dev bertahun-tahun yang lalu. Saya baru menyadari bahwa saya memutus tautan ketika saya terakhir memperbarui situs dan akan memperbaikinya di pagi hari ketika saya kembali ke komputer. Saya tidak tahu banyak orang menggunakan halaman itu!)
sumber
Masalah Anda adalah bahwa Anda menggunakan sinyal yang tidak disinkronkan untuk membuat keputusan di mesin negara Anda. Anda harus memberi makan semua sinyal eksternal melalui sinkronisasi FF ganda sebelum menggunakannya di mesin negara.
Ini masalah halus dengan mesin negara yang dapat muncul dalam setiap transisi negara yang melibatkan perubahan ke dua atau lebih bit dalam variabel status. Jika Anda menggunakan input yang tidak disinkronkan, salah satu bit mungkin berubah sementara yang lain gagal berubah. Ini membawa Anda ke negara yang berbeda dari yang dimaksudkan, dan itu mungkin atau mungkin bukan negara hukum.
Pernyataan terakhir itulah mengapa Anda juga harus selalu memiliki kasing default (dalam VHDL,
when others => ...
) di pernyataan kasing mesin negara Anda yang membawa Anda dari keadaan ilegal ke yang legal.sumber
when others =>
itu membantu, tapi ternyata itu tidak membuat Anda mendapatkan apa yang Anda klaim (di bawah synthesizer apa pun yang saya gunakan) kecuali Anda menambahkan atribut untuk memastikan bahwa synth mengerti Anda menginginkan mesin keadaan "aman". Perilaku normal adalah mengoptimalkan ke representasi satu-panas dan tidak menyediakan logika pemulihan. Lihat xilinx.com/support/answers/40093.html dan synopsys.com/Company/Publications/SynopsysInsight/Pages/… misalnya.