Your SlideShare is downloading. ×
Praktikum


                        Digitale Signalverarbeitung




Institut für Nachrichtengeräte
und Datenverarbeitung
P...
Inhaltsverzeichnis




1 Der Signalprozessor Analog Devices ADSP-21369                                             7
   1....
2.3.2   Benutzung von Hilfe-Funktionen       ..................                    58
             2.3.3   Verwendung von ...
3.3   Vorbereitende Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . .       97
         3.3.1   Aufgabe I . ....
4.7.4   Aufgabe 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
          4.7.5   Aufgabe 5 . . . . . ...
Kapitel 1


                  Der Signalprozessor Analog Devices
                                        ADSP-21369




  ...
Algorithmen sehr kompakten Code erzeugen, was zu einer Minimierung des Speicherbe-
    darfs in einem Produkt führt.
    D...
zyklus die Werte aus den Eingangsregistern multipliziert werden können und das Ergebnis
des Multiplizierers im Addierer ve...
Abbildung 1.2: Prinzipielle Architektur eines digitalen Signalprozessors
                               (DSP)


     erzeu...
Firma           Typ            Jahr   Zyklus/Frequenz   Wortbreite   Arithmetik    Daten-RAM
 Infineon        Carmel 10xx  ...
1.2 Signalprozessor Analog Devices ADSP-21369

1.2.1 Architektur

                    Zunächst soll ein Überblick über die...
eine „Super Harvard-Architektur“ (daher SHARC) mit SIMD-Funktionalität.




                Abbildung 1.3: Vereinfachtes B...
den Speicher oder die Register also zwei Datenwerte transferiert werden. Dementsprechend
               müssen die Daten a...
Abbildung 1.5: Gleitkomma-Formate für 32 bzw. 40 Bits



                                                               1....
Um einen schnellen Kontextwechsel zu ermöglichen, stehen alle Register in zweifacher
     Ausführung zur Verfügung. Die Mo...
zelne Bits gesetzt werden. Außerdem kann mit XOR kontrolliert werden, in welchen Posi-
tionen sich zwei Register untersche...
• F fractional

               Beide Operanden müssen entweder vom Typ integer oder fractional sein. Signed und unsi-
    ...
Abbildung 1.9: Beispiel für Funktionalität des Shifters


Der Kern des Programmsteuerwerks besteht aus dem Programmzähler ...
Abbildung 1.10: Verschiedene Programmstrukturen


     erst eine Flagge in einem Register, das u. a. vom ASTAT-Register ab...
Beispiel:
                R0 = 0;
            R1 = 100;
       increase: R0 = R0 + 1;               COMP (R0,R1);
        ...
