Jeu d`instruction
Transcription
Jeu d`instruction
DSP Plan Classification circuits Présentation générale du domaine DSP Le choix du DSP L’architecture interne du 21061 Jeu d’instruction Registres d’état du DSP Accès mémoire et pipeline Les interruptions Assembleur Sharc I. Classification circuits 1) Circuits numériques a. Qu’est ce qu’un circuit ? Définition : Un agencement de transistors qui groupés par composants réalisent une fonctionnalité particulière. 3 niveaux de description : C’est un dessin plannaire (layout, ou vue physique) C’est un réseau de transistors (vue structurelle ou netlist). La description est presque toujours hiérarchique. Vues comme des boîtes noires : encapsulation. C’est un comportement (vue fonctionnelle) Niveau composant: comportement numérique Couche physique Niveau portes: comportement logique Niveau transistor: comportement électrique Portes INV_ OR_ AND_ NAND_ NOR_ XOR_ MSI LSI MAJ_ PLA_ MUX_ ALU_ ALU_ DEC_ COMP_ HalfAdder_ HalfAdder_ ADDER_ Fonction MAJ_3 en PLA s = abc + abc + abc + abc abc abc abc b. Flot de conception d’un circuit Description textuelle ou graphique Compilation, vérification Simulation fonctionnelle Placement routage, simulation temporelle Programmation circuit Vérification Ex: VHDL, Verilog, SystemC-RTL 2. Les types de circuit Les différents types de circuits Cibles Processeurs Généralistes Circuits Spécifiques Classiques µ-contrôleurs DSP … … … Programmables ASIP FPGA … Dédiés ARD ASIC … Les différents types de circuits Cibles Processeurs Généralistes Circuits Spécifiques Classiques µ-contrôleurs DSP Pentium PowerPC Alpha Mips … 68705 68HC11 80C51 … ADSP21xx TMS320xx DSP56xx … Program mables ASIP FPGA Xilinx Altera Atmel … Dédiés ARD ASIC Full Custom Standard Cell Gate Array … Les sigles FPGA : Field Programmable Gate Array DSP : Digital Signal Processor ASIP : Application Specific Integrated Processor ASIC : Application Specific Integrated Circuit ARD : Architecture Reconfigurable Dynamiquement Les différences Adresse Circuit Données ASIC FPGA (circuit programmable) PROC I - Présentation générale Flexibilité Vs performances Flexibilité Proc. généraliste Coût de conception µ. Contrôleur DSP ASIP Performances-1 Idem avec l’efficacité énergétique Générations de DSP La R&D autour des DSP en France II – Le choix des DSP Comparaison DSP / Proc généraliste Sharc Pentium critère 60-66 MHz 2-3 GHz Fréquence 1 cycle/inst 2-3 cycles/inst Add 1cycle/inst 1-10 cycles/inst Mul 1 cycle/inst 20* plus vite = 20*60 = 1,2GHz ~20 cycles/inst (MMX) MAC : Lecture échantillon Lecture coef filtre Mul ACC Calcul prochaine @ Comparaison DSP / Proc généraliste Sharc Mem on Chip Pentium critère Bus système Bus + Mem 100-166MHz (Pentium = 100MHz) Cache : On-chip PCI = 66 MHz ISA = 8 MHz Unités de calcul // … FIR 3 cycles/inst Questions… Pourquoi le DSP n’a-t-il pas alors remplacé les CISC ? Embarquabilité Compatibilité Performance limité sur applis générales Espace adressable réduit sur DSP Futur du DSP : TMS 320C54 ou BlackFin = DSP + RISC Harvard / Von Neuman Les accès mémoire sont découplés Harvard : Accès instructions Accès données 2 accès // par instruction 2 fois + rapide Au prix d’une architecture beaucoup plus complexe : tout est doublé Von Neumann Accès mémoire séquentiels Plus flexible Archi plus simple III – Architecture Sharc Les concurrents Texas Instruments Motorola TMS320C54x, TMS320C6x… De 20 à 400 MIPS en mono core Plus de 2000 MIPS en multi core Plus souvent en virgule fixe 16 bits DSP 56x00 Analog Device 1er DSP, 1980, ADSP-2100 3 unités de calculs // : instructions multi-fonctions 1990, famille Sharc (Super Harvard Architecture) 2106x, 66MFLOPS Caractéristiques de l’architecture Sharc Architecture de type Harvard (bus Data 40 bits + Bus Program 48 bits) Mémoire On-Chip de 0,5Mmots de Cache 32 instructions 16 registres de données (40 bits) Structure pipeline à 3 étages (F + D + E) Parallélisme d’instructions (sous certaines conditions) Unité de calcul avec multiplieur, additionneur en virgule fixe ou flottante et registre à décalage 2 unités de génération d’adresses (DAG) avec buffers circulaires (convolutions) et adressage « bit-reversed » (FFT) Gestion câblée des boucles Vue système Architecture interne du 21061 CACHE MEMORY 32 x 48 DAG 1 8 x 4 x 32 DAG 2 8 x 4 x 24 JTAG TEST & EMULATION PROGRAM SEQUENCER PMA BUS 24 DMA BUS 32 PMD BUS 48 DMD BUS 40 FLAGS TIMER PMA DMA PMD BUS CONNECT FLOATING & FIXED-POINT MULTIPLIER, FIXED-POINT ACCUMULATOR DMD REGISTER FILE 16 x 40 32-BIT BARREL SHIFTER FLOATING-POINT & FIXED-POINT ALU Mémoire Organisation mémoire Memoire interne Mémoire partagée (0x0000 0000 -> 0x0007 FFFF) Registres du proc IO registers 2 blocs 0 et 1 de 0,5 Mbits = 16K*32 bits (0x0008 0000 => 0x003F FFFF) Bus externe = 6 Sharc Mémoire externe (0x0040 0000 => 0xFFFF FFFF) Memory Map Mémoire interne 256 registres internes 0x 0000 0000 Réservé 0x 0000 0100 Bloc 0 (8K x 48 ou 16K x 32) Adressage normal 0x 0002 0000 Bloc 1 (8K x 48 ou 16K x 32) Adressage normal 0x 0002 4000 Alias Bloc 1 Adressage normal 0x 0002 8000 Bloc 0 (32K x 16) Adressage court 0x 0004 0000 Bloc 1 (32K x 16) Adressage court 0x 0004 8000 Alias Bloc 1 Adressage court 0x 0005 0000 Mémoire partagée (Multiprocesseurs) 0x 0008 0000 Mémoire externe 0x 0040 0000 Accès par taille de mots variables Harvard modifiée : chaque bloc peut contenir code et données Organisation mémoire Mémoire en 2 blocs Bloc = 8 colonnes de 4K mots de 16 bits Un accès mot de 16, 32, 40 ou 48 bits 1 instruction = 48 bits Accès 40/48 bits Accès 32 bits Accès 16 bits Accès 32 bits Une donnée de 32 bits est stockée sur 2 colonnes => 16 K mots de 32 bits par bloc 8 colonnes utilisables 0x 2 1000 0x 2 0000 0x 2 0FFF H L H L 0x 2 5000 0x 2 4000 0x 2 4FFF 0x 2 1FFF H L 0x 2 5FFF 0x 2 3000 0x 2 2000 0x 2 2FFF H L 0x 2 7000 0x 2 6000 H L 0x 2 6FFF 0x 2 3FFF H L 0x 2 8000 Accès 40 et 48 bits Les données de 40 bits sont stockées dans des mots de 48 bits cadrées sur les MSB 48 bits = 3 colonnes (mot H, M et L) => uniquement les 6 pemière colonnes remplies => 8K mots de 48 bits 0x 2 0FFF H M L 0x 2 1FFF L H M H M L 0x 2 5FFF unused unused 0x 2 5000 0x 2 4000 0x 2 4FFF unused 0x 2 1000 0x 2 0000 L H M unused Exemple en bloc B1 0 1 2 3 4 5 6 7 140A 0004 0000 7FA1 1301 0002 A52D 05DF 142A 0000 0020 014B 063E 0002 BC45 69C3 suite Accès 48 bits : 0x 2 4000 0x 2 5001 Accès 32 bits : 0x 2 4000 0x 2 5001 140A 0004 000 063E 0002 014B 140A 0004 0020 014B Accès 16 bits : // Accès L et H identique à 32 0x 4 8000 0x 4 8001 0004 140A ‘Mapping’ de l’exécutable dans l’espace mémoire - segments MEMORY { isr_table { TYPE(PM RAM) START(0x00020000) END(0x000200cF) WIDTH(48) } seg_init { TYPE(PM RAM) START(0x000200d0) END(0x000200df) WIDTH(48) } b0_code { TYPE(PM RAM) START(0x000200e0) END(0x00020fff) WIDTH(48) } b0_data { TYPE(PM RAM) START(0x00022800) END(0x00022cff) WIDTH(32) } b0_idat { TYPE(DM RAM) START(0x00022d00) END(0x00022fff) WIDTH(32) } b1_code { TYPE(PM RAM) START(0x00024000) END(0x00024Bff) WIDTH(48) } b1_data { TYPE(DM RAM) START(0x00024C00) END(0x00024Eff) WIDTH(32) } seg_heap { TYPE(DM RAM) START(0x00026000) END(0x000266ff) WIDTH(32) } seg_stak { TYPE(DM RAM) START(0x00026700) END(0x00026Aff) WIDTH(32)} } Résultat de l’édition de lien 0xCF28 0x0000 File2.o Image exécutable ‘loader’ section ‘loader’ section Code/data (file1.o) Code/data .text section .text section code .text (file1.o) .text (file2.o) .bss section data code 0x0000 File1.o .text section code .data section Init data Bibliothèque.lib .data section .data (file1.o) .data (file2.o) .bss section .bss (file1.o) .bss (file2.o) .data section Init data .bss section data }} Sections de code ROCESSOR p0{ LINK_AGAINST( $COMMAND_LINE_LINK_AGAINST) // Other object files to link against. OUTPUT( $COMMAND_LINE_OUTPUT_FILE ) // Resulting executable file name. SECTIONS{ interupt_vector_table { INPUT_SECTIONS( $OBJECTS(seg_rth) $LIBRARIES(seg_rth)) } >isr_table seg_init { INPUT_SECTIONS( $OBJECTS(seg_init) $LIBRARIES(seg_init)) } >seg_init block0_program_code { INPUT_SECTIONS( dotprod_main.doj(seg_pmco) $LIBRARIES(seg_pmco)) } >b0_code block1_program_code { INPUT_SECTIONS( dotprod.doj(seg_pmco) dotprod_func.doj(pm_code1) dotprod_func.doj(pm_code2) dotprod_func.doj(pm_code3)) } >b1_code block0_data { INPUT_SECTIONS( $OBJECTS(seg_pmda) $LIBRARIES(seg_pmda)) } >b0_data block1_data { INPUT_SECTIONS( $OBJECTS(seg_dmda) $LIBRARIES(seg_dmda)) } >b1_data stackseg { ldf_stack_space = .; ldf_stack_length = MEMORY_SIZEOF(seg_stak); } > seg_stak heap { ldf_heap_space = .; ldf_heap_end = ldf_heap_space + MEMORY_SIZEOF(seg_heap) - 1; ldf_heap_length = ldf_heap_end - ldf_heap_space; } > seg_heap Bus d’adresse 3 bus : PM (programmes) Fetch 16Mmots 32 bits d’@ 32/40 bits de données Read/Wr 4Gmots IO 24 bits d’@ 48 bits de données (inst) DM (data) Accès // en 1 cycle sauf si même bloc 17 bits d’@ 48 bits de données PX Pont entre le bus PM et DM 6 contrôleurs DMA 128 Kmots Jeu d’instruction Modes d’adressage L’accès à la mémoire est utilisé avec : DM(adr) ou PM(adr) désigne l’accès mémoire par les bus DM/PM à l’adresse adr Adressage direct DM (0x24000) = R0; PM (0x24001) = R1; // accès par bus DM //accès par bus PM Adressage indirect : les DAGs Le Sharc dirpose de 2 jeux de registres DAG1 et DAG 2 (Data Adress Generator) permettant l’adressage indirect Dessin Chaque jeu de registre est doublé (shadows registres) DAG1 est relié au bus DM, 32 bits Les registres 0 à 7 sont donc de 32 bits DAG2 est relié au bus PM, 24 bits Les registres 8 à 15 sont donc de 24 bits Les DAGs en adressage indirect DAG1 (32 bits) I0 I1 I2 I3 I4 I5 I6 I7 M0 M1 M2 M3 M4 M5 M6 M7 L0 L1 L2 L3 L4 L5 L6 L7 B0 B1 B2 B3 B4 B5 B6 B7 registres d’index registres modifieurs registres longueur et base (buffers circulaires) registres principaux registres shadow Les DAGs DAG1 (32 bits) I0 I1 I2 I3 I4 I5 I6 I7 DAG2 (24 bits) I8 I9 I10 I11 I12 I13 I14 I15 M0 M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 M11 M12 M13 M14 M15 L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 L10 L11 L12 L13 L14 L15 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 registres principaux registres shadow registres principaux registres shadow DAGs Ix registres d’index Chaque registre Ix peut être associé à un offset sous forme D’un immédiat Ou d’un registre My (modificateur) 2 modes : Pré incrémenté sans modification de l’index Post incrémenté avec modification Exemple Post R0 = 0xA0A0A0A0; M6 = 2; I0 = 0x0002 4000; DM(I0, M6) = R0; // I0 est modifié après accès mémoire et devient 0x0002 4002 Pré R0 = 0xA0A0A0A0; I0 = 0x0002 4000; DM(0xFFFF FFFF, I0) = R0; // accès mémoire à l’@ 0x23FFF (+ (-1))et I0 reste à la valeur 0x0002 4000 Adressage circulaire Utilisation des registres Bx et Lx associés à un reg. Ix Bx = Base de buffer Lx = Longueur à initialiser à une valeur non nulle Lorsque Bx est initialisé, le Ix correspondant est automatiquement initialisé à la même valeur Ix est utilisé en indirect normal avec 2 restrictions : L’offset (My ou Imd) doit être inférieur à Lx Le mode pré incrémenté ne peut être utilisé Une interruptions indicative est généré au dépassement lorsque Ix est ramené circulairement Exemple Adressage de buffers circulaires : B0 = 0x00024000, //init. de B0 et I0 L0 = 8; //init. longueur du buffer M1 = 1; //init. modifieur R0 = DM(I0,M1); //si IO>B0+L0 alors // I0=B0 L’utilisation des buffers circulaires B0 L0 longueur et base du tableau I0 pointeur sur le tableau Les mécanismes de déplacement du pointeur, de test de la fin du tableau et de ré-initialisation sont câblés dans les DAG... Modes d’adressage Codes Conditions Jeu d’instruction du DSP Instructions de calcul En virgule fixe En virgule flottante Du multiplieur (utilisation de MRF et MRB) Du registre à décalage Instructions parallèles Instructions de rupture de séquence Instructions de déplacement avec @ immédiat Instructions diverses Architecture interne Les unités de calcul PM 48 bits DM 40/32 bits Register File Multiplier 16 x 40 bits Banc de registres 16 registres désignés par : De R0 à R15 en virgule fixe De F0 à F15 en virgule flottante Format désigné par le nom employé Les registres principaux sont doublés par des Shadows registers organisés en 2 blocs R0 à R7 R8 à R15 Chacun peut être actionné pour permettre des changements de contexte rapide. Activés (au bout de 2 cycles) par un bit du registre MODE1. Format de représentation des nombres Registres sur 40 bits : Les opérations entières sont sur 32 bits MSB Ignorant les 8 bits LSB en lecture Les forçant à 0 en écriture La lecture registre est réalisé en début de cycle L’écriture registre en fin de cycle Une instruction peut donc utiliser le même registre en entrée et en sortie Format (suite) Virgule Fixe Format Q1.31 (Virgule à droite du MSB) Dynamique [-1, +1[ Ou Q32.0 en entiers Virgule flottante IEEE/754 N = (-1)S.1,M.2E+127 M = 23 bits E = 8 bits Multiplieur Fonctionne en V. Fixe, Rx ou en Flottante, registres Fx ou MR Le type des opérandes est précisé dans l’instruction : modificateur XYZ X et Y désignent Signed ou Unsigned Z désigne le codage : Integer, Fraction, FR fraction avec arrondi Exemple : R2 = R0*R1 (SUF) Le résultat est sur 64 bits Registre MR du multiplieur Si le résultat 32 bits est écrit dans Rx, les 32 bits conservés sont : Les 32 bits LSB si les opérandes sont I Les 32 bits MSB si les opérandes sont F (Q1.31) 2 Accumulateurs MR sur 80 bits MRF (foreground) et MRB (background) Permutable par un bit de MODE1 Toujours accessibles contrairement aux shadows R. 3 parties : MR0 (32b), MR1(32b) et MR2(16b) Décaleur (figure en 8bits) Multi function instructions Séquenceur d’instructions Le pipeline instruction i instruction i+1 lecture décodage exécution lecture décodage exécution lecture instruction i+2 en permanence ! décodage exécution temps Pipeline Le sharc dispose d’un pipeline à 3 étages 3 instructions sont en cours de traitement 1 instruction se termine à chaque cycle Le fonctionnement idéal est perturbé lorsque : Une instruction N utilise le bus PM pour accéder à la mémoire. Il y a alors conflit avec la phase de fetch de l’instruction N+2. Le compilateur ajoute un cycle d’attente (NOP) L’instruction N provoque une rupture de séquence (JUMP, CALL). => Branchements retardés L’instruction N+1 adresse la mémoire avec un registre de DAG modifié par l’instruction N => 1 cycle d’attente exemple d’utilisation du cache 0x20000: ... 0x20001: R0 = PM(0x23013); // lecture sur bus PM 0x20002: R2 = R0 + R1; // opération arithmétique 0x20003: DM(0x24000) = R0; // écriture sur le bus DM 0x20004: ... 0x20005: ... exemple d’utilisation du cache (1ère occurrence) Execute Execute Decode Decode Execute Fetch Fetch Execute Decode Lecture data sur leDecode bus Fetch PM Fetch Decode Recherche sur le bus PM 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Les deux opérations nécessitent deux accès simultanés sur le même bus ! exemple d’utilisation du cache (1ère occurrence) .... .... Fetch + Mise en cache 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (1ère occurrence) Execute Decode .... 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (1ère occurrence) Execute Decode Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (1ère occurrence) Execute Decode Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (2ème occurrence) Execute Execute Decode Decode Execute Fetch Fetch Execute Decode Decode Fetch Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (2ème occurrence) Execute Decode Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (2ème occurrence) Execute Decode Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 exemple d’utilisation du cache (2ème occurrence) Execute Decode Fetch 0x20000: ... 0x20001: R0 = PM(0x23013); 0x20002: R2 = R0 + R1; 0x20003: DM(0x24000) = R0; 0x20004: ... 0x20005: ... Cache instructions : 00 0 01 0 03 1 DM(0x24000) = R0 Conclusion sur l’utilisation du cache Le pipeline entraîne l’ajout d’un cycle supplémentaire à chaque fois qu’un conflit d’accès au bus est détecté. La présence du cache d’instruction ne permet de résoudre le conflit qu’à la deuxième occurrence seulement. ⇒ Un code optimisé (en vitesse) ne contient de conflits de bus qu’à l’intérieur des boucles... problème 2 : Le cas des dépendances de données Lors de la phase de décodage de l’instruction, le processeur prépare l’accès aux données et calcule l’adresse des accès en mémoire. Il y a conflit de dépendance de données lorsqu’une instruction effectue un accès en mémoire avec un registre d’index (Ix) modifié lors de l’instruction précédente. Execute 0x20002: Decode 0x20003: 0x20004: I0 = 0x23013; // Init index I0 R2 = DM(I0,M0) // accès avec IO ... La modification de l’index I0 et le calcul de l’adresse avec I0 nécessiteront deux cycles au lieu d’un ! problème 3 : Le cas des ruptures de séquence Execute R0 = R1 + R2; Execute Decode R2 = lshift R2 by 12; Decode Execute Fetch CALL Fetch !! !! routine; routine: R5 = 1; R5 = 0; R9 = min(R5, R2); I8 = tableau; ... ... problème 3 : Le cas des ruptures de séquence R0 = R1 + R2; R2 = lshift R2 by 12; CALL NOP !! routine; routine: Fetch R5 = 1; R5 = 0; R9 = min(R5, R2); I8 = tableau; ... ... problème 3 : Le cas des ruptures de séquence R0 = R1 + R2; R2 = lshift R2 by 12; CALL routine; R5 = 0; NOP I8 = tableau; ... routine: Decode Fetch R5 = 1; R9 = min(R5, R2); ... problème 3 : Le cas des ruptures de séquence R0 = R1 + R2; R2 = lshift R2 by 12; CALL routine; R5 = 0; I8 = tableau; ... routine: Execute R5 = 1; Decode R9 = min(R5, R2); Fetch ... ⇒ Deux cycles sont perdus (idem lors du retour de la routine) !! solution : Utilisation des branchements retardés Execute Decode Execute CALL Execute Fetch Decode R0 = R1 + R2; Execute Decode Fetch Fetch Decode R2 = lshift R2 by 12; Execute Fetch Decode R5 = 0; routine (DB); I8 = tableau; routine: Fetch R5 = 1; R9 = min(R5, R2); ... ... ⇒ Pas de perte de cycles au moment du branchement (idem lors du retour du sous-programme) ! INCONVENIENT : si la rupture de séquence se trouve à l ’intérieur d’une boucle de moins de trois instructions, le branchement retardé est impossible ! Suite Benchmarks Registres du processeur ALU operations UAL Registre d’état ASTAT = résultat de l’opération courante Registre ASTAT STKY register STKY register Programmation du DSP Exemple Y(n) = a.X(n) + b.(n-1) // Acquisition R0 = dm (Entree_Droite); R0 = ashift r0 by -1; R1 = dm (Entree_Droite); R1 = ashift r1 by -1; R15 = r0 + r1; // Traitement R2 = 0x7FFF FFFF; // 1 en Q1.31 R3 = dm (Coeff_RC); // r3 = Coef a R2 = r2-r3; // r2 = coef b = 1-a R0 = r15*r3 (SSF); // r0 = x(n)*a R1 = dm (Accu_Sortie); // - R1 = r1 * r2 (SSF); //r1 = y(n-1) * b R4 = r1 + r0 // r4 = a.x(n) + b.y(n-1) Dm (Accu_sortie) = r4; // Sortie Dm(Sortie_Droite) = r4; Dm (Sortie_Gauche) = r4; Modes de programmation 3 choix de programmation Langage C Langage Assembleur Mixte Debut et fin d’un programme Le mode de programmation C est le mode par défaut si aucun fichier .LDF n’a été associé. Dans ce cas le fichier 21061.LDF est utilisé par défaut. Le fichier 060_hdr.doj qui initialise les tables d’interruptions et appelle la fonction main() est ajouté. Le point d’entrée du programme est à l’adresse 0x20005 (2e mot du vecteur RESET) La fin du programme est un boucle infinie contenant IDLE associée au label __lib_prog_term. Le paramètre n de return (n) est écrit dans R0 ou il peut être lu après l’exécution. Extension du C ANSI Mots clés : Inline <function> : indique que le code de function doit être intégré dans le code l’appelant asm(‘’texte’’) : insère le code ASM dans le prog C dm/pm : forcer l’allocation es variables globales ou statique en DM ou en PM Segment(‘nom’) : spécifier explicitement le nom du segment des instr/données qui suivent Bool : type booléen Registres et paramètres de fonctions Paramètres Les 3 premiers paramètres passés à une fonction asm sont passés dans les registres R4,R8 et R12 dans cet ordre. Ensuite les autres sont passés dans la pile. Gestion de la pile I6 est le frame pointer I7 est le stack pointer Valeur de retour La valeur retournée par une fonction est passée dans R0. Nommage Une variable nommée X et C doit être nommée _X en assembleur 2.4.4) Les instructions parallèles Certaines instructions peuvent provoquer des opérations en parallèle : Double additions-soustractions : R0 = R8+R12, R1 = R8-R12; Multiplication/Accumulation et addition/soustraction en virgule fixe R0 = R1*R4 (SSFR), R2 = R8-R12; Multiplication et opération arithmétique en virgule flottante F2 = F0*F4, F5 = (F8+F12)/2; Multiplication et double addition-soustraction en virgule fixe ou flottante R5=R0*R4(SSFR),R6=R9+R13,R7=R9-R13; F0=F1*F5, F1=F8+F12, F2=F8-F12; Ces instructions peuvent s’effectuer également en parallèle avec des transferts sur les bus DM et PM simultanés... F0=F1*F5, F1=F8+F12, F5=F8-F12, F8=DM(I0,M0),F12=PM(I8,M8); Exemple Code C : Int additionne(int a, int b, int c, int d, int e){ Int J; J = additionne (1,2,3,4,5); J++; } Code asm compilé R2 = 5; // dernier paramètre DM (I7, -1) = R2; // mise au sommet de pile R1 = 4; // avant dernier DM(I7, -1) = R1; R12 = 3; R8 = 2; R4 = 1; CJUMP _additionne(DB); // DB = retardé CJUMP fait aussi R2 = I6 et I6 = I7 DM (I7, -1) = R2; // empile R2 = ancien frame pointer DM(I7, -1) = PC; // empile l’@ courante MODIFY (I7, 2); R3 = R0; R3 = R3 +1; // Au retour de l’appel dépile les 2 dernier paramètres // J++ Code du fichier asm .SECTION/PM seg_pmco; .GLOBAL _Additionne; _Additionne : MODIFY(I7, -1); DM (-2, I6) = R1; R0 = R0 +R8; R0 = R0 + R12, R1 = DM(1, I6); // avant dernier paramètre R0 = R0 + R1, R1 = DM(1,I6); // dernier paramètre R0 = R0 + R1, R1 = DM(-2;I6);// Restitue R1 I12 = DM(-1, I6); // Adresse de retour -1 JUMP (M14, I12) (DB); I7 = I6; I6 = DM(0, I6); // saut de retour, M14 = 1 !! // restitue la pile // restitue ‘ancien frame pointer Illustration I7 Sauvegarde de R1 Adresse de retour -1 (@ de DM(I7, -1) = PC Ancienne valeur de I6 (dans le prog principal) 4 (avant dernier param) 5 (dernier param) I7 au début de la fonction I6 (I7 à la fin de la fonction) 2.4.3) Les boucles câblées Dans les applications de traitement du signal, la plupart des traitements font intervenir des calculs sur des structures de données de taille connue à l’avance (convolutions, matrices, FFT, etc). Pour optimiser ces calculs, le processeur DSP possède un mécanisme de boucles câblées (hardware loops) qui accélère les R4 boucle. = 8; tests de fin de test de fin MRF = 0; LCNTR = 8, DO _loop UNTIL LCE; R0 = DM(I0,M0); longueur de la boucle R5 = lshift R0 by R4; _loop: MRF= MRF + R5*R5; ... corps de la boucle R4 = 8; MRF = 0; LCNTR = 8, DO _loop UNTIL LCE; R0 = DM(I0,M0); A l’exécution : le compteur est initialisé, les adresses de fin et de début sont empilées dans des piles hardware R5 = lshift R0 by R4; _loop: MRF= MRF + R5*R5; ... Au cycle Fetch : le compteur est décrémenté, la condition de fin est testée et la prochaine adresse est calculée (adresse de début ou prochaine adresse) Pour la réalisation de boucles imbriquées, le processeur dispose de piles matérielles (hardware) de profondeur 6 (6 boucles imbriquées maximum) : Pile des compteurs de programme, Pile des compteurs de boucle, Pile des adresses de fin de boucle. problème 1 : Le pipeline et les boucles câblées R4 = 32; La condition est un résultat nul DO _loop UNTIL EQ; R0 = DM(I0,M0); _loop: R5 = R0 - R4; ... Au cycle Fetch : l’indicateur est celui résultant de l’exécution de R4=32 (pour le premier passage)... Pour des boucles de longueur faible, le processeur « duplique » les instructions dans son pipeline et ajoute, lorsque c’est nécessaire, des « NOP » en fin de boucle pour la terminaison : longueur 1 1 2 2 répétition 1 ou 2 >= 3 1 >= 2 dummy cycles 2 0 2 0 problème 2 : Le pipeline, les branchements et les boucles câblées R4 = 32; LCNTR = 8, DO _loop UNTIL LCE; ... CALL routine; R0 = DM(I0,M0); _loop: R5 = R0 - R4; ... Cette instruction sera lue (cycle Fetch) deux fois à cause de l’instruction de branchement : 1ère fois sans être exécutée (CALL), 2ème fois au retour de la routine. ⇒ Le compteur de boucle est décrémenté deux fois ! PAS de ruptures de séquence non retardées dans les trois dernières instructions d’une boucle câblée ! Plus généralement : pas de branchements dans les boucles (code peu efficace) ! Conclusion sur l’utilisation des boucles Les boucles câblées sont un moyen efficace d’optimiser les performances d’un code (traitement du signal = nombreuses boucles) mais le pipeline impose une attention particulière pour le codage des boucle ! ⇒ Un code optimisé (en vitesse) ne contient de conflits de bus qu’à l’intérieur des boucles... ⇒ Les boucles doivent être de longeurs significatives (déroulage des boucles courtes) ⇒ Les boucles ne doivent pas contenir de branchements !