Architecture et technologie des ordinateurs

publicité
Systèmes Embarqués
PROCESSEURS SPECIALISES
2ème année
ESIX MeSN
Responsable de Cours :
[email protected] - 02 31 45 27 61
Encadrants de Travaux Pratiques :
[email protected] - 02 31 45 27 56
2015-2016
Processeurs Spécialisés
POLYCOPIE
•
TRAVAUX PRATIQUES
•
ANNEXES
Les documents présents dans ce polycopié sont librement téléchargeables sur la plateforme
d'enseignement de l'ENSICAEN via un accès anonyme. Voici ci-dessous l'URL de la plateforme, se rendre
dans la section : Formation Classique >> Spécialité Informatique ou Spécialité Électronique et
Physique Appliquée >> 2ème année >> Processeurs Spécialisés >> download
http://foad.ensicaen.fr/
Ce document est protégé par une licence Creative Common, néanmoins les différents supports
restent librement réutilisables à des fins pédagogiques. Merci cependant de me prévenir par mail
([email protected]) et de citer le nom de l'ENSICAEN dans vos documents de cours.
Processeurs Spécialisés
COURS
Processeurs Spécialisés
SOMMAIRE
1. HETEROGENEITE DES ARCHITECTURES PROCESSEURS
1.1. MCU - Micro Controller Unit
1.2. AP - Application Processor
1.3. GPP - General Purpose Processor
1.4. GPU - Graphical Processor Unit
1.5. DSP - Digital Signal Processor
1.6. FPGA - Field Programmable Gate Arrays
1.7. SoC - System on Chip
1.8. ASIC - Application Specific Integrated Circuit
2. OBJECTIFS
2.1. Architectures parallèles
2.2. Stratégies d'optimisation
2.3. Méthodologie
3. ARCHITECTURE DSP C6600
3.1. Architecture VLIW
3.1.a. Principe
3.1.b. CPU DSP C6000
3.2. Algorithme de référence
3.2.a. Filtre FIR
3.2.b. Virgule fixe
3.2.c. Virgule flottante
3.3.Instructions arithmétiques
3.3.a. Multiplication
3.3.b. Addition
3.4. Instructions de management mémoire
3.4.a. Modes d'adressage
3.4.b. CPU à adressage complexe
3.4.c. CPU à modèle Load/Store
3.4.d. Instructions Load/Store
3.4.e. Concaténation de registres
Processeurs Spécialisés
34.f. Indexage
3.5. Instructions de saut
3.5.a. Instructions de contrôle
3.5.b. Saut conditionnel
3.5.c. Appel de procédure
3.5.d. Pile système
3.6. Algorithme en assembleur canonique
3.6.a. Assembleur Canonique
3.6.b. Delay slot
3.7. Limitations de l'architecture C6600
3.7.a. CPU VLIW C6600
3.7.b. Jeux d'instructions par unités
3.7.c. Chemins croisés de données
3.7.d. Formats entiers
3.7.e. Divers
3.8. Optimisation
3.8.a. Parallélisme de données
3.8.b. Vectorisation
3.8.c. Parallélisme d'instructions
3.8.d. Dépendances et branches d'exécution
3.8.e. Fonctions intrinsèques
3.8.f. Directives de compilation
3.9. Pipeline logiciel
3.9.a. Concept
3.9.b. Pipeline logiciel sur architecture C6600
4. PILELINE PROCESSEUR
4.1. Architecture in-order
4.2. Architecture superscalaire
4.2.a. Étage d'exécution
4.2.b. Exécution Out-Of-Order
4.3. Architecture VLIW
4.3.a. Étage d'exécution
4.3.b. Code Out-Of-Order
4.4. Architecture EPIC
5. CACHE PROCESSEUR
… à finir !
Processeurs Spécialisés
Architecture et technologie des ordinateurs
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Un système travaillant sur une architecture à CPU peut très
souvent être découpé en couche. Il s’agit d’une solution mixte
logicielle (OS et couches applicatives) et matérielle. Un développeur
logiciel dit ‘’bas niveau’’ travaille dans les couches basses de ce
modèle :
APPLICATION BINARY INTERFACE
Applications
Architecture et Technologie des Ordinateurs
Operating System
Hardware
2 – copyleft
Hugo Descoubes - Novembre 2013
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Observons en quelques chiffres, la répartition des marchés
des systèmes d’exploitation sur quelques grands domaines
d’application :
• Windows de Microsoft : ~91% du marché des ordinateurs
personnels en 2014 (56,3% pour W7 et 13,5% pour W8/8.1), ~2,5%
du marché des Smartphones en 2014, 55% des serveurs en 2014
Vous aurez un enseignement dédié aux systèmes
d’exploitation en 2A.
• UNIX et UNIX-like (GNU/Linux, iOS, MAC OS X, Android …): 90%
du marché des Smartphones en 2014 (Android ~47%), 67% des
serveurs en 2014, GNU/Linux ~97% des superordinateurs en 2014
sources : StatCounter, NetApplications
3 – copyleft
Chaîne de Compilation
-1-
4 – copyleft
Architecture et technologie des ordinateurs
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Malheureusement, développement bas niveau ne veut pas
dire développement simple. Un ingénieur travaillant dans ce domaine
doit notamment être compétent sur les points suivants :
Effectuons quelques rappels sur une chaîne de compilation C
(C toolChain ou C toolSuite). Les slides qui suivent sont à savoir par
cœur.
• Architectures matérielles (CPU, hiérarchie et gestion mémoire,
gestion périphériques, mécanismes d’optimisations …)
• Langages de programmation (essentiellement C/C++ et
assembleur)
• Outils de Développement Logiciel (IDE, chaîne de compilation C,
outils de debuggage et de profilage, programmation concurrente,
programmation parallèle …)
Les exemples suivants sont donnés sous la chaîne de
compilation GCC (GNU Compilation Collection, http://gcc.gnu.org/).
L’architecture est la même que toute autre toolChain C, cependant les
formats et extensions des fichiers intermédiaires ne sont pas
standardisées et peuvent changer d’une chaîne à une autre ou d’une
plateforme matérielle à une autre.
5 – copyleft
6 – copyleft
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Processus de compilation : Réalisation
respective des traitement suivants :
Analyse lexicale, pré-traitement,
analyse syntaxique, analyse
sémantique, génération de code,
‘’optimisation’’, édition des liens
main.c
hello.c
Preprocessor
Preprocessor
hello.h
gcc
• Analyse Lexicale : Elimination
commentaires, espaces, détection des
hello.i
mots clés, opérateurs (!=, <= …),
Parser
chaînes de caractères, constantes
numériques
Optimiser
Compiler
main.i
Parser
Cet enseignement s’appuie sur les compétences enseignées
dans les enseignements ‘’Outils de Développement Logiciel’’
et ‘’Programmation et langage C’’.
Optimiser
hello.s
main.s
staticLib.a
Assembler •
Assembler
hello.o
main.o
Linker
project.out (ou autre extension)
7 – copyleft
Chaîne de Compilation
-2-
Préprocesseur : Etapes d’inclusion de
code, de substitution de chaînes de
caractères et de compilation
conditionnelle. Directives de précompilation ‘’#’’ et opérateurs
‘’#pragma’’ depuis la norme C99
8 – copyleft
Architecture et technologie des ordinateurs
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Parser : Analyse séquentielle du code
o Analyse syntaxique : analyse la structure du
code, vérifie le respect des formalismes et
syntaxes inhérents au langage compilé
hello.c
main.c
hello.h
o Analyse sémantique : résolution des noms,
génération de la table des symboles,
compatibilité des types …
Preprocessor
gcc
Preprocessor
Preprocessor
hello.i
main.i
main.i
Compiler
• Génération du code : Etape dépendant
Parser
de l’architecture CPU cible (Instruction
Optimiser
Set Architecture). Nous parlons de
hello.s cross-compilation (vs compilation
Assembler
native) lorsque l’architecture cible est
différente de celle effectuant la
hello.o
compilation.
Parser
Optimiser
main.s
Assembler
staticLib.a
gcc
main.o
Parser
Optimiser
main.s
Assembler
staticLib.a
main.o
Linker
Linker
project.out (ou autre extension)
• Optimisation (optionnelle) : étage très
délicat à développer et dépendant de
l’architecture CPU cible.
9 – copyleft
project.out (ou autre extension)
10 – copyleft
Bas niveau – C ToolChain – ELF file
• make : utilitaire de
programmation pour
l’automatisation de procédures
de compilation
TOOLCHAIN
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
libFunct2.c
libFunct1.c
libHeader.h
gcc
• Archiver : construction de
bibliothèques statiques.
Archive réalisée à partir de
fichiers objets
main.c hello.c hello.h
• Editeur de liens : Liens entre fichiers
objets, bibliothèques statiques,
Preprocessor bibliothèques dynamiques chargées à
hello.i
l’exécution. Résolution de la table des
Parser
symboles et résolution des liens avec le
modèle mémoire de l’architecture cible.
Optimiser
hello.s Génération d’un code binaire
exécutable absolu ou relogeable
Assembler
(dépend de l’architecture cible et de la
hello.o stratégie de chargement du code
exécutable)
Compiler
hello.h
hello.c
main.c
• Assembleur : Génération code objet
relogeable pour architecture cible (code
binaire). Référencement par symboles,
aucune dépendance avec le modèle
mémoire de l’architecture cible.
Preprocessor
Preprocessor
libFunct2.i
libFunct1.i
staticLib.a
Parser
Parser
Optimiser
Optimiser
libFunct1.s
• Archiver : construction de
bibliothèques statiques.
Archive réalisée à partir de
fichiers objets
libFunct2.s
Assembler
Assembler
gcc
• make : utilitaire de
programmation pour
l’automatisation de procédures
de compilation
make
libFunct1.o
project.out
libFunct2.o
Linker
Archiver
staticLib.a
11 – copyleft
Chaîne de Compilation
-3-
12 – copyleft
Architecture et technologie des ordinateurs
IDE
Bas niveau – C ToolChain – ELF file
• make : utilitaire de
programmation pour
l’automatisation de procédures
de compilation
• Archiver : construction de
bibliothèques statiques.
Archive réalisée à partir de
fichiers objets
main.c hello.c hello.h
Text editor
make
project.out
Observons les 3 trois principaux environnements de compilation
utilisés sur architecture x86 :
• Visual Studio proposé par Windows
• Intel C++ Compiler XE proposé par Intel. Propose des outils très
puissants d’optimisation et de profilage pour architecture Intel
(Advisor XE, Vtune Amplifier, Inspector XE …)
staticLib.a
gcc
TOOLCHAIN
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Integrated Developement
Environment : Aide au
développement logiciel.
Intègre généralement un
éditeur de texte,
automatisation procédure de
compilation, debugger,
utilitaires divers…
13 – copyleft
• GCC (GNU Compiler Collection) avec/sans IDE (Eclipse, Netbeans
…) issu du monde de l’Open Source ayant vocation a être
multiplateforme (cross-compilation ARM, MIPS, PPC …). Les deux
principaux environnement de compilation rencontrés sous Windows
sont Cygwin et MinGW.
14 – copyleft
• Les 2 premières étapes de la
compilation sont architecture
agnostique.
hello.c
main.c
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Fichier ELF (Executable and Linkable Format) :
hello.h
gcc
Preprocessor
Preprocessor
hello.i
main.i
Parser
Parser
code generator
code generator
Optimiser
Optimiser
hello.s
main.s
staticLib.a
Assembler
Assembler
Vous aurez un enseignement
dédié à la compilation et l’étude
du parser en 2A (graphes et
automates)
Le format de fichier ELF sert à l’enregistrement de
programmes compilés (fichiers objets, exécutables, bibliothèques
statiques et dynamiques, modules kernel). Prenons les principales
extensions de fichiers compilés rencontrées sous GNU/Linux (tous des
fichiers ELF) .o (object), .a (archive de .o), .so (shared object), .ko
(kernel object). Ce format plus flexible unifie et remplace les anciens
format a.out et COFF.
• code generator, optimiser et
assembler : dépendance avec
l’architecture CPU cible mais
pas du modèle mémoire
(références symboliques)
hello.o
main.o
Linker
• Linker : dépend du modèle
mémoire de l’architecture cible
project.out
15 – copyleft
Le format ELF est extrêmement répandu sur systèmes UNIXlike (GNU/Linux, FreeBSD, Solaris, OpenBSD, Android …) ainsi que sur
grand nombre d’autres plateformes (PS-2, PS-3, PSP, Wii, SymbianOS
v9 …).
16 – copyleft
Chaîne de Compilation
-4-
Architecture et technologie des ordinateurs
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Avant de présenter le format de fichier ELF, présentons le
cycle de vie d’un programme (en vert, fichiers ELF) :
Source (Assembleur)
Compilation
Objet relogeable (.o)
(références symboliques)
Edition (statique)
des liens
…
Bibliothèques statiques (.a)
(archives de fichiers objets)
Objects files .o
Assemblage
Objet relogeable (.o)
(références symboliques)
Processus
de compilation
…
Objet relogeable (.o)
(références symboliques)
…
Adresse de
chargement
Objet absolu
ELF header
(Relocatable format)
Program header table
(optionnal/ignored)
linking
(executable format)
Program header table
Section n°1
Section n°1
Objet exécutable relogeable (.out .?)
(références translatables)
Chargement
Edition (dynamique)
(réalisé par le système)
des liens
Executable file
ELF header
Section n°1
Section n°2
Section n°2
Objets partagés (.so)
Section n°3
Section n°3
Section n°4
Section n°4
Section header table
17 – copyleft
Section n°4
Segment
n°2
Segment
n°3
Section header table
(optionnal/ignored)
18 – copyleft
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Segment
n°1
Section n°2
Section n°3
Bibliothèques dynamiques
Exécution
(runtime)
Source (C)
Un fichier ELF est toujours constitué d’une en-tête de fichier
(cf. fichier elf.h), le reste de la structure diffère en fonction du type de
fichier compilé (exécutable, bibliothèque partagée, objet …) :
Observons, en utilisant la commande readelf proposée avec
binutils (ou la commande objdump), les en-têtes de fichiers ELF
respectivement pour un fichier objet .o, exécutable et la bibliothèque
standard du C, qui est un objet partagé .so :
Rappelons que pour les langages compilés, deux grands types
d’allocations mémoire sont rencontrés pour la gestion des variables :
• Allocations statiques : Allocation mémoire à la compilation
(compile-time). Prenons l’exemple des variables globales, locales
qualifiées de static … Chaque chaîne de compilation C est très
structurée, classe les variables statiques par famille et les range
dans des sections spécifiques (sections présentent dans fichier ELF).
Observons quelques-unes des principales sections :
o .bss : variables statiques non-initialisées. Par définition
initialisées à zéro au démarrage
o .data : variables statiques initialisées
o .rodata : variables statiques en lecture seule (read-only)
19 – copyleft
Chaîne de Compilation
-5-
20 – copyleft
Architecture et technologie des ordinateurs
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Allocations dynamiques : Allocation mémoire à l’exécution (runtime). Vu par la suite.
Observons le contenu des sections d’un fichier objet
élémentaire après compilation. Accès aux variables par adressage
relatif aux adresses de base des sections :
o Gestion par la pile (ou stack) : variables locales, paramètres,
valeur de retour, adresse de retour et contexte d’exécution de
fonction. Nous parlons également souvent d’allocation
automatique.
Section .text :
binaire du
programme
o Gestion par le tas (ou heap) : fonctions malloc, free et
variantes.
Adresses relatives
dans les sections
(représentation hexadécimale)
Contenu des sections
(représentation hexadécimale)
Contenu des sections
(représentation
sous forme de caractères)
21 – copyleft
22 – copyleft
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
• Section .symtab : cette section, nommée table des symboles est
essentielle. La compilation est un processus dépendant du
langage et de l’architecture du CPU mais indépendant du
mapping mémoire. Les binaires compilés (fichiers objets)
travaillent par références symboliques, la table des symboles lie
les symboles à des adresses relatives vers différentes sections.
Observons l’en-tête contenant la table des sections pour le
programme précédemment compilé (fichier objet).
Section .data
Section .text
23 – copyleft
Chaîne de Compilation
-6-
Adresse dans la section
24 – copyleft
Architecture et technologie des ordinateurs
TOOLCHAIN
Bas niveau – C ToolChain – ELF file
Nous pouvons également rencontrer une table de relocation
(liste de trous à compléter avec la table des symboles). A titre
indicatif, les bibliothèques dynamiques sont liées à l’exécution par le
chargeur du noyau au démarrage du programme. La section .plt
(procedure linkage table) effectue une redirection à l’exécution vers
l’adresse absolue de la procédure cible (printf dans notre cas).
Merci de votre attention !
25 – copyleft
Chaîne de Compilation
-7-
ASSEMBLEUR
Architecture et technologie des ordinateurs
Assembleur – Architectures CPU – ISA Extensions
Un langage d’assemblage ou assembleur ou ASM est un
langage de programmation bas niveau représentant, sous forme
lisible pour un être humain, le code binaire exécutable par un
processeur (ou code machine). Prenons l’exemple d’une instruction
assembleur élémentaire raccrochée à aucune architecture connue :
LANGAGE D’ASSEMBLAGE
Architecture et Technologie des Ordinateurs
Étiquette ou
Adresse en mémoire
programme
Mnémonique ou
dénomination de
l’instruction
LABEL:
ADD
0x7B68
0110011 001 010 011
Opérandes
(source et/ou destination)
opSrc1, opSrc2,opDst3
Adresse mémoire de
l’instruction
ASSEMBLEUR
ASSEMBLEUR
Assembleur – Architectures CPU – ISA Extensions
Hormis label et commentaires, en général à tout champ d’une
instruction assembleur correspond un champ dans le code binaire
équivalent. Ce code binaire ne peut être compris et interprété que par
le CPU cible.
opSrc1, opSrc2,opDst3
L’assembleur est probablement le langage de programmation
le moins universel au monde. Il existe autant de langage
d’assemblage que de familles de CPU. Prenons l’exemple des jeux
d’instructions Cortex-M de ARM. La société Anglaise ARM propose à
elle seule 3 familles de CPU, cortex-M, -R, -A possédant chacune des
sous familles. Ne regardons que la famille cortex-M :
;commentaires
0110011 001 010 011
N’est utilisé que par
les instructions de
branchement
Assembleur – Architectures CPU – ISA Extensions
Cortex-Mx ARM Instruction set
ADD
Binaire interprété par
la machine cible (CPU)
2 – copyleft
Hugo Descoubes - Juin 2013
LABEL:
;commentaires
Opcode
ou
Code opératoire
3 – copyleft
Langage d’assemblage
-1-
4 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Observons les principaux acteurs dans le domaine des CPU’s.
Chaque fondeur présenté ci-dessous propose une voire plusieurs
architectures de CPU qui lui sont propres et possédant donc les jeux
d’instructions associés (CPU server et mainframe non présentés) :
• GPP CPU architectures : Intel (IA-32 et Intel 64), AMD (x86 et
AMD64), IBM (PowerPC), Renesas (RX CPU), Zilog (Z80), Motorola
(6800 et 68000) …
• Embedded CPU architectures (MCU, DSP, SoC) : ARM (Cortex –M –
R -A), MIPS (Rx000), Intel (Atom, 8051), Renesas, Texas Instrument
(MSPxxx, C2xxx, C5xxx, C6xxx), Microchip (PICxx) , Atmel (AVR),
Apple/IBM/Freescale (PowerPC) …
Tout CPU est capable de décoder puis d’exécuter un jeu
d’instruction qui lui est propre (ou instruction set ou ISA ou
Instruction Set Architecture). Dans tous les cas, ces instructions
peuvent être classées en grandes familles :
• Calcul et comparaison : opérations arithmétiques et logiques (en C :
+, -, *, /, &, |, ! ...) et opérations de comparaison, (en C : >=, <=, !=,
== …). Les formats entiers courts seront toujours supportés
nativement. En fonction de l’architecture du CPU, les formats entiers
long (16bits et plus) voire flottants peuvent l’être également.
• Management de données : déplacement de données dans
l’architecture matérielle (CPU vers CPU, CPU vers mémoire ou
mémoire vers CPU)
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
5 – copyleft
• Contrôle programme : saut en mémoire programme (saut dans le
code). Par exemple en langage C : if, else if, else, switch, for, while,
do while, appels de procédure. Nous pouvons rendre ces sauts
conditionnels à l’aide d’opérations arithmétiques et logiques ou de
comparaisons.
Certaines architectures, comme les architectures compatibles
x86-64 (Intel et AMD), possèdent des familles spécialisées :
• String manipulation : manipulation au niveau assembleur de
chaînes de caractères.
• Divers : arithmétique lourde (sinus, cosinus…), opérations
vectorielles (produit vectoriel, produit scalaire…) …
Assembleur – Architectures CPU – ISA Extensions
6 – copyleft
Assembleur – Architectures CPU – ISA Extensions
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Les jeux d’instructions et CPU associés peuvent être classés
en 2 grandes familles, RISC et CISC, respectivement Reduce et
Complex Instruction Set Computer. Les architectures RISC
n’implémentent en général que des instructions élémentaires (CPU’s
ARM, MIPS, 8051, PIC18 …). A l’inverse, les architectures CISC (CPU’s
x86-64, 68xxx …) implémentent nativement au niveau assembleur
des traitements pouvant être très complexes (division, opérations
vectorielles, opérations sur des chaînes de caractères …).
En 2012, la frontière entre ces deux familles est de plus en
plus fine. Par exemple, le jeu d’instructions des processeurs
spécialisés DSP RISC-like TMS320C66xx de TI compte 323 instructions.
Néanmoins, les architectures compatibles x86-64 sont des
architectures CISC. Nous allons rapidement comprendre pourquoi.
7 – copyleft
Langage d’assemblage
-2-
8 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Avantages architecture CISC :
• Empreinte mémoire programme faible, donc plus d’instructions
contenues en cache. Néanmoins sur CPU CISC, en moyenne près de
80% des instructions compilées sont de types RISC.
• Compatibles x86-64, rétrocompatibilité des applications
développées sur anciennes architectures.
Inconvénients architecture CISC :
• Architecture CPU complexe (mécanismes d’accélération matériels,
décodeurs, Execution Units …), donc moins de place pour le cache.
• Jeu d’instructions mal géré par les chaînes de compilation
(mécanismes d’optimisation)
Assembleur – Architectures CPU – ISA Extensions
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Inconvénients architecture RISC :
• Empreinte mémoire programme élevée, donc moins d’instructions
contenues en cache et mémoire principale.
Avantages architecture RISC :
• Architecture du CPU moins complexe (mécanismes d’accélération
matériels, décodeurs, unités d’exécution …).
• En général, tailles instructions fixes et souvent exécution en un ou
deux cycles CPU.
• Jeu d’instructions plus simple à appréhender pour le développeur
et donc le compilateur. Jeu d’instructions très bien géré par les
chaînes de compilations (mécanismes d’optimisation). Beaucoup
d’architectures RISC récentes, travaillent avec de nombreux
registres de travail généralistes, facilite le travail du compilateur.
Assembleur – Architectures CPU – ISA Extensions
10 – copyleft
ASSEMBLEUR
ASSEMBLEUR
9 – copyleft
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Assembleur – Architectures CPU – ISA Extensions
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
8051 Intel CPU (only CPU)
(1980)
8051 Instruction set
Observons le jeu d’instructions complet d’un CPU RISC 8051
proposé par Intel en 1980. En 2012, cette famille de CPU, même si
elle reste très ancienne, est toujours extrêmement répandue et
intégrée dans de nombreux MCU’s ou ASIC’s (licence libre). Prenons
quelques exemples de fondeurs les utilisant : NXP, silabs, Atmel …
MCU Silabs with 8051 CPU
(2012)
11 – copyleft
Langage d’assemblage
-3-
ACALL
Absolute Call
MOV
Move Memory
ADD, ADDC
Add Accumulator (With Carry)
MOVC
Move Code Memory
AJMP
Absolute Jump
MOVX
Move Extended Memory
ANL
Bitwise AND
MUL
Multiply Accumulator by B
CJNE
Compare and Jump if Not Equal
NOP
No Operation
CLR
Clear Register
ORL
Bitwise OR
CPL
Complement Register
POP
Pop Value From Stack
DA
Decimal Adjust
PUSH
Push Value Onto Stack
DEC
Decrement Register
RET
Return From Subroutine
DIV
Divide Accumulator by B
RETI
Return From Interrupt
DJNZ
Decrement Register and Jump if Not Zero
RL
Rotate Accumulator Left
INC
Increment Register
RLC
Rotate Accumulator Left Through Carry
JB
Jump if Bit Set
RR
Rotate Accumulator Right
JBC
Jump if Bit Set and Clear Bit
RRC
Rotate Accumulator Right Through Carry
JC
Jump if Carry Set
SETB
Set Bit
JMP
Jump to Address
SJMP
Short Jump
JNB
Jump if Bit Not Set
SUBB
Subtract From Accumulator With Borrow
JNC
Jump if Carry Not Set
SWAP
Swap Accumulator Nibbles
JNZ
Jump if Accumulator Not Zero
XCH
Exchange Bytes
JZ
Jump if Accumulator Zero
XCHD
Exchange Digits
LCALL
Long Call
XRL
Bitwise Exclusive OR
LJMP
Long Jump
12 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Original 8086 Instruction set
Observons le jeu d’instructions complet d’un CPU 16bits CISC
8086 proposé par Intel en 1978. Il s’agit du premier processeur de la
famille x86. En 2012, un corei7 est toujours capable d’exécuter le jeu
d’instruction d’un 8086. Bien sûr, la réciproque n’est pas vraie.
8086 Intel CPU
(1978)
Assembleur – Architectures CPU – ISA Extensions
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
AAA
ASCII adjust AL after addition
HLT
Enter halt state
AAD
ASCII adjust AX before division
IDIV
Signed divide
AAM
ASCII adjust AX after multiplication
IMUL
Signed multiply
AAS
ASCII adjust AL after subtraction
IN
Input from port
ADC
Add with carry
INC
Increment by 1
ADD
Add
INT
Call to interrupt
AND
Logical AND
INTO
Call to interrupt if overflow
CALL
Call procedure
IRET
Return from interrupt
CBW
Convert byte to word
Jcc
Jump if condition
CLC
Clear carry flag
JMP
Jump
CLD
Clear direction flag
LAHF
Load flags into AH register
CLI
Clear interrupt flag
LDS
Load pointer using DS
CMC
Complement carry flag
LEA
Load Effective Address
CMP
Compare operands
LES
Load ES with pointer
CMPSB
Compare bytes in memory
LOCK
Assert BUS LOCK# signal
CMPSW
Compare words
LODSB
Load string byte
CWD
Convert word to doubleword
LODSW
Load string word
DAA
Decimal adjust AL after addition
LOOP/LOOPx
Loop control
DAS
Decimal adjust AL after subtraction
MOV
Move
DEC
Decrement by 1
MOVSB
Move byte from string to string
DIV
Unsigned divide
MOVSW
Move word from string to string
ESC
Used with floating-point unit
MUL
Unsigned multiply
Original 8086 Instruction set
14 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
13 – copyleft
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
NEG
Two's complement negation
SCASB
Compare byte string
NOP
No operation
SCASW
Compare word string
NOT
Negate the operand, logical NOT
SHL
Shift left (unsigned shift left)
OR
Logical OR
SHR
Shift right (unsigned shift right)
OUT
Output to port
STC
Set carry flag
POP
Pop data from stack
STD
Set direction flag
POPF
Pop data from flags register
STI
Set interrupt flag
PUSH
Push data onto stack
STOSB
Store byte in string
PUSHF
Push flags onto stack
STOSW
Store word in string
RCL
Rotate left (with carry)
SUB
Subtraction
RCR
Rotate right (with carry)
TEST
Logical compare (AND)
REPxx
Repeat MOVS/STOS/CMPS/LODS/SCAS
WAIT
Wait until not busy
RET
Return from procedure
XCHG
Exchange data
RETN
Return from near procedure
XLAT
Table look-up translation
RETF
Return from far procedure
XOR
Exclusive OR
ROL
Rotate left
ROR
Rotate right
SAHF
Store AH into flags
SAL
Shift Arithmetically left (signed shift left)
SAR
Shift Arithmetically right (signed shift
right)
SBB
Subtraction with borrow
Assembleur – Architectures CPU – ISA Extensions
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Prenons un exemple d’instruction CISC 8086. Les deux codes
qui suivent réalisent le même traitement et permettent de déplacer
100 octets en mémoire d’une adresse source vers une adresse
destination :
CISC
MOV
MOV
MOV
REP
15 – copyleft
Langage d’assemblage
-4-
CX,100
DI, dst
SI, src
MOVSB
RISC
MOV
MOV
MOV
CX,100
DI, dst
SI, src
MOV
MOV
INC
INC
DEC
JNX
AL, [SI]
[DI], AL
SI
DI
CX
LOOP
LOOP:
16 – copyleft
Assembleur – Architectures CPU – ISA Extensions
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Attention, si vous lisez de l’assembleur x86-64, il existe deux
syntaxes très répandues. La syntaxe Intel et la syntaxe AT&T utilisée
par défaut par gcc (systèmes UNIX).
Jeu d’instruction RISC 8051
Jeu d’instruction CISC 8086
Prenons un exemple de code écrit dans les 2 syntaxes :
Intel Syntax
Intel Syntax
MOV
AT&T Syntax
ebx,0FAh
MOV
$0xFA, %ebx
MOV
MOV
MOV
CX,100
DI, dst
SI, src
MOV
MOV
INC
INC
DEC
JNX
AL, [SI]
[DI], AL
SI
DI
CX
LOOP
LOOP:
Syntaxe AT&T :
• Opérandes sources à gauche et
destination à droite
• Constantes préfixées par $ (adressage
immédiat)
• Constantes écrites avec syntaxe langage C
(0x + valeur = hexadécimal)
• Registres préfixés par %
• Segmentation : [ds:20] devient %ds:20,
[ss:bp] devient %ss:%bp …
•
•
•
Adressage indirect [ebx] devient (%ebx),
[ebx + 20h] devient 0x20(%ebx),
[ebx+ecx*2h-1Fh] devient -0x1F(%ebx,
%ecx, 0x2) …
Suffixes, b=byte=1o, w=word=2o,
s=short=4o, l=long=4o, q=quad=8o,
t=ten=10o, o=octo=16o=128bits (x64)
…
AT&T Syntax
movw
movw
movw
$100, %cx
dst, %di
src, %di
movb
movb
inc
inc
dec
jnx
(%si), %al
%al, (%di)
%si
%di
%cx
LOOP
LOOP:
18 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
17 – copyleft
Par abus de langage, les CPU compatibles du jeu d’instruction
80x86 (8086, 80386, 80486..) sont nommés CPU x86. Depuis l’arrivée
d’architectures 64bits ils sont par abus de langage nommés x64. Pour
être rigoureux chez Intel, il faut nommer les jeux d’instructions et
CPU 32bits associés IA-32 (depuis le 80386 en 1985) et les ISA 64bits
Intel 64 ou EM64T (depuis le Pentium 4 Prescott en 2004).
Assembleur – Architectures CPU – ISA Extensions
Extensions x86 et x64 n’opérant que sur des formats entiers :
CPU
Architecture
Nom
extension
Instructions
-
AAA, AAD, AAM, AAS, ADC, ADD, AND, CALL, CBW, CLC, CLD, CLI, CMC, CMP, CMPSzz, CWD, DAA, DAS, DEC, DIV, ESC, HLT,
IDIV, IMUL, IN, INC, INT, INTO, IRET, Jcc, LAHF, LDS, LEA, LES, LOCK, LODSzz, LODSW, LOOPcc, MOV, MOVSzz, MUL, NEG, NOP,
NOT, OR, OUT, POP, POPF, PUSH, PUSHF, RCL, RCR, REPcc, RET, RETF, ROL, ROR, SAHF, SAL, SALC, SAR, SBB, SCASzz, SHL, SAL,
SHR, STC, STD, STI, STOSzz, SUB, TEST, WAIT, XCHG,XLAT, XOR
8086
Original x86
80186/80188
-
BOUND, ENTER, INSB, INSW, LEAVE, OUTSB, OUTSW, POPA, PUSHA, PUSHW
80286
-
ARPL, CLTS, LAR, LGDT, LIDT, LLDT, LMSW, LOADALL, LSL, LTR, SGDT, SIDT, SLDT, SMSW, STR, VERR, VERW
-
BSF, BSR, BT, BTC, BTR, BTS, CDQ, CMPSD, CWDE, INSD, IRETD, IRETDF, IRETF, JECXZ, LFS, LGS, LSS, LODSD, LOOPD, LOOPED,
LOOPNED, LOOPNZD, LOOPZD, MOVSD, MOVSX, MOVZX, OUTSD, POPAD, POPFD, PUSHAD, PUSHD, PUSHFD, SCASD, SETA,
SETAE, SETB, SETBE, SETC, SETE, SETG, SETGE, SETL, SETLE, SETNA, SETNAE, SETNB, SETNBE, SETNC, SETNE, SETNG, SETNGE,
SETNL, SETNLE, SETNO, SETNP, SETNS, SETNZ, SETO, SETP, SETPE, SETPO, SETS, SETZ, SHLD, SHRD, STOSD
80386
L’une des grandes forces (et paradoxalement faiblesse) de ce
jeu d’instruction est d’assurer une rétrocompatibilité avec les jeux
d’instructions d’architectures antérieures. En contrepartie, il s’agit
d’une architecture matérielle très complexe, difficile à accélérer
imposant de fortes contraintes de consommation et d’échauffement.
19 – copyleft
80486
-
BSWAP, CMPXCHG, INVD, INVLPG, WBINVD, XADD
Pentium
-
CPUID, CMPXCHG8B, RDMSR, RDPMC, WRMSR, RSM
-
CMOVA, CMOVAE, CMOVB, CMOVB, CMOVE, CMOVG, CMOVGE, CMOVL, CMOVLE, CMOVNA, CMOVNAE, CMOVNB, CMOVNBE,
CMOVNC, CMOVNE, CMOVNG, CMOVNGE, CMOVNL, CMOVNLE, CMOVNO, CMOVNP, CMOVNS, CMOVNZ, CMOVO, CMOVP,
CMOVPE, CMOVPO, CMOVS, CMOVZ, RDPMC, SYSENTER, SYSEXIT, UD2
Pentium pro
Pentium III
SSE
MASKMOVQ, MOVNTPS, MOVNTQ, PREFETCH0, PREFETCH1, PREFETCH2, PREFETCHNTA, SFENCE
Pentium 4
SSE2
CLFLUSH, LFENCE, MASKMOVDQU, MFENCE, MOVNTDQ, MOVNTI, MOVNTPD, PAUSE
Pentium 4
SSE3
Hyper Threading
Pentium 4 6x2
VMX
X86-64
-
Pentium 4
VT-x
Langage d’assemblage
-5-
LDDQU, MONITOR, MWAIT
VMPTRLD, VMPTRST, VMCLEAR, VMREAD, VMWRITE, VMCALL, VMLAUNCH, VMRESUME, VMXOFF, VMXON
CDQE, CQO, CMPSQ, CMPXCHG16B, IRETQ, JRCXZ, LODSQ, MOVSXD, POPFQ, PUSHFQ, RDTSC, SCASQ, STOSQ, SWAPGS
VMPTRLD, VMPTRST, VMCLEAR, VMREAD, VMWRITE, VMCALL, VMLAUNCH, VMRESUME, VMXOFF, VMXON
20 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Les extensions x87 ci-dessous n’opèrent que sur des formats
flottants. Historiquement, le 8087 était un coprocesseur externe
utilisé comme accélérateur matériel pour des opérations flottantes.
Ce coprocesseur fut intégré dans le CPU principal sous forme d’unité
d’exécution depuis l’architecture 80486. Cette unité est souvent
nommée FPU (Floating Point Unit).
Assembleur – Architectures CPU – ISA Extensions
Les extensions présentées ci-dessous inplémentent toutes
des instructions dites SIMD (Single Instruction Multiple Data) :
• MMX : MultiMedia eXtensions
• SSE : Streaming SIMD Extensions
CPU
Architecture
Nom
extension
Instructions
-
F2XM1, FABS, FADD, FADDP, FBLD, FBSTP, FCHS, FCLEX, FCOM, FCOMP, FCOMPP, FDECSTP, FDISI, FDIV, FDIVP, FDIVR, FDIVRP,
FENI, FFREE, FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FINCSTP, FINIT, FIST, FISTP, FISUB, FISUBR, FLD, FLD1, FLDCW,
FLDENV, FLDENVW, FLDL2E, FLDL2T, FLDLG2, FLDLN2, FLDPI, FLDZ, FMUL, FMULP, FNCLEX, FNDISI, FNENI, FNINIT, FNOP, FNSAVE,
FNSAVEW, FNSTCW, FNSTENV, FNSTENVW, FNSTSW, FPATAN, FPREM, FPTAN, FRNDINT, FRSTOR, FRSTORW, FSAVE, FSAVEW,
FSCALE, FSQRT, FST, FSTCW, FSTENV, FSTENVW, FSTP, FSTSW, FSUB, FSUBP, FSUBR, FSUBRP, FTST, FWAIT, FXAM, FXCH, FXTRACT,
FYL2X, FYL2XP1
8087
Original x87
80287
-
80387
Instructions
MMX
EMMS, MOVD, MOVQ, PACKSSDW, PACKSSWB, PACKUSWB, PADDB, PADDD, PADDSB, PADDSW, PADDUSB, PADDUSW, PADDW,
PAND, PANDN, PCMPEQB, PCMPEQD, PCMPEQW, PCMPGTB, PCMPGTD, PCMPGTW, PMADDWD, PMULHW, PMULLW, POR, PSLLD,
PSLLQ, PSLLW, PSRAD, PSRAW, PSRLD, PSRLQ, PSRLW, PSUBB, PSUBD, PSUBSB, PSUBSW, PSUBUSB, PSUBUSW, PSUBW,
PUNPCKHBW, PUNPCKHDQ, PUNPCKHWD, PUNPCKLBW, PUNPCKLDQ, PUNPCKLWD, PXOR
Pentium 4
Float Inst.
Integer Inst.
SSE2
-
FCOS, FLDENVD, FNSAVED, FNSTENVD, FPREM1, FRSTORD, FSAVED, FSIN, FSINCOS, FSTENVD, FUCOM, FUCOMP, FUCOMPP
FCMOVB, FCMOVBE, FCMOVE, FCMOVNB, FCMOVNBE, FCMOVNE, FCMOVNU, FCMOVU, FCOMI, FCOMIP, FUCOMI, FUCOMIP,
FXRSTOR, FXSAVE
SSE3
SSE
Pentium III
FSETPM
Pentium pro
Float Inst.
Pentium 4
FISTTP
SSE3
ASSEMBLEUR
ASSEMBLEUR
21 – copyleft
Assembleur – Architectures CPU – ISA Extensions
Les instructions et opérandes usuellement manipulées par
grand nombre de CPU sur le marché sont dites scalaires. Nous
parlerons de processeur scalaire (PIC de Microchip, 8051 de Intel, AVR
de Atmel, C5xxx de TI…). Par exemple sur 8086 de Intel , prenons
l’exemple d’une addition : scalaire + scalaire = scalaire :
add
ADDPS, ADDSS, CMPPS, CMPSS, COMISS, CVTPI2PS, CVTPS2PI, CVTSI2SS, CVTSS2SI, CVTTPS2PI, CVTTSS2SI, DIVPS,
DIVSS, LDMXCSR, MAXPS, MAXSS, MINPS, MINSS, MOVAPS, MOVHLPS, MOVHPS, MOVLHPS, MOVLPS, MOVMSKPS,
MOVNTPS, MOVSS, MOVUPS, MULPS, MULSS, RCPPS, RCPSS, RSQRTPS, RSQRTSS, SHUFPS, SQRTPS, SQRTSS,
STMXCSR, SUBPS, SUBSS, UCOMISS, UNPCKHPS, UNPCKLPS
ANDNPS, ANDPS, ORPS, PAVGB, PAVGW, PEXTRW, PINSRW, PMAXSW, PMAXUB, PMINSW, PMINUB, PMOVMSKB,
PMULHUW, PSADBW, PSHUFW, XORPS
ADDPD, ADDSD, ANDNPD, ANDPD, CMPPD, CMPSD, COMISD, CVTDQ2PD, CVTDQ2PS, CVTPD2DQ, CVTPD2PI,
CVTPD2PS, CVTPI2PD, CVTPS2DQ, CVTPS2PD, CVTSD2SI, CVTSD2SS, CVTSI2SD, CVTSS2SD, CVTTPD2DQ, CVTTPD2PI,
CVTTPS2DQ, CVTTSD2SI, DIVPD, DIVSD, MAXPD, MAXSD, MINPD, MINSD, MOVAPD, MOVHPD, MOVLPD,
MOVMSKPD, MOVSD, MOVUPD, MULPD, MULSD, ORPD, SHUFPD, SQRTPD, SQRTSD, SUBPD, SUBSD, UCOMISD,
UNPCKHPD, UNPCKLPD, XORPD
Integer Inst.
MOVDQ2Q, MOVDQA, MOVDQU, MOVQ2DQ, PADDQ, PSUBQ, PMULUDQ, PSHUFHW, PSHUFLW, PSHUFD, PSLLDQ,
PSRLDQ, PUNPCKHQDQ, PUNPCKLQDQ
Float Inst.
ADDSUBPD, ADDSUBPS, HADDPD, HADDPS, HSUBPD, HSUBPS, MOVDDUP, MOVSHDUP, MOVSLDUP
22 – copyleft
Assembleur – Architectures CPU – ISA Extensions
Cette instruction vectorielle peut notamment être très
intéressante pour des applications de traitement numérique du signal
: dpps signifie dot product packet single, soit produit scalaire sur un
paquet de données au format flottant en simple précision (IEEE-754).
Observons le descriptif de l’instruction ainsi qu’un exemple :
%bl,%al
A titre indicatif, les instructions MMX, SSE, AVX, AES … sont
dîtes vectorielles. Les opérandes ne sont plus des grandeurs scalaires
mais des grandeurs vectorielles. Nous parlerons de processeur
vectoriel (d’autres architectures vectorielles existent). Prenons un
exemple d’instruction vectorielle SIMD SSE4.1, vecteur . vecteur =
scalaire :
dpps
AVX : Advanced Vector Extensions
AES : Advanced Encryption Standard
Nom
extension
Pentium MMX
CPU
Architecture
•
•
http://www.intel.com
0xF1, %xmm2,%xmm1
23 – copyleft
Langage d’assemblage
-6-
24 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Etudions un exemple d’exécution de l’instruction dpps :
dpps
Etudions un exemple d’exécution de l’instruction dpps :
XMMi (i = 0 à 15 with Intel 64)
128bits General Purpose Registers
for SIMD Execution Units
0xF1, %xmm2,%xmm1
128
XMM1
96
a3
128
XMM2
64
a2
96
x3
64
x2
dpps
XMMi (i = 0 à 15 with Intel 64)
128bits General Purpose Registers
for SIMD Execution Units
0xF1, %xmm2,%xmm1
0
32
a1
Assembleur – Architectures CPU – ISA Extensions
x0
128
128
Temp1
96
a3.x3
64
a2.x2
32
128
XMM2
0
Core2
Nehalem
0
32
x1
x0
ASSEMBLEUR
26 – copyleft
Assembleur – Architectures CPU – ISA Extensions
Instructions
PSIGNW, PSIGND, PSIGNB, PSHUFB, PMULHRSW, PMADDUBSW, PHSUBW, PHSUBSW, PHSUBD, PHADDW, PHADDSW, PHADDD,
PALIGNR, PABSW, PABSD, PABSB
MPSADBW, PHMINPOSUW, PMULLD, PMULDQ, DPPS, DPPD, BLENDPS, BLENDPD, BLENDVPS, BLENDVPD, PBLENDVB, PBLENDW,
PMINSB, PMAXSB, PMINUW, PMAXUW, PMINUD, PMAXUD, PMINSD, PMAXSD, ROUNDPS, ROUNDSS, ROUNDPD, ROUNDSD,
INSERTPS, PINSRB, PINSRD/PINSRQ, EXTRACTPS, PEXTRB, PEXTRW, PEXTRD/PEXTRQ, PMOVSXBW, PMOVZXBW, PMOVSXBD,
PMOVZXBD, PMOVSXBQ, PMOVZXBQ, PMOVSXWD, PMOVZXWD, PMOVSXWQ, PMOVZXWQ, PMOVSXDQ, PMOVZXDQ, PTEST,
PCMPEQQ, PACKUSDW, MOVNTDQA
SSE4.2
64
x2
L’instruction CPUID arrivée avec l’architecture Pentium
permet de récupérer très facilement toutes les informations relatives
à l’architecture matérielle du GPP (CPU’s, Caches, adressage virtuel..).
L’utilitaire libre CPU-Z utilise notamment ce registre pour retourner
des informations sur l’architecture :
SSSE3
CRC32, PCMPESTRI, PCMPESTRM, PCMPISTRI, PCMPISTRM, PCMPGTQ
AVX
VFMADDPD, VFMADDPS, VFMADDSD, VFMADDSS, VFMADDSUBPD, VFMADDSUBPS, VFMSUBADDPD, VFMSUBADDPS, VFMSUBPD,
VFMSUBPS, VFMSUBSD, VFMSUBSS, VFNMADDPD, VFNMADDPS, VFNMADDSD, VFNMADDSS, VFNMSUBPD, VFNMSUBPS,
VFNMSUBSD, VFNMSUBSS
AES
AESENC, AESENCLAST, AESDEC, AESDECLAST, AESKEYGENASSIST, AESIMC
Sandy Bridge
Nehalem
http://www.intel.com
25 – copyleft
SSE4.1
Core2 (45nm)
96
x3
0
Assembleur – Architectures CPU – ISA Extensions
Nom
extension
a0.x0 + a1.x1
+
a2.x2 + a3.x3
a0.x0 + a1.x1
+
a2.x2 + a3.x3
Les extensions x86-64 présentées jusqu’à maintenant ne
présentent que les évolutions des jeux d’instructions apportées par
Intel. Les extensions amenées par AMD ne seront pas présentées
(MMX+, K6-2, 3DNow, 3DNow!+, SSE4a..).
CPU
Architecture
0
32
0.0
a2.x2 + a3.x3
Temp3
Temp4
64
0.0
0
a0.x0 + a1.x1
32
96
0.0
XMM1
a0.x0
32
ASSEMBLEUR
0
32
a1.x1
Temp2
http://www.intel.com
a0.x0 + a1.x1
+
a2.x2 + a3.x3
Temp4
0
32
x1
0
32
a0
27 – copyleft
Langage d’assemblage
-7-
28 – copyleft
Assembleur – Architectures CPU – ISA Extensions
ASSEMBLEUR
ASSEMBLEUR
Architecture et technologie des ordinateurs
Sous Linux, vous pouvez également consulter le fichier
/proc/cpuinfo listant les informations retournées par l’instruction
CPUID :
Assembleur – Architectures CPU – ISA Extensions
De même, lorsque l’on est amené à développer sur un
processeur donné, il est essentiel de travailler avec les documents de
référence proposés par le fondeur, Intel dans notre cas. Vous pouvez
télécharger les différents documents de référence à cette URL :
http://www.intel.com/content/www/us/en/processors/architecturessoftware-developer-manuals.html
29 – copyleft
Merci de votre attention !
Langage d’assemblage
-8-
30 – copyleft
Processeurs Spécialisés
TRAVAUX PRATIQUES
Digital Signal Processor
Travaux Pratiques
SOMMAIRE
PREAMBULE
1. ALGORITHME DE FILTRAGE
1.1. Algorithme de référence
1.2. Test de performance
1.3. Intégration en C canonique
2. AUTOMATISATION DES TESTS
2.1. Test de conformité
2.2. Test de performance
2.3. Temps de compilation et empreinte mémoire
2.4. Temps de programmation
3. PROGRAMMATION VECTORIELLE
3.1. Assembleur canonique C6600
3.2. Assembleur VLIW C6600
3.3. Pipelining logiciel assembleur C6600
3.4. Assembleur vectoriel C6600
3.5. Déroulement de boucle en C
3.6. Vectorisation de code en C
3.7. C canonique sur architecture IA-64
3.8. Vectorisation SSE4.1 sur architecture IA-64
4. HIERARCHIE MEMOIRE
4.1. Mémoire locale adressable
4.2. Préchargement des données de DDR SRAM vers L2 SRAM
4.3. Préchargement des données de L2 SRAM vers L1D SRAM
5. PERIPHERIQUES D'ACCELERATION
5.1. Transferts par IDMA
5.2. Stratégie Ping Pong
5.3. Transferts par EDMA
BENCHMARKING
-1-
Digital Signal Processor
Travaux Pratiques
PREAMBULE
Durant cette trame de travaux pratiques, nous allons nous intéresser au workflow
typiquement rencontré en milieu industriel dans le cadre d'optimisation logicielle d'algorithme pour
une cible matérielle spécialisée. Ce processus de développement peut par exemple être rencontré
chez les acteurs des grands domaines du traitement du signal (traitement d'antenne, traitement
d'image, traitement du son …). Nous travaillons d'ailleurs à l'ENSICAEN avec plusieurs partenaires
industriels utilisant ce type de méthodologie.
Durant la séquence qui suit, nous nous intéresserons à l'implémentation d'un algorithme
simple et standard du domaine du traitement du signal, un filtre FIR ou produit scalaire. Nous nous
attarderons bien entendu aux stratégies d'optimisation sur une architecture matérielle spécialisée.
Tous nos développements seront guidés par le test, étape pouvant tenir une place très importante
dans le temps de développement global d'une application et le benchmarking (analyse comparative).
Nous constaterons que si cette étape n'est pas délaissée, il nous ait alors possible de gagner des jours
de développement sur une tâche courante. Observons le workflow de la trame de travaux pratiques :
•
Analyse mathématique théorique de l'algorithme
•
Modélisation et validation sur outil de prototypage. L'outil logiciel de prototypage
rapide le plus rencontré en milieu industriel à notre époque dans le domaine de
l'embarqué appliqué au traitement du signal est Matlab/Simulink. Cette étape est
essentielle afin de valider la structure en pseudo code des algorithmes ainsi que les
vecteurs d'entrée et de sortie pour les procédures de test à venir
•
Intégration sur cible d'un algorithme de référence
•
Intégration sur cible des procédures de test. Dans le cadre de nos développements,
nous nous intéresserons essentiellement aux tests de conformité et de performance
dans une optique de benchmarking
•
Intégrations et validations successives sur cible des stratégies d'optimisation sur
architecture processeur spécialisée (vectorisation monocœur, parallélisation
multicœur, gestion optimale de la hiérarchie mémoire et périphériques d'accélération)
-2-
Digital Signal Processor
Travaux Pratiques
Dans une optique pédagogique et professionnalisante, notre choix s'est porté sur
l'architecture C6600 proposée par Texas Instruments. Pour information, en 2015 la famille C6000 de
TI est l'architecture leader sur le marché des processeurs DSP (Digital Signal Processor). Le processeur
C6678 étudié en TP étant l'un des composant haut de gamme de la famille avec ses 8 cœurs vectoriels
VLIW. Cette architecture propose quelques atouts assurant une grande flexibilité et permettant une
bonne compréhension des architectures processeurs actuelles. Observons là plus en détails :
Le processeur DSP TMS320C6678 offre 8 cœurs vectoriels VLIW (Very Long Instruction Word)
possédant chacun 32Ko caches L1 program et L1 data, ainsi que 512Ko de cache L2. Chaque cœur
peut-être cadencé jusqu'à 1,4GHz. Le niveau mémoire L3 de 4Mo nommé MSM (Multi-core Shared
Memory) est partagé entre cœurs. Chaque niveau mémoire peut être configurable en cache ou en
mémoire adressable SRAM permettant une architecture matérielle configurable en modèle mémoire
uniforme ou non-uniforme. Chose impossible sur processeur généraliste GPP Intel par exemple. De la
mémoire RAM DDR3 externe peut également être ajoutée sur circuit imprimé et est alors
interconnectée via le périphérique d'interface EMIF (External Memory Interface).
En première année, nous avons découvert les bases du développement sur processeur
numérique. Afin de faciliter cette approche nous avons développé sur MCU (Micro Controller Unit) ou
microcontrôleur. Ces processeurs étant généralistes et non spécialisés pour du calcul numérique
massif, nous nous sommes donc assez longuement attardés sur les périphériques et interfaces de
communication :
-3-
Digital Signal Processor
Travaux Pratiques
L'enseignement de cette année est différent et doit être perçu comme une extension des
compétences de première année. Cette année nous allons travailler sur machine fortement parallèle
et nous nous efforcerons de comprendre puis d'exploiter au mieux les ressources matérielles
proposées par notre processeur. Les périphériques d'interface ne seront donc pas vus, seule la partie
processing nous intéressera. Nous nous focaliserons donc sur l'architecture vectorielle, parallèle et le
pipeline de chaque cœur, les hiérarchies mémoires ainsi que les périphériques d'accélération :
Même si l'exemple qui suit n'a que peu de sens, afin de bien comprendre les différences
majeures entre les architectures de première année et de deuxième année, comparons les
performances théoriques maximales des deux processeurs. Un MCU 8bits PIC18 de Microchip peut
exécuter jusqu'à 12MIPS (soit 12 millions instructions entières 8bits par seconde). Un DSP 32bits
TMS320C6678 de Texas Instruments peut exécuter jusqu'à 22,4GFLOP/core (soit 22,4 Giga
instructions flottantes 32bits en simple précision par seconde par cœur). Soit jusqu'à 179,2GFLOP
pour le processeur complet grâce à ses 8 cœurs, le tout avec une horloge à 1,4GHz. Avec ces quelques
chiffres, nous pouvons commencer à pressentir le potentiel pour du calcul numérique flottant de
cette famille de processeur.
La trame de travaux pratiques sera réalisée sur la plateforme de développement
TMDXEVM6678L EVM (EValuation Module) proposé par la société Advantec. Cette maquette
d'évaluation embarque notamment une sonde d'émulation USB XDS100 assurant la programmation
et le débogage des applicatifs :
-4-
Digital Signal Processor
Travaux Pratiques
Les outils de développement et bibliothèques sont librement téléchargeables et installables
depuis internet du moment que nous n'utilisons que la sonde de programmation XDS100 présente
sur la maquette ou le mode simulation. Les outils deviennent alors payants si nous souhaitons
travailler avec des sondes d'émulation évoluées, telle que la sonde XDS560 également présente à
l'école. Voici ci-dessous des liens vers les outils logiciels (IDE, SDK) utilisés afin de réaliser la trame de
travaux pratiques. Bien entendu, nous vous conseillons vivement d'installer ces outils sur vos
machines (IDE et bibliothèques spécifiées ci-dessous) :
•
Wiki Processeurs TI. Point d'entrée des différentes ressources en ligne proposées par Texas
afin de développer sur la totalité de leurs gammes de processeurs :
http://processors.wiki.ti.com/index.php/Main_Page
•
IDE Code Composer Studio v6.0 basé sur Eclipse et ABI associées (Application Binary
Interface). Nous vous conseillons une installation hors ligne. De plus, le téléchargement des
outils depuis le site officiel de Texas Instruments requiert une inscription à leur site. La
validation par TI de cette étape peut prendre quelques jours, ne pas être surpris :
http://processors.wiki.ti.com/index.php/Category:Code_Composer_Studio_v6
•
Bibliothèques de fonctions C CSL (Chip Support Library) proposées par TI pour la gestion des
périphériques internes de leurs familles de processeurs. Bien choisir la bibliothèque associée
au processeur C6678 :
http://processors.wiki.ti.com/index.php/Chip_support_library
Après installation, CSL est accessible depuis le répertoire suivant :
C:\ti\pdk_C6678_version\packages\ti\csl\
•
Bibliothèque DSPLIB (Digital Signal Processing Library) développée par TI et proposant des
fonctions C pour le domaine du traitement numérique du signal et optimisée pour leurs
architectures processeurs. Dans notre cas, seules les bibliothèques pour la famille C6600 nous
intéressent :
http://www.ti.com/tool/SPRC265
Après installation, la DSPLIB est accessible depuis le répertoire suivant :
C:\ti\dsplib_c66x_version\packages\ti\dsplib\
-5-
Digital Signal Processor
Travaux Pratiques
A partir de maintenant, le travail qui nous attend consiste à implémenter un algorithme de
filtrage FIR (ou produit scalaire) sur une architecture spécialisée pour du calcul numérique. Effectuons
quelques rappels sur cet algorithme. Une implémentation courante de ce filtre consiste à appeler
périodiquement (période d'échantillonnage) l'algorithme dans une optique de calcul en temps réel. A
chaque appel, seul un échantillon de sortie est calculé puis traité :
x() = vecteur d'échantillons d'entrée (taille
égale à N)
a() = vecteur de coefficients
y(k) = échantillon courant de sortie
k = indice courant
N = ordre du filtre
N+1 = nombre de coefficients
L'implémentation vue en travaux pratiques se fera en temps différé. Nous calculerons un
vecteur de sortie complet en traitant l'information depuis un vecteur d'entrée pouvant être très long
et dans tous les cas de figure de taille supérieure ou égale au nombre de coefficients :
x() = vecteur d'échantillons d'entrée (taille
supérieure ou égale à N)
a() = vecteur de coefficients
y() = vecteur d'échantillons de sortie
k = indice courant
N = ordre du filtre
N+1 = nombre de coefficients
Y = taille du vecteur de sortie
Y+N-1 = taille du vecteur d'entrée
La phase de prototypage rapide sous environnement Matlab/Simulink ne sera pas à faire et
vous est donnée (répertoire de projet /dsp/matlab/). Elle est entièrement recouverte par le
programme de 1ière année en Traitement Numérique du Signal. Nous nous attarderons uniquement
sur les problématiques liées à l'intégration sur DSP TMS320C6678. Observons l'implémentation en
pseudo code Matlab au format flottant simple précision IEEE754 de l'algorithme de filtrage en temps
différé précédemment présenté :
function yk = fir_sp(xk, coeff, coeffLength, ykLength)
yk = single(zeros(1,ykLength)); % output array preallocation
% output array loop
for i=1:ykLength
yk(i) = single(0);
% FIR filter algorithm - dot product
for j=1:coeffLength
yk(i) = single(yk(i)) + single(coeff(j)) * single(xk(i+j-1));
end
end
end
-6-
Digital Signal Processor
Travaux Pratiques
Voici enfin quelques compléments d'information concernant le filtre à intégrer sur cible ainsi
que sur les vecteurs d'entrée et de sortie. La synthèse et la génération du filtre a été réalisée sous
Matlab/Simulink via l'outil FDATool. Il s'agit d'un filtre passe bas coupant à 200Hz pour une fréquence
d'échantillonnage à 10KHz, d'ordre 63 (comprenant donc 64 coefficients de symétrie paire en flottant
simple précision IEEE754), offrant un gain unitaire dans la bande passante et une atténuation de
-40dB dans la bande coupée. La bande d'atténuation est comprise entre 200Hz et 600Hz. Ce filtre ne
vise aucune application ou domaine spécifique et ne nous servira qu'à illustrer les concepts et
stratégies d'optimisation sur processeur spécialisé.
Le vecteur de test d'entrée est une simple somme de deux sinusoïdes à 100Hz et 1KHz. Après
filtrage, seule l'harmonique à 100Hz est clairement identifiable. Génération du vecteur sous Matlab :
Fs = 10000;
Ts = 1/Fs;
F1 = 100;
F2 = 1000;
XK_LENGTH = 2048;
t=0 : Ts : (XK_LENGTH-1)*Ts;
% sample frequency
% period frequency
% harmonic n°1 of input vector
% harmonic n°2 of input vector
% 2K/8Kb samples : length of input vector
% time vector
%*** INPUT VECTOR GENERATION
xk = single(sin(2*pi*F1*t) + sin(2*pi*F2*t));
-7-
Digital Signal Processor
Travaux Pratiques
Radar de défense aérienne GM 400
développé par THALES AIR SYSTEMS
Enfin pour conclure, afin de bien prendre conscience de l’intérêt de tel développement et
phases d'optimisation, prenons l'exemple d'une application radar. Nous pouvons observer, ci-dessous,
l'architecture matérielle typique d'un radar. Observons l'emplacement du processeur de traitement
numérique du signal, dans le cas présent un DSP. Il faut savoir que d'autres familles de processeurs
peuvent également remplir cette tâche (GPP, GPU ou FPGA/SoC), chaque famille offrant son lot
d'avantages et d'inconvénients.
Une chaîne numérique de traitement radar implémente également un produit scalaire comme
celui étudié dans cette trame de travaux pratiques (algorithme FIR). A titre indicatif, à lui seul cet
algorithme pèse près de 40% de la charge CPU d'une chaîne complète. Compte-tenu des contraintes
temps réel excessivement lourdes imposées par ce type d'application (qqMo de données à traiter en
qqms), nous pouvons pressentir tout l'intérêt de développer des bibliothèques de fonctions
spécialisées pour l'architecture cible. Ce sera l'objectif premier de cet enseignement tout en
respectant des méthodologies assurant un développement le plus optimal possible.
-8-
Digital Signal Processor
Travaux Pratiques
1. ALGORITHME DE FILTRAGE
Travail préparatoire
•
1. (2,5pt) Il existe deux grandes familles de filtres numériques, les filtres FIR (Finite Impulse
Response) et IIR (Infinite Impulse Response). Quels sont les avantages et inconvénients des
filtres FIR et citer des exemples d'application dans lesquels nous pouvons les rencontrer ?
•
2. (2pt) En vous aidant de l'implémentation en pseudo code Matlab présentée durant le
préambule, proposer une implémentation en C canonique (sans optimisation) de l'algorithme
de filtrage (temps différé) en respectant le prototype de procédure ci-dessous.
/**
* @brief FIR filtering function in canonical C (single precision IEEE754)
* @param xk input array pointer (nyk+na-1 elements)
* @param a coefficients array pointer (na elements)
* @param yk output array pointer (nyk elements)
* @param na number of coefficients
* @param nyk output array size
*/
void fir_sp(
const float * restrict xk, \
const float * restrict a,
\
float * restrict yk,
\
int na,
\
int nyk);
•
3. (0,5pt) Dans le prototype de procédure présenté ci-dessus, nous pouvons observer le
qualificateur de type const. Rappeler son rôle et son utilité.
•
4. (1pt) En vous aidant d'internet, donner également le rôle du qualificateur de type restrict.
Expliquer dans quels cas nous pouvons le manipuler ainsi que son utilité dans le cadre de
cette trame d'enseignement.
•
5. (1,5pt) Rappeler ce qu'est un Timer (cf. programme de première année) pour un processeur
numérique (MCU, DSP, GPP …).
•
6. (0,5pt) Chez Texas Instruments, qu'est-ce que CSL (Chip Support Library) ? Télécharger CSL
pour notre architecture processeur.
•
7. (1pt) Chaque cœur de notre DSP possède un timer 64 bits mis à zéro au reset, extrêmement
simple d'utilisation et travaillant à la fréquence de travail du cœur (soit 1,4Ghz dans notre
cas). Ces timers se nomment TSC (Time Stamp Counter) et servent typiquement à de la
mesure de performance. Pour information, tout processeur GPP Intel actuel possède
également un timer nommé TSC dans chaque cœur. En vous aidant de la documentation
technique de CSL présente dans le répertoire de projet (/dsp/c6678/doc/csl/docs/doxygen
/html/index.html) ou sur internet, détailler l'API (Application Programming Interface) de
programmation proposée par TI afin d'utiliser ces timers.
•
8. (1pt) Proposer un code C permettant la mesure du temps écoulé en nombre de cycles CPU
concernant l'exécution d'une zone de code ainsi que l'affichage de celui-ci via la fonction
standard printf.
-9-
Digital Signal Processor
Travaux Pratiques
1. ALGORITHME DE FILTRAGE
Travail en séance
1.1. Algorithme de référence
Durant toute la trame de travaux pratiques, ne pas oublier de parcourir voire lire les annexes
proposant notamment un tutoriel d'utilisation de l'IDE Code Composer Studio de Texas Instruments,
un exemple de création de projet sous ce même IDE, un exemple de création de bibliothèque statique
… De même, durant la totalité de la trame, Nous vous demanderons de respecter impérativement les
règles de codage (coding style) imposées en annexe.
•
Créer un projet nommé firtest dans le répertoire /dsp/c6678/test/pjct/ incluant le source
/dsp/c6678/test/src/firtest_example.c. S'assurer de la bonne compilation et exécution du
projet.
•
Sans pour autant créer un nouveau projet, retirer le fichier source
/dsp/c6678/test/src/firtest_example.c puis ajouter le fichier source
/dsp/c6678/test/src/firtest_main.c. S'assurer de la bonne compilation et exécution du projet.
Bien penser à lire attentivement le contenu des fichiers d'en-tête.
Rq : Les fichiers d'en-tête servent souvent de documentation pour une libraire ou un
projet logiciel. Ils précisent notamment les services logiciel proposés par une
bibliothèque ou un applicatif et donc ce que nous pouvons faire et ne pas faire avec le
projet courant.
•
Nous allons maintenant intégrer puis exécuter un algorithme de filtrage développé par Texas
Instruments et optimisé pour l'architecture DSP C6600. Celui-ci jouera le rôle d'algorithme de
référence, nous permettra de valider nos futurs vecteurs de sortie et nous servira de mesure
étalon pour nos benchmarks à venir (analyses comparatives).
En vous aidant de la documentation technique de la bibliothèque DSPLIB de TI
présente sous /dsp/c6678/doc/dsplib/DSPLIB_Users_Manual.chm ou
C:\ti\dsplib_c66x_version\docs\ DSPLIB_Users_Manual.chm, appeler depuis la fonction
main la procédure DSPF_sp_fir_gen en respectant le prototype imposé et en passant comme
arguments les variables et macros définies dans les fichiers sources et d'en-tête actuellement
présent dans notre projet (a_sp.h, xk_sp_8Kb.h et firtest.h). Le vecteur de sortie sera sauvé
dans le tableau yk_sp_ti.
•
Visualiser les vecteurs d'entrée et de sortie après exécution du programme en utilisant
l'utilitaire graphique Graph proposé par CCS depuis l'espace de débogage. Après quelques
utilisations de cet utilitaire fort peu utile et performant, nous comprendrons l'utilité
d'automatiser les procédures de tests afin de gagner un temps considérable dans nos
développement.
◦
S'assurer que le projet soit bien configuré en mode Debug
◦
Après compilation, programmation et exécution, basculer dans l’environnement de
travail CCS Debug, puis mettre le programme en pause sans pour autant stopper la
session de debug.
- 10 -
Digital Signal Processor
Travaux Pratiques
◦
Ajouter les variables xk_sp et yk_sp_ti dans la fenêtre Expressions
◦
Clic droit sur chaque variable puis cliquer sur Graph
◦
Paramétrer l'outil comme vous pouvez. Cet utilitaire étant fortement bogué, lent et
peu flexible, nous nous efforcerons par la suite à l'utiliser le moins souvent possible.
◦
Valider graphiquement les vecteurs d'entrée et surtout celui de sortie (limiter le
nombre d'échantillons affichés à 200). Celui-ci nous servira de vecteur étalon pour tous
nos futurs développement :
vecteur d'entrée (200 échantillons)
vecteur de sortie (200 échantillons)
1.2. Test de performance
•
En vous aidant du travail préparatoire, valider l'utilisation du timer TSC puis effectuer une
mesure de performance de l'algorithme développé par TI en vous aidant de la fonction
standard printf. Rappelons, qu'une API d'utilisation du timer TSC est proposée par CSL.
Rq : Dans le projet actuellement en cours de développement, l'utilisation de la fonction
C standard printf ne pose que peu de soucis car seules certaines zones de code bien
spécifiques sont en cours de test et de validation. En effet, il ne s'agit pas d'un système
logiciel complet. Néanmoins, dans les années à venir, bien garder en tête que cette
fonction standard est rarement optimisé pour s'exécuter sur un processeur de type
MCU ou DSP et est à bannir durant vos phases de développement. Son temps
d'exécution est souvent excessivement long. Par exemple, ne jamais appeler un printf
depuis une ISR (Interrupt Software/Service Routine). Pour résumer, dans le domaine de
l'embarqué, bannir dès que possible la fonction printf de vos développements sauf si
vous développez sur processeur généraliste (GPP, SoC) sous un système d'exploitation
évolué (GNU\Linux, Android …). En général, des fonctions optimisées non standards
sont fournies par l'ABI, par exemple la fonction LOG_printf chez TI.
- 11 -
Digital Signal Processor
Travaux Pratiques
•
Temps écoulé en nombre de cycles CPU pour l'algorithme de référence développé par TI :
CPU cycles
1.3. Intégration en C canonique
Jusqu'à présent, nous n'avons fait que créer un projet et appeler des fonctions développées
par d'autres, en l’occurrence des ingénieurs d'application de chez Texas Instruments. Nous allons
attaquer nos premières phases de développement pour à terme essayer de faire mieux que Texas.
L'avenir nous dira si nous réussirons.
•
Sans créer de nouveau projet ni toucher au code précédemment écrit, rajouter un test de
performance pour l'algorithme de filtrage fir_sp. Inclure le fichier
/dsp/c6678/firlib/src/fir_sp.c au projet puis le modifier. Cet algorithme est une
implémentation standard C99 canonique (sans optimisation) d'un produit scalaire. Il nous
servira quant à lui de référence dans nos futures phases d'optimisation.
Ajouter le source au projet puis compléter la définition de la fonction en vous aidant du travail
préparatoire. S'assurer de la bonne compilation et exécution du projet. Valider également avec
l'outil Graph le vecteur de Sortie. Le vecteur de sortie sera sauvé dans le tableau yk_sp.
•
L'outil d'analyse graphique Graph permet-il une validation rigoureuse du vecteur de sortie ?
Justifier votre réponse.
•
Instrumenter le code afin de réaliser un mesure de performance. En vous aidant des annexes,
configurer le projet en mode Release puis effectuer le test (debug retiré et options
d'optimisation levées). Noter le temps écoulé en nombre de cycles CPU pour l'algorithme de
référence FIR en C canonique :
CPU cycles
•
Modifier maintenant votre projet de façon à respecter l'affichage suivant :
- 12 -
Digital Signal Processor
Travaux Pratiques
2. AUTOMATISATION DES TESTS
Travail préparatoire
•
1. (3pt) En utilisant des fonctions standards du C, proposer le code d'une fonction réalisant la
comparaison terme à terme de chaque élément de deux tableaux de même taille. Cette
fonction retournera par pointeur le pourcentage d'erreur maximum entre chaque élément
ainsi que ''1'' par valeur de retour si les deux vecteurs sont égaux et ''0'' sinon.
Boolean compare( float *error_percent, float *src1, float *src2, int size);
•
2. (3,5pt) Nous pouvons observer, ci-dessous, le prototype de la fonction ptFunction ayant
pour tâche d'assurer les futurs tests de performance des différents algorithmes de filtrage
optimisés. Nous pouvons constater que l'argument est un pointeur de fonction. La fonction
réellement implémentée dans la trame de TP aura un prototype légèrement différent, mais
utilisera également un pointeur de fonction. Le but de cette fonction est de réaliser une
mesure de performance par indirection de pointeur. Dans un premier, l'affichage sera réalisé
dans la fonction (fait dans le main par la suite).
void ptFunction( void (*fir_fct) (
const float * restrict,
const float * restrict,
float * restrict,
int,
int) );
\
\
\
\
En vous aidant d'internet, écrire l'appel de la procédure ptFunction en invoquant la
fonction fir_sp déjà définie. Proposer également une implémentation de la fonction
ptFunction afin qu'elle puisse invoquer la procédure dont le pointeur a été passé en
argument. Dans notre cas, la fonction fir_sp avec les arguments associés (cf. partie 1 –
algorithme de référence).
•
3. (1,5pt) Si nous les rencontrons dans un fichier source .c, quel est le rôle des directives préprocesseur #if en #endif ? Donner des exemples d'utilisation de ces directives.
•
4. (2pt) Nous pouvons observer ci-dessous l'implémentation Matlab du code assurant la
génération des vecteurs d'entrée (somme de 2 sinusoïdes à 100Hz et 1KHz et d'amplitude 1)
Fs = 10000;
F2 = 1000;
F1 = 100;
Ts = 1/Fs;
XK_LENGTH = 2048;
%XK_LENGTH = 1048576;
t=0 : Ts : (XK_LENGTH-1)*Ts;
% sample frequency
% frequency harmonic n°2 (input vector)
% frequency harmonic n°1 (input vector)
% sample period
% 2K/8Kb samples : length of input temporal vector
% 1M/4Mb samples : length of input temporal vector
% time vector
%*** INPUT ARRAY GENERATION
xk = single(sin(2*pi*F1*t) + sin(2*pi*F2*t));
Écrire une boucle en langage C assurant l'initialisation d'un tableau xk[XK_LENGTH]
avec une somme de deux sinusoïdes comme nous pouvons l'observer dans le code Matlab cidessus.
- 13 -
Digital Signal Processor
Travaux Pratiques
2. AUTOMATISATION DES TESTS
Travail en séance
Cette partie est essentielle et va nous permettre de gagner un temps non négligeable durant
nos futures phases de développement. Nous avons pu constater dans la partie précédente que la
validation des données de sortie d'algorithmes n'est pas forcément triviale, surtout dans le cas de
larges vecteurs de données. Une validation manuelle terme à terme faite par le développeur n'est pas
concevable dans un contexte industriel. Le temps ingénieur étant probablement la ressource la plus
précieuse dans une optique de conduite et de suivi de projet. Les exercices qui vont suivre seront
donc découpés en trois grandes parties dans une optique d'optimiser notre temps de développement
durant nos travaux futurs :
•
•
•
Écriture d'un test de conformité
Écriture d'un test de performance
Optimisation du temps ingénieur durant les phases de test
2.1. Test de conformité
Nous allons débuter par le développement d'un test de conformité assurant la validation de
nos futurs vecteurs de sortie. Nous utiliserons toujours pour référence le vecteur de sortie de
l'algorithme de Texas Instruments (algorithme DSPF_sp_fir_gen et vecteur de sortie yk_sp_ti). Ce test
est très générique et peut très bien être réutilisé dans un cadre plus large pour d'autres algorithmes à
tester.
•
Dans le fichier d'en-tête firtest.h présent dans le répertoire /dsp/c6678/test/h/ nous pouvons
observer la déclaration d'un type structure TestSystem_obj spécifique à notre projet. Pour
notre projet, il s'agit d'un objet logiciel mis à jour notamment par la fonction firtest_sys et
sauvegardant temporairement les sorties de nos différents tests de conformité. Observer son
contenu et le commenter exhaustivement ci-dessous. Ne pas hésiter à interroger l'enseignant
encadrant si vous avez un doute :
typedef struct {
char
uint64_t
float
float
} TestSystem_obj;
•
error_status[4];
error_samples;
error_percent;
error_margin;
// string to specify validity of test (''OK'' or ''NOK'')
// number of false samples
// maximum percentage error
// maximum tolerated margin. Have to be initialized
// conformity test object
Ajouter au projet le fichier source firtest_sys.c et s'assurer de sa bonne compilation.
- 14 -
Digital Signal Processor
Travaux Pratiques
•
Sachant que le champ error_margin est à initialiser au début du lancement du programme
(erreur tolérée pour notre procédure de test) et en vous aidant du travail préparatoire, écrire
et valider le code implémentant la fonction firtest_sys. Cette fonction est découpée en trois
parties :
◦
initialisation de la boucle de test
◦
boucle de test assurant une comparaison terme à terme des différents
éléments des vecteurs d'entrée, capturant le pourcentage d'erreur maximale
mesurée durant la totalité du test et comptant le nombre d'échantillons ayant
une erreur supérieure à la marge fixée dans le cahier des charges du projet.
Dans notre cas, nous tolérerons une erreur strictement inférieure à 1%.
◦
Mise à jour de l'objet logiciel capturant les informations de sortie du test de
conformité. Nous pourrons enfin quitter la procédure en retournant un
booléen indiquant la validité du test.
Rq : Comme dans beaucoup de développement logiciel dans l'embarqué, il est courant
d'utiliser la valeur de retour d'une fonction comme indicateur de sa bonne exécution
(valeur booléenne). De même, comme pour une approche orientée objet, notre
procédure de test n'a pour rôle que d'appliquer un traitement à l'objet d'entrée. Aucun
affichage ne doit être fait dans cette fonction. Tous les affichages et interfaces
développeur seront gérées depuis la fonction main. De plus, si votre implémentation
respecte des standards du C (exemple C99), ceci permet d'assurer une portabilité pour
d'éventuelles réutilisations dans d'autres projets.
•
Modifier maintenant votre projet de façon à respecter l'affichage suivant. L'affichage des
performances de l'algorithme en cours de test ne devra se faire que si la validité du vecteur de
sortie associé est actée.
- 15 -
Digital Signal Processor
Travaux Pratiques
2.2. Test de performance
Nous allons maintenant nous attaquer à l'écriture d'un test de performance générique,
pouvant être appliqué à la totalité des algorithmes de filtrage FIR sous tests mais également aux
futurs modèles mémoires déployés dans la suite de la trame de travaux pratiques.
•
Dans le fichier d'en-tête firtest.h présent dans le répertoire /dsp/c6678/test/h/ nous pouvons
observer la déclaration d'un type structure TestPerf_obj spécifique à notre projet. Pour notre
projet, il s'agit d'un objet logiciel mis à jour notamment par la fonction firtest_perf et
sauvegardant temporairement les sorties de nos différents tests de performance. Observer
son contenu et le commenter exhaustivement ci-dessous. Ne pas hésiter à interroger
l'enseignant encadrant si vous avez un doute :
typedef struct {
uint32_t
CSL_Uint64
float32_t
float32_t
} TestPerf_obj;
perf_rep;
perf_nbcycles;
perf_usertime_ms;
perf_macs;
// number of test repetitions
// duration in number of CPU cycles
// duration in ms
// algorithm performance in MAC per CPU cycle
// performance test object
Rq : Pour information, dans le domaine l'embarqué appliqué au traitement numérique
du signal, l'un des principaux indicateurs de performance d'un algorithme est donné par
son nombre de MAC's par cycle CPU (ou GMACS, Giga Multiply Accumulate per
Second). Cet indicateur étant lié à la complexité mathématique de l'algorithme (cf.
enseignement d'algorithmique). Dans le cas de notre architecture, un DSP de la famille
C6600 peut offrir jusqu'à 8 MAC/cy par cœur (soit un maximum de 11,2 GMACS avec
une horloge à 1,4GHz par cœur et 89,6 GMACS pour le processeur).
•
Que vaut la complexité en MACS de notre algorithme implémentant un produit scalaire (filtre
FIR) ?
•
Pourquoi, dans une optique de benchmarking, est-il intéressant de répéter plusieurs fois
l'exécution de l'algorithme sous test puis de moyenner les résultats de sortie (uniquement sur
architecture déterministe) ? Nous constaterons qu'il sera plus rigoureux de prendre la valeur
maximale sur architecture non déterministe (GPP, AP à CPU superscalaires ou sur GPU).
- 16 -
Digital Signal Processor
Travaux Pratiques
•
Ajouter au projet le fichier source firtest_perf.c et s'assurer de sa bonne compilation.
•
En vous aidant du travail préparatoire, écrire et valider le code implémentant la fonction
firtest_perf. Dans un premier temps, nous ne tiendrons pas compte du paramètre
memoryModel qui sera abordé par la suite. Le paramètre output permet quant à lui d'écraser
le vecteur précédemment sous test par le vecteur de sortie en cours de validation. Cette
fonction peut être découpée dans deux parties :
◦
Boucle fixant le nombre d'appels de l'algorithme en cours de test. La mesure et
l'accumulation des temps d'exécution se feront dans la boucle.
◦
Mise à jour de l'objet logiciel capturant les informations de sortie du test de
performance.
/**
* @brief performance test for FIR algorithms library
* @param benchmark statistics about current algorithm performance
* @param memoryModel memory model architecture. Accepted values :
* @li UMA_L2CACHE_L1DCACHE
* @li UMA_L2SRAM_L1DCACHE
* @li UMA_L2SRAM_L1DSRAM
* @param output output vector to overwrite
* @param fir_fct pointer to function under test
*/
void firtest_perf( TestPerf_obj *benchmark,
uint8_t memoryModel,
float * restrict output,
void (*fir_fct) ( const float * restrict,
const float * restrict,
float * restrict,
int,
int) );
•
Modifier maintenant votre projet de façon à respecter l'affichage suivant.
- 17 -
Digital Signal Processor
Travaux Pratiques
2.3. Temps de compilation et empreinte mémoire
Afin d'optimiser le temps de compilation de notre projet ainsi que l'empreinte mémoire du
code une fois chargé en mémoire interne du processeur, nous allons appliquer une astuce
excessivement simple. Cette étape peut jouer un léger rôle dans les phases de benchmarking. En
effet, en minimisant l'empreinte mémoire du code, nous pouvons améliorer l'usage du cache L1P
(Level 1 Cache Program), limité à 32Ko sur notre architecture. Cette partie reste non critique dans le
cadre de la trame de travaux pratiques compte-tenu de l'empreinte mémoire globale du projet.
•
En vous aidant du travail préparatoire, encapsuler la procédure de test de l'algorithme fir_sp
entre deux directives de pré-compilation #if et #endif (printf's, test de performance et test de
conformité). Nous pourrons ainsi ajouter ou retirer du code à la compilation en utilisant les
macros ci-dessous présentes dans le fichier firtest.h. Dans un premier temps, nous
n'utiliserons que la macro TEST_FIR_SP :
/* algorithms and memory model under test */
#define TEST_FIR_SP
#define TEST_FIR_SP_R4
#define TEST_FIR_SP_OPT_R4
#define TEST_FIR_ASM
#define TEST_FIR_ASM_MANUAL
#define TEST_FIR_ASM_PIPE
#define TEST_FIR_ASM_R4
#define TEST_FIR_L2SRAM_L1DCACHE
#define TEST_FIR_L2SRAM_L1DSRAM
#define TEST_FIR_L2SRAM_L1DIDMA
#define TEST_FIR_L2EDMA_L1DIDMA
1
0
0
0
0
0
0
0
0
0
0
Rq : Cette technique est très couramment utilisée dans le monde de l'embarqué dès
que nous avons à manipuler des bibliothèques ou outils logiciel de taille importante
(bibliothèques réseau, graphique, USB, systèmes de fichiers … ou également des
systèmes d'exploitation temps réel ...). En effet, par exemple nous pouvons très bien
utiliser une librairie réseau offrant un très grand nombre de services (IP, ICMP, UDP, TCP,
HTTP, SMTP, FTP, DNS ...) pour n'en exploiter qu'une poignée (par exemple ICMP, IP,
TCP). Auquel cas, inutile d'embarquer sur le processeur tous les autres services. La
bibliothèque est alors fournie avec un header incluant des macros de configuration des
services strictement nécessaires. Nous pouvons ainsi, après compilation, minimiser
l'empreinte mémoire globale de notre projet dans une optique de réduction des coûts
liés à la technologie processeur déployée. N'oublions pas que le domaine des systèmes
embarqués, contrairement au monde des ordinateurs, nous travaillons le plus souvent
en milieu contraint (ressources CPU et ressources mémoire).
- 18 -
Digital Signal Processor
Travaux Pratiques
2.4. Temps de programmation
Cette étape, quant à elle, est relativement importante. Nous allons effectuer un test simple
afin de bien en comprendre l'importance. L'objectif premier étant de minimiser notre temps de
développement global en jouant sur le temps de chargement du programme via la sonde de
programmation. Rappelons également que nous utilisons la sonde de programmation XDS100,
modèle bas de gamme porté directement sur la plateforme de développement. A titre indicatif, le
coût moyen d'une sonde XDS560 (haut de gamme) en fonction du distributeur peut varier entre
1000€ et 2500€. De plus, si nous souhaitons l'interfacer avec l'IDE, nous devons alors payer la licence
de celui-ci (1000-3000€).
•
Modifier l'inclusion du fichier d'en-tête xk_sp_8Kb.h (vecteur de 8Ko soit 2048 échantillons
d'entrée au format flottant simple précision IEEE754) afin d'appeler le header xk_sp_256Kb.h
(vecteur de 256Ko soit 64K échantillons d'entrée au format flottant simple précision IEEE754)
également présent dans le répertoire /dsp/c6678/test/h/. Ce fichier a également été créé
depuis l'environnement de prototypage Matlab/Simulink. Compiler puis expliquer le temps
passé à attendre la fin de la programmation du processeur.
Rq : Pour information, il faut savoir que dans le domaine des applications radar, les
vecteurs d'entrée ont le plus souvent une taille de plusieurs Mo. Il en est de même pour
le domaine du traitement d'image pour lequel ce processeur est également spécialisé.
•
Ajouter au projet le fichier source firtest_init.c et s'assurer de sa bonne compilation.
•
Intégrer les initialisations actuellement faites au début du main dans la fonction firtest_init
(TSC timer, erreur tolérée pour le test de conformité et nombre d'exécution de l'algorithme
sous test) puis s'assurer de la bonne compilation et du bon fonctionnement du projet.
•
Maintenant, nous n'utiliserons le vecteur d'entrée sous forme d'une allocation statique préinitialisée, mais sous forme d'un vecteur vide initialisé au démarrage de l'application par le
processeur lui-même.
En vous aidant du travail préparatoire, retirer les appels des fichiers d'en-têtes xk_sp_XXb.h
du projet, déclarer une variable globale de type tableau nommée xk_sp puis assurer son
initialisation depuis la fonction firtest_init. Compiler et valider graphiquement (outil Graph) la
forme d'onde et échantillons du vecteur d'entrée.
#include <firtest.h>
#include <a_sp.h>
/* input and ouputs vectors */
float32_t xk_sp[XK_LENGTH];
- 19 -
Digital Signal Processor
Travaux Pratiques
Voilà, à ce stade là de la trame de travaux pratiques, nous venons de terminer les phases de
développement les moins intéressantes mais qui restent néanmoins nécessaires. Nous commençons
à maîtriser l'environnement de développement, les procédures de test sont écrites, automatisées et
intégrées au projet. Nous sommes maintenant prêt à attaquer le vif du sujet, l'optimisation
d'algorithme architecture dépendante.
•
Valider une dernière fois l'interface offerte par notre projet de test en respectant l'affichage cidessous ainsi que les règles de codage imposées :
- 20 -
Digital Signal Processor
Travaux Pratiques
3. PROGRAMMATION VECTORIELLE
Travail préparatoire
•
1. (4,5pt) Sachant que les paramètres d'entrée de toute fonction assembleur appelée depuis
un fichier source C sont respectivement passés par les registres A4, B4, A6, B6, A8 … etc sous
notre chaîne de compilation et architecture, écrire en assembleur canonique C6600 (sans
optimisation) le code équivalent à la fonction C canonique de filtrage fir_sp.
; file firlib\src\fir_sp_asm.s
; brief FIR filtering function in canonical C6600 assembler (single precision IEEE754)
; author
; date
.global fir_sp_asm
; C prototype :
match registers :
;
; void fir_sp_asm ( const float * restrict xk, -> A4
;
const float * restrict a, -> B4
;
float * restrict yk,
-> A6
;
int na,
-> B6
;
int nyk);
-> A8
fir_sp_asm:
; user code
B
B3
NOP
5
; end of fir_sp_asm procedure
•
2. (5pt) Proposer une implémentation C pour la fonction de filtrage fir_sp_r4 effectuant un
déroulement de boucle d'un facteur 4 sur les deux boucles internes de l'algorithme de filtrage
fir_sp. Attention, ce travail est légèrement plus complexe qu'il n'y parait !
void fir_sp_r4 ( const float * restrict xk, \
const float * restrict a,
\
float * restrict yk,
\
int na,
\
int nyk){
int i, j;
float acc1, acc2, acc3, acc4;
float a0, a1, a2, a3;
float xk0, xk1, xk2, xk3, xk4, xk5, xk6;
/* input array loop */
for (i=0; i<nyk; i+=4) {
/* user code */
/* FIR filter algorithm - dot product */
for (j=0; j<na; j+=4){
/* user code */
}
/* user code */
}
}
- 21 -
Digital Signal Processor
Travaux Pratiques
•
3. (0,5pt) Nous constaterons dans la suite de la trame de travaux pratiques que
l'implémentation précédemment écrite est bien plus performante qu'une implémentation en
C canonique standard sans déroulement de boucle. Néanmoins, quels inconvénients et
limitations amènent cette implémentation ?
- 22 -
Digital Signal Processor
Travaux Pratiques
3. PROGRAMMATION VECTORIELLE
Travail en séance
Nous allons maintenant attaquer les premières phases d'optimisation mono-cœur
architecture dépendante de notre projet (DSP C6600 de Texas Instruments). Les exercices qui suivent
peuvent être découpés en trois grande parties :
•
•
•
•
Optimisations assembleur C6600
Optimisations en langage C
Comparatif architectures IA-64 extensions SSE4.1
Ajouter au répertoire logique src du projet un sous répertoire firlib puis y ajouter les sources
élèves des algorithmes de filtrage à développer présents dans /dsp/c6678/firlib/src/.
S'assurer de la bonne compilation du projet.
3.1. Assembleur canonique C6600
•
Sélectionner puis copier à la suite du main le code encapsulé entre les directives préprocesseur #if ( TEST_FIR_SP !=0 ) et #endif comprises. Modifier la section de code copiée
ainsi que les commentaires afin d'assurer les tests de la fonction assembleur canonique
fir_sp_asm. S'assurer de la bonne compilation du projet.
Cette étape sera à répéter durant nos développements futurs pour chaque implémentation
avec optimisation. Nous pourrons ainsi nous focaliser sur les stratégies et problématiques
d'optimisation et non plus sur les procédures de test et de benchmarking.
- 23 -
Digital Signal Processor
Travaux Pratiques
•
En vous aidant du travail préparatoire, implémenter le code de la fonction fir_sp_asm puis
valider son fonctionnement.
Voici ci-dessous quelques conseils afin de faciliter votre développement. Dans tous les cas, il
vous faudra impérativement travailler en mode Debug. Depuis l'espace de travail CCS Debug,
utiliser la fenêtre Registers afin d'observer en temps réel le contenu des registres CPU. Placer
des points d'arrêts (double clic dans la fenêtre d'édition sur le numéro de la ligne souhaitée)
judicieusement dans votre code puis exécuter pas à pas le programme (CCS Debug > Run >
Assembly Step). Maintenant, voici comment écrire ou tester efficacement la procédure :
◦
étape n°1 : s'assurer que l'appel et le retour de la procédure se déroulent bien
puis vérifier les valeurs des paramètres d'entrée de la fonction.
◦
étape n°2 : implémenter la boucle vide avec la condition de sortie associée.
S'assurer du bon nombre d'itérations et du fait de quitter celle-ci.
◦
étape n°3 : dans la boucle, vérifier les premiers chargements de données de la
mémoire vers le cœur ainsi que la validité des données pré-chargées.
◦
étape n°4 : écrire le code correspondant au produit scalaire dans le cœur de la
boucle. Cette partie est plus complexe à tester et à valider.
◦
remarque : de façon générale, toujours tester les valeurs limites : condition de
sortie de boucle, débordement de tableau, valeurs de pointeurs mémoire …
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
•
Valider l'affichage dans la console de sortie de l'IDE :
- 24 -
Digital Signal Processor
Travaux Pratiques
3.2. Assembleur VLIW C6600
Contrairement aux processeurs superscalaires, les architectures VLIW (Very Long Instruction
Word) et EPIC (Explicitly Parallel Instruction Computing) ont pour point commun d'avoir un code outof-order en mémoire (OOO ou dans le désordre) mais offrant une exécution in-order (exécution et
sortie du pipeline dans l'ordre). Dans cet exercice, nous allons jouer sur ce point sans pour autant
utiliser d'instructions vectorielles ni tenter d'avoir une utilisation optimale du pipeline. En effet, nous
n'utiliserons que des instructions scalaires.
Rappelons le principe de ce type d'optimisation, uniquement applicable sur architecture CPU
VLIW et EPIC. L'exemple qui suit présente un avancement de branche d'exécution sur architecture
C6600. Le code est alors dans le désordre en mémoire et pourtant les deux files d'instructions cidessous réalisent le même traitement :
Canonical C6600 assembly
instructions in-order in memory
(processing time : 14 CPU cycles)
MPYSP
A3,B9,A17
NOP
3
FADDSP A17,A5,A5
NOP
2
[A1] SUB
A1,1,A1
[A1] B
L2
NOP
5
VLIW C6600 assembly
instructions out-of-order in memory
(processing time : 7 CPU cycles)
MPYSP
|| [A1] SUB
[A1] B
NOP
FADDSP
NOP
A3,B9,A17 ; instructions bundle
A1,1,A1
L2
2
A17,A5,A5
2
•
Comme pour l'algorithme fir_sp_asm, copier un bloc de code assurant les tests de conformité
et de performance d'un algorithme puis le modifier de façon à obtenir une section de test
pour l'algorithme fir_sp_asm_vliw.
•
Implémenter le code de la fonction fir_sp_asm_vliw, effectuant une optimisation manuelle
par avancement de branches d'exécution de l'algorithme fir_sp_asm puis valider son
fonctionnement. Comme pour chaque phase de développement, bien travailler en mode
Debug.
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
- 25 -
Digital Signal Processor
Travaux Pratiques
3.3. Pipelining logiciel assembleur C6600
Dans cet exercice, nous allons nous efforcer d'obtenir une utilisation optimale du pipeline
logiciel pour l'algorithme écrit en assembleur canonique. Cet exercice consiste à jouer sur le nombre
d'unités d'exécution du cœur en essayant d'utiliser le CPU au maximum de son potentiel théorique.
Dans notre cas, chaque cœur possédant 8 unités d'exécution, toutes capables de travailler en
parallèle, nous chercherons à obtenir un maximum de 8 instructions exécutées par cycle CPU. Nous
allons donc nous intéresser au parallélisme d'instructions.
Pour notre algorithme, sans déroulement de boucle (vectorisation des données), le facteur
optimal d'optimisation en terme d'accélération sera obtenu à travers cet exercice. Observons de
façon graphique dans un tableau l'architecture du code à implémenter. Dans la table de
programmation ci-dessous, vous trouverez une implémentation de la boucle interne, la boucle
externe restant inchangée. Le prolog est exécuté une seule fois, la boucle kernel autant de fois qu'il y
a d'itérations de boucles en retranchant la profondeur du prolog et l'epilog sera également exécuté
qu'une seule fois. L'allocation des registres reste libre dans le cadre de cet exercice.
Unités d'Exécution
(occupation des pipelines hardware et software)
.D1
P
R
O
L
O
G
K
E
R
N
E
L
E
P
I
L
O
G
.M1
.S1
.L1
.L2
.S2
.M2
.D2
LDW
LDW
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
LDW
MPYSP
LDW
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
LDW
MPYSP
BDEC
FADDSP
LDW
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
MPYSP
FADDSP
NOP
NOP
NOP
NOP
NOP
NOP
FADDSP
NOP
NOP
- 26 -
Digital Signal Processor
Travaux Pratiques
•
Comme pour les algorithmes précédents, copier un bloc de code assurant les tests de
conformité et de performance d'un algorithme puis le modifier de façon à obtenir une section
de test pour l'algorithme fir_sp_asm_softPipeline.
•
Implémenter le code de la fonction fir_sp_asm_softPipeline, effectuant la technique de
pilpelining logiciel vu en cours sur l'algorithme fir_sp_asm puis valider son fonctionnement.
Comme pour chaque phase de développement, bien travailler en mode Debug.
Voici ci-dessous quelques conseils afin de faciliter votre développement :
•
◦
étape n°1 : la profondeur de la boucle kernel interne doit posséder une taille
supérieure ou égale au nombre de cycles CPU nécessaires à l'exécution de
l'instruction B (branch).
◦
étape n°2 : tester la condition de sortie de la boucle ainsi que le bon nombre
d'itérations.
◦
étape n°3 : vérifier les données préchargées depuis la mémoire par les
instructions de chargement sur plusieurs itérations.
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
Rq :
•
Les NOP's (delay slot) peuvent être vus comme des espaces libres dans lesquels
peuvent être insérer des instructions à exécuter en parallèle. Dans une optique de
déroulement de boucle (vu dans les parties qui suivent), nous pourrons alors être
amenés à charger de façon conséquente la boucle kernel actuellement sous
exploitée.
•
sans nous en rendre compte, au fil des exercices en assembleur précédents et
actuel nous nous efforçons d'entrer dans la tête de notre compilateur C. En effet,
lorsque les options d'optimisation sont levées, il tente d'appliquer un maximum
des techniques d'accélération que nous sommes en train de découvrir. Sans pour
autant obtenir une implémentation optimale. Nous constaterons par la suite que si
nous souhaitons obtenir des facteurs d'accélération optimaux en restant à l'étage
du langage C, il nous faudra effectuer du refactoring de code ainsi qu'aiguiller au
maximum la chaîne de compilation. Nous perdrons alors en portabilité de code.
- 27 -
Digital Signal Processor
Travaux Pratiques
3.4. Assembleur vectoriel C6600
Durant cet exercice, nous allons nous intéresser au parallélisme des données en utilisant un
maximum d'instructions vectorielles SIMD (Single Instruction Multiple Data) permettant un
traitement des opérandes à l'étage assembleur par vecteur de données (2, 4, 8 …) et non plus par
données scalaires (une à une). De plus, ceci nous permettra de minimiser l'usage du pipeline logiciel
d'instructions et donc le nombre d'unités d'exécution utilisées. Laissant ainsi la place potentielle à
d'autres instructions.Prenons l'exemple d'une même section de code avec des instructions scalaires
et vectorielles. Dans les deux cas, le traitement réalisé est le même :
C6600 scalars assembly instructions
(processing time : 18 CPU cycles, 4 instructions)
LDW
NOP
LDW
NOP
MPYSP
NOP
MPYSP
NOP
•
C6600 vectorials assembly instructions
(processing time : 9 CPU cycles, 2 instructions)
*A19++, A25
4
*A19++, A24
4
A25, B23, B5
3
A24, B22, B4
3
LDDW
NOP
DMPYSP
NOP
*A19++, A25:A24
4
A25:A24, B23:B22, B5:B4
3
Comme pour les algorithmes précédents, copier un bloc de code assurant les tests de
conformité et de performance d'un algorithme puis le modifier de façon à obtenir une section
de test pour l'algorithme fir_sp_asm_r4.
Nous allons maintenant effectuer un déroulement d'un facteur 4 de la boucle interne de
l'algorithme (boucle externe inchangée). Voici ci-dessous une implémentation C du code assembleur
à implémenter :
void fir_sp_r14 ( const float * restrict xk,
const float * restrict a,
float * restrict yk,
int na,
int nyk){
int i, j;
float xk0, xk1, xk2, xk3;
float a0, a1, a2, a3;
float acc0, acc1, acc2, acc3;
\
\
\
\
/* input array loop */
for (i=0; i<nyk; i++) {
acc0 = 0.0;
acc1 = 0.0;
acc2 = 0.0;
acc3 = 0.0;
/* FIR filter algorithm - dot product */
for (j=0; j<na; j+=4){
a0 = a[j];
a1 = a[j+1];
a2 = a[j+2];
a3 = a[j+3];
xk0 = xk[j+i];
- 28 -
Digital Signal Processor
Travaux Pratiques
xk1 = xk[j+i+1];
xk2 = xk[j+i+2];
xk3 = xk[j+i+3];
acc0 += a0*xk0;
acc1 += a1*xk1;
acc2 += a2*xk2;
acc3 += a3*xk3;
}
}
yk[i] = acc0 + acc1 + acc2 + acc3;
}
Afin d'implémenter de façon optimale cet algorithme, nous allons utiliser des instructions
vectorielles. En s'aidant des annexes ou de la documentation technique SPRUGH7, se documenter sur
les instructions suivantes :
•
•
•
•
•
LDDW
LDNDW
DMPYSP
DADDSP
En quoi diffère l'instruction LDDW de l'instruction LDNDW ?
Rq : Il faut savoir que notre chaîne de compilation réalise par défaut, notamment sur la
pile, des alignements mémoire sur les données (adresses des données multiples de N
octets, 8o dans notre cas). Observons ci-dessous un exemple de défaut d'alignement
mémoire et essayons de comprendre la nécessite d'aligner nos données en mémoire dans
le cas d'utilisation de fonctions intrinsèques. Un alignement mémoire garanti une utilisation
optimale des accès aux ressources mais ajoute néanmoins de nombreux espaces/slots vide
en mémoire cache et en RAM. Dans le cas présent, cette technique accélère donc
l'exécution du code mais néanmoins dégrade l'occupation des ressources mémoire.
Rappelons également que le taille des chemins/paths d'un CPU vers la mémoire L1 donnée
vaut 64 bits soit 8 octets sur notre architecture. Prenons ci-dessous un exemple de jeu de
données, les premières non-alignées et les secondes alignées modulo 8 octets :
short foo ;
float bar[3] ;
- 29 -
Digital Signal Processor
Travaux Pratiques
•
Implémenter le code de la fonction fir_sp_asm_r4, effectuant un déroulement de boucle d'un
facteur 4 sur la boucle interne de l'algorithme puis valider son fonctionnement. Comme pour
chaque phase de développement, bien travailler en mode Debug.
En observant le code élève fourni, nous pouvons constater un traitement étrange à l'entrée et
à la sortie de la procédure (cf. ci-dessous). En vous aidant de la documentation technique
SPRU187V – Optimizing Compiler, Chapter 7.3 Register Conventions, Table 7-2. Register
Usage dédiée aux mécanismes d'optimisation sur architectures C6000, comprendre et
expliquer le rôle des sections de code rajoutées.
save_context
.macro rsp
; save core working registers context on the top of stack
MV
B15,rsp ; save Stack Pointer
STDW
B15:B14,*rsp--[1]
STDW
B13:B12,*rsp--[1]
STDW
B11:B10,*rsp--[1]
STDW
A15:A14,*rsp--[1]
STDW
A13:A12,*rsp--[1]
STDW
A11:A10,*rsp--[1]
MVC
ILC,B15
MVC
RILC,B14
STDW
B15:B14,*rsp--[1]
; don't use rsp register in current assembly procedure
.endm
restore_context .macro rsp
; restore core working registers context from the top of stack
LDDW
*++rsp[1],B15:B14
MVC
B14,RILC
MVC
B15,ILC
LDDW
*++rsp[1],A11:A10
LDDW
*++rsp[1],A13:A12
LDDW
*++rsp[1],A15:A14
LDDW
*++rsp[1],B11:B10
LDDW
*++rsp[1],B13:B12
LDDW
*++rsp[1],B15:B14
MV
rsp,B15 ; restore Stack Pointer
NOP
3
.endm
fir_sp_asm_r4: ; save core registers context
save_context
A3
; user code
; restore core registers context and leave procedure
restore_context A3
B
B3
NOP
5
La directive préprocesseur assembleur .macro s'utilise comme une macro fonction en langage
C (macro avec paramètres). Par exemple :
#define mul(x, y) x * y
// mul(1,4) équivalent à 1*4 avant compilation
- 30 -
Digital Signal Processor
Travaux Pratiques
•
Quelle limitation amène l'implémentation de notre algorithme de filtrage ?
•
Durant l'implémentation de notre algorithme assembleur en base 4 (radix 4), quels sont les
deux registres de travail du CPU que nous ne devons en aucun cas utiliser durant la totalité du
traitement de la fonction ?
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
Rq : voilà, à ce stade là de la formation en systèmes embarqués, vous devez être apte à
porter un esprit critique sur les performances annoncées d'un processeur et ne plus
regarder que sa seule fréquence de travail. Un processeur, même mono-cœur, cadencé à
500MHz peut très bien offrir des performances supérieures à un processeur multi-coeurs
cadencé à 2GHz. Pour une architecture mono-cœur, il vous faut notamment tenir compte :
•
•
•
de sa faculté à traiter des instructions en parallèle (nombre d'unités d'exécution
pouvant travailler simultanément)
de sa faculté à traiter les données par paquet (largeur des paths CPU, largeur des
registres de travail et jeu d'instructions vectorielles)
de la technologie de son pipeline matériel (superscalaire, VLIW ou EPIC), voire pour
les plus curieux, des stratégies d'accélération interne au pipeline (le plus souvent
aux étages de décodage et d'exécution)
- 31 -
Digital Signal Processor
Travaux Pratiques
3.5. Déroulement de boucle en C
A partir de maintenant et jusqu'à la fin de votre cursus, j'ai fini de vous torturer avec du
développement en assembleur (en TP en tous cas). Même si ces exercices vous ont pour certains
semblé inutiles, ils sont nécessaires à la compréhension fine d'une architecture matérielle donnée et
des mécanismes d'accélération pouvant être utilisés. Mais se rassurer, notamment pour ceux ayant
bien assimilé, en se disant que vous êtes en train de développer sur l'une des architectures de cœur
pouvant être les plus complexes et performantes au monde. Tout dépend de notre degré de
compréhension de l'architecture et de notre capacité à l'exploiter.
A partir de maintenant, la totalité de nos développements se feront en langage C. Nous
découvrirons dans un premier temps des stratégies simples d'optimisation portables et applicables à
beaucoup de processeurs vectoriels. Nous conclurons par une stratégie d'accélération optimale pour
notre architecture mais amenant son lot d'inconvénients.
Le principe du déroulement de boucle consiste, sans mécanisme d'optimisation particulier, de
ré-implémenter en C canonique notre algorithme en doublant/quadruplant/etc le nombre de
chargement mémoire, d'opérations arithmétiques, etc dans nos boucles. Prenons un exemple de
déroulement de boucle d'un facteur 2, souvent nommé unrolling radix 2 (déroulement en base 2) :
Canonical C
/*
* FIR filtering function in canonical C
*/
void fir_sp (
const float * restrict xk,
const float * restrict a,
float * restrict pYk,
int nbCoeff){
int i;
float yk_tmp = 0.0f;
Unrolling radix 2
\
\
\
/* FIR filter algorithm - dot product */
for (i=0; i<nbCoeff; i++){
yk_tmp += a[i]*xk[i];
}
}
/*
* FIR filtering function with radix 2 unrolling
*/
void fir_sp_r2 ( const float * restrict xk, \
const float * restrict a,
\
float * restrict pYk,
\
int nbCoeff){
int i;
float xk_tmp1, xk_tmp2;
float a_tmp1, a_tmp2;
float mul1, mul2;
float add1 = 0.0f, add2 = 0.0f;
/* FIR filter algorithm - dot product */
for (i=0; i<nbCoeff; i+=2){
xk_tmp1 = xk[i];
xk_tmp2 = xk[i+1];
a_tmp1 = a[i];
a_tmp2 = a[i+1];
*pYk = yk_tmp;
mul1 = a_tmp1 * xk_tmp1;
mul2 = a_tmp2 * xk_tmp2;
add1 += mul1;
add2 += mul2;
}
*pYk = add1 + add2;
}
•
Quelle limitation amène l'implémentation en base 2 ci-dessus ?
- 32 -
Digital Signal Processor
Travaux Pratiques
•
Comme pour les algorithmes précédents, copier un bloc de code assurant les tests de
conformité et de performance d'un algorithme puis le modifier de façon à obtenir une section
de test pour l'algorithme fir_sp_r4.
•
En vous aidant du travail préparatoire, implémenter le code de la fonction fir_sp_r4,
effectuant un déroulement de boucle d'un facteur 4 sur les deux boucles internes de
l'algorithme puis valider son fonctionnement. Comme pour chaque phase de développement,
bien travailler en mode Debug.
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
- 33 -
Digital Signal Processor
Travaux Pratiques
3.6. Vectorisation de code en C
Dans cette ultime phase d'optimisation, nous allons réutiliser la totalité de nos anciens acquis
afin d'obtenir, enfin, une implémentation optimale de notre algorithme de filtrage. Nous allons
comprendre que les étapes précédentes, même si contre-performantes au seul regard des
benchmarking, étaient nécessaires à une bonne compréhension des stratégies présentées ci-dessous.
Afin, d'exploiter au mieux l'architecture matérielle parallèle, vectorielle, VLIW de chaque cœur, sans
pour autant descendre à l'étage assembleur qui peut être très chronophage en temps de
développement, nous allons devoir aiguiller au maximum notre chaîne de compilation en effectuant
du refactoring de code. Découvrons quelques une des techniques les plus efficaces. Pour maîtriser la
totalité du potentiel d'optimisation de notre chaîne de compilation, se référer au manuel SPRU187V
– Optimizing Compiler proposé par Texas Instruments :
•
Fonctions intrinsèques ou intrinsics functions : à l'image d'une fonction inline, une fonction
intrinsèque force le compilateur à générer automatiquement une séquence d'instructions.
Cependant, contrairement aux fonctions inlines, le compilateur possède une connaissance
poussée de la fonction intrinsèque qui lui assure une insertion optimale pour un contexte
donné. Toutes les fonctions intrinsèques supportées conjointement par notre chaîne de
compilation et notre architecture CPU sont énumérées dans la documentation technique
SPRU187V – Optimizing Compiler, Chapter 7.5.6 Using Intrinsics to Access Assembly
Language Statements
Examples of C intrinsics functions
for C6600 compiler and architecture
C6600 assembly equivalence
(automatics registers allocations by compiler)
__float2_t data10 ; // __float2_t container type
const float ldData[2] = {0.0f,1.0f};
data10 = _amemd8_const(&ldData[0]);
; data10 = A1:A0
; &ldData[0] = ldData = A2
LDDW
*A2++, A1:A0
__float2_t data10, data32 ;
__float2_t result31_20 ;
result31_20 = _daddsp(data10, data32);
; data10 = A1:A0, data32 = A3:A2,
; result31_20 = A11:A10
DADDSP
A1:A0, A3:A2, A11:A10
__float2_t data10, data32 ;
__float2_t result31_20 ;
result31_20 = _dmpysp(data10, data32);
; data10 = A1:A0, data32 = A3:A2,
; result31_20 = A11:A10
DMPYSP
A1:A0, A3:A2, A11:A10
__float2_t data10, data32 ;
__float2_t data21 ;
data21 = _ftod(_lof(data32), _hif(data10));
; data32 = A3:A2, data10 = A1:A0, data21= A11:A10
MV
A2, A11
MV
A1, A10
__float2_t data10 ;
float stData[2] ;
_amemd8(&stData[0]) = data10 ;
; data10 = A1:A0
; &stData[0] = stData = A2
STDW
A1:A0, *A2++
Observons un exemple de comparaison entre une implémentation C canonique et une
solution avec fonctions intrinsèques. __float2_t est un type conteneur (container type) sur 64bits et
pouvant contenir 2 flottants en simple précision sur 32 bits :
Canonical C example
Example of C intrinsic function
float data[2] = {0.0f, 1.0f};
float data0, data1 ;
const float data[2] = {0.0f, 1.0f};
__float2_t data10 ; // __float2_t container type
a0 = a[0];
a1 = a[1];
// LDDW assembly instruction
a10 = _amemd8_const(&data[0]);
// LDW assembly instruction
// LDW assembly instruction
- 34 -
Digital Signal Processor
Travaux Pratiques
•
Assertion : l'objectif de cette technique est de donner un maximum d'informations (garanties
par le développeur) sur une variable à la chaîne de compilation afin de l'aiguiller dans ses
phases d'optimisation futures : valeur minimale, variable d'une valeur toujours multiple d'une
autre valeur, alignement mémoire de données pointées …
Example of program with assertions
Description
short i ;
const float data[4] = {0.0f, 1.0f, 2.0f, 3.0f} ;
__float2_t acc = 0.0f ; // __float2_t container type
#pragma DATA_ALIGN(data, 8);
•
_nassert(i >= 2);
_nassert(i % 2 == 0);
_nassert((int) data % 8 == 0);
•
•
•
Force l'éditeur de lien (linker) à aligner en
mémoire les données du tableau data[]
modulo 8 octets
i est forcément supérieur ou égale à 2
i est forcément un multiple de 2
Les données pointées par a sont alignées
module 8 octets
for (i=0; i<4; i+=2) {
acc =_daddsp (acc, _amemd8_const(&data[i]);
}
•
Alignement mémoire : dans l'exemple ci-dessus, la directive de compilation #pragma
DATA_ALIGN force l'éditeur de lien ou linker à garantir un alignement mémoire modulo 8
octets des données visées par le pointeur passé en argument. Prenons un exemple
d'alignement mémoire de données modulo 8 octets sur chaîne de compilation :
/* arrays alignments - CPU data path length 64bits */
#pragma DATA_ALIGN(xk_sp, 8);
•
Dans l'exemple de code avec assertions et alignement mémoire ci-dessus, que vaut la variable
de type conteneur acc après exécution de la boucle ?
•
Comme pour les algorithmes précédent, copier un bloc de code assurant les tests de
conformité et de performance d'un algorithme puis le modifier de façon à obtenir une section
de test pour l'algorithme fir_sp_opt_r4.
•
En vous aidant des conseils précédemment présentés, implémenter le code de la fonction
fir_sp_opt_r4, effectuant un déroulement de boucle d'un facteur 4 sur les deux boucles
internes de l'algorithme et utilisant les directives de compilation précédemment citées.
Valider son fonctionnement en mode Debug.
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
- 35 -
Digital Signal Processor
Travaux Pratiques
•
En vous aidant de l'annexe 1, création de projet, générer une bibliothèque statique nommée
firlib.lib et la placer dans le répertoire /dsp/c6678/firlib/lib/.
•
Voilà, à ce stade nous venons d'effectuer une implémentation optimale de notre algorithme
pour notre architecture. Porter un regard critique sur nos développements et citer les
avantages et inconvénients de l'algorithme final.
Rq :
•
l'exécution de l'algorithme tel qu'il a été écrit mathématiquement dans sa forme
initiale ne peut pas être plus accélérée dans une optique de vectorisation en C sur
notre architecture. Si nous souhaitons améliorer le temps de calcul de notre produit
scalaire, il nous faudrait alors réduire sa complexité Mathématique en nombre de
MACS. Les transformations à apporter ne seraient donc plus d'ordres
technologiques dans un premier temps, mais d'ordre mathématique. Une fois ces
transformations appliquées, nous pourrions alors repasser sur des phases
d'optimisation architectures dépendantes telles que celles présentées dans cette
partie.
•
De plus, nous venons de le constater, du moment que nous avons une connaissance
poussée de l'architecture, du jeu d'instructions et de notre chaîne de compilation,
développer des bibliothèques spécialisées en assembleur peut s'avérer contreproductif au regard du ratio performance/effort. Ceci sera vrai sur toute
architecture VLIW, EPIC et superscalaire, du moment que l'étage d'optimisation de
la toolchain reste un minimum performant.
- 36 -
Digital Signal Processor
Travaux Pratiques
3.7. C canonique sur architecture IA-64
Dans cette dernière partie, nous allons effectuer un benchmarking entre architectures
processeurs en mettant en opposition une architecture superscalaire GPP (General Purpose
Processor) Intel corei7 en micro-architecture Haswell cadencée à 3,6GHz (machines de TP) et notre
architecture DSP VLIW C6600 cadencée à 1,4GHz proposée par Texas Instruments. Notre première
comparaison se fera sur une implémentation en C canonique.
Intel corei7 - 4790 Haswell 4th gen
3,6GHz, 105W en charge
prix unitaire : 325€
Texas Instruments TMS320C6678
1,4GHz, 10W en charge
prix unitaire : 240€ (160€ pour 1Ku)
•
Sauvegarder sur clé USB les fichiers fir_sp.c, fir_sp_r4.c et firtest_sys.c (ou directement le
répertoire de travail dsp) puis démarrer une session sous GNU\Linux (Ubuntu). Si nécessaire,
télécharger à nouveau l'archive dsp depuis la plateforme d'enseignement.
•
Copier les fichiers fir_sp.c et fir_sp_r4 dans le répertoire /dsp/ia64/firlib/src/ .
•
Copier le fichier firtest_sys.c dans le répertoire /dsp/ia64/test/src/ .
•
Ouvrir un shell, se placer dans le répertoire de travail /dsp/ia64/ (IA-64 ou Intel Architecture
64bits), ouvrir le fichier README.txt puis lancer la commande de compilation depuis le
répertoire courant. Interpréter et commenter la commande.
gcc -Wall -march=native -std=c99 -I./test/h -I./firlib/h -o3 ./test/src/firtest_main.c
./test/src/firtest_sys.c ./firlib/src/fir_sp.c ./firlib/src/fir_sp_r4.c ./firlib/src/fir_sp_sse_r4.c -o
./build/bin/firtest -lm
•
Après compilation, exécuter le fichier binaire de sortie et analyser les résultats obtenus. De
même, prenez le temps d'observer les quelques modifications mineures appliquées aux
différents fichiers sources et fichiers d'en-têtes du projet (firlib.h, firtest.h, firtest_main.c).
Rq : nous pouvons constater qu'une implémentation en C canonique, dans notre cas
respectant le standard C99, garantit une portabilité du code, même sur des architectures
matérielles différentes.
- 37 -
Digital Signal Processor
Travaux Pratiques
•
Dans le fichier firtest_main.c, nous pouvons observer la définition d'une fonction inline, dans
notre cas implémentée en assembleur 64bits x64. Cette fonction force le CPU courant à vider
son pipeline matériel puis réalise une lecture de la valeur courante du core timer TSC, comme
précédemment sur architecture C6600. Sur architectures compatibles x86-x64, ce timer 64bits
est démarré à la mise sous tension de la machine et compte jusqu'à débordement. En vous
aidant d'internet, rappeler le rôle du spécificateur inline, préciser l'intérêt et donner des
exemples d'utilisation.
/*
* flush pipeline and read current TSC value
*/
inline unsigned long long __attribute__((always_inline)) rdtsc_inline() {
unsigned int hi, lo;
__asm__ __volatile__(
// flush core pipeline
"xorl %%eax, %%eax\n\t"
"cpuid\n\t"
// read current TSC value
"rdtsc"
: "=a"(lo), "=d"(hi)
: // no parameters
: "rbx", "rcx");
return ((unsigned long long)hi << 32ull) | (unsigned long long)lo;
}
•
Quelles sont les principales différences entre une fonction inline et un fonction intrinsèque ?
•
Répéter plusieurs exécutions successives du programme de test. Que constatons-nous ?
•
Après une dizaine d'exécutions, reporter les valeurs maximales obtenues dans le tableau
d'analyses comparatives présent en fin de cette trame de travaux pratiques. Dans un contexte
réel, répéter une centaine d'exécution en chargeant notamment le cœur hyper-threadé par un
autre applicatif offrirait un résultat plus significatif.
Rq : voilà, nous venons d'observer un des principal problème des processeurs
superscalaires (GPP/SoC/AP), leur déterminisme. En effet, contrairement aux architectures
VLIW ou aux CPU à pipeline in-order classique présents sur MCU/GPU/DSP, la complexité
matérielle des pipelines superscalaires amène un non-déterminisme relatif à l'exécution
(dépendant de l'application ou de l'algorithme en cours de test). Ceci peut devenir gênant
dans des applications temps réel où le déterminisme est le maître mot. Néanmoins, les
processeurs généralistes (essentiellement architectures x64 et ARM cortex-A) restent très
utilisés dans les domaines du traitement du signal, essentiellement de part le culture des
développeur et l'emprise de certains acteurs sur le marché (Intel, ARM, AMD).
- 38 -
Digital Signal Processor
Travaux Pratiques
3.8. Vectorisation SSE4.1 sur architecture IA-64
Dans cette ultime partie, nous allons comparer ce qui peut être comparable, à savoir les
performances de notre code vectorisé sur architecture C6600 à du code vectorisé sur architecture IA64. Nous nous intéresserons notamment à l'extension vectorielle SIMD SSE4.1 proposée par Intel.
Pour information, courant 2014, Intel proposa sur sa micro-architecture Haswell une extension DSP
(Digital Signal Processing). Cette extension, nommée FMA (Fused Multiply-Add), est donc dédiée aux
applications de traitement numérique du signal mais ne sera néanmoins pas abordée en travaux
pratiques. Pour les plus curieux, ne pas hésiter à aller voir sur MSDN (MicroSoft Developer Network)
les quelques fonctions intrinsèques proposées.
Dans cette partie, il est bien entendu hors de question de découvrir en profondeur
l'architecture interne des processeurs compatibles x64 (Intel, AMD ...), notamment les architectures
Intel. Néanmoins, nous allons pouvoir constater que nos précédents acquis nous permettent
maintenant d'effectuer de la vectorisation de code sur toute architecture vectorielle processeur. Les
concepts resteront le plus souvent les mêmes.
Avant tout, il faut savoir que les architectures x64 actuelles possèdent plusieurs banques de
registres vectoriels :
•
•
•
registres MMX 64bits : peut par exemple contenir 2 flottants 32bits
registres XMM 128bits : peut par exemple contenir 4 flottants 32bits
registres YMM 256bits: peut par exemple contenir 8 flottants 32bits
Dans notre cas, les instructions de l'extension SSE4.1 travaillent avec les registres XMM
128bits et sont aptes à manipuler les flottants en simple précision (IEEE754) par paquets de 4.
Observons le pré-fixage et su-fixage des fonctions intrinsèques ainsi que l'un des types conteneurs
associé. Le type __m128 peut donc contenir 4 flottants simple précision :
Intrinsic syntax : _mm_<intrinsic_name>_<data_type>
container type (4 x 32bits floating point) : __m128
https://msdn.microsoft.com/fr-fr/library/y0dh78ez(v=vs.90).aspx
•
Mise à zéro des éléments d'un vecteur de flottants. Pour information, ps signifie Packet Single,
soit paquet de flottants en simple précision 32bits IEEE754 :
__m128 float_vector ;
float_vector = _mm_setzero_ps ();
https://msdn.microsoft.com/fr-fr/library/tk1t2tbz(v=VS.90).aspx
•
Initialise les éléments d'un vecteur de flottants :
__m128 float_vector ;
float_vector = _mm_set_ps (1.0, 2.2, 3.0, 7.0);
https://msdn.microsoft.com/fr-fr/library/afh0zf75(v=vs.90).aspx
- 39 -
Digital Signal Processor
Travaux Pratiques
•
Chargement d'un vecteur de 4 flottants alignés depuis la mémoire vers un registre XMM de
destination :
float tab[4] = {1.0, 3.4, 5.0, 6.0} ;
__m128 float_vector ;
float_vector = _mm_load_ps (&tab[0]);
https://msdn.microsoft.com/fr-fr/library/zzd50xxt(v=vs.90).aspx
•
Chargement d'un vecteur de 4 flottants non-alignés depuis la mémoire vers un registre XMM
de destination :
float tab[4] = {1.0, 3.4, 5.0, 6.0} ;
__m128 float_vector ;
float_vector = _mm_loadu_ps (&tab[0]);
https://msdn.microsoft.com/fr-fr/library/x1b16s7z(v=vs.90).aspx
•
Produit scalaire entre deux vecteurs de 4 flottants (result = a0.x0 + a1.x1 + a2.x2 + a3.x3). Le
résultat du produit scalaire étant donc un nombre scalaire, le résultat est par défaut sauvé
dans les 32bits de poids faibles du vecteur de destination :
__m128 float_vector_src1;
__m128 float_vector_src2;
__m128 float_vector_dst;
float_vector_dst = _mm_dp_ps (float_vector_src1, float_vector_src2, 0xff);
https://msdn.microsoft.com/fr-fr/library/bb514054(v=vs.90).aspx
•
Addition de deux vecteurs de 4 flottants :
__m128 float_vector_src1;
__m128 float_vector_src2;
__m128 float_vector_dst;
float_vector = _mm_add_ps (float_vector_src1, float_vector_src2);
https://msdn.microsoft.com/fr-fr/library/c9848chc(v=vs.90).aspx
•
Sauvegarde en mémoire d'un vecteur de 4 flottants alignés :
float tab[4];
__m128 float_vector ;
float_vector = _mm_set_ps (1.0, 2.2, 3.0, 7.0);
_mm_store_ps (&tab[0], float_vector);
https://msdn.microsoft.com/fr-fr/library/s3h4ay6y(v=vs.90).aspx
- 40 -
Digital Signal Processor
Travaux Pratiques
•
Ouvrir le fichier /dsp/ia64/firlib/h/firlib.h et observer les déclarations de type et d'union
présentées ci-dessous. Cette union, greffée à une déclaration de type, permet, après
déclaration d'une variable conteneur, d'accéder à un vecteur XMM soit élément par élément,
soit directement au vecteur 128bits complet. En vous aidant d'internet, rappeler ce qu'est une
union et préciser la différence avec une structure.
/* project specific types */
union xmm_t {
__m128 m128_vec;
float m128_f32[4];
} align16_xmm;
typedef union xmm_t xmm_t;
•
// full access to XMM vector
// access to XMM vector elements
Dans l'exemple ci-dessous, comment accéder au 3ième élément de la variable data_vec de type
conteneur XMM 128bits.
xmm_t data_vec;
•
Ouvrir le fichier /dsp/ia64/firlib/src/fir_sp_sse_r4.c puis implémenter le code vectorisé pour
architecture x64 correspondant à la fonction fir_sp_r4. Valider son bon fonctionnement sans
oublier de s'assurer de l'alignement mémoire des différents vecteurs de données traités par
l'algorithme.
•
Répéter plusieurs exécutions puis reporter les valeurs maximales obtenues dans le tableau
d'analyses comparatives présent en fin de cette trame de travaux pratiques.
Rq :
•
nous venons d'effectuer de la vectorisation de code tout en ayant une connaissance
minime de l'architecture processeur Intel. Une connaissance plus poussée de
l'architecture et du jeu d'instructions supporté permettrait un gain supplémentaire
en performance, sans pour autant être conséquent (extensions FMA3, FMA4 ...).
•
De même, nous avons effectué nos compilations sous gcc (GNU Collection
Compiler). Si nous souhaitons gagner en performance, il serait alors intéressant de
travailler directement avec les outils fondeurs. Par exemple, si nous souhaitons
garantir des performances optimales sur architecture Intel, il nous faudrait alors
utiliser icc (Intel C++ Compiler) un compilateur développé par Intel et donc optimisé
pour leurs architectures. Il en va de même pour tous les fondeurs du marché (Texas
Instruments, ARM, MIPS ...).
- 41 -
Digital Signal Processor
Travaux Pratiques
Nous venons de conclure la partie vectorisation monocœur de code et nous allons essayer de
synthétiser et d'analyser nos précédents résultats.
•
Choix d'une architecture CPU et d'une famille de processeurs :
◦
pipeline in-order classique : faible consommation, faible performance, faible empreinte
silicium, sauf si intégrés par milliers, prenons l'exemple des GPU. Applications sans OS
(Operating System), avec petits exécutifs ou systèmes d'exploitation temps réel (Real Time
Operating System). Dans le cas des GPU, applications de calcul massivement parallèle
(traitement d'image, vidéo, graphique 3D, matriciel …). Il s'agit des architectures CPU les
plus rencontrées en terme de volumes autour de nous, notamment grâce au marché des
MCU (MCU STM32 STMicroelectronics, MCU PIC Microchip, Intel 8051, ARM Cortex-M ...)
◦
pipeline superscalaire : grande polyvalence (généraliste), le plus souvent grande puissance
de calcul, pipeline complexe, donc forte intelligence déportée dans chaque cœur avec un
faible niveau de parallélisme (typiquement 1, 2, 4, 8 cœurs vectoriels) : prédiction au
branchement, étage d'exécution out-of-order, étage de retirement, étage de renommage
des registres, mécanismes d'accélération aux étages de décodage et d'exécution le plus
souvent, unités d'exécutions vectorielles … Applications avec OS lourds. Architectures
pensées pour exécuter du code faiblement optimisé notamment avec beaucoup de
branchements, le cœur se charge du reste. Cependant, dû à la complexité du pipeline, ces
architectures peuvent offrir quelques irrégularités au regard du déterminisme à
l'exécution (x86, x64, ARM cortex-A, IBM/APPLE/Freescale PowerPC, MIPS Aptiv…).
Rapport performance (parallélisme)/consommation faiblement intéressant.
◦
Pipeline VLIW/EPIC : forte puissance de calcul, pipeline relativement simple en opposition
aux architectures superscalaires, donc rapport performance (parallélisme)/consommation
intéressant (intelligence déportée vers le développeur et la toolchain), grand
déterminisme. Néanmoins, développements dépendants de l'architecture nécessaires,
problèmes de portabilité de code. Rétrocompatibilité au niveau binaire difficile à suivre
pour les fondeurs (DSP TI C6000, NXP TriMedia, DSP SHARC Analog Device, ST200
STMicroelectronics, Intel Itanium ...).
•
Langage C vs Assembleur : dans une optique d'optimisation, un développement en langage C
avec une bonne connaissance de l'architecture matérielle, du jeu d'instructions et des
mécanismes d'optimisation de la toolchain peut éviter un passage à l'étage assembleur à
notre époque (intrinsics, directives de compilation …) et donc accélérer le TTM du produit
(Time To Market).
•
Tests : ne pas négliger les procédures de test (conformité et performance), notamment dans
une optique de benchmarking et d'optimisation d'algorithmes.
Rq : En résumant le résumé …
•
processeurs superscalaires : intelligence déportée dans le cœur
•
processeurs VLIW et EPIC : intelligence et compétences déportées dans le
développeur et la chaîne de compilation
- 42 -
Digital Signal Processor
Travaux Pratiques
Dernière information, le processeur utilisé en travaux pratiques fait parti de la famille
Keystone I proposée par Texas Instruments (http://processors.wiki.ti.com/index.php/Multicore). TI
propose également la famille Keystone II. Il s'agit de SoC (System On Chip) intégrant 8 cœurs DSP
VLIW C6600 et jusqu'à 4 cœurs ARM Cortex-A15 superscalaires afin d'embarquer un voire plusieurs
systèmes d'exploitation avancés (Typiquement GNU\Linux). Cette architecture offre alors une grande
flexibilité ainsi qu'une forte puissance de calcul. Néanmoins, avec la consommation et le coût qui
l'accompagne.
Famille Keystone I, processeur C6678
Famille Keystone II, processeur 66AK2H14
- 43 -
Digital Signal Processor
Travaux Pratiques
4. HIERARCHIE MEMOIRE
Travail préparatoire
•
1. (1,5pt) Rappeler le principe de fonctionnement et l'usage fait du cache processeur par un
CPU.
•
2. (0,5pt) Qu'est-ce-qu'une ligne de cache ?
•
3. (1,5pt) Comme pour beaucoup d'architectures actuelles, la famille DSP C6000 utilise une
politique de remplacement de ligne de cache du type LRU (Least Recently Used). Que cela
signifie-t-il ? Illustrer votre réponse à l'aide d'un schéma.
•
4. (1,5pt) En s'aidant de l'annexe ou de la documentation technique SPRS691E, quel intérêt
pouvons-nous avoir à utiliser un ou plusieurs niveaux mémoire (L1/L2/L3) configurés en SRAM
et non en cache ? Quelles différences majeures rencontrons-nous à l'usage ?
•
5. (5pt) Nous pouvons observer ci-dessous une implémentation en pseudo-code Matlab d'une
émulation de transferts mémoire de DDR vers L2 SRAM. Bien entendu, sous Matlab, la notion
de hiérarchie n'a aucun sens, cette implémentation nous permet seulement de valider les
tailles des vecteurs intermédiaires de stockage ainsi que les valeurs limites des boucles.
Proposer une implémentation C en utilisant la fonction standard memcpy afin d'effectuer les
transferts mémoire :
%DDR_ARRAY_LENGTH = 1048576;
% 1M/4Mb : array length in DDR
DDR_ARRAY_LENGTH = 65536;
% 64K/256Kb : array length in DDR
L2_ARRAY_LENGTH = 32768;
% 32K/128Kb : array length in L2 SRAM
A_LENGTH = length(a_sp);
% 64/256b : length of coefficients array
YK_LENGTH = DDR_ARRAY_LENGTH - A_LENGTH + 1; % length of output temporal array in DDR
%*** MEMORY TRANSFERS
% floating point arrays lengths :
% xk_sp_DDR |---------------------- 256Kb or 4Mb --------------------------|
% xk_sp_L2
|------------- 128Kb + 256b - 4 -------------| overlap
% a_sp_L1
|- 256b -|
% yk_sp_L2
|------------- 128Kb -------------|
% yk_sp_DDR |------------- (256Kb or 4Mb) - 256b + 4 ----------------|
%
% copy part of input array from DDR to L2 SRAM
for k=1 : A_LENGTH
a_sp_L1(k) = a_sp(k);
end
for i=1 : L2_ARRAY_LENGTH : DDR_ARRAY_LENGTH
% memory copy DDR to L2 SRAM
% rq : DDR_ARRAY_LENGTH % L2_ARRAY_LENGTH = 0
if i < DDR_ARRAY_LENGTH - L2_ARRAY_LENGTH
for k=1 : L2_ARRAY_LENGTH + A_LENGTH - 1
xk_sp_L2(k) = xk_sp_DDR(i+k-1);
end
else
for k=1 : L2_ARRAY_LENGTH
- 44 -
Digital Signal Processor
Travaux Pratiques
xk_sp_L2(k) = xk_sp_DDR(i+k-1);
end
end
% FIR filtering - algorithm working with L1D cache
yk_sp_L2 = fir_sp(xk_sp_L2, a_sp_L1, A_LENGTH, L2_ARRAY_LENGTH);
% memory copy L2 SRAM to DDR - coherency of output DDR array
% rq : YK_LENGTH % L2_ARRAY_LENGTH = L2_ARRAY_LENGTH - A_LENGTH + 1
if i < YK_LENGTH - L2_ARRAY_LENGTH
for k=1 : L2_ARRAY_LENGTH
yk_sp_DDR(i+k-1) = yk_sp_L2(k);
end
else
for k=1 : L2_ARRAY_LENGTH - A_LENGTH + 1
yk_sp_DDR(i+k-1) = yk_sp_L2(k);
end
end
end
- 45 -
Digital Signal Processor
Travaux Pratiques
4. HIERARCHIE MEMOIRE
Travail en séance
Nous allons maintenant chercher à passer outre les mémoires caches L1D et L2 en ne
configurant qu'une partie des niveaux L1D et L2 en cache et le reste en mémoires SRAM adressable
par octet.
Dans l'exemple d'un produit scalaire, l'impact restera minime. Néanmoins, pour grand nombre
d'algorithmes du traitement numérique du signal (traitement d'image, traitement d'antenne ...),
laisser opérer les contrôleurs cache sans ordonnancement des données avant traitement peut avoir
une incidence énorme sur le temps d'exécution. Dans l'exemple qui suit, nous pouvons observer une
matrice de données manipulée par indexage (chargement/lecture donnée 1 puis 2 puis 3 …) :
- 46 -
Digital Signal Processor
Travaux Pratiques
Un cache étant chargé par ligne (principe de localité spatiale), nous sommes alors amenés à
charger un grand nombre de données ''potentiellement'' inutilisées en cache. Une technique
couramment utilisée, consiste à ré-implémenter l'algorithme de façon à traiter des données préordonnancées en mémoire (technique du corner turn). Cette stratégie permet de diminuer le nombre
d'accès au cache et assure une utilisation optimale des espaces de stockage en cache. Rappelons que
l'accès aux ressources en mémoire est l'un des principal goulot d'étranglement à notre époque. Nous
avons également remarqué que notre architecture possède un grand nombre de registres de travail
généralistes. L'objectif étant de charger les données locales à une fonction ou une boucle dans les
registres du cœur, pour les traiter par la suite en minimisant au plus les aller/retour vers le cache.
Écritures différées en mémoire en fin de traitement.
Dans notre cas, l'objectif est différent. L'algorithme d'un produit scalaire sans transformations
mathématiques dans une optique de diminution de sa complexité, manipule les vecteurs d'entrée
dans l'ordre de façon séquentielle. Cet exercice n'a donc pour objectif que d'illustrer la possibilité
d'obtenir un espace de stockage adressable à accès rapide physiquement proche du cœur (L1D et L2).
Nous maîtriserons alors avec certitude les données présentes aux niveaux L1D et L2 afin de garantir
un déterminisme à l'exécution. Cette solution permet par exemple de s'affranchir localement du
principe de corner turn précédemment présenté. En effet, nous allons manuellement effectuer le
travail des contrôleurs de cache utilisant quant à eux des mécanismes de localité temporelle (LRU)
pour leurs allocations/libérations mémoire. Traitement impossible sur processeur généraliste GPP.
Ceci permettra de rendre notre algorithmique quasiment insensible à la charge éventuelle du cache.
4.1. Mémoire locale adressable
Nous avons possibilité de configurer les différents niveaux mémoire en cache ou en SRAM
adressable de façon statique à la compilation ou dynamique à l'exécution. Étudions l'approche
statique. Rappelons avant tout l'architecture mémoire de notre processeur :
•
Dans notre projet CCS, remplacer le script C6678_unified.cmd par le fichier
/dsp/c6678/test/map/C6678_unified_fir_pjct.cmd.
Rq : Ce fichier est utilisé par le linker afin fixer les localisations mémoire des principaux
segments (tas/heap, pile/stack et sections statiques) du projet. Sans pour autant figer les
adresses mémoire (rôle de linker, résolution des liens symboliques avec la mémoire), nous
pouvons fixer leur localisation dans l'architecture (DDR3 SRAM, MSM SRAM, L2 SRAM ou
L1D/P SRAM).
- 47 -
Digital Signal Processor
Travaux Pratiques
•
Éditer le fichier /dsp/c6678/test/map/C6678_unified_fir_pjct.cmd et analyser son contenu.
-stack 0x2000
-heap 0x2000
MEMORY
{
LOCAL_L1P_SRAM: origin = 0x00E00000 length = 0x00008000
LOCAL_L1D_SRAM: origin = 0x00F00000 length = 0x00008000
LOCAL_L2_SRAM: origin = 0x00800000 length = 0x00080000
MSMCSRAM:
origin = 0x0C000000 length = 0x00400000
EMIF16_CS2:
EMIF16_CS3:
EMIF16_CS4:
EMIF16_CS5:
origin = 0x70000000 length = 0x04000000
origin = 0x74000000 length = 0x04000000
origin = 0x78000000 length = 0x04000000
origin = 0x7C000000 length = 0x04000000
/* 32kB LOCAL L1P/SRAM */
/* 32kB LOCAL L1D/SRAM */
/* 512kB LOCAL L2/SRAM */
/* 4MB Multicore shared Memmory */
/* 64MB EMIF16 CS2 Data Memory */
/* 64MB EMIF16 CS3 Data Memory */
/* 64MB EMIF16 CS4 Data Memory */
/* 64MB EMIF16 CS5 Data Memory */
/* TMDSEVM6678L board specific */
DDR3:
origin = 0x80000000 length = 0x20000000 /* 512MB external DDR3 SDRAM */
}
SECTIONS
{
.text
.stack
.bss
.cio
.const
.data
.switch
.sysmem
.far
.args
.ppinfo
.ppdata
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
> DDR3
> MSMCSRAM
> MSMCSRAM
> MSMCSRAM
/* COFF sections */
.pinit
> MSMCSRAM
.cinit
> DDR3
/* EABI sections */
.binit
> MSMCSRAM
.init_array > MSMCSRAM
.neardata
> MSMCSRAM
.fardata
> DDR3
.rodata
> MSMCSRAM
.c6xabi.exidx > MSMCSRAM
.c6xabi.extab > MSMCSRAM
/* project specific sections */
/* user sections */
}
•
A quoi correspond la zone MEMORY du script ?
- 48 -
Digital Signal Processor
Travaux Pratiques
•
En vous aidant de l'annexe ou de la documentation technique SPRU187V, préciser le rôle de la
zone SECTIONS du script.
•
Dans quelle section est rangée la pile ou stack système (allocations automatiques) ?
•
Dans quelle section est rangée le tas ou heap système (allocations dynamiques) ?
•
Dans quelle section est rangé le code utilisateur ?
•
Modifier le script de façon à configurer à la compilation les niveaux mémoire L1D et L2
comme suit :
◦ 32Ko L1D : 28Ko en L1D SRAM adressable et 4Ko en L1D cache
◦ 512Ko L2 : 480Ko en L2 SRAM adressable et 32Ko en L2 cache
•
Créer 3 nouvelles sections propres au projet et les placer tel que précisé ci-dessous :
◦ .ddrsdram : section statique présente en DDR
◦ .l2sram : section statique présente en L2 SRAM
◦ .l1dsram : section statique présente en L1D SRAM
•
Compiler puis exécuter le programme. Valider son bon fonctionnement.
Rq : Dans les langages compilés comme le langage C, une donnée est soit allouée statiquement
à la compilation soit allouée dynamiquement/automatiquement à l'exécution. Par essence, les
langages interprétés ne peuvent qu'allouer des ressources durant l'exécution (allocations
dynamiques) :
•
Allocations statiques : allocations à la compilation et structuration des familles
de variables en sections par l'éditeur de liens (variables globales ou statiques
initialisées, non-initialisées , constantes …). Exemples de sections :
•
•
•
.data : section mémoire où se situe les données statiques initialisées
.bss : section mémoire où se situe les données statiques non initialisées
.rodata : données statiques initialisées en lecture seule (read-only)
•
Allocations automatiques : allocations/dés-allocations à l'exécution sur la pile
ou stack (variables locales, paramètres de fonctions, adresses de retour des
fonctions)
•
Allocations dynamiques : allocations/dés-allocations à l'exécution sur le tas ou
heap (malloc, realloc, free ... et variantes). Attention à l'utilisation des fonctions
malloc et surtout free d'allocation dynamiques de ressources sur processeur ne
possédant pas de PMMU (Paged Memory Managment Unit). Ce qui est notre
cas. En effet, l'allocation dynamique de ressources amène une fragmentation
logique de la mémoire physique, palliée par l'unité de pagination sur processeur
superscalaire généraliste (GPP et AP) possédant tous une MMU.
- 49 -
Digital Signal Processor
Travaux Pratiques
4.2. Préchargement des données de DDR SRAM vers L2 SRAM
•
Déclarer des vecteurs statiques (variables globales) qui assureront des stockages partiels de
nos vecteurs d'entrée et de sortie. Ces vecteurs stockeront temporairement en L2 SRAM et
L1D SRAM des morceaux de vecteurs à traiter. Respecter les dénominations imposées dans le
fichier d'en-tête firtest.h :
/* arrays allocations (bytes) :
* xk_sp (DDR) |------------------------- 256Kb or 4Mb --------------------------|
* xk_sp_l2
|------------- 128Kb + 256b - 4 -------------| overlop
* xk_sp_l1d
|--- 8Kb + 256b - 4 ---| overlop
* a_sp_l1d
|- 256b -|
* yk_sp_l1d
|----- 8Kb -----|
* yk_sp_l2
|------------- 128Kb -------------|
* yk_sp (DDR) |------------------- (256Kb or 4Mb) - 256b + 4 -------------------|
*/
float32_t xk_sp_l2 [L2_ARRAY_LENGTH + A_LENGTH - 1];
float32_t yk_sp_l2 [L2_ARRAY_LENGTH];
float32_t xk_sp_l1d [L1D_ARRAY_LENGTH + A_LENGTH - 1];
float32_t yk_sp_l1d [L1D_ARRAY_LENGTH];
float32_t a_sp_l1d [A_LENGTH];
•
En modifiant le script linker, nous venons de customiser l'organisation mémoire de notre
architecture afin de l'adapter aux besoins de notre projet. En utilisant la directive d'édition des
liens #pragma DATA_SECTION, placer les précédentes déclarations dans les sections
concernées. Les vecteurs d'entrée et de sortie xk_sp, yk_sp, a_sp et yk_sp_ti seront quant à
eux placés en DDR SRAM externe. Ne pas oublier d'assurer un alignement mémoire sur
chaque nouveau vecteur (utilisation de l'algorithme fir_sp_opt_r4). Prenons un exemple :
/* memory segmentation */
#pragma DATA_SECTION (xk_sp,".ddrsdram");
•
Modifier le code de la fonction firtest_perf de façon à commuter d'un modèle mémoire à un
autre en fonction de la valeur du paramètre d'entrée memoryModel. Ces modifications
devront être intégrées dans la boucle assurant une répétition des tests de performance. Nous
nous laisserons également la possibilité de pouvoir retirer du code à la pré-compilation :
/* memory model selection */
if ( memoryModel == UMA_L2CACHE_L1DCACHE ) {
/* user code – partie 2.2 – test de performance */
}
#if ( TEST_FIR_L2SRAM_L1DCACHE != 0 )
else if ( memoryModel == UMA_L2SRAM_L1DCACHE ) {
/* user code – partie 4.2 – préchargement des données de DDR SRAM vers L2 SRAM */
}
#endif
•
A partir de maintenant et jusqu'à la fin de la trame de travaux pratiques, toutes nos futures
optimisations (mémoire et périphériques d'accélération) utiliseront l'algorithme vectorisé
fir_sp_opt_r4 précédemment développé et offrant un niveau optimum d'accélération.
- 50 -
Digital Signal Processor
Travaux Pratiques
•
Comme pour les algorithmes précédents, copier dans la fonction main un bloc de code
assurant les tests de conformité et de performance d'un algorithme puis le modifier de façon
à obtenir une section de test pour l'algorithme fir_sp_opt_r4. Nous utiliserons dans un
premier temps la macro TEST_FIR_L2SRAM_L1DCACHE qui nous permettra si nécessaire de
retirer du code à la pré-compilation. De même, nous passerons maintenant la macro
UMA_L2SRAM_L1DCACHE comme second argument de la fonction firtest_perf.
•
Notre procédure de test complète utilisant à tour de rôle des modèles mémoire pleinement
cachables ou des modèles mixtes cachables/SRAM adressables, nous allons nous offrir la
possibilité de modifier à l'exécution le modèle mémoire du processeur.
En vous aidant de la documentation technique de CSL présente dans le répertoire de projet
(/dsp/c6678/doc/csl/docs/doxygen /html/index.html) et des macros et énumérateurs
présents dans le header C:\ti\pdk_C6678_<version>\packages\ti\csl\csl_cache.h, compléter
la fonction firtest_perf de façon à reconfigurer les caches processeurs concernés. L'exemple
suivant permet de restaurer des niveaux L2 et L1D pleinement cachable :
/* memory model selection */
if ( memoryModel == UMA_L2CACHE_L1DCACHE ) {
/* caches levels configurations */
CACHE_setL2Size (CACHE_256KCACHE);
CACHE_setL1DSize (CACHE_L1_32KCACHE);
CACHE_setL1PSize (CACHE_L1_32KCACHE);
}
/* user code – partie 2.2 – test de performance */
#if ( TEST_FIR_L2SRAM_L1DCACHE != 0 )
else if ( memoryModel == UMA_L2SRAM_L1DCACHE ) {
/* caches levels configurations */
/* coefficients array loading */
/* user code – partie 4.2 – préchargement des données de DDR SRAM vers L2 SRAM */
#endif
}
•
En vous aidant du travail préparatoire, effectuer manuellement les transferts mémoire partiels
des différents vecteurs d'entrée et de sortie de la DDR SRAM vers la mémoire L2 SRAM.
Comme pour chaque phase de développement, bien travailler en mode Debug.
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
- 51 -
Digital Signal Processor
Travaux Pratiques
4.3. Préchargement des données de L2 SRAM vers L1D SRAM
•
Comme pour les algorithmes précédents, copier dans la fonction main un bloc de code
assurant les tests de conformité et de performance d'un algorithme puis le modifier de façon
à obtenir une section de test pour l'algorithme fir_sp_opt_r4. Nous utiliserons maintenant la
macro TEST_FIR_L2SRAM_L1DSRAM qui nous permettra si nécessaire de retirer du code à la
pré-compilation. De même, nous passerons maintenant la macro UMA_L2SRAM_L1DSRAM
comme second argument de la fonction firtest_perf.
•
Compléter le code de la fonction firtest_perf de façon à effectuer les traitements suivants :
◦
◦
◦
•
Commuter d'un modèle mémoire à un autre en fonction de la valeur du
paramètre d'entrée memoryModel.
Configurer les caches processeurs propre au modèle mémoire courant
Précharger le vecteur de coefficients a_sp de la DDR SRAM vers le vecteur
a_sp_l1d présent en niveau L1D. Utiliser la fonction standard memcpy.
En vous aidant du travail précédent, effectuer maintenant des copies partielles manuelles des
vecteurs de la mémoire L2 SRAM vers la mémoire L1D SRAM en respectant les tailles de
vecteurs suivants. Si nécessaire, s'aider du code de prototypage Matlab dans /dsp/matlab/ :
/* arrays allocations (bytes) :
* xk_sp (DDR) |------------------------- 256Kb or 4Mb --------------------------|
* xk_sp_l2
|------------- 128Kb + 256b - 4 -------------| overlop
* xk_sp_l1d
|--- 8Kb + 256b - 4 ---| overlop
* a_sp_l1d
|- 256b -|
* yk_sp_l1d
|----- 8Kb -----|
* yk_sp_l2
|------------- 128Kb -------------|
* yk_sp (DDR) |------------------- (256Kb or 4Mb) - 256b + 4 -------------------|
*/
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
Rq :
•
A ce stade là de nos développements, nous constatons une légère perte de
performance mais néanmoins un déterminisme garanti à l'exécution (maîtrise totale
des données présentes dans les niveaux L2 et L1D). Juste pour information, c'était déjà
partiellement le cas précédemment avec un modèle pleinement cachable dans le
cadre d'un produit scalaire (données manipulées séquentiellement dans l'ordre).
Néanmoins, pour grand nombre d'autres algorithmes DSP avec indexages complexes
(exemple de la FFT), nous aurions pu avoir de mauvaises surprises à n'utiliser que du
cache.
•
Nous avons cependant, sans pour autant modifier notre modèle mémoire, possibilité
d'accélérer nos transferts. En effet, jusqu'à présent nous avons utilisé la fonction
standard memcpy réalisant les transferts via le CPU (instructions LDx/STx). Nous allons
maintenant regarder de plus près les périphériques DMA (Direct Memory Access),
spécialisés dans les transferts mémoire autonomes sans passer par le CPU.
- 52 -
Digital Signal Processor
Travaux Pratiques
5. PERIPHERIQUES D'ACCELERATION
Travail préparatoire
•
1. (1,5pt) Qu'est-ce-qu'un DMA ? Bien entendu, Direct Memory Access n'est pas la réponse
attendue ...
•
2. (1,5pt) Qu'est-ce qu'un canal DMA (DMA channel) pour un DMA ?
•
3. (0,5pt) En s'aidant de la documentation technique ''SPRUGW0C – CorePac'' (répertoire
/dsp/c6678/doc/datasheet/ ou en ligne), combien de canaux possède chaque IDMA de notre
processeur. De même, combien d'IDMA possède notre processeur DSP C6678 ?
•
4. (2,5pt) En vous aidant de la documentation technique de CSL
(/dsp/c6678/doc/csl/docs/doxygen /html/index.html), présenter l'API de programmation
utile à l'utilisation du canal 0 de l'IDMA. La citer et expliquer brièvement son fonctionnement.
•
5. (4pt) Proposer une implémentation par transferts IDMA de la fonction standard memcpy
utilisant quant à elle le CPU pour effectuer des transferts mémoires (instructions LDx/STx).
S'aider de la documentation technique de CSL (/dsp/c6678/doc/csl/docs/doxygen
/html/index.html) ainsi que des exemples présentés. Voici le cahier des charges à respecter
ainsi que le prototype de la fonction :
◦
◦
◦
◦
◦
Utiliser le canal numéro 1
IDMA priorité maximale vis à vis du CPU
Attendre la fin de la totalité du transfert avant de quitter la procédure
Valider la génération et l'envoi d'une interruption après la fin du transfert
Les pointeurs d'entrée pointeront sur des flottants 32bits (float)
/**
* @brief IDMA (CorePac Internal Direct Memory Access) bytes copy
* from L2 array source to destination array in L1D memory
* @param dst pointer on destination array
* @param src pointer on source array
* @param nbBytes number of bytes to copy
*/
void idmacpy( void *dst,
\
void *src,
\
Uint16 nbBytes);
- 53 -
Digital Signal Processor
Travaux Pratiques
5. PERIPHERIQUES D'ACCELERATION
Travail en séance
Dans cet ultime partie, nous allons nous intéressé à des périphériques d'accélération
standards sur architectures processeurs évoluées, les DMA (Direct Memory Access). Rappelons que le
travail d'un DMA est d'effectuer des transferts mémoires sans passer par le CPU. Un DMA possède ses
propres bus et est, après configuration, une entité autonome de l'architecture du processeur. Un
DMA classique effectue des copies d'une zone mémoire à une autre et est capable, comme tout
périphérique, de prévenir le CPU de la fin d'un transfert par l'envoi d'une interruption matérielle (IRQ,
Interrupt ReQuest). Des DMA plus évolués (exemple des Enhanced DMA chez TI ou des Smart DMA
chez Freescale) sont capables d'effectuer des transferts avec modes d'adressages et indexations
complexes (manipulation de matrices, de cubes de données), peuvent synchroniser des transferts sur
événements matériels issus de périphériques, peuvent capturer et garder des grands nombres de préconfigurations … Une étude approfondie de ces types de DMA peut alors prendre des mois.
Dans un premier temps, nous travaillerons avec les IMDA (Internal DMA) de notre processeur.
Ces IMDA sont présents dans chaque cœur et ne proposent que des services standards de copies :
Rq : dans le schéma ci-dessus nous pouvons observer plus finement l'architecture interne
d'un corePac (ensemble CPU/cœur, caches associés et utilitaires matériels spécifiques,
exemple des IDMA). Bien faire la distinction entre les mémoires caches (espaces de stockages)
et les contrôleurs de caches (DMA autonome travaillant dans notre cas avec des mécanismes
de localités temporelles LRU pour le remplacement des lignes de caches). Un contrôleur de
cache effectuant des copies de mémoires à mémoires, il n'est donc qu'un DMA autonome.
- 54 -
Digital Signal Processor
Travaux Pratiques
5.1. Transferts par IDMA
•
Comme pour les algorithmes précédents, copier dans la fonction main un bloc de code
assurant les tests de conformité et de performance d'un algorithme puis le modifier de façon
à obtenir une section de test pour l'algorithme fir_sp_opt_r4. Nous utiliserons maintenant la
macro TEST_FIR_L2SRAM_L1DIDMA qui nous permettra si nécessaire de retirer du code à la
pré-compilation. De même, nous passerons maintenant la macro UMA_L2SRAM_L1DIDMA
comme second argument de la fonction firtest_perf.
•
Compléter le code de la fonction firtest_perf de façon à effectuer les traitements suivants :
◦
◦
◦
Commuter d'un modèle mémoire à un autre en fonction de la valeur du
paramètre d'entrée memoryModel.
Configurer les caches processeurs propre au modèle mémoire courant
Précharger le vecteur de coefficients a_sp de la DDR SRAM vers le vecteur
a_sp_l1d présent en niveau L1D
•
Ajouter au projet le fichier source /dsp/c6678/idmalib/src/idmacpy.c puis s'assurer de la
bonne compilation du projet.
•
En vous aidant du travail préparatoire, compléter le code de la fonction idmacpy de façon à
effectuer des transferts par IDMA et non plus par le CPU. Modifier le code de la fonction
firtest_perf (section UMA_L2SRAM_L1DIDMA) de façon à effectuer les transferts mémoires
L2 SRAM / L1D SRAM / L2 SRAM par IDMA.
•
Après validation, lancer une exécution en mode Release (mode Debug coupé et options
d'optimisations levées), puis reporter les résultats des tests dans le tableau d'analyse
comparative à la fin de ce support de travaux pratiques.
Rq : cette solution est légèrement plus rapide que la précédente car n'oublions pas qu'un
DMA reste un périphérique spécialisé et qu'en aucun cas il n'est capable d'exécuter des
instructions contrairement à un CPU (pipeline complexe : fetch, decode, execute et
writeback). Même si dans notre cas ses bus/paths restent moins larges que ceux du CPU, une
fois configuré, il les utilise exclusivement afin d'effectuer la fonction de copie pour laquelle il a
été détaché.
- 55 -
Digital Signal Processor
Travaux Pratiques
5.1. Stratégie Ping Pong
Même si la solution précédente reste plus rapide qu'une copie par CPU, elle n'est pas
optimale. Notre IMDA possède 2 canaux indépendants par cœurs. Ces canaux peuvent alors être
utilisés en parallèles en manipulant chacun deux vecteurs temporaires de stockage en L1D SRAM.
Cette stratégie très générique de copie se nomme Ping Pong, lorsque le côté Ping est en cours de
traitement (algorithme de traitement du signal), le côté Pong est en cours de
chargement/sauvegarde, etc. Rappelons d'ailleurs que nous possédons 24Ko de cache L1D SRAM
adressable, nous autorisant ainsi à pouvoir définir de nouveaux vecteurs temporaires de stockage.
Observons par exemple une séquence de transferts Ping Pong en 6 étapes :
•
•
Canal 0 : Dédié aux chargements mémoires L2 SRAM vers L1D SRAM
•
Canal 1 : Dédié aux sauvegardes mémoires L1D SRAM vers L2 SRAM
•
Étape 1 : Chargement donnée xk_sp du côté Ping de L2 SRAM vers L1D SRAM
•
Étape 2 : Chargement donnée xk_sp du côté Pong de L2 SRAM vers L1D SRAM et si
chargement du côté Ping terminé, application de l'algorithme de filtrage sur les
données précédemment chargées du côté Ping
•
Étape 3 : Si traitement algorithmique terminé, sauvegarde des données de sortie
yk_sp du côté Ping de L1D SRAM vers L2 SRAM et si chargement du côté Pong terminé,
application de l'algorithme de filtrage sur les données précédemment chargées du côté
Pong
•
Étape 4 : Chargement donnée xk_sp du côté Ping de L2 SRAM vers L1D SRAM
•
Étape 5 : Si traitement algorithmique terminé, sauvegarde des données de sortie
yk_sp du côté Pong de L1D SRAM vers L2 SRAM
•
Étape 6 : Si traitement algorithmique terminé, sauvegarde des données de sortie
yk_sp du côté Ping de L1D SRAM vers L2 SRAM
Voilà, pour cette année la trame de travaux pratiques s'arrête ici ! Si vous le souhaitez et si
vous avez le temps, vous pouvez tenter d'implémenter la stratégie Ping Pong. Bien entendu,
cela entraînera un refactoring non négligeable de code.
- 56 -
Digital Signal Processor
Travaux Pratiques
• Cœur de métier
Voilà, nous venons de faire un rapide tour d'horizon du potentiel de notre architecture
processeur. Nous comprenons mieux maintenant le sens du nom de l'enseignement ''Processeurs
Spécialisés''. Beaucoup des mécanismes d'accélération déterministes présentés sont impossibles ou
en tous cas bien moins performants sur architectures généralistes (MCU, AP, GPP). Néanmoins, une
bonne compréhension des stratégies présentées précédemment vous assurera une adaptabilité
relativement forte sur la plupart des architectures processeurs parallèles du marché (AP, GPP, DSP,
GPU).
Il faut maintenant savoir que le plus souvent en milieu industriel, notamment dans les grands
groupes, les ingénieurs bas niveaux chargés du développement des bibliothèques spécialisées sont
différents des ingénieurs assurant l'intégration logicielle :
•
Développeurs temps réel bas niveaux : ingénieurs spécialisés dans les architectures
matérielles processeurs et le développement de bibliothèques spécialisées
•
Développeurs logiciels hauts niveaux : intégrateurs système hauts niveaux travaillant dans les
couches hautes de l'applicatif et utilisant les bibliothèques en modes boîtes noires.
Par exemple, il ne nous reste plus qu'à faire un copier/coller du code proposant les copies
mémoires L2 SRAM / L1D SRAM / L2 SRAM par transferts EDMA/IDMA dans le code source de la
fonction fir_sp_r4 ainsi que d'inclure le code vectorisé développé dans la fonction fir_sp_opt_r4 afin
d'offrir une boîte noire utilisable par un développeur haut niveau dans une optique d'intégration
logicielle. Cette boîte noire offre alors une abstraction totale de l'architecture matérielle du
processeur et masque la grande complexité des mécanismes d'accélérations appliqués :
- 57 -
Digital Signal Processor
Travaux Pratiques
• Architecture multi-coeurs
Nous allons maintenant présenter un exemple d'utilisation d'architecture multi-coeurs, par
exemple la famille C6678 de TI. Nous constaterons alors tout le potentiel d'une programmation
vectorielle monocœur efficace. Une architecture multi-coeurs peut-être détournée à d'autres fins
qu'une simple parallélisation d'un algorithme sur plusieurs cœurs. Prenons l'exemple d'une chaîne de
traitement numérique du signal. Ce type de chaîne algorithmique peut par exemple être rencontrée
dans les domaines du traitement d'image, applications radars, applications télécoms ...
Supposons que nous travaillons sur une architecture 4 cœurs vectoriels et que nous avons de
lourdes contraintes temps réel afin d'exécuter la chaîne complète. Après instrumentations et mesures
sur la chaîne, l'algorithme imposant le plus long temps d'exécution est le numéro 4 (produit scalaire
par exemple). Observons deux exemples d'implémentations possibles, la plus efficace n'étant pas
forcément celle que l'on croit.
•
Optimisations vectorielles monocœurs et parallèles multi-coeurs appliquées à chaque
algorithme afin de minimiser leurs temps d'exécutions respectifs :
Cette solution implique donc une vectorisation ainsi qu'une parallélisation de code appliquées
à chaque algorithme. Tous les cœurs du processeur voient passer la chaîne algorithmique optimisée
complète. Cette solution, qui nécessite de nombreux allez/retours en mémoire principale, est la plus
couramment rencontrée sur processeurs GPP.
- 58 -
Digital Signal Processor
Travaux Pratiques
•
Optimisations vectorielles monocœurs et pipelining matériel algorithmique :
Chaque CPU est dédié à l'exécution d'un algorithme ou d'une série d'algorithmes. Nous
utilisons alors nos CPU comme un pipeline algorithmique. Les premières données traverseront le
pipeline complet, néanmoins celles qui suivent seront chaînées et ne dureront que le temps
d'exécution d'un étage du pipeline. Néanmoins, un ordonnanceur ou scheduler est nécessaire pour la
synchronisation des différents échanges. Sur notre processeur DSP C6678, nous pouvons nous
projeter avec 7 cœurs dédiés au traitement numérique de la chaîne et 1 cœur pour
l'ordonnancement et les échanges avec l'extérieur.
- 59 -
Digital Signal Processor
Travaux Pratiques
BENCHMARKING
ANALYSE COMPARATIVE
Algorithme
Temps
(vecteur d'entré 64Ko) Architecture d'exécution
(ms)
Performances
Temps de
(MACS per cycle, Développement
maximum 8)
(heures)
Observations et Limitations
cf. documentation DSPLIB TI
1,096ms
DSPF_sp_fir_gen
2,73
0
TI
C6600
Genuine
Intel
corei7
IA-64
(Haswell)
- 60 -
nr and nh are a multiples of 4 and greaters than or equals
to 4. x, h and r are double-word aligned. Interruptible
code.
Processeurs Spécialisés
ANNEXES
Processeurs Spécialisés
SOMMAIRE
1. CREATION DE PROJET
2. INTEGRATED DEVELOPMENT ENVIRONMENT
3. EXTRAITS – SPRU187V – OPTIMIZING COMPILER
4. EXTRAITS – SPRS691E – TMS320C6678
5. EXTRAITS – SPRABK5A1 – THROUGHPUT PEFORMANCE
6. EXTRAITS – SPRUGH7 – CPU AND INSTRUCTION SET
7. REGLES DE CODAGE
GLOSSAIRE
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
SOMMAIRE
1. CREATION DE PROJET ET COMPILATION
1. CREATION DE PROJET ET COMPILATION
L'annexe qui suit présente la procédure à appliquer pour la création de projet sous Code
Composer Studio v6 afin de travailler sur notre plateforme de développement (TMDXEVM6678L
EVM).
2. INTEGRATED DEVELOPMENT ENVIRONMENT
•
Ouvrir l'IDE CCS v6. Répertoire d'installation de l'exécutable : C:\ti\ccsv6\eclipse\ccstudio.exe
•
Créer un nouveau projet : File > New > CCS Project
•
Configuration du projet pour notre plateforme de développement. Bien entendu, il vous faut
adapter les différents chemins et noms pour le projet en cours et la machine host. Une fois
cette étape réalisée, cliquer sur Finish. S'assurer de la bonne création du projet.
3. EXTRAITS – SPRU187V – OPTIMIZING COMPILER
4. EXTRAITS – SPRS691E – TMS320C6678
5. EXTRAITS – SPRABK5A1 – THROUGHPUT PEFORMANCE
6. EXTRAITS – SPRUGH7 – CPU AND INSTRUCTION SET
7. REGLES DE CODAGE
GLOSSAIRE
-1-
-2-
Digital Signal Processor
Annexes
•
Créer une arborescence de projet sous l'IDE. Respecter celle imposée ci-dessous : clic droit sur
le projet > New > Folder
•
Ajouter un script linker propre à notre architecture. Ce script permet de fixer le modèle
mémoire du processeur, notamment les localisations mémoires des principaux segments et
sections du programme (sections concernant les allocations statiques, le code, la pile, le
tas ...). Ces scripts sont fournis par Texas Instruments et sont à adapter en fonction du projet
en cours. Pour le moment, nous garderons le script par défaut offrant un modèle unifié. Ne
pas hésiter à regarder son contenu, nous constaterons que celui-ci reste relativement simple à
lire et à interpréter. Lien de téléchargement :
Digital Signal Processor
Annexes
•
Ajouter le fichier source firtest_example.c, compiler puis exécuter le programme. Pour la
première compilation, ne lancer le code que sur le cœur n°0. S'assurer du bon fonctionnement
du projet. Utilisation de fonctions standards du C et affichage de ''Hello World !'' sur la console
de l'IDE.
•
Retirer le fichier firtest_example.c du projet (clic droit sur le fichier > delete), ajouter le fichier
firtest_main.c puis Compiler. Vous constaterez des erreurs de compilation. Il s'agit en fait
d'erreurs retournées par le pré-processeur de notre chaîne de compilation. En effet, le fichier
fait appel à des fichiers d'en-têtes, mais à aucun moment nous n'avons indiquer au
compilateur où aller les chercher.
http://processors.wiki.ti.com/index.php/Linker_CMD_Files_for_CCS
•
Utiliser dans le script présent dans /dsp/c6678/test/map/C6678_unified.cmd ou celui
présent sur internet : clic droit sur le projet > Add Files... > Link to Files > puis
sélectionner et faire glisser dans le répertoire memoryMap
•
Autre solution à déprécier, ajouter un script par défaut depuis les options de
compilation. Accéder aux options de compilation, puis ajouter le script
C6678_unified.cmd : clic droit sur le projet > Properties > General > Linker commed
file
-3-
-4-
Digital Signal Processor
Annexes
•
Ajouter les différents chemins vers les fichiers d'en-tête nécessaires à une bonne compilation
du projet. Attention, un header peu appeler d'autres headers. Tous les chemins doivent être
spécifiés à la chaîne de compilation. Accéder aux options de compilation, puis ajouter les
chemins : clic droit sur le projet > Properties > Include Options > Add...
◦
Chemin vers les headers spécifiques à notre projet :
/dsp/c6678/test/h/
/dsp/c6678/firlib/h/
/dsp/c6678/idma/h/
/dsp/c6678/edma/h/
◦
Chemins vers les headers pour la manipulation de la bibliothèque de traitement
numérique du signal développée par TI (DSPLIB). Attention, les chemins ci-dessous
peuvent changer d'une version de la DSPLIB à une autre :
C:\ti\dsplib_c66x_latest_version\inc
C:\ti\dsplib_c66x_latest_version\packages
◦
•
Ajouter les bibliothèques statiques pré-compilées nécessaires au projet. Extensions .ae66 si
spécifiques à l'architecture C6600 au format ELF (Editable Linkable Format) ou .a si génériques
à la famille C6000. Vous constaterez probablement que différents formats de bibliothèques
statiques sont possibles (.a66, .a66e, .ae66 et .ae66e). Rappelons également qu'une librairie
statique n'est qu'une concaténation de fichiers objets. La différence entre ces différents
formats de bibliothèque est le format des fichiers objets concaténés. Par exemple, les
extensions .a66e utilise des fichiers hérités COFF (Common Object File Format). Format en
cours d'obsolescence. Dans notre cas, nous utiliserons une EABI (ELF Application Binary
Interface) à la compilation, utilisant des fichiers binaire ELF durant le processus de
compilation. Format standard sur système UNIX/UNIX-like et utilisé par exemple sous système
GNU\Linux. Voici les chemins vers les bibliothèques statiques à utiliser pour notre projet :
◦
Bibliothèque dédiée au traitement numérique du signal (DSPLIB). Attention, le chemin
ci-dessous peut changer d'une version de la DSPLIB à une autre :
C:\ti\dsplib_c66x_latest_version\lib\dsplib.ae66
Chemin vers les headers pour la manipulation de la bibliothèque de gestion des
périphériques internes au processeur (CSL). Attention, le chemin ci-dessous peut
changer d'une version de CSL à une autre :
C:\ti\pdk_C6678_latest_version\packages
Au fil du projet, nous serons amenés à développer de nouvelles bibliothèques et donc
à ajouter de nouveaux fichiers d'en-tête. Bien penser à rajouter les chemins au fur et à mesure
de nos développement. A ce niveau là, le projet compile mais ne peut pas encore être exécuté
(erreurs à l'édition des liens). En effet, nous allons devoir ajouter les définitions ou binaires
des fonctions utilisées dans notre projet en spécifiant à l'éditeur des liens l'emplacement des
bibliothèques statiques à utiliser.
•
Digital Signal Processor
Annexes
◦
Bibliothèque de gestion des périphériques internes (CSL). Attention, le chemin cidessous peut changer d'une version de CSL à une autre :
C:\ti\pdk_C6678_latest_version\packages\ti\csl\lib\ti.csl.ae66
•
Durant les phases de développement, nous vous conseillons de travaillons en mode Debug
afin d'avoir accès aux différents outils de débogage proposés par l'IDE et l'ABI. Néanmoins,
durant les phases de benchmarking, il vous faudra utiliser le mode Release en coupant toute
forme de Debug et en levant les options d'optimisation à la compilation. Clic droit sur le projet
> Properties > Optimization > Optimization level > 3 et Debug options > Debugging model >
Suppress all symbolic debug generation
De façon général, lorsque nous travaillons sur des projets complexes avec plusieurs
bibliothèques sujettes à différentes versions. Nous pouvons rapidement nous retrouver avec
plusieurs fichiers du même nom sur une même machine (headers et bibliothèques). Il est
donc essentiel de spécifier lesquels utiliser à notre chaîne de compilation. Lancer alors une
recherche système (Windows, GNU\Linux …) afin de récupérer le chemin souhaité.
-5-
-6-
Digital Signal Processor
Annexes
Attention, cette partie n'est à faire que si vous souhaitez générer une bibliothèque statique.
Nous allons donc maintenant nous intéresser au processus de génération de bibliothèque statique
sous Code Composer Studio (environnement Eclipse). Rappelons qu'une bibliothèque statique n'est
qu'une archive (concaténation) de fichiers binaires objets pré-compilés (fichiers ELF dans le cas de
notre ABI). Toujours préférer une ou quelques fonctions par fichier source plutôt que d'inclure toutes
les fonctions d'une bibliothèque dans un seul fichier avant compilation. Ainsi par la suite, le linker
sera apte à n'inclure à l'édition des liens que les binaires pré-compilés (objets relogeables) des
fonctions réellement appelées par l'applicatif et non tout le contenu de la bibliothèque statique.
Sélection des fichiers objets utiles.
•
Créer un nouveau projet dans un nouveau répertoire propre à la génération de la bibliothèque
statique. Dans le cadre de cette trame de travaux pratiques, placer ce projet par exemple dans
/dsp/c6678/firlib/pjct/. Sélectionner néanmoins dans les options avancées le format du
fichier de sortie (sélection de l'archiver plutôt que du linker) : Advanced settings > Static
Library
-7-
Digital Signal Processor
Annexes
•
Créer un répertoire logique src et y placer les sources propres à la génération de la
bibliothèque statique. Bien entendu, retirer le fichier source de test contenant la fonction
main :
•
Dans les options de compilation du projet, lever les options d'optimisation -O3 et couper
toute forme de génération de code de Debug :
•
Compiler le projet et voilà, c'est fini. Effectuer une recherche Windows ou GNU\Linux afin de
rechercher le fichier nom_projet.lib puis le copier dans le répertoire /dsp/c6678/firlib/lib/.
•
Dans vos futurs projets, vous pourrez maintenant retirer les fichiers sources des projets pour
n'inclure que la bibliothèque statique (options de compilation, partie linker).
-8-
Digital Signal Processor
Annexes
2. INTEGRATED DEVELOPMENT ENVIRONMENT
Digital Signal Processor
Annexes
Cette annexe présente également un tutoriel succinct des workspaces (espaces de travail)
proposés par l'IDE Code Composer Studio v6. Le framework présenté est basé sur le plugin CDT (C/C+
+ Development Tools) classiquement utilisé sous IDE éclipse pour du développement C/C++.
Commençons par l'espace de travail d'édition et d'aide à la compilation :
Les outils de développement et bibliothèques sont librement téléchargeables et installables
depuis internet du moment que nous n'utilisons que la sonde de programmation XDS100 présente
sur la maquette ou le mode simulation. Les outils deviennent alors payants si nous souhaitons
travailler avec des sondes d'émulation évoluées, telle que la sonde XDS560 également présente à
l'école. Voici ci-dessous des liens vers les outils logiciels (IDE, SDK) utilisés afin de réaliser la trame de
travaux pratiques. Bien entendu, nous vous conseillons vivement d'installer ces outils sur vos
machines (IDE et bibliothèques spécifiées ci-dessous) :
http://processors.wiki.ti.com/index.php/Main_Page
•
IDE Code Composer Studio v6.0 basé sur Eclipse et ABI associées (Application Binary
Interface). Nous vous conseillons une installation hors ligne. De plus, le téléchargement des
outils depuis le site officiel de Texas Instruments requiert une inscription à leur site. La
validation par TI de cette étape peut prendre quelques jours, ne pas être surpris :
http://processors.wiki.ti.com/index.php/Category:Code_Composer_Studio_v6
•
Bibliothèques CSL (Chip Support Library) proposées par TI pour la gestion des périphériques
internes de leurs familles de processeurs. Bien choisir la bibliothèque associée au processeur
C6678 :
http://processors.wiki.ti.com/index.php/Chip_support_library
Après installation, CSL est accessible depuis le répertoire suivant :
C:\ti\pdk_C6678_version\packages\ti\csl\
•
Bibliothèque DSPLIB (Digital Signal Processing Library) développée par TI et proposant des
fonctions C pour le domaine du traitement numérique du signal et optimisée pour leurs
architectures processeurs. Dans notre cas, seules les bibliothèques pour la famille C6600 nous
intéressent :
http://www.ti.com/tool/SPRC265
Après installation, la DSPLIB est accessible depuis le répertoire suivant :
C:\ti\dsplib_c66x_version\packages\ti\dsplib\
-9-
- 10 -
Digital Signal Processor
Annexes
Présentons maintenant l'espace de travail d'aide au débogage sur cible, d'analyse et de
communication avec la sonde XDS100 :
Digital Signal Processor
Annexes
3. EXTRAITS – SPRU187V – OPTIMIZING COMPILER
Le schéma ci-dessous présente le workflow typique de la chaîne de compilation C6000
développée par Texas Instruments et utilisé durant la trame de travaux pratiques. Elle reprend bien
entendu la trame de toute chaîne de compilation C (standards C89 et C99) :
- 11 -
- 12 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 13 -
- 14 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 15 -
- 16 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 17 -
- 18 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 19 -
- 20 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
4. EXTRAITS – SPRS691E – TMS320C6678
http://www.ti.com/product/tms320c6678
http://www.ti.com/lit/ds/symlink/tms320c6678.pdf
- 21 -
- 22 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 23 -
- 24 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 25 -
- 26 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
5. EXTRAITS – SPRABK5A1 – THROUGHPUT PERFORMANCE
- 27 -
- 28 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
6. EXTRAITS – SPRUGH7 – CPU AND INSTRUCTION SET
- 29 -
- 30 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 31 -
- 32 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 33 -
- 34 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 35 -
- 36 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 37 -
- 38 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 39 -
- 40 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 41 -
- 42 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 43 -
- 44 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
- 45 -
- 46 -
Digital Signal Processor
Annexes
Digital Signal Processor
Annexes
7. REGLES DE CODAGE
Ce document à pour objectif de fixer un cadre et des règles de codage en langage C pour les
enseignements de Systèmes Embarqués à l'ENSICAEN. Cette démarche reflète les contraintes que
vous aurez d'ici quelques années à suivre dans le monde de l'entreprise. La plupart des entreprises
travaillant dans le domaine du développement logiciel, dont l'embarqué fait parti, imposent à leurs
développeurs des règles semblables à celles-ci (beaucoup plus exhaustives en général). L'objectif
étant de standardiser, clarifier, cadrer et faciliter le partage de codes sources au sein d'une équipe
voir de l'entreprise. Les règles de codage imposées sont inspirées du ''Linux Kernel Coding Style'' et
du ''GNU Coding Standard''. Voici le sommaire de ce document :
1. Indentation
2. Dénomination
3. Commentaire
4. Divers
INDENTATION
●
Tabulation : 8 caractères (à régler dans les préférences de l'éditeur de texte).
switch (data) {
case 'A':
case 'B':
/* comment */
data <<= 20;
break;
default:
break;
}
●
Nombres de niveaux d'indentation : Éviter plus de 3 niveaux d'indentation imbriqués.
Facilite la lisibilité du code.
●
Nombres de colonnes par page : 80 caractères/colonnes par page (à régler dans les
préférences de l'éditeur de texte).
●
procédures : cas spécial pour le fonctions, ouvrir l’accolade au début de la ligne
suivante
int function (int x, int y)
{
int z ;
...
return 0;
}
- 47 -
- 48 -
Digital Signal Processor
Annexes
●
Structures de contrôle : règles spécifiant l'indentation ainsi que les espaces à respecter
pour l'écriture de structures de contrôle.
Digital Signal Processor
Annexes
●
Macros : Toujours en Majuscule. Utiliser comme séparateur un ''_'' pour les noms
complexes. Pour les macros fonctions, appliquer les même règles de dénomination que
pour les fonctions.
do {
if (a == b) {
...
} else if (a > b) {
...
} else {
...
}
#define UART_BAUDRATE 0xFFFF
#define mul (x, y)
x*y
COMMENTAIRES
/* single statement */
if (condition)
action1();
else
action2();
} while (condition);
DENOMINATION
●
Fonctions : Toujours débuter par une minuscule. Pour donner un nom complexe,
utiliser comme séparateur une Majuscule ou un ''_''.
●
Variables locales : Toujours débuter par une minuscule. Les variables servant de
compteur de boucle peuvent avoir un nom sans sens précis, par exemple ''i''. Sinon,
toujours donner à une variable un nom court permettant d'identifier clairement son
rôle. Déclarez vos variables locales en début de fonction (portabilité de code).
●
Variables globales : Toujours débuter par une minuscule. Toujours donner à une
variable globale un nom permettant d'identifier clairement son rôle (séparateurs ''_''
ou Majuscules). Bien évidemment, éviter tant que faire se peut les variables globales
(ressources partagées).
●
Toujours penser à expliquer ce que votre code fait et non comment il le fait !
●
C99 style : Ne pas utiliser ''//'' (problème de portabilité)
●
C89 style : Toujours utiliser ''/* ... */'' (solution portable)
●
Balise pour la documentation de code (exemple de Doxygen) : Insérer dans vos
cartouches quelques tags standards, par exemple ceux de Doxygen. Attention, seuls les
fichiers d'en-tête doivent contenir des balises pour la génération de documentation.
Doxygen permet la génération automatique de documentation vers différents formats
(HTML, PDF, CHM ...). Ne pas utiliser de caractères accentués dans vos commentaires.
/**
* @file example.h
* @brief demo header file
* @author
*/
int temp_conv;
int tempProcessA, temp_process_B;
void lm4567_codec_ init (void)
{
temp_process_B++;
…
}
●
Définition de type : Toujours débuter par une Majuscule. Toujours définir un nom
permettant d'identifier clairement le type des variables déclarés.
HandleSpiModule a;
Hmod a;
/* converted data from temperature sensor */
/**
* @struct Str_data_buffer
* @brief Objet latch converted data from temperature sensor
*/
typedef struct {
int buffSize
int* allocCnvTab
} Str_data_buffer;
/**
* @brief read data codec LM4567 controller
* @param resetVal imput value
* @param cnvResult converted value
* @return null if data conversion error
*/
Handle_spi_module lm4567_codec_read (char resetVal, float cnvResult);
// Bien !
// Pas Bien !
- 49 -
- 50 -
Digital Signal Processor
Annexes
●
Quelques tags supplémentaires (cf. www.doxygen.org ):
/**
* @example example insertion
* @warning warning insertion
* @li bullet point insertion
* @mainpage main page comments insertion
* @image picture insertion
* @include code insertion
*/
Digital Signal Processor
Annexes
GLOSSAIRE
A
•
•
•
•
•
•
DIVERS
•
•
●
Valeur de retour : Essayer de faire en sorte que la valeur de retour d'une fonction soit
représentative de son bon traitement. Par exemple, retourne zéro (ou pointeur nul) en
cas d'échec et différent de zéro en cas de succès. Les résultats des procédures seront
retournés par pointeur via les paramètres d'entrée.
•
ABI : Application Binary Interface
ADC : Analog to Digital Converter
ALU : Arithmetic and Logical Unit
AMD : Advanced Micro Devices
ANSI : American National Standards Institute
API : Application Programming Interface
APU : Accelerrated Processor Unit
ARM : société anglaise proposant des architectures CPU RISC 32bits
ASCII : American Standar Code for Information Interchange
B
•
•
BP : Base Pointer
BSL : Board Support Library
C
•
•
•
•
•
CCS : Code Composer Studio
CEM : Compatibilité ElectroMagnétique
CISC : Complex Instruction Set Computer
CPU : Central Processing Unit
CSL : Chip Support Library
D
•
•
•
•
•
•
DAC : Digital to Analog Converter
DDR : Double Data Rate
DDR SDRAM: Double Data Rate Synchronous Dynamic Random Access Memory
DMA : Direct Memory Access
DSP : Digital Signal Processor
DSP : Digital Signal Processing
E
•
EDMA : Enhanced Direct Memory Access
EUSART : Enhanced Universal Synchronous Asynchronous Receiver Transmitter
EMIF : External Memory Interface
EPIC : Explicitly Parallel Instruction Computing
•
FPU : Floating Point Unit
•
•
•
F
- 51 -
- 52 -
Digital Signal Processor
Annexes
•
•
FLOPS : Floating-Point Operations Per Second
FMA: Fused Multiply-Add
Digital Signal Processor
Annexes
P
•
G
•
•
•
•
•
•
•
•
GCC : Gnu Collection Compiler
GLCD : Graphical Liquid Crytal Display
GNU : GNU's Not UNIX
GPIO : General Purpose Input Output
GPP : General Purpose Processor
GPU : Graphical Processing Unit
•
•
•
R
•
I
•
•
•
•
•
•
•
•
•
•
IA-64 : Intel Architecture 64bits
I2C : Inter Integrated Circuit
ICC : Intel C++ Compiler
IDE : Integrated Development Environment
IDMA : Internal Direct memory Access
IRQ : Interrupt ReQuest
ISR : Interrupt Software Routine
ISR : Interrupt Service Routine
•
•
•
•
•
•
•
•
•
•
•
•
•
L1D : Level 1 Data Memory
L1I : Level 1 Instruction Memory (idem L1P)
L1P : Level 1 Program Memory (idem L1I)
Lx : Level x Memory
LCD : Liquid Crytal Display
LRU : Least Recently Used
•
•
•
•
•
•
•
•
•
•
•
•
•
MAC: Multiply Accumulate
MCU : Micro Controller Unit
MIMD : Multiple Instructions on Multiple Data
MIPS : Mega Instructions Per Second
MMU : Memory Managment Unit
MPLABX : MicrochiP LABoratory 10, IDE Microchip
MPU : Micro Processor Unit ou GPP
MPU : Memory Protect Unit
SDK : Software Development Kit
SIMD : Single Instruction Multiple Date
SOB : System On Board
SOC : System On Chip
SOP : Sums of products
SP : Stack Pointer
SP : Serial Port
SPI : Serial Peripheral Interface
SRAM : Static Random Access Memory
SSE : Streaming SIMD Extensions
STM32 : STMicroelectronics 32bits MCU
T
M
•
RAM : Random Access Memory
RISC : Reduced Instruction Set Computer
RS232 : Norme standardisant un protocole de communication série asynchrone
RTOS : Real Time Operating System
S
L
•
PC : Program Counter
PC : Personal Computer
PIC18 : Famille MCU 8bits Microchip
PLD : Programmable Logic Device
POSIX : Portable Operating System Interface, héritage d'UNIX (norme IEEE 1003)
PPC : Power PC
•
•
TI : Texas Instruments
TNS : Traitement Numérique du Signal
TSC : Time Stamp Counter
TTM : Time To Market
U
•
•
UART : Universal Asynchronous Receiver Transmitter
USB : Universal Serial Bus
V
O
•
•
OS : Operating System
•
•
- 53 -
VHDL : VHSIC Hardware Description langage
VHSIC : Very High Speed Integrated Circuit
VLIW : Very Long Intruction Word
- 54 -
Téléchargement