Sunteți pe pagina 1din 99

3.

Considerente hardware asupra sistemelor embedded

3.1. Arhitecturi

Prin arhitectura unui sistem de tip IoT sau embbedded înțelegem modalitatea prin care
diferitele componente ale unui astfel de sistem sunt conectate între ele. În continuare vom
discuta despre arhitectura procesorului în principal.
Deci, unul dintre conceptele fundamentale ce definesc un procesor este acela al
arhitecturii acestuia. De asemenea, orice sistem de operare (Windows, Linux, iOS,
Android etc.) – deoarece rulează pe un anumit hardware – este direct dependent de
arhitectura procesorului și a sistemului embedded. În cadrul procesului de dezvoltare
conceptuală şi ulterior de implementare fizică a unui sistem embedded primul pas constă,
de altfel, în alegerea arhitecturii procesorului, ulterior această alegere ne va influenţa
întregul lanţ decizional. În continuare ceea ce este cu adevărat interesant este să definim
noţiunea de arhitectură şi să observăm influenţele pe care aceasta le are asupra
performanţelor diferitelor procesoare.
Arhitectura unui microprocesor precizează conceptele fundamentale ce stau la
baza construcţiei interne a acestuia, definind structura care susţine şi care se află în
spatele modului de manipulare a datelor – de exemplu, încărcarea/stocarea din/în
memorie, adunare, scădere, înmulţire şi împărţirea diferitelor valori.
La ora actuală există un număr foarte mare de arhitecturi. Câteva dintre acestea sunt
următoarele:
1. IA-32 (Intel Architecture 32-bit) cunoscută publicului larg sub denumirea x86,
x86-32 sau i386 este arhitectura utilizată şi impusă de către compania Intel. IA-32
este o arhitectură pe 32 de biţi de tipul von Neuman, implementată pentru prima
dată pe microprocesorul 80386. Această arhitectură a fost extinsă şi pentru
structuri pe 64 de biţi, IA-64, păstrându-se însă compatibilitatea cu IA-32. IA-64 a
fost implementată pentru prima dată în cadrul familiei de microporcesoare
Itanium. O prezentare în detaliu a procesoarelor ce au la bază această arhitectură
va fi realizată în subcapitolul „Unitatea centrală de procesare”. În principal
aceste procesoare sunt dedicate pieții calculatoarelor personale având prea puține
legături cu piața sistemelor embedded.
Începând cu familia de procesoare Atom, compania Intel își face simțită
prezența și pe piața sistemelor embedded. Această familie de procesoare are la
bază arhitecturile IA-321 și/sau IA-642 fiind capabile să ruleze la tensiuni de
operare foarte mici – de aici rezultând și consumul redus de energie, una din
cerințele fundamentale ce caracterizează o clasă largă de dispozitive embedded. În
principal, familia de procesoare Atom a fost lansată pentru a contrabalansa oferta
existentă a concurenților pe piața sistemelor optimizate pentru curenți și costuri
scăzute (de exemplu: familia Geode produsă de AMD sau familia ARM).

1
De exemplu, cele din seriile: Atom Z5xx, Atom Z6xx și Atom N2xx.
2
De exemplu, cele din seriile: Atom 2xx, 3xx, N4xx, D4xx și D5xx.

1
2. ARM (Advanced RISC Machines) este o arhitectură RISC pe 32 și pe 64 de biţi şi
a fost dezvoltată de Compania ARM Holdings din Cambridge, Marea Britanie.
Dacă procesoarele bazate pe arhitectura Intel domină piaţa calculatoarelor
personale, procesoarele bazate pe arhitectura de tip ARM domină în totalitate piaţa
bunurilor la larg consum fiind înglobate în telefoanele mobile, media playere,
console de jocuri, rutere şi în nenumărate periferice ale calculatoarelor personale
precum plăci video, hardisk-uri etc.
Arhitectura ARM este cea mai utilizată din punct de vedere a numărului de
procesoare produse, ce o au la bază, şi utilizate în diferite aplicaţii. Astfel în anul
2006 s-a estimat că în jur de 98% din telefoanele mobile au în componenţă cel
puţin un procesor bazat pe o arhitectură de tip ARM [Krazit, 2006]. În timp ce în
anul 2010 aproximativ 95% din piaţa telefoanelor inteligente erau bazate pe o
arhitectură de tip ARM [Wikipedia, 2011].
Un avantaj fundamental al procesoarelor bazate pe arhitectura ARM este dat de
consumul redus al acestor procesoare. Acest consum redus este generat în
principal de simplitatea arhitecturii. Arhitectura ARM a cunoscut în timp un număr
mare de versiuni, vezi Tabelul 3.1.
Versiunea 2 a arhitecturii (depăşită la ora actuală), ARMv2, a stat la baza
microprocesorul ARM2 – produs în 1986. Acest microprocesor a fost considerat
cel mai simplu microprocesor pe 32 de biţi, având în componenţă în jur de 30.000
de tranzistoare. Pentru comparaţie procesorul produs de Intel 80386 în anul 1986
îngloba 275.000 de tranzistoare. Simplitatea arhitecturii ARM a generat un
consum foarte scăzut; performanţele acestui microprocesor au fost superioare
microprocesorului 80286 produs de compania Intel. Continuându-şi evoluţia
această arhitectură şi-a păstrat simplitatea3, simplitate generatoare a acestor
consumuri reduse. Această simplicitate a arhitecturii (generatoare a unor
consumuri reduse) este dată în special de ne utilizarea microcodului în rularea
instrucţiunilor limbajului de asamblare.
Fiecare nouă versiune a arhitecturii ARM aduce o nouă îmbunătățire față de
generația anterioară. De exemplu, arhitectura ARMv4 introduce, față de
arhitectura anterioară ARMv3, un nou set de instrucțiuni (pe 16 biți denumit
Thumb) care permite obținerea unei densități mai mari a codului față de versiunea
standard a setului de instrucțiuni ARM care este pe 32 de biți – aceleași programe
ocupă mai puțină memorie chiar dacă anumite operații trebuie făcute cu mai multe
instrucțiuni Thumb decât în cazul în care erau utilizate setul standard ARM.
Tabelul 3.1. Versiunile arhitecturii ARM
Nr. Nuclee (CPU) dezvoltate de ARM Nuclee (CPU) dezvoltate de alte
Arhitectură
biți Holdings companii
ARMv1 32 ARM1
ARMv2 32 ARM2, ARM3 Amber
ARMv3 32 ARM6, ARM7

3
De exemplu, microprocesorul ARM6, bazat pe următoarea versiune a acestei arhitecturi (ARMv3), era
realizat cu ajutorul a doar 35.000 de tranzistoare.

2
ARMv4 32 ARM7TDMI, ARM8, ARM9TDMI StrongARM, FA526
ARM7EJ, ARM9E-S, ARM9EJ-S, XScale, FA626TE, Feroceon,
ARMv5 32
ARM10E PJ1/Mohawk
ARM11: ARM1136J(F)-S,
ARMv6 32 ARM1156T2(F)-S, ARM1176JZ(F)-S, și Raspberry Pi B+
ARM11MPCore
ARM Cortex-M0, ARM Cortex-M0+, ARM
ARMv6-M 32
Cortex-M1
ARMv7-M 32 ARM Cortex-M3
ARMv7E-M 32 ARM Cortex-M4, ARM Cortex-M7
ARM Cortex-R4, ARM Cortex-R5, ARM
ARMv7-R 32
Cortex-R7
ARM Cortex-A5, ARM Cortex-A7, ARM
Krait, Scorpion, PJ4/Sheeva,
ARMv7-A 32 Cortex-A8, ARM Cortex-A9, ARM Cortex-
Apple A6 (Swift)
A12, ARM Cortex-A15, ARM Cortex-A17
ARMv8-M 32 ARM Cortex-M23, ARM Cortex-M33
ARMv8-R 32 ARM Cortex-R52
Apple A7 (Cyclone), A8
32/ ARM Cortex-A35, ARM Cortex-A53, ARM (Typhoon), A8X, A9 (Twister),
ARMv8-A Cortex-A57, ARM Cortex-A72, ARM A9X, Raspberry Pi 3 (A53),
64 Cortex-A73 Jetson Nano (A57), Raspberry Pi
4 (A57)
64/
ARMv8.1-A Apple A10 (Hurricane + Zephyr)
32
64/ ARM Cortex-A55, ARM Cortex-A75, ARM
ARMv8.2-A Apple A11 (Monsoon, Mistral)
32 Cortex-A77, ARM Cortex-A78
64/
ARMv8.3-A Apple A12 (Lightning, Thunder)
32
ARMv8.4-A Apple A13 (Vortex,Tempest)
ARMv8.5-A Apple A14 (Firestorm,Icestorm)

Cerințele din piața dispozitivelor de tip embedded au determinat o diversificare


a versiunilor existente în vederea specializării acestora. Astfel, versiunea 7 a
arhitecturii ARM are următoarele subversiuni:
1. A (application – cunoscută sub numele de ARMv7-A) a fost realizată
pentru a putea rula sisteme de operare sofisticate, ce necesită utilizarea de
tehnici de tip memorie virtuală. Pe baza acestei versiuni (ARMv7-A) au
fost realizate următoarele procesoare sau CPU (Central Processing Unit):

3
a. Cortex-A8 (2.0 DMIPS/MHz) este un procesor pe 32 de biţi
proiectat special pentru a obţine cele mai bune performanţe având
consumul cel mai redus4;
b. ARM Cortex-A9 Single Core Processor (2.5 DMIPS/MHz);
c. ARM Cortex-A9 MPCore - procesor dezvoltat specific pentru a
conlucra cu mai multe nuclee similare de același tip etc.
d. Cortex-A12 este un succesor a ARM Cortex-A9 și este considerat a
fi cu 40% mai puternic decât ARM Cortex-A9 (3.0 DMIPS/MHz
pe nucleu);
e. Cortex-A15 (3.5 DMIPS/MHz pe nucleu) este implementat în
tehnologii de 28 și 32 nm mergând până la frecvențe maxime de 2.5
GHz.
2. R (ARMv7-R) a fost proiectată dedicat pentru procesoarele de tip
embedded de timp real (real-time systems). Utilizându-se această versiune
a arhitecturii ARM au fost construite următoarele procesoare: Cortex-
R4 şi Cortex-R4F. Aceste procesoare sunt dedicate de asemenea
aplicaţiilor de tip embedded comerciale ce sunt produse în volume mari
(hard disk-uri, imprimante, modemuri wireless etc.).
3. M (ARMv7-M) – arhitectură optimizată pentru diferitele tipuri de
microcontrolere. Această versiune este dedicată în special aplicaţiilor ce
urmăresc minimizarea costurilor (low-cost applications). Această familie
include următoarele procesoare:
a. Cortex-M0/M0+ – procesor de mici dimensiuni şi consum redus
(este considerat procesorul cu consumul cel mai mic construit
utilizându-se o arhitectură ARM) – 0.84 DMIPS/MHz pentru
Cortex-M0 și 0.93 DMIPS/MHz pentru Cortex-M0+;
b. Cortex-M1 este proiectat special spre a fi inclus în FPGA-uri;
c. Cortex-M3 (1.25 DMIPS/MHz) este proiectat special pentru a
obţine maximum de performanţă cu costuri minime. De exemplu,
sunt implementări ale acestui procesor care funcționează la 150
MHz (NXP LPC1800).
d. Cortex-M4 este un procesor Cortex-M3 (1.25 DMIPS/MHz) la care
s-au adăugat în plus un set de instrucțiuni de tip DSP (SIMD și
MAC) și, în mod opțional, poate să conțină și o unitate de tip FPU
(floating-point unit). Dacă nucleul conținue și un FPU atunci
numele familiei este Cortex-M4F.
Unul din nucleele (procesor sau CPU) cele mai de succes a versiunii 4 a
arhitecturii ARM a fost ARM7TDMI utilizat în milioane de dispozitive chiar și
astăzi. Numele acestui nucleu, ARM7TDMI, este dat de următoarea combinație
ARM7 + Thumb + Debug + Multiplier + ICE5. Operația de debug (depanare) a
programelor dezvoltate se poate realiza în mod firesc pe toate procesoarele,

4
Consumul acestui procesor este mai mic de 300 mW lucrând la frecvenţe ce încep de la 600MHz
ajungând şi depăşind 1GHz.
5
In-Circuit Emulator

4
deosebirea fundamentală a nucleului ARM7TDMI este dată de faptul că aici CPU
se oprește fizic în cazul unei cereri de debug. În plus, ARM7TDMI este primul
procesor în care a fost implementat setul de instrucțiuni Thumb.
Microcontrolerul AT91SAM7S256, componentă principală a modulului de
comandă și control (NXT brick), are la bază un procesor RISC de tipul
ARM7TDMI care este o implementare a versiunii 4 a arhitecturii ARM - ARMv4.
Noua versiune Lego Mindstorms EV3 este susținut de un microprocesor de tipul
TI Sitara AM1808 ce are la bază un nucleu ARM926EJ-S – arhitectură ARMv5.

Figura 3.1. Schema bloc a microprocesorului OMAP3530 produs de Texas Instruments

3. SH sau SuperH a fost la origine o arhitectură de tip RISC pe 32 de biţi dezvoltată


de compania Hitachi care ulterior prin versiunea arhitecturală SH5 a migrat către
64 de biţi. In acest moment sunt produse o serie largă de microprocesoare pe baza
arhitecturii SH de către compania japoneză Renesas Technology (companie
deţinută de firmele Hitachi şi Mitsubishi). Versiunile SH2, SH2A, SH3, SH4 şi
SH4A ale arhitecturii SuperH sunt în continuare active şi implementate în diferite
tipuri de procesoare.
4. PowerPC (Power Performance Computing sau Performance Optimization With
Enhanced RISC Performance Chip) a fost dezvoltată în anul 1991 de un consorţiu

5
format din trei companii Apple, IBM şi Motorola. Această arhitectură a fost
dezvoltată iniţial pentru piaţa calculatoarelor personale, iar ulterior s-a impus în pe
segmentul de piaţă al sistemelor embedded şi a sistemelor de foarte înaltă
performanţă6. De asemenea, aceste procesoare sunt utilizate la consolele de jocuri
Nintendo GameCube şi Wii precum şi la Xbox 360 produsă de Microsoft.
PowerPC este o arhitectură de tip RISC pe 32 sau pe 64 de biți ce are la bază
arhitectura Power dezvoltată de compania IBM. Aceste microprocesoare pot opera
cu numere cu virgula mobilă în simplă sau dublă precizie.

Arhitecturi CPU SoC Placă electronică

Samsung OMAP35x
S5PC100 Evaluation Module
IA-32
Texas Instruments
Cortex A8 OMAP3530
MIPS

Freescale
ARM i.MX515 BeagleBoard

SH ARM1136J(F)-S Texas Instruments


OMAP2430

PowerPC Qualcomm 7x00

Figura 3.2. Legăturile existente între arhitecturi, CPU, SoC şi plăcile electronice

Diferitele sisteme de operare sunt proiectate să lucreze cu diferite familii de


microprocesoare caracterizate de diferite arhitecturi şi de diferite versiuni ale acestora.
Făcând o analiză comparată a produselor existente, se observă că la ora actuală arhitectura
ARM domină piaţa telefoanelor mobile în timp ce compania Intel prin arhitecturile IA-32
şi IA-64 domină piaţa calculatoarelor personale (PC).
Arhitectura unui procesor nu este un lucru fizic, ea este o descriere a conceptelor
fundamentale ce stau la baza funcţionării unui CPU. După cum am văzut anterior fiecare
arhitectură are diferite versiuni, de exemplu arhitectura ARM are 8 versiuni (ARMv1,
ARMv2, ..., ARMv6, ARMv7 și ARMv8). Iar de cele mai multe ori este posibil ca o
anumită versiune a unei arhitecturi să aibă alte subversiuni.
În concluzie, implementarea unei anumite versiuni a unei anumite arhitecturi
este chiar procesorul sau CPU. Din punct de vedere a proiectantului după alegerea
arhitecturii, alegerea CPU este cel de al doilea pas. Funcţie de cerinţele impuse sistemului
(de exemplu: consum, cost redus, necesitatea existenţei unui sistem de operare sau/şi

6
Firma BAE SYSTEMS Platform Solutions a implementat pentru avionul de vânătoare F-35 Lightning II
(avion din generaţia a cincea multirol de tip stealth) produs de Lockheed Martin sistemul electronic de
comandă şi control ce are drept nucleu un procesor dual core dezvoltat pe baza unei arhitecturi de tip
PowerPC. Acest procesor a fost construit de firma Freescale. Procesoare, ce au la bază această
arhitectură, sunt utilizate și în: avioanele F-22 (Lockheed Martin) sau în avionul multirol francez
Dassault Rafale (Dassault Aviation).

6
rularea unor aplicaţii de tip real-time) se va alege procesorul sau CPU (de exemplu pentru
arhitectura ARM putem alege Cortex-M0 sau Cortex-A8 sau Cortex-R4F).

Figura 3.3. Schema bloc a microprocesorului i.MX515 produs de Freescale

Producătorii de circuite şi microprocesoare înglobează ulterior procesorul (CPU) într-


un SoC (System on a Chip). SoC7 înglobează pe lângă CPU și o serie de circuite logice
proiectate să susţină funcţionarea CPU împreună cu o serie de dispozitive periferice
(porturi de comunicație paralele sau seriale, convertoare ADC sau DAC, circuite suport și
drivere pentru elemente de afișare LCD etc.). Toate acestea sunt înglobate într-un SoC,
deci într-un singur circuit electronic – toate acestea sunt realizate pentru reducerea
costurilor dar şi pentru optimizarea la maximum a performanţelor sistemului global.
De exemplu, CPU-ul Cortex-A8 este înglobat într-o serie largă de microprocesoare de
tip SoC precum: OMAP3530 produs compania Texas Instruments, i.MX515 produs de
compania Freescale, S5PC100 produs de compania Samsung etc. Pentru o analiză
comparativă a acestor microprocesoare de tip SoC, a arhitecturii lor interne vezi Figura

7
De multe ori termenul SoC este utilizat greșit în legătură cu dispozitivele embedded de tip
microcontroler, deoarece și acestea înglobează un CPU împreună cu un număr larg de periferice care
asigură autonomia acestor dispozitive (numărul de componente externe suport este foarte mic).
Microcontrolerele sunt, în mod firesc, asimilate în familia sistemelor de tip SCS (single chip systems)
chiar dacă uneori această realitate nu este în totalitate valabilă.
Termenul SoC este folosit în directă legătură cu CPU puternice capabile să susţină sisteme pe
operare ce necesită existența unei memorii externe foarte mare – memorii ce nu por fi înglobate într-un
sistem de tip SoC în principal datorită: costurilor prohibitive şi a dificultăţilor tehnologice. Cu toate
acestea, sistemele SoC necesită existența unui număr considerabil de dispozitive periferice externe, mult
mai multe și mai complexe decât în cazul microcontrolerelor.
Teoretic vorbind utilizarea termenului SoC în legătură cu microcontrolerele nu este corectă dar este
utilizată.

7
3.2, Figura 3.3 şi Figura 3.4.
Ulterior aceste microprocesoare de tip SoC sunt înglobate pe diferite plăci electronice
devenind astfel un sistem de sine stătător. Aceste plăci electronice (board) sunt cunoscute
sub numele de plăci de dezvoltare, plăci de evaluare sau, de exemplu, plăci de bază
(mainboard).

Figura 3.4. Schema bloc a microprocesorului S5PC100 produs de compania Samsung

(a)
(b)

Figura 3.5. Schema bloc a microcontrolerelor: (a) CC3200 SimpleLink Wi-Fi and
Internet-of-Things și (b) CC2650 SimpleLink Multistandard Wireless

8
În mod similar platformele CC3200 SimpleLink Wi-Fi și CC2650 SimpleLink
urmăresc creșterea puterii de calcul prin înglobarea în aceeași structură a unor procesoare
specializate task-ului respectiv. De exemplu, CC3200 SimpleLink Wi-Fi are ca procesor
central un ARM Cortex-M4 iar ca procesor ce gestionează comunicarea wireless (Wi-Fi)
un așa zis Network Processor.

Studiu de caz: După cum am menționat anterior, pentru a spune cu certitudine care din
două procesoare este mai bun, în sensul unei puteri mai mare de calcul, trebuie
luate în considerare, pe lângă frecvența de lucru, și mulți alți factori, precum:
arhitectura, pe câți biți este procesorul sau numărul de nuclee pe care acesta le
conține.
Pentru a înțelege influențele diferitelor arhitecturi, a numărului de nuclee sau a
frecvenței de lucru prezint mai jos rezultatele obținute cu ajutorul testului Google
Octane [Shimpi, 2013], test compozit ce urmărește multiple aspecte ale modalității
de comportare a diferitelor procesoare, precum performanțele de: citire și scriere
în vectori, calcul matematic în virgulă mobilă, operații pe bit, management a
memoriei sistemului (creare și distrugere de obiecte), apelări de funcții etc.
Performanțele obținute cu testul Google Octane sunt prezentate în Figura 3.6.
Pentru a putea înțelege semnificația valorilor prezentate în această figură și a
realiza o analiză mai profundă a diferitelor nuclee analizate se va prezenta mai jos
o scurtă descriere a fiecăruia în parte.
Telefonul Apple iPhone 5 utilizează un circuit Apple A6, de tip package on
package (PoP) system on a chip (SoC), are nucleul ARM Cortex-A15 de tip dual-
core (vezi Tabelul 3.1) ce are la bază o arhitectură ARMv7.
Smartphon-ul Apple iPhone 5s are la bază procesorul A7 produs de Apple
(denumit comercial Cyclone) care este unul dual-core tactat la aceeaşi frecvenţă de
1,3 GHz, ca și predecesorul A6, dar acesta este o versiune îmbunătăţită şi
îmbogăţită cu suportul arhitecturii ARMv8 a procesorului Swift produs anterior
(A6). O caracteristică importantă este dată și de faptul că această versiune 8 a
arhitecturii ARM (ARMv8) este pe 64 de biți. A7 este primul procesorul pe 64 de
biţi apărut pe un terminal mobil, el fiind dezvoltat intern de către Apple şi nu unul
licenţiat direct de la compania ARM Holding.

Intel Bay Trail FFRD (Atom Z3770) 6219

Apple iPhone 5s 5500

Samsung Galaxy S4 (SHVE300s) 2999

Apple iPhone 5 2859

Figura 3.6. Testul compozit Google Octane utilizat în cuantizarea performanțelor


diferitelor nuclee de procesare – cu cât valoarea este mai mare cu atât procesorul
este mai performant [Shimpi, 2013]

9
Telefonul Samsung Galaxy S4 are un SoC (system on chip) de tip Exynos 5
Octa (octa-core) caracterizat de arhitectură de tip ARMv7 pe 32 de biți. Structura
procesorului pe 8 nuclee este compusă din procesorul Cortex-A15 (quad core)
tactat la 1.6 GHz și din procesorul Cortex-A7 (quad core) tactat la 1.2 GHz,
obținându-se astfel o împerechere între cluster-ul Cortex-A15, eficientizat pentru
obținerea unei puteri maxime de calcul, cu procesorul Cortex-A7, mai lent dar
mult mai eficient energetic. SoC-ul Exynos 5 Octa văzut de “afară” se prezintă ca
un procesor quad-core care în fiecare moment de timp va avea active doar 4
nuclee, de exemplu toate cele 4 nuclee Cortex-A15 sau toate cele 4 nuclee Cortex-
A7 sau orice combinație între aceste 2 cazuri “extreme” (generatoare de
performanțe maxime sau de consum minim).
Intel Bay Trail FFRD Z3770 este un procesor quad-core bazat pe arhitectura
Intel IA-64 ce poate lucra la o frecvență a semnalului de tact situată în domeniul
1.46 – 2.4 GHz.
Comparând performanțele obținute în graficul anterior între terminalele
Samsung Galaxy S4 și Apple iPhone 5s se observă că performanțele terminalului
iPhone sunt aproape duble față de a terminalului Galaxy S4 chiar dacă acesta din
urmă are cu două nuclee în plus. Aceste performanțe sunt obținute în special
datorită noii arhitecturii ARMv8 și a dublării numărului de biți a magistralelor
procesorului (64 de biți versus 32 de biți). Un rezultat interesant se poate observa
și în cazul comparării performanțelor procesoarelor A7 cu Atom Z3770. Cu toate
că ambele lucrează pe magistrale de 64 de biți, performanțele lor sunt foarte
apropiate (5500 versus 6219), aceasta ținând cont și de faptul că A7 lucrează la 1.3
GHz în timp ce Z3770 lucrează la minim 1.5GHz (în cadrul testului a căror
rezultate au fost prezentate anterior nu se precizează frecvența procesorului Z3770
– din acest motiv am presupus că aceasta este cea minimă). Din aceste valori
tragem concluzia că arhitectura ARMv8 este una mai eficientă decât arhitectura
IA-64 deoarece procesorul A7 are doar 2 nuclee în timp ce procesorul Z3770 are 4
nuclee.
Rezultatul intrigant apare însă atunci când comparăm rezultatele obținute de
Apple iPhone 5 și Samsung Galaxy S4, deoarece ambele conțin nucleul ARM
Cortex-A15 dar primul are 2 nuclee ce lucrează la 1.3 GHz iar cel de al doilea are
4 nuclee ce lucrează la 1.6 GHz în timp ce prerformanțele sunt de 2859 (A6) și
2999 (Exynos 5 Octa). O posibilă cauză a acestor rezultate va fi evidențiată în
studiul care urmează.
Studiu de caz: Influența codului, a modului în care acesta este scris, asupra puterii
consumate este un factor binecunoscut la ora actuală. De exemplu, având la
dispoziție același sistem (un calculator personal8) s-a testat timpul de viață a
acumulatorilor atunci când resursele sistemului sunt utilizate la maxim de aceeași
serie de aplicații în cazul rulării a două sisteme de operare diferite Windows
versiunea 7 și Mac OS X Snow Leopard (versiunea 10.6.1) [Ngo, 2009]. În

8
Calculatorul a fost un laptop MacBook Pro cu LCD de 15 inch având un procesor Intel Core 2 Duo la
2.5 GHz, 4 GB de RAM și o placă video Nvidia GeForce 9600M GT cu 512 MB RAM.

10
ambele teste un videoclip muzical de înaltă definiție9 a rulat într-o buclă infinită,
iluminatul ecranului și al tastaturii au fost setate pe maximum, volumul sonor a
fost dat la maxim, iar placa de rețea wireless Wi-Fi a fost pornită [Ngo, 2009]. În
final acumulatorul calculatorului personal a fost capabil să alimenteze sistemul
timp de 78 de minute atunci când acesta a rulat sistemul de operare Windows 7 și
111 minute atunci când sistemul de operare a fost Mac OS X Snow Leopard [Ngo,
2009]. În concluzie, durata de viață a acumulatorilor calculatorului personal este
de doar 66% (2/3) atunci când PC-ul rulează sistemul de operare Windows 7.
Deoarece sistemul de operare Android este creat să ruleze pe o largă paleta de
dispozitive hardware produse de un număr mare de companii acesta nu este
optimizat să ruleze pe un anumit SoC. Compania Apple produce atât sistemul de
operare cat și dispozitivul hardware pe care acesta rulează. De aici tragem
concluzia importanței optimizării codului de o așa natură astfel încât în final
performanțele obținute pe un sistem ce are 2 nuclee să fie foarte apropiate cu ale
unui alt sistem ce utilizează 4 nuclee.

9
Aceasta implică: un acces continuu la HDD sistemului, utilizarea resurselor procesorului și în special a
setului de instrucțiuni SIMD (decodarea informației video și audio) și prezentarea informației video
(deci transferuri masive de date – prin bus-urile sistemului) pe monitorul LCD înglobat.

11
3.2. Testarea performanțelor sistemelor embedded

Pe lângă testul testul Google Octane prezentat anterior (test care evidențiază atât
performanțele în virgulă fixă cât și în virulă mobilă), observăm din prezentarea familiei
ARM că un parametru fundamental, prin intermediul căruia se compară diferitele
arhitecturi este DMIPS. DMIPS are semnificația Dhrystone MIPS. Iar MIPS (Millions of
Instructions Per Second) semnifică numărul de instrucțiuni realizate de procesor într-o
secundă – în acest caz milioane de instrucțiuni pe secundă.
Dhrystone este un test, un benchmark, sintetic – deci este un program proiectat să
efectueze cele mai reprezentative elemente (reprezentative din punct de vedere statistic)
ce se regăsesc în orice tip de aplicație. Testul determină un indice de performanță
pentru operațiile de tip întreg a unui anumit tip de procesor.
Acest tip de test nu implică nici un fel de analiză a capabilităților procesorului de a
lucra cu numere și operații în virgulă mobilă.
Benchmark-ul Dhrystones caută să caracterizeze viteza de execuție a unui CPU pe
baza unor activități caracteristice unei clase largi de aplicații generice.
În cadrul acestui test sunt executate o serie largă de elemente fundamentale ale
limbajului de programare și diferite operații specifice, caracteristice precum: apeluri de
funcții, decizii logice, lucru cu pointeri, diferite tipuri de atribuiri, lucrul cu diferite
structuri de date, operații aritmetice simple, operații cu string-uri etc. Acest test nu
execută nici o operație de tip I/O (intrare/ieșire) sau apeluri de funcții ale sistemului de
operare. În final ieșirea acestui program este numărul de Dhrystones pe secundă, mai
exact numărul de iterații realizate ale buclei principale a programului pe unitatea de
timp – deci pe secundă.
Acest test a fost dezvoltat pentru prima dată în anul 1984 de către Reinhold P.
Weicker. Versiunile 2.0 și 2.1 ale acestui test apărute în lunile martie și mai ale anului
1988 încearcă să elimine influența generată de anumite optimizări pe care compilatoarele
sunt capabile să le efectueze asupra codului (sau a librăriilor utilizate de aceste
compilatoare) astfel încât să se obțină anumite performanțe ce nu sunt reprezentative
pentru procesor. Aceste performanțe sunt date în principal de abilitatea compilatorului de
a optimiza codul programului. Cu toate acestea versiunea 2.1 a testului înlătură doar
parțial optimizările ce pot fi realizate de diferite tipuri de compilatoare. Din acest punct de
vedere acest test poate fi utilizat și pentru a testa eficiența diferitelor compilatoare,
atunci când se utilizează același procesor și același sistem, sau eficiența diferitelor tehnici
de optimizare pe care un anumit compilator le oferă utilizatorilor.

