Differences

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

Link to this comparison view

soc:laboratoare:08 [2024/04/26 13:08]
george_mircea.grosu [5. Algoritmul lui Booth pentru împărțire]
soc:laboratoare:08 [2025/05/14 18:56] (current)
mihai_catalin.stan
Line 1: Line 1:
-===== Laboratorul 8 - Unitatea aritmetică logică ​=====+===== Laboratorul 8 - Proiectul SOC-1 =====
  
-==== 1. Scopul laboratorului ====+În acest laborator vom pune în aplicare cunoștințele despre calculatorul învățat la curs - SAP-1 - și vom implementa propriul nostru sistem de calcul pe care îl vom denumi SOC-1.
  
-În acest laborator vom proiecta o unitate fundamentală a oricarui procesor: Unitatea Aritmetică Logică (UAL, în literatura de specialitate Arithmetic Logic Unit sau ALU). +==== Module ====
  
-{{ :soc:​laboratoare:​08:​ual.jpg?​300 | }}+Scheletul de laborator cuprinde următoarele module: 
 +  * Clock - semnalul de ceas 
 +  * Controller 
 +  * Instruction Register 
 +  * Program Counter 
 +  * Memory (ROM) 
 +  * Registers 
 +  * ALU 
 +  * Flags
  
-Unitatea aritmetică logică se ocupă de aproape toate calculele numerice de care are nevoie un procesor. În ciclul de prelucrare ​instrucţiunilor,​ în diverse etape procesorul are nevoie de rezultatele unor calcule, fie că sunt solicitate de o intrucţiune explicită ​cum este //add// din limbajul de asamblare, fie dintr-un motiv intern (de exemplu adresa absolută a unei date se calculează în funcţie de //segment// şi //offset//ceea ce implică nevoia unor calcule).+Pentru ​vedea cum se unesc aceste module și cum interacționează între eleavem următorul schematic:
  
-==== 2. Cu ce se ocupă o UAL====+{{ :​soc:​laboratoare:​08:​sap-2-block.png?750 | }}
  
-O UAL poate fi proiectată să execute, în principiu, orice operație. Totuși, cu cât operațiile devin mai complexe UAL devine mai scumpă, ocupă mai mult loc și disipă mai multă căldură. Operațiile care sunt, în general, suportate ​de toate UAL sunt: +==== Semnalul ​de ceas ====
-  * Operații logice: AND, OR, NOT, XOR, NOR, NAND, etc. +
-  * Operații de shift: shift stânga, shift dreapta, shift circular, etc. +
-  * Operații aritmetice: adunare, scădere, înmulțire (nu toate), împărțire (nu toate).+
  
-Există întotdeauna un compromis pe care inginerii trebuie să îl facă la proiectarea unei UAL. Ideea este că o instrucţiune poate: +Toate modulele ​din modelul nostru ​de calculator sunt dependente ​de semnalul ​de ceas. Din acest motivam implementat ​un modul cât de simplist se poate pentru a simula acest semnal.
-  * să fie executată //într-un singur ciclu de ceas//, ceea ce ar fi //rapid//, dar foarte //​costisitor// ​din punctul ​de vedere al circuitului fizic (complex), cât şi din alte considerente (de exemplu, disiparea ​de căldură), +
-  * să fie executată //în mai mulţi paşi//, ceea ce e evident mai //lent//, dar //uşor de proiectat şi de adăugat funcţionalităţi unui acelaşi circuit//,​ +
-  * să nu fie executată ca o operaţie de sine stătătoare în UAL, ci implementată prin software pe baza operaţiilor deja suportate ​de UAL, ceea ce ar fi //foarte lent//, dar dă //o oarecare flexibilitate//​ celor care scriu programe la nivel mai înalt.+
  
-Procesoarele moderne folosesc întotdeauna prima variantă pentru instrucţiuni simple şi diverse implementări ale variantei a doua pentru operaţii de complexitate medie şi ridicată.+<code verilog>​ 
 +module clock( 
 + input hlt, 
 + input clk_in, 
 + output clk_out);
  
-De asemenea, o UAL trebuie să fie în stare la orice moment să ruleze operaţia corespunzătoare la fiecare comandă din partea procesorului. Pentru aceasta fiecare operație are un //cod al operației//​ implementat în hardware, astfel încât la o instrucţiune //add// să intre în funcţiune modulul pentru adunare, la o instrucţiune //and// să intre în funcţiune poarta AND, etc. +assign clk_out = (hlt) ? 1'b0 : clk_in;
  
-==== 3. Status Register ====+endmodule 
 +</​code>​
  
-În urma calculelor făcute de UAL pot apărea anumite situațiiprecum overflow la adunare/​scădere,​ care este util să fie semnalate în cadrul sistemului. În acest scop procesoarele au un **registru ​de status** în care prin setarea unui bit anume este semnalat un anumit eveniment. Cel mai adeseainformațiile din registrul ​de status ​(sau **Status Register**) sunt folosite pentru ​modifica fluxul de execuție al programului ​pe baza instrucțiunilor condiționale+De regulă, acest modul va trimite spre ieșire semnalul ​de ceas de intrarecu excepția momentului primirii unui semnal ​de halt (oprire ​a execuției programului ​- se setează la 0).
  
