zurück


Im folgenden Bericht wird gezeigt, wie DSP Funktionen -FFT und Multiplikationen- optimal auf die Architektur eines FPGAs zugeschneidert werden können, wenn man sich diese zum Vorteil macht und verschiedene Lösungsansätze durchspielt.

Aufgabenstellung

Im Rahmen einer Signalprozessoranwendung, war ein FPGA-System zu entwickeln, das eine 128-Punkte FFT (Fast Fourier Transformation) mit vorgeschalteter Hamming-Gewichtung abbildet. Im Anschluss an die FFT ist eine Betragsbildung über die transformierten Spektralwerte durchzuführen. Die Betragswerte, die sich dabei ergeben, müssen der Größe nach sortiert werden.
Ein gesamter FFT-Durchlauf hat innerhalb eines Rahmens von 256 ns stattzufinden; die Eingangsdatenbreite der in internen RAM-Blöcken bereitgestellten Daten ist acht Bit und die Daten liegen im Zweier-Komplement vor; die Genauigkeit muss innerhalb der FFT von Stufe zu Stufe um ein Bit zunehmen, so dass die Datenbreite nach der letzten Stufe -also nach sieben Stufen- fünfzehn Bit beträgt. Das waren soweit die Randbedingungen.



Schematisches Blockschaltbild


Es wurde entschieden, diese Funktion, sowie noch einige Zusatzaufgaben in zwei Xilinx FPGAs XCV1000-6 zu implementieren. Ein Umstieg auf die schnelleren und größeren Bausteine der Virtex-E Familie war leider nicht möglich, da die 5 Volt Kompatibilität der I/Os gefordert war; außerdem konnte keine zusätzliche Spannungslage in den Layer eingebracht werden. Ein Lösungsansatz, die Funktion aus einer Kombination von FPGA und DSP zu realisieren wurde verworfen, weil bei der geforderten Performance zu viele parallel arbeitende digitale Signalprozessoren benötigt worden wären.

Die FPGAs wurden entwickelt mit der Hardware-Beschreibungssprache VHDL. Außerdem fand der Xilinx Core Generator intensive Anwendung. Die Synthese erfolgte mit einem gängigen Synthesetool. Im Backend Flow kam insbesondere der Xilinx Floorplanner zum Einsatz, ohne den die Komponenten im zu 98% LUT-ausgelasteten Baustein nicht mehr platziert hätten werden können.

Realisierung in VHDL

Hamming-Fenster
Die Verwendung eines vorgeschalteten Hamming-Fensters im Zusammenhang mit der Fourier Transformation ist sinnvoll, um ungenaue Spektrallinien, verursacht durch die Breite des Messfensters bezogen auf die Periode der Grundschwingung des Signals zu vermeiden.

In der Praxis bedeutet Hamming-Gewichtung die Multiplikation sämtlicher Spektralwerte mit unterschiedlichen, vorher festgelegten Koeffizienten.
Im vorliegenden Fall werden 128 Datenströme à acht Bit (aus jedem der einkommenden acht Datenströme werden sechzehn Datenworte decodiert) mit jeweils acht Bit Koeffizienten multipliziert und bilden als Multiplikationsergebnis ein acht Bit Wort, dessen Ergebnisgenauigkeit in diesem Fall ausreicht.

Für die Implementierung im FPGA war zu beachten, dass die Auslastung im Baustein sehr hoch sein würde und deshalb mit Ressourcen sparsam umgegangen werden musste, andererseits aber auch die Verarbeitungsgeschwindigkeit ein große Rolle spielte, da nach 256ns bereits der nächste Datensatz ansteht. Somit wurden verschiedene Möglichkeiten der Realisierung überdacht bzw. an Beispielen ausgetestet

1. Aufbau von parallelen, mehrfach-genutzten 8*8 Bit Multiplikationen aus Core Generator Elementen
2. Beschreibung von parallelen 8*8 Bit Multiplikationen mit VHDL unter Verwendung von Registerstufen
3. Beschreibung von seriellen Multiplizierern mit VHDL
4. Aufbau von parallelen 8*8 Bit Konstanten-Multiplizierern


