Diferență între revizuiri ale paginii „CID Aplicatii 3”
(Nu s-au afișat 6 versiuni intermediare efectuate de un alt utilizator) | |||
Linia 2: | Linia 2: | ||
==Programarea unui FPGA – Introducere== | ==Programarea unui FPGA – Introducere== | ||
FPGA-ul (Field-programmable gate array) este un circuit programabil, capabil sa implementeze un anumit circuit definit de utilizator si este format dintr-o matrice de blocuri programabile, interconectate intre ele printr-o serie de conexiuni programabile. Aceste blocuri pot implementa atat functii logice, cat si memorii. | FPGA-ul (Field-programmable gate array) este un circuit programabil, capabil sa implementeze un anumit circuit definit de utilizator si este format dintr-o matrice de blocuri programabile, interconectate intre ele printr-o serie de conexiuni programabile. Aceste blocuri pot implementa atat functii logice, cat si memorii. | ||
+ | |||
+ | [[Fișier:fpga1.png|thumb|Schema simplificată a unui FPGA]] | ||
Cand se doreste implementarea unui circuit pe FPGA, acesta urmeaza mai multe etape: sinteza, | Cand se doreste implementarea unui circuit pe FPGA, acesta urmeaza mai multe etape: sinteza, | ||
Linia 12: | Linia 14: | ||
circuit la nivel de blocuri disponibile pe FPGA. Acest pas este dependent de FPGA-ul folosit. | circuit la nivel de blocuri disponibile pe FPGA. Acest pas este dependent de FPGA-ul folosit. | ||
− | [[Fișier: | + | [[Fișier:Fpga2.png|Structura unui bloc generator de functii]] |
+ | |||
+ | |||
+ | [[Fișier:fpga3.png|Nodurile de interconectare]] | ||
Registrul de Tip D sau bistabilul, este un circuit de memorie ce poate memora date la | Registrul de Tip D sau bistabilul, este un circuit de memorie ce poate memora date la | ||
Linia 34: | Linia 39: | ||
a FPGA-ului, configurand astfel dispozitivul. | a FPGA-ului, configurand astfel dispozitivul. | ||
− | [[Fișier:Fpgaimg.PNG| | + | [[Fișier:Fpgaimg.PNG|600px]] |
==Exemplul 1== | ==Exemplul 1== | ||
In exemplul de mai jos, vom descrie comportamental un comparator si il vom testa prin simulare. | In exemplul de mai jos, vom descrie comportamental un comparator si il vom testa prin simulare. | ||
+ | Comparatorul pe care il vom descrie este un circuit cu 2 intrari pe 4 biti si 3 iesiri pe un bit. | ||
+ | |||
+ | [[Fișier:Comparator4b.PNG|400px|]] | ||
+ | |||
+ | |||
+ | '''Implementarea in Verilog a circuitului''' | ||
+ | <syntaxhighlight lang="Verilog"> | ||
+ | module Comparator( | ||
+ | output reg equal, | ||
+ | output reg lower, | ||
+ | output reg greater, | ||
+ | input [3:0] in0, | ||
+ | input [3:0] in1 | ||
+ | ); | ||
− | + | always@(in0 or in1) begin | |
+ | if(in1 > in0) begin | ||
+ | equal = 0; | ||
+ | lower = 0; | ||
+ | greater = 1; | ||
+ | end else if(in1 == in0) begin | ||
+ | equal = 1; | ||
+ | lower = 0; | ||
+ | greater = 0; | ||
+ | end else begin | ||
+ | equal = 0; | ||
+ | lower = 1; | ||
+ | greater = 0; | ||
+ | end | ||
+ | end | ||
− | + | endmodule | |
+ | </syntaxhighlight> | ||
+ | |||
+ | [[Fișier:Tbcomparator4b.PNG|400px]] | ||
+ | |||
+ | |||
+ | '''Implementarea in Verilog a modulului de test''' | ||
+ | <syntaxhighlight lang="Verilog"> | ||
+ | |||
+ | `timescale 1ns/1ps | ||
+ | module Comparator_TB(); | ||
+ | |||
+ | wire equal_t; | ||
+ | wire lower_t; | ||
+ | wire greater_t; | ||
+ | reg [3:0] in0_t; | ||
+ | reg [3:0] in1_t; | ||
+ | |||
+ | initial begin | ||
+ | in0_t = 0; | ||
+ | in1_t = 0; | ||
+ | #1 in0_t = 1; | ||
+ | #1 in1_t = 2; | ||
+ | #1 $stop(); | ||
+ | end | ||
+ | |||
+ | Comparator DUT( | ||
+ | .equal(equal_t), | ||
+ | .lower(lower_t), | ||
+ | .greater(greater_t), | ||
+ | .in0(in0_t), | ||
+ | .in1(in1_t) | ||
+ | ); | ||
+ | |||
+ | endmodule | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''Observatii:''' | ||
+ | * Cele 3 iesiri ale modulului Comparator sunt de tip reg deoarece isi modifica valoarea intr-un bloc de tip '''always'''. | ||
+ | * Fiind vorba de un circuit combinational, blocul de tip always este sensibil la orice modificare a oricarui semnal de intrare. De aceea, punem in lista de sensitivitati ambele semnale de intrare. Mai simplu, pentru a fi siguri ca nu uitam niciun semnal pentru blocurile always mai complexe, putem pune * in lista de sensitivitati '''(always@(*) begin)'''. | ||
+ | *Pentru modulul de test (Comparator_TB), observam ca interfata acestuia este goala (nu exista intrari sau iesiri). | ||
+ | *Pentru fiecare intrare a modulului testat se defineste un semnal de tip '''reg''', iar pentru fiecare iesire se defineste un semnal de tip '''wire'''. Atentie! Dimensiunea acestor semnale trebuie sa corespunda cu dimensiunea semnalelor din interfata modului la care vor fi legate. | ||
+ | *Aici s-a optat pentru un test simplu, generand cate o combinatie pentru fiecare caz (egalitate, mai mic, mai mare), obsevatia facandu-se direct pe forma de unda (fara evaluare automata). | ||
+ | ==Exemplul 2== | ||
+ | In exemplul de mai jos, vom implementa un circuit de adunare pe 4 biti, folosind sumatoare pe 1 bit. | ||
+ | [[Fișier:4bitadder.PNG|800px]] | ||
+ | '''Implementarea in Verilog a Adder1b''' | ||
+ | <syntaxhighlight lang="Verilog"> | ||
+ | module Adder1b( | ||
+ | output sum, | ||
+ | output carry_out, | ||
+ | input in0, | ||
+ | input in1, | ||
+ | input carry_in | ||
+ | ); | ||
+ | assign {carry_out, sum} = in0 + in1 + carry_in; | ||
+ | endmodule | ||
+ | </syntaxhighlight> | ||
+ | '''Observatii''' | ||
+ | * Suma a doua numere pe n biti este un numar pe n+1 biti, cel de-al n+1 -ulea fiind bitul de carry. | ||
+ | * Operatorul {} concateneaza mai multe semnale. Concatenarea {carry_out, sum} va duce la obtinerea unui numar pe 2 biti: bitul cel mai semnificativ (cel mai din stanga), reprezentat de carry_out si bitul cel mai putin semnificativ (cel mai din dreapta), reprezentat de sum. | ||
− | '''Implementarea in Verilog a | + | '''Implementarea in Verilog a Adder4b''' |
<syntaxhighlight lang="Verilog"> | <syntaxhighlight lang="Verilog"> | ||
− | module | + | module Adder4b( |
− | output | + | output carry_out, |
− | output | + | output [3:0] sum, |
− | |||
input [3:0] in0, | input [3:0] in0, | ||
input [3:0] in1 | input [3:0] in1 | ||
); | ); | ||
− | + | ||
− | + | wire c1, c2, c3; | |
− | + | ||
− | + | Adder1b Adder0( | |
− | + | .sum(sum[0]), | |
− | + | .carry_out(c1), | |
− | + | .in0(in0[0]), | |
− | + | .in1(in1[0]), | |
− | + | .carry_in(0) | |
− | + | ); | |
− | + | Adder1b Adder1( | |
− | + | .sum(sum[1]), | |
− | + | .carry_out(c2), | |
+ | .in0(in0[1]), | ||
+ | .in1(in1[1]), | ||
+ | .carry_in(c1) | ||
+ | ); | ||
+ | Adder1b Adder2( | ||
+ | .sum(sum[2]), | ||
+ | .carry_out(c3), | ||
+ | .in0(in0[2]), | ||
+ | .in1(in1[2]), | ||
+ | .carry_in(c2) | ||
+ | ); | ||
+ | Adder1b Adder3( | ||
+ | .sum(sum[3]), | ||
+ | .carry_out(carry_out), | ||
+ | .in0(in0[3]), | ||
+ | .in1(in1[3]), | ||
+ | .carry_in(c3) | ||
+ | ); | ||
+ | |||
+ | endmodule | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''Implementarea in Verilog a modulului de test''' | ||
+ | |||
+ | <syntaxhighlight lang="Verilog"> | ||
+ | module Adder4b_TB(); | ||
+ | |||
+ | wire carry_out_t; | ||
+ | wire [3:0] sum_t; | ||
+ | reg [3:0] in0_t; | ||
+ | reg [3:0] in1_t; | ||
+ | |||
+ | reg flag; | ||
+ | integer idx, jdx; | ||
+ | initial begin | ||
+ | flag = 0; | ||
+ | for(idx = 0; idx < 16; idx = idx + 1) begin | ||
+ | for(jdx = 0; jdx < 16; jdx = jdx + 1) begin | ||
+ | in0_t = idx; | ||
+ | in1_t = jdx; | ||
+ | #1; | ||
+ | if({carry_out_t, sum_t} != (in0_t + in1_t)) | ||
+ | flag = 1; | ||
+ | end | ||
+ | end | ||
+ | |||
+ | if(flag == 0) | ||
+ | $display("TEST_PASS"); | ||
+ | else | ||
+ | $display("TEST_FAIL"); | ||
+ | |||
+ | #2 $stop(); | ||
end | end | ||
− | + | ||
+ | Adder4b DUT( | ||
+ | .carry_out(carry_out_t), | ||
+ | .sum(sum_t), | ||
+ | .in0(in0_t), | ||
+ | .in1(in1_t) | ||
+ | ); | ||
+ | |||
endmodule | endmodule | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | '''Observatii''' | |
+ | *Pentru modulul de test: Pe langa generarea semnalelor de test, observam ca la fiecare pas testam corectitudinea iesirii. | ||
+ | *Consideram un fanion flag, de tip reg deoarece isi va modifica valoarea intr-un bloc initial, pe care il vom initializa cu 0 (prezumtia de nevinovatie – consideram initial circuitul corect). Testam conditia de corectitudine la fiecare pas de generare a semnalelor de test (la fiecare iteratie a buclei for) si daca ea este indeplinita, nimic nu se intampla cu flag, ramanand 0. Daca acesta conditie nu este indeplinita cel putin o data, flag se va face 1, semnalizand ca apare cel putin un caz incorect, afisandu-se totodata in consola cu $display mesajul TEST_FAIL si starea semnalelor in momentul acestuia. La iesirea din bucla testam din nou fanionul si daca acesta este 0, inseamna ca nu a aparut nici macar un caz incorect, afisand in cazul acesta mesajul TEST_PASS. | ||
+ | |||
+ | ==Exercitii suplimentare== | ||
+ | 1. Folosind un multiplexor cu 2 intrari de date, realizati un multiplexor cu 8 intrari de date si o iesire. Stabiliti numarul de intrari de selectie. Realizati acest circuit structural. | ||
+ | |||
+ | 2. Realizati circuitul de la exercitiul anterior, descriindu-l comportamental. | ||
+ | |||
+ | 3. Realizati un modul de test pentru circuitul de mai sus. | ||
+ | |||
+ | 4. Utilizand circuitul half adder, prezentat in secventa de cod de mai jos, realizati un circuit full adder. Circuitul full adder are 3 porturi de intrare si 2 porturi de iesire. Acesta face suma celor 3 intrari si o scoate la iesire. | ||
+ | |||
+ | <syntaxhighlight lang="Verilog"> | ||
+ | module half_adder( | ||
+ | input wire a, | ||
+ | input wire b, | ||
+ | output wire carry, | ||
+ | output wire sum | ||
+ | ); | ||
+ | |||
+ | assign sum = a ^ b; | ||
+ | assign carry = a & b; | ||
+ | |||
+ | endmodule | ||
+ | |||
+ | </syntaxhighlight> |
Versiunea curentă din 15 martie 2021 07:21
Programarea unui FPGA – Introducere
FPGA-ul (Field-programmable gate array) este un circuit programabil, capabil sa implementeze un anumit circuit definit de utilizator si este format dintr-o matrice de blocuri programabile, interconectate intre ele printr-o serie de conexiuni programabile. Aceste blocuri pot implementa atat functii logice, cat si memorii.
Cand se doreste implementarea unui circuit pe FPGA, acesta urmeaza mai multe etape: sinteza, implementarea, generarea fisierului de implementare si programarea.
Sinteza este procesul prin care codul Verilog este transformat intr-un circuit la nivel de porti si registre si este independent de modelul de FPGA folosit.
Implementarea este procesul prin care circuitul obtinut la pasul anterior este transformat in circuit la nivel de blocuri disponibile pe FPGA. Acest pas este dependent de FPGA-ul folosit.
Registrul de Tip D sau bistabilul, este un circuit de memorie ce poate memora date la momentul aparitiei unui eveniment front crescator de ceas (vom discuta despre acesta in detaliu mai tarziu). MUX este un multiplexor si este capabil sa selecteze pentru iesire datele de la bistabil, sau datele de la LUT (Look-Up Table). Acest LUT este o memorie ROM capabila sa stocheze date ce vor fi accesate cu ajutorul intrarilor (intrarile au rol de adresa). LUT-ul este secretul din spatele implementarii functiilor logice. Circuitele implementate pe FPGA nu sunt efectiv porti interconectate intre ele, ci memorii ROM (LUT) care stiu sa memoreze iesirile asociate unor combinatii de intrare.
Asadar, in timpul implementarii, functiile logice sunt transformate in tabele de adevar (pentru fiecare combinatie de intrare se calculeaza iesirea iesirea), care sunt implementate prin LUT-uri.
Circuitul dezvoltat de utilizator trebuie legat apoi la pini fizici ai FPGA-ului, pentru a putea stimula intrarile si a observa starile iesirilor. Aceasta legare la pinii fizici este posibila, deoarece, asa cum am mentionat si mai sus, conexiunile in interiorul FPGA-ului sunt programabile. Pe placa, avem comutatoare si butoane pentru a controla starea intrarilor si LED-uri si afisaje pentru a observa starea iesirilor.
In figura de mai jos este prezentata o schema simplificata a conectarii modulului implementat pe FPGA la pinii fizici. Fiecare pin fizic este codat in tool-ul de sinteza cu un anumit cod. Mentionand in fisierul de constrangeri in dreptul fiecarui bit al fiecarei intrari si iesiri codul aferent pinului fizic, tool-ul va programa conexiunile programabile astfel incat portul modulului sa fie conectat la pinul fizic ales. Mai departe, pe placa de dezvoltare ce contine si FPGA-ul, pinii fizici sunt conectati prin trasee metalice la dispozitivele de control si observatie.
Folosind toate aceste informatii (implementarea circuitului si modul de conectare la porturile fizice ale FPGA-ului) se va genera fisierul de implementare, ce va fi apoi descarcat in memoria SRAM a FPGA-ului, configurand astfel dispozitivul.
Exemplul 1
In exemplul de mai jos, vom descrie comportamental un comparator si il vom testa prin simulare. Comparatorul pe care il vom descrie este un circuit cu 2 intrari pe 4 biti si 3 iesiri pe un bit.
Implementarea in Verilog a circuitului
module Comparator(
output reg equal,
output reg lower,
output reg greater,
input [3:0] in0,
input [3:0] in1
);
always@(in0 or in1) begin
if(in1 > in0) begin
equal = 0;
lower = 0;
greater = 1;
end else if(in1 == in0) begin
equal = 1;
lower = 0;
greater = 0;
end else begin
equal = 0;
lower = 1;
greater = 0;
end
end
endmodule
Implementarea in Verilog a modulului de test
`timescale 1ns/1ps
module Comparator_TB();
wire equal_t;
wire lower_t;
wire greater_t;
reg [3:0] in0_t;
reg [3:0] in1_t;
initial begin
in0_t = 0;
in1_t = 0;
#1 in0_t = 1;
#1 in1_t = 2;
#1 $stop();
end
Comparator DUT(
.equal(equal_t),
.lower(lower_t),
.greater(greater_t),
.in0(in0_t),
.in1(in1_t)
);
endmodule
Observatii:
- Cele 3 iesiri ale modulului Comparator sunt de tip reg deoarece isi modifica valoarea intr-un bloc de tip always.
- Fiind vorba de un circuit combinational, blocul de tip always este sensibil la orice modificare a oricarui semnal de intrare. De aceea, punem in lista de sensitivitati ambele semnale de intrare. Mai simplu, pentru a fi siguri ca nu uitam niciun semnal pentru blocurile always mai complexe, putem pune * in lista de sensitivitati (always@(*) begin).
- Pentru modulul de test (Comparator_TB), observam ca interfata acestuia este goala (nu exista intrari sau iesiri).
- Pentru fiecare intrare a modulului testat se defineste un semnal de tip reg, iar pentru fiecare iesire se defineste un semnal de tip wire. Atentie! Dimensiunea acestor semnale trebuie sa corespunda cu dimensiunea semnalelor din interfata modului la care vor fi legate.
- Aici s-a optat pentru un test simplu, generand cate o combinatie pentru fiecare caz (egalitate, mai mic, mai mare), obsevatia facandu-se direct pe forma de unda (fara evaluare automata).
Exemplul 2
In exemplul de mai jos, vom implementa un circuit de adunare pe 4 biti, folosind sumatoare pe 1 bit.
Implementarea in Verilog a Adder1b
module Adder1b(
output sum,
output carry_out,
input in0,
input in1,
input carry_in
);
assign {carry_out, sum} = in0 + in1 + carry_in;
endmodule
Observatii
- Suma a doua numere pe n biti este un numar pe n+1 biti, cel de-al n+1 -ulea fiind bitul de carry.
- Operatorul {} concateneaza mai multe semnale. Concatenarea {carry_out, sum} va duce la obtinerea unui numar pe 2 biti: bitul cel mai semnificativ (cel mai din stanga), reprezentat de carry_out si bitul cel mai putin semnificativ (cel mai din dreapta), reprezentat de sum.
Implementarea in Verilog a Adder4b
module Adder4b(
output carry_out,
output [3:0] sum,
input [3:0] in0,
input [3:0] in1
);
wire c1, c2, c3;
Adder1b Adder0(
.sum(sum[0]),
.carry_out(c1),
.in0(in0[0]),
.in1(in1[0]),
.carry_in(0)
);
Adder1b Adder1(
.sum(sum[1]),
.carry_out(c2),
.in0(in0[1]),
.in1(in1[1]),
.carry_in(c1)
);
Adder1b Adder2(
.sum(sum[2]),
.carry_out(c3),
.in0(in0[2]),
.in1(in1[2]),
.carry_in(c2)
);
Adder1b Adder3(
.sum(sum[3]),
.carry_out(carry_out),
.in0(in0[3]),
.in1(in1[3]),
.carry_in(c3)
);
endmodule
Implementarea in Verilog a modulului de test
module Adder4b_TB();
wire carry_out_t;
wire [3:0] sum_t;
reg [3:0] in0_t;
reg [3:0] in1_t;
reg flag;
integer idx, jdx;
initial begin
flag = 0;
for(idx = 0; idx < 16; idx = idx + 1) begin
for(jdx = 0; jdx < 16; jdx = jdx + 1) begin
in0_t = idx;
in1_t = jdx;
#1;
if({carry_out_t, sum_t} != (in0_t + in1_t))
flag = 1;
end
end
if(flag == 0)
$display("TEST_PASS");
else
$display("TEST_FAIL");
#2 $stop();
end
Adder4b DUT(
.carry_out(carry_out_t),
.sum(sum_t),
.in0(in0_t),
.in1(in1_t)
);
endmodule
Observatii
- Pentru modulul de test: Pe langa generarea semnalelor de test, observam ca la fiecare pas testam corectitudinea iesirii.
- Consideram un fanion flag, de tip reg deoarece isi va modifica valoarea intr-un bloc initial, pe care il vom initializa cu 0 (prezumtia de nevinovatie – consideram initial circuitul corect). Testam conditia de corectitudine la fiecare pas de generare a semnalelor de test (la fiecare iteratie a buclei for) si daca ea este indeplinita, nimic nu se intampla cu flag, ramanand 0. Daca acesta conditie nu este indeplinita cel putin o data, flag se va face 1, semnalizand ca apare cel putin un caz incorect, afisandu-se totodata in consola cu $display mesajul TEST_FAIL si starea semnalelor in momentul acestuia. La iesirea din bucla testam din nou fanionul si daca acesta este 0, inseamna ca nu a aparut nici macar un caz incorect, afisand in cazul acesta mesajul TEST_PASS.
Exercitii suplimentare
1. Folosind un multiplexor cu 2 intrari de date, realizati un multiplexor cu 8 intrari de date si o iesire. Stabiliti numarul de intrari de selectie. Realizati acest circuit structural.
2. Realizati circuitul de la exercitiul anterior, descriindu-l comportamental.
3. Realizati un modul de test pentru circuitul de mai sus.
4. Utilizand circuitul half adder, prezentat in secventa de cod de mai jos, realizati un circuit full adder. Circuitul full adder are 3 porturi de intrare si 2 porturi de iesire. Acesta face suma celor 3 intrari si o scoate la iesire.
module half_adder(
input wire a,
input wire b,
output wire carry,
output wire sum
);
assign sum = a ^ b;
assign carry = a & b;
endmodule