VHDL: bitleri sayarken alma modülü rastgele başarısız oluyor


9

Arka fon

Bu kişisel bir projedir; bir FPGA'nın bir N64'e bağlanmasıyla ilgili olarak, FPGA'nin aldığı bayt değerleri daha sonra UART üzerinden bilgisayarıma gönderilir. Aslında oldukça iyi çalışıyor! Ne yazık ki rastgele zamanlarda, cihaz başarısız olur, sonra iyileşir. Hata ayıklama yoluyla, sorunu bulmayı başardım, ancak VHDL ile oldukça beceriksiz olduğum için nasıl düzeltileceğimi bilemiyorum.

Birkaç gündür VHDL ile oynuyorum ve bunu çözemeyebilirim.

Sorun

FPGA'ya N64 sinyalini ölçen bir osiloskopum var ve diğer kanal FPGA'nın çıkışına bağlanıyor. Ayrıca sayaç değerini kaydeden dijital pinlerim var.

Esasen N64, bir DUR biti dahil 9 veri biti gönderir. Sayaç alınan veri bitlerini sayar ve 9 bite ulaştığımda FPGA UART üzerinden iletim yapmaya başlar.

İşte doğru davranış: resim açıklamasını buraya girin

FPGA mavi dalga formudur ve turuncu dalga formu N64'ün girdisidir. Alma süresi boyunca, FPGA benim hata ayıklama amacıyla giriş sinyalini "echos". FPGA 9'a kadar saydıktan sonra, verileri UART üzerinden aktarmaya başlar. N64 bittikten hemen sonra dijital pinlerin 9'a ve FPGA çıkışının DÜŞÜK olduğuna dikkat edin.

İşte bir başarısızlık örneği:

resim açıklamasını buraya girin

Sayacın 2. ve 7. bitleri atladığına dikkat edin! FPGA sona ulaştı, N64'ten bir sonraki başlangıç ​​bitini bekliyor ama hiçbir şey. Böylece FPGA zaman aşımına uğrar ve iyileşir.

Bu, N64 alma modülü için VHDL'dir. Şu sayacı içerir: 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;

Peki, herhangi bir fikir? Hata ayıklama ipuçları? Sonlu Durum Makinelerinin kodlanması hakkında ipuçları?

Bu arada, onunla oynamaya devam edeceğim (sonunda alacağım)! Stack Exchange'e yardım et, sen benim tek umudumsun!

Düzenle

Hata ayıklamamda bir başka keşifte, durumlar waitForStart'tan waitForStop'a geri dönecek. Her duruma '5' değerine waitForStart ve '4' değerine waitForStop ile bir değer verdim. Aşağıdaki resme bakın: resim açıklamasını buraya girin


1
İlk durum bloğunuzda "s_bitCount <= X" 0 ";" Bu X bir yazım hatası mı?
travisbartley

@ trav1s Hayır, "X" onaltılık anlamına gelir. Yani X "0" aslında ikili olarak "0000" dır.
Nick Williams

1
Bir linter aracılığıyla kodu çalışan birkaç hata var. N64RXD ve tdre sinyalleri, sıralı işlemin hat 36'nın duyarlılık listesinde kullanılmamalıdır.
travisbartley

1
@ trav1s İşaretçi için teşekkürler, bu parametreleri kaldırdım; haklısın, bunlar gerekli değil. Sorun hala maalesef var. Kapsamla, hangi durumda olduğumu saptamak için sinyaller ekledim. Bazı nedenlerden dolayı FPGA, "waitForStart" öğesinden "waitForStop" öğesine geri bir durum olmadan atlar! Bu yüzden saymıyor çünkü FPGA biraz saydığı duruma ulaşmıyor. Sorun "geri atlamak" gibi görünüyor.
Nick Williams

1
Ancak "waitForStart" -> "waitForStop" geçişi geçersiz. Bu sıçramayı tek bir döngüde yapmanın bir yolu yok. Aralarında çok kısa bir durum olmadığından emin olmak için çok yakından kontrol edin. Aksi takdirde, bir donanım / zamanlama hatası olmalıdır.
travisbartley

Yanıtlar:


9

Rx veri satırında bir eşleyici görmüyorum.

Tüm eşzamansız girişler, örnekleme saatiyle senkronize edilmelidir. Bunun birkaç nedeni vardır: metastabilite ve yönlendirme. Bunlar farklı problemlerdir ancak birbiriyle ilişkilidir.

Sinyallerin FPGA dokusu üzerinden yayılması zaman alır. FPGA içindeki saat ağı, bu "seyahat" gecikmelerini telafi etmek için tasarlanmıştır, böylece FPGA içindeki tüm flip floplar saati tam olarak aynı anda görür. Normal yönlendirme ağı buna sahip değildir ve bunun yerine, saat değişmeden önce tüm sinyallerin bir süre sabit kalması ve saat değiştikten sonra bir süre sabit kalması kuralına dayanır. Bu küçük zaman bitleri, belirli bir flip flop için kurulum ve tutma süreleri olarak bilinir. Takım zincirinin yer ve rota bileşeni, belirli bir cihaz için yönlendirme gecikmelerini çok iyi anlar ve bir sinyalin FPGA'daki flip flopların kurulum ve tutma sürelerini ihlal etmediğine dair temel bir varsayım yapar.

Örnekleme saati ile senkronize olmayan sinyalleriniz olduğunda, bir flip flopun bir sinyalin "eski" değerini gördüğü durumda, çünkü yeni değerin yayılması için zamanı olmamıştır. Şimdi, aynı sinyale bakan mantığın iki farklı değer gördüğü istenmeyen bir durumtasınız. Bu, yanlış çalışmaya, çökmüş durum makinelerine ve her türlü tahribatı zorlaştırmaya neden olabilir.