-Spre exemplu, procesorul calculează A + B și dorește să sară la o anumită etichetă dacă rezultatul este 0. UAL va calcula rezultatul lui A + B și va seta bitul **Z (Zero) din Status Register** dacă rezultatul adunării este 0. Instrucțiunea ​de salt **JZ (jump zero)** verifică bitul Z din Status Register și sare la eticheta data dacă bitul este 1, altfel execută ​instrucțiunea imediat următoare.  +<note tip> 
-   +Cea mai simplă metodă de a opri un computer ​din a mai executa ​instrucțiuni este oprirea ceasului acestuia
-În cadrul **x86** Status Register se numește **FLAGS** și are 16 biți. El are o extensie de 32 de biți numită **EFLAGS**. Biții din cele două registre au următoarea semnificație:​  +</​note>​
-{{:​soc:​laboratoare:​08:​eflags.png?​|x86 Status Register}}+
  
-Un exemplu mai simplu este registrul de status pentru **Atmega324** (AVR în general), care se numește **SREG** și are 8 biți:+==== Registre ====
  
-{{:​soc:​laboratoare:​08:​sreg_avr.png?​700|SREG }}+Registrele sunt o metodă simplă, scumpă și foarte rapidă de a stoca date de mare importanță cu care procesorul urmează să lucreze sau cu care abia a lucrat.
  
-În ambele ​registre de status prezentate mai sus se regăsesc o serie de **biți de interes ​pentru ​UAL**:  +SOC-1 va folosi 3 registre ​identice, denumite simplu: A, B, C. Acestea vor fi reprezentate pe 8 biți (foarte mici comparativ cu registrele din calculatoarele moderne care au 32 sau 64 de biți, dar suficient ​pentru ​procesorul nostru), și în funcție de valorile semnalelor de intrare vor suporta ​operațiile de: 
-  * **''​CF''​ sau ''​C''​ (carry flag)** este setat pe 1 când operația executată pe UAL are carry.  ​ +  * Reset - Dat de semnalul de rst 
-  * **''​ZF''​ sau ''​Z''​ (zero flag)** este setat pe 1 când rezultatul operației ​de pe UAL este 0.  +  * Load de pe bus - Dat de semnalul de load 
-  * **''​N''​ (negative)** este setat pe 1 când rezultatul operației ​de pe UAL este un număr negativ (bitul ​de semn al rezultatului este 1).  +  * Incrementare - Dat de semnalul de ''​inc''​ 
-  * **''​OF'' ​sau ''​V'' ​(overflow flag)** este setat pe 1 când operația executată pe UAL produce overflow, mai exact există 2 cazuri:  +  * Decrementare - Dat de semnalul de ''​dec''​
-    * dacă adunăm 2 numere pozitive și bitul de semn al rezultatului este 1 (rezultatul este număr negativ)  +
-    * dacă adunăm 2 numere negative și bitul de semn al rezultatului este 0 (rezultatul este număr pozitiv)+
  
- ==== 4. Algoritmul lui Booth pentru înmulțire ​====+<code verilog>​ 
 +always @(posedge clk, posedge rst) begin 
 + if (rst) begin 
 + data <8'​b0;​ 
 + end else if (load) begin 
 + data <bus[7:0]; 
 + end else if (inc) begin 
 + data <data + 1; 
 + end else if (dec) begin 
 + data <data - 1; 
 + end 
 +end 
 +</​code>​
  
-Înmulțirea a două numere cu semn pe UAL este o operație complexă și costisitoare. Ținând cont că operațiile de deplasare a biților sunt mai rapide ca cele de adunare, **Andrew Booth** a venit cu o propunere de algoritm pentru înmulțirea numerelor cu semn care acum îi poartă numele.+==== Program Counter (PC) ====
  
-=== 4.1. Cum funcționează algoritmul? === +Pentru a executa instrucțiunile (o serie de biți care codifică o anumită acțiune) stocate în memoria ROM, procesorul SOC-utilizează Program Counter-ul (PC), o componentă esențială care reține adresa următoarei instrucțiuni ce urmează a fi executatăInstrucțiunile sunt stocate secvențial,​ începând de la adresa 0, fiecare instrucțiune fiind codificată într-un singur octet.
  
-Ideea de la care pleacă algoritmul ​este că orice secvența continuă ​de 1 dintr-un număr binar poate fi rescrisă ca diferența a două numere binare:  +PC-ul asigură parcurgerea acestor instrucțiuni în ordine, incrementând automat adresa la fiecare front de ceas pozitiv, atunci când semnalul inc este activ. În funcție de semnalele de control (inc, rst și load), comportamentul PC-ului este următorul
-{{  :​soc:​laboratoare:​08:​booth_binary.png?​direct&​350 |}}  +  ​* Dacă ''​inc''​ este activ, valoarea PC-ului se incrementează cu 1 
-   +  ​* Dacă ''​rst''​ este activ, PC-ul se resetează la 0 
-În cazul înmulțirii a două numereurmătoarele expresii sunt echivalente: ​+  * Dacă ''​load''​ este activPC-ul încarcă o valoare de adresă de pe magistrala de date
  
