CURSUL 02

Arhitectura Sistemelor de Calcul

Durată: 120 min de predare Nivel: licență, anul III - recomandat după Cursul 01 Disciplină: Sisteme Încorporate PDF: descarcă suportul EN English version

Acest curs deschide „cutia neagră” din interiorul unui sistem embedded: ce este un set de instrucțiuni (ISA), cum au evoluat modelele de adresare de la mașini cu trei adrese până la mașini bazate pe stivă, ce separă o arhitectură CISC de una RISC - și, poate cel mai util practic, cum se calculează și se interpretează performanța reală a unui procesor, dincolo de cifra frecvenței de pe cutie.

1Obiectul și structura cursului6 min

În cursul trecut am tratat sistemele embedded „din exterior" - ce sunt, unde apar, ce le diferențiază de un calculator obișnuit. Acest curs coboară un nivel: ce se întâmplă, concret, în interiorul unității de procesare atunci când execută o instrucțiune - și de ce alegerea arhitecturii contează atât de mult pentru un sistem cu resurse limitate.

Recapitulare din cursul anterior
  • Un sistem embedded este optimizat pentru o funcție dedicată, nu pentru flexibilitate
  • Proiectarea lui înseamnă un compromis între performanță, cost, consum și fiabilitate
  • Majoritatea microcontrolerelor din sistemele embedded folosesc arhitecturi RISC (ARM Cortex-M, RISC-V, AVR) - o afirmație pe care astăzi o vom putea, în sfârșit, explica tehnic

Rezultate ale învățării

  • Să explicați ce este un ISA și de ce separă software-ul de implementarea hardware
  • Să recunoașteți și să comparați cele patru modele de adresare a instrucțiunilor
  • Să explicați diferența fundamentală dintre arhitecturile CISC și RISC
  • Să scrieți și să aplicați ecuația fundamentală a performanței unui procesor
  • Să calculați CPI, IPC și timpul de execuție pentru un program dat
  • Să explicați de ce frecvența de ceas, luată izolat, nu spune nimic despre performanța reală

2Instruction Set Architecture12 min

Instruction Set Architecture (ISA)
Interfața dintre software și hardware: ansamblul instrucțiunilor disponibile, registrelor accesibile programatorului, tipurilor de date, modurilor de adresare, organizării memoriei și mecanismelor de întreruperi și excepții pe care orice implementare hardware trebuie să le respecte.

Ideea esențială: ISA descrie ce poate face procesorul, nu cum o face intern. Compilatorul și programatorul de cod asamblare lucrează exclusiv la nivelul ISA, fără să aibă nevoie să știe cum arată circuitele care execută fiecare instrucțiune.

Programator - dezvoltă aplicația Aplicație și compilator C / C++ / Rust → cod mașină Instruction Set Architecture (ISA) instrucțiuni · registre · moduri de adresare · memorie · întreruperi Microarhitectură pipeline · cache · ALU · control Hardware - CPU, memorie, magistrale
Fig. 1 - ISA ca interfață între software și hardware. Programatorul și compilatorul „văd" doar ISA; tot ce e sub el (microarhitectura, hardware-ul propriu-zis) poate varia liber între generații sau producători, atât timp cât ISA rămâne identic.
Analogie ISA este ca un contract de șofat: pedala de accelerație, frâna, volanul și indicatoarele funcționează la fel, indiferent de producătorul mașinii. Ce se întâmplă sub capotă - motor pe benzină, electric, cu 4 sau 8 cilindri - poate diferi complet, fără ca șoferul să observe vreo schimbare în modul de conducere. La fel, două procesoare pot implementa același ISA cu microarhitecturi total diferite și tot vor executa același program corect.
Exemplu - x86-64: un ISA, două familii de procesoare

Procesoarele Intel Core și AMD Ryzen implementează ambele arhitectura x86-64, dar cu microarhitecturi interne complet diferite (pipeline, cache, predicție a ramificațiilor). Ambele execută exact același cod compilat pentru x86-64 - aceasta este puterea separării dintre ISA și implementare.

