Differences

This shows you the differences between two versions of the page.

Link to this comparison view

ac-is:lab:lab05 [2021/10/03 14:06]
eduard.ciurezu hide lab5
ac-is:lab:lab05 [2023/11/11 16:17] (current)
teodor.dicu [Resurse]
Line 1: Line 1:
-====== Laboratorul 5 - Debugging skills ​======+====== Laboratorul 5 - Limbajul Verilog: Circuite secvențiale - Partea a II-a ======
  
-Pagina va fi disponibilă începând cu data de **30.10.2021**.+În laboratorul anterior a fost introdus și studiat conceptul de “circuit secvențial”. Totodată au fost prezentate atribuirile non-blocante în contextul blocurilor always edge-triggered și vi s-a oferit o perspectivă generală asupra expresiilor regulate și asupra automatelor ​de stări. În laboratorul curent se vor parcurge următoarele noțiuni: 
 +  ​Sintaxă Verilog utilă în lucrul cu circuite secvențiale 
 +  ​Tipuri și implementări de automate de stări 
 +  ​Depozitarea informației digitale: Registrul 
 +  ​Memorie cu acces aleatoriu (RAM)
  
-/* 
-In laboratoarele anterioare au fost prezentate elementele de limbaj Verilog si de descriere comportamentala a doua tipuri de circuite: secventiale si combinationale. Acest laborator se va concentra pe modul in care ne asiguram ca un circuit are functionalitatea dorita. ​ 
  
-In acest laborator vom corecta erorile dintr-un adder8 caruia i-am oferit o functionalitate diferita decat a celorlalte module de tip adder implementate in laboratoarele anterioare.+===== Macro: `define =====
  
-== Descrierea functionala a modulului == 
-=== Sumar ===  
  
-Modulul adder8 va primi la intrare ​un pachet ​de date ce contine termenii adunarii ​pe 8 bitisi va returna la iesire suma tuturor termenilorModulul va avea un semnal de errorcare va fi asertat in cazul in care se detecteaza vreo eroare (i.e. overflow).+Un macro este un nume căruia i se poate asocia o valoare înainte ​de compilarea codului. Macro-urile sunt utile pe post de aliasurifără a utiliza resursele compilatoruluiAcestea nu sunt variabileprin urmare nu pot fi atribuite valori unui macro în timpul simulăriiMajoritatea limbajelor de programare, inclusiv Verilog suportă definirea de macrouri.
  
-=== Interfete ===+În Verilog, un macro este specificat cu ajutorul directivei de compilator **`define**. Aceasta înlocuiește textul definit cu o valoare specifică. Un nume de tip `define este un macro global, însemnând că dacă este declarat într-un modul, va rămâne declarat și la ieșirea din modul. După ce macroul este declarat, poate fi apelat în cod cu ajutorul caracterului ` (back-tic). Indiferent dacă sunt declarate în interiorul sau în afara unui modul, compilatorul le tratează la fel.
  
-<imgcaption interconnect | Adder8>{{ .:​lab05:​interfaces_2_.png?​nolink | Adder8}}</imgcaption>+<code systemverilog> 
 +`define MY_NUMBER 5 
 +`define MY_STRING “Hello world!” 
 +`define ADD2PLUS2 2 + 2   
 +</code>
  
  
-==== Interfata ​de intrare ​====+===== Tipuri ​de automate de stări =====
  
  
-<​tabcaption tabel-interfete_in center |Interfata ​de Intrare>+Automatele ​de stări sunt absolut necesare în implementarea oricărui circuit digital. Există două tipuri de automate de stări, clasificate după tipurile de ieșiri generate de fiecare. Primul tip este **Mașina Mealy,** caracterizată de faptul că una sau mai multe ieșiri depind atât de starea curentă, cât și de una sau mai multe intrări, iar al doilea este **Mașina Moore**, ale cărei ieșiri sunt doar o funcție care depinde doar de starea curentă.
  
-^  Port     ​^ ​ Width (bits) ​ ^  Directie ^  Valoare la reset  ^  Descriere ​ ^ 
-|  data_in ​ |  8  |   ​in ​ |   ​0 ​ |Folosit pentru a trimite termenii operatiei de adunare| 
-|  valid_in |  1  |   ​in ​ |   ​0 ​ |Valideaza un byte de date| 
-|  sot_in ​  ​| ​ 1  |   ​in ​ |   ​0 ​ |Marcheaza inceputul unei tranzactii. Va fi activ un singur ciclu de ceas| 
-|  eot_in ​  ​| ​ 1  |   ​in ​ |   ​0 ​ |Marcheaza sfarsitul unei tranzactii. Va fi activ un singur ciclu de ceas| 
  
 +==== Mașina Mealy ====
  
-</​tabcaption>​ 
  
 +Un model general al unei mașini secvențiale Mealy este format dintr-o rețea combinațională care generează ieșirile, starea următoare și o stare “Current State Register” reprezentând starea curentă, ca în figura de mai jos. Starea “Current State Register” este modelată utilizând bistabili D și este sensibilă la semnalul de ceas (Clock). Atât ieșirea, cât și determinarea stării următoare depind de intrare și de starea curentă.
  
-==== Interfata de iesire ====+{{ :​ac-is:​lab:​lab05:​mealy.jpg?​ |}}
  
 +== Exemplu de automat de stări Mealy: ==
  
-<​tabcaption tabel-interfete_out center ​|Interfata de Iesire>+{{ :ac-is:​lab:​lab05:​ex-mealy.jpg ​|}}
  
-^  Port     ​^ ​ Width (bits)  ^  Directie ^  Valoare la reset  ​^ ​ Descriere ​ ^ +<code systemverilog>​ 
-|  data_out ​ |  8  |   ​out ​ |   ​0 ​ |Folosit pentru a trimite rezultatul operatiei de adunare. | +module mealy_fsm( 
-|  valid_out |  1  |   ​out ​ |   ​0 ​ |Valideaza un byte de date| +    output reg parity, 
-|  sot_out ​  ​| ​ 1  |   ​out ​ |    ​|Marcheaza inceputul unei tranzactii. Va fi activ un singur ciclu de ceas| +    input clk, 
-|  eot_out ​  ​|  ​ ​| ​  ​out ​ |   ​0 ​ |Marcheaza sfarsitul unei tranzactii. Va fi activ un singur ciclu de ceas|+    input reset, 
 +    input x); 
 +     
 +    reg state, next_state;​ 
 +    parameter S0=0; 
 +    ​parameter S1=1;
  
 +    // Partea secvențială
 +    always @(posedge clk or negedge reset)
 +        if (!reset)
 +            state <= S0;
 +        else
 +            state <= next_state;
  
-</tabcaption>+    // Partea combinațională 
 +    always @(*) begin 
 +        parity = 1'​b0;​ 
 +        case(state) 
 +            S0:  
 +                if(x)  
 +                    next_state = S1; 
 +                else 
 +                    next_state = S0; 
 +            S1:  
 +                if(x) begin 
 +                    parity = 1; 
 +                    next_state = S0; 
 +                end 
 +                else begin 
 +                    parity = 1; 
 +                    next_state = S1; 
 +                end 
 +            default: 
 +                next_state = S0; 
 +        endcase 
 +    end 
 +endmodule 
 +</code>
  
  
-==== Interfata de Status ​====+==== Mașina Moore ====
  
  
-<​tabcaption tabel-interfete_stat center |Interfata ​de Status>+Un model general al unei mașini secvențiale Moore este prezentat mai jos. Ieșirea sa este dependentă doar de blocul stării curente, iar starea următoare este determinată pe baza intrării și a stării curente. În schema de mai jos, starea “Current State Register” este modelată utilizând bistabili D. Mașinile Moore obișnuite sunt descrise prin intermediul a trei blocuri, dintre care unul conține logică secvențială,​ iar celelalte două conțin logică de tip combinațională.
  
-^  Port     ​^ ​ Width (bits) ​ ^  Directie ^  Valoare la reset  ^  Descriere ​ ^ +{{ :ac-is:​lab:​lab05:​moore.jpg |}}
-|  error  |  1  |   ​out ​ |   ​0 ​ |Folosit pentru a marca o eroare ce s-a intamplat in timpul realizarii operatiei de adunare. |+
  
 +== Exemplu de automat de stări Moore: ==
  
-</​tabcaption>​+{{ :​ac-is:​lab:​lab05:​moore-ex.jpg |}}
  
 +<code systemverilog>​
 +module moore_fsm(
 +    output reg parity,
 +    input clk,
 +    input reset,
 +    input x);
 +    ​
 +    reg state, next_state;
 +    parameter S0=0;
 +    parameter S1=1;
  
 +     // Partea secvențială
 +    always @(posedge clk or negedge reset)
 +        if (!reset)
 +            state <= S0;
 +        else
 +            state <= next_state;
  
-=== Functionalitate ​===+    always @(*) begin 
 +        case(state) 
 +            S0: begin 
 +                parity ​0; 
 +                if (x) 
 +                    next_state ​S1; 
 +                else 
 +                    next_state ​S0; 
 +            end 
 +            S1: begin 
 +                parity ​1; 
 +                if(!x) 
 +                    next_state ​S1; 
 +                else 
 +                    next_state ​S0; 
 +            end 
 +        endcase 
 +    end 
 +endmodule 
 +</​code>​
  
-Modulul va primi unul sau mai multi bytes de date, va realiza operatia de adunare intre toti termenii, si apoi va pune rezultatul pe interfata de iesire. Rezultatul va fi reprezentat pe maximum 10 biti. Deoarece latimea semnalului de iesire este de doar 8 biti, intreg rezultatul va fi pus pe interfata de iesire in doi cicli consecutivi (vezi exemplu de la capitolul Protocol de iesire). ​ 
-In cazul in care rezultatul nu poate fi reprezentat pe 10 biti, semnalul de eroare va fi asertat, iar toti bitii sumei vor fi pusi pe 1. Semnalul de eroare trebuie asertat pe intreaga lungime a pachetului de iesire. 
  
-Tranzitiile RTL-ului (RTL register-transfer level sau modul) ​ si citirea semnalelor de intrare se va face pe frontul pozitiv al ciclului de ceas. +===== Depozitarea informației digitale: Registrul =====
-Resetul este activ pe 0, blocul va fi considerat in reset cand resetul este 0, si va functiona normal cand resetul este 1.+
  
-=== Protocol ===  
  
-==== Protocol ​de intrare ====+Bistabilul este o celulă ​de memorie având capacitate de un bit, care poate fi utilizată pentru a stoca date digitale. Pentru a extinde capacitatea de stocare în ceea ce privește numărul de biți, se va utiliza un grup de bistabili, cunoscut și sub termenul de **registru**. Registrul de **n** biți este alcătuit din **n** bistabili și are capacitate de stocare de **n** biți.
  
-<​imgcaption interconnect | O tranzactie ​de intrare>​{{ ​.:lab05:​wavedrom.png?​nolink |  O tranzactie ​de intrare}}</​imgcaption>​+Un registru, ca orice alt circuit secvențial este sensibil la schimbarea ​de front a semnalelor //clk// și //reset//De asemenea, pentru a putea fi conectat la o magistrală,​ acesta are nevoie să execute două operații simple: 
 +  * citire - informația deja existentă în registru este preluată și eliberată pe ieșirea registrului 
 +  * scriere - informația aflată pe magistrală la un moment ​de timp se scrie în registru
  
-Blocul nu poate accepta tranzactii consecutive. Acesta are nevoie ​de un delay de minim un ciclu de ceas intre 2 intrari consecutive+Pentru o vizualizare mai clară a specificațiilor unui astfel ​de circuit digital secvențial,​ studiați interfața acestuia:
  
-<​imgcaption interconnect | Distanta intre tranzactii consecutive>​{{ .:lab05:wavedrom_8_.png?​nolink ​ ​Distanta intre tranzactii}}</​imgcaption>​+{{ :ac-is:​lab:​lab06:register.png |}}
  
-==== Protocol de iesire ==== 
  
-1Pentru un rezultat pe maxim 8 biti+===== Memorie cu acces aleatoriu (RAM=====
  
-<​imgcaption interconnect | Pentru un rezultat pe maxim 8 biti>{{ .:​lab05:​wavedrom_2_.png?​nolink |  Distanta intre tranzactii}}</​imgcaption>​ 
-2) Pentru un rezultat pe maxim 10 biti 
  
-<​imgcaption interconnect | Pentru un rezultat pe maxim 10 biti>​{{ ​.:​lab05:​wavedrom_3_.png?nolink |  Distanta intre tranzactii}}</​imgcaption>​+Memoria poate fi asociată cu creierul uman, fiind folosită pentru a stoca date și instrucțiuniMemoria unui calculator este spațiul de stocare din calculator unde sunt păstrate datele care urmează să fie procesate și instrucțiunile necesare pentru procesare. Aceasta se împarte în mai multe elemente cu caracteristici similare, numite celule. Fiecare celulă are o adresă unică numerotată de la 0 la N-1, unde N este dimensiunea blocului de memorie (numărul de celule din memorie).
  
-Rezultatul operatiei va fi obtinut prin concatenarea S1 si S2; +Componenta hardware de tip memorie a unui calculator unde sunt stocate sistemul de operare, programele de bază și datele utilizate la momentul curent, pentru a fi accesate cu ușurință de procesor se numește **RAM** (Random Access Memory). RAM-ul este o memorie volatilă, însemnând că toate informațiile stocate în acesta vor fi pierdute la deconectarea calculatorului de la sursa electrică, urmând să fie recuperate la repornirea sistemului de pe HDD/SSD. RAM-ul este mic, atât ca dimensiune fizicăcât și din punct de vedere al capacității de stocare de date.
-Sum = {S1,S2}’+
  
-3) Pentru overflow+În comparație cu registrele, memoria de tip RAM este mai greu de accesat de către procesor. Fiind un circuit secvențial complex sunt necesari mai mulți cicli de ceas pentru a citi/scrie informația necesară. Totodată, oferă o capacitate mult mai mare de stocare, de care registrele nu dispun. Prin urmare, pentru implementarea eficientă a unui circuit digital, este foarte importantă gestionarea resurselor între memorie și registre, astfel încât să se permită stocarea și accesul la toate informațiile necesare într-un timp cât mai scurt.
  
-<​imgcaption interconnect | Overflow>​{{ .:​lab05:​wavedrom_4_.png?​nolink |  Distanta intre tranzactii}}</​imgcaption>​ 
  
-<note important>​ +===== Exerciții =====
-Observatie: ​ Semnalul de eroare este 1 pe durata intregului transfer de date. Toti cei 10 biti ai iesirii sunt pusi pe 1. +
-</​note>​+
  
-=== Exemplu de transfer ===  
-<​imgcaption interconnect | Exemplu de transfer>​{{ .:​lab05:​wavedrom_6_.png?​nolink |  Exemplu de transfer}}</​imgcaption>​ 
-=== Diagrama de stari === 
  
-<​imgcaption interconnect | Diagrama ​de stari>{{ .:lab05:diagramstate.png?​nolink ​Diagrama de stari}}</imgcaption>​+  - (Opțional - de implementat acasă) Implementați automatul de stări din figura de mai jos și identificați tipul acestuia: 
 +    - {{ :​ac-is:​lab:​lab05:​ex1a.jpg |}} 
 +    - {{ :ac-is:lab:lab05:ex1b.jpg |}} 
 +  -  
 +    - Implementați modulul register pornind de la declarația din fișierul ​//​register.v//​. Semnalele oe și we reprezintă Output Enable, respectiv Write Enable. 
 +      * oe controlează ieșirea registrului. Când oe este high ieșirea este activă având valoarea memorată de registru. Când oe este low ieșirea va fi 0. Acest semnal trebuie să fie asincron: modificarea lui va avea efect imediat asupra ieșirii și nu se va aștepta tranziția semnalului de ceas. 
 +      * we controlează scrierea în registru. Când we este high registrul va memora valoarea aflată în semnalul de intrare. Când we este low valoarea registrului nu se va modifica, ignorând practic semnalul de intrare. Acest semnal trebuie să fie sincron: modificarea valorii memorate de registru se face doar în momentul tranziției semnalului de ceas. 
 +      * Semnalul //​disp_out//​ este folosit pentru afișare/​debugging pe display, iar valoarea acestuia trebuie să fie cea memorată de registru în momentul curent. În mod normal acest semnal nu este prezent într-un calculator. Acest semnal nu trebuie să fie afectat de oe, valoarea disponibilă pe disp_out fiind în orice moment egală cu valoarea memorată de registru. 
 +      * Semnalul de reset rst_n este activ pe low (0). 
 +      * //Hint//: Puteți folosi operatorul condiţional. 
 +    - Parametrizați modulul **register** astfel încât data de intrare și ieșire din registru să aibă o dimensiune configurabilă. 
 +      * //Hint//: Utilizați construcția de limbaj **parameter** 
 +    - Pornind de la interfața modulului **sequential_multiplier** din scheletul de cod, implementați un automat de stări care să folosească instanțe parametrizate ale modulului **register** pentru a îndeplini următoarele funcționalități:​ 
 +      * La activarea semnalului //write// să se scrie pe câte un registru (parametrizat corespunzător) valorile semnalelor a și b 
 +      * La activarea semnalului //​multiply//​ să fie extrase valorile din cele două registre, să se înmulțească și să se adauge pe un al treilea registru. 
 +      * La activarea semnalului //​display//,​ semnalul //out// să primească valoarea aflată pe cel de-al treilea registru. 
 +      * Prioritatea celor trei semnale este dată de ordinea în care au fost descrise (e.g. dacă //write// este activ, se ignoră semnalele //​multiply//​ și //​display//;​ dacă //​multiply//​ este activ, se ignoră semnalul //​display//​) 
 +  - Vi se pune la dispoziție un RAM de tip [[https://​www.xilinx.com/​support/​documentation/​ip_documentation/​blk_mem_gen/​v8_2/​pg058-blk-mem-gen.pdf|Block Memory Generator]] instanțiat în modulul **ram_reader**. Completați modulul astfel încât să puteți gestiona citirea din memorie de la o adresă //am_out// în momentul în care semnalul //read// este activ (1).
  
  
-== Tehnici de debug ==  +===== Resurse ​=====
-=== Logs ===+
  
-Cel mai rapid mod de a identifica o eroare este prin a verifica log-ul de simulareAcesta e salvat in fisierul **isim.txt** sau poate fi urmarit direct in consola din simulator+  * {{.:​lab05:​lab5_skel.zip|Schelet ​de cod}} 
 +  * <​html><​class="​media mediafile mf_pdf"​ href="​https://​ocw.cs.pub.ro/​courses/​ac-is/​lab/​lab05?​do=export_pdf">​PDF laborator</​a></​html>​ 
 +  ​{{.:​lab05:​lab5_sol.zip|Soluție laborator}}
  
-Codul trebuie sa contina mesaje de debug pentru ca log-urile sa fie utile. Aceste mesaje pot fi scrise folosind functiile de sistem: +<ifauth @ac-is> 
-* ''​$display()''​ +---- 
-''​$warning()'' ​ ​- ​nu e supportat de Xilinx ISE +  {{.:​lab05:​lab5_sol.zip|Soluție laborator}} 
-''​$error()''​ - nu e supportat ​de Xilinx ISE+  * [[ac-is:​internal:​guidelines|Ghid asistent]] 
 +  todo: schema bloc pentru automatul ​de multiply 
 +</​ifauth>​
  
-Un mesaj simplu precum $display(“[ADDER_STATE_STATUS]:​ Entering state IDLE”), poate ajuta la debug-ul unei stari de **DEADLOCK** sau o tranzitie gresita. 
  
-=== Waveforms === 
- 
-Diagramele cu forme de unda (waveforms) sunt unealta principala de debug folosita, complementand log-ul. Acestea iti arata modul in care s-au modificat semnalele si variabilele locale in timp, pe tot parcursul simularii. 
- 
-Un caz tipic de debug este urmatorul: ​ 
- 
- 1) Testul pica cu urmatorul mesaj de eroare: 
-<​imgcaption interconnect | Console error>{{ .:​lab05:​test_log.png?​nolink | Console error}}</​imgcaption>​ 
- 2) Datorita log-ului ne uitam pe semnalul de eroare din simulare (marcat cu visiniu in imaginea de mai jos) 
- 
-<​imgcaption interconnect | Waves>{{ .:​lab05:​test_waves.png?​nolink | Console error}}</​imgcaption>​ 
- 
- 
-<note important>​ 
-Note: Pentru a intelege functionalitatea descrisa, cititi capitolul Descrierea functionala a modulului si urmariti scheletul de cod. 
- 
-</​note>​ 
- 
-3) Din diagrama formelor de unde ne dam seama ca semnalul nu este asertat in starea in care trebuie. Vom adauga variabila **state** in diagrama. 
- 
-<​imgcaption interconnect | Waves>{{ .:​lab05:​waves_with_state.png?​nolink | Console error}}</​imgcaption>​ 
- 
-4) Observam ca in starea **4 (GENERATE_OUTPUT)** semnalul **error** nu se aserteaza asa cum este de asteptat, de aici ne ducem in cod si observam ca lipseste linia: **error_reg = overflow;** 
- 
-5) Corectam codul: 
- 
-<​imgcaption interconnect | Cod corectat>​{{ .:​lab05:​code.png?​nolink | Console error}}</​imgcaption>​ 
- 
-6) Resimulam si obtinem comportamentul dorit 
- 
-<​imgcaption interconnect | Waves>{{ .:​lab05:​waves_corect.png?​nolink | Console error}}</​imgcaption>​ 
- 
-== Breakpoints === 
- 
-In cazul in care niciuna din metodele de mai sus nu merg, se vor pune breakpoint-uri direct in cod, pentru a verifica starea simularii intr-o anumita zona din cod. Aceasta solutie nu este una eficienta, si nu este viabila atunci cand codul devine mai mare. Pentru mai multe detalii urmariti [[..:​tutoriale:​3-ise-debug | tutorialul de debugging]]. 
- 
- === Teste ===  
- 
-Niciuna din metodele de mai sus nu ar functiona daca nu ar exista teste care sa stimuleze modulul si sa monitorizeze activitea de pe interfata de iesire. ​ 
- 
-Pentru fiecare functionalitate a blocului ar trebui scris un test. In cazul adder8 vom avea nevoie minim de: 
-* Un test care sa realizeze suma a N numere, iar rezultatul este pe 8 biti 
-* Un test care sa realizeze suma a N numere, iar rezultatul este pe 10 biti 
-* Un test care sa realizeze suma a N numere, iar rezultatul este pe mai mult de 10 biti 
-* Un test in care sa trimitem mai multe tranzactii consecutive 
-* Un test in care sa trimitem foarte multi termeni (N >= 40) 
-* Un test in care sa trimitem un singur termen. 
- 
-In cadrul scheletului de cod, in fisierul adder8_test vom avea un task numit **drive** care se ocupa de  stimularea corespunzatoare a modulului. Task-ul **monitor**,​ interpreteaza semnalele de output si verifica corectitudinea acestora. 
- 
-== Exercitii== 
- 
- - (**9p**). ​ Rulati scheletul de cod si corectati erorile care apar. (**6x1.5p**) 
- * Hint: Testele se vor termina fie cu eroare, fie se vor bloca 
- * Hint: Intreg exercitiul ruleaza timp de 2.2us. 
- 
- - (**1p**). ​ Pe modelul testelor din schelet, scrieti un nou test ce verifica o functionalitate a adder8 ce nu a fost explorata inca. 
- 
-- **(Bragging rights) 4p** . Prin testul dezvoltat la exercitiul 2 se gaseste o eroare de functionare a adder8. ​ 
- * Conditii de acordare a bonusului: 
- * Trimiteti un mail catre asistentul de laborator (cu sergiu.a.duda@gmail.com in CC) cu  
-         - Descrierea erorii 
-         - Pasii de reproducere (vectorul de test folosit) 
-<note warning> 
-Eroarea sa nu fi fost raportata inainte de un alt coleg (indiferent de grupa sau serie) 
-</​note>​ 
- 
- 
-== Resurse ==  
- 
-  * {{.:​lab05:​adder8.zip|Schelet de cod}} 
-  * {{.:​lab05:​adder8__sol_fixed.zip|Soluție laborator}} (disponibilă începând cu 02.11.2019) 
-  * <​html><​a class="​media mediafile mf_pdf"​ href="/​ac/​wiki/​lab/​lab05?​do=export_pdf">​PDF laborator</​a></​html>​ 
- 
-<ifauth @user> 
----- 
-  * [[:​internal:​seq|Ghid asistent]] 
-</​ifauth>​ 
-*/ 
ac-is/lab/lab05.1633259175.txt.gz · Last modified: 2021/10/03 14:06 by eduard.ciurezu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0