CHAPITRE 4
Programmation DSP BLACKFIN
Présentation du jeu d’instruction
Voir le PDF Jeu d’instructions
Les modes arithmétiques
Introduction
Le jeu d’instruction BLACKFIN offre des instructions arithmétiques et des modes de calcul
Ces instructions ont la syntaxe suivante (voir le jeu d’instruction):
◼ Instruction-arithmétique (opt_mode)
(opt_mode) permet de choisir un mode arithmétique à utiliser, exemple:
◼ (S) : active le mode saturation
◼ (NS) : active le mode sans saturation
◼ (T) : active le mode troncation
◼ Si aucun mode n’est actif, le mode arithmétique par défaut est utilisé
Les modes arithmétiques
Transferts Ax → Dreg
Mode par défaut: Transfert 32bits avec saturation 32bits
Transferts Ax → Dreg_lo ou Dreg_hi
Mode par défaut: Q signé + Transfert Ax.H + Saturation 16bits + Arrondi 16bits
(FU) Fraction unsigned: Q non signé + Transfert Ax.H + Saturation 16bits + Arrondi 16bits
(T) Troncation : Q signé + Transfert Ax.H + Troncation Ax.L
(IS) Integer Signed: Entier signé + Transfert Ax.L + Saturation 16bits
(IU) Integer Unsigned : Entier non signé + Transfert Ax.L + Saturation 16bits
◼ R2.L = A0;
◼ R2.L = A0 (T);
Les modes arithmétiques
Addition/soustraction 32bits
Par défaut ou (NS): Sans saturation 32bits
◼ R5 = R2 + R1 ; // Addition sans saturation et #b de l’instruction 16bits
◼ R5 = R2 + R1(NS) ; // Addition sans saturation et #b de l’instruction 32bits
(S): Saturation 32bits
◼ R5 = R2 + R1(S) ; // Addition avec saturation 32bits et #b de l’instruction 32bits
Les modes arithmétiques
Addition/soustraction 16bits
Par défaut: Sans saturation
◼ R5.L = R2.L+ R3.H (NS) ; // Addition/Soustraction sans saturation 16bits
◼ R5 = R2 +|- R1 ; // Addition/Soustraction sans saturation 16bits (sans ajout de NS)
(S): Saturation 16bits
◼ R5.L = R2.L+ R3.H (S) ; // Addition avec saturation 16bits
◼ R5 = R2 +|+ R1 (S) ; // Addition avec saturation 16bits
Les modes arithmétiques
Multiplications et MAC
Option Description
Par défaut Opérandes fractionnaires signés + décalage du bit signe
(TFU) Opérandes fractionnaires non-signés + Troncation
(T) Opérandes fractionnaires signés + Troncation
(IS) Opérandes entiers signés
(IU) Opérandes entiers non-signés
Instructions parallèles
Parallélisme
L’architecture du cœur DSP BLACKFIN offre les BUS mémoire L1 suivants :
◼ DA0/LD0 32bits
◼ DA1/LD1 32bits
◼ SD 32bits
◼ IAB 32bits
◼ IDB 64bits
Instructions parallèles
Parallélisme
BUS IDB permet un fetch d’un mot 64bits de 1 à 3 instructions parallèle (||)
Cela permet un fetch de plusieurs instructions en parallèle (1 cycle d’horloge):
◼ 1 x instruction arithmétique sur MAC0/1 (ou ALU0/1) en même temps
◼ 2 x transferts 32 bits ( 2 lecture ou 1 lecture et 1 écriture)
◼ 2 x mises à jour pointeurs
◼ 2 x mises à jour boucles matérielles
Instructions parallèles
Syntaxe en ||
Le DSP BLACKFIN permet de faire un fetch 64bits sous plusieurs arrangements:
Instruction ALU/MAC 32-bit Instruction 16bits Instruction 16bits
◼ Slot 1 : Instruction ALU/MAC 32bits ou un MNOP (Multiple NOP)
◼ Slot 2 : Instruction Load/Store 16bits ou un NOP
◼ Slot 3 : Instruction Load/Store 16bits ou un NOP
Les NOP/MNOP sont insérés automatiquement via l’assembleur
Instructions parallèles
Instructions possibles sur le slot 1
Les instructions ALU/MAC 32-bit qui occupent le slot 1 :
◼ Addition/soustraction 16bits et 32bits
◼ Addition/soustraction avec saturation
◼ Multiplication/MAC
◼ Transferts Ax → Dreg
◼ Transferts Dreg_hi_lo
◼ Auto-incrément/décrément Ax
Instructions parallèles
Instructions possibles sur le slot 2 et le slot 3
Les instructions 16bits suivantes occupent le slot 2 et slot 3:
◼ Lecture/Ecriture via pointeur Preg
◼ Lecture/Ecriture via pointeur Ireg
Instructions parallèles
Exemples
2 accés mémoires en paralléle
◼ MNOP || R1 = [I0++] || R3 = [I1++] ; // 2 lectures mémoire en méme temps
Les MNOP sont insérés automatiquement via l’assembleur
Addition 16bits + incrément Ireg + lecture mémoire via Ireg
◼ R2 = R2 +|+ R4, R4 = R2 -|- R4 || I0 += M0 || R1 = [I0] ;
Instructions parallèles
Exemples
2 x MAC + tranfert Dreg_lo_hi + 1 load via Preg + 1 load via Ireg
◼ R3.H=(A1+=R0.L*R1.H), R3.L=(A0+=R0.L*R1.L) || R0=[P0++] || R1=[I0] ;
2 x MAC + tranfert Ax + 1 load via Ireg + 1 store via Ireg
◼ A1=R2.L*R1.L, A0=R2.H*R1.H || R2.H=W[I2++] || [I3++]=R3;
Limitations du jeu d’instruction
Transferts Ax vers Dreg ou Dreg_lo_hi
L’accumulateur A0 doit être utilisé avec des registres paires Dreg_even ou Dreg_lo
L’accumulateur A1 doit être utilisé avec des registres impaires Dreg_odd ou Dreg_hi
◼ Exemples:
R3 = A1; // R impaire
R5.L = A0
R2 = (A0 += R0.L*R1.L);
R5.H = (A1 += R1.L*R2.L);
Limitations du jeu d’instruction
2x MAC vers 2x Dreg
Les registres destination doivent êtres des paires proches:
◼ R1/R0 ou R3 et R2 …. etc…
Exemples:
R3 = (A1 += R0.H*R1.L) , R2=(A0 += R0.L*R1.L);
R5 = (A1 += R1.H*R2.L) , R4=(A0 += R1.L*R2.L);
Flot de développement
Flot de développement
Compilation et assemblage
Flot de développement
Exemple assemblage en ASM
Flot de développement
Exemple compilation en C
Flot de développement
Exemple compilation en C sous différentes sections
Interface ASM-C/C++
Interface C/C++/ASM
Le DSP Blackfin peut être programmé en ASM ou en C/C++.
Pour atteindre un rendement maximum en terme de performances, il est possible d’utiliser
un mix de fonctions C/C++ et de routines ASM à travers des interfaces prédéfinis.
Ces interfaces peuvent être définies comme un:
◼ Programme C/C++ qui appel une fonction ASM
◼ Programme ASM qui appel une fonction C/C++
◼ Programme ASM qui associe des variables C/C++ comme symboles ASM
◼ Programme C/C++ qui utilise de l’assembleur in-line (instructions assembleurs en C)
Interface ASM-C/C++
Convention ASM → C
Appel d’une variable/fonction ASM en C :
C ASM
.global _asm_func;
extern void asm_func(….); …………
_asm_func:
extern int asm_var; .global _asm_func;
◼ L’appel d’une fonction/variable ASM en C exige que:
◼ Son nom en ASM doit étre précédé d’un tiret
◼ Déclarées comme globales en ASM et comme externes en C
Interface ASM-C/C++
Convention C → ASM
Appel d’une variable/fonction ASM en C :
C ASM
void c_func(….); .extern _c_func;
int c_var ; // variable globale .extern _asm_func;
◼ L’appel d’une fonction/variable C en ASM exige que:
◼ Son nom en ASM doit étre précédé d’un tiret
◼ Déclarées comme globales en C et comme externes en ASM
Interface ASM-C/C++
Appel d’une routine ASM en C
Soit une fonction:
_MafonctionASM (Arg1, Arg2, …. );
Passage des arguments C → fonction ASM:
◼ Arg1 → R0
◼ Arg2 → R1
◼ Arg3 → R2
◼ Arg4 et plus: sur le stack ([FP+20], [FP+24], [FP+28] ….etc….)
◼ Valeur de retour 32bits sur R0
◼ Valeur de retour 64bits sur R1:R0
Interface ASM-C/C++
Appel d’une routine ASM en C
Une fonction ASM appelée en C ne peut utiliser librement les registres DSP
Ces registres sont donc classés en registres:
◼ Compilateurs
◼ Préservés
◼ Indépendants
Interface ASM-C/C++
Appel d’une routine ASM en C
Registres compilateur
◼ Ce sont les registres L0-L3 et les pointeurs de pile SP et FP
◼ Ces registres sont réservés à un usage exclusive par le compilateur C/C++
◼ A chaque appel ou retour d’une fonction ASM:
◼ SP et FP doivent être remis à leurs valeurs initiales avant retour d’une fonction ASM
◼ L0-L3 doivent être remis à 0 en cas d’utilisation par la fonction ASM
Interface ASM-C/C++
Appel d’une routine ASM en C
Registres préservés
◼ Ce sont les registres R4-R7 et P3-P5
◼ A chaque appel fonction ASM, il faudra sauvegarder leurs valeurs
◼ A chaque retour fonction ASM, il faudra restaurer leurs valeurs
Interface ASM-C/C++
Appel d’une routine ASM en C
Registres indépendants
◼ Ce sont les registres:
◼ R0-R3
◼ Accumulateurs
◼ ASTAT
◼ P0-P2
◼ Registres DAG sauf les Lreg
◼ Registres des boucles matérielles
◼ Ces registres peuvent être utilisés par une fonction ASM appelée en C sans aucune restriction
particulière
Interface ASM-C/C++
Assembleur in-line
Un programme C peut utiliser la syntaxe assembleur sans faire appel à une interface ASM-
C en utilisant directement une construction asm( )
◼ asm(“R0 = W[P0];”);
◼ asm(“BITSET(R0,7);”);
◼ asm(“SSYNC;”);
L’utilisation d’instructions ASM est également possible via des fonctions C précompilées:
◼ Exemple routine assembleur MAC ASM en C :
add_fr1x32 (acc , mult_fr1x32 (in[i] , coeff[i] ) ) ;
Optimisation du code
Densité du code vs Performances
Il existe plusieurs façons d’utiliser les instructions en || afin d’optimiser son code
Exemple d’optimisation de la densité du code:
◼ R6=(A0+=R3.H*R2.H); // 32-bits
I2 -=M0 ; // 16-bits
◼ #Cycles: 2 cycles Taille du code: 6 octets
Exemple d’optimisation de la vitesse d’exécution:
◼ R6=(A0+=R3.H*R2.H) (FU) || I2 -=M0
◼ #Cycles: 1 cycle Taille du code: 8 octets
Optimisation du code
Optimisation en C
Afin d’optimiser son code C il faut tous d’abord bien mettre au point son code C en suivant
les recommandations suivantes:
◼ Mettre les tableaux dans deux sections différentes en mémoire
◼ Eviter d’utiliser des variables globales comme condition dans une boucle
◼ Eviter les boucles avec un pas différent de 1
◼ Eviter d’inclure des instructions de conditions dans une boucle
◼ Utiliser les fonctions assembleurs compilés en C le plus possible
Optimisation du code
Optimisation en C
Après avoir suivis ces recommandations, le code C est prêt à être optimisé par la CLI du
compilateur ou par l’ajout de directives pragmas spécifiques pour chaque section du code:
◼ #pragma optimize_for_space
…………………………….
…………………….
◼ #pragma optimize_for_space
…………………………………
……………………………….
Optimisation du code
Optimisation en ASM
L’utilisation de l’assembleur est plus efficace que la programmation en C pour les fonctions
critiques dans le code. Cela offre au programmeur plus de contrôle sur le DSP
L’optimisation ASM suit donc les recommandations suivantes:
◼ Eviter les boucles software
◼ Utiliser les deux unités MAC en même temps
◼ Fetch des instructions en parallèle au maximum
◼ Utiliser des sections différentes pour les tableaux de données
◼ Utiliser le pipeline en software
◼ Eviter les différents stalls qui peuvent avoir lieu