L'architecture qui rend le silicium capable de penser en signal
Du fil unique de Von Neumann jusqu'aux huit unités fonctionnelles parallèles du TMS320C6713, ces notes reconstruisent — sans raccourcis — la pile complète qui transforme un échantillon analogique en calcul utile : architectures matérielles, parallélisme VLIW, pipelines à étages multiples, registres, modes d'adressage, et programmation en assembleur sur DSP TI.
Architecture des systèmes µp
Avant le DSP, le microprocesseur. Ce chapitre pose le vocabulaire : comment le silicium est-il organisé physiquement (hardware), et comment exécute-t-il un programme (software). Tout ce qui suivra sur le C6713 — VLIW, pipeline, chemins de données — est une variation sophistiquée de ces idées fondamentales.
Hardware — Von Neumann vs Harvard
Un processeur a besoin de deux choses pour exécuter une instruction : le code de l'instruction lui-même (qui est en mémoire programme), et les données sur lesquelles cette instruction opère (qui sont en mémoire données). La question architecturale fondamentale est : ces deux mémoires partagent-elles le même bus, ou ont-elles chacune leur autoroute ? La réponse sépare le monde en deux familles.
Von Neumann
Une mémoire unique contient à la fois les instructions et les données. Un seul bus les transporte. Pour exécuter une instruction, le CPU doit : d'abord chercher le code-instruction, puis aller chercher les données. Sérialisé. Simple à concevoir, mais le bus devient le goulot d'étranglement classique — c'est le « Von Neumann bottleneck ».
→ Utilisé dans la plupart des processeurs généralistes (PC).
Harvard
Deux mémoires séparées (programme / données), deux bus indépendants. Le CPU peut lire une instruction et une donnée dans le même cycle d'horloge. La bande passante effective double, et la prédictibilité temporelle s'améliore — capital pour les applications temps-réel.
→ Utilisé dans la plupart des DSP et microcontrôleurs.
Le DSP traite un flux continu d'échantillons (« streaming data ») : chaque cycle compte. Doubler la bande passante mémoire en utilisant Harvard n'est pas une optimisation cosmétique, c'est ce qui rend le filtrage temps-réel possible. Le C6713 va plus loin avec une « Harvard modifiée » : la mémoire données est elle-même multi-accès (N bus internes), et il y a un cache séparé pour programme (L1P) et données (L1D).
CISC vs RISC — la philosophie du jeu d'instructions
Une autre division architecturale, orthogonale à Von Neumann/Harvard, concerne la nature des instructions que le processeur sait exécuter. Faut-il un grand vocabulaire (mots complexes, expressifs, mais lents à articuler) ou un petit vocabulaire (mots simples qu'on enchaîne à grande vitesse) ?
| CISC — Complex | RISC — Reduced | |
|---|---|---|
| Variété d'instructions | Très nombreuses, très différentes | Peu, simples, uniformes |
| Taille | Variable | Fixe |
| Registres | Peu, mais spécialisés | Beaucoup, généraux |
| Pipeline | Difficile (instr. de tailles différentes) | Naturel |
| Exemple | x86 (Intel) | ARM, MIPS, TMS320C6x |
Anatomie du CPU — UAL + UC + Registres
Le CPU se décompose en trois sous-unités reliées par des bus internes. Chacune a un rôle précis dans le cycle Fetch → Decode → Execute que tout processeur, du Z80 au C6713, répète des milliards de fois par seconde.
| Unité | Sigle | Rôle |
|---|---|---|
| Unité Arithmétique & Logique | UAL / ALU | Le cœur du calcul. Additionneurs, soustracteurs, comparateurs, ET/OU/NON. Souvent dédoublée en UAL entière, FPU (calcul réel), et unité multimédia. |
| Unité de Contrôle | UC / CU | Le chef d'orchestre. Récupère l'instruction (Fetch), la décode, génère les signaux qui pilotent l'UAL et la mémoire. |
| Registres | — | Mémoire interne minuscule, ultra-rapide. Stocke les opérandes et résultats temporaires pendant l'exécution. |
Le cycle Fetch–Decode–Execute en pratique
Considère cette séquence d'instructions, typique d'un programme assembleur :
LOAD R1, 0x100 ; charger en R1 la valeur stockée à l'adresse 0x100
ADD R1, R2 ; R1 ← R1 + R2
STORE R1, 0x100 ; écrire R1 à l'adresse 0x100
Pour LOAD R1, 0x100, le CPU :
- Fetch — l'UC va chercher l'instruction en mémoire programme, à l'adresse pointée par le compteur programme (PC).
- Decode — l'UC analyse les bits : opcode = LOAD, opérande source = adresse 0x100, opérande destination = R1.
- Execute — l'UC envoie un signal lecture sur le bus de contrôle, l'adresse 0x100 sur le bus d'adresses ; la mémoire renvoie la donnée sur le bus de données ; l'UC l'écrit dans R1.
Les types de registres
| Catégorie | Exemples (x86) | Fonction |
|---|---|---|
| Registre d'instruction (IR) | — | Contient l'instruction en cours. |
| Compteur Programme (PC) | — | Adresse de la prochaine instruction. |
| Registres généraux | AX, BX, CX, DX | Opérandes & résultats temporaires. |
| Registres d'adresses / pointeurs | SI, DI, SP, BP | Pointer vers des zones mémoire (sources, destinations, pile, base). |
Mémoires & Bus internes
La cache, ou comment cacher la lenteur de la RAM
La RAM principale est lente par rapport au CPU (typiquement 100× plus lente). La parade : intercaler une mémoire petite mais ultra-rapide près du CPU, qui garde une copie des données et instructions récemment utilisées. C'est la cache.
- L1 : la plus petite, la plus rapide, intégrée au CPU.
- L2 : plus grande, légèrement plus lente. Sur le C6713, on aura L1P (programme) + L1D (données) + L2 (mixte).
Les trois bus
Un bus, c'est juste un faisceau de pistes électriques partagé. Il y en a trois :
- Bus d'adresses — unidirectionnel (CPU → mémoire). « Je veux lire/écrire à cette adresse. »
- Bus de données — bidirectionnel. Transporte la donnée elle-même.
- Bus de contrôle — transporte les signaux read/write, interruptions, synchronisation.
Lecture d'une donnée en mémoire : (1) adresse sur le bus d'adresses, (2) signal « read » sur le bus de contrôle, (3) la mémoire pose la donnée sur le bus de données.
Classification des mémoires non-volatiles
| Type | Volatile ? | Caractéristique |
|---|---|---|
| RAM | Oui | Lecture/écriture rapide. Perd tout sans alim. |
| ROM | Non | Lecture seule. Contient le programme de démarrage. |
| OTP (One-Time Programmable) | Non | Programmable une fois. Plus de modification possible. |
| EPROM | Non | Effaçable aux UV (~15 min). Réutilisable mais lent. |
| EEPROM | Non | Effaçable électriquement, octet par octet. |
| FLASH | Non | EEPROM plus rapide, mais effacement seulement par bloc. |
Architecture logicielle — du C au binaire
Un programme est une séquence d'instructions ; une instruction est une opération élémentaire. Chaque µp a son propre jeu d'instructions, encodées en binaire (le langage machine), illisibles pour un humain.
Pour rendre cela écrivable, on utilise des couches d'abstraction :
Une instruction binaire se découpe en deux parties :
- Code opération (opcode) — ce que le µp doit faire (ADD, MOV, etc.).
- Code opérande — sur quoi il doit le faire (les registres, les valeurs, les adresses).
Instruction simple vs complexe
Simple
« Incrémenter R1. » Tout se passe à l'intérieur du CPU, un seul additionneur intervient. Typiquement 1 cycle d'horloge.
Complexe
« Incrémenter le contenu de l'adresse mémoire 0x200. » Implique trois étapes : lire la mémoire, additionner, réécrire. Plusieurs cycles.
Modes d'adressage — comment l'opérande est désigné
| Mode | Idée | Exemple |
|---|---|---|
| Immédiat | La valeur est codée dans l'instruction. | MOV #0x55, W0 → charge la constante 0x55 dans W0 |
| Direct | L'instruction donne l'adresse où trouver la valeur. | MOV EAX, [0x12345678] |
| Indirect | L'instruction désigne un registre qui contient l'adresse. | MOV EAX, [EBX] → EBX contient l'adresse cible |
L'indirect est crucial : c'est ce qui permet aux pointeurs et aux boucles. Sans lui, impossible de parcourir un tableau.
Le pipeline — exécuter plusieurs instructions à la fois
L'idée est volée à l'industrie automobile : une chaîne de montage où chaque poste fait une tâche pendant que les autres font les leurs. Au lieu d'exécuter Fetch → Decode → Execute séquentiellement pour chaque instruction, on dédie un étage à chaque phase et on les fait tourner en parallèle.
Avec N étages, le pipeline ne divise pas le temps par N (la première instruction prend toujours N cycles), mais il porte le débit à 1 instruction par cycle en régime établi. Le C6x pousse l'idée à l'extrême : l'étage Fetch est décomposé en 4 sous-phases (PG, PS, PW, PR), Decode en 2 (DP, DC), et Execute peut compter jusqu'à 10 phases (E1…E10). On y revient en détail au chapitre IV.
Architecture des DSP TMS320C6x
Maintenant qu'on a les bases, on regarde ce que Texas Instruments a fait pour pousser un processeur au-delà de 1800 MFLOPS sur un signal audio en temps réel. La réponse tient en quatre mots : parallélisme architectural à tous les étages.
Pourquoi le parallélisme — SISD, SIMD, MIMD, VLIW
Un processeur classique exécute une instruction sur une donnée à la fois (SISD). Pour aller plus vite sans monter en fréquence — ce qui coûte cher en chaleur et en énergie — il faut faire plusieurs choses à la fois. Plusieurs stratégies existent :
| Modèle | Signifie | Principe |
|---|---|---|
| SISD | Single Instr, Single Data | Le µp classique. Une opération, une donnée, à la fois. |
| SIMD | Single Instr, Multiple Data | Une même opération appliquée en parallèle à plusieurs données. Idéal pour additionner deux vecteurs. |
| MIMD | Multiple Instr, Multiple Data | Plusieurs cœurs exécutant chacun son propre programme sur ses propres données. C'est ton CPU multicœur. |
| VLIW | Very Long Instruction Word | Un seul processeur, mais une instruction qui pointe vers plusieurs unités fonctionnelles en parallèle. |
VLIW — la clé du C6x
L'architecture VLIW est la spécificité du TMS320C6x. Au lieu de demander au CPU de découvrir dynamiquement quelles instructions peuvent s'exécuter en parallèle (ce que font les CPUs « superscalaires » comme x86), VLIW délègue ce travail au compilateur. Le compilateur regroupe lui-même les instructions parallélisables en un mot long, et le matériel les distribue mécaniquement.
Avantages VLIW
- Parallélisme fin
- Unités indépendantes
- Hardware simple (pas de découverte dynamique)
- Bonne prédictibilité temporelle
Inconvénients
- Bande passante mémoire élevée (instructions plus longues)
- Code binaire plus volumineux
- Très dépendant de la qualité du compilateur
Exemple d'un mot d'instruction VLIW : ADD R1,R2,R3 | MUL R4,R5,R6 | LOAD R7,[R8] — trois opérations indépendantes, exécutées en un seul cycle par trois unités fonctionnelles différentes.
Familles TI & l'architecture VelociTI
Texas Instruments commercialise plusieurs familles de DSP, classées par arithmétique (virgule fixe / flottante) et largeur de registres :
| Famille | Arithmétique | Usage typique |
|---|---|---|
| C1x, C2x, C2xx, C54x | Virgule fixe 16 bits | Fax, contrôle embarqué, téléphones cellulaires |
| C3x, C4x | Virgule flottante 32 bits | Visiophones, traitement parallèle |
| C6x | Selon sous-famille | Stations de base, hautes performances |
| C62x, C64x | Virgule fixe | Vidéo, télécom |
| C67x (dont C6713) | Virgule flottante | Audio pro, imagerie médicale |
| C674x | Fixe + flottante | Fusion C67x et C64x |
Tous les C6x partagent la même architecture de base baptisée VelociTI, dérivée du VLIW classique mais modifiée pour autoriser plusieurs paquets d'exécution dans un seul paquet de lecture — souplesse qu'on détaille en §4.6.
Deux chemins de données symétriques
Le CPU se divise en deux moitiés (DP1 et DP2), chacune contenant :
- 16 registres 32 bits (A0–A15 pour DP1, B0–B15 pour DP2).
- 4 unités fonctionnelles spécialisées :
.L— ALU (arithmétique & logique).S— Décalages, branchements, manipulation de bits, ALU.M— Multiplication.D— Load/Store : mouvement de données mémoire ↔ registres
Soit 8 unités fonctionnelles au total (L1, S1, M1, D1, L2, S2, M2, D2). Le CPU peut donc lancer jusqu'à 8 instructions en parallèle par cycle.
Mot d'instruction = 8 × 32 bits = 256 bits par cycle. L'opération signature du DSP, le MAC (Multiply-Accumulate), nécessite trois étapes : .D charge la donnée, .M multiplie, .L additionne — et le C6x peut faire tout cela en un seul cycle grâce aux unités indépendantes. C'est ce qui rend le filtrage RIF temps réel possible.
TMS320C6713 — anatomie complète
Le C6713 est le membre virgule flottante de la famille C67x. C'est lui qu'on programme typiquement dans les TPs.
Spécifications clés
| Caractéristique | Valeur |
|---|---|
| Fréquence d'horloge | 300 MHz |
| Performance entière | 2400 MIPS (8 instr × 300 MHz) |
| Performance flottante | 1800 MFLOPS |
| Unités fonctionnelles | 2 ALU fixe (D1, D2), 4 ALU fixe/flot (L1, L2, S1, S2), 2 MUL fixe/flot (M1, M2) |
| Mémoire interne cache | 256 KB |
| Mémoire externe adressable | 1 GB |
| Conformité virgule flottante | IEEE 754 |
Hiérarchie mémoire & cartographie
Cache à deux niveaux
- L1P — 4 KB, cache programme de niveau 1.
- L1D — 4 KB, cache données de niveau 1, set-associative à 2 voies.
- L2 — 256 KB, partagé programme + données, organisé en :
- 64 KB de cache (divisé en 4 blocs de 16 KB)
- 192 KB de SRAM
La répartition L2 cache/SRAM est configurable via le registre L2CFG. C'est rare et puissant : tu peux dédier plus de SRAM si tu veux contrôler manuellement le placement, ou plus de cache si tu fais confiance au hardware.
Espace adressable — 4 GB
Le bus d'adresses fait 32 bits, donc 2³² = 4 GB théoriques. Cartographiés ainsi :
| Plage | Région | Taille |
|---|---|---|
0x0000 0000 → 0x0003 FFFF | Mémoire interne L2 | 256 KB |
0x0004 0000 → 0x7FFF FFFF | Espace mixte : registres périphériques (Timer, McBSP, HPI, GPIO…) + zones réservées | ~2 GB |
0x8000 0000 → 0xBFFF FFFF | Mémoire externe (EMIF), 4 blocs de 256 MB : CE0, CE1, CE2, CE3 | 1 GB |
0xC000 0000 → 0xFFFF FFFF | Réservé | 1 GB |
Les unités fonctionnelles L, S, M, D
Chaque chemin de données contient quatre spécialistes. Voici leur fiche de poste précise :
| Unité | En virgule fixe | En virgule flottante |
|---|---|---|
| .L (L1, L2) Arithmétique & logique | Arithmétique, comparaison, opérations logiques 32/40 bits | Conversions INT→SP, INT→DP, DP→SP |
| .S (S1, S2) Décalages, branches | Décalages 32/40 bits, branchements, génération de constantes, arithmétique 32 bits | Réciproque, valeur absolue, racine carrée ; conversion SP→DP ; comparaisons, add/sub en SP/DP |
| .M (M1, M2) Multiplication | 16×16 bits, 32×32 bits | Multiplication SP & DP |
| .D (D1, D2) Load/Store & adressage | Génération d'adresse 32 bits (linéaire ou circulaire), load/store avec offset 5 bits (15 bits sur .D2), arithmétique 32 bits | Idem (transferts de réels) |
Tous les transferts mémoire (load et store) doivent passer par .D1 ou .D2. C'est la seule porte d'entrée/sortie vers la mémoire. La FPU n'existe pas dans tous les modèles — par exemple le C6211 n'en a pas.
Fetch Packet (FP) & Execute Packet (EP)
VelociTI raffine le VLIW. Avant lui, un mot VLIW était aussi un paquet d'exécution. C'était rigide : si tu ne pouvais pas paralléliser 8 instructions, tu gaspillais des slots avec des NOP.
VelociTI dissocie deux notions :
- Fetch Packet (FP) — l'unité de lecture mémoire. Toujours 8 instructions de 32 bits = 256 bits.
- Execute Packet (EP) — un sous-ensemble d'instructions du FP qui s'exécutent réellement en parallèle.
Un FP contient au minimum 1 EP (8 instr en parallèle → cas idéal) et au maximum 8 EP (8 instructions séquentielles, comme si pas de parallélisme).
Le bit P — le marqueur de fin de paquet
Comment le hardware sait-il qu'un EP se termine ? Le LSB de chaque instruction (le bit P, noté ||) le dit :
- bit P = 1 → l'instruction suivante fait partie du même EP (parallèle).
- bit P = 0 → l'instruction suivante démarre un nouvel EP (séquentiel).
La dernière instruction d'un FP a toujours bit P = 0 (un EP ne peut pas déborder sur le FP suivant).
Dans un même EP, chaque instruction doit utiliser une unité fonctionnelle différente. Sinon collision matérielle. C'est au compilateur (ou à toi, en assembleur) de garantir ça.
Le pipeline C6x — Fetch (4), Decode (2), Execute (1 à 10)
On revient au pipeline, mais cette fois à l'échelle du C6x. Au lieu des 3 étages génériques F/D/E, chaque étage est décomposé en sous-phases, pour maximiser le débit.
Étage Fetch — 4 phases
| Sigle | Phase | Action |
|---|---|---|
| PG | Program address Generation | Le CPU calcule l'adresse du prochain FP. |
| PS | Program address Send | L'adresse est envoyée vers la mémoire. |
| PW | Program address Wait | Attente que la mémoire ait fini la lecture. |
| PR | Program Packet Receive | Le FP arrive au CPU. |
Étage Decode — 2 phases
| Sigle | Phase | Action |
|---|---|---|
| DP | Dispatch Phase | Chaque instruction de chaque EP est envoyée vers son unité fonctionnelle. |
| DC | Decode Phase | Les opérandes sont décodés (registres sources/destinations). |
Étage Execute — 1 à 10 phases
L'exécution se déroule en une phase E1 pour la plupart des instructions en virgule fixe (latence = 1 cycle). Mais certaines opérations sont plus lentes et bloquent leur unité pendant plusieurs phases :
| Instruction | Phases | Délai (cycles à insérer) |
|---|---|---|
Multiplication (MPY) | E1–E2 | 1 |
Load (LDH, LDW) | E1–E5 | 4 |
Branch (B) | E1–E6 | 5 |
Le nombre total de phases peut donc atteindre 6 en virgule fixe (E1…E6) et 10 en virgule flottante (E1…E10).
Quand tu écris en assembleur, tu dois insérer des NOP après les instructions à latence. Par exemple, après un MPY : 1 NOP. Après un LDH : 4 NOP. Après un B : 5 NOP. Sinon, l'instruction suivante essaiera d'utiliser un résultat pas encore disponible — comportement indéfini. Le compilateur C s'en occupe automatiquement, mais en assembleur c'est ta responsabilité.
Registres & chemins croisés (1X, 2X, T1, T2)
32 registres généraux
Deux files de 16 registres 32 bits chacune : A0–A15 pour le chemin A, B0–B15 pour le chemin B. Chaque registre peut servir indifféremment :
- De donnée —
ADD .L1 A1, A2, A3→ A3 = A1 + A2 - De pointeur d'adresse —
LDW .D1 *A4++, A5→ charge ce que pointe A4 dans A5, puis incrémente A4 - De condition —
[B0] B label→ branche àlabelsi B0 ≠ 0
Largeurs supportées
Un registre peut traiter des données de plusieurs tailles :
- 16 bits (SHORT) — paquet d'octets
- 32 bits (INT ou SP — simple précision flottante)
- 40 bits (LONG) — usage interne, paires de registres
- 64 bits (Double LONG ou DP — double précision flottante)
Pour 40 ou 64 bits, on utilise une paire de registres : LSB dans un registre d'index pair, MSB dans l'index impair suivant. Combinaisons valides : A1:A0, A3:A2, A5:A4… et B1:B0, B3:B2, B5:B4… etc.
Usages spéciaux
- A0, A1 et B0, B1, B2 — peuvent être utilisés comme registres de condition pour les instructions conditionnelles. Exemple :
[B1] B .S1 Loop(saute à Loop si B1 ≠ 0). - A4–A7 et B4–B7 — utilisés pour l'adressage circulaire.
Chemins croisés 1X et 2X
Par défaut, les unités du chemin A ne peuvent lire que les registres A, et symétriquement pour B. Mais TI a ajouté deux ponts (cross paths) pour souplesse :
- 1X — autorise L1, S1, M1 à lire les registres B.
- 2X — autorise L2, S2, M2 à lire les registres A.
Limite : une seule lecture croisée par chemin par cycle (donc 2 instructions en parallèle utilisant les cross paths, maximum).
Exemple : MPY .M1X A5, B5, A5 — le suffixe X signale qu'on traverse le chemin croisé pour lire B5.
Chemins croisés mémoire T1 et T2
Sur le côté mémoire, il existe deux bus d'adresses : DA1 (T1) et DA2 (T2). Cela permet de charger d'un côté tout en stockant de l'autre, dans le même cycle. Exemple :
LDW .D1T1 *A0, B5 ; charge *A0 dans B5 via T1
|| STW .D2T1 A5, *B5 ; stocke A5 à l'adresse pointée par B5 via T1
Registres de contrôle — CSR, PCE1, AMR
À côté des 32 registres généraux, le C6x possède une banque de registres de contrôle qui pilotent le comportement global du processeur. Accès uniquement via l'unité .S2, avec l'instruction spécialisée MVC (Move Control).
PCE1 — Program Counter phase E1
Contient l'adresse du FP actuellement en phase E1 du pipeline. Utile pour le debug et la gestion des exceptions.
MVC .S2 PCE1, A0 ; copie l'adresse courante dans A0
CSR — Control Status Register
Registre 32 bits qui regroupe plusieurs flags critiques :
- PWRD — Power Down mode
- SAT — Saturation après opération arithmétique
- EN — Endianness (Big- ou Little-Endian)
- PCC / DCC — Program / Data Cache Control
- GIE / PGIE — Global Interrupt Enable (et son état précédent)
Big-Endian
Le MSB est stocké à l'adresse basse. La valeur 0xAABBCCDD se lit en mémoire comme AA BB CC DD.
Little-Endian
Le LSB est stocké à l'adresse basse. La valeur 0xAABBCCDD se lit en mémoire comme DD CC BB AA.
AMR — Addressing Mode Register
Spécifie pour chacun des registres A4–A7 et B4–B7 s'ils opèrent en mode linéaire ou circulaire, et contient la taille du tampon circulaire (BK) avec la formule : taille = 2^(N+1) octets, N étant codé sur 5 bits → tailles de 2 à 4096 octets. On peut sélectionner deux tailles différentes : BK0 et BK1.
Périphériques & système d'interruptions
Communications & interconnexions
| Sigle | Nom complet | Fonction |
|---|---|---|
| EMIF | External Memory Interface | Pont vers la mémoire externe (4 zones CE0–CE3). |
| DMA / EDMA | (Enhanced) Direct Memory Access | Déplace des données sans intervention du CPU — opération en arrière-plan. |
| McBSP | Multichannel Buffered Serial Port | Port série synchrone full-duplex, double-buffered. Interface directe avec codecs et CAN/CNA. |
| McASP | Multichannel Audio Serial Port | Version audio (I²S et variantes). |
| HPI | Host Port Interface | Port parallèle : un processeur « hôte » peut accéder directement à la mémoire du DSP. |
| I²C | Inter-Integrated Circuit | Bus série standard. |
| GPIO | General Purpose I/O | Interface parallèle universelle. |
Services système
- Timers 32 bits — mesure de durée, comptage d'événements, génération d'impulsions et d'interruptions, synchronisation du DMA. Source d'horloge interne (Fosc/4) ou externe via le pin TINP, sortie via TOUT.
- PLL Controller — synchronise les horloges des périphériques.
- Power Down — mise hors tension quand le CPU est inactif.
- Boot Configuration — choix du mode de démarrage (mémoire externe, HPI, etc.).
Le système d'interruptions
Une interruption force le CPU à abandonner sa tâche pour exécuter une routine appelée ISR (Interrupt Service Routine). Le C6713 propose 16 sources d'interruption : 2 timers, 4 IRQ externes, 4 du McBSP, 4 du DMA. Un sélecteur les multiplexe vers 12 entrées CPU (INT4 à INT15) — plus RESET et NMI.
Chaque interruption a une adresse de vecteur espacée de 0x20 (32 octets, soit un FP) à partir de l'ISTB (Interrupt Service Table Base). Par exemple, INT15 est à ISTB+0x1E0. On y place typiquement un B ISR15 + 5 NOP (à cause du délai de branch), puis la vraie routine se trouve à ISR15.
Programmation des DSP TMS320C6x
On passe à la pratique. Le C6713 se programme en C, en assembleur, ou en linear assembly (un intermédiaire). Pour exploiter pleinement le parallélisme et le pipeline, comprendre l'assembleur reste indispensable — c'est lui qui te donne la clé sur les unités, les EP et les NOP.
Flot de compilation
Trois portes d'entrée vers le binaire :
- .c — code C, compilé puis assemblé. Productif, portable, mais le compilateur peut laisser des slots inutilisés.
- .asm — assembleur natif. Contrôle total, mais tu gères toi-même les latences et les unités.
- .sa — linear assembly. Style assembleur mais sans devoir choisir les unités et les latences ; un optimiseur s'en occupe. Souvent le meilleur compromis.
Toutes ces sources convergent vers un fichier exécutable .out.
Modes d'adressage du C6713
Deux familles sur l'architecture C6713 :
Adressage indirect / linéaire
Disponible sur tous les registres. Quatre variantes selon comment le pointeur est mis à jour :
| Syntaxe | Sémantique |
|---|---|
*R | Le registre R contient l'adresse. Pas de modification. |
*R++(d) | Post-incrément : on utilise R comme adresse, puis R ← R + d. (d=1 par défaut) |
*++R(d) | Pré-incrément avec modification : R ← R + d, puis on utilise comme adresse. |
*+R(d) | Pré-incrément sans modification : on utilise (R + d) comme adresse, mais R reste inchangé. |
Adressage circulaire — le killer feature DSP
Disponible seulement sur A4–A7 (via .D1) et B4–B7 (via .D2). Le hardware traite le pointeur modulo une taille de buffer : dès qu'il atteint la fin, il revient automatiquement au début. Zéro test logiciel.
Un filtre RIF d'ordre 8 nécessite les 8 échantillons les plus récents : x[n], x[n-1], …, x[n-7]. À chaque nouvel échantillon, on doit « décaler » le buffer : copier toutes les valeurs d'une case ? Trop coûteux. La solution : un buffer circulaire où le nouveau pointeur écrit « par-dessus » le plus ancien échantillon, qui devient ainsi le plus récent. Le hardware C6x fait cela en un cycle, sans branch, sans test. C'est cette opération qui rend possible le filtrage temps réel à plusieurs MHz.
Le format des instructions assembleur
Une ligne d'assembleur C6x suit cette structure (jusqu'à 7 champs) :
Label: || [condition] Instruction .Unit Operands ; commentaire
| Champ | Optionnel ? | Rôle |
|---|---|---|
| Label | Oui | Étiquette d'adresse symbolique (cible de branchement). |
| || | Oui | Indique que l'instruction s'exécute en parallèle avec la précédente (même EP). |
| [cond] | Oui | Exécution conditionnelle. Registres autorisés : A1, A2, B0, B1, B2. [A2] = si A2≠0 ; [!A2] = si A2=0. |
| Instruction | Non | Soit une mnémonique (ADD, MPY…), soit une directive (.word, .data…). |
| .Unit | Oui | Choix de l'unité fonctionnelle (.L1, .S2, etc.). Si omis, l'assembleur choisit. |
| Operands | Selon instr. | Registres, constantes, pointeurs. |
| ; comment | Oui | Commentaire jusqu'en fin de ligne. |
Note : l'assembleur C6x n'est pas case-sensitive. ADD ≡ add ≡ Add.
Exemples d'instructions usuelles
Arithmétique
ADD .L1 A1, A2, A3 ; A3 = A1 + A2
SUB .L2 B1, B2, B3 ; B3 = B1 − B2
MPY .M1 A4, A5, A6 ; A6 = A4 × A5 (16×16 bits)
Load / Store
LDH .D2 *B2++, B7 ; B7 ← mem[B2] (16 bits), B2 ← B2+2
|| LDH .D1 *A2++, A7 ; en parallèle : A7 ← mem[A2], A2++
STW .D1 A3, *A7 ; mem[A7] ← A3 (32 bits)
LDW *++A4(20), A1 ; pré-incr de 20 mots (80 bytes), charge A1
LDH charge un half-word (16 bits). LDW charge un mot complet (32 bits) — il occupe simultanément les deux chemins D1 et D2.
Branchement & Move
MV .L1 A1, A2 ; A2 ← A1 (copie)
MVK .S1 42, A1 ; A1 ← 42 (16 bits bas)
MVKH .S1 0, A1 ; charge les 16 bits hauts (ici 0)
[B1] B .S1 Loop ; saute à Loop si B1 ≠ 0
Notes : une constante 32 bits ne peut pas être chargée d'un coup ; on utilise MVK (16 bits bas) suivi de MVKH (16 bits hauts).
Exemple complet — programme de produit scalaire
On veut calculer somme = Σ c[i] × x[i] pour i = 0…9, avec deux vecteurs entiers signés. Voici le squelette :
; --- Section données ---
.data
result: .word 0 ; mot 32 bits initialisé à 0
; --- Section code ---
.text
.global main
main:
MVK .S1 42, A1 ; A1 = 42 (LSB)
MVKH .S1 0, A1 ; A1 = 0x0000_002A
MVK .S2 58, A2
MVKH .S2 0, A2 ; A2 = 58
ADD .L1 A1, A2, A3 ; A3 = 100
MVKL .S1 result, A4 ; adresse de 'result' (16 bits bas)
MVKH .S1 result, A4 ; 16 bits hauts
STW .D1 A3, *A4 ; *result = A3
B .S2 $ ; boucle infinie ($ = adresse courante)
NOP 5 ; 5 NOP requis après branch
Quand tu écris en assembleur, tu dois compenser les latences explicitement. Récapitulatif des règles pour le C6713 :
- 1 NOP après une
MPY - 4 NOP après une
LDHouLDW - 5 NOP après une instruction
B
À tout instant, jusqu'à 16 instructions successives peuvent occuper les trois étages du pipeline simultanément.
Directives d'assembleur
Les directives ne sont pas des instructions du CPU — elles s'adressent à l'assembleur pour structurer le fichier source.
Sections
| Directive | Rôle |
|---|---|
.sect "name" | Crée une section nommée (données ou code). |
.data | Section de données initialisées. |
.text | Section de code exécutable. |
.space N | Réserve N octets vides. Ex : buffer .space 128 |
Symboles
| Directive | Rôle | Analogie C |
|---|---|---|
.set | Définit une valeur constante. count .set 40 remplace count par 40 partout. | #define |
.def | Exporte un symbole pour qu'il soit visible des autres fichiers. | déclaration globale |
.ref | Importe un symbole défini ailleurs. | extern |
Initialisation de constantes
| Directive | Type |
|---|---|
.short | Entier 16 bits |
.int / .word / .long | Entier 32 bits |
.float | Réel 32 bits simple précision IEEE 754 |
.double | Réel 64 bits double précision IEEE 754 |
Le compromis Linear Assembly
Le linear assembly (extension .sa) est un langage intermédiaire :
- Syntaxe proche de l'assembleur classique.
- Mais : tu utilises des noms symboliques de variables, pas les registres physiques.
- Tu ne précises ni les unités fonctionnelles, ni les NOP de latence.
- Un optimizer fait le placement et la planification.
Tu obtiens un code plus efficace qu'en C, plus rapide à écrire qu'en assembleur. Le meilleur des deux mondes quand tu n'as pas le temps d'optimiser à la main.
DSK C6713 — la carte de développement
La théorie incarnée. Le DSK est le kit officiel TI pour mettre les mains dans le silicium. Comprendre ses composants, c'est savoir où plugger un signal et comment le lire après traitement.
La plateforme DSK C6713
Le DSK (DSP Starter Kit) est une carte de développement autonome, bon marché, conçue par TI pour qu'on puisse évaluer et développer rapidement des applications C67xx. C'est la référence matérielle pour le C6713.
Composants principaux
- DSP TMS320C6713 cadencé à 225 MHz (au lieu de 300 MHz max — pour la robustesse industrielle de la carte).
- Codec stéréo AIC23 — entrée/sortie audio.
- 16 MB de DRAM synchrone (mémoire de travail).
- 512 KB de mémoire FLASH non volatile (256 KB utilisables par défaut).
- 4 LEDs et 4 commutateurs DIP accessibles par l'utilisateur — typique pour signaler l'état ou changer le mode d'exécution.
- Configuration logicielle via registres CPLD.
- Options de boot configurables.
- Connecteurs d'extension pour cartes filles.
- Émulateur JTAG embarqué avec interface USB hôte (debug intégré, plus besoin de boîtier externe).
Codec AIC23, McBSP, HPI, EDMA
AIC23 — la passerelle audio
Le codec AIC23 est ce qui permet de faire du DSP utile : il convertit l'audio analogique en numérique (CAN) et inversement (CNA). Il dialogue avec le DSP via le port série McBSP. C'est ce composant qui te donne accès à des entrées Line-in / Mic, et des sorties Line-out / Headphone.
McBSP — le canal série synchrone
Le Multichannel Buffered Serial Port :
- Full-duplex (lecture et écriture simultanées).
- Registres de données double-buffered — pendant qu'un échantillon est traité, le suivant est déjà reçu.
- Horloges et signaux d'encadrage indépendants pour TX et RX.
- Interface directe avec les codecs et autres composants A/N et N/A connectés en série.
HPI — la porte d'entrée d'un hôte
Le Host Port Interface est un port parallèle qui permet à un processeur hôte (typiquement un microcontrôleur de la carte mère, ou un PC) d'accéder directement à la mémoire du DSP. Pratique pour charger un programme sans passer par JTAG, ou échanger des données à haut débit.
EDMA — la mémoire qui se déplace toute seule
L'Enhanced Direct Memory Access transfère des blocs de données entre régions de la memory map sans intervention du CPU. Tu programmes la source, la destination, la taille — le DMA fait le travail en parallèle. Pendant ce temps, le CPU peut continuer à calculer.
Pour un filtre audio temps réel sur DSK C6713 : l'AIC23 numérise un échantillon → McBSP le pousse en mémoire → EDMA accumule un buffer de N échantillons → quand le buffer est plein, il déclenche une interruption → le CPU lance la routine de filtrage → le résultat repart par le chemin inverse. Le CPU ne touche jamais aux opérations d'I/O. Toute sa puissance reste dédiée au calcul. C'est l'élégance d'une architecture DSP bien conçue.
Cheatsheet — à retenir pour l'examen / le TP
Si tu n'as que 5 minutes avant l'épreuve, lis ceci.
Les 10 points qui résument tout
- Harvard ≠ Von Neumann : bus séparés programme/données → 2 accès simultanés. Tous les DSP sont en Harvard (modifié).
- RISC + VLIW : instructions courtes & uniformes, et un mot d'instruction qui pilote plusieurs unités en parallèle. C'est la combinaison gagnante du C6x.
- C6713 = 8 unités fonctionnelles : L1, S1, M1, D1 sur le chemin A ; L2, S2, M2, D2 sur le chemin B. Chacune sa spécialité : L=ALU, S=shift/branch, M=mul, D=load/store.
- 32 registres : A0–A15 et B0–B15, 32 bits chacun. Cross paths 1X/2X permettent à une unité de lire un registre de l'autre côté (limité à 1 traversée par chemin par cycle).
- Mémoire : L1P 4KB + L1D 4KB + L2 256KB (mixte). Espace adressable 4 GB.
- FP & EP : un Fetch Packet = 8 instr × 32 bits = 256 bits ; il contient 1 à 8 Execute Packets. Le bit P (LSB) marque la fin d'un EP.
- Pipeline en 3 étages, mais décomposé : Fetch (PG, PS, PW, PR) → Decode (DP, DC) → Execute (E1…E6 fixe, E1…E10 flottant).
- NOP obligatoires en assembleur : 1 après MPY, 4 après LDH/LDW, 5 après B.
- Adressage circulaire sur A4–A7 et B4–B7 : parfait pour buffers de filtres RIF. Géré en hardware, taille définie par AMR.
- Périphériques clés : EMIF (mémoire externe), DMA/EDMA (transferts autonomes), McBSP (série, audio), HPI (accès parallèle hôte), Timers, 16 sources d'interruption multiplexées vers INT4–INT15.
Si tu veux vraiment internaliser tout ça, un projet de filtrage audio temps réel sur DSK C6713 — par exemple un égaliseur 3 bandes ou un effet d'écho — te forcera à toucher chaque couche : AIC23 → McBSP → buffer circulaire → unité M pour MAC → unité D pour load/store → IRQ. Tu écris d'abord en C, puis tu optimises le hot loop en linear assembly, puis tu compares les performances. C'est exactement ce qu'on demande à un ingénieur embarqué sénior : comprendre la pile entière et savoir où optimiser. À ajouter au portfolio GitHub avec mesures avant/après et oscillogrammes — c'est le genre de projet qui parle aux recruteurs en embedded.