Les programmes du DSP
Introduction sur le DSP de Texas Instrument TMS 320C54x
Texas Instrument a fait des efforts particuliers pour faciliter la tâche aux programmeurs. En effet, lassembleur na jamais été un langage très " proche de lhomme " et sa syntaxe en a déjà repoussé plus dun TI a conçu une syntaxe nommée AIS (Algebraic Instruction Set) dans le but de simplifier la lecture et la programmation dapplications DSP.
Ce nest pas une évolution du langage mais uniquement de la syntaxe : Ce qui veut dire quune instruction AIS sera toujours équivalente à une instruction en syntaxe standard.
Prenons un exemple très simple.
Voici un tableau comparatif :
Syntaxe standard | Syntaxe AIS |
SETC INTM | INTM = 1 |
LDP #1 | DP = #1 |
OPL #1,PMST | PMST = #1 |
SPLK #1,IMR | IMR = #1 |
SPM 1 | PM = 1 |
La syntaxe standard exigeait lutilisation
de dizaines dinstructions différentes pour une seule et même
opération : Assigner à un registre la valeur 1. La
syntaxe AIS laisse rêveur
Bien entendu, nous remarquons quil
reste encore des vestiges de cette syntaxe : le # en est un très
bon exemple. Quand le mettre ? Quand ne pas le mettre ? Il
existe une règle mais également des exceptions qui confirment
cette règle !
Notre but ici nest pas de reprendre point
par point les caractéristiques du DSP telles que ses registres,
ses types dadressage, ses caractéristiques
Nous
voudrions uniquement attirer lattention du lecteur sur
certains points qui nous ont posés problème lors de notre première
prise en main du DSP. Pour de plus amples informations, les
documentations des DSP sont disponibles à lIUT.
Les symboles # et @
Un point important à noter est lutilisation des symboles # et @.
Un certain nombre de règles doivent être
appliquées afin de ne pas se tromper.
Le symbole # doit être utilisé :
Ø Devant une valeur immédiate. Par exemple : #1 #0Ch
(sauf dans certains cas, comme on le voit dans le tableau plus haut)
Ø Devant une constante. En effet, les constantes sont remplacées, lors de la compilation, par la valeur immédiate quelle représente. Par exemple, si on définit une constante " bonjour " à 1, lorsque le compilateur verra " #bonjour ", il le remplacera par " #1 ".
Ø
Devant un nom de variable si on souhaite accéder à ladresse
de celle-ci. Par exemple, le code " A = #variable "
assignera à A ladresse de la variable en mémoire.
Le symbole @ doit être utilisé :
Ø
Devant un nom de variable si on souhaite accéder au contenu de
la variable.
Configuration de la mémoire
Les premières instructions dun programme
doivent comporter linitialisation de la mémoire du DSP.
Ceci se fait par lassignation dune valeur correcte au
registre PMST (Processor Mode Status Register). Les différentes
possibilités dassignation sont répertoriées dans les
sources " envoi.asm " et " recep.asm
". Dautres registres peuvent (et devraient) être définis
afin de sassurer que le DSP agira selon nos besoins. Les
principaux registres (ASM, OVM,
) ont été définis et
commentés dans ces mêmes sources.
Les interruptions
Le traitement dinformations en temps réel
exige que le programmeur sadapte à une logique dinterruptions.
En effet, il est difficile à un programme " séquentiel
" de réagir en fonction dun événement. Pour cela,
toutes nos fonctions démission et de réception sont stockées
dans une routine dinterruption nommée : Receive.
Une autre routine (Transmit) existe et elle joue le même
rôle que la précédente. Malgré nos tentatives, nous navons
trouvé aucune différence entre ces deux routines.
Les vecteurs dinterruption
La table des vecteurs dinterruptions est
une zone mémoire où lon peut définir le comportement du
DSP face à différents événements. Par exemple, cest là
que lon définira si le DSP doit réagir quand le PC veut
faire appel à lui (interruption HPI INT), ce quil
doit faire lorsque lAIC aura terminé une conversion
Cest une table primordiale lorsque lon souhaite
utiliser le DSP dune manière intéressante.
Les pages
Le DSP divise sa mémoire un plusieurs blocs de 128 mots (quon appelle une page), afin de diminuer la taille du code en mémoire. Pour mieux comprendre ce principe, on peut reprendre lexemple déjà utilisé dune ville.
Plutôt que de numéroter chaque maison de 1 à 8192, on divise la ville en 64 rues de 128 maisons.
Ainsi, lorsque lon se trouve dans la rue n°60, on ne parlera plus de la maison numéro 7681, mais de la maison numéro 1, et ainsi de suite. Cest plus économique en place et en temps.
DP est le registre qui contient le numéro de
la rue, de la page courante.
Les instructions de test
Lune des manières les plus simples pour
réaliser un test est lutilisation de linstruction
" if ", combinée à un accumulateur " a " ou
" b ".
Par exemple :
if (aeq) goto A_est_egal_a_zero Si A = 0,
on saute à lendroit indiqué.
if (alt) execute(1) Si A < 0, on lui ajoute 10.
A += #10
" aeq " signifie " a equal " ou A = 0
" agt " signifie " a greater than " ou A > 0
" alt " signifie " a lower than " ou A < 0
" aleq " signifie " a lower or equal than " ou A <= 0
Et ainsi de suite
Cest une syntaxe facile à retenir et également
facile à comprendre.
Convivialité
Beaucoup dinstructions ont été développées
pour ressembler au C, ou plutôt pour en approcher la convivialité.
Par exemple :
A = B >> 1 A sera égal à B décalé de 1 vers la gauche (divisé par 2).
A += #1 A sera incrémenté de 1.
B = ~B B est complémenté.
B &= #1 B est masqué par 1.
Les pointeurs
Il existe également des pointeurs dont la
gestion est remarquablement bien faite. Ils se nomment AR0, AR1,
AR2,
, AR7.
Si lon a un tableau nommé " tab
", voici les possibilités offertes par les pointeurs :
AR0 = #tab AR0 va pointer sur le tableau.
A = *AR0 A va contenir le premier élément du tableau.
B = *AR0+ B va également contenir le premier élément du tableau.
Le " + " indique que AR0 va pointer sur lélément suivant.
A = *AR0- A va contenir le deuxième élément.
Le " - " indique que AR0 va pointeur
sur lélément précédent.
Bien dautres possibilités sont proposées,
comme par exemple définir le pas davancement dans la table,
ou alors définir un buffer circulaire (cest à dire que
lorsquon aura dépassé un élément bien défini, le
pointeur se repositionnera automatiquement sur le premier élément,
et vice versa).
Organisation de la mémoire
La mémoire est organisée de la
façon suivante :
Adresse | Limite | Contenu |
0180h | 01FFh | Vecteurs dinterruption |
0200h | 02C3h | Table des sinus (195 valeurs) |
0400h | Variable | Fichier à envoyer |
1080h | Variable | Données |
1800h | Variable | Programme |
Nous nallons pas détailler la
description des variables et des constantes que nous avons utilisées
du fait quelles sont bien documentées dans les fichiers
source. (voir annexe)
Emploi fréquent de linstruction
" nop "
Lemploi de linstruction " nop
" (No Opération) pourrait sembler inutile. Pourtant, celle-ci
est primordial. En effet, le DSP utilisé utilise une technologie
dite " pipeline ". En fait, pour augmenter la
vitesse dexécution des programmes, les instructions
sont décomposées en plusieurs opérations de base. Afin de
ne pas trop rentrer dans des détails qui remplissent à eux
seuls plus dun chapitre de la documentation du DSP, nous
allons donner un exemple pratique afin de mieux cerner le problème.
Voici deux instructions :
DP = #ma_variable
A = @ma_variable
Notre but est de placer dans A la valeur contenue dans " ma_variable ".
DP est ce quon appelle un registre de
page. Pour en comprendre son principe, prenons lexemple
dun village. Une personne habite 3 rue des Pommiers,
une autre, 3 rue des Poiriers. DP, cest le nom de la
rue. Le symbole @ serait alors le numéro de maison.
Ici, on définit DP (rue) comme il faut, puis
on tente daccéder à la bonne case mémoire (numéro de
maison). Mais le problème, cest que quand la deuxième
instruction commencera à sexécuter, la première ne
sera pas achevée ! Ce qui va donner quau lieu daccéder
à la rue des Poiriers, on sera à la rue des Pommiers ! Le résultat
sera donc faux.
Pour palier à cela, il faut insérer deux
" nop " entre ces deux instructions afin de
laisser au DSP le temps de se positionner sur la bonne rue et
seulement là, on recherchera la maison voulue.
Ce problème est loin dêtre mineur du
fait quil peut fausser totalement des résultats et est
entièrement transparent à lutilisateur.
Fréquence de fonctionnement
Lors de linitialisation de lAIC (circuit qui permet de convertir un une valeur analogique [dun micro, ] en numérique), il nous est possible de définir le temps que celui-ci devra mettre pour effectuer les conversions.
La formule est la suivante :
Fréquence = 5.000.000 / (Ta * Tb)
Ta et Tb sont deux registres que nous définissons
à linitialisation. Ceux-ci ont tous deux la valeur 16.
Ainsi, nous savons que nous pourrons réaliser 19531,25
conversions à la seconde. Selon le théorème de Shannon,
la fréquence maximale que nous pourrons générer sera de 9765Hz,
ce qui est bien au-dessus de nos 1300 et 2100Hz.
Lorsque lAIC achève une conversion, il génère une interruption. Pour être plus clair, le programme principal sinterrompt et une routine à part est exécutée.
Du fait que notre DSP fonctionne à 40MHz,
nous pouvons déterminer le nombre maximal de cycles dhorloge
pouvant être utilisés pour notre routine :
40.000.000 / 19.531 = 2.048
Sachant que beaucoup dinstructions du DSP
ne font quun seul cycle dhorloge, il est facile de
voir que la quantité dinstructions possibles est énorme.
Fonctionnement
Ce programme ne doit pas uniquement générer une simple sinusoïde. Mais en plus de réaliser une modulation de fréquence (FSK), il doit sadapter à un protocole qui a été décrit plus haut.
Pour cela, le meilleur moyen est de requérir
à une logique dautomate. Le principe en est simple
: Associer des actions à un certain nombre détapes. Ces
étapes sont divisées de la même manière que le protocole lui-même.
Etape 0 : Envoi dune fréquence de 1300Hz pendant 20.000 cycles horloge (env. 1 seconde).
Etape 1 : Envoi de 128 " 1 " modulés pour permettre au récepteur de déterminer la vitesse démission.
Etape 2 : Envoi de 16 " 0 " modulés pour achever la synchronisation.
Etape 3 : Envoi du fichier
Etape 4 : Envoi de 16 " 0
" pour clore la transmission.
Extraction du bit voulu
Nous avons vu que chaque mot en mémoire comportait 16 bits. Le problème est donc le suivant : Comment extraire le bit désiré, et passer au mot suivant au moment opportun ?
Par chance, 16 est un multiple de deux. Il y a donc certainement un moyen dextraire les informations désirées grâce à des masques.
La première chose à savoir est que nous
employons une variable " position " qui indique où on
en est dans le fichier.
Ce mot est constitué comme suit : 11111111 11112222
Les 2, ici, représentent lindice du bit désiré alors que les 1 nous informent du mot courant.
Par exemple :
Position = 131 = 83h = 00000000 10000011b
En décomposant, la partie en gras nous indique
que nous désirons le bit n°3 alors que la partie non grasse
nous informe que nous accédons au huitième mot.
Ce principe très simple permet déviter
beaucoup de complications.
Génération de la sinusoïde
Une variable " pas " contient le pas utilisé pour léchantillonnage dans notre table. A nouveau, pour simplifier nos calculs, nous navons pas adapté le pas à la taille de la table, mais nous avons fait linverse.
Voulant une fréquence de 1300Hz et une
autre de 2100Hz, nous nous sommes fixés des pas
respectifs de 13 et de 21. De là, daprès la
fréquence déchantillonnage, nous avons déterminés la
taille requise pour la table. Voici en bref cette logique en
calcul, qui a déjà été abordée dans la partie théorique :
Fréquence déchantillonnage : 19531.25
Hz
(19531.25 / 1300) * 13 = 195.3125 valeurs
(19531.25 / 2100) * 21 = 195.3125
valeurs
Par cette simple logique, nous avons défini nos valeurs.
Avec une table de 195 valeurs, nous tombons sur
des fréquences de :
Ø 19500 * 1300 / 19531.25 = 1297.92Hz
Ø 19500
* 2100 / 19531.25 = 2096.64Hz
Ce qui est très proche des fréquences désirées.
Cette table est stockée en Q15, ce qui signifie que la plage de
variation est de :
-1 <= nombre Q15 <= 0.9999694
Et que la précision est de 1/32768 = 0.0000305.
Conclusion sur lémetteur
A présent, il ne reste plus quau lecteur
à passer en revue les sources de lémetteur qui sont détaillées
au mieux.
La mémoire est organisée de la
façon suivante :
Adresse | Limite | Contenu |
0180h | 01FFh | Vecteurs dinterruption |
0300h | 030Fh | Buffer utilisé pour le retard |
1010h | Variable | Données |
1200h | 15FFh | Fichier reçu |
1800h | Variable | Programme |
Nous nallons pas détailler la
description des variables et des constantes que nous avons utilisées
du fait quelles sont bien documentées dans les fichiers
source. (voir a
Fréquence de fonctionnement
LAIC est configuré en réception de la même
manière quen émission. Nous avons la même fréquence déchantillonnage
de 19531Hz.
Les étapes
Les différentes étapes de la démodulation,
représentées dans lalgorithme ci-dessus, sont les
suivantes :
Etape 0 : Attente dune sinusoïde à 1300Hz sur la ligne.
Etape 1 : Attente dun " 1 " modulé sur la ligne.
Etape 2 : Comptage jusquà ce quil y ait un " 0 " modulé sur la ligne. Détermination du débit.
Etape 3 : Attente de la fin de la synchronisation.
Etape 4 : Démodulation et stockage
jusquà la fin de lémission.
Extraction du bit voulu
Pour le stockage du bit en mémoire, le même
principe que pour la modulation est utilisé.
Conclusion sur lémetteur
Une fois que le principe de fonctionnement est
compris, il est aisé de parcourir les fichiers source et den
retirer les informations nécessaires.
Ce programme na pas été prévu dès le commencement de lannée, mais son besoin sest fait sentir. En effet, comment savoir si la transmission a bien eu lieu sans avoir un retour dinformation aussi complet que possible ?
Pour sa réalisation, nous sommes partis des fichiers sources du programme " loadapp " fourni par Texas Instrument.
Bien que ce " squelette " soit assez mal conçu dun point de vue purement informatique (beaucoup de variables globales, mauvaise organisation des routines, gestion dangereuse des pointeurs, ), nous avons décidé de laisser la base telle quelle et de rajouter les fonctions dont nous avions besoin.
Nous ne ressentions pas le besoin de tout
reprendre du fait que ce programme a un rôle mineur et ne fait
pas parti du projet que nous avions dès le départ.
A ce même titre, nous nallons pas développer son fonctionnement du fait dun nombre trop important de routines à commenter ainsi que du protocole HPI qui nest pas des plus aisés. Toute personne ayant un niveau de base en C pourra tirer beaucoup de profit des commentaires du fichier source.
Conception des modules audio
Entrée/Sortie Auxiliaires des DSP C54x
Nous avons voulu ajouter au DSP Starter
Kit une possibilité de pouvoir brancher un micro et un
haut parleur avec réglage du gain et du volume, permettant ainsi
de faire des applications audio.
Pour cela, les DSP C54x sont équipés d'une
sortie (out+) et d'une entrée auxiliaire (aux+) analogique
permettant d'obtenir un accès direct sur l'AIC. La configuration
de ces accès externes à l'AIC se fait par programmation dans le
registre 5, ce qui permet d'obtenir soit les entrées/sorties
auxiliaires soit les entrées/sorties de la carte DSP, soit la
somme des deux. En général, les bits 1 et 2 sont laissés à 1
et 1, pour avoir les 2 accès validés.
Les sorties sont utilisées en mode commun avec
la sortie out- et l'entrée aux- reliées à la masse. La plage
de tension des signaux entrants et sortants dépend de la
configuration des bits A0 et A1 du registre 4 de l'AIC.