Kysymys:
FPGA alkaa toimia merkityksettömien muutosten jälkeen, miksi?
Rehin
2019-10-12 16:39:08 UTC
view on stackexchange narkive permalink

Olen kirjoittanut UART-moduulin Verilogiin. Käyttämällä kyseistä moduulia saan tietoja tietokoneelta UART: n kautta ja lähetän sitten tiedot takaisin tämän UART-moduulin kautta. Latasin sen FPGA: hun testausta varten. Se toimii moitteettomasti riippumatta siitä, kuinka monta merkkiä lähetän alla olevalla koodilla (itse asiassa et ehkä tarvitse TX-tilaa, mutta laitan täyden koodin selvitykseen):

  -moduuli uart_transceiver

 # (parametri [15: 0] CLK_PERIOD = 434) // 50000000/115200

(
    syöttö sys_rst,
    syöte sys_clk,

    tulo uart_rx,
    lähtö reg uart_tx,

    // syötteen [15: 0] jakaja,

    lähtö reg [7: 0] rx_data,
    lähtö reg rx_err,
    tuotos [1: 0] state_uart,
    lähtö [7: 0] kellon melkein8,
    tuotos [7: 0] kello_hyvin8,

    syöte [7: 0] tx_data,
    syöttö tx_wr,
    lähtö reg rx_busy,
    lähtö reg rx_done,
    lähtö reg tx_busy
);

reg [1: 0] tila_tx, tila_rx;

// ------------------------------------------------ -----------------
// UART RX -logiikka
// ------------------------------------------------ -----------------
reg [3: 0] rx_bitcount;
reg [15: 0] clk_luku_rx = 16'h0001;
reg [7: 0] rx_reg;
reg rx1;

määritä state_uart = state_rx;
// määritä kellonlähde8 = clk_luku_rx [7: 0];
// määritä kellon_lähin8 = clk_luku_rx [15: 8];


parametri IDLERX = 2'd0,
                TAKESTART = 2 'd1,
                TAKE8BITS = 2'd2,
                TAKESTOP = 2'd3;

parametri [15: 0] FIRST_CHECK = CLK_PERIOD / 2;



aina @ (posedge sys_clk) alkaa

    jos (sys_rst) alkaa

        state_rx < = IDLERX;
        rx_err < = 1'b0;
        rx_done < = 1'b0;

    loppu alkaa

        tapaus (state_rx)

            IDLERX: aloita
                rx_bitcount < = 4'd0;
                rx_busy < = 1'b0;
                jos (uart_rx == 1'b0 && rx_err! = 1'b1) alkaa
                    clk_luku_rx < = 16'h0001;
                    state_rx < = TAKESTART;
                loppuun
            loppuun

            // Ota yksi käynnistyssignaali ja tarkista, onko se 0
TAKESTART: aloita
                rx_busy < = 1'b1;
                rx_done < = 1'b0;
                clk_luku_rx < = clk_luku_rx + 16'h0001;

                jos (clk_luku_rx == FIRST_CHECK)
                    alkaa
                    rx1 < = uart_rx;
