VHDL: menerima modul gagal secara acak saat menghitung bit

9

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: masukkan deskripsi gambar di sini

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:

masukkan deskripsi gambar di sini

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: masukkan deskripsi gambar di sini

Nick Williams
sumber
1
Di blok kasus pertama Anda, ada baris "s_bitCount <= X" 0 ";" Apakah itu salah ketik?
travisbartley
@ trav1s Tidak, "X" itu menunjukkan heksadesimal. Jadi X "0" sebenarnya "0000" dalam biner.
Nick Williams
1
Saya mendapat beberapa kesalahan menjalankan kode melalui linter. Sinyal N64RXD dan tdre tidak boleh digunakan dalam daftar sensitivitas dari proses sekuensial, baris 36.
travisbartley
1
@ trav1s Terima kasih atas penunjuknya, saya menghapus parameter tersebut; Anda benar, itu tidak perlu. Sayangnya saya masih memiliki masalah. Dengan cakupan, saya menambahkan sinyal untuk mendeteksi keadaan saya. Untuk beberapa alasan, FPGA melompat dari "waitForStart" kembali ke "waitForStop" tanpa keadaan di antaranya! Inilah sebabnya mengapa itu tidak dihitung karena FPGA tidak mencapai keadaan di mana ia menghitung bit. "Lompatan mundur" tampaknya menjadi masalah.
Nick Williams
1
Tetapi transisi "waitForStart" -> "waitForStop" tidak valid. Tidak ada cara untuk melakukan lompatan itu dalam satu siklus. Periksa dengan cermat untuk memastikan tidak ada keadaan yang sangat singkat di antaranya. Kalau tidak, pasti ada kesalahan perangkat keras / waktu.
travisbartley

Jawaban:

9

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:

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;

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!)

akohlsmith
sumber
Terima kasih atas jawaban yang bagus dan komprehensif! Saya akan mencoba ini dan membuat mesin saya lebih kuat.
Nick Williams
2
Ini sebenarnya sangat sedikit hubungannya dengan metastabilitas (meskipun itu juga menjadi perhatian), dan semuanya berkaitan dengan keterlambatan jalur yang berbeda dari input asinkron ke berbagai FF yang menahan bit dari variabel state.
Dave Tweed
Anda benar, @DaveTweed; Saya cenderung menyatukan keduanya dan itu adalah pemikiran yang salah.
akohlsmith
Saya telah mengedit jawaban saya untuk memperhitungkan komentar @ DaveTweed.
akohlsmith
1
@akohlsmith Luar Biasa! Saya menambahkan sinkronisasi dan itu solusinya. Juga, itu kebetulan yang luar biasa bahwa Anda menulis halaman mixdown; Saya menemukan banyak sumber daya pada protokol N64 yang mereferensikan artikel itu dan saya kecewa tautannya rusak. Terima kasih telah memperbaikinya.
Nick Williams
6

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.

Dave Tweed
sumber
Ya, ini adalah kesimpulan yang akan saya isolasi, tetapi saya tidak ingin melompat ke sana sebelum mendapatkan informasi yang cukup ...
travisbartley
1
Sial, kamu mengalahkanku. Saya menyalahkan saya mengetik semua ini di tablet. :-)
akohlsmith
@ Akohlsmith, menjadi senjata tercepat di timur bukan satu-satunya hal yang penting dalam menjawab. Jawaban Anda bermanfaat, dan jelas tidak curang karena Anda memposting begitu cepat setelah ini.
travisbartley
Saya dulu berpikir 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.
Martin Thompson
Wow! Itu tip yang bagus dan itu bekerja seperti pesona.
Nick Williams