CID Seminar 4

De la WikiLabs
Versiunea din 14 aprilie 2014 08:55, autor: Rhobincu (discuție | contribuții) (Pagină nouă: Numărătorul (''counter'') este unul din cele mai utilizate circuite digitale şi sigur unul dintre cele mai cunoscute în afara lumii electroniştilor. Orice microprocesor are un n...)
(dif) ← Versiunea anterioară | Versiunea curentă (dif) | Versiunea următoare → (dif)
Jump to navigationJump to search

Numărătorul (counter) este unul din cele mai utilizate circuite digitale şi sigur unul dintre cele mai cunoscute în afara lumii electroniştilor. Orice microprocesor are un numărător de program (program counter) fără de care nu poate executa programele din calculator. Numărătorul de program generează o secvenţă de adrese de memorie în ordine crescătoare, câte una în fiecare perioadă de ceas, şi din când în când, când se execută un salt în program, sare la o adresă diferită de cea imediat următoare.

Exemplul 1

Cel mai simplu numărător este registrul cu incrementare. Exemplul 6 din seminarul 2 v-a cerut descrierea unui circuit combinaţional de incrementare comandată, una din variantele posibile fiind:

module increment(
    input inc,
    input [3:0] a,
    output reg [3:0] out
);

always @(*)
    out = inc ? a + 1 : a;

endmodule

Cu minime modificări descrierea de mai sus poate fi transformată într-un numărător cu incrementare comandată. Pentru a genera o secvenţă crescătoare de numere, numărul de la ieşire este readus la intrare. Bucla o putem închide în interiorul modulului, renunţând la intrarea a:

module incorect(
    input inc,
    output reg [3:0] out
);

always @(*)
    out = inc ? out + 1 : out; // buclă infinită!

endmodule

Închiderea unei bucle peste un circuit combinaţional duce de cele mai multe ori la un circuit instabil, iar în cazul descrierii de mai sus simularea va intra într-o buclă infinită. Reţineţi aşadar o regulă de proiectare:

Într-un circuit combinaţional sunt interzise buclele! O variabilă nu poate fi folosită niciodată în calculul propriei sale valori!

Construcţii precum

        out = inc ? out + 1 : out;

sau

	out <= inc ? out + 1 : out;

nu pot fi folosite într-un bloc de descriere combinaţională, fie assign, fie always @(*).

Bucla poate fi închisă numai peste un circuit combinaţional şi un registru, obţinându-se un circuit secvenţial. Prin urmare vom transforma blocul always dintr-unul combinaţional (sensibil la orice modificare a variabilelor din expresiile din partea dreapta) într-unul secvenţial, sensibil numai la fronturile active ale semnalului de ceas (obs. 1). Semnalul de ceas trebuie declarat ca intrare (obs. 2). Şi, foarte important, atribuirea din blocul secvenţial este obligatoriu nonblocantă/non-blocking (obs. 3), regulă de bază în descrierea circuitelor secvenţiale (vezi exemplul 2 din seminarul 3):

module counter(
    input clk, // <-- obs. 2
    input inc,
    output reg [3:0] out
);

always @(posedge clk) // <-- obs. 1
    out <= inc ? out + 1 : out; // <-- obs. 3

endmodule

Descrierea de mai sus este echivalentă următoarei scheme bloc:

Fișiser:sem5_fig1.png

Scrieţi un modul de testare a modulului counter care să genereze semnalul periodic de ceas şi semnalul de comandă, iniţial inactiv, apoi activ, şi simulaţi circuitul. Ce se observă la ieşirea lui?

Descrierea de mai sus este sintetizabilă şi numărătorul sintetizat funcţionează ireproşabil. Şi totuşi la simulare ieşirea este tot timpul nedefinită. Motivul îl reprezintă valoarea iniţială a acesteia. Orice actualizare a variabilei out se bazează pe valoarea ei precedentă, iar prima execuţie a instrucţiunii de atribuire (out <= inc ? out + 1 : out;) ia în calcul valoarea iniţială. Valoare care este nedefinită, adică x! Adunând 1 la o valoare nedefinită se obţine tot o valoare nedefinită. Simularea este totuşi doar o aproximare a realităţii şi trebuie să fim conştienţi de limitele acesteia.

Un artificiu pentru a iniţializa variabila out ar fi forţarea valorii ei din afara modulului, din modulul de test.

În timpul verificării putem avea acces direct la orice semnal (variabilă) din interiorul modulului testat dacă folosim numele complet al acestuia, nume unic în cadrul ierarhiei de blocuri din structura modulului testat. Numele complet se specifică asemenea numelui unei variabile dintr-o ierarhie de obiecte dintr-un program scris într-un limbaj orientat pe obiecte:

    numeInstanţăTop.semnal

    numeInstanţăTop.numeInstanţăBloc.semnal

    numeInstanţăTop.numeInstanţăBloc.numeInstanţăSubbloc.semnal

În cazul numărătorului nostru, dacă în modulul de test acesta este instanţiat cu numele cnt, variabila out poate fi iniţializată în pasul 0 al simulării direct din modulul de test:

    initial mycounter.out = 0;

Acest mod de acces trebuie folosit cu parcimonie şi numai în modului de test, nefiind o instrucţiune sintetizabilă! Se recomandă folosirea accesului direct doar în scopul citirii (monitorizării) unor variabile la care nu avem acces direct prin interfaţa instanţei de top. Atribuirile prin acces direct trebuie limitate strict la situaţiile în care ştim foarte clar toate efectele pe care le produce. Reţineţi:

Este interzisă în codul unui modul de circuit folosirea accesului direct la un semnal intern al altui modul.

Mult mai elegant este să proiectăm numărătorul cu o intrare de reset sau de ştergere (clear), intrare utilă în practică oricând dorim să aducem imediat numărătorul la zero. Iniţializarea numărătorului va fi făcută prin activarea intrării de ştergere.