Tabelul 3.2. Efectul optimizărilor codului asupra performanțelor


obținute [Riemersma, 2012]
Lungime
Compilator/optimizare cod/mod de lucru Dhrystone/s
program
Compilator GCC, neoptimizat, mod de lucru ARM 8092 18.558
Compilator GCC, optimizat (-O2), mod de lucru ARM 4536 51.488
Compilator GCC, neoptimizat, mod de lucru Thumb 6428 15.225
Compilator GCC, optimizat (-O2), mod de lucru Thumb 3660 44.239

12
Pentru a exemplifica efectele tehnicilor software de optimizare a codului prezentăm
datele din Tabelul 3.2, [Riemersma, 2012]. Rezultatele au fost obținute pe un
microcontroler LPC2106 ce rulează la o frecvență de 58.9824 MHz (sistemul avea un
cristal de 14.7456 MHz, în timp ce frecvența de lucru a microcontrolerului fost obținută în
urma unei multiplicări cu 4). Microcontrolerele de tipul LPC2106 au la bază un CPU
ARM7TDMI. Din acest motiv, putem presupune că rezultatele prezentate în Tabelul 3.2
sunt relevante și pentru microcontrolerul Atmel AT91SAM7S256 cu ajutorul căruia este
realizat blocul de comandă și control (NXT brick) a roboților LEGO Mindstorm NXT2.0.
Din Tabelul 3.2 se observă că viteza de execuție a codului scris folosind numai
instrucțiuni de tip Thumb este apropiată (deși inferioară) de cea a setului de instrucțiuni
ARM în timp ce lungimea programelor este mai mică, chiar dacă sunt necesare mai multe
instrucțiuni Thumb (pe 16 biți) pentru a scrie același program care anterior a fost scris
utilizând setul de instrucțiuni ARM (pe 32 de biți). Tot din același tabel se observă și
influența tehnicilor de optimizare înglobate în compilator asupra programelor rezultante la
nivelul lungimii codului generat și a vitezei de execuție a acestui cod.
Bechmark-ul Dhrystone este mult mai semnificativ decât valoarea MIPS10 (million
instructions per second) în momentul în care comparăm diferite tipuri de procesoare
construite cu ajutorul a diferitelor tipuri de arhitecturi existente. De exemplu, o anumită
aplicație ce rulează pe un procesor cu o arhitectură RISC (set redus de simple instrucțiuni)
necesită pentru execuția ei un număr mult mai mare de instrucțiuni pentru a fi finalizată
decât dacă aceeași aplicație ar fi rulată pe un CPU implementat pe baza unei arhitecturi de
tip CISC (arhitectură cu instrucțiuni complexe). Vom obține astfel un număr mult mai
mare de instrucțiuni/secundă pentru procesoarele RISC, dar acest lucru nu ne va spune
care din cele 2 sisteme a terminat primul de rulat același program sau o anumită zona a
codului. În situația utilizării valorii Dhrystone, care cuantizează numărul de iterații a unei
părți principale a programului în unitatea de timp, influența setului de instrucțiuni este
astfel eliminată din ecuație. Valoarea MIPS poate fi utilizată, având semnificație, doar
atunci când se compară CPU din aceeași clasă ce sunt implementări ale aceleiași versiuni
a unei anumite arhitecturi specifice.
În momentul în care se precizează performanțele Dhrystone de cele mai multe ori se
specifică și versiunea acestuia. În cazul în care versiunea nu se precizează, se presupune
implicit că aceasta este versiunea 2.1 a testului Dhrystone – cea care s-a și impus ca
standard.
DMIPS (Dhrystone MIPS) este valoarea ce se obține în momentul în care se împarte
valoarea Dhryestone obținută (în urma testului) la 1.757 – numărul de Dhryestone pe
secundă obținut pe un sistem VACS 11/780 considerat un sistem de referință ce realiza 1
MIPS.
De exemplu, un sistem care obține un scor de 33 DMIPS (33.6 DMIPS pentru
AT91SAM7S256 în modul Thumb) este de 33 de ori mai rapid decât un sistem VACS
11/780.
După cum am prezentat anterior testul Dhrystone surprinde doar performanțele în
virgulă fixă. Dar o mare parte a sistemelor de tip embedded sau IoT au înglobate nuclee
de tip FPU. Pentru a surprinde și performanțele acestor sisteme se utilizează un alt test
(benchmark) denumit Linpack. Testul Linpack măsoară puterea de calcul în virgulă

10
A nu se confunda cu arhitectura de tip MIPS (Microprocessor without Interlocked Pipeline Stages)

13
mobilă a unui sistem – operații reprezentate pe numere în virgulă mobilă pe 64 de biți.
Acest test cuantizează cât de repede un sistem este capabil să rezolve un sistem de n
ecuații cu n necunoscute de tipul Aꞏx = b (unde: A este o matrice n x n, x este vectorul
necunoscutelor de tip n x 1, iar b este un vector de valori n x 1). În cadrul testului
LINPACK 100 matricea A are un ordin egal cu 100. În timp ce în cadrul testului
LINPACK 1000 matricea A este de tipul 1000 x 1000.
Ultima versiune a acestui test, denumită HPLinpack (Linpack’s Highly Parallel
Computing benchmark), a fost dezvoltată pentru testarea performanțelor sistemelor de
calcul paralele și este cea utilizată în determinarea celor mai performante 500 de
calculatoare din întreaga lume – TOP500.

Tabelul 3.3. Performanțe comparative între 2 arhitecturi diferite ARM versus IA-64
[Hoffman, 2009]
Dhrystone Linpack
(DMIPS) (KFLOPS)
Valori brute obținute
Beagle Board 883 23376
Atom 330 Desktop 1822 933638
Performanțe per MHz (Beagle la 600 MHz, Atom la 1.6 GHz)
Beagle Board 1.47 38.96
Atom 330 Desktop 1.14 583.52
Performanțe per W (consum Beagle 0.5W, Atom 2W)
Beagle Board 1766 46752
Atom 330 Desktop 227.75 116704.75
Performanțe per Dolar (preț: Beagle 32$, Atom 43$)
Beagle Board 27.59 730.5
Atom 330 Desktop 42.37 21712.51
Performanțe per mm de siliciu ocupat de procesor (Beagle 60 mm2, Atom 52 mm2)
2

Beagle Board 14.72 389.6


Atom 330 Desktop 35.03 17954.58

Studiu de caz: În continuare pentru conștientizarea aspectelor multiple ce trebuie


analizate de către un inginer atunci când alege un anumit tip de SoC/procesor
pentru o anumită aplicație de tip embedded sau IoT vom face o altă comparație
între familiile de procesoare ARM și Intel. Pentru aceasta vom analiza comparativ
performanțele obținute de un sistem de tipul BeagleBoard (ce este construit în
jurul SOC-ul OMAP3530 ce rulează la 600 MHz, de tip Cortex-A8, având la bază
arhitectura ARMv7, arhitectură pe 32 de biți) și de un sistem Atom11 N330

11
Familia de procesoare Atom a fost dezvoltată de compania Intel pentru a satisface piața dispozitivelor
mobile. Seria Atom-Z este dedicată calculatoarelor desktop ieftine și notebook-urilor; în timp ce familia

14
Desktop (procesor dual-core pe 64 biți ce rulează la o frecvență de 1.6 GHz având
o FSB12 ce rulează la 533MHz). Performanțele obținute sunt prezentate
comparativ în Tabelul 3.3 [Hoffman, 2009].
Analizând datele din Tabelul 3.3 observăm că sistemul Atom obține performanțe
superioare atât în domeniul operațiilor în virgulă mobilă, dar și în cazul celor pe
întregi. În schimb sistemul Beagle Board este unul mai eficient, oferind o putere de
calcul mai mare (în cazul numerelor întregi) raportată la puterea consumată și la prețul
dispozitivului. Sistemul Beagle Board fiind mai eficient în cazul operațiilor realizate
pe numere întregi comparativ cu calculele în virgulă mobilă.
Funcție de operația pe care un dispozitiv o execută, cerințele necesare sistemului
pot fi diferite (de ex. performanțe superioare de grafică sunt necesare în cadrul unui
joc în timp ce rularea unui film necesită performanțe superioare de decodificare a
fluxurilor video). Astfel, costurile implicate sunt și ele diferite. De exemplu, existența
în interiorul SoC-ului OMAP3530 a unui DSP determină un consum aproximativ de
1W atunci când se decodifică un fișier video, în timp ce pentru același fișier sistemul
Atom 330 necesită 8W.

de procesoare Atom-N a fost proiectată pentru a fi înglobată în așa zisele sisteme de tip MID (mobile
internet devices).
12
FSB – front-side bus este magistrala de date ce transportă datele de la CPU la northbridge.

15
3.3. Managementul puterii consumate

Actualmente trendul tuturor sistemelor portabile este de a îngloba cât mai multe
facilități multimedia, de comunicare, de miniaturizare a echipamentelor, susținute de
autonomii din ce în ce mai mari (minim 240 de ore în standby fiind un standard de facto).
Înglobarea unui număr din ce în ce mai mare de facilități determină integrarea în același
SoC a unui număr din ce în ce mai mare de dispozitive. Dar toate aceste dispozitive
pentru a fi portabile implică dimensiuni din ce în ce mai mici. Din acest motiv
dimensiunile tehnologice de realizare a tranzistoarelor din aceste circuite scad în mod
continuu, vezi și Figura 3.7, pentru a crește densitatea de integrare și frecvența de lucru.
Toți acești factori determină creșterea consumului; consum care este minimizat în mod
clasic prin scăderea tensiunii de alimentare. Scăderea tensiunii de alimentare are drept
rezultat scăderea proporțională a tensiunii de prag a tranzistoarelor (în vederea păstrării
funcționării corecte a structurii și a existenței unei margini de zgomot corespunzătoare),
iar la rândul ei această scădere determină creșterea curentului static de scurgere a acestora,
respectiv a structurii logice. Dacă la începuturile circuitelor digitale de tip CMOS
consumul static era neglijabil comparativ cu consumul dinamic, actualmente situația s-a
schimbat.
Consumul static a crescut în mod continuu, majorându-se cu 3-5 ordine de mărime
la fiecare nouă generație, implementată într-o nouă tehnologie. În cazul lipsei
controlului acestui consum, ajungându-se actualmente la un consum static ce este în
jurul valorii sau chiar depășind valoarea de 50% din întreaga putere consumată.

Figura 3.7. Consumul static versus consumul dinamic în diferite tehnologii

Diferitele companii (precum Texas Instruments, dar nu numai) urmăresc atât


reducerea consumurilor statice dar și a acelora dinamice printr-o abordare holistică a
problemei implicând atât nivelul hardware a circuitului SoC sau microcontroler, circuitele
externe suport dar și partea software a sistemului de operare și a aplicației ce rulează pe
dispozitiv.
De exemplu, în cadrul circuitelor din familia OMAP35x consumul static al circuitului
este redus foarte mult prin utilizarea unei tehnici de tip SLM (static leakage
management).
Există doar două moduri de consum redus în care un SoC se poate găsi: standby și un

16
alt mod de consum foarte redus aproape similar cu modul device-off (întreruperea totală a
alimentării circuitului). Diferența fundamentală între aceste moduri este că în primul mod
de lucru, standby (mod ce poate fi inițiat și de utilizatorul sistemului), SoC-ul sau
microcontrolerul își păstrează starea internă în timp ce în cel de al doilea mod de lucru
starea internă este salvată iar blocurile constitutive ale circuitului nu mai sunt alimentate.
Ulterior intrării în una din aceste două stări, la repornirea dispozitivului, a SoC-ul, acesta
va continua din starea anterioară intrării în modul standby sau device-off.
Din ambele stări SoC își revine mult mai repede cu timpi de latență mult mai mici,
comparativ cu o nouă repornire a întregului sistem, în special datorită existenței și
respectiv a memorării stării anterioare – de exemplu, un sistem de operare (SO) nu mai
este necesar să-și reînceapă procesul de boot-are el continuându-și execuția din starea
anterioară intrării în unul din cele două moduri.
Există mai multe moduri prin care se încearcă diminuarea consumurilor. Într-o primă
abordare în cadrul familiei CC2650 SimpleLink prin utilizarea mai multor procesoare
specifice fiecare unei anumite activități se urmărește îndeplinirea acestui deziderat – de
scădere a consumurilor.
În cadrul familiei OMAP35x compania Texas Instruments a realizat diminuarea
consumurilor dinamice prin trei mecanisme distincte (mecanisme reunite sub numele
generic SmartReflex™ 2):
1. Dynamic voltage and frequency scaling (DVFS) – schimbarea frecvenței de
lucru și a tensiunii de alimentare funcție de necesitățile practice ale aplicațiilor;
2. Adaptive voltage scaling (AVS) – printr-o adaptare dinamică a tensiunii de
alimentare în conformitate cu caracterisiticile constructive ale SoC-ului;
3. Dynamic power switching (DPS) – analizează funcționarea diferitelor blocuri
constitutive ale SoC sau microcontrolerului iar în momentul în care acestea și-
au finalizat activitatea sunt trecute în mod automat într-un mod de consum
redus.

Diminuarea consumurilor dinamice prin existența mai multor procesoare


diferite

În Figura 3.8 se preezintă la nivel conceptual schema internă a microcontrolerului


CC2650 SimpleLink Multistandard Wireless.
Observăm existența mai multor procesoare care lucrează în segmente de timp distincte
pentru a reduce consumul de energie global al sistemului cât mai mult posibil.
De exemplu CC2650 are trei astfel de procesoare. Primul dintre ele este un Cortex-M0
dedicat gestionării tuturor sarcinilor radio Bluetooth de nivel scăzut pentru sistem. Acest
ARM Cortex M0 (CM0) este responsabil atât pentru interfața cu partea de radiofrecvență,
cât și pentru traducerea instrucțiunilor complexe de la ARM Cortex-M3 (CM3) în biți
care sunt transmiși pe calea aerului prin modemul radio. Acest lucru elimină o parte din
sarcinile CPU-ul principal. Pentru protocolul Bluetooth Low Energy, CM0
implementează stratul PHY (Physical Layer) al stivei de protocol. De multe ori, CM0 este
capabil să funcționeze în mod autonom, ceea ce eliberează CM3 de această sarcină și
permite rularea protocoalelor de nivel superior și procesarea layer-urilor de
aplicație. CM3 comunică cu CM0 printr-o interfață hardware numită doorbell.

17
Figura 3.8. Schema bloc a microcontrolerului CC2650 SimpleLink Multistandard
Wireless

Cel de al doilea procesor este un ARM Cortex-M3 (CM3) care este procesorul
principal al platformei SimpleLink. CM3 lucrează la o viteză maximă de 48 MHz la care
consumă mai puțin de 3 mA [Monnier, 2015]. Acest procesor oferă toată performanța
necesară pentru a servi ca MCU autonom, care poate gestiona în mod inteligent un sistem
bazat pe senzori. CM3 este proiectat să ruleze stack-ul protocolului BLE de la stratul de
legătură până la stratul aplicației utilizatorului. Stratul de legătură interfațează cu nucleul
radio printr-un modul software numit driverul RF, care se află deasupra RF doorbell.
Driverul RF rulează pe CM3 și acționează ca o interfață radio pentru CC2650 și
gestionează, de asemenea, domeniile de putere ale nucleului radio. Deasupra driverului
RF este setul de protocoale TI Bluetooth low energy (BLE). Cortex-M3 oferă o mare
putere de procesare pentru a gestiona aplicația și stiva de protocoale Bluetooth LE la nivel
înalt și este extrem de eficientă din punct de vedere energetic, cu o putere de procesare de
48 MIPS.
Controller-ul de senzori (SCE – sensor controller engine) este un MCU integrat pe 16
biți, cu o putere atât de calcul cât și consumată extrem de scăzută. Acesta se ocupă de
monitorizarea rapidă și eficientă a senzorilor. Acesta este conceput pentru a oferi doar
nivelul necesar de putere de procesare pentru eșantionarea datelor și luarea deciziilor
simple în legătură cu informația preluată de la senzorilor. În plus, memoria este limitată și

18
nu are periferice externe. SCE a fost proiectat să lucreze autonom de restul sistemului care
este configurat într-un mod de lucru specific și necesar pentru reducerea consumului de
energie. Acest lucru îl face extrem de eficient pentru sarcini cum ar fi verificarea regulată
a ieșirii unui senzor și determinarea producerii unui anumit eveniment. El execută toate
sarcinile legate de senzori și evită cât mai mult să trezească procesorul principal când
acest lucru nu este necesar.

Figura 3.9. Consumul platformei CC2650 SimpleLink într-o aplicație de monitorizare a


ritmului cadiac

Dar să analizăm o analiză practică pentru a înțelege mai bine mecanismul utilizat în
reducerea puterii globale consumate de un sistem ce are drept nucleu un astfel de
microcontroler compus din trei procesoare distincte.

Studiu de caz: Într-un sistem de monitorizare a activității inimii, pentru determinarea


corectă a ritmului cardiac, este necesară achiziționarea de 10 ori pe secundă a
activității acestuia. Pornirea întregului sistem, dintr-un mod de economisire a
energiei, de 10 ori pe secundă ar fi total ineficient din punct de vedere energetic.
Cu ajutorul platformei CC2650 SimpleLink, microcontrolerul SCE va
achiziționa semnalul cardiac, iar la fiecare a zecea achiziție va “trezi” procesorul
principal (ARM Cortex-M3) pentru procesări suplimentare și transmiterea mai
departe a acestei informații.
În acest mod cele 10 achiziții se realizează la un consum mediu mai mic de 3
uA. La acest consum se adaugă, doar atunci când sunt active, consumul mediu al
procesorului principal (CM3) de 3 mA și consumul mediu al părții de
emisie/recepție de 6mA. Consumul în stadby al procesorului CM3 este de 1 uA,
consum care permite reținerea tutror datelor în memoria SRAM (20 Kb), păstrarea
stării tutror datelor din regiștrii procesorului și funcționarea RTC-ului (real-time
clock).
În Figura 3.9 se prezintă variația în timp a consumului platformei CC2650
SimpleLink defalcat pe consumul mediu de stadby, emisie, recepție și funcționare

19
normală a procesorului ARM Cortex-M3 și a microcontrolerului SCE.

Figura 3.10. Consumul platformei CC2650 SimpleLink într-o simplă interogare


prin intermediul protocolului Bluetooth

Dacă am analiza consumul procesorului ARM Cortex-M3 și a modulelor de


recepție și emisie la o simplă interogare prin intermediul protocolului Bluetooth
am obține un grafic al consumului de curent similar cu cel din Figura 3.10.
Regiunile prezentate în Figura 3.10 reprezintă:
1. Denumită Wake up & pre-processing – durează 1165 us și înglobează
ieșirea din standby a CM3 și intrarea în regimul normal de funcționare,
ieșirea și intrarea în regim normal de funcționare a RTOS, preluarea și
procesarea datelor de la SCE și configurarea modulului radio. Curentul
mediu consumat în această etapă este de 3.22 mA.
2. Pregătirea începerii procesului de comunicare: modulul radio este pornit și
setat pe recepție. Durata acestui process este de 132 us iar consumul mediu
de 3.99 mA.
3. Recepționarea (RX) – modulul rario începe procesul de recepționare a
datelor, primind pachetele de date de la master. Acest interval de timp este
dependent în principal de master și de mecanismele de tip Scan Request
sau Connection Request. Consumul mediu este de 6.48 mA.
4. Tranziția de la RX la TX – durează în jur de 149 us, iar consumul mediu
este de 5.49 mA.
5. Transmisia datelor (TX) – în acest moment se transmit pachetele de date
către master în corelație direct cu cerințele acestuia pe unul din cele 37 de
canale asociate transmisiei prin Bluetooth. Intervalul de timp în care
sistemul stă în această stare este dependent de lungimea pachetelor de date
ce trebuiesc transmise. Consumul mediu este de 7.66 mA.
6. Etapa de post-procesare și intrare în standby – stiva de protocoale
Bluetooth LE finalizează operațiile necesare, modulul radio este închis și
sistemul intră în standby. Această etapă durează în jur de 775 us, iar
consumul mediu este 2.59 mA.

20
Dynamic voltage and frequency scaling (DVFS)
Scalarea dinamică a tensiunii și frecvenței

În modul de lucru Dynamic voltage and frequency scaling (DVFS) o componentă


software, ce rulează pe SoC, determină schimbarea frecvențelor de lucru a diferitelor părți
componente ale SoC-ului funcție de necesitățile practice ale celorlalte componente ce
rulează pe sistem. Aceasta este o tehnică clasică de optimizare a consumului aplicată și
utilizată pe larg în cadrul dispozitivelor portabile (laptop-uri, telefoane mobile etc.). Ideea
fundamentală a tehnicii DVFS este aceea de a defini un număr de puncte de operare OPP
(Operating Power Points) în care SoC va funcționa cu certitudine, iar ulterior funcție de
încărcarea procesorului sau a procesoarelor interne o componentă software a SO, care
analizează în mod continuu încărcarea procesorului sau a procesoarelor, va comunica cu
circuitele regulatoare de tensiune și cu cele care generează semnalele de tact astfel încât
SoC să opereze în unul din aceste puncte. În noul mod de lucru – caracrterizat de o nouă
tensiune de alimentare și frecvență de lucru, procesorul sau procesoarele încă mai sunt
capabile să răspundă cerințelor de încărcare computațională a aplicațiilor. Deoarece
consumul este direct proporțional cu tensiunea de alimentare (P = V2 / R) și cu frecvența
de lucru a sistemului (cu numărul de treceri prin regiunea liniară a porții) acesta este
influențat de acești doi parametrii.
Tabelul 3.4. Punctele de funcționare ale procesorului ARM și a DSP-ului pentru SOC-ul
OMAP3530
Frecvența µP ARM Frecvența DSP-ului VDD_MPU_IVA
OPP
[MHz] [MHz] [V]
6 720 520 1.4
5 650 430 1.35
4 550 400 1.27
3 500 360 1.2
2 250 180 1
1 125 90 0.95

În cadrul familiei OMAP35x se poate varia simultan tensiunea procesorului ARM și a


DSP-ului (VDD_MPU_IVA) iar în mod independent a sistemelor ce interconectează cele
două procesoare și, simultan, a perifericelor (VDD_CORE), [Musah, 2008]. Prin aceste
două linii de alimentare se influențează în jur de 75-80% din consumul unui circuit SoC
din cadrul familiei OMAP35x [Musah, 2008]. Punctele de operare în cadrul acestei
familii sunt cele prezentate în Tabelul 3.4 și Tabelul 3.5.

Tabelul 3.5. Punctele de funcționare ale sistemelor periferice interne SoC


OPP L3 [MHz] VDD_CORE [V]
3 166 1.15
2 100 1
1 41.5 0.95

Cu toate că un SoC de tipul OMAP3530 poate rula la o frecvență maximă de 720

21
MHz, nu întotdeauna această frecvență este necesară. Pentru o aplicație specifică13 ce
implică numai o anumită încărcare a procesorului ARM frecvența maximă necesară poate
fi de exemplu de numai 500 MHz sau chiar mai puțin14.

Figura 3.11. Două caracteristici tehnologice diferite a două dispozitive de tip SoC a unui
dispozitiv rapid și a unuia lent

Adaptive voltage scaling (AVS)


Scalarea adaptivă a tensiunii

Tehnica de adaptare dinamică a tensiunii de alimentare, AVS, atunci când temperatura


SoC și încărcarea procesorului sau a procesoarelor componente permite, este cunoscută
sub denumirea tehnică de SmartReflex™. Dar, această denumire comercială,
SmartReflex™, este utilizată în mod global pentru a descrie toate cele trei tehnici

13
De exemplu, pentru o achiziție simultană de sunet (captură sunet și codare eșantioane: „AACe+ at 48
kHz and 32k bps stereo”) și imagine (capturat, codat “H.264 VGA resolution at 20 frames/sec, 2.4
Mbps”), urmată de stocarea acestor fluxuri de date și afișarea simultană a informației video, practica a
demonstrat că este necesară o frecvență a procesorului ARM de 500 MHz, în timp ce DSP rulează la o
frecvență de 360 MHz [jjj]. Din Tabelul 3.4 și Tabelul 3.5 rezultând tensiuni de 1.2 V și de 1.15 V
pentru procesoare și periferice. Consumul global obținut a fost de 540 mW excluzând consumul
circuitelor periferice [jjj].
14
Pentru decodarea unei melodii în format MP3 sunt necesari doar 22 mW, [jjj] – consum obținut în
aceleași condiții ca mai sus (excluzând consumul circuitelor periferice). Pentru îndeplinirea acestei
sarcini procesorul ARM a rulat inițial la o frecvență de 125 MHz setând un canal DMA să achiziționeze
datele de la un periferic extern pe care apoi să le depoziteze în memoria SDRAM, ulterior procesorul
ARM a trecut în sleep mode. Procesorul IVA (DSP-ul ce rulează la o frecvență de 90 MHz) decodează
fișierul MP3 (44.1 kHz, 128k bps stereo) și trimite datele într-o zonă de memorie SDRAM. De aici un
DMA intern IVA trimite datele către un codec harware pentru a fi redate. În momentul în care IVA nu
procesează date trece, la rândul lui, într-un mod de consum redus.

22
prezentate în mod succint anterior.
Această tehnică are la bază un fenomen existent în cadrul liniilor tehnologice de
realizare a circuitelor SoC. Astfel, datorită dispersiei tehnologice vor exista întotdeauna
dispozitive performante (cunoscute sub diferite nume de jargon de tipul: “hot”, “strong”
sau “fast”) care pentru o anumită frecvență specifică de lucru vor necesita o tensiune
mai scăzută de lucru în timp ce alte dispozitive (“cold”, “weak” sau “slow”) pentru
aceeași frecvență de lucru vor necesita o tensiune de alimentare mai mare.
În mod clasic producătorii circuitelor, pentru a fi siguri că acestea funcționează la o
frecvență nominală de, să zicem, 125 MHz setează tensiunea de alimentare a tuturor
dispozitivelor la o valoare de 0.95 V (tensiunea V1) astfel încât chiar și cele mai “slabe”
dispozitive (caracteristica „Cold Device” din Figura 3.11) cu certitudine vor fi capabile să
lucreze la această frecvență, în timp ce dispozitivele cele mai performante ar putea
teoretic să lucreze la o frecvență de peste 200 MHz dar vor lucra doar la 125 MHz. Prin
intermediul tehnicii adaptive SmartReflex™ dacă un dispozitiv performant (caracteristica
“Hot Devices” din Figura 3.11) este introdus într-un astfel de sistem alimentat la 0.95 V
datorită mecanismului de reglare intern al SoC tensiune exterioară va fi scăzută automat la
0.85 V sau chiar 0.83 V (tensiunea V2) tensiuni la care dispozitivul încă poate să lucreze
fără nici o problemă în schimb consumul lui se reduce în mod corespunzător.
Acest mecanism de reglare este independent de utilizatorul sistemului și caută să
optimizeze în mod dinamic tensiunea funcție de dispersia tehnologică, temperatură și
funcție de îmbătrânirea inerentă a siliciului. Mecanismul software DVFS alege un anumit
punct de funcționare pentru SoC conform încărcării computaționale în timp ce
mecanismul hardware AVS va regla incremental tensiunea exterioară până în momentul
în care procesorul mai este încă capabil să funcționeze, dar la tensiunea minimă.
Mecanismele AVS și DVFS sunt complementare și sinergice căutând să optimizeze
consumul SoC în conformitate cu necesitățile computaționale momentane.
Din cauza mecanismelor existente de control continuu a tensiunii externe și a
frecvenței de operare (AVS și DVFS), SoC din familia OMAP35x necesită existența unor
circuite analogice suport externe (regulatoarele de tensiune) cu care trebuie să comunice
continuu prin intermediul unui bus I2C. Soluția completă pentru familia OMAP35x este
dată de circuitul suport TPS65950. Alte circuite cum sunt TPS65930 și TPS65920 pot fi
de asemenea utilizate dar nu oferă flexibilitatea circuitului TPS65950.

Dynamic power switching (DPS)


Comutarea dinamică a puterii

Tehnica Dynamic power switching (DPS) analizează în mod continuu funcționarea


diferitelor blocuri constitutive ale SoC. În momentul în care un anumit bloc și-a finalizat
activitatea, ne mai fiind necesar în continuare este trecut în mod automat într-un mod de
consum redus. Utilizarea acestei tehnici implică existența unei topologii insulare a
circuitului și tăierea alimentării diferitelor blocuri individuale în parte. Variabilitatea
consumului în momentul utilizării mecanismului DPS și atunci când acesta nu este
prezent în cadrului unui SoC se prezintă în Figura 3.12.
Necesitatea utilizării acestui mecanism devine evidentă atunci când transferăm datele
(de exemplu o imagine) prin DMA sau EDMA și o unitate de tip CPU trebuie să aștepte
sosirea întregului pachet de date, atunci când se așteaptă ca acceleratorul grafic să termine