Pentru sistemele embedded, alegerea ISA influențează direct performanța, consumul energetic, dimensiunea codului executabil și disponibilitatea uneltelor software. Majoritatea microcontrolerelor moderne folosesc arhitecturi RISC (ARM Cortex-M, RISC-V, AVR), datorită eficienței energetice și simplității setului de instrucțiuni - un fir pe care îl vom relua în secțiunile despre CISC și RISC, mai jos.

3Mașini cu trei adrese10 min

Cel mai intuitiv format de instrucțiune specifică explicit ambii operanzi sursă și locația în care va fi memorat rezultatul - modelul cu trei adrese, folosit frecvent ca reprezentare intermediară de compilatoare, tocmai pentru că reflectă direct expresiile matematice din limbajele de nivel înalt.

Formă generală
ADD DEST, OP1, OP2 → DEST = OP1 + OP2

Considerăm expresia A = B + C × D − E + F + A. Cu o mașină cu trei adrese:

trei_adrese.asm
MUL T, C, D
ADD T, T, B
SUB T, T, E
ADD T, T, F
ADD A, T, A

Fiecare instrucțiune este ușor de urmărit - se vede direct ce se adună cu ce, și unde ajunge rezultatul. Prețul plătit: fiecare instrucțiune trebuie să codifice trei adrese, deci instrucțiunile sunt mai lungi, iar codul executabil ocupă mai multă memorie - o problemă reală pentru un sistem embedded cu Flash limitat.

4Mașini cu două adrese8 min

Modelul cu două adrese reduce câmpurile explicite: unul dintre operanzi este folosit simultan ca sursă și ca destinație a rezultatului.

Formă generală
ADD OP1, OP2 → OP1 = OP1 + OP2
doua_adrese.asm
LOAD T, C
MUL  T, D
ADD  T, B
SUB  T, E
ADD  T, F
ADD  A, T

Instrucțiunile sunt mai scurte decât la modelul cu trei adrese, dar codul e ușor mai greu de urmărit, pentru că valoarea inițială a primului operand este suprascrisă.

Exemplu - x86 folosește exact acest model

Instrucțiunea ADD EAX, EBX adună valoarea din EBX la EAX, iar rezultatul rămâne în EAX - registrul EAX este simultan sursă și destinație. Recunoașteți acum de ce sintaxa x86 arată așa cum arată.

5Mașini cu o adresă8 min

Modelul cu o adresă merge un pas mai departe: introduce un registru special, numit acumulator (AC), folosit implicit de aproape toate operațiile aritmetice și logice. Instrucțiunea specifică explicit un singur operand; celălalt operand și destinația rezultatului sunt mereu acumulatorul.

Formă generală
ADD OP → AC = AC + OP
o_adresa.asm
LOAD  C
MUL   D
ADD   B
SUB   E
ADD   F
ADD   A
STORE A

Instrucțiunile sunt și mai scurte, iar decodificarea hardware e mai simplă - dar prețul e un număr mai mare de accesări ale memoriei (fiecare LOAD/STORE mută date către și dinspre acumulator) și o flexibilitate redusă pentru calcule complexe.

Notă istorică

Primele generații de microprocesoare - Intel 8080, MOS Technology 6502 - foloseau extensiv registre de tip acumulator. Arhitecturile moderne folosesc în principal registre generale, dar conceptul rămâne util pentru a înțelege de ce arhitecturile au evoluat cum au evoluat.

6Mașini cu zero adrese și comparația modelelor12 min

Modelul cel mai compact: zero adrese, bazat pe o structură de date de tip stivă (LIFO - Last In, First Out). Instrucțiunile aritmetice nu mai conțin niciun operand explicit - folosesc automat primele două elemente din vârful stivei.

Formă generală
ADD → TOS = TOS₋₁ + TOS (TOS = Top Of Stack)
Stivă 7 5 TOS = vârf ADD POP, POP, ADD, PUSH Stivă 12 rezultat: 5+7
Fig. 2 - O mașină cu zero adrese: ADD extrage implicit cele două valori din vârful stivei, le adună, și pune rezultatul înapoi în vârf. Niciun operand nu apare explicit în instrucțiune.
Evaluarea expresiei (A+B)*C pe o masina cu stiva: puneti instructiunile in ordine
zero_adrese.asm
PUSH B
PUSH C
PUSH D
MUL
ADD
PUSH E
SUB
PUSH F
ADD
PUSH A
ADD
POP  A
Exemplu - mașina virtuală Java

