CID aplicatii 3 : Circuite combinationale elementare
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