Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ac-is:lab:lab02 [2021/09/20 21:54]
alexandru.predescu
ac-is:lab:lab02 [2023/10/21 14:51] (current)
teodor.dicu [Resurse]
Line 1: Line 1:
-= Laboratorul 2 - Limbajul Verilog: Circuite combinaționale ​=+====== Laboratorul 2 -  ​Operatori. Atribuire continuă. Parametrizare. Testare ======
  
-În laboratorul anterior au fost prezentate elementele Verilog ​folosite ​pentru descrierea structurală a circuitelor logice. ​Acestea constau în [[.:​lab01#​wires | wires]], în [[.:​lab01#​primitive | instanțierea de primitive]] și în [[.:​lab01#​instantiere-module | instanțierea modulelor]] declarate de utilizator. Folosirea descrierii structurale devine însă ​complicată și greu de înțeles pentru circuite ​cu mai mult de câteva componente.+În laboratorul anterior au fost prezentate elementele Verilog ​necesare ​pentru descrierea structurală a circuitelor logice. ​Aceasta poate deveni ​complicată și dificil ​de înțeles pentru circuite ​ce îndeplinesc o funcționalitate complexă.
  
-Deși partiționarea circuitelor în cadrul unei arhitecturii duce la blocuri componente mai simpleacestea sunt rareori descrise la nivel de porți logice. Este folosită în loc o descriere comportamentală ​circuitului, ​care permite ​implementare mai rapidă. În plus, această metodă are și avantajul că este mai ușor de înteles și de modificat.+Laboratorul curent va prezenta elementele Verilog folosite pentru descrierea comportamentală. Aceasta poate descrie **ce face** circuitul ​și nu **cum** va fi acesta implementat. Mai mult, vom completa un modul funcțional cu un modul de testareastfel încât să avem posibilitatea ​de a verifica implementarea pe care o concepem.
  
-Laboratorul curent va prezenta elementele Verilog folosite pentru descrierea comportamentală a circuitelor logice. Aceasta descrie **ce face** circuitul și nu **cum** va fi acesta implementat. Se folosesc construcții de nivel înalt, care pot defini direct funcția booleană a unui circuit sau chiar algoritmul care calculează ieșirile circuitului. Atât circuitele combinaționale,​ cât și cele secvențiale pot fi descrise comportamental. Acest laborator se va axa pe descrierea circuitelor combinaționale,​ urmând ca descrierea celor secvențiale să fie făcută în laboratoarele următoare. 
  
-<note important>​ +===== Structura limbajului ​Verilog - continuare =====
-Inițial, limbajul ​Verilog ​a fost conceput numai pentru simularea circuitelor logice. Din acest considerent el oferă o paletă largă de modalități de descriere a comportamentului unui circuit (orice instrucțiune poate fi relativ ușor simulată în software). Nu toate facilitățile oferite de Verilog pot fi însă implementate direct în hardware. Suportul pentru sintetizare (compilarea unei surse într-o listă de porți sau într-o mască de circuit integrat) a fost adăugat mai târziu în limbaj. De aceea, este nevoie de atenție la descrierea unui circuit care se vrea a fi sintetizat, pentru a nu folosi construcții care sunt ne-sintetizabile.+
  
-În cadrul laboratorului,​ scopul este întotdeauna de a obține un circuit sintetizabil,​ care poate fi apoi testat pe placa FPGA. 
-</​note>​ 
  
-== Assign == +==== Assign ====
-Pentru descrierea directă a unei funcții booleene, Verilog oferă o instrucțiune numită **atribuire continuă**. Aceasta folosește cuvântul cheie ''​assign''​ și "​atribuie"​ direct, unei variabile de tip //wire// (nu de tip //​[[.:​lab02:#​tipul-reg| reg]]//!), valoarea expresiei aflată în partea dreaptă a semnului egal.+
  
-<code verilog Exemplu atribuire continua>​ 
-module exemplu_assign(output out, input a, b, c); 
  
-assign out = (&& ​!b|| c;+Deși partiționarea circuitelor în cadrul unei arhitecturii duce la simplificarea implementării unui modul, implementarea acestuia la nivel de porți logice este rareori folosită, întrucât aceasta devine complicată și dificil de înțeles. 
 + 
 +Primul pas este reprezentat de ușurarea modalității de scriere a unei funcții logice. Pentru aceasta, Verilog oferă o instrucțiune numită **atribuire continuă**. Aceasta folosește cuvântul cheie ''​assign''​ și “atribuie” unei variabile de tip ''​wire'',​ valoarea expresiei aflată în partea dreaptă a semnului egal. Atribuirea are loc la fiecare moment de timp, deci orice schimbare a valorii expresiei din partea dreaptă se va propaga imediat. 
 +În partea stângă a unei atribuiri continue se poate afla orice variabilă declarată de tip wire sau orice ieșire a modulului care nu are altă declarație (ex. reg). Expresiile din partea dreaptă pot fi formate din orice variabile sau porturi de intrare și de ieșire și orice operatori suportați de Verilog.  
 + 
 +Bazându-ne pe circuitul descris în figura de mai jos, acesta se poate scrie sub formă de atribuiri continue în următoarea formă: 
 + 
 +|  {{ .:​lab01:​gates.png?​400&​nolink |}}  |  <code verilog>​wire y1, y2; 
 +xor(out, y1, y2); 
 +and(y1, in1, in2); 
 +nand(y2, in3, in4, in5); 
 +</​code> ​ | 
 + 
 +== Exemplu atribuire continuă == 
 + 
 +<code systemverilog>​ 
 +module my_beautiful_module ​( 
 +    output out, 
 +    input  i1,​i2,​i3,​i4,​i5); ​        
 + 
 +          assign y1 = i1 i2; 
 +          assign y2 = ~(i3 i4 & i5)
 + 
 +          assign out = y1 ^ y2;
  
 endmodule endmodule
 </​code>​ </​code>​
  
-Această instrucțiune diferă însă de atribuirile normale oferite de limbajele de programare (CC++, Java etc.) prin faptul că este executată continuu, valoarea semnalulul asignat (partea stângă a semnului egal) fiind recalculată **de fiecare dată** când se modifică una dintre variabilele din partea dreaptă. Acest lucru contrastează cu funcționarea unei atribuiri obișnuite care modifică variabila asignată o singură dată.+saumai concis:
  
-<note warning+<code systemverilog
-Este o eroare să folosiți aceeași variabilă destinație pentru mai multe atribuiri continue. Ele vor încerca simultan să modifice variabilalucru care nu este posibil în hardware și va duce la rezultate imprevizibile în simulare. +module my_beautiful_module ( 
-</​note>​+    output out
 +    ​input ​ i1,​i2,​i3,​i4,​i5); ​       ​
  
-În partea stângă a unei //atribuiri continue// se poate afla orice variabilă declarată de tip wire sau orice ieșire a modulului care nu are altă declarație ​(ex. //reg//). Expresiile din partea dreaptă pot fi formate din orice variabile sau porturi de intrare și de ieșire și orice operatori suportați de Verilog.+          assign out = (i1 & i2^  (~(i3 & i4 & i5));
  
-<note important>​ +endmodule 
-Pentru a descrie un circuit combinațional folosind atribuiri continue evitați să creați bucle între semnale. Buclele pot duce la sintetizarea de elemente de memorare (eng. //latch//), sau chiar pot face circuitul nesintetizabil. +</code>
-</note>+
  
-Se poate observa că o //atribuire continuă// este mult mai ușor de scris, de înteles ​și de modificat decât o descriere echivalentă bazată pe instanțierea de primitive. Circuitul descris de o atribuire continuă poate fi însă relativ ușor sintetizat ca o serie de porți logice care implementează expresia dorită, unii operatori având o corespondență directă cu o poartă logică ​(ex''&&''​ - ȘI''​||''​ - SAU etc.).+Se poate observa că o atribuire continuă este mult mai ușor de scris, de înțeles ​și de modificat decât o descriere echivalentă bazată pe instanțierea de primitive. Circuitul descris de o atribuire continuă poate fi însă relativ ușor sintetizat ca o serie de porți logice care implementează expresia dorită, unii operatori având o corespondență directă cu o poartă logică. 
 + 
 +<note important>​Este o eroare să folosiți aceeași variabilă destinație pentru mai multe atribuiri continue. Ele vor încerca simultan să modifice variabilalucru ce nu este posibil în hardware.</​note>​ 
 + 
 + 
 +==== Constante ====
  
-== Constante == 
  
 Pentru specificarea valorilor întregi este folosită următoarea sintaxă: Pentru specificarea valorilor întregi este folosită următoarea sintaxă:
  
-<code verilog>​ +''​[size]['​radix] constant_value''​ 
-[size]['​radix] constant_value  + 
-</​code>​+  * numerele conțin doar caracterele bazei lor și caracterul '​_'​  
 +  * pentru a ușura citirea, se poate folosi caracterul '​_'​ ca delimitator  
 +  * caracterul '?'​ specifică impedanță mare (z)  
 +  * caracterul '​x'​ specifică valoare necunoscută ​ 
 +  * se poate specifica dimensiunea numărului în biți dar și baza acestuia (b,​B,​d,​D,​h,​H,​o,​O - binar, zecimal, hexa, octal) ​
  
-  * numerele conțin doar caracterele bazei lor și caracterul //'​_'//​ +<​code ​systemverilog
-  * pentru a ușura citirea, se poate folosi caracterul //'​_'//​ ca delimitator +8'​b1; ​        ​//binar, pe 8 biti, echivalent cu 1 sau 8'​b00000001 
-  * caracterele '​z'​ sau '?'​ specifică impedanță mare +8'​b1010_0111;​ //binar, ​echivalent cu 167 sau 8'​b10100111 
-  * caracterul '​x'​ specifică valoare necunoscută +4'b10;        //binar, pe 4 biti, echivalent cu 2 sau 4'b0010 etc.  
-  * se poate specifica dimensiunea numărului în biți dar și baza acestuia (b,​B,​d,​D,​h,​H,​o,​O - binar, zecimal, hexa, octal) +126;          //​scriere in decimal 
-Exemple: +16'​habcd; ​    //scriere in hexazecimal
-<​code ​verilog+
-8'​b1; ​         // echivalent cu 1 sau 8'​b00000001 ​etc. +
-8'​b1010_0111; ​ // echivalent cu 167 sau 8'​b10100111 ​etc. +
-8'b 10;        // echivalent cu 2 sau 8'b00000010 ​etc. +
-126; +
-16'​habcd;​+
 </​code>​ </​code>​
  
-== Operatori == 
-Verilog pune la dispoziție mai multe tipuri de operatori. Unii dintre aceștia sunt cunoscuți din limbajele de programare precum C, C++, Java, și au aceeași funcționalitate. Alții sunt specifici limbajului Verilog și sunt folosiți în special pentru a descrie ușor circuite logice. <tabref tabel-operatori>​ conține operatorii suportați de Verilog, împreună cu nivelul lor de precedență. 
  
-<​tabcaption tabel-operatori center | Operatori Verilog>+==== Operatori ​==== 
 + 
 + 
 +Descrierea comportamentală la nivelul fluxului de date, descrisă anterior, presupune în continuare cunoașterea schemei hardware la nivelul porților logice sau, măcar, expresia logică. Deși reprezintă o variantă mai simplă decât utilizarea primitivelor,​ nu este cea mai facilă.  
 + 
 +Pentru a ușura implementarea,​ Verilog pune la dispoziție mai multe tipuri de operatori. Unii dintre aceștia sunt cunoscuți din limbajele de programare precum C, C++, Java, și au aceeași funcționalitate. Alții sunt specifici limbajului Verilog și sunt folosiți în special pentru a descrie ușor circuite logice. Cu ajutorul acestora putem simplifica implementarea,​ apelând la construcții folosind limbajul de nivel înalt. 
 + 
 +Tabelul de mai jos conține operatorii suportați de Verilog, împreună cu nivelul lor de precedență. 
 ^  Simbol ​                                 ^  Funcție ​                      ​^ ​ Precedență ​ ^ ^  Simbol ​                                 ^  Funcție ​                      ​^ ​ Precedență ​ ^
 |  ''<​nowiki>​! ~ + -</​nowiki>''​ (unari) ​   |  Complement, Semn              |  1           | |  ''<​nowiki>​! ~ + -</​nowiki>''​ (unari) ​   |  Complement, Semn              |  1           |
Line 71: Line 95:
 |  ''<​nowiki><< ​ >> <<< ​ >>></​nowiki>'' ​   |  Shiftare ​                     |  5           | |  ''<​nowiki><< ​ >> <<< ​ >>></​nowiki>'' ​   |  Shiftare ​                     |  5           |
 |  ''<​nowiki><​ <= > >= == !=</​nowiki>'' ​   |  Relaționali ​                  ​| ​ 6           | |  ''<​nowiki><​ <= > >= == !=</​nowiki>'' ​   |  Relaționali ​                  ​| ​ 6           |
-|  ''<​nowiki>&​ | ^</​nowiki>'' ​             |  Bitwise ​                     |  7           | +|  ''<​nowiki>&​ ~& ^ ~^ ^~ | ~|</​nowiki>'' ​ |  Reducere ​                     |  ​          | 
-|  ''<​nowiki>&​ ~& ^ ~^ ^~ | ~|</​nowiki>'' ​ |  Reducere ​                     |  ​          | +|  ''<​nowiki>&&​ ||</​nowiki>'' ​             |  Logici ​                       |  ​          | 
-|  ''<​nowiki>&&​ ||</​nowiki>'' ​             |  Logici ​                       |  ​          | +|  ''<​nowiki>?:</​nowiki>'' ​                ​| ​ Condițional ​                  ​|  ​         | 
-|  ''<​nowiki>?:</​nowiki>'' ​                ​| ​ Condițional ​                  ​|  ​10          | +|  ''<​nowiki>​{,​}</​nowiki>'' ​         |  Concatenare ​                  ​| ​             | 
-|  ''<​nowiki>​{,​} {n{}}</​nowiki>'' ​         |  Concatenare ​                  ​| ​             | + 
-</​tabcaption>​+În continuare sunt prezentați operatorii mai neobișnuiți suportați de Verilog:
  
-În conținuare sunt prezentați operatorii mai neobișnuiți suportați de Verilog: 
   * Operatorii de shiftare aritmetică;​ realizează shiftarea cu păstrarea bitului de semn, pentru variabilele declarate ca fiind cu semn.   * Operatorii de shiftare aritmetică;​ realizează shiftarea cu păstrarea bitului de semn, pentru variabilele declarate ca fiind cu semn.
 +    <code systemverilog>​
 +    wire signed[7:0] a, x, y;
 +    assign x = a >>>​ 1;  // dacă bitul de semn al lui a este 0 bitul nou
 +                         //​introdus este 0 
 +                         // dacă bitul de semn al lui a este 1 bitul nou
 +                         // introdus este 1
 +    assign y = a <<<​ 1;  // bitul nou introdus este tot timpul 0,
 +                         //​asemănător cu operatorul << ​
 +    </​code>​
  
-<​code ​verilog Exemplu operatori de shiftare aritmetica+  * Operatorii de reducere; se aplică pe un semnal de mai mulți biți și realizează operația logică între toți biții semnalului 
-wire signed[7:0] ax, y;+    ​<​code ​systemverilog
 +    wire[7:0] a;  
 +    wire x, y, z; 
 +    assign x = &​a; ​ // realizeaza AND între toți biții lui a  
 +    assign y = ~&a; // realizează NAND între toți biții lui a  
 +    assign z = ~^a; // realizeaza XNOR între toți biții lui a,  
 +                    // echivalent cu ^~ 
 +    </​code>​
  
-assign x = a >>> 1; // daca bitul de semn al lui a este 0 bitul nou introdus este +  * Operatorul de concatenare;​ realizează concatenarea ​două sau mai multe semnale, într-un semnal de lățime mai mare. 
-                    // daca bitul de semn al lui a este 1 bitul nou introdus este +    <code systemverilog> 
-assign ​= a <<< ​1; // bitul nou introdus este tot timpul 0asemănător cu operatorul << +    wire[3:0] a, b; 
-</​code>​+    wire[9:0] x;  
 +     
 +    ​// biții 9:6 din x vor fi egali cu biții 3:ai lui b 
 +    // biții 5:4 din x vor fi egali cu 01 
 +    // biții 3:2 din x vor fi egali cu biții 2:1 ai lui a  
 +    // biții ​1:0 din x vor fi egali cu 00 
 +    assign ​{b, 2'​b01, ​a[2:1]2'​b00};​ 
 +    </​code>​
  
-  * Operatorii de reducere; se aplică pe un semnal de mai multi biți și realizează operația logică între toți biții semnalului. 
  
-<code verilog Exemplu operatori de reducere>​ +==== Parametrizarea modulelor ====
-wire[7:0] a; +
-wire x, y, z;+
  
-assign x = &​a; ​ // realizează SI intre toti bitii lui a 
-assign y = ~&a; // realizează SI-NU intre toti bitii lui a 
-assign z = ~^a; // realizează XNOR intre toti bitii lui a, echivalent cu ^~ 
-</​code>​ 
  
-  * Operatorul de concatenare;​ realizează concatenarea a două sau mai multe semnale, într-un semnal de lățime mai mare.+=== Parameter ===
  
-<​code ​verilog ​Exemplu operator ​de concatenare>​ +Cuvântul rezervat ''​parameter''​ este o construcție de limbaj în verilog ​care permite unui modul să fie reutilizat cu specificații diferite. Spre exemplu, un sumator poate fi parametrizat să accepte o valoare pentru numărul ​de biți care poate să fie configurată diferit de la o simulare la alta. Comportamentul lor este similar cu cel al argumentelor unor funcții în alte limbaje de programare cunoscute. Folosind ​ ''​parameter''​ este declarată o valoare constantăprin urmare este ilegală modificarea valorii acesteia în timpul simulării. De asemenea, este ilegal ca un alt tip de dată să aibă același nume ca unul dintre parametri.
-wire[3:0] ab; +
-wire[9:0] x;+
  
-assign x {b, 2'b01, a[2:1], 2'b00}  ​// bitii 9:6 din x vor fi egali cu bitii 3:0 ai lui b +<code systemverilog>​ 
-                                        ​// bitii 5:4 din x vor fi egali cu 01 +parameter MSB 7      ​// MSB este un parametru ​cu valoarea constantă 7 
-                                        // bitii 3:din x vor fi egali cu bitii 2:1 ai lui a +parameter [7:0] number = 2’b11; ​   ​// o valoare de biți este convertită 
-                                        // bitii 1:0 din x vor fi egali cu 00+                                   ​// într-o valoare de 8 biți 
 </​code>​ </​code>​
  
-== Tipul reg ==+O variabilă de tip parametru este vizibilă local, în modulul ce a fost declarată.
  
-În laboratorul trecut a fost prezentat tipul //wire// pentru reprezentarea semnalelor. Porturile unui modul erau wires, la fel și semnalele de legătură dintre instanțele primitivelor și porților. ​ Deoarece acestea realizează conexiuni, nu au o stare și nu li se pot atribui valori. Pentru a putea reține stări/​valori și a face atribuiri avem nevoie de tipul **//​reg//​**. 
  
-Declararea variabilelor de tip //reg// se poate face într-un mod similar variabilelor de tip //wire//, cum este exemplificat ​și mai jos: +=== Construirea ​și instanțierea modulelor parametrizabile ===
-<code verilog>​ +
-reg [7:0] a;  +
-reg [0:7] b; +
-reg c;+
  
-// vector de reg 
-reg [7:0] d[3:​0]; ​      // array 2D de 4 linii x 8 biți  
  
-// matrice de reg +Instanțierea modulelor a fost folosită șîn laboratorul anterior pentru a invoca logica implementată într-un alt modul. În acel context, era necesar să cunoaștem dimensiunea semnalelor din interfață pentru a le potrivi cu variabilele conectate la instanță. În cazul în care un modul are dimensiunile porturilor parametrizate,​ acesta poate fi instanțiat ​ cu valori particulare ale parametrilor (diferite de cele predefinite). Să considerăm ca exemplu un modul de mai jos:
-reg [7:0] e[3:​0][3:​0]; ​ // array 3D de 4 linii x 4 coloane x 8 biți+
  
 +<code systemverilog>​
 +module my_beautiful_module (out, a, b);
 +    output [7:0] out;
 +    input [3:0] a;
 +    input [4:0] b;
 +
 +    …// some logic
 +endmodule
 </​code>​ </​code>​
  
-Declararea variabilelor //reg// și //wire// se face în afara blocurilor //always// și //​initial//,​ iar atribuirile acestora se fac doar în interiorul acestor blocuri, prezentate în secțiunile [[.:​lab02:#​blocul-always| Blocul always]] și [[.:​lab02:#​blocul-initial| Blocul initial]]. +Pentru a instanția acest modulvom avea nevoie ​de 3 variabile ​de 84, respectiv 5 fire pe care le vom conecta astfel:
- +
-==Blocul always@ == +
- +
-În afară de folosirea atribuirilor continuecircuitele pot fi descrise comportamental și prin blocuri ​ //always//. În interiorul acestora se pot folosi construcții ​de limbaj similare celor din limbajele procedurale,​ decrise în secțiunea [[.:​lab02#​constructii-de-control | Construcții ​de control]]. +
- +
-Blocurile //always// descriu un comportament cicliccodul acestora fiind executat în continuu. Prezența operatorului ''​@''​ face ca blocul să se "​execute"​ doar la apariția unor evenimente. Evenimentele sunt reprezentate de modificarea unuia sau mai multor semnale. +
- +
-În cadrul acestui laborator ne axăm doar pe descrierea circuitelor combinaționale,​ și vom folosi doar blocuri ''​always @(*)'',​ unde ''​(*)''​ se numește //​sensitivity list//. Folosirea wildcard-ului ''​*''​ implică "​execuția"​ blocului //always// la orice eveniment de modificare a semnalelor folosite în cadrul blocului. +
- +
-Instrucțiunile din blocul //always@// sunt încadrate între cuvintele cheie //begin// și //end// și sunt "​executate"​ secvențial atunci când blocul este activat.+
  
-<​code ​verilog Exemplu bloc always+<​code ​systemverilog
-always @(*) +My_beautiful_module inst1(out, ab);
-begin +
-    b = 0;      // registrul b este inițializat cu 0 la orice modificare ​unui semnal +
-    c = 0;      // registrul c este inițializat cu 0 la orice modificare a unui semnal +
-    c = ^ a // registrul c va primi valoarea expresiei din dreapta la orice  +
-                // modificare a unui semnal (nu doar a sau b) +
-end+
 </​code>​ </​code>​
  
-Pentru ca blocul //always// să fie combinațional este necesar ca toate variabile care trebuie calculate să fie inițializate la începutul blocului //always//. În caz contrar variabilele care nu au fost atribuite niciodată într-o anumită execuție a blocului always vor trebui memorate ​de la execuția anterioară ceea ce va duce la sintetizarea unui circuit secvențial asincron+Pe de altă parte, având modulul:
  
-<hidden>+<code systemverilog> 
 +module my_beautiful_parameterized_module(out,​ a, b); 
 +    parameter a_width = 4; 
 +    parameter b_width = 5; 
 +    parameter out_width = 8;
  
-În locul wildcard-ului, ''​*'',​ sensitivity list-ul poate conține o listă de semnale la modificarea cărora blocul //always// să fie activat. Acestea se declară prin numele lor, folosind ''​or''​ sau '',''​ pentru ​le separa.+    output [out_width-1:0] out; 
 +    input [a_width-1:0] a
 +    input [b_width-1:​0] b;
  
-<code verilog Exemplu specificare lista de semnale>​ +    …// some logic 
-always @(a or b or c)       // sintaxa Verilog-1995 +endmodule
-always @(a, b, c)           // sintaxa Verilog-2001,​ 2005 +
-always @(a, b or c)         // permis dar nerecomandat,​ ingreuneaza lizibilitatea codului +
-always @(*)                 // toate semnalele din modul (intrari + wires declarate in modul)+
 </​code>​ </​code>​
  
-<note important>​+Îi putem utiliza logica fără a depinde de o dimensiune predefinită a semnalelor din interfață
  
-Este foarte important ca lista de semnale dată unui bloc //always@// să fie **completă**,​ altfel nu toate combinațiile de intrări vor fi acoperite și este posibil ca unele variabile de ieșire să nu fie recalculate. Pentru ​evita astfel de erori se recomandă folosirea wildcard-ului ''​*''​.+<code systemverilog>​ 
 +wire [4:0] out1; 
 +wire [4:0] out2; 
 +wire [2:0] a
 +wire [1:0] b;
  
-</​note>​+my_beautiful_parameterized_module #​(.a_width(3),​ 
 +                                    .b_width(2),​ 
 +                                    .out_width(5)) inst2(out, a, b);
  
-În modulul următor care implementează o poartă XORieșirea ''​out''​ se va schimba doar când semnalul ''​a''​ se schimbă, ceea ce duce la un comportament incorect care nu ia în considerare și schimbarea lui ''​b''​. În plus, modulul generat nu va fi unul combinațional,​ deoarece este nevoie de memorie pentru a menține starea ieșirii atunci când ''​b'' ​se modifică.+// Sau, menținându-se ordinea parametrilor,​ doar prin specificarea noilor // dimensiuni:
  
-<code verilog Folosire incorecta sensitivity list> +my_beautiful_parameterized_module #(325inst3(outab);
-module my_xor(output reg outinput ainput b)+
- +
-always @(a) +
-begin +
-    ​out b; +
-end +
-endmodule+
 </​code>​ </​code>​
  
-La fel și varianta ''​always @(b)''​ ar fi incorectă pentru că ''​out''​ s-ar modifica doar când semnalul ''​b''​ se modifică. 
  
-Pentru a evita astfel de greșeli folosiți ''​always @(*)''​.+===== Testare =====
  
-</​hidden>​ 
  
-<note warning> Nu se pot instanția primitive și module în interioriul blocurilor //​always// ​și //initial//</​note>​+Pentru testarea unui modul folosind simulatorul ​se creează ​module ​speciale de test, în care, printre altele, se vor atribui valori intrărilor. Simularea permite detecția rapidă a erorilor de implementare ​și corectarea acestora
  
-=== Construcții de control ===+Pentru a creea un modul de test și a-l simula puteți urma tutorialul de simulare [[https://​ocw.cs.pub.ro/​courses/​ac-is/​tutoriale/​2-ise-simulare|aici]],​ iar această secțiune va prezenta câteva din construcțiile de limbaj pe care le puteți folosi într-un astfel ​de modul. ​
  
 +{{ :​ac-is:​lab:​lab02:​circuit_tb.png }}
  
-^Cod Verilog ^ Cod C^ 
-| <code verilog> ​ 
- if(a == 0)  
- begin 
-     b = 2; 
- end 
- else 
- begin 
-     b = 4; 
- end 
-</​code> ​ | <code c> 
- if(a == 0)  
- ​{ ​ 
-     b = 2;  
- } 
- ​else ​ 
- ​{ ​ 
-     b = 4;  
- } 
-</​code>​ | 
-| <code verilog> 
-case(sig) 
-    0: a = 2; 
-    1: a = 1; 
-    default: a = 0; 
-endcase 
-</​code>​ | <code c> 
-switch(sig) { 
-    case 0: a = 2; break; 
-    case 1: a = 1; break; 
-    default: a = 0; 
-} 
-</​code>​ | 
-| <code verilog> 
-for(i = 0; i < 10; i = i + 1)  
-begin 
-    a = a % i; 
-end 
-</​code>​ | <code c> 
-for(i = 0; i < 10; i = i + 1)  
-{ 
-    a = a % i; 
-} 
-</​code>​ |  
-| <code Verilog> 
-i = 0; 
-while(i < 10)  
-begin 
-    a = a % i; 
-    i = i + 1; 
-end 
-</​code>​|<​code c> 
-i = 0; 
-while(i < 10)  
-{ 
-    a = a % i; 
-    i = i + 1; 
-} 
-</​code>​ | 
-|<code verilog> 
-repeat(10) 
-begin 
-    a = a + 1; 
-end 
-</​code>​ | 
  
-<note important>​Construcțiile de repetiție trebuie folosite cu atenție, deoarece pot duce foarte ușor la circuite nesintetizabile. Pentru a obține un circuit sintetizabil o condiție __necesară__ pentru o buclă este ca numărul de iterații să fie cunoscut în momentul sintetizării. În alte cuvinte, numărul de iterații trebuie să fie fix și nu poate depinde de variabilele de intrare (direct sau indirect). +==== Blocul initial ====
-</​note>​+
  
-<code verilog Exemplu modul descris comportamental>​ 
-module my_module( 
-    output reg[3:0] o,  // o trebuie să fie reg ca să îl folosim în atribuiri în always 
-    input[3:0] a, b); 
  
-reg[2:0] i;     // poate fi maxim 7; noi avem nevoie ​de maxim 4 +Blocurile ​//initial// descriu un comportament executat o singură dată la începerea/​activarea simulării și sunt folosite pentru inițializări și în module ​de test. Instrucțiunile sale trebuie încadrate între cuvintele cheie //begin// și //end// și sunt executate secvențial.
-reg      c;     // ținem minte transportul+
  
-always @(*) +<code systemverilog>​ 
-begin +    ​initial ​begin  
-    ​i ​= 0;      // la orice modificare a intrărilor i va fi inițializat cu 0 +        ​a ​= 0;  
-    ​c ​0     // transportul inițial este 0 +        ​b ​1;  
-    o = 0     // la orice modificare a intrărilor o va fi inițializat cu 0 +        #10; // delay 10 unități de timp de simulare ​ 
-     +        ​a ​1;  
-    // toti bitii lui o sunt recalculați la modificarea intrărilor +        ​0;  
-    ​for(i ​0i < 4; i = i + 1) begin +    end 
-        ​{c, o[i]} a[i] + b[i] + c+
-    end +
-end +
- +
-endmodule+
 </​code>​ </​code>​
  
-<​note>​ +Blocurile ''​initial''​ nu sunt sintetizabile,​ fiind folosite doar în simulări.
-Pentru ca un bloc //always// să fie sintetizat într-un circuit combinațional este necesar ca orice "​execuție"​ a blocului să atribuie **cel puțin** o valoare pentru fiecare ieșire a modulului.+
  
-Bineînțeles,​ acea valoare nu poate fi calculată pe baza ieșirilor sau valorilor anterioare ale variabilelor din interiorul modulului. Asta ar însemna că este necesară o memorie pentru a menține acele valori, transformând circuitul într-unul secvențial. 
-</​note>​ 
  
-== Testare ​== +==== Sincronizarea prin întârziere ====
  
-Pentru testarea unui modul folosind simulatorul se creează module speciale de test, în care, printre altele, se vor atribui valori intrărilor. Pentru a creea un modul de test și a-l simula puteți urma [[..:​tutoriale:​2-ise-simulare|tutorialul de simulare]]. Această secțiune va prezenta câteva din construcțiile de limbaj pe care le puteți folosi într-un astfel de modul. 
-=== Blocul initial === 
  
-Blocurile ​//initial// descriu un comportament executat ​singură dată la începutul modulelor ​și sunt folosite ​pentru ​inițializăriInstrucțiunile sale trebuie încadrate între cuvintele cheie //begin// și //end// și sunt executate secvențial.+Folosind operatorul ​//#// se poate specifica ​durată de timp între apariția instrucțiunii ​și momentul executării acesteia. Aceasta este utilă ​pentru ​a separa temporal diversele atribuiri ale intrărilorDurata de timp este reprezentată prin unități de timp de simulare. De exemplu, dacă simularea folosește un //timescale// în nanosecunde, ​//#n// va reprezenta n nanosecunde.
  
-<code Verilog> 
  
-initial begin +==== Afișare ​====
-0;        +
-  b 1;       +
-  #10;      // delay 10 unități de timp de simulare +
-        a 1; +
-        b 0;       +
-end +
-</​code>​ +
-=== Sincronizarea prin întârziere ​===+
  
-Folosind operatorul ''#''​ se poate specifica o durată de timp între apariția instrucțiunii și momentul executării acesteia. Aceasta este utilă pentru a separa temporal diversele atribuiri ale intrărilor. Durata de timp este reprezentată prin unități de timp de simulare. De exemplu, dacă simularea foloseste un //​timescale//​ în nanosecunde,​ //#n// va reprezenta //n// nanosecunde. 
  
-<code verilog>​ +Atât în modulele de test cât și în modulele testate se pot folosi construcții pentru afișare în interiorul blocurilor //initial// ​ș//always//. Una dintre aceste instrucțiuni este ''​display'':​ 
-initial ​begin + 
-   a = 0; +<code systemverilog>​ 
-   #​100; ​     ​// delay 100 unitățde timp de simulare +    $display(arguments);​
-   a = 1; +
-end+
 </​code>​ </​code>​
  
-=== Afișare === +Argumentele acestei comenzi sunt similare cu cele ale funcției //printf// din C, ca în exemplul de mai jos, iar specificația completă o putețgăsi [[https://www.chipverify.com/verilog/verilog-display-tasks|aici]]//$display// adaugă o linie nouă, iar dacă nu se dorește acest lucru se poate folosi comanda //​$write//​. ​
- +
-Atât în modulele de test cât și în modulele testate se pot folosi construcții pentru afișare în interiorul blocurilor ​//initial// și //always//. Una dintre aceste instrucțiuni este ''​$display'':​+
  
-<​code ​verilog+<​code ​systemverilog
-$display(arguments)+    a = 1; b = 4; 
 +     
 +    ​$display("​suma=%d",​ a+b);
 </​code>​ </​code>​
-Argumentele acestei comenzi sunt similare cu cele ale funcției //printf// din C, ca în exemplul de mai jos, iar specificația completă o puteți găsi [[http://​verilog.renerta.com/​source/​vrg00013.htm |aici]]. ''​$display''​ adaugă o linie nouă, iar dacă nu se dorește acest lucru se poate folosi comanda ''​$write''​. 
  
-<code verilog> 
-for(i = 0; i < 10; i = i + 1) begin 
-    $display("​i=%d",​ i); 
-end 
-</​code>​ 
  
 +===== Exerciții =====
  
-== Exerciții == 
-  - (**3p**) Implementați și simulați un circuit combinațional care calculează partea întreagă a mediei aritmetice a două numere a și b pe 4 biți, fără a utiliza operatorul ''/''​. 
-    * Hint: Verilog are un operator pentru sumă. 
-    * Hint: Care este suma maximă care poate fi obținută prin adunarea a două numere pe 4 biți? De câți biți este nevoie pentru a o reprezenta? 
-    * Hint: Cum poate fi substituit operatorul ''/''?​ 
-    * //Bonus (**1p**):// Adaptați circuitul de mai sus pentru a aproxima media aritmetică la cel mai apropiat număr întreg. 
-  - (**3p**) Implementați și simulați un multiplexor 8:2 folosind un bloc comportamental ciclic (''​always''​). 
-    * Hint: Respectați interfața impusă în scheletul de cod. Ce dimensiuni vor avea semnalele de intrare, ieșire și selecție? 
-    * Hint: Folosiți intrucțiunea ''​case''​. 
-  - (**4p**) Implementați și simulați un dispozitiv digital ce are ca intrări termenul inițial și rația unei progresii aritmetice, iar semnalul de ieșire reprezintă al nouălea termen al progresiei, fără a folosi operatorul ''​*''​ (înmulțire). Respectați interfața impusă în scheletul de cod. 
-    * Hint: ''​ T(k) = T(1) + (k-1)*r ''​ - formula de calcul al termenului k al unei progresii aritmetice. 
-  - //(**1p**) - Bonus:// Implementați o unitate aritmetico-logică simplă, pe 4 biți, cu două operații: adunare și medie aritmetică. Folosiți o intrare de selecție de 1 bit pentru a alege între cele două operații astfel: 0 - adunare, 1 - medie aritmetică. Utilizați circuitul implementat la execițiul 1. 
-    * Hint: Comparați numărul de biți ai ieșirii unui sumator pe 4 biți cu numărul de biți ai ieșirii circuitului ''​average''​. Câți biți va avea ieșirea Unității Aritmetice Logice? 
-    * Hint: Câți biți are semnalul asociat ieșirii sumatorului,​ respectiv iesirii mediei aritmetice? Dar a UAL-ului? 
-    * Hint: Folosiți o atribuire continuă pentru a selecta între adunare și medie aritmetică. 
  
-== Resurse ==+Pentru implementarea exercițiilor se vor utiliza scheletele de cod din arhiva laboratorului. Scheletele de cod conțin deja un proiect Xilinx ISE, iar unele din ele și un modul de testare. Urmăriți cerința și zonele marcate cu TODO.
  
-  * {{.:lab02:lab2_skel.zip|Schelet ​de cod}} +  ​**(4p)** Sumatorul pe 4 bițiImplementare și testare. 
-  {{.:lab02:sol:​lab2_sol.zip|Soluție laborator}} ​(disponibilă începând ​cu 12.10.2019) +    - (1p) Implementați și simulați un **sumator pe 4 biți**, cu două intrări și o ieșire 
-  * <​html><​class="​media mediafile mf_pdf"​ href="/ac/wiki/lab/lab02?​do=export_pdf">​PDF laborator<​/a></html>+      * //Hint//Utilizați atribuirea continuă pentru implementare 
 +      * //Hint//Atenție la dimensiunea semnalelor de ieșire 
 +    - (2p) Folosind [[https://​ocw.cs.pub.ro/​courses/​ac-is/​tutoriale/​2-ise-simulare|tutorialul ​de simulare]], implementați un modul de test care să stimuleze sumatorul în cât mai multe situații posibile 
 +      //Hint//: Variabilele pe care le atribuim în modulul de test vor fi de tip //reg//. 
 +      * //Hint//Testați atât situații obișnuite de adunare, cât și situații speciale (ex. carry = 1) 
 +      * //Hint//Consultați forma de undă pentru a determina corectitudinea implementării.Verificați corectitudinea sumatorului vizualizând semnalele în baza 10. 
 +    - (1p) Analizând implementarea din [[ac-is:​lab:​lab01|Laboratorul 1]] și varianta curentă, analizați cele două tipuri de implementări. 
 +      * //Hint//: Comparați puncte forte, puncte slabe pentru fiecare din cele două variante 
 +  - **(3p)** Implementați și simulați un **sumator parametrizat pe n biți**, ​cu două intrări și o ieșireParametrizarea se va efectua asupra dimensiunii variabilelor. 
 +    * //Hint//: De câți parametri este nevoie? Observați dependența între dimensiunea variabilelor de între și cea de ieșire. 
 +    * //Hint//: Luând exemplul modulului de test implementat la exercițiul 1, instanțiați un sumator pe 6 biți și adăugați stimuli corespunzători pentru a-i testa întreaga plajă de valori
 +  ​**(3p)** Implementați și simulați un **comparator** pe 4 biți. Acesta are două intrări și 3 ieșiri (pentru mai mic, egal și mai mare).  
 +    * //Hint//: Unei variabile îi poate fi atribuită valoarea unei expresii logice.  
 +    * //Hint//: Considerând experiențexercițiului 2, există vreo posibilitate să parametrizați comparatorul?​ 
 +  - **(2p)** Implementați și simulați un **multiplexor 4:1**. Urmăriți diagrama de semnale generată:​ 
 +    * //Hint//: Consultați [[ac-is:lab:​lab00|laboratorul 0]] pentru implementarea unui multiplexor 4:1.  
 +    * //Hint//: Respectați interfațcerută în scheletul de cod.  
 +    - (1p) Implementați multiplexorul folosind ecuația logică dedusă din tabelul de adevăr. 
 +    - (1p) Implementați multiplexorul folosind operatorul condițional ‘?’ 
 +      * //Hint//: Operatorul poate apărea de mai multe ori într-o expresie. ex: ''​assign x = (a == 0) ? 1 : ( (a == 1) ? : 2 : 0 );''​
  
  
-<ifauth @user>+===== Resurse ===== 
 +  * {{.:​lab02:​lab2_skel.zip|Schelet de cod}}  
 +  * <​html><​a class="​media mediafile mf_pdf"​ href="​https://​ocw.cs.pub.ro/​courses/​ac-is/​lab/​lab02?​do=export_pdf">​PDF laborator</​a></​html>​  
 +  * {{.:​lab02:​sol:​lab2_sol.zip|Soluție laborator}} 
 + 
 +<ifauth @ac-is>
 ---- ----
-  * [[:​internal:​comb|Ghid asistent]]+  ​* {{.:​lab02:​sol:​lab2_sol.zip|Soluție laborator}} 
 +  ​* [[ac-is:internal:guidelines|Ghid asistent]]
 </​ifauth>​ </​ifauth>​
- 
- 
-== Referințe == 
-  * Ciletti, Michael D. "​Advanced digital design with the Verilog HDL". Prentice Hall, 2011 
-  * [[http://​www-inst.eecs.berkeley.edu/​~cs150/​sp13/​resources/​Always.pdf | Verilog: always@ Blocks]] 
-  * [[http://​www-inst.eecs.berkeley.edu/​~cs150/​sp13/​resources/​Nets.pdf | Verilog: wire vs. reg]] 
ac-is/lab/lab02.1632164074.txt.gz · Last modified: 2021/09/20 21:54 by alexandru.predescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0