Documente Academic
Documente Profesional
Documente Cultură
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)
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.
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.
Samsung OMAP35x
S5PC100 Evaluation Module
IA-32
Texas Instruments
Cortex A8 OMAP3530
MIPS
Freescale
ARM i.MX515 BeagleBoard
Figura 3.2. Legăturile existente între arhitecturi, CPU, SoC şi plăcile electronice
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).
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).
(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.
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.
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
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ă.
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.
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.
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.
19
normală a procesorului ARM Cortex-M3 și a microcontrolerului SCE.
20
Dynamic voltage and frequency scaling (DVFS)
Scalarea dinamică a tensiunii și frecvenței
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
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.
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.
24
3.4. Arhitecturi CISC şi RISC
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.
CISC RISC
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
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
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
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.
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
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
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
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)
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ă.
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).
Memoria cache
primară
Memoria cache
secundară
Memoria RAM
sistem
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
38
Figura 3.23. Bus-urile de date și adrese ale ARM Cortex M3/M4
8 biți
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.
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.
Address: Data:
0x20000000 0x44 ‘D’
0x20000001 0x61 ‘a’
0x20000002 0x6E ‘n’
0x20000003 0x00 null
Big Endian și Little Endian
Î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]
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ă);
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
Î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
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
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
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
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
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
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.
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
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
Instr1 IF ID OF EX WB
Instr2 - - IF ID OF EX WB
Tact 1 2 3 4 5 6 7 8
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
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.
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.
Algoritm Ecuație
Filtru FIR 𝑦𝑛 𝑎 𝑥𝑛 𝑘
Filtru IIR 𝑦𝑛 𝑎 𝑥𝑛 𝑘 𝑏 𝑦𝑛 𝑘
Convoluție 𝑦𝑛 𝑥𝑘ℎ𝑛 𝑘
𝜋 2𝑥 1
Transformata cosinus discreta 𝐹 𝑢 𝛼𝑢 𝑓 𝑥 𝑐𝑜𝑠 𝑢
2𝑁
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.
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.
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ă).
Î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
32 K
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.
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.
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
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)
62
ori mai multă putere decât Cortex-M4.
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.
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.
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.
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
... ...
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
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}
}
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;
}
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.
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.
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;
} }
Figura 3.53. Comparație între utilizarea vectorilor de date de tip short și int
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.
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
Î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;
} }
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.
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ă.
$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.
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.
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
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;
}
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).
+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}
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}
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
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 !!!!
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}
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)
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.
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;
}
Ș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.
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 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)
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;
} }
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
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.
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
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ă.
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.
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.
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.
Operating
Temperature -40 to 85
Range (C)
Package (mm) 9 × 9 QFN, 0.5 Pitch, 64-pin
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.
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.
Î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.
Î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
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
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.
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