JVM (Java Virtual Machine) folosește exact acest model: instrucțiuni bytecode precum iadd, imul, isub operează implicit pe vârful stivei de execuție. Alegerea contribuie direct la portabilitatea aplicațiilor Java.

Comparând toate cele patru modele

Caracteristică3 adrese2 adrese1 adresă0 adrese
Operanzi expliciți3210
Registru implicitnunuacumulatorstivă
Lungime instrucțiunemaremediemicăfoarte mică
Complexitate hardwareridicatămedieredusăredusă
Ușurința programăriifoarte bunăbunămedieredusă
Potriviți fiecare model de adresare cu exemplul lui real
De reținut În prezent, majoritatea procesoarelor comerciale folosesc arhitecturi bazate pe registre generale (o variantă apropiată de modelul cu două/trei adrese, dar cu un set mare de registre în loc de un singur acumulator) - cel mai bun compromis practic între performanță și flexibilitate. Modelele istorice rămân importante pentru că explică de ce arhitecturile moderne arată cum arată.

7Arhitectura CISC10 min

Arhitecturile CISC (Complex Instruction Set Computer) au apărut într-o perioadă în care memoria era scumpă, iar compilatoarele aveau posibilități limitate de optimizare. Obiectivul: reduce numărul de instrucțiuni necesare unui algoritm, introducând instrucțiuni complexe, capabile să execute mai multe operații „dintr-o mișcare".

Procesoarele CISC includ, de regulă, sute de instrucțiuni, numeroase moduri de adresare și instrucțiuni de lungime variabilă - unele pot accesa simultan memoria, face o operație aritmetică și actualiza registre. Rezultatul: programe mai compacte, dar un procesor mult mai complex de implementat.

Microcodul

Intern, un procesor CISC descompune fiecare instrucțiune complexă în micro-operații mai simple, printr-un mecanism numit microcod. Programatorul „vede" o instrucțiune complexă; hardware-ul, în spate, execută o secvență de pași elementari - exact modul în care se pot adăuga instrucțiuni noi fără să se schimbe circuitele de bază ale procesorului.

Familia Intel x86 este cel mai cunoscut exemplu de arhitectură CISC. Interesant: chiar și procesoarele x86 moderne traduc intern instrucțiunile complexe în micro-operații asemănătoare celor din arhitecturile RISC - o convergență pe care o discutăm mai jos.

8Arhitectura RISC10 min

Conceptul RISC (Reduced Instruction Set Computer) a apărut din cercetări de la Berkeley și Stanford, în anii 1980, care au arătat că majoritatea programelor folosesc, de fapt, doar o fracțiune din instrucțiunile disponibile într-o arhitectură CISC. Concluzia: simplificați setul de instrucțiuni și optimizați execuția celor rămase.

Procesoarele RISC folosesc instrucțiuni de lungime fixă, un număr mare de registre generale și o arhitectură load/store: operațiile aritmetice se fac exclusiv pe registre, iar memoria este accesată numai prin instrucțiuni dedicate de încărcare și stocare.

De ce contează asta pentru un sistem embedded Instrucțiunile de lungime fixă și setul redus simplifică radical decodificarea - mai puține tranzistoare dedicate controlului, mai mult spațiu (și energie) rămas pentru execuția propriu- zisă. Simplitatea permite și pipeline eficient: mai multe instrucțiuni „în zbor" simultan, fiecare într-o etapă diferită de execuție. Combinația explică de ce ARM Cortex-M, AVR și RISC-V domină microcontrolerele moderne: performanță bună, consum energetic redus.