Unter Lösung zwei ist zu verstehen, dass die 8*8 Bit Multiplikation einfach als solche kombinatorisch (d.h. ungetaktet) beschrieben wird und im Anschluss daran die benötigte Anzahl an Registerstufen angehängt wird. Die Synthesetools erzeugen daraus eine getaktete Multiplikation, indem sie die Registerstufen sinnvoll innerhalb der Multiplikation aufteilen. Der große Nachteil hierbei ist, dass diese Multiplizierer 128 mal aufgebaut werden müssen, was mit den vorhandenen Ressourcen nicht möglich gewesen wäre.
Lösungsansatz Nummer drei hätte ebenfalls das Problem mit sich geführt, dass ein serieller Multiplizierer 128 mal eingebaut hätte werden müssen, was zu einem erheblichen Aufwand an Ressourcen geführt hätte, wenngleich der Aufwand unter Lösungsansatz zwei natürlich deutlich höher ist - auf den genauen Aufbau eines seriellen Multiplizierers wird im nachfolgenden Kapitel ("FFT") noch eingegangen.
Die Verwendung von Konstanten-Multiplizierern - Lösungsansatz Nummer vier - wäre für die Realisierung der Hamming-Gewichtung natürlich ideal gewesen, da sie genau auf die Anwendung zugeschnitten gewesen wäre, jedoch wären auch in diesem Fall 128 Stück davon benötigt worden und das wäre mit den vorhandenen Ressourcen ebenfalls nicht möglich gewesen.
Somit zeigte sich, dass aufgrund der großen Anzahl an gleichzeitig durchzuführenden Multiplikationen die geeignetste Lösung der Ansatz Nummer eins war. Dabei wurden insgesamt nur acht Parallel-Multiplizierer aufgebaut, von denen jeder innerhalb eines Zykluses 16 mal angesprochen wird. Da sich alle 256 ns die Daten erneuern, können bei einem Systemtakt von 16 ns innerhalb eines Zykluses genau 16 Multiplikationen durchgeführt werden. Die Koeffizienten werden hierfür in acht ROMs - jeweils 16 Werte tief - abgespeichert. Die Adressierung der ROMs wird in gleicher Weise durchgeführt, wie die der RAMs, in denen die Daten bereits abgelegt sind. Somit ist es möglich, innerhalb der vorgegebenen 256 ns 128 Parallel-Multiplikationen durchzuführen, mit nur acht Multiplizierern.

Im Anschluss an die Koeffizienten-Multiplikation werden alle die Datenpakete, die über jeweils einen Multiplizierer geführt wurden, parallel-seriell gewandelt und wieder auf sechzehn Datenströme aufgeteilt, um in der nachgeschalteten FFT verarbeitet werden zu können.



Datendecodierung und Hamming-Gewichtung
FFT

Mit Hilfe einer Fourier Transformation können in einem Zeitsignal enthaltene Frequenzen ermittelt werden. Hierfür wird das Signal über einen bestimmten Zeitraum beobachtet und abgetastet. Die Abtastwerte werden gespeichert und aus diesen Daten kann das Spektrum des beobachteten Signals berechnet werden. Ein periodisches Signal mit einer Frequenz von n Hertz würde im Idealfall (gut gewählter Abtastzeitraum, keine Rundungsfehler) als Spektrum genau ein Amplitudenmaximum bei der Frequenz n aufweisen. Eine Fast Fourier Transformation (FFT) basiert auf dem Gedanken, dass durch Parallelisierung in der Bearbeitung des Zeitsignals der Rechenaufwand optimiert werden kann.

Die 128-Punkte FFT, die es zu realisieren galt, wurde völlig modular aufgebaut, um jedes Modul für sich optimal im Hinblick auf die Datenbreite und die benötigten Rechenoperationen beschreiben zu können und es anschließend nur auf die benötigte Anzahl vervielfältigen zu müssen. Die FFT besteht, wie schon in der Einleitung erwähnt, aus sieben Stufen (128 Punkte entspricht 2^7). Die Eingangs-Datenbreite liegt bei acht Bit – das sind die Ergebnisse aus der Hamming-Gewichtung – und nimmt durch die geforderte Genauigkeit in dieser Anwendung in jeder Stufe um ein Bit zu


FFT bestehend aus sieben Verarbeitungsstufen

Jede Stufe wiederum wird in die benötigten 64 Butterfly-PEs (PE= Prozessor Element) aufgeteilt. Unter Butterfly versteht man eine spezielle Anordnung einer PE. Zugeführt werden zwei Datenströme, wobei es sich in der ursprünglichen und damit aufwendigsten Ausbauform einer PE um komplexe Datenströme handelt. Diese Datenströme werden mit einer Komponentenform aus sin- und cos- Termen beaufschlagt. Als Ergebnis verlassen zwei meist komplexe Datenströme die Butterfly-PE. Die Berechnungen innerhalb einer PE lassen sich zurückführen auf vier reelle Multiplikationen und sechs reelle Additionen bzw. auf drei reelle Multiplikationen und sieben reelle Additionen, je nachdem, welche Berechnung besser vereinfacht werden kann.
Komplexe Butterfly-PE