Tüm giriş sinyallerinizi senkronize etmenizin diğer nedeni, metastabilite adı verilen bir şeydir. Bu konuda yazılmış ciltler vardır, ancak kısaca dijital mantık devresi en temel seviyede bir analog devredir. Saat çizginiz yükseldiğinde, giriş çizgisinin durumu yakalanır ve bu giriş o sırada sabit bir yüksek veya düşük seviye değilse, bilinmeyen bir "arada" değeri örnekleme flip flopuyla yakalanabilir.

Bildiğiniz gibi, FPGA'ler dijital hayvanlardır ve ne yüksek ne de düşük bir sinyale iyi tepki vermezler. Daha da kötüsü, bu belirsiz değer örnekleme flip flopundan ve FPGA'ya doğru ilerlerse, mantığın daha büyük kısımları artık belirsiz bir değer görüp anlamlandırmaya çalıştığından her türlü tuhaflığa neden olabilir.

Çözüm, sinyali senkronize etmektir. En temel seviyesinde bu, girişi yakalamak için bir parmak arası terlik zinciri kullandığınız anlamına gelir. İlk flip flop tarafından yakalanan ve bunu başarabilen herhangi bir metastabil seviye, karmaşık mantığınıza çarpmadan önce çözülmesi için bir şans daha kazanır. İki flip flop genellikle girişleri senkronize etmek için fazlasıyla yeterlidir.

Temel bir eşleyici şöyle görünür:

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;

N64 denetleyicisinin rx veri hattı için fiziksel pimi senkronizörün async_in girişine bağlayın ve sync_out sinyalini UART'ın rxd girişine bağlayın.

Eşitlenmemiş sinyaller neden olabilir garip sorunlara . Bir FPGA elemanına bağlı, sinyali okuyan işlemin saati ile senkronize olmayan herhangi bir girişin senkronize olduğundan emin olun. Bu düğmeler, UART 'rx' ve 'cts' sinyallerini içerir ... FPGA'nın sinyali örneklemek için kullandığı saate senkronize olmayan herhangi bir şey.

(Bir yana: Sayfayı uzun yıllar önce www.mixdown.ca/n64dev adresine yazdım. Siteyi en son güncellediğimde bağlantıyı kopardığımı fark ettim ve sabah bilgisayara döndüğümde düzeltirim. Pek çok insanın bu sayfayı kullandığı hakkında hiçbir fikrim yoktu!)


Harika ve kapsamlı cevap için teşekkürler! Bunu deneyeceğim ve makinemi daha sağlam hale getireceğim.
Nick Williams

2
Aslında metastabilite ile çok az ilgisi vardır (bu bir endişe olsa da) ve asenkron girişten durum değişkeninin bitlerini tutan çeşitli FF'lere kadar farklı yol gecikmeleriyle ilgili her şey.
Dave Tweed

Haklısın, @DaveTweed; İkisini bir araya getirme eğilimindeyim ve bu yanlış kafalı bir düşünce.
akohlsmith

@ DaveTweed'in yorumlarını dikkate almak için cevabımı düzenledim.
akohlsmith

1
@akohlsmith İnanılmaz! Eşitleyiciyi ekledim ve çözüm buydu. Ayrıca, mixdown sayfasını yazmanız inanılmaz bir tesadüf; N64 protokolünde bu makaleye atıfta bulunan bir sürü kaynak buldum ve bağlantı koptu hayal kırıklığına uğradım. Düzelttiğiniz için teşekkürler.
Nick Williams

6

Sorununuz, durum makinenizde karar vermek için senkronize edilmemiş sinyaller kullanmanızdır. Durum makinesinde kullanmadan önce tüm bu harici sinyalleri çift FF senkronizatörlerinden beslemelisiniz.

Durum değişkeninde iki veya daha fazla bitte değişiklik içeren herhangi bir durum geçişinde ortaya çıkabilen durum makineleri ile ilgili ince bir sorundur. Senkronize olmayan bir giriş kullanırsanız, bitlerden biri değişirken diğeri değişebilir. Bu sizi amaçlanandan farklı bir duruma götürür ve yasal bir durum olabilir veya olmayabilir.

Bu son ifade, when others => ...durum makine durum bildiriminizde sizi her zaman yasadışı bir durumdan yasal bir duruma götüren varsayılan bir vakanın (VHDL'de ) olması gerektiğidir.


Evet, bu izole etmek üzere
olduğum bir sonuçtu

1
Kahretsin beni yendin. Tüm bunları bir tablete yazdığım için suçluyorum. :-)
akohlsmith

@akohlsmith, doğudaki en hızlı silah olmak cevap vermede önemli olan tek şey değil. Cevabınız faydalıdır ve bundan kısa bir süre sonra yayınladığınız için hile yapmadığı açıktır.
travisbartley

Eskiden bunun when others =>yardımcı olduğunu düşünürdüm , ancak synth'in "güvenli" bir durum makinesi istediğinizi anladığından emin olmak için öznitelikler eklemediğiniz sürece , iddia ettiğiniz şeyi (kullandığım herhangi bir synthesizer altında) almazsınız . Normal davranış, tek bir temsili gösterime göre optimize etmek ve kurtarma mantığı vermemektir. Örneğin, xilinx.com/support/answers/40093.html ve synopsys.com/Company/Publications/SynopsysInsight/Pages/… adresine bakın .
Martin Thompson

Vaov! Bu harika bir ipucu ve bir cazibe gibi çalıştı.
Nick Williams
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.