Noi di solito un circuito lo “disegniamo” , oppure facciamo la tabella di verità o la formula , per descrivere la sua funzionalità. In verità la formula non rappresenta quel determinato circuito , infatti da una formula possiamo ottenere infiniti circuiti che fanno la stessa cosa.

Esiste infatti un linguaggio chiamato Hardware Description Languages ( HDL ) che ci permettono di “descrivere” un circuito attraverso del testo. In questo modo possiamo passare quel testo che descrive il nostro circuito ad un programma che ci permette di simulare il circuito e vedere se fa la roba che vogliamo , invece di costruirlo veramente il circuito e poi buttare tutto se non fa la roba che vogliamo. Inoltre ci permette di fare la sintesi del circuito , ovvero che passando HDL ad un’altro programma ( Yosys ) , questo ci dia la rappresentazione schematica del circuito ( il disegno del circuito ).

I due più diffusi linguaggi di questo tipo ( veri e propri linguaggi di programmazione ) sono :

Verilog

In Verilog funziona tutto in moduli , e ogni blocchetto hardware è un modulo. Le porte elementari sono dei moduli già presenti nel linguaggio , infatti da questi possiamo descrivere circuiti ( moduli ) più complessi.

Consideriamo per esempio questo circuito :

la sua descrizione in Verilog sarebbe questa :

module example (a, b, c, y);
	  input a, b, c;
	  output y;
 
	  wire w1, w2;
 
	  not (w1, b);
	  and (w2, a, w1);
	  xor (y, c, w2);
endmodule

per definire un module si usa la sintassi module <nome-modulo> ( <parametri di input e output del circuito> ) , dopo si deve specificare quali dei parametri passati sono di input o di output con input <parametri input> e output <parametri di output>. Ora siccome l’output di deve andare a finire come input dell’and con , e siccome poi l’output di deve andare a finire come input dello xor con , chiamiamo questi “fili” (wire) che escono da e come e e lo facciamo in questo modo wire w1, w2. Ora per utilizzare le porte elementari di solito la sintassi è <porta>(<parametro di ouput>, <parametri di input>) , infatti per abbiamo not(w1, b). Infine si finisce sempre la descrizione del modulo con endmodule.

Proviamo ora a scrivere un modulo Verilog di questo circuito :

quindi abbiamo :

module circuito(x0,x1,x2,y);
	input x0, x1, x2;
	output y;
 
	wire w0,w1,w2,w3;
 
	not(w0,x1);
	not(w1,x2);
	and(w2,w0,w1,x0);
	and(w3,x1,x0);
	or(y,w3,w2);
 
endmodule

Quindi una volta definito un modulo possiamo richiamarlo per utilizzarlo ( come una funzione nei linguaggi di programmazione ). Prendiamo per esempio come si definisce un circuito Full-Adder con HDL :

module half_adder(a , b , sum , carry);
	input a , b;
	output sum , carry;
 
	 xor(sum,a,b);
	 and(carry, a, b);
endmodule
 
module full_adder( a, b , cin , sum , cout );
	input a , b , cin;
	output sum , cout;
 
	wire s1 , c1 , c2;
	half_adder ha1( .a(cin) , .b(a) , .sum(s1) , .carry(c1) );
	half_adder ha2( .a(s1) , .b(b) , .sum(sum) , .carry(c2) );
	or( cout , c1 , c2 );
endmodule

dove abbiamo utilizzato la notazione .nomeInput(<a cosa è riferito>) , per esempio ad ha1 abbiamo definito .a(cin) , quindi avremo che l’input definito cin prenderà il ruolo di a ( ovvero l’input definito nel modulo half-adder ).

Inoltre possiamo definire le nostre variabili anche come vettori , come descritto qui un MUX 2:1 :

module mux2to1( x , s , y );
	input [1:0]x ; // definizione di un input vettore di due elementi x[0] e x[1]
	input s ;
	output y ;
 
	wire [1:0] a; // definizione di un cavo vettore a di due elementi a[0] e a[1]
	wire n;
 
	not(n , s);
	and(a[0], x[0], n);
	and(a[1], x[1], n);
	or(y , a[0], a[1] );
endmodule

Metodo Behavioral

Fino ad ora abbiamo definito dei circuito in modo structural , ovvero che specifichiamo esattamente le specifiche di ogni porta , possiamo anche descrivere un circuito in modo behavioral , ovvero definiamo la funzione che deve fare il circuito. Per esempio definiamo lo stesso MUX 2:1 ma in modo behavioral :

module mux2to1(x , s , y);
	input [1:0]x ;
	input s;
	output y;
	assign y = s ? x[1] : x[0];
endmodule

dove assign y = s ? x[1] : x[0] ci dice : assegna y = x[1] se s = 1 altrimenti ( else if s = 0 ) assegna y = x[0] - come in Java.