Forum: FPGA, VHDL & Co. Grundsätzliche Frage zur Realisation von Mathe im FPGA


von Oli P. (Gast)


Lesenswert?

Ich habe eine Formel, die im FPGA gerechnet werden soll, weil der 
Prozessor zu langsam ist.

Dabei habe ich festgestellt, daß ich aufgrund der Tatsache, daß ich Core 
verwenden muss, recht gewaltige Delays erhalte.

Es ist z.B. so, daß ich die Wurzel aus der Summe zweier Thermen bilden 
muss, die dann ihrerseits mit weiteren Ergebnissen (Sinus und Tangen) 
verrechnet werden muss.

Jetzt kann man ja ein Ergebnis erst verwenden, wenn es aus dem Core 
herauskommt. Was macht man solange mit den anderen Werten?

In einer echten pipeline müssen ja alle Werte rechtzeitig vorliegen. 
Bisher habe ich alle Werte immer mit Registern verzögert, um sie dann 
zum richtigen Zeitpunkt parat zu haben. Geht das auch anders?

Ich benutzen z.B. den Xilinx Cordic Core, der je nach Funktion manchmal 
40-50 Takte Latenz aufweist. Das bedeutet, dass ich die anderen Daten, 
die ja auch bis zu 32Bit breite Vektoren dartellen, mit jeweils 32x50 
FFs oder RAMS verzögern muss. Geht das auch anders?

Wie macht man das mit den RAMSs?

Zu jedem Datensatz gibt es eine ganze Hand voll an Paramtern, die vom 
Prozessor zum selben Paket zusammengefasst gespeichert werden, z.B. 
Paket 1 ab Adresse 0, Paket 2 ab Adresse 32, u.s.w.

Wenn ich jetzt Paket 1 adressiere, bekomme ich ja nicht schlagartig alle 
Daten heraus, es sei denn ich bringe sie in parallelel Rams unter. Dann 
kommen sie alle zeitgleich heraus. Sie werden aber nicht alle gleich im 
ersten pipeline Schritt benötigt. Wie regelt man sowas?

von Frank B. (foobar)


Lesenswert?

Du programmierst eine Statemachine, die das entsprechend verzögert und 
die Latenz für das RAM auch berücksichtigt, also Adresse anlegen, 
nächsten Takt nächste Adresse anlegen und nächsten Takt den Wert vom 
vorherigen Takt auslesen und nächste Adresse anlegen usw.

Ich frage mich aber, wenn du bis zu 50 Takte warten musst, warum soll 
dann der FPGA schneller sein? Wenn du noch Umwandlungen brauchst, da 
CORDIC ja nicht mit floating point rechnet und die Daten noch zum FPGA 
hin und zurücktransportieren willst, wird ein kleiner ARM 
Microcontroller nicht langsamer sein. Gibt sogar mittlerweile preiswerte 
Microcontroller mit floating point Einheit, da dauert das dann nur 
wenige Takte pro Berechunngen.

von Oli P. (Gast)


Lesenswert?

Ein FPGA ist sowieso drauf und es soll als pipeline laufen. Bei einer 
state machine verschwende ich ja Zeit, weil manche Architekturteile 
nicht rechnen. Die Berechnerei muss in jedem Takt (50MHz, gfs sogar 
mehr) einen Kanal / Datum abarbeiten.

Die Daten kommen so, daß mit >80MHz / (14 Bit 6+x) = 5MHz parallele 
Daten kommen und das von momentan 2 Kanälen. Das sollen aber 4 oder 
sogar 8 werden. Somit kommen Daten mit bis zu 40Mhz.

Einfache Sachen, wie Gain-Offset habe ich ja schon gemacht, aber dort 
treten nur geringe Verzögerungen auf.

>Gibt sogar mittlerweile preiswerte
>Microcontroller mit floating point Einheit, da dauert das dann nur
>wenige Takte pro Berechunngen.

Frage: Wie macht ein DSP das in nur so wenigen Takten und noch dazu so 
schnell? Ist das alles asynchron verdrahtet und arbeitet ohne pipeline, 
ist aber wengen optimiertem Silikon so schnell? oder benutzen die DSPs 
andere Schaltungen?

