https://wiki.dcae.pub.ro/api.php?action=feedcontributions&user=Mihai.antonescu&feedformat=atomWikiLabs - Contribuții utilizator [ro]2024-03-28T19:53:33ZContribuții utilizatorMediaWiki 1.31.1https://wiki.dcae.pub.ro/index.php?title=SDA_Lucrarea_1&diff=7533SDA Lucrarea 12023-10-03T20:41:09Z<p>Mihai.antonescu: /* Probleme propuse */</p>
<hr />
<div>După acest laborator veți putea folosi IDE-ul Netbeans pentru a scrie și depana programe în C. Totodată veți relua și recapitula noțiunile legate de pointeri în C.<br />
<br />
= Utilizarea IDE-ului Netbeans =<br />
<br />
[https://netbeans.org/ Netbeans] este un mediu integrat de dezvoltare (IDE) open-source și gratuit care permite dezvoltarea de programe în limbajele Java, C, C++, Javascript, HTML, PHP și Groovy. Netbeans nu instalează și compilatoare pentru Java și C/C++, acestea trebuie instalate manual, în prealabil. <br />
<br />
<div class="regula"><span style="color: red; font-weight: bold">Atenție:</span> Imaginea de Linux folosită la Programarea Calculatoarelor are deja instalat compilatorul de C.</div><br />
<br />
== Instalarea GCC ==<br />
<br />
=== Instalarea GCC în Linux ===<br />
<br />
Pentru instalarea compilatorului de C (GCC) în distribuțiile de Linux provenite din Ubuntu (Ubuntu, Kubuntu, Xubuntu, Mint, LMDE, etc.) se poate folosi comanda:<br />
<br />
apt-get install -y build-essential<br />
<br />
=== Instalarea GCC în Windows ===<br />
<br />
Pentru compilarea de programe cu GCC în Windows, aveți nevoie de instalarea acestui compilator care poate fi făcută prin instalarea unuia din următoarele două suite de programe:<br />
* [https://cygwin.com Cygwin] - Tutorial de instalare [https://cygwin.com/install.html aici].<br />
* [http://www.mingw.org/ MinGW] - Tutorial de instalare [http://www.mingw.org/wiki/InstallationHOWTOforMinGW aici].<br />
<br />
<div class="regula"><span style="color: red; font-weight: bold">Atenție:</span> Nu uitați ca la instalare să bifați în lista de pachete și compilatorul de C (<code>gcc</code>), <code>make</code> și <code>git</code>.</div><br />
<br />
== Instalarea Netbeans ==<br />
<br />
De la adresa https://netbeans.org/downloads/ puteți descărca kitul de instalare pentru sistemul vostru de operare (imaginea de Virtualbox cu Linux folosită la Programarea Calculatoarelor ruelază un Linux pe 32 de biți). Descărcați una din variantele care suporta C/C++ ('''C/C++''' sau '''All''').<br />
<br />
=== Instalare Netbeans în Linux ===<br />
<br />
Fișierul descărcat pentru Linux va avea extensia '''.sh'''. Pentru a instala programul, deschideți un terminal și rulați următoarele comenzi:<br />
<br />
<syntaxhighlight lang="bash"><br />
cd ~/Downloads # navigare catre directorul unde a fost salvat fisierul descarcat<br />
chmod +x *.sh # activarea fanionului 'executabil' pentru toate fisierele cu extensia .sh<br />
sudo ./netbeans-8.1-cpp-linux-x86.sh # folositi numele corespunzator al fisierului descarcat<br />
</syntaxhighlight><br />
<br />
Mai departe urmați pașii din programul de instalare.<br />
<br />
=== Instalare Netbeans în Windows ===<br />
<br />
Fișierul descărcat va avea extensia '''.exe'''. Rulați-l și urmăriți instrucțiunile. Veți fi întrebați unde este compilatorul de C/C++. Executabilele pentru acestea sunt în directorul unde ați instalat MinGW sau Cygwin, în subdirectorul <code>/usr/bin</code>.<br />
<br />
== Realizarea unui proiect ==<br />
<br />
Aplicațiile în Netbeans sunt structurate în '''Proiecte'''. O aplicație este un proiect. Nu este suficient să scrieți un fișier sursă C și să-l rulați. Netbeans trebuie să creeze fișiere '''Makefile''' și să pregătească mediul pentru compilare și execuție precum și să fie capabil să vă ofere informații legate de corectitudinea codului și să vă ofere sugestii de îmbunătățire. Pentru a crea un proiect nou, se folosește meniul <code>File - New Project... </code> sau combinația <code>Ctrl-Shift-n</code>:<br />
<br />
[[Fișier:netbeans_new_project.png|1000px|Proiect nou]]<br />
<br />
Mai departe selectați categoria '''C/C++''' și la '''Project''' selectați '''C/C++ Application'''. Va apărea următoarea fereastră:<br />
<br />
[[Fișier:netbeans_new_application.png|Aplicație nouă]]<br />
<br />
Completați următoarele informații:<br />
* '''Project Name''' - numele proiectului/ al aplicației voastre; va fi numele directorului creat de Netbeans pentru proiect;<br />
* '''Project Location''' - directorul unde va fi salvat directorul proiectului de către Netbeans;<br />
* '''Project Folder''' - calea completă până la directorul proiectului; este obținut din concatenarea celor două de mai sus; nu poate fi modificat;<br />
* '''Project Makefile Name''' - numele fișierului Makefile care va fi creat de către Netbeans pentru proiectul vostru;<br />
* '''Create Main File''' - se bifează dacă se dorește crearea unui fișier sursă care să conțină funcția '''main''' și se completează numele acestui fișier (fără extensie); în căsuța de selecție din dreapta se alege limabjul (C sau C++) și standardul folosit pentru compilarea codului și generarea fișierului main; <br />
* '''Build Host''' - numele sau adresa IP a calculatorului unde se realizează compilarea ('''localhost''' este calculatorul pe care lucrați);<br />
* '''Tool Collection''' - dacă există mai multe compilatoare instalate, de aici se alege care compilator este utilizat pentru proiect.<br />
<br />
După ce dați click pe '''Finish''', proiectul se va deschide și fereastra se va schimba în modul de editare.<br />
<br />
== Componentele IDE-ului Netbeans ==<br />
<br />
[[Fișier:netbeans_env.png|1000px|Netbeans]]<br />
<br />
Zonele din imagine sunt:<br />
<br />
# Zona <span style="color: gold">gabenă</span> - bara de instrumente - conține butoane folosite pentru acces rapid la funcționalitatea cea mai utilizată (compilare, rulare, etc.) și conține:<br />
#* selecția tipului de compilare ('''Debug''' sau '''Release''') are efect asupra nivelului de optimizare comandat compilatorului și selectează sau nu facă simbolurile de debug sunt adăugate executabilului; pe scurt, nu se poate face debug pe executabilul de Release, dar executabilul de Debug este mai lent decât cel de Release;<br />
#* testare în browser, este accesibil doar pentru aplicații web (HTML, PHP, Javascript, etc.), nu și pentru aplicații C/C++;<br />
#* '''Build Project''' - se apelează '''make all''' pentru compilarea programului;<br />
#* '''Clean and Build Project''' - se apelează '''make clean all''' pentru ștergerea binarelor și apoi recompilarea programului;<br />
#* '''Run Project''' - se rulează executabilul generat de build (dacă acesta nu există sau nu este actualizat, se rulează automat '''Build Project''' înainte);<br />
#* '''Debug Project''' - se rulează executabilul generat de build în modul de debug (cu ajutorul GDB, vezi [[PC Laborator 10#Tool-ul de depanare GDB]]), permițând plasarea de breakpoints, execuție pas cu pas, vizualizarea valorilor variabilelor, etc.<br />
#* '''Profile Project''' - indisponibil în C/C++, permite vizualizarea resurselor consumate și ajută la optimizarea programului.<br />
# Zona <span style="color: blue">albastră</span> - conține 4 tab-uri din care importante pentru proiectele în C sunt primele două: '''Projects''' și '''Files'''.<br />
#* '''Projects''' - arată proiectele deschise și tipul lor; în cazul de față este un singur proiect deschis - '''TestProjectHelloWorld'''; sub el se află 5 categorii în care sunt plasate fișierele care fac parte din proiect:<br />
#** ''Header Files'' - fișierele header (cu extensia .h);<br />
#** ''Resource Files'' - diferite fișiere cu resurse folosite de aplicație (fișiere de configurare, xml, imagini, etc.);<br />
#** ''Source Files'' - fișiere sursă C (cu extensia .c);<br />
#** ''Test Files'' - fișiere de test pentru funcțiile din fișierele sursă;<br />
#** ''Important Files'' - fișiere importante pentru proiect (cum ar fi fișierul Makefile).<br />
#* '''Files''' - se poate vedea directorul proiectului cu fișierele și subdirectoarele sale; în directorul proiectului se realizează implicit următoarele subdirectoare:<br />
#** ''build'' - conține fișierele obiect pentru fiecare din surse, pentru fiecare tip de compilare ('''Debug''' sau '''Release''');<br />
#** ''dist'' - conține fișierele executabile pentru fiecare tip de compilare ('''Debug''' sau '''Release''');<br />
#** ''nbproject'' - conține fișiere de configurare pentru proiect.<br />
# Zona <span style="color: green">verde</span> - conține câte un tab pentru fiecare fișier deschis pentru compilare; implicit bara verde se află deasupra ferestrei de editare (portocaliu) dar poate fi mutată.<br />
# Zona <span style="color: orange">portocalie</span> - fereastra de editare a codului ce conține pe bara de sus butoane pentru navigarea rapidă prin fișierele deschise și / sau modificate recent; de remarcat aici este butonul '''History''' care, dacă fișierul este salvat într-un sistem de ''revision control'' (cum ar fi Git), permite vizualizarea istoriei modificărilor fișierului.<br />
# Zona <span style="color: hotpink">roz</span> - listează elementele definite în fișierul deschis în zona portocalie; dacă fișierul este un header sau un fișier sursă C, aici se vor vedea tipurile de date definite, variabilele globale, funcțiile cu argumentele lor, macrourile de preprocesor, etc.; dacă în schimb este deschis un Makefile, se vor vedea toate rețetele definite în Makefile-ul respectiv;<br />
# Zona <span style="color: red">roșie</span> - zona de ''Output'' reprezintă consola unde se afișează rezultatele compilării cât și ieșirea generată de program în timpul execuției, pe ambele stream-uri (stdout și stderr).<br />
<br />
== Exemplu de program ==<br />
<br />
Copiați codul de mai jos înlocuind programul deja existent în fișierul ''main.c'':<br />
<syntaxhighlight lang="C"><br />
#include <stdio.h><br />
<br />
int main(){<br />
printf("Hello Netbeans!\n");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
Dați apoi click pe '''Run Project''' (simbolul verde ''Play'') sau tastați <code>Ctrl+F11</code>:<br />
<br />
[[Fișier:netbeans_run.png|1000px|După execuție]]<br />
<br />
Observați consola din partea de jos a ecranului unde se afișează mesajul "Hello Netbeans" și apoi se semnalează sfârșitul programului raportându-se timpul de execuție. <br />
<br />
== Exemplu de program în modul ''debug'' ==<br />
<br />
Copiați codul de mai jos înlocuind programul deja existent în fișierul ''main.c'':<br />
<syntaxhighlight lang="C" line><br />
#include <stdio.h><br />
<br />
int main(){<br />
printf("Debugging program...\n");<br />
int value = 0;<br />
value = value + 1;<br />
value++;<br />
value = value * 2;<br />
value -= 1;<br />
printf("Value is now %d!\n", value);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
Dați click pe numărul liniei 5 (în dreptul definiției variabilei <code>value</code>) și ar trebui ca în loc de numărul liniei să vă apară un pătrat roșu. Acela semnalează un ''breakpoint'' la acea linie. Dați apoi click pe '''Debug Project''' sau tastați <code>F11</code>. Acesta va programul în modul de ''debug'' și astfel se va opri la primul ''breakpoint'':<br />
<br />
[[Fișier:netbeans_debug.png|1000px|După execuție]]<br />
<br />
În interfață au apărut două zone noi:<br />
# Zona <span style="color: red">roșie</span> - instrumente de control al execuției în modul de debug; în ordine, acestea sunt:<br />
#* '''Finish Debugger Session''' - execuția se încheie și interfața grafică revine la forma anterioară;<br />
#* '''Restart''' - execuția se reia de la începutul programului, oprindu-se la primul ''breakpoint'';<br />
#* '''Pause''' - dacă programul nu este oprit (într-un ''breakpoint''), atunci accesta poate fi întrerupt facând click pe acest buton;<br />
#* '''Continue''' - dacă programul este oprit (într-un ''breakpoint''), atunci el poate fi rulat mai reparte până la următorul ''breakpoint'' cu ajutorul acestui buton;<br />
#* '''Step Over''' - programul va executa linia curentă (inclusiv orice apel de funcție ar fi pe linia curentă) și trece la linia următoare unde se oprește;<br />
#* '''Step Into''' - programul va executa linia curentă iar dacă pe linia curentă există un apel de funcție, se va opri pe prima linie din funcția respectivă;<br />
#* '''Step Out''' - programul va termina de executat funcția curentă și se va opri pe prima linie după linia unde a fost apelată funcția respectivă;<br />
#* '''Run to Cursor''' - programul va executa toate liniile și se va opri pe linia unde este plasat cursorul; dacă acesta nu este plasat în calea execuției programului, acesta se va rula până la final;<br />
#* '''Step Into Last or Selected Function Called From Current Line''' - similar cu '''Step Into''' doar că dacă există mai multe apeluri de funcții pe linia curentă, această opțiune permite selectarea celei în care se va opri programul.<br />
<br />
# Zona <span style="color: blue">albastră</span> - vizualizarea stării execuției programului; aveți la dispoziție trei taburi noi:<br />
#* '''Variables''' - aici se pot vedea valorile curente ale tuturor variabilelor valide în ''scope''-ul curent al programului; inclusiv aici se pot adăuga ''watches'' care permit vizualizarea valorilor calculate din expresii complexe scrise în limbajul C;<br />
#* '''Call Stack''' - aici se poate vedea în orice moment stiva de execuție a programului;<br />
#* '''Breakpoints''' - aici se poate vedea lista de ''breakpoints'' din programul curent.<br />
<br />
Pentru a exersa utilizarea sistemului de debug, realizați următoarele operații:<br />
# Parcurgeti tot programul folosind '''Step Over''' și vizualizând valoarea variabilei <code>value</code> la fiecare pas, până la ultima linie din program, unde utilizați '''Continue'''.<br />
# Reporniți aplicația în modul de debug și puneți un al doilea ''breakpoint'' pe linia cu apelul funcției <code>printf</code>. <br />
# Folosiți '''Continue''' pentru a ajunge la al doilea ''breakpoint''.<br />
# Odată ajunși acolo, folosiți '''Step In''' pentru a intra în funcția <code>printf</code> (neavând codul sursă, veți vedea codul de asamblare obținut din dezasamblarea fișierului obiect de către GDB).<br />
# Folosiți '''Step Out''' pentru a reveni în funcția <code>main</code> și apoi '''Continue''' pentru a termina execuția.<br />
# Reporniți aplicația în modul de debug și realizați un ''watch'' cu valoarea lui <code>value + 1</code>. Vizualizați pas cu pas execuția programului.<br />
<br />
== Tips & Tricks ==<br />
<br />
=== Suport pentru instrumente de ''Revision Control'' ===<br />
<br />
Netbeans oferă suport grafic pentru cele mai utilizate intrumente de ''revision control'': SVN, Git, Mercurial, etc. Pentru a crea un repository nou de Git pentru proiectul curent din interfața grafică, puteți da click dreapta pe numele proiectului, apoi '''Versioning -> Initialize Git repository...''':<br />
<br />
[[Fișier:netbeans_git_init.png|1000px|Git Init]]<br />
<br />
După operațiunea de init, meniul '''Versioning''' se va schimba în '''Git''', de unde se pot realiza toate operațiile cunoscute din Git: add, remove, commit, push, pull, merge, rebase, diff, etc.<br />
<br />
=== Formatare automată a codului ===<br />
<br />
Netbeans oferă opțiunea de a formata automat codul prin selectarea '''Source -> Format'''. <br />
<br />
Ce probleme rezolvă această formatare automată:<br />
* alinierea liniilor în funcție de blocurile de instrucțiuni și tipurile de ''statements'' de pe fiecare linie;<br />
* plasarea sau eliminarea de spații acolo unde este necasar.<br />
Ce probleme NU rezolvă această formatare automată;<br />
* lipsa acoladelor de la blocurile '''if''' și '''for''';<br />
* numele insuficient de sugestive pentru numele de funcții, structuri sau variabile.<br />
<br />
Nu uitați de regulile următoare: [[Convenții de cod - C]]<br />
<br />
<!--<br />
= Exerciții =<br />
<br />
*Săptămâna 1<br />
*# Realizați un proiect nou în Netbeans și scrieți un program care să citească de la tastatură un număr întreg fără semn <code>size</code>. Se va aloca apoi dinamic în HEAP un vector de <code>size</code> valori numerice întregi pe 16 biți. Scrieți o funcție care să citească <code>size</code> valori de la tastatură și să le plaseze în vector. Scrieți apoi o funcție care să găsească valoarea maximă din vector. Apelați în <code>main</code> funcțiile de mai sus și afișați valoarea obținută pe ecran. Nu uitați să dezalocați memoria alocată. Pentru funcțiile de alocare și dezalocare de memorie, puteți recapitula [[PC Laborator 12]].<br />
*# Modificați programul de mai sus astfel încât citirea variabilei <code>size</code> cât și alocarea de memorie să fie făcută într-o funcție separată. Această funcție trebuie să întoarcă un pointer cu adresa de început a vectorului dar și dimensiunea acestuia.<br />
*# Realizați un alt proiect în Netbeans și scrieți un program care să citească de la tastatură un șir de caractere de lungime maximă 255. Folosind aritmetica pointerilor și o singură buclă '''for''', afișați pe ecran doar cifrele din șirul citit.<br />
* Săptămâna 2<br />
*# Realizați un proiect nou în Netbeans și scrieți un program care să citească de la tastatură un număr întreg fără semn <code>size</code>. Scrieți apoi o funcție care să aloce memorie pentru un șir de caractere care să conțină de <code>size</code> ori secvența "ha" (spre exemplu, pentru size == 3, string-ul va trebui să conțină "hahaha"), să umple șirul cu numărul cerut de "ha"-uri și să întoarcă adresa memoriei alocate. În funcția <code>main</code> afișați șirul de caractere (nu uitați la alocare de terminatorul de sfârșit de șir). Nu uitați să dezalocați memoria. Pentru funcțiile de alocare și dezalocare de memorie, puteți recapitula [[PC Laborator 12]].<br />
*# Modificați programul de mai sus astfel încât în loc de secvența "ha", secvența repetată să fie citită de la tastatură cu <code>scanf</code> și trimisă ca argument funcției de alocare. <br />
*# Realizați un alt proiect în Netbeans și scrieți un program care să citească de la tastatură un șir de caractere de lungime maximă 255. Scrieți o singură funcție care să numere și să întoarcă numărul de litere și numărul de cifre din șirul citit. Aceste valori se vor afișa pe ecran în funcția <code>main</code>.<br />
--><br />
<br />
<br />
= Probleme propuse =<br />
<br />
Problemele de mai jos au anumite erori care trebuie gasite si raparate folosind debugger-ul.<br />
<br />
<br />
== Problema 1 ==<br />
Programul de mai jos ar trebui să citească de la tastatură un șir de maxim 1024 caractere și să afișeze câte litere mici, câte litere mari și câte cifre conține acest șir. Totuși acest program are o eroare. Folosiți debugger-ul pentru a o identifica și repara.<br />
<br />
Exemplu de intrare:<br />
<br />
Ana a plecat la piata si a cumparat 10 saci de cartofi.<br />
<br />
Ieșirea pentru intrarea de mai sus:<br />
<br />
40 1 2<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <stdio.h><br />
<br />
int main() {<br />
char c;<br />
unsigned uppercaseChars = 0;<br />
unsigned lowecaseChars = 0; <br />
unsigned digits = 0;<br />
<br />
while(scanf("%c", c) != EOF) {<br />
if(c > 'a' || c < 'z') {<br />
lowecaseChars++;<br />
} else if(c > 'A' && c < 'Z') {<br />
uppercaseChars++;<br />
} else {<br />
digits++;<br />
}<br />
}<br />
printf("%u %u %u", lowecaseChars, uppercaseChars, digits);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
== Problema 2 ==<br />
Un tehnician a măsurat un număr mare n de rezistori şi a obţinut n valori de rezistenţe. Tehnicianul ştie că rezistorii sunt de acelaşi fel, dar pentru că sunt vechi şi codul culorilor nu mai este vizibil, doreşte să calculeze valoarea nominală a rezistenţei (R) şi dispersia valorilor, (S). Apoi, având aceste valori, el vrea să determine câte din rezistenţele testate (procentual) se încadrează în intervalul [R – S; R + S]. Rezistenţa nominală se calculează ca media aritmetică a valorilor rezistenţelor, iar formula dispersiei este dată mai jos.<br />
<br />
Cerinţă<br />
Dându-se un număr n de rezistori şi valorile rezistenţelor acestora Ri (i = 1, ..., n), să se determine procentul rezistoarelor care au rezistenţa în intervalul [R– S; R + S].<br />
<br />
Date de intrare<br />
Pe prima linie se află numărul întreg n. Pe următoarea linie, separate printr-un spaţiu, sunt n valori fracţionare de rezistenţe (în ohmi).<br />
<br />
Date de ieşire<br />
Se va afişa o singură valoare fracţionară, cu exact două zecimale reprezentând procentul de rezistori cu rezistenţa în intervalul [R – S; R + S].<br />
<br />
Restricţii şi precizări:<br />
<br />
1 < n < 1000<br />
<br />
1.0 <= Ri <= 10000.0 <br />
<br />
S=sqrt( pow(sum(R[i]−R),2) / n )<br />
<br />
Exemplu:<br />
<br />
Input =<br />
10<br />
<br />
76 1 20 37 19 92 61 96 37 77<br />
<br />
Output = <br />
50.00<br />
<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <vector><br />
#include <string><br />
#include <unordered_map><br />
#include <algorithm><br />
#include <cstdio><br />
<br />
<br />
int main() {<br />
int n;<br />
double values[n];<br />
scanf("%d", &n);<br />
int sum = 0;<br />
for(int i = 0; i < n; i++){<br />
scanf("%lf", &values[i]);<br />
sum += values[i];<br />
}<br />
double average = sum / n;<br />
double dispersion = 0;<br />
for(int i = 0; i < n; i++){<br />
dispersion += (values[i] - average) * (values[i] - average);<br />
}<br />
dispersion = sqrt(dispersion / n);<br />
int larger = 0;<br />
for(int i = 0; i < n; i++) {<br />
if(values[i] >= average - dispersion <br />
&& values[i] <= average + dispersion) larger++;<br />
}<br />
printf("%.2lf", 100 * larger / n);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
<br />
== Problema 3 ==<br />
Sunteți angajați de ministerul învățamântului să scrieți un program care să listeze toți elevii care au promovat examenul de bacalaureat. De la tastatură se introduce un număr n reprezentând numărul de candidați, apoi pe următoarele n linii, un nume de elev (format din exact două cuvinte: nume și prenume) și notele la cele patru materii, format fracționar, valori între 1 și 10. Să se afișeze toți studenții care au promovat examenul de bacalaureat (media mai mare ca 6, fiecare notă mai mare ca 5) în ordinea inversă a introducerii lor, împreună cu media de promovare, afișată cu fix două zecimale.<br />
<br />
Exemplu de date de intrare:<br />
<br />
4<br />
<br />
Gheorghe Ghita 8 7 5.5 10<br />
<br />
Vuia Vasile 4 10 10 10<br />
<br />
Andreescu Andra 9 10 9 10<br />
<br />
Elenescu Elena 5 5 5 5<br />
<br />
Ieșirea pentru intrarea de mai sus:<br />
<br />
Andreescu Andra 9.50<br />
<br />
Gheorghe Ghita 7.63<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <stdio.h><br />
struct studenti{<br />
char n[128];<br />
char m[128];<br />
float a,b,c,d;<br />
};<br />
int main(){<br />
int n,i;<br />
struct studenti s[n];<br />
scanf("%d", &n);<br />
for(i=0;i<n;i++)<br />
scanf("%s%s%f%f%f%f",&s[i].n,&s[i].m,&s[i].a,&s[i].b,&s[i].c,&s[i].d);<br />
<br />
for(i=n;i>0;i--){<br />
float m=(s[i].a+s[i].b+s[i].c+s[i].d)/4;<br />
if(s[i].a>5&&s[i].b>5&&s[i].c>5&&s[i].d>5||m>=6){<br />
printf("%s %s %.2f\n",s[i].n,s[i].m,m);<br />
}<br />
}<br />
return 0;<br />
}<br />
</syntaxhighlight></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=SDA_Lucrarea_1&diff=7532SDA Lucrarea 12023-10-03T18:18:13Z<p>Mihai.antonescu: </p>
<hr />
<div>După acest laborator veți putea folosi IDE-ul Netbeans pentru a scrie și depana programe în C. Totodată veți relua și recapitula noțiunile legate de pointeri în C.<br />
<br />
= Utilizarea IDE-ului Netbeans =<br />
<br />
[https://netbeans.org/ Netbeans] este un mediu integrat de dezvoltare (IDE) open-source și gratuit care permite dezvoltarea de programe în limbajele Java, C, C++, Javascript, HTML, PHP și Groovy. Netbeans nu instalează și compilatoare pentru Java și C/C++, acestea trebuie instalate manual, în prealabil. <br />
<br />
<div class="regula"><span style="color: red; font-weight: bold">Atenție:</span> Imaginea de Linux folosită la Programarea Calculatoarelor are deja instalat compilatorul de C.</div><br />
<br />
== Instalarea GCC ==<br />
<br />
=== Instalarea GCC în Linux ===<br />
<br />
Pentru instalarea compilatorului de C (GCC) în distribuțiile de Linux provenite din Ubuntu (Ubuntu, Kubuntu, Xubuntu, Mint, LMDE, etc.) se poate folosi comanda:<br />
<br />
apt-get install -y build-essential<br />
<br />
=== Instalarea GCC în Windows ===<br />
<br />
Pentru compilarea de programe cu GCC în Windows, aveți nevoie de instalarea acestui compilator care poate fi făcută prin instalarea unuia din următoarele două suite de programe:<br />
* [https://cygwin.com Cygwin] - Tutorial de instalare [https://cygwin.com/install.html aici].<br />
* [http://www.mingw.org/ MinGW] - Tutorial de instalare [http://www.mingw.org/wiki/InstallationHOWTOforMinGW aici].<br />
<br />
<div class="regula"><span style="color: red; font-weight: bold">Atenție:</span> Nu uitați ca la instalare să bifați în lista de pachete și compilatorul de C (<code>gcc</code>), <code>make</code> și <code>git</code>.</div><br />
<br />
== Instalarea Netbeans ==<br />
<br />
De la adresa https://netbeans.org/downloads/ puteți descărca kitul de instalare pentru sistemul vostru de operare (imaginea de Virtualbox cu Linux folosită la Programarea Calculatoarelor ruelază un Linux pe 32 de biți). Descărcați una din variantele care suporta C/C++ ('''C/C++''' sau '''All''').<br />
<br />
=== Instalare Netbeans în Linux ===<br />
<br />
Fișierul descărcat pentru Linux va avea extensia '''.sh'''. Pentru a instala programul, deschideți un terminal și rulați următoarele comenzi:<br />
<br />
<syntaxhighlight lang="bash"><br />
cd ~/Downloads # navigare catre directorul unde a fost salvat fisierul descarcat<br />
chmod +x *.sh # activarea fanionului 'executabil' pentru toate fisierele cu extensia .sh<br />
sudo ./netbeans-8.1-cpp-linux-x86.sh # folositi numele corespunzator al fisierului descarcat<br />
</syntaxhighlight><br />
<br />
Mai departe urmați pașii din programul de instalare.<br />
<br />
=== Instalare Netbeans în Windows ===<br />
<br />
Fișierul descărcat va avea extensia '''.exe'''. Rulați-l și urmăriți instrucțiunile. Veți fi întrebați unde este compilatorul de C/C++. Executabilele pentru acestea sunt în directorul unde ați instalat MinGW sau Cygwin, în subdirectorul <code>/usr/bin</code>.<br />
<br />
== Realizarea unui proiect ==<br />
<br />
Aplicațiile în Netbeans sunt structurate în '''Proiecte'''. O aplicație este un proiect. Nu este suficient să scrieți un fișier sursă C și să-l rulați. Netbeans trebuie să creeze fișiere '''Makefile''' și să pregătească mediul pentru compilare și execuție precum și să fie capabil să vă ofere informații legate de corectitudinea codului și să vă ofere sugestii de îmbunătățire. Pentru a crea un proiect nou, se folosește meniul <code>File - New Project... </code> sau combinația <code>Ctrl-Shift-n</code>:<br />
<br />
[[Fișier:netbeans_new_project.png|1000px|Proiect nou]]<br />
<br />
Mai departe selectați categoria '''C/C++''' și la '''Project''' selectați '''C/C++ Application'''. Va apărea următoarea fereastră:<br />
<br />
[[Fișier:netbeans_new_application.png|Aplicație nouă]]<br />
<br />
Completați următoarele informații:<br />
* '''Project Name''' - numele proiectului/ al aplicației voastre; va fi numele directorului creat de Netbeans pentru proiect;<br />
* '''Project Location''' - directorul unde va fi salvat directorul proiectului de către Netbeans;<br />
* '''Project Folder''' - calea completă până la directorul proiectului; este obținut din concatenarea celor două de mai sus; nu poate fi modificat;<br />
* '''Project Makefile Name''' - numele fișierului Makefile care va fi creat de către Netbeans pentru proiectul vostru;<br />
* '''Create Main File''' - se bifează dacă se dorește crearea unui fișier sursă care să conțină funcția '''main''' și se completează numele acestui fișier (fără extensie); în căsuța de selecție din dreapta se alege limabjul (C sau C++) și standardul folosit pentru compilarea codului și generarea fișierului main; <br />
* '''Build Host''' - numele sau adresa IP a calculatorului unde se realizează compilarea ('''localhost''' este calculatorul pe care lucrați);<br />
* '''Tool Collection''' - dacă există mai multe compilatoare instalate, de aici se alege care compilator este utilizat pentru proiect.<br />
<br />
După ce dați click pe '''Finish''', proiectul se va deschide și fereastra se va schimba în modul de editare.<br />
<br />
== Componentele IDE-ului Netbeans ==<br />
<br />
[[Fișier:netbeans_env.png|1000px|Netbeans]]<br />
<br />
Zonele din imagine sunt:<br />
<br />
# Zona <span style="color: gold">gabenă</span> - bara de instrumente - conține butoane folosite pentru acces rapid la funcționalitatea cea mai utilizată (compilare, rulare, etc.) și conține:<br />
#* selecția tipului de compilare ('''Debug''' sau '''Release''') are efect asupra nivelului de optimizare comandat compilatorului și selectează sau nu facă simbolurile de debug sunt adăugate executabilului; pe scurt, nu se poate face debug pe executabilul de Release, dar executabilul de Debug este mai lent decât cel de Release;<br />
#* testare în browser, este accesibil doar pentru aplicații web (HTML, PHP, Javascript, etc.), nu și pentru aplicații C/C++;<br />
#* '''Build Project''' - se apelează '''make all''' pentru compilarea programului;<br />
#* '''Clean and Build Project''' - se apelează '''make clean all''' pentru ștergerea binarelor și apoi recompilarea programului;<br />
#* '''Run Project''' - se rulează executabilul generat de build (dacă acesta nu există sau nu este actualizat, se rulează automat '''Build Project''' înainte);<br />
#* '''Debug Project''' - se rulează executabilul generat de build în modul de debug (cu ajutorul GDB, vezi [[PC Laborator 10#Tool-ul de depanare GDB]]), permițând plasarea de breakpoints, execuție pas cu pas, vizualizarea valorilor variabilelor, etc.<br />
#* '''Profile Project''' - indisponibil în C/C++, permite vizualizarea resurselor consumate și ajută la optimizarea programului.<br />
# Zona <span style="color: blue">albastră</span> - conține 4 tab-uri din care importante pentru proiectele în C sunt primele două: '''Projects''' și '''Files'''.<br />
#* '''Projects''' - arată proiectele deschise și tipul lor; în cazul de față este un singur proiect deschis - '''TestProjectHelloWorld'''; sub el se află 5 categorii în care sunt plasate fișierele care fac parte din proiect:<br />
#** ''Header Files'' - fișierele header (cu extensia .h);<br />
#** ''Resource Files'' - diferite fișiere cu resurse folosite de aplicație (fișiere de configurare, xml, imagini, etc.);<br />
#** ''Source Files'' - fișiere sursă C (cu extensia .c);<br />
#** ''Test Files'' - fișiere de test pentru funcțiile din fișierele sursă;<br />
#** ''Important Files'' - fișiere importante pentru proiect (cum ar fi fișierul Makefile).<br />
#* '''Files''' - se poate vedea directorul proiectului cu fișierele și subdirectoarele sale; în directorul proiectului se realizează implicit următoarele subdirectoare:<br />
#** ''build'' - conține fișierele obiect pentru fiecare din surse, pentru fiecare tip de compilare ('''Debug''' sau '''Release''');<br />
#** ''dist'' - conține fișierele executabile pentru fiecare tip de compilare ('''Debug''' sau '''Release''');<br />
#** ''nbproject'' - conține fișiere de configurare pentru proiect.<br />
# Zona <span style="color: green">verde</span> - conține câte un tab pentru fiecare fișier deschis pentru compilare; implicit bara verde se află deasupra ferestrei de editare (portocaliu) dar poate fi mutată.<br />
# Zona <span style="color: orange">portocalie</span> - fereastra de editare a codului ce conține pe bara de sus butoane pentru navigarea rapidă prin fișierele deschise și / sau modificate recent; de remarcat aici este butonul '''History''' care, dacă fișierul este salvat într-un sistem de ''revision control'' (cum ar fi Git), permite vizualizarea istoriei modificărilor fișierului.<br />
# Zona <span style="color: hotpink">roz</span> - listează elementele definite în fișierul deschis în zona portocalie; dacă fișierul este un header sau un fișier sursă C, aici se vor vedea tipurile de date definite, variabilele globale, funcțiile cu argumentele lor, macrourile de preprocesor, etc.; dacă în schimb este deschis un Makefile, se vor vedea toate rețetele definite în Makefile-ul respectiv;<br />
# Zona <span style="color: red">roșie</span> - zona de ''Output'' reprezintă consola unde se afișează rezultatele compilării cât și ieșirea generată de program în timpul execuției, pe ambele stream-uri (stdout și stderr).<br />
<br />
== Exemplu de program ==<br />
<br />
Copiați codul de mai jos înlocuind programul deja existent în fișierul ''main.c'':<br />
<syntaxhighlight lang="C"><br />
#include <stdio.h><br />
<br />
int main(){<br />
printf("Hello Netbeans!\n");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
Dați apoi click pe '''Run Project''' (simbolul verde ''Play'') sau tastați <code>Ctrl+F11</code>:<br />
<br />
[[Fișier:netbeans_run.png|1000px|După execuție]]<br />
<br />
Observați consola din partea de jos a ecranului unde se afișează mesajul "Hello Netbeans" și apoi se semnalează sfârșitul programului raportându-se timpul de execuție. <br />
<br />
== Exemplu de program în modul ''debug'' ==<br />
<br />
Copiați codul de mai jos înlocuind programul deja existent în fișierul ''main.c'':<br />
<syntaxhighlight lang="C" line><br />
#include <stdio.h><br />
<br />
int main(){<br />
printf("Debugging program...\n");<br />
int value = 0;<br />
value = value + 1;<br />
value++;<br />
value = value * 2;<br />
value -= 1;<br />
printf("Value is now %d!\n", value);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
Dați click pe numărul liniei 5 (în dreptul definiției variabilei <code>value</code>) și ar trebui ca în loc de numărul liniei să vă apară un pătrat roșu. Acela semnalează un ''breakpoint'' la acea linie. Dați apoi click pe '''Debug Project''' sau tastați <code>F11</code>. Acesta va programul în modul de ''debug'' și astfel se va opri la primul ''breakpoint'':<br />
<br />
[[Fișier:netbeans_debug.png|1000px|După execuție]]<br />
<br />
În interfață au apărut două zone noi:<br />
# Zona <span style="color: red">roșie</span> - instrumente de control al execuției în modul de debug; în ordine, acestea sunt:<br />
#* '''Finish Debugger Session''' - execuția se încheie și interfața grafică revine la forma anterioară;<br />
#* '''Restart''' - execuția se reia de la începutul programului, oprindu-se la primul ''breakpoint'';<br />
#* '''Pause''' - dacă programul nu este oprit (într-un ''breakpoint''), atunci accesta poate fi întrerupt facând click pe acest buton;<br />
#* '''Continue''' - dacă programul este oprit (într-un ''breakpoint''), atunci el poate fi rulat mai reparte până la următorul ''breakpoint'' cu ajutorul acestui buton;<br />
#* '''Step Over''' - programul va executa linia curentă (inclusiv orice apel de funcție ar fi pe linia curentă) și trece la linia următoare unde se oprește;<br />
#* '''Step Into''' - programul va executa linia curentă iar dacă pe linia curentă există un apel de funcție, se va opri pe prima linie din funcția respectivă;<br />
#* '''Step Out''' - programul va termina de executat funcția curentă și se va opri pe prima linie după linia unde a fost apelată funcția respectivă;<br />
#* '''Run to Cursor''' - programul va executa toate liniile și se va opri pe linia unde este plasat cursorul; dacă acesta nu este plasat în calea execuției programului, acesta se va rula până la final;<br />
#* '''Step Into Last or Selected Function Called From Current Line''' - similar cu '''Step Into''' doar că dacă există mai multe apeluri de funcții pe linia curentă, această opțiune permite selectarea celei în care se va opri programul.<br />
<br />
# Zona <span style="color: blue">albastră</span> - vizualizarea stării execuției programului; aveți la dispoziție trei taburi noi:<br />
#* '''Variables''' - aici se pot vedea valorile curente ale tuturor variabilelor valide în ''scope''-ul curent al programului; inclusiv aici se pot adăuga ''watches'' care permit vizualizarea valorilor calculate din expresii complexe scrise în limbajul C;<br />
#* '''Call Stack''' - aici se poate vedea în orice moment stiva de execuție a programului;<br />
#* '''Breakpoints''' - aici se poate vedea lista de ''breakpoints'' din programul curent.<br />
<br />
Pentru a exersa utilizarea sistemului de debug, realizați următoarele operații:<br />
# Parcurgeti tot programul folosind '''Step Over''' și vizualizând valoarea variabilei <code>value</code> la fiecare pas, până la ultima linie din program, unde utilizați '''Continue'''.<br />
# Reporniți aplicația în modul de debug și puneți un al doilea ''breakpoint'' pe linia cu apelul funcției <code>printf</code>. <br />
# Folosiți '''Continue''' pentru a ajunge la al doilea ''breakpoint''.<br />
# Odată ajunși acolo, folosiți '''Step In''' pentru a intra în funcția <code>printf</code> (neavând codul sursă, veți vedea codul de asamblare obținut din dezasamblarea fișierului obiect de către GDB).<br />
# Folosiți '''Step Out''' pentru a reveni în funcția <code>main</code> și apoi '''Continue''' pentru a termina execuția.<br />
# Reporniți aplicația în modul de debug și realizați un ''watch'' cu valoarea lui <code>value + 1</code>. Vizualizați pas cu pas execuția programului.<br />
<br />
== Tips & Tricks ==<br />
<br />
=== Suport pentru instrumente de ''Revision Control'' ===<br />
<br />
Netbeans oferă suport grafic pentru cele mai utilizate intrumente de ''revision control'': SVN, Git, Mercurial, etc. Pentru a crea un repository nou de Git pentru proiectul curent din interfața grafică, puteți da click dreapta pe numele proiectului, apoi '''Versioning -> Initialize Git repository...''':<br />
<br />
[[Fișier:netbeans_git_init.png|1000px|Git Init]]<br />
<br />
După operațiunea de init, meniul '''Versioning''' se va schimba în '''Git''', de unde se pot realiza toate operațiile cunoscute din Git: add, remove, commit, push, pull, merge, rebase, diff, etc.<br />
<br />
=== Formatare automată a codului ===<br />
<br />
Netbeans oferă opțiunea de a formata automat codul prin selectarea '''Source -> Format'''. <br />
<br />
Ce probleme rezolvă această formatare automată:<br />
* alinierea liniilor în funcție de blocurile de instrucțiuni și tipurile de ''statements'' de pe fiecare linie;<br />
* plasarea sau eliminarea de spații acolo unde este necasar.<br />
Ce probleme NU rezolvă această formatare automată;<br />
* lipsa acoladelor de la blocurile '''if''' și '''for''';<br />
* numele insuficient de sugestive pentru numele de funcții, structuri sau variabile.<br />
<br />
Nu uitați de regulile următoare: [[Convenții de cod - C]]<br />
<br />
<!--<br />
= Exerciții =<br />
<br />
*Săptămâna 1<br />
*# Realizați un proiect nou în Netbeans și scrieți un program care să citească de la tastatură un număr întreg fără semn <code>size</code>. Se va aloca apoi dinamic în HEAP un vector de <code>size</code> valori numerice întregi pe 16 biți. Scrieți o funcție care să citească <code>size</code> valori de la tastatură și să le plaseze în vector. Scrieți apoi o funcție care să găsească valoarea maximă din vector. Apelați în <code>main</code> funcțiile de mai sus și afișați valoarea obținută pe ecran. Nu uitați să dezalocați memoria alocată. Pentru funcțiile de alocare și dezalocare de memorie, puteți recapitula [[PC Laborator 12]].<br />
*# Modificați programul de mai sus astfel încât citirea variabilei <code>size</code> cât și alocarea de memorie să fie făcută într-o funcție separată. Această funcție trebuie să întoarcă un pointer cu adresa de început a vectorului dar și dimensiunea acestuia.<br />
*# Realizați un alt proiect în Netbeans și scrieți un program care să citească de la tastatură un șir de caractere de lungime maximă 255. Folosind aritmetica pointerilor și o singură buclă '''for''', afișați pe ecran doar cifrele din șirul citit.<br />
* Săptămâna 2<br />
*# Realizați un proiect nou în Netbeans și scrieți un program care să citească de la tastatură un număr întreg fără semn <code>size</code>. Scrieți apoi o funcție care să aloce memorie pentru un șir de caractere care să conțină de <code>size</code> ori secvența "ha" (spre exemplu, pentru size == 3, string-ul va trebui să conțină "hahaha"), să umple șirul cu numărul cerut de "ha"-uri și să întoarcă adresa memoriei alocate. În funcția <code>main</code> afișați șirul de caractere (nu uitați la alocare de terminatorul de sfârșit de șir). Nu uitați să dezalocați memoria. Pentru funcțiile de alocare și dezalocare de memorie, puteți recapitula [[PC Laborator 12]].<br />
*# Modificați programul de mai sus astfel încât în loc de secvența "ha", secvența repetată să fie citită de la tastatură cu <code>scanf</code> și trimisă ca argument funcției de alocare. <br />
*# Realizați un alt proiect în Netbeans și scrieți un program care să citească de la tastatură un șir de caractere de lungime maximă 255. Scrieți o singură funcție care să numere și să întoarcă numărul de litere și numărul de cifre din șirul citit. Aceste valori se vor afișa pe ecran în funcția <code>main</code>.<br />
--><br />
<br />
<br />
= Probleme propuse =<br />
<br />
== Problema 1 ==<br />
Programul de mai jos ar trebui să citească de la tastatură un șir de maxim 1024 caractere și să afișeze câte litere mici, câte litere mari și câte cifre conține acest șir. Totuși acest program are o eroare. Folosiți debugger-ul pentru a o identifica și repara.<br />
<br />
Exemplu de intrare:<br />
<br />
Ana a plecat la piata si a cumparat 10 saci de cartofi.<br />
<br />
Ieșirea pentru intrarea de mai sus:<br />
<br />
40 1 2<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <stdio.h><br />
<br />
int main() {<br />
char c;<br />
unsigned uppercaseChars = 0;<br />
unsigned lowecaseChars = 0; <br />
unsigned digits = 0;<br />
<br />
while(scanf("%c", c) != EOF) {<br />
if(c > 'a' || c < 'z') {<br />
lowecaseChars++;<br />
} else if(c > 'A' && c < 'Z') {<br />
uppercaseChars++;<br />
} else {<br />
digits++;<br />
}<br />
}<br />
printf("%u %u %u", lowecaseChars, uppercaseChars, digits);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
== Problema 2 ==<br />
Un tehnician a măsurat un număr mare n de rezistori şi a obţinut n valori de rezistenţe. Tehnicianul ştie că rezistorii sunt de acelaşi fel, dar pentru că sunt vechi şi codul culorilor nu mai este vizibil, doreşte să calculeze valoarea nominală a rezistenţei (R) şi dispersia valorilor, (S). Apoi, având aceste valori, el vrea să determine câte din rezistenţele testate (procentual) se încadrează în intervalul [R – S; R + S]. Rezistenţa nominală se calculează ca media aritmetică a valorilor rezistenţelor, iar formula dispersiei este dată mai jos.<br />
<br />
Cerinţă<br />
Dându-se un număr n de rezistori şi valorile rezistenţelor acestora Ri (i = 1, ..., n), să se determine procentul rezistoarelor care au rezistenţa în intervalul [R– S; R + S].<br />
<br />
Date de intrare<br />
Pe prima linie se află numărul întreg n. Pe următoarea linie, separate printr-un spaţiu, sunt n valori fracţionare de rezistenţe (în ohmi).<br />
<br />
Date de ieşire<br />
Se va afişa o singură valoare fracţionară, cu exact două zecimale reprezentând procentul de rezistori cu rezistenţa în intervalul [R – S; R + S].<br />
<br />
Restricţii şi precizări<br />
1 < n < 1000<br />
1.0 <= Ri <= 10000.0 <br />
S=sqrt( pow(sum(R[i]−R),2) / n )<br />
<br />
Exemplu:<br />
<br />
Input =<br />
10<br />
<br />
76 1 20 37 19 92 61 96 37 77<br />
<br />
Output = <br />
50.00<br />
<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <vector><br />
#include <string><br />
#include <unordered_map><br />
#include <algorithm><br />
#include <cstdio><br />
<br />
<br />
int main() {<br />
int n;<br />
double values[n];<br />
scanf("%d", &n);<br />
int sum = 0;<br />
for(int i = 0; i < n; i++){<br />
scanf("%lf", &values[i]);<br />
sum += values[i];<br />
}<br />
double average = sum / n;<br />
double dispersion = 0;<br />
for(int i = 0; i < n; i++){<br />
dispersion += (values[i] - average) * (values[i] - average);<br />
}<br />
dispersion = sqrt(dispersion / n);<br />
int larger = 0;<br />
for(int i = 0; i < n; i++) {<br />
if(values[i] >= average - dispersion <br />
&& values[i] <= average + dispersion) larger++;<br />
}<br />
printf("%.2lf", 100 * larger / n);<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
<br />
== Problema 3 ==<br />
Sunteți angajați de ministerul învățamântului să scrieți un program care să listeze toți elevii care au promovat examenul de bacalaureat. De la tastatură se introduce un număr n reprezentând numărul de candidați, apoi pe următoarele n linii, un nume de elev (format din exact două cuvinte: nume și prenume) și notele la cele patru materii, format fracționar, valori între 1 și 10. Să se afișeze toți studenții care au promovat examenul de bacalaureat (media mai mare ca 6, fiecare notă mai mare ca 5) în ordinea inversă a introducerii lor, împreună cu media de promovare, afișată cu fix două zecimale.<br />
<br />
Exemplu de date de intrare:<br />
<br />
4<br />
<br />
Gheorghe Ghita 8 7 5.5 10<br />
<br />
Vuia Vasile 4 10 10 10<br />
<br />
Andreescu Andra 9 10 9 10<br />
<br />
Elenescu Elena 5 5 5 5<br />
<br />
Ieșirea pentru intrarea de mai sus:<br />
<br />
Andreescu Andra 9.50<br />
<br />
Gheorghe Ghita 7.63<br />
<br />
<syntaxhighlight lang="C++"><br />
#include <stdio.h><br />
struct studenti{<br />
char n[128];<br />
char m[128];<br />
float a,b,c,d;<br />
};<br />
int main(){<br />
int n,i;<br />
struct studenti s[n];<br />
scanf("%d", &n);<br />
for(i=0;i<n;i++)<br />
scanf("%s%s%f%f%f%f",&s[i].n,&s[i].m,&s[i].a,&s[i].b,&s[i].c,&s[i].d);<br />
<br />
for(i=n;i>0;i--){<br />
float m=(s[i].a+s[i].b+s[i].c+s[i].d)/4;<br />
if(s[i].a>5&&s[i].b>5&&s[i].c>5&&s[i].d>5||m>=6){<br />
printf("%s %s %.2f\n",s[i].n,s[i].m,m);<br />
}<br />
}<br />
return 0;<br />
}<br />
</syntaxhighlight></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_12_:_Exercitii_cu_circuite_secventiale&diff=7527CID aplicatii 12 : Exercitii cu circuite secventiale2023-05-21T13:17:06Z<p>Mihai.antonescu: /* Exercitii */</p>
<hr />
<div>==Teorie==<br />
Acest laborator are rolul de a sedimenta cunostintele dobandite anterior. <br />
<br />
El consta in exercitii separate, unele date ca subiect la lucrarea 2 in anii anteriori.<br />
<br />
Unele exercitii contin si automate. Daca acestea nu au fost inca predate, puteti ignora partea aceea de circuit.<br />
<br />
Dupa cum se poate observa, exemplele sunt diverse ca domeniu de aplicabilitate. Prin asta se doreste a se arata importanta si diversitatea circuitelor digitale in societatea moderna.<br />
<br />
<br />
<br />
==Exercitii==<br />
<br />
===Exercitiul 1: password checker===<br />
<br />
Verilog suporta codul ASCII deci puteti introduce si litere/string-uri care vor fi tratate prin reprezentarea lor binara.<br />
<br />
[https://wiki.dcae.pub.ro/images/5/5c/Subiect_l2_password_checker.pdf password_checker.pdf]<br />
<br />
<br />
<br />
===Exercitiul 2: pseudorandom number generator===<br />
<br />
[https://wiki.dcae.pub.ro/images/0/05/Subiect_l2_pseudorandom_nr_generator.pdf pseudorandom_nr_generator.pdf]<br />
<br />
<br />
<br />
===Exercitiul 3: calculator de siruri dupa formula data===<br />
<br />
[https://wiki.dcae.pub.ro/images/a/a2/Subiect_l2_sir_calculator.pdf calculator_siruri.pdf]<br />
<br />
<br />
<br />
===Exercitiul 4: UART tx - structural===<br />
<br />
UART este un standard pentru transmisiune de date seriale. Pentru o descriere a protocolului puteti citi: [https://www.circuitbasics.com/basics-uart-communication/ functionare_uart]<br />
<br />
[https://wiki.dcae.pub.ro/images/d/d0/Subiect_l2_uart_tx.pdf uart_tx.pdf]<br />
<br />
<br />
<br />
===Exercitiul 5: UART rx - behavioural=== <br />
<br />
Daca ati inteles cum functioneaza mecanismul de UART (si pentru a testa functionarea TX de la exercitiul anterior) puteti scrie modulul de RX (receiver) al protocolului, astfel incat cele 2 sa comunice intre ele.<br />
<br />
La nivel de TB ar fi amandoua instantiate si conectate intre ele. <br />
<br />
UART RX ar trebui sa scoata un puls al semnalului "valid" impreuna cu 8 biti de date daca transmisiunea a fost efectuata cu succes, datele care ies din el fiind cele care au intrat in modulul de tx. <br />
<br />
Pentru a compara abordarea structurala cu cea comportamentala, acest modul se doreste a fi facut comportamental.<br />
In always-urile acestuia vor aparea mecanisme de numarare care practic alcatuiesc un mic FSM de control. <br />
<br />
<br />
<br />
===Exercitiul 6: microcontrollere - oscillator control register=== <br />
<br />
Acest subiect prezinta un modul simplificat al mecanismului de control al oscilatorului de pe un microcontroller. Acest modul controleaza generarea smenalului de ceas ce se propaga catre microcontroller si deci viteza la care sistemul va functiona.<br />
<br />
[https://wiki.dcae.pub.ro/images/0/09/Subiect_l2_uc_osccon.pdf uc_osccon.pdf]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Boolean_Board_-_Pinout&diff=7524Boolean Board - Pinout2023-04-20T11:18:46Z<p>Mihai.antonescu: /* Boolean Board - Pinout */</p>
<hr />
<div>==Boolean Board - Pinout==<br />
<br />
Aici veți găsi lista cu corespondența pin - componentă pentru placa Boolean Board de la Real Digital. Aceasta se folosește fie în fișierul XDC cu constrângeri, fie în etapa ''Elaboration'', atunci când se selectează conexiunile fizice dorite între intrările și ieșirile circuitului și componentele de pe placă (LED-uri, butoane, comutatoare, etc.).<br />
<br />
[[Fișier:Boolean board.png|500px]]<br />
<br />
Pentru funcționarea corectă a plăcii se va selecta "LVCMOS33" ca standard de I/O, unde "33" vine de la 3.3V.<br />
<br />
'''Clock''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Clock 100MHz || F14<br />
|}<br />
<br />
<br />
<br />
'''Comutatoare''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW0 || V2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|SW1 || U2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW2 || U1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW3 || T2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW4 || T1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW5 || R2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW6 || R1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW7 || P2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW8 || P1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW9 || N2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW10 || N1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW11 || M2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW12 || M1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW13 || L1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW14 || K2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW15 || K1<br />
|}<br />
<br />
'''Butoane''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN0 || J2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN1 || J5 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN2 || H2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN3 || J1 <br />
|}<br />
<br />
'''LED-uri''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED0 || G1 <br />
|- bgcolor="#ddffdd" align="center"<br />
|LED1 || G2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED2 || F1<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED3 || F2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED4 || E1<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED5 || E2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED6 || E3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED7 || E5<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED8 || E6<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED9 || C3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED10 || B2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED11 || A2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED12 || B3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED13 || A3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED14 || B4<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED15 || A4<br />
|}<br />
<br />
'''LED-uri RGB''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_RED || V6<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_GREEN || V4<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_BLUE || U6<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_RED || U3<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_GREEN || V3<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_BLUE || V5<br />
|}<br />
<br />
'''Afisajul cu 7 segmente'''<br />
<br />
[[Fișier:Afisaj7Segmente.png|200px]]<br />
<br />
Observație: Semnalele de control pentru aprinderea segmentelor și selecția cifrelor sunt active in 0. Numerotarea cifrelor dintr-un grup se face de la drepta la stânga.<br />
<br />
''Grupul 0 (LEFT)''<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 0 || D7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 1 || C5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 2 || A5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 3 || B7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 4 || A7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 5 || D6<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 6 || B5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Punctul || A6<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 0 || D5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 1 || C4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 2 || C7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 3 || A8<br />
|}<br />
<br />
''Grupul 1 (RIGHT)''<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 0 || F4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 1 || J3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 2 || D2<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 3 || C2<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 4 || B1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 5 || H4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 6 || D1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Punctul || C1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 0 || H3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 1 || J4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 2 || F3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 3 || E4<br />
|}<br />
<br />
Pentru lista completă a pinilor puteți accesa [https://www.realdigital.org/hardware/boolean Boolean Board - pagina oficiala] - secțiunea ''User manual'' sau ''Master XDC''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7523CID aplicatii 7 : Circuite secventiale elementare2023-04-11T21:25:05Z<p>Mihai.antonescu: /* Exercițiul 4 */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
<br />
==Atribuirea blocanta si non-blocanta==<br />
Pentru ca simulatorul sa functioneze in mod corect si semnalele sa se modifice in simulare asa cum se modifica si in realitate este nevoie sa folosim atribuirea blocanta ( semnul "=") la circuite combinationale si atribuirea non-blocanta (semnul "<=") la circuite secventiale. <br />
<br />
In mod uzual orice always combinational ( '''always@(*)''' ) va folosi '''atribuirea blocanta ( = )''' si orice always pe ceas ( '''always@(posedge clock)''' ) va folosi '''atribuirea non-blocanta ( <= )'''.<br />
<br />
Diferenta dintre cele 2 atribuiri consta in modul in care simulatorul "executa" instructiunea de atribuire. La atribuirea blocanta se simuleaza linie cu linie, in ordinea in care acestea au fost scrise. La atribuirea non-blocanta se salveaza toti termenii din dreapta si se pun deodata (in acelasi pas de simulare) in termenii din stanga. Pentru a clarifica acest concept avem exemplul de mai jos: <br />
<br />
<syntaxhighlight lang="Verilog"><br />
a = b;<br />
b = a;<br />
<br />
c <= d;<br />
d <= c;<br />
</syntaxhighlight><br />
<br />
In primul caz, deoarece am folosit atribuirea blocanta, a ia valoarea lui b si apoi b ia valoarea lui a, care deja a devenit b deci concret b ramane pe loc si deci atat a cat si b vor avea aceasi valoare la final.<br />
<br />
In al doilea caz, deoarece am folosit atribuirea non-blocanta, ce este in partea dreapta se transfera peste operanzii din stanga deodata, astfel c ia valoarea lui d si d ia valoarea lui c, la final avand loc o interschimbare.<br />
<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
[[Fișier:Poza_hazard.png | 800px]]<br />
<br />
In acest exemplu se observa ca iesirea c se modifica la 1ns dupa modificarile intrarilor (intarizerea adaugata pentru transmiterea prin porti) si iesirea d se modifica de 2 ori: prima data la 1ns din cauza intrarilor si a doua oara dupa inca 1ns din cauza lui c. In intervalul [6-7]ns apare o valoare parazita, un hazard.<br />
<br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.<br />
<br />
===Exercițiul 4===<br />
Pentru evidentierea hazardului combinational (si a modului in care acesta se rezolva) descrieti in Verilog si simulati urmatoarele 2 circuite:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_circuit.png | 600px]]<br />
<br />
Pentru a putea observa efectele hazardului, portile trebuie descrise ca avand timpi de propagare.<br />
<br />
Formele de unda pentru semnalele de la intrare vor fi:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_forme_unda.png | 600px]]<br />
<br />
===Exercițiul 5===<br />
Pentru evidentierea efectului atribuirii blocant si non-blocante se va implementa si simula circuitul de mai jos. El este alcatuit din 3 registre, fiecare memorand (si punand la iesire) pe frontul pozitiv al ceasului valoarea de la intrare.<br />
<br />
Cele 3 registre de sus vor folosii atribuirea blocanta si cele 3 registre de jos vor folosii atribuirea non-blocanta. In simulare se doreste a se vizualiza: intrarile, iesirile, firele interne de legatura dintre registrii.<br />
<br />
Pentru a scrie semnificativ mai putin, nu este nevoie sa instantiati fiecare registru dintro serie ca modul separat ci se pot face toate cele 3 atribuiri in cadrul aceluiasi bloc "always", la nivelul de top. Practic tot circuitul se reduce la un fisier de top ce contine intrari/iesiri, declararea registrilor pe 1b si cele 2 always-uri care dicteaza functionarea.<br />
<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_5_circuit.png | 600px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Boolean_Board_-_Pinout&diff=7522Boolean Board - Pinout2023-04-11T09:49:49Z<p>Mihai.antonescu: /* Boolean Board - Pinout */</p>
<hr />
<div>==Boolean Board - Pinout==<br />
<br />
Aici veți găsi lista cu corespondența pin - componentă pentru placa Boolean Board de la Real Digital. Aceasta se folosește fie în fișierul XDC cu constrângeri, fie în etapa ''Elaboration'', atunci când se selectează conexiunile fizice dorite între intrările și ieșirile circuitului și componentele de pe placă (LED-uri, butoane, comutatoare, etc.).<br />
<br />
[[Fișier:Boolean board.png|500px]]<br />
<br />
Pentru funcționarea corectă a plăcii se va selecta "LVCMOS33" ca standard de I/O, unde "33" vine de la 3.3V.<br />
<br />
'''Clock''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Clock|| F14<br />
|}<br />
<br />
<br />
<br />
'''Comutatoare''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW0 || V2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|SW1 || U2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW2 || U1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW3 || T2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW4 || T1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW5 || R2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW6 || R1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW7 || P2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW8 || P1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW9 || N2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW10 || N1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW11 || M2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW12 || M1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW13 || L1<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW14 || K2<br />
|- bgcolor="#ddffdd" align="center"<br />
|SW15 || K1<br />
|}<br />
<br />
'''Butoane''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN0 || J2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN1 || J5 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN2 || H2 <br />
|- bgcolor="#ddffdd" align="center"<br />
|BTN3 || J1 <br />
|}<br />
<br />
'''LED-uri''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED0 || G1 <br />
|- bgcolor="#ddffdd" align="center"<br />
|LED1 || G2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED2 || F1<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED3 || F2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED4 || E1<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED5 || E2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED6 || E3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED7 || E5<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED8 || E6<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED9 || C3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED10 || B2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED11 || A2<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED12 || B3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED13 || A3<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED14 || B4<br />
|- bgcolor="#ddffdd" align="center"<br />
|LED15 || A4<br />
|}<br />
<br />
'''LED-uri RGB''':<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_RED || V6<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_GREEN || V4<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB0_BLUE || U6<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_RED || U3<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_GREEN || V3<br />
|- bgcolor="#ddffdd" align="center"<br />
|RGB1_BLUE || V5<br />
|}<br />
<br />
'''Afisajul cu 7 segmente'''<br />
<br />
[[Fișier:Afisaj7Segmente.png|200px]]<br />
<br />
Observație: Semnalele de control pentru aprinderea segmentelor și selecția cifrelor sunt active in 0. Numerotarea cifrelor dintr-un grup se face de la drepta la stânga.<br />
<br />
''Grupul 0 (LEFT)''<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 0 || D7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 1 || C5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 2 || A5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 3 || B7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 4 || A7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 5 || D6<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 6 || B5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Punctul || A6<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 0 || D5<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 1 || C4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 2 || C7<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 3 || A8<br />
|}<br />
<br />
''Grupul 1 (RIGHT)''<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 0 || F4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 1 || J3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 2 || D2<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 3 || C2<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 4 || B1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 5 || H4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Segmentul 6 || D1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Punctul || C1<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 0 || H3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 1 || J4<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 2 || F3<br />
|- bgcolor="#ddffdd" align="center"<br />
|Selectie Cifra 3 || E4<br />
|}<br />
<br />
Pentru lista completă a pinilor puteți accesa [https://www.realdigital.org/hardware/boolean Boolean Board - pagina oficiala] - secțiunea ''User manual'' sau ''Master XDC''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_5_:_Exercitii_cu_circuite_combinationale&diff=7521CID aplicatii 5 : Exercitii cu circuite combinationale2023-04-02T08:57:46Z<p>Mihai.antonescu: /* Teorie: Concatenarea */</p>
<hr />
<div>==Teorie==<br />
<br />
Acest laborator are rolul de a sedimenta cunostiintele dobandite anterior. <br />
<br />
El consta in exercitii separate, unele date ca subiect la lucrarea 1 in anii anteriori si cateva notiuni de teorie si sintaxa ajutatoare.<br />
<br />
<br />
<br />
==Teorie: Parametrizare==<br />
Parametrizarea este un mod de a generaliza codul scris pentru a nu fi nevoie sa scrii acelasi modul de mai multe ori doar pentru ca circuitul isi schimba o dimensiune. <br />
<br />
Pentru a intelege mai clar avantajele si sintaxa urmariti urmatorul exemplu:<br />
<br />
Fisierul sumator.v:<br />
<syntaxhighlight lang="Verilog"><br />
module sumator # // <= diez ca sa stie ca urmeaza lista cu paramteri<br />
( // parametri <br />
parameter data_size = 4 // valoare default 4<br />
// alti parametri aici daca este nevoie, separati prin virgula<br />
)<br />
( // interfata <br />
input wire [data_size-1:0] in0, // si pot folosii parametrul "data_size" pentru dimensiunea bus-ului<br />
input wire [data_size-1:0] in1,<br />
output wire [data_size-1:0] out0<br />
);<br />
<br />
assign out0 = in0 + in1; <br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
Cand se instantiaza un modul parametrizat, se specifica valorile parametrilor astfel: <br />
<syntaxhighlight lang="Verilog"><br />
sumator # ( // parametri <br />
.data_size(8) // se genereaza un sumator pe 8b<br />
) <br />
nume_instanta_0<br />
( // interfata <br />
.in0(fir_in0), <br />
.in1(fir_in1),<br />
.out0(fir_out0)<br />
);<br />
<br />
sumator # ( // parametri <br />
.data_size(32) // se genereaza un sumator pe 32b<br />
) <br />
nume_instanta_1<br />
( // interfata <br />
.in0(fir_in0), <br />
.in1(fir_in1),<br />
.out0(fir_out1)<br />
);<br />
<br />
sumator nume_instanta_2 // se genereaza un sumator de dimensiune default, aici 4, asa cum e scris in modulul "sumator"<br />
( // interfata <br />
.in0(fir_in0), <br />
.in1(fir_in1),<br />
.out0(fir_out2)<br />
);<br />
</syntaxhighlight><br />
<br />
<br />
Puteti folosi parametrizarea in exercitiul 3 de mai jos. Acolo sunt 2 tipuri de multiplexoare, unul cu intrarile pe 1 bit si unul cu intrarile pe 2 biti. Poate fi scris un singur modul parametrizat care sa acopere ambele situatii.<br />
<br />
==Teorie: Concatenarea==<br />
<br />
Pentru o mai usoara conectare a firelor sau pentru o mai buna organizare si expresivitate a codului, de multe ori este util ca fire individuale sa fie grupate impreuna sau ca un bus de mai multi biti sa fie separat in fire individuale. Acest lucru se face folosind concatenarea, prin simbolurile "{ }".<br />
<br />
Un exemplu de folosire a concatenarii este oferit mai jos.<br />
Un sumator pe 8b are rezultatul pe maxim 9b, in cazul in care ambele numere sunt mari. Astfel apare un bit de carry out. <br />
<syntaxhighlight lang="Verilog"><br />
module sumator ( <br />
input wire [7:0] in0,<br />
input wire [7:0] in1,<br />
output wire [7:0] out0,<br />
output wire carry_out<br />
);<br />
<br />
assign {carry_out,out0} = in0 + in1; // fac suma intre in0 si in1 iar rezultatul il pun pe cei 9b alcatuiti din: 1b carry_out si 8b out0, in ordinea asta<br />
<br />
endmodule <br />
</syntaxhighlight><br />
Se pot concatena oricat de multe semnale, puse in "{ }" si separate prin virgula. Atentie la dimensiunile firelor care se concateneaza.<br />
<br />
Se poate de asemenea face concatenare si la dreapta egalului, astfel:<br />
<syntaxhighlight lang="Verilog"><br />
assign fir_pe_10_b = {fir_pe_3b,fir_pe_5b,fir_pe_1b,fir_pe_1b};<br />
</syntaxhighlight><br />
In exemplul anterior ultimi 2b ai "fir_pe_10_b" vor avea mereu aceeasi valoare, provenind din acelasi fir. Sintaxa Verilog permite asta.<br />
<br />
Puteti folosii concatenarea, in exercitiul 2, la flag-ul de overflow.<br />
<br />
==Teorie: Constante ca intrari in circuite==<br />
In cazul in care se doreste scrierea unor constante la intrarea unor module, acestea se pun direct intre paranteze la instantiere. <br />
De exemplu, pentru multiplexorul de jos din subiectul "alu structural", intrarea "in3" este conectata la valoarea "1". Sintaxa pentru aceasta este: ".in3(1),". <br />
<br />
<br />
==Teorie: "_"==<br />
Simbolul "_" (underscore) este ignorat de verilog si ajuta vizual la citirea semnalelor pe mai multi biti. De exemplu 16'b1010010111110000 este identic cu 16'b1010_0101_1111_0000, al doilea fiind totusi mai usor de citit.<br />
<br />
<br />
==Exercitii==<br />
===Exercitiul 1: ALU - descriere comportamentala===<br />
Descrieți comportamental o unitate aritmetico-logică (ALU) care are la intrare 2 numere binare de câte 8 biți și poate calcula următoarele funcții:<br />
:- suma celor două numere<br />
:- diferența celor două numere<br />
:- operații logice bit cu bit (bitwise): SI, SAU, XOR și inversele lor<br />
:- operandul din stanga trece neschimbat<br />
:- operandul din dreapta trece neschimbat<br />
:- numărul din stânga este deplasat la stânga cu nr. de poziții indicat de numărul din dreapta<br />
:- numărul din stânga este deplasat la dreapta cu nr. de poziții indicat de numărul din dreapta<br />
<br />
Funcția executată la un anumit moment este determinată de configurația binară de pe intrarea de comandă (function).<br />
<br />
ALU are de asemenea intrare de carry și ieșire de carry, plus alte două ieșiri - indicatori - pentru cazurile când rezultatul este zero și când cei doi operanzi sunt egali.<br />
<br />
Intrarea de control va fi facuta pe 4 biti ca sa permita existenta a 16 operatii posibile. Fiecare numar de pe intrarea de control (combinatie de 0 si 1) va reprezenta o operatie din cele de mai sus. Scrierea se va face folosind un bloc "case" in functie de aceasta intrare. <br />
<br />
<br />
<br />
===Exercitiul 2: ALU - descriere structurala===<br />
<br />
[https://wiki.dcae.pub.ro/images/8/8f/Subiect_big_alu.pdf subiect_alu.pdf]<br />
<br />
Daca se doreste selectarea doar a anumitor biti dintr-un bus (cum se vrea din instruction) acest lucru se poate face in 2 feluri:<br />
<br />
a) cu fir aditional:<br />
:''wire [1:0] fir_aditional1;''<br />
:''assign fir_aditional1 = instruction[11:10];''<br />
:// apoi la instantiere: ''.sel(fir_aditional1),''<br />
<br />
b) direct in instantiere;<br />
:la instantierea celor 2 mux4 din stanga, direct: <br />
::''.sel(instruction[11:10]),''<br />
<br />
Pentru a scrie mai putin (a nu face toate modulele de calcul separat + instantierea lor) si pentru ca e vorba de operatii simple, se poate pune operatia cu assign direct in top pe un fir declarat acolo, sau si mai prescurtat, direct operatia dorita cand se intantiaza modulul in care aceasta intra. De exemplu, pentru operatia de shift la dreapta care intra in primul multiplexor, se poate pune: ".in0(data0 >> data1),". <br />
<br />
<br />
===Exercitiul 3:===<br />
<br />
[https://wiki.dcae.pub.ro/images/3/3a/Subiect_muxes.pdf subiect_muxes.pdf]<br />
<br />
<br />
===Exercitiul 4:===<br />
<br />
[https://wiki.dcae.pub.ro/images/2/2e/Subiect_rom_luts.pdf rom_luts.pdf]<br />
<br />
<br />
<br />
<br />
<br />
===Exercitiul 5: Sumator cu reprezentare in cifre zecimale===<br />
Proiectati si verificati un sumator zecimal pentru numere cu 2 cifre<br />
<br />
Modulul de top (Figura 1) este alcatuit din 2 blocuri de tip '''digitsum'''. Fiecare bloc aduna cifrele de pe aceasi pozitie.<br />
<br />
<br />
'''Figura 1''' <br />
Top level<br />
<br />
[[Fișier: bcdsum.png]]<br />
<br />
Blocul '''digitsum''' (Figura 2) este alctuit din 4 blocuri, 2 sumatoare binare pe 4 biti (de tip '''sum4'''), o instanta de '''cmp''' si un multiplexor elementar mux2<br />
<br />
<br />
'''Figura 2''' <br />
Blocul DIGIT SUM <br />
<br />
[[Fișier: digit.png]]<br />
<br />
Primul sumator aduna cifrele din domeniul [0...9] avand un rezultat intre [0 ... 18] <br />
<br />
Comparatorul are la iesire 1 daca rezultatul este mai mare decat 9.<br />
<br />
Daca rezultatul este mai mic decat 9, acesta este trimis in mod direct la iesirea '''digit''' a blocului '''digitsum'''.<br />
<br />
Daca rezultatul este mai mare decat 9, o crectie este necesara si se aduna 6 la rezultat.<br />
<br />
Comparatorul cu valoarea 9 este descris structural din porti in figura de mai jos: <br />
<br />
<br />
'''Figure 3''' <br />
the comparator <br />
<br />
[[Fișier: cmp.png]]<br />
<br />
Testbench-ul va genera stimuli pentru '''bcdsum''' ca in Figura 4.<br />
<br />
Intrarea b0 a '''bcdsum''' se schimba la fiecare 5 pasi de simulare .<br />
<br />
b1, a0 si a1 se schimba sincron cu b0 ca in Figura 4.<br />
<br />
'''Figure 4'''<br />
<br />
[[Fișier: teststimuli.png]]<br />
<br />
Cerinte:<br />
# '''cmp'''- descris structural la nivel de porti ca in Figura 3.<br />
# '''sum4''' descris comportamental cu un assign continuu.<br />
# '''mux''' descris comportamental cu always.<br />
# '''digitsum''' descris strctural ca in Figura 2.<br />
# modulul de top numit '''bcdsum''', descris structural ca in Figure 1.<br />
# modulul de testbench, '''bcdsum_tb''', instantiaza '''bcdsum''' cu numele '''dut'''.<br />
# in testbench, generati stimuli pentru intrarile circuitului testat.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_3_:_Circuite_combinationale_elementare&diff=7515CID aplicatii 3 : Circuite combinationale elementare2023-03-20T21:50:29Z<p>Mihai.antonescu: /* Exemplu */</p>
<hr />
<div><br />
==Teorie==<br />
Acest laborator are ca scop scrierea unor circuite combinationale simple si explorarea variantelor de sintaxa diferite ce ajung sa ofere aceeasi functionalitate. Sintaxa va fi explicata in comentariile exemplului rezolvat mai jos.<br />
<br />
Descrierea circuitelor in Verilog se face in una din doua forme:<br />
<br />
:1) structurala - descrie cum un modul e alcatuit din module sau primitive mai simple<br />
<br />
:2) comportamentala/behavioural - descrie cum se calculeaza iesirile din intrari<br />
<br />
<br />
<br />
==Exemplu - Decodorul==<br />
Decodorul este un circuit a carui iesire pe n biti are un singur bit de "1", anume cel selectat prin singura sa intrare pe log2(n) biti. <br />
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: <br />
<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| in(3b) || out(8b) <br />
|- bgcolor="#ddffdd" align="center"<br />
|000 || 0000_0001<br />
|- bgcolor="#ddffdd" align="center"<br />
|001 || 0000_0010<br />
|- bgcolor="#ddffdd" align="center"<br />
|010 || 0000_0100<br />
|- bgcolor="#ddffdd" align="center"<br />
|011 || 0000_1000<br />
|- bgcolor="#ddffdd" align="center"<br />
|100 || 0001_0000<br />
|- bgcolor="#ddffdd" align="center"<br />
|101 || 0010_0000<br />
|- bgcolor="#ddffdd" align="center"<br />
|110 || 0100_0000<br />
|- bgcolor="#ddffdd" align="center"<br />
|111 || 1000_0000<br />
|}<br />
<br />
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".<br />
<br />
'''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).<br />
<br />
<br />
Circuitul exemplu al acestui laborator va fi un decodor mai mic, cu 2 biti de intrare si 4 de iesire, avand tabelul de adevar:<br />
<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| in(2b) || out(4b) <br />
|- bgcolor="#ddffdd" align="center"<br />
|00 || 0001<br />
|- bgcolor="#ddffdd" align="center"<br />
|01 || 0010<br />
|- bgcolor="#ddffdd" align="center"<br />
|10 || 0100<br />
|- bgcolor="#ddffdd" align="center"<br />
|11 || 1000<br />
|}<br />
<br />
O varianta de schema care implementeaza acest circuit este cea de mai jos: <br />
<br />
[[Fișier:w3_exemplu_rezolvat.png | 600px]]<br />
<br />
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 foarte mari sau fire care se propaga in multe intrari si probleme de cadere a tensiunilor, se folosesc astfel de buffere/repetoare. <br />
<br />
Codul de mai jos exemplifica moduri diferite de a exprima aceasta functionalitate.<br />
El contine comentarii referitoare la sintaxa de baza Verilog, necesara implementarii modulelor in diverse moduri. <br />
<br />
'''Descrierea modulului decodor - varianta structurala din porti, cu primitive (fisierul decodor_v1.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v1<br />
(<br />
input wire [1:0] in, // definirea intrarilor sub forma de "bus" <br />
// bus-ul de intrare se numeste "in" si este pe 2 biti: in[1], in[0]<br />
// fizic sunt 2 fire distincte, grupate pentru o mai simpla folosire.<br />
output wire [3:0] out // avem o iesire, numita "out" pe 4 biti.<br />
);<br />
<br />
wire in_0_inv;<br />
wire in_1_inv;<br />
wire in_0_nat;<br />
wire in_1_nat;<br />
<br />
not not_gate_0(in_0_inv,in[0]); // aleg bitul 0 al lui in sa intre in aceasta poarta not;<br />
<br />
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.<br />
<br />
not not_gate_2(in_0_nat,in_0_inv);<br />
<br />
not not_gate_3(in_1_nat,in_1_inv);<br />
<br />
and and_gate_0(out[0],in_1_inv,in_0_inv);<br />
<br />
and and_gate_1(out[1],in_1_inv,in_0_nat);<br />
<br />
and and_gate_2(out[2],in_1_nat,in_0_inv);<br />
<br />
and and_gate_3(out[3],in_1_nat,in_0_nat);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului decodor - varianta cu assign, urmand schema (fisierul decodor_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v2<br />
(<br />
input wire [1:0] in,<br />
output wire [3:0] out <br />
);<br />
<br />
// fiecare bit din iesire calculat independent<br />
assign out[0] = ~in[0] & ~in[1];<br />
assign out[1] = in[0] & ~in[1];<br />
assign out[2] = ~in[0] & in[1];<br />
assign out[3] = in[0] & in[1];<br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului decodor - varianta cu assign, cu operatorul "?" (fisierul decodor_v3.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v3<br />
(<br />
input wire [1:0] in,<br />
output wire [3:0] out <br />
);<br />
<br />
// operatorul "?": identic c/c++; (conditie) ? (if_true) : (if_flase);<br />
<br />
assign out = (in==0) ? // if in == 0 <br />
1 : // if true: out = 1 <br />
(in==1) ? // else, verifica alt if. <br />
2 : // if in !=0 si in == 1<br />
(in==2) ?<br />
4 :<br />
8; // last case in==3<br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului decodor - varianta cu always cu if (fisierul decodor_v4.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v4<br />
(<br />
input wire [1:0] in,<br />
output reg [3:0] out // <<== !!!! important: orice semnal ia valoarea intrun "always", trebuie sa fie reg<br />
); // always => reg ; assign => wire<br />
<br />
always @(*) // mereu cand se schimba ce se afla in paranteze, se executa (ish "executa") ce e mai jos<br />
// steluta inseamna "orice"<br />
// 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"<br />
// ce este intre paranteze se numeste lista de senzitivitati<br />
begin // begin si end pe post de acolade din c/c++; delimiteaza if/else si altele asemenea; inclusiv always cu totul<br />
if(in == 0)<br />
begin // nota autorului: eu prefer sa pun begin/end asa. ele se pot pune si dupa conditie sau aliniate altfel.<br />
// ^ preferinta personala legata de coding style, important e sa fiti consecventi in cum scrieti<br />
out = 1; <br />
end<br />
else<br />
begin<br />
if(in == 1)<br />
out = 2; // fiind o singura instructiune, begin/end se poate omite (identic c/c++)<br />
else<br />
begin // nota autorului: eu personal le pun mereu. in caz ca mai adaug linii in if dupa, sa fie deja puse<br />
if(in == 2)<br />
begin<br />
out = 4;<br />
end<br />
else<br />
begin<br />
out = 8;<br />
end<br />
end<br />
end<br />
end//end pt always<br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului decodor - varianta cu always cu case (fisierul decodor_v5.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v5<br />
(<br />
input wire [1:0] in,<br />
output reg [3:0] out <br />
);<br />
<br />
always@(*)<br />
begin<br />
case(in) // functioneaza ca switch/case din c/c++; sintaxa usor diferita<br />
0: // if in == 0<br />
begin<br />
out = 1;<br />
end<br />
2'd1: // if in == 1; 2'd1 se traduce: pe 2 biti, in baza zece (eng: decimal, "d"), valoarea 1<br />
begin // pt "0" de mai sus: daca nu se pune nimic se considera in baza zece.<br />
out = 2;<br />
end<br />
2'b10: // if in == 2; scris in binar "b", 2'b10 => adica valoarea 2(10 binar) pe 2 biti <br />
begin<br />
out = 4;<br />
end<br />
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.<br />
begin<br />
out = 8;<br />
end<br />
default: // daca in == un caz netrat; <br />
begin // aici inutil pt ca am tratat toate cazurile posibile; pus ca exemplu de sintaxa <br />
out = 0;<br />
end<br />
endcase // orice "case" se inchide cu "endcase"<br />
end <br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului decodor - varianta cu assign comportamental (fisierul decodor_v6.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module decodor_v6<br />
(<br />
input wire [1:0] in,<br />
output wire [3:0] out <br />
);<br />
<br />
assign out = 1 << in; <br />
// prin shiftare<br />
// 1<<0 == 1 == 4'b0001; <br />
// 1<<1 == 2 == 4'b0010; <br />
// 1<<2 == 4 == 4'b0100; <br />
// 1<<3 == 8 == 4'b1000; <br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
'''Descrierea modulului top (fisierul top.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
// observatie: <br />
// puteti da in vivado: rtl analysis -> open elaborated design -> schematic pentru a vedea desenul rezultat<br />
// puteti vedea ca moduri diferite de scriere produc aparitia a diferite primitive de sinteza<br />
module top<br />
(<br />
input wire [1:0] in,<br />
output wire [3:0] out_v1, // fiecare varianta de decodor cu iesirea lui<br />
output wire [3:0] out_v2, // toate ar trebui sa scoata acelasi rezultat <br />
output wire [3:0] out_v3,<br />
output wire [3:0] out_v4,<br />
output wire [3:0] out_v5,<br />
output wire [3:0] out_v6<br />
);<br />
<br />
decodor_v1 decodor_v1_0<br />
(<br />
.in(in),<br />
.out(out_v1)<br />
);<br />
<br />
decodor_v2 decodor_v2_0<br />
(<br />
.in(in),<br />
.out(out_v2)<br />
);<br />
<br />
decodor_v3 decodor_v3_0<br />
(<br />
.in(in),<br />
.out(out_v3)<br />
);<br />
<br />
decodor_v4 decodor_v4_0<br />
(<br />
.in(in),<br />
.out(out_v4)<br />
);<br />
<br />
decodor_v5 decodor_v5_0<br />
(<br />
.in(in),<br />
.out(out_v5)<br />
);<br />
<br />
decodor_v6 decodor_v6_0<br />
(<br />
.in(in),<br />
.out(out_v6)<br />
);<br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/c/cb/W3_exemplu_decodor.zip W3_exemplu_decodor.zip]<br />
<br />
==Exercitii==<br />
===Exercitiul 1: Multiplexorul===<br />
<br />
Multiplexorul (mux) este un circuit ce are rolul de a selecta una dintre intrari pentru ca aceasta sa ajunga la iesire.<br />
<br />
Imaginati-va o intersectie in care toate masinile vor sa se iasa 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.<br />
Multiplexorul poate fi de asemenea vazut ca un comutator.<br />
<br />
'''Observatie 1:''' conceptul de baza al multiplexarii este absolut vital si o sa apara des in multe zone ale ingineriei si la materii foarte diferite. <br />
Ca niste mici exemple, el se foloseste in:<br />
:a) telecomunicatii - cine are voie sa comunice pe un fir la un moment de timp <br />
:b) internet - routere - cine trimite pachetele tcp/ip catre destinatia x<br />
:c) panouri cu leduri - aprindere pe rand a ledurilor (de la instalatii de craciun pana la ecrane/televizoare care aprind randurile alternativ)<br />
<br />
'''Observatie 2:''' numarul de intrari maxim posibile este 2^(nr_biti_selectie)<br />
<br />
'''Observatie 3:''' multiplexoarele pot avea intrarile date si iesirea pe mai multi biti.<br />
<br />
'''Observatie 4:''' este de ajutor sa incepeti sa vedeti circuitele ca avand o cale de date ce sunt procesate si o cale de control care decide cum se face asta.<br />
Aici calea de date este alcatuita din intrari si iesire, iar calea de control din firele de selectie. <br />
Acest principiu se propaga si in arhitectura microprocesoarelor unde vor aparea magistrale si memorii de date/instructiuni.<br />
<br />
Pornind de la exemplul oferit, implementati multiplexorul in cat mai multe variante.<br />
<br />
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). <br />
<br />
[[Fișier:mux2_general.png | 300px]]<br />
[[Fișier:mux2_schema_interna.png | 300px]]<br />
<br />
Pentru testarea circuitului, generati in testbench urmatoarele forme de unda (liniile punctate reprezinta 10ns): <br />
<br />
[[Fișier:Wavedrom_mux2.png | 500px]]<br />
<br />
<br />
===Exercitiul 2: Implementati un mux4 (multiplexor cu 4 intrari)===<br />
<br />
Interfata lui este ca in figura de mai jos. <br />
<br />
[[Fișier:mux_general.png | 300px]]<br />
<br />
Pentru varianta lui structurala, mux4 se construieste din mai multe mux2 legate corespunzator. Incercati sa construiti voi aceasta schema.<br />
<br />
Pentru testarea circuitului, generati in testbench urmatoarele forme de unda (liniile punctate reprezinta 10ns): <br />
<br />
[[Fișier:Wavedrom_mux4.png | 500px]]<br />
<br />
<br />
===Exercitiul 3: Demultiplexorul===<br />
<br />
Demultiplexorul (demux) este un circuit ce are rolul de a selecta catre care iesire se va duce intrarea. <br />
Imaginati-va o intersectie in care intra o singura masina si trebuie sa decida daca iese catre stanga/inainte/dreapta. <br />
<br />
Pentru acest exercitiu se va implementa un demux2 (cu 2 iesiri).<br />
<br />
'''Observatie 1''': celelalte iesiri, cele neselectate iau valoarea 0.<br />
<br />
'''Observatie 2''': numarul de iesiri maxim posibile este 2^(nr_biti_selectie)<br />
<br />
Interfata demultiplexorului este data mai jos. Incercati sa construiti acest circuit in cat mai multe variante de sintaxa posibile (minim4). <br />
<br />
[[Fișier:demux2_general.png | 300px]]<br />
<br />
Generati forme de unda pentru a vedea functionarea demultiplexorului.<br />
<br />
<br />
===Exercitiul 4: Sumatorul===<br />
<br />
Sumatorul are rolul de a scoate la iesire suma celor 2 intrari.<br />
<br />
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). <br />
<br />
[[Fișier:sumator_8b_general.png | 300px]]<br />
<br />
Observatie: desi scrierea cu assign si + este cea mai usoara si clara pentru un sumator, si celelalte moduri de scriere sunt posibile.<br />
La aceasta scriere, daca se doresta ca sumatorul sa aiba 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. <br />
<br />
Generati formele de unda necesare testarii sumatorului pe 8b.<br />
<br />
Pentru a vedea pas cu pas cum se construieste un sumator pe 8b puteti parcurge urmatoarea platforma de laborator: [https://wiki.dcae.pub.ro/images/0/0f/Cid_gan_sumator_aplicatia_2.pdf Sumator.pdf]<br />
<br />
<br />
===Exercitiul 5: Comparatorul===<br />
<br />
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. <br />
<br />
Astfel el poate avea 3 iesiri dintre care se activeaza doar una: <br />
:- iesire de egalitate<br />
:- iesire de in0 < in1 <br />
:- iesire de in0 > in1 <br />
<br />
<br />
Se mai poate face si o varianta de comparator cu 2 iesiri, asa cum veti studia la arhitectura microprocesoarelor.<br />
:- iesire de egalitate<br />
:- iesire care este 0 daca in0<in1 sau 1 daca in0>in1<br />
<br />
Aceasta abordare este folosita la AMP la flag-urile din procesoare, folosind operatia de scadere si flag-urile de "zero" si "negativ".<br />
<br />
Scrieti modulul de comparator, in ambele variante de interfata. Intrarile pot avea oricat de multi biti (comparator mai mic sau mai mare), pentru acest exercitiu sa il faceti pe 4b.<br />
<br />
Scrieti un testbench care sa genereze toate combinatiile posibile de date de la intrare. <br />
<br />
'''Discutie''': Daca acest comparator are datele pe 32b sau mai mult (sau daca circuitul devine mult mai complex), nu se mai poate face o astfel de testare exhaustiva a combinatiilor posibile de date de la intrare pentru ca simularea ar dura prea mult. De situatia asta se ocupa inginerii de verificare ce lucreaza in paralel cu inginerii de design. Pentru mai multe detalii puteti cauta "constrained random functional verification". Incercati sa identificati cazurile/situatiile ce ar trebui testate pentru un comparator pe 32b inainte de a spune ca acesta functioneaza corect (minim 10).</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_1_:_Generare_de_forme_de_unda&diff=7475CID aplicatii 1 : Generare de forme de unda2023-02-28T10:27:58Z<p>Mihai.antonescu: /* Exercițiul 3 */</p>
<hr />
<div>În această sesiune de aplicații vom învăța cum să generăm semnale digitale cu ajutorul limbajului Verilog. Generarea de semnale este utilizată în testarea prin simulare a circuitelor digitale. Semnalele generate sunt introduse la intrarea circuitului, observându-se apoi cum se modifică starea ieșirilor acestuia.<br />
<br />
Proiectarea circuitelor digitale se bazează pe generarea unor circuite digitale fizice. Deși se va scrie cod în limbajul Verilog, mereu trebuie avut în vedere faptul că în spate se va genera un circuit fizic, codul nu se rulează pas cu pas, aceasta fiind deosebirea fundamentală dintre limbajele de descriere hardware (generează circuite fizice) și limbajele de programare (cod care se execută pas cu pas). Unele concepte cât și unele elemente de sintaxa este posibil să semene cu ce ați facut la programare, dar este important să înțelegeți că prin codul scris se desenează/generează circuite. Sau invers, pornind de la un desen, se poate face o descriere în Verilog a acestuia.<br />
<br />
Proiectele exemplu care pot fi descărcate au fost implementate folosind Vivado 2021.2. Dacă aveți altă versiune este posibil să vă ceară să le salvați în versiunea respectivă sau să fie nevoie să vă faceți un proiect nou, în care să adaugați fișierele cu cod.<br />
<br />
==Noțiuni și cunoștințe necesare==<br />
<br />
* [[Introducere. Verilog HDL]] (Sintaxa [[Verilog]])<br />
<br />
* [[Tutorial Vivado]].<br />
<br />
==Modulul de test (Testbench)==<br />
Modulul de test este un modul nesintetizabil (nu poate fi transformat într-un circuit de către utilitarul care realizează sinteza) și este folosit, așa cum sugerează și numele, în testarea circuitelor. Acest modul este folosit exclusiv în simulare. Simularea permite detecția rapidă a erorilor de implementare și corectarea acestora. Ideea generală a unui modul de test este descrisă în figura de mai jos (considerăm un circuit cu numele ''circuit'' și modulul său de test ''circuit_tb''):<br />
<br />
[[Fișier:CircuitTestbench.png | 400px]]<br />
<br />
După cum se vede în figura de mai sus, modulul de test conține printre altele și un '''generator de semnale de test'''. Acesta are rolul de a genera câte un semnal de test pentru fiecare intrare a circuitului. Forma acestor semnale de test este stabilită de către cel care realizează verificarea și trebuie aleasă astfel încât să detectăm cât mai multe posibile erori.<br />
<br />
==Exemple==<br />
====Exemplul 1: Generarea a doua semnale digitale de 1 bit====<br />
Să se genereze două semnale digitale de 1 bit, care să aibă următoarea variație în timp (unitatea de timp este de 1ns):<br />
<br />
[[Fișier:CID Aplicatii1 ex1.svg|400px]]<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps // setăm unitatea de timp la 1ns, cu o precizie de 1ps<br />
<br />
module waveform1(); // modulul se numește waveform1 și nu are nicio intrare și nicio ieșire. Semnalele de test generate sunt semnale interne.<br />
<br />
reg a, b; // cele două semnale de test sunt modificate într-un bloc de tip initial și trebuie declarate ca elemente de tip reg.<br />
<br />
initial begin<br />
$monitor("time = %2d, a = %b, b=%b", $time, a, b); // monitorizăm în consolă starea semnalelor a si b<br />
a = 0; // semnalul a va avea valoarea 0 la momentul inițial de timp (la momentul t = 0)<br />
b = 0; // semnalul b va avea valoarea 0 la momentul inițial de timp (la momentul t = 0)<br />
#1 a = 1; // după 1ns de la momentul inițial, a se face 1<br />
#1 b = 1; // după 2ns de la momentul inițial, b se face 1<br />
#1 a = 0; // după 3ns de la momentul inițial, a se face 0<br />
b = 0; // după 3ns de la momentul inițial, b se face 0<br />
#2 $stop(); // simularea este oprită<br />
end<br />
<br />
endmodule // incheiem descrierea modulului de generare de semnale digitale<br />
</syntaxhighlight><br />
<br />
'''Observație:''' Atunci când un semnal are dimensiunea de 1 bit, nu va apărea nimic intre tipul acestuia și nume. Atunci când semnalul are o dimensiune mai mare sau egală cu doi biți, dimensiunea va fi descrisă sub forma '''[n-1:0]''', unde '''n''' este numărul de biți. Conform acestei descrieri, bitul '''n-1''' este cel mai semnificativ.<br />
<br />
<syntaxhighlight lang="Verilog"><br />
reg a; //semnal cu dimeniunea de 1 bit<br />
reg [7:0] data; //semnal/magistrală/bus cu dimensiunea de 8 biți<br />
</syntaxhighlight><br />
<br />
====Exemplul 2: Generarea unui semnal periodic de 1 bit====<br />
Să se genereze un semnal digital periodic de 1 bit, cu perioada egală cu 2ns:<br />
<br />
[[Fișier:Clock wave.png | 600px]]<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps // setăm unitatea de timp la 1ns, cu o precizie de 1ps<br />
<br />
module waveform2(); // modulul se numește waveform2 si nu are nicio intrare și nicio iesire<br />
<br />
reg clock; // semnalul de test se va modifica într-un bloc initial, așadar este declarat de tip reg<br />
<br />
initial begin<br />
clock = 0; // valoarea inițială a semnalului va fi 0 (la momentul t = 0)<br />
forever #1 clock = ~clock; // forever indică faptul că ce urmează se va repeta într-o buclă continuă. <br />
// La trecea unui timp egal cu unitatea de timp definită, semnalul clock iși va nega valoarea<br />
end<br />
<br />
initial begin<br />
#20 $stop(); // La 20 de unități de timp (aici, 20 ns), simularea se va opri. <br />
// Aici punem $stop într-un bloc separat, deoarece forever blochează blocul initial în care se află<br />
// Toate blocurile initial încep la același moment de timp (t = 0) și au efect în paralel<br />
end<br />
<br />
endmodule <br />
</syntaxhighlight><br />
<br />
==Exerciții==<br />
====Exercițiul 1====<br />
Generați două semnale digitale '''a''' și '''b''' de 1 bit, astfel încât să se formeze cu ele toate cele 4 combinații posibile, după cum urmează:<br />
<br />
'''t = 0ns''': a = 0, b = 0<br />
'''t = 1ns''': a = 0, b = 1<br />
'''t = 2ns''': a = 1, b = 0<br />
'''t = 3ns''': a = 1, b = 1<br />
<br />
====Exercițiul 2====<br />
Să se genereze un semnal digital de 8 biți aliniat la fronturile crescătoare ale unui semnal periodic (semnal de ceas), ce are perioada de 2ns:<br />
<br />
[[Fișier:Clock and data wave.png | 600px]]<br />
<br />
'''Observație:'''<br />
* Frontul crescător al unui semnal este definit ca tranziția dintre nivelul de 0 logic și 1 logic.<br />
* Spunem că un semnal este aliniat la fronturile crescătoare ale unui alt semnal dacă acesta își modifică valoarea imediat după apariția acelui front crescător. În figura de mai sus, se observă că '''data''' își schimbă valoarea imediat după fiecare front crescător al semnalului '''clock'''.<br />
<br />
'''Indicații''': <br />
<br />
* Așteptarea unui front crescător al semnalului ''NumeSemnal'' se realizează cu '''@(posedge ''NumeSemnal'')'''. Aceasta va înlocui întârzierile de tip '''#n''' folosite anterior.<br />
* Pentru semnalele ce se doresc aliniate la un eveniment de tip '''posedge''', vom folosi atribuirea non-blocantă: de exemplu, '''data <= 1;'''.<br />
* Nu uitați! Folosirea ''forever'' pentru generarea semnalului de ceas va bloca acel bloc ''initial''.<br />
* Incrementarea valorii unui semnal se poate realiza cu un bloc de tip ''for'', inclus in blocul ''initial''. Blocul ''for'' va avea nevoie de un contor de tip ''integer'', declarat în afara blocului ''initial''. Pentru mai multe detalii, consultați tutorialul de sintaxa Verilog.<br />
<br />
<br />
Folosind indicațiile anterioare, înlocuiți al doilea bloc ''initial'' din '''Exemplul 2''' cu următoarea secvență și completați liniile de cod lipsă:<br />
<syntaxhighlight lang="Verilog"><br />
integer index;<br />
<br />
initial begin<br />
for(index = 0; index < 11; index = index + 1) begin<br />
//completati codul <br />
end<br />
#20 $stop();<br />
end<br />
</syntaxhighlight><br />
<br />
====Exercițiul 3====<br />
Desenați formele de undă ale semnalelor de test ''a'', ''b'' și ''c'', generate cu ajutorul următoarelor două blocuri ''initial'':<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps <br />
<br />
module waveform(); <br />
<br />
reg a, b, c; <br />
<br />
initial begin<br />
a = 0; <br />
b = 0; <br />
#2 b = 1; <br />
#1 a = 1; <br />
#3 b = 0; <br />
#3 a = 0; <br />
#2 $stop(); <br />
end <br />
<br />
initial begin<br />
c = 1; <br />
#2 c = 0; <br />
#3 c = 1; <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
====Exercițiul 4====<br />
Generati un semnal digital '''clock''' cu dimensiunea de 1 bit si perioada de 2ns si un semnal digital '''data''', cu dimensiunea de 4 biti, aliniat la fronturile crescatoare ale semnalului '''clock'''. Semnalul '''data''' va varia astfel: '''0, 1, 2, 3, 0, 1, 2 ,3, 0, 1 ... '''. Opriti simularea dupa ce semnalul '''data''' a realizat 4 variatii complete (s-a generat secventa '''0, 1, 2, 3''' de 4 ori).</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7457CID aplicatii 7 : Circuite secventiale elementare2023-02-18T15:20:57Z<p>Mihai.antonescu: /* Exerciții */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
<br />
==Atribuirea blocanta si non-blocanta==<br />
Pentru ca simulatorul sa functioneze in mod corect si semnalele sa se modifice in simulare asa cum se modifica si in realitate este nevoie sa folosim atribuirea blocanta ( semnul "=") la circuite combinationale si atribuirea non-blocanta (semnul "<=") la circuite secventiale. <br />
<br />
In mod uzual orice always combinational ( '''always@(*)''' ) va folosi '''atribuirea blocanta ( = )''' si orice always pe ceas ( '''always@(posedge clock)''' ) va folosi '''atribuirea non-blocanta ( <= )'''.<br />
<br />
Diferenta dintre cele 2 atribuiri consta in modul in care simulatorul "executa" instructiunea de atribuire. La atribuirea blocanta se simuleaza linie cu linie, in ordinea in care acestea au fost scrise. La atribuirea non-blocanta se salveaza toti termenii din dreapta si se pun deodata (in acelasi pas de simulare) in termenii din stanga. Pentru a clarifica acest concept avem exemplul de mai jos: <br />
<br />
<syntaxhighlight lang="Verilog"><br />
a = b;<br />
b = a;<br />
<br />
c <= d;<br />
d <= c;<br />
</syntaxhighlight><br />
<br />
In primul caz, deoarece am folosit atribuirea blocanta, a ia valoarea lui b si apoi b ia valoarea lui a, care deja a devenit b deci concret b ramane pe loc si deci atat a cat si b vor avea aceasi valoare la final.<br />
<br />
In al doilea caz, deoarece am folosit atribuirea non-blocanta, ce este in partea dreapta se transfera peste operanzii din stanga deodata, astfel c ia valoarea lui d si d ia valoarea lui c, la final avand loc o interschimbare.<br />
<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
[[Fișier:Poza_hazard.png | 800px]]<br />
<br />
In acest exemplu se observa ca iesirea c se modifica la 1ns dupa modificarile intrarilor (intarizerea adaugata pentru transmiterea prin porti) si iesirea d se modifica de 2 ori: prima data la 1ns din cauza intrarilor si a doua oara dupa inca 1ns din cauza lui c. In intervalul [6-7]ns apare o valoare parazita, un hazard.<br />
<br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.<br />
<br />
===Exercițiul 4===<br />
Pentru evidentierea hazardului combinational (si a modului in care acesta se rezolva) descrieti in Verilog si simulati urmatoarele 2 circuite:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_circuit.png | 600px]]<br />
<br />
Formele de unda pentru semnalele de la intrare vor fi:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_forme_unda.png | 600px]]<br />
<br />
===Exercițiul 5===<br />
Pentru evidentierea efectului atribuirii blocant si non-blocante se va implementa si simula circuitul de mai jos. El este alcatuit din 3 registre, fiecare memorand (si punand la iesire) pe frontul pozitiv al ceasului valoarea de la intrare.<br />
<br />
Cele 3 registre de sus vor folosii atribuirea blocanta si cele 3 registre de jos vor folosii atribuirea non-blocanta. In simulare se doreste a se vizualiza: intrarile, iesirile, firele interne de legatura dintre registrii.<br />
<br />
Pentru a scrie semnificativ mai putin, nu este nevoie sa instantiati fiecare registru dintro serie ca modul separat ci se pot face toate cele 3 atribuiri in cadrul aceluiasi bloc "always", la nivelul de top. Practic tot circuitul se reduce la un fisier de top ce contine intrari/iesiri, declararea registrilor pe 1b si cele 2 always-uri care dicteaza functionarea.<br />
<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_5_circuit.png | 600px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Poza_circ_secventiale_basic_exercitiu_5_circuit.png&diff=7456Fișier:Poza circ secventiale basic exercitiu 5 circuit.png2023-02-18T15:13:17Z<p>Mihai.antonescu: </p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7455CID aplicatii 7 : Circuite secventiale elementare2023-02-18T14:25:13Z<p>Mihai.antonescu: /* Exerciții */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
<br />
==Atribuirea blocanta si non-blocanta==<br />
Pentru ca simulatorul sa functioneze in mod corect si semnalele sa se modifice in simulare asa cum se modifica si in realitate este nevoie sa folosim atribuirea blocanta ( semnul "=") la circuite combinationale si atribuirea non-blocanta (semnul "<=") la circuite secventiale. <br />
<br />
In mod uzual orice always combinational ( '''always@(*)''' ) va folosi '''atribuirea blocanta ( = )''' si orice always pe ceas ( '''always@(posedge clock)''' ) va folosi '''atribuirea non-blocanta ( <= )'''.<br />
<br />
Diferenta dintre cele 2 atribuiri consta in modul in care simulatorul "executa" instructiunea de atribuire. La atribuirea blocanta se simuleaza linie cu linie, in ordinea in care acestea au fost scrise. La atribuirea non-blocanta se salveaza toti termenii din dreapta si se pun deodata (in acelasi pas de simulare) in termenii din stanga. Pentru a clarifica acest concept avem exemplul de mai jos: <br />
<br />
<syntaxhighlight lang="Verilog"><br />
a = b;<br />
b = a;<br />
<br />
c <= d;<br />
d <= c;<br />
</syntaxhighlight><br />
<br />
In primul caz, deoarece am folosit atribuirea blocanta, a ia valoarea lui b si apoi b ia valoarea lui a, care deja a devenit b deci concret b ramane pe loc si deci atat a cat si b vor avea aceasi valoare la final.<br />
<br />
In al doilea caz, deoarece am folosit atribuirea non-blocanta, ce este in partea dreapta se transfera peste operanzii din stanga deodata, astfel c ia valoarea lui d si d ia valoarea lui c, la final avand loc o interschimbare.<br />
<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
[[Fișier:Poza_hazard.png | 800px]]<br />
<br />
In acest exemplu se observa ca iesirea c se modifica la 1ns dupa modificarile intrarilor (intarizerea adaugata pentru transmiterea prin porti) si iesirea d se modifica de 2 ori: prima data la 1ns din cauza intrarilor si a doua oara dupa inca 1ns din cauza lui c. In intervalul [6-7]ns apare o valoare parazita, un hazard.<br />
<br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.<br />
<br />
===Exercițiul 4===<br />
Pentru evidentierea hazardului combinational (si a modului in care acesta se rezolva) descrieti in Verilog si simulati urmatoarele 2 circuite:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_circuit.png | 600px]]<br />
<br />
Formele de unda pentru semnalele de la intrare vor fi:<br />
<br />
[[Fișier:Poza_circ_secventiale_basic_exercitiu_4_forme_unda.png | 600px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Poza_circ_secventiale_basic_exercitiu_4_forme_unda.png&diff=7454Fișier:Poza circ secventiale basic exercitiu 4 forme unda.png2023-02-18T14:23:28Z<p>Mihai.antonescu: Mihai.antonescu a încărcat o versiune nouă pentru Fișier:Poza circ secventiale basic exercitiu 4 forme unda.png</p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Poza_circ_secventiale_basic_exercitiu_4_forme_unda.png&diff=7453Fișier:Poza circ secventiale basic exercitiu 4 forme unda.png2023-02-18T14:22:00Z<p>Mihai.antonescu: </p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Poza_circ_secventiale_basic_exercitiu_4_circuit.png&diff=7452Fișier:Poza circ secventiale basic exercitiu 4 circuit.png2023-02-18T14:15:02Z<p>Mihai.antonescu: </p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7451CID aplicatii 7 : Circuite secventiale elementare2023-02-18T13:51:04Z<p>Mihai.antonescu: /* Exemple */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
<br />
==Atribuirea blocanta si non-blocanta==<br />
Pentru ca simulatorul sa functioneze in mod corect si semnalele sa se modifice in simulare asa cum se modifica si in realitate este nevoie sa folosim atribuirea blocanta ( semnul "=") la circuite combinationale si atribuirea non-blocanta (semnul "<=") la circuite secventiale. <br />
<br />
In mod uzual orice always combinational ( '''always@(*)''' ) va folosi '''atribuirea blocanta ( = )''' si orice always pe ceas ( '''always@(posedge clock)''' ) va folosi '''atribuirea non-blocanta ( <= )'''.<br />
<br />
Diferenta dintre cele 2 atribuiri consta in modul in care simulatorul "executa" instructiunea de atribuire. La atribuirea blocanta se simuleaza linie cu linie, in ordinea in care acestea au fost scrise. La atribuirea non-blocanta se salveaza toti termenii din dreapta si se pun deodata (in acelasi pas de simulare) in termenii din stanga. Pentru a clarifica acest concept avem exemplul de mai jos: <br />
<br />
<syntaxhighlight lang="Verilog"><br />
a = b;<br />
b = a;<br />
<br />
c <= d;<br />
d <= c;<br />
</syntaxhighlight><br />
<br />
In primul caz, deoarece am folosit atribuirea blocanta, a ia valoarea lui b si apoi b ia valoarea lui a, care deja a devenit b deci concret b ramane pe loc si deci atat a cat si b vor avea aceasi valoare la final.<br />
<br />
In al doilea caz, deoarece am folosit atribuirea non-blocanta, ce este in partea dreapta se transfera peste operanzii din stanga deodata, astfel c ia valoarea lui d si d ia valoarea lui c, la final avand loc o interschimbare.<br />
<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
[[Fișier:Poza_hazard.png | 800px]]<br />
<br />
In acest exemplu se observa ca iesirea c se modifica la 1ns dupa modificarile intrarilor (intarizerea adaugata pentru transmiterea prin porti) si iesirea d se modifica de 2 ori: prima data la 1ns din cauza intrarilor si a doua oara dupa inca 1ns din cauza lui c. In intervalul [6-7]ns apare o valoare parazita, un hazard.<br />
<br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Poza_hazard.png&diff=7450Fișier:Poza hazard.png2023-02-18T13:48:22Z<p>Mihai.antonescu: </p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale&diff=7449Circuite Integrate Digitale2023-02-18T13:28:13Z<p>Mihai.antonescu: /* Tutoriale și documentații */</p>
<hr />
<div>== Scopul laboratorului ==<br />
<br />
<br />
Laboratorul de circuite integrate digitale vine ca o completare practica a cursului de CID. <br />
Prin definitie, acesta se va axa pe elemente practice de simulare, sinteza si testare a circuitelor ce sunt prezentate la curs. <br />
<br />
Acest laborator se bazeaza pe limbajul "Verilog", acesta fiind unul din cele 2 limbaje de descriere hardware folosite in industrie la ora actuala (celalalt fiind "VHDL"). <br />
<br />
Platformele sunt structurate astfel incat sa contina cateva elemente de teorie minimala, un exemplu de circuit descris in cod Verilog si apoi exercitii pe care sa le rezolvati in ora de aplicatii (simulare/testare pe placa FPGA) si acasa (simulare). <br />
<br />
In laborator se va lucra in Vivado, program al firmei Xilinx, pe o placa PYNQ-Z2 impreuna cu o placa de extensie ce adauga leduri si butoane suplimentare. <br />
<br />
Circuitele digitale sunt o parte fundamentala a electronici moderne, cu numeroase oportunitati in industrie, atat in design cat si in verificarea circuitelor.<br />
<br />
Pe langa ce veti invata in acest laborator, limbajul ofera si capabilitati mai avansate pentru a descrie mai eficient circuitele dorite, cat si pentru testarea acestora. Sintaxa nu se termina cu ce invatati aici si pentru cei pasionati, nu ezitati sa va contactati cadrul didactic cu intrebari.<br />
<br />
Speram ca o sa va placa, veti invata si vi se va parea interesant ce veti vedea in orele de aplicatii ce urmeaza. Spor.<br />
<br />
<br />
== Tutoriale și documentații ==<br />
<br />
Programul Vivado este gratuit (necesita cont, dar este gratis) si poate fi descarcat si instalat urmand tutorialul de aici:<br />
# [[Tutorial instalare Vivado]]<br />
# [[Tutorial Vivado]].<br />
# [https://www.tulembedded.com/FPGA/ProductsPYNQ-Z2.html Pynq-Z2 - pagina oficiala]<br />
# [https://dpoauwgwqsy2x.cloudfront.net/Download/pynqz2_user_manual_v1_0.pdf Pynq-Z2 - user manual]<br />
# [[Pynq-Z2 - Pinout]].<br />
# [https://www.realdigital.org/doc/02013cd17602c8af749f00561f88ae21 Boolean Board - user manual]<br />
# [[Boolean Board - Pinout]].<br />
# [[FPGA - Introducere]].<br />
<br />
O lista de link-uri utile poate fi gasita aici:<br />
# [[Introducere. Verilog HDL]] (Sintaxa [[Verilog]])<br />
# [https://www.asic-world.com/digital/index.html Asic-world - digital]<br />
# [https://www.asic-world.com/verilog/index.html Asic-world - verilog]<br />
# [https://nandgame.com/ Joc online: Nandgame] <br />
# [https://circuitverse.org/simulator Simulator/joc online si grafic de circuite: Circuitverse] <br />
# [https://falstad.com/circuit/ Simulator online si grafic de circuite (analog+digital): Falstad]<br />
# [https://github.com/hneemann/Digital Simulator grafic de circuite digitale: hneemann-digital]<br />
# [https://play.google.com/store/apps/details?id=com.ViacheslavRud.Circuit&hl=en_US&gl=US Joc android - Make it True: Solve the Circuit]<br />
# [https://www.edaplayground.com/ Simulatoare online (mod text): Edaplayground]<br />
# [https://wavedrom.com/ Utilitar desenare forme de unda]<br />
# [https://www.draw.io/ Utilitar desenare diagrame]<br />
# [[Domenii conexe]]<br />
<br />
== Lucrări de laborator ==<br />
<br />
# [[CID_aplicatii_1 : Generare de forme de unda]]<br />
# [[CID_aplicatii_2 : Instantiere si porti logice]]<br />
# [[CID_aplicatii_3 : Circuite combinationale elementare]]<br />
# [[CID_aplicatii_4 : Alte circuite combinationale]]<br />
# [[CID_aplicatii_5 : Exercitii cu circuite combinationale]]<br />
# CID_aplicatii_6 : Lucrarea 1 - circuite combinationale<br />
# [[CID_aplicatii_7 : Circuite secventiale elementare]]<br />
# [[CID_aplicatii_8 : Registre si memorii RAM]]<br />
# [[CID_aplicatii_9 : Numaratorul]]<br />
# [[CID_aplicatii_10 : Aplicatii cu numaratoare]]<br />
# [[CID_aplicatii_11 : Automate finite]]<br />
# [[CID_aplicatii_12 : Exercitii cu circuite secventiale]]<br />
# CID_aplicatii_13 : Lucrarea 2 - circuite secventiale<br />
# [[CID_aplicatii_14 : Circuite digitale complexe]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7448CID aplicatii 7 : Circuite secventiale elementare2023-02-17T21:48:11Z<p>Mihai.antonescu: </p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
<br />
==Atribuirea blocanta si non-blocanta==<br />
Pentru ca simulatorul sa functioneze in mod corect si semnalele sa se modifice in simulare asa cum se modifica si in realitate este nevoie sa folosim atribuirea blocanta ( semnul "=") la circuite combinationale si atribuirea non-blocanta (semnul "<=") la circuite secventiale. <br />
<br />
In mod uzual orice always combinational ( '''always@(*)''' ) va folosi '''atribuirea blocanta ( = )''' si orice always pe ceas ( '''always@(posedge clock)''' ) va folosi '''atribuirea non-blocanta ( <= )'''.<br />
<br />
Diferenta dintre cele 2 atribuiri consta in modul in care simulatorul "executa" instructiunea de atribuire. La atribuirea blocanta se simuleaza linie cu linie, in ordinea in care acestea au fost scrise. La atribuirea non-blocanta se salveaza toti termenii din dreapta si se pun deodata (in acelasi pas de simulare) in termenii din stanga. Pentru a clarifica acest concept avem exemplul de mai jos: <br />
<br />
<syntaxhighlight lang="Verilog"><br />
a = b;<br />
b = a;<br />
<br />
c <= d;<br />
d <= c;<br />
</syntaxhighlight><br />
<br />
In primul caz, deoarece am folosit atribuirea blocanta, a ia valoarea lui b si apoi b ia valoarea lui a, care deja a devenit b deci concret b ramane pe loc si deci atat a cat si b vor avea aceasi valoare la final.<br />
<br />
In al doilea caz, deoarece am folosit atribuirea non-blocanta, ce este in partea dreapta se transfera peste operanzii din stanga deodata, astfel c ia valoarea lui d si d ia valoarea lui c, la final avand loc o interschimbare.<br />
<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_11_:_Automate_finite&diff=7447CID aplicatii 11 : Automate finite2023-02-17T19:35:44Z<p>Mihai.antonescu: /* Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal */</p>
<hr />
<div>== Automate finite ==<br />
<br />
Automatul finit este folosit pentru a descrie sisteme ce tranziteaza un numar finit de stari. Acest tip de sistem este foarte util atunci cand starile nu sunt tranzitate intr-un mod simplu, evident sau repetitiv. <br />
<br />
Automatul finit, numit și FSM (Finite State Machine), este un model matematic de computație, definit de următoarele elemente:<br />
* O mulțime finită de simboluri de intrare, numită alfabet de intrare;<br />
* O mulțime finită și nevidă de simboluri de ieșire, numită alfabet de ieșire;<br />
* O mulțime finită și nevidă de stări, din care doar una, numită starea curentă, este activă la un moment dat;<br />
* O stare inițială, care face parte din mulțimea stărilor;<br />
* O funcție de tranziție a stărilor, care calculează starea următoare a automatului în funcție de starea curentă și de simbolul de intrare;<br />
* O funcție de calcul a ieșirii, care determină simbolul de ieșire în funcție de starea curentă (în cazul automatelor de tip Moore) sau în funcție de starea curentă și de simbolul de intrare (în cazul automatelor de tip Mealy).<br />
<br />
[[[[Fișier:Fsm.png]]]]<br />
<br />
La fiecare front al ceasului, valoarea stării următoare, calculată de către CLC, va fi încărcată în registru. Dacă registrul are n biți, numărul maxim de stări care pot fi reprezentate este 2<sup>n</sup>. Acest număr poate fi mai mic, în funcție de schema de codare a stărilor. De exemplu, pentru n = 4, folosind codarea binară se pot reprezenta cel mult 16 stări (0000, 0001, 0010, ..., 1111), în timp ce pentru codarea one-hot, în care doar un singur bit poate lua valoarea 1, se pot reprezenta 4 stări (1000, 0100, 0010, 0001).<br />
<br />
Pentru a reprezenta comportamentul unui automat finit, putem folosi grafuri sau organigrame, conventiile de notare depinzand de tipul automatului (Moore sau Mealy).<br />
<br />
== Exemple == <br />
=== Exemplul 1: Automat ce detecteaza secvente de tipul 11....100....0===<br />
In acest exemplu va fi implementat un automat care detecteaza secventele de tipul 11....100....0. Pentru aceasta, automatul va avea o intrare pe un bit (''in'', pe care va veni secventa de biti) si doua iesiri: ''detectOk'', care semnalizeaza prin 1 ca nu a fost inca detectata o secventa ilegala si ''detectFail'', care semnalizeaza prin 1 ca a fost detectata o secventa ilegala (un 1 dupa 0). <br />
<br />
Automatul va avea 3 stari: <br />
* Q0 - STATE_READ1: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 1 (inca nu a aparut niciun 0).<br />
* Q1 - STATE_READ0: automatul intra in aceasta stare atunci cand apare pe intrare primul 0 si va ramane in aceasta atata timp cat pe intrare este 0.<br />
* Q2 - STATE_ERROR: automatul intra in aceasta stare atunci cand apare un 1 dupa 0 si va ramane blocat aici pana la reset.<br />
<br />
Pentru a coda cele 3 stari avem nevoie de minim 2 biti: STATE_READ1 = 2'b00, STATE_READ0 = 2'b01, STATE_ERROR = 2'b10.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM 111000.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1(<br />
input clock,<br />
input in,<br />
input reset,<br />
output detectOk,<br />
output detectFail<br />
);<br />
<br />
//Se asociaza sirurilor de biti folositi pentru codarea starilor nume ce pot fi folosite mai usor in cod.<br />
//La compilare, numele vor fi inlocuite in cod cu numerele asociate la inceput.<br />
localparam STATE_READ1 = 2'b00;<br />
localparam STATE_READ0 = 2'b01;<br />
localparam STATE_ERROR = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
//registrul de stare<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= STATE_READ1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
//circuit combinational pentru calculul starii urmatoare<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
STATE_READ1: begin<br />
if(in == 0) state_next = STATE_READ0;<br />
end<br />
STATE_READ0: begin<br />
if(in == 1) state_next = STATE_ERROR;<br />
end<br />
STATE_ERROR: state_next = STATE_ERROR;<br />
default: state_next = STATE_READ1;<br />
<br />
endcase<br />
end<br />
<br />
<br />
//circuit combinational pentru calculul iesirilor<br />
assign detectOk = (state == STATE_READ0) || (state == STATE_READ1);<br />
assign detectFail = (state == STATE_ERROR);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM1'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire detectOk_t;<br />
wire detectFail_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 1;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 0;<br />
#10 in_t = 1;<br />
#10 $stop();<br />
end<br />
<br />
FSM1 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.detectOk(detectOk_t),<br />
.detectFail(detectFail_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal===<br />
Automatul ce detecteaza fronturile crescatoare are un singur semnal de intrare ''in'', care reprezinta semnalul analizat si o singura iesire, ''out'', generand pe aceasta un puls lung cat o perioada de ceas la fiecare aparitie a unui front crescator pe ''in''.<br />
<br />
Vom avea nevoie de 3 stari:<br />
* Q0: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 0 (inca nu a aparut niciun front crescator).<br />
* Q1: automatul intra in aceasta stare atunci cand apare pe intrare un front crescator. Dupa aparitia frontului crescator sunt doua posibilitati: linia ramane in 1, ceea ce duce la trecerea in starea Q2 care va duce iesirea in 0 dupa o perioada de ceas, sau linia trece in 0 dupa un ciclu de ceas, ceea ce duce la revenirea in starea Q0.<br />
* Q2: automatul intra in aceasta stare atunci cand linia de intrare ramane in 1 mai mult de o perioada de ceas. Automatul va ramane in aceasta stare atata timp cat ''in'' ramane in 1 si va reveni in starea Q0 imediat ce ''in'' revine in 0.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM rising edge.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_Moore(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam Q0 = 2'b00;<br />
localparam Q1 = 2'b01;<br />
localparam Q2 = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= Q0;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
Q0: begin<br />
if(in == 1) state_next = Q1;<br />
end<br />
Q1: begin<br />
if(in == 0) state_next = Q0;<br />
if(in == 1) state_next = Q2;<br />
end<br />
Q2: begin<br />
if(in == 0) state_next = Q0;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
assign out = (state == Q1); // iesirea depinde numai de stare => Moore<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_mealy(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam stare_wait_1 = 1'b0;<br />
localparam stare_wait_0 = 1'b1;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= stare_wait_1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
stare_wait_1: begin<br />
if(in == 1) state_next = stare_wait_0;<br />
end<br />
stare_wait_0: begin<br />
if(in == 0) state_next = stare_wait_1;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
// daca pana acum semnalul a fost "0" si astept sa vina "1" si intrarea chiar vine "1" => am avut front crescator<br />
assign out = (state==stare_wait_1) & (in==1); // in apare aici => dependenta iesiri de intrare => Mealy<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM2'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire out_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 0;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 $stop();<br />
end<br />
<br />
FSM2_Moore DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.out(out_t)<br />
);<br />
<br />
endmodule<br />
<br />
</syntaxhighlight><br />
<br />
Se poate observa ca automatele de tip Moore au un numar mai mare de stari iar automatele de tip Mealy au o logica mai complexa pentru calcularea iesirilor.<br />
<br />
==Exerciții==<br />
=== Exercițiul 1 ===<br />
Implementați un automat finit ce detectează fronturile descrescătoare. Acesta are un singur semnal de intrare ''in'', care reprezintă semnalul analizat și o singură ieșire ''out'', generând pe această un puls lung cât o perioadă de ceas la fiecare apariție a unui front descrescător pe ''in''.<br />
<br />
Realizați implementarea Verilog plecând de la graful automatului:<br />
<br />
[[Fișier:Graf FSM falling edge.png]]<br />
<br />
=== Exercițiul 2 ===<br />
Implementați un automat finit ce detectează atât fronturile crescătoare, cât și pe cele descrescătoare.<br />
<br />
=== Exercițiul 3 ===<br />
Să se implementeze un circuit care generează un semnal PWM cu un factor de umplere ce variază triungular, la fiecare front descrescător al unui semnal de intrare.<br />
<br />
Schema circuitului este:<br />
<br />
[[Fișier:FSM Exercitiu.png]]<br />
<br />
Modulele componente ale circuitului sunt:<br />
* '''FallingEdgeDetector''': Automat ce detectează fronturile descrescătoare ale semnalului de intrare.<br />
* '''TriangularCounter''': Automat ce generează la iesire secvența de numere 64, 128, 192, 255, 192, 128, 64.<br />
* '''Counter''': Numărător pe 8 biți cu reset sincron.<br />
* '''COMP''': Comparator cu două intrări pe 8 biți. Ieșirea sa va fi 1 atunci când counter < out și 0 în rest.<br />
<br />
=== Exercițiul 4 ===<br />
Implementați automatul de control al unui aparat care vinde ciocolata.<br />
<br />
O cafea costa 2.5 lei. Automatul accepta monezi de 50 de bani si hartii de 1 leu. Daca se depaseste suma, restul ramane in aparat si va fi folosit la urmatoarea tranzactie. Se accepta introducerea si a unei monezi si a unei bancnote in acelasi timp.<br />
<br />
Intrarile sistemului sunt: clock (1b), reset (1b), in_50_b (1b), in_100_b (1b); iesirile sistemului sunt: give_cioco(1b).<br />
<br />
Implementati automatul de control care va tine minte suma curenta si va da sau nu ciocolata in functie de aceasta. Implementati acest automat in ambele variante: Mealy si Moore.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_11_:_Automate_finite&diff=7446CID aplicatii 11 : Automate finite2023-02-17T19:34:44Z<p>Mihai.antonescu: /* Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal */</p>
<hr />
<div>== Automate finite ==<br />
<br />
Automatul finit este folosit pentru a descrie sisteme ce tranziteaza un numar finit de stari. Acest tip de sistem este foarte util atunci cand starile nu sunt tranzitate intr-un mod simplu, evident sau repetitiv. <br />
<br />
Automatul finit, numit și FSM (Finite State Machine), este un model matematic de computație, definit de următoarele elemente:<br />
* O mulțime finită de simboluri de intrare, numită alfabet de intrare;<br />
* O mulțime finită și nevidă de simboluri de ieșire, numită alfabet de ieșire;<br />
* O mulțime finită și nevidă de stări, din care doar una, numită starea curentă, este activă la un moment dat;<br />
* O stare inițială, care face parte din mulțimea stărilor;<br />
* O funcție de tranziție a stărilor, care calculează starea următoare a automatului în funcție de starea curentă și de simbolul de intrare;<br />
* O funcție de calcul a ieșirii, care determină simbolul de ieșire în funcție de starea curentă (în cazul automatelor de tip Moore) sau în funcție de starea curentă și de simbolul de intrare (în cazul automatelor de tip Mealy).<br />
<br />
[[[[Fișier:Fsm.png]]]]<br />
<br />
La fiecare front al ceasului, valoarea stării următoare, calculată de către CLC, va fi încărcată în registru. Dacă registrul are n biți, numărul maxim de stări care pot fi reprezentate este 2<sup>n</sup>. Acest număr poate fi mai mic, în funcție de schema de codare a stărilor. De exemplu, pentru n = 4, folosind codarea binară se pot reprezenta cel mult 16 stări (0000, 0001, 0010, ..., 1111), în timp ce pentru codarea one-hot, în care doar un singur bit poate lua valoarea 1, se pot reprezenta 4 stări (1000, 0100, 0010, 0001).<br />
<br />
Pentru a reprezenta comportamentul unui automat finit, putem folosi grafuri sau organigrame, conventiile de notare depinzand de tipul automatului (Moore sau Mealy).<br />
<br />
== Exemple == <br />
=== Exemplul 1: Automat ce detecteaza secvente de tipul 11....100....0===<br />
In acest exemplu va fi implementat un automat care detecteaza secventele de tipul 11....100....0. Pentru aceasta, automatul va avea o intrare pe un bit (''in'', pe care va veni secventa de biti) si doua iesiri: ''detectOk'', care semnalizeaza prin 1 ca nu a fost inca detectata o secventa ilegala si ''detectFail'', care semnalizeaza prin 1 ca a fost detectata o secventa ilegala (un 1 dupa 0). <br />
<br />
Automatul va avea 3 stari: <br />
* Q0 - STATE_READ1: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 1 (inca nu a aparut niciun 0).<br />
* Q1 - STATE_READ0: automatul intra in aceasta stare atunci cand apare pe intrare primul 0 si va ramane in aceasta atata timp cat pe intrare este 0.<br />
* Q2 - STATE_ERROR: automatul intra in aceasta stare atunci cand apare un 1 dupa 0 si va ramane blocat aici pana la reset.<br />
<br />
Pentru a coda cele 3 stari avem nevoie de minim 2 biti: STATE_READ1 = 2'b00, STATE_READ0 = 2'b01, STATE_ERROR = 2'b10.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM 111000.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1(<br />
input clock,<br />
input in,<br />
input reset,<br />
output detectOk,<br />
output detectFail<br />
);<br />
<br />
//Se asociaza sirurilor de biti folositi pentru codarea starilor nume ce pot fi folosite mai usor in cod.<br />
//La compilare, numele vor fi inlocuite in cod cu numerele asociate la inceput.<br />
localparam STATE_READ1 = 2'b00;<br />
localparam STATE_READ0 = 2'b01;<br />
localparam STATE_ERROR = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
//registrul de stare<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= STATE_READ1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
//circuit combinational pentru calculul starii urmatoare<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
STATE_READ1: begin<br />
if(in == 0) state_next = STATE_READ0;<br />
end<br />
STATE_READ0: begin<br />
if(in == 1) state_next = STATE_ERROR;<br />
end<br />
STATE_ERROR: state_next = STATE_ERROR;<br />
default: state_next = STATE_READ1;<br />
<br />
endcase<br />
end<br />
<br />
<br />
//circuit combinational pentru calculul iesirilor<br />
assign detectOk = (state == STATE_READ0) || (state == STATE_READ1);<br />
assign detectFail = (state == STATE_ERROR);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM1'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire detectOk_t;<br />
wire detectFail_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 1;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 0;<br />
#10 in_t = 1;<br />
#10 $stop();<br />
end<br />
<br />
FSM1 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.detectOk(detectOk_t),<br />
.detectFail(detectFail_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal===<br />
Automatul ce detecteaza fronturile crescatoare are un singur semnal de intrare ''in'', care reprezinta semnalul analizat si o singura iesire, ''out'', generand pe aceasta un puls lung cat o perioada de ceas la fiecare aparitie a unui front crescator pe ''in''.<br />
<br />
Vom avea nevoie de 3 stari:<br />
* Q0: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 0 (inca nu a aparut niciun front crescator).<br />
* Q1: automatul intra in aceasta stare atunci cand apare pe intrare un front crescator. Dupa aparitia frontului crescator sunt doua posibilitati: linia ramane in 1, ceea ce duce la trecerea in starea Q2 care va duce iesirea in 0 dupa o perioada de ceas, sau linia trece in 0 dupa un ciclu de ceas, ceea ce duce la revenirea in starea Q0.<br />
* Q2: automatul intra in aceasta stare atunci cand linia de intrare ramane in 1 mai mult de o perioada de ceas. Automatul va ramane in aceasta stare atata timp cat ''in'' ramane in 1 si va reveni in starea Q0 imediat ce ''in'' revine in 0.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM rising edge.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_Moore(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam Q0 = 2'b00;<br />
localparam Q1 = 2'b01;<br />
localparam Q2 = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= Q0;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
Q0: begin<br />
if(in == 1) state_next = Q1;<br />
end<br />
Q1: begin<br />
if(in == 0) state_next = Q0;<br />
if(in == 1) state_next = Q2;<br />
end<br />
Q2: begin<br />
if(in == 0) state_next = Q0;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
assign out = (state == Q1); // iesirea depinde numai de stare => Moore<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_mealy(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam stare_wait_1 = 1'b0;<br />
localparam stare_wait_0 = 1'b1;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= stare_wait_1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
stare_wait_1: begin<br />
if(in == 1) state_next = stare_wait_0;<br />
end<br />
stare_wait_0: begin<br />
if(in == 0) state_next = stare_wait_1;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
// daca pana acum semnalul a fost "0" si astept sa vina "1" si intrarea chiar vine "1" => am avut front crescator<br />
assign out = (state==stare_wait_1) & (in==1); // in apare aici => dependenta iesiri de intrare => Mealy<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM2'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire out_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 0;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 $stop();<br />
end<br />
<br />
FSM2_Moore DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.out(out_t)<br />
);<br />
<br />
endmodule<br />
<br />
</syntaxhighlight><br />
<br />
==Exerciții==<br />
=== Exercițiul 1 ===<br />
Implementați un automat finit ce detectează fronturile descrescătoare. Acesta are un singur semnal de intrare ''in'', care reprezintă semnalul analizat și o singură ieșire ''out'', generând pe această un puls lung cât o perioadă de ceas la fiecare apariție a unui front descrescător pe ''in''.<br />
<br />
Realizați implementarea Verilog plecând de la graful automatului:<br />
<br />
[[Fișier:Graf FSM falling edge.png]]<br />
<br />
=== Exercițiul 2 ===<br />
Implementați un automat finit ce detectează atât fronturile crescătoare, cât și pe cele descrescătoare.<br />
<br />
=== Exercițiul 3 ===<br />
Să se implementeze un circuit care generează un semnal PWM cu un factor de umplere ce variază triungular, la fiecare front descrescător al unui semnal de intrare.<br />
<br />
Schema circuitului este:<br />
<br />
[[Fișier:FSM Exercitiu.png]]<br />
<br />
Modulele componente ale circuitului sunt:<br />
* '''FallingEdgeDetector''': Automat ce detectează fronturile descrescătoare ale semnalului de intrare.<br />
* '''TriangularCounter''': Automat ce generează la iesire secvența de numere 64, 128, 192, 255, 192, 128, 64.<br />
* '''Counter''': Numărător pe 8 biți cu reset sincron.<br />
* '''COMP''': Comparator cu două intrări pe 8 biți. Ieșirea sa va fi 1 atunci când counter < out și 0 în rest.<br />
<br />
=== Exercițiul 4 ===<br />
Implementați automatul de control al unui aparat care vinde ciocolata.<br />
<br />
O cafea costa 2.5 lei. Automatul accepta monezi de 50 de bani si hartii de 1 leu. Daca se depaseste suma, restul ramane in aparat si va fi folosit la urmatoarea tranzactie. Se accepta introducerea si a unei monezi si a unei bancnote in acelasi timp.<br />
<br />
Intrarile sistemului sunt: clock (1b), reset (1b), in_50_b (1b), in_100_b (1b); iesirile sistemului sunt: give_cioco(1b).<br />
<br />
Implementati automatul de control care va tine minte suma curenta si va da sau nu ciocolata in functie de aceasta. Implementati acest automat in ambele variante: Mealy si Moore.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_11_:_Automate_finite&diff=7445CID aplicatii 11 : Automate finite2023-02-17T19:23:01Z<p>Mihai.antonescu: /* Exerciții */</p>
<hr />
<div>== Automate finite ==<br />
<br />
Automatul finit este folosit pentru a descrie sisteme ce tranziteaza un numar finit de stari. Acest tip de sistem este foarte util atunci cand starile nu sunt tranzitate intr-un mod simplu, evident sau repetitiv. <br />
<br />
Automatul finit, numit și FSM (Finite State Machine), este un model matematic de computație, definit de următoarele elemente:<br />
* O mulțime finită de simboluri de intrare, numită alfabet de intrare;<br />
* O mulțime finită și nevidă de simboluri de ieșire, numită alfabet de ieșire;<br />
* O mulțime finită și nevidă de stări, din care doar una, numită starea curentă, este activă la un moment dat;<br />
* O stare inițială, care face parte din mulțimea stărilor;<br />
* O funcție de tranziție a stărilor, care calculează starea următoare a automatului în funcție de starea curentă și de simbolul de intrare;<br />
* O funcție de calcul a ieșirii, care determină simbolul de ieșire în funcție de starea curentă (în cazul automatelor de tip Moore) sau în funcție de starea curentă și de simbolul de intrare (în cazul automatelor de tip Mealy).<br />
<br />
[[[[Fișier:Fsm.png]]]]<br />
<br />
La fiecare front al ceasului, valoarea stării următoare, calculată de către CLC, va fi încărcată în registru. Dacă registrul are n biți, numărul maxim de stări care pot fi reprezentate este 2<sup>n</sup>. Acest număr poate fi mai mic, în funcție de schema de codare a stărilor. De exemplu, pentru n = 4, folosind codarea binară se pot reprezenta cel mult 16 stări (0000, 0001, 0010, ..., 1111), în timp ce pentru codarea one-hot, în care doar un singur bit poate lua valoarea 1, se pot reprezenta 4 stări (1000, 0100, 0010, 0001).<br />
<br />
Pentru a reprezenta comportamentul unui automat finit, putem folosi grafuri sau organigrame, conventiile de notare depinzand de tipul automatului (Moore sau Mealy).<br />
<br />
== Exemple == <br />
=== Exemplul 1: Automat ce detecteaza secvente de tipul 11....100....0===<br />
In acest exemplu va fi implementat un automat care detecteaza secventele de tipul 11....100....0. Pentru aceasta, automatul va avea o intrare pe un bit (''in'', pe care va veni secventa de biti) si doua iesiri: ''detectOk'', care semnalizeaza prin 1 ca nu a fost inca detectata o secventa ilegala si ''detectFail'', care semnalizeaza prin 1 ca a fost detectata o secventa ilegala (un 1 dupa 0). <br />
<br />
Automatul va avea 3 stari: <br />
* Q0 - STATE_READ1: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 1 (inca nu a aparut niciun 0).<br />
* Q1 - STATE_READ0: automatul intra in aceasta stare atunci cand apare pe intrare primul 0 si va ramane in aceasta atata timp cat pe intrare este 0.<br />
* Q2 - STATE_ERROR: automatul intra in aceasta stare atunci cand apare un 1 dupa 0 si va ramane blocat aici pana la reset.<br />
<br />
Pentru a coda cele 3 stari avem nevoie de minim 2 biti: STATE_READ1 = 2'b00, STATE_READ0 = 2'b01, STATE_ERROR = 2'b10.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM 111000.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1(<br />
input clock,<br />
input in,<br />
input reset,<br />
output detectOk,<br />
output detectFail<br />
);<br />
<br />
//Se asociaza sirurilor de biti folositi pentru codarea starilor nume ce pot fi folosite mai usor in cod.<br />
//La compilare, numele vor fi inlocuite in cod cu numerele asociate la inceput.<br />
localparam STATE_READ1 = 2'b00;<br />
localparam STATE_READ0 = 2'b01;<br />
localparam STATE_ERROR = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
//registrul de stare<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= STATE_READ1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
//circuit combinational pentru calculul starii urmatoare<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
STATE_READ1: begin<br />
if(in == 0) state_next = STATE_READ0;<br />
end<br />
STATE_READ0: begin<br />
if(in == 1) state_next = STATE_ERROR;<br />
end<br />
STATE_ERROR: state_next = STATE_ERROR;<br />
default: state_next = STATE_READ1;<br />
<br />
endcase<br />
end<br />
<br />
<br />
//circuit combinational pentru calculul iesirilor<br />
assign detectOk = (state == STATE_READ0) || (state == STATE_READ1);<br />
assign detectFail = (state == STATE_ERROR);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM1'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire detectOk_t;<br />
wire detectFail_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 1;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 0;<br />
#10 in_t = 1;<br />
#10 $stop();<br />
end<br />
<br />
FSM1 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.detectOk(detectOk_t),<br />
.detectFail(detectFail_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal===<br />
Automatul ce detecteaza fronturile crescatoare are un singur semnal de intrare ''in'', care reprezinta semnalul analizat si o singura iesire, ''out'', generand pe aceasta un puls lung cat o perioada de ceas la fiecare aparitie a unui front crescator pe ''in''.<br />
<br />
Vom avea nevoie de 3 stari:<br />
* Q0: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 0 (inca nu a aparut niciun front crescator).<br />
* Q1: automatul intra in aceasta stare atunci cand apare pe intrare un front crescator. Dupa aparitia frontului crescator sunt doua posibilitati: linia ramane in 1, ceea ce duce la trecerea in starea Q2 care va duce iesirea in 0 dupa o perioada de ceas, sau linia trece in 0 dupa un ciclu de ceas, ceea ce duce la revenirea in starea Q0.<br />
* Q2: automatul intra in aceasta stare atunci cand linia de intrare ramane in 1 mai mult de o perioada de ceas. Automatul va ramane in aceasta stare atata timp cat ''in'' ramane in 1 si va reveni in starea Q0 imediat ce ''in'' revine in 0.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM rising edge.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam Q0 = 2'b00;<br />
localparam Q1 = 2'b01;<br />
localparam Q2 = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= Q0;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
Q0: begin<br />
if(in == 1) state_next = Q1;<br />
end<br />
Q1: begin<br />
if(in == 0) state_next = Q0;<br />
if(in == 1) state_next = Q2;<br />
end<br />
Q2: begin<br />
if(in == 0) state_next = Q0;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
assign out = (state == Q1);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM2'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire out_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 0;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 $stop();<br />
end<br />
<br />
FSM2 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.out(out_t)<br />
);<br />
<br />
endmodule<br />
<br />
</syntaxhighlight><br />
<br />
==Exerciții==<br />
=== Exercițiul 1 ===<br />
Implementați un automat finit ce detectează fronturile descrescătoare. Acesta are un singur semnal de intrare ''in'', care reprezintă semnalul analizat și o singură ieșire ''out'', generând pe această un puls lung cât o perioadă de ceas la fiecare apariție a unui front descrescător pe ''in''.<br />
<br />
Realizați implementarea Verilog plecând de la graful automatului:<br />
<br />
[[Fișier:Graf FSM falling edge.png]]<br />
<br />
=== Exercițiul 2 ===<br />
Implementați un automat finit ce detectează atât fronturile crescătoare, cât și pe cele descrescătoare.<br />
<br />
=== Exercițiul 3 ===<br />
Să se implementeze un circuit care generează un semnal PWM cu un factor de umplere ce variază triungular, la fiecare front descrescător al unui semnal de intrare.<br />
<br />
Schema circuitului este:<br />
<br />
[[Fișier:FSM Exercitiu.png]]<br />
<br />
Modulele componente ale circuitului sunt:<br />
* '''FallingEdgeDetector''': Automat ce detectează fronturile descrescătoare ale semnalului de intrare.<br />
* '''TriangularCounter''': Automat ce generează la iesire secvența de numere 64, 128, 192, 255, 192, 128, 64.<br />
* '''Counter''': Numărător pe 8 biți cu reset sincron.<br />
* '''COMP''': Comparator cu două intrări pe 8 biți. Ieșirea sa va fi 1 atunci când counter < out și 0 în rest.<br />
<br />
=== Exercițiul 4 ===<br />
Implementați automatul de control al unui aparat care vinde ciocolata.<br />
<br />
O cafea costa 2.5 lei. Automatul accepta monezi de 50 de bani si hartii de 1 leu. Daca se depaseste suma, restul ramane in aparat si va fi folosit la urmatoarea tranzactie. Se accepta introducerea si a unei monezi si a unei bancnote in acelasi timp.<br />
<br />
Intrarile sistemului sunt: clock (1b), reset (1b), in_50_b (1b), in_100_b (1b); iesirile sistemului sunt: give_cioco(1b).<br />
<br />
Implementati automatul de control care va tine minte suma curenta si va da sau nu ciocolata in functie de aceasta. Implementati acest automat in ambele variante: Mealy si Moore.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7444CID aplicatii 7 : Circuite secventiale elementare2023-02-17T16:26:32Z<p>Mihai.antonescu: /* Exemplul 3: Descrierea comportamentală a bistabilului de tip D */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in; // atribuire non-blocanta<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_7_:_Circuite_secventiale_elementare&diff=7443CID aplicatii 7 : Circuite secventiale elementare2023-02-17T16:24:50Z<p>Mihai.antonescu: /* Exercițiul 3 */</p>
<hr />
<div>==Hazardul combinațional== <br />
<br />
Hazardul apare atunci când modificarea unei intrări a unui circuit combinațional determină modificări nedorite ale ieșirii. Aceste variații apar datorită diferențelor de întârzieri pe diverse căi de la intrare către ieșire.<br />
<br />
De unde aceste întârzieri? Fiecare poartă este, așa cum am văzut la începutul aplicațiilor, un circuit electronic format, de obicei, din tranzistoare MOS. Răspunsul acestor dispozitive nu este instantaneu, introducând întârzieri. Timpul scurs de la modificarea unei intrări a unei porți până la modificarea corespunzătoare a ieșirii se numește timp de propagare.<br />
<br />
În figura următoare avem evidențiat timpul de propagare printr-o poartă ''SAU'' (Tp). Acesta este reprezentat de timpul scurs de la modificarea lui a până la modificarea ieșirii.<br />
<br />
[[Fișier:Comutare poarta SAU.png | 400px]]<br />
<br />
Avănd un circuit cu mai multe porți în cascadă, căile prin care anumite semnale ajung la intrările unei anumite porți pot diferi. Datorită timpilor de propagare diferiți care afectează aceste semnale până să ajungă la poarta curentă și a timpului de propagare efectiv al porții curente, pot apărea la ieșire tranziții nedorite ale semnalelor. Dacă la ieșire se așteaptă ca linia să rămană constant în 1, dar apare o scurtă tranziție prin 0, hazardul se numește ''1 static''. Dacă la ieșire se așteaptă ca linia să rămană constant în 0, dar apare o scurtă tranziție prin 1, hazardul se numește ''0 static''. Dacă se așteaptă o tranziție la ieșire, dar apare un regim tranzitoriu cu numeroase tranziții până la stabilizare, hazardul se numește dinamic.<br />
<br />
Pentru a înțelege mai bine, să considerăm exemplul de mai jos:<br />
<br />
[[Fișier:Circuit comb hazard.png | 400px]]<br />
<br />
Observăm că intrările porții ''P2'' urmează căi diferite: semnalul ''c'' este ieșirea unei porți, având o întârziere cauzată de timpul de propagare al porții ''P1'', pe când semnalul ''b'' vine direct de la intrare, propagarea prin fir fiind neglijabilă. Să considerăm cazul în care intrarea ''a'' rămane permanent în 0, iar intrarea ''b'' comută la un moment dat din 0 în 1. Inițial, semnalul ''c'' este 1, iar ''d'' este 1. După comutarea lui ''b'', semnalul ''c'' va comuta din 1 in 0, iar ''d'' va rămane 1. Acesta este cazul ideal. În realitate, datorită timpilor de propagare ai porților logice ''P1'' și ''P2'', vom avea o scurtă tranziție prin 0:<br />
<br />
[[Fișier:Circuit comb hazard propagare.png | 400px]]<br />
<br />
==Latch-ul==<br />
Latch-urile sunt dispozitive elementare de memorare, sensibile la nivelul semnalelor de intrare. Exemple de astfel de dispozitive sunt latch-urile de tip SR și latch-urile de tip D.<br />
<br />
<br />
===Latch-ul SR===<br />
Latch-ul de tip SR poate fi realizat cu două porți ''SI NU'' sau ''SAU NU'' și este un dispozitiv asincron controlat de stările semnalelor ''S'' (set) și ''R'' (reset). Tabelul de adevăr al acestui circuit este prezentat mai jos. <br />
<br />
[[Fișier:Latch SR.png|400px]]<br />
<br />
Atunci când ''S'' este 1 și ''R'' este 0, ieșirea ''Q'' va deveni 1, iar ''Qn'' va deveni 0. Atunci când ''R'' este 1 și ''S'' este 0, ieșirea ''Q'' se resetează (devine 0), iar ''Qn'' devine 1. Starea de memorare apare atunci când atât ''R'' cât și ''S'' sunt 0 în același timp. Cazul în care ''R'' și ''S'' sunt 1 în același timp duce la un comportament nedorit. (atât ''Q'' cât și ''Qn'' vor fi 0, ceea ce este incorect din punct de vedere al logicii dorite – ''Qn'' să fie negatul lui ''Q''). În plus, dacă din aceasta stare se dorește trecerea în starea de memorare (''R'' = 0, ''S'' = 0), poate apărea oscilația. În realitate, cele două porți nu vor avea același timp de propagare datorită variațiilor de producție și circuitul va ajunge în cele din urma într-o stare stabilă, nepredictibilă.<br />
<br />
===Latch-ul de tip D===<br />
Latch-ul de tip D elimină problema combinațiilor nedorite de la ieșire. Acesta modifică ieșire doar atunci când semnalul de enable (''E'') este 1. Altfel, atunci când ''E'' este 0, va memora starea anterioara (''Qt-1'').<br />
<br />
[[Fișier:Latch D.png|500px]]<br />
<br />
<br />
==Bistabilul de tip D==<br />
Bistabilul de tip D este un dispozitiv de memorare ce salvează valoarea intrării pe unul din fronturile ceasului (în mod uzual, frontul crescător). El poate fi obținut prin conectarea a două latch-uri de tip D, conform schemei de mai jos. De obicei, singura ieșire care ne interesează este ''Q''.<br />
<br />
[[Fișier:Bistabil D.png|300px]]<br />
<br />
Comportamentul bistabilului de tip D poate fi observat în forma de undă următoare. Modificările lui ''data_out'' sunt determinate de fronturile crescătoare ale semnalului ''clock''. La apariția acestora, ''data_out'' va lua valoarea intrării ''data_in''.<br />
<br />
[[Fișier:Forma unda bistabil.png]]<br />
<br />
===Bistabilul de tip D cu reset sincron===<br />
Resetarea unui bistabil înseamnă aducerea valorii memorate la 0 sau la o altă valoare de reset definită de cel care proiectează circuitul. Vom considera în exemplul nostru că semnalul de ''reset'' va fi activ în 0 și va face 0 valoarea memorată atunci când este activ. Un reset sincron înseamnă că acesta va acționa asupra valorii memorate''data_out'' pe frontul crescător al ceasului.<br />
<br />
[[Fișier:Forma unda bistabil reset sincron.png]]<br />
<br />
===Bistabilul de tip D cu reset asincron===<br />
Un reset asincron înseamnă că acesta va acționa asincron, fără a ține cont de ceas. Acest lucru înseamnă că el va acționa asupra valorii memorate imediat ce devine activ. În exemplele noastre, vom considera semnalul de ''reset'' ca fiind activ în 0. Trecerea sa în 0 (frontul căzător) determină imediat resetarea circuitului. De asemenea, orice eveniment de front crescător de ceas ce apare cât timp reset-ul este activ, va duce la menținerea resetării circuitului. <br />
<br />
[[Fișier:Forma unda bistabil reset asincron.png]]<br />
<br />
<br />
==Exemple==<br />
===Exemplul 1: Evidențierea hazardului combinațional===<br />
<br />
Implementați circuitul cu porți logice prezentat în secțiunea de introducere teoretică și reproduceți în modulul de testare variațiile semnalelor ''a'' și ''b'' propuse, astfel încât să observăm pe ieșirea ''d'' hazardul.<br />
<br />
Deoarece în simulare propagarea este ideală, va trebui să introducem un timp de propagare folosind ''#n''. Acesta va avea efect doar în simulare și va fi ignorat la o eventuală sinteză.<br />
<br />
Pentru modulul de test, va trebui să alegem un timp de modificare a valorilor semnalelor mai mare decât timpul de propagare ales. Aici, vom folosi 1ns timp de propagare și 5ns timp de variație a intrărilor in testbench.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
module Circuit(<br />
input a,<br />
input b,<br />
output c,<br />
output d<br />
);<br />
<br />
assign #1 c = ~(a | b);<br />
assign #1 d = ~(c & b);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test care să evidențieze hazardul'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Circuit_TB();<br />
<br />
reg a_t, b_t;<br />
wire c_t, d_t;<br />
<br />
initial begin<br />
a_t = 0;<br />
b_t = 0;<br />
#5 b_t = 1;<br />
#5 $stop();<br />
end<br />
<br />
Circuit DUT(<br />
.a(a_t),<br />
.b(b_t),<br />
.c(c_t),<br />
.d(d_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Descrierea comportamentală a latch-ului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module latch_D(<br />
input D,<br />
input E,<br />
output Q<br />
);<br />
<br />
assign Q = (E == 1) ? D : Q;<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modul de test pentru latch-ul D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module latch_D_TB();<br />
<br />
reg D_t, E_t;<br />
wire Q_t;<br />
<br />
initial begin<br />
D_t = 0;<br />
E_t = 1;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#1 E_t = 0;<br />
#1 D_t = 1;<br />
#1 D_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
latch_D DUT1(<br />
.D(D_t),<br />
.E(E_t),<br />
.Q(Q_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 3: Descrierea comportamentală a bistabilului de tip D===<br />
'''Descrierea Verilog a modulului'''<br />
<syntaxhighlight lang="Verilog"><br />
module flipflop_D(<br />
input data_in,<br />
input clock,<br />
output reg data_out<br />
);<br />
<br />
always@(posedge clock) begin<br />
data_out <= data_in;<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Modulul de test pentru bistabilul de tip D'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module flipflop_D_TB();<br />
<br />
reg data_in_t, clock_t;<br />
wire data_out_t;<br />
<br />
initial begin<br />
data_in_t = 0;<br />
#2 data_in_t = 1;<br />
#4 data_in_t = 0;<br />
#5 $stop();<br />
end<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~ clock_t;<br />
end<br />
<br />
flipflop_D DUT(<br />
.data_in(data_in_t),<br />
.clock(clock_t),<br />
.data_out(data_out_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observatie:''' Atunci cand avem un circuit secvential, se foloseste atribuirea non-blocanta ("<=”)<br />
<br />
<br />
==Exerciții==<br />
===Exercițiul 1===<br />
Descrieți structural latch-ul de tip SR, conform schemei din secțiunea de introducere teoretică. Puteți simula întârzierile prin porți folosind #1.<br />
<br />
Realizați un modul de test care să pună în evidență funcționarea. Respectați la generarea stimulilor următoarea variație (fiecare segment de timp durează 5ns).<br />
<br />
[[Fișier:Forma unda latchSR.png]]<br />
<br />
===Exercițiul 2===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea sincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset sincron'''.<br />
<br />
===Exercițiul 3===<br />
Modificați descrierea bistabilului de tip D prezentată în exemple, astfel încât acesta să permită resetarea asincronă. Semnalul de reset va fi activ în 0.<br />
<br />
Realizați un modul de test care să respecte la generarea stimulilor forma de unda prezentată în secțiunea de introducere teoretică, la '''Bistabilul de tip D cu reset asincron'''.<br />
<br />
'''Indicație''': Frontul căzător al semnalului ''reset'' va trebui să fie adăugat în lista de sensitivități a blocului always care determină modificarea lui ''data_out''.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Boolean_Board_-_Pinout&diff=7442Boolean Board - Pinout2023-02-17T16:22:39Z<p>Mihai.antonescu: Pagină nouă: ==Boolean Board - Pinout== Aici veti gasi lista cu corespondenta pin<->componenta pentru placa Boolean Board de la Real Digital. Aceasta se foloseste fie in fisierul XDC cu constr...</p>
<hr />
<div>==Boolean Board - Pinout==<br />
<br />
Aici veti gasi lista cu corespondenta pin<->componenta pentru placa Boolean Board de la Real Digital. Aceasta se foloseste fie in fisierul XDC cu constrangeri fie in elaboration cand se selecteaza conexiunile fizice dorite intre intrarile si iesirile din design si leduri/butoane.<br />
<br />
Pentru functionarea corecta a placii se va selecta "LVCMOS33" ca standard de I/O, unde "33" vine de la 3.3V.<br />
<br />
{| class="wikitable"<br />
|- bgcolor="#ddeeff" align="center"<br />
|- bgcolor="#ddeeff" align="center"<br />
| Componenta || Pin<br />
|- bgcolor="#ddffdd" align="center"<br />
|XXXX|| YYYY<br />
<br />
|}<br />
<br />
<br />
Pentru lista completa a pinilor puteti accesa [https://www.realdigital.org/hardware/boolean Boolean Board - pagina oficiala] - sectiunea User manual sau Master XDC.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale&diff=7441Circuite Integrate Digitale2023-02-17T16:19:37Z<p>Mihai.antonescu: /* Tutoriale și documentații */</p>
<hr />
<div>== Scopul laboratorului ==<br />
<br />
<br />
Laboratorul de circuite integrate digitale vine ca o completare practica a cursului de CID. <br />
Prin definitie, acesta se va axa pe elemente practice de simulare, sinteza si testare a circuitelor ce sunt prezentate la curs. <br />
<br />
Acest laborator se bazeaza pe limbajul "Verilog", acesta fiind unul din cele 2 limbaje de descriere hardware folosite in industrie la ora actuala (celalalt fiind "VHDL"). <br />
<br />
Platformele sunt structurate astfel incat sa contina cateva elemente de teorie minimala, un exemplu de circuit descris in cod Verilog si apoi exercitii pe care sa le rezolvati in ora de aplicatii (simulare/testare pe placa FPGA) si acasa (simulare). <br />
<br />
In laborator se va lucra in Vivado, program al firmei Xilinx, pe o placa PYNQ-Z2 impreuna cu o placa de extensie ce adauga leduri si butoane suplimentare. <br />
<br />
Circuitele digitale sunt o parte fundamentala a electronici moderne, cu numeroase oportunitati in industrie, atat in design cat si in verificarea circuitelor.<br />
<br />
Pe langa ce veti invata in acest laborator, limbajul ofera si capabilitati mai avansate pentru a descrie mai eficient circuitele dorite, cat si pentru testarea acestora. Sintaxa nu se termina cu ce invatati aici si pentru cei pasionati, nu ezitati sa va contactati cadrul didactic cu intrebari.<br />
<br />
Speram ca o sa va placa, veti invata si vi se va parea interesant ce veti vedea in orele de aplicatii ce urmeaza. Spor.<br />
<br />
<br />
== Tutoriale și documentații ==<br />
<br />
Programul Vivado este gratuit (necesita cont, dar este gratis) si poate fi descarcat si instalat urmand tutorialul de aici:<br />
# [[Tutorial instalare Vivado]]<br />
# [[Tutorial Vivado]].<br />
# [https://www.tulembedded.com/FPGA/ProductsPYNQ-Z2.html Pynq-Z2 - pagina oficiala]<br />
# [https://dpoauwgwqsy2x.cloudfront.net/Download/pynqz2_user_manual_v1_0.pdf Pynq-Z2 - user manual]<br />
# [[Pynq-Z2 - Pinout]].<br />
# [https://www.realdigital.org/doc/02013cd17602c8af749f00561f88ae21 Boolean Board - user manual]<br />
# [[Boolean Board - Pinout]].<br />
# [[FPGA - Introducere]].<br />
<br />
O lista de link-uri utile poate fi gasita aici:<br />
# [[Introducere. Verilog HDL]] (Sintaxa [[Verilog]])<br />
# [https://www.asic-world.com/digital/index.html Asic-world - digital]<br />
# [https://www.asic-world.com/verilog/index.html Asic-world - verilog]<br />
# [https://nandgame.com/ Joc online: Nandgame] <br />
# [https://circuitverse.org/simulator Simulator/joc online si grafic de circuite: Circuitverse] <br />
# [https://play.google.com/store/apps/details?id=com.ViacheslavRud.Circuit&hl=en_US&gl=US Joc android - Make it True: Solve the Circuit]<br />
# [https://www.edaplayground.com/ Simulatoare online (mod text): Edaplayground]<br />
# [https://wavedrom.com/ Utilitar desenare forme de unda]<br />
# [https://www.draw.io/ Utilitar desenare diagrame]<br />
# [[Domenii conexe]]<br />
<br />
== Lucrări de laborator ==<br />
<br />
# [[CID_aplicatii_1 : Generare de forme de unda]]<br />
# [[CID_aplicatii_2 : Instantiere si porti logice]]<br />
# [[CID_aplicatii_3 : Circuite combinationale elementare]]<br />
# [[CID_aplicatii_4 : Alte circuite combinationale]]<br />
# [[CID_aplicatii_5 : Exercitii cu circuite combinationale]]<br />
# CID_aplicatii_6 : Lucrarea 1 - circuite combinationale<br />
# [[CID_aplicatii_7 : Circuite secventiale elementare]]<br />
# [[CID_aplicatii_8 : Registre si memorii RAM]]<br />
# [[CID_aplicatii_9 : Numaratorul]]<br />
# [[CID_aplicatii_10 : Aplicatii cu numaratoare]]<br />
# [[CID_aplicatii_11 : Automate finite]]<br />
# [[CID_aplicatii_12 : Exercitii cu circuite secventiale]]<br />
# CID_aplicatii_13 : Lucrarea 2 - circuite secventiale<br />
# [[CID_aplicatii_14 : Circuite digitale complexe]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_4_:_Alte_circuite_combinationale&diff=7440CID aplicatii 4 : Alte circuite combinationale2023-02-17T16:11:18Z<p>Mihai.antonescu: /* Exercițiul 2 */</p>
<hr />
<div>== Memoria ROM==<br />
Memoria este un dispozitiv electronic utilizat pentru stocarea datelor. Aceasta poate fi de mai multe feluri, având diverse caracteristici, cum ar fi capacitatea de a-și păstra sau nu conținutul după întreruperea alimentării sau capacitatea de a-și modifica sau nu conținutul după terminarea procesului de producție. <br />
<br />
Memoria ROM (Read-Only Memory) este un circuit combinational folosit pentru stocarea unor date ce pot fi accesate cu ajutorul unei intrari de adresă. Asa cum sugerează și numele, memoria ROM clasică nu poate fi modificată. În plus, datorită modului în care este implementată, ea își păstrează conținutul după întreruperea alimentării (este nevolatilă). <br />
<br />
Pentru a înțelege termenul de memorie, putem face următorul exercițiu de imaginație: să ne gândim la un dulap cu mai multe sertare. Conținutul memoriei este reprezentat de conținutul sertarelor, iar adresele sunt reprezentate de etichetele lipite pe aceste sertare pentru a le identifica. Dacă avem nevoie de conținutul unui sertar, va trebui să știm eticheta aferentă acestuia (adresa).<br />
<br />
O memorie ROM are următorii parametri: numărul de biți ai adresei, care este legat de numarul de locații de memorie (cu n biți de adresă putem forma 2<sup>𝑛</sup> combinații diferite, deci putem accesa maxim 2<sup>𝑛</sup> locații de memorie) și dimensiunea locației de memorie, care ne spune cât de multă informație poate stoca o locație de memorie.<br />
<br />
<br />
Să urmărim exemplul din figura următoare:<br />
<br />
[[Fișier:ROM memory.PNG | 400px]]<br />
<br />
<br />
Aici, adresa are dimensiunea de 4 biți, asadar putem accesa cu aceasta 16 locații diferite de memorie. Dimensiunea fiecărei locații este de 8 biți, așadar fiecare locație de memorie poate stoca numere de 8 biți. Spunem că memoria ROM este o memorie 16x8. Capacitatea acestei memorii este de 16 x 8 biți = 128 biți.<br />
<br />
== Exemple ==<br />
<br />
Urmatoarele circuite pot fi vazute in 2 moduri: ca memorii de tip ROM, cu date de iesire care depind de adresa (data de intrare) de la care citesti cat si ca circuite combinationale a caror iesire este generata prin porti din intrare.<br />
<br />
=== Exemplul 1: Decodorul ===<br />
Decodorul este circuitul care, pentru o anumită valoare a intrării, va genera la ieșire un șir binar care conține 1 pe poziția cu indicele egal cu valorea intrării și 0 in rest. De exemplu, decodorul de mai jos pe 2 biți va avea următoarea corespondență intrare-iesire:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''in''' || '''out'''<br />
|- align="center"<br />
| 2'b00 || 4'b0001<br />
|- align="center"<br />
|2'b01 || 4'b0010<br />
|- align="center"<br />
|2'b10 || 4'b0100<br />
|- align="center"<br />
|2'b11 || 4'b1000<br />
|}<br />
<br />
[[Fișier:Decoder.PNG | 400px]]<br />
<br />
Vom implementa acest circuit ca memorie ROM. "in" are rolul de adresa iar out are rolul de date de la adresa respectiva. <br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<syntaxhighlight lang="Verilog"><br />
module Decoder(<br />
input [1:0] in,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
2'b00: out = 4'b0001; //la adresa 0 avem valoarea 1<br />
2'b01: out = 4'b0010; //la adresa 1 avem valoarea 2<br />
2'b10: out = 4'b0100; //la adresa 2 avem valoarea 4<br />
2'b11: out = 4'b1000; //la adresa 3 avem valoarea 8<br />
default: out = 4'b0000;<br />
endcase<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Vom implementa acest circuit ca circuit combinational cu iesirile generate din intrari prin operatii logice.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<syntaxhighlight lang="Verilog"><br />
module Decodor_v2(<br />
input [1:0] in,<br />
output reg [3:0] out<br />
);<br />
<br />
// fiecare bit din iesire calculat independent<br />
assign out[0] = ~in[0] & ~in[1];<br />
assign out[1] = in[0] & ~in[1];<br />
assign out[2] = ~in[0] & in[1];<br />
assign out[3] = in[0] & in[1];<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Implementarea Verilog a modulului de test'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Decoder_TB();<br />
reg [1:0] in_tb;<br />
wire [3:0] out_tb;<br />
<br />
integer idx;<br />
<br />
initial begin<br />
for(idx=0; idx<4; idx = idx + 1) begin<br />
in_t = idx;<br />
#1;<br />
end <br />
#2 $stop();<br />
end<br />
<br />
Decoder DUT(<br />
.in(in_tb),<br />
.out(out_tb)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observații:'''<br />
* Circuitul Decoder va avea o intrare pe 2 biți, care va reprezenta adresa memoriei ROM și o iesire pe 4 biți, reprezentând conținuturile memoriei ROM corespunzătoare fiecărei combinații de la intrare.<br />
* Chiar dacă în interiorul instrucțiunii ''case'' se acoperă toate cazurile posibile (toate combinațiile de 2 biți), este bine sa punem și un ''default'' pentru a evita inserarea în locul circuitului combinațional dorit (aici, memoria ROM) a unui latch.<br />
<br />
<br />
===Exemplul 2: Transcodorul===<br />
Trancodorul este o memorie ROM care asociază unei anumite intrări, reprezentată de adresă, un anumit cod, reprezentat de conținutului locației de memorie asociată acelei adrese.<br />
<br />
In acest exemplu, vom folosi transcodorul pentru a realiza un modul ce controlează afișajul cu 7 segmente. Acest dispozitiv de afișare este format, așa cum sugerează și numele, din 7 segmente ce pot fi controlate separat. Putem aprinde sau stinge fiecare segment controlând tensiunea aplicată pe acesta. Pentru acest afișaj, comanda este negativă: aplicând 0 logic pe un segment acesta se va aprinde, iar aplicând 1 logic, acesta se va stinge.<br />
<br />
Structura unui afisaj cu 7 segmente este prezentată în imaginea de mai jos:<br />
<br />
[[Fișier:7LED display.PNG | 200px]]<br />
<br />
Pentru a realiza controlul afișării, avem nevoie de o memorie ROM ce trebuie să asocieze codului binar al fiecărui element ce poate fi afișat un șir de biți care sting sau aprind diversele segmente ale afișajului, astfel încât să apară imaginea elementului dorit.<br />
<br />
Pentru a reprezenta, de exemplu, cele 16 numere hexa, segmentele trebuie aprinse conform Anexei 1. De exemplu, pentru primele 8 numere (0 până la 7), tabelul de asociere este următorul:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Număr''' || '''Cod intrare''' || '''Șir de afisare''' <br />
|- align="center"<br />
| 0 || 4'b0000 || 7'b100_0000<br />
|- align="center"<br />
| 1 || 4'b0001 || 7'b111_1001<br />
|- align="center"<br />
| 2 || 4'b0010 || 7'b010_0100<br />
|- align="center"<br />
| 3 || 4'b0011 || 7'b011_0000<br />
|- align="center"<br />
| 4 || 4'b0100 || 7'b001_1001<br />
|- align="center"<br />
| 5 || 4'b0101 || 7'b001_0010<br />
|- align="center"<br />
| 6 || 4'b0110 || 7'b000_0010<br />
|- align="center"<br />
| 7 || 4'b0111 || 7'b111_1000<br />
|}<br />
<br />
'''Implementarea Verilog a modulului Display7Seg'''<br />
<syntaxhighlight lang="Verilog"><br />
module Display7Seg(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
4'd0: out = 7'b1000000;<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
default: out = 7'b0000110;<br />
endcase <br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
== Exerciții:==<br />
=== Exercițiul 1===<br />
Completați codul din Exemplul 2 astfel încât să se poată reprezenta toate numerele în bază 16 (hexazecimal), conform Anexei 1<br />
<br />
=== Exercițiul 2===<br />
Un procesor are nevoie de un șir de biți numit instrucțiune pentru a știi ce trebuie să facă. Această instrucțiune codează în șirul său de biți toate informațiile necesare. <br />
<br />
De exemplu, pentru un grup de instrucțiuni care operează cu registre, aceste informații pot fi:<br />
* codul instrucțiunii (operation code - opcode): un șir de biți care ne spune ce operație se execută.<br />
* destinația: un șir de biți care codează numărul registrului destinație.<br />
* sursa1: un șir de biți care codează numărul registrului folosit ca operand 1.<br />
* sursa2: un șir de biți care codează numărul registrului folosit ca operand 2.<br />
<br />
<br />
Vom considera codul operației pe 4 biți, ceea ce inseamnă ca putem avea 16 instrucțiuni diferite. In plus, câmpurile care codează destinația și cei doi operanzi vor avea fiecare câte 4 biți, ceea ce inseamnă că vom avea 16 registre cu care putem lucra (R0 ... R15). Adunând dimensiunile fiecărui câmp, vom obține o instrucțiune pe 16 biți.<br />
<br />
'''Instrucțiunile și codul operațiilor:'''<br />
<br />
Vom considera ca procesorul nostru elementar poate suporta următoarele instrucțiuni:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Operație''' || '''Cod operație''' || '''Sintaxă''' ||'''Descriere''' <br />
|- align="center"<br />
| ADD || 4'b0000 || ADD Rd Rs1 Rs2 || Rd = Rs1 + Rs2 <br />
|- align="center"<br />
| SUB || 4'b0001 || SUB Rd Rs1 Rs2 || Rd = Rs1 - Rs2<br />
|- align="center"<br />
| OR || 4'b0010 || OR Rd Rs1 Rs2 || Rd = Rs1 OR Rs2<br />
|- align="center"<br />
| AND || 4'b0011 || AND Rd Rs1 Rs2 || Rd = Rs1 AND Rs2<br />
|- align="center"<br />
| LOADC || 4'b0100 || LOADC Rd, const || Rd = const<br />
|}<br />
<br />
Pentru instrucțiunile prezentate mai sus, vom avea următoarele moduri de codare (de împărțire a informației în cadrul instrucțiunii):<br />
<br />
[[Fișier:Codare instructiune.png | 400px]]<br />
<br />
'''Registrele:'''<br />
<br />
Cele 16 registre vor fi codate cu un număr egal cu indexul său. De exemplu, R0 va avea codul 4'b000, R1 va avea codul 4'b0001 și așa mai departe.<br />
<br />
<br />
Considerând cele spuse mai sus, vom avea următoarele exemple de instrucțiuni:<br />
<br />
[[Fișier:Exemple codare instructiuni.png | 400px]]<br />
<br />
<br />
Având toate informațiile de mai sus, descrieți o memorie ROM cu adresă pe 4 biți și locații pe 16 biți, care să conțină următorul program, începând cu adresa 0:<br />
<br />
<syntaxhighlight lang="Verilog"><br />
<br />
LOADC R0, 2h<br />
LOADC R1, 5h<br />
LOADC R2, 7h<br />
ADD R3, R1, R0<br />
SUB R4, R2, R1<br />
OR R5, R3, R4<br />
<br />
</syntaxhighlight><br />
<br />
=== Exercițiul 3===<br />
Proiectați un Codor Cezar cu cheie fixă sub forma unei memorii ROM. <br />
<br />
Un Codor Cezar preia un mesaj și codează independent fiecare literă a acestuia pentru a obține un mesaj cifrat. El se folosește de o cheie fixă care se adună la fiecare literă a mesajului. De exemplu, pentru cheia "+3": a->d; b->e; c->f;...; y->b; z->c. Observați că atunci când alfabetul a ajuns la capăt, se reia de la început. <br />
Pentru decodare, se va folosi procedura inversă: pentru fiecare literă, vom aplica cheia cu semn invers (în exemplul nostru, cheia de decodare este "-3"). <br />
<br />
Observații, sfaturi, task-uri suplimentare:<br />
* Codorul va putea prelucra doar litere mici și spațiu gol (acesta rămâne neschimbat).<br />
* Cel mai usor mod de a scrie această funcționalitate este folosind un bloc de tip ''case''.<br />
* Pentru testare, se pot folosi în testbench variabile de tip ''reg'' pe 8 biți, astfel: <br />
<syntaxhighlight lang="Verilog"><br />
reg [7:0] litera;<br />
litera = "A";<br />
</syntaxhighlight><br />
* Avansat: În SystemVerilog există și tipul de date ''string'', ceea ce permite parcurgerea cu un ''for'' a mesajului. În Verilog clasic, astfel de parcurgeri sunt posibile, dar sintaxa este mai complicată.<br />
* Se va folosi codul ASCII pentru valori. Pentru o ușoara vizualizare a datelor în simulare, se poate schimba radix-ul în ASCII. <br />
* Avansat: Pentru a testa automat codul, se poate instanția încă un modul în testbench cu aceeași funcționalitate pe post de "golden model". Acest modul nu trebuie să fie sintetizabil și atunci sintaxa permite mai multă libertate (cum ar fi operația de modulo "%", utilă pentru testare, dar care determină un circuit complicat dacă ar fi sintetizată). Ieșirile celor două module din testbench sunt apoi comparate pentru a se verifica că ambele implementări oferă același rezultat.<br />
* Implementați și Decodorul Cezar și testați Codorul împreună cu acesta. Aveți grijă ca cele două module să aibă aceeași cheie.<br />
<br />
==Anexa1== <br />
<br />
[[Fișier:Anexa1.PNG | 800px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_4_:_Alte_circuite_combinationale&diff=7439CID aplicatii 4 : Alte circuite combinationale2023-02-17T16:08:42Z<p>Mihai.antonescu: /* Exemple */</p>
<hr />
<div>== Memoria ROM==<br />
Memoria este un dispozitiv electronic utilizat pentru stocarea datelor. Aceasta poate fi de mai multe feluri, având diverse caracteristici, cum ar fi capacitatea de a-și păstra sau nu conținutul după întreruperea alimentării sau capacitatea de a-și modifica sau nu conținutul după terminarea procesului de producție. <br />
<br />
Memoria ROM (Read-Only Memory) este un circuit combinational folosit pentru stocarea unor date ce pot fi accesate cu ajutorul unei intrari de adresă. Asa cum sugerează și numele, memoria ROM clasică nu poate fi modificată. În plus, datorită modului în care este implementată, ea își păstrează conținutul după întreruperea alimentării (este nevolatilă). <br />
<br />
Pentru a înțelege termenul de memorie, putem face următorul exercițiu de imaginație: să ne gândim la un dulap cu mai multe sertare. Conținutul memoriei este reprezentat de conținutul sertarelor, iar adresele sunt reprezentate de etichetele lipite pe aceste sertare pentru a le identifica. Dacă avem nevoie de conținutul unui sertar, va trebui să știm eticheta aferentă acestuia (adresa).<br />
<br />
O memorie ROM are următorii parametri: numărul de biți ai adresei, care este legat de numarul de locații de memorie (cu n biți de adresă putem forma 2<sup>𝑛</sup> combinații diferite, deci putem accesa maxim 2<sup>𝑛</sup> locații de memorie) și dimensiunea locației de memorie, care ne spune cât de multă informație poate stoca o locație de memorie.<br />
<br />
<br />
Să urmărim exemplul din figura următoare:<br />
<br />
[[Fișier:ROM memory.PNG | 400px]]<br />
<br />
<br />
Aici, adresa are dimensiunea de 4 biți, asadar putem accesa cu aceasta 16 locații diferite de memorie. Dimensiunea fiecărei locații este de 8 biți, așadar fiecare locație de memorie poate stoca numere de 8 biți. Spunem că memoria ROM este o memorie 16x8. Capacitatea acestei memorii este de 16 x 8 biți = 128 biți.<br />
<br />
== Exemple ==<br />
<br />
Urmatoarele circuite pot fi vazute in 2 moduri: ca memorii de tip ROM, cu date de iesire care depind de adresa (data de intrare) de la care citesti cat si ca circuite combinationale a caror iesire este generata prin porti din intrare.<br />
<br />
=== Exemplul 1: Decodorul ===<br />
Decodorul este circuitul care, pentru o anumită valoare a intrării, va genera la ieșire un șir binar care conține 1 pe poziția cu indicele egal cu valorea intrării și 0 in rest. De exemplu, decodorul de mai jos pe 2 biți va avea următoarea corespondență intrare-iesire:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''in''' || '''out'''<br />
|- align="center"<br />
| 2'b00 || 4'b0001<br />
|- align="center"<br />
|2'b01 || 4'b0010<br />
|- align="center"<br />
|2'b10 || 4'b0100<br />
|- align="center"<br />
|2'b11 || 4'b1000<br />
|}<br />
<br />
[[Fișier:Decoder.PNG | 400px]]<br />
<br />
Vom implementa acest circuit ca memorie ROM. "in" are rolul de adresa iar out are rolul de date de la adresa respectiva. <br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<syntaxhighlight lang="Verilog"><br />
module Decoder(<br />
input [1:0] in,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
2'b00: out = 4'b0001; //la adresa 0 avem valoarea 1<br />
2'b01: out = 4'b0010; //la adresa 1 avem valoarea 2<br />
2'b10: out = 4'b0100; //la adresa 2 avem valoarea 4<br />
2'b11: out = 4'b1000; //la adresa 3 avem valoarea 8<br />
default: out = 4'b0000;<br />
endcase<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Vom implementa acest circuit ca circuit combinational cu iesirile generate din intrari prin operatii logice.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<syntaxhighlight lang="Verilog"><br />
module Decodor_v2(<br />
input [1:0] in,<br />
output reg [3:0] out<br />
);<br />
<br />
// fiecare bit din iesire calculat independent<br />
assign out[0] = ~in[0] & ~in[1];<br />
assign out[1] = in[0] & ~in[1];<br />
assign out[2] = ~in[0] & in[1];<br />
assign out[3] = in[0] & in[1];<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Implementarea Verilog a modulului de test'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Decoder_TB();<br />
reg [1:0] in_tb;<br />
wire [3:0] out_tb;<br />
<br />
integer idx;<br />
<br />
initial begin<br />
for(idx=0; idx<4; idx = idx + 1) begin<br />
in_t = idx;<br />
#1;<br />
end <br />
#2 $stop();<br />
end<br />
<br />
Decoder DUT(<br />
.in(in_tb),<br />
.out(out_tb)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observații:'''<br />
* Circuitul Decoder va avea o intrare pe 2 biți, care va reprezenta adresa memoriei ROM și o iesire pe 4 biți, reprezentând conținuturile memoriei ROM corespunzătoare fiecărei combinații de la intrare.<br />
* Chiar dacă în interiorul instrucțiunii ''case'' se acoperă toate cazurile posibile (toate combinațiile de 2 biți), este bine sa punem și un ''default'' pentru a evita inserarea în locul circuitului combinațional dorit (aici, memoria ROM) a unui latch.<br />
<br />
<br />
===Exemplul 2: Transcodorul===<br />
Trancodorul este o memorie ROM care asociază unei anumite intrări, reprezentată de adresă, un anumit cod, reprezentat de conținutului locației de memorie asociată acelei adrese.<br />
<br />
In acest exemplu, vom folosi transcodorul pentru a realiza un modul ce controlează afișajul cu 7 segmente. Acest dispozitiv de afișare este format, așa cum sugerează și numele, din 7 segmente ce pot fi controlate separat. Putem aprinde sau stinge fiecare segment controlând tensiunea aplicată pe acesta. Pentru acest afișaj, comanda este negativă: aplicând 0 logic pe un segment acesta se va aprinde, iar aplicând 1 logic, acesta se va stinge.<br />
<br />
Structura unui afisaj cu 7 segmente este prezentată în imaginea de mai jos:<br />
<br />
[[Fișier:7LED display.PNG | 200px]]<br />
<br />
Pentru a realiza controlul afișării, avem nevoie de o memorie ROM ce trebuie să asocieze codului binar al fiecărui element ce poate fi afișat un șir de biți care sting sau aprind diversele segmente ale afișajului, astfel încât să apară imaginea elementului dorit.<br />
<br />
Pentru a reprezenta, de exemplu, cele 16 numere hexa, segmentele trebuie aprinse conform Anexei 1. De exemplu, pentru primele 8 numere (0 până la 7), tabelul de asociere este următorul:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Număr''' || '''Cod intrare''' || '''Șir de afisare''' <br />
|- align="center"<br />
| 0 || 4'b0000 || 7'b100_0000<br />
|- align="center"<br />
| 1 || 4'b0001 || 7'b111_1001<br />
|- align="center"<br />
| 2 || 4'b0010 || 7'b010_0100<br />
|- align="center"<br />
| 3 || 4'b0011 || 7'b011_0000<br />
|- align="center"<br />
| 4 || 4'b0100 || 7'b001_1001<br />
|- align="center"<br />
| 5 || 4'b0101 || 7'b001_0010<br />
|- align="center"<br />
| 6 || 4'b0110 || 7'b000_0010<br />
|- align="center"<br />
| 7 || 4'b0111 || 7'b111_1000<br />
|}<br />
<br />
'''Implementarea Verilog a modulului Display7Seg'''<br />
<syntaxhighlight lang="Verilog"><br />
module Display7Seg(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
4'd0: out = 7'b1000000;<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
default: out = 7'b0000110;<br />
endcase <br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
== Exerciții:==<br />
=== Exercițiul 1===<br />
Completați codul din Exemplul 2 astfel încât să se poată reprezenta toate numerele în bază 16 (hexazecimal), conform Anexei 1<br />
<br />
=== Exercițiul 2===<br />
Un procesor are nevoie de un șir de biți numit instrucțiune pentru a știi ce trebuie să facă. Această instrucțiune codează în șirul său de biți toate informațiile necesare. <br />
<br />
De exemplu, pentru un grup de instrucțiuni care operează cu registre, aceste informații pot fi:<br />
* codul instrucțiunii (operation code - opcode): un șir de biți care ne spune ce operație se execută.<br />
* destinația: un șir de biți care codează numărul registrului destinație.<br />
* sursa1: un șir de biți care codează numărul registrului folosit ca operand 1.<br />
* sursa2: un șir de biți care codează numărul registrului folosit ca operand 2.<br />
<br />
<br />
Vom considera codul operației pe 4 biți, ceea ce inseamnă ca putem avea 16 instrucțiuni diferite. ]n plus, câmpurile care codează destinația și cei doi operanzi vor avea fiecare câte 4 biți, ceea ce inseamnă că vom avea 16 registre cu care putem lucra (R0 ... R15). Adunând dimensiunile fiecărui câmp, vom obține o instrucțiune pe 16 biți.<br />
<br />
'''Instrucțiunile și codul operațiilor:'''<br />
<br />
Vom considera ca procesorul nostru elementar poate suporta următoarele instrucțiuni:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Operație''' || '''Cod operație''' || '''Sintaxă''' ||'''Descriere''' <br />
|- align="center"<br />
| ADD || 4'b0000 || ADD Rd Rs1 Rs2 || Rd = Rs1 + Rs2 <br />
|- align="center"<br />
| SUB || 4'b0001 || SUB Rd Rs1 Rs2 || Rd = Rs1 - Rs2<br />
|- align="center"<br />
| OR || 4'b0010 || OR Rd Rs1 Rs2 || Rd = Rs1 OR Rs2<br />
|- align="center"<br />
| AND || 4'b0011 || AND Rd Rs1 Rs2 || Rd = Rs1 AND Rs2<br />
|- align="center"<br />
| LOADC || 4'b0100 || LOADC Rd, const || Rd = const<br />
|}<br />
<br />
Pentru instrucțiunile prezentate mai sus, vom avea următoarele moduri de codare (de împărțire a informației în cadrul instrucțiunii):<br />
<br />
[[Fișier:Codare instructiune.png | 400px]]<br />
<br />
'''Registrele:'''<br />
<br />
Cele 16 registre vor fi codate cu un număr egal cu indexul său. De exemplu, R0 va avea codul 4'b000, R1 va avea codul 4'b0001 și așa mai departe.<br />
<br />
<br />
Considerând cele spuse mai sus, vom avea următoarele exemple de instrucțiuni:<br />
<br />
[[Fișier:Exemple codare instructiuni.png | 400px]]<br />
<br />
<br />
Având toate informațiile de mai sus, descrieți o memorie ROM cu adresă pe 4 biți și locații pe 16 biți, care să conțină următorul program, începând cu adresa 0:<br />
<br />
<syntaxhighlight lang="Verilog"><br />
<br />
LOADC R0, 2h<br />
LOADC R1, 5h<br />
LOADC R2, 7h<br />
ADD R3, R1, R0<br />
SUB R4, R2, R1<br />
OR R5, R3, R4<br />
<br />
</syntaxhighlight><br />
<br />
=== Exercițiul 3===<br />
Proiectați un Codor Cezar cu cheie fixă sub forma unei memorii ROM. <br />
<br />
Un Codor Cezar preia un mesaj și codează independent fiecare literă a acestuia pentru a obține un mesaj cifrat. El se folosește de o cheie fixă care se adună la fiecare literă a mesajului. De exemplu, pentru cheia "+3": a->d; b->e; c->f;...; y->b; z->c. Observați că atunci când alfabetul a ajuns la capăt, se reia de la început. <br />
Pentru decodare, se va folosi procedura inversă: pentru fiecare literă, vom aplica cheia cu semn invers (în exemplul nostru, cheia de decodare este "-3"). <br />
<br />
Observații, sfaturi, task-uri suplimentare:<br />
* Codorul va putea prelucra doar litere mici și spațiu gol (acesta rămâne neschimbat).<br />
* Cel mai usor mod de a scrie această funcționalitate este folosind un bloc de tip ''case''.<br />
* Pentru testare, se pot folosi în testbench variabile de tip ''reg'' pe 8 biți, astfel: <br />
<syntaxhighlight lang="Verilog"><br />
reg [7:0] litera;<br />
litera = "A";<br />
</syntaxhighlight><br />
* Avansat: În SystemVerilog există și tipul de date ''string'', ceea ce permite parcurgerea cu un ''for'' a mesajului. În Verilog clasic, astfel de parcurgeri sunt posibile, dar sintaxa este mai complicată.<br />
* Se va folosi codul ASCII pentru valori. Pentru o ușoara vizualizare a datelor în simulare, se poate schimba radix-ul în ASCII. <br />
* Avansat: Pentru a testa automat codul, se poate instanția încă un modul în testbench cu aceeași funcționalitate pe post de "golden model". Acest modul nu trebuie să fie sintetizabil și atunci sintaxa permite mai multă libertate (cum ar fi operația de modulo "%", utilă pentru testare, dar care determină un circuit complicat dacă ar fi sintetizată). Ieșirile celor două module din testbench sunt apoi comparate pentru a se verifica că ambele implementări oferă același rezultat.<br />
* Implementați și Decodorul Cezar și testați Codorul împreună cu acesta. Aveți grijă ca cele două module să aibă aceeași cheie.<br />
<br />
==Anexa1== <br />
<br />
[[Fișier:Anexa1.PNG | 800px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale&diff=7438Circuite Integrate Digitale2023-02-17T15:56:39Z<p>Mihai.antonescu: /* Lucrări de laborator */</p>
<hr />
<div>== Scopul laboratorului ==<br />
<br />
<br />
Laboratorul de circuite integrate digitale vine ca o completare practica a cursului de CID. <br />
Prin definitie, acesta se va axa pe elemente practice de simulare, sinteza si testare a circuitelor ce sunt prezentate la curs. <br />
<br />
Acest laborator se bazeaza pe limbajul "Verilog", acesta fiind unul din cele 2 limbaje de descriere hardware folosite in industrie la ora actuala (celalalt fiind "VHDL"). <br />
<br />
Platformele sunt structurate astfel incat sa contina cateva elemente de teorie minimala, un exemplu de circuit descris in cod Verilog si apoi exercitii pe care sa le rezolvati in ora de aplicatii (simulare/testare pe placa FPGA) si acasa (simulare). <br />
<br />
In laborator se va lucra in Vivado, program al firmei Xilinx, pe o placa PYNQ-Z2 impreuna cu o placa de extensie ce adauga leduri si butoane suplimentare. <br />
<br />
Circuitele digitale sunt o parte fundamentala a electronici moderne, cu numeroase oportunitati in industrie, atat in design cat si in verificarea circuitelor.<br />
<br />
Pe langa ce veti invata in acest laborator, limbajul ofera si capabilitati mai avansate pentru a descrie mai eficient circuitele dorite, cat si pentru testarea acestora. Sintaxa nu se termina cu ce invatati aici si pentru cei pasionati, nu ezitati sa va contactati cadrul didactic cu intrebari.<br />
<br />
Speram ca o sa va placa, veti invata si vi se va parea interesant ce veti vedea in orele de aplicatii ce urmeaza. Spor.<br />
<br />
<br />
== Tutoriale și documentații ==<br />
<br />
Programul Vivado este gratuit (necesita cont, dar este gratis) si poate fi descarcat si instalat urmand tutorialul de aici:<br />
# [[Tutorial instalare Vivado]]<br />
# [[Tutorial Vivado]].<br />
# [https://www.tulembedded.com/FPGA/ProductsPYNQ-Z2.html Pynq-Z2 - pagina oficiala]<br />
# [https://dpoauwgwqsy2x.cloudfront.net/Download/pynqz2_user_manual_v1_0.pdf Pynq-Z2 - user manual]<br />
# [[Pynq-Z2 - Pinout]].<br />
# [[FPGA - Introducere]].<br />
<br />
O lista de link-uri utile poate fi gasita aici:<br />
# [[Introducere. Verilog HDL]] (Sintaxa [[Verilog]])<br />
# [https://www.asic-world.com/digital/index.html Asic-world - digital]<br />
# [https://www.asic-world.com/verilog/index.html Asic-world - verilog]<br />
# [https://nandgame.com/ Joc online: Nandgame] <br />
# [https://circuitverse.org/simulator Simulator/joc online si grafic de circuite: Circuitverse] <br />
# [https://play.google.com/store/apps/details?id=com.ViacheslavRud.Circuit&hl=en_US&gl=US Joc android - Make it True: Solve the Circuit]<br />
# [https://www.edaplayground.com/ Simulatoare online (mod text): Edaplayground]<br />
# [https://wavedrom.com/ Utilitar desenare forme de unda]<br />
# [https://www.draw.io/ Utilitar desenare diagrame]<br />
# [[Domenii conexe]]<br />
<br />
== Lucrări de laborator ==<br />
<br />
# [[CID_aplicatii_1 : Generare de forme de unda]]<br />
# [[CID_aplicatii_2 : Instantiere si porti logice]]<br />
# [[CID_aplicatii_3 : Circuite combinationale elementare]]<br />
# [[CID_aplicatii_4 : Alte circuite combinationale]]<br />
# [[CID_aplicatii_5 : Exercitii cu circuite combinationale]]<br />
# CID_aplicatii_6 : Lucrarea 1 - circuite combinationale<br />
# [[CID_aplicatii_7 : Circuite secventiale elementare]]<br />
# [[CID_aplicatii_8 : Registre si memorii RAM]]<br />
# [[CID_aplicatii_9 : Numaratorul]]<br />
# [[CID_aplicatii_10 : Aplicatii cu numaratoare]]<br />
# [[CID_aplicatii_11 : Automate finite]]<br />
# [[CID_aplicatii_12 : Exercitii cu circuite secventiale]]<br />
# CID_aplicatii_13 : Lucrarea 2 - circuite secventiale<br />
# [[CID_aplicatii_14 : Circuite digitale complexe]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_11_:_Exercitii_cu_circuite_secventiale&diff=7437CID aplicatii 11 : Exercitii cu circuite secventiale2023-02-17T15:56:16Z<p>Mihai.antonescu: Mihai.antonescu a redenumit pagina CID aplicatii 11 : Exercitii cu circuite secventiale în CID aplicatii 12 : Exercitii cu circuite secventiale</p>
<hr />
<div>#REDIRECTEAZA [[CID aplicatii 12 : Exercitii cu circuite secventiale]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_12_:_Exercitii_cu_circuite_secventiale&diff=7436CID aplicatii 12 : Exercitii cu circuite secventiale2023-02-17T15:56:15Z<p>Mihai.antonescu: Mihai.antonescu a redenumit pagina CID aplicatii 11 : Exercitii cu circuite secventiale în CID aplicatii 12 : Exercitii cu circuite secventiale</p>
<hr />
<div>==Teorie==<br />
Acest laborator are rolul de a sedimenta cunostintele dobandite anterior. <br />
<br />
El consta in exercitii separate, unele date ca subiect la lucrarea 2 in anii anteriori.<br />
<br />
Unele exercitii contin si automate. Daca acestea nu au fost inca predate, puteti ignora partea aceea de circuit.<br />
<br />
Dupa cum se poate observa, exemplele sunt diverse ca domeniu de aplicabilitate. Prin asta se doreste a se arata importanta si diversitatea circuitelor digitale in societatea moderna.<br />
<br />
<br />
<br />
==Exercitii==<br />
===Exercitiul 1: Miniprocesor===<br />
<br />
Un exemplu de procesor Harvard minimal, avand memorii de data si instructiuni, un program counter si un ALU. <br />
<br />
Dimensiunile nu sunt realiste, acest lucru fiind asa pentru a putea fi sintetizat si testat pe placa.<br />
<br />
[https://wiki.dcae.pub.ro/images/7/76/Subiect_l2_miniprocesor.pdf miniprocesor.pdf]<br />
<br />
<br />
<br />
===Exercitiul 2: password checker===<br />
<br />
Verilog suporta codul ASCII deci puteti introduce si litere/string-uri care vor fi tratate prin reprezentarea lor binara.<br />
<br />
[https://wiki.dcae.pub.ro/images/5/5c/Subiect_l2_password_checker.pdf password_checker.pdf]<br />
<br />
<br />
<br />
===Exercitiul 3: pseudorandom number generator===<br />
<br />
[https://wiki.dcae.pub.ro/images/0/05/Subiect_l2_pseudorandom_nr_generator.pdf pseudorandom_nr_generator.pdf]<br />
<br />
<br />
<br />
===Exercitiul 4: calculator de siruri dupa formula data===<br />
<br />
[https://wiki.dcae.pub.ro/images/a/a2/Subiect_l2_sir_calculator.pdf calculator_siruri.pdf]<br />
<br />
<br />
<br />
===Exercitiul 5: UART tx - structural===<br />
<br />
UART este un standard pentru transmisiune de date seriale. Pentru o descriere a protocolului puteti citi: [https://www.circuitbasics.com/basics-uart-communication/ functionare_uart]<br />
<br />
[https://wiki.dcae.pub.ro/images/d/d0/Subiect_l2_uart_tx.pdf uart_tx.pdf]<br />
<br />
<br />
<br />
===Exercitiul 6: UART rx - behavioural=== <br />
<br />
Daca ati inteles cum functioneaza mecanismul de UART (si pentru a testa functionarea TX de la exercitiul anterior) puteti scrie modulul de RX (receiver) al protocolului, astfel incat cele 2 sa comunice intre ele.<br />
<br />
La nivel de TB ar fi amandoua instantiate si conectate intre ele. <br />
<br />
UART RX ar trebui sa scoata un puls al semnalului "valid" impreuna cu 8 biti de date daca transmisiunea a fost efectuata cu succes, datele care ies din el fiind cele care au intrat in modulul de tx. <br />
<br />
Pentru a compara abordarea structurala cu cea comportamentala, acest modul se doreste a fi facut comportamental.<br />
In always-urile acestuia vor aparea mecanisme de numarare care practic alcatuiesc un mic FSM de control. <br />
<br />
<br />
<br />
===Exercitiul 7: microcontrollere - oscillator control register=== <br />
<br />
Acest subiect prezinta un modul simplificat al mecanismului de control al oscilatorului de pe un microcontroller. Acest modul controleaza generarea smenalului de ceas ce se propaga catre microcontroller si deci viteza la care sistemul va functiona.<br />
<br />
[https://wiki.dcae.pub.ro/images/0/09/Subiect_l2_uc_osccon.pdf uc_osccon.pdf]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_13_:_Automate_finite&diff=7435CID aplicatii 13 : Automate finite2023-02-17T15:56:04Z<p>Mihai.antonescu: Mihai.antonescu a redenumit pagina CID aplicatii 13 : Automate finite în CID aplicatii 11 : Automate finite</p>
<hr />
<div>#REDIRECTEAZA [[CID aplicatii 11 : Automate finite]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_11_:_Automate_finite&diff=7434CID aplicatii 11 : Automate finite2023-02-17T15:56:02Z<p>Mihai.antonescu: Mihai.antonescu a redenumit pagina CID aplicatii 13 : Automate finite în CID aplicatii 11 : Automate finite</p>
<hr />
<div>== Automate finite ==<br />
<br />
Automatul finit este folosit pentru a descrie sisteme ce tranziteaza un numar finit de stari. Acest tip de sistem este foarte util atunci cand starile nu sunt tranzitate intr-un mod simplu, evident sau repetitiv. <br />
<br />
Automatul finit, numit și FSM (Finite State Machine), este un model matematic de computație, definit de următoarele elemente:<br />
* O mulțime finită de simboluri de intrare, numită alfabet de intrare;<br />
* O mulțime finită și nevidă de simboluri de ieșire, numită alfabet de ieșire;<br />
* O mulțime finită și nevidă de stări, din care doar una, numită starea curentă, este activă la un moment dat;<br />
* O stare inițială, care face parte din mulțimea stărilor;<br />
* O funcție de tranziție a stărilor, care calculează starea următoare a automatului în funcție de starea curentă și de simbolul de intrare;<br />
* O funcție de calcul a ieșirii, care determină simbolul de ieșire în funcție de starea curentă (în cazul automatelor de tip Moore) sau în funcție de starea curentă și de simbolul de intrare (în cazul automatelor de tip Mealy).<br />
<br />
[[[[Fișier:Fsm.png]]]]<br />
<br />
La fiecare front al ceasului, valoarea stării următoare, calculată de către CLC, va fi încărcată în registru. Dacă registrul are n biți, numărul maxim de stări care pot fi reprezentate este 2<sup>n</sup>. Acest număr poate fi mai mic, în funcție de schema de codare a stărilor. De exemplu, pentru n = 4, folosind codarea binară se pot reprezenta cel mult 16 stări (0000, 0001, 0010, ..., 1111), în timp ce pentru codarea one-hot, în care doar un singur bit poate lua valoarea 1, se pot reprezenta 4 stări (1000, 0100, 0010, 0001).<br />
<br />
Pentru a reprezenta comportamentul unui automat finit, putem folosi grafuri sau organigrame, conventiile de notare depinzand de tipul automatului (Moore sau Mealy).<br />
<br />
== Exemple == <br />
=== Exemplul 1: Automat ce detecteaza secvente de tipul 11....100....0===<br />
In acest exemplu va fi implementat un automat care detecteaza secventele de tipul 11....100....0. Pentru aceasta, automatul va avea o intrare pe un bit (''in'', pe care va veni secventa de biti) si doua iesiri: ''detectOk'', care semnalizeaza prin 1 ca nu a fost inca detectata o secventa ilegala si ''detectFail'', care semnalizeaza prin 1 ca a fost detectata o secventa ilegala (un 1 dupa 0). <br />
<br />
Automatul va avea 3 stari: <br />
* Q0 - STATE_READ1: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 1 (inca nu a aparut niciun 0).<br />
* Q1 - STATE_READ0: automatul intra in aceasta stare atunci cand apare pe intrare primul 0 si va ramane in aceasta atata timp cat pe intrare este 0.<br />
* Q2 - STATE_ERROR: automatul intra in aceasta stare atunci cand apare un 1 dupa 0 si va ramane blocat aici pana la reset.<br />
<br />
Pentru a coda cele 3 stari avem nevoie de minim 2 biti: STATE_READ1 = 2'b00, STATE_READ0 = 2'b01, STATE_ERROR = 2'b10.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM 111000.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1(<br />
input clock,<br />
input in,<br />
input reset,<br />
output detectOk,<br />
output detectFail<br />
);<br />
<br />
//Se asociaza sirurilor de biti folositi pentru codarea starilor nume ce pot fi folosite mai usor in cod.<br />
//La compilare, numele vor fi inlocuite in cod cu numerele asociate la inceput.<br />
localparam STATE_READ1 = 2'b00;<br />
localparam STATE_READ0 = 2'b01;<br />
localparam STATE_ERROR = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
//registrul de stare<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= STATE_READ1;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
//circuit combinational pentru calculul starii urmatoare<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
STATE_READ1: begin<br />
if(in == 0) state_next = STATE_READ0;<br />
end<br />
STATE_READ0: begin<br />
if(in == 1) state_next = STATE_ERROR;<br />
end<br />
STATE_ERROR: state_next = STATE_ERROR;<br />
default: state_next = STATE_READ1;<br />
<br />
endcase<br />
end<br />
<br />
<br />
//circuit combinational pentru calculul iesirilor<br />
assign detectOk = (state == STATE_READ0) || (state == STATE_READ1);<br />
assign detectFail = (state == STATE_ERROR);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM1'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM1_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire detectOk_t;<br />
wire detectFail_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 1;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 0;<br />
#10 in_t = 1;<br />
#10 $stop();<br />
end<br />
<br />
FSM1 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.detectOk(detectOk_t),<br />
.detectFail(detectFail_t)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
===Exemplul 2: Automat ce detecteaza fronturile crescatoare ale unui semnal===<br />
Automatul ce detecteaza fronturile crescatoare are un singur semnal de intrare ''in'', care reprezinta semnalul analizat si o singura iesire, ''out'', generand pe aceasta un puls lung cat o perioada de ceas la fiecare aparitie a unui front crescator pe ''in''.<br />
<br />
Vom avea nevoie de 3 stari:<br />
* Q0: automatul intra in aceasta stare la reset si va ramane in aceasta atata timp cat intrarea este 0 (inca nu a aparut niciun front crescator).<br />
* Q1: automatul intra in aceasta stare atunci cand apare pe intrare un front crescator. Dupa aparitia frontului crescator sunt doua posibilitati: linia ramane in 1, ceea ce duce la trecerea in starea Q2 care va duce iesirea in 0 dupa o perioada de ceas, sau linia trece in 0 dupa un ciclu de ceas, ceea ce duce la revenirea in starea Q0.<br />
* Q2: automatul intra in aceasta stare atunci cand linia de intrare ramane in 1 mai mult de o perioada de ceas. Automatul va ramane in aceasta stare atata timp cat ''in'' ramane in 1 si va reveni in starea Q0 imediat ce ''in'' revine in 0.<br />
<br />
Graful automatului este:<br />
<br />
[[Fișier:Organigrama FSM rising edge.png]]<br />
<br />
'''Implementarea automatului'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2(<br />
input clock,<br />
input in,<br />
input reset,<br />
output out<br />
);<br />
<br />
localparam Q0 = 2'b00;<br />
localparam Q1 = 2'b01;<br />
localparam Q2 = 2'b10;<br />
<br />
reg [1:0] state, state_next;<br />
<br />
always@(posedge clock) begin<br />
if(reset == 1)<br />
state <= Q0;<br />
else<br />
state <= state_next;<br />
end<br />
<br />
always@(*) begin<br />
state_next = state;<br />
case(state)<br />
Q0: begin<br />
if(in == 1) state_next = Q1;<br />
end<br />
Q1: begin<br />
if(in == 0) state_next = Q0;<br />
if(in == 1) state_next = Q2;<br />
end<br />
Q2: begin<br />
if(in == 0) state_next = Q0;<br />
end<br />
default: state_next = Q0;<br />
endcase<br />
end<br />
<br />
assign out = (state == Q1);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Implementarea unui modul de test pentru FSM2'''<br />
<syntaxhighlight lang="Verilog"><br />
module FSM2_TB();<br />
<br />
reg clock_t;<br />
reg reset_t;<br />
reg in_t;<br />
wire out_t;<br />
<br />
initial begin<br />
clock_t = 0;<br />
forever #1 clock_t = ~clock_t;<br />
end<br />
<br />
initial begin<br />
in_t = 0;<br />
reset_t = 1;<br />
#2 reset_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 in_t = 1;<br />
#5 in_t = 0;<br />
#10 $stop();<br />
end<br />
<br />
FSM2 DUT(<br />
.clock(clock_t),<br />
.reset(reset_t),<br />
.in(in_t),<br />
.out(out_t)<br />
);<br />
<br />
endmodule<br />
<br />
</syntaxhighlight><br />
<br />
==Exerciții==<br />
=== Exercițiul 1 ===<br />
Implementați un automat finit ce detectează fronturile descrescătoare. Acesta are un singur semnal de intrare ''in'', care reprezintă semnalul analizat și o singură ieșire ''out'', generând pe această un puls lung cât o perioadă de ceas la fiecare apariție a unui front descrescător pe ''in''.<br />
<br />
Realizați implementarea Verilog plecând de la graful automatului:<br />
<br />
[[Fișier:Graf FSM falling edge.png]]<br />
<br />
=== Exercițiul 2 ===<br />
Implementați un automat finit ce detectează atât fronturile crescătoare, cât și pe cele descrescătoare.<br />
<br />
=== Exercițiul 3 ===<br />
Să se implementeze un circuit care generează un semnal PWM cu un factor de umplere ce variază triungular, la fiecare front descrescător al unui semnal de intrare.<br />
<br />
Schema circuitului este:<br />
<br />
[[Fișier:FSM Exercitiu.png]]<br />
<br />
Modulele componente ale circuitului sunt:<br />
* '''FallingEdgeDetector''': Automat ce detectează fronturile descrescătoare ale semnalului de intrare.<br />
* '''TriangularCounter''': Automat ce generează la iesire secvența de numere 64, 128, 192, 255, 192, 128, 64.<br />
* '''Counter''': Numărător pe 8 biți cu reset sincron.<br />
* '''COMP''': Comparator cu două intrări pe 8 biți. Ieșirea sa va fi 1 atunci când counter < out și 0 în rest.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_4_:_Alte_circuite_combinationale&diff=7433CID aplicatii 4 : Alte circuite combinationale2023-02-17T15:48:57Z<p>Mihai.antonescu: /* Exercițiul 1 */</p>
<hr />
<div>== Memoria ROM==<br />
Memoria este un dispozitiv electronic utilizat pentru stocarea datelor. Aceasta poate fi de mai multe feluri, având diverse caracteristici, cum ar fi capacitatea de a-și păstra sau nu conținutul după întreruperea alimentării sau capacitatea de a-și modifica sau nu conținutul după terminarea procesului de producție. <br />
<br />
Memoria ROM (Read-Only Memory) este un circuit combinational folosit pentru stocarea unor date ce pot fi accesate cu ajutorul unei intrari de adresă. Asa cum sugerează și numele, memoria ROM clasică nu poate fi modificată. În plus, datorită modului în care este implementată, ea își păstrează conținutul după întreruperea alimentării (este nevolatilă). <br />
<br />
Pentru a înțelege termenul de memorie, putem face următorul exercițiu de imaginație: să ne gândim la un dulap cu mai multe sertare. Conținutul memoriei este reprezentat de conținutul sertarelor, iar adresele sunt reprezentate de etichetele lipite pe aceste sertare pentru a le identifica. Dacă avem nevoie de conținutul unui sertar, va trebui să știm eticheta aferentă acestuia (adresa).<br />
<br />
O memorie ROM are următorii parametri: numărul de biți ai adresei, care este legat de numarul de locații de memorie (cu n biți de adresă putem forma 2<sup>𝑛</sup> combinații diferite, deci putem accesa maxim 2<sup>𝑛</sup> locații de memorie) și dimensiunea locației de memorie, care ne spune cât de multă informație poate stoca o locație de memorie.<br />
<br />
<br />
Să urmărim exemplul din figura următoare:<br />
<br />
[[Fișier:ROM memory.PNG | 400px]]<br />
<br />
<br />
Aici, adresa are dimensiunea de 4 biți, asadar putem accesa cu aceasta 16 locații diferite de memorie. Dimensiunea fiecărei locații este de 8 biți, așadar fiecare locație de memorie poate stoca numere de 8 biți. Spunem că memoria ROM este o memorie 16x8. Capacitatea acestei memorii este de 16 x 8 biți = 128 biți.<br />
<br />
== Exemple ==<br />
=== Exemplul 1: Decodorul ===<br />
Decodorul este circuitul care, pentru o anumită valoare a intrării, va genera la ieșire un șir binar care conține 1 pe poziția cu indicele egal cu valorea intrării și 0 in rest. De exemplu, decodorul de mai jos pe 2 biți va avea următoarea corespondență intrare-iesire:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''in''' || '''out'''<br />
|- align="center"<br />
| 2'b00 || 4'b0001<br />
|- align="center"<br />
|2'b01 || 4'b0010<br />
|- align="center"<br />
|2'b10 || 4'b0100<br />
|- align="center"<br />
|2'b11 || 4'b1000<br />
|}<br />
<br />
[[Fișier:Decoder.PNG | 400px]]<br />
<br />
Vom implementa acest circuit ca memorie ROM.<br />
<br />
'''Implementarea Verilog a circuitului'''<br />
<syntaxhighlight lang="Verilog"><br />
module Decoder(<br />
input [1:0] in,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
2'b00: out = 4'b0001;<br />
2'b01: out = 4'b0010;<br />
2'b10: out = 4'b0100;<br />
2'b11: out = 4'b1000;<br />
default: out = 4'b0000;<br />
endcase<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Implementarea Verilog a modulului de test'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns/1ps<br />
<br />
module Decoder_TB();<br />
reg [1:0] in_tb;<br />
wire [3:0] out_tb;<br />
<br />
integer idx;<br />
<br />
initial begin<br />
for(idx=0; idx<4; idx = idx + 1) begin<br />
in_t = idx;<br />
#1;<br />
end <br />
#2 $stop();<br />
end<br />
<br />
Decoder DUT(<br />
.in(in_tb),<br />
.out(out_tb)<br />
);<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Observații:'''<br />
* Circuitul Decoder va avea o intrare pe 2 biți, care va reprezenta adresa memoriei ROM și o iesire pe 4 biți, reprezentând conținuturile memoriei ROM corespunzătoare fiecărei combinații de la intrare.<br />
* Chiar dacă în interiorul instrucțiunii ''case'' se acoperă toate cazurile posibile (toate combinațiile de 2 biți), este bine sa punem și un ''default'' pentru a evita inserarea în locul circuitului combinațional dorit (aici, memoria ROM) a unui latch.<br />
<br />
<br />
===Exemplul 2: Transcodorul===<br />
Trancodorul este o memorie ROM care asociază unei anumite intrări, reprezentată de adresă, un anumit cod, reprezentat de conținutului locației de memorie asociată acelei adrese.<br />
<br />
In acest exemplu, vom folosi transcodorul pentru a realiza un modul ce controlează afișajul cu 7 segmente. Acest dispozitiv de afișare este format, așa cum sugerează și numele, din 7 segmente ce pot fi controlate separat. Putem aprinde sau stinge fiecare segment controlând tensiunea aplicată pe acesta. Pentru acest afișaj, comanda este negativă: aplicând 0 logic pe un segment acesta se va aprinde, iar aplicând 1 logic, acesta se va stinge.<br />
<br />
Structura unui afisaj cu 7 segmente este prezentată în imaginea de mai jos:<br />
<br />
[[Fișier:7LED display.PNG | 200px]]<br />
<br />
Pentru a realiza controlul afișării, avem nevoie de o memorie ROM ce trebuie să asocieze codului binar al fiecărui element ce poate fi afișat un șir de biți care sting sau aprind diversele segmente ale afișajului, astfel încât să apară imaginea elementului dorit.<br />
<br />
Pentru a reprezenta, de exemplu, cele 16 numere hexa, segmentele trebuie aprinse conform Anexei 1. De exemplu, pentru primele 8 numere (0 până la 7), tabelul de asociere este următorul:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Număr''' || '''Cod intrare''' || '''Șir de afisare''' <br />
|- align="center"<br />
| 0 || 4'b0000 || 7'b100_0000<br />
|- align="center"<br />
| 1 || 4'b0001 || 7'b111_1001<br />
|- align="center"<br />
| 2 || 4'b0010 || 7'b010_0100<br />
|- align="center"<br />
| 3 || 4'b0011 || 7'b011_0000<br />
|- align="center"<br />
| 4 || 4'b0100 || 7'b001_1001<br />
|- align="center"<br />
| 5 || 4'b0101 || 7'b001_0010<br />
|- align="center"<br />
| 6 || 4'b0110 || 7'b000_0010<br />
|- align="center"<br />
| 7 || 4'b0111 || 7'b111_1000<br />
|}<br />
<br />
'''Implementarea Verilog a modulului Display7Seg'''<br />
<syntaxhighlight lang="Verilog"><br />
module Display7Seg(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(in) begin<br />
case(in)<br />
4'd0: out = 7'b1000000;<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
default: out = 7'b0000110;<br />
endcase <br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
== Exerciții:==<br />
=== Exercițiul 1===<br />
Completați codul din Exemplul 2 astfel încât să se poată reprezenta toate numerele în bază 16 (hexazecimal), conform Anexei 1<br />
<br />
=== Exercițiul 2===<br />
Un procesor are nevoie de un șir de biți numit instrucțiune pentru a știi ce trebuie să facă. Această instrucțiune codează în șirul său de biți toate informațiile necesare. <br />
<br />
De exemplu, pentru un grup de instrucțiuni care operează cu registre, aceste informații pot fi:<br />
* codul instrucțiunii (operation code - opcode): un șir de biți care ne spune ce operație se execută.<br />
* destinația: un șir de biți care codează numărul registrului destinație.<br />
* sursa1: un șir de biți care codează numărul registrului folosit ca operand 1.<br />
* sursa2: un șir de biți care codează numărul registrului folosit ca operand 2.<br />
<br />
<br />
Vom considera codul operației pe 4 biți, ceea ce inseamnă ca putem avea 16 instrucțiuni diferite. ]n plus, câmpurile care codează destinația și cei doi operanzi vor avea fiecare câte 4 biți, ceea ce inseamnă că vom avea 16 registre cu care putem lucra (R0 ... R15). Adunând dimensiunile fiecărui câmp, vom obține o instrucțiune pe 16 biți.<br />
<br />
'''Instrucțiunile și codul operațiilor:'''<br />
<br />
Vom considera ca procesorul nostru elementar poate suporta următoarele instrucțiuni:<br />
<br />
{| class="wikitable"<br />
|- align="center"<br />
||'''Operație''' || '''Cod operație''' || '''Sintaxă''' ||'''Descriere''' <br />
|- align="center"<br />
| ADD || 4'b0000 || ADD Rd Rs1 Rs2 || Rd = Rs1 + Rs2 <br />
|- align="center"<br />
| SUB || 4'b0001 || SUB Rd Rs1 Rs2 || Rd = Rs1 - Rs2<br />
|- align="center"<br />
| OR || 4'b0010 || OR Rd Rs1 Rs2 || Rd = Rs1 OR Rs2<br />
|- align="center"<br />
| AND || 4'b0011 || AND Rd Rs1 Rs2 || Rd = Rs1 AND Rs2<br />
|- align="center"<br />
| LOADC || 4'b0100 || LOADC Rd, const || Rd = const<br />
|}<br />
<br />
Pentru instrucțiunile prezentate mai sus, vom avea următoarele moduri de codare (de împărțire a informației în cadrul instrucțiunii):<br />
<br />
[[Fișier:Codare instructiune.png | 400px]]<br />
<br />
'''Registrele:'''<br />
<br />
Cele 16 registre vor fi codate cu un număr egal cu indexul său. De exemplu, R0 va avea codul 4'b000, R1 va avea codul 4'b0001 și așa mai departe.<br />
<br />
<br />
Considerând cele spuse mai sus, vom avea următoarele exemple de instrucțiuni:<br />
<br />
[[Fișier:Exemple codare instructiuni.png | 400px]]<br />
<br />
<br />
Având toate informațiile de mai sus, descrieți o memorie ROM cu adresă pe 4 biți și locații pe 16 biți, care să conțină următorul program, începând cu adresa 0:<br />
<br />
<syntaxhighlight lang="Verilog"><br />
<br />
LOADC R0, 2h<br />
LOADC R1, 5h<br />
LOADC R2, 7h<br />
ADD R3, R1, R0<br />
SUB R4, R2, R1<br />
OR R5, R3, R4<br />
<br />
</syntaxhighlight><br />
<br />
=== Exercițiul 3===<br />
Proiectați un Codor Cezar cu cheie fixă sub forma unei memorii ROM. <br />
<br />
Un Codor Cezar preia un mesaj și codează independent fiecare literă a acestuia pentru a obține un mesaj cifrat. El se folosește de o cheie fixă care se adună la fiecare literă a mesajului. De exemplu, pentru cheia "+3": a->d; b->e; c->f;...; y->b; z->c. Observați că atunci când alfabetul a ajuns la capăt, se reia de la început. <br />
Pentru decodare, se va folosi procedura inversă: pentru fiecare literă, vom aplica cheia cu semn invers (în exemplul nostru, cheia de decodare este "-3"). <br />
<br />
Observații, sfaturi, task-uri suplimentare:<br />
* Codorul va putea prelucra doar litere mici și spațiu gol (acesta rămâne neschimbat).<br />
* Cel mai usor mod de a scrie această funcționalitate este folosind un bloc de tip ''case''.<br />
* Pentru testare, se pot folosi în testbench variabile de tip ''reg'' pe 8 biți, astfel: <br />
<syntaxhighlight lang="Verilog"><br />
reg [7:0] litera;<br />
litera = "A";<br />
</syntaxhighlight><br />
* Avansat: În SystemVerilog există și tipul de date ''string'', ceea ce permite parcurgerea cu un ''for'' a mesajului. În Verilog clasic, astfel de parcurgeri sunt posibile, dar sintaxa este mai complicată.<br />
* Se va folosi codul ASCII pentru valori. Pentru o ușoara vizualizare a datelor în simulare, se poate schimba radix-ul în ASCII. <br />
* Avansat: Pentru a testa automat codul, se poate instanția încă un modul în testbench cu aceeași funcționalitate pe post de "golden model". Acest modul nu trebuie să fie sintetizabil și atunci sintaxa permite mai multă libertate (cum ar fi operația de modulo "%", utilă pentru testare, dar care determină un circuit complicat dacă ar fi sintetizată). Ieșirile celor două module din testbench sunt apoi comparate pentru a se verifica că ambele implementări oferă același rezultat.<br />
* Implementați și Decodorul Cezar și testați Codorul împreună cu acesta. Aveți grijă ca cele două module să aibă aceeași cheie.<br />
<br />
==Anexa1== <br />
<br />
[[Fișier:Anexa1.PNG | 800px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Domenii_conexe&diff=7408Domenii conexe2022-07-12T12:42:56Z<p>Mihai.antonescu: Pagină nouă: ==Introducere== Aceasta pagina exista pentru a va oferi cateva directii de invatare/cercetare, in cazul in care v-a placut aceasta materie si sunteti entuziasmati sa o aprofundati....</p>
<hr />
<div>==Introducere==<br />
Aceasta pagina exista pentru a va oferi cateva directii de invatare/cercetare, in cazul in care v-a placut aceasta materie si sunteti entuziasmati sa o aprofundati.<br />
Fiecare capitol va avea cateva propozitii despre respectivul domeniu si un link cu unde pot fi gasite mai multe informatii. <br />
<br />
Aceste domenii si teme sunt mai avansate si se recomanda intai finalizarea cursului de CID. <br />
<br />
<br />
<br />
==Verificare Functionala Pre-Siliciu==<br />
A fabrica un chip implica sume semnificative mai ales in etapa initiala de creere a mastilor. Din acest motiv un chip care se fabrica trebuie sa nu aibe defecte. <br />
<br />
Pentru a preveni defectele logice se ruleaza un numar urias de simulari cu tot felul de scenarii asupra codului Verilog/VHDL ce urmeaza a fi sintetizat. Domeniul care se ocupa cu construirea testbench-ului si a testelor ce vor fi rulate se numeste Verificare Functionala Presiliciu (Pre-Silicon Functional Verification). <br />
<br />
Pentru a putea scrie un cod cat mai general si reutilizabil, un inginer de verificare are nevoie de cunostiinte atat de circuite digitale, cat si de programare obiect orientat. <br />
<br />
In prezent se foloseste metodologia "UVM". Un loc de unde puteti incepe sa intelegeti aceasta metodologie este: [https://cluelogic.com/2011/07/uvm-tutorial-for-candy-lovers-overview/ cluelogic_uvm].<br />
<br />
<br />
<br />
==Procesoare== <br />
Prin natura sa, un FPGA permite flexibilitate maxima privind circuitele ce sunt puse pe el. Procesorul (fie ca vorbim de cele relativ simple din microcontroller, cele foarte complexe din calculatoarele moderne, sau orice intre acestea) este un circuit digital ce poate fi sintetizat din cod Verilog. Se realizeaza astfel sisteme complexe de calcul, cu unul sau mai multe procesoare, care uzual ajuta la coordonarea unui accelerator sau a altui sistem pus langa acestea in FPGA.<br />
<br />
Daca doriti aprofundarea acestui subiect, puteti consulta cursurile de Arhitectura Microprocesoarelor (sau chiar sa incercati sa construiti versiuni rudimentare ale procesoarelor prezentate acolo in Verilog) sau cursurile/laboratoarele de Arhitectura Sistemelor de Calcul de aici: [[Arhitectura Sistemelor de Calcul]]<br />
<br />
De asemenea se poate intra pe [https://opencores.org/ open cores]. Acest site contine numeroase core-uri ce pot fi studiate sau sintetizate si folosite. <br />
<br />
<br />
<br />
==Vivado - Zynq & MicroBlaze==<br />
Tot din categoria procesoare, Xilinx ne ofera 2 variante, in functie de placa pe care o avem la dispozitie. Este utila o diferentiere a procesoarelor in 2 categorii: <br />
<br />
1) hard cores - procesoare ce exista fizic pe placa sub forma de siliciu<br />
<br />
2) soft cores - procesoare ce sunt sintetizate si instantiate pe FPGA din LUT-uri.<br />
<br />
Sistemele de tip Zynq ofera pe langa FPGA si un procesor hard ARM dual core. Placa din laborator PYNQ-Z2 este un astfel de sistem.<br />
<br />
In cazul in care placa contine doar FPGA, se poate folosi un procesor MicroBlaze care este de tip soft core.<br />
<br />
Ambele procesoare se instantiaza folosind Vivado Block Design si sunt apoi programate in C folosind Vitis. <br />
<br />
Un tutorial despre programare sistemelor ZYNQ puteti gasi aici: [https://github.com/xupgit/Zynq-Design-using-Vivado Tutorial ZYNQ]<br />
<br />
Un tutorial despre programarea sistemelor cu MicroBlaze puteti gasi aici: [https://digilent.com/reference/programmable-logic/guides/getting-started-with-ipi Tutorial MicroBlaze]<br />
<br />
<br />
<br />
==Vivado - IP si Block Design==<br />
Cu cat proiectele devin mai mari si cu cat creste complexitatea submodulelor, cu atat devine necesarea reutilizari unor module ce au fost testat anterior si abordarea proiectului ca fiind alcatuit din mai multe IP-uri (Intellectual Property) ce comunica intre ele pe un anumite protocol. <br />
<br />
Pentru a usura aceasta sarcina, Vivado pune la dispozitia utilizatorului un mediu grafic in care sa instantieze si conecteze astfel de IP-uri. Se pot utiliza IP-uri puse la dispozitie de Xilinx sau se pot genera IP-uri proprii. <br />
<br />
Protocolul de comunicatie intre componente folosit de Xilinx se numeste AXI. <br />
<br />
<br />
<br />
==Protocoale de comunicatie==<br />
Pentru a putea avea componente facute de ingineri diferiti care sa poata comunica intre ele, este nevoie de protocoale clare ce guverneaza transmisia informatiei. <br />
<br />
Cei de la Xilinx folosesc protocolul AXI pentru comunicatie in cadrul aceluiasi fpga. <br />
<br />
Acest protocol are mai multe versiuni, o versiune completa ce cuprinde toate semnalele pentru o folosire cat mai generalizata, o versiune cu mai putine semnale, mai usor de implementat, ce contine semnalele vitale transmisiunii si o versiune de tip "stream" pentru transmisia datelor in cantitati mari. <br />
<br />
Pentru o mai buna intelegere a protocoalelor se poate vedea: [https://developer.arm.com/Architectures/AMBA Reference Manual] sau pentru o explicatie mai succinta: [https://developer.arm.com/documentation/102202/0200/AXI-protocol-overview#:~:text=AXI%20is%20an%20interface%20specification,These%20interface%20types%20are%20symmetrical. AXI-explicatii rapide] sau [https://www.allaboutcircuits.com/technical-articles/introduction-to-the-advanced-extensible-interface-axi/ AXI-explicatii rapide2]<br />
<br />
Exista si multe alte protocolae de comunicatie folosite in afara FPGA-ului sau pentru comunicarea cu exteriorul. Si in interiorul FPGA-ului se pot folosii protocoale custom, specifice aplicatiei dorite. Este totusi de dorit, pe cat posibil sa se foloseasca protocoale deja implementate pentru a fi mai usor celorlalti ingineri sa interactioneze cu componenta respectiva.<br />
<br />
<br />
<br />
==HLS==<br />
HLS (High Level Synthesis) reprezinta generarea si sinteza circuitelor pornind de la limbaje de nivel inalt (uzual C). <br />
Astfel se doreste reducerea timpului de lucru deoarece: <br />
<br />
1) Inginerul nu mai trebuie sa invete un limbaj nou (Verilog/VHDL) ci poate sa foloseasca un limbaj de nivel inalt pe care il cunoaste deja.<br />
<br />
2) Fiind un limbaj de nivel inalt, sansele sa apara erori/bug-uri sunt mai scazute decat la un limbaj de nivel foarte jos.<br />
<br />
3) Procesul de debug poate fi usurat.<br />
<br />
4) Codul scris este mai scurt.<br />
<br />
Xilinx ofera capabilitati de HLS prin programul Vitis HLS. Exmple privind Vitis HLS pot fi gasite aici: [https://github.com/Xilinx/Vitis-HLS-Introductory-Examples Vitis HLS exemple]<br />
<br />
O observatie importanta este faptul ca in unele situatii, circuitul rezultat prin HLS poate sa iasa semnificativ mai mare decat un circuit proiectat de mana in Verilog, astfel trebuie pus in balanta performanta dorita si timpul avut la dispozitie pentru dezvoltarea respectivului produs.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Circuite_Integrate_Digitale&diff=7407Circuite Integrate Digitale2022-07-12T11:07:35Z<p>Mihai.antonescu: /* Tutoriale și documentații */</p>
<hr />
<div>== Scopul laboratorului ==<br />
<br />
<br />
Laboratorul de circuite integrate digitale vine ca o completare practica a cursului de CID. <br />
Prin definitie, acesta se va axa pe elemente practice de simulare, sinteza si testare a circuitelor ce sunt prezentate la curs. <br />
<br />
Acest laborator se bazeaza pe limbajul "Verilog", acesta fiind unul din cele 2 limbaje de descriere hardware folosite in industrie la ora actuala (celalalt fiind "VHDL"). <br />
<br />
Platformele sunt structurate astfel incat sa contina cateva elemente de teorie minimala, un exemplu de circuit descris in cod Verilog si apoi exercitii pe care sa le rezolvati in ora de aplicatii (simulare/testare pe placa FPGA) si acasa (simulare). <br />
<br />
In laborator se va lucra in Vivado, program al firmei Xilinx, pe o placa PYNQ-Z2 impreuna cu o placa de extensie ce adauga leduri si butoane suplimentare. <br />
<br />
Circuitele digitale sunt o parte fundamentala a electronici moderne, cu numeroase oportunitati in industrie, atat in design cat si in verificarea circuitelor.<br />
<br />
Pe langa ce veti invata in acest laborator, limbajul ofera si capabilitati mai avansate pentru a descrie mai eficient circuitele dorite, cat si pentru testarea acestora. Sintaxa nu se termina cu ce invatati aici si pentru cei pasionati, nu ezitati sa va contactati cadrul didactic cu intrebari.<br />
<br />
Speram ca o sa va placa, veti invata si vi se va parea interesant ce veti vedea in orele de aplicatii ce urmeaza. Spor.<br />
<br />
<br />
== Tutoriale și documentații ==<br />
<br />
Programul Vivado este gratuit (necesita cont, dar este gratis) si poate fi descarcat si instalat urmand tutorialul de aici:<br />
# [[Tutorial instalare Vivado]]<br />
# [[Tutorial Vivado]].<br />
# [https://www.tulembedded.com/FPGA/ProductsPYNQ-Z2.html Pynq-Z2 - pagina oficiala]<br />
# [https://dpoauwgwqsy2x.cloudfront.net/Download/pynqz2_user_manual_v1_0.pdf Pynq-Z2 - user manual]<br />
# [[Pynq-Z2 - Pinout]].<br />
# [[FPGA - Introducere]].<br />
<br />
O lista de link-uri utile poate fi gasita aici:<br />
# [[Introducere. Verilog HDL]] (Sintaxa [[Verilog]])<br />
# [https://www.asic-world.com/digital/index.html Asic-world - digital]<br />
# [https://www.asic-world.com/verilog/index.html Asic-world - verilog]<br />
# [https://nandgame.com/ Joc online: Nandgame] <br />
# [https://circuitverse.org/simulator Simulator/joc online si grafic de circuite: Circuitverse] <br />
# [https://play.google.com/store/apps/details?id=com.ViacheslavRud.Circuit&hl=en_US&gl=US Joc android - Make it True: Solve the Circuit]<br />
# [https://www.edaplayground.com/ Simulatoare online (mod text): Edaplayground]<br />
# [https://wavedrom.com/ Utilitar desenare forme de unda]<br />
# [https://www.draw.io/ Utilitar desenare diagrame]<br />
# [[Domenii conexe]]<br />
<br />
== Lucrări de laborator ==<br />
<br />
# [[CID_aplicatii_1 : Generare de forme de unda]]<br />
# [[CID_aplicatii_2 : Instantiere si porti logice]]<br />
# [[CID_aplicatii_3 : Circuite combinationale elementare]]<br />
# [[CID_aplicatii_4 : Memorii ROM]]<br />
# [[CID_aplicatii_5 : Exercitii cu circuite combinationale]]<br />
# CID_aplicatii_6 : Lucrarea 1 - circuite combinationale<br />
# [[CID_aplicatii_7 : Circuite secventiale elementare]]<br />
# [[CID_aplicatii_8 : Registre si memorii RAM]]<br />
# [[CID_aplicatii_9 : Numaratorul]]<br />
# [[CID_aplicatii_10 : Aplicatii cu numaratoare]]<br />
# [[CID_aplicatii_11 : Exercitii cu circuite secventiale]]<br />
# CID_aplicatii_12 : Lucrarea 2 - circuite secventiale<br />
# [[CID_aplicatii_13 : Automate finite]]<br />
# [[CID_aplicatii_14 : Circuite digitale complexe]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Tutorial_Vivado&diff=7406Tutorial Vivado2022-07-12T11:02:31Z<p>Mihai.antonescu: /* Simularea unui circuit digital */</p>
<hr />
<div>== Instalare Vivado ==<br />
Pentru a instala utilitarul Vivado, acesta trebuie descarcat de pe site-ul [https://www.xilinx.com/support/download.html Xilinx], alegand versiunea potrivita sistemului de operare folosit. Pentru a putea descarca orice utilitar Xilinx, va trebui mai intai sa creati un cont. Pentru instalarea Vivado 2020.2 pe sistemul de operare Windows, puteti consulta pagina [[Tutorial instalare Vivado]].<br />
<br />
== Utilizarea Vivado pentru simularea si sinteza circuitelor digitale ==<br />
=== Crearea unui proiect ===<br />
Pentru a crea un proiect nou, mergeti la '''File->Project->New''', iar apoi vom urma pasii de configurare:<br />
<br />
'''Pasul 1: Denumirea proiectului si stabilirea caii catre acesta'''<br />
<br />
La acest pas vom denumi proiectul (de obicei, numele trebuie sa corespunda cu numele modulului TOP) si vom stabili calea la care se va crea acesta.<br />
<br />
[[Fișier:Vivado tutorial pas1.png]]<br />
<br />
'''Pasul 2: Alegerea tipului proiectului'''<br />
<br />
La acest pas, vom selecta RTL Project, apoi vom da click pe '''Next'''<br />
<br />
[[Fișier:Vivado tutorial pas 2.png]]<br />
<br />
'''Pasul 3: Adaugarea surselor deja existente'''<br />
<br />
La acest pas vom adauga proiectului sursele modulelor pentru care avem deja descrierea in fisiere de tip Verilog. Acest lucru se face prin '''Add Files''' si selectarea acestora. Daca nu avem fisiere de adaugat, putem da click direct pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 3.png|600px]]<br />
<br />
'''Pasul 4: Adaugarea fisierelor de constrangeri'''<br />
<br />
La acest pas vom adauga fisierele de constrangeri pentru sinteza si implemenatrea pe FPGA, daca este cazul. Daca nu, putem da direct click pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 4.png|600px]]<br />
<br />
'''Pasul 5: Alegerea dispozitivului tinta'''<br />
<br />
La acest pas vom alege dispozitivul tinta. In cazul laboratorului nostru, acesta este placuta '''PYNQ Z2'''. Mergem la tab-ul '''Boards''', alegem PYNQ Z2, iar apoi dam click pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 5.png|600px]]<br />
<br />
Dupa alegerea dispozitivului, va aparea o pagina cu rezumatul setarilor proiectului. Daca totul este corect, dam click pe '''Finish'''.<br />
<br />
'''Pasul 6: Pagina principala a proiectului''' <br />
<br />
[[Fișier:Vivado tutorial pas 6.png]]<br />
<br />
In fereastra principala a proiectului se pot observa urmatoarele zone: '''Flow navigator''', in care avem principalele comenzi utilizate in crearea, simularea, sinteza si implementarea unui circuit digital pe FPGA, '''Sources''', in care vedem lista cu sursele proiectului (fisiere verilog in care descriem modulele, fisiere utilizate in simulare, in care vom descrie modulele de test, fisierele de constrangeri), '''Consola''', in josul paginii si zona '''Project Sumary''', in care se vor deschide fisierele proiectului.<br />
<br />
<br />
=== Adaugarea sau crearea de fisiere===<br />
'''Pasul 1:''' In zona '''Sources''', dam click pe '''+''', iar apoi apare pe ecran fereastra in care trebuie sa selectam tipul fisierului: Vom alege '''Add or create constains''', daca dorim crearea sau adaugarea unui fisier de constrangeri, '''Add or create design sources''', in cazul in care dorim crearea sau sa adaugarea de fisiere in care vom descrie modulele digitale, sau '''Add or create simulation sources''', in cazul in care dorim adaugarea sau crearea de fisiere ce vor fi utlizate pentru simulare.<br />
<br />
[[Fișier:Vivado tutorial pas 7.png|600px]]<br />
<br />
'''Pasul 2:''' Daca dorim adaugarea unor fisiere existente, dam click pe '''Add File''' si navigam pana la locul in care se afla aceste fisere. Daca dorim crearea unui nou fisier, dam click pe '''Create File'''. Se va deschide o frestra mica in care vom completa numele fisierului (de obicei, acesta corespunde cu numele modului descris in interiorul sau) si vom selecta limbajul pe care il vom folosi (Verilog). Dam click pe '''OK''', iar apoi pe '''Finish'''.<br />
<br />
[[Fișier:Vivado tutorial pas 8.png|600px]]<br />
<br />
'''Pasul 3:''' Vom completa in urmatoarea fereastra numele modulului descris. Obtional, putem completa si lista porturile impreuna cu tipul si dimensiunea lor, Vivado creand astfel pentru noi scheletele descrierii. <br />
<br />
[[Fișier:Vivado tutorial pas 9.png|600px]]<br />
<br />
'''Pasul 4:''' Fisierul creat va aparea in '''Sources'''. Pentru a-l deschide, vom da doublu-click pe acesta, deschizandu-se in zona '''Project Summary'''.<br />
<br />
[[Fișier:Vivado tutorial pas 10.png|780px]]<br />
<br />
<br />
=== Simularea unui circuit digital ===<br />
Pentru a putea simula un circuit digital, avem nevoie de cel putin doua fisiere: un fisier ce contine descrierea circuitului si un fisier ce contine descrierea modulului de test (test bench). Dupa crearea celor doua module, pentru a rula simularea vom urma pasii urmatori:<br />
<br />
'''Pasul 1:''' In zona '''Flow navigator''' dam click pe '''Run Simulation''', iar apoi click pe '''Run Behavioral Simulation'''.<br />
<br />
[[Fișier:Vivado tutorial pas 11.png|800px]]<br />
<br />
'''Pasul 2:''' Dupa rularea simularii vom observa pe ecran trei zone principale: '''Scope''', in care vedem modulul si submodulele simulate, '''Objects''', in care vom vedea semnalele din interiorul fiecarui modul/submodul si '''Wave''', in care vom vedea forma de unda obtinuta in urma simularii.<br />
<br />
[[Fișier:Vivado tutorial pas 12.png|800px]]<br />
<br />
'''Pasul 3:''' Putem adauga semnale la forma de unda selectand semnalul dorit din fereastra '''Objects''', dand click dreapta si '''Add to Wave Window'''.<br />
<br />
[[Fișier:Vivado tutorial pas 13.png|800px]]<br />
<br />
'''Pasul 4:''' Pentru a observa evolutia in timp a semnalelor nou adaugate, simularea trebuie repornita. Pentru acesta, dam click pe '''Restart''', apoi pe '''Run All'''.<br />
<br />
[[Fișier:Vivado tutorial pas 14.png|800px]]<br />
<br />
'''Pasul 5:''' Folosind Zoom In / Zoom Out, stabilim un nivel optim de zoom, astfel incat sa vizualizarea formelor de unda sa fie semnificativa.<br />
<br />
[[Fișier:Vivado tutorial pas 15.png|800px]]<br />
<br />
Setarile initiale ale Vivado opresc simularea dupa 1000ns. Daca se doreste o simulare mai lunga si pentru a nu fi nevoie sa se apese run dupa cele 1000ns initiale, se poate introduce "-all" in Settings->Simulation->Simulation->xsim.simulate.runtime.<br />
<br />
=== Sinteza și implementarea unui circuit digital pe FPGA ===<br />
După ce am descris modulul și ne-am asigurat că nu există erori de sintaxă sau chiar erori funcșionale prin simulare, putem merge mai departe la sinteza și implementarea acestuia pe FPGA.<br />
<br />
'''Pasul 1: Elaborarea circuitului''': La acest pas, utilitarul interpretează codul Verilog, identificând componentele și modul în care ele sunt conectate, generând '''netlist'''-ul. Această etapă este independentă de tehnologie.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul1.png|800px]]<br />
<br />
După elaborare, putem vizualiza schema circutului din secțiunea '''Flow Navigator -> RTL ANALYSIS -> Open Elaborated Design -> Schematic'''<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul1 1.png|800px]]<br />
<br />
'''Pasul 2:''' Vom deschide planificatorul de pini din '''Layout -> I/O planning'''<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul2.png|800px]]<br />
<br />
'''Pasul 3: Asocierea pinilor plăcii cu porturile circuitului''' La acest pas vom asocia porturile circuitului descris cu [[Pynq-Z2 - Pinout|pinii plăcii FPGA]]. În plus, vom selecta pentru toți pinii standardul '''LVCMOS33''' (coloana '''I/O Std'''). Aceasta este o caracteristică hardware și poate diferi de la un dispozitiv la altul.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul3.png|800px]]<br />
<br />
'''Pasul 4: Sinteza circuitului''' La acest pas se realizează transformarea circuitului descris într-un netlist dependent de tehnologie. Se vor folosi la acest pas primitivele disponibile pe FPGA. În plus, deoarece am introdus în I/O Planning constrângerile legate de legarea porturilor la pinii plăcii, tot aici va fi integrată și această informație.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul4.png|800px]]<br />
<br />
'''Pasul 5: Implementarea circuitului''' La acest pas se preia netlist-ul ce conține primitivele FPGA și modul lor de interconectare realizat la pasul anterior și se realizează maparea lor efectivă în FPGA (place and route).<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul5.png|800px]]<br />
<br />
'''Pasul 6:''' După implementare, putem deschide circuitul elaborat pentru a studia căteva elemente precum schema circuitului sintetizat sau diverse rapoarte de sinteză.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6.png|800px]]<br />
<br />
Schema circuitului cu componente disponibile pe FPGA.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6 1.png|800px]]<br />
<br />
Fișierul de constrângeri generat. Acesta putea fi adăugat la proiect și scris manual, dacă nu doream să utilizîm I/O Planning.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6 2.png|800px]]<br />
<br />
'''Pasul 7: Generare a fișierului de programare''' Din circuitul de la punctul anterior, se generează un fișier cu extensia .bit care conține biții de configurare pentru blocurile logice, blocurile de I/O și blocurile de interconectare corespunzătoare circuitului.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul7.png|800px]]<br />
<br />
'''Pasul 8: Conectarea la placa FPGA''' Se deschide managerul hardware și ne conectăm la placa FPGA prin Auto Connect. Aceeași operațiunea poate fi realizată și din '''Flow Navigator -> PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target -> Auto Connect'''.<br />
[[Fișier:Vivado tutorial sinteza pasul8.png|800px]]<br />
<br />
'''Pasul 9: Programarea plăcii''' La acest pas se descarcă fișierul de configurare pe placă.<br />
[[Fișier:Vivado tutorial sinteza pasul9.png|800px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Tutorial_Vivado&diff=7405Tutorial Vivado2022-07-12T11:01:55Z<p>Mihai.antonescu: /* Simularea unui circuit digital */</p>
<hr />
<div>== Instalare Vivado ==<br />
Pentru a instala utilitarul Vivado, acesta trebuie descarcat de pe site-ul [https://www.xilinx.com/support/download.html Xilinx], alegand versiunea potrivita sistemului de operare folosit. Pentru a putea descarca orice utilitar Xilinx, va trebui mai intai sa creati un cont. Pentru instalarea Vivado 2020.2 pe sistemul de operare Windows, puteti consulta pagina [[Tutorial instalare Vivado]].<br />
<br />
== Utilizarea Vivado pentru simularea si sinteza circuitelor digitale ==<br />
=== Crearea unui proiect ===<br />
Pentru a crea un proiect nou, mergeti la '''File->Project->New''', iar apoi vom urma pasii de configurare:<br />
<br />
'''Pasul 1: Denumirea proiectului si stabilirea caii catre acesta'''<br />
<br />
La acest pas vom denumi proiectul (de obicei, numele trebuie sa corespunda cu numele modulului TOP) si vom stabili calea la care se va crea acesta.<br />
<br />
[[Fișier:Vivado tutorial pas1.png]]<br />
<br />
'''Pasul 2: Alegerea tipului proiectului'''<br />
<br />
La acest pas, vom selecta RTL Project, apoi vom da click pe '''Next'''<br />
<br />
[[Fișier:Vivado tutorial pas 2.png]]<br />
<br />
'''Pasul 3: Adaugarea surselor deja existente'''<br />
<br />
La acest pas vom adauga proiectului sursele modulelor pentru care avem deja descrierea in fisiere de tip Verilog. Acest lucru se face prin '''Add Files''' si selectarea acestora. Daca nu avem fisiere de adaugat, putem da click direct pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 3.png|600px]]<br />
<br />
'''Pasul 4: Adaugarea fisierelor de constrangeri'''<br />
<br />
La acest pas vom adauga fisierele de constrangeri pentru sinteza si implemenatrea pe FPGA, daca este cazul. Daca nu, putem da direct click pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 4.png|600px]]<br />
<br />
'''Pasul 5: Alegerea dispozitivului tinta'''<br />
<br />
La acest pas vom alege dispozitivul tinta. In cazul laboratorului nostru, acesta este placuta '''PYNQ Z2'''. Mergem la tab-ul '''Boards''', alegem PYNQ Z2, iar apoi dam click pe '''Next'''.<br />
<br />
[[Fișier:Vivado tutorial pas 5.png|600px]]<br />
<br />
Dupa alegerea dispozitivului, va aparea o pagina cu rezumatul setarilor proiectului. Daca totul este corect, dam click pe '''Finish'''.<br />
<br />
'''Pasul 6: Pagina principala a proiectului''' <br />
<br />
[[Fișier:Vivado tutorial pas 6.png]]<br />
<br />
In fereastra principala a proiectului se pot observa urmatoarele zone: '''Flow navigator''', in care avem principalele comenzi utilizate in crearea, simularea, sinteza si implementarea unui circuit digital pe FPGA, '''Sources''', in care vedem lista cu sursele proiectului (fisiere verilog in care descriem modulele, fisiere utilizate in simulare, in care vom descrie modulele de test, fisierele de constrangeri), '''Consola''', in josul paginii si zona '''Project Sumary''', in care se vor deschide fisierele proiectului.<br />
<br />
<br />
=== Adaugarea sau crearea de fisiere===<br />
'''Pasul 1:''' In zona '''Sources''', dam click pe '''+''', iar apoi apare pe ecran fereastra in care trebuie sa selectam tipul fisierului: Vom alege '''Add or create constains''', daca dorim crearea sau adaugarea unui fisier de constrangeri, '''Add or create design sources''', in cazul in care dorim crearea sau sa adaugarea de fisiere in care vom descrie modulele digitale, sau '''Add or create simulation sources''', in cazul in care dorim adaugarea sau crearea de fisiere ce vor fi utlizate pentru simulare.<br />
<br />
[[Fișier:Vivado tutorial pas 7.png|600px]]<br />
<br />
'''Pasul 2:''' Daca dorim adaugarea unor fisiere existente, dam click pe '''Add File''' si navigam pana la locul in care se afla aceste fisere. Daca dorim crearea unui nou fisier, dam click pe '''Create File'''. Se va deschide o frestra mica in care vom completa numele fisierului (de obicei, acesta corespunde cu numele modului descris in interiorul sau) si vom selecta limbajul pe care il vom folosi (Verilog). Dam click pe '''OK''', iar apoi pe '''Finish'''.<br />
<br />
[[Fișier:Vivado tutorial pas 8.png|600px]]<br />
<br />
'''Pasul 3:''' Vom completa in urmatoarea fereastra numele modulului descris. Obtional, putem completa si lista porturile impreuna cu tipul si dimensiunea lor, Vivado creand astfel pentru noi scheletele descrierii. <br />
<br />
[[Fișier:Vivado tutorial pas 9.png|600px]]<br />
<br />
'''Pasul 4:''' Fisierul creat va aparea in '''Sources'''. Pentru a-l deschide, vom da doublu-click pe acesta, deschizandu-se in zona '''Project Summary'''.<br />
<br />
[[Fișier:Vivado tutorial pas 10.png|780px]]<br />
<br />
<br />
=== Simularea unui circuit digital ===<br />
Pentru a putea simula un circuit digital, avem nevoie de cel putin doua fisiere: un fisier ce contine descrierea circuitului si un fisier ce contine descrierea modulului de test (test bench). Dupa crearea celor doua module, pentru a rula simularea vom urma pasii urmatori:<br />
<br />
'''Pasul 1:''' In zona '''Flow navigator''' dam click pe '''Run Simulation''', iar apoi click pe '''Run Behavioral Simulation'''.<br />
<br />
[[Fișier:Vivado tutorial pas 11.png|800px]]<br />
<br />
'''Pasul 2:''' Dupa rularea simularii vom observa pe ecran trei zone principale: '''Scope''', in care vedem modulul si submodulele simulate, '''Objects''', in care vom vedea semnalele din interiorul fiecarui modul/submodul si '''Wafe''', in care vom vedea forma de unda obtinuta in urma simularii.<br />
<br />
[[Fișier:Vivado tutorial pas 12.png|800px]]<br />
<br />
'''Pasul 3:''' Putem adauga semnale la forma de unda selectand semnalul dorit din fereastra '''Objects''', dand click dreapta si '''Add to Wave Window'''.<br />
<br />
[[Fișier:Vivado tutorial pas 13.png|800px]]<br />
<br />
'''Pasul 4:''' Pentru a observa evolutia in timp a semnalelor nou adaugate, simularea trebuie repornita. Pentru acesta, dam click pe '''Restart''', apoi pe '''Run All'''.<br />
<br />
[[Fișier:Vivado tutorial pas 14.png|800px]]<br />
<br />
'''Pasul 5:''' Folosind Zoom In / Zoom Out, stabilim un nivel optim de zoom, astfel incat sa vizualizarea formelor de unda sa fie semnificativa.<br />
<br />
[[Fișier:Vivado tutorial pas 15.png|800px]]<br />
<br />
Setarile initiale ale Vivado opresc simularea dupa 1000ns. Daca se doreste o simulare mai lunga si pentru a nu fi nevoie sa se apese run dupa cele 1000ns initiale, se poate introduce "-all" in Settings->Simulation->Simulation->xsim.simulate.runtime.<br />
<br />
=== Sinteza și implementarea unui circuit digital pe FPGA ===<br />
După ce am descris modulul și ne-am asigurat că nu există erori de sintaxă sau chiar erori funcșionale prin simulare, putem merge mai departe la sinteza și implementarea acestuia pe FPGA.<br />
<br />
'''Pasul 1: Elaborarea circuitului''': La acest pas, utilitarul interpretează codul Verilog, identificând componentele și modul în care ele sunt conectate, generând '''netlist'''-ul. Această etapă este independentă de tehnologie.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul1.png|800px]]<br />
<br />
După elaborare, putem vizualiza schema circutului din secțiunea '''Flow Navigator -> RTL ANALYSIS -> Open Elaborated Design -> Schematic'''<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul1 1.png|800px]]<br />
<br />
'''Pasul 2:''' Vom deschide planificatorul de pini din '''Layout -> I/O planning'''<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul2.png|800px]]<br />
<br />
'''Pasul 3: Asocierea pinilor plăcii cu porturile circuitului''' La acest pas vom asocia porturile circuitului descris cu [[Pynq-Z2 - Pinout|pinii plăcii FPGA]]. În plus, vom selecta pentru toți pinii standardul '''LVCMOS33''' (coloana '''I/O Std'''). Aceasta este o caracteristică hardware și poate diferi de la un dispozitiv la altul.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul3.png|800px]]<br />
<br />
'''Pasul 4: Sinteza circuitului''' La acest pas se realizează transformarea circuitului descris într-un netlist dependent de tehnologie. Se vor folosi la acest pas primitivele disponibile pe FPGA. În plus, deoarece am introdus în I/O Planning constrângerile legate de legarea porturilor la pinii plăcii, tot aici va fi integrată și această informație.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul4.png|800px]]<br />
<br />
'''Pasul 5: Implementarea circuitului''' La acest pas se preia netlist-ul ce conține primitivele FPGA și modul lor de interconectare realizat la pasul anterior și se realizează maparea lor efectivă în FPGA (place and route).<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul5.png|800px]]<br />
<br />
'''Pasul 6:''' După implementare, putem deschide circuitul elaborat pentru a studia căteva elemente precum schema circuitului sintetizat sau diverse rapoarte de sinteză.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6.png|800px]]<br />
<br />
Schema circuitului cu componente disponibile pe FPGA.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6 1.png|800px]]<br />
<br />
Fișierul de constrângeri generat. Acesta putea fi adăugat la proiect și scris manual, dacă nu doream să utilizîm I/O Planning.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul6 2.png|800px]]<br />
<br />
'''Pasul 7: Generare a fișierului de programare''' Din circuitul de la punctul anterior, se generează un fișier cu extensia .bit care conține biții de configurare pentru blocurile logice, blocurile de I/O și blocurile de interconectare corespunzătoare circuitului.<br />
<br />
[[Fișier:Vivado tutorial sinteza pasul7.png|800px]]<br />
<br />
'''Pasul 8: Conectarea la placa FPGA''' Se deschide managerul hardware și ne conectăm la placa FPGA prin Auto Connect. Aceeași operațiunea poate fi realizată și din '''Flow Navigator -> PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target -> Auto Connect'''.<br />
[[Fișier:Vivado tutorial sinteza pasul8.png|800px]]<br />
<br />
'''Pasul 9: Programarea plăcii''' La acest pas se descarcă fișierul de configurare pe placă.<br />
[[Fișier:Vivado tutorial sinteza pasul9.png|800px]]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_10_:_Aplicatii_cu_numaratoare&diff=7393CID aplicatii 10 : Aplicatii cu numaratoare2022-05-05T09:31:08Z<p>Mihai.antonescu: /* Exercitiul 4: PWM cu limita variabila */</p>
<hr />
<div><br />
==Teorie==<br />
<br />
Numaratoarele au extrem de multe aplicatii in lumea reala, cateva dintre acestea fiind descrise mai jos.<br />
<br />
<br />
==Exemplu: Numaratul apasarilor unui buton==<br />
<br />
Un prim sistem simplu cu numarator poate fi cel care contorizeaza de cate ori a fost apasat un buton sau, echivalent, cate persoane/masini au trecut printr-un senzor, de cate ori a fost deschisa o usa etc. Acest sistem se poate apoi dezvolta cu usurinta pentru a numara si oamenii care trec invers prin respectivul set de senzori prin adaugarea functionalitatii de up/down la numarator.<br />
<br />
Un astfel de sistem simplu (varianta doar cu numarat intrari) ar arata in felul urmator: <br />
<br />
[[Fișier:Aplicatii_numarator_exemplu_contor_senzor.png | 800px]]<br />
<br />
Circuitul de debounce are rolul de a "curata" semnalul de intrare, astfel incat chiar si in prezenta zgomotului sau a unei activari indelungate a senzorului/butonului, sa se contorizeze un singur eveniment. O explicatie mai detaliata puteti gasi [[Circuitul_de_debounce|aici]].<br />
<br />
Numaratorul numara evenimentele.<br />
<br />
Transcodorul pentru display cu 7 segmente este apoi folosit pentru o mai usoara vizualizare a numarului curent. <br />
<br />
'''Observatie:''' transcodorul pentru 7seg din exemplul de mai jos este in logica negativa. Daca circuitul fizic este in logica pozitiva trebuie adaugat un inversor la iesirea din acesta. De asemnea consider segmentul "a" pe bitul "0" si segmentul "h" pe bitul "6".<br />
<br />
<br />
'''Descrierea debouncer-ului (fisierul debounce.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module debounce<br />
#(<br />
parameter limit = 20'd650000<br />
) (<br />
input wire clock,<br />
input wire in,<br />
output wire out<br />
);<br />
<br />
reg [19:0] counter; // observatie: si circuitul de debounce foloseste un numarator<br />
reg hit;<br />
<br />
assign out = (counter == limit);<br />
<br />
always@(posedge clock) <br />
begin<br />
if(in == 0) <br />
begin<br />
counter <= 0;<br />
hit <= 0; <br />
end <br />
else <br />
begin<br />
if(counter == limit) <br />
begin<br />
hit <= 1;<br />
counter <= counter + 1;<br />
end <br />
else <br />
begin<br />
if(in == 1 & hit == 0) <br />
begin<br />
counter <= counter + 1;<br />
end<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea numaratorului pe 4b(fisierul counter_4b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module counter_4b<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire en,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(posedge clock)<br />
begin<br />
if(reset == 1)<br />
begin<br />
out <=0;<br />
end<br />
else<br />
begin<br />
if(en == 1)<br />
begin<br />
out <= out + 1;<br />
end<br />
else<br />
begin<br />
out <= out;<br />
end<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea transcodorului pentru afisaj cu 7seg(fisierul transcodor_7seg.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module transcodor_7seg // logica negativa <br />
(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(*)<br />
begin<br />
case(in)<br />
4'd0: out = 7'b1000000;// h....a<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
4'd8: out = 7'b0000000;<br />
4'd9: out = 7'b0010000;<br />
4'd10: out = 7'b0001000;<br />
4'd11: out = 7'b0000011;<br />
4'd12: out = 7'b1000110;<br />
4'd13: out = 7'b0100001;<br />
4'd14: out = 7'b0000110;<br />
4'd15: out = 7'b0001110;<br />
default: out = 7'b1111111;//tot stins <br />
endcase <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea sistemului, modulul de top (fisierul top.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
<br />
module top<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire button,<br />
output wire [6:0] out // logica negativa <br />
);<br />
<br />
wire debounce_0_X_out;<br />
wire [3:0] counter_4b_0_X_out; <br />
<br />
debounce<br />
#(<br />
.limit(20'd50_000)<br />
) debounce_0 (<br />
.clock(clock),<br />
.in(button),<br />
.out(debounce_0_X_out)<br />
);<br />
<br />
counter_4b counter_4b_0<br />
(<br />
.clock(clock),<br />
.reset(reset),<br />
.en(debounce_0_X_out),<br />
.out(counter_4b_0_X_out)<br />
); <br />
<br />
transcodor_7seg transcodor_7seg_0 // logica negativa <br />
(<br />
.in(counter_4b_0_X_out),<br />
.out(out)<br />
); <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea test bench-ului(fisierul tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg button_tb;<br />
wire [6:0] out_tb;<br />
<br />
top dut<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.button(button_tb),<br />
.out(out_tb) // logica negativa <br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever<br />
begin<br />
#5 clock_tb = ~clock_tb;<br />
end<br />
end<br />
<br />
initial<br />
begin<br />
reset_tb = 0;<br />
button_tb = 0;<br />
<br />
#50;<br />
reset_tb = 1;<br />
#50;<br />
reset_tb = 0;<br />
#100;<br />
<br />
repeat(5) // vreau 5 apasari de buton<br />
begin<br />
button_tb = 1;<br />
repeat(70_000) <br />
begin<br />
@(posedge clock_tb);<br />
end<br />
button_tb = 0;<br />
repeat(70_000)<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
end<br />
<br />
#1000 $stop();<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/5/53/Aplicatii_numarator_exemplu_contor.zip Exemplu_aplicatii_numarator.zip]<br />
<br />
==Exercitii==<br />
<br />
=== Exercitiul 1: Divizor de frecventa cu puteri ale lui 2=== <br />
<br />
Divizorul de frecventa are rolul de a genera un semnal de ceas mai lent din semnalul de ceas principal. <br />
El este alcatuit doar dintr-un numarator, iar fiecare bit al acestuia va fi practic un semnal de ceas cu frecventa din ce in ce mai mica astfel (la jumate): <br />
<br />
bit[0] - frecventa de 2 ori mai mica decat semnalul de ceas<br />
<br />
bit[1] - frecventa de 2 ori mai mica decat bit[0], deci de 4 ori mai mica decat semnalul de ceas <br />
<br />
bit[2] - frecventa de 2 ori mai mica decat bit[1], deci de 8 ori mai mica decat semnalul de ceas <br />
<br />
<br />
Acest lucru se poate observa si in poza ce urmeaza: <br />
<br />
[[Fișier:Freq_divider_output.png| 800px]] <br />
<br />
Implementati acest circuit, si folosind ledurile prezente pe placa, incercati sa conectati bitul corespunzator la leduri astfel incat cel mai din dreapta led sa clipeasca cu o frecventa cat mai apropiata de 1s. <br />
<br />
'''Observatie:''' Printr-un astfel de divizor de frecventa minimal, un numar foarte mic de frecvente poate fi generat. Daca se doreste generarea unui semnal periodic cu o perioada exacta, diferita de cele posibile prin acest mecanism, se poate construi urmatorul circuit:<br />
<br />
<br />
===Exercitiul 2: Divizor de frecventa ce poate genera orice perioada=== <br />
<br />
Un astfel de divizor de frecventa este construit ca in figura de mai jos:<br />
<br />
[[Fișier:Freq_div_orice_freq.png | 800px]]<br />
<br />
Prin adaugarea constantei "limit", perioada semnalului de iesire poate sa fie orice multiplu a perioadei semnalului de ceas. <br />
<br />
Bistabilul de tip t, se modifica atunci cand limita este atinsa si practic iesirea lui este semnalul periodic dorit (cu observatia ca o perioada a semnalului de iesire necesita 2 numarari pana la limita).<br />
<br />
Sa se calculeze valoarea necesara pentru "limit" astfel incat pe leduri sa se genereze semnal cu perioada exact 1s.<br />
<br />
<br />
'''Observatie:''' In FPGA exista fizic trasee speciale pentru reset si ca sinteza sa le foloseasca pe acestea in mod optim nu este recomandat sa se puna logica (cum e aici poarta sau) pe acestea. Modificati schema de mai sus astfel incat sa eliminati respectiva poarta de pe reset. (Sfat: numaratorul va avea acum si intrari de load: semnalul de comanda si data propriu zisa)<br />
<br />
===Exercitiul 3: PWM (Pulse Width Modulation)===<br />
<br />
PWM este un concept folosit foarte des in multiple ramuri ale ingineriei, de la transmisiunea informatiei pana la controlul motoarelor sau al intensitatii ledurilor din instalatii de Craciun. El se refera la a avea un semnal periodic cu factor de umplere variabil, asa cum este aratat in poza de mai jos :<br />
<br />
[[Fișier:Pwm.png| 800px]]<br />
<br />
<br />
Generarea unui astfel de semnal periodic se face printr-un numarator si un comparator, ca in figura de mai jos:<br />
<br />
[[Fișier:Pwm_schematic.png | 800px]]<br />
<br />
<br />
Raportul dintre limita pusa si valoarea la care numaratorul se reseteaza este practic factorul de umplere selectat.<br />
<br />
Pentru testare pe placa, cei 6b ai limitei provin de la switch-uri si butoane. Afisarea se face pe led[0].<br />
<br />
Testati acest circuit in simulare si apoi implementati pe placa pentru o valoare fixa a limitei. Implementati in paralel mai multe generatoare de semnal PWM cu limite diferite, astfel incat sa observati diferentele de intentistate dintre ledurile comandate de acestea.<br />
<br />
<br />
===Exercitiul 4: PWM cu limita variabila===<br />
<br />
Un exemplu foarte uzual de folosire a PWM este aprinderea ledurilor cu diverse pattern-uri. Factorul de umplere determina intensitatea cu care ledul este aprins. Un PWM cu o limita variabila automata va face ledul sa para din ce in ce mai stins sau din ce in ce mai luminos (exemplu: instalatii de Craciun). Combinand asta cu leduri RGB se obtin efecte de schimbare a culorii ledului aparent la intamplare. <br />
<br />
Un astfel de circuit se realizeaza punand inca un numarator in locul limitei, ca in poza de mai jos:<br />
<br />
[[Fișier:Pwm_duty_cycle_up_schematic.png | 800px]]<br />
<br />
<br />
Pentru o functionalitate suplimentara, anume a pastra un anumit factor de umplere mai multe perioade, am adaugat inca un numarator.<br />
<br />
Pentru claritatea desenului, nu am mai tras efectiv firul de ceas catre intrarile unde acesta se duce.<br />
<br />
<br />
Rezultatul este: <br />
<br />
[[Fișier:Pwm_duty_cycle_up_output.png | 800px]]<br />
<br />
<br />
Testati circuitul propus prin simulare si apoi vizualizati implementarea sa pe placa. Alegeti limite potrivite astfel incat sa puteti observa usor rezultatul.<br />
<br />
<br />
'''Bonus:''' Incercati sa adaugati si parametri pentru LIMIT_DUTY_CYCLE_LOW si LIMIT_DUTY_CYCLE_HIGH, care sa permita factorului de umplere sa varieze doar intre ele (numaratorul va avea nevoie de intrari pentru comanda de load si data load, ca sa poata incepe de la orice valoare).<br />
<br />
'''Bonus:''' Modificati circuitul astfel incat semnalul de iesire sa isi schimbe sensul de variatie al factorului de umplere cand ajunge cu acesta la capat. In forma curenta factorul de umplere creste de la 0% la 100% si apoi se reseteaza brusc la 0%, repetand acest ciclu. Se vrea ca la ajungerea la 100% sa inceapa o scadere treptata catre 0%, urmata apoi de o urcare s.a.m.d.<br />
<br />
===Exercitiul 5: Ceas===<br />
<br />
Un ceas poate fi construit cifra cu cifra, folosind numaratoare si comparatoare (si transcodoare pentru display cu 7seg ca sa se vada totul mai bine pe placa).<br />
<br />
Incercati sa implementati un ceas cu milisecunde, secunde si minute in varianta comportamentala. <br />
<br />
'''Observatie:''' Va fi nevoie fie separat, fie in modul de un numarator cu frecventa rezolutiei dorite (aici 1 ms).<br />
<br />
'''Observatie:''' Codul va contine multe "if" de tipul "daca cifra unitatilor secundelor a ajuns la 10, se face 0 si cresc cifra zecilor", repetate pentru fiecare cifra.<br />
<br />
<br />
Incercati si o implementare structurala a ceasului, mergand pe aceeasi idee. Fiecare cifra va contine numaratorul ei. Cand numaratorul unei cifre ajunge ajunge la limita sa, va da enable pentru numaratorul cifrei urmatoare. Daca este nevoie se pot folosi si porti aditionale (ex: ca sa creasca minutul cifra zecilor secundelor trebuie sa fie 5 si cifra unitatilor secundelor trebuie sa fie 9). <br />
<br />
'''Observatie:''' Va fi nevoie de un numarator cu frecventa rezolutiei dorite (aici 1 ms sau 1s).<br />
<br />
'''Observatie:''' Pentru a scrie mai putin puteti face un numarator doar cu secunde si minute. Principiul de baza este acelasi si daca aveati milisecunde.<br />
<br />
'''Observatie:''' Simularea a cateva milisecunde sau chiar secunde este un proces indelungat (dureaza minute-ore fizice), astfel strict pentru simulari se recomanda ceasul sa functioneze cu microsecunde sau milisecunde maxim.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_8_:_Registre_si_memorii_RAM&diff=7387CID aplicatii 8 : Registre si memorii RAM2022-04-18T13:09:30Z<p>Mihai.antonescu: /* Teorie */</p>
<hr />
<div><br />
<br />
==Teorie==<br />
<br />
Acest laborator are scopul de a prezenta circuitele secventiale simple: registre si memorii RAM. <br />
<br />
<br />
Pornind de la bistabilii prezentati/construiti in laboratorul anterior, apare notiunea de registru, acesta fiind o grupare de bistabili. Astfel in loc sa se memoreze un singur bit de informatie (bistabil), se pot memora acum numere pe mai multi biti (registru).<br />
<br />
Din exterior, un registru este vazut astfel: <br />
<br />
[[Fișier:Registru_exterior_view.png | 400px]]<br />
<br />
<br />
:Semnalul de "clock" controleaza sincronizarea registrilor din tot sistemul.<br />
:Semnalul de "reset" aduce registrul la o valoare initiala (uzual 0).<br />
:Semnalul de "we" (write enable) controleaza salvarea unor date noi. Cand acesta este activ, data de pe intrarea "data_in" se salveaza in registru.<br />
:Semnalul "data_in" reprezinta datele ce se doresc a fi scrise in registru.<br />
:Semnalul "data_out" reprezinta valoarea stocata in registru.<br />
<br />
<br />
Semnalele "data_in" si "data_out" pot fi pe oricat de multi biti se doreste, in mod uzual multipli de 8.<br />
<br />
<br />
<br />
Pornind de la notiunea de registru (care poate fi vazut ca o memorie cu o locatie si avand "m" biti) se doreste cresterea acestei memorii astfel incat aceasta sa curpinda mai multe locatii adresabile, unde se pot stoca date. Adaugand mai multi registri in paralel, unul langa altul, si cateva circuite de tip mux/demux se poate obtine o memorie de tip RAM (Random Access Memory), cu "m" locatii de "n" biti fiecare. Aceste memorii se cheama "Random Access" deoarece permit si scriere si citire. In functie de tehologia folosita, memoriile RAM pot fi construite din bistabili sau din latch-uri.<br />
<br />
<br />
Din exterior, o memorie RAM este vazuta astfel: <br />
<br />
[[Fișier:Ram_mXn_1read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Semnalele acestui circuit se pot imparti in semnale ce tin de interfata de scriere sau interfata de citire si mai apoi in semnale de date si semnale de control. Interfata sa este: <br />
<br />
:Semnalul de "clock": controleaza sincronizarea registrilor din memorie.<br />
:Semnalul "addr_read": adresa de la care se citesc datele. <br />
:Semnalul "data_read": data ce se citeste.<br />
:Semnalul de "we": semnal de control ce controleaza activarea scrierii. Scrierea are loc doar cand acest semnal este activ.<br />
:Semnalul "addr_write": adresa la care se scriu datele.<br />
:Semnalul "data_write": data ce urmeaza a fi scrisa atunci cand semnalul "we" este activ.<br />
<br />
<br />
<br />
'''Observatie''': Orice memorie are "m" locatii de "n" biti. Semnalele de "data_read" si "data_write" au aceeasi dimensiune, "n", numarul de biti ai fiecarei locatii. Semnalele de adresa au dimensiunea log2(m). Pentru o memorie cu 16 locatii va fi nevoie de 4 biti de adresa pentru a putea selecta orice locatie, pentru 32 locatii 5b s.a.m.d. <br />
<br />
'''Observatie''': In unele situatii se mai poate pune un registru suplimentar pe iesirea datelor, cu rol in sincronizare si evitarea timpilor de propagare prea lungi intre elemente de memorare (ajuta implementarea conceptului de pipeline).<br />
<br />
'''Observatie''': Exista mai multe variante de memorii RAM (cu registru pe iesire/fara, multiport/single port citire, adrese separate sau nu pentru citire/scriere). Cea de mai sus este o memorie cu un singur port de citire, fara registru suplimentar pe iesire, ce foloseste adrese distincte pentru citire si pentru scriere (la fel de bine se poate lucra si cu o singura adresa, comuna pentru scriere si citire).<br />
<br />
'''Observatie''': Memoriile RAM pot sa nu aiba reset, utilizatorul trebuie apoi sa aiba grija sa citeasca doar din locatii scrise de el anterior.<br />
In mod uzual ele nu au reset.<br />
<br />
==Exemple==<br />
<br />
===Exemplul 1 : Registrul===<br />
<br />
In urmatorul exemplu se implementeaza registrul descris mai sus:<br />
<br />
<br />
'''Descrierea registrului (fisierul register_8b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b<br />
(<br />
input wire clock,<br />
input wire reset, // activ pe "1"<br />
input wire we,<br />
input wire [7:0] data_in, <br />
output reg [7:0] data_out // data_out are aceeasi dimensiune ca data_in<br />
);<br />
<br />
always@(posedge clock) // clock sincronizeaza actiunile circuitului<br />
begin // doar pe edge-ul pozitiv circuitul actioneaza<br />
if(reset == 1)<br />
begin<br />
data_out <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1) // comanda de scriere<br />
begin<br />
data_out <= data_in;<br />
end<br />
else // puteam sa omit acest else<br />
begin<br />
data_out <= data_out; // raman datele salvate anterior<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
Un alt mod de a scrie un registru este dat mai jos, cu observatia ca aici am pus semnalul de reset activ in logica negativa:<br />
<br />
'''Descrierea registrului (fisierul register_8b_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b_v2<br />
(<br />
input wire clock,<br />
input wire reset_n, // activ in "0"<br />
// uzual semnalel cu n in fata sau la sfarsit sunt in logica negativa: nreset, resetn<br />
input wire we,<br />
input wire [7:0] data_in,<br />
output wire [7:0] data_out<br />
);<br />
<br />
reg [7:0] memorie_efectiva; <br />
<br />
assign data_out = memorie_efectiva;<br />
<br />
always@(posedge clock)<br />
begin <br />
if( reset_n == 0) <br />
begin<br />
memorie_efectiva <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1)<br />
begin<br />
memorie_efectiva <= data_in;<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
<br />
'''Descrierea test bench-ului registrului pe 8biti: (fisierul register_8b_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module register_8b_tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg we_tb;<br />
reg [7:0] data_in_tb;<br />
wire [7:0] data_out_tb;<br />
<br />
register_8b dut // varianta cu reset activ in "1"<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.we(we_tb),<br />
.data_in(data_in_tb),<br />
.data_out(data_out_tb)<br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
reset_tb <= 0;<br />
we_tb <= 0;<br />
data_in_tb <= 0;<br />
// observatie: pana la primul reset sau prima scriere, valoarea din registru va fi necunoscuta (in simulare X)<br />
<br />
// dau reset la circuit<br />
@(posedge clock_tb); // astept sa treaca 1 clock cycle<br />
reset_tb <= 1; <br />
@(posedge clock_tb);<br />
reset_tb <= 0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb <= 1;<br />
data_in_tb <= 5;<br />
@(posedge clock_tb);<br />
we_tb <= 0;<br />
data_in_tb <= 10; // scrierea asta nu se face deoarece nu am write enable activ<br />
@(posedge clock_tb);<br />
data_in_tb <= 11; // nici asta<br />
@(posedge clock_tb);<br />
data_in_tb <= 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb = 1;<br />
data_in_tb <= 42; // asta da<br />
@(posedge clock_tb);<br />
data_in_tb <= 51; // si asta da<br />
@(posedge clock_tb); <br />
we_tb <= 0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/7/7b/Exemplu_registru_8b.zip Exemplu_registru_8b.zip]<br />
<br />
<br />
<br />
===Exemplul 2: Memorie RAM===<br />
<br />
In urmatorul exemplu se implementeaza memoria RAM descrisa mai sus, particularizata pentru 64 de locatii a cate 8b fiecare:<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v1.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v1<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read, // 64 locatii => 6 biti de adresa <br />
output wire [7:0] data_read, // fiecare locatie are 8b <br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; // memorie cu locatiile de la 0 la 63, fiecare avand 8 biti <br />
<br />
assign data_read = memorie_efectiva[addr_read]; // fara registru pe iesire => citire asincrona fata de clock <br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] <= data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Acesta va fi folosit si pe post de modul de "top". <br />
<br />
Echivalent se poate folosi si sintaxa cu always ca mai jos.<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v2 // varianta cu always combinational pe citire<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read,<br />
output reg [7:0] data_read,<br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; <br />
<br />
always@(*) // citire asincrona fata de clock <br />
begin<br />
data_read = memorie_efectiva[addr_read]; <br />
end<br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] <= data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Descrierea test bench-ului memoriei RAM: (fisierul ram64x8_v1_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module ram64x8_tb();<br />
<br />
reg clock_tb;<br />
reg [5:0] addr_read_tb;<br />
wire [7:0] data_read_tb;<br />
reg we_tb;<br />
reg [5:0] addr_write_tb;<br />
reg [7:0] data_write_tb;<br />
<br />
<br />
ram64x8_v1 dut<br />
(<br />
.clock(clock_tb),<br />
//interfata de citire<br />
.addr_read(addr_read_tb), // 64 locatii => 6 biti de adresa <br />
.data_read(data_read_tb), // fiecare locatie are 8b <br />
// interfata de scriere<br />
.we(we_tb),<br />
.addr_write(addr_write_tb),<br />
.data_write(data_write_tb)<br />
);<br />
<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
we_tb <= 0;<br />
data_write_tb <= 0;<br />
addr_read_tb <= 0; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
addr_write_tb <= 0;<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb <= 1; // scriu data 5 la adresa 10<br />
addr_write_tb <= 10;<br />
data_write_tb <= 5;<br />
addr_read_tb <= 11; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
@(posedge clock_tb);<br />
we_tb <= 0; // scrierea asta nu se face deoarece nu am write enable activ <br />
addr_write_tb <= 11;<br />
data_write_tb <= 10; <br />
@(posedge clock_tb);<br />
data_write_tb <= 11; // nici asta<br />
@(posedge clock_tb);<br />
data_write_tb <= 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb <= 1;<br />
addr_write_tb <= 20; // scriere ok <br />
data_write_tb <= 42; // scriu data 42 la adresa 20<br />
@(posedge clock_tb);<br />
data_write_tb <= 51; // si asta; suprascriu datele anterioare la adresa 20.<br />
@(posedge clock_tb);<br />
addr_write_tb <= 21;<br />
data_write_tb <= 11; // scriere ok<br />
addr_read_tb <= 20; // citesc de la adresa 20, scrisa anteior deci voi vedea date cunoscute pe iesire.<br />
@(posedge clock_tb);<br />
addr_write_tb <= 23;<br />
data_write_tb <= 14; // scriere ok <br />
addr_read_tb <= 21; // variez adresa de citire si pot parcurge memorie locatie cu locatie<br />
@(posedge clock_tb); <br />
we_tb <= 0; // opresc scrierea<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
[[Fișier:Ram64X8_waveform.png|1000px]]<br />
<br />
In forma de unda de mai sus sunt marcate momentele de timp cand au loc scrieri in memorie (cand am write enable activ si valori cunoscute pentru data si adresa de scriere). Se salveaza valorile imediat la stanga frontului de ceas.<br />
<br />
In simularea completa, se poate observa si scrierea in registrii din memorie a datelor dorite la adresa setata.<br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/9/94/Exemplu_ram64x8.zip Exemplu_ram64x8.zip]<br />
<br />
==Exercitii==<br />
===Exercitiul 1: RAM cu registru la iesire=== <br />
<br />
Pornind de la exemplul anterior, se doreste adaugarea unui registru la citirea datelor, astfel citirea devenind sincrona cu semnalul de ceas. In cod acest lucru se poate face foarte usor, punand operatia de citire pe ceas.<br />
<br />
Se doreste simularea si apoi testarea fizica a acestei memorii. Simularea noii memorii se face prin executarea a 3 scrieri la adrese diferite si citirea apoi a datelor respective. Sinteza si testarea fizica necesita micsorarea memoriei din cauza numarului limitat de switchuri/butoane al placii. Vom lucra acum cu o memorie de 8 locatii X 4 biti. <br />
Adresa va fi comandata de switch-uri si datele de intrare din butoane. Se va folosi Button[0] pentru semnalul de "we" (write enable). Afisarea datelor se va face pe leduri.<br />
<br />
===Exercitiul 2: RAM multiport citire===<br />
<br />
Pornind de la exercitiul 1, se doreste modificarea memoriei astfel incat sa poata fi citite 2 locatii simultan si independent de adresa la care se face scrierea. Se pastreaza citirea sincrona.<br />
<br />
Se obtine astfel un circuit a carui interfata este la randul ei compusa din 3 interfete (+semnal comun "clock"): <br />
<br />
:- interfata de scriere <br />
<br />
:- interfata de citire 0<br />
<br />
:- interfata de citire 1<br />
<br />
<br />
Interfata circuitului arata ca in figura de mai jos:<br />
<br />
[[Fișier:Ram_64X8_2read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceasta memorie.<br />
<br />
'''Observatie''': Blocul de registri (register file) din interiorul procesoarelor (vedeti AMP) poate fi o astfel de memorie RAM. Are 2 adrese pentru citirea celor 2 operanzi care intra in ALU si o adresa pentru rezultatul calculului ce este salvat.<br />
<br />
===Exercitiul 3: Registrul paralel-serie===<br />
<br />
Descrieti comportamental un registru paralel-serie. Acesta are rolul de a salva datele de la intrare pe "n" biti (aici 8) si apoi de a le scoate la iesire bit cu bit.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_paralel_serie.png | 400px]]<br />
<br />
<br />
:Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
:Semnalul de write_en/start/save are rolul de a salva cei 8 biti care urmeaza a fi serializati.<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/serializari).<br />
<br />
<br />
'''Bonus''': Realizati acelasi circuit folosind o descriere structurala.<br />
<br />
===Exercitiul 4: Registrul serie-paralel===<br />
<br />
Descrieti comportamental un registru serie-paralel. Acesta are rolul de a salva date introduse bit cu bit ca apoi sa fie scoase cate "n" biti deodata.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_serie_paralel.png | 400px]]<br />
<br />
Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/paralelizari).<br />
<br />
Citirea se poate face oricand, desi doar dupa "n" pasi, va fi cu valoarea corecta.<br />
<br />
===Exercitiul 5: Registrul de intarziere pe 8b===<br />
<br />
Prin conectarea in serie a mai multor registri pe 8b (astfel incat data ce iese din unul sa fie intrare pentru urmatorul) se pot construi registri de intarziere cu "x" cicli de ceas. Cum fiecare registru salveaza datele pe ceas, se poate spune ca datele de la iesire sunt intarziate cu un ceas fata de datele de la intrare, astfel o intarziere cu "x" cicli de ceas se obtine prin punerea in serie a "x" registri. <br />
<br />
Pornind de la un registru simplu pe 8b (exemplul 1), construiti structural un registru de intarziere cu 4 cicli de ceas. <br />
<br />
Testati functionarea acestuia prin simulare. Datale introduse trebuie sa iasa defazate cu 4 cicli de ceas (adica cu 4 cicli de ceas mai tarziu).</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Exemplu_ram64x8.zip&diff=7386Fișier:Exemplu ram64x8.zip2022-04-18T11:10:34Z<p>Mihai.antonescu: Mihai.antonescu a încărcat o versiune nouă pentru Fișier:Exemplu ram64x8.zip</p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Exemplu_ram64x8.zip&diff=7385Fișier:Exemplu ram64x8.zip2022-04-18T11:10:21Z<p>Mihai.antonescu: Mihai.antonescu a încărcat o versiune nouă pentru Fișier:Exemplu ram64x8.zip</p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Exemplu_ram64x8.zip&diff=7384Fișier:Exemplu ram64x8.zip2022-04-18T11:02:36Z<p>Mihai.antonescu: Mihai.antonescu a încărcat o versiune nouă pentru Fișier:Exemplu ram64x8.zip</p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Exemplu_registru_8b.zip&diff=7383Fișier:Exemplu registru 8b.zip2022-04-18T11:01:44Z<p>Mihai.antonescu: Mihai.antonescu a încărcat o versiune nouă pentru Fișier:Exemplu registru 8b.zip</p>
<hr />
<div></div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_8_:_Registre_si_memorii_RAM&diff=7382CID aplicatii 8 : Registre si memorii RAM2022-04-18T10:54:21Z<p>Mihai.antonescu: /* Exemple */</p>
<hr />
<div><br />
<br />
==Teorie==<br />
<br />
Acest laborator are scopul de a prezenta circuitele secventiale simple: registre si memorii RAM. <br />
<br />
<br />
Pornind de la bistabilii prezentati/construiti in laboratorul anterior, apare notiunea de registru, acesta fiind o grupare de bistabili. Astfel in loc sa se memoreze un singur bit de informatie (bistabil), se pot memora acum numere pe mai multi biti (registru).<br />
<br />
Din exterior, un registru este vazut astfel: <br />
<br />
[[Fișier:Registru_exterior_view.png | 400px]]<br />
<br />
<br />
:Semnalul de "clock" controleaza sincronizarea registrilor din tot sistemul.<br />
:Semnalul de "reset" aduce registrul la o valoare initiala (uzual 0).<br />
:Semnalul de "we" (write enable) controleaza salvarea unor date noi. Cand acesta este activ, data de pe intrarea "data_in" se salveaza in registru.<br />
:Semnalul "data_in" reprezinta datele ce se doresc a fi scrise in registru.<br />
:Semnalul "data_out" reprezinta valoarea stocata in registru.<br />
<br />
<br />
Semnalele "data_in" si "data_out" pot fi pe oricat de multi biti se doreste, in mod uzual multipli de 8.<br />
<br />
<br />
<br />
Pornind de la notiunea de registru (care poate fi vazut ca o memorie cu o locatie si avand "m" biti) se doreste cresterea acestei memorii astfel incat aceasta sa curpinda mai multe locatii adresabile, unde se pot stoca date. Adaugand mai multi registri in paralel, unul langa altul, si cateva circuite de tip mux/demux se obtine o memorie de tip RAM (Random Access Memory), cu "m" locatii de "n" biti fiecare. Aceste memorii se cheama "Random Access" deoarece permit si scriere si citire.<br />
<br />
<br />
Din exterior, o memorie RAM este vazuta astfel: <br />
<br />
[[Fișier:Ram_mXn_1read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Semnalele acestui circuit se pot imparti in semnale ce tin de interfata de scriere sau interfata de citire si mai apoi in semnale de date si semnale de control. Interfata sa este: <br />
<br />
:Semnalul de "clock": controleaza sincronizarea registrilor din memorie.<br />
:Semnalul "addr_read": adresa de la care se citesc datele. <br />
:Semnalul "data_read": data ce se citeste.<br />
:Semnalul de "we": semnal de control ce controleaza activarea scrierii. Scrierea are loc doar cand acest semnal este activ.<br />
:Semnalul "addr_write": adresa la care se scriu datele.<br />
:Semnalul "data_write": data ce urmeaza a fi scrisa atunci cand semnalul "we" este activ.<br />
<br />
<br />
<br />
'''Observatie''': Orice memorie are "m" locatii de "n" biti. Semnalele de "data_read" si "data_write" au aceeasi dimensiune, "n", numarul de biti ai fiecarei locatii. Semnalele de adresa au dimensiunea log2(m). Pentru o memorie cu 16 locatii va fi nevoie de 4 biti de adresa pentru a putea selecta orice locatie, pentru 32 locatii 5b s.a.m.d. <br />
<br />
'''Observatie''': In unele situatii se mai poate pune un registru suplimentar pe iesirea datelor, cu rol in sincronizare si evitarea timpilor de propagare prea lungi intre elemente de memorare (ajuta implementarea conceptului de pipeline).<br />
<br />
'''Observatie''': Exista mai multe variante de memorii RAM (cu registru pe iesire/fara, multiport/single port citire, adrese separate sau nu pentru citire/scriere). Cea de mai sus este o memorie cu un singur port de citire, fara registru suplimentar pe iesire, ce foloseste adrese distincte pentru citire si pentru scriere (la fel de bine se poate lucra si cu o singura adresa, comuna pentru scriere si citire).<br />
<br />
'''Observatie''': Memoriile RAM pot sa nu aiba reset, utilizatorul trebuie apoi sa aiba grija sa citeasca doar din locatii scrise de el anterior.<br />
In mod uzual ele nu au reset.<br />
<br />
<br />
<br />
==Exemple==<br />
<br />
===Exemplul 1 : Registrul===<br />
<br />
In urmatorul exemplu se implementeaza registrul descris mai sus:<br />
<br />
<br />
'''Descrierea registrului (fisierul register_8b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b<br />
(<br />
input wire clock,<br />
input wire reset, // activ pe "1"<br />
input wire we,<br />
input wire [7:0] data_in, <br />
output reg [7:0] data_out // data_out are aceeasi dimensiune ca data_in<br />
);<br />
<br />
always@(posedge clock) // clock sincronizeaza actiunile circuitului<br />
begin // doar pe edge-ul pozitiv circuitul actioneaza<br />
if(reset == 1)<br />
begin<br />
data_out <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1) // comanda de scriere<br />
begin<br />
data_out <= data_in;<br />
end<br />
else // puteam sa omit acest else<br />
begin<br />
data_out <= data_out; // raman datele salvate anterior<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
Un alt mod de a scrie un registru este dat mai jos, cu observatia ca aici am pus semnalul de reset activ in logica negativa:<br />
<br />
'''Descrierea registrului (fisierul register_8b_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b_v2<br />
(<br />
input wire clock,<br />
input wire reset_n, // activ in "0"<br />
// uzual semnalel cu n in fata sau la sfarsit sunt in logica negativa: nreset, resetn<br />
input wire we,<br />
input wire [7:0] data_in,<br />
output wire [7:0] data_out<br />
);<br />
<br />
reg [7:0] memorie_efectiva; <br />
<br />
assign data_out = memorie_efectiva;<br />
<br />
always@(posedge clock)<br />
begin <br />
if( reset_n == 0) <br />
begin<br />
memorie_efectiva <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1)<br />
begin<br />
memorie_efectiva <= data_in;<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
<br />
'''Descrierea test bench-ului registrului pe 8biti: (fisierul register_8b_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module register_8b_tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg we_tb;<br />
reg [7:0] data_in_tb;<br />
wire [7:0] data_out_tb;<br />
<br />
register_8b dut // varianta cu reset activ in "1"<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.we(we_tb),<br />
.data_in(data_in_tb),<br />
.data_out(data_out_tb)<br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
reset_tb <= 0;<br />
we_tb <= 0;<br />
data_in_tb <= 0;<br />
// observatie: pana la primul reset sau prima scriere, valoarea din registru va fi necunoscuta (in simulare X)<br />
<br />
// dau reset la circuit<br />
@(posedge clock_tb); // astept sa treaca 1 clock cycle<br />
reset_tb <= 1; <br />
@(posedge clock_tb);<br />
reset_tb <= 0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb <= 1;<br />
data_in_tb <= 5;<br />
@(posedge clock_tb);<br />
we_tb <= 0;<br />
data_in_tb <= 10; // scrierea asta nu se face deoarece nu am write enable activ<br />
@(posedge clock_tb);<br />
data_in_tb <= 11; // nici asta<br />
@(posedge clock_tb);<br />
data_in_tb <= 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb = 1;<br />
data_in_tb <= 42; // asta da<br />
@(posedge clock_tb);<br />
data_in_tb <= 51; // si asta da<br />
@(posedge clock_tb); <br />
we_tb <= 0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/7/7b/Exemplu_registru_8b.zip Exemplu_registru_8b.zip]<br />
<br />
<br />
<br />
===Exemplul 2: Memorie RAM===<br />
<br />
In urmatorul exemplu se implementeaza memoria RAM descrisa mai sus, particularizata pentru 64 de locatii a cate 8b fiecare:<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v1.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v1<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read, // 64 locatii => 6 biti de adresa <br />
output wire [7:0] data_read, // fiecare locatie are 8b <br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; // memorie cu locatiile de la 0 la 63, fiecare avand 8 biti <br />
<br />
assign data_read = memorie_efectiva[addr_read]; // fara registru pe iesire => citire asincrona fata de clock <br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] <= data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Acesta va fi folosit si pe post de modul de "top". <br />
<br />
Echivalent se poate folosi si sintaxa cu always ca mai jos.<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v2 // varianta cu always combinational pe citire<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read,<br />
output reg [7:0] data_read,<br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; <br />
<br />
always@(*) // citire asincrona fata de clock <br />
begin<br />
data_read = memorie_efectiva[addr_read]; <br />
end<br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] <= data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Descrierea test bench-ului memoriei RAM: (fisierul ram64x8_v1_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module ram64x8_tb();<br />
<br />
reg clock_tb;<br />
reg [5:0] addr_read_tb;<br />
wire [7:0] data_read_tb;<br />
reg we_tb;<br />
reg [5:0] addr_write_tb;<br />
reg [7:0] data_write_tb;<br />
<br />
<br />
ram64x8_v1 dut<br />
(<br />
.clock(clock_tb),<br />
//interfata de citire<br />
.addr_read(addr_read_tb), // 64 locatii => 6 biti de adresa <br />
.data_read(data_read_tb), // fiecare locatie are 8b <br />
// interfata de scriere<br />
.we(we_tb),<br />
.addr_write(addr_write_tb),<br />
.data_write(data_write_tb)<br />
);<br />
<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
we_tb <= 0;<br />
data_write_tb <= 0;<br />
addr_read_tb <= 0; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
addr_write_tb <= 0;<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb <= 1; // scriu data 5 la adresa 10<br />
addr_write_tb <= 10;<br />
data_write_tb <= 5;<br />
addr_read_tb <= 11; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
@(posedge clock_tb);<br />
we_tb <= 0; // scrierea asta nu se face deoarece nu am write enable activ <br />
addr_write_tb <= 11;<br />
data_write_tb <= 10; <br />
@(posedge clock_tb);<br />
data_write_tb <= 11; // nici asta<br />
@(posedge clock_tb);<br />
data_write_tb <= 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb <= 1;<br />
addr_write_tb <= 20; // scriere ok <br />
data_write_tb <= 42; // scriu data 42 la adresa 20<br />
@(posedge clock_tb);<br />
data_write_tb <= 51; // si asta; suprascriu datele anterioare la adresa 20.<br />
@(posedge clock_tb);<br />
addr_write_tb <= 21;<br />
data_write_tb <= 11; // scriere ok<br />
addr_read_tb <= 20; // citesc de la adresa 20, scrisa anteior deci voi vedea date cunoscute pe iesire.<br />
@(posedge clock_tb);<br />
addr_write_tb <= 23;<br />
data_write_tb <= 14; // scriere ok <br />
addr_read_tb <= 21; // variez adresa de citire si pot parcurge memorie locatie cu locatie<br />
@(posedge clock_tb); <br />
we_tb <= 0; // opresc scrierea<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
[[Fișier:Ram64X8_waveform.png|1000px]]<br />
<br />
In forma de unda de mai sus sunt marcate momentele de timp cand au loc scrieri in memorie (cand am write enable activ si valori cunoscute pentru data si adresa de scriere). Se salveaza valorile imediat la stanga frontului de ceas.<br />
<br />
In simularea completa, se poate observa si scrierea in registrii din memorie a datelor dorite la adresa setata.<br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/9/94/Exemplu_ram64x8.zip Exemplu_ram64x8.zip]<br />
<br />
==Exercitii==<br />
===Exercitiul 1: RAM cu registru la iesire=== <br />
<br />
Pornind de la exemplul anterior, se doreste adaugarea unui registru la citirea datelor, astfel citirea devenind sincrona cu semnalul de ceas. In cod acest lucru se poate face foarte usor, punand operatia de citire pe ceas.<br />
<br />
Se doreste simularea si apoi testarea fizica a acestei memorii. Simularea noii memorii se face prin executarea a 3 scrieri la adrese diferite si citirea apoi a datelor respective. Sinteza si testarea fizica necesita micsorarea memoriei din cauza numarului limitat de switchuri/butoane al placii. Vom lucra acum cu o memorie de 8 locatii X 4 biti. <br />
Adresa va fi comandata de switch-uri si datele de intrare din butoane. Se va folosi Button[0] pentru semnalul de "we" (write enable). Afisarea datelor se va face pe leduri.<br />
<br />
===Exercitiul 2: RAM multiport citire===<br />
<br />
Pornind de la exercitiul 1, se doreste modificarea memoriei astfel incat sa poata fi citite 2 locatii simultan si independent de adresa la care se face scrierea. Se pastreaza citirea sincrona.<br />
<br />
Se obtine astfel un circuit a carui interfata este la randul ei compusa din 3 interfete (+semnal comun "clock"): <br />
<br />
:- interfata de scriere <br />
<br />
:- interfata de citire 0<br />
<br />
:- interfata de citire 1<br />
<br />
<br />
Interfata circuitului arata ca in figura de mai jos:<br />
<br />
[[Fișier:Ram_64X8_2read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceasta memorie.<br />
<br />
'''Observatie''': Blocul de registri (register file) din interiorul procesoarelor (vedeti AMP) poate fi o astfel de memorie RAM. Are 2 adrese pentru citirea celor 2 operanzi care intra in ALU si o adresa pentru rezultatul calculului ce este salvat.<br />
<br />
===Exercitiul 3: Registrul paralel-serie===<br />
<br />
Descrieti comportamental un registru paralel-serie. Acesta are rolul de a salva datele de la intrare pe "n" biti (aici 8) si apoi de a le scoate la iesire bit cu bit.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_paralel_serie.png | 400px]]<br />
<br />
<br />
:Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
:Semnalul de write_en/start/save are rolul de a salva cei 8 biti care urmeaza a fi serializati.<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/serializari).<br />
<br />
<br />
'''Bonus''': Realizati acelasi circuit folosind o descriere structurala.<br />
<br />
===Exercitiul 4: Registrul serie-paralel===<br />
<br />
Descrieti comportamental un registru serie-paralel. Acesta are rolul de a salva date introduse bit cu bit ca apoi sa fie scoase cate "n" biti deodata.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_serie_paralel.png | 400px]]<br />
<br />
Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/paralelizari).<br />
<br />
Citirea se poate face oricand, desi doar dupa "n" pasi, va fi cu valoarea corecta.<br />
<br />
===Exercitiul 5: Registrul de intarziere pe 8b===<br />
<br />
Prin conectarea in serie a mai multor registri pe 8b (astfel incat data ce iese din unul sa fie intrare pentru urmatorul) se pot construi registri de intarziere cu "x" cicli de ceas. Cum fiecare registru salveaza datele pe ceas, se poate spune ca datele de la iesire sunt intarziate cu un ceas fata de datele de la intrare, astfel o intarziere cu "x" cicli de ceas se obtine prin punerea in serie a "x" registri. <br />
<br />
Pornind de la un registru simplu pe 8b (exemplul 1), construiti structural un registru de intarziere cu 4 cicli de ceas. <br />
<br />
Testati functionarea acestuia prin simulare. Datale introduse trebuie sa iasa defazate cu 4 cicli de ceas (adica cu 4 cicli de ceas mai tarziu).</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_10_:_Aplicatii_cu_numaratoare&diff=7380CID aplicatii 10 : Aplicatii cu numaratoare2022-04-15T21:24:42Z<p>Mihai.antonescu: </p>
<hr />
<div><br />
==Teorie==<br />
<br />
Numaratoarele au extrem de multe aplicatii in lumea reala, cateva dintre acestea fiind descrise mai jos.<br />
<br />
<br />
==Exemplu: Numaratul apasarilor unui buton==<br />
<br />
Un prim sistem simplu cu numarator poate fi cel care contorizeaza de cate ori a fost apasat un buton sau, echivalent, cate persoane/masini au trecut printr-un senzor, de cate ori a fost deschisa o usa etc. Acest sistem se poate apoi dezvolta cu usurinta pentru a numara si oamenii care trec invers prin respectivul set de senzori prin adaugarea functionalitatii de up/down la numarator.<br />
<br />
Un astfel de sistem simplu (varianta doar cu numarat intrari) ar arata in felul urmator: <br />
<br />
[[Fișier:Aplicatii_numarator_exemplu_contor_senzor.png | 800px]]<br />
<br />
Circuitul de debounce are rolul de a "curata" semnalul de intrare, astfel incat chiar si in prezenta zgomotului sau a unei activari indelungate a senzorului/butonului, sa se contorizeze un singur eveniment. O explicatie mai detaliata puteti gasi [[Circuitul_de_debounce|aici]].<br />
<br />
Numaratorul numara evenimentele.<br />
<br />
Transcodorul pentru display cu 7 segmente este apoi folosit pentru o mai usoara vizualizare a numarului curent. <br />
<br />
'''Observatie:''' transcodorul pentru 7seg din exemplul de mai jos este in logica negativa. Daca circuitul fizic este in logica pozitiva trebuie adaugat un inversor la iesirea din acesta. De asemnea consider segmentul "a" pe bitul "0" si segmentul "h" pe bitul "6".<br />
<br />
<br />
'''Descrierea debouncer-ului (fisierul debounce.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module debounce<br />
#(<br />
parameter limit = 20'd650000<br />
) (<br />
input wire clock,<br />
input wire in,<br />
output wire out<br />
);<br />
<br />
reg [19:0] counter; // observatie: si circuitul de debounce foloseste un numarator<br />
reg hit;<br />
<br />
assign out = (counter == limit);<br />
<br />
always@(posedge clock) <br />
begin<br />
if(in == 0) <br />
begin<br />
counter <= 0;<br />
hit <= 0; <br />
end <br />
else <br />
begin<br />
if(counter == limit) <br />
begin<br />
hit <= 1;<br />
counter <= counter + 1;<br />
end <br />
else <br />
begin<br />
if(in == 1 & hit == 0) <br />
begin<br />
counter <= counter + 1;<br />
end<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea numaratorului pe 4b(fisierul counter_4b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module counter_4b<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire en,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(posedge clock)<br />
begin<br />
if(reset == 1)<br />
begin<br />
out <=0;<br />
end<br />
else<br />
begin<br />
if(en == 1)<br />
begin<br />
out <= out + 1;<br />
end<br />
else<br />
begin<br />
out <= out;<br />
end<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea transcodorului pentru afisaj cu 7seg(fisierul transcodor_7seg.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module transcodor_7seg // logica negativa <br />
(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(*)<br />
begin<br />
case(in)<br />
4'd0: out = 7'b1000000;// h....a<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
4'd8: out = 7'b0000000;<br />
4'd9: out = 7'b0010000;<br />
4'd10: out = 7'b0001000;<br />
4'd11: out = 7'b0000011;<br />
4'd12: out = 7'b1000110;<br />
4'd13: out = 7'b0100001;<br />
4'd14: out = 7'b0000110;<br />
4'd15: out = 7'b0001110;<br />
default: out = 7'b1111111;//tot stins <br />
endcase <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea sistemului, modulul de top (fisierul top.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
<br />
module top<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire button,<br />
output wire [6:0] out // logica negativa <br />
);<br />
<br />
wire debounce_0_X_out;<br />
wire [3:0] counter_4b_0_X_out; <br />
<br />
debounce<br />
#(<br />
.limit(20'd50_000)<br />
) debounce_0 (<br />
.clock(clock),<br />
.in(button),<br />
.out(debounce_0_X_out)<br />
);<br />
<br />
counter_4b counter_4b_0<br />
(<br />
.clock(clock),<br />
.reset(reset),<br />
.en(debounce_0_X_out),<br />
.out(counter_4b_0_X_out)<br />
); <br />
<br />
transcodor_7seg transcodor_7seg_0 // logica negativa <br />
(<br />
.in(counter_4b_0_X_out),<br />
.out(out)<br />
); <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea test bench-ului(fisierul tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg button_tb;<br />
wire [6:0] out_tb;<br />
<br />
top dut<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.button(button_tb),<br />
.out(out_tb) // logica negativa <br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever<br />
begin<br />
#5 clock_tb = ~clock_tb;<br />
end<br />
end<br />
<br />
initial<br />
begin<br />
reset_tb = 0;<br />
button_tb = 0;<br />
<br />
#50;<br />
reset_tb = 1;<br />
#50;<br />
reset_tb = 0;<br />
#100;<br />
<br />
repeat(5) // vreau 5 apasari de buton<br />
begin<br />
button_tb = 1;<br />
repeat(70_000) <br />
begin<br />
@(posedge clock_tb);<br />
end<br />
button_tb = 0;<br />
repeat(70_000)<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
end<br />
<br />
#1000 $stop();<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/5/53/Aplicatii_numarator_exemplu_contor.zip Exemplu_aplicatii_numarator.zip]<br />
<br />
==Exercitii==<br />
<br />
=== Exercitiul 1: Divizor de frecventa cu puteri ale lui 2=== <br />
<br />
Divizorul de frecventa are rolul de a genera un semnal de ceas mai lent din semnalul de ceas principal. <br />
El este alcatuit doar dintr-un numarator, iar fiecare bit al acestuia va fi practic un semnal de ceas cu frecventa din ce in ce mai mica astfel (la jumate): <br />
<br />
bit[0] - frecventa de 2 ori mai mica decat semnalul de ceas<br />
<br />
bit[1] - frecventa de 2 ori mai mica decat bit[0], deci de 4 ori mai mica decat semnalul de ceas <br />
<br />
bit[2] - frecventa de 2 ori mai mica decat bit[1], deci de 8 ori mai mica decat semnalul de ceas <br />
<br />
<br />
Acest lucru se poate observa si in poza ce urmeaza: <br />
<br />
[[Fișier:Freq_divider_output.png| 800px]] <br />
<br />
Implementati acest circuit, si folosind ledurile prezente pe placa, incercati sa conectati bitul corespunzator la leduri astfel incat cel mai din dreapta led sa clipeasca cu o frecventa cat mai apropiata de 1s. <br />
<br />
'''Observatie:''' Printr-un astfel de divizor de frecventa minimal, un numar foarte mic de frecvente poate fi generat. Daca se doreste generarea unui semnal periodic cu o perioada exacta, diferita de cele posibile prin acest mecanism, se poate construi urmatorul circuit:<br />
<br />
<br />
===Exercitiul 2: Divizor de frecventa ce poate genera orice perioada=== <br />
<br />
Un astfel de divizor de frecventa este construit ca in figura de mai jos:<br />
<br />
[[Fișier:Freq_div_orice_freq.png | 800px]]<br />
<br />
Prin adaugarea constantei "limit", perioada semnalului de iesire poate sa fie orice multiplu a perioadei semnalului de ceas. <br />
<br />
Bistabilul de tip t, se modifica atunci cand limita este atinsa si practic iesirea lui este semnalul periodic dorit (cu observatia ca o perioada a semnalului de iesire necesita 2 numarari pana la limita).<br />
<br />
Sa se calculeze valoarea necesara pentru "limit" astfel incat pe leduri sa se genereze semnal cu perioada exact 1s.<br />
<br />
<br />
'''Observatie:''' In FPGA exista fizic trasee speciale pentru reset si ca sinteza sa le foloseasca pe acestea in mod optim nu este recomandat sa se puna logica (cum e aici poarta sau) pe acestea. Modificati schema de mai sus astfel incat sa eliminati respectiva poarta de pe reset. (Sfat: numaratorul va avea acum si intrari de load: semnalul de comanda si data propriu zisa)<br />
<br />
===Exercitiul 3: PWM (Pulse Width Modulation)===<br />
<br />
PWM este un concept folosit foarte des in multiple ramuri ale ingineriei, de la transmisiunea informatiei pana la controlul motoarelor sau al intensitatii ledurilor din instalatii de Craciun. El se refera la a avea un semnal periodic cu factor de umplere variabil, asa cum este aratat in poza de mai jos :<br />
<br />
[[Fișier:Pwm.png| 800px]]<br />
<br />
<br />
Generarea unui astfel de semnal periodic se face printr-un numarator si un comparator, ca in figura de mai jos:<br />
<br />
[[Fișier:Pwm_schematic.png | 800px]]<br />
<br />
<br />
Raportul dintre limita pusa si valoarea la care numaratorul se reseteaza este practic factorul de umplere selectat.<br />
<br />
Pentru testare pe placa, cei 6b ai limitei provin de la switch-uri si butoane. Afisarea se face pe led[0].<br />
<br />
Testati acest circuit in simulare si apoi implementati pe placa pentru o valoare fixa a limitei. Implementati in paralel mai multe generatoare de semnal PWM cu limite diferite, astfel incat sa observati diferentele de intentistate dintre ledurile comandate de acestea.<br />
<br />
<br />
===Exercitiul 4: PWM cu limita variabila===<br />
<br />
Un exemplu foarte uzual de folosire a PWM este aprinderea ledurilor cu diverse pattern-uri. Factorul de umplere determina intensitatea cu care ledul este aprins. Un PWM cu o limita variabila automata va face ledul sa para din ce in ce mai stins sau din ce in ce mai luminos (exemplu: instalatii de Craciun). Combinand asta cu leduri RGB se obtin efecte de schimbare a culorii ledului aparent la intamplare. <br />
<br />
Un astfel de circuit se realizeaza punand inca un numarator in locul limitei, ca in poza de mai jos:<br />
<br />
[[Fișier:Pwm_duty_cycle_up_schematic.png | 800px]]<br />
<br />
<br />
Pentru o functionalitate suplimentara, anume a pastra un anumit factor de umplere mai multe perioade, am adaugat inca un numarator.<br />
<br />
Pentru claritatea desenului, nu am mai tras efectiv firul de ceas catre intrarile unde acesta se duce.<br />
<br />
<br />
Rezultatul este: <br />
<br />
[[Fișier:Pwm_duty_cycle_up_output.png | 800px]]<br />
<br />
<br />
Testati circuitul propus prin simulare si apoi vizualizati implementarea sa pe placa. Alegeti limite potrivite astfel incat sa puteti observa usor rezultatul.<br />
<br />
<br />
'''Bonus:''' Incercati sa adaugati si parametri pentru LIMIT_DUTY_CYCLE_LOW si LIMIT_DUTY_CYCLE_HIGH, care sa permita factorului de umplere sa varieze doar intre ele (numaratorul va avea nevoie de intrari pentru comanda de load si data load, ca sa poata incepe de la orice valoare).<br />
<br />
<br />
===Exercitiul 5: Ceas===<br />
<br />
Un ceas poate fi construit cifra cu cifra, folosind numaratoare si comparatoare (si transcodoare pentru display cu 7seg ca sa se vada totul mai bine pe placa).<br />
<br />
Incercati sa implementati un ceas cu milisecunde, secunde si minute in varianta comportamentala. <br />
<br />
'''Observatie:''' Va fi nevoie fie separat, fie in modul de un numarator cu frecventa rezolutiei dorite (aici 1 ms).<br />
<br />
'''Observatie:''' Codul va contine multe "if" de tipul "daca cifra unitatilor secundelor a ajuns la 10, se face 0 si cresc cifra zecilor", repetate pentru fiecare cifra.<br />
<br />
<br />
Incercati si o implementare structurala a ceasului, mergand pe aceeasi idee. Fiecare cifra va contine numaratorul ei. Cand numaratorul unei cifre ajunge ajunge la limita sa, va da enable pentru numaratorul cifrei urmatoare. Daca este nevoie se pot folosi si porti aditionale (ex: ca sa creasca minutul cifra zecilor secundelor trebuie sa fie 5 si cifra unitatilor secundelor trebuie sa fie 9). <br />
<br />
'''Observatie:''' Va fi nevoie de un numarator cu frecventa rezolutiei dorite (aici 1 ms sau 1s).<br />
<br />
'''Observatie:''' Pentru a scrie mai putin puteti face un numarator doar cu secunde si minute. Principiul de baza este acelasi si daca aveati milisecunde.<br />
<br />
'''Observatie:''' Simularea a cateva milisecunde sau chiar secunde este un proces indelungat (dureaza minute-ore fizice), astfel strict pentru simulari se recomanda ceasul sa functioneze cu microsecunde sau milisecunde maxim.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_12_:_Exercitii_cu_circuite_secventiale&diff=7379CID aplicatii 12 : Exercitii cu circuite secventiale2022-04-15T19:14:36Z<p>Mihai.antonescu: </p>
<hr />
<div>==Teorie==<br />
Acest laborator are rolul de a sedimenta cunostintele dobandite anterior. <br />
<br />
El consta in exercitii separate, unele date ca subiect la lucrarea 2 in anii anteriori.<br />
<br />
Unele exercitii contin si automate. Daca acestea nu au fost inca predate, puteti ignora partea aceea de circuit.<br />
<br />
Dupa cum se poate observa, exemplele sunt diverse ca domeniu de aplicabilitate. Prin asta se doreste a se arata importanta si diversitatea circuitelor digitale in societatea moderna.<br />
<br />
<br />
<br />
==Exercitii==<br />
===Exercitiul 1: Miniprocesor===<br />
<br />
Un exemplu de procesor Harvard minimal, avand memorii de data si instructiuni, un program counter si un ALU. <br />
<br />
Dimensiunile nu sunt realiste, acest lucru fiind asa pentru a putea fi sintetizat si testat pe placa.<br />
<br />
[https://wiki.dcae.pub.ro/images/7/76/Subiect_l2_miniprocesor.pdf miniprocesor.pdf]<br />
<br />
<br />
<br />
===Exercitiul 2: password checker===<br />
<br />
Verilog suporta codul ASCII deci puteti introduce si litere/string-uri care vor fi tratate prin reprezentarea lor binara.<br />
<br />
[https://wiki.dcae.pub.ro/images/5/5c/Subiect_l2_password_checker.pdf password_checker.pdf]<br />
<br />
<br />
<br />
===Exercitiul 3: pseudorandom number generator===<br />
<br />
[https://wiki.dcae.pub.ro/images/0/05/Subiect_l2_pseudorandom_nr_generator.pdf pseudorandom_nr_generator.pdf]<br />
<br />
<br />
<br />
===Exercitiul 4: calculator de siruri dupa formula data===<br />
<br />
[https://wiki.dcae.pub.ro/images/a/a2/Subiect_l2_sir_calculator.pdf calculator_siruri.pdf]<br />
<br />
<br />
<br />
===Exercitiul 5: UART tx - structural===<br />
<br />
UART este un standard pentru transmisiune de date seriale. Pentru o descriere a protocolului puteti citi: [https://www.circuitbasics.com/basics-uart-communication/ functionare_uart]<br />
<br />
[https://wiki.dcae.pub.ro/images/d/d0/Subiect_l2_uart_tx.pdf uart_tx.pdf]<br />
<br />
<br />
<br />
===Exercitiul 6: UART rx - behavioural=== <br />
<br />
Daca ati inteles cum functioneaza mecanismul de UART (si pentru a testa functionarea TX de la exercitiul anterior) puteti scrie modulul de RX (receiver) al protocolului, astfel incat cele 2 sa comunice intre ele.<br />
<br />
La nivel de TB ar fi amandoua instantiate si conectate intre ele. <br />
<br />
UART RX ar trebui sa scoata un puls al semnalului "valid" impreuna cu 8 biti de date daca transmisiunea a fost efectuata cu succes, datele care ies din el fiind cele care au intrat in modulul de tx. <br />
<br />
Pentru a compara abordarea structurala cu cea comportamentala, acest modul se doreste a fi facut comportamental.<br />
In always-urile acestuia vor aparea mecanisme de numarare care practic alcatuiesc un mic FSM de control. <br />
<br />
<br />
<br />
===Exercitiul 7: microcontrollere - oscillator control register=== <br />
<br />
Acest subiect prezinta un modul simplificat al mecanismului de control al oscilatorului de pe un microcontroller. Acest modul controleaza generarea smenalului de ceas ce se propaga catre microcontroller si deci viteza la care sistemul va functiona.<br />
<br />
[https://wiki.dcae.pub.ro/images/0/09/Subiect_l2_uc_osccon.pdf uc_osccon.pdf]</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_8_:_Registre_si_memorii_RAM&diff=7378CID aplicatii 8 : Registre si memorii RAM2022-04-15T06:18:38Z<p>Mihai.antonescu: </p>
<hr />
<div><br />
<br />
==Teorie==<br />
<br />
Acest laborator are scopul de a prezenta circuitele secventiale simple: registre si memorii RAM. <br />
<br />
<br />
Pornind de la bistabilii prezentati/construiti in laboratorul anterior, apare notiunea de registru, acesta fiind o grupare de bistabili. Astfel in loc sa se memoreze un singur bit de informatie (bistabil), se pot memora acum numere pe mai multi biti (registru).<br />
<br />
Din exterior, un registru este vazut astfel: <br />
<br />
[[Fișier:Registru_exterior_view.png | 400px]]<br />
<br />
<br />
:Semnalul de "clock" controleaza sincronizarea registrilor din tot sistemul.<br />
:Semnalul de "reset" aduce registrul la o valoare initiala (uzual 0).<br />
:Semnalul de "we" (write enable) controleaza salvarea unor date noi. Cand acesta este activ, data de pe intrarea "data_in" se salveaza in registru.<br />
:Semnalul "data_in" reprezinta datele ce se doresc a fi scrise in registru.<br />
:Semnalul "data_out" reprezinta valoarea stocata in registru.<br />
<br />
<br />
Semnalele "data_in" si "data_out" pot fi pe oricat de multi biti se doreste, in mod uzual multipli de 8.<br />
<br />
<br />
<br />
Pornind de la notiunea de registru (care poate fi vazut ca o memorie cu o locatie si avand "m" biti) se doreste cresterea acestei memorii astfel incat aceasta sa curpinda mai multe locatii adresabile, unde se pot stoca date. Adaugand mai multi registri in paralel, unul langa altul, si cateva circuite de tip mux/demux se obtine o memorie de tip RAM (Random Access Memory), cu "m" locatii de "n" biti fiecare. Aceste memorii se cheama "Random Access" deoarece permit si scriere si citire.<br />
<br />
<br />
Din exterior, o memorie RAM este vazuta astfel: <br />
<br />
[[Fișier:Ram_mXn_1read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Semnalele acestui circuit se pot imparti in semnale ce tin de interfata de scriere sau interfata de citire si mai apoi in semnale de date si semnale de control. Interfata sa este: <br />
<br />
:Semnalul de "clock": controleaza sincronizarea registrilor din memorie.<br />
:Semnalul "addr_read": adresa de la care se citesc datele. <br />
:Semnalul "data_read": data ce se citeste.<br />
:Semnalul de "we": semnal de control ce controleaza activarea scrierii. Scrierea are loc doar cand acest semnal este activ.<br />
:Semnalul "addr_write": adresa la care se scriu datele.<br />
:Semnalul "data_write": data ce urmeaza a fi scrisa atunci cand semnalul "we" este activ.<br />
<br />
<br />
<br />
'''Observatie''': Orice memorie are "m" locatii de "n" biti. Semnalele de "data_read" si "data_write" au aceeasi dimensiune, "n", numarul de biti ai fiecarei locatii. Semnalele de adresa au dimensiunea log2(m). Pentru o memorie cu 16 locatii va fi nevoie de 4 biti de adresa pentru a putea selecta orice locatie, pentru 32 locatii 5b s.a.m.d. <br />
<br />
'''Observatie''': In unele situatii se mai poate pune un registru suplimentar pe iesirea datelor, cu rol in sincronizare si evitarea timpilor de propagare prea lungi intre elemente de memorare (ajuta implementarea conceptului de pipeline).<br />
<br />
'''Observatie''': Exista mai multe variante de memorii RAM (cu registru pe iesire/fara, multiport/single port citire, adrese separate sau nu pentru citire/scriere). Cea de mai sus este o memorie cu un singur port de citire, fara registru suplimentar pe iesire, ce foloseste adrese distincte pentru citire si pentru scriere (la fel de bine se poate lucra si cu o singura adresa, comuna pentru scriere si citire).<br />
<br />
'''Observatie''': Memoriile RAM pot sa nu aiba reset, utilizatorul trebuie apoi sa aiba grija sa citeasca doar din locatii scrise de el anterior.<br />
In mod uzual ele nu au reset.<br />
<br />
<br />
<br />
==Exemple==<br />
<br />
===Exemplul 1 : Registrul===<br />
<br />
In urmatorul exemplu se implementeaza registrul descris mai sus:<br />
<br />
<br />
'''Descrierea registrului (fisierul register_8b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b<br />
(<br />
input wire clock,<br />
input wire reset, // activ pe "1"<br />
input wire we,<br />
input wire [7:0] data_in, <br />
output reg [7:0] data_out // data_out are aceeasi dimensiune ca data_in<br />
);<br />
<br />
always@(posedge clock) // clock sincronizeaza actiunile circuitului<br />
begin // doar pe edge-ul pozitiv circuitul actioneaza<br />
if(reset == 1)<br />
begin<br />
data_out <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1) // comanda de scriere<br />
begin<br />
data_out <= data_in;<br />
end<br />
else // puteam sa omit acest else<br />
begin<br />
data_out <= data_out; // raman datele salvate anterior<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
Un alt mod de a scrie un registru este dat mai jos, cu observatia ca aici am pus semnalul de reset activ in logica negativa:<br />
<br />
'''Descrierea registrului (fisierul register_8b_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module register_8b_v2<br />
(<br />
input wire clock,<br />
input wire reset_n, // activ in "0"<br />
// uzual semnalel cu n in fata sau la sfarsit sunt in logica negativa: nreset, resetn<br />
input wire we,<br />
input wire [7:0] data_in,<br />
output wire [7:0] data_out<br />
);<br />
<br />
reg [7:0] memorie_efectiva; <br />
<br />
assign data_out = memorie_efectiva;<br />
<br />
always@(posedge clock)<br />
begin <br />
if( reset_n == 0) <br />
begin<br />
memorie_efectiva <= 0;<br />
end<br />
else<br />
begin<br />
if(we == 1)<br />
begin<br />
memorie_efectiva <= data_in;<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
<br />
'''Descrierea test bench-ului registrului pe 8biti: (fisierul register_8b_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module register_8b_tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg we_tb;<br />
reg [7:0] data_in_tb;<br />
wire [7:0] data_out_tb;<br />
<br />
register_8b dut // varianta cu reset activ in "1"<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.we(we_tb),<br />
.data_in(data_in_tb),<br />
.data_out(data_out_tb)<br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
reset_tb = 0;<br />
we_tb =0;<br />
data_in_tb = 0;<br />
// observatie: pana la primul reset sau prima scriere, valoarea din registru va fi necunoscuta (in simulare X)<br />
<br />
// dau reset la circuit<br />
@(posedge clock_tb); // astept sa treaca 1 clock cycle<br />
reset_tb = 1; <br />
@(posedge clock_tb);<br />
reset_tb = 0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb =1;<br />
data_in_tb = 5;<br />
@(posedge clock_tb);<br />
we_tb =0;<br />
data_in_tb = 10; // scrierea asta nu se face deoarece nu am write enable activ<br />
@(posedge clock_tb);<br />
data_in_tb = 11; // nici asta<br />
@(posedge clock_tb);<br />
data_in_tb = 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb =1;<br />
data_in_tb = 42; // asta da<br />
@(posedge clock_tb);<br />
data_in_tb = 51; // si asta da<br />
@(posedge clock_tb); <br />
we_tb =0;<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/7/7b/Exemplu_registru_8b.zip Exemplu_registru_8b.zip]<br />
<br />
<br />
<br />
===Exemplul 2: Memorie RAM===<br />
<br />
In urmatorul exemplu se implementeaza memoria RAM descrisa mai sus, particularizata pentru 64 de locatii a cate 8b fiecare:<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v1.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v1<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read, // 64 locatii => 6 biti de adresa <br />
output wire [7:0] data_read, // fiecare locatie are 8b <br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; // memorie cu locatiile de la 0 la 63, fiecare avand 8 biti <br />
<br />
assign data_read = memorie_efectiva[addr_read]; // fara registru pe iesire => citire asincrona fata de clock <br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] = data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Acesta va fi folosit si pe post de modul de "top". <br />
<br />
Echivalent se poate folosi si sintaxa cu always ca mai jos.<br />
<br />
'''Descrierea memoriei RAM: (fisierul ram64x8_v2.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module ram64x8_v2 // varianta cu always combinational pe citire<br />
(<br />
input wire clock,<br />
//interfata de citire<br />
input wire [5:0] addr_read,<br />
output reg [7:0] data_read,<br />
// interfata de scriere<br />
input wire we,<br />
input wire [5:0] addr_write,<br />
input wire [7:0] data_write<br />
);<br />
<br />
reg [7:0] memorie_efectiva [0:63]; <br />
<br />
always@(*) // citire asincrona fata de clock <br />
begin<br />
data_read = memorie_efectiva[addr_read]; <br />
end<br />
<br />
always@(posedge clock)<br />
begin <br />
if(we == 1)<br />
begin<br />
memorie_efectiva[addr_write] = data_write; // scriu la locatia data de "addr_write" din memoria efectiva datele "data_write"<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
'''Descrierea test bench-ului memoriei RAM: (fisierul ram64x8_v1_tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module ram64x8_tb();<br />
<br />
reg clock_tb;<br />
reg [5:0] addr_read_tb;<br />
wire [7:0] data_read_tb;<br />
reg we_tb;<br />
reg [5:0] addr_write_tb;<br />
reg [7:0] data_write_tb;<br />
<br />
<br />
ram64x8_v1 dut<br />
(<br />
.clock(clock_tb),<br />
//interfata de citire<br />
.addr_read(addr_read_tb), // 64 locatii => 6 biti de adresa <br />
.data_read(data_read_tb), // fiecare locatie are 8b <br />
// interfata de scriere<br />
.we(we_tb),<br />
.addr_write(addr_write_tb),<br />
.data_write(data_write_tb)<br />
);<br />
<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever <br />
begin<br />
#5 clock_tb = ~clock_tb; // perioada totala 10 !!!<br />
end<br />
end <br />
<br />
initial<br />
begin<br />
we_tb =0;<br />
data_write_tb = 0;<br />
addr_read_tb = 0; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
addr_write_tb = 0;<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
<br />
// incep sa fac scrieri<br />
we_tb =1; // scriu data 5 la adresa 10<br />
addr_write_tb = 10;<br />
data_write_tb = 5;<br />
addr_read_tb = 11; // citind de la o adresa nescrisa inca, iesirea e necunoscuta<br />
@(posedge clock_tb);<br />
we_tb =0; // scrierea asta nu se face deoarece nu am write enable activ <br />
addr_write_tb = 11;<br />
data_write_tb = 10; <br />
@(posedge clock_tb);<br />
data_write_tb = 11; // nici asta<br />
@(posedge clock_tb);<br />
data_write_tb = 12; // nici asta<br />
@(posedge clock_tb);<br />
we_tb =1;<br />
addr_write_tb = 20; // scriere ok <br />
data_write_tb = 42; // scriu data 42 la adresa 20<br />
@(posedge clock_tb);<br />
data_write_tb = 51; // si asta; suprascriu datele anterioare la adresa 20.<br />
@(posedge clock_tb);<br />
addr_write_tb = 21;<br />
data_write_tb = 11; // scriere ok<br />
addr_read_tb = 20; // citesc de la adresa 20, scrisa anteior deci voi vedea date cunoscute pe iesire.<br />
@(posedge clock_tb);<br />
addr_write_tb = 23;<br />
data_write_tb = 14; // scriere ok <br />
addr_read_tb = 21; // variez adresa de citire si pot parcurge memorie locatie cu locatie<br />
@(posedge clock_tb); <br />
we_tb =0; // opresc scrierea<br />
<br />
repeat(5) // dupa 5 cicli de ceas<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
$stop();<br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
[[Fișier:Ram64X8_waveform.png|1000px]]<br />
<br />
In forma de unda de mai sus sunt marcate momentele de timp cand au loc scrieri in memorie (cand am write enable activ si valori cunoscute pentru data si adresa de scriere). Se salveaza valorile imediat la stanga frontului de ceas.<br />
<br />
In simularea completa, se poate observa si scrierea in registrii din memorie a datelor dorite la adresa setata.<br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/9/94/Exemplu_ram64x8.zip Exemplu_ram64x8.zip]<br />
<br />
==Exercitii==<br />
===Exercitiul 1: RAM cu registru la iesire=== <br />
<br />
Pornind de la exemplul anterior, se doreste adaugarea unui registru la citirea datelor, astfel citirea devenind sincrona cu semnalul de ceas. In cod acest lucru se poate face foarte usor, punand operatia de citire pe ceas.<br />
<br />
Se doreste simularea si apoi testarea fizica a acestei memorii. Simularea noii memorii se face prin executarea a 3 scrieri la adrese diferite si citirea apoi a datelor respective. Sinteza si testarea fizica necesita micsorarea memoriei din cauza numarului limitat de switchuri/butoane al placii. Vom lucra acum cu o memorie de 8 locatii X 4 biti. <br />
Adresa va fi comandata de switch-uri si datele de intrare din butoane. Se va folosi Button[0] pentru semnalul de "we" (write enable). Afisarea datelor se va face pe leduri.<br />
<br />
===Exercitiul 2: RAM multiport citire===<br />
<br />
Pornind de la exercitiul 1, se doreste modificarea memoriei astfel incat sa poata fi citite 2 locatii simultan si independent de adresa la care se face scrierea. Se pastreaza citirea sincrona.<br />
<br />
Se obtine astfel un circuit a carui interfata este la randul ei compusa din 3 interfete (+semnal comun "clock"): <br />
<br />
:- interfata de scriere <br />
<br />
:- interfata de citire 0<br />
<br />
:- interfata de citire 1<br />
<br />
<br />
Interfata circuitului arata ca in figura de mai jos:<br />
<br />
[[Fișier:Ram_64X8_2read_1write_exterior_view.png | 400px]]<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceasta memorie.<br />
<br />
'''Observatie''': Blocul de registri (register file) din interiorul procesoarelor (vedeti AMP) poate fi o astfel de memorie RAM. Are 2 adrese pentru citirea celor 2 operanzi care intra in ALU si o adresa pentru rezultatul calculului ce este salvat.<br />
<br />
===Exercitiul 3: Registrul paralel-serie===<br />
<br />
Descrieti comportamental un registru paralel-serie. Acesta are rolul de a salva datele de la intrare pe "n" biti (aici 8) si apoi de a le scoate la iesire bit cu bit.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_paralel_serie.png | 400px]]<br />
<br />
<br />
:Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
:Semnalul de write_en/start/save are rolul de a salva cei 8 biti care urmeaza a fi serializati.<br />
<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/serializari).<br />
<br />
<br />
'''Bonus''': Realizati acelasi circuit folosind o descriere structurala.<br />
<br />
===Exercitiul 4: Registrul serie-paralel===<br />
<br />
Descrieti comportamental un registru serie-paralel. Acesta are rolul de a salva date introduse bit cu bit ca apoi sa fie scoase cate "n" biti deodata.<br />
<br />
Interfata acestuia este data in desenul de mai jos. <br />
<br />
[[Fișier:Registru_serie_paralel.png | 400px]]<br />
<br />
Semnalul de "en" (enable) are rolul de a controla deplasarea, care se executa prin operatia de shiftare la dreapta ">>". Daca acesta are valoarea "1", datele salvate se muta la dreapta cu o pozitie.<br />
<br />
Generati forme de unda corespunzatoare pentru a testa aceast circuit (minim 3 scrieri/paralelizari).<br />
<br />
Citirea se poate face oricand, desi doar dupa "n" pasi, va fi cu valoarea corecta.<br />
<br />
===Exercitiul 5: Registrul de intarziere pe 8b===<br />
<br />
Prin conectarea in serie a mai multor registri pe 8b (astfel incat data ce iese din unul sa fie intrare pentru urmatorul) se pot construi registri de intarziere cu "x" cicli de ceas. Cum fiecare registru salveaza datele pe ceas, se poate spune ca datele de la iesire sunt intarziate cu un ceas fata de datele de la intrare, astfel o intarziere cu "x" cicli de ceas se obtine prin punerea in serie a "x" registri. <br />
<br />
Pornind de la un registru simplu pe 8b (exemplul 1), construiti structural un registru de intarziere cu 4 cicli de ceas. <br />
<br />
Testati functionarea acestuia prin simulare. Datale introduse trebuie sa iasa defazate cu 4 cicli de ceas (adica cu 4 cicli de ceas mai tarziu).</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=CID_aplicatii_10_:_Aplicatii_cu_numaratoare&diff=7377CID aplicatii 10 : Aplicatii cu numaratoare2022-04-14T15:28:46Z<p>Mihai.antonescu: /* Exercitiul 2: Divizor de frecventa ce poate genera orice perioada */</p>
<hr />
<div><br />
==Teorie==<br />
<br />
Numaratoarele au extrem de multe aplicatii in lumea reala, cateva dintre acestea fiind descrise mai jos.<br />
<br />
<br />
==Exemplu: Numaratul apasarilor unui buton==<br />
<br />
Un prim sistem simplu cu numarator poate fi cel care contorizeaza de cate ori a fost apasat un buton, sau echivalent, cate persoane/masini au trecut printr-un senzor, de cate ori a fost deschisa o usa etc. Acest sistem se poate apoi dezvolta cu usurinta pentru a numara si oamenii care trec invers prin respectivul set de senzori prin adaugarea functionalitatii de up/down la numarator.<br />
<br />
Un astfel de sistem simplu (varianta doar cu numarat intrari) ar arata in felul urmator: <br />
<br />
[[Fișier:Aplicatii_numarator_exemplu_contor_senzor.png | 800px]]<br />
<br />
Circuitul de debounce are rolul de a "curata" semnalul de intrare, astfel incat chiar si in prezenta zgomotului sau a unei activari indelungate a senzorului/butonului, sa se contorizeze un singur eveniment. O explicatie mai detaliata puteti gasi [[Circuitul_de_debounce|aici]].<br />
<br />
Numaratorul numara evenimentele.<br />
<br />
Transcodorul pentru display cu 7 segmente este apoi folosit pentru o mai usoara vizualizare a numarului curent. <br />
<br />
'''Observatie:''' transcodorul pt 7seg din exemplul de mai jos este in logica negativa. Daca circuitul fizic este in logica pozitiva trebuie adaugat un inversor la iesirea din acesta. De asemnea consider segmentul "a" pe bitul "0" si segmentul "h" pe bitul "6".<br />
<br />
<br />
'''Descrierea debouncer-ului (fisierul debounce.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module debounce<br />
#(<br />
parameter limit = 20'd650000<br />
) (<br />
input wire clock,<br />
input wire in,<br />
output wire out<br />
);<br />
<br />
reg [19:0] counter; // observatie: si circuitul de debounce foloseste un numarator<br />
reg hit;<br />
<br />
assign out = (counter == limit);<br />
<br />
always@(posedge clock) <br />
begin<br />
if(in == 0) <br />
begin<br />
counter <= 0;<br />
hit <= 0; <br />
end <br />
else <br />
begin<br />
if(counter == limit) <br />
begin<br />
hit <= 1;<br />
counter <= counter + 1;<br />
end <br />
else <br />
begin<br />
if(in == 1 & hit == 0) <br />
begin<br />
counter <= counter + 1;<br />
end<br />
end<br />
end<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea numaratorului pe 4b(fisierul counter_4b.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module counter_4b<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire en,<br />
output reg [3:0] out<br />
);<br />
<br />
always@(posedge clock)<br />
begin<br />
if(reset == 1)<br />
begin<br />
out <=0;<br />
end<br />
else<br />
begin<br />
if(en == 1)<br />
begin<br />
out <= out + 1;<br />
end<br />
else<br />
begin<br />
out <= out;<br />
end<br />
end <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea transcodorului pentru afisaj cu 7seg(fisierul transcodor_7seg.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
module transcodor_7seg // logica negativa <br />
(<br />
input [3:0] in,<br />
output reg [6:0] out<br />
);<br />
<br />
always@(*)<br />
begin<br />
case(in)<br />
4'd0: out = 7'b1000000;// h....a<br />
4'd1: out = 7'b1111001;<br />
4'd2: out = 7'b0100100;<br />
4'd3: out = 7'b0110000;<br />
4'd4: out = 7'b0011001;<br />
4'd5: out = 7'b0010010;<br />
4'd6: out = 7'b0000010;<br />
4'd7: out = 7'b1111000;<br />
4'd8: out = 7'b0000000;<br />
4'd9: out = 7'b0010000;<br />
4'd10: out = 7'b0001000;<br />
4'd11: out = 7'b0000011;<br />
4'd12: out = 7'b1000110;<br />
4'd13: out = 7'b0100001;<br />
4'd14: out = 7'b0000110;<br />
4'd15: out = 7'b0001110;<br />
default: out = 7'b1111111;//tot stins <br />
endcase <br />
end <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea sistemului, modulul de top (fisierul top.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
<br />
module top<br />
(<br />
input wire clock,<br />
input wire reset,<br />
input wire button,<br />
output wire [6:0] out // logica negativa <br />
);<br />
<br />
wire debounce_0_X_out;<br />
wire [3:0] counter_4b_0_X_out; <br />
<br />
debounce<br />
#(<br />
.limit(20'd50_000)<br />
) debounce_0 (<br />
.clock(clock),<br />
.in(button),<br />
.out(debounce_0_X_out)<br />
);<br />
<br />
counter_4b counter_4b_0<br />
(<br />
.clock(clock),<br />
.reset(reset),<br />
.en(debounce_0_X_out),<br />
.out(counter_4b_0_X_out)<br />
); <br />
<br />
transcodor_7seg transcodor_7seg_0 // logica negativa <br />
(<br />
.in(counter_4b_0_X_out),<br />
.out(out)<br />
); <br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
<br />
'''Descrierea test bench-ului(fisierul tb.v):'''<br />
<syntaxhighlight lang="Verilog"><br />
`timescale 1ns / 1ps<br />
<br />
module tb();<br />
<br />
reg clock_tb;<br />
reg reset_tb;<br />
reg button_tb;<br />
wire [6:0] out_tb;<br />
<br />
top dut<br />
(<br />
.clock(clock_tb),<br />
.reset(reset_tb),<br />
.button(button_tb),<br />
.out(out_tb) // logica negativa <br />
);<br />
<br />
initial<br />
begin<br />
clock_tb = 0;<br />
forever<br />
begin<br />
#5 clock_tb = ~clock_tb;<br />
end<br />
end<br />
<br />
initial<br />
begin<br />
reset_tb = 0;<br />
button_tb = 0;<br />
<br />
#50;<br />
reset_tb = 1;<br />
#50;<br />
reset_tb = 0;<br />
#100;<br />
<br />
repeat(5) // vreau 5 apasari de buton<br />
begin<br />
button_tb = 1;<br />
repeat(70_000) <br />
begin<br />
@(posedge clock_tb);<br />
end<br />
button_tb = 0;<br />
repeat(70_000)<br />
begin<br />
@(posedge clock_tb);<br />
end<br />
end<br />
<br />
#1000 $stop();<br />
end<br />
<br />
endmodule<br />
</syntaxhighlight><br />
<br />
Proiectul complet se poate descarca de aici: [https://wiki.dcae.pub.ro/images/5/53/Aplicatii_numarator_exemplu_contor.zip Exemplu_aplicatii_numarator.zip]<br />
<br />
==Exercitii==<br />
<br />
=== Exercitiul 1: Divizor de frecventa cu puteri ale lui 2=== <br />
<br />
Divizorul de frecventa are rolul de a genera un semnal de ceas mai lent din semnalul de ceas principal. <br />
El este alcatuit doar dintr-un numarator, iar fiecare bit al acestuia va fi practic un semnal de ceas cu frecventa din ce in ce mai mica astfel (la jumate): <br />
<br />
bit[0] - frecventa de 2 ori mai mica decat semnalul de ceas<br />
<br />
bit[1] - frecventa de 2 ori mai mica decat bit[0], deci de 4 ori mai mica decat semnalul de ceas <br />
<br />
bit[2] - frecventa de 2 ori mai mica decat bit[1], deci de 8 ori mai mica decat semnalul de ceas <br />
<br />
<br />
Acest lucru se poate observa si in poza ce urmeaza: <br />
<br />
[[Fișier:Freq_divider_output.png| 800px]] <br />
<br />
Implementati acest circuit si folosind ledurile prezente pe placa, incercati sa conectati bitul corespunzator la leduri astfel incat cel mai din dreapta led sa clipeasca cu o frecventa cat mai apropiata de 1s. <br />
<br />
'''Observatie:''' Printr-un astfel de divizor de frecventa minimal, un numar f mic de frecvente poate fi generat. Daca se doreste generarea unui semnal periodic cu o perioada exacta, diferita de cele posibile prin acest mecanism, se poate construi urmatorul circuit:<br />
<br />
<br />
===Exercitiul 2: Divizor de frecventa ce poate genera orice perioada=== <br />
<br />
Un astfel de divizor de frecventa este construit ca in figura de mai jos:<br />
<br />
[[Fișier:Freq_div_orice_freq.png | 800px]]<br />
<br />
Prin adaugarea constantei "limit", perioada semnalului de iesire poate sa fie orice multiplu a perioadei semnalului de ceas. <br />
<br />
Bistabilul de tip t, se modifica atunci cand limita este atinsa si practic iesirea lui este semnalul periodic (o perioada a semnalului de iesire necesita 2 numarari pana la limita).<br />
<br />
Sa se calculeze valoarea necesara pentru "limit" astfel incat pe led-uri sa se genereze semnal cu perioada exact 1s.<br />
<br />
<br />
'''Observatie:''' In FPGA exista fizic trasee speciale pentru reset si ca sinteza sa le foloseasca pe acestea in mod optim nu este recomandat sa se puna logica (cum e aici poarta sau) pe acestea. Modificati schema de mai sus astfel incat sa eliminati respectiva poarta de pe reset. (Sfat: numaratorul va avea acum si intrari de load (comanda si data) )<br />
<br />
===Exercitiul 3: PWM (Pulse Width Modulation)===<br />
<br />
PWM este un concept folosit foarte des in multiple ramuri ale ingineriei, de la transmisiunea informatiei pana la controlul motoarelor sau al intensitatii ledurilor din instalatii de Craciun. El se refera la a avea un semnal periodic cu factor de umplere variabil, asa cum este aratat in poza de mai jos :<br />
<br />
[[Fișier:Pwm.png| 800px]]<br />
<br />
<br />
Generarea unui astfel de semnal periodic se face printr-un numarator si un comparator, ca in figura de mai jos:<br />
<br />
[[Fișier:Pwm_schematic.png | 800px]]<br />
<br />
<br />
Raportul dintre limita pusa si valoarea la care numaratorul se reseteaza este practic factorul de umplere selectat.<br />
<br />
Pentru testare pe placa, cei 6b ai limitei provin de la switch-uri si butoane. Afisarea se face pe led[0].<br />
<br />
<br />
===Exercitiul 4: PWM cu limita variabila===<br />
<br />
Un exemplu foarte uzual de folosire a PWM este aprinderea ledurilor cu diverse pattern-uri. Factorul de umplere determina intensitatea cu care ledul este aprins. Un PWM cu o limita variabila automata va face ledul sa para din ce in ce mai stins sau din ce in ce mai luminos (exemplu: instalatii de Craciun). Combinand asta cu leduri RGB se obtin efecte de schimbare a culorii ledului aparent la intamplare. <br />
<br />
Un astfel de circuit se realizeaza punand inca un numarator in locul limitei, ca in poza de mai jos:<br />
<br />
[[Fișier:Pwm_duty_cycle_up_schematic.png | 800px]]<br />
<br />
<br />
Pentru o functionalitate suplimentara, a pastra un anumit factor de umplere mai multe perioade, am adaugat inca un numarator.<br />
<br />
Pentru claritatea desenului, nu am mai tras efectiv firul de ceas catre intrarile unde acesta se duce.<br />
<br />
<br />
Rezultatul este: <br />
<br />
[[Fișier:Pwm_duty_cycle_up_output.png | 800px]]<br />
<br />
<br />
'''Bonus:''' Incercati sa adaugati si parametri pentru LIMIT_DUTY_CYCLE_LOW si LIMIT_DUTY_CYCLE_HIGH, care sa permita factorului de umplere sa varieze doar intre ele. (Numaratorul va avea nevoie si de intrari pentru comanda de load si data load, ca sa poata incepe de la orice valoare)<br />
<br />
<br />
===Exercitiul 5: Ceas===<br />
<br />
Un ceas poate fi construit cifra cu cifra, folosind numaratoare si comparatoare (si transcodoare pentru display cu 7seg ca sa se vada totul mai bine pe placa)<br />
<br />
Incercati sa implementati un ceas cu milisecunde, secunde si minute in varianta comportamentala. <br />
<br />
'''Observatie:''' Va fi nevoie fie separat, fie in modul de un numarator cu frecventa rezolutiei dorite (aici 1 ms).<br />
<br />
'''Observatie:''' Codul va contine multe "if" de tipul "daca cifra unitatilor secundelor a ajuns la 10, se face 0 si cresc cifra zecilor", repetate pentru fiecare cifra.<br />
<br />
<br />
Incercati si o implementare structurala a ceasului, mergand pe aceasi idee. Fiecare cifra va contine numaratorul ei. Cand numaratorul unei cifre ajunge ajunge la limita sa, va da enable pentru numaratorul cifrei urmatoare. Daca este nevoie se pot folosi si porti aditionale (ex: ca sa creasca minutul cifra zecilor secundelor trebuie sa fie 5 si cifra unitatilor secundelor trebuie sa fie 9). <br />
<br />
'''Observatie:''' Va fi nevoie de un numarator cu frecventa rezolutiei dorite (aici 1 ms sau 1s).<br />
<br />
'''Observatie:''' Pentru a scrie mai putin puteti face un numarator doar cu secunde si minute. Principiul de baza este acelasi si daca aveati ms.<br />
<br />
'''Observatie:''' Simularea a cateva milisecunde sau chiar secunde este un proces indelungat (dureaza minute-ore fizice). Astfel strict pentru simulari se recomanda ceasul sa functioneze cu microsecunde sau milisecunde maxim.</div>Mihai.antonescuhttps://wiki.dcae.pub.ro/index.php?title=Fi%C8%99ier:Freq_div_orice_freq.png&diff=7376Fișier:Freq div orice freq.png2022-04-14T15:23:20Z<p>Mihai.antonescu: </p>
<hr />
<div></div>Mihai.antonescu