Bei 128 Eingangsdatenströmen müssen 64 PEs pro FFT-Stufe aufgebaut werden. Die Daten sind über die gesamte FFT hinweg im Zweier-Komplement zu halten. Zur Vereinfachung der gesamten Struktur ist zu überlegen, welche Reduzierungen innerhalb der jeweiligen PEs durchgeführt werden können. In der ersten Stufe beispielsweise liegen noch keine komplexen Daten vor, d.h. die Imaginär-Zweige innerhalb der PE können ignoriert werden. Desweiteren sind die Ergebnisse der Multiplikationen mit Sinus- und Cosinuswerte von bestimmten Winkeln recht einfach zu bestimmen und benötigen nicht den gesamten Ausbausatz einer Butterfly-PE.

Für den Aufbau einer komplexen PE wurden, wie auch bei der Hamming-Gewichtung, wieder verschiedene Lösungsmöglichkeiten ausgetestet und bewertet. Als Realisierung der Multiplikationen kamen im Prinzip die Ansätze aus dem vorherigen Kapitel in Betracht. Die Möglichkeiten für die Additionen waren einfach zu finden, nämlich seriell oder parallel, je nach Art der vorangegangenen Multiplikationen.

Es zeigte sich, dass nur die Verwendung der seriellen Multiplikation zum gewünschten Ergebnis führen konnte, da alle anderen Möglichkeiten das FPGA zu weit mehr als 100% ausgelastet hätten. Die Multiplikation mit zunehmender Bitbreite wird immerhin viele 100 Mal durchgeführt.

Eine serielle Multiplikation mit Zweier-Komplement Zahlen ist wie folgt aufgebaut:

pro Rechenoperation werden jeweils zwei Multiplikationen und die angehängte Addition bzw. Subtraktion zusammengefasst (vgl. Bild oben). Folglich ergeben sich also folgende zwei Terme (die Vorzeichen sind hierbei mit beachtet):

und

wobei x und y für jede PE als Konstante zu sehen sind,

a und b kennzeichnen die zu multiplizierenden Daten.

Wird nun der erste Term genauer untersucht, so ergeben sich, wenn man sich vorstellt, dass a und b jeweils nur die Werte ’0’ oder ’1’ annehmen können, vier mögliche Ergebnisse:

0, -sin(y), cos(x) und cos(x) –sin(y).

Diese Werte werden in ROMs abgelegt und mit der Adresse, die aus der Zusammenfassung der Datenströme a und b gebildet wird, adressiert.

Die Ausgänge der ROMs müssen nun aufakkumuliert werden, da für eine 8-bit serielle Multiplikation die ROM-Tabelle acht mal adressiert werden muß. Hierzu wird der konfigurierbare Akkumulator aus dem Xilinx Core Generator verwendet. Dabei ist ganz speziell darauf zu achten, dass wegen der Zweier-Komplement-Darstellung der Werte, das höchstwertige Bit des Datums nicht aufaddiert sondern subtrahiert werden muss. Bei der Ansteuerung des Akkumulators muss zusätzlich beachtet werden, dass nach jedem Akkumulationsdurchlauf der Inhalt zu löschen ist, da sonst beim nächsten Durchlauf schon ein falscher Initialwert vorhanden ist.

Ausgangsseitig sind die Ergebnisse nun leider parallel vorhanden und müssen im Anschluss daran wieder parallel-seriell gewandelt werden. Es fehlt noch die Anpassung an den Verarbeitungszyklus, denn da sich der Datensatz alle 256 ns erneuert, ist es sinnvoll, diesen Rahmen durch die Stufen hindurch aufrecht zu erhalten. Dies geschieht durch den Einsatz der sehr flächengünstigen SRLs (Shift Register LUT). SRLs sind keine neuen Komponenten, sondern einfach eine alternative Beschaltung innerhalb der Look-Up-Tables in den CLBs. Sie können, neben vielerlei komplexerer Funktionen, ein Schieberegister von einer Länge bis zu 16 in einem Funktionsgenerator abbilden und sind damit sehr platzsparend. Die Schieberegister werden von FFT-Stufe zu FFT-Stufe immer kürzer, da die relevanten Daten des seriellen Datenstroms mit jeder Stufe um ein Bit mehr werden und damit der Durchlauf durch den Funktionsteil immer länger dauert. Da die letzte Stufe 15 Bit breit ist, reicht die Zeit jedoch aus.