23
de prelucrat datele sau chiar atunci când acceleratorul grafic nu este utilizat și poate fi
închis. Avantajul acestei tehnici de reducere a consumului stă în faptul că controlul
consumului diferitelor blocuri funcționale este transparent utilizatorului fiind realizat
independent de controlul acestuia.

Figura 3.12. Variația consumului fără și cu mecanismul Dynamic power switching

24
3.4. Arhitecturi CISC şi RISC

Două abordări arhitectural au influențat profund lumea sistemelor de calcul: CISC


(complex instruction set computers) și RISC (reduced instruction set computers).
În anii `70 o dată cu scăderea continuă a prețului părții hardware a sistemelor de calul
și cu creșterea prețurilor componentelor software s-a lansat ideea mutării complexității
de la nivelul aplicațiilor software către nivelul hardware a sistemului – ideea
fundamentală ce stă în spatele arhitecturii de tip CISC. În acest mod un programator nu
mai era obligat să scrie o anumită structură repetitivă de comenzi de fiecare dată când era
necesar, deoarece această structură era implementată la nivelul hardware a CPU printr-o
singură instrucțiune mașină complexă ce executa toți acești pași. Prin această abordare
setul de instrucțiuni al CPU-ului era foarte puternic și se apropia de cel al limbajelor de
nivel înalt. Pentru ca aceste instrucțiuni să fie executate era necesară existența unui
microcod (stocat în interiorul CPU-ului într-o memorie de tip ROM, vezi Figura 3.29 și
Figura 3.30), a unor subrutine ce erau executate și asociate cu fiecare instrucțiune a CPU-
ului. Aceste instrucțiuni complexe erau anterior decodate și ulterior microcodul asociat
fiecărei instrucțiuni complexe era executat.
Un deziderat imprtant în acest domeniu a fost acela al creșterii vitezei de execuție a
CPU-ului. Pentru atingerea acestui obiectiv s-a urmărit identificarea acelui set de
instrucțiuni cel mai utilizat și ulterior optimizarea acestuia, în vederea obținerii vitezei
maxime de execuție, chiar dacă restul de instrucțiuni se executau puțin mai greu. În final
s-a observat:
1. că în medie într-un CPU 20% din instrucțiuni execută 80% din întregul program
și, în plus,
2. utilizându-se doar o colecție de instrucțiuni simple același program se poate
executa mai repede decât utilizând un set complex de instrucțiuni.
Aceste două observații au determinat proiectarea și implementarea unor arhitecturi de tip
RISC (reduced instruction set computers) caracterizate de un set mic de instrucțiuni
simple. În final s-a ajuns la consensul ca cea mai mare parte a instrucțiunilor de tip
RISC să se execute într-un singur ciclu mașină. Astfel, multe din instrucțiunile mașină
ale unui CPU de tip RISC (deci vorbim despre instrucțiunile limbajului de asamblare al
CPU-ului) sunt chiar microinstrucțiunile unui procesor de tip CISC.
Dezvoltarea primului CPU cu arhitectură RISC a fost realizat la Berkeley în anul 1980
de o echipă condusă de profesorul David Patterson. Primul procesor de tip RISC,
cunoscut sub numele de arhitectură RISC-I, a fost finalizat în anul 1982 având un set de
doar 32 de instrucțiuni. Procesorul bazat pe arhitectura RISC-II a fost finalizat în 1983
având 39 de instrucțiuni și fiind de 3 ori mai rapid decât predecesorul sau. În acest
moment toate CPU-urile de tip RISC au la bază conceptele dezvoltate și implementate în
procesorul RISC-II.
Actualmente marea majoritate a CPU-urilor sunt de tip RISC. Astfel, arhitecturi
precum Alpha, ARC, ARM, AVR, MIPS, PA-RISC, PowerPC, SuperH și SPARC sunt de
tip RISC.
Arhitectura x86 produsă de compania Intel este una de tip CISC, în principal deoarece
setul de instrucțiuni este unul de tip complex. Cu toate acestea, intern aceste procesoare
sunt de tip RISC ce emulează arhitectura de tip CISC pentru a asigura

25
compatibilitatea cu primele modele de procesoare care erau în totalitate de tip CISC. Alte
instrucțiuni precum cele de tipul MMX15 nu au în spate nici un cod de execuție de tip
microcod, aceste instrucțiuni fiind implementate direct hardware.
De asemenea, se observă actualmente că setul de instrucțiuni RISC devine din ce în ce
mai mare, tinzând din ce în ce mai mult să fie similar cu cel de tip CISC.

Tabelul 3.6. CISC versus RISC

CISC RISC

Multe instrucțiuni Puține instrucțiuni


Instrucțiunile au lungimi variabile Instrucțiunile au lungimi fixe
Instrucțiunile se execută în intervale de
Instrucțiunile se execută într-un singur
timp variabile dependente de fiecare
ciclu mașină sau în două cicluri mașină.
instrucțiune în parte.
Foarte puține instrucțiuni accesează
memoria. Astfel:
Există multe instrucțiuni care pot accesa  Încarcă datele din memorie într-
memoria. un registru
 Stochează datele din registru în
memorie
Într-o singură instrucțiune procesorul
Nu există instrucțiuni care să realizeze
poate realiza simultan:
simultan, în aceeași instrucțiune, atât
 Citirea memoriei și
citirea cât și scrierea memoriei.
 Scrierea memoriei
Puțini regiștri și foarte specializați (de
Mulți regiștri identici care pot fi folosiți
ex. unii pot conține doar date, alții doar
în scop general.
adrese de memorie).
Există un număr limitat de metode de
adresare a memoriei:
Există foarte multe moduri de adresare a
 Imediat
memoriei.
 Indexat și
 Relativ la PC.

Utilizarea arhitecturii de tip RISC, în unele cazuri, determină îmbunătăţiri ale


performanţelor procesoarelor de până la 10 ori. Aşa, de exemplu, este cazul μC
ATtiny13A16 produs de compania Atmel™. Acesta este un microcontroler AVR pe 8 biţi

15
Acest acronim are mai multe semnificaii (semnificații diferite funcție de sursa citată): MultiMedia
eXtension, Multiple Math eXtension, or Matrix Math eXtension. Inițial acest set de instrucțiuni a fost
folosit pentru îmbunătățirea capabilităților de prelucrare a graficii 2D și 3D de către procesor.
Actualmente, o dată cu dezvoltarea explosivă a plăcilor grafice, acest set de instrucțiuni este oarecum
redundant și mai puțin folosit.
16
“The resulting architecture is more code efficient while achieving throughputs up to ten times faster than
conventional CISC microcontrollers” [ATtiny13A, 2011]

26
ce are la bază o arhitectură RISC îmbunătăţită.
În final punând în paralel caracterisitcile celor două tipuri de arhitecturi va rezulta
Tabelul 3.6.

Studiu de caz: Pentru a înțelege diferențele între seturile de instrucțiuni ale celor două
tipuri de arhitecturi studiate (CISC versus RISC) prezint mai jos schematic,
modalitatea prin care se realizează o simplă operație de înmulțire:

CISC RISC
LOAD R1, D1
LOAD R2, D2
MULT D1, D2
MULT R1, R2
STORE D1, R1

Dacă analizăm procesoarele ce au la bază arhitectura ARM Cortex-M4 observăm că


acestea au un număr mic de instrucțiuni, în jur de 200 de instrucțiuni (chiar dacă aparent
acestea sunt multe instrucțiuni, comparativ cu procesoarele de tip CISC acest număr de
instrucțiuni este mic), setul de instrucțiuni Thumb-2 are cea mai mare parte a
instrucțiunilor pe 16 biți (doar o mică parte din instrucțiuni sunt pe 32 de biți) – deci și
aici se încadrează destul de bine în familia de procesoare de tip RISC, restul condițiilor
prezentate în tabelul de mai sus sunt respectate strict.
Pentru a avea o înțelegere mai profundă a modului de lucru a procesoarelor ARM se
va prezenta în cele ce urmează cele trei moduri de adresare specifice ARM Cortex-
M3/M4.

R0 0x00000096 150 = 0x96


ROM (EEPROM)
PC 0x000012F4 0x000012F0
0x000012F4 F04F0096 MOV R0, #150
0x000012F8
0x000012FC

Figura 3.13. Modul de adresare imediată

Cel mai simplu mod de adresare este cel imediat în care o valoare constantă
(înglobată în codul instrucțiunii) este stocată într-un registu. De exemplu, dacă
considerăm instrucțiunea:
MOV R0, #150
observăm din Figura 3.13 că în momentul în care PC a ajuns la adresa 0x000012F4 se
execută înstrucțiunea iar valoarea 150, care este inglobată în codul mașină a instrucțiunii,
va fi încărată în registul R0.
Adresarea indexată (cel de al doilea mod de adresare) este similară lucrului cu
pointeri din limbajul C – constând în acesarea valorii unei varibile atunci când se știe
adresa ei, deci prin intermediul adresei variabilei respective. Analizând datele prezentate

27
în Figura 3.14 observăm că atunci când PC ajunge la adresa 0x000001F2 se va executa
instrucțiunea:
LDR R0, [R1]
Prin această instrucțiune se citește valoarea de la adresa stocată în registrul R1:
0x200005A4. Deci, în R1 trebuie să se salveze anterior o adresă a unei locații de
memorie. Ulterior această valoare (0xDA123210) de la adresa 0x200005A4 se va salva în
registrul R0.

EEPROM
0x000001F0
PC 0x000001F2 0x000001F2 6808 LDR R0, [R1]
0x000001F4
0x000001F8
R0 0xDA123210
RAM
0x200005A0 0xFA123210
R1 0x200005A4 0x200005A4 0xDA123210
0x200005A8 0xEA123210
R1 + 4 0x200005AC 0xBA123210

Figura 3.14. Modul de adresare indexat

O altă variantă a adresării indexate este următoarea (adresare indexată cu offset):


LDR R0, [R1, #4]
Unde, în mod similar ca în cazul anterior, se va salva în registrul R0 valoarea de la adresa
de memorie RAM indicată de registrul R1 la care se adună offset-ul (deplasarea) 4. În
final în registrul R0 vom avea valoarea 0xEA123210 – vezi Figura 3.14.
După cum am prezentat în ultimul exemplu, arhitectura ARM suține modul de
adresare indexat cu ofset, în care registrul care memorează adresa poate fi oricare din
regiștrii procesorului. Deci, putem utiliza inclusiv registrul R15. Registul R15 nu este
orice registru de uz general al procesorului, el este PC (Program Counter). O instrucțiune
precum cea de mai jos:
LDR R0, [R15, #80]
va încărca în registrul R0 o secvență de cod (4 octeți sau 32 de biți) de la adresa curentă în
memoria ROM (care poate de tip EEPROM sau Flash), adresa la care se găsește
instrucțiunea LDR R0, [R15, #80], la care se adaugă offset-ul 80. Acest mod de adresare
este cunoscut sub numele de adresare relativă față de PC (program counter relative
addressing). Diferența majoră între acest mod de adresare și adresarea indexată este că
aici datele se iau din memoria ROM (sau FLASH) în timp ce la adresarea indexată se iau
din memoria RAM – acesta este cel de al treilea mod de adresare.

28
3.5. Arhitecturi Von Neumann şi Harvard

Funcția principală a unui CPU este de a citi codul unei instrucțiuni, de a o decodifica,
și de a executa instrucțiunea. Pentru citirea instrucțiunilor, citirea datelor și scrierea
datelor (văzute aici ca subprocese necesare executării unei instrucțiuni) sunt necesare
anumite medii de stocare de tip RAM (random-access memory), flash sau ROM (read-
only memory).
Arhitectura von Neumann (sau arhitectură Princeton, dezvoltată pentru prima dată
pentru calculatorul ENIAC) presupune proiectarea sistemului de calcul de o așa natură
astfel încât să fie compus, la un nivel grosier de descriere, dintr-o unitate de procesare și o
memorie, vezi Figura 3.15(a). Memoria stochează atât programul ce trebuie rulat cât și
datele necesare sau generate de acest program. Această arhitectură a fost denumită după
numele matematicianului John von Neumann, cel ce a propus-o. Actualmente, această
arhitectură s-a impus ca standard. Datorită vitezei de transfer limitată a datelor între CPU
(capabil să atingă viteze foarte mari de procesare) și memoria exterioară (memorie lentă)
a fost necesară găsirea altor soluții practice. În plus, în ultimii ani viteza de lucru a
procesoarelor a crescut mult mai repede decât viteza de acces a memoriilor externe.
Aceste cauze au dus la găsirea unor tehnici software (de minimizare a numărului de
accesări a memoriei externe) sau hardware (de îmbunătățire a ratelor de transfer) de
creștere a vitezei de transfer a datelor.
Arhitectura Harvard a fost dezvoltată, ulterior arhitecturii von Neumann, în ideea
îmbunătățirii performanțelor, a vitezei de acces către informațiile stocate în memoria
externă. Conceptul fundamental al acestei arhitecturi este acela de a separa memoria
program și memoria de date. Numele acestei arhitecturi a fost dat de calculatorul Harvard
Mark I17, produs de firma IBM pentru universitatea Harvard, calculator ce implementa
pentru prima dată această arhitectură.

Adrese
Memoria de
CPU date și
program
Date

8 biți (a).

Adrese Adrese
Memoria Memoria
de date
CPU program
Date Date

8 biți 12 biți (b).

Figura 3.15. O reprezentare schematică a arhitecturilor (a). von Neumann și (b). Harvard

17
În cadrul acestui calculator instrucțiunile erau stocate pe o bandă magnetică (24 de biți) în timp ce datele
pe un suport electo-mecanic (23 de digiți).

29
În cadrul arhitecturii Harvard datorită existenței a două zone separate de memorie
viteza de transfer poate fi crescută prin accesări simultane a codului instrucțiunii (situat în
memoria program) și a datelor (plasate în memoria de date). În plus, în timp ce procesorul
execută instrucțiunea codul următoarei instrucțiuni poate fi încărcat. Un alt avantaj este
dat de posibilitatea de a avea lățimi diferite pentru date (de exemplu 8 biți) și instrucțiuni
(de exemplu 12 biți, vezi Figura 3.15(b).)18. În situația unui microcontroler pe 8 biți
caracterizat de o arhitectură von Neumann atât datele cât și instrucțiunile în mod
obligatoriu au același număr de biți.
Arhitectura Harvard modificată îmbină arhitecturile von Neumann și Harvard în
vederea îmbunătățirii performanțelor sistemului global. Sistemul global, deci vorbim
despre ce este la exteriorul procesorului, utilizează arhitectura von Neumann, vezi Figura
3.15(a), în timp ce intern procesorul are două memorii cache diferite (vezi Figura 3.17,
Figura 3.29 și Figura 3.30): pentru instrucțiuni și pentru date. Deci, arhitectura internă a
procesorului este de tip Harvard în timp cea externă este de tip von Neumann.
Dezavantajul arhitecturii Harvard este dat de existența a 2 bus-uri de date și adrese
diferite implicând existența a mai multe trasee și pini ai microprocesorului.
Foarte multe procesoare ce utilizează arhitectura ARM sunt de tip Harvard. Din
familia microcontrolerelor, familia de circuite PIC produse de compania Microchip
Technology, familia AVR produsă de compania Atmel utilizează această arhitectură și o
mare parte din procesoarele familiei ARM. De asemenea, întreaga familie de DSP-uri
(TMS320Cxxxx) produsă de firma Texas Instrument are la bază o arhitectură de tip
Harvard.

3.6. Arhitecturi de tip Very Long Instruction Word (VLIW)

Comparativ cu arhitecturile suprascalare în care fiecare instrucțiune execută o singură


operație, în CPU-urile de tipul VLIW o singură instrucțiune include mai multe operații,
numărul acestor operații este egal cu numărul unităților de execuție pe care CPU le deține.
Din acest motiv numărul biților ce codează o instrucțiune este foarte mare (de cele mai
multe ori este mai mare de 64 de biți).
În cazul DSP-urilor din familia TMS320C6000 o instrucțiune are 256 de biți. Aceste
procesoare, din această familie, au 8 unități de execuție (fiecare unitate putând procesa
date reprezentate pe 32 de biți). O instrucțiune conține 8 operații distincte și se executa
într-un singur ciclu mașină.
Tratarea interdependențelor între instrucțiuni, a hazardelor de orice natură, a salturilor
condiționate sau nu, nu sunt realizate în unitățile de procesare, ce sunt caracterizate de
arhitecturi de tipul VLIW. Toate aceste probleme sunt tratate de către compilatoare și nu
de componente hardware a CPU.
Deci, în cazul arhitecturilor VLIW compilatorul este cel care reorganizează programul
original “împachetând” într-o singură instrucţiune mai multe instrucţiuni independente.
Aceste instrucțiuni sunt alocate unităţilor de execuţie în conformitate strictă cu poziţia lor

18
Un exemplu relevant în acest sens este dat de microcontrolerele PIC pe 8 biți (produse de compania
Microchip) ce au o lățime a cuvintelor de date de 8 biți în timp ce lățimea cuvintelor instrucțiune poate
fi de 12, 14 sau 16 biți în conformitate cu subfamilia din care face parte.

30
în instrucţiunea multiplă.
Dezavantajul fundamental în utilizarea acestor procesoare este dat de paralelismul
limitat al aplicaţiilor. Acest paralelism determină ca unităţile de execuţie să nu fie ocupate
permanent – problemă care apare frecvent şi la modelul superscalar de CPU. Din acest motiv
utilitatea practică a acestor procesoare se remarcă mai ales în cazul aplicaţiilor ce prelucrează
digital diferite semnale (aplicații multimedia, imagistică medicală etc.). Un alt dezavantaj este
dat de faptul că procesorul VLIW și compilatorul asociat trebuie să fie într-o simbioză
perfectă, nemaiputând exista compatibilitatea în jos existentă în familia Intel (cod
dezvoltat pentru un procesor sau o clasă de procesoare mai „bătrân(e)” să ruleze pe
versiunile mai noi ale arhitecturii).
În implementări comerciale procesoarele VLIW sunt mai rare decât cele superscalare. De
exemplu, compania Analog Devices produce familia SHARC19 de DSP-uri bazate pe
această arhitectură, în timp ce compania Texas Instruments produce familia C6000 de
DSP-uri ce utilizează de asemenea arhitectura VLIW.
Modelele de arhitecturi superscalar şi VLIW nu sunt exclusive (numai superscalar sau
numai VLIW), în implementările reale se întâlnesc adesea procesoare hibride, ce urmăresc să
optimizeze raportul performanţă preţ. Un astfel de exemplu este arhitectura procesoarelor IA-
6420 (existentă în procesoarele de tip Itanium) de tip EPIC21, arhitectură ce a evoluat și are ca
bază arhitectura VLIW și a înglobat multe din conceptele arhitecturii de tip superscalar.

19
Super Harvard Architecture Single-Chip Computer
20
Arhitectura IA-64 a fost dezvoltată de firmele Hewlett-Packard și Intel
21
Explicitly Parallel Instruction Computing

31
3.7. Unitatea centrală de procesare

Unitatea centrală de procesare (CPU - Central Processing Unit) sau procesorul este
acea parte a unui sistem de calcul responsabilă cu execuţia instrucţiunilor unui anumit
program sau a mai multor programe ce accesează concurențial resursele existente în
sistem.
Dar, conform prezentării anterioare, CPU-ul este, de asemenea, implementarea unei
versiuni specifice a unei anumite arhitecturi.

Regiștri
Unitatea Unitatea de
de control execuție
Biți de stare
(flags)
Figura 3.16. Părțile componente ale unui CPU

Programele ce sunt rulate pe un CPU sunt scrise de un programator şi au un anumit


scop bine definit. Unitatea centrală de procesare controlează de asemenea toate acţiunile
efectuate de celelalte componente ale calculatorului, controlând deci întregul sistem.
Începând cu primul calculator de uz general ENIAC22 şi urmărind evoluţia calculatoarelor
şi a tehnicii de calcul, unitatea centrală de procesare, CPU, a cunoscut şi suferit o serie
largă de transformări şi modificări.
Anterior, CPU-urile erau realizate utilizându-se diferite circuite integrate şi
componente discrete fiind, uneori, o sumă de mai multe circuite imprimate realizate cu
aceste componente. La ora actuală microprocesoarele, ce sunt unităţi centrale de
procesare, înglobează într-un singur circuit toate aceste componente.
În acest moment, structura de bază a unităţilor centrale de procesare moderne, a
microprocesoarelor, este formată din:
 Unitatea de control – determină funcţia şi rolul fiecărei instrucţiuni, citind,
decodificând și lansând în execuție codul acestora, coordonând funcţionarea CPU.
Unitatea de control este la rândul ei formată din mai multe blocuri individuale. În
continuare le prezentăm pe cele mai importante:
 Unitatea de predicţie a salturilor (branch predictor) – încearcă să determine
ce informaţie urmează a fi utilizată de unitatea/unităţile de execuţie în
vederea aducerii acesteia în memoria internă a CPU anterior utilizării
acesteia de către unitatea de execuţie. Este considerată de multe ori bloc
distinct, vezi Figura 3.29, sau parte integrantă a unităţii de control - vezi
Figura 3.17.

22
ENIAC (Electronic Numerical Integrator And Computer) – calculator construit în 1946 ce conţinea
17.468 tuburi electronice, 7.200 diode, 1.500 relee, 70.000 rezistoare, 10.000 condensatoare, având în
jur de 5 milioane de lipituri realizate manual. ENIAC cântărea în jur de 27 de tone. Acest calculator a
fost primul construit ce avea la bază o arhitectură Von Neuman.

32
 Microcodul este un program al CPU ce este rulat în momentul execuției
fiecărei instrucțiuni mașină (o instrucțiune a codului scrisă în limbajul de
asamblare). Pentru execuția acestui cod este necesară existența unei memorii
ROM foarte rapide, vezi Figura 3.29 și Figura 3.30 și a unui bloc de
translatare, traducere a instrucțiunilor mașină în secvențe operative de
comenzi pe care circuitele hardware ale CPU să le înțeleagă și să le
proceseze. Acest microcod este utilizat de asemenea în implementarea unor
instrucțiuni complexe, compuse din mai mulți pași de execuție, fără însă a
crește în mod inutil complexitatea hardware a CPU-ului.
 Blocul de distribuţie (dispatcher) al datelor este utilizat în cazul CPU ce
conțin mai multe unități de execuție. Are rolul de a distribui datele către
unitățile de procesare corecte.
Memorie cache L1

Memorie cache L1
Instrucțiununi

ALU FPU
Unitate de

date
control

Regiştri

Memorie cache L2

Unitatea de intrare/ieșire

Figura 3.17. O schemă bloc simplificată a unei unităţi centrale de procesare

 Unitatea de procesare sau de execuție – partea principală a CPU dedicată


execuţiei instrucţiunilor efectuează toate operațiile şi calculele necesare. Ea este
partea cea mai rapidă a CPU-ului. Această unitate poate fi formată din mai multe
unităţi de execuţie individuale:
 Unitatea de calcul în virgulă mobilă (FPU – Floating Point Unit) realizează
toate operaţiile matematice în reprezentări reale, deci în virgulă mobilă
(floating point).
 Unitatea logică și aritmetică (ALU - Arithmetic and Logic Unit) realizează

33
toate calculele aritmetice (adunare, scădere, înmulțire și împărțire23),
funcțiile logice (și, sau, sau exclusiv etc.) și operații de rotire sau deplasare a
datelor. ALU operează doar în reprezentări ale tipurilor de date întregi
(integer).
 Regiștri – atunci când unitatea de execuție realizează diferitele procesări asupra
datelor are nevoie de locații de memorie din care să preia datele și în care să
salveze rezultatele, aceste locații de memorie sunt regiștrii procesorului. Ne putem
imagina acești regiștri drept variabile interne CPU-ului utilizate de acesta în
prelucrarea datelor.
În plus, fiecare procesor mai are registrul/regiștrii biților de stare (flags).
Acești biți indică apariția diferitelor evenimente în interiorul CPU, în momentul
execuției anumitor comenzi – de exemplu, un registru a luat valoarea zero (în
urma unui proces de decrementare) sau în urma unei operații matematice de
adunare valoarea rezultată este mai mare decât capacitatea unui registru de a stoca
valori.
Arhitecturile IA-32 sau IA-64 (de tip CISC) au același număr de regeșiștri, cu
aceleași funcții, singura diferență fiind dată de numărul de biți ai acestora. De
exemplu, registrul acumulator, în cadrul arhitecturii IA-64, este numit RAX și are
64 biți. Arhitectura IA-32 are și ea un registru acumulator denumit EAX, care în
cadrul arhitecturii IA-64 se regăsește în cei mai puțin semnificativi biți ai
registrului RAX (biții 0 ... 31). Deoarece la origini arhitectura Intel a pornit de la 8
biți (8008, 8080, 8085), trecând mai departe prin celebrul Intel 8086 (pe 16 biți) și,
mai mult, pentru a păstra compatibilitatea în jos, procesoarele ce au la bază
arhitectura IA-64 recunosc toți regiștri arhitecturilor precedente și toate
instrucțiunile care lucrează cu aceștia.
RAX
63 0
EAX
63 32 31 0
AX
31 16 15 0
AH AL
15 87 0

Figura 3.18. Registrul RAX al arhitecturii IA-64 și regiștri arhitecturilor


precedente

Regăsim în arhitectura IA-32 următorii regiștri de „uz genral” cu următoarele


funcții: EAX (Accumulator Register - registul acumulator utilzat pentru stocarea
operanzilor și a rezultatului final), EBX (Base Register – este utilizat pentru
stocarea adreselor de date), ECX (Counter Register – utilizat în operațiile cu
stringuri și ca registru contor în bucle), EDX (Data Register – utilizat ca pointer în
operațiile de I/O), ESI și EDI (regiștri de date sursă și destinație utilizați în variate

23
Nu toate procesoarele au unități ALU ce suportă operațiile matematice de tip înmulțire sau împărțire - în
principal datorită complexității ridicate a blocurilor ce susțin aceste funcții.

34
operații de lucru cu memoria), ESP (Stack Pointer Register – indică virful stivei)
și EBP (Stack Data Pointer Register).
Față de acești regiștri mai există o serie de regiștri dedicați lucrului cu
memoria. În general acești regiștri nu mai sunt folosiți și sunt păstrați doar pentru
compatibilitate cu momentul în care se folosea conceptul de segmentare a
memoriei. Actualmente se merge pe un sistem de paginare a memoriei. Acești
regiștri sunt: CS (Code Segment), DS (Data segment), SS (Stack Segment), ES, FS
sau GS (Extra segments).

Nume Funcție
R0 Regisru de uz general
R1 Regisru de uz general
R2 Regisru de uz general
R3 Regisru de uz general
Regiștri inferiori
R4 Regisru de uz general
R5 Regisru de uz general
R6 Regisru de uz general
R7 Regisru de uz general
R8 Regisru de uz general
R9 Regisru de uz general
R10 Regisru de uz general Regiștri superiori
R11 Regisru de uz general
R12 Regisru de uz general
R13 (MSP) R13 (PSP) Main Stack Pointer (MSP), Process Stack Pointer (PSP)
R14 Link Register (LR)
R15 Program Counter (PC)

Figura 3.19. Setul de regiștii pe 32 biți ai procesoarelor construite pe arhitectura ARM


Cortex-M3/M4

Procesoarele pe arhitectura ARM Cortex-M3/M4 au un număr de 16 regiștri pe


32 de biți (arhitectura este pe 32 de biți) – vezi Figura 3.19. Cu toate că primii 12
regiștri sunt de uz general, în momentul folosirii limbajelor de nivel înalt s-a
încetățenit obiceiul ca în momentul apelării unei funcții primul argument să fie
în R0, al doilea în R1 și așa mai departe, în cazul în care funcția întoarce un
argument acesta va fi în R0. Vârful stivei va fi indicat de conținutul din R13 – în
registul de CONTROL (neprezentat în Figura 3.19) există un bit (bitul 1) care
permite selectarea registrului care va indica stiva MSP (pentru o valoare 0) sau
PSP (pentru o valoare 1). O particularitate a arhitecturi ARM este Link Register
(LR) care memorează adresa de întoarcere din subrutine/funcții la apelarea
acestora – în acest mod se accelerează execuția programului. Registul R15

35
memorează adresa curentă din memoria program de unde se execută de către
procesor instrucțiunea în limbaj de asamblare, fiind deci PC (Program Counter).
Dacă ar fi să discutăm și să analizăm arhitectura ARM pe 64 de biți (de
ecemplu, ARMv8) acolo vom avea 31 de regiștri de uz general (denumiți X0-
X30), toți pe 64 de biți.
 Memoria cache – este o memorie foarte rapidă de mici dimensiuni în care se
stochează copii ale datelor asupra căror se lucrează în acel moment, a datelor cel
mai recent utilizate și a datelor potențiale pe care procesorul le va manipula în
scurt timp. În mod uzual memoria cache este implementată pe aceeași pastilă de
siliciu pe care se realizează și CPU-ul.
Conceptul fundamental care stă la baza funcționării memoriei de tip cache este
de a determina CPU-ul să preia și să prelucreze date doar din memoriile
cache interne (memorii rapide) și nu din memoria RAM externă (mult mai lentă).
Memoria cache își bazează funcționarea pe proprietatea de poziționarea
spațială, local grupată a zonelor de date accesate la un anumit moment de timp
de un program – un program tinde să proceseze date stocate cam în aceeași zonă.

Figura 3.20. Memoria cache unificată

Memoria cache a unui procesor poate avea una din următoarele două
organizări:
1. memorie cache unificată – aceeași memorie atât pentru date cât și pentru cod,
Figura 3.20 și
2. Memorie cache separată pentru date și instrucțiuni (vezi Figura 3.17, Figura
3.29 și Figura 3.30) – această organizare este denumită arhitectură Harvard
modificată.

36
În cazul ultimei metode de organizare a memoriei cache, specifică
procesoarelor produse de Intel (IA-32 și IA-64), dar nu umai, și prezentată în
Figura 3.17, Figura 3.29 și Figura 3.30 întâlnim următoarele tipuri de memorii:
 Memoria cache primară – numită şi memoria L1 este situată cât mai aproape
de unitatea de execuţie (fiind integrată în CPU). Această memorie este o
memorie SRAM (static RAM) de viteză (cu timpi de acces24 foarte mici) mult
mai rapidă decât memoria externă a calculatorului. Ea este împărțită în două
regiuni diferite25 (vezi Figura 3.17): memoria de date și memoria de
instrucțiuni. Necesitatea utilizării acestei memorii a fost generată în principal
de viteza de lucru foarte mare a procesorului comparativ cu ratele mici și
foarte mici de transfer a datelor cu mediile externe de stocare (memorie RAM
sau hard disk).
Circuitele suport ale memoriilor cache copie din memoria RAM externă
datele și programul necesar și caută să „ghicească” ce va fi necesar în viitorul
imediat, aducând aceste informații în memoriile corespondente (L1D sau L1P).

Regiștri Mică Mic Mare

Memoria cache 
primară 

Memoria cache 
secundară 

Memoria RAM
sistem 

Mare Mare Mic


Hard disk

Tip Capacitate Timp


Cost
memorie stocare acces
Figura 3.21. Caracterizarea diferitelor tipuri de memorii

 Memoria cache secundară sau de nivel 2 (denumită L2 Cache sau Level 2


Cache) este de asemenea o memorie SRAM, localizată pe acelaşi cip cu CPU.
L2 este o memorie tampon între procesor şi memoria externă RAM. Ea este o

24
Sinonim cu latență.
25
Această divizare a memoriei cache L1 este una din cele mai importante îmbunătăţiri a procesoarelor din
familia Pentium faţă de familia anterioară 486.

37
memorie mai rapidă decât memoria RAM externă dar este mai lentă decât
memoria cache L1 – comparativ cu aceasta timpii de latență nu mai sunt foarte
apropiați de zero.
 Bus-ul de date/adrese conectează întregul sistem la memoria externă şi la toate
celelalte componente ale calculatorului. Dacă în cazul arhitecturii IA-32 sau IA-64
avem o singură magistrală externă de adrese și una de date (arhitectură von
Neuman), în interior lucrurile se schimbă puțin deoarece avem o arhitectură
Harvard și pentru accesarea memorie cache L1 date și L2 cod vom avea totul la
dublu. În alte familii de sisteme sau în alte arhitecturi lucrurile pot sta radical
diferit.

Figura 3.22. Bus-urile de date și adrese ale DSP-urilor din familia TMS320C67xx

De exemplu, DSP-urile din familia TMS320C67x au intern un număr de 5


bus-uri iar extern 3 bus-uri (adrese plus date), vezi Figura 3.22.
Dacă analizăm procesoarele ce au la bază arhitectura ARM Cortex-M3/M4
vom găsi 3 bus-uri de tip Advanced High-performance Bus (AHB) și un bus
dedidcat conectării la periferice – Advanced Peripheral Bus (APB).
Se poate observa ușor din Figura 3.23 existența celor 4 bus-uri. Prin
intermediul bus-ului I-code vom accesa numai zona de cod a memoriei,
poziționată în cadrul arhitecturii ARM Cortex M3/M4 în regiunea
0x00000000 ... 0x1FFFFFFF – vezi Figura 3.24. Bus-ul System accesează
memoria RAM (internă și externă) și spațiul de memorie asociat perifericelor
de I/O. Aceste două bus-uri (I-Code și System) sunt cele 2 bus-uri clasice ale
arhitecturii Harward, prin care se accesează în mod independent zona de cod și
cea de date.

38
Figura 3.23. Bus-urile de date și adrese ale ARM Cortex M3/M4

8 biți

Figura 3.24. Maparea zonelor de memorie (RAM și ROM) și a perifericelor


în zona de adresare unică

Advance Peripheral Bus accesează doar zona de “memorie” 0xE0000000


... 0xE00FFFFF de 1 Mb. Partea interesantă a arhitecturii este bus-ul D-code,

39
bus dedicat în totalitate operațiilor de debug – celelate bus-uri trebuie să
satisfacă un set de reguli în corelație cu bus-ul D-code, de exemplu D-code are
o prioritate suferioară în fața lui I-code.
Toate procesoare construite pe baza arhitecturii de tip ARM Cortex M3 și
M4 au un singur spațiu de adresare pe 32 de biți. Deci, vom avea 232 locații de
memorie – adică 4 Gb spațiu adresabil, dar acest spațiu va fi adresabil doar pe
un octet. În acest spațiu este mapat ROM-ul, RAM-ul (intern și extern) și
perifericele (spațiu de I/O) fiecare din aceste zone fiind accesate doar de un
anumit bus specific.
Din Figura 3.24 observăm că fiecare adresă din spațiului de memorie este
un octet. De aici apare o altă problemă. Cum să accesezi o valoare pe 32 de biți
(sau pe 16 biți), care datorită modului de organizare a spațiului de adresare se
va găsi distribuită pe 4 octeți (sau 2 octeți) consecutivi, atâta timp cât spațiul
de adresare se poate accesa doar pe octeți individuali.

32 bits 2000000 = 0x001E8480


MSB LSB
Adresă: Data: Adresă: Data:
0x20000000 0x00 MSB 0x20000000 0x80 LSB
0x20000001 0x1E 0x20000001 0x84
0x20000002 0x84 0x20000002 0x1E
0x20000003 0x80 LSB 0x20000003 0x00 MSB
Big Endian Little Endian
Figura 3.25. Reprezentarea unui număr pe 32 de biți în formatele
Big Endian și Little Endian

16 bits 2000 = 0x07D0

Adresă: Data: Adresă: Data:


0x20000000 0x07 MSB 0x20000000 0xD0 LSB
0x20000001 0xD0 LSB 0x20000001 0x07 MSB
Big Endian Little Endian

Figura 3.26. Reprezentarea unui număr pe 16 de biți în formatele


Big Endian și Little Endian

Presupunând că numărul pe care dorim să îl stocăm în memoria RAM (din


unul din regiștri procesorului) este 2000000, cei 4 octeți care îl compun (0x00,
0x1E, 0x84 și 0x80) pot fi salvați în 4 locații de memorie diferite în două
modalități distincte. În prima abordare, cel mai semnificativ octet (MSB –
Most Significant Byte) va fi stocat în adresa de valoare minimă, din cele patru,
în timp cel mai puțin semnificativ octet (LSB – Least Significant Byte) va fi
stocat la adresa de valoare maximă, 0x20000003 în cazul nostru – vezi Figura
3.25 partea din stânga. Acest mod de stocare poartă numele de Big Endian.

40
Procesoarele din familia Cortex M nu stochează datele în acest mod, ele
utilizează modul Little Endian, în care la adresa de valoare minimă se
stochează cel mai puțin semnificativ octet, iar la adresa de valoare maximă se
stochează MSB – vezi Figura 3.25 partea din dreapta.

Șirul ACII “Dan” = 0x44, 0x61, 0x6E, 0x00

Address: Data:
0x20000000 0x44 ‘D’
0x20000001 0x61 ‘a’
0x20000002 0x6E ‘n’
0x20000003 0x00 null
Big Endian și Little Endian

Figura 3.27. Stocarea unui șir de caractere în formatele


Big Endian și Little Endian

Figura 3.28. Arhitectura procesorului ARM7TDMI

În mod similar au loc procesele atunci când vom stoca în memorie numere
pe 16 biți - pentru exemplificare a se vedea Figura 3.26. Singura excepție de
la această regulă este dată de stocarea șirurilor de caractere care în ambele
reprezentări (Big Endian și Little Endian) sunt stocate în memorie sub aceeași

41
formă – a se vedea Figura 3.27.
De asemenea, toate procesoarele produse de compania Intel sunt de tipul
little endian.

Figura 3.29. Schema bloc a unui procesor Pentium (generația a cincea) [Pentium, 2011]

Studiu de caz: Am precizat anterior că necesitatea introducerii memoriilor cache interne


a fost generată de discrepanța existentă între vitezele de prelucrare/preluare a CPU
față de ratele de transfer a mediilor de stocare externe. Pentru a face doar o simplă
comparație să analizăm următoarele date:
 Dacă luăm în calcul un HDD foarte rapid, de exemplu SATA revision 3.0
vom obține o viteză maximă de transfer de 600 Mocteți/s – dar, atenție, viteza
reală este semnificativ mai mică la scrierea/accesarea fişierelor de mici
dimensuni, în multe din situațiile reale nedepășind 140 Mocteți/s. Chiar în
cazul utilizării unui SSD extreme de rapid conectat la o interfață PCI-E 3.0
putem vorbi de viteze de 2150 Mocteți/s la operaţiile de citire a datelor şi de
până la 1550 Mocteți/s la cele de scriere – model SSD SM951 produs de
Samsung.
 Pentru o memorie DDR4, care este accesată pe 64 biți (vezi Figura 3.29) vom

42
obține o viteză de transfer de 17 GB/s (DDR4 2133), 19.2 GB/s (DDR4
2400), 21.3 GB/s (DDR4 2666) sau 25.6 GB/s26 (DDR4 320027). În schimb,
problema fundamentală a memoriilor nu este rata de transfer ci timpul de
latență (cu cât o memorie întârzie prezentarea datelor atunci când acestea sunt
cerute). De exemplu, dacă o memorie are CL (CAS Latency) de 3, acest fapt
semnifică că memoria va întoarce data cerută după 3 cicli (impulsuri de tact)
față de momentul cererii. În tot acest interval de timp CPU va trebui să
aștepte. Deci teoretic ar trebui să împărțim viteza de transfer la 3. Cu toate
acestea performanțele nu scad atât de dramatic deoarece memoriile lucrează în
mod normal într-un mod de lucru de tip „burst mode” (după prima accesare
care este întârziată cu CL datele vor fi preluate la fiecare puls de tact, dacă se
accesează locații consecutive din aceeași pagină);

Figura 3.30. Schema bloc simplificată a unui procesor Pentium

26
25.6 [GB/s] = 3200 [MT/s]  64 (nr. de biți transferați) / 8 (nr. de biți dintr-un octet)
27
Acești 3200 reprezintă rata de transfer măsurată în MT/s (megatransfers per second) – numărul de
operații de transfer ce au loc în fiecare secundă

43
 Un CPU ce rulează intern la 3.7 GHz (de exemplu un i7-8700K) și care are
bus-urile interne pe 64 de biți va avea o viteză de transfer a datelor de 29.6
Gocteți/s. Dar, dacă luăm în calcul că bus-urile de date între memoria L2 și
memoria L1 pot fi de 128 (ca la un AMD standard) sau de 256 (ca la un Intel)
biți atunci vom obține viteze mult mai mari: 59.2 Gocteți/s sau 118.4 Gocteți/s
[Intel, 2013].

Din punct de vedere a funcționării, în momentul în care CPU va dori să preia o nouă
instrucţiune acesta o va căuta pentru prima dată în memoria cache de instrucţiuni L1P,
dacă o găsește („memory hit”) o va prelua și va lucra cu ea. În schimb, dacă nu o găseşte
(„memory miss”), va continua căutarea instrucţiunii respective în memoria L2, dacă nici
aici nu există, în final, o va prelua direct din memoria RAM a sistemului. Având
instrucţiunea căutată, blocul decodor de instrucţiuni va determina imediat paşii necesari
executării instrucţiunii respective. Utilizând microcodul asociat fiecărei instrucţiuni
unitatea de control va determina toţi paşii intermediari necesari executării instrucţiunii şi
va lansa secvenţele de comenzi individuale necesare finalizării acestor instrucţiuni către
unităţile de execuţie.
În memoria cache L1D se stochează datele ce sunt procesate. Dacă citirea datelor,
incluzând toate mecanismele implicate, nu ridică nici o problemă conceptuală, în schimb
scrierea acestora în memoria cache și sincronizarea memoriei L1D cu memoria RAM a
sistemului se poate realiza prin două mecanisme diferite: write-back și write-through. Prin
intermediul metodei write-back28 modificările făcute în memoria cache nu sunt copiate
imediat în memoria RAM externă. Scrierea datelor este realizată în memoria RAM doar
atunci când este absolută nevoie, de exemplu când zona ocupată și procesată anterior este
necesară pentru stocarea unor noi date. În contrast în cadrul metodei write-through o dată
cu scrierea datelor în memoria cache sunt scrise simultan și în memoria RAM. Datorită
mecanismelor ce guvernează aceste metode, de cele mai multe ori metoda write-back
determină obținerea unor performanțe mai bune (a unei viteze mai mari) decât atunci când
se utilizează metoda write-through. Aceste performanțe sunt superioare datorită
minimizării numărului de scrieri în memoria RAM exterioară a sistemului.
În cadrul unităţilor de procesare moderne (CPU) sunt înglobate mai multe
unităţi de execuţie. Aceste unităţi de execuţie sunt proiectate pentru a rula în paralel, iar
o astfel de arhitectură poartă numele de arhitectură suprascalară29. De exemplu, în
cazul utilizării a patru unităţi de execuţie, teoretic, se pot executa până la patru
instrucţiuni în paralel obţinându-se astfel o creştere cu 4 a vitezei de lucru. În mod ideal,
un CPU cu n unităţi de execuție ar trebui sa ofere o viteza de prelucrare de n ori mai mare
decât un sistem cu o singură unitate de execuție. Dar, în realitate, consumul suplimentar
de resurse necesar pentru coordonarea activității unităților de execuție și soluționarea
competiției pentru resursele comune (date, magistrale de informații, rezolvată oarecum
parțial prin utilizarea pipeline-urilor) nu permite atingerea acestui deziderat. În plus și
programul rulat trebuie dezvoltat de o așa natură astfel încât să permită o rulare în paralel

28
Această metodă a fost folosită în clasa procesoarelor Intel începând cu procesorul 80486.
29
De exemplu, procesoarele de tip Pentium 4 (procesor de generația a șaptea) au cinci unităţi de execuţie
care lucrează în paralel

44
a mai multor instrucțiuni.
O altă caracteristică a acestor unități de execuție este dată de faptul că acestea nu sunt
identice, ele sunt specializate pe un anumit gen de operații (de exemplu, numere întregi –
unitatea de execuţie ALU –, sau numere în virgulă mobilă – unitatea de execuţie FPU).
În final, putem concluziona că prin intermediul unei astfel de abordări, similară cu cea
anterioară, se obține o creștere substanțială a performanțelor globale ale
microprocesorului. De exemplu, în Figura 3.29 se observă că o anumită clasă de
procesoare Pentium au două unităţi ALU şi o singură unitate FPU.
Aceeași schema bloc a unui procesor Pentium prezentată în Figura 3.29 este
reprezentată puţin mai schematizat în Figura 3.30.

45
3.8. Utilizarea tehnicilor de tip pipeline în procesoare

Introducere

Tehnicile de pipelining sunt metode de creștere a performantei globale a unui


procesor prin utilizarea paralelismului existent la nivelul fiecărei instrucțiuni, prin
suprapunerea proceselor de execuție a instrucțiunilor. În acest mod se obține o
creștere a vitezei de execuție a instrucțiunilor fără a crește viteza de lucru a procesorului.
Tehnica de tip pipelining, de altfel la fel ca și utilizarea arhitecturilor scalare, este o
tehnică transparentă programatorului, acesta ne fiind obligat să țină cont de acestea în
scrierea programelor. Cu toate acestea, o scriere a programelor, ce are în vedere existența
acestor tehnologii înglobate în procesor, poate determina o maximizare a performanțelor
ce se obțin cu aceste CPU-uri.

Etapele execuţiei unei instrucţiuni

O instrucţiune, în limbaj de asamblare, constă dintr-o secvenţă de octeţi (1-4 octeţi).


Dintre acești octeţi ai unei instrucţiuni, cel puţin unul reprezintă codul de instrucţiune
(care spune ce operații realizează respectiva instrucțiune) iar restul sunt utilizaţi pentru a
coda regiştrii ce stochează date, datele sau adresele datelor ce vor fi prelucrate şi/sau locul
unde vor fi stocate.
O instrucţiune are o durată de execuție cunoscută sub numele de ciclu instrucțiune.
Funcţie de tipul procesorului, de complexitatea instrucțiunii durata unui ciclu de
instrucţiune poate varia sau, altfel spus, o instrucțiune poate fi compusă dintr-un număr
variabil de cicluri mașină.
Pentru utilizarea tehnologiei de tip pipeline un ciclu instrucţiune este descompus într-
un număr fix de segmente (stage). Fiecare segment reprezentând un subproces elementar
în care se descompune procesul de execuţie a unei instrucţiuni.
În general, execuţia oricărei instrucţiunii se poate descompune în următoarele
subprocese sau segmente mai simple:
1. IF - instruction fetch (citirea codului fiecărei instrucţiuni, extragerea
instrucţiunii din memoria L1P, L2, L3 sau RAM),
2. ID - instruction decoding (înţelegerea, decodificarea codului instrucţiunii – în
urma acestui proces CPU va şti exact ce are de făcut),
3. OF - operand fetch (extragerea operandului, preluarea acestuia şi stocare în unul
din regiştri interni ai CPU),
4. EX - execution (executia operației prin folosirea unuia din unitățile de execuţie
de care CPU dispune),
5. WB - write-back (rezultatul obținut în urma procesării de către una din unităţile
de execuţie este salvat în memorie).

Tehnica utilizării pipline-urilor

În continuare vom presupune că durata unui segment este egală cu perioada ceasului CPU
– presupunere adevărată în cea mai mare parte a situațiilor. Din Figura 3.31 se observă că
în cazul unui CPU ce nu utilizează tehnica de tip pipeline pentru execuția unei secvențe de

46
3 instrucţiuni sunt necesare 15 tacturi. În general acest mod de lucru este specific
microcontrolerelor de generaţii mai vechi care datorită simplității constructive
(generatoare de costuri reduse) nu au înglobate în CPU tehnici de tip pipelining. Cu toate
acestea microcontrolerele ce fac parte din generaţii recente înglobează intern tehnica de
tip pipeline30.

Instr1 IF ID OF EX WB
Instr2 IF ID OF EX WB
Instr3 IF ID OF EX WB

Tact 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Figura 3.31. Modalitatea de execuţie a trei instrucţiuni pe un CPU


ce nu utilizează tehnica de tip pipelining

Una dintre metodele cele mai directe de a evita execuţia secvenţială prezentată în
Figura 3.31 este aceea de a începe unele din subprocesele necesare execuţiei unei
instrucţiuni (de exemplu IF şi ID) înainte ca instrucţiunea anterioară (care se află, de
exemplu, în etapa OF) să-şi fi terminat execuţia. O astfel de acţiune se poate realiza în
principal deoarece circuitele de procesare interne CPU ce se ocupă diferitele segmente ale
unei instrucţiuni sunt independente, unele de altele, şi nu necesită accesul la aceleaşi
resurse – de cele mai multe ori.
Procesa

Procesa

Procesa
Intrare
Circuit

Circuit

Circuit

re WB
Latch
Latch

Latch
re ID
re IF

...

Semnal
clock Iesire

Figura 3.32. Structura unui pipeline ce corespunde etapelor de descompunere a unei


instrucţiuni anterior prezentate

În Figura 3.32 se prezintă structura unui pipeline static care corespunde


descompunerii anterioare a unei instrucţiuni în cele 5 subprocese.
În Figura 3.33 se prezintă funcţionarea schematică a unui pipeline ce are o adâncime
de 5 subprocese şi o singură unitate de execuţie. Din această figură se observă că cel puţin
teoretic un astfel de pipeline poate susţine execuţia unui instrucţiuni în fiecare ciclu sau
perioadă procesor.

30
De exemplu, microcontrolerele produse de Atmel şi Microchip, AVR respectiv PIC înglobează un
pipeline de două segmente (stages).

47
Starea pipeline-ului la
al 5-lea tact
Instr1 IF ID OF EX WB
Instr2 IF ID OF EX WB
Instr3 IF ID OF EX WB
Instr4 IF ID OF EX WB
Instr5 IF ID OF EX WB
Tact 1 2 3 4 5 6 7 8 9

Figura 3.33. Modalitatea de execuţie simultană a unui număr de instrucţiuni pe un CPU


ce utilizează un pipeline cu o „adâncime” de 5

Din figura anterioară și din prezentarea subproceselor în care poate fi descompusă o


instrucțiune, ar rezulta că toate procesoarele care au pipeline îl au cu o adâncime de 5.
Dar, cu toate acestea procesoarele Cortex-M3 și Cortex-M4, caracterizate de o
arhitectură Harward, au un pipeline de adâncime 3. Pentru a realiza aceasta compania
ARM a grupat diferitele etape de execuție a unei instrucțiuni, rezultând în final doar 3
etape distincte, Figura 3.34: preluare instrucțiune, decodificare instrucțiune și execuție. În
acest mod implementarea unui pipeline de adâncime 3 devine o operație foarte facilă.
Observăm de asemenea în cadrul etapei de decodare a unei instrucțiuni, etapa de
conversie a codului instrucțiunii thumb (pe 16 biți) către o instrucțiune ARM (pe 32 de
biți).

Decodare ARM
Instruction  Decompresie Citire  Scrie
ALU
Fetch Thumb → ARM Reg. Reg.
Selectare Reg.

Preluare  Decodificare (cod 
Execuție
instrucțIune instrucțiune)
(Fetch)

Figura 3.34. Gruparea diferitelor etape ale unei instrucțiuni în cadrul procesoarelor
Cortex-M3 și Cortex-M4

Pentru îmbunătăţirea în continuare a performanţelor, a vitezei de execuție, se


utilizează arhitecturi suprascalare – arhitecturi ce au mai multe unităţi de execuţie.
Pentru arhitecturile suprascalare pipeline-ul trebuie să fie de o dimensiune mult mai
mare31 comparativ cu arhitecturile subscalare. Unul din blocurile fundamentale ale unei

31
În cadrul procesoarelor produse de compania Intel, familia de procesoare Pentium 4 are implementată în
CPU o structură de tip pipeline cu 20 segmente. Noile ediții revizuite ale procesoarelor Intel cu nume de
cod Prescott (scos pe piață pe data de 1 februarie 2004), Prescott 2M (în prima parte a anului 2005,
arhitectură IA-64) și Cedar Mill (5 ianuarie 2006, cu o arhitectură IA-64) înglobează pipeline-uri cu 31
de segmente. Comparativ DSP-urile produse de compania Texas Instruments acceptă între 7 și 11

48
structuri suprascalare este blocul de distribuţie (dispatcher), înglobat în unitatea de
control, ce are rolul de a determina ce instrucţiuni pot fi executate în paralel, pe ce unități
de execuție şi care instrucțiuni nu pot fi executate în paralel.
Starea pipeline-ului la
al 5-lea tact
Instr1 IF ID OF EX WB
Instr2 IF ID OF EX WB
Instr3 IF ID OF EX WB
Instr4 IF ID OF EX WB
Instr5 IF ID OF EX WB
Instr6 IF ID OF EX WB
Instr7 IF ID OF EX WB
Instr8 IF ID OF EX WB
Instr9 IF ID OF EX WB
Instr10 IF ID OF EX WB
Tact 1 2 3 4 5 6 7 8 9

Figura 3.35. Modalitatea de execuţie simultană a unui număr de instrucţiuni pe un CPU


suprascalar ce utilizează un pipeline cu o „adâncime” de 10

Dacă două instrucţiuni pot fi executate în paralel şi sunt disponibile unităţi de execuţie
atunci acestea sunt procesate în paralel. În acest mod, teoretic, numărul de instrucţiuni ce
pot fi executate în paralel (fără a implica pipeline-uri) este egal nu numărul unităţilor de
execuţie disponibile.
Funcţie de eficienţa blocului de distribuţie a instrucţiunilor (dispatcher), a capacităţii
acestuia de a „umple” într-un mod eficient, corect şi la timp pipeline-ul, astfel încât cât
mai multe din unităţile de execuţie să fie utilizate, va rezulta un număr mai mic sau mai
mare de instrucţiuni ce pot fi executate în paralel. Pentru pipeline-ul prezentat în Figura
3.35 un număr de maximum zece instrucţiuni pe ciclu pot fi executate, avînd la dispoziţie
două unităţi de execuţie.
Latch

Latch
Latch

Figura 3.36. O posibilă structură unui pipeline dinamic

Pipeline-uri dinamice

Structura unui pipeline dinamic este prezentată în Figura 3.36. Într-un astfel de

subprocese simultan în cadrul familiei TMS320C6200 și între 7-16 subprocese în cadrul familiei de
DSP-uri TMS320C6700.

49
pipeline structura lui se poate schimba/modifica în timp ce datele îl parcurg. De exemplu,
o anumită instrucţiune are nevoie doar de subprocesele 1, 2 şi 3 în timp ce o alta are
nevoie de doar subprocesele 1 şi 3. Prin această schimbare a arhitecturii pipeline-ului se
obţine caracterul dinamic al acestuia. Dezavantajul major al acestor pipeline-uri dinamice
este dat de complexitatea circuitelor de control a structurii pipeline-ului.

Probleme ce apar în cazul CPU ce utilizează pipeline-uri

Există mai multe probleme ce pot apare în cazul utilizării tehnicilor de tip pipelining.
Aceste dificultăți în utilizarea pipeline-urilor sunt generate în principal de:
1. necesitatea furnizării rapide şi corecte de instrucțiuni la intrarea pipeline-ului
(problemă costisitoare din punctul de vedere al ariei chip-ului necesară
îndeplinirii corecte a acestui deziderat),
2. “strangularea” (bottleneck) a unui subproces datorită volumului de lucru
afectat unui segment al instrucţiunii,
3. probleme denumite generic a emiterii instrucţiunii (issuing) – deci, a
existenţei unei instrucţiunii disponibile, dar care nu poate fi executata datorită
unui anumit tip de hazard: structural, al datelor sau al controlului
programului.
Problemele de tip hazard structural sunt generate de imposibilitatea utilizării unor
anumite resurse ale CPU care sunt blocate de alte activități în derulare. De cele mai multe
ori aceste probleme sunt rezolvate prin implementarea hardware a unui număr mai mare
de unități de execuție și/sau registre.

Instr1 IF ID OF EX WB
Instr2 IF ID OF EX WB

Tact 1 2 3 4 5 6

Figura 3.37. Repartizarea în pipeline a instrucțiunilor

Hazard creat de date este generat de dependența unei instrucțiuni de o instrucțiune


anterioara, de rezultatul obținute de la această instrucțiune anterioară. De exemplu, dacă
dorim să executăm următoarea structură de cod:
Instr1: ADD R2, R3, R1 R1 = R2 + R3
Instr2: ADD R1, R4, R5 R5 = R1 + R4
pe un procesor ce are un pipeline, vom obține o repartizare a subproceselor necesare
executării acestor două instrucțiuni conformă cu Figura 3.37. Din Figura 3.37 se observă
că în momentul în care cea de a doua instrucțiune execută procesul operand fetch (OF), în
ciclul 4, și dorește să preia datele din registrele R1 și R4 prima instrucțiune a finalizat
operația de adunare (EX - execuție), dar nu a stocat încă rezultatul – această operație se va
realiza abia în ciclul 5 imediat următor.

50
Instr1 IF ID OF EX WB
Instr2 IF ID - - OF EX WB

Tact 1 2 3 4 5 6 7 8
Figura 3.38. Prima modalitate de rezolvare a problemei

Pentru rezolvarea acestei probleme se introduc în mod forțat un anumit număr de


întârzieri ce au drept scop blocarea (stalled) execuției celei de a doua instrucțiuni. Există
două soluții posibile ce sunt prezentate în Figura 3.38 și Figura 3.39.
Introducerea întârzierilor se realizează cu ajutorul unui bloc denumit „pipeline
interlocks”32. Acest bloc hardware analizează în mod continuu fluxul de date și
detectează independența unei instrucțiuni de precedenta și, ulterior, întârzie instrucțiunile
interdependente până la soluționarea conflictului existent.

Instr1 IF ID OF EX WB
Instr2 - - IF ID OF EX WB

Tact 1 2 3 4 5 6 7 8

Figura 3.39. Cea de a doua modalitate de rezolvare a problemei

O altă modalitate de rezolvare a unei astfel de probleme este aceea de a rearanja codul
astfel încât aceste interdependențe să nu mai aibă loc. Aranjarea codului poate fi făcută de
către compilator sau de către procesor. Astfel următoarea structură de comenzi:
Instr1: ADD R2, R3, R1 R1 = R2 + R3
Instr2: ADD R1, R4, R5 R5 = R1 + R4
Instr3: INC R8 R8 = R8 + 1
Instr4: ADD R6, R6, R7 R7 = R6 + R6
poate fi aranjată sub forma:
Instr1: ADD R2, R3, R1 R1 = R2 + R3
Instr3: INC R8 R8 = R8 + 1
Instr4: ADD R6, R6, R7 R7 = R6 + R6
Instr2: ADD R1, R4, R5 R5 = R1 + R4

32
De aici conștientizăm avantajele unei arhitecturi de tip MIPS (Microprocessor without Interlocked
Pipeline Stages), arhitectură în care aceste blocări ale pipeline-urilor nu mai au loc.

51
Instr1 IF ID OF EX WB
Instr3 IF ID OF EX WB
Instr4 IF ID OF EX WB
Instr2 IF ID OF EX WB

Tact 1 2 3 4 5 6 7 8
Figura 3.40. Structura pipeline-ului după rearanjarea codului

În cadrul pipeline-ului noua structură de comenzi se prezintă ca în Figura 3.40. În


cazul în care, de exemplu, compilatorul nu găsește o situație optimă de rearanjare a
codului astfel încât problema interdependenței între instrucțiuni să dispară, la executarea
subproceselor în pipeline, va introduce o serie de instrucțiuni de tip nop (no operation)
pentru rezolvarea problemei.
În cadrul procesoarelor moderne codul (format din instrucțiuni) nu este executat
secvențial în ordinea în care a fost scris de programator ci intr-o altă ordine. Scopul
rearanjării programului este acela de a maximiza performanța procesorului. Procedeul se
cheamă Out of Order Execution (OOE). Acest procedeu implica rearanjarea codului
compilat (daca este posibil), executarea lui si apoi scrierea rezultatelor în memorie în
ordinea codului original pentru a nu denatura scopul original al programului. Pentru orice
program și pentru orice utilizator, procedeul este unul transparent. Numai CPU știe
ordinea exactă în care s-au executat instrucțiunile.
În afară de problemele de hazard ce țin de datele ce intră într-un pipeline, de
interdependența instrucțiunilor, există și alte probleme ce apar datorită modalităților de
scriere/citire a datelor. Aceste probleme se împart în trei mari clase: RAW (read after
write), WAR ( write after read) și WAW (write after write).
Hazardul WAR a fost prezentat anterior în Figura 3.37. Din punct de vedere teoretic
prima instrucțiune trebuie să scrie date într-un anumit registru, iar cea de a doua, ulterior,
trebuie să citească acest registru, dar datorită pipeline-ului sau a reordonării instrucțiunilor
scrierea se produce ulterior citirii.
În situația problemelor de tip WAW prima instrucțiune trebuie să scrie date într-un
registru, iar ulterior o a doua instrucțiune trebuie să scrie date în același registru. În
realitate cea de a doua instrucțiune este cea care scrie prima.
Hazardul de tip RAW (read after write) apare atunci când două instrucțiuni
consecutive trebuie să acceseze o resursă comună, de exemplu un registru, astfel încât
prima instrucțiune să citească datele iar ulterior cea de a doua să scrie noi date în resursa
comună. Cu toate acestea scrierea are loc înaintea citirii determinând o afectare a ordinii
normale și firești a programului ce generează rezultate imprevizibile.
Ultima clasă de probleme, pe care o studiem în acest subcapitol, este dată de hazardul
rezultat în urma evoluției programului – hazardul de comandă sau control. O
instrucțiune care modifică liniaritatea programului (secvențierea acestuia) poate genera o
modificare a fluxului instrucțiunilor, a inexistenței datelor corecte în memoriile cache ale
CPU (L1 sau L2) și de aici o imposibilitate de umplere la capacitatea maximă a pipeline-
ului. Acest fapt poate genera în cea mai nefavorabilă situație o execuție a programului

52
similară cu cea din Figura 3.31. De aici reiese importanța unității de predicţie a salturilor
(branch predictor) care are rolul de a identifica aceste “discontinuități” existente în codul
programului și de a încărca în mod corect instrucțiunile necesare astfel încât pipeline-urile
să fie „încărcate” la maxim pentru atingerea performanțelor maxime ale CPU.
Funcție de abordarea pe care compania producătoare o preferă toate aceste hazarde pot
fi gestionate în două moduri distincte. În prima abordare procesoarele pot gestiona toate
aceste hazarde (ca în cazul companiei Intel). Această metodă implică existența unui
hardware suplimentar necesar în analiza fluxurilor de date și în aplicarea tehnicilor de
corecție. În cea de a doua metodă compilatorul și asamblorul generează codul de o așa
natură astfel încât aceste probleme să nu apară. În această situație CPU îndeplinește mai
puține sarcini și este mai simplu arhitectural. Această simplitate arhitecturală determină
de asemenea și un consum mult mai redus.

3.9. Diferenţele existente între microprocesoare, DSP şi


microcontrolere

Microprocesoarele (μP) de uz general33, μP, ce stau la baza calculatoarelor personale


(de uz general), sunt utilizate în special în situaţia în care sunt necesare capacităţi mari de
stocare a datelor (memorie RAM şi HDD). Aceste calculatoare personale sunt
caracterizate în general de utilizarea unor sisteme de operare complexe care pot fi sau nu
sisteme de operare de timp real (Real Time Operating System – RTOS). O descriere a
acestor μP a fost realizată pe larg în unul din capitolele anterioare.
Microcontrolerele (μC) sunt circuite care înglobează în acelaşi cip pe lângă CPU şi o
serie largă de periferice, având de cele mai multe ori un set redus de instrucţiuni,
comparativ cu μP şi DSP-urile. De cele mai multe ori un microcontroler înglobează o
paletă largă de perifericele (ADC, DAC, porturi seriale – RS232, I2C, SPI, CAN etc. –,
porturi paralele, numărătoare, sistemul de întreruperi etc.), memoria (program şi cea de
date), oscilatorul etc. şi sunt capabile în implementările practice să aibă un număr minim
de componente suport externe. μC lucrează la frecvenţe inferioare μP şi DSP-urilor, având
în general putere de procesare redusă, ele fiind de altfel dedicate sistemelor embedded ce
necesită costuri şi dimensiuni reduse.
Un DSP (digital signal processor) este un microprocesor ce are aceleași caracteristici
principale și componente ca și acesta. Diferența între un DSP și un microprocesor constă
în faptul că cea mai mare parte a componentelor unui DSP-ului sunt puțin modificate
astfel încât acesta să fie capabil să execute procesări numerice de semnal la o viteză
superioară unui μP, îndeplinind totodată cerințele de lucru în timp real impuse de o
anumită aplicație.
În continuare se vor prezenta puncte comune și elemente ce diferențiază
microprocesoarele, DSP-urile și microcontrolerele. Actualmente este din ce în ce mai
dificil de a pune o graniță foarte exactă între toate aceste dispozitive. Din ce în ce mai
mult producătorii, acestor dispozitive, împrumută tehnici utilizate și specifice unui
anumite familii (μP, μC, DSP) pentru sporirea performanțelor propriei familii de
dispozitive.

33
Cele mai cunoscute familii de microprocesoare sunt cele produse de companiile Intel™ şi AMD™.

53
Registrul acumulator

Dacă analizăm ecuațiile matematice a celor mai utilizați algoritmi dedicați diferitelor
procesări de semnale se observă între acești algoritmi o similitudine formală, vezi Tabelul
3.7. Din relațiile prezentate în Tabelul 3.7 se observă că toți acești algoritmi realizează o
operație de sumare a unui anumit număr de termeni de tip produs. Din aceste observații
rezultă că este necesar să avem regiștri capabili să stocheze aceste rezultate (în unele
situații sumele prezentate în Tabelul 3.7 pot depăși 1000 de termeni).
În cadrul diferitelor tipuri de unități centrale de procesare (CPU) s-a stabilit convenția
ca în urma unei operații matematice rezultatul să se salveze într-un registru special numit
registrul acumulator34. Dacă în cazul calculatoarelor de uz general35 registrul acumulator
are același număr de biți (număr de biți egal cu lățimea bus-ului de date al procesorului36)
ca orice alt registru de uz general, în cazul DSP-urilor acestea au implementate registre
acumulatoarea cu un număr de biți mult mai mare, capabile să stocheze rezultatele unor
operații numerice de procesare a semnalelor similare cu cele prezentate în Tabelul 3.7.

Tabelul 3.7. Algoritmi clasici utilizați în prelucrarea semnalelor

Algoritm Ecuație

Filtru FIR 𝑦𝑛 𝑎 𝑥𝑛 𝑘

Filtru IIR 𝑦𝑛 𝑎 𝑥𝑛 𝑘 𝑏 𝑦𝑛 𝑘

Convoluție 𝑦𝑛 𝑥𝑘ℎ𝑛 𝑘

Transformata Fourier Discretă 𝑋𝑛 𝑥𝑘𝑒

𝜋 2𝑥 1
Transformata cosinus discreta 𝐹 𝑢 𝛼𝑢 𝑓 𝑥 𝑐𝑜𝑠 𝑢
2𝑁

De exemplu, DSP-urile pe 16 biți în virgulă fixă ADSP-21xx și ADSP-219x produse


de compania Analog Devices au înglobate un acumulator pe 40 de biți, în timp ce familia

34
AX, EAX (in cadrul familiei Intel), ACC or A (pentru microcontrolerele din cadrul familiei 8051), W
register (working register – pentru familia de microcontrolere produsa de Microchip) etc.
35
În cazul unei operații de înmulțire de tipul MUL ECX (operație realizată pe un microprocesor din clasa
Pentium, se realizează înmulțirea conținutului registrului pe 32 de biți ECX cu conținutul registrului
EAX) rezultatul final, pe 64 de biți, se va găsi împărțit între registrele EAX și EDX.
36
De exemplu, pentru familia Pentium acest număr este egal cu 32.

54
de DSP-uri ADSP-21xxx („SHARC”) au un acumulator pe 80 de biți – această ultimă
familie de DSP-uri susține calcule matematice pe 32-bit în virgulă fixă în timp ce
calculele matematice în virgulă mobilă sunt efectuate pe 32 și 40 biți. În mod similar,
familia de DSP-uri 56300 produsă de Frescale are înglobat un acumulator pe 56 de biți în
timp ce bus-ul este pe 24 de biți. În timp ce, DSP-urile familiei TMS320C67x, produse de
compania Texas Instruments, au două bancuri de registre pe 32 de biți, fiecare banc
incluzând un număr de 32 de regiștri. În cadrul acestei familii în cazul unei înmulțiri a
două numere în virgulă mobilă reprezentate pe 32 de biți, rezultatul este stocat în două
registre pe 32 de biți alăturate.

Instrucțiuni de tipul MAC

Din Tabelul 3.7 se observă că anterior stocării rezultatului sunt necesare efectuarea
unui număr de înmulțiri și de adunări consecutive, operații denumite MAC (multiply
accumulate). Din acest motiv producătorii microprocesoarele de tip DSP au optimizat
această operație implementând-o hardware într-o unitate specializată. Cele mai multe
procesoare de tipul DSP efectuează această înmulțire și adunare într-un singur ciclu
mașină. Pentru a realiza acest deziderat multiplicatorul și circuitul care realizează
adunarea sunt implementate în circuite realizate exclusiv combinațional.
Astfel de unități, de tip MAC, există în cadrul familiei TMS320C55x (produsă de
Texas Instruments), a familiei ST100 (STMicroelectronics37), ADSP-21xx (Analog
Devices) etc. Mai mult, în cadrul aceluiași DSP existând implementate chiar două astfel
de unități. În schimb alte familii, precum familia TMS320C6000, nu au implementate o
astfel de unitate (MAC). Familia TMS320C6000 realizează operațiile de tip MAC prin
înmulțiri și adunări succesive. În mod normal aceste 2 operații ar necesita 2 cicluri
mașină, dar datorită structurii de tip pipeline existente, aceste operații sunt executate în
mod aparent într-un singur ciclu mașină. Primele dispozitive ce aveau implementate
unități de execuție de tip MAC au fost DSP-urile, dar actualmente multe din procesoarele
de uz general au înglobate astfel de unități38.

Multiplicare hardware versus microcod

În momentul realizării unui operații de tip MAC ce utilizează numere reprezentate în


virgulă mobilă39, în mod implicit, sunt necesare rotunjiri ale valorilor implicate. Există
două abordări ale acestei probleme.
În prima metodă, se execută două rotunjiri succesive a operanzilor utilizați și
respectiv a rezultatului. Prima rotunjire are loc asupra produsului operanzilor de intrare
către alte valori intermediare dar de o precizie mai mare decât precizia finală utilizată și

37
Anterior SGS Microelettronica, Italia
38
De exemplu, microprocesoarele ce utilizează o arhitectură de tip SH3 (produse de compania Renesas
Technology), precum microprocesorul SH7708, au o singură unitate de tip MAC. Această unitate de
execuție poate multiplica 2 operanzi pe 32 de biți, stoca rezultatul în memorie sub forma unui număr pe
32 de biți și, ulterior, aduna acest produs la valoarea existentă în registrul acumulator. Acumulatorul
fiind un registru pe 42 de biți.
39
Numerele reprezentate în virgulă mobilă sunt caracterizate de 2 perechi de numere, mantisa (notată în
continuare cu n) și exponentul (notat cu e), ambele numere fiind de tip întreg cu semn. Utilizând aceste
valori orice număr real se scrie sub forma: n ∙ 2e.

55
suportată de CPU. Ulterior după efectuarea tuturor calculelor rezultatul mai este rotunjit
încă o dată către precizia normală de lucru suportată de CPU. De exemplu, dacă unitatea
MAC trebuie să realizeze la un anumit moment de timp operația a + b ∙ c, în primul pas
rezultatul produsului b ∙ c va fi rotunjit, la un anumit număr de biți semnificativi, iar
ulterior acest număr rezultant va fi adunat cu a iar acest ultim rezultat va fi din nou
rotunjit la un anumit număr de biți semnificativi. Această modalitate de lucru este
specifică celor mai multe procesoare de tip DSP.
În cea de a doua abordare, inițial se realizează toate calculele într-o precizie
superioară iar, în final, are loc rotunjirea (deci doar la sfârșit) – această rotunjire fiind
aplicată doar rezultatului (adică numărului a + b ∙ c). Această ultimă metodă este
cunoscută sub numele de fused multiply-add (FMA) sau fused multiply-accumulate
(FMAC). Avantajul unităților de execuție de tip FMAC este dat de ușurința în
implementare a unor funcții precum radicalul sau exponentul.
La ora actuală există un număr mic de microprocesoare ce au înglobate unități de tip
FMAC. Dintre acestea putem aminti microprocesoarele de tipul Itanium (produs de Intel)
sau familia Loongson, SPARC64 și SPARC64 II (produse de Fujitsu). Compania AMD a
anunțat înglobarea unităților de execuție FMAC în procesoarele sale în anul 2011 în timp
ce compania Intel utilizează aceste module în procesoarele Pentium din clasa Haswell de
abia din anul 2012.

DMA-ul

Există două modalități de transfer a datelor între diferitele periferice și memorie sau
între diferitele zone de memorie. Prima modalitate de transfer a datelor este realizată de
CPU, iar cea de a doua este realizată de DMA (direct memory access).
În cadrul microprocesoarelor de tip DSP datorită lucrului intensiv cu date de diferite
tipuri (de exemplu cu eșantioane diferitelor semnale, cu imagini etc. sau cu diferite
rezultate obținute în urma procesării acestor date) s-a impus necesitatea transferului rapid
al informațiilor între diferite periferice și memorie sau între anumite zone de memorie în
background (în timpul în care CPU-ul DSP-ului realizează prelucrări asupra altor
informații). Deci, utilizând aceste unități de tip DMA datele vor fi mutate dintr-un loc în
altul fără intervenția CPU-ului. CPU-ul va configura doar la început DMA-ul. Din
motivele enunțate anterior, cele mai multe dintre DSP au înglobate intern diferite sisteme
de tip DMA precum EDMA (Enhanced DMA, utilizat în principal pentru transferul de
date între un periferic și memorie), QDMA (Quick DMA – utilizat în principal pentru
transferul de date, de blocuri de date, în cadrul memoriei externe) sau IDMA (Intrernal
DMA) toate aceste tipuri de DMA cu un număr mare și foarte mare de canale. De
exemplu, procesoarele din familia TMS320C67xx înglobează controlere de tip EDMA cu
16 canale în timp ce DSP-urile din familiile TMS320C64xx și TMS320DM64xx au 64 de
canale ce pot fi utilizate pentru transferuri de tip DMA. În plus, DSP-urile din familia
TMS320DMx+ mai au înglobat în CPU o unitate de tip DMA cu două canale, unitate
numită IDMA, capabilă să transfere date intern între memoria cache program L1 (L1P),
memoria cache date L1 (L1D), memoria cache L2 (L2) și zona de configurare a
perifericelor externe.
Calculatoarele de uz general, bazate pe procesoare de tip Intel, utilizau la începuturi
un singur controler DMA (de tipul Intel 8237) ce îngloba doar 4 canale. Ulterior, datorită

56
necesităților practice, a mai fost introdus un controler DMA de același tip (Intel 8237).
Primul canal al celui de al doilea DMA este utilizat în cascadarea unităților DMA. Din
acest motiv un PC are doar 7 canale DMA disponibile. Pentru păstrarea compatibilității
cu sistemele anterioare această arhitectură s-a păstrat40 și a suferit modificări minore de-a
lungul timpului. Aceste modificări au fost necesare în principal pentru a ține pasul cu
evoluția capacităților de stocare a memoriilor de tip RAM, deci a existenței unui: spațiu
de memorie mult mai amplu din care și în care trebuiau stocate datele și a numărului de
biți transferați într-un singur ciclu.
În general există: (a) două tipuri de transfer; transfer de tip flyby și fetch-and-deposit
(calculatoarele personale pe arhitectură Intel acceptă doar primul tip de transfer), [Harvey,
1991], și (b) trei moduri distincte de lucru a controlerelor DMA, [Harvey, 1991]: de tip
element, bloc și la cerere.
Transferul DMA de tip flyby41 (cunoscut și sub numele de transfer de tip un singur
ciclu, single-cycle, sau de o singură adresă, single-address) este realizat printr-un singur
acces la bus-ul sistemului în care se realizează simultan atât citirea sursei cât și scrierea
destinației. Datorită acestui singur acces, acest transfer este unul foarte eficient
comparativ cu metoda fetch-and-deposit care presupune două cicluri de acces la bus-ul
sistemului (în primul pas se preia data ce se stochează local în controlerul DMA iar
ulterior informația este scrisă în destinația aleasă).

Figura 3.41. Transferul DMA de tip flyby

În transferul de tip flyby (existent atât în sistemele cu bus ISA sau EISA) dispozitivul
ce dorește să realizeze transferul de date prin DMA depune o cere către controlerul DMA.
Controlerul DMA preia controlul bus-ului de date al sistemului, de la procesor, și
generează o anumită adresă sursă sau destinație (stocată anterior în controlerul DMA) în

40
Utilizarea a două unități DMA, fiecare cu câte 4 canale, s-a utilizat pentru prima dată începând cu
microprocesorul 80286 în anul 1982. Acest procesor a fost primul din familia Intel capabil să ruleze
toate aplicațiile scrise pentru predecesorul său.
41
Capabil să asigure viteze maxime de transfer de 33 Mocteți/secundă (mod de operare burst pe 32 de
biți).

57
conformitate cu sensul transferului. Ulterior generării adresei, controlerul DMA trimite
înapoi către dispozitiv un semnal de acceptare a transferului. Ca urmare a acestui accept
dispozitivul va citi datele de pe bus sau își va pune propriile date. Acest tip de transfer
este similar cu un ciclu de citire sau scriere a unei locații de memorie de către CPU, cu
diferența că de această dată controlerul DMA controlează bus-ul de adrese al sistemului
precum și liniile de comandă și control în timp ce dispozitivul citește sau scrie datele.
Dezavantajul acestui mod de lucru este dat de imposibilitatea transferului de date între
oricare două locații de memorie (este imposibil modul de lucru: sursa să fie o locație de
memorie iar destinația să fie o altă locație de memorie). Pentru a implementa un transfer
de tip memorie-memorie se utilizează două canale DMA [Harvey, 1991].
Într-un calculator personal construit în jurul unei arhitecturi Intel toate cele trei moduri
de lucru utilizează tipul de transfer DMA flayby. În modul de lucru de tip element (single
transfer mode) controlerul DMA transferă doar o singură valoare. În modurile de lucru
bloc (block) și la cerere (demnad) controlerul DMA transferă multiple elemente atunci
când controlerul DMA a preluat controlul bus-ul sistemului de la procesor. În modul bloc
de date controlerul DMA transferă întregul bloc de date cât mai rapid (lungimea blocului
este stocată într-un registru intern controlerului DMA) în urma unei singure cereri DMA
lansată de dispozitiv. În modul de lucru la cerere transferul are loc doar atâta timp cât
dispozitivul extern lansează cereri către controlerul DMA.
Comparând controlerele de tip DMA (existente în PC) cu cele de tip EDMA (existente
în DSP-uri) vom observa că acestea din urmă furnizează o flexibilitate sporită în
transferul diferitelor tipuri de date. Pentru a susține această idee se vor prezenta doar două
facilități foarte importante a acestor controlere EDMA modurile de lucru de tip înlănțuit
(chaining) și legătură (linking).
Comparativ cu primele subfamilii de dispozitive de tip C620x/C670x, ce aparțin
familiei C6xxx, a căror arhitectură a controlerului DMA este de tip registru, noile
subfamilii au o arhitectură a controlerului DMA de tip memorie RAM. Deosebirea
fundamentală între aceste două tipuri de arhitecturi constă în locul de depozitare a setului
de parametri utilizați în configurarea controlerului DMA. În cadrul familiei C6xxx aceste
zone de memorie variază de la capacități de stocare a unui număr de maximum 85 de
grupuri de parametri (2 Kocteți de RAM) până la maximum 213 grupuri de parametri (5
Kocteți de RAM).

Memoria cache

Un alt avantaj al CPU intern DSP-urilor este dat de flexibilitatea utilizării memoriei
interne, locale a CPU (L1 sau L2) drept memorie cache sau drept memorie RAM (prin
maparea acestei memorii cache în spațiul extern al memoriei RAM). De exemplu, dacă
analizăm foaia de catalog a unui DSP de tipul TMS320C64x vom găsi următoarele
informații:
 L1P (program)
o 32 Kbytes configurable: Memory-mapped (default after reset) or direct-mapped
cache – 32bytes cache line
 L1D (data)
o 32 Kbytes configurable: Memory-mapped (default after reset) or 2-way set
associative cache – 64bytes cache line
o 48K bytes memory-mapped

58
 L2 (program and data)
o 64Kbytes configurable: Memory-mapped (default after reset) or 2-way set
associative cache—128bytes cache line
o 32Kbytes memory-mapped
o 16Kbytes ROM
Dacă analizăm funcționarea memoriei L1D observăm că după resetarea sistemului
întreaga cantitate de memorie internă de tip L1D va fi map-ată și utilizată drept memorie
RAM. Funcție de setările interne ale DSP-ului se pot aloca 4 Kocteți, 8 Kocteți, 16
Kocteți sau 32 de Kocteți drept memorie cache internă de tip L1D, vezi Figura 3.42.
Chiar în situația în care se alocă 32 Kbiți drept memorie internă cache, 48 Kocteți vor
rămâne în continuare map-ați în spațiul memoriei RAM.
Posibilitate utilizării memoriei cache interne de tip program (L1P) sau date (L1D) sau
a memoriei L2 pentru a fi map-ată în spațiul memoriei de date sau program externe este o
facilitate foarte importantă oferită de familia de DSP-uri produse de firma Texas
Instruments. Să presupunem o aplicație tipică de tip procesare de semnale. În primul pas
se achiziționează semnalele, ulterior vectorii de date se procesează de către DSP și, în
ultimuil pas, rezultatele sunt trimise, de exemplu, către un convertor DAC.
0K
RAM 0 CACHE 0 CACHE 0 CACHE 0 CACHE 0
4K
RAM 1 RAM 1 CACHE 1 CACHE 1 CACHE 1
8K
RAM 2 RAM 2 RAM 2 CACHE 2 CACHE 2
16 K

RAM 3 RAM 3 RAM 3 RAM 3 CACHE 3

32 K

RAM 4 RAM 4 RAM 4 RAM 4 RAM 4


(48 Koct.) (48 Koct.) (48 Koct.) (48 Koct.) (48 Koct.)

Figura 3.42. Modalități de configurare a memoriei L1D interne a DSP-ului

Deoarece într-un microprocesor de tip RISC memoria cache este interna iar
manipularea ei este transparentă utilizatorului (nu este sub controlul lui, a programului
dezvoltat de el – CPU-ul o utilizează pentru creșterea performanțelor proprii dar o face
independent de voința utilizatorului) pentru stocarea datelor obținute de la un dispozitiv
extern (de exemplu un ADC) putem folosi doar memoria RAM, externă a sistemului.

59
Buffer de date

CPU
(2)
(5) (3)
EMIF Unitatea
Memorie
externă (4) de control
RAM EDMA L1D și de
procesare
(1)
cache a datelor
Echipament (6)
periferic (de ex. Port
ADC/DAC) SoC

Figura 3.43. Transferul datelor și procesarea lor într-un microprocesor RISC clasic

Într-un astfel de sistem fluxul de date este similar cu cel prezentat în Figura 3.43. În
primul pas pentru a nu încărca procesorul inutil cu achiziționarea datelor acestea sunt
preluate, aduse cu ajutorul unui DMA (care poate fi sau nu înglobat în interiorul SoC) în
memoria RAM, externă a sistemului, pasul (1). După achiziționarea tuturor datelor CPU
începe procesarea lor, la prima citire sau atunci când sistemul de predicție identifică
corect calea pe care programul o urmează, datele sun aduse în memoria cache internă
procesorului, pasul (2). Procesorul începe prelucrarea lor conform algoritmului utilizat (a
programului), pașii (3) și (4). Datele vor fi rescrise în memoria RAM externă prin unul
din mecanismele pe care le utilizează write-back sau write-through – opțiunea (5). În
final, după finalizarea procesării digitale a întregului vector de date acestea vor fi trimise,
prin intermediul unui circuit de tip DMA, către echipamentul periferic destinatar, de
exemplu un circuit DAC.

Memorie cache map-ată


ca memorie RAM

CPU
(4)
Memorie (2)
EMIF EDMA Unitatea
externă
RAM (3) de control
și de
procesare
L1D
a datelor
Echipament (1)
cache
periferic Port
(de ex. ADC) SoC
Figura 3.44. Transferul datelor și procesarea lor într-un DSP din familia TMS320Cxxxx

60
În cazul DSP-urilor, de la firma Texas Instruments, datorită posibilității map-ării
memoriei cache interne în spațiul memoriei RAM externe (cu păstrarea performanțelor
unei memorii de tip L1D – adică latență egală aproape cu zero) fluxul de date este similar
cu cel din Figura 3.44. În afara faptului că memoria RAM externă (mult mai lentă) nu
mai intervine de loc în stocarea datelor, că fluxul de date este mult simplificat, se oferă
posibilitatea stocării datelor obținute de la dispozitivele periferice direct în memoria cache
internă (memorie foarte iute) și, mai mult, zone de cod critice sau diferitele variabile42
utilizate foarte des pot să fie stocate intern în memoria cache.
O abordare oarecum similară cu cea prezentată anterior se găsește în cadrul familiei de
procesoare ARM ce au intern o memorie de tip TCM (Tightly-Coupled Memory). În
aceasta memorie rapidă, internă procesorului se pot plasa date și părți din programe,
performanțele obținute sunt similare cu acelea obținute atunci când codul sau datele sunt
în memora cache a procesorului. Singura diferență între aceste memorii (TCM și cache)
este dată de posibilitatea controlului direct, a plasării voliționale de către programator a
codului și/sau a datelor în memoria TCM comparativ cu modalitatea transparentă
programatorului (ne aflată sub controlul acestuia) de aducere automată a informațiilor
necesare blocurilor interne procesorului.

Facilități specifice de adresare a memoriei.

Cea mai mare parte a microprocesoarelor de uz general pot executa algoritmi specifici
procesărilor digitale de semnale, deci algoritmi specifici dispozitivelor de tip DSP, fără
prea mari probleme. Dar aceste dispozitive de tip μP nu sunt potrivite pentru acest gen de
activități, în principal datorită costurilor existente şi a puterii consumate43. Întotdeauna în
momentul alegerii unor astfel de dispozitive trebuie să punem în balanţă puterea
consumata (de exemplu trebuie să avem în vedere atât consumul unui DSP dar şi acela a
unui microprocesor Pentium de tipul simplu, dual core etc.) cât şi costurile dispozitivelor
în sine (cât costă un DSP şi cât costă un μP din familia Intel).

42
Utilizarea lui register din limbajul C determină ca o anumită variabilă să fie stocată într-un registru al
procesorului obținându-se astfel o creștere a vitezei de execuție a codului. Dar, numărul de regiștri pe
care un procesor îi are este limitat. În schimb cantitatea de memorie cache este mult, mult mai mare
permițând astfel ca un număr mult mai mare de variabile să fie stocate și manipulate foarte rapid
determinând astfel creșterea performanțelor globale ale aplicației.
43
De exemplu, puterea consumată de către un DSP se poate situa în domeniul 10-100 mW. DSP-ul
TMS320C5510 consumă doar 80 mW la o frecvență de 160 MHz oferind o putere de calcul de 320
MIPS.

61
3.10. CC2650 și CC3200

În cadrul acestei discipline vom lucra cu două sisteme ce au la bază următoarele


micrcontrolere: CC2650 SimpleLink multi-standard 2.4 GHz ultra-low power wireless
MCU și CC3200 SimpleLink™ Wi-Fi® and Internet-of-Things solution, a Single-Chip
Wireless MCU.
Aceste două microcontrolere au la bază două procesoare foarte asemănătoare: Cortex-
M3 (CC2650) și Cortex-M4 (CC3200).

Cortex-M3 Cortex-M4
Arhitectură ARMv7-M (Harvard) ARMv7-M (Harvard)
Instrucțiuni Thumb/Thumb-2 Thumb/Thumb-2
Single cycle 16,32-bit MAC
Single cycle dual 16-bit MAC
DSP - 8,16-bit SIMD arithmetic
Hardware Divide (2-12
Cycles)
Single precision floating point
FPU -
unit IEEE 754 compliant
Pipeline 3-stage + branch speculation 3-stage + branch speculation
Dhrystone 1.25 DMIPS/MHz 1.25 DMIPS/MHz
Non-maskable Interrupt Non-maskable Interrupt
Întreruperi (NMI) + 1 to 240 physical (NMI) + 1 to 240 physical
interrupts interrupts
Latență întreruperi 12 cycles 12 cycles
Priorități 8 to 256 priority levels 8 to 256 priority levels
Optional JTAG & Serial-Wire Optional JTAG & Serial-Wire
Debug Ports. Up to 8 Debug Ports. Up to 8
Debug Breakpoints and 4 Breakpoints and 4
Watchpoints. Watchpoints.
Optional Instruction Trace Optional Instruction Trace
(ETM), Data Trace (DWT), (ETM), Data Trace (DWT),
Trace and Instrumentation Trace and Instrumentation Trace
(ITM) (ITM)

Amândouă procesoarele oferă o performanță de 1.25 DMIPS/MHz și 2.1 MIPS/MHz,


putând lucra la o frecvență maximă de 200 MHz. Amândouă procesoarele au aceeași
arhitectură ARMv7-M, având un set de instrucțiuni Thumb/Thumb-2 care include
instrucțiuni pe 16 și 32 de biți. Dar diferența fundamentală stă în capabilitățile oferite de
setul de instrucțiuni de tip DSP (SIMD) existent în Cortex-M4. Opțional poate exista și un
procesor de tip FPU – pentru Cortex-M4F. Astfel, numărul total de instrucțiuni ajunge la
291 (186 de bază Cortex-M3 + 80 SIMD + 25 FPU), pentru Cortex-M4F, față de doar
cele 186 de instrucțiuni ale procesorului Cortex-M3.
Studiu de caz: De exemplu, având o aplicație care realizează o transformată Furier rapidă
(FFT) pe 512 puncte la fiecare 0.5 secunde, pe cele două procesoare (Cortex-M3
și Cortex-M4), vom observa că Cortex-M3 consumă pentru același task de trei

62
ori mai multă putere decât Cortex-M4.

Figura 3.45. Setul de instrucțiuni Cortex-M0/M1 versus Cortex-M3 versus Cortex-M4


versus Cortex-M4F

Deci, dacă o aplicație nu necesită frecvent operații care să implice DSP sau FPU
atunci cel mai indicat procesor este Cortex-M3 – deoarece vom obține același nivel sau un
nivel similar de performanțe și consum de putere ca atunci când utilizăm un Cortex-M4.
În caz contrar Cortex-M4 este indicat.

Setul de instrucțiuni Thumb și Thumb-2

Una din caracteristicile fundamentale ale acestui procesor (de fapt a tuturor
procesoarelor implementate ce au la bază versiunea 7 a arhitecturii ARM subversiunile A,
M și R) este dată de existența implicită a unui nou set de instrucțiuni, denumit Thumb-2
ISA (Instruction Set Architectures), ce are drept obiectiv fundamental unificarea setului
inițial de instrucțiuni ARM pe 32 de biți cu cel de tip Thumb codat doar pe 16 biți
(introdus pentru prima dată în cadrul arhitecturii ARMv4 și implementat, tot pentru prima
dată în cadrul nucleului ARM7TDMI). Acest set de instrucțiuni, Thumb-2, a fost introdus
opțional (în diferite variante ale versiunii arhitecturale ARMv6) încă din anul 2001.
Necesitatea introducerii, pentru prima dată, a setului de instrucțiuni Thumb (codat pe
16 biți), în plus față de setul standard de instrucțiuni existent în arhitectura ARM (set de
instrucțiuni codat pe 32 de biți), a fost generată de dorința optimizării trioului:
performanță (de dorit cât mai ridicată), costuri (cât mai reduse) și consum (cât mai
mic).

63
Tabelul 3.8. Decodarea unei instrucțiuni Thumb
Instrucțiunea ARM (32 biți)
Instrucțiunea Thumb (16 biți) echivalentă obținută în urma
decodării instrucțiunii Thumb
ADD r0, #10 ADD r0, r0, #10

Instrucțiunile din setul Thumb sunt direct echivalente cu un subset, cel mai utilizat,
din setul de instrucțiuni ARM. Deci setul de instrucțiuni Thumb este un subset al setului
de instrucțiuni ARM. Aceste instrucțiuni sunt codate pe 16 biți fiind obținute, de
exemplu, prin considerarea diferiților operanzi existenți din setul inițial de instrucțiuni
ARM drept operanzi impliciți (vezi Tabelul 3.8) sau a limitării accesului diferitelor
instrucțiuni la diferiții regiștri existenți – în acest mod se obține o reducere a lungimii
instrucțiunilor pe seama pierderii flexibilității și a diferitelor funcționalități existente44 în
setul inițial de comenzi ARM.

Tabelul 3.9. Accesul instrucțiunilor de tip Thumb la regiștrii procesorului


Regiștri Acces
r0 – r7 Acces total
r8 – r12 Accesibili doar instrucțiunilor:
MOV, ADD și CMP
R13 (sp) Acces limitat
r14 (lr) Acces limitat
r15 (pc) Acces limitat
cpsr45 Doar acces indirect
spsr Accesul nu este permis

Cea mai mare parte a instrucțiunilor din setul Thumb sunt decodate și, ulterior, mapate
direct în instrucțiuni din setul ARM, permițând în final funcționalități identice cu acestea.
Deoarece nu întreg setul de instrucțiuni ARM își are corespondent în setul de instrucțiuni
Thumb uneori mai multe instrucțiuni de tip Thumb sunt necesare pentru a executa o
instrucțiune de tip ARM. În plus, nu există instrucțiuni de tip Thumb utilizate în accesarea
regiștrilor coprocesorului (dacă există) sau instrucțiuni de tipul SIMD (single instruction
multiple data). Prin utilizarea acestui set de instrucțiuni, de tip Thumb, se scad
performanțele procesorului (viteză mai mică de execuție datorată necesității decodării
instrucțiunilor) în schimbul obținerii unei scăderii a lungimii programelor (o creștere a
densității codului) cu aproximativ 30% [Phelan, 2003], vezi și Figura 3.46. Chiar dacă un
program ce utilizează setul de instrucțiuni Thumb necesită mai multe instrucțiuni decât un
program identic scris cu setul ARM codul rezultant va fi mai scurt.
Prin utilizarea combinată a seturilor de instrucțiuni ARM și Thumb se poate jongla
între obținerea unor performanțe ridicate, a unui cost redus sau a unui consum redus.

44
De exemplu, numai o parte din regiștrii de uz general interni procesorului pot fi accesați.
45
Current Program Status Register

64
Obținerea unor programe de dimensiuni reduse influențează în mod direct
cantitatea de memorie (internă sau externă) necesară sistemului (în sensul diminuări
acesteia) și de aici rezultând o scădere a costurilor generale ale sistemului dezvoltat.
Trecerea la un microcontroler sau DSP din aceeași familie dar cu caracteristici superiore
de memorie (de cele mai multe ori dispozitivul superior dublează cantitate de memorie ce
o conține cel inferior, din aceeași clasă) determină creșterea costurilor46 cu sume variind
de la câțiva euro pana la câțiva zeci de euro.

Figura 3.46. Performanțele diferitelor seturi de instrucțiuni existente obținute în mediul


RealView Development Suite

Obținerea unei mari “densități” a codului determină atât scăderea costurilor dar și
scăderea dimensiunii fizice a dispozitivelor (SoC sau microcontrolere).
De asemenea, programe mici pot fi stocate din ce în ce mai mult în memoria
internă a SoC sau microcontrolerului. Prin accesarea memoriei externe se activează
circuitele periferice de interfațare cu memoria externă rezultând o creștere a
consumului, dar simultan cu accesarea memoriei externe (memorie lentă ce implică
utilizarea mai multor cicli procesor) rezultând, din nou, consumuri mai mari.
Dacă performanțele necesare finalizării unei anumite activități (comanda unui
dispozitiv, decodarea unui fișier MP3 etc.) sunt întotdeauna prioritare, aceste activități
trebuie executate de o așa natură astfel încât consumul global al sistemului să fie cât mai
redus, obținându-se simultan și o maximizare a timpului de viață a sistemului de
acumulatori sau baterii. Această maximizare a timpului de viață a acumulatorilor este
importantă mai ales în cazul dispozitivelor portabile (tablete, telefoane mobile,
videocamere etc.).
Prin utilizarea unui set de instrucțiuni mai rapide, puternice și eficiente se poate
reduce frecvența de lucru a procesorului. Reducerea frecvenței de lucru generează
costuri reduse (aceeași activitate se poate realiza pe un procesor mai lent și mai ieftin),

46
De exemplu, dacă comparăm microcontrolerele pe 8 biți ATMega64 (ATMEGA64-16AI circuit cu 64
de pini, ce lucrează la o frecvență de 16 MHz, având o memorie flash de 64 Kb) cu un preț de 8.9 USD,
pentru o singură componentă, versus ATMega128 (ATMEGA128-16AC circuit cu 64 de pin, 16 MHz,
128 Kb) preț untar de 13.90 USD, vom obține o diferență de 5 USD.

65
reducându-se totodată și consumul. Într-o altă abordare, păstrând o frecvență constantă
de lucru simultan cu utilizarea unui set de instrucțiuni mai rapid se poate reduce
consumul, prin reducerea timpului necesar îndeplinirii unei anumite sarcini și trecerea mai
rapidă a sitemului, de tip SoC sau microcontroler, într-un mod de consum redus (de tip
idle).

Figura 3.47. Comparație între seturile de înstrucțiuni ARM, Thumb și Thumb-2 din două
perspective: lungimea codului și viteza de execuție

În situația arhitecturii de tip ARM, de cele mai multe ori, părțile sensibile ale
aplicației, precum tratarea unor întreruperi, vor fi scrise cu ajutorul setului de instrucțiuni
de tip ARM, în timp ce restul aplicației (pe cât de mult este posibil) va fi scrisă utilizând
setul de instrucțiuni Thumb. Pentru a nu fi obligați să tot schimbăm modurile de lucru
între ARM și Thumb ISA s-a creat un nou set de instrucțiuni Thumb-2 ce este format din
vechiul set de instrucțiuni Thumb ce a fost completat cu noi instrucțiuni (aproximativ cu
130 noi instrucțiuni) pe 16 și pe 32 de biți (cele pe 32 de biți derivate din instrucțiunile
ARM echivalente) plus un număr nou de instrucțiuni ce îmbunătățesc setul de ISA ARM
pe 32 de biți. Codul rezultat prin utilizarea noului set Thumb-2 urmărește simultan
aceleași obiective tradiționale: creșterea densității codului (obiectiv similar Thumb ISA)
și obținerea unor performanțe similare setului de instrucțiuni ARM pe 32 de biți.
Programele pot fi scrise în întregime utilizând Thumb-2 ISA ce tinde să devină setul
standard de instrucțiuni a arhitecturii ARM. De altfel, subversiunea M (Cortex-M3 și
Cortex-M4) a versiunii ARMv7 (microcontrolere) suportă doar setul de instrucțiuni
Thumb-2. Noile instrucțiuni Thumb-2 au fost implementate ținând cont de analiza unui
număr mare de programe scrise utilizând seturile de instrucțiuni ARM și Thumb având în
vedere creșterea performanțelor, a puterii și eficienței setului de instrucțiuni, a existenței
unor instrucțiuni echivalente întregului set ARM, a capacității noului set de instrucțiuni de
a accesa întreaga funcționalitate a coprocesorului, de a fi capabile să lucreze cu
instrucțiuni de tipul SIMD etc.

66
Programarea eficientă în limbajul C pentru arhitecturi de tip ARM

Scopul acestui capitol este acela de a prezenta diferite moduri de lucru și implementări
specifice în dezvoltarea programelor ce au drept scop optimizarea diferitelor subroutine
(în special a celor mai des utilizate) pentru o viteză mai mare de execuție sau o lungime
mai mica a codului.
Din păcate pe lângă avantajele existentente date de diferitele tehnici utilizate în
optimizarea codului, acestea necesită o cunoaștere profundă a părții hardware pe care
codul rulează, iar la sfârșit codul nu va mai fi la fel de ușor de înțeles – din acest motiv
este foarte important să comentați aceste zone de cod și nu numai.

Apelul de funcții

Există mai multe standarde care reglementează modalitatea în care se apelează


funcțiile dezvoltate în C în cadrul familiei de procesoare ARM: ARM Procedure Call
Standard (APCS), Thumb Procedure Call Standard (TPCS) și ARM Architecture
Procedure Call Standard (AAPCS) ce a apărut pe data de 24 noiembrie 2015. Dacă
primele două standarde (APCS, TPCS) nu mai sunt utilizate fiind considerate depășite,
standardul utilizat actual este ultimul – AAPCS.
Sintetizând, primele 4 variabile de tip integer vor fi trimise ca argumente în cadrul
funcției prin intermediul primilor 4 regiștri: r0, r1, r2 și r3, în timp ce următoarele
argumente de tip întreg vor fi plasate pe stivă – vezi Figura 3.48.

... ...
sp+12 Argument 7
sp+8 Argument 6
sp+4 Argument 5
sp Argument 4

r3 Argument 3
r2 Argument 2
r1 Argument 1
r0 Argument 0 Valoarea întoarsă
Figura 3.48. Mecanisme de trimitere a argumentelor către o funcție conform AAPCS

În situația în care o funcție va întoarce o valoare de tip întreg aceasta se va regăsi,


după finalizarea execuției funcției, în registrul r0.
Dacă valori pe 64 de biți trebuie trimise către funcții (precum long long sau double),
acestea vor fi trimise prin doi registri consecutivi (primul argument r0 și r1, al doilea

67
argument r2 și r3) iar următoarele valori prin intermediul stivei. Dacă aceleași tip de
valoare ar trebui returnată aceasta se va realiza prin intermediul regiștrilor r0 și r1.
Din prezentarea anterioară putem trage o concluzie directă aceea că este mult mai
eficient (se obține o viteză mai mare de transfer) să realizăm apelul funcțiilor cu
argumente care se stochează în regiștri, decât trimiterea valorilor prin intermediul stivei,
vezi și Figura 3.49.

int suma_secventiala(int a, int b, 
                     int c, int d,  push {r3, r4, r5, lr}
                     int e, int f) 

    int sum=0;  ldr r5, [sp, #0x14] ; r5 = f
  ldr r4, [sp, #0x10] ; r4 = e
    sum += a;  adds r4, r4, r5 ; r4 = e + f
    sum += b;  adds r3, r3, r4 ; r3 = d + r4
    sum += c;  adds r2, r2, r3 ; r2 = c + r3
    sum += d; 
    sum += e;  adds r1, r1, r2 ; r1 = b + r2
    sum += f;  adds r0, r0, r1 ; r0 = a + r1
 
    return sum;  pop {r3, r4, r5, pc}
}

Figura 3.49. Transferul argumentelor prin intermediul regiștrilor și stivei

Pentru păstrarea aceleiași eficiențe în transmiterea argumentelor către funcții, atunci


când avem de trimis mai mult de 4 valori de tip întreg, se recomandă gruparea acestora în
structuri de date.

typedef struct { 
    int a; 
    int b; 
    int c; 
    int d; 
ldr r1, [r0] ; r1 = a
    int e;  ldr r2, [r0, #4] ; r2 = b
    int f;  adds r2, r2, r1 ; r2 = a + b
} sase_val;  ldr r1, [r0, #8] ; r1 = c
  adds r1, r1, r2 ; r1 = r2 + c
int suma_paralela(sase_val *data)  ldr r2, [r0, #0xc] ; r2 = d

    int sum=0;  adds r2, r2, r1 ; r2 = d + r1
  ldr r1, [r0, #0x10] ; r1 = e
    sum += data‐>a;  adds r1, r1, r2 ; r1 = e + r2
    sum += data‐>b;  ldr r0, [r0, #0x14] ; r0 = f
    sum += data‐>c;  adds r0, r0, r1 ; r0 = f + r1
    sum += data‐>d; 
    sum += data‐>e; 
    sum += data‐>f;  bx lr ;return
 
    return sum; 
}

Figura 3.50. Transferul argumentelor prin intermediul unei structuri

68
Lucrul cu variabile locale

Arhitectura ARM poare lucra (încărca/salva) într-un mod foarte eficient date pe 8 biți,
16 biți sau 32 de biți, în schimb cele mai multe operații de procesare sunt pe 32 de biți.
Aceasta este motivația pentru care tipurile de date pe 32 de biți sunt preferate
reprezentărilor pe 8 sau 16 biți.

Tabelul 3.10. Implementarea diferitelor tipuri de date


Tip de data în C Implementare
char signed/unsigned 8 bits – byte
short signed/unsigned 16 bits – halfword
int signed/unsigned 32 bits – word
long signed/unsigned 32 bits – word
long long signed/unsigned 64 bits – double word
floating point Half precision 16 bits
Single precision (IEEE 754) 32 bits
Double precision (IEEE 754) 62 bits
Pentru a conștientiza efectul generat de utilizarea unei variabile locale care nu este pe
32 de biți să considerăm următoarele 2 programe:

int suma_char(int *data)  int suma_int(int *data) 


{  { 
    char i;      int i; 
    int sum = 0;      int sum = 0; 
   
    for (i = 0; i < 64; i++)      for (i = 0; i < 64; i++) 
        sum += data[i];          sum += data[i]; 
   
    return sum;      return sum; 
} }

mov r2, r0 ; r2 = data mov r2, r0 ; r2 = data


movs r0, #0 ; sum = 0 movs r0, #0 ; sum = 0
mov r1, r0 ;i=0 mov r1, r0 ;i=0
cmp r1, #0x40 ; i < 64 cmp r1, #0x40 ; i < 64
bge $C$L3 ; greater or equal bge $C$L3
$C$L2: $C$L2:
ldr.w r3, [r2, r1, lsl #2] ; r3 = data[i] ldr.w r3, [r2, r1, lsl #2] ; r3 = data[i]
adds r0, r3, r0 ; sum += r3 adds r0, r3, r0 ; sum += r3
adds r1, r1, #1 ; i++ adds r1, r1, #1 ; i++
uxtb r1, r1
cmp r1, #0x40 ; i < 64 cmp r1, #0x40 ; i < 64
blt $C$L2 ; less than blt $C$L2 ; less than
$C$L3: $C$L3:
bx lr ; return bx lr ; return

Figura 3.51. Comparație între utilizarea variabilelor locale de tip char și int

69
Analizând versiunea din dreapta a programului, din Figura 3.38, ne-am aștepta ca
prin declararea variabilei i de tip char programul să lucreze mult mai eficient deoarece
utilizează un spațiu mai mic de memorie atât în regiștri cât și pe stivă. Dar, în cazul
arhitecturii ARM regiștrii sunt pe 32 de biți, iar fiecare valoare care se depune pe stivă
trebuie să fie pe 32 de biți. Mai mult, pentru incrementarea corectă a variabilei i
compilatorul trebuie să ia în calcul și situația în care i este 255 și la o nouă incrementare a
variabilei i aceasta trebuie să ia valoarea zero – problemă rezolvată prin intermediul
instrucțiunii uxtb47. De aici apare o nouă instrucțiune în plus.
Analizând programul din Figura 3.51 se constată prezența instrucțiunii:
ldr.w r3, [r2, r1, lsl #2]
În această instrucțiune conținutul registrului r1 este deplasat la stânga (se shift-ează, lsl –
logical shift left) cu 2 biți (deci, conținutul variabilei i este înmulțit cu 4) iar rezultatul se
adună cu r2 de unde va rezulta adresa de memorie de unde se preia valoarea care se va
stoca în r3. Din modalitatea de funcționare a acestei instrucțiuni înțelegem necesitatea
existenței modulului Barrel Shifter în schemele interne ale procesoarelor (prezentate în
Figura 3.28, pentru arhitectura ARM, și în Figura 3.29 și Figura 3.30 pentru
arhitecturile Intel) pentru realizarea eficientă a ei.

Figura 3.52. Blocul Barrel Shifter

Modificatorul .w din instrucțiunea anterioară determină compilatorul să genereze o


instrucțiune pe 32 de biți într-o zonă de cod Thumb-2 chiar dacă zona de memorie supusă
acțiunii poate fi accesată utilizând o instrucțiune ldr codată pe 16 biți. Dacă vom găsi o
instrucțiune ldr fără .w aceasta va fi codată întotdeauna48 pe 16 biți într-un segment de
cod de tip Thumb.
În exemplul următor datele de intrare sunt stocate pe 16 biți. Se observă și aici că
compilatorul înserează o nouă instrucțiune în plus sxth (sign extend halfword) prin
intermediul căreia convertește o valoare pe 16 biți la una pe 32 de biți – deci, în final

47
Instrucțiunea uxtb extinde o valoare pe 8 biți către o valoare pe 32 de biți – în cazul nostru particular,
extrage din sursa r1 biții 7...0 și îi depozitează în destinație (pe aceeași poziție), tot registrul r1, și pune
pe zero biții superiori (31 ... 8).
48
Chiar dacă la adresa respectivă de memorie nu se poate ajunge cu o astfel de instrucțiune ce are o astfel
de reprezentare.

70
sistemul va lucra tot pe 32 de biți chiar dacă intermediar datele sunt pe 16 biți.
Pentru evitarea acestei probleme apărute prin existența unei instrucțiuni suplimentarer
putem defini variabila locală sum drept o variabilă de tip int și, în această situație
particulară conversia intermediară va dispare.
int suma_int(int *data)  int suma_short_arg(short *data) 
{  { 
    int i;      int i; 
    int sum = 0;      short sum = 0; 
   
    for (i = 0; i < 64; i++)      for (i = 0; i < 64; i++) 
        sum += data[i];          sum += data[i]; 
    return sum;      return sum; 
} }

mov r2, r0 ; r2 = data mov r2, r0 ; r2 = data


movs r0, #0 ; sum = 0 movs r0, #0 ; sum = 0
mov r1, r0 ;i=0 mov r1, r0 ;i=0
cmp r1, #0x40 ; i < 64 cmp r1, #0x40 ; i < 64
bge $C$L3 ; greater or equal bge $C$L3
$C$L2: $C$L2:
ldr.w r3, [r2, r1, lsl #2] ; r3 = data[i] ldrsh.w r3, [r2, r1, lsl #1] ; r3 = data[i]
adds r0, r3, r0 ; sum += r3 adds r3, r3, r0 ; sum += r3
sxth r0, r3
adds r1, r1, #1 ; i++ adds r1, r1, #1 ; i++
cmp r1, #0x40 ; i < 64 cmp r1, #0x40 ; i < 64
blt $C$L2 ; less than blt $C$L2
$C$L3: $C$L3:
bx lr ; return bx lr ; return

Figura 3.53. Comparație între utilizarea vectorilor de date de tip short și int

int medie_int(int a, int b)  unsigned int medie_uint(unsigned int a,  


                          unsigned int b) 
{  { 
    return (a+b)/2;      return (a+b)/2; 
} } 

adds r1, r1, r0 adds r1, r1, r0


add.w r1, r1, r1, lsr #31
asrs r0, r1, #1 lsrs r0, r1, #1
bx lr bx lr

Figura 3.54. Comparație între utilizarea variabilelor signed int și unsigned int în cadrul
unei împărțiri la o putere a lui doi

Dacă mai sus am dovedit avantajele utilizării tipului de dată int față de tipurile de date
char sau short, în definirea diferitelor tipuri de variabile, în cele ce urmează ne propunem

71
să comparăm eficiența utilizării variabilelor signed int comparativ cu cele unsigned int.
Din punctul de vedere al operațiilor de adunare, scădere și multiplicare nu există
diferențe de performanțe între operațiile cu semn și cele fără semn [ref. bibliog]. Dar
atunci când vorbim de operațiile de împărțire încep să apară diferențe. Vom studia două
situații distincte (a) atunci când utilizăm împărțirea la valori ce sunt puteri ale lui 2 și (b)
când împărțim la alte valori oarecare care nu respectă această regulă (nu sunt puteri ale lui
2).
Din Figura 3.54 observăm că înaintea deplasării rezultatului prin intermediul
instrucțiunii asrs (arithmetic shift right) există o instrucțiune suplimentară pentru
realizarea unei rotiri corecte inclusiv a numerelor cu semn. Deci, din acest punct de
vedere este mai eficient utilizarea valorilor de tip unsigned int în cadrul unei împărțiri la
o putere a lui doi și, cu certitudine, este mai eficient a utiliza împărțiri prin puteri ale
numărului doi (care se transformă în limbaj de asamblare în deplasări la dreapta,
instrucțiuni ce sunt realizate într-un singur ciclu mașină) decât împărțiri la alte valori care
utilizează instrucțiuni precum sdiv (signed divide) sau udiv (unsigned sivide), vezi Figura
3.55, care se execută între minim 2 cicluri mașină și maxim 12 cicluri mașină – numărul
ciclurilor mașină în care se realizează o operație de împărțire este dependent de structura
numerică a datelor care se împart.

int medie_int(int a, int b)  unsigned int medie_uint(unsigned int a, 


                          unsigned int b) 
{  { 
    return (a+b)/3;      return (a+b)/3; 
} } 

adds r1, r1, r0 adds r1, r1, r0


movs r0, #3 movs r0, #3
sdiv r0, r1, r0 udiv r0, r1, r0
bx lr bx lr

Figura 3.55. Comparație între utilizarea variabilelor signed int și unsigned int în cadrul
unei operații de împărțire la un număr oarecare

Execuția repetitivă a instrucțiunilor

În acest subcapitol vom analiza diferite moduri de execuție repetitivă a diferitelor zone
de cod (prin intermediul buclelor for și while) și vom găsi acele modalități cele mai
optime de implementare a acestor secțiuni de cod.
În continuare vom considera aceeași funcție prezentată anterior în care vom
implementa în două moduri diferite bucla for de o așa natură astfel încât ambele
implementări să execute în mod corect programul.
În prima implementare prezentată în Figura 3.56, partea stângă bucla for este translată
în cod asamblare în trei instrucțiuni: (a) în prima se incrementează registrul în care se
stochează valoarea contorului buclei (variabila i), (b) în cea de a doua se verifică dacă
contorul este mai mic decât 64 și (c) în ultima se face un salt pentru a continua execuția
buclei dacă contorul este mai mic decât 64.

72
int suma_for1(int *data)  int suma_for2(int *data) 
{  { 
    unsigned int i;      unsigned int i; 
    int sum = 0;      int sum=0; 
   
    for (i = 0; i < 64; i++)      for (i=64; i!=0; i‐‐) 
        sum += *(data++);          sum += *(data++); 
   
    return sum;      return sum; 
} }

mov r1, r0 ; r1 = data mov r2, r0 ; r2 = data


movs r0, #0 ; sum = 0 movs r1, #0 ; sum = 0
mov r2, r0 ;i=0 mov r0, #0x3F ; i = 63
cmp r2, #0x40 ; i < 64
bhs $C$L3 ; higher or same beq $C$L3 ; equal or zero
$C$L2: $C$L2:
ldr r3, [r1], #4 ; r3 = *(data++) ldr r3, [r2], #4 ; r3 = *(data++)
adds r0, r3, r0 ; sum += r3 adds r1, r3, r1 ; sum += r3
adds r2, r2, #1 ; i++ subs r0, r0, #1 ; i--
cmp r2, #0x40 ; i < 64
blo $C$L2 ; lower bne $C$L2 ; not equal
$C$L3: $C$L3:
bx lr ; return mov r0, r1
bx lr ; return

Figura 3.56. Comparație între două modalități diferite de a coda bucla for

Această implementare nu este una optimală, deoarece se poate realiza totul numai în 2
instrucțiuni – așa cum este prezentat în partea dreaptă a figurii unde prin intermediul
instrucțiunilor subs și bne se implementează practic bucla. Observăm de asemenea că prin
utilizarea pointerilor adunarea elementelor se face în aceeași ordine chiar dacă contorul
buclei este decrementat – oricum pentru această problemă nu are importanță ordinea în
care se face adunarea.
Pentru o variabilă de tip contor fără semn (unsigned int) verificarea condiției de
oprire a buclei i != 0 sau i > 0 va genera același cod. Dar, dacă variabila va fi de tip “cu
semn” (signed int) o condiție de tipul i > 0 va genera codul:
subs r0, r0, #1
cmp r0, #0
bgt $C$L2
Deci, vom ajunge din nou la o implementare neoptimală.
O altă modalitate prin care putem îmbunătăți viteza de execuție a unui program care
conține bucle este dată de a combinarea 2 sau mai multe buclelor existente într-una
singură – deci, vom combina corpurilor celor 2 bucle. De exemplu, în Figura 3.57 codul
din partea stângă se poate foarte ușor transforma în codul din partea dreaptă fără a vicia
sub nici o formă funcționarea programului. În acest mod se elimină trei instrucțiuni, cele

73
în chenar roșu din Figura 3.56. Având în vedere că aceste 3 instrucțiuni se execută de
10000 de ori și, în plus, am putea să rescriem codul buclei (din Figura 3.57 dreapta) ca în
Figura 3.56 (și am mai elimina o instrucțiune) putem observa ușor sporul de performanță
obținut.

for (i = 0; i < 10000; i++)  for (i = 0; i < 10000; i++) { 


       a[i] = b[i] + c[i];         a[i] = b[i] + c[i]; 
for (j = 0; j < 10000; j++)         d[i] = b[i] * c[i]; 
       d[j] = b[j] * c[j]; }

Figura 3.57. Optimizarea programelor prin combinarea buclelor

Mergând mai departe dorim să dezvoltăm programul analizat în Figura 3.56 astfel
încât acesta să primească drept argument numărul variabilelor pe care dorim să le sumăm.
În Figura 3.58 se prezintă două implementări diferite. În versiunea din partea stângă,
implementată cu o buclă for, se observă (în codul generat în limbaj de ansamblare)
existența unei instrucțiuni chiar la început în care se verifică dacă N nu este zero. Pentru a
elimina această verificare, care de cele mai multe ori este inutilă, vom implementa acest
program cu ajutorul unei bucle do while precum în Figura 3.58 în partea dreaptă.

int suma_variab1(int *data,  int suma_variab2(int *data,  


                 unsigned int N)                   unsigned int N) 
{  { 
    int sum=0;      int sum=0; 
       
    for (; N!=0; N‐‐)      do { 
        sum += *(data++);          sum += *(data++); 
      } while (‐‐N != 0); 
    return sum;       
}     return sum; 

mov r2, r0 ; r2 = data mov r2, r0 ; r2 = data
mov r0, r1 ; r0 = N mov r0, r1 ; r0 = N
movs r1, #0 ; sum = 0 movs r1, #0 ; sum = 0
cbz r0, $C$L3 ;compare and branch on zero

$C$L2: $C$L2:
ldr r3, [r2], #4 ; r3 = *(data++) ldr r3, [r2], #4 ; r3 = *(data++)
adds r1, r3, r1 ; sum += r3 adds r1, r3, r1 ; sum += r3
subs r0, r0, #1 ; N-- subs r0, r0, #1 ; N--
bne $C$L2 ; not equal or bne $C$L2 ; not equal or
; non‐zero ; non‐zero
$C$L3:
mov r0, r1 mov r0, r1
bx lr bx lr
Figura 3.58. Optimizarea programelor ce conțin bucle cu număr variabil de parametri

74
Din cele prezentate până acum, putem concluziona că în mod minimal pentru execuția
repetitivă a unei secțiuni de program avem nevoie de 2 instrucțiuni49: prima, necesară
decrementării contorului buclei (care durează un ciclu mașină) și, cea de a doua, este o
instrucțiune de salt condiționat (funcție de valoarea contorului poate dura între 2-4 cicluri
mașină50). Aceste două instrucțiuni sunt suplimentare secțiunii de program în care efectiv
se procesează date. Pentru reducerea impactului acestor instrucțiuni se poate repeta corpul
buclei (secvența de procesare care se tot execută în buclă) de câteva ori.
De exemplu, în Figura 3.59, instrucțiunile din corpul bucelei for se repetă de patru ori
și se pleacă de la presupunerea că numărul valorilor de tip întreg care trebuie sumate este
un multiplu de patru.

push {r4, lr}


mov r4, r0 ;r4 = data
movs r0, #0 ; sum = 0
int suma_paralela(int *data,  
                  unsigned int N)  $C$L2:
{  ldr r2, [r4], #4 ; r2 = *(data++)
    int sum=0;  adds r2, r2, r0 ; r2 = r2 + sum
    do 
        {  ldr r3, [r4], #4 ; r3 = *(data++)
        sum += *(data++);  adds r3, r3, r2 ; r3 = r3 + r2
        sum += *(data++); 
        sum += *(data++);  ldr r2, [r4], #4 ; r2 = *(data++)
        sum += *(data++);  adds r2, r2, r3 ; r2 = r2 + r3
        N ‐= 4; 
    } while (N!=0);  ldr r0, [r4], #4 ; r0 = *(data++)
      adds r0, r0, r2 ; r0 = r0 + r3
    return sum; 
} subs r1, r1, #4 ; N ‐= 4
bne $C$L2 ; not equal or
; non‐zero
pop {r4, pc}

Figura 3.59. Minimizare impactului instrucțiunilor necesare implementării buclei

Prin implementarea din Figura 3.59 se elimină 3 secvențe de instrucțiuni subs și bne
și se economisesc astfel între 9 și 15 cicluri mașină. Luând în calcul că o instrucțiune ldr
se execută în 2 cicluri mașină în timp ce o instrucțiune adds se execută într-un singur ciclu
mașină, corpul buclei care efectiv procesează date durează 12 cicluri mașină. Obținându-
se astfel în medie o dublare a vitezei de execuție a funcției.
O altă metodă de îmbunătățire a performanțelor buclelor ține de interschimbarea
ordinii de execuție a buclelor for sau while. Analizând cele 2 programe prezentate în
Figura 3.60 observăm că prin variabila i parcurgem elementele vectorului linie cu linie,

49
Numărul de cicli mașină ale acestor instrucțiuni sunt prezentate pentru procesoarele Cortex-M3/M4
50
Numărul de cicli mașină ai instrucțiunii de salt condiționat (Cortex-M3/M4): 1 + (1 ... 3). Partea
variabilă, care poate dura minim un ciclu mașină și maxim 3 cicluri mașină este dată de numărul de cicli
necesari reumplerii pipeline-ului procesorului, de capacitatea procesorului de a prezice apariția saltului,
de numărul de biți ai instrucțiunii etc.

75
deci cu pași “mici”, în timp ce prin contorul j parcurgem matricea cu pași mai mari sărind
la următorul element de pe următoarea linie.

for (i = 0; i < 10000; i++){  for (j = 0; j < 10000; j++){ 


  for (j = 0; j < 10000; j++){    for (i = 0; i < 10000; i++){ 
       a[j][i] = 0;}         a[j][i] = 0;} 
} }

Figura 3.60. Optimizarea programelor prin combinarea buclelor

Implementarea din Figura 3.60 dreapta permite o utilizare mai bună a memoriei
cache interne și de aici o viteză mai mare de lucru. Datorită dimensiunii destul de mari a
matricii, pe care dorim să o punem pe zero, circuitele suport ale procesorului vor aduce în
memoria cache o parte cât mai mare din ea. Printr-o parcurgere a matricii element cu
element, totul urmat de elementul liniei următoare avem șanse mult mai mari să putem
șterge întreaga matrice cu timpi de așteptare minimi. Utilizând prima abordare (Figura
3.60 partea stângă), prin saltul cu o linie (10000 de valori de tip întreg) avem șanse mult
mai mari să tot transferăm elemente din memoria RAM în chace și invers de mai multe
ori până vom reuși să punem întreaga matrice pe zero. Pentru a conștientiza mai bine cele
prezentate anterior, menționez că memoria cache de tip date (D-cache) pentru procesorul
Cortex-M4 (funcție de firma care implementează acest nucleu) poate varia între 4 Kbytes
până la 16 Kbytes. O asemenea memorie cache poate stoca la un moment dat între 1024 ...
4096 de valori întregi.
Există multe alte metode utilizate în eficientizarea codului. De exemplu, pentru
creșterea performanțelor, tot codul care este invariant (care nu depinde de variabila contor
a buclei) trebuie plasat în afara bucle. Am văzit anterior că lucrul cu stiva diminuează
viteza de execuție, de aici putem trage o concluzie directă de a diminua cât mai mult
operațiile pe stivă. De exemplu, recursivitatea utilizează foarte mult stiva, de aici putem
trage concluzia că pentru creșterea vitezei de lucru este de preferat să evităm cât de mult
putem recursivitatea ca metodă de implementare a programelor.

76
Alinierea datelor și a structurilor în memorie

Modalitatea în care sunt utilizate și stocate variabilele și structurile în memorie are un


impact foarte puternic asupra densității codului și a vitezei de execuție a acestuia.
Alinierea datelor precizează modalitate în care datele sunt aranjate și accesate în
memoria sistemului. Un procesor va efectua citirile și scrierile datelor într-un mod mai
eficient atunci când datele sunt aliniate în mod natural. Prin alinierea naturală a datelor
înțelegem că adresa de început a variabilei de un anumit tip specific de dată (char, int,
double, etc.) este un multiplu (în octeți) a dimensiunii datei respective. Pentru modurile
de aliniere, corespondența cu tipurile de date și potențialele instrucțiuni de manipulare a
acestor date analizați Tabelul 3.11.

Tabelul 3.11. Tipuri de date și moduri de aliniere


Dimensiune Tip de data
Implementare Tip aliniere Instrucțiuni
transfer în C
signed/unsigned 8 STRB, STRSB,
1 octet char Orice adresă
bits – byte LDRSB, LDRB
signed/unsigned 16
short Multiplu de 2 STRH, STRSH,
2 octeți bits – halfword
octeți LDRH, LDRSH
floating point Half precision 16 bits
signed/unsigned 32
int
bits – word
STR,
signed/unsigned 32 Multiplu de 4
4 octeți long LDRH,
bits – word octeți LDR
Single precision
floating point
(IEEE 754) 32 bits
signed/unsigned 64
long long
bits – double word Multiplu de 8 STRD,
8 octeți
Double precision octeți LDRD
floating point
(IEEE 754) 64 bits

Figura 3.61. Accesarea memorie RAM de către un sistem Cortex-M3/M4 prin


intermediul bus-ului System (bus pe 32 de biți)

77
Procesoarele Cortex-M3/M4 sunt procesoare pe 32 de biți. Aceste procesoare au
spațiul de adresare de 4 Gocteți (232 octeți) și o magistrală de date cu o lățime de 32 biți,
vezi Figura 3.24 și Figura 3.61. Presupunând că avem o variabilă de tip integer stocată
începând cu adresa 8 (vezi Figura 3.61) aceasta va fi transferată într-un singur ciclu de
citire. Dacă, adresa de început a aceleiași variabile ar fi 17 sunt necesare 2 cicluri de citire
(a unor date aliniate) pentru realizarea aceluiași transfer plus un număr de operații de
șiftare și mascare pentru obținerea rezultatului final în mod corect. De aici înțelegem
importanța alinierii datelor.
În principal există 2 abordări în alinierea datelor: (a) soft alignment – alinierea nu este
obligatorie, dar dacă este corect realizată citirea se realizează mult mai repede. De
exemplu, procesoarele familiei Intel sunt de acest tip; și (b) hard alignment, în această
situație alinierea datelor este obligatorie, aceasta este o abordare întâlnită în special la
procesoarele de tip RISC, în cadrul familiei ARM anterior versiunii 6 (ARMv6) datele
trebuiau să fie aliniate pentru un acces corect la ele – în caz contrar rezultatele citirii sau
scrierii erau neasteptate sau nedefinite.
În cele ce urmează pentru înțelegerea mecanismului de aliniere a datelor vom
exemplifica cu un prim program, vezi Figura 3.62. În acest program se definește o
structură de date (care include un char, un short, un alt char și un int) și o serie de
variabile, aceleași ca în interiorul structurii (chiar și poziționate în aceeași ordine) și se va
prezenta modalitate în care compilatorul le alocă în memorie.

typedef struct { 
    char  a; 
    short b; 
    char  c; 
    int   d; 
} sase_val; 
 
sase_val val4; 
 
char  aa; 
short bb; 
char  cc; 
int   dd; 
 
int main() 

    val4.a = 0x55;  val4.b = 0x1020;  val4.c = 0xAA;  val4.d = 0x30405060; 
 
    aa = 0x77;  bb = 0x7080;  cc = 0x99;  dd = 0x90A0B0C0; 
 
    return 0; 
}

Figura 3.62. Un simplu cod utilizat în analiza mecanismului de aliniere

În cadrul compilatorului Code Composer Studio pentru a prezenta modalitate de


stocare a datelor în memorie avem nevoie de adresa de început a blocului unde acestea
sunt stocate. Pentru a afla această adresă trebuie, în primul rând, să o punem în fereastra
de Watch, iar apoi în ultimul câmp vom afla adresa variabilei respective, Figura 3.63.

78
Figura 3.63. Modalitatea de afișare a zonelor ocupate în memorie de diferitele variabile

Deoarece compilatorul generează un cod care să fie accesat cât mai eficient (cu o
viteză maximă posibilă) va alinia datele de o așa natură astfel fiecare dintre ele să fie
aliniate în mod natural – de exemplu, tipul de dată short să înceapă de la adrese care sunt
multiplu de 2, tipul de date int să înceapă de la adrese care sunt multiplu de 4 etc., precum
este prezentat în Figura 3.61. Observăm că pentru atingerea acestui obiectiv, alinierea
naturală a datelor, compilatorul înserează o serie de elemente tampon (padding).

Adresa de bază: 0x2001D038

+3 +2 +1 +0
+0 b[15, 8] = {0x10} b[7, 0] = {0x20} 0 a = {0x55}
+4 0 0 0 c = {0xAA}
+8 d[23, 16] = {0x30} d[23, 16] = {0x40} d[15, 8] = {0x50} d[7, 0] = {0x60}

Figura 3.64. Modalitatea de stocare a datelor structurii

În schimb dacă analizăm modalitate de alocare a variabilelor globale aa, bb, cc și dd


(același lucru s-ar fi întâmplat și dacă aceste variabile ar fi fost locale) observăm că acest
fenomen, de înserare de zone tampon (de padding), nu mai există sau va fi mult diminuat
iar compilatorul grupează datele de o așa natură astfel încât să economisească și spațiul de
memorie și să permită și realizarea unei viteze maxime de accesare a lor, vezi Figura 3.63
și Figura 3.65.

Adresa de bază: 0x2001D044

79
+3 +2 +1 +0
+0 dd[23, 16] = {0x90} dd[23, 16] = {0xA0} dd[15, 8] = {0xB0} dd[7, 0] = {0xC0}
+4 cc = {0x99} aa = {0x77} bb[15, 8] = {0x70} bb[7, 0] = {0x80}

Figura 3.65. Modalitatea de stocare a datelor globale

Din cele prezentate putem concluziona că compilatorul va încerca întotdeauna să


optimizeze plasarea variabilelor locale și globale în memorie dar nu va face același lucru
și pentru variabilele din interiorul structurilor de date. Dar oare de ce se întâmplă acest
lucru? Cel mai simplu răspuns ar fi că atât standardul C cât și standardul C++ interzic o
astfel de optimizare. În timp ce alte limbaje o permit. Pentru o înțelegere a acestei situații
se prezintă în Figura 3.66 antetul (hederul) unui pachet de tip IP (header of an IP
datagram) în timp ce cea de a doua structură reprezintă heder-ul unui fișier de tip ZIP.

struct ip   

#if BYTE_ORDER == LITTLE_ENDIAN 
    unsigned int ip_hl:4,     /* header length */ 
             ip_v:4;          /* version */ 
#endif 
#if BYTE_ORDER == BIG_ENDIAN 
    unsigned int ip_v:4,      /* version */ 
             ip_hl:4;         /* header length */ 
#endif 
    u_int8_t  ip_tos;         /* type of service */ 
    u_int16_t ip_len;         /* total length */ 
    u_int16_t ip_id;          /* identification */ 
    u_int16_t ip_off;         /* fragment offset field */ 
    u_int8_t  ip_ttl;         /* time to live */ 
    u_int8_t  ip_p;           /* protocol */ 
    u_int16_t ip_sum;         /* checksum */ 
    struct    in_addr ip_src, ip_dst; /* source and dest address */ 
} __packed;

struct __attribute__((__packed__)) LocalFileHeader { 
    uint32_t signature; 
    uint16_t minVersion, flag, method, modTime, modDate; 
    uint32_t crc32, compressedSize, uncompressedSize; 
    uint16_t nameLength, extraLength; 
};

Figura 3.66. Prima structură reprezintă antetul (hederul) unui pachet de tip IP (header of
an IP datagram) în timp ce cea de a doua structură reprezintă hederul unui fișier de tip ZIP

Deci, la recepționarea unui flux de date, de la conexiunea de internet sau la citire


datelor de pe hard disk, vom dori să găsim o cale de a identifica semnificația diferitelor
valori din aceste fluxuri de date cu diferitele caracteristici specifice care sunt stocate în
variabilele structurilor asociate. Daca compilatorul ar optimiza aceste variabile din
structuri prin schimbarea ordinii lor, pentru a ocupa spațiul minim atunci aceste date nu ar
mai fi inteligibile.

80
O altă cauză a interzicerii acestor optimizări în C și C++ este dată de structurile cu
lungime variabilă care se definesc precum în Figura 3.67.

   
struct PERSON    
{  struct student *createStudent(struct student *s, 
   unsigned stud_id;                 int id, char a[]) 
   int  name_len;  { 
   int  struct_size;      s = malloc( sizeof(*s) +  
   char name[]; // vector fără                   sizeof(char) * strlen(a) ); 
};              // dimensiune       
      s‐>stud_id = id; 
      s‐>name_len = strlen(a); 
struct student      strcpy(s‐>stud_name, a); 
{       
   int  stud_id;      s‐>struct_size = ( sizeof(*s) +  
   int  name_len;             sizeof(char) * strlen(s‐>stud_name) ); 
   int  struct_size;       
   char stud_name[0];      return s;     
};    // ultimul element vector    }
      // cu dimensiune 0 !!!! 

Figura 3.67. Structuri cu lungime variabilă

În cadrul structurilor cu lungime variabilă există o regulă de bază: elementul cu


lungimea variabilă trebuie, în mod obligatoriu, să fie ultimul element al structurii. Dacă s-
ar permite compilatorului să reorganizeze variabilele pentru ca structura să ocupe spațiul
minim de memorie și toate variabilele să fie aliniate (viteză de accesare maximă) atunci
această regulă ar fi încălcată și, mai mult, ar fi foarte dificil de accesat datele dacă nu s-ar
ști în mod exact ordinea lor sau nu s-ar stoca cumva dimensiunea variabilă a elementului.
Dacă am reorganiza datele din structură și am redefini structura din Figura 3.62 sub
următoarea formă:

typedef struct { 
    char  a; 
    char  c; 
short b; 
    int   d; 
} sase_val; 

atunci vom obține alocare a datelor în memorie în conformitate cu Figura 3.68. În acest
mod dimensiunea structuri va scădea de la 12 octeți (Figura 3.64) la doar 8 octeți (Figura
3.68). Acum ne putem imagina că avem un vector de astfel de elemente și putem vedem
astfel că la fiecare element vom economisi 4 octeți – iar la 1024 de lemente vorbim deja
de 4 Kocteți de date. De asemenea, din Figura 3.64, Figura 3.65 și Figura 3.68 mai
observăm că modalitatea de alocare a datelor în memorie este de tipul Little Endian.
Din cele prezentate anterior putem concluziona că este o foarte bună idee să grupăm
variabilele de acceași dimensiune astfel încât compilatorul să nu introducă elementele de
tip tampon care determină o creștere artificială a dimensiunii structurii.

81
Adresa de bază
+3 +2 +1 +0
+0 b[15, 8] = {0x10} b[7, 0] = {0x20} c = {0xAA} a = {0x55}
+4 b[23, 16] = {0x30} b[23, 16] = {0x40} b[15, 8] = {0x50} b[7, 0] = {0x60}

Figura 3.68. Modalitatea de stocare a datelor structurii rearanjate

Din Figura 3.64 putem concluziona și că alinierea datelor în cadrul unei structuri
influențează în mod direct dimensiunea acelei structuri.
Putem realiza această grupare automată a datelor prin directivele #pragma
pack(push,1) și __attribute__((packed)) așa cum este prezentat în Figura 3.69.

  #pragma pack(push,1) 
typedef struct {  typedef struct { 
    char  a;      char  a; 
    short b;      short b; 
    char  c;      char  c; 
    int   d;      int   d; 
} __attribute__((packed)) sase_val; } sase_val; 
#pragma pack(pop)

Figura 3.69. Modalitatea de împachetare automată a datelor structurii

Din păcate această metodă automată de eliminare a valorilor tampon determină ca datele
să nu mai fie corect aliniate, precum variabila b, de aici rezultând timpi mari de întârziere
la accesarea acestor variabile.

Adresa de bază: 0x2001D038


+3 +2 +1 +0
+0 c = {0xAA} b[15, 8] = {0x10} b[7, 0] = {0x20} a = {0x55}
+4 d[23, 16] = {0x30} d[23, 16] = {0x40} d[15, 8] = {0x50} d[7, 0] = {0x60}

Figura 3.70. Alocarea variabilelor la împachetare automată a datelor structurii

int main()
 

typedef struct { 
    val4.a = 0x55; 
    char  a; 
    val4.b = 0x10203040; 
    int   b; 
    val4.c = 0x5060; 
    short c; 
    val4.d = 0xAA; 
    char  d; 
} __attribute__((packed)) sase_val;     return 0; 
}

Figura 3.71. Modalitatea de împachetare automată și inițializare a datelor structurii

Și încă această situație nu este cea mai defavorabilă posibilă. În situația în care în
structura de date, variabilele ar fi grupate ca în Figura 3.71 și inițializate ca în aceeași

82
figură, atunci din Figura 3.72 observăm că atât variabila b (de tip int) cât și variabila c
(de tip short) sunt aliniate în mod necorespunzător iar citirea și scrierea lor se va realiza
mult mai lent comparativ cu variabilele aliniate în mod corect.

Adresa de bază: 0x2001D038


+3 +2 +1 +0
+0 b[23, 16] = {0x20} b[15, 8] = {0x30} b[7, 0] = {0x40} a = {0x55}
+4 d = {0xAA} c[15, 8] = {0x50} c[7, 0] = {0x60} b[23, 16] = {0x10}

Figura 3.72. Alocarea variabilelor la împachetare automată a datelor


structurii din Figura 3.71

Din toate exemplele de până acum observăm că niciodată variabilele de tip char nu pot
fi aliniate necorespunzător și sunt singurele variabile care indiferent de plasarea lor în
memorie vor avea în mod continuu viteza maximă de accesare.

83
Alocarea regiștrilor

În cadrul corpului funcțiilor, compilatorul va încerca să aloce fiecare variabilă unui


registru. Mai mult, dacă numărul variabilelor locale funcției este mai mare decât cel al
regiștrilor, compilatorul va încerca să aloce aceeași registru mai multor variabile dar fără a
apărea suprapuneri și păstrând funcționarea corectă a funcției. Dacă numărul variabileor
locale este mai mare decât numărul regiștrilor existenți, iar operația de alocare a unui
registru la mai multe variabile nu mai poate fi utilizată, atunci aceste variabile vor fi
alocate pe stivă.
Pentru implementarea cât mai eficientă a unei funcții programatorul trebuie să țină
cont măcar de următorii doi factori: (a) să minimizeze numărul variabilelor alocate pe
stivă și (b) cele mai utilizate variabile să fie stocate în regiștri.
În Tabelul 3.12 se precizează rolul fiecărui registru în conformitate cu standardul
AAPCS. Din tabel se observă că doar 11 regiștri, cu certitudine, pot stoca variabile.
Registrul r9 putând fi sa nu utilizat în această direcție. De exemplu, funcție de sistemul de
operare care rulează pe procesor acest registru poate lua funcția de thread register (TR)
indicând zona de date locală firului de execuție.

Tabelul 3.12. Utilizarea regiștrilor procesorului conform cu AAPCS

În mod tipic doar regiștrii r4 – r8, r10 și r11 pot stoca variabilele locale ale unei
funcții. Dintre acești regiștri doar r4 – r7 sunt regiștrii care pot fi utilizați de întregul set
de instrucțiuni Thumb – deci, de aici apar alte limitări. Mai mult, toate funcțiile trebuie să
păstreze nemodificată starea regiștrilor r4 – r8, r10, r11, SP și r9 (atunci când este utilizat

84
ca registru pentru stocarea variabilelor locale ale funcției) – toți acești regiștri trebuie să-și
păstreze valoare la ieșirea din funcție identică cu cea de la intrarea în funcție.
Cu toate că aparent avem la dispoziție 7 regiștri dedicați cu certitudine stocării
variabilelor locale, în calculul expresiilor complexe compilatorul asociază diferiți regiștri
diferitelor calcule intermediare.

Înmulțirea și împărțirea

Operațiile de înmulțire și de împărțire sunt unele dintre cele mai costisitoare funcții
(din punctul de vedere a numărului de cicluri mașină) care se pot executa pe
microcontrolere. Există foarte multe microcontrolere care nici nu au implementate
instrucțiuni de împărțire sau de înmulțire – în aceste cazuri, cand se utilizează o astfel de
operație se apelează o funcție de bibliotecă care ocupă mulți octeți de cod și durează
multe cicluri mașină. În asemenea situații este de dorit evitarea folosirii acestor operații.
În situația în care operațiile de înmulțire sau împărțire nu pot fi evitate este de preferat
ca acestea să se facă cu valori care sunt puteri ale lui 2. Astfel de operații realizate pe
numere întregi se pot implementa foarte ușor prin deplasări la stânga sau la dreapta a
informației
De exemplu, în relațiile (3.1) și (3.2) se realizează înmulțiri cu 2 și 8 a unei valori
întregi stocate în variabila A prin deplasări la stânga.
A = A << 1; (3.1)

A = A << 3; (3.2)
Dacă se combină deplasările și cu operații de adunare putem realiza înmulțiri cu 6 (B
* 6 = B * 4 + B *2) sau 10 (C * 10 = C * 8 + C * 2) prin intermediul relațiilor prezentate
în ecuațiile (3.3) și (3.4).
B = (B << 2) + (B << 1); (3.3)

C = (C << 3) + (C << 1); (3.4)


În mod similar se pot implementa împărțiri la diferite numere, ce sunt puteri ale lui 2,
prin deplasări la dreapta. De exemplu, în relațiile (3.5) și (3.6) se realizează împărțirea
numerelor D și E la 2 și la 8 prin rotiri la dreapta o singură dată (pentru împărțirea la 2)
sau de 3 ori pentru o împărțire la 8.
D = D >> 1; (3.5)

E = E >> 3; (3.6)
Combinând în mod ingenios operațiile de înmulțire (implementate prin rotiri) sau
adunare putem obține operații de împărțire la diferite numere chiar și subunitare. De
exemplu, ne propunem să împărțim un număr întreg la 0.4, aceasta este echivalent cu a-l
împărți la 2/5 sau cu a-l înmulți cu 5 și împărți la 2. Pașii intermediari pentru
implementarea împărțirii la 0.4 sunt prezentați în realțiile (3.7) și (3.8).
F = (F << 2) + F; (3.7)

85
F = F >> 1; (3.8)
O modalitate elegantă de a implementa împărțirea la trei este următoarea:
𝐺
𝐺 ⁄4 𝐺 ⁄16 𝐺 ⁄64 ⋯ 0.3333 ∙ 𝐺 (3.9)
3
Cu cât utilizăm mai mulți termeni în suma din relația (3.9) cu atât aproximarea numărului
0.3333...3 va fi mai exactă. De exemplu, utilizând un singur termen vom obține 0.25,
pentru doi termeni obținem 0.3125, pentru trei termeni 0.328125, în timp ce cu patru
termeni vom obține 0.332 etc. Deoarece toate operațiile de împărțire sunt realizate cu
valori care sunt puteri ale numărului 2, implementarea tuturor acestora se va face foarte
ușor prin operații de deplasare la dreapta.
Există multe alte optimizări a diferitelor împărțiri, funcție de valoare la care vom
împărți. În general toate aceste cazuri trebuie analizate separat. De exemplu, dacă într-un
program avem împărțiri repetate la un număr x pentru creșterea vitezei vom calcula 1/x,
stocând rezultatul într-o variabilă, și apoi vom tot refolosi această valoare transformând
împărțirile multiple într-o împărțire urmată de mai multe înmulțiri. Ținând cont că
procesorul Cortex-M4 poate realiza o înmulțire într-un singur ciclu mașină, observăm
imediat sporul de performanță pe care îl putem obține foarte ușor prin aplicarea acestei
metode.
Una dintre cele mai lente funcții este cea modulo. Cu toate că este utilizată pe larg în
diferite operații de procesare a semnalelor (de exemplu, filtrări FIR sau IIR)
implementarea acestei funcții în limbaj de asamblare durează un număr foarte mare de
cicli mașină – între 6 și 16 cicluri mașină, vezi partea stângă a Figura 3.73. Aici această
variabilitate a numărului de cicluri mașină este dată structura numerelor care se împart,
deci de instrucțiunea sdiv. Calculul numărului de cicluri a fost realizat ținând cont că
programul rulează pe un Cortex-M4 care implementează, datorită propriilor capabilități
de tip DSP, înmulțirea într-un singur ciclu mașină.

int modulo(int contor)  int modulo(int contor) 
{  { 
    contor = (contor+1) % 33;      if (++contor >= 33) contor = 0; 
   
    return contor;      return contor; 
} }

movs r2, #0x21 ; r2 = 33 adds r0, r0, #1 ; r0 = ++contor


adds r0, r0, #1 ; contor = contor + 1 cmp r0, #0x21 ; contor >= 33
sdiv r1, r0, r2 ; r1 = round (r0 / r2) blt $C$L2 ; less than
muls r1, r2, r1 ; r1 = 33 round (r0/33) movs r0, #0 ; contor = 0
subs r0, r0, r1 ; r0 = contor – r1
$C$L2:
bx lr bx lr

Figura 3.73. O comparație între două modalități de implementare a funcției modulo

86
În cea de a doua versiune a implementării, din Figura 3.73 partea stângă, întreaga
structură de instrucțiuni durează minim 4 cicluri mașină și maxim 6 cicluri mașină. Aici
variabilitatea de execuție a secvenței de cod este dată de instrucțiunea blt care determină
disfuncționalități în cadrul pipeline-ului.

87
Capabilitățile de tip DSP ale procesorului Cortex-M4

Procesorul Cortex-M4 înglobează un modul pe 32 de biți capabil să efectueze operații


de tip MAC (multiply and accumulate) într-un singur ciclu mașină. La limita superioară
acest modul efectuează operații de forma 32 x 32 + 64 → 64, fiind configurabil și
permițând înmulțiri pe 16 sau 32 de biți și operații de acumulatre (de adunare a
rezultatului obținut în urma înmulțirii) pe 32 sau 64 de biți. Datorită acestui grad de
flexibilitate, cu același modul de tip MAC se pot efectua două operații 16 x 16 într-un
singur ciclu mașină.
Setul SIMD include o paleta largă de operații de tip adunare, scădere, înmulțire sau
înmulțire și adunare utilizate în implementarea unui mare număr de operații de tip DSP
(precum, filtrări – FIR sau IIR, transformate FFT complexe, PID sau operații de tip
adunări, scăderi sau multiplicări de matrici). Acest operații pot fi efectuate în parallel pe
patru valori (de 8 biți) sau două valori (pe 16 biți). Fiecare instrucțiune din setul de
înstrucțiuni de tip SIMD se execută într-un singur ciclu mașină.

Tabelul 3.13. Instrucțiunile de tip DSP specifice arhitecturi Cortex-M4


Operație Instrucțiune/instrucțiuni Cicli
16 x 16 = 32 SMULBB, SMULBT, SMULTB, SMULTT 1
16 x 16 + 32 = 32 SMLABB, SMLABT, SMLATB, SMLATT 1
16 x 16 + 64 = 64 SMLALBB, SMLALBT, SMLALTB, SMLALTT 1
16 x 32 = 32 SMULWB, SMULWT 1
(16 x 32) + 32 = 32 SMLAWB, SMLAWT 1
(16 x 16) ± (16 x 16) = 32 SMUAD, SMUADX, SMUSD, SMUSDX 1
(16 x 16) ± (16 x 16) + 32 = 32 SMLAD, SMLADX, SMLSD, SMLSDX 1
(16 x 16) ± (16 x 16) + 64 = 64 SMLALD, SMLALDX, SMLSLD, SMLSLDX 1
32 x 32 = 32 MUL 1
32 ± (32 x 32) = 32 MLA, MLS 1
32 x 32 = 64 SMULL, UMULL 1
(32 x 32) + 64 = 64 SMLAL, UMLAL 1
(32 x 32) + 32 + 32 = 64 UMAAL 1
2 ± (32 x 32) = 32 (upper) SMMLA, SMMLAR, SMMLS, SMMLSR 1
(32 x 32) = 32 (upper) SMMUL, SMMULR 1

Operații de tip SIMD definesc procesorul Cortex-M4 ca un procesor vectorial. Un


procesor vectorial este un CPU proiectat să execute instrucțiuni și operații nu numai
asupra unei singure valori (cum sunt procesoarele scalare) ci și asupra unei mulțimi de
valori simultan. Actualmente prin intermediul instrucțiunilor de tipul SIMD cea mai mare
parte a procesoarelor sunt de tip vectorial. Exemplele cele mai cunoscute de astfel de
seturi de instrucțiuni sunt SSE (Streaming SIMD Extensions) și AltiVec (dezvoltat de
Apple, IBM și Freescale Semiconductor). Setul de instrucțiuni AltiVec este implementat
în diferite versiuni de procesoare ce aparțin familiei PowerPC. De la prima lansare, setul
de instrucțiuni SSE (dezvoltat de Intel) a fost îmbunătățit continuu, rezultând SSE2
(introdus pentru prima dată în procesoarele de tip Pentium 4). SSE2 acceptă pentru prima
dată înmulțiri în dublă precizie (64 de biți), SSE3 (cunoscut sub numele PNI - Prescott
New Instructions este o îmbunătățire incrementală a SSE2), SSSE3 (Supplemental
Streaming SIMD Extension 3, de asemenea o îmbunătățire incrementală a SSE3) și în
final SSE4 (este o îmbunătățire majoră a setului de instrucțiuni SSSE4 și cunoaște un

88
număr de trei versiuni SSE4.1, SSE4.2 și SSE4a). Ulterior Intel a continuat îmbunătățirea
instrucțiunilor de tip SIMD prin intermediul extensiilor AVX (Advanced Vector
Extensions), AVX2 (care introduce operațiile de tip FMA - fused multiply-accumulate) și
în final AVX-512.
Considerând următoarea operație aritmetică:
Sumă = Sumă + (A x C) + (B x D) (3.10)
în care se realizează două înmulțiri pe 16 biți și 2 adunări pe 32 de biți, observăm
complexitatea operațiilor realizate într-o singură instrucțiune.

Figura 3.74. Modalitatea de împachetare a datelor pentru operația matematică prezentată


anterior

Studiu de caz: De exemplu, pentru decodarea unui fișier MP3 procesorul Cortex-M3
trebuie să lucreze la o frecvență de 20-25 MHz în timp ce un procesor Cortex-M4
ce utilizează instrucțiunile de tip SIMD necesită o frecvență de tact de 10-12
MHz.
Tabelul 3.14. Comparație Cortex-M3 versus Cortex-M4
Cortex-M3 Cortex-M4
xN = *x++; 2 2
yN = xN * b0; 4-7 1
yN += xN1 * b1; 4-7 1
yN += xN2 * b2; 4-7 1
yN -= yN1 * a1; 4-7 1
yN -= yN2 * a2; 4-7 1
*y++ = yN; 2 2
xN2 = xN1; 1 1
xN1 = xN; 1 1
yN2 = yN1; 1 1
yN1 = yN; 1 1
Decrementare
1 1
contor buclă
Verificare contor și
2 2
salt

Studiu de caz: În cele ce urmează se evidențiază avantajele utilizării unui procesor


Cortex-M4, față de utilizarea unui procesor Cortex-M3, în implementarea unui

89
simplu filtru IIR cu 5 coeficienți. Deci, vom analiza următoarea implementare
discretă a filtrului:
𝑦𝑛 𝑏 𝑥𝑛 𝑏𝑥𝑛 1 𝑏 𝑥𝑛 2 𝑎 𝑦𝑛 1 𝑎 𝑦𝑛 2 (3.11)
În continuare vom prezenta numărul de cicluri mașină necesari implementării
și execuției relației (3.11) de către cele 2 procesoare: Cortex-M3 și Cortex-M4.
Pentru simplitatea expunerii, se vor prezenta în partea stângă reprezentările în cod
ANSI C a instrucțiunilor ce se vor executa de către ambele procesoare și nu codul
în limbaj de ansamblare, vezi Tabelul 3.14.
Analizând datele din tabel se constată că procesorului Cortex-M4 îi sunt
necesari 16 cicli mașină pentru obținerea unui singur eșantion filtrat, în timp ce
procesorului Cortex-M3 îi sunt necesari 31-46 cicli mașină. Această variabilitate a
numărului de cicli mașină necesari finalizării operației pentru procesorul Cortex-
M3 se datorează faptului că durata operației de înmulțire (pentru instrucțiunea
SMLAL avem un necesar de 4-7 cicluri mașină pentru finalizare) este dependentă
de datele care se înmulțesc. Deci, vom obținue o creștere a vitezei de execuție cu
un coeficient de 1.9...2.9 doar prin utilizarea, în principal, a multiplicatorului
hardware capabil să realizeze înmulțirea într-un singur ciclu mașină.

Embedded Trace Macrocells

În cadrul procesorului ARM, tehnica Embedded Trace MacrocellsTM (ETM) oferă


utilizatorilor facilități de urmărire și de analiză hardware a programelor în timp real în
vederea identificării erorilor din cadrul acestora. Această tehnică permite ca starea internă
a procesorului să fie preluată înaintea sau după realizarea unui anumit eveniment fără însă
a încărca computațional în plus procesorul, în timp ce acesta rulează la viteza sa normală
de funcționare (de exemplu, la cea maximă).
Capturarea stării procesorului (de exemplu, a valorii unor anumiți regiștri) poate fi
configurată software pentru a obține astfel o informație specifică și numai în momentul în
care anumite condiții sunt îndeplinite.
În plus, această tehnică neinvazivă de depanare a codului unui program este extinsă
prin introducerea unor zone de memorie locale procesorului - Embedded Trace Buffer
(ETB) – unde informațiilor ce privesc starea procesorului vor fi stocate și ulterior
transferate către utilizator. În acest mod nu mai este necesar transferul imediat de date la
viteze foarte mari. Toate aceste date sunt transferate ulterior către utilizator prin
intermediul unui port JTAG51.
Avantajul fundamental al acestei tehnici este dat de faptul că atunci când se analizează
un program nu vor exista diferențe foarte mari între varianta lui de tip debug față de

51
Standardul JTAG (Joint Test Action Group) a fost propus de un grup de producători începând cu anul
1985 pentru a testa diferitele plăcile cu circuite integrate realizate în tehnologii multistrat după lipirea
componentelor. Testarea s-a dorit a se realiza din interiorul circuitului integrat. S-a urmărit inițial doar
testarea continuității traseelor și a conexiunilor realizate – operație dificilă pe plăcile de cablaj
multistrat. Ulterior datorită posibilității accesării din interior a diferitelor blocuri ale circuitului integrat
standardul s-a dezvoltat, iar actualmente el este utilizat și în depanarea codului programelor dezvoltate.

90
varianta release. În acest mod, de exemplu, analizele de tip profiling52 vor reflecta mult
mai corect starea programului și performanțele acestuia.
Procesoarele din familia Intel, x86, nu prezintă acest mod de analiză directă a stării
interne a procesorului. În momentul în care programul este în modul debug mediul de
dezvoltare înserează o cantitate mare de cod care are drept unică destinație atingerea
obiectivelor acestui mod de rulare.

O scurtă comparație între dispozitivele familiei CC3200

SoC-urile din familia CC32xx, vezi Tabelul 3.15, fac parte din platforma de
dispozitive de tip SimpleLink care este formată din SoC-uri de putere redusă, ce au
capacitatea de a se conecta la internet prin intermediul unei conexiuni Wi-Fi standard, ce
lucrează pe o frecvență de 2.4 GHz.
Această familie de SoC este ideală atunci când se dorește dezvoltare de dispozitive de
tip IoT care să nu necesite existența unui sistem de operare de tip Linux și care să ofere
simultan și nivele foarte mici de consum. De exemplu, consumul unui astfel de dispozitiv
este de zeci de miliamperi în timpul transferurilor de date (cu un consum mai mare pe
transmiterea lor – așa cum era și de așteptat) și, acest consum scade, la 120 uA atunci
cand dispozitivul rămâne conectat la Access Point dar nu schimbă date cu acesta.

Figura 3.75. Schema bloc internă a SoC-urilor din familia CC32xx

Procesorul Wi-Fi (Network Processor-ul din Figura 3.75) integrează o mare parte din
stiva protocoalelor Wi-Fi și Internet minimizând astfel încărcarea procesorului principal –
ARM Cortex-M4.
Un aspect interesant al acestei familii de SoC-uri este modulul de management al
puterii consumate care înglobează un convertor DC-DC și care astfel permite un domeniu
al tensiunilor de alimentare destul de larg: 2.1V ... 3.6V.
52
Tehnică de analiză prin intermediul căreia se urmărește determinarea duratei de execuție a unei anumite
subrutine sau a unui bloc de instrucțiuni ce aparțin unui program.

91
Cu toate că SoC-urile familiei CC32xx au înglobate intern atât memorie de tip ROM
cât și memorie RAM acestea sunt utilizate într-un mod cu totul particular. Codul este
conținut într-o memorie Flash externă și este încărcat în memoria RAM (SRAM) internă
pentru a fi executat. În memoria ROM este stocat boot loader-ul (o secțiune de cod
utilizată în pornirea sistemului – în procesul de boot) și DriverLib (o librărie utilizată în
lucrul cu dispozitivele periferice – peripheral driver library). Boot loader-ul este
responsabil de asemenea cu scrierea imaginii aplicației împreună cu orice alte fișiere pe
care utilizatorul dorește să le utilizeze în memoria Flash externă de tip serial. Tot Boot
loader-ul este acela care încarcă această imagine a programului în memoria RAM și,
ulterior, îi oferă controlul.

Tabelul 3.15. Comparație între SoC-urile familiei CC32xx


CC3200 CC3220 CC3220S CC3220SF
Device Type SimpleLink Wireless MCU
RAM (KB) 256
Flash (KB) - - - 1000
Throughput 16 (UDP)
(Max) (Mbps) 13 (TCP)
Cryptographic acceleration
Networking security
Secure FW and SW update
Device identity
- External memory protection
Security Physical security
Enabler Debug security
Initial secure programming
Secure boot
- - Secure storage
Software IP protection
Trusted execution environment
802.11bgn
STA
AP
IPv4
Direct Mode
SmartConfig
Features AP & WPS Provisioning
IPv6
2-Secure Sockets 6-Secure Sockets
1-STA & Wi-Fi 4-STA's & Wi-Fi
Low-power Wi-Fi Optimized Low-Power and Enhanced Security capabilities

Operating
Temperature -40 to 85
Range (C)
Package (mm) 9 × 9 QFN, 0.5 Pitch, 64-pin

Prin înglobarea în memoria ROM a librăriei perifericelor se obține o reducere a


dimensiunii programelor și astfel se permite programelor să elibereze din memoria RAM

92
care astfel poate fi utilizată în alte scopuri. Funcțiile din această librărie sunt link-editate
cu aplicația utilizatorului.
Mai jos se prezintă API-urile care sunt suținute de funcțiile existente în ROM prin
intermediul DriverLib:
 ADC_Analog_to_Digital_Converter_api
 AES_Advanced_Encryption_Standard_api
 Camera_api
 CRC_Cyclic_Redundancy_Check_api
 DES_Data_Encryption_Standard_api
 Flash_api
 GPIO_General_Purpose_InputOutput_api
 HwSpinLock_api
 I2C_api
 I2S_api
 Interrupt_api
 Pin_api
 PRCM_Power_Reset_Clock_Module_api
 Secure_Digital_Host_api
 SHA_Secure_Hash_Algorithm_api
 SPI_Serial_Peripheral_Interface_api
 Systick_api
 GPT_General_Purpose_Timer_api
 UART_api
 UDMA_Micro_Direct_Memory_Access_api
 Utils_api
 WDT_Watchdog_Timer_api

Acceleratorul criptografic

În Figura 3.76 se prezintă o schemă bloc mai detaliată (comparativ cu cea prezentată
în Figura 3.75) a blocurilor interne existente în cadrul SoC-urilor familiei CC3200. Unul
din aceste blocuri este cel care va fi analizat în acest subcapitol: acceleratorul
criptografic.

O scurtă introducere în criptografie

Termenul criptografie este de origine grecească și este format din două cuvinte:
kryptos care înseamnă ascuns și graphein care înseamnă a scrie. În principal criptografia
se ocupă cu analiza și dezvoltarea de metode matematice destinate scrierii și citirii
mesajelor criptate dar și de aflare a cheii de criptare.
Criptografia este o componentă a unui domeniu mult mai larg, numit securitatea
informației. Obiectivele urmărite de acesta pot fi sumarizate în:

93
1. Confidențialitatea (privacy) este proprietatea de a păstra secretul informaței,
pentru ca aceasta să fie folosită numai de persoanele autorizate.
2. Integritatea datelor este proprietatea de a evita orice modificare (înserare, ștergere
sau substituire) neautorizată a informației.
3. Autentificare este proprietatea de a identifica o entitate conform anumitor
standarde. Este compusă din: (a) autentificarea unei entități și (b) autentificarea
sursei informației.
4. Non-repudierea este proprietatea care previne negarea unor evenimente anterioare.

Figura 3.76. O schema bloc internă mai detaliată a SoC-urilor familiei CC3200

94
Criptanaliza reprezintă o metoda de analiză a mesajelor codificate având drept scop
decodarea mesajelor prin intermediul vulnerabilităților algoritmilor de criptare sau prin
alte metode (de exemplu prin forță brută). Atacul prin forță brută (brute force) este una
din cele mai simple metode utilizate în decriptare și constă în parcurgerea tuturor cheilor
posibile și verificarea textului criptat până se găsește cheia corectă. Deoarece există
întotdeauna o cheie utilizată în criptare el reușește totdeauna. Din acest motiv trebuie
utilizate sisteme de criptare care să aibă chei de un cardinal cât mai mare.

Figura 3.77. Situția generică a criptografiei

În situația cea mai simplă și mai generală expeditorul dorește să trimită destinatarului
un mesaj (email, fișier, imagine, secvență vocală etc.) prin intermediul unui canal
(internet, telefonie mobilă etc.) care nu este sigur. Insecuritatea este dată de un criptanalist
care dorește să cunoască mesajul (modul pasiv) sau să-l modifice (modul activ – în care se
modifică mesajul, se schimbă timpul de expediere, destinatarul sau expeditorul etc.) și să-l
trimită mai departe chiar dacă acesta nu-i este destinat. Dar mai există o situație, atunci
când expeditorul neagă că a trimis anumite mesaje – în această situație trebuie introduse
anumite mecanisme de protecție care să permită autentificarea expeditorului (non-
repudiere).
Expeditorul deține mesajul în forma sa originală – acest mesaj este denumit text clar
(plaintext). Expeditorul, utilizând un anumit algoritm (algorithm sau cipher) cunoscut
numai de el (eventual și de destinatar) rescrie acest mesaj, deci îl criptează sau îl cifrează,
obținând un anumit text criptat. În plus, algoritmul de criptare mai utilizează și o cheie
împreună cu textul în clar pentru a obține mesajul criptat. Dacă algoritmii de criptare sunt
binecunoscuți confidențialitatea mesajului stă în cheia de criptare. În general este dificil să
ții secret algoritmul de criptare (deși nu imposibil pentru un anumit interval de timp) – o
astfel de securitate bazată pe o asemenea metodă se numește securitate prin obscuritate.
Compromiterea unui algoritm poate avea un impact mult mai mare decât
compromiterea unei chei – necesitând schimbarea acestora din toate locurile unde au fost
instalați. Din acest motiv algoritmii de încriptare sunt publici pentru ca diferiți experți să
îi poată verifica. În schimb, dacă o cheie este compromisă aceasta se poate schimba mult
mai ușor. În multe situații se recomandă de altfel schimbarea cheii din timp în timp pentru
a diminua impactul unei chei compromise.

Tipuri de algoritmi de criptare

În principal există două mari clase de algoritmi de criptare care vin cu două tipuri
diferite de chei.

95
Sistemele simetrice de criptare utilizează o singură cheie (de fapt sunt două chei,
care pot coincide, sau dacă nu coincid cheia de decriptare se obține imediat din cea de
criptare sau invers – aflarea oricărei dintre cele 2 chei determină imediat aflarea
celeilalte). Astfel de sisteme mai sunt cunoscute și sub numele de sisteme de criptare cu
cheie privată. Acestea au fost primele sisteme de criptare apărute. De altfel, așa zisele
sistemele de criptare clasice se numesc sisteme simetrice. Sistemele simetrice sunt
deosebit de rapide dar au dezevantajul problemelor generate de distribuția cheii
algorimului (care trebuie să fie secretă) în special prin intermediul unui mediu care prin
natura lui este considerat un mediu nesigur.
Printre algoritmii de criptare simetrici, dar care nu mai sunt utilizați astăzi putem
aminti: DES (Data Encryption Standard), 3DES sau RC4. Algoritmul simetric AES
(Advanced Encryption Standard) este considerat algoritmul de criptare simetric standard
folosit și adoptat astăzi de multe agenții și guverne din întreaga lume.
Criptarea cu cheie publică sau criptarea asimetrică utilizează două chei, una dintre
ele este publică iar cealaltă este privată. Mesajul criptat cu cheia publică poate fi decriptat
doar cu cheia privată. Iar mesajul criptat cu cheia privată poate fi decriptat doar cu cheia
publică. Aceste sisteme mai au proprietatea fundamentală că având la dispoziție cheia
publică este imposibil de aflat cheia privată. După cum vedem criptarea cu cheie publică
nu prezintă nici o problemă în distribuția acestei chei – a cheii publice, oricine o poate
avea dar pentru decriptare este necesară cheia privată. Dezavantajul acestui tip de
algoritm este dat de faptul că este mult mai lent comparativ cu cel de tip simetric.
Prima dată conceptul de criptare cu cheie publică a apărut în 1976 [Diffie, 1976].
Actualmente cel mai utilizat algoritm de criptare asimetric este RSA (numit așa după
prima literă a numelor celor care l-au inventat Rivest, Shamir și Adleman). Acest algoritm
este utilizat în cadrul protocoalelor SSL/TSL (secure sockets layer/transport layer
security). TSL este algoritmul utilizat actualmente de către webservere pentru securizarea
comunicațiilor între web browsers și servere.

Hashing-ul

Hashing-ul este o metodă de validare a integrității mesajelor. Prin această metodă se


verifică dacă mesajul trimis este același cu mesajul recepționat. Prin această metodă se
asigură că în mesajul original nu s-a schimbat nimic – în mod accidental (datorită
zgomotelor de pe canal) sau în mod intenționat.
Indiferent de lungimea mesajului de intrare, ieșirea din acest algoritm, amprenta
numerică (message digest) sau semmătura, va avea o lungime fixă. Dacă din mesajul de
intrare un singur bit își schimbă valoarea, atunci amprenta numerică va fi total diferită față
de cea inițială.
O altă caracteristică importantă a acestei metode este dată de faptul că având la
dispoziție amprenta numerică nu vom putea reconstrui mesajul inițial. Din acest motiv,
parolele utilizate în logarea în diferite sisteme informatice (precum calculatoare
personale) sau numerele de tip PIN ale ATM-urilor nu sunt stocate niciodată în clar, sunt
stocate doar amprentele numerice rezultante în urma aplicării algoritmului de tip hashing.
În mod similar atunci când descărcați un fișier de pe internet veți observa afișat sau puteți
descărca și amprenta lui digitală - message digest. Comparând această valoare cu cea

96
calculată după descărcarea programului puteți ști dacă aveți sau nu versiunea originală a
lui.
Un algoritm de hashing trebuie retras atunci când devine foarte ușor să găsești, printr-
o metodă sau alta, intrări multiple care produc același mesaj de ieșire (message digest),
deci aceeași amprentă digitală. Aceasta a fost situația algoritmilor MD5 și SHA-1. De
exemplu SHA-1 avea o amprentă digitală de doar 160 de biți. Unul din algoritmii utilizați
actualmente este SHA-256 care generează o amprentă de 256 de biți.

Autotități certificante

O autoritate certifcantă (certificate authority - CA) este o firmă care generează


certifcate digitale. Actualmente cele mai importante autorități certificante (CA), care au
cea mai mare parte din piață, sunt Symantec, Comodo și GoDaddy.
De fiecare dată când cumpărăm diferite articole pe internet sau facem diferite
tranzacții bancare dorim să avem o conexiune încriptată între web browser-ul nostru și
serverul web cu ajutorul căruia realizăm tranzacțiile financiare. Dar mai mult, dorim să
certificăm că serverul cu care comunicăm este cel dorit și nu unul fals care ne va lua
elementele de autentificare. În mod similar web server-ul (de exemplu, banca) dorește
încriptarea canalelor de comunicație pentru păstrarea confidențialității tranzacției,
protejarea clienților și a propriilor resurse. Compania care gestionează serverul va folosi
CA drept un notar digital. Atât banca cât și clientul trebuie să aibă încredere în CA pentru
ca întregul sistem să funcționeze.
După ce CA verifică legitimitatea unei companii sau unei bănci, îi va genera acesteia
un certificat pentru a fi utilizat de către serverul lor. Pentru generarea acestuia CA are
nevoie de cheia publică a companiei, cheie ce va fi inclusă în certificat. Cu aceasta CA
construiește un certifcat public pe care îl semnează și îl returnează companiei pentru a fi
plasat în rădăcina propriului server web. Prin intermediul acestui certificat se realizează
încriptarea conexiunii între server și web browser-ul utilizatorului și autentificarea
serverului.

Mecanismul de criptare/autentificare a unui server prin TSL

În momentul când un web browser accesează un server web, ce suportă o conexiune


de tip HTTPS (HTTP Secure cu HTTP având semnificația Hypertext Transfer Protocol),
serverul web trimite propriul certificat către web browser. Web browser-ul generează o
cheie simetrică în mod aleator funcție de mișcările mouse-ului și de tastele apăsate
anterior. În pasul următor, web browser-ul încriptează cheia simetrică cu cheia publică (a
algoritmului asimetric) a serverului web (pe care o extrage din certificatul primit de la
server) și trimite rezultatul obținut serverului. Serverul web decriptează mesajul cu
ajutorul cheii private și obține cheia simetrică trimisă de web browser. În acest moment
ambele părți implicate în schimbul de date dețin aceeași cheie simetrică trimisă printr-un
mediu nesigur fără putința unei alte părți de a o intercepta. Din acest moment procesul de
transfer criptat de date poate începe utilizând pentru securitatea transmisiei un algoritm
simetric de criptare/decriptare. Observăm că prin această metodă ne folosim de viteza
algoritmului simetric de criptare fără a expune în vreun fel cheia utilizată pe care am
încriptat-o cu cheia publică a serverului. Această abordare prezentată aici este de altfel

97
modalitatea standardizată la ora actuală de utilizare a algoritmilor asimetrici – de a
schimba chei și nu de a încripta date.
Problema reală care mai rămâne acum să o rezolvăm este de a autentifica serverul cu
care comunicăm. De exemplu, este web serverul la care tocmai ne-am logat serverul
oficial al băncii sau vânzătorului on-line cu care noi dorim să facem tranzacții? Punând
aceeași problemă din punct de vedere criptografic: cum știm că certificatul public cu care
am lucrat aparține în mod real companiei/firmei cu care noi dorim să comunicăm?
Răspunsul stă în semnătura existentă în certificat. Dar și aici ne putem pune întrebare de
unde știm că această semnătură este validă? Singura metodă este de a valida că CA a
semnat întradevăr certifcatul, iar de aici rezultând că cheia publică din certificat este chiar
a serverului ce apartine entitații cu care dorim noi să comunicăm.
Pentru a autentifica serverul, pentru a dovedi aparteneța acestuia la o anumită entitate
(companie, bancă etc.), CA atunci când generează certificatul va prelua cheia publică a
entității și utilizând un algoritm de hashing va genera o amprentă a acestei chei (un
message digest). În pasul următor va cripta această amprentă cu cheia privată a CA –
rezultatul este chiar semnătura care este inclusă în certifcatul ce va fi eliberat entității și
care este plasat în rădăcina serverului gestionat de entitatea respectivă. În momentul
inițierii comunicării cu web serverul web browser-ul primește certificatul serverulului și
utilizează cheia publică a CA extrasă din certificatul acesteia, stocat local pe calculatorul
ce rulează web browser-ul, pentru a decripta semnătura din certificatul serverului. Prin
trecerea cheii publice a serverului prin același algoritm de tip hashing obține o amprentă
locală a cheii publice. Dacă cele două amprente coincid rezultă că semnătura a putut fi
criptată doar de CA deoarece decriptarea s-a realizat cu cheia publică a CA. De aici având
încredere că CA a validat în mod corect entitatea rezultă că toate interacțiunile realizate
(logări, tranzacționări etc.) au fost realizate pe web site-ul entității cu care noi dorim să
interacționăm.
Toate aceste operații prezentate anterior sunt realizate înainte de schimbul cheii
simetrice și începerea tranzacționării datelor. Mai mult web browser-ul verifică și CRL-ul
(certification revocation list) pentru a verifica dacă certificatul/certifciatele aparținând
diferitelor autorități certificante (CA) mai sunt încă valide.

Acceleratorul criptografic existent în CC32xx

Dispozitivele din familia SimpleLink permit stocarea internă a 8 perechi de chei


(private-publice) pe 256 de biți. Un prim set de chei este utilizat în identificarea unică a
dispozitivului hardware, a SoC-ului. Aceste chei (publica și cea privată) sunt încastrate în
hardware, fiind imposibilă modificarea acestora. Celelalte perechi de chei pot fi:
temporare (create la cerere în conformitate cu diferitele necesități ale aplicației) sau
instalate și gestionate de o terță parte. Cheile private existente în acceleratorul criptografic
nu pot fi citite din exterior. Acestea pot fi aplicate doar prin apelarea unor funcții
specifice. Cheile publice pot fi citite de către aplicația care rulează pe SoC.
Acceleratorul criptografic, implementat hardware, suportă următoarele funcționalități:
1. Algoritmul de criptare simetric AES capabil să lucreze cu chei pe 128, 192 sau
256 de biți atât pe criptare cât și pe decriptare.

98
2. Algoritmul DES sau triplu DES (TDES sau 3DES). În mod similar se permite
criptarea și decriptarea mesajelor.
3. Următorii algoritmi de tip hash sunt implementați: MD5, SHA-1 și SHA-2
(SHA-224 și SHA-256).
4. Algoritmii de detecție a erorilor, CRC (Cyclic Redundancy Check): CRC-16-
IBM, CRC-16-CCITT, CRC32, CRC-32C și TCP.
Scopul fundamental al acestui motor criptografic este de a degreva procesorul central,
ARM Cortex-M4, de toate activitățile criptografice care implică operații mtematice
intensive computațional.

99

S-ar putea să vă placă și