-<​code>​ +<​code ​verilog
-P = M x 10011 =  M x (2⁴ + 2¹ + 2⁰+always @(posedge clk, posedge rstbegin 
-               M x (2⁵ - 2⁴ + 2² - 2⁰+ if (rst) begin 
-               ​M ​<< 5 - M << 4 M << 2 - M << 0+ pc <16'​b0;​ 
 + end else if (loadbegin 
 + pc <bus; 
 + end else if (inc) begin 
 + pc <= pc 1; 
 + end 
 +end
 </​code>​ </​code>​
  
-Practic, algoritmul lui Booth se reduce la a identifica secvențele continue de biți de 1 din termenul R pentru P M x R și a le înlocui cu o diferență. Astfel pentru o operație de înmulțire,​ numărul adunărilor este redus, realizându-se în schimb mai multe operații de deplasare a biților.  +==== Instruction Register (IR) ====
-   +
-Grafic, algoritmul face următoarea transformare:​  +
-{{ :​soc:​laboratoare:​08:​booth_multiplication_example.png?​direct&​600 |}}+
  
-=== 4.2. Prezentarea algoritmului === +În timp ce PC-ul se ocupă de stocarea adresei următoarei instrucțiuni,​ IR-ul încarcă din memorie următoarea instrucțiune de executat sau o resetează în cazul unui semnal de rst.
  
-Dorim să calculăm produsul **P M x R** unde  +==== Magistrala ====
-  * **M, R** sunt numere cu semn reprezentate pe **n biți**  +
-  * **P** este un număr cu semn reprezentat pe **2 * n** biți  +
-   +
-**1. Formăm P**  +
-   +
-  * P biții cei mai nesemnificativi (din dreapta) ai lui P vor lua valoarea lui R, restul vor fi 0.  +
-  * Adăugăm un bit de '​0'​ în dreapta lui P (LSB). Numim acest bit Z.+
  
-<note undefined>​  +Magistrala este modul în care modulele îștransmit date, respectiv instrucțiuni între acesteaÎn timp ce un modul pune date pe magistrală,​ cel căruia îi sunt dedicate datele începe să citească de pe aceasta. Magistrala implementată ​în proiectul SOC-1 fiind implementat pe FPGA folosește niște semnale pentru a alege modulul de pe care citește datele. Spre exemplumagistrala ​va primi semnalul de instruction register enable (ir_en) ​pentru a citi date de la output-ul ir-ului (ir_out).
-P are acum 2 * n + 1 biți. El va fi folosit astfel ​în cadrul algoritmuluiiar bitul extra va fi eliminat la sfârșit ​pentru a obține rezultatul final +
-</​note>​+
  
-**2. Determinăm două valori auxiliare M<sub>+</sub> și M<​sub>​-</sub>** +<code verilog> 
 +always @(*) begin 
 + // [...] 
 + end else if (pc_en) begin 
 + bus = pc_out; 
 + // [...] 
 + end else begin 
 + bus = 16'​b0;​ 
 + end 
 +end 
 +</code>
  
-Valorile auxiliare M<​sub>​+</​sub>​ și M<​sub>​-</​sub>​ vor fi adunate lui P în cadrul algoritmului. Ele vor avea tot 2 * n + 1 biți.  +==== Memorie ====
-  * M<​sub>​+</​sub> ​biții cei mai semnificativi (din stânga) vor lua valoarea lui M, restul vor fi 0.  +
-  * M<​sub>​-</​sub> ​biții cei mai semnificativi (din stânga) vor lua valoarea lui -M, restul vor fi 0.  +
-   +
-**3. Verificăm primii 2 biți ai lui P**+
  
-Verificăm primii 2 biți ai lui P (cei mai nesemnificativi ​biți):  +Memoria modelului nostru de sistem de calcul este de 64K (65536 bytes sau 524288 bits), având adrese pe 16 biți într-un spațiu de adrese ce se extinde de la 0x0000 până la 0xFFFF.
-  ​dacă sunt 01 => P = P + M<​sub>​+</​sub>​ (ignorăm overflow)  +
-  - dacă sunt 10 => P = P + M<​sub>​-</​sub>​ (ignorăm overflow)  +
-  - dacă sunt 00 => nu facem nimic  +
-  - dacă sunt 11 => nu facem nimic+
  
-<spoiler De ce?> +În comportamentul acestui modul puteți observa două registre importanteMAR (Memory Address Register) șMDR (Memory Data Register)ce vor reține adresa din memorie ​de la care citim/scriemrespectiv datele pe care le scriem în memorie sau citim din memorie.
-Tabelul de mai jos surprinde motivul pentru care algoritmul verifică biții LSB ai lui P +
-  * Începutul unei secvențe înseamnă că trebuie să adunăm M<​sub>​-</​sub>​ * 2<​sup>​i</​sup>​unde i e indexul bitului de început ​ secvenței de 1  +
-  * Sfârșitul unei secvențe înseamnă că trebuie să adunăm M<​sub>​+</​sub>​ * 2<​sup>​j<​/sup>unde j e indexul bitului de sfârșit ​ secvenței de 1 +
  
-^ Bitul curent ^  Bitul din dreapta ​ ^  Explicație  ​^ ​ Exemplu ​ ^  +Un ultim aspect important este faptul că memoria permite apelul de funcții - dar nu și recursivitate - datorită ultimelor două adrese ​din memorie 0xFFFF si 0xFFFE ce pot memora valoarea PC-ului la intrarea într-o funcție (call) ​și restituirea acestuia la întoarcerea dintr-un astfel ​de apel (ret).
-| 1 |  0  | Începutul unei secvențe continue de 1  |  000111**10**00 ​ |  +
-| 1 |  1  | Interiorul unei secvențe continue de 1  |  00011**11**000 ​ |  +
-| 0 |  1  | Sfârșitul unei secvențe continue de 1  |  00**01**111000 |  +
-| 0 |  0  | Interiorul unei secvențe continue de 0  |  **00**01111000 ​ |  +
-   +
-De asemenea, observam ca motivul pentru care lui P se adauga ​un bit Z pe pozitia LSB este ca sa putem verifica inceputul unei secvente continue ​de 1 ce porneste de pe indexul 0 (ea poate fi identificata corect doar prin secventa de tip "​10"​). +
-</​spoiler>​ +
-   +
-**4. Deplasăm aritmetic la dreaptă biții lui P cu o poziţie** ​+
  
-<note important>  +<code verilog
-Atenție la [[https://​en.wikipedia.org/​wiki/​Arithmetic_shift | Arithemtic Shift]] vs [[https://​en.wikipedia.org/​wiki/​Logical_shift | Logical Shift]].  +always @(posedge clk) begin 
-</​note> ​+ if (mar_loadh) begin 
 + mar[15:8<= bus[15:8]; 
 + end
  
-**5. Repetăm pașii 3 și 4 de n ori** + if (mar_loadl) begin 
-   + mar[7:0] <= bus[7:0]; 
-**6. Eliminăm bitul Z**+ end
  
-Eliminăm bitul Z, adică cel mai nesemnificativ bit al lui P. Valoarea din P este rezultatul înmulțirii. ​+ if (mdr_load) begin 
 + mdr[7:0] <= bus[7:0]; 
 + end
  
-{{ :​soc:​laboratoare:​08:​booth_multiplication.png?​direct450 |}} + if (ret) begin 
- + mdr[15:8] <ram[16'​hFFFE];​ 
-=== 4.3. Exemplu înmulțire folosind algoritmul lui Booth === + mdr[7:​0] ​<= ram[16'​hFFFF];​ 
-<spoiler Exemplu înmulțire folosind algoritmul lui Booth> + end else if (callbegin 
-Fie M = 00010 (2), -M ~M + 1 = 11110 (-2) și R = 10011 (-13). ​ + ram[16'​hFFFE] <bus[15:8]; 
-   + ram[16'hFFFF] ​<= bus[7:0]; 
-**Pasul 1** + end else if (ram_enhbegin 
- + mdr[15:​8] ​<= ram[mar]; 
-Îl construim pe P prin concatenarea valorilor n{1'b0}, R și Z.  + end else if (ram_enl) begin 
- + mdr[7:​0] ​<= ram[mar]; 
-<code> ​ + end else if (ram_loadbegin 
-P = {(00000), (R), (Z)} = 00000 10011 0  + ram[mar] ​<= mdr; 
-</​code> ​ + end 
-   +end
-**Pasul 2** +
- +
-Formăm M<sub>​+</​sub>​ și M<​sub>​-</​sub>​. +
- +
-<​code>​  +
-M<​sub>​+</​sub>​ = { (M), (00000), (Z)} = 00010 00000 0 +
-M<sub>​-</​sub> ​{(-M), (00000), (Z)} = 11110 00000 0+
 </​code>​ </​code>​
-  ​ 
-**Pașii 3 și 4** 
  
-Pașii 3 și 4 vor fi efectuați de **n 5** ori:  +==== Controller ​====
-  - P 00000 1001//**1 0**//  +
-    * Ultimii 2 biți sunt 10 (am identificat începutul unei secvențe continue de biți de 1) +
-      * P P + M<​sub>​-</​sub> ​00000 10011 0 + 11110 00000 0 11110 10011 0 +
-    * Îl deplasăm aritmetic la dreapta pe P => P 11111 01001 1 +
-  - P 11111 0100//**1 1**//  +
-    * Ultimii 2 biți sunt 11 (suntem în interiorul unei secvențe continue de biți de 1) => Nu modificăm P  +
-    * Îl deplasăm aritmetic la dreapta pe P => P = 11111 10100 1 +
-  - P = 11111 1010//**0 1**//  +
-    * Ultimii 2 biți sunt 01 (am identificat finalul unei secvențe continue de biți de 1) +
-      * P = P + M<​sub>​+</​sub>​ = 11111 10100 1 + 00010 00000 0 = 00001 10100 1 +
-    * Îl deplasăm aritmetic la dreapta pe P => P = 00000 11010 0  +
-  - P = 00000 1101//**0 0**//  +
-    * Ultimii 2 biți sunt 00 (suntem în interiorul unei secvențe continue de biți de 0) => Nu modificăm P  +
-    * Îl deplasăm aritmetic la dreapta pe P => P = 00000 01101 0 +
-  - P = 00000 0110//**1 0**//  +
-    * Ultimii 2 biți sunt 10 (am identificat începutul unei secvențe continue de biți de 1) +
-      * P = P + M<​sub>​-</​sub>​ = 00000 01101 0 + 11110 00000 0 = 11110 01101 0 +
-    * Îl deplasăm aritmetic la dreapta pe P => P = 11111 00110 1 +
-   +
-**Pasul 5**+
  
-Eliminăm bitul Ziar P = 11111 00110 (-26) este rezultatul. +Unitatea de control (UC): decodifică instrucțiunea curentă și pe baza acesteia și a stării internegenerează semnale de control pentru toate resursele procesorului ​(e.g. registrele de intrare/ieșire și operația aritmetică necesare unei instrucțiuni) și coordonează activarea acestor resurse.
-</spoiler>+
  
-<​hidden>​ +În SOC-1, fiecare instrucțiune este reprezentată pe 8 biți, iar execuția acesteia se desfășoară în mai multe etape
-Fie M = 0011 (3) (-M = 1101 (-3)) și R = 1100 (-4) +
-   +
-**Pasul 1**+
  
-Îl construim ​pe P prin concatenarea valorilor R și ZRestul biților vor fi padding cu 0Practic adunăm valoarea 2R la P+Etapele executării unei instrucțiuni 
 +Orice procesor poate avea un ciclu de prelucrare a instrucțiunilor diferit, în funcție de ISA-ul ​pe care îl implementează,​ însă toate vor urma următoarea structură:​ 
 +  * IF (Instruction Fetch) - următoarea instrucțiune este adusă din memorie de la adresa către care pointează registrul Program Counter (PC) și este stocată în registrul Instruction Register (IR)PC este apoi incrementat pentru a pointa către următoarea instrucțiune de încărcat. 
 +  * ID (Instruction Decode) -  instrucțiunea din IR este decodificată
 +  * EX (Execute) - execuția efectivă a instrucțiunii. Aceasta etapă variază în funcție de tipul instrucțiunii curente. 
 +  * MEM (Memory Access) - ciclu folosit în cazul în care instrucțiunea accesează memoria. 
 +  * WB (Write-Back) - scrierea noilor valori în registre.
  
-<​code>​  +Vom studia mai multe despre acestea în Laboratorul 9.
-P = {(0000), (R), (Z)} = 0000 1100 0  +
-</​code>​  +
-   +
-**Pasul 2**+
  
-Formăm A șS.+Adresa în ROM este formată prin concatenarea codului instrucțiunii (8 biți) cu etapa curentă a execuției (4 biți), rezultând o adresă pe 12 biți. Conținutul de la acea adresă este un cuvânt de control, care indică exact ce semnale trebuie activate pentru instrucțiunea respectivă,​ în etapa respectivă.
  
-<​code>​  +==== UAL ====
-{(M), (0000), (Z)} 0011 0000 0  +
-{(-M), (0000), (Z)} 1101 0000 0  +
-</​code>​  +
-   +
-**Pașii 3 și 4**+
  
-Pașii 3 și 4 vor fi efectuațde **n = 4** ori:  +Unitatea Aritmetică Logică (UAL sau ALU în engleză) este modulul ce definește potențialul unui sistem ​de calcul. Calculatorul SAP-1 despre care ați învățat la curs era capabil să efectueze operații ​de adunare șscădere. Spre deosebire ​de acesta, SOC-1 este capabil să efectueze 8 tipuri de operații diferite: 
-  ​P = 0000 110//**0 0**//  +  Adunare (ADD) 
-    * Ultimii 2 biți sunt 00 (ne aflăm într-o secvența continuă de biți de 0) => Nu modificăm P  +  * Scădere (SUB) 
-    Îl shiftam aritmetic la dreapta pe P => P = 0000 0110 0  +  ȘI logic (AND) 
-  ​- P = 0000 011//**0 0**//  +  * SAU logic (OR) 
-    * Ultimii 2 biți sunt 00 => Nu modificăm P  +  SAU Exclusiv ​(XOR
-    Îl shiftam aritmetic la dreapta pe P => P = 0000 0011 0  ​ +  Complement (CMA) 
-  ​- P = 0000 001//**1 0**//  +  * Shiftare la stânga ​(RAL
-    Ultimii 2 biți sunt 10 (am identificat începutul unei secvențe continue de biți de 1=> P = P + S = 1101 0011 0  +  Shiftare ​la dreapta ​(RAR)
-    Îl shiftam aritmetic la dreapta pe P => P = 1110 1001 1  +
-  ​- P = 1110 100//**1 1**//  +
-    * Ultimii 2 biți sunt 11 (suntem în interiorul unei secvențe continue de biți de 1=> Nu modificăm P  +
-    Îl shiftam aritmetic ​la dreapta ​pe P => P = 1111 0100 1  +
-   +
-**Pasul 5**+
  
-Eliminăm bitul Z, iar P = 1111 0100 (-12) este rezultatul. +{{ :​soc:​laboratoare:​08:​ual_sap2.png?300 | }}
-</​hidden>​+
  
-==== 5Algoritmul Non-Restoring Division ====+De menționat este faptul că shiftările se vor face aritmetic, nu logic. Spre exemplu pentru un shift la stânga, dacă ultimul bit înainte de shift era 1 acesta nu va fi pierdut, ci va deveni primul bit. Semnalul de op codifică operația pe care calculatorul trebuie să o efectueze, operații pe care voi va trebui să le implementați.
  
-Asemenea înmulțiriiîmpărțirea pe UAL este o operație complexă și costisitoare. Acest algoritm reduce operația ​de înmulțire la operații de deplasare a biților, adunare și scădere.+De asemeneaîn cele două blocuri always se poate observa faptul că registrul intern al UAL-ului este updatat sincron cu frontul pozitiv al semnalului ​de ceas, în timp ce operațiile efectuate ​de acesta sunt executate asincron.
  
-<​hidden>​ +==== Flags ====
-Asemenea înmulțirii,​ împărțirea pe UAL este o operație complexă și costisitoare. Întrucât operațiile de deplasare a biților sunt mai rapide ca cele de scădere, similar ca în cazul înmulțirii,​ Andrew Booth a creat un algoritm pentru împărțirea numerelor.+
  
-=== 5.1. Cum funcționează algoritmul? === +Ultimul modul necesar pentru ca sistemul de calcul făcut să funcționeze normal este registrul de flag-uri, un registru special pentru a memora diverse caracteristici ale operației anterioareUn astfel de exemplu este Zero Flag, un bit ce este setat pe în registrul nostru în momentul în care rezultatul ultimei operații efectuate a fost 0.
  
-Similar algoritmului pentru înmulțire, ​și în cazul împărțirii pornim ​de la rescrierea ​secvențelor continue ​de 1 dintr-un număr binar ca diferență a două numere puteri ale lui 2 +Proiectul SOC-1 are doar două astfel de flag-uri la momentul curent: Zero Flag și Sign Flag, ale căror valori sunt setate la finalul fiecărui ciclu de ceas (deci pe frontul negativ al semnalului ​de ceas). Modul în care sunt setate poate fi examinat în secvențde cod de mai jos:
-{{  :​soc:​laboratoare:​08:​booth_binary.png?​direct&​350 |}} +
  
-În cazul împărțirii a două numere, următoarele expresii sunt echivalente+<code verilog>​ 
 +reg[2:0] flags;
  
-<code> +always @(negedge clk, posedge rst) begin 
-5'b10011 + if (rst) begin 
-{R, C} Q / M  Q / (2⁴ + 2¹ + 2⁰+ flags ​<= 2'b0; 
-               ​ Q / (2⁵ - 2⁴ + 2² - 2⁰+ end else if (load_a) begin 
-               ​ Q >> 5 - Q >> 4 + Q >> 2 - Q >> ​0+ flags[FLAG_Z] <(a == 0) ? 1'b1 : 1'b0; 
 + flags[FLAG_S] <(a[7] == 1) ? 1'b1 : 1'​b0;​ 
 + end else if (load_bbegin 
 + flags[FLAG_Z] <= (b == 0? 1'b1 : 1'b0; 
 + flags[FLAG_S] <= (b[7] == 1) ? 1'b1 : 1'​b0;​ 
 + end else if (load_c) begin 
 + flags[FLAG_Z] <= (c == 0) ? 1'b1 : 1'​b0;​ 
 + flags[FLAG_S] <= (c[7] == 1) ? 1'b1 : 1'​b0;​ 
 + end 
 +end
 </​code>​ </​code>​
-</​hidden>​ 
  
-=== 5.1 Prezentarea algoritmului ===+<spoiler Descriere extinsa flag-uri>​ 
 +  * Z (Zero) - indică dacă rezultatul unei operații aritmetice este zero. 
 +  * C (Carry) - indică faptul că s-a realizat un transport la nivelul bitului cel mai semnificativ in cazul unei operații aritmetice. Altfel spus, a avut loc o depășire în aritmetica modulo N considerată
  
-Dorim să calculăm împărțirea ​**Q / M = R[3:0] rest R[7:4]** unde: +**Explicație:** În procesorul nostru pe 8 biți, 255 + 1, deși ar trebui să aibă rezultatul 256, de fapt acesta ​este 0 din cauza aritmeticii modulo 256 (28). Pentru a diferenția dintre un apărut real și unul cauzat de o depășire, se utilizează acest semnal de carry.
-  * Q este deîmpărțitul +
-  * M este împărțitorul +
-  * R[3:0] este câtul +
-  * R[7:4] este restul+
  
-**1Initializam R**+  ​V (Overflow) - arată că, în cazul unei operații aritmetice cu semn, complementul față de doi al rezultatului nu ar încăpea în numărul de biți folosiți pentru a reprezenta operanzii
  
-biții cei mai nesemnificativi ​(din dreaptaai lui R vor lua valoarea lui Qrestul vor fi 0+**Explicație:​** Cu alte cuvinte, se poate întampla ca adunând două numere pozitive, să obținem unul negativ (127signed + 1signed ​-128signed),​ dar și adunând două numere negative să obținem unul pozitiv ​(-128signed + (-1)signed = +127signed). Evidentrezultatul nu este corect în aceste situații, și semnalarea se face prin flag-ul de overflow. Dacă am aduna un număr pozitiv (MSB = 0) cu unul negativ (MSB = 1), atunci nu obțin overflow.
  
-**2. Determinăm două valori auxiliare M<​sub>​-</​sub>​ si M<​sub>​+</​sub>​**+  ​N (Negative) - semnul rezultatului unei operații aritmetice (este pur și simplu valoarea bitului de semn al rezultatului) 
 +  ​S (Sign) - este un flag unic AVR, calculat după formula S = N xor V, ce reprezintă “care ar fi trebuit să fie semnul corect al rezultatului”. ​
  
-Valorile auxiliare M<​sub>​-</​sub> ​și M<​sub>​+</subvor fi adunate lui R in cadrul algoritmului (vor avea 8 biți)+**Explicație:​** Cu alte cuvinte, dacă N == 1, dar și V == 1, înseamnă că rezultatul este negativ, dar din cauza unei depășiri S este setat în acest caz pe 0, semnalând că semnul “corect” al operației ar fi trebuit să fie pozitiv. 
 +</spoiler>
  
-  * M<​sub>​-</​sub> ​biții cei mai semnificativi (din stânga) vor lua valoarea lui -M, iar restul vor fi 0 +==== TL;DR ====
-  * M<​sub>​+</​sub> ​biții cei mai semnificativi (din stânga) vor lua valoarea lui M, iar restul vor fi 0+
  
-**3. Verificăm semnul lui R**+Arhitectura include următoarele module: 
 +  ​Clock - semnalul de ceas 
 +  ​Controller 
 +  * Instruction Register 
 +  * Program Counter 
 +  * Memory (ROM) 
 +  * Registers 
 +  ​ALU 
 +  ​Flags
  
-Verificăm primul bit al lui R (cel mai semnificativ bit):+== Clock==
  
-  * dacă este 0 => deplasăm aritmetic la stânga pe R cu o poziție și adaugăm pe M<​sub>​-</​sub>​ la R, R = R + M<​sub>​-</​sub>​ +Generează semnalul de ceas pentru sincronizarea tuturor modulelor. 
-  * dacă este 1 => deplasăm aritmetic la stânga pe R cu o poziție ​și adaugăm pe M<​sub>​+</​sub> ​la R, R = R + M<​sub>​+</​sub>​+Se oprește la primirea semnalului hlt.
  
-**4. Verificăm semnul lui R**+== Controller / Unitatea de Control: ==
  
-Verificăm primul bit al lui R (cel mai semnificativ bitiar:+Decodifică instrucțiunea curentă și generează semnalele de control necesare execuției. 
 +Folosește o memorie ROM cu adrese pe 12 biți (8 pentru instrucțiune,​ 4 pentru etapă→ returnează cuvântul de control.
  
-  * dacă este 0 => setăm ultimul bit (cel mai nesemnificativ) al lui R pe 1 +== Registre ==
-  * dacă este 1 => setăm ultimul bit (cel mai nesemnificativ) al lui R pe 0+
  
-**5. Repetăm pașii 3 și 4 de n (n = 4numărul de biți) ori**+Trei registre ​(AB, C) pe 8 biți – suportă reset, load, inc, dec. 
 +Utilizate pentru stocarea temporară a datelor.
  
-**6. Verificăm semnul lui R**+== Program Counter (PC) == 
  
-Verificăm primul bit al lui R (cel mai semnificativ bit) o ultimă dată:+Păstrează adresa următoarei instrucțiuni.
  
-  * dacă este 0 => nu modifcăm nimic +== Instruction Register (IR) ==
-  * dacă este 1 => adăugăm la R pe M<​sub>​+</​sub>,​ R R + plus_M+
  
-**7. Extragem câtul si restul ​din R**+Încarcă instrucțiunea curentă adusă ​din memorie.
  
-  * câtul ​R[3:0] +== Magistrala (Bus) ==
-  * restul ​R[7:4]+
  
-{{ :​soc:​laboratoare:​08:​booth_division.png?​direct&​349 |}} +Canal comun de date între ​module.
-==== 6. TL;DR ==== +
-  * **Unitatea Aritmetică Logică** se ocupă ​de aproape toate calculele cerute de procesor, solicitate de o instrucțiune explicită sau necesară intern. +
-  * Instrucțiuni uzuale: +
-    * **Operații Logice**: ''​AND'',​ ''​OR'',​ ''​NOT'',​ ''​XOR'',​ ''​NOR'',​ ''​NAND'',​ etc. +
-    * **Operații de shift**: ''​shift stânga'',​ ''​shift dreapta'',​ ''​shift circular'',​ etc. +
-    * **Operații aritmetice**:​ ''​ADD'',​ ''​SUB'',​ ''​MUL''​ (nu toate), ''​DIV''​ (nu toate). +
-  * Există un compromis ​între ​//viteză// și //cost (complexitate hardware)//​ +
-  * **Status Register**: Registru de flag-uri: '''​ZF''/''​Z''​ - Zero, ''​CF''/''​C''​ - Carry, ''​N''​ - Negative, ''​OF''/''​V''​ - Overflow +
-    * Setate de UAL în momentul producerii unor evenimente. +
-  * **Algoritmul lui Booth** este o metodă eficientă de a înmulți două numere cu semn, care folosește operații de ''​shift''​+
  
-==== 7. Exerciții ====+== Memorie ​==
  
-<note warning> +Spațiu de 64K adrese (16 biți) – accese prin registrele MAR/MDR. 
-Va trebui să faceți atât **implementarea** cât și **simularea** cu seturi de date relevante. +Suportă apeluri de funcții (call, ret) cu salvarea/​restaurarea PC-ului din adresele 0xFFFE ​și 0xFFFF.
-</​note>​+
  
-**Task 0 (2p)** - Implementați Algoritmul lui Booth pentru înmulțire în ''​task0.v''​.+== ALU (UAL==
  
-**Task 1 (2p)** - Implementați Algoritmul lui Booth pentru împărțire (non-restoring division) în ''​task1.v''​.+Realizează 8 operații: ADD, SUB, AND, OR, XOR, CMA, RAL, RAR.
  
-<note tip> +== Flags ==
-În Verilog folosim operatorii ''<​nowiki><<<</​nowiki>''​ și ''<​nowiki>>>></​nowiki>''​ pentru deplasarea aritmetică a biților. Valoarea bitului de fill se determină pe baza contextului (signed sau unsigned). Din acest motiv, pentru a deplasa aritmetic la dreapta un număr negativ va trebui să specificăm contextul ''​signed''​ al valorii shiftate.+
  
-<code verilog> +Reține rezultatul logic al ultimei operații: 
-assign magic_shift = 4'​b1010;​ +  * Z (Zero): rezultat ​0 
-assign logic_right_shift = (magic_shift >> 1);                       // 4'​0101 +  * S (Sign): bitul de semn al rezultatului 
-assign logic_left_shift ​(magic_shift << 1);                        // 4'​b0100 +  * Alte flag-uri posibile în arhitecturi mai avansate: C (Carry), V (Overflow), N (Negative), (semn logic: N xor V)
-assign arithmetic_right_shift = (magic_shift >>>​ 1);                 // 4'​b0101 <- greșit pentru numere cu semn +
-assign arithmetic_left_shift = (magic_shift <<<​ 1);                  // 4'​0100 +
-assign signed_arithmetic_right_shift = ($signed(magic_shift>>>​ 1); // 4'​b1101 <- corectsemnul se păstrează +
-assign signed_arithmetic_left_shift = ($signed(magic_shift<<<​ 1);  // 4'​b0100 <- nu este relevant aici +
-</​code>​ +
-</​note>​ +
-<note important>​ +
-În implementarea algoritmului lui Booth aveți nevoie de:+
  
-<code verilog> 
-assign signed_arithmetic_right_shift = ($signed(magic_shift) >>>​ 1); // 4'​b1101 <- corect, semnul se păstrează 
-</​code>​ 
-</​note>​ 
  
-**Task 2 (6p)** ​ - Implementați un modul UAL ''​task2.v''​ cu intrări pe 4 biți. Modulul va primi la intrare 2 numere (i_w_a și i_w_b) ​și indicatorul unei operații ce se va efectua asupra numerelor ​(i_w_op_sel). Ieșirea modului ​(o_w_resultva fi un număr pe 8 biți ce reprezintă rezultatul aplicării operației asupra numerelor i_w_a și i_w_b (Dacă rezultatul are mai puțin de 8 biți se va face padding cu zero pe cei mai semnificativi biți)Pentru valorile lui i_w_op_sel avem următoarele operații:+==== Exerciții de laborator ==== 
 +  ​- ​(5p) Implementați un adder carry-look ahead, astfel: urmăriți ​codul din ALU, ce se instanțiază ​și completați modulul corespunzător ​(cla_adder), conform cunoștințelor de la labul precedent. 
 +  - (5pImplementați flagul de Carry folosind indicatiile din modulul de flags, dar si logica prezentata in descrierea flag-urilor. 
 +  - (Bonus) Implementați flagul de Overflow asemeni celui de Carry de la task-ul anterior.
  
-  *  **(0.5p)** 0 : ''​AND''​ +==== Resurse ==== 
-  ​* ​ **(0.5p)** 1 : ''​XOR''​ +<​hidden>​ 
-  *  ​**(0.5p)** 2 ''​OR''​ +  * [[https://github.com/​cs-pub-ro/​SOC-1|Solutie laborator]]
-  *  **(0.5p)** 3 : ''​ADD''​ +
-  *  **(0.5p)** 4 : ''​SUB''​ (folosind ADD) +
-  *  **(1.5p)** 5 : ''​MUL''​ (folosind algoritmul lui Booth) +
-  *  **(1.0p)** 6 : ''​DIV''​ (folosind algoritmul lui Booth pentru împărțire) +
-  *  **(1.0p)** 7 : ''​MOD''​ (folosind algoritmul lui Booth pentru împărțire)+
  
-<note tip> 
-În acest laborator aveți fișiere de testare pentru fiecare task. Acesta are hardcodate operanzi și rezultatele unor operații, iar rolul lui este de a vă ajuta pentru testare. 
-</​note>​ 
  
-**OPȚIONALTask 3** - Încărcați modulul ''​task3.v''​ pe placa de dezvoltare din laborator. +Daca nu aveti acces la repo, mai jos este arhiva solutiei
- +  {{soc:laboratoare:soc-1-master.zip | Solutie laborator}}
-    ​Switch-urile vor fi folosite pentru a da valoare intrărilor''​i_w_a'',​ ''​i_w_b''​ și ''​i_w_op_sel'';​ +
-    * Butoanele vor fi folosite ''​pentru i_w_reset'';​ +
-    * Afișajul cu 7 segmente va afișa rezultatul operației în format zecimal (utilizați modulul din display_7_segment_driver.v);​ +
-    * LED-urile din dreptul switch-urilor vor afișa valoarea în format binar a intrărilor''​i_w_a'',​ ''​i_w_b''​ și ''​i_w_op_sel'';​ +
-    * Folosiți doar 4 dintre cele 8 cifre de pe afișaj (spre exemplu cele cu anozii AN0-AN3). +
- +
-<​hidden>​ +
-Recomandat să faceți la tabla câte un exemplu atât de înmulțire (factorii de max 4 biți) și de împărțire (deîmpărțitul pe max 8 biți) cu algoritmul lui Booth; prezentați-le exemplul din lab, cu explicații. La UAL și Status Register nu e nevoie să intrați la fel de mult în detalii.+
 </​hidden>​ </​hidden>​
-<ifauth @user> 
-</​ifauth>​ 
- 
-==== 8. Resurse ==== 
-  * [[https://​github.com/​cs-pub-ro/​SOC/​tree/​main/​lab08|Scheletul de laborator]] 
- 
-==== 9. Linkuri utile ==== 
  
-  * [[https://​en.wikipedia.org/​wiki/​Booth%27s_multiplication_algorithm|Booth algorithm Wiki]] +{{soc:laboratoare:sap-2_skel.zip Scheletul de laborator}}
-  * [[http://​www.vlsiip.com/​download/​booth.pdf|Booth algorithm]] +
-  * [[http://​www.massey.ac.nz/​~mjjohnso/​notes/​59304/​l5.html|Exemplu Booth]] +
-  * [[https://​en.wikipedia.org/​wiki/​Logical_shift|Logical Shift]] +
-  * [[https://​en.wikipedia.org/​wiki/​Arithmetic_shift|Arithmetic Shift]] +
-  * [[https://​digilent.com/​reference/​_media/​reference/​programmable-logic/​nexys-a7/​nexys-a7_rm.pdf|Datasheet Digilent Nexys A7]] +
-  * [[https://​digilent.com/​reference/​_media/​reference/​programmable-logic/​nexys-a7/​nexys-a7-sch.pdf|Schema Digilent Nexys A7]]+
soc/laboratoare/08.1714126114.txt.gz · Last modified: 2024/04/26 13:08 by george_mircea.grosu
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