Interrupt Vector Table ADSP-21369

                                                         Emulator (read-only,      HIGH...
Abbildung 1.12: Programmablauf beim Auslösen des Interrupts IRQ1


                /* global interrupt disable*/
         ...
Beispiel:
                              /* reset interrupt address */
                      .SEGMENT/PM pm_rti;
          ...
Beispiel:
        1. Lade in R6 den Inhalt der Speicherstelle 0x0000C000 des Datenspei-
           chers.
                ...
Abbildung 1.13: Beispiel eines zyklischen Puffers


     Nach Initialisierung der B und L-Register wird zuerst die Basisadr...
/* write output data */
                   DM(I7, 0) = F0;

                  /* read input data */
                   F0 ...
wohl Befehle als auch Daten gespeichert werden können, so kann der PM Datenbus Befehle
                   zum Programmrech...
Abbildung 1.14: Aufruf der Entwicklungsumgebung Visual DSP++




                                                         ...
A

                                                                                 C
                                    ...
Abbildung 1.16: File-Dialog


• Menüpunkt Projekt:
  In diesem Menüpunkt können Projekt spezifische Operationen vorgenommen...
Abbildung 1.17: Edit-Dialog


           entsprechenden Speicherstellen können in verschiedenem Format dargestellt werden....
Abbildung 1.18: Session-Dialog


erreicht wird, hält die Ausfühung an, und alle Ausgabefenster werden aktualisiert. Mit
SH...
Abbildung 1.19: View-Dialog


                   einer Auflistung aller existierenden Symbole. Nach Auffinden des Symbols wir...
Abbildung 1.20: Project-Dialog


   • Das Programm 2 zeigt die Anwendung von Befehlen für die parallele Verarbeitung
     ...
Abbildung 1.21: Register-Dialog


     ject → Build Project oder F7) und dann die Programmausführung beginnen (Debug → Run...
Abbildung 1.22: Debug-Dialog



ARCHITECTURE(ADSP-21369)
MEMORY
{
  seg_rth { TYPE(PM RAM) START(0x00090000) END(0x000900f...
Abbildung 1.23: Settings-Dialog


     fügt. Diese dient dem Aufruf der Funktion _convolution bei jedem Eintritt der Inter...
dass sie das Zahlenformat (Float) und die Länge richtig einstellen. Verfizieren Sie
     die Richtigkeit der Darstellung an...
jump (pc,0);

     __TMZHI:             // 0x10: Core timer interrupt (higher priority option)
                jump   (pc,...
jump   (pc,0);
          jump   (pc,0);
          jump   (pc,0);
          jump   (pc,0);

__SP1I:             // 0x38: SP...
__P11I :
             jump     (pc,0);
             jump     (pc,0);
             jump     (pc,0);
             jump     (...
__CB15I:             // 0x7C: Circular buffer 15 overflow exception
           jump   (pc,0);
           jump   (pc,0);
  ...
jump   (pc,0);
                jump   (pc,0);
                jump   (pc,0);
                jump   (pc,0);




     Progr...
idle;
           JUMP end;

/* Delimit the ’_main’ symbol so the linker knows which code is associated
   with the symbol....
Aufgaben

       • Vorarbeit: Laden Sie die Datenfelder in den Dateien fircoefs.dat und rect.dat als FIR
         Filterkoe...
#include <def21369.h>

.SECTION/PM seg_pm32;

           /*FIR coefficients stored in file */
           .VAR     coefs[TA...
/*coefficient buffer pointer initialisation*/
                B8=coefs;

                /*set modify-register M8=1*/
    ...
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Dsvdoc
Upcoming SlideShare
Loading in...5
×

Dsvdoc

591

Published on

None

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
591
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
6
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Dsvdoc"

  1. 1. Praktikum Digitale Signalverarbeitung Institut für Nachrichtengeräte und Datenverarbeitung Prof. Dr.-Ing. Peter Vary
  2. 2. Inhaltsverzeichnis 1 Der Signalprozessor Analog Devices ADSP-21369 7 1.1 Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1.1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1.2 Grundstruktur der Signalprozessor-Architektur . . . . . . . . . . . 7 1.2 Signalprozessor Analog Devices ADSP-21369 . . . . . . . . . . . . . . . . 12 1.2.1 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.2.2 Datenformate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.2.3 Recheneinheiten und Datenregister . . . . . . . . . . . . . . . . . 15 1.2.4 Das Programm-Steuerwerk . . . . . . . . . . . . . . . . . . . . . . 18 1.2.5 Datenadressierung . . . . . . . . . . . . . . . . . . . . . . . . . . 24 1.2.6 Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 1.3 Entwicklungsumgebung: Visual DSP++ . . . . . . . . . . . . . . . . . . . 28 1.3.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1.3.2 Implementierung des Source-Codes . . . . . . . . . . . . . . . . . 29 1.4 Versuchsdurchführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 1.4.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 1.4.2 Programm 1: Interruptgesteuerte Ein-/Ausgabe . . . . . . . . . . . 35 1.4.3 Programm 2: Parallele Verarbeitung von Befehlen . . . . . . . . . . 45 1.4.4 Programm 3: Benutzung des Rahmenprogramms . . . . . . . . . . 49 2 Grundlagen MATLAB und SIMULINK 53 2.1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 2.2 Grundlagen der digitalen Signalverarbeitung . . . . . . . . . . . . . . . . . 54 2.2.1 Abtastung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 2.2.2 Quantisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 2.3 Einführung in MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 2.3.1 Starten von MATLAB . . . . . . . . . . . . . . . . . . . . . . . . 56 3
  3. 3. 2.3.2 Benutzung von Hilfe-Funktionen .................. 58 2.3.3 Verwendung von Variablen . . . . . . . . . . . . . . . . . . . . . . 60 2.3.4 Operationen mit Vektoren und Matrizen . . . . . . . . . . . . . . . 62 2.3.5 Elementare Funktionen . . . . . . . . . . . . . . . . . . . . . . . . 64 2.3.6 Grafische Darstellungsmöglichkeiten . . . . . . . . . . . . . . . . 65 2.3.7 Audiowiedergabe . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 2.3.8 Verwalten von Daten und Dateien . . . . . . . . . . . . . . . . . . 65 2.3.9 Erstellen von Skripten und Funktionen . . . . . . . . . . . . . . . . 66 2.4 Einführung in SIMULINK . . . . . . . . . . . . . . . . . . . . . . . . . . 67 2.4.1 Starten von SIMULINK . . . . . . . . . . . . . . . . . . . . . . . 68 2.4.2 Die Quellen eines SIMULINK-Modells . . . . . . . . . . . . . . . 70 2.4.3 Die Senken eines SIMULINK-Modells . . . . . . . . . . . . . . . 71 2.4.4 Starten einer Simulation mit Angabe von Simulationsparametern . . 71 2.5 DSP-Debugging mit MATLAB . . . . . . . . . . . . . . . . . . . . . . . . 72 2.6 Vorbereitende Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 2.6.1 Aufgabe I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 2.6.2 Aufgabe II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 2.6.3 Aufgabe III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 2.6.4 Aufgabe IV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 2.7 Versuchsdurchführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 2.7.1 Versuch A: Grundlegende MATLAB-Operationen . . . . . . . . . 77 2.7.2 Versuch B: MATLAB-Funktionen . . . . . . . . . . . . . . . . . . 78 2.7.3 Versuch C: DSP-Debugging mit MATLAB . . . . . . . . . . . . . 79 2.7.4 Versuch D: Echo-Effekt mit dem DSP / Zyklischer Speicher . . . . 79 2.7.5 (*) Versuch E: Rückfaltungen . . . . . . . . . . . . . . . . . . . . 80 2.7.6 (*)Versuch F: Grobquantisierung . . . . . . . . . . . . . . . . . . . 81 2.7.7 (*)Versuch G: Amplitudenmodulation mit Hüllkurvenempfang . . . 82 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 3 Diskrete Fourier-Transformation (DFT) 85 3.1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 3.2 Theoretische Grundlagen . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 3.2.1 Definition und Eigenschaften der DFT . . . . . . . . . . . . . . . . 85 3.2.2 Fensterfunktionen .......................... 87 3.2.3 Schnelle Fourier-Transformation (FFT) . . . . . . . . . . . . . . . 93 3.2.4 Schnelle Faltung . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4
  4. 4. 3.3 Vorbereitende Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 3.3.1 Aufgabe I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 3.3.2 Aufgabe II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 3.3.3 Aufgabe III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 3.4 Versuchsdurchführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 3.4.1 Versuch A: DFT spezieller Signale . . . . . . . . . . . . . . . . . . 101 3.4.2 Versuch B: Zero-Padding . . . . . . . . . . . . . . . . . . . . . . . 102 3.4.3 Versuch C: Fensterung und Leckeffekt . . . . . . . . . . . . . . . . 102 3.4.4 Versuch D: Schnelle Faltung mittels overlap add Verfahren . . . . . 103 3.4.5 Versuch E: FFT auf dem DSP . . . . . . . . . . . . . . . . . . . . 106 3.4.6 (*)Versuch F: Gemeinsame Transformation zweier reeller Signale . 106 3.4.7 (*)Versuch G: Analyse von Signalen mit diskr. spektr. Komponenten 107 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 4 Digitale FIR- und IIR-Filter 109 4.1 Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 4.2 Beschreibung linearer zeitdiskreter Systeme . . . . . . . . . . . . . . . . . 109 4.3 FIR-Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 4.3.1 FIR-Filter mit linearer Phase . . . . . . . . . . . . . . . . . . . . . 111 4.3.2 FIR-Filterentwurf . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 4.4 IIR-Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 4.4.1 Stabilität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 4.4.2 Filterstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 4.4.3 IIR-Filterentwurf . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 4.5 Spezielle Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 4.5.1 Differentiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 4.5.2 Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 4.5.3 Hilbert-Transformation . . . . . . . . . . . . . . . . . . . . . . . . 126 4.5.4 Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 4.6 Eigenschaften realer Filter . . . . . . . . . . . . . . . . . . . . . . . . . . 128 4.6.1 FIR-Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 4.6.2 IIR-Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.7 Vorbereitende Aufgaben . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.7.1 Aufgabe 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.7.2 Aufgabe 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.7.3 Aufgabe 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 5
  5. 5. 4.7.4 Aufgabe 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.7.5 Aufgabe 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.7.6 Aufgabe 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.8 Versuchsdurchführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.8.1 FIR-Versuch A: Rechteck-, Hamming- und Blackman-Fenster . . . 137 4.8.2 FIR-Versuch B: Kaiser-Fenster . . . . . . . . . . . . . . . . . . . . 137 4.8.3 FIR-Versuch D: Differentiation . . . . . . . . . . . . . . . . . . . . 138 4.8.4 FIR-Versuch E: FIR-Filter auf dem DSP . . . . . . . . . . . . . . . 138 4.8.5 (*)FIR-Versuch F: Koeffizienten-Quantisierung . . . . . . . . . . . 139 4.8.6 (*) FIR-Versuch G: Tschebyscheff-Approximation . . . . . . . . . 139 4.8.7 IIR-Versuch A: Rekursive Differenzengleichung . . . . . . . . . . 139 4.8.8 IIR-Versuch B: Spezielle rekursive Filter . . . . . . . . . . . . . . 140 4.8.9 IIR-Versuch C: Sprungantwort und Einschwingzeit . . . . . . . . . 142 4.8.10 (*)IIR-Versuch D: Vergleich verschiedener Approximationsverfahren 142 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 6
  6. 6. Kapitel 1 Der Signalprozessor Analog Devices ADSP-21369 1.1 Grundlagen 1.1.1 Einleitung Im ersten Versuch wird der Aufbau und die Programmierung des Prozessors ADSP-21369 von Analog Devices erklärt. Um einen leichten Einstieg in die Projektphase im Anschluss zu ermöglichen, wurde am IND eine Software erstellt. Die Prinzipien dieser Software sind im zusätzlichen Dokument „Rapid Prototyping on Embedded targets: RTProcDSP“ erläu- tert. Durch die Software-Vorgabe wird es den Studenten ermöglicht, den Prototypen eines neuartigen Algorithmus zu erstellen, ohne allzuviel mit den Programmierungsdetails des Echtzeit- Betriebssystems in Kontakt zu kommen. Zur Programmierung der Hardware bietet Analog Devices eine Entwicklungsumgebung an, die sich Visual DSP++ nennt. Diese wird schließlich kurz vorgestellt. Mit ihrer Hilfe wer- den im Anschluss die Übungsbeispiele simuliert bzw. emuliert. 1.1.2 Grundstruktur der Signalprozessor-Architektur In den letzten Jahren war bei Bauteilen in Hardware-Komponenten neuer Produkte eine Spezialisierung auf den Anwendungsfall zu beobachten. Es macht keinen Sinn, für Algo- rithmen zur reinen Signalverarbeitung die Kosten eines General-Purpose Prozessors (z.B. Intel Pentium Prozessor) bezahlen zu müssen, wenn dieselben Algorithmen von einem Pro- zessor ausgeführt werden können, der auf diese Art von Anwendungen spezialisiert ist. Gleichzeitig erscheint es auch in vielen Bereichen sinnvoll, Algorithmen nicht in „Silicon“, also Hardware, sondern in Software zu realisieren. Dadurch kann auf sich ändernde Sys- temanforderungen bei existierenden Produkten schnell und umfassend reagiert werden, in- dem zum Beispiel Software-Updates durchgeführt werden. DSPs sind für Echtzeitanwendungen, z. B. im Bereich der Audio- und Videoverarbeitung, entwickelt worden und verfügen über für ihren Einsatz angepasste Befehlssätze und Archi- tekturen. Sie haben einen niedrigen Leistungsverbrauch und sind durch ihre Spezialisierung in der Anschaffung sehr günstig. Ein Programmierer kann im Bereich der Signalverarbeitungs- 7
  7. 7. Algorithmen sehr kompakten Code erzeugen, was zu einer Minimierung des Speicherbe- darfs in einem Produkt führt. Die ersten Signalprozessoren waren modular aus Bit-Slice-Elementen aufgebaut. Der damit verbundene hohe Aufwand beschränkte den Einsatz der digitalen Signalverarbeitung auf spezielle Gebiete. Erst die Entwicklung monolithisch integrierter Signalprozessoren An- fang der achtziger Jahre ermöglichte den Einsatz der DSV in vielen kommerziellen Berei- chen. Mittlerweile existiert ein breites Angebot digitaler Signalprozessoren verschiedener Hersteller. Trotz ihrer unterschiedlichen und komplexen Architekturen enthalten die heutigen Signal- prozessoren gemeinsame Grundstrukturen. Diese Grundstrukturen ergeben sich aus den An- forderungen der DSV-Algorithmen. Die für die DSV exemplarischen Algorithmen wie z. B. die Fourier-Transformation, die Korrelation und die digitale Filterung enthalten in ihrem Kern vektorielle Skalarprodukte. Um diese Skalarprodukte schnell berechnen zu können, sind Signalprozessoren mit einem Hardware-Multiplizierer ausgestattet. Diesem ist ein Addierer/Akkumulator nachgeschal- tet. Abbildung 1.1 zeigt den prinzipiellen Aufbau der Recheneinheit (Daten-ALU (Arithmetic Logical Unit)) am Beispiel eines 16 Bit Festkomma Signalprozessors. Die zu verarbeiten- den Daten werden in Eingangsregistern zwischengespeichert und dem Multiplizierer zu- geführt. Damit keine Genauigkeitsverluste auftreten, wird das Ergebnis der Multiplikation zunächst mit einer Wortbreite von 31 bzw. 32 Bit dargestellt und zum Inhalt des Ausgangs- registers addiert. Die mit insgesamt 40 Bit um 8 Bit größeren Wortbreiten von Addierer und Ausgangsregister ermöglichen die überlauffreie Addition von bis zu 28 = 256 Produkten. Abbildung 1.1: Daten-ALU eines 16-Bit Signalprozessors Erst wenn das Ergebnis feststeht, also alle Multiplikationen und Additionen durchgeführt wurden, verringert ein sogenannter Barrelshifter die Ergebniswortbreite auf das ursprüng- liche 16 Bit Format. Dabei kann eine mathematische Rundungsoperation und eine betrags- mäßige Begrenzung eingeschlossen sein. Multiplizierer und Addierer arbeiten gleichzeitig. Das bedeutet, dass in einem Maschinen- 8
  8. 8. zyklus die Werte aus den Eingangsregistern multipliziert werden können und das Ergebnis des Multiplizierers im Addierer verarbeitet werden kann. Damit werden zwei Rechenope- rationen pro Zyklus ausgeführt. Das vektorielle Skalarprodukt y der Dimension N kann im Prinzip in N Prozessorzyklen berechnet werden: N−1 y = x · bT = bi · xi (1.1) i=0 mit x = {x0 , x1 , . . . , xN−1 } (1.2) und b = {b0 , b1 , . . . , bN−1 }. (1.3) Das hier beschriebene gleichzeitige Addieren und Multiplizieren ist eines der wichtigsten Merkmale eines Signalprozessors und wird durch spezielle multiply/accumulate-Befehle unterstützt. Bei einigen Signalprozessoren werden die Multiplikation und die Addition in zwei aufeinander folgenden Zyklen, d.h. im „Pipeline“ - Betrieb durchgeführt. Im Akku- mulator wird dabei jeweils das Produktergebnis aus dem vorhergehenden Befehlszyklus auf das Ausgangsregister aufaddiert. Da der Multiplizierer und der Akkumulator gleichzei- tig arbeiten, dauert die Berechnung des Skalarproduktes der Dimension N insgesamt N + 1 Takte. Selbstverständlich bietet ein DSP auch die Möglichkeit, nur zu addieren oder nur zu multi- plizieren. Weiterhin gehören Bit-Manipulationsbefehle und logische Operationen zum Be- fehlsrepertoire eines Signalprozessors. Im vorgestellten Beispiel müssen nach jedem Maschinenzyklus zwei neue Eingangsdaten bereitstehen, wenn die Daten-ALU ausgelastet werden soll. Zusätzlich muss in jedem Pro- grammschritt ein Befehlswort gelesen werden. Das bedeutet drei Datenübertragungen pro Zyklus. Diese hohe Übertragungrate wird durch eine sogenannte „Harvard-Architektur“ er- möglicht. Diese Architektur verfügt im Gegensatz zur „von Neumann-Struktur“ über se- parate Programm- und Datenspeicher sowie über separate Daten- und Adressbusse. Da- durch sind Schreib- und Leseoperationen im Datenspeicher möglich, während gleichzeitig Instruktionen aus dem Programmspeicher gelesen werden. Eine Super-Harvard-Architektur bietet zusätzlich die Möglichkeit, Instruktionen in einem Cache zwischenzuspeichern. Der betrachtete Prozessor ADSP-21369 entspricht in seinem Aufbau einer Super-Harvard Architektur. Abbildung 1.2 zeigt das Prinzipschaltbild eines derartigen Signalprozessors mit erweiterter Harvard-Architektur. Neben der Daten-ALU, dem Programm- und dem Datenspeicher sind zwei weitere Kom- ponenten zu erkennen: die Programm-Ablaufsteuerung und die Adress-ALU. Die Adress-ALU berechnet bei dieser Architektur pro Maschinenzyklus die beiden Spei- cheradressen der Daten für die nächste Operation der Daten-ALU. Erst dadurch wird si- chergestellt, dass in jedem Befehlszyklus neue Daten zu den Eingangsregistern übertragen werden können. Der Befehlssatz der Adress-ALU ist auf die Belange der DSV abgestimmt. Hervorzuheben ist z. B. die in der Regel verfügbare Modulo-Adressarithmetik. Bei dieser Adressierungsart 9
  9. 9. Abbildung 1.2: Prinzipielle Architektur eines digitalen Signalprozessors (DSP) erzeugt die Adress-ALU aufeinanderfolgende Adressen innerhalb eines bestimmten Berei- ches. Ist das Bereichsende erreicht, so wird mit dem Bereichsanfang fortgefahren, ohne dass dazu zusätzliche Programmschritte notwendig sind. Diese Eigenschaft lässt sich z. B. vorteilhaft in Programmen nutzen, in denen Daten blockweise verarbeitet werden (Modulo- Adressierung, vgl. Abschnitt 1.2.5). Die Leistungsmerkmale der heute verfügbaren Prozessoren unterscheiden sich bezüglich • Arithmetik • Speicherkapazität • Geschwindigkeit • Architektur und Befehlssatz. Für eine kleine, repräsentative Auswahl aus dem derzeitigen Angebot wurden in Tabelle 1.1 einige Angaben zum Entwicklungsstand integrierter Signalprozessoren zusammengestellt. 10
  10. 10. Firma Typ Jahr Zyklus/Frequenz Wortbreite Arithmetik Daten-RAM Infineon Carmel 10xx 4 ns/250 MHz 16 bit Festkomma (← Core) Carmel 20xx (Core) a 32k b Tricore 97-99 6 ns/167 MHz 16/32 Gleitkomma (MCU-DSP) Texas TMS320C25 1987 80 ns/12,5 MHz 16 Festkomma 4k x 16 Instruments TMS 320C40 33 ns/30 MHz 32 Gleitkomma 2k x 16 TMS 320C50 25 ns/40 MHz 16 Festkomma 10k x 16 TMS 320C62 1997 5 ns/200 MHz 16 Festkomma 2 x 512k TMS 320C67 1997 6 ns/167 MHz 32 Gleitkomma 2 x 512k TMS 320C64 2000 0,9 ns/1100 MHz (← Core) (Core) Motorola 56001 1987 100 ns/10 MHz 24 Festkomma 2 x 512k x 24 56301 12,5 ns/80 MHz 24 Festkomma 2 x 2k x 24 56800E 2000 5 ns/200 MHz 8-32 Festkomma (← Core) (MCU-DSP) 56853 2001 8,3 ns/120 MHz 32k Analog ADSP-21060 1995 25 ns/40 MHz 32 Gleitkomma 2 x 2M Devices ADSP-21065L 1995 16 ns/60 MHz 32 Gleit/Fest 1M 2192 (Dual) 2000 6,25 ns/160 MHz 16 Festkomma (← Core) 21116 ($5) 2000 10 ns/100 MHz 32 Gleitkomma ADSP-21369 2005 2,5 ns/400 MHz 32 Gleit/Fest 16M a CLIW and power plugs support b Caches-support Tabelle 1.1: Auswahl von aktuellen und alten DSPs 11
  11. 11. 1.2 Signalprozessor Analog Devices ADSP-21369 1.2.1 Architektur Zunächst soll ein Überblick über die Prozessorarchitektur und seine für den Versuch re- levanten Komponenten vorgestellt werden. Abschnitt 1.2 ist als eine kurze Einführung zu verstehen. Für weitere Informationen wird auf die „ADSP-2136x SHARC Programming Reference“ verwiesen, die im Folgenden kurz „Handbuch“ genannt wird. Der in Versuch 4 und der anschließenden Projektphase eingesetzte Signalprozessor ADSP- 21369 ist ein 32 Bit Gleitkommaprozessor, der bei einer Taktfrequenz von 333 MHz arbeitet und 8 MB on-chip-Speicher hat (2 MB SRAM und 6 MB ROM). Er hat darüberhinaus einen Cache-Speicher, der 32 Befehle halten kann, und eine große Anzahl von Registern (spezielle Speicherstellen, die z. B. Operanden und Ergebnisse enthalten). Dank paralleler Ausführung großer Teile der ALU werden SIMD-Befehle (Single Instruction Multiple Data) unterstützt, so dass der Prozessor bei voller Auslastung 2 GFLOPS (Giga Floating Point Operations per Second) verrichten kann. Multi-Prozessor-Einsatz wird durch Link Ports unterstützt. Mit dem DMA (Direct Memory Access) Controller lassen sich große Datenblöcke ein- und ausgeben, ohne dass die Rechenkapazität des Prozessors reduziert wird. Auf der Versuchshardware ist ein solcher Prozessor vorhanden. Desweiteren befindet sich auf derselben Platine ein externer Speicher (SDRAM), der Platz für 4 Mega-Wort Daten beinhaltet (32-Bit Worte) sowie ein 1 MB großer nichtflüchtiger Speicher (Flash-EEPROM). Zur Peripherie gehören außerdem ein AD/DA-Wandler (AD1835A), über den die Audioein- und ausgabe abläuft, sowie eine UART-Schnittstelle (ADM3202 RS-232), die der Laufzeit- Komunikation mit dem PC dient. Die Audiodaten werden mit einer Abtastfrequenz von bis zu 96 KHz, in der Regel aber 48 KHz eingelesen (bei einem DAC sogar bis zu 192Hz) und mit 16, 20 oder 24 Bit quantisiert. Die UART-Schnittstelle hat eine Datenübertragungsrate von 9600 bis 115200 Baud und dient der Übergabe von Variablen vom PC an den DSP und andersherum. Die einzelnen Prozessor-Kennzahlen sind in Tabelle 1.2 zusammengefasst. Prozessortyp ADSP-21369 Datentyp Gleitkomma (32 oder 40 Bit) Festkomma (16 oder 32 Bit) Speicher 2 MB SRAM + 6 MB ROM + 512 kBit SRAM + 4 Mx32 SDRAM + 8 MBit parallel flash + 2 Mbit SPI flash Taktfrequenz 333 MHz Analoge Ausgänge 4 Stück, Stereo Analoge Eingänge 1 Stück, Stereo Abtastraten bis zu 96 kHz 16, 20 oder 24 Bit Analoge Verstärkung Vor- und Nachverstärkung Schnittstelle UART, 16550, 115200 Baud Tabelle 1.2: Prozessor-System-Merkmale Abbildung 1.3 zeigt das vereinfachte Blockschaltbild des ADSP-21369. Es handelt sich um 12
  12. 12. eine „Super Harvard-Architektur“ (daher SHARC) mit SIMD-Funktionalität. Abbildung 1.3: Vereinfachtes Blockschaltbild des ADSP-21369 Der Prozessorkern (core processor) des ADSP-21369 besteht aus zwei Verarbeitungseinhei- ten (PEX und PEY), von denen jede aus drei Recheneinheiten (ALU, Multiplizierer, Shif- ter) besteht sowie einem Programm-Steuerwerk, zwei Daten-Adressrechnern (DAG1 und DAG2) mit jeweils acht Registern für indirekte Adressierung, zwei Timern, dem Befehls- Cache und aus 16 Data-Registern im Data Register File. Darüber hinaus existieren meh- rere Modus- und Statusregister, die die Funktion des Prozessors steuern (z. B. die Register MODE1 und MODE2) und Aussage über Rechenergebnisse geben (z. B. das ASTAT Register). Diese Register sind nicht in Abbildung 1.3 eingezeichnet. Weiterhin sind in obiger Abbildung die DMA-Controller, acht full-duplex-fähige seriel- le Schnittstellen, eine digitale Audioschnittstelle, die vier Präzisionstaktgeber (PCG), ei- ne Eingabeschnittstelle (IDP), ein S/PDIF-Empfänger/-Sender, acht Kanäle mit jeweils ei- nem asynchronen Abtastratenkonverter, acht serielle Schnittstellen, eine 16-bit Parallelein- gabeschnittstelle (PDAP) und eine flexible Signal-Routing-Einheit (DAI SRU) enthält so- wie ein Digital-Peripheral-Interface dargestellt, das drei Taktgeber, eine I2C-Schnittstelle, zwei UARTs, zwei serielle periphere Schnittstellen (SPI) und eine flexible Signal-Routing- Einheit (DPI SRU) enthält. Wie oben schon erwähnt, verfügt der ADSP-21369 über SIMD-Funktionalität. Generell wird die Verarbeitungseinheit PEX benutzt. Eine gleichzeitige Verwendung der Einheit PEY kann durch Setzen des PEYEN-Mode-Bits im MODE1-Register aktiviert werden. Ist dies der Fall, so werden die selben Instruktionen in beiden Verarbeitungseinheiten ausgeführt, operieren allerdings auf verschiedenen Daten. Diese Architektur ist besonders bei rechen- intensiven Algorithmen effizient. Das Einschalten des SIMD-Modus’ hat auch eine Auswirkung auf die Art und Weise, wie Daten zwischen Speicher und den beiden Verarbeitungseinheiten übertragen wird. Ist dieser Modus aktiviert, so wird eine doppelte Datenbandbreite gebraucht, um den Rechenfluss in beiden Einheiten aufrecht zu erhalten, so dass im SIMD-Modus mit jedem Zugriff auf 13
  13. 13. den Speicher oder die Register also zwei Datenwerte transferiert werden. Dementsprechend müssen die Daten auch im Speicher auf korrekte Weise abgelegt werden müssen. Bevor nun die Einheiten des Prozessors näher betrachtet werden, sollen erst die zur Verfü- gung stehenden Datenformate erwähnt werden: 1.2.2 Datenformate Der ADSP-21369 bietet sowohl Fest- als auch Gleitkomma-Formate an. Mit der Festkomma- darstellung wird eine Zahl durch 32 Bits repräsentiert. In einem Register im Data Register File belegen sie die obersten 32 der 40 Bits. Man unterscheidet zwischen den Darstellungen signed und unsigned bzw. integer und fractional. Beim signed-Format ist das ers- te Bit das Vorzeichenbit. Negative Zahlen werden durch das Zweierkomplement dargestellt. Der Wert des LSBs ist für signed integer gleich 1 bzw. für signed fractional 2−31 . So ergeben sich die Zahlenbereiche − 231 ≤ x ≤ 231 − 1 (signed integer, SI) (1.4) −31 −1 ≤ x ≤ 1 − 2 (signed fractional, SF). (1.5) Mit dem unsigned-Format können nur positive Zahlen dargestellt werden. Es gelten die Zahlenbereiche 0 ≤ x ≤ 232 − 1 (unsigned integer, UI) bzw. 0 ≤ x ≤ 1 − 2−32 (unsigned fractional, UF). Die Gewichte der einzelnen Bits sind in Abbildung 1.4 verdeutlicht. Abbildung 1.4: Die Gewichte einzelner Bits verschiedener Festkommafor- mate bei Speicherung in einem 40-Bit Register Zusätzlich zu der Festkommadarstellung bietet der ADSP-21369 ein Gleitkomma-Format mit entweder 32 Bits oder 40 Bits an. Es besteht aus einem Vorzeichenbit s, 8 Bits für den Exponenten e und 23 bzw. 31 Bits für den Bruchteil f der Basis, der durch 1. f dargestellt wird. 1. f ist dabei eine Zahl zwischen 1 und 2 − 2−23 bzw. 1 und 2 − 2−31 . Der Wert einer Zahl x ergibt sich dann aus dem Zusammenhang x = (−1)s · (1. f ) · 2e−127 (1.6) Abbildung 1.5 zeigt die Bitbelegung im Speicher. Durch die Gleitkommadarstellung lassen sich Quantisierung und Übersteuerungsprobleme weitgehend vermeiden. Sie bietet sich deswegen bei der Entwicklung neuer Algorithmen und Prototypen an. 14
  14. 14. Abbildung 1.5: Gleitkomma-Formate für 32 bzw. 40 Bits 1.2.3 Recheneinheiten und Datenregister Der Prozessorkern enthält wie in Abbildung 1.6 veranschaulicht drei unabhängige Rechen- einheiten: eine ALU, einen Multiplizierer und einen Shifter. Abbildung 1.6: Aufbau der Core Unit Alle drei Datenformate können verarbeitet werden: 32 Bit Festkomma, 32 Bit Gleitkomma bzw. 40 Bit Gleitkomma. Zwischen den Recheneinheiten und den Datenbussen befinden sich die Datenregister (register file). Sie bestehen aus 16 Registern der Breite 40 Bit, in de- nen Rechenoperanden und Ergebnisse gespeichert werden. Da alle Rechenoperationen nur einen Prozessorzyklus benötigen, können die Ergebnisse eines Zyklus’ im darauffolgenden Zyklus beliebig weiterverwendet werden. Ein Register wird mit „F“ und einer Nummer 0-15 angesprochen, wenn es für Gleitkomma- operationen verwendet wird, und mit „R“ und einer Nummer für Festkommaoperationen. Das MR Register dient zur Speicherung eines Fixed-Point-Multiplikations Ergebnisses. 15
  15. 15. Um einen schnellen Kontextwechsel zu ermöglichen, stehen alle Register in zweifacher Ausführung zur Verfügung. Die Modifikation eines entsprechenden Statusbits verursacht, dass alle Prozessoroperationen auf diesen „Schatten“-Registern ausgeführt werden. ALU Die ALU verfügt über eine Vielzahl arithmetischer und logischer Operationen für sowohl Festkomma- als auch Gleitkommadarstellungen. Für eine umfassende Liste dieser Opera- tionen sei auf das Handbuch verwiesen. Hier ein kleines Beispiel: Beispiel: 1. Addiere den Inhalt des Registers F0 zu dem des Registers F1 und spei- chere das Ergebnis in F0 F0 = F0 + F1; 2. Nimm den Betrag von F0 und speichere das Ergebnis in F1 F1 = ABS F0; 3. Nimm den maximalen Wert von R1 und R2 und speichere das Ergebnis in R0 R0 = MAX (R1, R2); Alle Befehle sowie „F“ und „R“ können entweder groß oder klein geschrieben werden, da der Assembler Groß- und Kleinschreibung nicht unterscheidet. In dem Statusregister ASTAT befinden sich Flaggen, die nach dem Beenden einer ALU-Operation gesetzt wer- den. Z. B. wird die Flagge AZ (Bit 0) gesetzt, wenn das Ergebnis der Operation Null ist. Abbildung 1.7 zeigt die Bedeutung der einzelnen Bitstellen des Statusregisters ASTAT. Abbildung 1.7: Aufbau des ASTAT-Registers Die ALU wird auch für bitbezogene Operationen wie logische AND, OR, NOR und NOT verwendet. Beispielsweise können mit AND einzelne Bits herausmaskiert und mit OR ein- 16
  16. 16. zelne Bits gesetzt werden. Außerdem kann mit XOR kontrolliert werden, in welchen Posi- tionen sich zwei Register unterscheiden. Die logischen Operationen sind nur für Festkom- maoperanden definiert und haben nur einen Einfluss auf die oberen 32 Bits eines Registers. Die letzten 8 Bits werden zu Null gesetzt. Abbildung 1.8: Beispiel für ALU-Operationen Im Register MODE1 befinden sich drei Flaggen, mit denen u.a. entschieden wird, ob das Ergebnis einer Festkommaoperation ggf. gesättigt werden soll und wie Gleitkommaresultate gerundet werden sollen, siehe Handbuch, Abschnitt 2.5.2. Eine vollständige Beschreibung des MODE1-Registers befindet sich im Handbuch auf den Seiten E-14 und E-15. Multiplizierer Mit dem Multiplizierer können Festkomma- oder Gleitkommamultiplikationen durchge- führt werden. Das Resultat einer Festkommamultiplikation kann in einem der beiden Mul- tiplikatorregister (MRF oder MRB) oder in einem Datenregister R0 - R15 gespeichert wer- den. Bei Nutzung der Multiplikatorregister kann im selben Zyklus eine Addition zu oder Subtraktion von dem letzten Inhalt des Registers vorgenommen werden. Beispiel: 1. Multipliziere den Inhalt des Registers R0 mit dem von R1. Das Ergebnis wird zum Inhalt von MRF addiert. MRF = MRF + R0 · R1; Auf diese Art und Weise kann sehr einfach eine multiply/accumulate-Operation implemen- tiert werden. Das Standardformat für Festkommamultiplikationen ist signed fractional. Wenn eine andere Darstellung erwünscht ist, muss diese explizit angegeben werden. Dies erfolgt, in- dem nach dem Befehl in Klammern die Buchstaben S oder U bzw. I oder F angefügt werden, siehe Abschnitt 2.6.6 des Handbuches. Hierbei bezeichnen: • S signed • U unsigned • I integer 17
  17. 17. • F fractional Beide Operanden müssen entweder vom Typ integer oder fractional sein. Signed und unsi- gned sind mischbar. Beispiel: 1. Die Zahlen in R1 und R2 sind beide vom Format signed fractional R0 = R1 · R2; oder R0 = R1 · R2 (SSF); 2. Die Zahlen in R1 und R2 sind beide vom Format unsigned integer R0 = R1 · R2 (UUI); 3. Die Zahl in R1 ist unsigned fractional und die in R2 signed fractional. Das Ergebnis soll abgerundet werden R0 = MRF - R1 · R2(USFR); Eine Gleitkomma multiply/accumulate-Operation wird durch parallele Nutzung der ALU und des Multiplizierers durchgeführt. Dabei ist jedoch zu beachten, dass für die Addition in dem Beispiel der Wert in F8 zur Berechnung gewählt wird, der einen Takt zuvor bestimmt wurde. Weiterhin sind die zu verwendenden Register einer Begrenzung unterworfen (siehe hierzu auch Handbuch S. B-95). Beispiel: 1. Berechne C := A · B + C Die Werte von A, B und C liegen in F1, F2 bzw. F12. F8 = F1 · F2, F12 = F8 + F12; Genau wie die ALU hat auch der Multiplizierer eine Wirkung auf das Statusregister. Bei- spielsweise kann mit dem MN-Bit im ASTAT-Register kontrolliert werden, ob das Ergebnis einer Multiplikation negativ ist. Shifter Der Shifter stellt die dritte Recheneinheit dar. Mit dem Shifter kann z. B. der Inhalt eines Re- gisters verschoben werden. Der Shifter arbeitet grundsätzlich nur in Festkommaarithmetik, d.h. nur mit den oberen 32 Bits eines Registers. Die letzten 8 der 40 Bits im Resultatre- gister werden immer zu Null gesetzt. Der Shifter hat auch eine Wirkung auf das ASTAT Register. Zusätzlich zu den normalen Shift-Funktionen bietet der Shifter die Möglichkeit zur Manipulation und zum Test einzelner Bits. Der Befehl zur Verschiebung des Inhaltes eines Registers heißt LSHIFT (Logic shift). Die Wirkung wird durch das folgende Beispiel illustriert. 1.2.4 Das Programm-Steuerwerk Das Programm-Steuerwerk (Program Sequencer), siehe Abbildung 1.3, steuert den Ablauf des Programms. Dieser ist meistens linear (sequentiell). Abweichungen von diesem linearen Fluss werden durch die fünf Programmstrukturen loop, jump, subroutine, interrupt und idle ermöglicht. Diese Strukturen sind in Abbildung 1.10 dargestellt. 18
  18. 18. Abbildung 1.9: Beispiel für Funktionalität des Shifters Der Kern des Programmsteuerwerks besteht aus dem Programmzähler (Program Counter, PC). Dieser enthält am Anfang eines Zyklus’ die Adresse des durchzuführenden Befehls und wird während des Zyklus’ hochgezählt. Ein Sprung im Programm geschieht, indem dem PC eine neue Adresse gegeben wird. Alle oben erwähnten Programmstrukturen beste- hen auf die ein oder andere Weise aus Programmsprüngen. Ein Sprung erfolgt im allgemei- nen zu einem in Assembler deklarierten „label“ (Sprungmarke). Die Sprungmarke besteht aus einer willkürlichen, nicht reservierten Zeichenfolge gefolgt von einem Doppelpunkt, z. B. „counter:“ oder „loop1:“. Bei Schleifen gibt die Sprungmarke den letzten Befehl der Schleife an. Kommentare können im Assemblerprogramm wie in C-Programmen zwischen „/*“ und „*/“ eingebettet werden. Sie helfen später sowohl dem Programmierer als auch anderen, die mit dem Quellencode zu tun haben, das Programm zu verstehen. Die Programmstrukturen werden im folgenden kurz erklärt: Loop Eine Schleife (loop) lässt sich sehr bequem durch den loop counter (LCNTR) implementieren. In LCNTR wird dabei zuerst die Zahl der Wiederholungen angegeben. Mit dem Befehl DO < label > UNTIL LCE (loop counter expired) wird die Schleife ausgeführt. Das notwendige Label < label > bezeichnet den letzten Befehl des zu iterierenden Loops. Beispiel: Zähle R0 von 0 bis 100 R0 = 0; LCNTR = 100, DO counter UNTIL LCE; counter: R0 = R0 + 1; /* this is the last instruction of the loop */ Jump Durch einen Sprung (jump) lässt sich eine Programmverzweigung implementieren. Der Sprung kann entweder unbedingt oder bedingt sein. Bei einem bedingten Sprung wird zu- 19
  19. 19. Abbildung 1.10: Verschiedene Programmstrukturen erst eine Flagge in einem Register, das u. a. vom ASTAT-Register abhängig ist, kontrolliert. Wenn diese Flagge eins ist, erfolgt der Programmsprung, im anderen Fall nicht. Unter anderem gibt es die Flaggen EQ Result equal to zero NE Result not equal to zero LT Result less than zero LE Result less than or equal to zero AV Addition overflow MV Multiplier overflow Beispiel: Wenn R0 − R1 ≤ 0, springe nach negative R2 = R0 -R1; IF LE JUMP negative; positive: ... negative: ... Mit dem JUMP-Befehl kann auch eine Schleife implementiert werden. 20
  20. 20. Beispiel: R0 = 0; R1 = 100; increase: R0 = R0 + 1; COMP (R0,R1); IF NE JUMP increase; ... Subroutine Eine Subroutine kann mit einem Unterprogramm verglichen werden, zu dem jederzeit ge- sprungen werden kann und von dem automatisch an die richtige Stelle im Hauptprogramm wieder zurückgesprungen wird, wenn das Unterprogramm zu Ende ist. Mit dem Befehl CALL wird der Sprung initiert und mit RTS (Return From Subroutine) wird die Subroutine beendet. Vor dem Sprung wird die aktuelle Programmadresse auf den Stack gelegt. Durch RTS wird sie wieder dem Programmzähler zurückgegeben. Ein CALL kann genau wie JUMP sowohl unbedingt als auch bedingt sein. Beispiel: IF MV CALL overflow; ... overflow: /* multiplier overflow*/ ... /* do something! */ RTS; Man beachte den Unterschied zwischen JUMP und CALL. Bei JUMP handelt es sich um einen einfachen Sprung zu einer anderen Stelle im Programm, wo das Programm weiter läuft. Mit CALL soll nur gesprungen werden, wenn durch RTS auch ein Sprung zurück gemacht wird. Es ist wichtig, dass alle Paare CALL-RTS zusammenpassen. Wird ein Sprung durch RTS ver- langt, ohne dass zu der Subroutine mit CALL gesprungen wurde, folgt ein Programmfehler, weil auf dem Stack die falsche (oder gar keine) Retour-Adresse vorhanden war. Interrupts Interrupts stellen eine sehr flexible und effiziente Lösung für das Handhaben von Ereignis- sen dar und müssen von jedem Assembler-Programmierer beherrscht werden. Ein Interrupt ist ein Ereignis, das einen Sprung zu einer vordefinierten Adresse auslöst. Das Ereignis kann entweder intern im Prozessor auftreten (z. B. ein ALU-Overflow) oder extern (z. B. wenn ein neuer Abtastwert vorliegt). Der ADSP-21369 hat vier externe Hardware-Interrupts, inklusi- ve eines speziellen Reset-Interrupts, und eine Vielzahl interner Interrupts, siehe Abbildung 1.11. Für die Programmsteuerung bei Interrupts gibt es die Interruptvektortabelle, vergleiche den beispielhaften Programmablauf in Abbildung 1.12. Beim Auslösen eines Interrupts wird zu der dem Interrupttyp entsprechenden Adresse ge- sprungen (Schritt 1). Im allgemeinen enthält diese Adresse den Befehl JUMP, so dass das 21
  21. 21. Interrupt Vector Table ADSP-21369 Emulator (read-only, HIGH PRIORITY 0x90000 + 0x00 0 EMUI non-maskable) Reset (read-only RSTI 0x04 1 non-maskable) Illegal input condition 2 IICDI 0x08 detected Status loop or mode 0x0C 3 stack overflow; or PC SOVFI stack full Timer=0 (high 0x10 4 TMZHI priority option) SPERRI 0x14 SP error interrupt 5 Hardware breakpoint BKPI 0x18 6 interrupt 0x1C 7 Reserved 0x20 8 IRQ2I IRQ2I asserted 0x24 9 IRQ1I IRQ1I asserted 0x28 10 IRQ0I IRQ0I asserted ... ... ... ... User Software 0x98 28 SFT0I Interrupt 0 User Software 0x9C 29 SFT1I Interrupt 1 User Software 0xA0 30 SFT2I Interrupt 2 User Software 0xA4 31 SFT3I LOW PRIORITY Interrupt 3 Abbildung 1.11: Inhalt der Interrupt-Vector-Tabelle Programm mit einer Interrupt-Subroutine fortfahren kann (Schritt 2). Am Ende der Interrupt- Subroutine wird mit RTI (Return From Interrupt) zu der Adresse im Programm zurückge- sprungen, die der Programmzähler enthielt, als der Interrupt ausgelöst wurde (Schritt 3). Bei einem externen Interrupt wird zuerst der Inhalt des ASTAT-Registers und des MODE1- Registers sowie die Adresse des Programmzählers (PC) vor dem Sprung auf den Stack ge- legt. Durch RTI werden diese Registerwerte vom Stack zurückgenommen. Für externe Inter- rupts und für den Timer-Interrupt ist eine Verschachtelungstiefe (nesting) von vier möglich. Folgendes Beispiel erläutert die Verwendung von Timer-Interrupts: Beispiel: 1. Es soll ein Timer-Interrupt jede 1ms ausgelöst werden. 1ms entspricht 6000010 = EA6016 Zyklen bei 60 MHz, vergleiche Abschnitt 11 des Handbuches (zu beachten: Der Prozessor verfügt über zwei völlig gleichwertige Timer-Sektionen 0 und 1) /* timer interrupt address (high Prio timer IRQ)*/ .SEGMENT/PM pm_tmzhi; JUMP timer_interrupt; .ENDSEG; ... 22
  22. 22. Abbildung 1.12: Programmablauf beim Auslösen des Interrupts IRQ1 /* global interrupt disable*/ BIT CLR MODE1 IRPTEN; /* Timer disable and setup first*/ BIT CLR MODE2 TIMEN0; /* stop timer */ BIT SET MODE2 PWMOUT0; /* set PWMOUT modus */ TPERIOD0 = 0xEA5F; /* 60000 - 1 */ /* timer interrupt enable*/ BIT SET IMASK TMZHI; /* start timer (set counter to 0)*/ BIT SET MODE2 TIMEN0; /* global interrupt enable*/ BIT SET MODE1 IRPTEN; ... timer_interrupt: ... /* Process timer-interrupt service*/ RTI; Beim Start oder Reset eines Programms startet der Prozessor immer bei der Adresse des Reset-Interrupts. Diese ist für den ADSP-21369 gleich 0x90005. Dementsprechend muss an dieser Speicherstelle ein Sprung zum Programmanfang vorhanden sein. Dies kann durch den folgenden Programmcode erreicht werden. 23
  23. 23. Beispiel: /* reset interrupt address */ .SEGMENT/PM pm_rti; JUMP prog_start; .ENDSEG; ... /* the program begins here */ prog_start: ... Idle Mit dem Befehl IDLE wird der Prozessor angehalten bis ein externer Interrupt oder ein Timer-Interrupt auftritt. Im Gegensatz zu einer endlosen Warteschleife, die mit dem Be- fehl JUMP implementiert werden kann, wird mit IDLE der Prozessor in einen „low-power“- Zustand versetzt. Nach dem Rücksprung aus der Interrupt-Subroutine wird die Programma- barbeitung mit dem Befehl fortgesetzt, der IDLE folgt. 1.2.5 Datenadressierung Für die Berechnung von Adressen im Programm- und Datenspeicher stehen zwei Daten- adressrechner (data address generators, DAG) zur Verfügung. Mit diesen wird eine indirekte Adressierung ermöglicht, d.h. anstatt die tatsächliche Speicherstelle im Programm anzuge- ben, wird sie durch den DAG berechnet. In jedem der zwei DAG gibt es acht Adressregister, die I-Register oder Index-Register ge- nannt werden. Der DAG1, der Adressen für den Datenspeicher (DM) (siehe Abschnitt 4.2.6) berechnet, enthält die Register I0 bis I7, während der DAG2, der Adressen für den Pro- grammspeicher (PM) berechnet, über die Register I8 bis I15 verfügt. Die Zuordnung der Adressierungs-Register zu Programm-/Datenspeicher ist durch die Tat- sache bedingt, dass der Programm-Speicher Adressbus nur 24 Bit breit ist. Somit sind auch alle Register der zweiten Adressierungslogik nur 24 Bit breit. Mit den zugehörigen Modify-Registern (M0-M7 für DAG1 bzw. M8-M15 für DAG2) kann eine Adresse vor oder nach dem Speicherzugriff hoch- oder runtergezählt (inkrementiert bzw. dekrementiert) werden. Hierdurch wird u.a. eine laufende Abspeicherung oder ein laufen- des Auslesen von Daten ermöglicht, ohne dass dem Index-Register manuell ein neuer Wert gegeben werden muss. Die Adressrechner werden mit den Befehlen DM bzw. PM angespro- chen. Diese Befehle haben zwei Argumente: ein I-Register bzw. ein M-Register oder einen konstanten Modify-Wert. Bei post-modify Adressierung erfolgt erst der Speicherzugriff und dann die Berechnung des neuen Inhalts vom I-Register. Die Syntax lautet dann DM (Ia, Mb) oder PM (Ic, Md), wobei a und b Zahlen zwischen 0 und 7 und c bzw. d Zahlen zwi- schen 8 und 15 sind. Bei pre-modify wird die neue Adresse berechnet bevor der Zugriff erfolgt. Hierfür wird die Syntax DM(Ma, Ib) oder PM(Mc, Id) verwendet. 24
  24. 24. Beispiel: 1. Lade in R6 den Inhalt der Speicherstelle 0x0000C000 des Datenspei- chers. R6 = DM(0x0000C000); oder I0 = 0x0000C000; R6 = DM (I0, 0); 2. Lade in R6 den Inhalt der DM-Speicherstelle, die in I5 vorgegeben ist, und zähle danach die Adresse in I5 um 2 hoch R6 = DM (I5, 2); 3. Addiere die Adresse in I8 mit dem Wert in M10, und speichere danach den Inhalt von R0 an der neu berechneten PM-Adresse (M10 kann negativ sein) PM (M10, I8) = R0; Beispiel: 1. Berechne die Summe N der Elemente in den Speicherstellen DM(I0) bis DM(I0+N-1), für N=10. #define N 10 /* number of elements */ F0=DM (I0,1); F1=DM(I0,1); LCNTR = N-1, DO endloop UNTIL LCE; /* result in F0 */ endloop: F0 = F0 +F1, F1 = DM (I0, 1); Ablauf der Schleife: in DM(I0) bis DM(I0+N-1) sind die Zahlen a0 ... aN−1 vorhanden. Iteration LCNTR F0 F1 Initialisierung N-1 a0 a1 i=1 N-2 a0 +a1 a2 ... N−2 i = N-2 1 m=0 ak aN−1 N−1 i = N-1 0 m=0 ak undefiniert Zyklische Daten-Puffer Zyklische Daten-Puffer werden für effiziente Implementierungen von Filtern, Verzögerungs- gliedern und anderen, in der digitalen Signalverarbeitung häufig vorkommenden Struktu- ren eingesetzt. Die Implementierung zyklischer Daten-Puffer, sog. Ringspeicher (circular buffer), wird durch Modulo-Adressierung erheblich erleichtert. Hierfür werden die base- Register (B) und die length-Register (L) verwendet. Ein B-Register enthält dabei die Basi- sadresse eines Ringspeichers und L die Anzahl der Elemente (die Länge). Das Register L0 und das Register M0 gehören immer zusammen, usw.. 25
  25. 25. Abbildung 1.13: Beispiel eines zyklischen Puffers Nach Initialisierung der B und L-Register wird zuerst die Basisadresse dem entsprechenden I-Register automatisch übergeben. Bei einer post-modify Modifizierung des I-Registers (ei- ne pre-modify Modifizierung ist bei zyklischen Puffern nicht erlaubt) wird kontrolliert, ob die neue Adresse innerhalb des Bereichs zwischen B und B+L-1 liegt. Wenn dies der Fall ist, wird die neue Adresse behalten. Wenn sie zu groß ist, wird die Länge L subtrahiert, und dementsprechend wird L addiert, wenn sie kleiner als B ist. Dadurch ergibt sich immer die neue Adresse durch den Zusammenhang Ia := Ba + (Ia + Mb − Ba) mod La (1.7) a und b können verschiedene Zahlen aus den Bereichen 0-7 oder 8-15 sein (DM bzw PM). Es ist notwendig, dass |Mb| < La. Wie die Register der ALU stehen auch die Register der Adressierungslogik doppelt zur Verfügung, um einen schnellen Kontext Wechsel zu ermöglichen. Beispiel: 1. Es soll ein Verzögerungsglied implementiert werden. Die Verzögerung beträgt 20 Abtastwerte. Der Eingangswert ist beim Aufruf der Subrou- tine delay_20 (wird mit call aufgerufen) in DM(I6) vorhanden und der (verzögerte) Ausgangswert soll in DM(I7) geschrieben werden. /* base address*/ B0 = 0x0C200; /* length 2010 = 1416 */ L0 = 0x14; ... /* read oldest data from buffer */ delay_20: F0 = DM(I0,0): 26
  26. 26. /* write output data */ DM(I7, 0) = F0; /* read input data */ F0 = DM (I6, 0); /* put data in circular buffer, * address is increased by one*/ DM(I0, 1) = F0; RTS; 1.2.6 Speicher Der Programm- und Datenspeicher Der im Praktikum eingesetzte Prozessor ADSP-21369 hat 2 MB on-chip und 4 Mega Wort externen Speicher, der grob in zwei Kategorien aufgeteilt wird: der Programmspeicher (PM - Program Memory) und der Datenspeicher (DM - Data Memory). Die Wortbreite 32 Bit wird für Fest- und Gleitkommadaten verwendet. Es können auch 48-Bit Wörter definiert werden, die entweder Befehle oder 40-Bit Gleitkommadaten enthalten. Schließlich steht auch ein 16-Bit Format für Festkommadaten zur Verfügung. Die Aufteilung des gesamten Speichers in Segmente kann weiterhin für jede Anwendung spezifisch bestimmt werden. Hierfür wird ein sogenanntes Link-Description-File (LDF) angegeben, in dem Symbolen (Namen) physikalische Adressen zugeordnet werden. • Im Adressraum sind die internen Speicherbereiche in Blöcke aufgeteilt, weiterhin gibt es jeweils einen Normal-Word- und einen Short-Word-Bereich. Die vorgegebe- nen Adress-Grenzen müssen dementsprechend auch im Link-Description-File einge- halten werden, für eine genaue Zuordnung der Blöcke zu den Adressen sei auf das Handbuch verwiesen. • Der externe Speicher ist ebenfalls im Adressraum des ADSP 21369 vorgesehen, der schliesslich verwendete Adressbereich ist schaltungstechnisch bedingt von Adresse 0x00200000 bis 0x08FF0000 anzusprechen, näheres hierzu kann der Beschreibung der EZKIT-Hardware entnommen werden. Auf dieselbe Art und Weise sind auch die Steuerungs- und Datenregister der anderen Peripherien (z. B. UART ADM3202) Teil des Adressraums (siehe wiederum Manual zu EZKIT). Im PM können sowohl Programmbefehle als auch Daten gespeichert werden. Im Datenspei- cher (DM) können ausschließlich Daten gespeichert werden. In sowohl PM als auch DM kön- nen auch Wörter anderer Breiten verwendet werden. In diesem Fall ändert sich die Adres- sierung, siehe auch hierzu Kapitel „External Memory Interface“ im Handbuch. Instruktionen müssen über den 32 Bit breiten Programmspeicher Adressbus angefordert werden, weshalb diese sich nur in den entsprechenden Teilen des Speichers befinden kön- nen. Die PM/DM-Datenbusse Über die PM und DM Datenbusse (PMD bzw. DMD ) werden die Befehle und Daten zwischen dem Speicher und den verschiedenen Einheiten des Prozessors verschoben. Wie im PM so- 27
  27. 27. wohl Befehle als auch Daten gespeichert werden können, so kann der PM Datenbus Befehle zum Programmrechenwerk oder auch Daten zu z. B. den Registern transportieren, jedoch nicht gleichzeitig. Der parallele Datenbus ermöglicht zwei gleichzeitige Speicherzugriffe. So kann im selben Zyklus sowohl ein neuer Befehl vom PM als auch ein Wert vom DM gelesen werden. Hierbei wird die Adresse im PM vom Programmsteuerwerk über den PM Adressbus (PMA) und die Adresse im DM über den DM Adressbus (DMA) vermittelt. Alternativ können, wenn der auszuführende Befehl schon im Cache-Speicher vorhanden ist, beide Datenbusse für den Transport von Daten verwendet werden. Vom PM und DM kann dann jeweils ein Wert einem Register übergeben werden - alles innerhalb eines Zyklus’. Dieses Verfahren wird dual data access genannt. Um dieses zu ermöglichen, empfiehlt es sich immer, die zu verarbeitenden Daten zu trennen (z. B. Filterkoeffizienten im Programm- speicher und Abtastwerte im Datenspeicher). Weiter muss der Befehl in der Form compute, DM read or write, PM read or write; sein, zum Beispiel R0=R0+R1, DM(I1, M1)=R0, R1=PM(I8, M8); Wenn der Befehl nicht im Cache-Speicher liegt, werden zusätzliche Zyklen benötigt. 1.3 Entwicklungsumgebung: Visual DSP++ 1.3.1 Einführung Im Laufe der letzten Jahre wurde die Hardware im Bereich Prozessoren immer weiter ent- wickelt. Die heutigen Architekturen sind im allgemeinen sehr leistungsstark, und die Wahl eines DSPs für einen Algorithmus wird somit auch von Faktoren jenseits der Performance mitbestimmt. Zu diesen anderen Faktoren zählt insbesondere die zur Verfügung stehende Software. Die „Time to market“, also der Zeitrahmen, dessen es bedarf, ein Produkt auf den Markt zu bringen, kann durch guten Software-Support drastisch verkürzt werden. C- Compiler gehören hierbei zum Standard. Analog Devices liefert zu den Sharc-DSPs die Software Visual DSP++. In Anlehnung an die Entwicklungsumgebung Visual Studio unter Windows können Algorithmen hiermit schneller implementiert werden. Im Folgenden soll eine kurze Einführung in die Umgebung Visual DSP++ gegeben werden: Das Software-Entwicklungspaket von Analog Devices ist eine IDE (Integrated Develop- ment Environment). Dazu gehören Tools zur Erstellung der Software (z. B. Editor) sowie welche zur Verifikation des erstellten Software (z. B. Debugger). Da die Release von Visual DSP++, die im Praktikum verwendet wird, regelmässig auf den neusten Stand gebracht wird, können sich zu den im folgenden präsentierten Bildern unter Umständen leichte Abweichungen ergeben. 28
  28. 28. Abbildung 1.14: Aufruf der Entwicklungsumgebung Visual DSP++ 1.3.2 Implementierung des Source-Codes Für die Entwicklung eines Algorithmus’ unter Visual DSP++ sowie unter allen Visual- Umgebungen ist der Begriff Projekt von großer Bedeutung: Ein Projekt umfasst die Gesamtheit aller zu der Implementation einer Anwendung gehö- rigen Module. Ein Modul kann zum Beispiel eine Text-Datei sein, in der Code in einer Programmiersprache gespeichert ist, oder eine vorkompilierte Bibliothek, auf deren Metho- den zugegriffen wird. Die Module eines Projektes werden gegebenenfalls kompiliert und gelinkt. Sie bilden somit die Basis für einen ausführbaren Algorithmus. Im einfachsten Fall besteht ein Projekt aus einer Datei, die den gesamten Source-Code enthält. Starten der Entwicklungsumgebung Die Entwicklungsumgebung Visual DSP++ wird wie unter Windows üblich aus dem Start- Menü gestartet (siehe Abbildung 1.14). Die Bedienung gestaltet sich wie in den meisten Fällen sehr intuitiv. Sollte in einer vorher- gehenden Sitzung ein Projekt bearbeitet worden sein, so erscheint dieses Projekt direkt zu Beginn. Es können Unterfenster für das Projekt selbst (A), aber auch für Quellcode (B) bzw. Prozessor-Zustände (z. B. Disassembly(C)) angezeigt werden (siehe Abbildung 1.15). Im Projekt-Fenster wird angezeigt, welche Dateien alle Teil des Projekts sind. Sollte hier noch kein Projekt angezeigt werden, so muss erst eins angelegt werden bzw. ein vorhande- nes eingeladen werden. 29
  29. 29. A C B Abbildung 1.15: Fenster nach Programmstart Menüpunkte In Abbildung 1.15 ist zu erkennen, welche Menüpunkte es bei Visual DSP++ gibt. Die wichtigen Unterpunkte werden im folgenden kurz aufgelistet: • Menüpunkt File: In diesem Menüpunkt können Dateien geladen oder gespeichert werden. Dabei wer- den Projekte und Textdateien im gleichen Dialog ausgewählt. Desweiteren kann auf den DSP ein ganzes Programm bzw. in den Debugger die Symboltabelle eines Pro- gramms geladen werden (Unterpunkte Load ...). Für einen beschleunigten Zugriff stehen Links zu kürzlich geöffneten Projekten usw. bereit (Unterpunkte Recent ...). • Menüpunkt Edit: In diesem Menüpunkt kann Text editiert werden. Dabei stehen die üblichen Opera- tionen wie Copy/Paste zur Verfügung. • Menüpunkt Session: In diesem Menüpunkt kann die aktuelle Session gewählt werden. Im Falle des Prak- tikums dient dies insbesondere der Wahl zwischen der Emulation mithilfe des JTAG- Interfaces (V3+Projektphase) und der Simulation (V1+V2). Andere Sessions werden im Rahmen dieses Praktikums nicht verwendet. • Menüpunkt View: In diesem Menüpunkt können Fenster angezeigt bzw. nicht angezeigt werden. Ins- besondere kann wie in Abbildung 1.19 angedeutet (Plot) auch ein Speicherbereich grafisch ausgegeben werden. 30
  30. 30. Abbildung 1.16: File-Dialog • Menüpunkt Projekt: In diesem Menüpunkt können Projekt spezifische Operationen vorgenommen wer- den. Abbildung 1.20 veranschaulicht die Möglichkeiten, die wichtigsten Funktionen im einzelnen: – Add to Project: Hiermit können einem Projekt Module hinzugefügt werden. – Build File: Hiermit können einzelne Module kompiliert werden. – Build Project: Hiermit werden alle Module eines Projekts kompiliert (sofern notwendig) und zusammengelinkt. – Rebuild all: Hiermit werden sämtliche Projekte wie unter Menüpunkt Build Project bearbei- tet, die momentan geöffnet sind. – Project Options: Compile- und Link-Options können hier spezifiziert werden. • Menüpunkt Register: Dieser Menüpunkt dient dem Debuggen eines gerade auf dem DSP ablaufenden Pro- gramms. Es können hier Register des DSPs zur Anzeige ausgewählt werden. Für eine Benutzung im Rahmen des Praktikums ist insbesondere die Anzeige der inaktiven Register wichtig (Inactive Registers). • Menüpunkt Memory: In diesem Menüpunkt können Speicherbereiche angezeigt werden. Die Daten an den 31
  31. 31. Abbildung 1.17: Edit-Dialog entsprechenden Speicherstellen können in verschiedenem Format dargestellt werden. In den meisten Fällen ist eine Darstellung im Format Two Column sinnvoll. Mit dem Befehl Dump können ganze Speicherbereiche auch direkt in einer Datei gespeichert werden. Dies ist nützlich, um Rechenergebnisse zum Beispiel mit Matlab zu verifi- zieren. • Menüpunkt Debug: Im Menüpunkt Debug kann der Debugger gestartet und kontrolliert werden. • Menüpunkt Settings: In diesem Menüpunkt können Einstellungen vorgenommen werden. Insbesondere können hier Breakpoints aufgelistet und Interrupts simuliert werden (siehe V1). Vorgehensweise bei der Erzeugung von Objekt-Codes (DSP Executable) Im Laufe der Versuche dieses Praktikums und in der anschließenden Projektphase soll das Rahmenprogramm erweitert und auf die Applikation angepasst werden. Hierzu sollen dem Projekt Module hinzugefügt werden. Nachdem dies geschehen ist, werden alle Module er- neut zusammengelinkt. Dabei werden eventuell aufgetretene Fehler im Ausgabefenster an- gezeigt. Sofern die Anwendung komplett erzeugt wurde, wird das auszuführende Programm direkt in den Programmspeicher des DSP geladen. Der Prozessor hält direkt an und erwartet weitere Instruktionen. Ausführen des Programms Zur weiteren Ausführung des geladenen Sourcecodes wird F5 gedrückt. Alle Anzeige- Fenster werden zu diesem Zeitpunkt deaktiviert. Sofern ein zuvor eingestellter Breakpoint 32
  32. 32. Abbildung 1.18: Session-Dialog erreicht wird, hält die Ausfühung an, und alle Ausgabefenster werden aktualisiert. Mit SHIFT-F5 kann der Prozessor auch zu jedem anderen Zeitpunkt angehalten werden. Setzen von Breakpoints Mit F9 kann an der Stelle der aktuellen Eingabeposition ein Breakpoint gesetzt werden. Dies ist jedoch nur möglich, wenn der Prozessor gerade nicht im laufenden Zustand ist. Ausführung Instruktion für Instruktion Wenn der Prozessor angehalten wurde, sei es aufgrund von Breakpoints oder manuell, kann mit F11 der Programmcode Schritt für Schritt abgearbeitet werden (sog. durchsteppen). Zu bedenken ist jedoch, dass auch im angehaltenen Modus Interrupts ausgelöst werden können. Aus diesem Grunde springt der Prozessor zumeist beim Durchgehen Instruktion für Instruktion in die nächste Interrupt Service Routine höherer Ordnung. Vorgehen beim Debuggen Ein DSP-Object-Code wird wie beschrieben eingeladen. Daraufhin kann der Code gedebug- ged werden. Zumeist wird nur ein Teilaspekt untersucht, der in einer Unterfunktion imple- mentiert wurde. Um den zugehörigen Source-Code anzuzeigen, muss der Program-Counter (PC) auf einer Stelle stehen, die zu der zu untersuchenden Funktion gehört. Nach Programmstart muss hierfür der Disassembly-Output nach einem Symbol in der Funk- tion ( z. B. Funktionsname ) durchsucht werden. Dies geschieht durch Betätigung der rech- ten Maustaste sowie Aufruf der Goto-Funktionalität. Die Aktivierung von Browse führt zu 33
  33. 33. Abbildung 1.19: View-Dialog einer Auflistung aller existierenden Symbole. Nach Auffinden des Symbols wird ein Break- point gesetzt. Nun wird der Programmcode bis zum Breakpoint ausgeführt (Run, F5). 1.4 Versuchsdurchführung 1.4.1 Einführung Die nun folgenden Programmbeispiele sollen schrittweise in die Programmierung des ADSP- 21369 einführen. Beachten Sie bitte, dass die ersten zwei Programme im Simulator-Modus ausgeführt werden und erst das dritte Beispiel auf dem DSP emuliert wird. Ziel der hier durchgeführten ersten beiden Versuche ist das Erlernen und Verstehen von Prinzipien Hard- ware-naher Programmierung and der Funktionsweise des DSPs auf Assembler-Programm- Ebene. Der dritte Versuch dient der Einarbeitung in die vorgegebene Software RTProcDSP. Da dieses Programm in C/C++ programmiert wurde, geschieht von diesem Zeitpunkt an auch die Programmierung der Algorithmen in C/C++. Es sei jedoch darauf verwiesen, das es im Verlauf des Projekts notwendig sein kann, Fragmente der Programme in Assembler zu realisieren, um die Ausführungsgeschwindigkeit des DSPs zu erhöhen. Für die Spezifi- kation der Schnittstelle zwischen C/C++ und Assembler sei auf die Anleitung in der DSP Literatur „C/C++ Compiler and Library Manual for Sharc Processors“ verwiesen. • Im Programm 1 werden Daten interruptgesteuert ein- und ausgelesen. Die von ei- ner Datei in einen Ringspeicher eingelesenen Werte können in einer Interruptroutine modifiziert werden und anschließend in einen Speicherbereich ausgegeben werden. Hierbei behandeln wir auch den Aufbau von Ringspeichern mit Hilfe der Modulo- Adressierung. 34
  34. 34. Abbildung 1.20: Project-Dialog • Das Programm 2 zeigt die Anwendung von Befehlen für die parallele Verarbeitung von Multiplikations- und Additionsbefehlen anhand eines Tiefpass-Filters. • Zum Schluss wird das Programm RTProcDSP vorgestellt. Da es im folgenden die Grundlage zur Umsetzung der Projekte bildet, werden alle funktionalen Merkmale an kleinen Übungsbeispielen verdeutlicht. 1.4.2 Programm 1: Interruptgesteuerte Ein-/Ausgabe Der Sinn dieses Programms besteht darin, einen schnellen Einstieg in die Assemblerpro- grammierung zu geben, insbesondere sollen zyklische Datenspeicher und die Programm- steuerung durch Interrupts dargestellt werden. Unter einem Interrupt (engl. für Unterbrechung) versteht man eine von dem Prozessor ge- steuerte Unterbrechung des sequentiellen Programmablaufs. Stellen wir uns den sequenti- ellen Programmablauf so vor, dass ein Ablaufzeiger (oder auch Program Counter) nachein- ander die Speicherstellen anzeigt, die das Programm abarbeiten soll, dann ist ein Interrupt eine Unterbrechung, in der der Zeiger auf eine vorher vereinbarte Speicherstelle „umge- bogen“ wird. An dieser Stelle kann dann auch ein Verweis auf ein Unterprogramm stehen oder direkt ein kurzes Unterprogramm vorhanden sein, das bearbeitet wird. Nach dem Ende dieses Unterprogrammes wird der Programmcode wieder an alter Stelle aufgenommen und weiter sequentiell bearbeitet. Starten Sie das VisualDSP++ Environment und wählen Sie zunächst eine Session zur Pro- zessor-Simulation aus (sollte so eine Session auf Ihrem Rechner noch nicht vorhanden sein, kontaktieren Sie den Betreuer). Laden Sie anschliessend das Projekt V1. Um das Programm auf dem DSP auszuführen, müssen Sie zunächst alle Dateien kompilieren und linken (Pro- 35
  35. 35. Abbildung 1.21: Register-Dialog ject → Build Project oder F7) und dann die Programmausführung beginnen (Debug → Run oder F5). Die Ausführung des Programms bleibt nun zu Beginn der _main-Routine an ei- nem Breakpoint stehen. Von hier aus können alle Schritte des Programms durch F11 einzeln ausgeführt werden. Programmcode von Projekt 1 Das vorliegende Programmbeispiel V1.asm aus dem Projekt V1 liest interruptgesteuert einen Wert aus dem mit input angegebenen Speicherbereich in ein Register. Anschliessend wird dieser Wert an den mit output bezeichneten Speicherplatz weitergegeben. Es ist als ein Pro- grammierbeispiel anzusehen und soll später gemäß der Aufgabenstellung modifiziert wer- den. Es ist sinnvoll, den Programmcode parallel zu dieser Beschreibung zu lesen. Der physikalische Speicherplatz wird durch ein sogenanntes Link-Description-File (LDF) in logische Segmente aufgeteilt. Durch den Linker werden die Symbole (z. B. Namen von Variablen) im Programmcode mit den physikalischen Speicheradressen verbunden, wobei das LDF dafür die Regeln spezifiziert (in „Linker & Utilities Manual for ADSP-21xxx Family DSPs“ wird dieser Vorgang illustriert). Den verschiedenen Segmenten werden wei- terhin unterschiedliche Bedeutungen als Program- oder Datamemory zugewiesen. Zur Ver- anschaulichung hier nun der Auszug aus dem verwendeten Link-Description-File V1.ldf: 36
  36. 36. Abbildung 1.22: Debug-Dialog ARCHITECTURE(ADSP-21369) MEMORY { seg_rth { TYPE(PM RAM) START(0x00090000) END(0x000900ff) WIDTH(48) } seg_pmco { TYPE(PM RAM) START(0x000902D0) END(0x000903FF) WIDTH(48) } seg_pm64 { TYPE(PM RAM) START(0x0004c6c0) END(0x0004dfff) WIDTH(64) } seg_dm64 { TYPE(DM RAM) START(0x0005d800) END(0x0005d81f) WIDTH(64) } seg_pm40 { TYPE(PM RAM) START(0x00090400) END(0x000904ff) WIDTH(48) } seg_dm40 { TYPE(DM RAM) START(0x000b0000) END(0x000b01ff) WIDTH(48) } seg_pm32 { TYPE(PM RAM) START(0x00098c00) END(0x00098cff) WIDTH(32)} seg_dm32 { TYPE(DM RAM) START(0x000b8400) END(0x000bafff) WIDTH(32) } } Das LDF wird bearbeitet, indem mit der rechten Maustaste auf die Datei geklickt wird, um dort dann die Option Open with Source Window zu wählen. Die symbolischen Namen der Segmente im LDF, zum Beispiel seg_dm32, werden im Quellcode von V1.asm wiederver- wendet, um dort Variablen einem bestimmten Speicherbereich zuzuordnen. Eine besondere Rolle spielt das Segment seg_rth, in dem der Programmcode des Interrupt- Vector-Tables abgelegt wird. Zugehörig findet man das Assembler-Programm in der Datei 21369_IVT.asm. Im folgenden soll nicht weiter auf das Link-Description-File eingegangen werden, da dieses in der Regel unverändert übernommen werden kann. Die beiden Assembler Programmcode-Dateien 21369_IVT.asm und V1.asm werden nun einzeln erläutert. In der Datei 21369_IVT.asm werden die Instruktionen im Interrupt-Vector- Table spezifiziert. Der Programmcode ist eine Eins-zu-Eins-Abbildung der Tabelle in Ab- bildung 1.11. Insbesondere wird hier das auszuführende Programm angesprungen, sobald ein Reset-Interrupt ausgelöst wurde (Zeile 15). Um eine Interrupt gesteuerte Eingabe zu ermöglichen, ist weiterhin eine jump-Instruktion an der Position von IRQ0, Zeile 69, einge- 37
  37. 37. Abbildung 1.23: Settings-Dialog fügt. Diese dient dem Aufruf der Funktion _convolution bei jedem Eintritt der Interrupt- Ereignisses. In der Datei V1.asm wird der tatsächliche Programmcode der _main- Funktion sowie der Interrupt-Service-Routine _convolution aufgelistet. Bei Programmausführung dient die _main-Funktion der Initialisierung. In dieser Funktion werden die Adress-Generatoren so eingestellt, dass anschliessend die Einlese-Operation erfolgreich verläuft. Am Ende der _main-Routine versetzt sich der Prozessor in den idle-Modus, um Energy zu sparen (Zeile 53). Aufgaben • Öffnen Sie die Datei V1.ldf im Source Window. Lokalisieren Sie die Speicher- Segmente und identifizieren sie die verwendeten Segmente in den beiden Programm- codedateien. An welcher Adresse liegt die Variable output? • Starten Sie die Programmausführung. Diese hält am ersten Breakpoint sofort wieder an. Das Programm und alle Variablen wurden zu diesem Zeitpunkt bereits in den Speicher geladen. Öffnen Sie die folgenden Fenster zur Betrachtung – des Disassembly-Outputs und des zugehörigen Programmcodes. – des relevanten Adress-Generators (DAG). – des Datenspeichers – der Interrupt-Register. Plotten Sie das Signal, das im Speicher zur Variable input gespeichert ist. Wählen Sie dafür die Option View → Debug Windows → Plot → New. Wählen sie den dar- zustellenden Speicherbereich mithilfe der Browse-Funktionalität aus. Beachten Sie, 38
  38. 38. dass sie das Zahlenformat (Float) und die Länge richtig einstellen. Verfizieren Sie die Richtigkeit der Darstellung anhand der Zahlenwerte, die sie durch Darstellung des Speichers (Memory → Two Column) ablesen können. • Führen Sie das Programm nun Schritt für Schritt bis zum Erreichen der IDLE-Instruktion aus (durchsteppen), indem Sie die Taste F11 bedienen. Beobachten Sie die Initia- lisierung der Interruptregister und der für die Verwaltung des zirkulären Speichers notwendigen Register. Was bedeuten die Kürzel A,D,P,F und der gelbe Pfeil? • Nach der Initialisierung kann das Programm mit F5 (Run) ausgeführt werden. Da wir uns im Simulations-Modus befinden kann ein auftretender externer Interrupt nur si- muliert werden. Dies geschieht durch Auswahl von Settings → Interrupts . Wählen Sie den Interrupt IRQ0 und eine sinnvolle Eistellung für die Interrupt-Periode, sowie die verbleibenden Einstellmöglichkeiten. Was bedeuten die einzelnen Parameter? Be- achten Sie, dass die Aktivierung des Interrupts durch Hinzufügen (ADD) geschieht. • Bevor Sie nun die Ausführung mit F5 weiterlaufen lassen, muss ein Breakpoint an der ensprechende Stelle im Interrupt Vector Table eingefügt werden. Überlegen Sie, an welcher Stelle dieses sinnvoll ist • Verändern Sie das Programm in der Interruptroutine, indem Sie den Befehl R0 = ABS R0; benutzen, um den Betrag des Eingangssignals am Ausgang zu erhalten. Erklären Sie das (unerwartete) Ergebnis. • Für die Experten: In der Vorgabe ist ein Speicher-Zugriffsfehler. Können Sie diesen identifizeren und wenn ja, wie ist er zu beheben? Programmcode 21369_IVT.asm // 21369 Interrupt Vector Table // .extern _main; .extern _convolution; .section/pm seg_rth; __EMUI: // 0x00: Emulator interrupt (highest priority, read-only, non-maskable) nop; nop; nop; nop; __RSTI: // 0x04: Reset (read-only, non-maskable) nop; // <-- (this line is not executed) jump _main; nop; nop; __IICDI: // 0x08: Illegal Input Condition Detected jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SOVFI: // 0x0C: Status loop or mode stack overflow or PC stack full jump (pc,0); jump (pc,0); jump (pc,0); 39
  39. 39. jump (pc,0); __TMZHI: // 0x10: Core timer interrupt (higher priority option) jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SPERRI: // 0x14: jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __BKPI: // 0x18: Hardware breakpoint interrupt jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __res1I: // 0x1C: (reserved) jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __IRQ2I: // 0x20: IRQ2 is asserted jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __IRQ1I: // 0x24: IRQ1 is asserted jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __IRQ0I: // 0x28: IRQ0 is asserted nop; jump _convolution; rti; rti; __DAIHI: // 0x2C: DAI interrupt (higher priority option) __P0I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SPIHI: __P1I : // 0x30: SPI transmit or receive (higher priority option) __SPII : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __GPTMR0I: // 0x34: General Purpose timer 0 interrupt __P2I : 40
  40. 40. jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP1I: // 0x38: SPORT 1 interrupt __P3I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP3I: // 0x3C: SPORT 3 interrupt __P4I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP5I: // 0x40: SPORT 5 interrupt __P5I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP0I: // 0x44: SPORT 0 interrupt __P6I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP2I: // 0x48: SPORT 2 interrupt __P7I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP4I: // 0x4C: SPORT 4 interrupt __P8I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __EP0I: // 0x50: External port0 interrupt. Thats the only label we’re using here __P9I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __GPTMR1I: // 0x54: General Purpose timer 1 interrupt __P10I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP7I: // 0x58: serial port 7 interrupt 41
  41. 41. __P11I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __DAILI: // 0x5C: DAI interrupt (lower priority option) __P12I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __EP1I: // 0x60: External port1 interrupt __P13I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __DPII: // 0x64: DPI interrupt __P14I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __MTMI: // 0x68: Memory to Memory interface interrupt __P15I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SP6I: // 0x6C: serial port 6 interrupt __P16I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __GPTMR2I: // 0x70: General Purpose timer 2 interrupt __P17I : jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SPILI: // 0x74: SPI transmit or receive (lower priority option) __P18I : __SPIBI: jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __CB7I: // 0x78: Circular buffer 7 overflow exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); 42
  42. 42. __CB15I: // 0x7C: Circular buffer 15 overflow exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __TMZLI: // 0x80: Core timer interrupt (lower priority option) jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __FIXI: // 0x84: Fixed-point overflow exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __FLTOI: // 0x88: Floating-point overflow exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __FLTUI: // 0x8C: Floating-point underflow exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __FLTII: // 0x90: Floating-point invalid exception jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __EMULI: // 0x94: Emulator low priority interrupt jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SFT0I: // 0x98: User software interrupt 0 jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SFT1I: // 0x9C: User software interrupt 1 jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SFT2I: // 0xA0: User software interrupt 2 jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); __SFT3I: // 0xA4: User software interrupt 3 (lowest priority) 43
  43. 43. jump (pc,0); jump (pc,0); jump (pc,0); jump (pc,0); Programmcode V1.asm /***************************************************************************** * V1.asm *****************************************************************************/ #include <def21369.h> .SECTION/PM seg_dm32; #define SAMPLES 9 /* Input */ .VAR input[SAMPLES]= -4.,-3.,-2.,-1. ,0., 1.,2.,3.,4.; /* Output */ .VAR output[SAMPLES]= 0.,0.,0.,0.,0.,0.,0.,0.,0.; .ENDSEG; .SECTION/PM seg_pmco; /* Set the scope of the ’_main’ symbol to global. This makes the symbol available for reference in object files that are linked to the current one. Use ’.EXTERN’ to refer to a global symbol from another file.*/ .GLOBAL _main; /* Declare the ’_main’ symbol. */ _main: /* Set bit called IRPTEN to enable interrupts */ BIT SET MODE1 IRPTEN; /* Set irq0i interrupt bit */ BIT SET IMASK IRQ0I; /* set value for modify-register M0 */ M0=1; /* set length of ringbuffer B0 */ L0=@input; /* name buffer B0, I0 is set at the same time */ B0=input; /* set length of ringbuffer B1 */ L1=@output; /* name buffer B1, set I1 */ B1=output; /* Switch to low power mode and expect interrupt */ end: 44
  44. 44. idle; JUMP end; /* Delimit the ’_main’ symbol so the linker knows which code is associated with the symbol. */ ._main.END: .GLOBAL _convolution; _convolution: /* put the current value of buffer addressed by I0 to register F0 and increase buffer with M0 */ R0=DM(I0,M0); /* Here the modification for the input value can be placed: Calculate the absolute value (ASM insn abs) */ /* put value from register F0 to buffer B1 and increase with M0 */ DM(I1,M0)=R0; _convolution.END: RTI; .ENDSEG; 1.4.3 Programm 2: Parallele Verarbeitung von Befehlen In diesem Programm sollen die besonderen Vorzüge des DSP für eine schnelle Verarbeitung von Signalen anhand eines Tiefpass-FIR-Filters dargestellt werden. Das Programm realisiert eine Faltung: m−1 y(n) = a(k)x(n − k), (1.8) k=0 wobei a(k) die Filterkoeffizienten, x(n-k) die um k verzögerten Eingangswerte und y(n) die berechneten Ausgangswerte sind. Das FIR-Filter benutzt vordefinierte Eingangswerte, die in einem Puffer eingeladen sind, und Koeffizienten, die in einer Datei abgelegt sind. Zeitlich zurückliegende Eingangswerte werden in einem zyklischen Speicher als sogenannte States berücksichtigt. Das Programm stellt ein Filter der Länge 23 dar, das auf 200 vordefinierte Werte angewendet wird. Es wird durch einen Interrupt wiederholt aufgerufen. Das zugehörige Projekt V2 ist im Verzeichnis V2 abgelegt. Zusätzlich zu den Quellcode- Dateien werden Variablenfelder in Dateien angegeben: • Eine Datei mit Filterkoeffizienten fircoefs.dat • Eine Datei mit dem Eingabesignal rect.dat Die Filterkoeffizienten werden bei Versuchstermin 4 (s. Kap. 4.8.4) wiederverwendet. Ziel dieses Versuches ist, die Funktion des Programmes durch Debuggen zu verstehen. Da der Algorithmus (vom prinzip her) bekannt ist, soll zunächst eine Referenzimplementierung in Matlab erstellt werden, die das Verständnis des DSP Codes erleichtern soll. 45
  45. 45. Aufgaben • Vorarbeit: Laden Sie die Datenfelder in den Dateien fircoefs.dat und rect.dat als FIR Filterkoeffizienten and Eingangswerte respektive in Matlab ein. Zum Einladen dient der load-Befehl. Realisieren Sie das FIR Filter als Matlab Funktion und verifizieren Sie Ihre Version mit der filter Funktion. Realisieren Sie Ihre Funktion in der Form, dass die Filterzustände als Parameter mit übergeben und wieder zurückgegeben wer- den (analog zur filter-Funktion und der DSP Version). • Öffnen Sie nun das Projekt V2. Verwenden Sie den Interrupt IRQ0 wie bei der ers- ten Aufgabe und setzen Sie einen Breakpoint an der Stelle, von wo aus jeweils die _filter-Routine angesprungen wird. • Vollziehen Sie die Ausführung des Programms nach. Versuchen Sie, die Verarbei- tung von Hand (auf Papier) auszuführen. Was bedeutet der Zusatz (dB) in Zeile 99? Welche Variable im DSP Code entsprechen den Größen h(k), x(k) und x(k − N)? Was genau passiert während der Interrupt-Service-Routine? Wozu dient die XOR- Verknüpfung in Zeile 146? Was passiert, wenn statt der Register R8, R12 und R4 andere Register verwendet werden? Plotten Sie inbuf und outbuf, wenn die Verar- beitung aller Eingangssamples beendet ist. Plotten Sie auch die Koeffizienten im Feld coefs. • Wie könnte man die FIR-Routine verkürzen, wenn eine Rechengenauigkeit von 32 bit integer ausreicht? Programmcode V2 /******************************************************** * * V2.ASM FIR filterprogram for ADSP21369 * Filename: V22.asm * * Description: * * The program shows how to effectivly perform a convolution * with the multiple instructions feature of the ADSP-21369 * * Registers affected: * * F0 F8 I0 I1 * F4 F12 I8 I2 * * Used parameters: * F0 = input sample x(n) * R1 = number of taps in the filter minus 1 * B0 = address of the delay line buffer * M0 = modify value for the delay line buffer * L0 = length of the delay line buffer * B8 = address of the coefficent buffer * M8 = modify value of the coefficent buffer * L8 = length of the coefficent buffer * ********************************************************/ #define SAMPLE 200 /*number of input samples to be filtered*/ #define TAPS 23 /*length of filter*/ 46
  46. 46. #include <def21369.h> .SECTION/PM seg_pm32; /*FIR coefficients stored in file */ .VAR coefs[TAPS]=quot;fircoefs.datquot;; .ENDSEG; .SEGMENT/DM seg_dm32; /*buffer that holds the delay line*/ .VAR dline[TAPS]; /*input samples stored in file */ .VAR inbuf[SAMPLE] = quot;rect.datquot;; /*buffer that contains output coefficients*/ .VAR outbuf[SAMPLE]; .ENDSEG; .SECTION/PM seg_pmco; /* Set the scope of the ’_main’ symbol to global. This makes the symbol available for reference in object files that are linked to the current one. Use ’.EXTERN’ to refer to a global symbol from another file.*/ .GLOBAL _main; /* Declare the ’_main’ symbol. */ _main: /*enable interrupts*/ BIT SET MODE1 IRPTEN; /* enable circular buffering */ BIT SET MODE1 CBUFEN; L0=TAPS; /*delay line buffer pointer initialisation*/ B0=dline; /*set modify-register M0=1*/ M0=1; /*set modify-register M1=1*/ M1=1; L1=@inbuf; /*input buffer pointer initialisation*/ B1=inbuf; L2=@outbuf; /*output buffer pointer initialisation*/ B2=outbuf; L8=TAPS; 47
  47. 47. /*coefficient buffer pointer initialisation*/ B8=coefs; /*set modify-register M8=1*/ M8=1; /*initialize delay line buffer to zero*/ CALL fir_init (DB); /* Set coefficient to second filter coeff */ MODIFY(I8,M8); R0=TAPS; /*enable interrupt IRQ0I*/ BIT SET IMASK IRQ0I; done: /*wait for interrupt */ JUMP done; fir_init: LCNTR=R0, DO zero1 UNTIL LCE; zero1: /*initialize the delay line to 0*/ DM(I0,M0)=0; R0=SAMPLE; LCNTR=R0, DO zero2 UNTIL LCE; zero2: /*initialize the output buffer to 0*/ DM(I2,M1)=0; RTS; /* Delimit the ’_main’ symbol so the linker knows which code is associated with the symbol. */ ._main.END: .GLOBAL _filter; _filter: /* Filter one input sample. */ CALL fir(DB); /* At first setup the loop counter for circular buffer */ R1=TAPS-3; /* Read in h(1) (the second filter coefficient) and x(k-1) */ R8=R8 XOR R8, F0=DM(I0,M0), F4=PM(I8,M8); /* ======> Call fir subroutine */ /*result is stored in outbuf */ DM(I2,M1)=F0; RTI; 48

×