Den Cordic will ich ja nicht unbedingt nehmen, ich habe nur im CoreGen 
nichts anderes. Bietet Xilinx da noch IP-Cores an? - die schneller sind?

Ich brauche eigentlich so ziemlich alles:

Quadrat von 24 Bit-Zahl, trivial
Addierer bis 25 Bit, trivial
Wurzel aus 48Bit-Zahl
Sinus (mit Phase von 0...2047)
Logarithmus zur Basis 2 (von 48 Bit-Zahl) mit Auflösung 256 digit
Logarithmus zur Basis 10 (von 24 Bit-Zahl) mit Auflösung 16384 digit
Exponent von 16Bit-Zahl (8+8 Auflösung) zur Basis 10, Ergebnis ist 
maximal 24Bit gross
Reziprokfunktion von 16Bit-Zahl, Auflösung aber 24 Bit genau
Division von 16Bit / 24Bit-Zahl, Auflösung 16 Bit genau

Insgesamt habe ich überschlagen, daß ich bei geschicktem Verteilen 13 
Rechenschritte brauche, von denen 7 "lange" Cores sind mit z.T. 50 
Takten, die anderen sind "+" , "-" und "*", also nur 2-3 Takte.

Das Ganze gibt ja eine Art Baum, wo alle Blätter in den Stamm münden, 
bei mir eine finale Division aus 2 Termen, die selber aus Einzeltermen 
aufgebaut sind.

Ich habe geschätzt, daß ich <500 Takte Latenz bekomme (auch für den 
letzen Kanal) -> das sind immerhin noch 100kHz. Da die 5MHz schnellen 
Daten sowieso durch Filter laufen , die Frequenzen um die 10kHz 
betrachten, ist das allemal schnell genug.

Ich mache mir nur Sorgen, um die Resourcen.

von Frank B. (foobar)


Lesenswert?

Wieviel LEs hast du denn im Xilinx noch frei? Ein CORDIC brauchst ja 
nicht viel, ich habe sowas vor einiger Zeit mal zum Spaß selber 
programmiert mit 32 Bit Auflösung, für die Sinusgenerierung in meinem 
Funktionsgenerator:

http://www.frank-buss.de/SignalGenerator/index.html

Daher könntest du eine Pipeline implementieren mit x instatiierten 
CORDICs  (per VHDL "generate" und "loop") und wärst somit x-mal 
schneller, da du nun mit Eingangsfrequenz*x/CoreGenMax neue Werte 
berechnen kannst (mit deinen 40-50 Takte für CoreGenMax), statt 
Eingangsfrequenz/CoreGenMax. Die maximale Latenz wäre CoreGenMax, plus 
ein wenig Overhead für die Verwaltung.

DSPs arbeiten auch mit Pipelines, was auch der Grund ist, daß 
konditionale Sprünge meist teuer sind, da die Pipeline dann u.U. geleert 
werden müssen. Die Pipelines sind allerdings nicht so lang, da die 
mathematischen Operationen schneller als per CORDIC berechnet werden, 
aber sowas kostet eine ganze Menge Transistoren.

Gibt für deinen Fall bestimmt noch andere Optimierungen. Den Sinus mit 
2048 Werten könntest du per ROM implementieren. Für die Quadrierer hast 
du ja bereits meist Hardwaremultiplizierer je nach Xilinx Chip. 
Logarithmus zur Basis 2 müsstest du auch in einem Takt per Hardware 
berechnen können und für Logarithmus zur Basis 10 gibt es auch 
schnellere Verfahren als CORDIC, wenn du Multiplierer zur Verfügung 
hast. Nur die Division könnte aufwendiger werden.

von Harald (Gast)


Lesenswert?

>Logarithmus zur Basis 2 müsstest du auch in einem Takt per Hardware
>berechnen können

Wie geht das denn bitte in einem Takt?

von D. I. (Gast)


Lesenswert?

Harald schrieb:
>>Logarithmus zur Basis 2 müsstest du auch in einem Takt per Hardware
>>berechnen können
>
> Wie geht das denn bitte in einem Takt?

