CID aplicatii 2 : Instantiere si porti logice
Teorie: observatii generale
1) Proiectarea circuitelor digitale (ce facem in acest laborator) se bazeaza pe generarea unor circuite digitale fizice. Desi se va scrie cod in limbajul Verilog, mereu trebuie avut in vedere faptul ca in spate se va genera un circuit fizic, codul nu se ruleaza pas cu pas. Aceasta fiind deosebirea fundamentala intre limbajele de descriere hardware (genereaza circuite fizice) si limbajele de programare (cod care se executa pas cu pas). Unele concepte e posibil sa semene cat si unele elemente de sintaxa cu ce ati facut la programare, dar este important sa intelegeti ca prin codul scris se deseneaza/genereaza circuite. Sau invers, pornind de la un desen, se poate face o descriere in Verilog a acestuia.
2) Proiectele exemplu care pot fi descarcate au fost facut in Vivado 2021.2. Daca aveti alta versiune e posibil sa va ceara sa le salvati in versiunea respectiva sau sa fie nevoie sa va faceti un proiect nou in care sa adaugati fisierele cu cod.
Teorie: incapsulare si instantiere
Unitatile constructive de baza din care se formeaza circuitele digitale se numesc porti logice. Acestea implementeaza functii logice si prin conectarea mai multor astfel de circuite simple complexitatea unui circuit poate creste pana la nivelul procesoarelor actuale. Pentru a putea controla cresterea complexitatii in proiectarea unui circuit de dimensiuni mari se folosesc 2 concepte cheie: 1) incapsularea functiei dorite intr-un modul 2) instantierea unor module mai mici si asamblarea acestora pentru a forma un modul mai mare.
Incapsularea se refera la a grupa elementele ce alcatuiesc o anumita functionalitate intre-un modul. Aceste elemente pot la randul lor sa fie alte module. Instantierea (asemanator cu POO) se refera la a apela un modul deja scris pentru a fi folosit efectiv in circuitul curent. Pentru a face o analogie cu programarea, cand se declara o variabila (sau un obiect), tipul variabilei este echivalent cu modulul si numele ei este numele instantei.
Pentru a intelege aceste concepte se da circuitul de mai jos:
Observatii:
1) Notatie aici: Numele modulului este scris in interior.
2) Notatie aici: Numele instantei este scris deasupra.
3) Idee fundamentala: prin instantierea si incapsularea unor circuite simple, mici, apar circuite mai complexe.
4) Important: Urmatoarele propozitii sunt adevarate, daca vreuna e neclara consultati cadrul didactic:
a) "modul_2" este instantiat o singura data in tot proiecul iar instanta se cheama "x"
b) "modul_2" are o intrare numita "in0" si o iesire numita "out"
c) "modul_2" este instantiat in cadrul "modul_6"
d) Modulul cel mai mare, ce cuprinde toata functionalitatea dorita a sistemului (aici "modul_6") se numeste uzual top.
e) Instanta top-ului care apare atunci cand se doreste testarea sa in simulare intr-un testbench (tb) se numeste uzual DUT (design under test)
f) "modul_0" apare instantiat de 3 ori. Circuitul final, cuprinde 3 subcircuite de tip "modul_0".
g) La nivel de top, instanta sa se cheama "b".
h) La nivel de "modul_5", cele 2 instante se cheama "a" si "c".
i) "a" si "c" sunt 2 circuite fizice diferite chiar daca ambele sunt de tipul "modul_0". Fiind instante ale aceluiasi modul deci identice in alcatuire, luate separat ele fac acelasi lucru. Luate in contextul lui "modul_5", "a" genereaza datele de pe firul "w1" si "c" genereaza datele pentru iesire.
j) circuitul "b" de tip "modul_1" (instantiat in "modul_5") este complet diferit de circuitul "b" de tip "modul_0" (instantiat in top). Este permis ca ele sa aibe acelasi nume (cele 2 instante) deoarece se afla in locatii diferite.
k) Identic, mai multe module au o intrare numita "in_0". La sinteza circuitului nu se face confuzie intre acestea deoarece fiecare e vazut la nivelul altei instante.
l) Identic, firele de legatura "w0", "w1".
m) La nivelul "top", firul de legatura "w2" leaga iesirea "out_0" a instantei "b" la intrarea "in2" a instantei "y".
n) La nivelul "top", firul de legatura "w1" leaga iesirea "out_0" a instantei "x" la intrarea "in1" a instantei "y".
o) La nivelul "modul_5", firul de legatura "w1" leaga iesirea instantei "b" la intrarea instantei "c".
Pentru claritatea desenului, respectivele intrari si iesiri nu au fost denumite. In cod este obligatoriu ca ele sa fie definite si denumite.
p) La nivelul "top", "in0","in1","in2","in3","in4" si "out0" formeaza interfata modulului (semnalele care intra sau ies din modul).
q) La nivelul "top", "w0", "w1", "w2" sunt fire interne de legatura.
r) La nivelul "top", firul "in3" este conectat ca intrare pentru 3 submodule.
Teorie: testarea circuitelor
Pentru a se testa functionarea corecta a circuitului final, acesta este instantiat intr-un modul numit "test_bench" sau "tb". Acest modul este folosit strict in simulare. Testarea unui circuit are loc conform schemei urmatoare:
Generarea datelor de intrare se va face asemanator cu laboratorul 1.
Rolul modelului ideal si al comparatorului datelor de iesire il veti avea voi, privind formele de unda din simulare.
In cazul unui sistem automat, se genereaza mesaje de eroare sau mesaje ca functionarea este in regula.
Observatie: proiectarea si verificarea sunt 2 domenii diferite, firmele avand departamente separate pentru acestea.
Proiectarea/Design se ocupa cu scrierea in Verilog a modului de top si toate modulele ce se afla in acesta, avand ca scop final realizarea fizica pe placa a unui circuit.
Verificarea se ocupa de scrierea in SystemVerilog (limbaj format din Verilog cu concepte de POO) a modului de testbench si generarea de stimuli si scenarii care sa testeze functionarea design-ului.
Teorie: PORTI LOGICE
Portile logice folosite uzual, impreuna cu tabelele lor de adevar sunt date mai jos.
Se folosesc porti logice cu 1 sau 2 intrari, cele mai mari fiind construite din acestea.
in | buffer (repetor) out | inverter (not) out |
0 | 0 | 1 |
1 | 1 | 0 |
A | B | A AND B | A NAND B | A OR B | A NOR B | A XOR B | A NXOR B |
0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
Exemplu rezolvat: NAND2 din AND2 si NOT
In urmatorul exemplu se implementeaza o poarta NAND din o poarta AND si o poarta NOT.
Codul de mai jos exemplifica idea de instantiere si contine comentarii legate de sintaxa verilog necesara.
In mod uzual fisierele sunt denumite dupa modulul ce se afla in ele, in fiecare fisier fiind un singur modul.
Schema circuitul care se doreste a fi creat este:
Fisierul not_gate.v:
// comentarii cu "//" sau cu /* .... */ ;
/*
ca in c/c++
*/
module not_gate // cuvant cheie "module" apoi numele modulului (asemanator clase din c++)
( // intre paranteze se pune interfata (firele care intra sau ies din modul)
input wire in0, // in0 este o intrare => input, in0 este fir => wire ;
output wire out0 // out0 este o intrare => output, out0 este fir => wire
); // aici ";" sa nu il uitati
assign out0 = ~in0; // cuvant cheie assign;
// semnalele de tip wire (cum e out0) iau valoare prin assign
// ~ e semnul pentru negatie pe biti (ca in c/c++)
endmodule // cuvant cheie "endmodule". orice module se inchide cu endmodule.
Fisierul and_gate.v:
module and_gate
(
input in0, // aici sunt 2 intrari
input in1, // se poate omite "wire". default e wire daca nu e pus nimic
output out0
); // nu conteaza ordinea in care sunt puse intrarile si iesirile.
// uzual si pentru usurinta se ordoneaza si grupeaza dupa functionalitate
// in cazul de mai sus, am pus intai intrarile, apoi iesirile.
assign out0 = in0 & in1; // operatia propriu zisa
endmodule
Fisierul top.v:
module top
(
input a,
input b,
output c
);
wire w0; //declarat un fir intern de legatura
and_gate and_gate_0 // instantiere: nume_modul nume_instanta (asemanator int x din c/c++)
(
.in0(a), // la intrarea "in0" a instantei "and_gate_0" se conecteaza firul "a"
.in1(b), // grija ca "in0", "in1", "out0" sa existe in declararea modulului "and_gate"
.out0(w0) // grija ca "a", "b", "w0" sa existe la nivelul modulului in care se face instantierea
);
not_gate not_gate_0
(
.in0(w0), // "w0" care iese din "and_gate_0" intra in "not_gate_0"
.out0(c) // "c" care iese din "not_gate_0" iese din modulul "top" (e iesire in interfata de sus)
); // se poate scrie si ".in0(w0),.out0(c)" dar se prefera fiecare fir pe randul sau (lizibilitate si loc de comentarii pt design-uri complexe)
// Observatie: la varianta de mai sus de instantiere, nu conteaza ordinea firelor.
/*
not_gate not_gate_0
( // se poate instantia si in forma prescurtata ca aici
w0, // in acest caz se pun conexiunile in ordinea in care sunt declarate intrarile si iesirile din modul
c // NU se recomanda stilul asta de instantiere
); // apar greseli frecvent la ordinea firelor, la numarul lor,
*/ // mai ales daca modulul e complex si are multe intrari si intrari
endmodule
Echivalent se pot folosii si "primitive" Verilog pentru scrierea top-ului.
Primitivele sunt porti logice care exista deja in limbaj, folosite atunci cand se doreste o descriere structurala a circuitului.
In instantiere acestora, iesirea se pune prima, urmata de intrari.
Observatie: daca se doreste scrierea acestor porti de catre voi (ca mai sus), numele modulului nu trebuie sa fie un cuvant cheie ocupat de primitive.
Fisierul top_v2.v:
module top_v2
(
input a,
input b,
output c
);
wire w0;
and and_gate_0(w0,a,b); // primitiva pentru poarta and
not not_gate_0(c,w0); // primitiva pentru poarta not
endmodule
Fiind un modul simplu, intreaga functionalitate putea fi scrisa la nivel de "top" si simplificat ca mai jos.
Fisierul top_v3.v:
module top_v3
(
input a,
input b,
output c
);
assign c = ~ (a & b); // a si b, negate
endmodule
Testarea circuitul se face printr-un testbench, acesta fiind:
Fisierul tb.v:
`timescale 1ns / 1ps
module tb(); // din/in tb nu intra si iese nimic. niciodata.
reg tb_a; // ce va fi intrare pentru dut, aici e declarat ca "reg", ca sa poata tine valori
reg tb_b;
wire tb_c; // ce e declarat ca iesire pentru dut, aici e declarat ca "wire" pentru ca device-ul ii va da valorile
top dut // instantierea modulului de tip "top" sub numele "dut"
(
.a(tb_a),
.b(tb_b),
.c(tb_c)
);
initial
begin // in loc de { ... } din c/c++, in Verilog se pune begin ... end
#10; // dupa 10 unitati de timp
tb_a = 0; // tb_a ia valoarea 0
tb_b = 0;
#10; // dupa inca 10 unitati de timp, deci in total la 20
tb_a = 1;
tb_b = 0;
#10;
tb_a = 0;
tb_b = 1;
#10;
tb_a = 1;
tb_b = 1;
#10;
tb_a = 0;
tb_b = 0;
#20 $stop(); // oprirea simularii
end //end pentru initial
endmodule
Formele de unda rezultate din simulare se pot vedea mai jos:
In chenarul verde se pot observa instantele din simulare.
Daca se doreste adaugarea de semnale noi pentru a fi vazute, se selecteaza modulul instantiat in care acestea se afla (chenar verde).
Apoi, din chenarul rosu se aleg semnalele si se adauga prin click dreapta->add to wave window.
Pentru o mai usoara vizualizare semnalele se pot grupa asa cum se vede in chenarul mov prin click dreapta->new group.
Se observa o functionare corecta circuitului, acesta fiind o poarta NAND. El scoate "0" cand ambele semnale de intrare sunt "1" si scoate "1" in rest.
Dupa ce circuitul a fost testat in simulare se doreste punerea sa fizica pe placa.
Pentru asta se urmeaza pasii descrisi in tutorialul Vivado.
Proiectul complet se poate descarca de aici: W2_exemplu_nand.zip
Exercitii
Pentru urmatoarele exercitii se doreste atat testarea designurilor prin simulare cat si punerea acestora pe placa. Legat de placa, intrarile vor fi conectate la butoane iar iesirile la leduri. Se va consulta tabelul cu pini disponibili de // INSERT LINK AICI DUPA CE AVEM PLACA CLAR
Exista moduri mai rapide de a scrie functionalitatea dorita (vezi exemplu), dar tema principala a acestui laborator este instantierea asa ca sunteti rugati sa respectati desenele si sa instantiati fiecare poarta individual.
EX 1) AND4 din AND2
Acest exercitiu arata cum se construieste o poarta AND cu 4 intrari din porti AND mai simple, cu 2 intrari. Iesirea acesteia va fi "1" doar daca toate intrarile sunt "1".
EX 2) OR4 din OR2
Asemanator cu exercitiul anterior, se poate construi si o poarta or cu 4 intrari din porti or cu 2 intrari. Iesirea acesteia va fi "1" doar daca oricare din intrari (cel putin una) este "1".
EX 3) AND4 din AND2 aranjat pe lung (nu arbore)
In multe cazuri, aceasi functionalitate poate fi atinsa prin circuite care arata diferit. O alta varianta de a face o poarta AND cu 4 intrari este prezentata mai jos.
Comparati cele 2 variante.
EX 4) and4 din nand2
Orice poarta logica de baza poate fi construita doar din porti NAND sau doar din porti NOR.
Desenati si implementati circuitul pentru o poarta AND4 din porti NAND cu 2 intrari.
Tip: Incercati intai sa generati o poarta AND2 din NAND2.
EX 5) and4 pe 4b din and4 pe 1b
Se pot face operatii logice si pe mai multi biti deodata prin punerea in paralel a mai multor porti cu o singura iesire. exemplu : pentru intrarile 0011 si 1110 iesirea va fi 0010
Pentru exersarea instantierii si intelegerea circuitului din spatele operatilor multibit implementati acest circuit prin instantierea a 4 porti AND2 pe 1 bit intr-o poarta AND2 pe 4 biti.
Exista si un mod mai rapid si simplu de a scrie aceasta functionalitate, grupand intrarile si iesirile in "bus"-uri, ca in codul de mai jos. Acest mod de a grupa firele este desenat mai jos.
Fisierul and4_4b.v:
module and4_4b
(
input wire [3:0] in0, // in0 are 4b, de la bit 3 la bit 0 inclusiv
input wire [3:0] in1, // se noteaza msb:lsb (most significant bit: least significant bit) (echivalent cu conceptul de cifra sutelor, cifra unitatilor, pt binar)
output wire [3:0] out0
);
assign out0 = in0 & in1;
endmodule
EX 6) Desenati schemele logice pentru urmatoarele circuite:
a)
module schema1 (a, b, c, d, f);
input a, b, c, d;
output f, g;
wire w1;
wire w2;
and P1 ( w1, a, c );
or P2 ( f, w1, w2, d);
not P3 (w2, b);
and P4 (g, w1, b, d);
endmodule
Observatie: In versiunea veche de verilog (si sa pastrat si in cea curenta), se pot specifica directiile porturilor si in exteriorul parantezelor, ca mai sus. Aceasta scriere nu este recomandata.
b)
module schema2 (a, b, c, d, f);
input a, b, c, d;
output f;
wire w1, w2;
nand P1 ( w1, a, b ), P2 (w2, c, d);
and P3 ( f, w1, w2);
endmodule
Observatie, asemanator cu c/c++, se pot face declaratii in aceasi linie a mai multor "variabile", aici fire sau instante, cum se poate vedea la w1 si w2 (ambele fiind fire) sau la P1 si P2 (ambele fiind porti nand).