Diferență între revizuiri ale paginii „CID aplicatii 3 : Circuite combinationale elementare”
Linia 296: | Linia 296: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/ | + | Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/c/cb/W3_exemplu_decodor.zip W3_exemplu_decodor.zip] |
− | |||
− | |||
==Exercitii== | ==Exercitii== |
Versiunea de la data 12 februarie 2022 17:43
Teorie
Acest laborator are ca scop scrierea unor circuite combinationale simple si explorarea variantelor de sintaxa diferite ce ajung sa ofere aceasi functionalitate. Sintaxa va fi explicata in comentariile exemplului rezolvat de mai jos.
Descrierea circuitelor in Verilog se face in 1 din 2 forme:
- 1) structurala - descrie cum un modul e alcatuit din module sau primitive mai simple
- 2) comportamentala/behavioural - descrie cum se calculeaza iesirile din intrari
Exemplu rezolvat
Decodorul este un circuit a caruie iesire pe n biti are un singur bit de "1", anume cel selectat prin singura sa intrare pe log2(n) biti. Astfel, pentru un decodor cu iesire pe 8b, intrarea va avea 3b (logaritm in baza 2 din 8) si tabelul de adevar va arata astfel:
in(3b) | out(8b) |
000 | 0000_0001 |
001 | 0000_0010 |
010 | 0000_0100 |
011 | 0000_1000 |
100 | 0001_0000 |
101 | 0010_0000 |
110 | 0100_0000 |
111 | 1000_0000 |
Iesirea este numerotata de la bitul 7 (msb) pana la bitul 0 (lsb). Se observa ca daca intrarea are valoare "a", singurul bit de "1" din iesire este cel de pe pozitia "a".
Observatie: Atat in tabelul de mai sus cat si in codul Verilog poate sa apara "_" (underscore) ca sa ajute vizual la separarea/numararea cifrelor. Se foloseste in mod uzual la scrierea in binar ca sa grupeze 4b (o cifra in hexa).
Circuitul exemplu al acestui laborator va fi un decodor mai mic cu 2 biti de intrare si 4 de iesire, avand tabelul de adevar:
in(2b) | out(4b) |
00 | 0001 |
01 | 0010 |
10 | 0100 |
11 | 1000 |
O varianta de schema care implementeaza acest circuit este cea de mai jos:
In circuitul de mai sus, apare dubla negatie, respectivul semnal putand fi luat si direct de la intrare. Acest lucru se intampla din considerente electrice. Desi pe desene nu sunt trecute, fiecare poarta are si fire de alimentare si de masa. Astfel pentru a evita fire de lungimi f mari sau fire care se propaga in multe intrari si probleme de cadere a tensiunilor, se folosesc astfel de buffere/repetoare.
Codul de mai jos exemplifica moduri diferite de a exprima aceasta functionalitate. El contine comentarii referitoare la sintaxa de baza verilog, necesara implementarii modulelor in diverse moduri.
Fisierul decodor_v1.v:
// varianta structurala din porti - cu primitive
module decodor_v1
(
input wire [1:0] in, // definirea intrarilor sub forma de "bus"
// bus-ul de intrare se numeste "in" si este pe 2 biti, in[1], in[0]
// fizic sunt 2 fire distincte, grupate pentru o mai simpla folosire.
output wire [3:0] out // avem o iesire, numita "out" pe 4 biti.
);
wire in_0_inv;
wire in_1_inv;
wire in_0_nat;
wire in_1_nat;
not not_gate_0(in_0_inv,in[0]); // aleg bitul 0 al lui in sa intre in aceasta poarta not;
not not_gate_1(in_1_inv,in[1]); // observatie: cele 2 fire si cele 2 porti putea fi comprimate intr-un bus + poarta not pe 2 biti.
not not_gate_2(in_0_nat,in_0_inv);
not not_gate_3(in_1_nat,in_1_inv);
and and_gate_0(out[0],in_1_inv,in_0_inv);
and and_gate_1(out[1],in_1_inv,in_0_nat);
and and_gate_2(out[2],in_1_nat,in_0_inv);
and and_gate_3(out[3],in_1_nat,in_0_nat);
endmodule
Fisierul decodor_v2.v:
// varianta cu assign, urmand schema
module decodor_v2
(
input wire [1:0] in,
output wire [3:0] out
);
// fiecare bit din iesire calculat independent
assign out[0] = ~in[0] & ~in[1];
assign out[1] = in[0] & ~in[1];
assign out[2] = ~in[0] & in[1];
assign out[3] = in[0] & in[1];
endmodule
Fisierul decodor_v3.v:
// varianta cu assign, cu "?"
module decodor_v3
(
input wire [1:0] in,
output wire [3:0] out
);
// operatorul "?": identic c/c++; (conditie) ? (if_true) : (if_flase);
assign out = (in==0) ? // if in == 0
1 : // if true: out = 1
(in==1) ? // else, verifica alt if.
2 : // if in !=0 si in == 1
(in==2) ?
4 :
8; // last case in==3
endmodule
Fisierul decodor_v4.v:
// varianta cu always cu if
module decodor_v4
(
input wire [1:0] in,
output reg [3:0] out // <<== !!!! important: orice semnal ia valoarea intrun "always", trebuie sa fie reg
); // always => reg ; assign => wire
always @(*) // mereu cand se schimba ce se afla in paranteze, se executa (ish "executa") ce e mai jos
// steluta inseamna "orice"
// se putea pune si always @(in) => mereu cand se schimba "in", aici fiind singura intrare. daca sunt mai multe se poate pune "in0 or in1 ...or inx"
// ce este intre paranteze se numeste lista de senzitivitati
begin // begin si end pe post de acolade din c/c++; delimiteaza if/else si altele asemenea; inclusiv always cu totul
if(in == 0)
begin // nota autorului: eu prefer sa pun begin/end asa. ele se pot pune si dupa conditie sau aliniate altfel.
// ^ preferinta personala legata de coding style, important e sa fiti consecventi in cum scrieti
out = 1;
end
else
begin
if(in == 1)
out = 2; // fiind o singura instructiune, begin/end se poate omite (identic c/c++)
else
begin // nota autorului: eu personal le pun mereu. in caz ca mai adaug linii in if dupa, sa fie deja puse
if(in == 2)
begin
out = 4;
end
else
begin
out = 8;
end
end
end
end//end pt always
endmodule
Fisierul decodor_v5.v:
// varianta cu always cu case
module decodor_v5
(
input wire [1:0] in,
output reg [3:0] out
);
always@(*)
begin
case(in) // functioneaza ca switch/case din c/c++; sintaxa usor diferita
0: // if in == 0
begin
out = 1;
end
2'd1: // if in == 1; 2'd1 se traduce: pe 2 biti, in baza zece (eng: decimal, "d"), valoarea 1
begin // pt "0" de mai sus: daca nu se pune nimic se considera in baza zece.
out = 2;
end
2'b10: // if in == 2; scris in binar "b", 2'b10 => adica valoarea 2(10 binar) pe 2 biti
begin
out = 4;
end
2'h3: // if in == 3; scris in hexa (baza 16 "h", se poate si "x"), fiind numar mic, nu se vede diferenta fata de zecimal.
begin
out = 8;
end
default: // daca in == un caz netrat;
begin // aici inutil pt ca am tratat toate cazurile posibile; pus ca exemplu de sintaxa
out = 0;
end
endcase // orice "case" se inchide cu "endcase"
end
endmodule
Fisierul decodor_v6.v:
// varianta cu assign comportamental
module decodor_v6
(
input wire [1:0] in,
output wire [3:0] out
);
assign out = 1 << in;
// prin shiftare
// 1<<1 == 1 == 4'b0001;
// 1<<2 == 2 == 4'b0010;
// 1<<2 == 4 == 4'b0100;
// 1<<3 == 8 == 4'b1000;
endmodule
Fisierul top.v:
// observatie:
// puteti da in vivado: rtl analysis -> open elaborated design -> schematic pentru a vedea desenul rezultat
// puteti vedea ca moduri diferite de scriere produc aparitia a diferite primitive de sinteza
module top
(
input wire [1:0] in,
output wire [3:0] out_v1, // fiecare varianta de decodor cu iesirea lui
output wire [3:0] out_v2, // toate ar trebui sa scoata acelasi rezultat
output wire [3:0] out_v3,
output wire [3:0] out_v4,
output wire [3:0] out_v5,
output wire [3:0] out_v6
);
decodor_v1 decodor_v1_0
(
.in(in),
.out(out_v1)
);
decodor_v2 decodor_v2_0
(
.in(in),
.out(out_v2)
);
decodor_v3 decodor_v3_0
(
.in(in),
.out(out_v3)
);
decodor_v4 decodor_v4_0
(
.in(in),
.out(out_v4)
);
decodor_v5 decodor_v5_0
(
.in(in),
.out(out_v5)
);
decodor_v6 decodor_v6_0
(
.in(in),
.out(out_v6)
);
endmodule
Proiectul complet se poate descarca de aici: W3_exemplu_decodor.zip
Exercitii
1) Multiplexorul (mux) este un circuit ce are rolul de a selecta una din intrari pentru ca aceasta sa ajunga la iesire.
Imaginati-va o intersectie in care toate masinile vor sa se duca catre aceasi strada. Pentru a evita accidente, este nevoie de un element de control, de selectie, prin care se hotaraste care dintre intrari este lasata sa ajunga la iesire, in respectivul moment de timp. Multiplexorul poate fi de asemenea vazut ca un comutator.
Observatie 1: conceptul de baza al multiplexarii este absolut vital si o sa apara des in multe zone ale ingineriei si la materii f diferite. Ca niste mici exemple, el se foloseste in:
- a) telecomunicatii - cine are voie sa comunice pe un fir la un moment de timp
- b) internet - routere - cine trimite pachetele tcp/ip catre destinatia x
- c) panouri cu leduri - aprindere pe rand a leduri-lor (de la instalatii de craciun pana la ecrane/televizoare care aprind randurile alternativ)
Observatie 2: numarul de intrari maxim posibile este 2^(nr_biti_selectie)
Observatie 3: multiplexoarele pot avea intrarile date si iesirea pe mai multi biti.
Observatie 4: este de ajutor sa incepti sa vedeti circuitele ca avand o cale de date ce sunt procesate si o cale de control care decide cum se face asta. Aici calea de date este alcatuita din intrari si iesire, calea de control din firele de selectie. Acest principiu se propaga si in arhitectura microprocesoarelor unde vor aparea magistrale si memorii de date/instructiuni.
Multiplexorul cu 2 intrari pe un bit fiecare (mux elementar) este desenat in figurile de mai jos: cum este vazut din exterior (prima figura) si alcatuirea lui interna (a doua figura).
2) Implementati un mux4 (multiplexor cu 4 intrari).
Interfata lui este ca in figura de mai jos.
Pentru varianta lui structurala, mux4 se construieste din mai multe mux2 legate corespunzator. Incercati sa construiti voi aceasta schema.
3) Demultiplexor
Demultiplexorul (demux) este un circuit ce are rolul de a selecta catre care iesire se va duce intrarea. Imaginati-va o intersectie in care intra o singura masina si trebuie sa decida daca face stanga/inainte/drept.
Pentru acest exercitiu se va implementa un demux2 (cu 2 iesiri).
Observatie: celelalte iesiri, cele neselectate iau valoarea 0.
Observatie: numarul de iesiri maxim posibile este 2^(nr_biti_selectie)
Interfata demultiplexorului este data mai jos. Incercati sa construiti acest circuit in cat mai multe variante de sintaxa posibile (minim4).
4) Sumator
Sumatorul are rolul de a scoate la iesire suma celor 2 intrari.
Uzual el mai are si intrare si iesire de "carry" adica de transport (cand se depaseste baza de numarare si e nevoie de inca o cifra, ex in zecimal: 88 + 20 = 108; apare o cifra in plus, cifra sutelor).
Observatie: desi scrierea cu assign si + este cea mai usoara si clara pentru un sumator, si celelalte moduri de scriere sunt posibile. La aceasta scriere, daca se doresta ca sumatorul sa aibe carry out, sumatorul se poate face cu un bit mai mare decat intrarile iar acel bit sa fie conectat la carry out. Practic obtii un rezultat pe "n" biti + un bit de depasire, anume msb-ul.
Pentru a vedea pas cu pas cum se construieste un sumator pe 8b puteti parcurge urmatoarea platforma de laborator: Sumator.pdf
5) Comparator
Comparatorul are rolul de a vedea relatia dintre cele 2 intrari ale sale, daca cele 2 numere sunt egale, daca primul e mai mic sau mai mare.
Astfel el poate avea 3 iesiri din care se activeaza doar una:
- - iesire de egalitate
- - iesire de in0 < in1
- - iesire de in0 > in1
Se mai poate face si o varianta de comparator cu 2 iesiri, asa cum veti studia la arhitectura microprocesoarelor.
- - iesire de egalitate
- - iesire care este 0 daca in0<in1 sau 1 daca in0>in1
Aceasta abordare este folosita la AMP la flag-urile din procesoare, folosind operatia de scadere si flag-urile de "zero" si "negativ".
Scrieti modulul de comparator, in ambele variante de interfata, intrarile pot avea oricat de multi biti, pentru acest exercitiu sa fie pe 4b.