Fluxul tipic de execuție într-un pipeline RISC clasic are cinci etape: Fetch (preluarea instrucțiunii), Decode (decodificare și citirea registrelor), Execute (operația în ALU), Memory Access (acces la memorie, dacă e nevoie) și Write Back (scrierea rezultatului în registru) - etape care se pot suprapune, instrucțiuni diferite fiind, în fiecare moment, în etape diferite ale pipeline-ului.

9RISC vs. CISC: comparație și convergență8 min

Deși multă vreme privite ca abordări concurente, implementările moderne împrumută elemente din ambele lumi: procesoarele x86 au o ISA CISC, dar execută intern micro-operații RISC-like; procesoarele ARM moderne includ predicție de ramificații și execuție speculativă - tehnici asociate istoric cu procesoarele de performanță înaltă, indiferent de familie.

CaracteristicăRISCCISC
Complexitatea instrucțiunilorredusăridicată
Lungimea instrucțiunilorfixăvariabilă
Număr registremareredus/mediu
Pipelinefoarte eficientmai dificil
Acces la memorieload/storedirect în instrucțiuni
Dimensiunea coduluimai maremai redusă
ExempleARM, AVR, RISC-VIntel x86, AMD64

Comparația trebuie privită prin prisma compromisurilor inginerești, nu ca un verdict „mai bun / mai rău" - de aceea servere și desktop-uri (unde compatibilitatea software contează enorm) continuă să folosească x86, în timp ce sistemele embedded (unde consumul și costul dominează) au migrat aproape universal spre RISC.

10Performanța unui procesor: CPI, IPC, timp de execuție7 min

Performanța unui sistem embedded nu se reduce la „cât de rapid" execută instrucțiuni - un controler de frânare are nevoie de determinism, nu de viteză maximă. Totuși, pentru a discuta riguros despre performanță, avem nevoie de o formulă precisă, nu de intuiții.

Ecuația fundamentală a performanței
Execution Time = Instruction Count × CPI × Clock Cycle
unde Instruction Count (IC) = numărul total de instrucțiuni executate, CPI (Cycles Per Instruction) = numărul mediu de cicluri per instrucțiune, iar Clock Cycle = 1 / frecvența de ceas.

Observația centrală: timpul de execuție poate fi redus pe trei căi independente - mai puține instrucțiuni (algoritm/compilator mai bun), CPI mai mic (arhitectură mai eficientă) sau frecvență mai mare (cu cost energetic și termic). Niciuna dintre ele, luată separat, nu spune toată povestea.

Calculatorul ecuației de performanță

11CPI, IPC și ecuația de performanță9 min

Aceeasi sarcina, trei arhitecturi: unde se duce timpul
CPI - Cycles Per Instruction CPI = Clock Cycles / Instruction Count

Cu cât mai mic, cu atât mai eficientă arhitectura. Instrucțiunile nu costă la fel: o adunare simplă poate lua 1 ciclu, un acces la memorie sau un salt condiționat, mai multe.
IPC - Instructions Per Cycle IPC = Instruction Count / Clock Cycles = 1 / CPI

Un procesor scalar clasic are IPC ≤ 1. Procesoarele superscalare moderne, cu unități de execuție multiple, pot finaliza mai multe instrucțiuni pe ciclu - IPC > 1.
Exercițiu rezolvat - frecvența mai mare nu înseamnă mereu mai rapid

Două procesoare execută același program de 1 000 000 de instrucțiuni. Procesorul A rulează la 1 GHz cu CPI = 2. Procesorul B rulează la 800 MHz cu CPI = 1. Care e mai rapid?

Vezi rezolvarea

T_A = (1 000 000 × 2) / 10⁹ = 2 ms

T_B = (1 000 000 × 1) / (800 × 10⁶) = 1,25 ms

Deși procesorul B are frecvența mai mică, este mai rapid - CPI-ul redus compensează din plin diferența de frecvență. Concluzia practică: frecvența de ceas, izolată, nu e un indicator suficient al performanței reale.

În practică, performanța reală depinde de trei categorii de factori care acționează simultan: arhitectura procesorului (frecvență, pipeline, cache, număr de nuclee), eficiența codului executat (algoritm, structura datelor, localitatea de acces) și compilatorul și optimizările (alocarea registrelor, eliminarea codului redundant, selecția instrucțiunilor). Niciuna singură nu garantează performanță - toate trei trebuie aliniate.

