În acest laborator vom discuta la nivel teoretic despre FPGA-uri și vom încerca o implementare formală a unor circuite simple. Apoi, vom intra un pic mai în detaliu în ceea ce privește simularea codului. La finalul acestui laborator veţi avea cunoștințele minime necesare atât pentru implementarea unui circuit pe FPGA, cât și pentru simularea/depanarea codului.
Un FPGA (Field-Programmable Gate Array) este un circuit integrat care poate fi programat pentru a se comporta ca orice alt circuit digital. Spre deosebire de un procesor, care stochează și execută instrucțiuni, programarea unui FPGA înseamnă reconfigurarea hardware a acestuia pentru a realiza funcționalitatea dorită.
FPGA-urile sunt construite dintr-un număr mare de blocuri logice configurabile (eng. configurable logic block - CLB), identice, interconectate printr-o matrice de fire și switch-uri programabile.
Numărul de celule dintr-un FPGA variază de la model la model, putând ajunge până la câteva sute de mii și chiar milioane. Un exemplu este familia Virtex-7 de la Xilinx care poate conține chiar şi 2 milioane de celule logice.
Pentru mai multe detalii vezi aici.
Implementarea modulelor folosind un FPGA se numește sintetizare. Acesta este procesul de a transforma descrierea high-level (high-level design) a unui modul, ce nu are un corespondent direct în hardware, într-o descriere low-level (gate-level design), ce are un corespondent direct în hardware.
După pasul de sintetizare nu putem totuși programa un FPGA. Ne trebuie un fişier XDC (Xilinx Design Constraints) pentru a asocia pinii fizici ai FPGA-ului la intrările și ieșirile modulului. Structura sa de bază aratǎ în felul următor:
set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { i_w_switch }]; #Intrarea numita "i_w_switch" este rutata la pinul J15 al FPGA set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS33 } [get_ports { o_w_led }]; #Iesirea numita "o_w_led" este rutata la pinul H17 al FPGA
După ce stabilim constrângerile putem implementa design-ul. Odată implementat, pentru a putea încărca acest design pe un FPGA, trebuie să creăm un fișier de programare, ce va avea extensia .bit . Folosind un programator și acest fișier vom încărca design-ul pe un FPGA.
În general în momentul în care apăsăm un buton sau schimbăm un întrerupător semnalul la ieşire nu este unul treaptă (cum ar fi ideal şi ne-ar ajuta foarte mult). De fapt semnalul arată ca în figura următoare.
Din această cauză citirea unui astfel de input poate genera aparenţa mai multor apăsări. Pentru a detecta corect o apăsare trebuie să folosim un circuit (sau o logică) de debouncing. În cea mai simplă formă acesta este un delay: citim semnalul, aşteptăm o perioadă până când acesta s-a stabilizat şi acum luăm în considerare valoarea obţinută. Forma corectă de a proiecta un debouncer este de a ţine minte perioada dintre două schimbări ale semnalului. În cazul în care această perioadă este mai mică decât o valoare de prag considerăm că semnalul nu s-a stabilizat şi valoarea nu este de încredere, altfel valoarea poate fi folosită.
Demo!
Task 0 (3p) Implementați circuitul corespunzător unei unități de procesare a operațiilor logice. Avem:
Utilizați descrierea la nivel procedural și eșantionați datele doar pe fronturile ceasului. Rezultatul va fi legat pe portul o_w_out.
Task 1 (3p) Pentru modulul implementat anterior, definiți un fișier de constrângeri potrivit pentru circuitul modelat.
Task 2 (2p) Implementați un modul debouncer.
Task 3 (2p) Treceți intrările de la Task 1 prin module de debouncer. Simulați circuitul modelat și asigurați-vă că rezultatele sunt cele dorite. Încărcați noul modul pe FPGA, urmărind pașii de aici