z. B. mit ner Schleife

von Harald (Gast)


Lesenswert?

Meines Erachtens muss man dann mindestens soviele Entscheider einbauen, 
wie der Ausgangsvektor an Breite braucht. Bei 16 Bit geht das dann 
sicher nicht mehr in einem Takt.

von D. I. (Gast)


Lesenswert?

Harald schrieb:
> Meines Erachtens muss man dann mindestens soviele Entscheider einbauen,
> wie der Ausgangsvektor an Breite braucht. Bei 16 Bit geht das dann
> sicher nicht mehr in einem Takt.

Falsch und Falsch. Bzgl. Takt kommt es immer auf die Freuqenz an und 
zweitens kann man das logarithmisch lösen.

von Frank B. (foobar)


Lesenswert?

Das hier wäre ein Beispiel für eine asynchrone Berechnung:
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
entity main is
6
  generic(
7
    inputLength: natural := 16;
8
    outputLength: natural := 4
9
  );
10
  port(
11
    -- Dateneingang
12
    input: in unsigned(inputLength-1 downto 0);
13
    
14
    -- Logarithmus 2 Ausgang
15
    output: out unsigned(outputLength-1 downto 0)
16
  );
17
end main;
18
19
architecture rtl of main is
20
begin
21
  process(input)