Ecuatia de performanta a procesorului

12Erori frecvente5 min

  • „Frecvența mai mare înseamnă întotdeauna un procesor mai rapid." Fals - vezi exercițiul rezolvat de mai sus. CPI-ul contează la fel de mult, uneori mai mult, decât frecvența. Comparați întotdeauna timpul de execuție real, nu doar frecvența afișată pe foaia de catalog.
  • „RISC înseamnă mai puține instrucțiuni în program." de regulă e invers: un program RISC are nevoie de mai multe instrucțiuni (fiecare mai simplă) decât echivalentul CISC, dar fiecare execută în mai puține cicluri, iar suma poate fi, totuși, mai rapidă. Separați „numărul de instrucțiuni" de „timpul total de execuție" - ecuația performanței le leagă pe amândouă, nu sunt totuna.
  • „CISC și RISC sunt categorii complet separate azi." Nu mai sunt curat separate - x86 (CISC ca ISA) traduce intern în micro-operații RISC-like, iar ARM (RISC) a adăugat mecanisme de performanță avansate, istoric asociate cu CISC. Tratați RISC/CISC ca pe un spectru de filozofii de proiectare, nu ca pe o etichetă binară fixă.

13Rezumat și glosar5 min

ISA este contractul stabil dintre software și hardware - ceea ce permite evoluția liberă a implementării interne fără să strice compatibilitatea programelor existente. Modelele de adresare (trei, două, o adresă, zero adrese) reprezintă puncte diferite pe axa flexibilitate-vs-compactitate. RISC și CISC sunt filozofii de proiectare care astăzi converg parțial. Iar performanța unui procesor se măsoară riguros prin ecuația Execution Time = IC × CPI × Clock Cycle - nu prin frecvența singură.

ISA
Instruction Set Architecture - interfața dintre software și hardware.
Microarhitectură
implementarea internă concretă a unui ISA (pipeline, cache).
Acumulator
registru implicit folosit de instrucțiunile mașinilor cu o adresă.
Load/Store
model în care memoria e accesată doar prin instrucțiuni dedicate.
CISC
Complex Instruction Set Computer - instrucțiuni complexe, ISA bogat.
RISC
Reduced Instruction Set Computer - instrucțiuni simple, lungime fixă.
CPI
Cycles Per Instruction - cicluri medii necesare per instrucțiune.
IPC
Instructions Per Cycle - reciproca CPI-ului.
Pipeline
suprapunerea etapelor de execuție ale mai multor instrucțiuni.

14Întrebări de verificare8 min

  1. Ce reprezintă ISA și care este rolul lui în relația dintre hardware și software?
  2. Care sunt avantajele și dezavantajele unei mașini cu trei adrese, respectiv cu o adresă?
  3. Cum funcționează o mașină bazată pe stivă (zero adrese)? Dați un exemplu real.
  4. Care sunt principalele caracteristici ale arhitecturilor CISC și, respectiv, RISC?
  5. De ce arhitecturile RISC sunt preferate în sistemele embedded?
  6. Definiți CPI și IPC și precizați relația dintre ele.
  7. Scrieți ecuația performanței procesorului și explicați fiecare termen.
  8. De ce frecvența de ceas, luată izolat, nu este suficientă pentru evaluarea performanței?
  9. Calculați timpul de execuție pentru un program cu 2 000 000 de instrucțiuni, CPI = 2, la 500 MHz.

15Direcții de aprofundare2 min

Cursul următor continuă direct: acum că știm cum arată o instrucțiune și cum se măsoară performanța unui procesor, trecem la ierarhia de memorie și la particularitățile arhitecturilor ARM - cele mai folosite azi în microcontrolerele și SoC-urile pentru sisteme embedded.

Conceptul de frecvență vs. performanță discutat aici revine, aplicat concret, în Laboratorul 01 (cursurile 4-6), unde măsurați pe un Raspberry Pi 5 real cum se schimbă timpul de execuție și consumul atunci când variați frecvența procesorului.