// clk_count_rx < = clk_count_rx + 16'd1;
                    loppuun

                if (clk_luku_rx == CLK_PERIOD && rx1! = 1'b0)
                    alkaa
                    rx_err < = 1'b1;
                    state_rx < = IDLERX;
                    loppuun

                muuten jos (clk_count_rx == CLK_PERIOD && rx1 == 1'b0)
                    alkaa
                    clk_luku_rx < = 16'h0001;
                    state_rx < = TAKE8BITS;
                    loppuun

            loppuun


            // Ota 8 bittiä dataa, mutta ole varovainen ja odota 1 CLK_PERIOD kutakin bittiä kohti
            TAKE8BITS: aloita

                if (rx_bitcount < 4'd8)
                    alkaa
                    clk_luku_rx < = clk_luku_rx + 16'h0001;

                    jos (clk_luku_rx == FIRST_CHECK)
                        alkaa
                        rx1 < = uart_rx;
                        loppuun

                    else if (clk_luku_rx == CLK_PERIOD)
                        alkaa
                        clk_luku_rx < = 16'h0001;
                        rx_reg < = {rx1, rx_reg [7: 1]};
                        rx_bitcount < = rx_bitcount + 1'b1;
                        loppuun

                    loppuun

                muu
                    alkaa
                    clk_luku_rx < = 16'h0001;
                    state_rx < = TAKESTOP;
                    loppuun

            loppuun

            // Ota yksi pysäytysbitti viimeisessä CLK_PERIOD
TAKESTOP: aloita

                clk_luku_rx < = clk_luku_rx + 16'h0001;

                jos (clk_luku_rx == FIRST_CHECK)
                    alkaa
                    rx1 < = uart_rx;
                    loppuun

                muuten jos (clk_count_rx == CLK_PERIOD && rx1! = 1'b1)
                    alkaa
                    rx_err < = 1'b1;
                    state_rx < = IDLERX;
                    loppuun

                muuten jos (clk_count_rx == CLK_PERIOD && rx1 == 1'b1)
                    alkaa
                    rx_data < = rx_reg;
                    rx_done < = 1'b1;
                    clk_luku_rx < = 16'h0001;
                    state_rx < = IDLERX;
                    loppuun
                else if (clk_luku_rx > CLK_PERIOD)
                    alkaa
                    rx_err < = 1'b1;
                    state_rx < = IDLERX;
                    loppuun
            loppuun

        pääkotelo
    loppuun

loppuun



// ------------------------------------------------ -----------------
// UART TX -logiikka
// ------------------------------------------------ -----------------
parametri IDLE = 2'd0,
                SENDSTART = 2 d1,
                SEND8BITS = 2'd2,
                LÄHETYS = 2 d3;


reg [3: 0] tx_bitcount;
reg [15: 0] clk_määrä;
reg [7: 0] tx_reg;

aina @ (posedge sys_clk) alkaa

    jos (sys_rst) alkaa

        state_tx < = IDLE;

    loppu alkaa

        tapaus (state_tx)

            IDLE: aloita
                uart_tx < = 1'b1;
                // tx_reg < = tx_data;
                clk_luku < = 16'd0;
                tx_bitcount < = 4'd0;
                tx_busy < = 1'b0;
                jos (tx_wr) alkaa
                    tx_reg < = tx_data;
                    state_tx < = LÄHETÄ;
                loppuun
            loppuun
// Lähetä yksi aloitussignaali yhdelle CLK_PERIODille
            LÄHETÄ: aloita
                tx_busy < = 1'b1;
                uart_tx < = 1'b0;
                jos (clk_count == CLK_PERIOD) alkaa
                    clk_luku < = 16'd0;
                    // tx_reg < = tx_data;
                    state_tx < = LÄHETTÄ 8BITS;
                loppu alkaa
                    clk_luku < = clk_luku + 16'd1;
                loppuun

            loppuun


            // Lähetä 8 bittiä tietoja, mutta ole varovainen ja odota 1 CLK_PERIOD kutakin bittiä kohti
            SEND8BITS: aloita
                jos (tx_bitcount == 4'd0) alkaa
                    tx_bitcount < = tx_bitcount + 4'd1;
                    uart_tx < = tx_reg [0];
                    tx_reg < = {1'b0, tx_reg [7: 1]};
                loppuun
                jos (clk_count == CLK_PERIOD) alkaa
                    clk_luku < = 16'd0;
                    jos (tx_bitcount < 4'd8) alkaa
                        tx_bitcount < = tx_bitcount + 4'd1;
                        uart_tx < = tx_reg [0];
                        tx_reg < = {1'b0, tx_reg [7: 1]};
                    loppuun
                    muuten alkaa
                        clk_luku < = 16'd0;
                        state_tx < = LÄHETÄ;
                    loppuun
                loppuun
                muuten alkaa
                        clk_luku < = clk_luku + 16'd1;
                loppuun
            loppuun

            // Lähetä yksi pysäytysbitti yhdelle CLK_PERIODille
            LÄHETÄ: aloita
                uart_tx < = 1'b1;
                jos (clk_count == CLK_PERIOD) alkaa
                    clk_luku < = 16'd0;
                    tx_busy < = 1'b0;
                    state_tx < = IDLE;
                loppu alkaa
                    clk_luku < = clk_luku + 16'd1;
                loppuun
loppuun

        pääkotelo
    loppuun

loppuun

endmoduuli
 

Jos kuitenkin poistan viimeisen muutakin lohkoa alla olevasta TAKESTOP-tilasta, koodi lakkaa toimimasta, kun se on toiminut hyvin joidenkin satunnaismerkkien kohdalla, ja juuttuu TAKESTOP-tilaan. Kun sovellan sys_rst, se toimii jälleen joillekin hahmoille ja jumittuu sitten samassa TAKESTOP-tilassa. Mielenkiintoista on, että kun tämä muu lohko sijoitetaan ja koodi toimii moitteettomasti, se ei koskaan anna tätä muuta jos. Tiesin sen, koska muuten se olisi jumissa IDLERX-tilassa, koska rx_err olisi 1:

  else if (clk_count_rx > CLK_PERIOD)
        alkaa
        rx_err < = 1'b1;
        state_rx < = IDLERX;
        loppuun
 

Olen vihainen tällaisista ongelmista. Sen sijaan, että lisättäisit tämän muun if-lohkon, ongelma häviää myös, kun määritän clk_count_rx-lähdön poistamalla kommentin näistä kahdesta rivistä yllä olevasta täydestä koodikoodista:

  määritä clock_least8 = clk_count_rx [7: 0];
määritä kellon eniten8 = clk_luku_rx [15: 8];
 

Toisin sanoen tilakoneeni ei toimi hyvin ja vakaana, jos en määritä rekisteriä lähdölle, vaikka en käytä tätä lähtöä. Tätä on tapahtunut vielä pari kertaa erilaisissa Verilog-projekteissani.

Kysymys, kuinka tällaiset merkityksettömät muutokset voivat korjata koodini? Mikä minussa on vikana? Asia ei näytä loogiselta. Teenkö rakenteellisen käsitteellisen virheen ja tämä muutos näyttää korjaavan sen jotenkin? En tiedä. Käytän Quartus 16.1: tä Cyclone V GX Starter Boardin kanssa.

Muuten tämä on toimiva UART-moduuli, ja kuka tahansa on tervetullut käyttämään sitä. Olen varmasti hyvin iloinen siitä, kuka auttaa minua tässä asiassa.

Ehdotan, että aloitat synkronointilaitteen lisäämisen uart_rx-tuloosi.Siihen asti mikronesi ei ole vakaa ja käyttäytyy epätasaisesti.
Kiitos nopeasta vastauksesta.Itse asiassa täällä on vain yksi kello: sys_clk.Tarvitsenko edelleen synkronointilaitetta?
No, määritelmän mukaan se ei voi olla merkityksetön muutos - sanot sen olevan täysin merkityksellistä.Joten se tarkoittaa, ettet ymmärrä, mitä koneet tekevät jatkuvasti.Kuten @oldfart sanoo, lisää tulopiiri nopean melun hylkimiseksi ja suojaa metastabiilisuudelta.Tulosi on täysin asynkroninen kellotunnuksesi kanssa.
Kiitos TonyM.Näen.Tarkoitat, että uart_rx tulee toisen kellon lähdöstä.Joten jos syötän uart_rx: n kiikkuun ja käytän kyseisen kiikun lähtöä uart_rx: nä, ongelma on ratkaistava.Mutta sitten en ymmärrä, että ei ole mitään ongelmaa, jos käytän sitä muuta jos lohkoa, mikä ei ratkaise viittaamaasi ongelmaa.Testasin virtapiiriä noin 12 tuntia lähettämällä merkkejä jatkuvasti, mutta se ei koskaan epäonnistunut.
Kaikkien saman virran tietojenkäsittelylohkojen tulisi kelloa samalla kellolla, jotta vältetään tietojen putoaminen halkeamien väliin.
kahden flopin synkronoijan käyttö ei ratkaise ongelmaa ... se välttää metastabiilius, mutta sanotaan silti esim., lopullinen arvo saattaa silti olla 0, vaikka rx olisi '1' .... mikä johtaa bittivirheisiinsanalla vastaanotettu ... uart-vastaanotin käyttää yleensä 8x tai 16x ylinäytteistä kelloa ja keskittää rx: n arvon tämän välttämiseksi ...
üks vastaus:
Dave Tweed
2019-10-12 17:22:22 UTC
view on stackexchange narkive permalink

uart_rx -signaalisi on asynkroninen kellosi kanssa. Sinulla on kuitenkin yksi paikka koodissasi, jossa käytät sitä suoraan valtion koneessa. Tämä on utelias, koska kaikissa muissa tapauksissa olet varovainen, että määrität sen rx1 : lle ja testat sen sitten.

  IDLERX: aloita
      rx_bitcount < = 4'd0;
      rx_busy < = 1'b0;
      jos (uart_rx == 1'b0 && rx_err! = 1'b1) alkaa
        clk_luku_rx < = 16'h0001;
        state_rx < = TAKESTART;
      loppuun
    loppuun
 

Ongelmana on, että tämä if -lauseke muuttaa sekä valtion rekisteriä että laskurirekisteriä, yhteensä 18 FF: ää. Jokaisella FF: llä on logiikka, joka määrittää sen seuraavan tilan. Kun syötät tätä logiikkaa asynkronisella tulolla ja tulo muuttuu lähellä kellon reunaa, jotkut FF: t näkevät sen yhdessä tilassa, kun taas toiset näkevät sen eri tilassa. Kellorajan jälkeen ne ovat epäjohdonmukaisessa tilassa, jolla ei ole mitään tekemistä todellisen logiikkasi kanssa. 1

Kun muokkaat koodiasi, laitteistoresurssit ja sirun sisäiset reitityspolut määritetään eri tavalla, mikä tarkoittaa, että viiveet uart_rx : n ja näiden 18 FF: n välillä muuttuvat. Tämä muuttaa valtion koneen käyttäytymistä arvaamattomilla tavoilla.

Alarivi: KAIKKI tilakoneen tulot PITÄÄ synkronoida kelloon! Sinun tapauksessasi helpoin tapa tehdä se on laittaa tehtävä

  rx1 < = uart_rx;
 

case -lausekkeen ulkopuolella ja käytä rx1 -sisältöä yksinomaan sen sisällä.

BTW, et ole ensimmäinen, joka tekee tämän virheen: UART-vastaanotin ei toimi... etkä ole viimeinen.


1 Huomaa, että tällä ei ole mitään tekemistä metastabiilisuuden kanssa, joka on ongelma myös asynkronisissa tuloissa.Saat parhaat tulokset syöttämällä uart_rx vähintään kahteen FF: ään ennen sen käyttöä.

Se oli täydellinen vastaus ja ratkaisi ongelmani ja todennäköisesti useimmat edessämme olevat asiat.Ongelma, joka näyttää olevan ratkaistu satunnaisilla muutoksilla, saa hänet hulluksi.Mutta vastauksesi selittää tämän täysin.Kiitollinen siitä.
Tämä videoluento on hyödyllinen ymmärtääkseen asia täysin: https://www.youtube.com/watch?v=ZPpuDMk9BOU
@Rehin: Tämä video koskee metastabiilisuutta, josta, kuten sanoin, EI ole kyse.
Se selittää myös kellotunnusten synkronoinnin tai ylityksen alussa.Ja toisen ff: n vaatimus metastabiilisuuden vähentämiseksi esiintyy todennäköisyyttä jäljellä olevassa osassa, kun suunnittelet vastauksesi lopussa.


Tämä Q & A käännettiin automaattisesti englanniksi.Alkuperäinen sisältö on saatavilla stackexchange-palvelussa, jota kiitämme cc by-sa 4.0-lisenssistä, jolla sitä jaetaan.
Loading...