Serielle Multiplikation

Betragsbildung

Ein Betrag aus einer komplexen Zahl wird normalerweise gebildet, indem man den Realteil und den Imaginärteil jeweils quadriert, die beiden Quadrate addiert und aus dem Ergebnis die Quadratwurzel zieht. Da die Beträge in diesem Fall jedoch nur für eine Größensortierung verwendet werden, kann auf das Radizieren verzichtet werden. Quadrieren an dieser Stelle bedeutet aber, dass zwei 15 Bit große Werte mit sich selbst multipliziert werden müssen. Da die Betragsbildung aus den Ergebnissen der FFT durchgeführt wird, muss diese Berechnung 64 mal stattfinden. Somit ergeben sich 128 15*15 Bit Multiplikationen und 64 31 Bit Additionen. Die verschiedenen Möglichkeiten der Realisierung führten zu keinem Ergebnis, das im verwendeten Baustein Platz gefunden hätte und dabei auch noch die Verarbeitungszykluszeit eingehalten hätte. Damit konnte diese Art der Betragsbildung nicht umgesetzt werden.

Es musste mit einer Näherungsformel weitergearbeitet werden, was glücklicherweise aus Systemsicht das Ergebnis nicht zu sehr beeinträchtigt. Für diesen Näherungswert wird der Imaginäranteil mit dem Realanteil der komplexen Zahl verglichen und zu dem größeren der beiden die Hälfte des kleineren Wertes hinzuaddiert. Der größte Fehler bei dieser Näherung tritt auf, wenn beide Anteile gleich groß sind.

In der praktischen Umsetzung müssen die seriellen Daten aus der FFT, zuerst seriell-parallel gewandelt werden. Anschließend ist zu prüfen, ob es sich bei den Daten um negative Werte handelt. Ist dies der Fall, so muss eine Zweier-Komplement-Bildung durchgeführt werden, da für die Betragsbildung nur die positiven Werte Verwendung finden. Zweier-Komplement-Bildung bedeutet, dass alle Bits invertiert werden und anschließend der Wert ‚1’ aufaddiert wird.

Dies wird realisiert, indem in einer Schleife alle Bits mit dem Vorzeichenbit XOR-verknüpft werden. Ist das Vorzeichenbit ‚0’, so bleibt der ursprüngliche Wert erhalten, ist er ‚1’ so wird er invertiert. Nun muss nur noch zum Ergebnis das Vorzeichenbit addiert werden und schon erhält man die in Hardware kleinstmögliche Realisierung der Zweier-Komplement-Bildung. Der Vergleich der beiden positiven Werte, die Halbierung des kleineren Wertes durch einfaches Verschieben um ein Bit nach rechts und die Addition der beiden Werte sind in VHDL so beschrieben worden, dass sie vom Synthesetool als solches erkannt wurden und durch entsprechende Softmacros ersetzt werden konnten.

Sortierer

Aufgabe des Sortierers ist es, die Ergebnisse aus der Betragsbildung der Größe nach zu sortieren. Die größte Anforderung hierbei bestand darin, die 15-Bit Werte innerhalb der vorgegebenen Verarbeitungszeit von 256 ns zu ordnen, da danach bereits der nächste Datensatz ansteht.

Um diese Performance zu erreichen, muss hier leider auf die doppelte Taktfrequenz von 125 MHz umgestiegen werden – mittels DLL (Delay Locked Loop) lässt sich diese ‚skew-frei’ innerhalb des FPGAs erzeugen. Da durch die Taktverdoppelung nun 32 Takte zur Verfügung stehen, konnte ein hierarchischer Vergleicherbaum für die parallel verfügbaren Daten aufgebaut werden, der immer zwei Werte miteinander vergleicht. D.h. in der ersten Stufe werden 32 Vergleiche parallel durchgeführt, in der zweiten dann 16 (das sind die Ergebnisse und damit die größeren Werte aus dem ersten Vergleich) usw. bis der letzte Vergleich dann in der sechsten Stufe, also nach sechs Taktzyklen den größten Wert hervorbringt. Innerhalb des Zeitraumes von 32 Takten lassen sich auf diese Weise die fünf größten Werte aus den 64 möglichen finden. Allerdings ist darauf zu achten, dass die jeweils gefundenen Maximalwerte für die weitere Suche maskiert werden, da sonst immer wieder der gleiche Wert gefunden würde. Da sich aus der Ausführung ergibt, dass für das Auffinden der fünf größten Werte 30 Takte benötigt werden (jeweils sechs Takte für einen Durchlauf des Vergleicherbaumes) und sich aus dem Verarbeitungsrahmen von 256 ns nur 32 Takte Zeit ergeben, kann das Ausmaskieren leider nur asynchron erfolgen. Dieser Funktionsteil führte bei der Umsetzung in die Gatter-Netzliste zu den größten Problemen, das Timing einhalten zu können – ein Verarbeitungstakt von 125 Mhz mit asynchronen Rücksetzpulsen, die noch in der Zwischenzeit durchgeführt werden müssen.

