CID aplicatii 3 : Circuite combinationale elementare

De la WikiLabs
Jump to navigationJump to search

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:

W3 exemplu rezolvat.png

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).

Mux2 general.png Mux2 schema interna.png


2) Implementati un mux4 (multiplexor cu 4 intrari).

Interfata lui este ca in figura de mai jos.

Mux general.png

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).

Demux2 general.png


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).

Sumator 8b general.png

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.