22
    variable mask: unsigned(input'high downto 0);
23
  begin
24
    output <= (others => '0');
25
    mask := (others => '1');
26
    for i in 0 to input'length loop
27
      mask := mask(input'high-1 downto 0) & '0';
28
      if (input and mask) = to_unsigned(0, input'length) then
29
        output <= to_unsigned(i, output'length);
30
        exit;
31
      end if;
32
    end loop;
33
  end process;
34
end architecture rtl;

Für den langsamsten XC9572XL implementiert braucht das ca. 7% an 
Resourcen bei den 16 Bit und läuft bis zu ca. 90 MHz laut Timing Report 
(11 ns worst-case von input zu output). Es berechnet den Logarithmus zur 
Basis 2, jeweils abgerundet, sodaß Bit 0 des Eingangs nicht gebraucht 
wird.

Hier ein Testbench dazu:
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
use work.all;
5
6
entity testbench is
7
end entity testbench;
8
9
architecture test of testbench is
10
  signal input: unsigned(15 downto 0) := (others => '0');
11
  signal output: unsigned(3 downto 0) := (others => '0');
12
13
begin
14
  
15
  log_inst: entity main
16
  port map(
17
    input => input,
18
    output => output
19
  );
20
21
  process
22
  procedure test(inp: natural; expectedResult: natural) is
23
  begin
24
    input <= to_unsigned(inp, input'length);
25
    wait for 10ns;
26
    assert output = to_unsigned(expectedResult, output'length) report "Berechnungsfehler" severity failure;
27
  end;
28
  begin
29
    test(0, 0);
30
    test(1, 0);
31
    test(2, 1);
32
    test(3, 1);
33
    test(4, 2);
34
    test(12345, 13);
35
    test(32768, 15);
36
    test(65535, 15);
37
    assert false report "kein Fehler, Testbench ok" severity failure;
38
  end process;
39
40
end architecture test;

Der letzte "Fehler" per "assert false" ist übrigens ein guter Trick, um 
ein Testbench am Ende anhalten zu lassen.

Interessant übrigens auch, was der Synthetisierer daraus gemacht hat 
(sieht man im Text-Report von ISE) :
1
output(0) <= ((input(15))
2
  OR (NOT input(8) AND NOT input(12) AND NOT input(10) AND NOT input(14) AND 
3
  NOT input(6) AND input(5))
4
  OR (NOT input(8) AND NOT input(12) AND NOT input(10) AND NOT input(14) AND 
5
  NOT input(6) AND NOT input(4) AND input(3))
6
  OR (NOT input(8) AND NOT input(12) AND NOT input(10) AND NOT input(14) AND 
7
  NOT input(6) AND NOT input(4) AND NOT input(2) AND input(1))
8
  OR (NOT input(14) AND input(13))
9
  OR (NOT input(12) AND input(11) AND NOT input(14))
10
  OR (input(9) AND NOT input(12) AND NOT input(10) AND NOT input(14))
11
  OR (NOT input(8) AND NOT input(12) AND NOT input(10) AND NOT input(14) AND 
12
  input(7)));
13
14
15
output(1) <= NOT (((NOT input(11) AND NOT input(10) AND NOT input(15) AND NOT input(14) AND 
16
  NOT input(6) AND input(5) AND NOT input(7))
17
  OR (NOT input(11) AND NOT input(10) AND NOT input(15) AND NOT input(14) AND 
18
  NOT input(6) AND NOT input(2) AND NOT input(7) AND NOT input(3))
19
  OR (input(12) AND NOT input(15) AND NOT input(14))
20
  OR (NOT input(15) AND NOT input(14) AND input(13))
21
  OR (input(9) AND NOT input(11) AND NOT input(10) AND NOT input(15) AND 
22
  NOT input(14))
23
  OR (input(8) AND NOT input(11) AND NOT input(10) AND NOT input(15) AND 
24
  NOT input(14))
25
  OR (NOT input(11) AND NOT input(10) AND NOT input(15) AND NOT input(14) AND 
26
  NOT input(6) AND input(4) AND NOT input(7))));
27
28
29
output(2) <= NOT (((input(9) AND NOT input(12) AND NOT input(15) AND NOT input(14) AND 
30
  NOT input(13))
31
  OR (input(8) AND NOT input(12) AND NOT input(15) AND NOT input(14) AND 
32
  NOT input(13))
33
  OR (NOT input(12) AND input(11) AND NOT input(15) AND NOT input(14) AND 
34
  NOT input(13))
35
  OR (NOT input(12) AND input(10) AND NOT input(15) AND NOT input(14) AND 
36
  NOT input(13))
37
  OR (NOT input(12) AND NOT input(15) AND NOT input(14) AND NOT input(13) AND 
38
  NOT input(6) AND NOT input(5) AND NOT input(4) AND NOT input(7))));
39
40
41
output(3) <= NOT ((NOT input(9) AND NOT input(8) AND NOT input(12) AND NOT input(11) AND 
42
  NOT input(10) AND NOT input(15) AND NOT input(14) AND NOT input(13)));

Viel besser hätte ich es auch nicht selber machen können :-)

Man muß dazu aber nicht extra eine eigene Entity schreiben. Kann man 
auch einfach per Funktion berechnen, entweder parallel wie bei meinem 
Ansatz oder auch seriell, mit einem 0-Test pro Takt, was u.U. weniger 
LEs verbraucht:

Beitrag "log2 in VHDL auf integer?"

von D. I. (Gast)


Lesenswert?

ein wait; am ende der Testbench tuts aber auch zum Anhalten eines 
prozesses

von Frank (Gast)


Lesenswert?

Da möchte ich aber nicht wissen, wie schnell das wird, wenn mehr als nur 
4 Bit benötigt werden.

von Frank B. (foobar)


Lesenswert?

Es wird nicht allzu viel langsamer. Habe es gerade mal mit 32 Eingängen 
und 8 Ausgängen probiert: belegt dann im CPLD 38% und geht bis 37 MHz. 
Der relativ starke Geschwindigkeitseinbruch (ich hätte mit 
logarithmischen Zuwachs relativ zur Anzahl Bits gerechnet) kommt wohl 
daher, daß ISE intern noch einen zusätzlichen Buffer synthesisieren 
musste, da die seitenlange Logikverknüpfungen nicht mehr alle in ein 
"function block" passten. Modernere FPGAs können sowas bestimmt 
problemlos noch mit über 100 MHz implementieren, wenn man bedenkt, daß 
z.B. bei einem Cyclone 3 der 18x18 Bit Multiplizierer mit mehreren 100 
MHz gut läuft.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Frank Buss schrieb:
> Habe es gerade mal mit 32 Eingängen und 8 Ausgängen probiert:
Da sind eigentlich nur 5 Ausgänge nötig. Eben der log2... ;-)

> Es wird nicht allzu viel langsamer. Habe es gerade mal mit 32 Eingängen
> und 8 Ausgängen probiert: belegt dann im CPLD 38% und geht bis 37 MHz.
In einem kleinen Spartan3AN 50k FPGA werden 25 von 704 Slices (=3%) der 
Ressourcen verbraucht bei einer Durchlaufzeit incl. IO-Buffer von
> Maximum combinational path delay: 18.855ns
Wenn die IO-Buffer wegfallen, dann sind die 100MHz nicht so abwegig...

Bei 64 Bits braucht das dann 31 ns, bei einem Ressourcenverbrauch von 
238 Slices (=33%). Man sieht, dass trotz der wesentlich aufwendigeren 
Logik nicht allzuviel zusätzliche Durchlaufzeit gebraucht wird...

von Johann (Gast)


Lesenswert?

Wenn man dann einen Spartan 6 verwendet, dann ist da einiges mehr drin, 
da die 6Bit Lookup Table benutzen. Dann werden die ganzen AND und ODER 
Verknüpfungen in den Lookup Tablen gespeichert. Das bringt dann so 
einiges. Außerdem haben die Spartan 6 enorme Slice Reserven und kostet 
fast nichts ^^.

von Harald (Gast)


Lesenswert?

30ns sind aber nur 30MHz. Wie spaltet man das so auf, daß es 150 werden?

von D. I. (Gast)


Lesenswert?

Harald schrieb:
> 30ns sind aber nur 30MHz. Wie spaltet man das so auf, daß es 150 werden?

in dem mans pipelined

von T. (Gast)


Lesenswert?

>Wenn man dann einen Spartan 6 verwendet, dann ist da einiges mehr drin,
Tolle Aussage eines Ingenieurs. Was ist denn "so einiges"?

>Dann werden die ganzen AND und ODER Verknüpfungen in den Lookup Tablen
>gespeichert.
Bei anderen FPGAs nicht?

>Das bringt dann so einiges.
Siehe oben.

>Außerdem haben die Spartan 6 enorme Slice Reserven und kostet fast nichts
Ich benutze "grosse" FPGAs mit "viel" Power für "wenig" Geld, in denen 
"enorm viel" Elektronik steckt, die "fast" keinen Strom verbraucht und 
"super schnell" rechnet. Im Ernst, hat jemand belastbare Zahlen?

Kann man davon ausgehen, daß ein FPGa mit 6fach LUTs im Vergleich zu 
einem mit 5fach oder 4fach nur 5/6 oder 4/6 der LUTs benötigt? Oder ist 
es noch weniger aufgrund der Verkettung?

In der Uni hat der Prof immer so abgeschätzt, daß je zusätzlicher 
serieller Stufe ein LUT-Eingang wegfällt, da dieser von der vorherigen 
kommt (Ergebnisverktor). Dann wäre das Verhältnis 4/5 bzw 3/5.

von Ollih (Gast)


Lesenswert?

Also ich hab sowas auch mal gemacht.

Ich geh mal davon aus, dass du deine Formel möglichst schnell berechnet 
haben willst. Alle Latenzen deiner Cores insgesamt ergeben eine 
Verzögerung bis du dein erstes Ergebnis bekommst. Ab dann kann man es 
aber so machen, dass du jeden Takt ein neues Ergebnis deiner Formel 
bekommst.

Musst halt nur eine geschickte pipelining Variente deiner Statemachine 
schreiben.

Bei dem Floating Point Operator von Xilinx ist es ja so, dass man 
erstmal einpaar Takte warten muss, bis das erste Ergebnis kommt. Das 
gilt für alle Rechenoperationen. Sobald das erste Rechenergebnis da ist, 
bekommt man aber jeden Takt ein neues.

Ist natürlich nicht einfach eine Formel als gepipelinte Version in eine 
Statemachine zu packen. Aber gehn tut es :)

von Johann (Gast)


Lesenswert?

Wenn einer ISE 12.3 installiert hat dann kann man den oben geposteten 
Code mal auf einen Spartan 6 routen und dann sehen wir wirklich wie viel 
die neue Struktur des Spartan 6 an Performance bringt.

von Johann (Gast)


Lesenswert?

Es ist schade das keiner den Code mal auf einem Spartan 6 routen kann. 
Leider habe ich kein ISE 12 Version installiert. Mich würde mal die 
Performance auf einen Spartan 6 interessieren

von Frank B. (foobar)


Lesenswert?

Ich habe es mal ohne UCF-Datei und für XC95* laufen lassen (also ISE 
sucht die Pins, Speed Grade und den konkreten Typ selbst aus) und da 
geht es laut Timing Report dann bis 166 MHz. Für Spartan 6 (xc6slx25) 
nur bis 82 MHz, aber vielleicht habe ich da nur noch nicht alles richtig 
eingestellt. Habe dann mal Virtex 6 probiert (xc6vlx75-t) und das läuft 
dann bis 142 MHz, also merkwürdigerweise auch nicht allzu schnell. Also 
besser ein CPLD für schnelle Designs einsetzen, statt ein Virtex :-)

Grundsätzlich sind so lange Logikkombinationen natürlich nicht sinnvoll 
und sollte man seriell implementieren, und zusätzlich per Pipelining, 
wenn man es wirklich schnell braucht.

von Johann (Gast)


Lesenswert?

Das kann ich nicht nachvollziehen warum ein COLD da deutlich schneller 
sein soll ^^.

Kannst Du den gleichen Test auch mal für einen Spartan 3 durchführen, 
dann kann man den Unterschied zwischen Spartan 3 und Spartan 6 direkt 
vergleichen.

von Frank B. (foobar)


Lesenswert?

Spartan 3 schafft mit meinem Test 64 MHz. Aber ich mache da bestimmt was 
falsch. Gibt auch keinen richtigen Timing-Report, wie beim CPLD, sondern 
nur bei "Report Navigation" ein "Timing report description". Vielleicht 
sollte man besser eine UCF-Datei anlegen? Die Pin-Bezeichnungen sind 
aber für die Chips immer anders, vielleicht möchte das ja mal ein 
anderer testen. Die ISE Webedition kann jeder nach einer kostenlosen 
Registrierung bei Xilinx frei herunterladen und installieren.

von Johann (Gast)


Lesenswert?

Gut laut Deinem Test geht funktioniert die Schaltung beim Spartan 3 mit 
maximal 64MHz und beim Spartan 6 mit dis zu 82MHz. Dies ist doch schon 
mal sehrerfreulich. Dadurch kann man endlich mal sehen wie viel 
schneller der Spartan 6 gegenüber dem Spartan 3 ist.

Demnach ist der Spartan 6 28% schneller als der Spartan 3.  Dies ist 
doch schon mal sehr beachtlich wenn man bedenkt das auch die 
Stromaufnahme deutlich gerigner ist.

Vielen Dank für Deine Infos.

von Duke Scarring (Gast)


Lesenswert?

Johann schrieb:
> Demnach ist der Spartan 6 28% schneller als der Spartan 3.

Das gilt aber nur für diesen speziellen Fall (leider) und darf nicht 
unbedingt verallgemeinert werden. Der Geschwindigkeitszuwachs kommt ja 
durch verschiedene Faktoren zu stande:

- neuere Prozesstechnologie (kleinere Strukturen -> kürzere Schalt- und 
Laufzeiten)
- andere Granularität (LUT6 statt LUT4, RAM256 statt RAM64)
- vermutlich einfacheres Routing, da die Granularität anders ist, evtl. 
auch andere Routingressourcen vorhanden sind

Durch größere Chips und komplexere Designs, kannst Du Dir den 
Geschwindigkeitsvorteil auch wieder zu nichte machen.
Andererseits gibt es für PCIe und DDR-Speicher integrierte Hard-IP, so 
das u.U. Logik frei wird, die vorher von IP-Cores belegt wurde...

Alles in allem, es ist nicht nur einfach auf eine Zahl zu reduzieren.

Duke

von Johann (Gast)


Lesenswert?

Ich glaube ich werde mir mal ein Evaluation Board zulegen und ein wenig 
mit dem Spartan 6 spielen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.