Umsetzung in Gatter

Der Schnitt zwischen dem ersten und dem zweiten Virtex-1000-Baustein wurde zwischen FFT und Betragsbildung gemacht. An dieser Stelle liegen die Daten seriell an und somit sind die I/O-Kapazitäten ausreichend.

Da es sich um ein sehr Core-intensives Design handelt, kam die Synthese, die die Cores nur als Black Boxes behandelt, sehr schnell zu einem Ergebnis. Es zeigte sich jedoch, dass ein automatisches Plazieren und Verdrahten in den vorgegebenen Bausteinen nicht möglich war. Der erste Baustein (also der, der die Hamming-Gewichtung und die FFT beinhaltet) ist zu 98% ausgelastet, wobei von großem Vorteil ist, dass sich die Flussrichtung der Daten sehr geradlinig verhält.

Auch die Datenrichtung in der Betragsbildung im zweiten Baustein ist einfach zu erkennen, bei der Sortiererfunktion wird das Ganze schon etwas schwieriger, denn da hat ein Ergebnis wieder direkten Einfluß auf alle davor-liegenden Stufen des Vergleicherbaumes. Eine weitere Schwierigkeit beim Plazieren der Komponenten im FPGA und vor allem beim Verdrahten ist die Tatsache, daß das zweite FPGA noch einige Zusatzfunktionen hat, die u.a. alle Block-RAMs benötigen und damit viele Netze mit hohem Fanout von zentraler Stelle bis an die Bausteinkanten (wo die Block-RAMs plaziert sind) geführt werden müssen. Durch Duplizierung der bildenden Funktionen für diese Netze konnte die Problematik in den Griff bekommen werden.

Bei beiden Bausteinen musste die gesamte Struktur durch manuelles Plazieren erzeugt werden, was sich als sehr aufwändig herausstellte. Es war sogar notwendig, die vorgegebenen Plazierungsmakros bestimmter Core-Elemente zu ändern, um sie enger an andere Elemente schieben zu können. Mit Hilfe der erzeugten Floorplan-Dateien liefen die Xilinx-Backend-Tools jedoch recht schnell über das Design und brachten das gewünschte Ergebnis, das durch Statische Timinganalyse und geeignete Timingsimulation schließlich noch verifiziert wurde.

Fazit und Ausblick

Zum Abschluss der teilweise sehr genauen Ausführungen ist anzumerken, daß sich bei dieser Anwendung mit Sicherheit der Aufwand gelohnt hat, die möglichen Lösungsansätze an kleinen Beispielen im Hinblick auf Timing und Ressourcen durchzutesten und hochzurechnen, denn ohne diese strukturierte und detaillierte Kleinarbeit wäre das gesamte Projekt an der Komplexität der Funktionen gescheitert.

Das Design wurde in großem Maße optimal an die Technologie angepasst, was genaue Kenntnisse derer voraussetzt und nur dadurch, gepaart mit der immensen Ausdauer, immer neue Wege zu begehen, konnte es auch realisiert werden.

In einem anderen Baustein mit anderer Technologie hätten mit Sicherheit auch ganz andere Lösungsmöglichkeiten in Betracht gezogen werden müssen.

Beispielsweise, die Xilinx Baustein-Familie Virtex II genauer betrachtend - zu Beginn der Entwicklungsphase stand sie leider noch nicht zur Verfügung -, wäre eine Realisierung deutlich anders und an einigen Stellen auch wesentlich einfacher gewesen - man denke nur an die Möglichkeit der Verwendung der schon eingebauten Parallel-Multiplizierer. Aber auch für diese FPGAs wird es wieder Anwendungsfälle geben, die die Bausteine an den Rand ihrer Aufnahmekapazität bringen und genauso wieder Erfahrung und beste Kenntnisse in Tools und Technologien erfordern.